memcpy(m_lastFFT.data(), &(freqSpectrum[0]), fftWindow/2 * sizeof(float));
uint right = ((float) m_freqMax)/(m_freq/2) * (m_lastFFT.size() - 1);
- dbMap = interpolatePeakPreserving(m_lastFFT, m_innerScopeRect.width(), 0, right, -180);
+ dbMap = FFTTools::interpolatePeakPreserving(m_lastFFT, m_innerScopeRect.width(), 0, right, -180);
m_lastFFTLock.release();
// because the position could already have changed in the meantime (-> crash)
if (m_lastFFT.size() > 0 && mouseX >= 0 && mouseX < m_innerScopeRect.width()) {
uint right = ((float) m_freqMax)/(m_freq/2) * (m_lastFFT.size() - 1);
- QVector<float> dbMap = AudioSpectrum::interpolatePeakPreserving(m_lastFFT, m_innerScopeRect.width(), 0, right, -120);
+ QVector<float> dbMap = FFTTools::interpolatePeakPreserving(m_lastFFT, m_innerScopeRect.width(), 0, right, -120);
db = dbMap[mouseX];
y = topDist + m_innerScopeRect.height()-1 - (dbMap[mouseX] - m_dBmin) / (m_dBmax-m_dBmin) * (m_innerScopeRect.height()-1);
}
-const QVector<float> AudioSpectrum::interpolatePeakPreserving(const QVector<float> in, const uint targetSize, uint left, uint right, float fill)
-{
-#ifdef DEBUG_AUDIOSPEC
- QTime start = QTime::currentTime();
-#endif
-
- if (right == 0) {
- right = in.size()-1;
- }
- Q_ASSERT(targetSize > 0);
- Q_ASSERT(left < right);
-
- QVector<float> out(targetSize);
-
-
- float x;
- float x_prev = 0;
- int xi;
- uint i;
- for (i = 0; i < targetSize; i++) {
-
- // i: Target index
- // x: Interpolated source index (float!)
- // xi: floor(x)
-
- // Transform [0,targetSize-1] to [left,right]
- x = ((float) i) / (targetSize-1) * (right-left) + left;
- xi = (int) floor(x);
-
- if (x > in.size()-1) {
- // This may happen if right > in.size()-1; Fill the rest of the vector
- // with the default value now.
- break;
- }
-
-
- // Use linear interpolation in order to get smoother display
- if (xi == 0 || xi == in.size()-1) {
- // ... except if we are at the left or right border of the input sigal.
- // Special case here since we consider previous and future values as well for
- // the actual interpolation (not possible here).
- out[i] = in[xi];
- } else {
- if (in[xi] > in[xi+1]
- && x_prev < xi) {
- // This is a hack to preserve peaks.
- // Consider f = {0, 100, 0}
- // x = {0.5, 1.5}
- // Then x is 50 both times, and the 100 peak is lost.
- // Get it back here for the first x after the peak (which is at xi).
- // (x is the first after the peak if the previous x was smaller than floor(x).)
- out[i] = in[xi];
- } else {
- out[i] = (xi+1 - x) * in[xi]
- + (x - xi) * in[xi+1];
- }
- }
- x_prev = x;
- }
- // Fill the rest of the vector if the right border exceeds the input vector.
- for (; i < targetSize; i++) {
- out[i] = fill;
- }
-
-#ifdef DEBUG_AUDIOSPEC
- qDebug() << "Interpolated " << targetSize << " nodes from " << in.size() << " input points in " << start.elapsed() << " ms";
-#endif
-
- return out;
-}
-
-
#ifdef DEBUG_AUDIOSPEC
#undef DEBUG_AUDIOSPEC
#endif
/** The user has chosen a custom frequency. */
bool m_customFreq;
- /** This is linear interpolation with the special property that it preserves peaks, which is required
- for e.g. showing correct Decibel values (where the peak values are of interest).
- Consider f = {0, 100, 0}
- x = {0.5, 1.5}: With default linear interpolation x0 and x1 would both be mapped to 50.
- This function maps x1 (the first position after the peak) to 100.
-
- @param in The source vector containing the data
- @param targetSize Number of interpolation nodes between ...
- @param left the left array index in the in-vector and ...
- @param right the right array index (both inclusive).
- @param fill If right lies outside of the array bounds (which is perfectly fine here) then this value
- will be used for filling the missing information.
- */
- static const QVector<float> interpolatePeakPreserving(const QVector<float> in, const uint targetSize, uint left = 0, uint right = 0, float fill = 0.0);
-
#ifdef DEBUG_AUDIOSPEC
long m_timeTotal;
long m_showTotal;
#endif
}
+const QVector<float> FFTTools::interpolatePeakPreserving(const QVector<float> in, const uint targetSize, uint left, uint right, float fill)
+{
+#ifdef DEBUG_FFTTOOLS
+ QTime start = QTime::currentTime();
+#endif
+
+ if (right == 0) {
+ right = in.size()-1;
+ }
+ Q_ASSERT(targetSize > 0);
+ Q_ASSERT(left < right);
+
+ QVector<float> out(targetSize);
+
+
+ float x;
+ float x_prev = 0;
+ int xi;
+ uint i;
+ for (i = 0; i < targetSize; i++) {
+
+ // i: Target index
+ // x: Interpolated source index (float!)
+ // xi: floor(x)
+
+ // Transform [0,targetSize-1] to [left,right]
+ x = ((float) i) / (targetSize-1) * (right-left) + left;
+ xi = (int) floor(x);
+
+ if (x > in.size()-1) {
+ // This may happen if right > in.size()-1; Fill the rest of the vector
+ // with the default value now.
+ break;
+ }
+
+
+ // Use linear interpolation in order to get smoother display
+ if (xi == 0 || xi == in.size()-1) {
+ // ... except if we are at the left or right border of the input sigal.
+ // Special case here since we consider previous and future values as well for
+ // the actual interpolation (not possible here).
+ out[i] = in[xi];
+ } else {
+ if (in[xi] > in[xi+1]
+ && x_prev < xi) {
+ // This is a hack to preserve peaks.
+ // Consider f = {0, 100, 0}
+ // x = {0.5, 1.5}
+ // Then x is 50 both times, and the 100 peak is lost.
+ // Get it back here for the first x after the peak (which is at xi).
+ // (x is the first after the peak if the previous x was smaller than floor(x).)
+ out[i] = in[xi];
+ } else {
+ out[i] = (xi+1 - x) * in[xi]
+ + (x - xi) * in[xi+1];
+ }
+ }
+ x_prev = x;
+ }
+ // Fill the rest of the vector if the right border exceeds the input vector.
+ for (; i < targetSize; i++) {
+ out[i] = fill;
+ }
+
+#ifdef DEBUG_FFTTOOLS
+ qDebug() << "Interpolated " << targetSize << " nodes from " << in.size() << " input points in " << start.elapsed() << " ms";
+#endif
+
+ return out;
+}
+
#ifdef DEBUG_FFTTOOLS
#undef DEBUG_FFTTOOLS
#endif
void fftNormalized(const QVector<int16_t> audioFrame, const uint channel, const uint numChannels, float *freqSpectrum,
const WindowType windowType, const uint windowSize, const float param = 0);
+
+ /** This is linear interpolation with the special property that it preserves peaks, which is required
+ for e.g. showing correct Decibel values (where the peak values are of interest).
+ Consider f = {0, 100, 0}
+ x = {0.5, 1.5}: With default linear interpolation x0 and x1 would both be mapped to 50.
+ This function maps x1 (the first position after the peak) to 100.
+
+ @param in The source vector containing the data
+ @param targetSize Number of interpolation nodes between ...
+ @param left the left array index in the in-vector and ...
+ @param right the right array index (both inclusive).
+ @param fill If right lies outside of the array bounds (which is perfectly fine here) then this value
+ will be used for filling the missing information.
+ */
+ static const QVector<float> interpolatePeakPreserving(const QVector<float> in, const uint targetSize, uint left = 0, uint right = 0, float fill = 0.0);
+
private:
QHash<QString, kiss_fftr_cfg> m_fftCfgs; // FFT cfg cache
QHash<QString, QVector<float> > m_windowFunctions; // Window function cache
QImage spectrum(m_scopeRect.size(), QImage::Format_ARGB32);
spectrum.fill(qRgba(0,0,0,0));
QPainter davinci(&spectrum);
- const uint w = m_innerScopeRect.width();
const uint h = m_innerScopeRect.height();
const uint leftDist = m_innerScopeRect.left() - m_scopeRect.left();
const uint topDist = m_innerScopeRect.top() - m_scopeRect.top();
- float f;
- float x;
- float x_prev = 0;
float val;
uint windowSize;
- uint xi;
uint y;
bool completeRedraw = true;
if (newData || m_parameterChanged) {
m_parameterChanged = false;
+ QVector<float> dbMap;
+ uint right;
for (QList<QVector<float> >::iterator it = m_fftHistory.begin(); it != m_fftHistory.end(); it++) {
windowSize = (*it).size();
- // TODO use function
- for (uint i = 0; i < w; i++) {
+ // Interpolate the frequency data to match the pixel coordinates
+ right = ((float) m_freqMax)/(m_freq/2) * (windowSize - 1);
+ dbMap = FFTTools::interpolatePeakPreserving((*it), m_innerScopeRect.width(), 0, right, -180);
- // i: Pixel coordinate
- // f: Target frequency
- // x: Frequency array index (float!) corresponding to the pixel
- // xi: floor(x)
- // val: dB value at position x (Range: [-inf,0])
+ for (int i = 0; i < dbMap.size(); i++) {
+ val = dbMap[i];
- f = i/((float) w-1.0) * m_freqMax;
- x = 2*f/freq * (windowSize - 1);
- xi = (int) floor(x);
-
- if (x >= windowSize) {
- break;
- }
-
- // Use linear interpolation in order to get smoother display
- if (i == 0 || xi == windowSize-1) {
- // ... except if we are at the left or right border of the display or the spectrum
- val = (*it)[xi];
- } else {
-
- if ((*it)[xi] > (*it)[xi+1]
- && x_prev < xi) {
- // This is a hack to preserve peaks.
- // Consider f = {0, 100, 0}
- // x = {0.5, 1.5}
- // Then x is 50 both times, and the 100 peak is lost.
- // Get it back here for the first x after the peak.
- val = (*it)[xi];
- } else {
- val = (xi+1 - x) * (*it)[xi]
- + (x - xi) * (*it)[xi+1];
- }
- }
-
- // Normalize to [0 1], 1 corresponding to 0 dB and 0 to dbMin dB
+ // Normalize dB value to [0 1], 1 corresponding to 0 dB and 0 to dbMin dB
val = (val-m_dBmax)/(m_dBmax-m_dBmin) + 1;
if (val < 0) {
val = 0;
} else if (val > 1) {
val = 1;
}
-
spectrum.setPixel(leftDist + i, topDist + h-1 - y, qRgba(255, 255, 255, val * 255));
-
- x_prev = x;
}
y++;
int m_dBmin;
int m_dBmax;
- uint m_freqMax;
+ int m_freqMax;
bool m_customFreq;
bool m_parameterChanged;