libcm is a C development framework with an emphasis on audio signal processing applications.
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

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. }