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

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