diff NoveltyCurveProcessor.cpp @ 14:c11367df624d

* Renamed NoveltyCurve.* and Spectrogram.* to $(Name)Processor.* * Aligned novelty curve with audio - when performing FIRFilter::process(params), take inputLength after group delay. * Removed trail of Spectrogram. * General tidying!
author Carl Bussey <c.bussey@se10.qmul.ac.uk>
date Thu, 14 Aug 2014 10:31:49 +0100
parents NoveltyCurve.cpp@7680cc4c0073
children 203551cbad47
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/NoveltyCurveProcessor.cpp	Thu Aug 14 10:31:49 2014 +0100
@@ -0,0 +1,168 @@
+//
+//  NoveltyCurve.cpp
+//  Tempogram
+//
+//  Created by Carl Bussey on 10/07/2014.
+//  Copyright (c) 2014 Carl Bussey. All rights reserved.
+//
+
+//Spectrogram dimensions should be flipped?
+
+#include "NoveltyCurveProcessor.h"
+using namespace std;
+
+NoveltyCurveProcessor::NoveltyCurveProcessor(const float &samplingFrequency, const size_t &fftLength, const size_t &numberOfBlocks, const size_t &compressionConstant) :
+    m_samplingFrequency(samplingFrequency),
+    m_fftLength(fftLength),
+    m_blockSize(fftLength/2 + 1),
+    m_numberOfBlocks(numberOfBlocks),
+    m_compressionConstant(compressionConstant),
+    m_numberOfBands(5),
+    m_pBandBoundaries(0),
+    m_pBandSum(0)
+{
+    initialise();
+}
+
+NoveltyCurveProcessor::~NoveltyCurveProcessor(){
+    cleanup();
+}
+
+//allocate all space and set variable
+void
+NoveltyCurveProcessor::initialise(){
+    
+    // for bandwise processing, the band is split into 5 bands. m_pBandBoundaries contains the upper and lower bin boundaries for each band.
+    m_pBandBoundaries = new int[m_numberOfBands+1];
+    m_pBandBoundaries[0] = 0;
+    for (unsigned int band = 1; band < m_numberOfBands; band++){
+        float lowFreq = 500*pow(2.5, (int)band-1);
+        m_pBandBoundaries[band] = m_fftLength*lowFreq/m_samplingFrequency;
+    }
+    m_pBandBoundaries[m_numberOfBands] = m_blockSize;
+    
+    m_pBandSum = new float [m_numberOfBands];
+}
+
+//delete space allocated in initialise()
+void
+NoveltyCurveProcessor::cleanup(){
+    delete []m_pBandBoundaries;
+    m_pBandBoundaries = 0;
+    delete []m_pBandSum;
+    m_pBandSum = 0;
+}
+
+//calculate max of spectrogram
+float NoveltyCurveProcessor::calculateMax(const vector< vector<float> > &spectrogram) const
+{
+    float max = 0;
+    
+    for (unsigned int j = 0; j < m_numberOfBlocks; j++){
+        for (unsigned int i = 0; i < m_blockSize; i++){
+            max = max > fabs(spectrogram[i][j]) ? max : fabs(spectrogram[i][j]);
+        }
+    }
+    
+    return max;
+}
+
+//subtract local average of novelty curve
+//uses m_hannWindow as filter
+void NoveltyCurveProcessor::subtractLocalAverage(vector<float> &noveltyCurve, const size_t &smoothLength) const
+{
+    vector<float> localAverage(m_numberOfBlocks);
+    
+    float * m_hannWindow = new float[smoothLength];
+    WindowFunction::hanning(m_hannWindow, smoothLength, true);
+    
+    FIRFilter filter(m_numberOfBlocks, smoothLength);
+    filter.process(&noveltyCurve[0], m_hannWindow, &localAverage[0]);
+    
+    assert(noveltyCurve.size() == m_numberOfBlocks);
+    for (unsigned int i = 0; i < m_numberOfBlocks; i++){
+        noveltyCurve[i] -= localAverage[i];
+        noveltyCurve[i] = noveltyCurve[i] >= 0 ? noveltyCurve[i] : 0;
+    }
+    
+    delete []m_hannWindow;
+    m_hannWindow = 0;
+}
+
+//smoothed differentiator filter. Flips upper half of hanning window about y-axis to create coefficients.
+void NoveltyCurveProcessor::smoothedDifferentiator(vector< vector<float> > &spectrogram, const size_t &smoothLength) const
+{
+    
+    float * diffHannWindow = new float [smoothLength];
+    WindowFunction::hanning(diffHannWindow, smoothLength, true);
+    
+    if(smoothLength%2) diffHannWindow[(smoothLength+1)/2 - 1] = 0;
+    for(unsigned int i = (smoothLength+1)/2; i < smoothLength; i++){
+        diffHannWindow[i] = -diffHannWindow[i];
+    }
+    
+    FIRFilter smoothFilter(m_numberOfBlocks, smoothLength);
+    
+    for (unsigned int i = 0; i < m_blockSize; i++){
+        smoothFilter.process(&spectrogram[i][0], diffHannWindow, &spectrogram[i][0]);
+    }
+}
+
+//half rectification (set negative to zero)
+void NoveltyCurveProcessor::halfWaveRectify(vector< vector<float> > &spectrogram) const
+{
+    for (unsigned int block = 0; block < m_numberOfBlocks; block++){
+        for (unsigned int k = 0; k < m_blockSize; k++){
+            if (spectrogram[k][block] < 0.0) spectrogram[k][block] = 0.0;
+        }
+    }
+}
+
+//process method
+vector<float>
+NoveltyCurveProcessor::spectrogramToNoveltyCurve(Spectrogram spectrogram) const
+{
+    std::vector<float> noveltyCurve(m_numberOfBlocks);
+    
+    //cout << spectrogram[0].size() << " : " << m_numberOfBlocks << endl;
+    assert(spectrogram.size() == m_blockSize);
+    assert(spectrogram[0].size() == m_numberOfBlocks);
+    
+    //normalise and log spectrogram
+    float normaliseScale = calculateMax(spectrogram);
+    for (unsigned int block = 0; block < m_numberOfBlocks; block++){
+        for (unsigned int k = 0; k < m_blockSize; k++){
+            if(normaliseScale != 0.0) spectrogram[k][block] /= normaliseScale; //normalise
+            spectrogram[k][block] = log(1+m_compressionConstant*spectrogram[k][block]);
+        }
+    }
+
+    //smooted differentiator
+    smoothedDifferentiator(spectrogram, 5); //make smoothLength a parameter!
+    //halfwave rectification
+    halfWaveRectify(spectrogram);
+    
+    //bandwise processing
+    for (unsigned int block = 0; block < m_numberOfBlocks; block++){
+        for (unsigned int band = 0; band < m_numberOfBands; band++){
+            int k = m_pBandBoundaries[band];
+            int bandEnd = m_pBandBoundaries[band+1];
+            m_pBandSum[band] = 0;
+            
+            while(k < bandEnd){
+                m_pBandSum[band] += spectrogram[k][block];
+                k++;
+            }
+        }
+        float total = 0;
+        for(unsigned int band = 0; band < m_numberOfBands; band++){
+            total += m_pBandSum[band];
+        }
+        noveltyCurve[block] = total/m_numberOfBands;
+    }
+    
+    //subtract local averages
+    subtractLocalAverage(noveltyCurve, 65);
+    
+    return noveltyCurve;
+}