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

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