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..de438c7 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__output-container { + display: flex; + align-items: center; + align-content: center; +} + @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..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,6 +13,7 @@ class LiveDetection extends Component { audioContext: AudioContext | null = null; recorder: RecorderWorkletNode | null = null; levelAnalyzer: AudioAnalyzerNode | null = null; + gainNode: GainNode | 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,7 +159,23 @@ class LiveDetection extends Component { .setValueAtTime(0, contextTime + 0.1); }; - render({}, { connected, analyzing, result, error }) { + updateGain = (event: JSX.TargetedEvent) => { + if (!this.gainNode) return; + 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, gain }) { + const formattedGain = gain === 0 ? 0 : (10 ** gain).toFixed(2); + const sign = gain >= 0 ? '+' : '-'; + return (
{error &&

{error}

} @@ -191,12 +212,23 @@ class LiveDetection extends Component { disabled={!analyzing} />
-
+
+ {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 62e6598..8f1af25 100644 --- a/packages/key-finder-web/src/Utils/audioUtils.ts +++ b/packages/key-finder-web/src/Utils/audioUtils.ts @@ -63,6 +63,12 @@ export function createDataArrayForAnalyzerDevice( return dataArray; } +export function createGainNode(audioContext: AudioContext): GainNode { + const gainNode = audioContext.createGain(); + gainNode.gain.value = 1; + return gainNode; +} + export function connectAudioNodes( audioSource: AudioNode, audioRecorder: AudioNode