Mercurial > hg > qm-dsp
changeset 467:1db23b9a8da4
Fix mismatch between time- and frequency-domain versions of the
chromagram process method - the frequency-domain one expects fftshifted
input and the kernel is set up for that, but the time-domain one wasn't
doing it
author | Chris Cannam <cannam@all-day-breakfast.com> |
---|---|
date | Wed, 29 May 2019 15:56:30 +0100 |
parents | 46375e6d1b54 |
children | a72d98f8baa3 |
files | dsp/chromagram/Chromagram.cpp dsp/chromagram/Chromagram.h tests/TestChromagram.cpp |
diffstat | 3 files changed, 46 insertions(+), 10 deletions(-) [+] |
line wrap: on
line diff
--- a/dsp/chromagram/Chromagram.cpp Wed May 29 14:08:43 2019 +0100 +++ b/dsp/chromagram/Chromagram.cpp Wed May 29 15:56:30 2019 +0100 @@ -121,7 +121,7 @@ } -double* Chromagram::process( const double *data ) +double *Chromagram::process(const double *data) { if (!m_skGenerated) { // Generate CQ Kernel @@ -139,12 +139,20 @@ } m_window->cut(m_windowbuf); + // The frequency-domain version expects pre-fftshifted input - so + // we must do the same here + for (int i = 0; i < m_frameSize/2; ++i) { + double tmp = m_windowbuf[i]; + m_windowbuf[i] = m_windowbuf[i + m_frameSize/2]; + m_windowbuf[i + m_frameSize/2] = tmp; + } + m_FFT->forward(m_windowbuf, m_FFTRe, m_FFTIm); return process(m_FFTRe, m_FFTIm); } -double* Chromagram::process( const double *real, const double *imag ) +double *Chromagram::process(const double *real, const double *imag) { if (!m_skGenerated) { // Generate CQ Kernel
--- a/dsp/chromagram/Chromagram.h Wed May 29 14:08:43 2019 +0100 +++ b/dsp/chromagram/Chromagram.h Wed May 29 15:56:30 2019 +0100 @@ -35,10 +35,35 @@ public: Chromagram( ChromaConfig Config ); ~Chromagram(); - - double* process( const double *data ); // time domain - double* process( const double *real, const double *imag ); // frequency domain - void unityNormalise( double* src ); + + /** + * Process a time-domain input signal of length equal to + * getFrameSize(). + * + * The returned buffer contains the chromagram values indexed by + * bin, with the number of values corresponding to the BPO field + * in the ChromaConfig supplied at construction. It is owned by + * the Chromagram object and is reused from one process call to + * the next. + */ + double *process(const double *data); + + /** + * Process a frequency-domain input signal generated from a + * time-domain signal of length equal to getFrameSize() that has + * been windowed and "fftshifted" to place the zero index in the + * centre of the frame. The real and imag buffers must each + * contain the full getFrameSize() frequency bins. + * + * The returned buffer contains the chromagram values indexed by + * bin, with the number of values corresponding to the BPO field + * in the ChromaConfig supplied at construction. It is owned by + * the Chromagram object and is reused from one process call to + * the next. + */ + double *process(const double *real, const double *imag); + + void unityNormalise(double* src); // Complex arithmetic double kabs( double real, double imag );
--- a/tests/TestChromagram.cpp Wed May 29 14:08:43 2019 +0100 +++ b/tests/TestChromagram.cpp Wed May 29 15:56:30 2019 +0100 @@ -34,11 +34,9 @@ int length) { vector<double> buffer; - buffer.reserve(length); for (int i = 0; i < length; ++i) { buffer.push_back(sin((i * M_PI * 2.0 * frequency) / sampleRate)); } - return buffer; } @@ -50,7 +48,7 @@ BOOST_AUTO_TEST_CASE(sinusoid_12tET) { double concertA = 440.0; - double sampleRate = 44100; + double sampleRate = 44100.0; int bpo = 60; ChromaConfig config { @@ -85,7 +83,7 @@ vector<double> signal = generateSinusoid(frequency, sampleRate, blockSize); - + double *output = chroma.process(signal.data()); int peakBin = -1; @@ -107,6 +105,11 @@ if (peakBin != expectedPeakBin) { cout << "NOTE: peak bin " << peakBin << " does not match expected " << expectedPeakBin << endl; + cout << "bin values are: "; + for (int i = 0; i < bpo; ++i) { + cout << i << ": " << output[i] << " "; + } + cout << endl; } } }