diff --git a/src/lenses/SpectrogramLens/Spectrogram.tsx b/src/lenses/SpectrogramLens/Spectrogram.tsx index 8dd7f5e0..bfd77463 100644 --- a/src/lenses/SpectrogramLens/Spectrogram.tsx +++ b/src/lenses/SpectrogramLens/Spectrogram.tsx @@ -51,4 +51,40 @@ const amplitudeToDb = (amplitude: number, ref: number, amin: number) => { return log_spec; }; -export { unitType, freqType, fixWindow, amplitudeToDb }; +const hzToMel = (freq: number) => { + // Fill in the linear part + const f_min = 0.0; + const f_sp = 200.0 / 3; + + let mel = (freq - f_min) / f_sp; + + // Fill in the log-scale part + const min_log_hz = 1000.0; // beginning of log region (Hz) + const min_log_mel = (min_log_hz - f_min) / f_sp; // same (Mels) + const logstep = Math.log(6.4) / 27.0; // step size for log region + + if (freq >= min_log_hz) { + mel = min_log_mel + Math.log(freq / min_log_hz) / logstep; + } + + return mel; +}; + +const melToHz = (mel: number) => { + const f_min = 0.0; + const f_sp = 200.0 / 3; + let freq = f_min + f_sp * mel; + + // And now the nonlinear scale + const min_log_hz = 1000.0; // beginning of log region (Hz) + const min_log_mel = (min_log_hz - f_min) / f_sp; // same (Mels) + const logstep = Math.log(6.4) / 27.0; // step size for log region + + if (mel >= min_log_mel) { + // If we have scalar data, check directly + freq = min_log_hz * Math.exp(logstep * (mel - min_log_mel)); + } + return freq; +}; + +export { unitType, freqType, fixWindow, amplitudeToDb, hzToMel, melToHz }; diff --git a/src/lenses/SpectrogramLens/index.tsx b/src/lenses/SpectrogramLens/index.tsx index 70e48ba1..ff22e17a 100644 --- a/src/lenses/SpectrogramLens/index.tsx +++ b/src/lenses/SpectrogramLens/index.tsx @@ -10,7 +10,7 @@ import { ColorsState, useColors } from '../../stores/colors'; import { Lens } from '../../types'; import useSetting from '../useSetting'; import MenuBar from './MenuBar'; -import { fixWindow, freqType, unitType, amplitudeToDb } from './Spectrogram'; +import { fixWindow, freqType, unitType, amplitudeToDb, hzToMel } from './Spectrogram'; const Container = tw.div`flex flex-col w-full h-full items-stretch justify-center`; const EmptyNote = styled.p` @@ -208,6 +208,7 @@ const SpectrogramLens: Lens = ({ columns, urls, values }) => { const widthScale = d3.scaleLinear([0, width], [0, frequenciesData.length]); let drawData = []; + let min = 0; let max = 0; @@ -240,6 +241,24 @@ const SpectrogramLens: Lens = ({ columns, urls, values }) => { } } + drawData[i] = col; + } + } else if (ampScale === 'mel') { + for (let i = 0; i < frequenciesData.length; i++) { + const col = []; + + for (let j = 0; j < frequenciesData[i].length; j++) { + const amplitude = frequenciesData[i][j]; + col[j] = hzToMel(amplitude ** 2); + + if (col[j] > max) { + max = col[j]; + } + + if (col[j] < min) { + min = col[j]; + } + } drawData[i] = col; } } else { @@ -448,7 +467,7 @@ const SpectrogramLens: Lens = ({ columns, urls, values }) => { availableFreqScales={['linear', 'logarithmic']} freqScale={freqScale} onChangeFreqScale={handleFreqScaleChange} - availableAmpScales={['decibel', 'linear']} + availableAmpScales={['decibel', 'linear', 'mel']} ampScale={ampScale} onChangeAmpScale={handleAmpScaleChange} />