rt300@0
|
1 /*
|
rt300@0
|
2 * scanpath.cpp
|
rt300@0
|
3 * springstructure
|
rt300@0
|
4 *
|
rt300@0
|
5 * Created by Robert Tubb on 09/06/2011.
|
rt300@0
|
6 * Copyright 2011 __MyCompanyName__. All rights reserved.
|
rt300@0
|
7 *
|
rt300@0
|
8 */
|
rt300@0
|
9 #include <vector>
|
rt300@0
|
10 #include "scanpath.h"
|
rt300@0
|
11 #include "testApp.h"
|
rt300@0
|
12
|
rt300@3
|
13 ScanPath scanPath;
|
rt300@3
|
14
|
rt300@3
|
15 // audio thread does not touch mesh lumps/springs
|
rt300@0
|
16 //----------------------------------------------------------------
|
rt300@3
|
17 ScanPath::ScanPath() : maxElements(10000) {
|
rt300@0
|
18 // construct an empty scanpath
|
rt300@0
|
19 // an ' element ' consists of 1 lump and one spring
|
rt300@0
|
20
|
rt300@0
|
21
|
rt300@0
|
22 numElements = 0; // numElements is a dynamic running count of elements when building - a bit crap
|
rt300@3
|
23
|
rt300@0
|
24
|
rt300@0
|
25 frameInterpolator = 0.0;
|
rt300@0
|
26 currentLength = 0.0;
|
rt300@0
|
27 restLength = 1.0; // ?
|
rt300@0
|
28
|
rt300@0
|
29 scanMode = DISPLACEMENT;
|
rt300@3
|
30 initWavetables();
|
rt300@3
|
31 framesPerSample = 60.0/SAMPLE_RATE;
|
rt300@4
|
32 updateAccessing = false;
|
rt300@4
|
33 audioAccessing = false;
|
rt300@4
|
34
|
rt300@3
|
35 }
|
rt300@3
|
36 void ScanPath::init(){
|
rt300@3
|
37 framesPerSample = ofGetFrameRate()/SAMPLE_RATE;
|
rt300@0
|
38
|
rt300@0
|
39 }
|
rt300@0
|
40 //----------------------------------------------------------------
|
rt300@0
|
41 ScanPath::~ScanPath(){
|
rt300@4
|
42
|
rt300@3
|
43
|
rt300@0
|
44 delete [] wavetableNew;
|
rt300@0
|
45 delete [] wavetableOld;
|
rt300@0
|
46 delete [] wavetableUpdate;
|
rt300@0
|
47 cout << "destructed scanpath\n";
|
rt300@0
|
48 }
|
rt300@0
|
49
|
rt300@0
|
50 void ScanPath::clear(){
|
rt300@4
|
51
|
rt300@3
|
52 for(vector<Element>::iterator elitr = pathElements.begin(); elitr < pathElements.end(); elitr++){
|
rt300@3
|
53 (*elitr).eLump->removeFromScanPath();
|
rt300@3
|
54 (*elitr).eSpring->removeFromScanPath();
|
rt300@0
|
55 }
|
rt300@3
|
56 pathElements.clear();
|
rt300@0
|
57 numElements = 0;
|
rt300@3
|
58
|
rt300@0
|
59 }
|
rt300@0
|
60 int ScanPath::howManyElements(){
|
rt300@3
|
61 //return pathElements.size();
|
rt300@0
|
62 return numElements;
|
rt300@0
|
63 }
|
rt300@0
|
64 //----------------------------------------------------------------
|
rt300@0
|
65 void ScanPath::inscribe(double ax, double ay){
|
rt300@0
|
66 // look at coordinates, add the closest lump and it's connecting string
|
rt300@0
|
67 // if we're further away from current lump
|
rt300@0
|
68
|
rt300@0
|
69 // check end points of connecting springs, pick closest
|
rt300@3
|
70
|
rt300@3
|
71 // NOW IN MESH
|
rt300@0
|
72 }
|
rt300@0
|
73 void ScanPath::draw(){
|
rt300@0
|
74 // draw the actual waveform in the corner
|
rt300@0
|
75
|
rt300@0
|
76 int width = 768;
|
rt300@0
|
77 int height = 128;
|
rt300@0
|
78 double sampval = 0.0;
|
rt300@0
|
79 int leftsampnum = 0;
|
rt300@0
|
80 int rightsampnum = 0;
|
rt300@0
|
81 float sampscale = 0.0, prevsampscale = 0.0, interp = 0.0;
|
rt300@0
|
82
|
rt300@8
|
83 ofSetColor(256, 0, 0);
|
rt300@0
|
84 double step = double(numElements)/width; // how much we are stepping thru wave per pixel
|
rt300@0
|
85 for(int i = 0; i < width; i++){
|
rt300@0
|
86
|
rt300@0
|
87 leftsampnum = floor(i * step); // basic nearest neighbour interpolation
|
rt300@0
|
88 rightsampnum = ceil(i*step);
|
rt300@0
|
89 interp = (i*step)-leftsampnum;
|
rt300@0
|
90 if(rightsampnum < numElements){
|
rt300@0
|
91 sampval = (1 - interp)*wavetableNew[leftsampnum] + interp*wavetableNew[rightsampnum];
|
rt300@0
|
92 }
|
rt300@0
|
93 sampscale = (sampval * 700) + height/2.0; // centre and scale
|
rt300@0
|
94 ofSetLineWidth(2);
|
rt300@0
|
95 ofLine(sampscale, i, prevsampscale, i-1); // draw a line from pixel to pixel (?)
|
rt300@0
|
96 prevsampscale = sampscale;
|
rt300@0
|
97 }
|
rt300@0
|
98
|
rt300@0
|
99 }
|
rt300@0
|
100 void drawCubic(){
|
rt300@0
|
101
|
rt300@0
|
102 }
|
rt300@0
|
103 //----------------------------------------------------------------
|
rt300@0
|
104 // add spring
|
rt300@0
|
105 void ScanPath::addSpring(){
|
rt300@0
|
106 // see add element
|
rt300@0
|
107 }
|
rt300@0
|
108 //----------------------------------------------------------------
|
rt300@0
|
109 // add lump
|
rt300@0
|
110 void ScanPath::addLump(){
|
rt300@0
|
111 // see add element
|
rt300@0
|
112 }
|
rt300@0
|
113 //----------------------------------------------------------------
|
rt300@0
|
114 // add lump
|
rt300@0
|
115 double ScanPath::getTotalLength(){
|
rt300@0
|
116 // for interesting modulations...
|
rt300@0
|
117 currentLength = 0.0;
|
rt300@3
|
118
|
rt300@3
|
119 for(vector<Element>::iterator elitr = pathElements.begin(); elitr < pathElements.end(); elitr++){
|
rt300@3
|
120
|
rt300@3
|
121 currentLength = (*elitr).eSpring->getLength();
|
rt300@0
|
122 }
|
rt300@0
|
123 return currentLength;
|
rt300@0
|
124
|
rt300@0
|
125 }
|
rt300@0
|
126 //----------------------------------------------------------------
|
rt300@0
|
127 void ScanPath::initWavetables(){
|
rt300@0
|
128 wavetableNew = new double[maxElements];
|
rt300@0
|
129 wavetableOld = new double[maxElements];
|
rt300@0
|
130 wavetableUpdate = new double[maxElements];
|
rt300@0
|
131
|
rt300@0
|
132 for(int i = 0; i < maxElements; i++){
|
rt300@0
|
133 wavetableOld[i] = 0.0;
|
rt300@0
|
134 wavetableNew[i] = 0.0;
|
rt300@0
|
135 wavetableUpdate[i] = 0.0;
|
rt300@0
|
136 }
|
rt300@0
|
137
|
rt300@0
|
138 }
|
rt300@0
|
139 //----------------------------------------------------------------
|
rt300@3
|
140 void ScanPath::addElement(Lump* const aLump, Spring * const aSpring){
|
rt300@0
|
141 // insert ptr to the lump and spring into array
|
rt300@3
|
142
|
rt300@3
|
143
|
rt300@3
|
144 if(numElements + 1 >= maxElements){
|
rt300@3
|
145 cout << " max elements reached!\n";
|
rt300@0
|
146 return;
|
rt300@0
|
147 }
|
rt300@3
|
148
|
rt300@3
|
149 pathElements.push_back(Element(aLump,aSpring));
|
rt300@0
|
150
|
rt300@3
|
151 // maybe do this in mesh?
|
rt300@0
|
152 aLump->addToScanPath();
|
rt300@0
|
153 aSpring->addToScanPath();
|
rt300@3
|
154 numElements++;
|
rt300@0
|
155
|
rt300@3
|
156
|
rt300@0
|
157 }
|
rt300@0
|
158 //----------------------------------------------------------------
|
rt300@8
|
159 Lump * ScanPath::getLump(int index){
|
rt300@8
|
160 if(index < pathElements.size()){
|
rt300@8
|
161 return pathElements[index].eLump;
|
rt300@8
|
162 }else{
|
rt300@8
|
163 cerr << "bad scanpath::getLump index\n";
|
rt300@8
|
164 return NULL;
|
rt300@8
|
165 }
|
rt300@8
|
166
|
rt300@8
|
167 }
|
rt300@8
|
168 //----------------------------------------------------------------
|
rt300@8
|
169 Spring * ScanPath::getSpring(int index){
|
rt300@8
|
170 if(index < pathElements.size()){
|
rt300@8
|
171 return pathElements[index].eSpring;
|
rt300@8
|
172 }else{
|
rt300@8
|
173 cerr << "bad scanpath::getSpring index\n";
|
rt300@8
|
174 return NULL;
|
rt300@8
|
175 }
|
rt300@8
|
176 }
|
rt300@8
|
177
|
rt300@8
|
178 //----------------------------------------------------------------
|
rt300@0
|
179 void ScanPath::updateWavetables(){
|
rt300@0
|
180 // swap old , new
|
rt300@0
|
181 double * temp;
|
rt300@5
|
182 if(pathElements.size() == 0) return;
|
rt300@1
|
183 // TODO THRED MUTEX HERE!??
|
rt300@3
|
184 // this is called from graphics thread
|
rt300@3
|
185 // reset the interp between frames
|
rt300@5
|
186 /*
|
rt300@3
|
187 int i = 0;
|
rt300@3
|
188 while(audioAccessing){
|
rt300@3
|
189 i++;
|
rt300@3
|
190 }
|
rt300@4
|
191 if(i > 0){
|
rt300@4
|
192 cout << "Update wavetables had to wait for audio access " << i << " times\n";
|
rt300@4
|
193 // hardly ever happens
|
rt300@4
|
194 }
|
rt300@5
|
195 */
|
rt300@3
|
196 updateAccessing = true;
|
rt300@3
|
197
|
rt300@4
|
198
|
rt300@0
|
199 switch(scanMode){
|
rt300@0
|
200 case DISPLACEMENT:
|
rt300@0
|
201 // now fill with new values
|
rt300@0
|
202 for(int i = 0; i < numElements; i++){
|
rt300@3
|
203 // double check
|
rt300@5
|
204
|
rt300@8
|
205 wavetableUpdate[i] = pathElements[i].eLump->scanDisplacement();
|
rt300@5
|
206
|
rt300@0
|
207
|
rt300@0
|
208 }
|
rt300@0
|
209 break;
|
rt300@0
|
210 case SPEED:
|
rt300@0
|
211 for(int i = 0; i < numElements; i++){
|
rt300@5
|
212
|
rt300@5
|
213 wavetableUpdate[i] = pathElements[i].eLump->scanLumpSpeed();
|
rt300@5
|
214
|
rt300@0
|
215 }
|
rt300@0
|
216 break;
|
rt300@0
|
217 case SPRING_FORCE:
|
rt300@0
|
218 for(int i = 0; i < numElements; i++){
|
rt300@3
|
219 if(pathElements[i].eSpring->isInScanPath){
|
rt300@3
|
220 wavetableUpdate[i] = pathElements[i].eSpring->getForceMag();
|
rt300@3
|
221 }
|
rt300@0
|
222 }
|
rt300@0
|
223 break;
|
rt300@0
|
224 case YPOS:
|
rt300@0
|
225 for(int i = 0; i < numElements; i++){
|
rt300@3
|
226 if(pathElements[i].eLump->isInScanPath){
|
rt300@3
|
227 wavetableUpdate[i] = pathElements[i].eLump->scanYPos();
|
rt300@3
|
228 }
|
rt300@0
|
229 }
|
rt300@0
|
230 break;
|
rt300@0
|
231 default:
|
rt300@0
|
232 break;
|
rt300@4
|
233
|
rt300@0
|
234 }
|
rt300@4
|
235
|
rt300@0
|
236 temp = wavetableOld;
|
rt300@0
|
237 wavetableOld = wavetableNew;
|
rt300@0
|
238 wavetableNew = wavetableUpdate;
|
rt300@0
|
239 wavetableUpdate = temp;
|
rt300@3
|
240
|
rt300@3
|
241 // END THREAD MUTEX
|
rt300@0
|
242 updateAccessing = false;
|
rt300@0
|
243
|
rt300@0
|
244 frameInterpolator = 0.0;
|
rt300@0
|
245 framesPerSample = 2.0*ofGetFrameRate()/SAMPLE_RATE; // attempt to get a reasonable est. of how fast to interp
|
rt300@0
|
246
|
rt300@0
|
247 }
|
rt300@0
|
248 //----------------------------------------------------------------
|
rt300@3
|
249 //----------------------------------------------------------------
|
rt300@3
|
250 // AUDIO THREAD STUFF
|
rt300@3
|
251 //----------------------------------------------------------------
|
rt300@0
|
252 // get next sample
|
rt300@3
|
253
|
rt300@0
|
254 double ScanPath::getNextSample(double aPhasor){
|
rt300@0
|
255 // move along path, interpolating between points
|
rt300@0
|
256 // move between frames too
|
rt300@5
|
257 if(pathElements.size() == 0) return 0.0;
|
rt300@5
|
258
|
rt300@0
|
259 double alongPath = aPhasor*double(numElements);
|
rt300@0
|
260
|
rt300@0
|
261 // indexes for interpolated points
|
rt300@0
|
262 int n0 = floor(alongPath);
|
rt300@0
|
263 int n1 = n0+1;
|
rt300@0
|
264 if(n1 >= numElements){
|
rt300@0
|
265 n1 = 0;
|
rt300@0
|
266 }
|
rt300@0
|
267
|
rt300@0
|
268 double frac = alongPath - double(n0);
|
rt300@0
|
269
|
rt300@3
|
270 // TODO THRED MUTEX HERE!??
|
rt300@3
|
271 // this is called from audio thread
|
rt300@5
|
272 /*
|
rt300@3
|
273 int i = 0;
|
rt300@3
|
274 while(updateAccessing){
|
rt300@3
|
275 i++;
|
rt300@3
|
276 }
|
rt300@5
|
277 */
|
rt300@0
|
278 audioAccessing = true;
|
rt300@5
|
279 // if(i>0) cout << "Audio thread had to wait for wavetable update " << i << " times\n";
|
rt300@4
|
280
|
rt300@1
|
281
|
rt300@0
|
282 double oldsample = (1 - frac) * wavetableOld[n0] + frac * wavetableOld[n1];
|
rt300@0
|
283
|
rt300@0
|
284 double newsample = (1 - frac) * wavetableNew[n0] + frac * wavetableNew[n1];
|
rt300@0
|
285
|
rt300@1
|
286 // END THREAD MUTEX
|
rt300@0
|
287 audioAccessing = false;
|
rt300@0
|
288
|
rt300@0
|
289 frameInterpolator += framesPerSample;
|
rt300@0
|
290 if(frameInterpolator >= 1.0){
|
rt300@0
|
291 //cout << "frame interp > 1\n";
|
rt300@0
|
292 frameInterpolator = 1.0; // just stays outputting new
|
rt300@0
|
293 }
|
rt300@0
|
294
|
rt300@0
|
295 double sample = (1 - frameInterpolator)*oldsample + frameInterpolator*newsample;
|
rt300@0
|
296 //cout << sample << endl;
|
rt300@0
|
297 // keep within the bounds of acceptability
|
rt300@14
|
298
|
rt300@0
|
299 return sample;
|
rt300@0
|
300
|
rt300@0
|
301 }
|
rt300@0
|
302 //----------------------------------------------------------------
|
rt300@13
|
303 // get next sample: just one sample at a time at sample rate
|
rt300@0
|
304 double ScanPath::getNextSample(){
|
rt300@0
|
305 // move along wavetable, no interpolation ie: length of path is pitch
|
rt300@0
|
306 static int n = 0;
|
rt300@0
|
307 double oldsample = wavetableOld[n];
|
rt300@0
|
308
|
rt300@0
|
309 double newsample = wavetableNew[n];
|
rt300@0
|
310 n++;
|
rt300@0
|
311 if (n >= numElements){
|
rt300@0
|
312 n = 0;
|
rt300@0
|
313 }
|
rt300@0
|
314
|
rt300@0
|
315 frameInterpolator += framesPerSample;
|
rt300@0
|
316 if(frameInterpolator >= 1.0){
|
rt300@0
|
317 //cout << "frame interp > 1\n";
|
rt300@0
|
318 frameInterpolator = 1.0; // just stays outputting new
|
rt300@0
|
319 }
|
rt300@0
|
320
|
rt300@0
|
321 double sample = (1 - frameInterpolator)*oldsample + frameInterpolator*newsample;
|
rt300@13
|
322 // beef up
|
rt300@13
|
323 sample = sample*10.0;
|
rt300@13
|
324
|
rt300@13
|
325 if(sample > 0.99){
|
rt300@13
|
326 cout << "BIG" << endl;
|
rt300@13
|
327
|
rt300@13
|
328 sample = 0.99;
|
rt300@13
|
329 }else if(sample < -0.0){
|
rt300@13
|
330 cout << "SMALL" << endl;
|
rt300@13
|
331 sample = 0.0;
|
rt300@13
|
332 }
|
rt300@0
|
333
|
rt300@0
|
334 }
|
rt300@0
|
335 //----------------------------------------------------------------
|
rt300@0
|
336 //----------------------------------------------------------------
|
rt300@0
|
337 // get next sample with cubic interpolation
|
rt300@0
|
338 double ScanPath::getNextSampleCubic(double aPhasor){
|
rt300@0
|
339 // move along path, interpolating between points
|
rt300@0
|
340 // move between frames too
|
rt300@0
|
341 double alongPath = aPhasor*double(numElements);
|
rt300@0
|
342
|
rt300@0
|
343 // indexes for interpolated points
|
rt300@0
|
344 int n1 = floor(alongPath);
|
rt300@0
|
345
|
rt300@0
|
346 int n0 = n1-1;
|
rt300@0
|
347 if(n0 < 0){
|
rt300@0
|
348 n0 = numElements;
|
rt300@0
|
349 }
|
rt300@0
|
350 int n2 = n1+1;
|
rt300@0
|
351 if(n2 >= numElements){
|
rt300@0
|
352 n2 = 0;
|
rt300@0
|
353 }
|
rt300@0
|
354 int n3 = n2+1;
|
rt300@0
|
355 if(n3 >= numElements){
|
rt300@0
|
356 n3 = 0;
|
rt300@0
|
357 }
|
rt300@0
|
358 double frac = alongPath - double(n0);
|
rt300@0
|
359 double fracSquared = frac * frac;
|
rt300@0
|
360 double fracCubed = fracSquared * frac;
|
rt300@0
|
361 double a0,a1,a2,a3;
|
rt300@0
|
362 //cout << n0 << endl;
|
rt300@0
|
363 // cubic interp
|
rt300@0
|
364 /*
|
rt300@0
|
365 double y0,double y1,
|
rt300@0
|
366 double y2,double y3,
|
rt300@0
|
367 double mu)
|
rt300@0
|
368 {
|
rt300@0
|
369 double a0,a1,a2,a3,mu2;
|
rt300@0
|
370
|
rt300@0
|
371 mu2 = mu*mu;
|
rt300@0
|
372 a0 = y3 - y2 - y0 + y1;
|
rt300@0
|
373 a1 = y0 - y1 - a0;
|
rt300@0
|
374 a2 = y2 - y0;
|
rt300@0
|
375 a3 = y1;
|
rt300@0
|
376
|
rt300@0
|
377 return(a0*mu*mu2+a1*mu2+a2*mu+a3);
|
rt300@0
|
378 */
|
rt300@0
|
379 a0 = wavetableOld[n3] - wavetableOld[n2] - wavetableOld[n0] + wavetableOld[n1];
|
rt300@0
|
380 a1 = wavetableOld[n0] - wavetableOld[n1] - a0; // y0 - y1 - a0;
|
rt300@0
|
381 a2 = wavetableOld[n2] - wavetableOld[n0]; // y2 - y0;
|
rt300@0
|
382 a3 = wavetableOld[n1];
|
rt300@0
|
383
|
rt300@0
|
384 double oldsample = a0*fracCubed + a1*fracSquared + a2*frac + a3;
|
rt300@0
|
385
|
rt300@0
|
386 a0 = wavetableNew[n3] - wavetableNew[n2] - wavetableNew[n0] + wavetableNew[n1];
|
rt300@0
|
387 a1 = wavetableNew[n0] - wavetableNew[n1] - a0; // y0 - y1 - a0;
|
rt300@0
|
388 a2 = wavetableNew[n2] - wavetableNew[n0]; // y2 - y0;
|
rt300@0
|
389 a3 = wavetableNew[n1];
|
rt300@0
|
390
|
rt300@0
|
391 double newsample = a0*fracCubed + a1*fracSquared + a2*frac + a3;
|
rt300@0
|
392
|
rt300@0
|
393 frameInterpolator += framesPerSample;
|
rt300@0
|
394 if(frameInterpolator >= 1.0){
|
rt300@0
|
395 frameInterpolator = 1.0; // just stays outputting new
|
rt300@0
|
396 }
|
rt300@0
|
397
|
rt300@0
|
398 double sample = (1 - frameInterpolator)*oldsample + frameInterpolator*newsample;
|
rt300@0
|
399 //cout << sample << endl;
|
rt300@0
|
400 // keep within the bounds of acceptability
|
rt300@0
|
401
|
rt300@0
|
402 // beef up
|
rt300@13
|
403 sample = sample*10.0;
|
rt300@0
|
404
|
rt300@0
|
405 if(sample > 0.99){
|
rt300@13
|
406 cout << "BIG" << endl;
|
rt300@13
|
407
|
rt300@0
|
408 sample = 0.99;
|
rt300@13
|
409 }else if(sample < -0.0){
|
rt300@13
|
410 cout << "SMALL" << endl;
|
rt300@13
|
411 sample = 0.0;
|
rt300@0
|
412 }
|
rt300@0
|
413
|
rt300@0
|
414 return sample;
|
rt300@0
|
415
|
rt300@0
|
416 }
|
rt300@8
|
417 //----------------------------------------------------------------
|
rt300@8
|
418
|
rt300@8
|
419 Json::Value ScanPath::convertToJsonForSaving(){
|
rt300@8
|
420 Json::Value jscanpath;
|
rt300@9
|
421
|
rt300@9
|
422 for(int i=0;i<pathElements.size();i++){
|
rt300@9
|
423 //scanPath.addElement(&lumps[scanPathElements[i]["lumpNo"].asInt()], &springs[scanPathElements[i]["springNo"].asInt()]);
|
rt300@9
|
424 jscanpath[i]["lumpNo"] = pathElements[i].eLump->myIndexInMesh;
|
rt300@9
|
425 jscanpath[i]["springNo"] = pathElements[i].eSpring->myIndexInMesh;
|
rt300@9
|
426 }
|
rt300@9
|
427
|
rt300@8
|
428 return jscanpath;
|
rt300@8
|
429 }
|
rt300@8
|
430
|
rt300@8
|
431 //----------------------------------------------------------------
|
rt300@8
|
432
|
rt300@8
|
433 //----------------------------------------------------------------
|
rt300@8
|
434
|
rt300@8
|
435 //----------------------------------------------------------------
|
rt300@8
|
436
|