changeset 89:25947630486b

More toward inverse CQ
author Chris Cannam <c.cannam@qmul.ac.uk>
date Fri, 09 May 2014 08:25:24 +0100
parents c3e1a08c97f0
children bfc7cf71f2ef
files Makefile.inc cpp-qm-dsp/CQInverse.cpp cpp-qm-dsp/CQInverse.h cpp-qm-dsp/ConstantQ.h
diffstat 4 files changed, 71 insertions(+), 57 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile.inc	Thu May 08 19:01:15 2014 +0100
+++ b/Makefile.inc	Fri May 09 08:25:24 2014 +0100
@@ -20,8 +20,8 @@
 PLUGIN	:= cqvamp$(PLUGIN_EXT)
 TEST	:= $(LIB_DIR)/test
 
-LIB_HEADERS := $(LIB_DIR)/CQKernel.h $(LIB_DIR)/ConstantQ.h $(LIB_DIR)/CQInterpolated.h
-LIB_SOURCES := $(LIB_DIR)/CQKernel.cpp $(LIB_DIR)/ConstantQ.cpp $(LIB_DIR)/CQInterpolated.cpp
+LIB_HEADERS := $(LIB_DIR)/CQKernel.h $(LIB_DIR)/ConstantQ.h $(LIB_DIR)/CQInterpolated.h $(LIB_DIR)/CQInverse.h
+LIB_SOURCES := $(LIB_DIR)/CQKernel.cpp $(LIB_DIR)/ConstantQ.cpp $(LIB_DIR)/CQInterpolated.cpp $(LIB_DIR)/CQInverse.cpp
 
 VAMP_HEADERS := $(VAMP_DIR)/CQVamp.h
 VAMP_SOURCES := $(VAMP_DIR)/CQVamp.cpp $(VAMP_DIR)/libmain.cpp
--- a/cpp-qm-dsp/CQInverse.cpp	Thu May 08 19:01:15 2014 +0100
+++ b/cpp-qm-dsp/CQInverse.cpp	Fri May 09 08:25:24 2014 +0100
@@ -37,6 +37,11 @@
 #include "maths/MathUtilities.h"
 #include "dsp/transforms/FFT.h"
 
+#include <algorithm>
+#include <complex>
+#include <iostream>
+#include <stdexcept>
+
 using std::vector;
 using std::complex;
 using std::cerr;
@@ -119,71 +124,52 @@
 
     m_bigBlockSize = m_p.fftSize * pow(2, m_octaves - 1);
 
-
-//!!! GOT HERE!
-#error continue from here please!
-/*
-    // Now add in the extra padding and compensate for hops that must
-    // be dropped in order to align the atom centres across
-    // octaves. Again this is a bit trickier because we are doing it
-    // at input rather than output and so must work in per-octave
-    // sample rates rather than output blocks
-
-    int emptyHops = m_p.firstCentre / m_p.atomSpacing;
-
-    vector<int> drops;
+    //!!! review this later for the hops-dropped stuff
+    int maxLatency = 0;
     for (int i = 0; i < m_octaves; ++i) {
-	int factor = pow(2, i);
-	int dropHops = emptyHops * pow(2, m_octaves - i - 1) - emptyHops;
-	int drop = ((dropHops * m_p.fftHop) * factor) / m_p.atomsPerFrame;
-	drops.push_back(drop);
+	if (latencies[i] > maxLatency) maxLatency = latencies[i];
     }
 
-    int maxLatPlusDrop = 0;
-    for (int i = 0; i < m_octaves; ++i) {
-	int latPlusDrop = latencies[i] + drops[i];
-	if (latPlusDrop > maxLatPlusDrop) maxLatPlusDrop = latPlusDrop;
-    }
-
-    // we want to design totalLatency such that totalLatency -
-    // latencies[0] - drops[0] is a multiple of m_p.fftHop, so that we
-    // can get identical results in octave 0 to our reference
-    // implementation, making for easier testing (though other octaves
-    // will differ because of different resampler implementations)
-
-    int totalLatency = maxLatPlusDrop;
-    int lat0 = totalLatency - latencies[0] - drops[0];
-    totalLatency = ceil(double(lat0 / m_p.fftHop) * m_p.fftHop)
-	+ latencies[0] + drops[0];
-
-//    cerr << "total latency = " << totalLatency << endl;
-
-    // Padding as in the reference (will be introduced with the
-    // latency compensation in the loop below)
-    m_outputLatency = totalLatency + m_bigBlockSize
-	- m_p.firstCentre * pow(2, m_octaves-1);
-
-//    cerr << "m_bigBlockSize = " << m_bigBlockSize << ", firstCentre = "
-//	 << m_p.firstCentre << ", m_octaves = " << m_octaves << ", so m_outputLatency = " << m_outputLatency << endl;
+    m_outputLatency = maxLatency; //!!! for now
 
     for (int i = 0; i < m_octaves; ++i) {
 
-	double factor = pow(2, i);
-
 	// Calculate the difference between the total latency applied
 	// across all octaves, and the existing latency due to the
-	// decimator for this octave, and then convert it back into
-	// the sample rate appropriate for the output latency of this
-	// decimator -- including one additional big block of padding
-	// (as in the reference).
-
-	double octaveLatency =
-	    double(totalLatency - latencies[i] - drops[i]
-		   + m_bigBlockSize) / factor;
+	// upsampler for this octave
 
         m_buffers.push_back
-            (vector<double>(int(round(octaveLatency)), 0.0));
+            (vector<double>(m_outputLatency - latencies[i], 0.0));
     }
-*/
+
     m_fft = new FFTReal(m_p.fftSize);
 }
