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

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