libcm is a C development framework with an emphasis on audio signal processing applications.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

cmAudioFileDev.c 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563
  1. #include "cmGlobal.h"
  2. #include "cmFloatTypes.h"
  3. #include "cmRpt.h"
  4. #include "cmErr.h"
  5. #include "cmMem.h"
  6. #include "cmMallocDebug.h"
  7. #include "cmAudioFile.h"
  8. #include "cmThread.h"
  9. #include "cmAudioPort.h"
  10. #include "cmAudioFileDev.h"
  11. #include <unistd.h> // usleep()
  12. #ifdef OS_OSX
  13. #include "osx/clock_gettime_stub.h"
  14. #endif
  15. #ifdef OS_LINUX
  16. #include <time.h> // clock_gettime()
  17. #endif
  18. cmAfdH_t cmAfdNullHandle = cmSTATIC_NULL_HANDLE;
  19. #define cmAfd_Billion (1000000000)
  20. #define cmAfd_Million (1000000)
  21. typedef struct
  22. {
  23. cmErr_t err; // error object
  24. cmApCallbackPtr_t callbackPtr; // client callback function
  25. void* cbDataPtr; // argument to be passed with the client callback
  26. unsigned devIdx;
  27. cmChar_t* label;
  28. cmChar_t* oFn;
  29. unsigned oBits;
  30. unsigned oChCnt;
  31. cmAudioFileH_t iAfH; // audio input file handle
  32. cmAudioFileH_t oAfH; // audio output file handle
  33. cmThreadH_t tH; // thread handle
  34. double srate; // file device sample rate
  35. unsigned framesPerCycle; // count of samples sent/recv'd from the client on each callback
  36. cmApAudioPacket_t iPkt; // audio packet used sent to the client via callbackPtr.
  37. cmApAudioPacket_t oPkt; //
  38. cmApSample_t** iChArray; // audio buffer channel arrays used with cmAudioFile
  39. cmApSample_t** oChArray; //
  40. bool runFl; // set to true as long as the thread should continue looping
  41. bool rewindFl; // set to true when the input file should rewind
  42. unsigned readErrCnt; // count of read errors from the input file
  43. bool eofFl; // set to true when the input file reaches the EOF
  44. unsigned writeErrCnt; // count of write errors from the output file
  45. long nanosPerCycle; // nano-seconds per cycle
  46. struct timespec baseTime;
  47. struct timespec nextTime; // next execution time
  48. unsigned cycleCnt; // count of cycles completed
  49. } cmAfd_t;
  50. cmAfd_t* _cmAfdHandleToPtr( cmAfdH_t h )
  51. {
  52. cmAfd_t* p = (cmAfd_t*)h.h;
  53. assert(p != NULL );
  54. return p;
  55. }
  56. //
  57. void _cmAudioFileDevExec( cmAfd_t* p )
  58. {
  59. unsigned iPktCnt = 0;
  60. unsigned oPktCnt = p->oPkt.chCnt!=0;
  61. // if the input device is enabled
  62. if( p->iPkt.chCnt )
  63. {
  64. unsigned actualFrmCnt = p->framesPerCycle;
  65. // if the input file has reached EOF - zero the input buffer
  66. if( p->eofFl )
  67. memset(p->iPkt.audioBytesPtr,0,p->framesPerCycle*sizeof(cmApSample_t));
  68. else
  69. {
  70. // otherwise fill the input buffer from the input file
  71. if( cmAudioFileReadSample(p->iAfH, p->framesPerCycle, p->iPkt.begChIdx, p->iPkt.chCnt, p->iChArray, &actualFrmCnt) != kOkAfRC )
  72. ++p->readErrCnt;
  73. // if the input file reachged EOF the set p->eofFl
  74. if( (actualFrmCnt < p->framesPerCycle) && cmAudioFileIsEOF(p->iAfH) )
  75. p->eofFl = true;
  76. }
  77. iPktCnt = actualFrmCnt>0;
  78. }
  79. // callback to the client to provde incoming samples and receive outgoing samples
  80. p->callbackPtr(iPktCnt ? &p->iPkt : NULL, iPktCnt, oPktCnt ? &p->oPkt : NULL, oPktCnt );
  81. // if the output device is enabled
  82. if( p->oPkt.chCnt )
  83. {
  84. // write the output samples
  85. if( cmAudioFileWriteSample( p->oAfH, p->framesPerCycle, p->oPkt.chCnt, p->oChArray ) != kOkAfRC )
  86. ++p->writeErrCnt;
  87. }
  88. ++p->cycleCnt;
  89. }
  90. // incrment p->nextTime to the next execution time
  91. void _cmAfdIncrNextTime( cmAfd_t* p )
  92. {
  93. long nsec = p->nextTime.tv_nsec + p->nanosPerCycle;
  94. if( nsec < cmAfd_Billion )
  95. p->nextTime.tv_nsec = nsec;
  96. else
  97. {
  98. p->nextTime.tv_sec += 1;
  99. p->nextTime.tv_nsec = nsec - cmAfd_Billion;
  100. }
  101. }
  102. // calc the time between t1 and t0 - t1 is assummed to come after t0 in order to produce a positive result
  103. long _cmAfdDiffMicros( const struct timespec* t0, const struct timespec* t1 )
  104. {
  105. long u0 = t0->tv_sec * cmAfd_Million;
  106. long u1 = t1->tv_sec * cmAfd_Million;
  107. u0 += t0->tv_nsec / 1000;
  108. u1 += t1->tv_nsec / 1000;
  109. return u1 - u0;
  110. }
  111. // thread callback function
  112. bool _cmAudioDevThreadFunc(void* param)
  113. {
  114. cmAfd_t* p = (cmAfd_t*)param;
  115. struct timespec t0;
  116. // if this is the first time this callback has been called after a call to cmAudioFileDevStart().
  117. if( p->cycleCnt == 0 )
  118. {
  119. // get the baseTime - all other times will be relative to this time
  120. clock_gettime(CLOCK_REALTIME,&p->baseTime);
  121. p->nextTime = p->baseTime;
  122. p->nextTime.tv_sec = 0;
  123. _cmAfdIncrNextTime(p);
  124. }
  125. // if the thread has not been requested to stop
  126. if( p->runFl )
  127. {
  128. // get the current time as an offset from baseTime.
  129. clock_gettime(CLOCK_REALTIME,&t0);
  130. t0.tv_sec -= p->baseTime.tv_sec;
  131. // get length of time to next exec point
  132. long dusec = _cmAfdDiffMicros(&t0, &p->nextTime);
  133. // if the execution time has not yet arrived
  134. if( dusec > 0 )
  135. {
  136. usleep(dusec);
  137. }
  138. // if the thread is still running
  139. if( p->runFl )
  140. {
  141. // read/callback/write
  142. _cmAudioFileDevExec(p);
  143. // calc the next exec time
  144. _cmAfdIncrNextTime(p);
  145. }
  146. }
  147. return p->runFl;
  148. }
  149. cmAfdRC_t cmAudioFileDevInitialize(
  150. cmAfdH_t* hp,
  151. const cmChar_t* label,
  152. unsigned devIdx,
  153. const cmChar_t* iFn,
  154. const cmChar_t* oFn,
  155. unsigned oBits,
  156. unsigned oChCnt,
  157. cmRpt_t* rpt )
  158. {
  159. cmAfdRC_t rc;
  160. cmRC_t afRC;
  161. if((rc = cmAudioFileDevFinalize(hp)) != kOkAfdRC )
  162. return rc;
  163. // allocate the object
  164. cmAfd_t* p = cmMemAllocZ(cmAfd_t,1);
  165. hp->h = p;
  166. cmErrSetup(&p->err,rpt,"AudioFileDevice");
  167. // create the input audio file handle
  168. if( iFn != NULL )
  169. {
  170. cmAudioFileInfo_t afInfo;
  171. // open the input file
  172. if(cmAudioFileIsValid( p->iAfH = cmAudioFileNewOpen(iFn,&afInfo,&afRC,rpt)) == false )
  173. {
  174. rc = cmErrMsg(&p->err,kAudioFileFailAfdRC,"The audio input file '%s' could not be opened.", iFn);
  175. goto errLabel;
  176. }
  177. p->iPkt.devIdx = devIdx;
  178. p->iPkt.begChIdx = 0;
  179. p->iPkt.chCnt = afInfo.chCnt; // setting iPkt.chCnt to a non-zero value marks the input file as active
  180. p->iPkt.audioFramesCnt = 0;
  181. p->iPkt.bitsPerSample = afInfo.bits;
  182. p->iPkt.flags = kFloatApFl;
  183. p->iPkt.audioBytesPtr = NULL;
  184. p->iChArray = cmMemResizeZ( cmApSample_t*, p->iChArray, afInfo.chCnt );
  185. p->readErrCnt = 0;
  186. p->eofFl = false;
  187. }
  188. // create the output audio file handle
  189. if(cmAudioFileIsValid( p->oAfH = cmAudioFileNewOpen(NULL,NULL,NULL,rpt)) == false )
  190. {
  191. rc = cmErrMsg(&p->err,kAudioFileFailAfdRC,"The audio output file object allocation failed.");
  192. goto errLabel;
  193. }
  194. // create the driver thread
  195. if( cmThreadCreate(&p->tH, _cmAudioDevThreadFunc, p, rpt ) != kOkThRC )
  196. {
  197. rc = cmErrMsg(&p->err,kThreadFailAfdRC,"The internal thread could not be created.");
  198. goto errLabel;
  199. }
  200. p->runFl = true;
  201. p->devIdx = devIdx;
  202. p->label = cmMemAllocStr(label);
  203. p->oFn = cmMemAllocStr(oFn);
  204. p->oBits = oBits;
  205. p->oChCnt = oChCnt;
  206. errLabel:
  207. if( rc != kOkAfdRC )
  208. cmAudioFileDevFinalize(hp);
  209. return rc;
  210. }
  211. cmAfdRC_t cmAudioFileDevFinalize( cmAfdH_t* hp )
  212. {
  213. if( hp == NULL || cmAudioFileDevIsValid(*hp) == false )
  214. return kOkAfdRC;
  215. cmAfd_t* p = _cmAfdHandleToPtr(*hp);
  216. p->runFl = false;
  217. if( cmThreadIsValid(p->tH) )
  218. cmThreadDestroy(&p->tH);
  219. cmAudioFileDelete(&p->iAfH);
  220. cmAudioFileDelete(&p->oAfH);
  221. cmMemPtrFree(&p->label);
  222. cmMemPtrFree(&p->oFn);
  223. cmMemPtrFree(&p->iPkt.audioBytesPtr);
  224. cmMemPtrFree(&p->oPkt.audioBytesPtr);
  225. cmMemPtrFree(&p->iChArray);
  226. cmMemPtrFree(&p->oChArray);
  227. cmMemPtrFree(&p);
  228. hp->h = NULL;
  229. return kOkAfdRC;
  230. }
  231. bool cmAudioFileDevIsValid( cmAfdH_t h )
  232. { return h.h != NULL; }
  233. cmAfdRC_t cmAudioFileDevSetup(
  234. cmAfdH_t h,
  235. unsigned baseDevIdx,
  236. double srate,
  237. unsigned framesPerCycle,
  238. cmApCallbackPtr_t callbackPtr,
  239. void* cbDataPtr )
  240. {
  241. cmAfdRC_t rc = kOkAfdRC;
  242. bool restartFl = false;
  243. unsigned i;
  244. if( cmAudioFileDevIsStarted(h) )
  245. {
  246. if((rc = cmAudioFileDevStop(h)) != kOkAfdRC )
  247. return rc;
  248. restartFl = true;
  249. }
  250. cmAfd_t* p = _cmAfdHandleToPtr(h);
  251. /*
  252. // close the existing input file
  253. if(cmAudioFileClose(&p->iAfH) != kOkAfRC )
  254. rc = cmErrMsg(&p->err,kAudioFileFailAfdRC,"Audio file close failed on input audio file.");
  255. p->iPkt.chCnt = 0; // mark the input file as inactive
  256. */
  257. if( cmAudioFileIsValid( p->iAfH ) )
  258. if( cmAudioFileSeek( p->iAfH, 0 ) != kOkAfRC )
  259. rc = cmErrMsg(&p->err,kAudioFileFailAfdRC,"Audio file device rewind failed.");
  260. // close the existing output file
  261. if(cmAudioFileClose(&p->oAfH) != kOkAfRC )
  262. rc = cmErrMsg(&p->err,kAudioFileFailAfdRC,"Audio file close failed on output audio file.");
  263. p->oPkt.chCnt = 0; // mark the output file as inactive
  264. // if an output audio file was given ...
  265. if( p->oFn != NULL )
  266. {
  267. // ... then open it
  268. if( cmAudioFileCreate( p->oAfH, p->oFn, srate, p->oBits, p->oChCnt ) != kOkAfRC )
  269. {
  270. rc = cmErrMsg(&p->err,kAudioFileFailAfdRC, "The audio output file '%s' could not be created.",p->oFn);
  271. goto errLabel;
  272. }
  273. cmApSample_t* bp = (cmApSample_t*)p->oPkt.audioBytesPtr;
  274. p->oPkt.devIdx = p->devIdx + baseDevIdx;
  275. p->oPkt.begChIdx = 0;
  276. p->oPkt.chCnt = p->oChCnt;
  277. p->oPkt.audioFramesCnt = framesPerCycle;
  278. p->oPkt.bitsPerSample = p->oBits;
  279. p->oPkt.flags = kFloatApFl;
  280. p->oPkt.audioBytesPtr = bp = cmMemResizeZ( cmApSample_t, bp, framesPerCycle*p->oChCnt );
  281. p->oPkt.userCbPtr = cbDataPtr;
  282. p->oChArray = cmMemResizeZ( cmApSample_t*, p->oChArray, p->oChCnt );
  283. for(i=0; i<p->oChCnt; ++i)
  284. p->oChArray[i] = bp + (i*framesPerCycle);
  285. }
  286. if( cmAudioFileIsValid( p->iAfH) )
  287. {
  288. cmApSample_t* bp = (cmApSample_t*)p->iPkt.audioBytesPtr;
  289. p->iPkt.devIdx = p->devIdx + baseDevIdx;
  290. p->iPkt.audioFramesCnt = framesPerCycle;
  291. p->iPkt.audioBytesPtr = bp = cmMemResizeZ( cmApSample_t, bp, framesPerCycle*p->iPkt.chCnt ); ;
  292. p->iPkt.userCbPtr = cbDataPtr;
  293. for(i=0; i<p->iPkt.chCnt; ++i)
  294. p->iChArray[i] = bp + (i*framesPerCycle);
  295. }
  296. p->callbackPtr = callbackPtr;
  297. p->cbDataPtr = cbDataPtr;
  298. p->framesPerCycle = framesPerCycle;
  299. p->srate = srate;
  300. p->cycleCnt = 0;
  301. p->nanosPerCycle = floor((double)framesPerCycle / srate * cmAfd_Billion );
  302. if( restartFl )
  303. {
  304. if((rc = cmAudioFileDevStart(h)) != kOkAfdRC )
  305. {
  306. rc = cmErrMsg(&p->err,kRestartFailAfdRC,"The audio file device could not be restarted.");
  307. }
  308. }
  309. errLabel:
  310. return rc;
  311. }
  312. const char* cmAudioFileDevLabel( cmAfdH_t h )
  313. {
  314. cmAfd_t* p = _cmAfdHandleToPtr(h);
  315. return p->label;
  316. }
  317. unsigned cmAudioFileDevChannelCount( cmAfdH_t h, bool inputFl )
  318. {
  319. cmAfd_t* p = _cmAfdHandleToPtr(h);
  320. return inputFl ? p->iPkt.chCnt : p->oPkt.chCnt;
  321. }
  322. double cmAudioFileDevSampleRate( cmAfdH_t h )
  323. {
  324. cmAfd_t* p = _cmAfdHandleToPtr(h);
  325. return p->srate;
  326. }
  327. unsigned cmAudioFileDevFramesPerCycle( cmAfdH_t h, bool inputFl )
  328. {
  329. cmAfd_t* p = _cmAfdHandleToPtr(h);
  330. return inputFl ? p->iPkt.audioFramesCnt : p->oPkt.audioFramesCnt;
  331. }
  332. cmAfdRC_t cmAudioFileDevRewind( cmAfdH_t h )
  333. {
  334. cmAfd_t* p = _cmAfdHandleToPtr(h);
  335. p->rewindFl = true;
  336. return kOkAfdRC;
  337. }
  338. cmAfdRC_t cmAudioFileDevStart( cmAfdH_t h )
  339. {
  340. cmAfdRC_t rc = kOkAfdRC;
  341. cmAfd_t* p = _cmAfdHandleToPtr(h);
  342. p->cycleCnt = 0;
  343. if( cmThreadPause( p->tH, 0 ) != kOkThRC )
  344. {
  345. rc = cmErrMsg(&p->err,kThreadFailAfdRC,"Thread start failed.");
  346. goto errLabel;
  347. }
  348. fputs("Start\n",stderr);
  349. errLabel:
  350. return rc;
  351. }
  352. cmAfdRC_t cmAudioFileDevStop( cmAfdH_t h )
  353. {
  354. cmAfdRC_t rc = kOkAfdRC;
  355. cmAfd_t* p = _cmAfdHandleToPtr(h);
  356. if( cmThreadPause( p->tH, kPauseThFl | kWaitThFl ) != kOkThRC )
  357. {
  358. rc = cmErrMsg(&p->err,kThreadFailAfdRC,"Thread stop failed.");
  359. goto errLabel;
  360. }
  361. fputs("Stop\n",stderr);
  362. errLabel:
  363. return rc;
  364. }
  365. bool cmAudioFileDevIsStarted( cmAfdH_t h )
  366. {
  367. cmAfd_t* p = _cmAfdHandleToPtr(h);
  368. return cmThreadState(p->tH) == kRunningThId;
  369. }
  370. void cmAudioFileDevReport( cmAfdH_t h, cmRpt_t* rpt )
  371. {
  372. cmAfd_t* p = _cmAfdHandleToPtr(h);
  373. cmRptPrintf(rpt,"label:%s thr state:%i srate:%f\n",p->label,cmThreadState(p->tH),p->srate);
  374. cmRptPrintf(rpt, "in chs:%i %s\n",p->iPkt.chCnt,cmAudioFileName(p->iAfH));
  375. cmRptPrintf(rpt, "out chs:%i %s\n",p->oPkt.chCnt,p->oFn);
  376. }
  377. // device callback function used with cmAudioFileDevTest() note that this assumes
  378. // that the packet buffer contain non-interleaved data.
  379. void _cmAfdCallback(
  380. cmApAudioPacket_t* inPktArray,
  381. unsigned inPktCnt,
  382. cmApAudioPacket_t* outPktArray,
  383. unsigned outPktCnt )
  384. {
  385. cmApAudioPacket_t* ip = inPktArray;
  386. cmApAudioPacket_t* op = outPktArray;
  387. unsigned opi = 0;
  388. unsigned ipi = 0;
  389. unsigned oci = 0;
  390. unsigned ici = 0;
  391. while(1)
  392. {
  393. if( ici == ip->chCnt)
  394. {
  395. ici = 0;
  396. if( ++ipi >= inPktCnt )
  397. break;
  398. ip = inPktArray + ipi;
  399. }
  400. if( oci == op->chCnt )
  401. {
  402. oci = 0;
  403. if( ++opi >= outPktCnt )
  404. break;
  405. ip = outPktArray + opi;
  406. }
  407. assert( ip->audioFramesCnt == op->audioFramesCnt );
  408. assert( cmIsFlag(ip->flags,kInterleavedApFl)==false && cmIsFlag(ip->flags,kInterleavedApFl)==false );
  409. cmApSample_t* ibp = ((cmApSample_t*)ip->audioBytesPtr) + (ip->audioFramesCnt*ici);
  410. cmApSample_t* obp = ((cmApSample_t*)op->audioBytesPtr) + (op->audioFramesCnt*oci);
  411. memcpy(obp,ibp,ip->audioFramesCnt*sizeof(cmApSample_t));
  412. ++ici;
  413. ++oci;
  414. }
  415. }
  416. void cmAudioFileDevTest( cmRpt_t* rpt )
  417. {
  418. cmAfdH_t afdH = cmAfdNullHandle;
  419. double srate = 44100;
  420. unsigned framesPerCycle = 512;
  421. void* cbDataPtr = NULL;
  422. unsigned devIdx = 0;
  423. const cmChar_t* iFn = "/home/kevin/media/audio/McGill-1/1 Audio Track.aiff";
  424. const cmChar_t* oFn = "/home/kevin/temp/afd0.aif";
  425. unsigned oBits = 16;
  426. unsigned oChCnt = 2;
  427. if( cmAudioFileDevInitialize(&afdH,"file",devIdx,iFn,oFn,oBits,oChCnt,rpt) != kOkAfdRC )
  428. goto errLabel;
  429. if( cmAudioFileDevSetup(afdH,0,srate,framesPerCycle,_cmAfdCallback,cbDataPtr) != kOkAfdRC )
  430. goto errLabel;
  431. char c;
  432. fputs("q=quit 1=start 0=stop\n",stderr);
  433. fflush(stderr);
  434. while((c=getchar()) != 'q')
  435. {
  436. switch(c)
  437. {
  438. case '1': cmAudioFileDevStart(afdH); break;
  439. case '0': cmAudioFileDevStop(afdH); break;
  440. }
  441. c = 0;
  442. fflush(stdin);
  443. }
  444. errLabel:
  445. cmAudioFileDevFinalize(&afdH);
  446. }