comparison DrumTimingLoader_OF/src/RecordedMultipleAudio.cpp @ 0:82352cfc0b23

Added files from ISMIR groove drum timing work
author Andrew N Robertson <andrew.robertson@eecs.qmul.ac.uk>
date Mon, 01 Oct 2012 22:24:32 +0100
parents
children 50ba55abea8c
comparison
equal deleted inserted replaced
-1:000000000000 0:82352cfc0b23
1 /*
2 * RecordedMultipleAudio.cpp
3 * MultipleAudioMathcher
4 *
5 * Created by Andrew on 31/01/2012.
6 * Copyright 2012 QMUL. All rights reserved.
7 *
8 */
9
10 #include "RecordedMultipleAudio.h"
11
12 RecordedMultipleAudio::RecordedMultipleAudio(){
13
14 infoFilepath = "../../../data/errorData.txt";
15 //infoFilepath = "/Users/andrew/errorData.txt";
16
17 timingOffset = 0;
18 }
19
20 void RecordedMultipleAudio::loadTestAudio(){
21
22
23 numberOfAudioTracks = 2;
24
25 printf("loaded max val is %f\n", loadedAudioFiles[0].fileLoader.onsetDetect.onsetDetector.maximumDetectionValue);
26
27 int multitrackToLoad = 5;
28 setDifferentMultitracks(multitrackToLoad);//command to load this set of audio files - see below
29
30 drumTimingAnalyser.phaseCost = 1000;//v high - i.e. dont do phase
31
32 drawWindow = 1;
33 trackScreenHeight = 0.25;
34
35
36
37 // printf("AFTER LOADING: \n");
38 // printInfo();
39
40 }
41 #pragma mark -loadingPrerecordedTracks
42 void RecordedMultipleAudio::setDifferentMultitracks(const int& setToLoad){
43 const char *kickfilename ;//= "../../../data/sound/LiveDues/kick_liveDues.wav";
44 const char *roomfilename ;//"../../../data/sound/LiveDues/bass_upsideLive.wav";
45 const char *snarefilename ;
46 std::string sonicVizBeatsFilename ;
47
48 switch (setToLoad) {
49 case 0:
50 kickfilename = "/Users/andrew/Documents/work/programming/MadMax/AudioFiles/futureHides/kickFuture.wav";
51 roomfilename = "/Users/andrew/Documents/work/programming/MadMax/AudioFiles/futureHides/roomFuture.wav";
52 snarefilename = "/Users/andrew/Documents/work/programming/MadMax/AudioFiles/futureHides/snareFuture.wav";
53 sonicVizBeatsFilename = "/Users/andrew/Documents/work/programming/MadMax/AudioFiles/futureHides/FutureHidesBeats.txt";
54 break;
55
56
57 case 1:
58 roomfilename = "/Volumes/Supersaurus/TractorsAlbum/tractorsDemo/Bounces/PennyArcadeStudio14aMultitrack/Mixdown/PennyArcade_StudioMixdown.wav";
59 kickfilename = "/Volumes/Supersaurus/TractorsAlbum/tractorsDemo/Bounces/PennyArcadeStudio14aMultitrack/kick.wav";
60 snarefilename = "/Volumes/Supersaurus/TractorsAlbum/tractorsDemo/Bounces/PennyArcadeStudio14aMultitrack/snare.wav";
61 sonicVizBeatsFilename = "/Volumes/Supersaurus/TractorsAlbum/tractorsDemo/Bounces/PennyArcadeStudio14aMultitrack/Mixdown/PennyArcade_StudioMixdown_beats.txt";
62 break;
63
64 case 2:
65 roomfilename = "/Users/andrew/Documents/work/programming/MadMax/AudioFiles/MattIngramGreenSection/colesL_bip.wav";
66 kickfilename = "/Users/andrew/Documents/work/programming/MadMax/AudioFiles/MattIngramGreenSection/Kick_bip.wav";
67 snarefilename = "/Users/andrew/Documents/work/programming/MadMax/AudioFiles/MattIngramGreenSection/Snare_bip.wav";
68 sonicVizBeatsFilename = "/Users/andrew/Documents/work/programming/MadMax/AudioFiles/MattIngramGreenSection/IngramGreenSectionBeats.txt";
69 break;
70
71 case 3:
72 roomfilename = "/Volumes/Supersaurus/TractorsAlbum/tractorsDiamondWhite/tractorsDiamondWhite/Bounces/diamondWhiteMultiTakeOne/coles_bip.wav";
73 kickfilename = "/Volumes/Supersaurus/TractorsAlbum/tractorsDiamondWhite/tractorsDiamondWhite/Bounces/diamondWhiteMultiTakeOne/kick d112_bip.wav";
74 snarefilename = "/Volumes/Supersaurus/TractorsAlbum/tractorsDiamondWhite/tractorsDiamondWhite/Bounces/diamondWhiteMultiTakeOne/snare bottom_bip.wav";
75 sonicVizBeatsFilename = "/Volumes/Supersaurus/TractorsAlbum/tractorsDiamondWhite/tractorsDiamondWhite/Bounces/diamondWhiteMultiTakeOne/TakeOneBeats.txt";
76 break;
77
78 case 4:
79 roomfilename = "/Volumes/Supersaurus/TractorsAlbum/tractorsDemo/Bounces/PennyArcade_Multitracks/TakeOne_4/neuamnn_bip.wav";
80 kickfilename = "/Volumes/Supersaurus/TractorsAlbum/tractorsDemo/Bounces/PennyArcade_Multitracks/TakeOne_4/kick_bip.wav";
81 snarefilename = "/Volumes/Supersaurus/TractorsAlbum/tractorsDemo/Bounces/PennyArcade_Multitracks/TakeOne_4/snare_bip.wav";
82 sonicVizBeatsFilename = "/Volumes/Supersaurus/TractorsAlbum/tractorsDemo/Bounces/PennyArcade_Multitracks/TakeOne_4/PennyArcade_take4_beats.txt";
83 break;
84
85 case 5:
86 roomfilename = "/Volumes/Supersaurus/TractorsAlbum/tractorsDemo/Bounces/PennyArcade_Multitracks/TakeTwo_5/neuamnn_bip.wav";
87 kickfilename = "/Volumes/Supersaurus/TractorsAlbum/tractorsDemo/Bounces/PennyArcade_Multitracks/TakeTwo_5/kick_bip.wav";
88 snarefilename = "/Volumes/Supersaurus/TractorsAlbum/tractorsDemo/Bounces/PennyArcade_Multitracks/TakeTwo_5/snare_bip.wav";
89 sonicVizBeatsFilename = "/Volumes/Supersaurus/TractorsAlbum/tractorsDemo/Bounces/PennyArcade_Multitracks/TakeTwo_5/PennyArcade_take5_beats.txt";
90 break;
91
92
93
94 }
95 if (kickfilename != NULL){
96
97 loadAudioTrack(kickfilename, 0);
98 }
99
100 if (roomfilename != NULL){
101 printf("roomfilename: %s\n", roomfilename);
102 loadAudioTrack(roomfilename, 1);
103 }
104
105 if (snarefilename != NULL)
106 loadAudioTrack(snarefilename, 2);
107
108 if (sonicVizBeatsFilename.c_str() != NULL){
109 readInBeatsFile(sonicVizBeatsFilename);
110 printBeatTimes();
111 checkFileErrors(0);
112 checkFileErrors(2);
113 findBeatOnsets();
114 }
115 }
116
117 void RecordedMultipleAudio::loadAudioTrack(std::string name, const int& channel){
118 //kick - track type 0
119 //bass - type 1
120 //snare type 2
121 //guitar type 3
122 if (channel >= 0 && channel <= numberOfAudioTracks){
123 loadedAudioPtr = new LoadedAudioHolder;
124 //set tracktype before we do analysis
125 //so we dont do unnecessary chroma and pitch calculations
126 if (channel == 0 || channel == 2){
127 loadedAudioPtr->setTrackType(channel);
128 }
129 else{
130 loadedAudioPtr->setTrackType(0);
131 }
132 loadedAudioPtr->loadAudioFile(name);
133
134 loadedAudioFiles[channel] = *loadedAudioPtr;
135 loadedAudioFiles[channel].fileLoader.onsetDetect.window.setToRelativeSize(0, trackScreenHeight*channel, 1, trackScreenHeight);
136 //loadedAudioFiles[channel].setTrackType(channel);
137 }
138 }
139
140
141
142 void RecordedMultipleAudio::readInBeatsFile(std::string& pathName){
143
144 // "/Users/andrew/Documents/work/MuseScore/RWC/ANNOTATION/RM-C002_annotation+WavPos.csv"
145 beatTimes.clear();
146
147 printf("- - - - \n\nREAD FILE %s\n", pathName.c_str());
148 ifstream file ( pathName.c_str());
149 string value, tmpLine;
150 stringstream iss;
151 int count = 0;
152
153 while ( file.good() )
154 {
155 getline(file, tmpLine);
156 iss << tmpLine;
157 int lineCount = 0;
158 // printf("tmp line %s\n", tmpLine.c_str());
159 while(getline ( iss, value, '\t' )){ // read a string until next comma: http://www.cplusplus.com/reference/string/getline/
160 // cout << string( value, 1, value.length()-2 ); // display value removing the first and the last character from it
161 // printf("line:%s\n", value.c_str());
162 string::size_type start = value.find_first_not_of(" ,\t\v\n");
163
164 string part = value.substr(start, string::npos);
165
166 //printf("%s\n", firstpart.c_str());
167 if (lineCount == 0){
168 //printf("First part of line found '%s'\n", part.c_str());
169 double newBeatTime = atof(part.c_str());
170 beatTimes.push_back(newBeatTime);
171 }
172 lineCount++;
173
174 }//end while reading line
175 iss.clear();
176
177
178 }//end while
179
180 // printBeatTimes();
181 printf("There are %i BEAT annotations\n", (int)beatTimes.size());
182
183 }
184
185 void RecordedMultipleAudio::printBeatTimes(){
186 for (int i = 0;i < beatTimes.size();i++){
187 printf("Beat[%i] = %f\n", i, beatTimes[i]);
188 }
189 }
190
191
192 void RecordedMultipleAudio::checkFileErrors(int channel){
193 int beatIndex = 0;
194 int cutoff = 50;//ms width to check
195 for (int i = 0;i < loadedAudioFiles[channel].onsetTimesMillis.size();i++){
196 while (beatIndex < beatTimes.size() && 1000.0*beatTimes[beatIndex] < loadedAudioFiles[channel].onsetTimesMillis[i] - cutoff) {
197 beatIndex++;
198 }
199 double error = (1000.0*beatTimes[beatIndex] - loadedAudioFiles[channel].onsetTimesMillis[i]);
200 if (fabs(error) < cutoff){
201 if (channel == 0)
202 printf("Pos: %i Beat Time %f Kick Time %f Error %f\n", beatIndex%4, 1000.0*beatTimes[beatIndex], loadedAudioFiles[0].onsetTimesMillis[i], error);
203 else
204 printf("Pos: %i Beat Time %f Snare Time %f Error %f\n", beatIndex%4, 1000.0*beatTimes[beatIndex], loadedAudioFiles[0].onsetTimesMillis[i], error);
205
206 }else{
207 if (channel == 0)
208 printf("Out of Beat: Kick Time %f beat error %f\n", 1000.0*beatTimes[beatIndex], loadedAudioFiles[0].onsetTimesMillis[i], error);
209 else
210 printf("Out of Beat: %f Snare Time %f best error %f\n", 1000.0*beatTimes[beatIndex], loadedAudioFiles[0].onsetTimesMillis[i], error);
211
212 }
213 }
214 }
215
216
217 #pragma mark -labelExactOnsets
218
219 void RecordedMultipleAudio::findBeatOnsets(){
220 //tries to find kicks on 1, 3; snares on 2,4
221 int beatIndex = 0;
222 int kickIndex = 0;
223 int snareIndex = 0;
224 double kickTime, snareTime;
225 int cutoff = 50;//ms width to check
226
227 onsetInfo.clear();
228
229 // kickErrors.clear();
230 // snareErrors.clear();
231
232 bool beatFound;
233 for (int k = 0;k < beatTimes.size();k++){
234 beatFound = false;
235 double newBeatTime = beatTimes[k]*1000.0;
236 int beatPosition = k % 4;
237 OnsetInformation information;
238 switch (beatPosition) {
239 case 0: case 2://check for kick when it is on the `one' or 'three' (0 or 2 in our metrical position)
240 // printf("check %i kindex %i\n", beatPosition, kickIndex);
241 while (kickIndex < loadedAudioFiles[0].onsetTimesMillis.size() && loadedAudioFiles[0].onsetTimesMillis[kickIndex] < newBeatTime - cutoff){
242 kickIndex++;
243 kickTime = loadedAudioFiles[0].onsetTimesMillis[kickIndex];
244 // printf("checking beat[%i] %f kick %f error %f\n", k, beatTimes[k]*1000.0, kickTime, (kickTime - beatTimes[k]*1000.0));
245 if (fabs(kickTime - beatTimes[k]*1000.0) < cutoff){
246 beatFound = true;
247 printf("beat[%i] %f kick %f error %f\n", k, beatTimes[k]*1000.0, kickTime, (kickTime - beatTimes[k]*1000.0));
248
249 information.error = (kickTime - beatTimes[k]*1000.0);//FOR NOW ONLY
250 information.metricalPosition = beatPosition;
251 information.type = 0;
252 information.exactOnsetTime = kickTime;
253 // exactBeatPositions.push_back(kickTime);
254 }
255 }
256
257 break;
258 case 1: case 3://snare
259 while (snareIndex < loadedAudioFiles[1].onsetTimesMillis.size() && loadedAudioFiles[1].onsetTimesMillis[snareIndex] < newBeatTime - cutoff ){
260 snareIndex++;
261 snareTime = loadedAudioFiles[1].onsetTimesMillis[snareIndex];
262 if (fabs(snareTime - beatTimes[k]*1000.0) < cutoff){
263 beatFound = true;
264 // snareErrors.push_back((beatTimes[k]*1000.0 - snareTime));
265 information.error = (snareTime - beatTimes[k]*1000.0);
266 information.metricalPosition = beatPosition;//.push_back(beatPosition);
267 information.type = 1;
268 information.exactOnsetTime = snareTime;
269 printf("beat[%i] %f snare %f error %f\n", k, beatTimes[k]*1000.0, snareTime, (snareTime - beatTimes[k]*1000.0));
270 // exactBeatPositions.push_back(snareTime);
271 }
272 }
273
274 break;
275 }
276 if (!beatFound){
277 information.type = -1;//not a kick or snare
278 information.exactOnsetTime = beatTimes[k]*1000.0;
279 information.metricalPosition = beatPosition;//.push_
280 // exactBeatPositions.push_back(beatTimes[k]*1000.0);//have to go with the annotated beat instead (no matching kick or snare)
281
282 printf("beat[%i] %f NOT FOUND, kicktime %f snaretime %f\n", k, beatTimes[k]*1000.0, kickTime, snareTime );
283 }
284
285 onsetInfo.push_back(information);
286
287 }//end for all beat annotations
288
289 correctExactBeatTiming();//get rid of the first onset time
290
291 calculateTimingAnalysis();
292
293 }
294
295 #pragma mark -doTimingAnalysis
296 void RecordedMultipleAudio::correctExactBeatTiming(){
297 //get rid of firtst onset time
298 if (onsetInfo.size() > 0){
299 timingOffset = onsetInfo[0].exactOnsetTime;//s[0];
300 double tmpPosn;
301 for (int i = 0;i < onsetInfo.size();i++){
302 onsetInfo[i].beatTimeToProcess = onsetInfo[i].exactOnsetTime - timingOffset;
303 printf("exact [%i] type %i %f, corrected %f\n", i, onsetInfo[i].type, onsetInfo[i].exactOnsetTime, onsetInfo[i].beatTimeToProcess );
304 }
305 }
306 }
307
308 void RecordedMultipleAudio::calculateTimingAnalysis(){
309
310 for (int i = 0;i < onsetInfo.size();i++){
311 drumTimingAnalyser.updateCostToPoint(onsetInfo[i].beatTimeToProcess, i);
312 //updatecounter is the beat position for this note event - can be used to do other kinds of durations than just simple beats
313
314 drumTimingAnalyser.beatPosition.push_back(onsetInfo[i].exactOnsetTime);
315 }
316 drumTimingAnalyser.processPathHistory();
317 drumTimingAnalyser.calculateTempoLimits();
318
319 getErrorTimesFromAnalysis();
320
321 alternativeKickRelativeAnalysis();
322
323 exportErrorInformation();
324
325 displayKickRelativeMedianErrors();//NB messes ther order of these
326
327 //this was how we did it in multimatch program
328 // timer.processPathHistory();
329 // timer.calculateTempoLimits();
330 // timer.exportTimingData();
331 // timer.exportProcessedBeatTimes(firstNoteTime);
332 }
333
334 void RecordedMultipleAudio::getErrorTimesFromAnalysis(){
335 printf("\nDrumTimingLoader: get error times from analysis!!!\n");
336 setUpErrorsByMetricalPosition();
337 //gets the errors from the drum timing analyser (i.e. ISMIR paper code) and attributes these to the onsets
338 for (int i = 0;i < drumTimingAnalyser.timingData.size();i++){
339 onsetInfo[i].error = drumTimingAnalyser.timingData[i][5];
340 onsetInfo[i].clickTime = drumTimingAnalyser.timingData[i][1] + timingOffset;//this is where teh timing analyser placed the click times
341 errorsByMetricalPosition[onsetInfo[i].metricalPosition].push_back(onsetInfo[i].error);
342 printf("beat %i metrical posn %i exact beat time %f error %i\n", i, onsetInfo[i].metricalPosition, onsetInfo[i].exactOnsetTime, (int)onsetInfo[i].error);
343 }
344 displayMedianErrors();
345 }
346
347
348 void RecordedMultipleAudio::alternativeKickRelativeAnalysis(){
349 printf("\n\nAnalysis Relative to the Kick Drum on the ONE\n");
350 //this sees kicks as ON the beat, looks at relative error of the three other beats if we chop the bar evenly
351
352 double recentBeatTime, currentTempo;
353 kickRelativeErrors.clear();
354 kickRelativeClickTimes.clear();
355
356 for (int i = 0;i < drumTimingAnalyser.timingData.size();i++){
357 int beatPosition = i%4;
358 if (beatPosition == 0){
359 recentBeatTime = onsetInfo[i].exactOnsetTime;
360 if (i+4 < onsetInfo.size())
361 currentTempo = (onsetInfo[i+4].exactOnsetTime - onsetInfo[i].exactOnsetTime)/4.0;
362 printf("new beat time %f tempo %f\n", recentBeatTime, currentTempo);
363 }
364 double error = onsetInfo[i].exactOnsetTime - (recentBeatTime + beatPosition*currentTempo);
365 printf("Beat %i KR Predicted Beat %f Actual exact %f KRerror %f DTerror %i\n", beatPosition, (recentBeatTime + beatPosition*currentTempo), onsetInfo[i].exactOnsetTime, error, (int)onsetInfo[i].error);
366 kickRelativeErrors.push_back(error);
367 kickRelativeClickTimes.push_back(recentBeatTime + beatPosition*currentTempo);
368 }
369 }
370
371 #pragma label -exportInfo
372 void RecordedMultipleAudio::exportErrorInformation(){
373 printf("Export final timing information\n");
374
375 ofstream ofs(infoFilepath.c_str());
376 for (int i = 0;i < onsetInfo.size() && kickRelativeErrors.size();i++){// drumTimingAnalyser.timingData.size()
377 ofs << i << "," << (i%4) << "," << onsetInfo[i].exactOnsetTime << ",";
378 ofs << onsetInfo[i].clickTime << "," << onsetInfo[i].error << ",";//the error for the ISMIR timing analyser
379 ofs << kickRelativeClickTimes[i] << "," << kickRelativeErrors[i];//the click and error for beats evenly between kicks
380 ofs << endl;
381 }
382
383 }
384
385 void RecordedMultipleAudio::printKickRelativeErrors(){
386 for (int i = 0;i < kickRelativeErrors.size();i++){
387 printf("KR error [%i] : %.1f\n", i, kickRelativeErrors[i]);
388 }
389 }
390
391 void RecordedMultipleAudio::setUpErrorsByMetricalPosition(){
392 //clear this matrix
393 errorsByMetricalPosition.clear();
394 for (int i = 0;i < 4;i++){
395 DoubleVector v;
396 errorsByMetricalPosition.push_back(v);
397 }
398
399 }
400
401 void RecordedMultipleAudio::displayMedianErrors(){
402 printf("Medians of the Decoded Tempo variations\n");
403 for (int i = 0;i < 4;i++){
404 //printErrorsForMetricalPosition(i);
405 std::sort(errorsByMetricalPosition[i].begin(), errorsByMetricalPosition[i].end());//sort vector
406 double median = errorsByMetricalPosition[i][(int)(errorsByMetricalPosition[i].size()/2)];
407 printf("median for metrical position %i is %f\n", i, median);
408 }
409 }
410
411
412 void RecordedMultipleAudio::displayKickRelativeMedianErrors(){
413 printf("Medians of the KR variations\n");
414
415 DoubleVector tmpKRErrors;
416
417
418 for (int i = 0;i < 4;i++){
419
420 tmpKRErrors.clear();
421 int index = 0;
422
423 while (index+i < kickRelativeErrors.size()) {
424 tmpKRErrors.push_back(kickRelativeErrors[index + i]);
425 index += 4;
426 }
427
428 // for (int k = 0;k < tmpKRErrors.size();k++)
429 // printf("kr %i [%i] = %f\n", i, k, tmpKRErrors[k]);
430
431 //printErrorsForMetricalPosition(i);
432 std::sort(tmpKRErrors.begin(), tmpKRErrors.end());//sort vector
433
434 // for (int k = 0;k < tmpKRErrors.size();k++)
435 // printf("sorted kr %i [%i] = %f\n", i, k, tmpKRErrors[k]);
436
437
438 double median = tmpKRErrors[(int)(tmpKRErrors.size()/2)];
439 printf("median for metrical position %i is %f\n", i, median);
440 }
441 }
442
443 void RecordedMultipleAudio::printErrorsForMetricalPosition(const int& i){
444 for (int k = 0;k < errorsByMetricalPosition[i].size();k++){
445 printf("metrical posn %i, [%i] = %f\n", i, k, errorsByMetricalPosition[i][k]);
446 }
447 }
448
449 #pragma mark -drawTracks
450
451 void RecordedMultipleAudio::drawTracks(){
452 if (drawWindow == 0){
453 for (int i = 0;i < numberOfAudioTracks;i++){
454 loadedAudioFiles[i].draw();
455 }
456 } else {
457 drumTimingAnalyser.drawTempoCurve();
458 }
459 }
460
461 #pragma mark -update
462 void RecordedMultipleAudio::updatePosition(){
463 for (int i = 0;i < numberOfAudioTracks;i++)
464 loadedAudioFiles[i].updateToPlayPosition();
465 }
466
467 void RecordedMultipleAudio::updatePositionToMillis(const double& millis){
468 for (int i = 0;i < numberOfAudioTracks;i++)
469 loadedAudioFiles[i].updateToMillisPosition(millis);
470 }
471
472 void RecordedMultipleAudio::updatePlaybackPositionToMillis(const double& millis){
473 for (int i = 0;i < numberOfAudioTracks;i++)
474 loadedAudioFiles[i].updatePlaybackPositionToMillis(millis);
475 }
476
477 void RecordedMultipleAudio::switchScreens(){
478 for (int i = 0;i < numberOfAudioTracks;i++)
479 loadedAudioFiles[i].switchScreens();
480 }
481
482
483 void RecordedMultipleAudio::togglePlay(){
484 for (int i = 0;i < numberOfAudioTracks;i++)
485 loadedAudioFiles[i].togglePlay();
486 }
487
488 void RecordedMultipleAudio::stop(){
489 for (int i = 0;i < numberOfAudioTracks;i++)
490 loadedAudioFiles[i].stop();
491 }
492
493
494 void RecordedMultipleAudio::printInfo(){
495 loadedAudioFiles[0].fileLoader.onsetDetect.printChromaInfo();
496 loadedAudioFiles[0].printEvents();
497 }
498
499 void RecordedMultipleAudio::windowResized(const int& w, const int& h){
500 for (int i = 0;i < numberOfAudioTracks;i++)
501 loadedAudioFiles[i].windowResized(w, h);
502 }
503
504 void RecordedMultipleAudio::zoomIn(){
505 if (drawWindow == 0){
506 printf("zoom in\n");
507 for (int i = 0;i < numberOfAudioTracks;i++)
508 loadedAudioFiles[i].fileLoader.zoomIn();
509 }
510
511 if (drawWindow == 1)
512 drumTimingAnalyser.zoomIn();//numberOfPointsPerPage /= 2;
513 }
514
515 void RecordedMultipleAudio::zoomOut(){
516 printf("zoom out\n");
517 for (int i = 0;i < numberOfAudioTracks;i++)
518 loadedAudioFiles[i].fileLoader.zoomOut();
519
520 if (drawWindow == 1)
521 drumTimingAnalyser.zoomOut();//numberOfPointsPerPage *= 2;
522
523 }
524
525