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;
         }
     }
 }