changeset 102:b0c0051d647e

Latency fixes: avoid rounded fractional per-octave latencies (makes the inverse difficult to handle because we'd need to have access to exact forward resampler latencies); reduce inverse transform latency by one big block
author Chris Cannam <c.cannam@qmul.ac.uk>
date Tue, 13 May 2014 12:34:51 +0100
parents 4ad90a51a067
children 790832f18419
files cpp-qm-dsp/CQInverse.cpp cpp-qm-dsp/ConstantQ.cpp
diffstat 2 files changed, 54 insertions(+), 14 deletions(-) [+]
line wrap: on
line diff
--- a/cpp-qm-dsp/CQInverse.cpp	Tue May 13 11:45:22 2014 +0100
+++ b/cpp-qm-dsp/CQInverse.cpp	Tue May 13 12:34:51 2014 +0100
@@ -43,6 +43,8 @@
 using std::cerr;
 using std::endl;
 
+//#define DEBUG_CQ 1
+
 CQInverse::CQInverse(double sampleRate,
                      double minFreq,
                      double maxFreq,
@@ -108,7 +110,9 @@
         Resampler *r = new Resampler
             (sourceRate / factor, sourceRate, 60, 0.02);
 
+#ifdef DEBUG_CQ
         cerr << "inverse: octave " << i << ": resample from " << sourceRate/factor << " to " << sourceRate << endl;
+#endif
 
 	// See ConstantQ.cpp for discussion on latency -- output
 	// latency here is at target rate which, this way around, is
@@ -138,17 +142,21 @@
 	pushes.push_back(push);
     }
 
-    int bigBlockSize = m_p.fftSize * pow(2, m_octaves - 1);
     int maxLatLessPush = 0;
     for (int i = 0; i < m_octaves; ++i) {
-	int latLessPush = bigBlockSize + latencies[i] - pushes[i];
+	int latLessPush = latencies[i] - pushes[i];
 	if (latLessPush > maxLatLessPush) maxLatLessPush = latLessPush;
     }
 
-    int totalLatency = maxLatLessPush;
+    int totalLatency = maxLatLessPush + 10;
+    if (totalLatency < 0) totalLatency = 0;
 
     m_outputLatency = totalLatency + m_p.firstCentre * pow(2, m_octaves-1);
 
+#ifdef DEBUG_CQ
+    cerr << "totalLatency = " << totalLatency << ", m_outputLatency = " << m_outputLatency << endl;
+#endif
+
     for (int i = 0; i < m_octaves; ++i) {
 
 	// Calculate the difference between the total latency applied
@@ -157,6 +165,10 @@
 
         int latencyPadding = totalLatency - latencies[i] + pushes[i];
 
+#ifdef DEBUG_CQ
+        cerr << "octave " << i << ": push " << pushes[i] << ", resampler latency inc overlap space " << latencies[i] << ", latencyPadding = " << latencyPadding << " (/factor = " << latencyPadding / pow(2, i) << ")" << endl;
+#endif
+
         m_buffers.push_back(RealSequence(latencyPadding, 0.0));
     }
 
--- a/cpp-qm-dsp/ConstantQ.cpp	Tue May 13 11:45:22 2014 +0100
+++ b/cpp-qm-dsp/ConstantQ.cpp	Tue May 13 12:34:51 2014 +0100
@@ -45,6 +45,8 @@
 using std::cerr;
 using std::endl;
 
+//#define DEBUG_CQ 1
+
 ConstantQ::ConstantQ(double sampleRate,
                      double minFreq,
                      double maxFreq,
@@ -110,7 +112,9 @@
         Resampler *r = new Resampler
             (sourceRate, sourceRate / factor, 60, 0.02);
 
+#ifdef DEBUG_CQ
         cerr << "forward: octave " << i << ": resample from " << sourceRate << " to " << sourceRate / factor << endl;
+#endif
 
         // We need to adapt the latencies so as to get the first input
         // sample to be aligned, in time, at the decimator output
@@ -170,26 +174,37 @@
 	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 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;
+    // We want (totalLatency - latencies[i]) to be a multiple of 2^i
+    // for each octave i, so that we do not end up with fractional
+    // octave latencies below. In theory this is hard, in practice if
+    // we ensure it for the last octave we should be OK.
+    double finalOctLat = latencies[m_octaves-1];
+    double finalOctFact = pow(2, m_octaves-1);
+    totalLatency =
+        int(round(finalOctLat +
+                  finalOctFact *
+                  ceil((totalLatency - finalOctLat) / finalOctFact)));
+
+#ifdef DEBUG_CQ
+    cerr << "total latency = " << totalLatency << endl;
+#endif
 
     // 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;
+#ifdef DEBUG_CQ
+    cerr << "m_bigBlockSize = " << m_bigBlockSize << ", firstCentre = "
+	 << m_p.firstCentre << ", m_octaves = " << m_octaves
+         << ", so m_outputLatency = " << m_outputLatency << endl;
+#endif
 
     for (int i = 0; i < m_octaves; ++i) {
 
@@ -206,6 +221,21 @@
 	    double(totalLatency - latencies[i] - drops[i]
 		   + m_bigBlockSize) / factor;
 
+#ifdef DEBUG_CQ
+        cerr << "octave " << i << ": resampler latency = " << latencies[i]
+             << ", drop " << drops[i] << " (/factor = " << drops[i]/factor
+             << "), octaveLatency = " << octaveLatency << " -> "
+             << int(round(octaveLatency)) << " (diff * factor = "
+             << (octaveLatency - round(octaveLatency)) << " * "
+             << factor << " = "
+             << (octaveLatency - round(octaveLatency)) * factor << ")" << endl;
+
+        cerr << "double(" << totalLatency << " - " 
+             << latencies[i] << " - " << drops[i] << " + " 
+             << m_bigBlockSize << ") / " << factor << " = " 
+             << octaveLatency << endl;
+#endif
+
         m_buffers.push_back
             (RealSequence(int(round(octaveLatency)), 0.0));
     }
@@ -234,8 +264,6 @@
 	bool enough = true;
 	for (int i = 0; i < m_octaves; ++i) {
 	    int required = m_p.fftSize * pow(2, m_octaves - i - 1);
-//	    cerr << "for octave " << i << ", buf len =  "<< m_buffers[i].size() << " (need " << required << ")" << endl;
-
 	    if ((int)m_buffers[i].size() < required) {
 		enough = false;
 	    }