From e6f9b3fbda263a8b3218ba8161b92af8149c9d2e Mon Sep 17 00:00:00 2001 From: Nima Jahanshahlou Date: Fri, 24 May 2024 11:17:18 +0200 Subject: [PATCH 1/3] Add gain node and input range --- packages/key-finder-web/package.json | 3 +- .../src/LiveDetection/LiveDetection.css | 6 ++++ .../src/LiveDetection/LiveDetection.tsx | 35 +++++++++++++++++-- .../key-finder-web/src/Utils/audioUtils.ts | 6 ++++ 4 files changed, 47 insertions(+), 3 deletions(-) diff --git a/packages/key-finder-web/package.json b/packages/key-finder-web/package.json index 725cd76..f76b718 100644 --- a/packages/key-finder-web/package.json +++ b/packages/key-finder-web/package.json @@ -6,7 +6,8 @@ "copy:html": "cp src/index.html dist/index.html", "copy:favicon": "cp src/favicon.ico dist/favicon.ico", "build:release": "yarn clean && yarn build && yarn copy:html && yarn copy:favicon", - "serve": "serve ./dist -l 3000 -s" + "serve": "serve ./dist -l 3000 -s", + "dev": "rollup -c --watch & serve ./dist -l 3000 -s" }, "dependencies": { "@rollup/plugin-node-resolve": "15.2.3", diff --git a/packages/key-finder-web/src/LiveDetection/LiveDetection.css b/packages/key-finder-web/src/LiveDetection/LiveDetection.css index 51f68f7..2598938 100644 --- a/packages/key-finder-web/src/LiveDetection/LiveDetection.css +++ b/packages/key-finder-web/src/LiveDetection/LiveDetection.css @@ -21,6 +21,12 @@ width: 100%; } +.live-detection__gain-input { + display: flex; + flex-direction: column; + max-width: 10rem; +} + @media (max-width: 440px) { .live-detection__container { grid-template-columns: 1fr; diff --git a/packages/key-finder-web/src/LiveDetection/LiveDetection.tsx b/packages/key-finder-web/src/LiveDetection/LiveDetection.tsx index 18b0be1..b137c78 100644 --- a/packages/key-finder-web/src/LiveDetection/LiveDetection.tsx +++ b/packages/key-finder-web/src/LiveDetection/LiveDetection.tsx @@ -13,6 +13,7 @@ class LiveDetection extends Component { audioContext: AudioContext | null = null; recorder: RecorderWorkletNode | null = null; levelAnalyzer: AudioAnalyzerNode | null = null; + gainNode: AudioNode | null = null; keyAnalyzer: Worker | null = null; sampleRate: number | null = null; canvas = createRef(); @@ -24,6 +25,7 @@ class LiveDetection extends Component { analyzing: false, result: null, error: null, + gain: 0.0, }; componentDidMount() { @@ -75,13 +77,16 @@ class LiveDetection extends Component { this.recorder = await audioUtils.createRecordingDevice(this.audioContext); this.levelAnalyzer = audioUtils.createAnalyserDevice(this.audioContext); + this.gainNode = audioUtils.createGainNode(this.audioContext); this.dataArray = audioUtils.createDataArrayForAnalyzerDevice( this.levelAnalyzer ); this.canvasContext = this.canvas.current.getContext('2d'); - audioUtils.connectAudioNodes(source, this.recorder); - audioUtils.connectAudioNodes(source, this.levelAnalyzer); + audioUtils.connectAudioNodes(source, this.gainNode); + const postGainSource = this.gainNode; + audioUtils.connectAudioNodes(postGainSource, this.recorder); + audioUtils.connectAudioNodes(postGainSource, this.levelAnalyzer); this.drawLevelAnalysis(); @@ -154,6 +159,14 @@ class LiveDetection extends Component { .setValueAtTime(0, contextTime + 0.1); }; + updateGain = ({ target: { value } }) => { + if (!this.gainNode) return; + const newGain = 10 ** value; + this.setState({ gain: value }); + console.log(this.gainNode.gain); + this.gainNode.gain.value = newGain; + }; + render({}, { connected, analyzing, result, error }) { return (
@@ -191,6 +204,24 @@ class LiveDetection extends Component { disabled={!analyzing} />
+ {connected && ( +
+ + +
+ )}
Date: Fri, 24 May 2024 14:46:14 +0200 Subject: [PATCH 2/3] Add gain control component & update types --- .../src/LiveDetection/LiveDetection.css | 75 ++++++++++++++++++ .../src/LiveDetection/LiveDetection.tsx | 57 +++++++------- .../LiveDetection/components/GainControl.css | 78 +++++++++++++++++++ .../LiveDetection/components/GainControl.tsx | 43 ++++++++++ .../key-finder-web/src/Utils/audioUtils.ts | 2 +- 5 files changed, 226 insertions(+), 29 deletions(-) create mode 100644 packages/key-finder-web/src/LiveDetection/components/GainControl.css create mode 100644 packages/key-finder-web/src/LiveDetection/components/GainControl.tsx diff --git a/packages/key-finder-web/src/LiveDetection/LiveDetection.css b/packages/key-finder-web/src/LiveDetection/LiveDetection.css index 2598938..36a41bb 100644 --- a/packages/key-finder-web/src/LiveDetection/LiveDetection.css +++ b/packages/key-finder-web/src/LiveDetection/LiveDetection.css @@ -21,10 +21,85 @@ width: 100%; } +.live-detection__output-container { + display: flex; + align-items: center; + align-content: center; +} + .live-detection__gain-input { display: flex; flex-direction: column; max-width: 10rem; + align-items: center; +} + +.live-detection__gain-input label { + width: 100px; + margin-bottom: 20px; + text-align: center; +} + +.range-input { + -webkit-appearance: none; + appearance: none; + width: 100%; + appearance: slider-vertical; + -webkit-appearance: slider-vertical; + cursor: pointer; + outline: none; + border-radius: 15px; + height: 6px; + background: #ccc; + height: 200px; + width: 100px; +} + +.range-input::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + height: 15px; + width: 15px; + background-color: #f50; + border-radius: 50%; + border: none; + transition: 0.2s ease-in-out; +} + +.range-input::-moz-range-thumb { + height: 15px; + width: 15px; + background-color: #f50; + border-radius: 50%; + border: none; + transition: 0.2s ease-in-out; +} + +.range-input::-webkit-slider-thumb:hover { + box-shadow: 0 0 0 10px rgba(255, 85, 0, 0.1); +} +.range-input:active::-webkit-slider-thumb { + box-shadow: 0 0 0 13px rgba(255, 85, 0, 0.2); +} +.range-input:focus::-webkit-slider-thumb { + box-shadow: 0 0 0 13px rgba(255, 85, 0, 0.2); +} + +.range-input::-moz-range-thumb:hover { + box-shadow: 0 0 0 10px rgba(255, 85, 0, 0.1); +} +.range-input:active::-moz-range-thumb { + box-shadow: 0 0 0 13px rgba(255, 85, 0, 0.2); +} +.range-input:focus::-moz-range-thumb { + box-shadow: 0 0 0 13px rgba(255, 85, 0, 0.2); +} + +.live-detection__gain-reset { + font-size: 12px; + margin-top: 12px; + padding: 5px; + max-width: 50px; } @media (max-width: 440px) { diff --git a/packages/key-finder-web/src/LiveDetection/LiveDetection.tsx b/packages/key-finder-web/src/LiveDetection/LiveDetection.tsx index b137c78..703c730 100644 --- a/packages/key-finder-web/src/LiveDetection/LiveDetection.tsx +++ b/packages/key-finder-web/src/LiveDetection/LiveDetection.tsx @@ -1,9 +1,9 @@ -import { h, createRef, Fragment, Component } from 'preact'; +import { h, createRef, Fragment, Component, JSX } from 'preact'; import { audioUtils, keyFinderUtils } from '../Utils'; import CircleOfFifths from '../CircleOfFifths'; import { keysNotation } from '../defaults'; import theme from '../theme'; - +import GainControl from './components/GainControl'; import './LiveDetection.css'; const WIDTH = 200; @@ -13,7 +13,7 @@ class LiveDetection extends Component { audioContext: AudioContext | null = null; recorder: RecorderWorkletNode | null = null; levelAnalyzer: AudioAnalyzerNode | null = null; - gainNode: AudioNode | null = null; + gainNode: GainNode | null = null; keyAnalyzer: Worker | null = null; sampleRate: number | null = null; canvas = createRef(); @@ -159,15 +159,23 @@ class LiveDetection extends Component { .setValueAtTime(0, contextTime + 0.1); }; - updateGain = ({ target: { value } }) => { + updateGain = (event: JSX.TargetedEvent) => { if (!this.gainNode) return; - const newGain = 10 ** value; - this.setState({ gain: value }); - console.log(this.gainNode.gain); - this.gainNode.gain.value = newGain; + const newGain = Number(event.currentTarget.value); + this.setState({ gain: newGain }); + const gainValue = 10 ** newGain; + this.gainNode.gain.value = gainValue; + }; + + resetGain = () => { + this.setState({ gain: 0 }); + this.gainNode.gain.value = 1; }; - render({}, { connected, analyzing, result, error }) { + render({}, { connected, analyzing, result, error, gain }) { + const formattedGain = gain === 0 ? 0 : (10 ** gain).toFixed(2); + const sign = gain >= 0 ? '+' : '-'; + return (
{error &&

{error}

} @@ -204,30 +212,23 @@ class LiveDetection extends Component { disabled={!analyzing} />
- {connected && ( -
- - + {connected && ( + -
- )} -
+ )}
diff --git a/packages/key-finder-web/src/LiveDetection/components/GainControl.css b/packages/key-finder-web/src/LiveDetection/components/GainControl.css new file mode 100644 index 0000000..8d0dd02 --- /dev/null +++ b/packages/key-finder-web/src/LiveDetection/components/GainControl.css @@ -0,0 +1,78 @@ +.gain-input { + display: flex; + flex-direction: column; + max-width: 10rem; + align-items: center; +} + +.gain-input label { + width: 100px; + margin-bottom: 20px; + text-align: center; +} + +.range-input { + -webkit-appearance: none; + appearance: none; + width: 100%; + appearance: slider-vertical; + -webkit-appearance: slider-vertical; + cursor: pointer; + outline: none; + border-radius: 15px; + height: 6px; + background: #ccc; + height: 200px; + width: 100px; +} + +.range-input::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + height: 15px; + width: 15px; + background-color: #f50; + border-radius: 50%; + border: none; + transition: 0.2s ease-in-out; +} + +.range-input::-moz-range-thumb { + height: 15px; + width: 15px; + background-color: #f50; + border-radius: 50%; + border: none; + transition: 0.2s ease-in-out; +} + +.range-input::-webkit-slider-thumb:hover { + box-shadow: 0 0 0 10px rgba(255, 85, 0, 0.1); +} + +.range-input:active::-webkit-slider-thumb { + box-shadow: 0 0 0 13px rgba(255, 85, 0, 0.2); +} + +.range-input:focus::-webkit-slider-thumb { + box-shadow: 0 0 0 13px rgba(255, 85, 0, 0.2); +} + +.range-input::-moz-range-thumb:hover { + box-shadow: 0 0 0 10px rgba(255, 85, 0, 0.1); +} + +.range-input:active::-moz-range-thumb { + box-shadow: 0 0 0 13px rgba(255, 85, 0, 0.2); +} + +.range-input:focus::-moz-range-thumb { + box-shadow: 0 0 0 13px rgba(255, 85, 0, 0.2); +} + +.gain-reset { + font-size: 12px; + margin-top: 12px; + padding: 5px; + max-width: 50px; +} diff --git a/packages/key-finder-web/src/LiveDetection/components/GainControl.tsx b/packages/key-finder-web/src/LiveDetection/components/GainControl.tsx new file mode 100644 index 0000000..20d8840 --- /dev/null +++ b/packages/key-finder-web/src/LiveDetection/components/GainControl.tsx @@ -0,0 +1,43 @@ +import { FunctionalComponent, h, JSX } from 'preact'; +import './GainControl.css'; + +interface GainControlProps { + gain: number; + updateGain: (event: JSX.TargetedEvent) => void; + resetGain: () => void; +} + +const GainControl: FunctionalComponent = ({ + gain, + updateGain, + resetGain, +}) => { + const formattedGain = gain === 0 ? '0.00' : (10 ** gain).toFixed(2); + const sign = gain >= 0 ? '+' : '-'; + + return ( +
+ + + +
+ ); +}; + +export default GainControl; diff --git a/packages/key-finder-web/src/Utils/audioUtils.ts b/packages/key-finder-web/src/Utils/audioUtils.ts index 8f37f93..8f1af25 100644 --- a/packages/key-finder-web/src/Utils/audioUtils.ts +++ b/packages/key-finder-web/src/Utils/audioUtils.ts @@ -63,7 +63,7 @@ export function createDataArrayForAnalyzerDevice( return dataArray; } -export function createGainNode(audioContext: AudioContext): AudioNode { +export function createGainNode(audioContext: AudioContext): GainNode { const gainNode = audioContext.createGain(); gainNode.gain.value = 1; return gainNode; From 456b096dbf62c0a74ee0efcf35de7eb12ab6650e Mon Sep 17 00:00:00 2001 From: Nima Jahanshahlou Date: Fri, 24 May 2024 14:57:35 +0200 Subject: [PATCH 3/3] clean-up --- .../src/LiveDetection/LiveDetection.css | 75 ------------------- 1 file changed, 75 deletions(-) diff --git a/packages/key-finder-web/src/LiveDetection/LiveDetection.css b/packages/key-finder-web/src/LiveDetection/LiveDetection.css index 36a41bb..de438c7 100644 --- a/packages/key-finder-web/src/LiveDetection/LiveDetection.css +++ b/packages/key-finder-web/src/LiveDetection/LiveDetection.css @@ -27,81 +27,6 @@ align-content: center; } -.live-detection__gain-input { - display: flex; - flex-direction: column; - max-width: 10rem; - align-items: center; -} - -.live-detection__gain-input label { - width: 100px; - margin-bottom: 20px; - text-align: center; -} - -.range-input { - -webkit-appearance: none; - appearance: none; - width: 100%; - appearance: slider-vertical; - -webkit-appearance: slider-vertical; - cursor: pointer; - outline: none; - border-radius: 15px; - height: 6px; - background: #ccc; - height: 200px; - width: 100px; -} - -.range-input::-webkit-slider-thumb { - -webkit-appearance: none; - appearance: none; - height: 15px; - width: 15px; - background-color: #f50; - border-radius: 50%; - border: none; - transition: 0.2s ease-in-out; -} - -.range-input::-moz-range-thumb { - height: 15px; - width: 15px; - background-color: #f50; - border-radius: 50%; - border: none; - transition: 0.2s ease-in-out; -} - -.range-input::-webkit-slider-thumb:hover { - box-shadow: 0 0 0 10px rgba(255, 85, 0, 0.1); -} -.range-input:active::-webkit-slider-thumb { - box-shadow: 0 0 0 13px rgba(255, 85, 0, 0.2); -} -.range-input:focus::-webkit-slider-thumb { - box-shadow: 0 0 0 13px rgba(255, 85, 0, 0.2); -} - -.range-input::-moz-range-thumb:hover { - box-shadow: 0 0 0 10px rgba(255, 85, 0, 0.1); -} -.range-input:active::-moz-range-thumb { - box-shadow: 0 0 0 13px rgba(255, 85, 0, 0.2); -} -.range-input:focus::-moz-range-thumb { - box-shadow: 0 0 0 13px rgba(255, 85, 0, 0.2); -} - -.live-detection__gain-reset { - font-size: 12px; - margin-top: 12px; - padding: 5px; - max-width: 50px; -} - @media (max-width: 440px) { .live-detection__container { grid-template-columns: 1fr;