Mercurial > hg > precise-onset-detection
view src/testApp.cpp @ 8:184a7c232049 tip
changed files since updating computer
author | Venetian |
---|---|
date | Thu, 14 Aug 2014 17:53:57 +0100 |
parents | 7ec1ed0b2eb0 |
children |
line wrap: on
line source
#include "testApp.h" //-------------------------------------------------------------- void testApp::setup(){ ofSetVerticalSync(true); ofSetCircleResolution(80); ofBackground(54, 54, 54); // 0 output channels, // 2 input channels // 44100 samples per second // 256 samples per buffer // 4 num buffers (latency) soundStream.listDevices(); soundStream.setDeviceID(0);//this now uses the audio input rather than mic input for mac //outputStream.setDeviceID(1); //if you want to set a different device id //soundStream.setDeviceID(0); //bear in mind the device id corresponds to all audio devices, including input-only and output-only devices. bufferSize = 512; left.assign(bufferSize, 0.0); right.assign(bufferSize, 0.0); volHistory.assign(400, 0.0); bufferCounter = 0; drawCounter = 0; smoothedVol = 0.0; scaledVol = 0.0; soundStream.setup(this, 0, 2, 44100, bufferSize, 4); //peak analysis onsetSamples.assign(bufferSize, 0.0); // recentBufferSamples.assign(bufferSize, 0.0); holdOn = false; exactOnsetIndex = 0; precisionLocator.setup(bufferSize); } //-------------------------------------------------------------- void testApp::update(){ //lets scale the vol up to a 0-1 range scaledVol = ofMap(smoothedVol, 0.0, 0.17, 0.0, 1.0, true); //lets record the volume into an array volHistory.push_back( scaledVol ); //if we are bigger the the size we want to record - lets drop the oldest value if( volHistory.size() >= 400 ){ volHistory.erase(volHistory.begin(), volHistory.begin()+1); } } //-------------------------------------------------------------- void testApp::draw(){ ofSetColor(225); ofDrawBitmapString("AUDIO INPUT :: PRECISE ONSET DETECTION " + ofToString(exactOnsetIndex), 32, 32); ofDrawBitmapString("press 's' to unpause the audio\n'e' to pause the audio", 31, 92); ofNoFill(); // draw the left channel: ofPushStyle(); ofPushMatrix(); ofTranslate(32, 120, 0); ofSetColor(225); ofDrawBitmapString("Left Channel", 4, 18); ofSetLineWidth(1); ofRect(0, 0, 512, 200); ofSetColor(245, 58, 135); ofSetLineWidth(3); ofBeginShape(); for (int i = 0; i < left.size(); i++){ ofVertex(i, 100 -left[i]*180.0f); } ofEndShape(false); ofPopMatrix(); ofPopStyle(); // draw the right channel: ofPushStyle(); ofPushMatrix(); ofTranslate(32, 320, 0); ofSetColor(225); ofDrawBitmapString("Onset DF function", 4, 18); ofSetLineWidth(1); ofRect(0, 0, 512, 200); //param float heightFactor = 0.15; ofSetColor(245, 58, 135); ofSetLineWidth(3); ofBeginShape(); for (int i = 0; i < peakProcess.recentDFsamples.size(); i++){ int height = 200 - peakProcess.recentDFsamples[i]*heightFactor; ofVertex(i*6, height); // if (recentDFonsetFound[i]){ // ofCircle(32+i*6, 376+height , 10); // } // ofVertex(i*2, 100 -right[i]*180.0f); } ofEndShape(false); ofPopMatrix(); ofPopStyle(); //slope values ofPushStyle(); ofPushMatrix(); ofTranslate(32, 320, 0); ofSetLineWidth(1); ofSetColor(25, 124, 235); ofSetLineWidth(3); ofBeginShape(); for (int i = 0; i < peakProcess.recentDFsamples.size(); i++){ int height = 200 - peakProcess.recentDFslopeValues[i]*heightFactor; ofVertex(i*6, height); } ofEndShape(false); ofPopMatrix(); ofPopStyle(); //ONSETS ofPushStyle(); ofPushMatrix(); ofTranslate(32, 320, 0); ofSetColor(25, 224, 135); ofSetLineWidth(3); ofBeginShape(); for (int i = 0; i < peakProcess.recentDFsamples.size(); i++){ if (peakProcess.recentDFonsetFound[i]) ofVertex(i*6, 0); else { ofVertex(i*6, 200); } } ofEndShape(false); ofPopMatrix(); ofPopStyle(); //ONSET Samples ofPushStyle(); ofPushMatrix(); ofTranslate(32, 520, 0); ofSetColor(245, 224, 235); ofDrawBitmapString("exact onset index (in buffer) "+ofToString(exactOnsetIndex), 0, 20); ofSetLineWidth(3); ofBeginShape(); for (int i = 0; i < precisionLocator.onsetSamples.size(); i++){ int height = 100 + 100 * precisionLocator.onsetSamples[i]; ofVertex(i, height); } ofEndShape(false); ofSetColor(240,0,0); ofLine(exactOnsetIndex, 0, exactOnsetIndex, 200);//stripe where on ofPopMatrix(); ofPopStyle(); // draw the average volume: ofPushStyle(); ofPushMatrix(); ofTranslate(565, 120, 0); ofSetColor(225); ofDrawBitmapString("Scaled average vol (0-100): " + ofToString(scaledVol * 100.0, 0), 4, 18); ofRect(0, 0, 400, 400); ofSetColor(245, 58, 135); ofFill(); ofCircle(200, 200, scaledVol * 190.0f); //lets draw the volume history as a graph ofBeginShape(); for (int i = 0; i < volHistory.size(); i++){ if( i == 0 ) ofVertex(i, 400); ofVertex(i, 400 - volHistory[i] * 70); if( i == volHistory.size() -1 ) ofVertex(i, 400); } ofEndShape(false); ofPopMatrix(); ofPopStyle(); drawCounter++; ofSetColor(225); string reportString = "buffers received: "+ofToString(bufferCounter)+"\ndraw routines called: "+ofToString(drawCounter)+"\nticks: " + ofToString(soundStream.getTickCount()); ofDrawBitmapString(reportString, 32, 89); if (peakProcess.newOnsetFound){ ofSetColor(255,0,0); ofCircle(200,200,200); } } //-------------------------------------------------------------- void testApp::audioIn(float * input, int bufferSize, int nChannels){ float curVol = 0.0; // samples are "interleaved" int numCounted = 0; double frame[bufferSize]; for (int i = 0;i < bufferSize;i++){ frame[i] = (double) input[i*2]; } double df_sample = (float) odf.getDFsample(frame); bool peakFound = peakProcess.peakProcessing(df_sample);//our new fn to look for DF onset events //when we find a peak, we get the precise location of it if (peakFound && !holdOn){ exactOnsetIndex = precisionLocator.findExactOnset(&frame[0]);//divide by 512.0 or bufferSize to get [0,1] value } //need to store these continually to help in location process precisionLocator.storeSamples(&frame[0]); //lets go through each sample and calculate the root mean square which is a rough way to calculate volume for (int i = 0; i < bufferSize; i++){ //recentBufferSamples[i] = frame[i];//store the last buffer in case needed for exact onset detection left[i] = input[i*2]*0.5; right[i] = input[i*2+1]*0.5; curVol += left[i] * left[i]; curVol += right[i] * right[i]; numCounted+=2; } //this is how we get the mean of rms :) curVol /= (float)numCounted; // this is how we get the root of rms :) curVol = sqrt( curVol ); smoothedVol *= 0.93; smoothedVol += 0.07 * curVol; bufferCounter++; } /* int testApp::findExactOnset(){ double energySum = 0; double lastEnergySum, hopsizeLastEnergySum; double energyDifference; int bestEnergyIndex = 0; double bestEnergyDifference = 0; int endIndex = bufferSize; int hopSize; for (int resolution = bufferSize/2;resolution > 1;resolution/=2){ printf("resolution %i\n", resolution); /// for (int i = bufferSize - resolution;i < bufferSize;i++){ // lastEnergySum += recentBufferSamples[i] * recentBufferSamples[i]; // } bestEnergyDifference = 0; // printf("previous energy %f", lastEnergySum); //initialise last energySum hopSize = resolution/2; lastEnergySum = getLastEnergySum(bestEnergyIndex, resolution); hopsizeLastEnergySum = getLastEnergySum(bestEnergyIndex + hopSize, resolution); for (int startIndex = bestEnergyIndex;startIndex + resolution <= endIndex;startIndex += hopSize){ printf("index %i last energy %f hop energy %f ", startIndex, lastEnergySum, hopsizeLastEnergySum); //sum the energy for this new frame energySum = 0; for (int i = 0;i < resolution;i++){ energySum += onsetSamples[startIndex + i] * onsetSamples[startIndex + i]; } printf("energysum %f\n", energySum); //check if new max difference energyDifference = energySum - lastEnergySum; if (energyDifference > bestEnergyDifference){ bestEnergyDifference = energyDifference; bestEnergyIndex = startIndex; } //store the values for checking in two loops time (because proceeding at resolution/2 each step) //eg 0_to_128 compared to -128_to_0, 64_to_196 compared to -64_to_64, then 128_256 compared with 0_to_128, lastEnergySum = hopsizeLastEnergySum;// energySum; hopsizeLastEnergySum = energySum; } printf("winning index is %i\n", bestEnergyIndex); endIndex = bestEnergyIndex + resolution; } printf("TOTAL WINNER %i\n", bestEnergyIndex); return bestEnergyIndex; } double testApp::getLastEnergySum(const int& startIndex, const int& vectorSize){ double lastEnergySum = 0; for (int i = startIndex - vectorSize;i < startIndex;i++){ if (i > 0) lastEnergySum += onsetSamples[i] * onsetSamples[i]; else { lastEnergySum += recentBufferSamples[bufferSize + i] * recentBufferSamples[bufferSize + i]; } } return lastEnergySum; } */ /* bool testApp::peakProcessing(const double& newDFval){ recentDFsamples.erase (recentDFsamples.begin(), recentDFsamples.begin()+1);//erase first val recentDFsamples.push_back(newDFval); double slopeVal = getBestSlopeValue(newDFval); newOnsetFound = checkForSlopeOnset(slopeVal); printf("slope %f median %f det median %f\n", slopeVal, bestSlopeMedian, detectionTriggerThreshold); if (newOnsetFound) printf("BANG!\n"); recentDFslopeValues.erase (recentDFslopeValues.begin(), recentDFslopeValues.begin()+1);//erase first val recentDFslopeValues.push_back(slopeVal); recentDFonsetFound.erase (recentDFonsetFound.begin(), recentDFonsetFound.begin()+1);//erase first val recentDFonsetFound.push_back(newOnsetFound); //printf("\n"); // for (int i = 0;i < recentDFsamples.size();i++){ // printf("rdf[%i] %f\n", i, recentDFsamples[i]); // } //printf("SLOPE %f\n", slopeVal); } double testApp::getBestSlopeValue(const float& dfvalue){ //the idea is we want a high slope double bestValue = 0; for (int i = 1;i < min(numberOfDetectionValuesToTest, (int)recentDFsamples.size() - 1);i++){ double angle = 0; int otherIndex = recentDFsamples.size() - i + 1; double testValue = 0; if (otherIndex > 0 && recentDFsamples[otherIndex] > 0 && recentDFsamples[otherIndex] < dfvalue ){ angle = atan((float)(i * dfvalue)/ (numberOfDetectionValuesToTest*(dfvalue-recentDFsamples[otherIndex])) ); testValue = (dfvalue - recentDFsamples[otherIndex]) * cos(angle); } if (testValue > bestValue) bestValue = testValue; } return bestValue; } bool testApp :: checkForSlopeOnset(const float& bestValue){ bool onsetDetected = false; //check for onset relative to our processed slope function //a mix between increase in value and the gradient of that increase currentFrame++; if (bestValue > bestSlopeMedian * thresholdRelativeToMedian && //better than recent average (currentFrame - lastSlopeOnsetFrame) > cutoffForRepeatOnsetsFrames //after cutoff time && slopeFallenBelowMedian // has had onset and fall away again && bestValue > detectionTriggerThreshold * detectionTriggerRatio //longer term ratio of winning onsets ){ // printf("frame diff between onsets %6.1f", (1000*framesToSeconds(currentFrame - lastMedianOnsetFrame)) ); onsetDetected = true; lastSlopeOnsetFrame = currentFrame; slopeFallenBelowMedian = false; updateDetectionTriggerThreshold(bestValue); } if (bestValue > bestSlopeMedian){ bestSlopeMedian += (bestValue - bestSlopeMedian)*0.04;//was 1.1 } else{ bestSlopeMedian *= 0.99; slopeFallenBelowMedian = true;; } //bestSlopeMedian += 0.02* (bestValue - bestSlopeMedian); return onsetDetected; } void testApp :: updateDetectionTriggerThreshold(const float& val){ float detectionAdaptSpeed = 0.05;//moving average, roughly last twenty onsets detectionTriggerThreshold *= 1- detectionAdaptSpeed; detectionTriggerThreshold += (val * detectionAdaptSpeed); } */ //-------------------------------------------------------------- void testApp::keyPressed (int key){ if( key == 's' ){ soundStream.start(); } if( key == 'e' ){ soundStream.stop(); } if (key == 'h'){ holdOn = !holdOn; } } //-------------------------------------------------------------- void testApp::keyReleased(int key){ } //-------------------------------------------------------------- void testApp::mouseMoved(int x, int y ){ } //-------------------------------------------------------------- void testApp::mouseDragged(int x, int y, int button){ } //-------------------------------------------------------------- void testApp::mousePressed(int x, int y, int button){ } //-------------------------------------------------------------- void testApp::mouseReleased(int x, int y, int button){ } //-------------------------------------------------------------- void testApp::windowResized(int w, int h){ } //-------------------------------------------------------------- void testApp::gotMessage(ofMessage msg){ } //-------------------------------------------------------------- void testApp::dragEvent(ofDragInfo dragInfo){ }