libcm is a C development framework with an emphasis on audio signal processing applications.
Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

cmAudioFileDev.c 15KB

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