+
+std::vector<double> process(const std::vector<std::vector<double> > &blocks)
+{
+    // The input data is of the form produced by ConstantQ::process --
+    // an unknown number N of columns of varying height. We assert
+    // that N is a multiple of atomsPerFrame * 2^(octaves-1).
+    //
+    // Our procedure:
+    // 
+    // 1. Slice the list of columns into a set of lists of columns,
+    // one per octave, each of width N / (2^octave-1) and height
+    // binsPerOctave, containing the values present in that octave
+    //
+    // 2. Group each octave list by atomsPerFrame columns at a time,
+    // and stack these so as to achieve a list, for each octave, of
+    // taller columns of height binsPerOctave * atomsPerFrame
+    //
+    // 3. For each column, take the product with the inverse CQ kernel
+    // (which is the conjugate of the forward kernel) and perform an
+    // inverse FFT
+    //
+    // 4. Overlap-add each octave's resynthesised blocks (unwindowed)
+    //
+    // 5. Resample each octave's overlap-add stream to the original
+    // rate, and sum
+    
+}
+
+
--- a/cpp-qm-dsp/CQInverse.h	Thu May 08 19:01:15 2014 +0100
+++ b/cpp-qm-dsp/CQInverse.h	Fri May 09 08:25:24 2014 +0100
@@ -60,6 +60,9 @@
     // Input is the format produced by ConstantQ class, not
     // CQInterpolated (or can we make this support either?)
 
+    //!!! no, we need complex not magnitudes! CQ should probably
+    //!!! produce totally raw output and something like CQInterpolated
+    //!!! do the magnitude stuff as well as interpolation
     std::vector<double> process(const std::vector<std::vector<double> > &);
     std::vector<double> getRemainingOutput();
 
--- a/cpp-qm-dsp/ConstantQ.h	Thu May 08 19:01:15 2014 +0100
+++ b/cpp-qm-dsp/ConstantQ.h	Fri May 09 08:25:24 2014 +0100
@@ -57,7 +57,32 @@
     double getMinFrequency() const; // actual min, not that passed to ctor
     double getBinFrequency(int bin) const;
 
+    /**
+     * Given a series of time-domain samples, return a series of
+     * constant-Q columns. Any samples left over (that did not fit
+     * into a constant-Q processing block) are saved for the next call
+     * to process or getRemainingBlocks.
+     *
+     * Each column contains a series of constant-Q bin values ordered
+     * from highest to lowest frequency.
+     *
+     * Columns are of variable height: each will contain at least
+     * getBinsPerOctave() values, because the highest-frequency octave
+     * is always present, but a second octave (if requested) will
+     * appear only in alternate columns, a third octave only in every
+     * fourth column, and so on.
+     *
+     * If you need a format in which all columns are of equal height
+     * and every bin contains a value, use CQInterpolated instead of
+     * ConstantQ.
+     */
     std::vector<std::vector<double> > process(const std::vector<double> &);
+
+    /**
+     * Return the remaining constant-Q columns following the end of
+     * processing. Any buffered input is padded so as to ensure that
+     * all input provided to process() will have been returned.
+     */
     std::vector<std::vector<double> > getRemainingBlocks();
 
 private: