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