rt300@0: /* rt300@0: * scanpath.cpp rt300@0: * springstructure rt300@0: * rt300@0: * Created by Robert Tubb on 09/06/2011. rt300@0: * Copyright 2011 __MyCompanyName__. All rights reserved. rt300@0: * rt300@0: */ rt300@0: #include rt300@0: #include "scanpath.h" rt300@0: #include "testApp.h" rt300@0: rt300@3: ScanPath scanPath; rt300@3: rt300@3: // audio thread does not touch mesh lumps/springs rt300@0: //---------------------------------------------------------------- rt300@3: ScanPath::ScanPath() : maxElements(10000) { rt300@0: // construct an empty scanpath rt300@0: // an ' element ' consists of 1 lump and one spring rt300@0: rt300@0: rt300@0: numElements = 0; // numElements is a dynamic running count of elements when building - a bit crap rt300@3: rt300@0: rt300@0: frameInterpolator = 0.0; rt300@0: currentLength = 0.0; rt300@0: restLength = 1.0; // ? rt300@0: rt300@0: scanMode = DISPLACEMENT; rt300@3: initWavetables(); rt300@3: framesPerSample = 60.0/SAMPLE_RATE; rt300@4: updateAccessing = false; rt300@4: audioAccessing = false; rt300@4: rt300@3: } rt300@3: void ScanPath::init(){ rt300@3: framesPerSample = ofGetFrameRate()/SAMPLE_RATE; rt300@0: rt300@0: } rt300@0: //---------------------------------------------------------------- rt300@0: ScanPath::~ScanPath(){ rt300@4: rt300@3: rt300@0: delete [] wavetableNew; rt300@0: delete [] wavetableOld; rt300@0: delete [] wavetableUpdate; rt300@0: cout << "destructed scanpath\n"; rt300@0: } rt300@0: rt300@0: void ScanPath::clear(){ rt300@4: rt300@3: for(vector::iterator elitr = pathElements.begin(); elitr < pathElements.end(); elitr++){ rt300@3: (*elitr).eLump->removeFromScanPath(); rt300@3: (*elitr).eSpring->removeFromScanPath(); rt300@0: } rt300@3: pathElements.clear(); rt300@0: numElements = 0; rt300@3: rt300@0: } rt300@0: int ScanPath::howManyElements(){ rt300@3: //return pathElements.size(); rt300@0: return numElements; rt300@0: } rt300@0: //---------------------------------------------------------------- rt300@0: void ScanPath::inscribe(double ax, double ay){ rt300@0: // look at coordinates, add the closest lump and it's connecting string rt300@0: // if we're further away from current lump rt300@0: rt300@0: // check end points of connecting springs, pick closest rt300@3: rt300@3: // NOW IN MESH rt300@0: } rt300@0: void ScanPath::draw(){ rt300@0: // draw the actual waveform in the corner rt300@0: rt300@0: int width = 768; rt300@0: int height = 128; rt300@0: double sampval = 0.0; rt300@0: int leftsampnum = 0; rt300@0: int rightsampnum = 0; rt300@0: float sampscale = 0.0, prevsampscale = 0.0, interp = 0.0; rt300@0: rt300@8: ofSetColor(256, 0, 0); rt300@0: double step = double(numElements)/width; // how much we are stepping thru wave per pixel rt300@0: for(int i = 0; i < width; i++){ rt300@0: rt300@0: leftsampnum = floor(i * step); // basic nearest neighbour interpolation rt300@0: rightsampnum = ceil(i*step); rt300@0: interp = (i*step)-leftsampnum; rt300@0: if(rightsampnum < numElements){ rt300@0: sampval = (1 - interp)*wavetableNew[leftsampnum] + interp*wavetableNew[rightsampnum]; rt300@0: } rt300@0: sampscale = (sampval * 700) + height/2.0; // centre and scale rt300@0: ofSetLineWidth(2); rt300@0: ofLine(sampscale, i, prevsampscale, i-1); // draw a line from pixel to pixel (?) rt300@0: prevsampscale = sampscale; rt300@0: } rt300@0: rt300@0: } rt300@0: void drawCubic(){ rt300@0: rt300@0: } rt300@0: //---------------------------------------------------------------- rt300@0: // add spring rt300@0: void ScanPath::addSpring(){ rt300@0: // see add element rt300@0: } rt300@0: //---------------------------------------------------------------- rt300@0: // add lump rt300@0: void ScanPath::addLump(){ rt300@0: // see add element rt300@0: } rt300@0: //---------------------------------------------------------------- rt300@0: // add lump rt300@0: double ScanPath::getTotalLength(){ rt300@0: // for interesting modulations... rt300@0: currentLength = 0.0; rt300@3: rt300@3: for(vector::iterator elitr = pathElements.begin(); elitr < pathElements.end(); elitr++){ rt300@3: rt300@3: currentLength = (*elitr).eSpring->getLength(); rt300@0: } rt300@0: return currentLength; rt300@0: rt300@0: } rt300@0: //---------------------------------------------------------------- rt300@0: void ScanPath::initWavetables(){ rt300@0: wavetableNew = new double[maxElements]; rt300@0: wavetableOld = new double[maxElements]; rt300@0: wavetableUpdate = new double[maxElements]; rt300@0: rt300@0: for(int i = 0; i < maxElements; i++){ rt300@0: wavetableOld[i] = 0.0; rt300@0: wavetableNew[i] = 0.0; rt300@0: wavetableUpdate[i] = 0.0; rt300@0: } rt300@0: rt300@0: } rt300@0: //---------------------------------------------------------------- rt300@3: void ScanPath::addElement(Lump* const aLump, Spring * const aSpring){ rt300@0: // insert ptr to the lump and spring into array rt300@3: rt300@3: rt300@3: if(numElements + 1 >= maxElements){ rt300@3: cout << " max elements reached!\n"; rt300@0: return; rt300@0: } rt300@3: rt300@3: pathElements.push_back(Element(aLump,aSpring)); rt300@0: rt300@3: // maybe do this in mesh? rt300@0: aLump->addToScanPath(); rt300@0: aSpring->addToScanPath(); rt300@3: numElements++; rt300@0: rt300@3: rt300@0: } rt300@0: //---------------------------------------------------------------- rt300@8: Lump * ScanPath::getLump(int index){ rt300@8: if(index < pathElements.size()){ rt300@8: return pathElements[index].eLump; rt300@8: }else{ rt300@8: cerr << "bad scanpath::getLump index\n"; rt300@8: return NULL; rt300@8: } rt300@8: rt300@8: } rt300@8: //---------------------------------------------------------------- rt300@8: Spring * ScanPath::getSpring(int index){ rt300@8: if(index < pathElements.size()){ rt300@8: return pathElements[index].eSpring; rt300@8: }else{ rt300@8: cerr << "bad scanpath::getSpring index\n"; rt300@8: return NULL; rt300@8: } rt300@8: } rt300@8: rt300@8: //---------------------------------------------------------------- rt300@0: void ScanPath::updateWavetables(){ rt300@0: // swap old , new rt300@0: double * temp; rt300@5: if(pathElements.size() == 0) return; rt300@1: // TODO THRED MUTEX HERE!?? rt300@3: // this is called from graphics thread rt300@3: // reset the interp between frames rt300@5: /* rt300@3: int i = 0; rt300@3: while(audioAccessing){ rt300@3: i++; rt300@3: } rt300@4: if(i > 0){ rt300@4: cout << "Update wavetables had to wait for audio access " << i << " times\n"; rt300@4: // hardly ever happens rt300@4: } rt300@5: */ rt300@3: updateAccessing = true; rt300@3: rt300@4: rt300@0: switch(scanMode){ rt300@0: case DISPLACEMENT: rt300@0: // now fill with new values rt300@0: for(int i = 0; i < numElements; i++){ rt300@3: // double check rt300@5: rt300@8: wavetableUpdate[i] = pathElements[i].eLump->scanDisplacement(); rt300@5: rt300@0: rt300@0: } rt300@0: break; rt300@0: case SPEED: rt300@0: for(int i = 0; i < numElements; i++){ rt300@5: rt300@5: wavetableUpdate[i] = pathElements[i].eLump->scanLumpSpeed(); rt300@5: rt300@0: } rt300@0: break; rt300@0: case SPRING_FORCE: rt300@0: for(int i = 0; i < numElements; i++){ rt300@3: if(pathElements[i].eSpring->isInScanPath){ rt300@3: wavetableUpdate[i] = pathElements[i].eSpring->getForceMag(); rt300@3: } rt300@0: } rt300@0: break; rt300@0: case YPOS: rt300@0: for(int i = 0; i < numElements; i++){ rt300@3: if(pathElements[i].eLump->isInScanPath){ rt300@3: wavetableUpdate[i] = pathElements[i].eLump->scanYPos(); rt300@3: } rt300@0: } rt300@0: break; rt300@0: default: rt300@0: break; rt300@4: rt300@0: } rt300@4: rt300@0: temp = wavetableOld; rt300@0: wavetableOld = wavetableNew; rt300@0: wavetableNew = wavetableUpdate; rt300@0: wavetableUpdate = temp; rt300@3: rt300@3: // END THREAD MUTEX rt300@0: updateAccessing = false; rt300@0: rt300@0: frameInterpolator = 0.0; rt300@0: framesPerSample = 2.0*ofGetFrameRate()/SAMPLE_RATE; // attempt to get a reasonable est. of how fast to interp rt300@0: rt300@0: } rt300@0: //---------------------------------------------------------------- rt300@3: //---------------------------------------------------------------- rt300@3: // AUDIO THREAD STUFF rt300@3: //---------------------------------------------------------------- rt300@0: // get next sample rt300@3: rt300@0: double ScanPath::getNextSample(double aPhasor){ rt300@0: // move along path, interpolating between points rt300@0: // move between frames too rt300@5: if(pathElements.size() == 0) return 0.0; rt300@5: rt300@0: double alongPath = aPhasor*double(numElements); rt300@0: rt300@0: // indexes for interpolated points rt300@0: int n0 = floor(alongPath); rt300@0: int n1 = n0+1; rt300@0: if(n1 >= numElements){ rt300@0: n1 = 0; rt300@0: } rt300@0: rt300@0: double frac = alongPath - double(n0); rt300@0: rt300@3: // TODO THRED MUTEX HERE!?? rt300@3: // this is called from audio thread rt300@5: /* rt300@3: int i = 0; rt300@3: while(updateAccessing){ rt300@3: i++; rt300@3: } rt300@5: */ rt300@0: audioAccessing = true; rt300@5: // if(i>0) cout << "Audio thread had to wait for wavetable update " << i << " times\n"; rt300@4: rt300@1: rt300@0: double oldsample = (1 - frac) * wavetableOld[n0] + frac * wavetableOld[n1]; rt300@0: rt300@0: double newsample = (1 - frac) * wavetableNew[n0] + frac * wavetableNew[n1]; rt300@0: rt300@1: // END THREAD MUTEX rt300@0: audioAccessing = false; rt300@0: rt300@0: frameInterpolator += framesPerSample; rt300@0: if(frameInterpolator >= 1.0){ rt300@0: //cout << "frame interp > 1\n"; rt300@0: frameInterpolator = 1.0; // just stays outputting new rt300@0: } rt300@0: rt300@0: double sample = (1 - frameInterpolator)*oldsample + frameInterpolator*newsample; rt300@0: //cout << sample << endl; rt300@0: // keep within the bounds of acceptability rt300@14: rt300@0: return sample; rt300@0: rt300@0: } rt300@0: //---------------------------------------------------------------- rt300@13: // get next sample: just one sample at a time at sample rate rt300@0: double ScanPath::getNextSample(){ rt300@0: // move along wavetable, no interpolation ie: length of path is pitch rt300@0: static int n = 0; rt300@0: double oldsample = wavetableOld[n]; rt300@0: rt300@0: double newsample = wavetableNew[n]; rt300@0: n++; rt300@0: if (n >= numElements){ rt300@0: n = 0; rt300@0: } rt300@0: rt300@0: frameInterpolator += framesPerSample; rt300@0: if(frameInterpolator >= 1.0){ rt300@0: //cout << "frame interp > 1\n"; rt300@0: frameInterpolator = 1.0; // just stays outputting new rt300@0: } rt300@0: rt300@0: double sample = (1 - frameInterpolator)*oldsample + frameInterpolator*newsample; rt300@13: // beef up rt300@13: sample = sample*10.0; rt300@13: rt300@13: if(sample > 0.99){ rt300@13: cout << "BIG" << endl; rt300@13: rt300@13: sample = 0.99; rt300@13: }else if(sample < -0.0){ rt300@13: cout << "SMALL" << endl; rt300@13: sample = 0.0; rt300@13: } rt300@0: rt300@0: } rt300@0: //---------------------------------------------------------------- rt300@0: //---------------------------------------------------------------- rt300@0: // get next sample with cubic interpolation rt300@0: double ScanPath::getNextSampleCubic(double aPhasor){ rt300@0: // move along path, interpolating between points rt300@0: // move between frames too rt300@0: double alongPath = aPhasor*double(numElements); rt300@0: rt300@0: // indexes for interpolated points rt300@0: int n1 = floor(alongPath); rt300@0: rt300@0: int n0 = n1-1; rt300@0: if(n0 < 0){ rt300@0: n0 = numElements; rt300@0: } rt300@0: int n2 = n1+1; rt300@0: if(n2 >= numElements){ rt300@0: n2 = 0; rt300@0: } rt300@0: int n3 = n2+1; rt300@0: if(n3 >= numElements){ rt300@0: n3 = 0; rt300@0: } rt300@0: double frac = alongPath - double(n0); rt300@0: double fracSquared = frac * frac; rt300@0: double fracCubed = fracSquared * frac; rt300@0: double a0,a1,a2,a3; rt300@0: //cout << n0 << endl; rt300@0: // cubic interp rt300@0: /* rt300@0: double y0,double y1, rt300@0: double y2,double y3, rt300@0: double mu) rt300@0: { rt300@0: double a0,a1,a2,a3,mu2; rt300@0: rt300@0: mu2 = mu*mu; rt300@0: a0 = y3 - y2 - y0 + y1; rt300@0: a1 = y0 - y1 - a0; rt300@0: a2 = y2 - y0; rt300@0: a3 = y1; rt300@0: rt300@0: return(a0*mu*mu2+a1*mu2+a2*mu+a3); rt300@0: */ rt300@0: a0 = wavetableOld[n3] - wavetableOld[n2] - wavetableOld[n0] + wavetableOld[n1]; rt300@0: a1 = wavetableOld[n0] - wavetableOld[n1] - a0; // y0 - y1 - a0; rt300@0: a2 = wavetableOld[n2] - wavetableOld[n0]; // y2 - y0; rt300@0: a3 = wavetableOld[n1]; rt300@0: rt300@0: double oldsample = a0*fracCubed + a1*fracSquared + a2*frac + a3; rt300@0: rt300@0: a0 = wavetableNew[n3] - wavetableNew[n2] - wavetableNew[n0] + wavetableNew[n1]; rt300@0: a1 = wavetableNew[n0] - wavetableNew[n1] - a0; // y0 - y1 - a0; rt300@0: a2 = wavetableNew[n2] - wavetableNew[n0]; // y2 - y0; rt300@0: a3 = wavetableNew[n1]; rt300@0: rt300@0: double newsample = a0*fracCubed + a1*fracSquared + a2*frac + a3; rt300@0: rt300@0: frameInterpolator += framesPerSample; rt300@0: if(frameInterpolator >= 1.0){ rt300@0: frameInterpolator = 1.0; // just stays outputting new rt300@0: } rt300@0: rt300@0: double sample = (1 - frameInterpolator)*oldsample + frameInterpolator*newsample; rt300@0: //cout << sample << endl; rt300@0: // keep within the bounds of acceptability rt300@0: rt300@0: // beef up rt300@13: sample = sample*10.0; rt300@0: rt300@0: if(sample > 0.99){ rt300@13: cout << "BIG" << endl; rt300@13: rt300@0: sample = 0.99; rt300@13: }else if(sample < -0.0){ rt300@13: cout << "SMALL" << endl; rt300@13: sample = 0.0; rt300@0: } rt300@0: rt300@0: return sample; rt300@0: rt300@0: } rt300@8: //---------------------------------------------------------------- rt300@8: rt300@8: Json::Value ScanPath::convertToJsonForSaving(){ rt300@8: Json::Value jscanpath; rt300@9: rt300@9: for(int i=0;imyIndexInMesh; rt300@9: jscanpath[i]["springNo"] = pathElements[i].eSpring->myIndexInMesh; rt300@9: } rt300@9: rt300@8: return jscanpath; rt300@8: } rt300@8: rt300@8: //---------------------------------------------------------------- rt300@8: rt300@8: //---------------------------------------------------------------- rt300@8: rt300@8: //---------------------------------------------------------------- rt300@8: