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@0
|
83 ofSetColor(0, 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@0
|
159 void ScanPath::updateWavetables(){
|
rt300@0
|
160 // swap old , new
|
rt300@0
|
161 double * temp;
|
rt300@0
|
162
|
rt300@1
|
163 // TODO THRED MUTEX HERE!??
|
rt300@3
|
164 // this is called from graphics thread
|
rt300@3
|
165 // reset the interp between frames
|
rt300@4
|
166
|
rt300@3
|
167 int i = 0;
|
rt300@3
|
168 while(audioAccessing){
|
rt300@3
|
169 i++;
|
rt300@3
|
170 }
|
rt300@4
|
171 if(i > 0){
|
rt300@4
|
172 cout << "Update wavetables had to wait for audio access " << i << " times\n";
|
rt300@4
|
173 // hardly ever happens
|
rt300@4
|
174 }
|
rt300@3
|
175 updateAccessing = true;
|
rt300@3
|
176
|
rt300@4
|
177
|
rt300@0
|
178 switch(scanMode){
|
rt300@0
|
179 case DISPLACEMENT:
|
rt300@0
|
180 // now fill with new values
|
rt300@0
|
181 for(int i = 0; i < numElements; i++){
|
rt300@3
|
182 // double check
|
rt300@3
|
183 if(pathElements[i].eLump->isInScanPath){
|
rt300@3
|
184 wavetableUpdate[i] = pathElements[i].eLump->scanRadialDisplacement()/1.5;
|
rt300@3
|
185 }
|
rt300@0
|
186
|
rt300@0
|
187 }
|
rt300@0
|
188 break;
|
rt300@0
|
189 case SPEED:
|
rt300@0
|
190 for(int i = 0; i < numElements; i++){
|
rt300@3
|
191 if(pathElements[i].eLump->isInScanPath){
|
rt300@3
|
192 wavetableUpdate[i] = pathElements[i].eLump->scanLumpSpeed();
|
rt300@3
|
193 }
|
rt300@0
|
194 }
|
rt300@0
|
195 break;
|
rt300@0
|
196 case SPRING_FORCE:
|
rt300@0
|
197 for(int i = 0; i < numElements; i++){
|
rt300@3
|
198 if(pathElements[i].eSpring->isInScanPath){
|
rt300@3
|
199 wavetableUpdate[i] = pathElements[i].eSpring->getForceMag();
|
rt300@3
|
200 }
|
rt300@0
|
201 }
|
rt300@0
|
202 break;
|
rt300@0
|
203 case YPOS:
|
rt300@0
|
204 for(int i = 0; i < numElements; i++){
|
rt300@3
|
205 if(pathElements[i].eLump->isInScanPath){
|
rt300@3
|
206 wavetableUpdate[i] = pathElements[i].eLump->scanYPos();
|
rt300@3
|
207 }
|
rt300@0
|
208 }
|
rt300@0
|
209 break;
|
rt300@0
|
210 default:
|
rt300@0
|
211 break;
|
rt300@4
|
212
|
rt300@0
|
213 }
|
rt300@4
|
214
|
rt300@0
|
215 temp = wavetableOld;
|
rt300@0
|
216 wavetableOld = wavetableNew;
|
rt300@0
|
217 wavetableNew = wavetableUpdate;
|
rt300@0
|
218 wavetableUpdate = temp;
|
rt300@3
|
219
|
rt300@3
|
220 // END THREAD MUTEX
|
rt300@0
|
221 updateAccessing = false;
|
rt300@0
|
222
|
rt300@0
|
223 frameInterpolator = 0.0;
|
rt300@0
|
224 framesPerSample = 2.0*ofGetFrameRate()/SAMPLE_RATE; // attempt to get a reasonable est. of how fast to interp
|
rt300@0
|
225
|
rt300@0
|
226 }
|
rt300@0
|
227 //----------------------------------------------------------------
|
rt300@3
|
228 //----------------------------------------------------------------
|
rt300@3
|
229 // AUDIO THREAD STUFF
|
rt300@3
|
230 //----------------------------------------------------------------
|
rt300@0
|
231 // get next sample
|
rt300@3
|
232
|
rt300@0
|
233 double ScanPath::getNextSample(double aPhasor){
|
rt300@0
|
234 // move along path, interpolating between points
|
rt300@0
|
235 // move between frames too
|
rt300@0
|
236 double alongPath = aPhasor*double(numElements);
|
rt300@0
|
237
|
rt300@0
|
238 // indexes for interpolated points
|
rt300@0
|
239 int n0 = floor(alongPath);
|
rt300@0
|
240 int n1 = n0+1;
|
rt300@0
|
241 if(n1 >= numElements){
|
rt300@0
|
242 n1 = 0;
|
rt300@0
|
243 }
|
rt300@0
|
244
|
rt300@0
|
245 double frac = alongPath - double(n0);
|
rt300@0
|
246
|
rt300@3
|
247 // TODO THRED MUTEX HERE!??
|
rt300@3
|
248 // this is called from audio thread
|
rt300@4
|
249
|
rt300@3
|
250 int i = 0;
|
rt300@3
|
251 while(updateAccessing){
|
rt300@3
|
252 i++;
|
rt300@3
|
253 }
|
rt300@0
|
254 audioAccessing = true;
|
rt300@4
|
255 if(i>0) cout << "Audio thread had to wait for wavetable update " << i << " times\n";
|
rt300@4
|
256
|
rt300@1
|
257
|
rt300@0
|
258 double oldsample = (1 - frac) * wavetableOld[n0] + frac * wavetableOld[n1];
|
rt300@0
|
259
|
rt300@0
|
260 double newsample = (1 - frac) * wavetableNew[n0] + frac * wavetableNew[n1];
|
rt300@0
|
261
|
rt300@1
|
262 // END THREAD MUTEX
|
rt300@0
|
263 audioAccessing = false;
|
rt300@0
|
264
|
rt300@0
|
265 frameInterpolator += framesPerSample;
|
rt300@0
|
266 if(frameInterpolator >= 1.0){
|
rt300@0
|
267 //cout << "frame interp > 1\n";
|
rt300@0
|
268 frameInterpolator = 1.0; // just stays outputting new
|
rt300@0
|
269 }
|
rt300@0
|
270
|
rt300@0
|
271 double sample = (1 - frameInterpolator)*oldsample + frameInterpolator*newsample;
|
rt300@0
|
272 //cout << sample << endl;
|
rt300@0
|
273 // keep within the bounds of acceptability
|
rt300@0
|
274 if(sample > 0.99){
|
rt300@0
|
275 // cout << "OUCH\n";
|
rt300@0
|
276 sample = 0.99;
|
rt300@0
|
277 }else if(sample < -0.99){
|
rt300@0
|
278 sample = -0.99;
|
rt300@0
|
279 }
|
rt300@0
|
280 return sample;
|
rt300@0
|
281
|
rt300@0
|
282 }
|
rt300@0
|
283 //----------------------------------------------------------------
|
rt300@0
|
284 // get next sample
|
rt300@0
|
285 double ScanPath::getNextSample(){
|
rt300@0
|
286 // move along wavetable, no interpolation ie: length of path is pitch
|
rt300@0
|
287 static int n = 0;
|
rt300@0
|
288 double oldsample = wavetableOld[n];
|
rt300@0
|
289
|
rt300@0
|
290 double newsample = wavetableNew[n];
|
rt300@0
|
291 n++;
|
rt300@0
|
292 if (n >= numElements){
|
rt300@0
|
293 n = 0;
|
rt300@0
|
294 }
|
rt300@0
|
295
|
rt300@0
|
296 frameInterpolator += framesPerSample;
|
rt300@0
|
297 if(frameInterpolator >= 1.0){
|
rt300@0
|
298 //cout << "frame interp > 1\n";
|
rt300@0
|
299 frameInterpolator = 1.0; // just stays outputting new
|
rt300@0
|
300 }
|
rt300@0
|
301
|
rt300@0
|
302 double sample = (1 - frameInterpolator)*oldsample + frameInterpolator*newsample;
|
rt300@0
|
303 return sample*3.0;
|
rt300@0
|
304
|
rt300@0
|
305 }
|
rt300@0
|
306 //----------------------------------------------------------------
|
rt300@0
|
307 //----------------------------------------------------------------
|
rt300@0
|
308 // get next sample with cubic interpolation
|
rt300@0
|
309 double ScanPath::getNextSampleCubic(double aPhasor){
|
rt300@0
|
310 // move along path, interpolating between points
|
rt300@0
|
311 // move between frames too
|
rt300@0
|
312 double alongPath = aPhasor*double(numElements);
|
rt300@0
|
313
|
rt300@0
|
314 // indexes for interpolated points
|
rt300@0
|
315 int n1 = floor(alongPath);
|
rt300@0
|
316
|
rt300@0
|
317 int n0 = n1-1;
|
rt300@0
|
318 if(n0 < 0){
|
rt300@0
|
319 n0 = numElements;
|
rt300@0
|
320 }
|
rt300@0
|
321 int n2 = n1+1;
|
rt300@0
|
322 if(n2 >= numElements){
|
rt300@0
|
323 n2 = 0;
|
rt300@0
|
324 }
|
rt300@0
|
325 int n3 = n2+1;
|
rt300@0
|
326 if(n3 >= numElements){
|
rt300@0
|
327 n3 = 0;
|
rt300@0
|
328 }
|
rt300@0
|
329 double frac = alongPath - double(n0);
|
rt300@0
|
330 double fracSquared = frac * frac;
|
rt300@0
|
331 double fracCubed = fracSquared * frac;
|
rt300@0
|
332 double a0,a1,a2,a3;
|
rt300@0
|
333 //cout << n0 << endl;
|
rt300@0
|
334 // cubic interp
|
rt300@0
|
335 /*
|
rt300@0
|
336 double y0,double y1,
|
rt300@0
|
337 double y2,double y3,
|
rt300@0
|
338 double mu)
|
rt300@0
|
339 {
|
rt300@0
|
340 double a0,a1,a2,a3,mu2;
|
rt300@0
|
341
|
rt300@0
|
342 mu2 = mu*mu;
|
rt300@0
|
343 a0 = y3 - y2 - y0 + y1;
|
rt300@0
|
344 a1 = y0 - y1 - a0;
|
rt300@0
|
345 a2 = y2 - y0;
|
rt300@0
|
346 a3 = y1;
|
rt300@0
|
347
|
rt300@0
|
348 return(a0*mu*mu2+a1*mu2+a2*mu+a3);
|
rt300@0
|
349 */
|
rt300@0
|
350 a0 = wavetableOld[n3] - wavetableOld[n2] - wavetableOld[n0] + wavetableOld[n1];
|
rt300@0
|
351 a1 = wavetableOld[n0] - wavetableOld[n1] - a0; // y0 - y1 - a0;
|
rt300@0
|
352 a2 = wavetableOld[n2] - wavetableOld[n0]; // y2 - y0;
|
rt300@0
|
353 a3 = wavetableOld[n1];
|
rt300@0
|
354
|
rt300@0
|
355 double oldsample = a0*fracCubed + a1*fracSquared + a2*frac + a3;
|
rt300@0
|
356
|
rt300@0
|
357 a0 = wavetableNew[n3] - wavetableNew[n2] - wavetableNew[n0] + wavetableNew[n1];
|
rt300@0
|
358 a1 = wavetableNew[n0] - wavetableNew[n1] - a0; // y0 - y1 - a0;
|
rt300@0
|
359 a2 = wavetableNew[n2] - wavetableNew[n0]; // y2 - y0;
|
rt300@0
|
360 a3 = wavetableNew[n1];
|
rt300@0
|
361
|
rt300@0
|
362 double newsample = a0*fracCubed + a1*fracSquared + a2*frac + a3;
|
rt300@0
|
363
|
rt300@0
|
364 frameInterpolator += framesPerSample;
|
rt300@0
|
365 if(frameInterpolator >= 1.0){
|
rt300@0
|
366 frameInterpolator = 1.0; // just stays outputting new
|
rt300@0
|
367 }
|
rt300@0
|
368
|
rt300@0
|
369 double sample = (1 - frameInterpolator)*oldsample + frameInterpolator*newsample;
|
rt300@0
|
370 //cout << sample << endl;
|
rt300@0
|
371 // keep within the bounds of acceptability
|
rt300@0
|
372
|
rt300@0
|
373 // beef up
|
rt300@0
|
374 sample = sample*3.0;
|
rt300@0
|
375
|
rt300@0
|
376 if(sample > 0.99){
|
rt300@0
|
377 sample = 0.99;
|
rt300@0
|
378 }else if(sample < -0.99){
|
rt300@0
|
379 sample = -0.99;
|
rt300@0
|
380 }
|
rt300@0
|
381
|
rt300@0
|
382 return sample;
|
rt300@0
|
383
|
rt300@0
|
384 }
|
rt300@0
|
385 //---------------------------------------------------------------- |