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 60522df1..13ecb298 100644 --- a/src/lenses/SpectrogramLens/index.tsx +++ b/src/lenses/SpectrogramLens/index.tsx @@ -11,7 +11,14 @@ import { Lens } from '../../types'; import useSetting from '../useSetting'; import MenuBar from './MenuBar'; import chroma from 'chroma-js'; -import { fixWindow, freqType, unitType, amplitudeToDb } from './Spectrogram'; +import { + fixWindow, + freqType, + unitType, + amplitudeToDb, + hzToMel, + melToHz, +} from './Spectrogram'; const Container = tw.div`flex flex-col w-full h-full items-stretch justify-center`; const EmptyNote = styled.p` @@ -246,6 +253,28 @@ const SpectrogramLens: Lens = ({ columns, urls, values }) => { drawData[i] = col; } colorScale = colorPalette.scale().domain([log_spec_min, log_spec_max]); + } else if (ampScale === 'mel') { + let mel_spec_min = 0; + let mel_spec_max = 0; + + 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); + + if (col[j] > mel_spec_max) { + mel_spec_max = col[j]; + } + + if (col[j] < mel_spec_min) { + mel_spec_min = col[j]; + } + } + drawData[i] = col; + } + colorScale = colorPalette.scale().domain([mel_spec_min, mel_spec_max]); } else { // ampScale === 'linear' colorScale = colorPalette.scale().domain([0, 256]); @@ -441,7 +470,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} />