c@5: // c@17: // NoveltyCurveProcessor.cpp c@5: // Tempogram c@5: // c@5: // Created by Carl Bussey on 10/07/2014. c@5: // Copyright (c) 2014 Carl Bussey. All rights reserved. c@5: // c@5: c@11: //Spectrogram dimensions should be flipped? c@11: c@14: #include "NoveltyCurveProcessor.h" c@5: using namespace std; c@5: c@22: NoveltyCurveProcessor::NoveltyCurveProcessor(const float &samplingFrequency, const size_t &fftLength, const size_t &compressionConstant) : c@5: m_samplingFrequency(samplingFrequency), c@7: m_fftLength(fftLength), c@7: m_blockSize(fftLength/2 + 1), c@5: m_compressionConstant(compressionConstant), c@5: m_numberOfBands(5), c@13: m_pBandBoundaries(0), c@13: m_pBandSum(0) c@5: { c@5: initialise(); c@5: } c@5: c@14: NoveltyCurveProcessor::~NoveltyCurveProcessor(){ c@5: cleanup(); c@5: } c@5: c@9: //allocate all space and set variable c@5: void c@14: NoveltyCurveProcessor::initialise(){ c@5: c@13: // for bandwise processing, the band is split into 5 bands. m_pBandBoundaries contains the upper and lower bin boundaries for each band. c@13: m_pBandBoundaries = new int[m_numberOfBands+1]; c@13: m_pBandBoundaries[0] = 0; c@13: for (unsigned int band = 1; band < m_numberOfBands; band++){ c@13: float lowFreq = 500*pow(2.5, (int)band-1); c@13: m_pBandBoundaries[band] = m_fftLength*lowFreq/m_samplingFrequency; c@5: } c@13: m_pBandBoundaries[m_numberOfBands] = m_blockSize; c@5: c@13: m_pBandSum = new float [m_numberOfBands]; c@5: } c@5: c@9: //delete space allocated in initialise() c@5: void c@14: NoveltyCurveProcessor::cleanup(){ c@13: delete []m_pBandBoundaries; c@13: m_pBandBoundaries = 0; c@13: delete []m_pBandSum; c@13: m_pBandSum = 0; c@5: } c@5: c@9: //subtract local average of novelty curve c@9: //uses m_hannWindow as filter c@14: void NoveltyCurveProcessor::subtractLocalAverage(vector &noveltyCurve, const size_t &smoothLength) const c@13: { c@22: int numberOfBlocks = noveltyCurve.size(); c@22: vector localAverage(numberOfBlocks); c@5: c@13: float * m_hannWindow = new float[smoothLength]; c@13: WindowFunction::hanning(m_hannWindow, smoothLength, true); c@9: c@22: FIRFilter filter(numberOfBlocks, smoothLength); c@15: filter.process(&noveltyCurve[0], m_hannWindow, &localAverage[0], FIRFilter::middle); c@5: c@22: for (int i = 0; i < numberOfBlocks; i++){ c@5: noveltyCurve[i] -= localAverage[i]; c@5: noveltyCurve[i] = noveltyCurve[i] >= 0 ? noveltyCurve[i] : 0; c@5: } c@9: c@11: delete []m_hannWindow; c@13: m_hannWindow = 0; c@5: } c@5: c@9: //smoothed differentiator filter. Flips upper half of hanning window about y-axis to create coefficients. c@22: void NoveltyCurveProcessor::smoothedDifferentiator(SpectrogramTransposed &spectrogramTransposed, const size_t &smoothLength) const c@13: { c@22: int numberOfBlocks = spectrogramTransposed[0].size(); c@22: c@7: float * diffHannWindow = new float [smoothLength]; c@7: WindowFunction::hanning(diffHannWindow, smoothLength, true); c@7: c@7: if(smoothLength%2) diffHannWindow[(smoothLength+1)/2 - 1] = 0; c@20: for(int i = (smoothLength+1)/2; i < (int)smoothLength; i++){ c@7: diffHannWindow[i] = -diffHannWindow[i]; c@7: } c@7: c@22: FIRFilter smoothFilter(numberOfBlocks, smoothLength); c@7: c@20: for (int i = 0; i < (int)m_blockSize; i++){ c@22: smoothFilter.process(&spectrogramTransposed[i][0], diffHannWindow, &spectrogramTransposed[i][0], FIRFilter::middle); c@7: } c@7: } c@7: c@9: //half rectification (set negative to zero) c@24: void NoveltyCurveProcessor::halfWaveRectify(Spectrogram &spectrogram) const c@13: { c@24: int length = spectrogram.size(); c@25: int height = length > 0 ? spectrogram[0].size() : 0; c@22: c@24: for (int i = 0; i < length; i++){ c@24: for (int j = 0; j < height; j++){ c@24: if (spectrogram[i][j] < 0.0) spectrogram[i][j] = 0.0; c@7: } c@7: } c@7: } c@7: c@9: //process method c@5: vector c@22: NoveltyCurveProcessor::spectrogramToNoveltyCurve(const Spectrogram &spectrogram) const //make argument const & c@13: { c@22: int numberOfBlocks = spectrogram.size(); c@22: std::vector noveltyCurve(numberOfBlocks); c@25: SpectrogramTransposed spectrogramTransposed(m_blockSize, vector(spectrogram.size())); c@5: c@9: //normalise and log spectrogram c@25: float normaliseScale = SpectrogramProcessor::calculateMax(spectrogram); c@22: for (int block = 0; block < (int)numberOfBlocks; block++){ c@20: for (int k = 0; k < (int)m_blockSize; k++){ c@25: float magnitude = spectrogram[block][k]; c@25: if(normaliseScale != 0.0) magnitude /= normaliseScale; //normalise c@25: spectrogramTransposed[k][block] = log(1+m_compressionConstant*magnitude); c@7: } c@7: } c@24: c@9: //smooted differentiator c@22: smoothedDifferentiator(spectrogramTransposed, 5); //make smoothLength a parameter! c@9: //halfwave rectification c@22: halfWaveRectify(spectrogramTransposed); c@7: c@9: //bandwise processing c@22: for (int block = 0; block < (int)numberOfBlocks; block++){ c@20: for (int band = 0; band < (int)m_numberOfBands; band++){ c@13: int k = m_pBandBoundaries[band]; c@13: int bandEnd = m_pBandBoundaries[band+1]; c@13: m_pBandSum[band] = 0; c@5: c@7: while(k < bandEnd){ c@22: m_pBandSum[band] += spectrogramTransposed[k][block]; c@7: k++; c@5: } c@5: } c@5: float total = 0; c@20: for(int band = 0; band < (int)m_numberOfBands; band++){ c@13: total += m_pBandSum[band]; c@5: } c@13: noveltyCurve[block] = total/m_numberOfBands; c@5: } c@5: c@9: //subtract local averages c@29: subtractLocalAverage(noveltyCurve, 65); //maybe smaller? c@5: c@13: return noveltyCurve; c@7: }