comparison projects/d-box/main.cpp @ 0:8a575ba3ab52

Initial commit.
author andrewm
date Fri, 31 Oct 2014 19:10:17 +0100
parents
children 09f03ac40fcc
comparison
equal deleted inserted replaced
-1:000000000000 0:8a575ba3ab52
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.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 "../../include/RTAudio.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 bool useNewSensors = false;
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] = "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)
266 {
267 // Default filename;
268 gPartialFilename = strdup("D-Box_sound_250_60_40_h88_2.txt");
269
270 // TODO: complete this
271 struct option long_option[] =
272 {
273 {"help", 0, NULL, 'h'},
274 {"period", 1, NULL, 'p'},
275 {"verbose", 1, NULL, 'v'},
276 {"audioin", 1, NULL, 'i'},
277 {"file", 1, NULL, 'f'},
278 {"keyboard", 1, NULL, 'k'},
279 {"audio-test", 0, NULL, 'A'},
280 {"new-sensors", 0, NULL, 'S'},
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 while (1)
294 {
295 int c;
296 if ((c = getopt_long(args.argc, args.argv, "hp:vf:ki:sAQ:R:Sl:u:o:n:g:", long_option, NULL)) < 0)
297 break;
298 switch (c)
299 {
300 case 'h':
301 morehelp++;
302 break;
303 case 'p':
304 gPeriodSize = atoi(optarg);
305 break;
306 case 'v':
307 gVerbose = 1;
308 break;
309 case 'f':
310 free(gPartialFilename);
311 gPartialFilename = strdup(optarg);
312 break;
313 case 'k':
314 forceKeyboard = true;
315 break;
316 case 'i':
317 gAudioIn = (atoi(optarg)==0) ? false : true;
318 break;
319 case 's':
320 forceSensors = true;
321 break;
322 case 'A':
323 useAudioTest = true;
324 break;
325 case 'S':
326 useNewSensors = true;
327 break;
328 case 'Q':
329 touchSensor0Address = atoi(optarg);
330 break;
331 case 'R':
332 touchSensor1Address = atoi(optarg);
333 break;
334 case 'l':
335 tmp = atoi(optarg);
336 if(tmp==0)
337 forceLog = false;
338 else if(tmp>0)
339 forceLog = true;
340 break;
341 case 'u':
342 tmp = atoi(optarg);
343 if(tmp==0)
344 useSD = false;
345 else if(tmp>0)
346 useSD = true;
347 break;
348 case 'o':
349 oscBnkOversampling = atoi(optarg);
350 break;
351 case 'n':
352 gId = *optarg;
353 cout << "-set box number to: " << gId << endl;
354 break;
355 case 'g':
356 gGroup = *optarg;
357 cout << "-set group to: " << gId << endl;
358 break;
359 default:
360 break;
361 }
362 }
363 }
364
365 int main(int argc, char *argv[])
366 {
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);
375
376 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(initAudio(gPeriodSize, 1, &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, useNewSensors) != 0)
402 return -1;
403 }
404
405 if(gVerbose == 1)
406 cout << "main() : creating audio thread" << endl;
407
408 if(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, 95, 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
456 signal(SIGINT, interrupt_handler);
457
458 // load all other files into oscBanks
459 loadAudioFiles(false);
460 cout << "Finished loading analysis files\n";
461 gIsLoading = false;
462
463 // Run until told to stop
464 while(!gShouldStop) {
465 usleep(100000);
466 }
467
468 stopAudio();
469
470 if(!useAudioTest)
471 rt_task_join(&rtSensorThread);
472
473 cleanupAudio();
474
475 pthread_join( keyboardThread, NULL);
476 pthread_join( logThread, NULL);
477
478 for(unsigned int i = 0; i < gOscBanks.size(); i++)
479 delete gOscBanks[i];
480
481 NE10_FREE(filterState[0]);
482 NE10_FREE(filterState[1]);
483 NE10_FREE(filterIn[0]);
484 NE10_FREE(filterIn[1]);
485 NE10_FREE(filterOut[0]);
486 NE10_FREE(filterOut[1]);
487
488 printf("Program ended\nBye bye\n");
489 return 0;
490 }