comparison examples/10-Instruments/d-box/main.cpp @ 464:8fcfbfb32aa0 prerelease

Examples reorder with subdirectories. Added header to each project. Moved Doxygen to bottom of render.cpp.
author Robert Jack <robert.h.jack@gmail.com>
date Mon, 20 Jun 2016 16:20:38 +0100
parents
children
comparison
equal deleted inserted replaced
463:c47709e8b5c9 464:8fcfbfb32aa0
1 /*
2 * RTAudio.cpp
3 *
4 * Central control code for hard real-time audio on BeagleBone Black
5 * using PRU and Xenomai Linux extensions. This code began as part
6 * of the Hackable Instruments project (EPSRC) at Queen Mary University
7 * of London, 2013-14.
8 *
9 * (c) 2014 Victor Zappi and Andrew McPherson
10 * Queen Mary University of London
11 */
12
13
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <strings.h>
18 #include <math.h>
19 #include <iostream>
20 #include <signal.h> // interrupt handler
21 #include <assert.h>
22 #include <vector>
23 #include <dirent.h> // to handle files in dirs
24 #include <mntent.h> // to check if device is mounted
25 #include <sys/mount.h> // mount()
26 #include <sys/time.h> // elapsed time
27 #include <ne10/NE10.h> // neon library
28
29 // thread priority
30 #include <pthread.h>
31 #include <sched.h>
32
33 // get_opt_long
34 #include <getopt.h>
35
36 #include <Bela.h>
37 #include "config.h"
38 #include "sensors.h"
39 #include "OscillatorBank.h"
40 #include "StatusLED.h"
41 #include "logger.h"
42
43 using namespace std;
44
45 //----------------------------------------
46 // main variables
47 //----------------------------------------
48 vector<OscillatorBank*> gOscBanks;
49 int gCurrentOscBank = 0;
50 int gNextOscBank = 0;
51 int oscBnkOversampling = 1; // oscillator bank frame oversampling
52
53 const int kStatusLEDPin = 30; // P9-11 controls status LED
54 StatusLED gStatusLED;
55
56 pthread_t keyboardThread;
57 pthread_t logThread;
58
59 // general settings
60 int gVerbose = 0; // verbose flag
61 bool forceKeyboard = true; // activate/deactivate keyboard control
62 bool forceSensors = false; // activate/deactivate sensor control
63 bool forceLog = true; // activate/deactivate log on boot partition
64 bool useSD = true; // activate/deactivate file loading from SD [as opposed to emmc]
65 bool useAudioTest = false; // activate/deactivate sensors and test audio only
66
67 // audio settings
68 unsigned int gPeriodSize = 8; // period size for audio
69 char* gPartialFilename = 0; // name of the partials file to load
70 bool gAudioIn = false; // stereo audio in status
71
72 int touchSensor0Address = 0x0C; // I2C addresses of touch sensors
73 int touchSensor1Address = 0x0B;
74 int sensorType = 0;
75
76 char sdPath[256] = "/dev/mmcblk0p2"; // system path of the SD, partition 2
77 char mountPath[256] = "/root/d-box/usersounds"; // mount point of SD partition 2 [where user files are]
78 char gUserDirName[256] = "usersounds"; // Directory in which user analysis files can be found [dir of mountPath]
79 char gDefaultDirName[256] = "/root/d-box/sounds"; // Directory in which built in analysis files can be found
80 char *gDirName;
81 bool gIsLoading = false;
82 int fileCnt = 0;
83 std::vector <std::string> files;
84
85 char gId = 'f'; // from 0 to 14, hexadecimal [0-d]! f means not set
86 char gGroup = '2'; // 0 is no info, 1 info. 2 is not set
87
88 // audio in filter
89 extern ne10_float32_t *filterState[2];
90 extern ne10_float32_t *filterIn[2];
91 extern ne10_float32_t *filterOut[2];
92
93 struct arg_data
94 {
95 int argc;
96 char **argv;
97 };
98
99 arg_data args;
100
101
102 int readFiles()
103 {
104 if(useSD)
105 gDirName = gUserDirName;
106 else
107 gDirName = gDefaultDirName;
108 DIR *dir;
109 struct dirent *ent;
110
111 // From http://stackoverflow.com/questions/612097/how-can-i-get-a-list-of-files-in-a-directory-using-c-or-c
112 if ((dir = opendir (gDirName)) != NULL) {
113 /* print all the files and directories within directory */
114 while ((ent = readdir (dir)) != NULL) {
115 // Ignore dotfiles and . and .. paths
116 if(!strncmp(ent->d_name, ".", 1))
117 continue;
118
119 //dbox_printf("%s\n", ent->d_name);
120
121 // take only .dbx and .txt files
122 string name = string(ent->d_name);
123 int len = name.length();
124
125 bool dboxFile = false;
126
127 if( (name[len-4]=='.') && (name[len-3]=='d') && (name[len-2]=='b') && (name[len-1]=='x') )
128 dboxFile = true;
129 if( (name[len-4]=='.') && (name[len-3]=='t') && (name[len-2]=='x') && (name[len-1]=='t') )
130 dboxFile = true;
131
132 if(dboxFile)
133 {
134 fileCnt++;
135 //dbox_printf("%s\n", ent->d_name);
136 files.push_back( std::string( ent->d_name ) );
137 }
138 }
139 closedir (dir);
140 } else {
141 /* could not open directory */
142 printf("Could not open directory %s\n", gDirName);
143 return 1;
144 }
145
146 // order by name
147 std::sort( files.begin(), files.end() );
148
149 if(fileCnt==0)
150 {
151 printf("No .dbx or .txt files in %s!\n", gDirName);
152 return 1;
153 }
154
155 return 0;
156 }
157
158 /* Load sounds from the directory */
159 void loadAudioFiles(bool loadFirstFile)
160 {
161 char fullFileName[256];
162
163 if(loadFirstFile) {
164 strcpy(fullFileName, gDirName);
165 strcat(fullFileName, "/");
166 strncat(fullFileName, files[0].c_str(), 255 - strlen(gDirName));
167 dbox_printf("Loading first file %s...\n", fullFileName);
168 OscillatorBank *bank = new OscillatorBank(fullFileName);
169 if(bank->initBank(oscBnkOversampling)) {
170 bank->setLoopHops(100, bank->getLastHop());
171 gOscBanks.push_back(bank);
172 }
173 }
174
175 else {
176 for(int i=1; i<fileCnt; i++){
177 strcpy(fullFileName, gDirName);
178 strcat(fullFileName, "/");
179 strncat(fullFileName, files[i].c_str(), 255 - strlen(gDirName));
180 dbox_printf("Loading file %s...\n", fullFileName);
181 OscillatorBank *bank = new OscillatorBank(fullFileName);
182 if(bank->initBank(oscBnkOversampling)) {
183 bank->setLoopHops(100, bank->getLastHop());
184 gOscBanks.push_back(bank);
185 }
186 }
187 }
188 }
189
190 // adapted from http://program-nix.blogspot.co.uk/2008/08/c-language-check-filesystem-is-mounted.html
191 int checkIfMounted (char * dev_path)
192 {
193 FILE * mtab = NULL;
194 struct mntent * part = NULL;
195 int is_mounted = 0;
196
197 if ( ( mtab = setmntent ("/etc/mtab", "r") ) != NULL)
198 {
199 while ( ( part = getmntent ( mtab) ) != NULL)
200 {
201 if ( ( part->mnt_fsname != NULL ) && ( strcmp ( part->mnt_fsname, dev_path ) ) == 0 )
202 is_mounted = 1;
203 }
204 endmntent(mtab);
205 }
206 return is_mounted;
207 }
208
209 int mountSDuserPartition()
210 {
211 if(checkIfMounted(sdPath))
212 {
213 printf("device %s already mounted, fair enough, let's move on\n", sdPath);
214 return 0;
215 }
216 // if mount rootfs from SD [rootfs eMMC not used] or from eMMC via properly formatted SD [SD rootfs used as storage volume]
217 // we always use rootfs on SD as storage volume ----> "/dev/mmcblk0p2"
218 int ret = mount(sdPath, "/root/d-box/usersounds", "vfat", 0, NULL);
219 if (ret!=0)
220 {
221 printf("Error in mount...%s\n", strerror(ret));
222 return 1;
223 }
224 return 0;
225 }
226
227 int initSoundFiles()
228 {
229 if(gVerbose==1)
230 cout << "---------------->Init Audio Thread" << endl;
231
232 if(useSD)
233 {
234 // mount the SD partition where user sounds are located
235 // [this is p2, p1 is already mounted and we will log data there]
236 if(mountSDuserPartition()!=0)
237 return -1;
238 }
239
240 gIsLoading = true;
241
242 // read files from SD and order them alphabetically
243 if(readFiles()!=0)
244 return 1;
245
246 // load first file into oscBank
247 loadAudioFiles(true);
248
249 return 0;
250 }
251
252 //---------------------------------------------------------------------------------------------------------
253
254 // Handle Ctrl-C
255 void interrupt_handler(int var)
256 {
257 // kill keyboard thread mercilessly
258 if(forceKeyboard)
259 pthread_cancel(keyboardThread);
260
261 gShouldStop = true;
262 }
263
264
265 void parseArguments(arg_data args, BelaInitSettings *settings)
266 {
267 // Default filename;
268 gPartialFilename = strdup("D-Box_sound_250_60_40_h88_2.txt");
269
270 const int kOptionAudioTest = 1000;
271
272 // TODO: complete this
273 struct option long_option[] =
274 {
275 {"help", 0, NULL, 'h'},
276 {"audioin", 1, NULL, 'i'},
277 {"file", 1, NULL, 'f'},
278 {"keyboard", 1, NULL, 'k'},
279 {"audio-test", 0, NULL, kOptionAudioTest},
280 {"sensor-type", 1, NULL, 't'},
281 {"sensor0", 1, NULL, 'q'},
282 {"sensor1", 1, NULL, 'r'},
283 {"log", 1, NULL, 'l'},
284 {"usesd", 1, NULL, 'u'},
285 {"oversamp", 1, NULL, 'o'},
286 {"boxnumber", 1, NULL, 'n'},
287 {"group", 1, NULL, 'g'},
288 {NULL, 0, NULL, 0},
289 };
290 int morehelp = 0;
291 int tmp = -1;
292
293 Bela_defaultSettings(settings);
294
295 while (1)
296 {
297 int c;
298 if ((c = Bela_getopt_long(args.argc, args.argv, "hf:ki:sq:r:t:l:u:o:n:g:", long_option, settings)) < 0)
299 break;
300 switch (c)
301 {
302 case 'h':
303 morehelp++;
304 break;
305 case 'f':
306 free(gPartialFilename);
307 gPartialFilename = strdup(optarg);
308 break;
309 case 'k':
310 forceKeyboard = true;
311 break;
312 case 'i':
313 gAudioIn = (atoi(optarg)==0) ? false : true;
314 break;
315 case 's':
316 forceSensors = true;
317 break;
318 case kOptionAudioTest:
319 useAudioTest = true;
320 break;
321 case 't':
322 sensorType = atoi(optarg);
323 break;
324 case 'q':
325 touchSensor0Address = atoi(optarg);
326 break;
327 case 'r':
328 touchSensor1Address = atoi(optarg);
329 break;
330 case 'l':
331 tmp = atoi(optarg);
332 if(tmp==0)
333 forceLog = false;
334 else if(tmp>0)
335 forceLog = true;
336 break;
337 case 'u':
338 tmp = atoi(optarg);
339 if(tmp==0)
340 useSD = false;
341 else if(tmp>0)
342 useSD = true;
343 break;
344 case 'o':
345 oscBnkOversampling = atoi(optarg);
346 break;
347 case 'n':
348 gId = *optarg;
349 cout << "-set box number to: " << gId << endl;
350 break;
351 case 'g':
352 gGroup = *optarg;
353 cout << "-set group to: " << gId << endl;
354 break;
355 default:
356 break;
357 }
358 }
359
360 gPeriodSize = settings->periodSize;
361 gVerbose = settings->verbose;
362 }
363
364 int main(int argc, char *argv[])
365 {
366 BelaInitSettings settings; // Standard audio settings
367 RT_TASK rtSensorThread;
368 const char rtSensorThreadName[] = "dbox-sensor";
369 int oscBankHopSize;
370
371 // Parse command-line arguments
372 args.argc = argc;
373 args.argv = argv;
374 parseArguments(args, &settings);
375
376 Bela_setVerboseLevel(gVerbose);
377 if(gVerbose == 1 && useAudioTest)
378 cout << "main() : running in audio test mode" << endl;
379
380 // Load sound files from directory
381 if(initSoundFiles() != 0)
382 return -1;
383
384 oscBankHopSize = gOscBanks[gCurrentOscBank]->getHopSize()/gOscBanks[gCurrentOscBank]->getMinSpeed();
385
386 // Initialise the audio device
387 if(Bela_initAudio(&settings, &oscBankHopSize) != 0)
388 return -1;
389
390 // Initialise the status LED
391 if(!gStatusLED.init(kStatusLEDPin)) {
392 if(gVerbose)
393 cout << "Couldn't initialise status LED pin\n";
394 }
395
396 // Free file name string which is no longer needed
397 if(gPartialFilename != 0)
398 free(gPartialFilename);
399
400 if(!useAudioTest) {
401 if(initSensorLoop(touchSensor0Address, touchSensor1Address, sensorType) != 0)
402 return -1;
403 }
404
405 if(gVerbose == 1)
406 cout << "main() : creating audio thread" << endl;
407
408 if(Bela_startAudio()) {
409 cout << "Error: unable to start real-time audio" << endl;
410 return -1;
411 }
412
413 // LED on...
414 gStatusLED.on();
415
416 if(forceSensors && !useAudioTest) {
417 if(gVerbose==1)
418 cout << "main() : creating control thread" << endl;
419
420 if(rt_task_create(&rtSensorThread, rtSensorThreadName, 0, BELA_AUDIO_PRIORITY - 5, T_JOINABLE | T_FPU)) {
421 cout << "Error:unable to create Xenomai control thread" << endl;
422 return -1;
423 }
424 if(rt_task_start(&rtSensorThread, &sensorLoop, 0)) {
425 cout << "Error:unable to start Xenomai control thread" << endl;
426 return -1;
427 }
428 }
429
430 if(forceKeyboard) {
431 if(gVerbose==1)
432 cout << "main() : creating keyboard thread" << endl;
433
434 if ( pthread_create(&keyboardThread, NULL, keyboardLoop, NULL) ) {
435 cout << "Error:unable to create keyboard thread" << endl;
436 return -1;
437 }
438 }
439
440 if(forceLog) {
441 if(gVerbose==1)
442 cout << "main() : creating log thread" << endl;
443
444 if(initLogLoop()!=0) {
445 cout << "Error:unable to create log thread" << endl;
446 return -1;
447 }
448
449 if ( pthread_create(&logThread, NULL, logLoop, NULL) ) {
450 cout << "Error:unable to create keyboard thread" << endl;
451 return -1;
452 }
453 }
454
455 // Set up interrupt handler to catch Control-C and SIGTERM
456 signal(SIGINT, interrupt_handler);
457 signal(SIGTERM, interrupt_handler);
458
459 // load all other files into oscBanks
460 loadAudioFiles(false);
461 cout << "Finished loading analysis files\n";
462 gIsLoading = false;
463
464 // Run until told to stop
465 while(!gShouldStop) {
466 usleep(100000);
467 }
468
469 Bela_stopAudio();
470
471 if(!useAudioTest)
472 rt_task_join(&rtSensorThread);
473
474 Bela_cleanupAudio();
475
476 pthread_join( keyboardThread, NULL);
477 pthread_join( logThread, NULL);
478
479 for(unsigned int i = 0; i < gOscBanks.size(); i++)
480 delete gOscBanks[i];
481
482 NE10_FREE(filterState[0]);
483 NE10_FREE(filterState[1]);
484 NE10_FREE(filterIn[0]);
485 NE10_FREE(filterIn[1]);
486 NE10_FREE(filterOut[0]);
487 NE10_FREE(filterOut[1]);
488
489 printf("Program ended\nBye bye\n");
490 return 0;
491 }