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.

cmSyncRecd.c 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563
  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 "cmTime.h"
  9. #include "cmFile.h"
  10. #include "cmAudioFile.h"
  11. #include "cmSyncRecd.h"
  12. #include "cmVectOpsTemplateMain.h"
  13. #include "cmMidi.h"
  14. typedef enum
  15. {
  16. kInvalidSrId,
  17. kMidiSrId,
  18. kAudioSrId
  19. } cmSrTypeId_t;
  20. typedef struct cmSrMidi_str
  21. {
  22. cmTimeSpec_t timestamp;
  23. unsigned status;
  24. unsigned d0;
  25. unsigned d1;
  26. } cmSrMidi_t;
  27. typedef struct cmSrAudio_str
  28. {
  29. cmTimeSpec_t timestamp;
  30. unsigned smpIdx;
  31. } cmSrAudio_t;
  32. typedef struct cmSrRecd_str
  33. {
  34. cmSrTypeId_t tid;
  35. union
  36. {
  37. cmSrMidi_t m;
  38. cmSrAudio_t a;
  39. } u;
  40. } cmSrRecd_t;
  41. enum
  42. {
  43. kReadSrFl = 0x01, // This is a read (not a write) file
  44. kFileUUSrId = 0xf00d
  45. };
  46. typedef struct cmSr_str
  47. {
  48. cmErr_t err;
  49. cmFileH_t fH;
  50. cmAudioFileH_t afH;
  51. unsigned flags; // See kXXXSrFl
  52. cmSrRecd_t* cache; // cache[cn]
  53. cmSrAudio_t* map; //
  54. unsigned cn; // count of records in cache[].
  55. unsigned ci; // next cache recd index
  56. unsigned fn; // count of recds written to file
  57. long offs;
  58. cmAudioFileInfo_t afInfo;
  59. } cmSr_t;
  60. cmSyncRecdH_t cmSyncRecdNullHandle = cmSTATIC_NULL_HANDLE;
  61. cmSr_t* _cmSrHtoP( cmSyncRecdH_t h )
  62. {
  63. cmSr_t* p = (cmSr_t*)h.h;
  64. assert(p!=NULL);
  65. return p;
  66. }
  67. cmSrRC_t _cmSrWriteCache( cmSr_t* p )
  68. {
  69. if( cmFileWrite(p->fH,p->cache,p->ci * sizeof(cmSrRecd_t)) != kOkFileRC )
  70. return cmErrMsg(&p->err,kFileFailSrRC,"File write failed.");
  71. p->fn += p->ci;
  72. p->ci = 0;
  73. return kOkSrRC;
  74. }
  75. cmSrRC_t _cmSrFinal( cmSr_t* p )
  76. {
  77. cmSrRC_t rc = kOkSrRC;
  78. // write any remaining cache records
  79. if( cmIsFlag(p->flags,kReadSrFl) == false )
  80. {
  81. if((rc = _cmSrWriteCache(p)) == kOkSrRC )
  82. {
  83. if(cmFileSeek(p->fH,kBeginFileFl,p->offs) != kOkFileRC )
  84. rc = cmErrMsg(&p->err,kFileFailSrRC, "File seek fail on file offset positioning.");
  85. else
  86. if(cmFileWriteUInt(p->fH,&p->fn,1) != kOkFileRC )
  87. rc = cmErrMsg(&p->err,kFileFailSrRC, "File write failed on record count.");
  88. }
  89. }
  90. // release the audio file object
  91. if( cmAudioFileIsValid(p->afH) )
  92. if( cmAudioFileDelete(&p->afH) != kOkAfRC )
  93. return cmErrMsg(&p->err,kAudioFileFailSrRC,"Audio file object delete failed.");
  94. // release the sync-recd file object
  95. if( cmFileIsValid(p->fH) )
  96. if( cmFileClose(&p->fH) != kOkFileRC )
  97. return cmErrMsg(&p->err,kFileFailSrRC,"File close failed.");
  98. cmMemFree(p->cache);
  99. cmMemFree(p->map);
  100. cmMemFree(p);
  101. return rc;
  102. }
  103. cmSr_t* _cmSrAlloc( cmCtx_t* ctx, unsigned flags )
  104. {
  105. cmSr_t* p = cmMemAllocZ(cmSr_t,1);
  106. cmErrSetup(&p->err,&ctx->rpt,"SyncRecd");
  107. p->cn = 2048;
  108. p->cache = cmMemAllocZ(cmSrRecd_t,p->cn);
  109. return p;
  110. }
  111. cmSrRC_t cmSyncRecdCreate( cmCtx_t* ctx, cmSyncRecdH_t* hp, const cmChar_t* srFn, const cmChar_t* audioFn, double srate, unsigned chCnt, unsigned bits )
  112. {
  113. cmSrRC_t rc = kOkSrRC;
  114. cmRC_t afRC = kOkAfRC;
  115. assert( audioFn != NULL );
  116. if((rc = cmSyncRecdFinal(hp)) != kOkSrRC )
  117. return rc;
  118. cmSr_t* p = _cmSrAlloc(ctx,0);
  119. if( cmFileOpen(&p->fH,srFn,kWriteFileFl,&ctx->rpt) != kOkFileRC )
  120. {
  121. rc = cmErrMsg(&p->err,kFileFailSrRC,"Unable to create the sync-recd file '%s'.",cmStringNullGuard(srFn));
  122. goto errLabel;
  123. }
  124. if( cmAudioFileIsValid(p->afH = cmAudioFileNewCreate(audioFn,srate,bits,chCnt,&afRC,&ctx->rpt))==false)
  125. {
  126. rc = cmErrMsg(&p->err,kAudioFileFailSrRC,"Unable to create the sync-recd audio file '%s'.",cmStringNullGuard(audioFn));
  127. goto errLabel;
  128. }
  129. unsigned fileUUId = kFileUUSrId;
  130. unsigned audioFnCnt = strlen(audioFn)+1;
  131. if( cmFileWriteUInt(p->fH,&fileUUId,1) != kOkFileRC )
  132. {
  133. rc = cmErrMsg(&p->err,kFileFailSrRC,"File write failed on UUID.");
  134. goto errLabel;
  135. }
  136. if( cmFileWriteUInt(p->fH,&audioFnCnt,1) != kOkFileRC )
  137. {
  138. rc = cmErrMsg(&p->err,kFileFailSrRC,"File write failed on audio file length write count.");
  139. goto errLabel;
  140. }
  141. if( cmFileWriteChar(p->fH,audioFn,audioFnCnt) != kOkFileRC )
  142. {
  143. rc = cmErrMsg(&p->err,kFileFailSrRC,"File write failed on audio file string.");
  144. goto errLabel;
  145. }
  146. if( cmFileTell(p->fH,&p->offs) != kOkFileRC )
  147. {
  148. rc = cmErrMsg(&p->err,kFileFailSrRC,"Unable to determine file offset.");
  149. goto errLabel;
  150. }
  151. if( cmFileWriteUInt(p->fH,&p->fn,1) != kOkFileRC )
  152. {
  153. rc = cmErrMsg(&p->err,kFileFailSrRC,"File write failed on initial record count.");
  154. goto errLabel;
  155. }
  156. hp->h = p;
  157. errLabel:
  158. if( rc != kOkSrRC )
  159. _cmSrFinal(p);
  160. return rc;
  161. }
  162. cmSrRC_t cmSyncRecdOpen( cmCtx_t* ctx, cmSyncRecdH_t* hp, const cmChar_t* srFn )
  163. {
  164. cmSrRC_t rc = kOkSrRC;
  165. cmRC_t afRC = kOkAfRC;
  166. unsigned fileUUId = cmInvalidId;
  167. unsigned audioFnCnt = 0;
  168. cmChar_t* audioFn = NULL;
  169. unsigned i;
  170. unsigned acnt = 0;
  171. unsigned mcnt = 0;
  172. unsigned* tiV = NULL;
  173. if((rc = cmSyncRecdFinal(hp)) != kOkSrRC )
  174. return rc;
  175. cmSr_t* p = _cmSrAlloc(ctx,kReadSrFl);
  176. if( cmFileOpen(&p->fH,srFn,kReadFileFl,&ctx->rpt) != kOkFileRC )
  177. {
  178. rc = cmErrMsg(&p->err,kFileFailSrRC,"Unable to open the sync-recd file '%s'.",cmStringNullGuard(srFn));
  179. goto errLabel;
  180. }
  181. if( cmFileReadUInt(p->fH,&fileUUId,1) != kOkFileRC )
  182. {
  183. rc = cmErrMsg(&p->err,kFileFailSrRC,"File read failed on UUId.");
  184. goto errLabel;
  185. }
  186. if( cmFileReadUInt(p->fH,&audioFnCnt,1) != kOkFileRC )
  187. {
  188. rc = cmErrMsg(&p->err,kFileFailSrRC,"File read failed on audio file name count.");
  189. goto errLabel;
  190. }
  191. audioFn = cmMemAllocZ(cmChar_t,audioFnCnt);
  192. if( cmFileReadChar(p->fH,audioFn,audioFnCnt) != kOkFileRC )
  193. {
  194. rc = cmErrMsg(&p->err,kFileFailSrRC,"File read failed on audio file string.");
  195. goto errLabel;
  196. }
  197. if( cmFileReadUInt(p->fH,&p->fn,1) != kOkFileRC )
  198. {
  199. rc = cmErrMsg(&p->err,kFileFailSrRC,"File read failed on record count.");
  200. goto errLabel;
  201. }
  202. // store the file offset to the first recd
  203. if( cmFileTell(p->fH,&p->offs) != kOkFileRC )
  204. {
  205. rc = cmErrMsg(&p->err,kFileFailSrRC,"Unable to determine the current file offset.");
  206. goto errLabel;
  207. }
  208. // read each file - and count the types
  209. for(i=0; i<p->fn; ++i)
  210. {
  211. cmSrRecd_t r;
  212. if( cmFileRead(p->fH,&r,sizeof(r)) != kOkFileRC )
  213. {
  214. rc = cmErrMsg(&p->err,kFileFailSrRC,"Unable to read the record at index %i.");
  215. goto errLabel;
  216. }
  217. switch(r.tid)
  218. {
  219. case kMidiSrId:
  220. mcnt += 1;
  221. break;
  222. case kAudioSrId:
  223. acnt += 1;
  224. break;
  225. default:
  226. { assert(0); }
  227. }
  228. }
  229. printf("%i %i = %i\n",mcnt,acnt,p->fn);
  230. // rewind to the begining of the records
  231. if( cmFileSeek(p->fH,kBeginFileFl,p->offs) != kOkFileRC )
  232. {
  233. rc = cmErrMsg(&p->err,kFileFailSrRC,"Unable to seek to first recd offset.");
  234. goto errLabel;
  235. }
  236. // allocate space to hold the MIDI records
  237. p->cn = mcnt;
  238. p->ci = 0;
  239. p->cache = cmMemResizeZ(cmSrRecd_t,p->cache,p->cn);
  240. p->map = cmMemAllocZ(cmSrAudio_t,p->cn);
  241. tiV = cmMemAllocZ(unsigned,p->cn);
  242. for(i=0; p->ci<p->cn && i<p->fn; ++i)
  243. {
  244. if( cmFileRead(p->fH,p->cache + p->ci,sizeof(cmSrRecd_t)) != kOkFileRC )
  245. {
  246. rc = cmErrMsg(&p->err,kFileFailSrRC,"Unable to read the record at index %i.");
  247. goto errLabel;
  248. }
  249. if( p->cache[p->ci].tid == kMidiSrId )
  250. p->ci += 1;
  251. }
  252. // assert that all the MIDI records were read
  253. assert( p->ci == p->cn);
  254. // rewind to the first recd
  255. if( cmFileSeek(p->fH,kBeginFileFl,p->offs) != kOkFileRC )
  256. {
  257. rc = cmErrMsg(&p->err,kFileFailSrRC,"Unable to seek to first recd offset.");
  258. goto errLabel;
  259. }
  260. // for each recd in the file
  261. for(i=0; i<p->fn; ++i)
  262. {
  263. cmSrRecd_t r;
  264. if( cmFileRead(p->fH,&r,sizeof(r)) != kOkFileRC )
  265. {
  266. rc = cmErrMsg(&p->err,kFileFailSrRC,"Unable to read the record at index %i.");
  267. goto errLabel;
  268. }
  269. // if this is an audio record
  270. if( r.tid == kAudioSrId )
  271. {
  272. unsigned j;
  273. // for each midi record
  274. for(j=0; j<p->cn; ++j)
  275. {
  276. // measure the time interval between this midi and this audio recd
  277. unsigned time_interval_micros = cmTimeAbsElapsedMicros(&r.u.a.timestamp,&p->cache[j].u.m.timestamp);
  278. // if the audio recd is closer to this midi recd than prior audio records ...
  279. if( time_interval_micros < tiV[j] || i==0 )
  280. {
  281. // ... then store the audio time stamp in the map
  282. tiV[j] = time_interval_micros;
  283. p->map[j].timestamp = r.u.a.timestamp;
  284. p->map[j].smpIdx = r.u.a.smpIdx;
  285. }
  286. }
  287. }
  288. }
  289. // open the audio file
  290. if( cmAudioFileIsValid(p->afH = cmAudioFileNewOpen(audioFn,&p->afInfo,&afRC,&ctx->rpt ))==false)
  291. {
  292. rc = cmErrMsg(&p->err,kAudioFileFailSrRC,"Unable to open the sync-recd audio file '%s'.",cmStringNullGuard(audioFn));
  293. goto errLabel;
  294. }
  295. p->flags = cmSetFlag(p->flags,kReadSrFl);
  296. hp->h = p;
  297. errLabel:
  298. cmMemFree(tiV);
  299. cmMemFree(audioFn);
  300. if( rc != kOkSrRC )
  301. _cmSrFinal(p);
  302. return rc;
  303. }
  304. cmSrRC_t cmSyncRecdFinal( cmSyncRecdH_t* hp )
  305. {
  306. cmSrRC_t rc = kOkSrRC;
  307. if( hp==NULL || cmSyncRecdIsValid(*hp)==false)
  308. return rc;
  309. cmSr_t* p = _cmSrHtoP(*hp);
  310. if((rc = _cmSrFinal(p)) != kOkSrRC )
  311. return rc;
  312. hp->h = NULL;
  313. return rc;
  314. }
  315. bool cmSyncRecdIsValid( cmSyncRecdH_t h )
  316. { return h.h != NULL; }
  317. cmSrRC_t cmSyncRecdMidiWrite( cmSyncRecdH_t h, const cmTimeSpec_t* timestamp, unsigned status, unsigned d0, unsigned d1 )
  318. {
  319. cmSrRC_t rc = kOkSrRC;
  320. cmSr_t* p = _cmSrHtoP(h);
  321. cmSrRecd_t* rp = p->cache + p->ci;
  322. rp->tid = kMidiSrId;
  323. rp->u.m.timestamp = *timestamp;
  324. rp->u.m.status = status;
  325. rp->u.m.d0 = d0;
  326. rp->u.m.d1 = d1;
  327. p->ci += 1;
  328. if( p->ci == p->cn )
  329. rc = _cmSrWriteCache(p);
  330. return rc;
  331. }
  332. cmSrRC_t cmSyncRecdAudioWrite( cmSyncRecdH_t h, const cmTimeSpec_t* timestamp, unsigned smpIdx, const cmSample_t* ch[], unsigned chCnt, unsigned frmCnt )
  333. {
  334. cmSrRC_t rc = kOkSrRC;
  335. cmSr_t* p = _cmSrHtoP(h);
  336. cmSrRecd_t* rp = p->cache + p->ci;
  337. rp->tid = kAudioSrId;
  338. rp->u.a.timestamp = *timestamp;
  339. rp->u.a.smpIdx = smpIdx;
  340. p->ci += 1;
  341. if( p->ci == p->cn )
  342. if((rc = _cmSrWriteCache(p)) != kOkSrRC )
  343. goto errLabel;
  344. if( cmAudioFileWriteSample(p->afH,frmCnt,chCnt,(cmSample_t**)ch) != kOkAfRC )
  345. rc = cmErrMsg(&p->err,kAudioFileFailSrRC,"Audio file write failed.");
  346. errLabel:
  347. return rc;
  348. }
  349. cmSrRC_t cmSyncRecdPrint( cmSyncRecdH_t h )
  350. {
  351. cmSrRC_t rc = kOkSrRC;
  352. cmSr_t* p = _cmSrHtoP(h);
  353. unsigned i;
  354. if( cmIsFlag(p->flags,kReadSrFl)==false)
  355. return cmErrMsg(&p->err,kInvalidOpSrRC,"The 'print' operation is only valid on sync-recd files opened for reading.");
  356. for(i=0; i<p->cn; ++i)
  357. {
  358. cmSrRecd_t* r = p->cache + i;
  359. cmRptPrintf(p->err.rpt,"0x%x %3i %3i %ld %5.3f : %ld %5.3f %i",r->u.m.status,r->u.m.d0,r->u.m.d1,r->u.m.timestamp.tv_sec,r->u.m.timestamp.tv_nsec/1000000000.0,p->map[i].timestamp.tv_sec,p->map[i].timestamp.tv_nsec/1000000000.0,p->map[i].smpIdx);
  360. }
  361. return rc;
  362. }
  363. cmSrRC_t cmSyncRecdAudioFile( cmSyncRecdH_t h, const cmChar_t* fn )
  364. {
  365. cmSrRC_t rc = kOkSrRC;
  366. cmSr_t* p = _cmSrHtoP(h);
  367. cmAudioFileH_t afH = cmNullAudioFileH;
  368. unsigned chCnt = 2;
  369. unsigned frmCnt = 1024;
  370. unsigned chIdx = 0;
  371. cmRC_t afRC = kOkAfRC;
  372. unsigned actFrmCnt = 0;
  373. unsigned smpIdx = 0;
  374. cmSample_t* chs[chCnt];
  375. cmSample_t buf[frmCnt*2];
  376. chs[0] = buf;
  377. chs[1] = buf+frmCnt;
  378. if( cmIsFlag(p->flags,kReadSrFl)==false)
  379. return cmErrMsg(&p->err,kInvalidOpSrRC,"The 'audio-file-output' operation is only valid on sync-recd files opened for reading.");
  380. /// Open an audio file for writing
  381. if(cmAudioFileIsValid(afH = cmAudioFileNewCreate(fn, p->afInfo.srate, p->afInfo.bits, chCnt, &afRC, p->err.rpt))==false)
  382. {
  383. rc = cmErrMsg(&p->err,kAudioFileFailSrRC,"Unable to create the synchronized audio file '%s'.",cmStringNullGuard(fn));
  384. goto errLabel;
  385. }
  386. // rewind the input audio file
  387. if( cmAudioFileSeek(p->afH,0) != kOkAfRC )
  388. {
  389. rc = cmErrMsg(&p->err,kAudioFileFailSrRC,"Seek failed during synchronized audio file output.");
  390. goto errLabel;
  391. }
  392. actFrmCnt = frmCnt;
  393. // for each buffer of audio
  394. for(smpIdx=0; actFrmCnt==frmCnt; smpIdx+=actFrmCnt)
  395. {
  396. unsigned i;
  397. // read frmCnt samples from the first channel of the input audio file
  398. if( cmAudioFileReadSample(p->afH, frmCnt, chIdx, 1, chs, &actFrmCnt ) != kOkAfRC )
  399. {
  400. rc = cmErrMsg(&p->err,kAudioFileFailSrRC,"Audio file read failed.");
  401. break;
  402. }
  403. // zero the output buffer for the second audio channel
  404. cmVOS_Zero(chs[1],frmCnt);
  405. // insert impulses at the location of the MIDI messages.
  406. for(i=0; i<p->cn; ++i)
  407. if( p->cache[i].u.m.status==kNoteOnMdId && smpIdx <= p->map[i].smpIdx && p->map[i].smpIdx < smpIdx+frmCnt )
  408. chs[1][ p->map[i].smpIdx - smpIdx ] = 1.0;
  409. // write the audio output samples
  410. if( cmAudioFileWriteSample(afH, frmCnt, chCnt, chs ) != kOkAfRC )
  411. {
  412. rc = cmErrMsg(&p->err,kAudioFileFailSrRC,"Synchronized audio file write failed.");
  413. break;
  414. }
  415. }
  416. errLabel:
  417. if( cmAudioFileDelete(&afH) != kOkAfRC )
  418. rc = cmErrMsg(&p->err,kAudioFileFailSrRC,"Synchronized audio file close failed.");
  419. return rc;
  420. }
  421. cmSrRC_t cmSyncRecdTest( cmCtx_t* ctx )
  422. {
  423. enum
  424. {
  425. kOkTestRC,
  426. kTestFailRC,
  427. };
  428. cmSrRC_t rc = kOkSrRC;
  429. const cmChar_t* srFn = "/home/kevin/temp/kr/sr/sr0.sr";
  430. const cmChar_t* aFn = "/home/kevin/temp/kr/sr/sync_af.aiff";
  431. cmErr_t err;
  432. cmSyncRecdH_t srH = cmSyncRecdNullHandle;
  433. cmErrSetup(&err,&ctx->rpt,"SyncRecdTest");
  434. if((rc = cmSyncRecdOpen(ctx, &srH, srFn )) != kOkSrRC )
  435. {
  436. cmErrMsg(&err,kTestFailRC,"Sync-recd open failed.");
  437. goto errLabel;
  438. }
  439. cmSyncRecdPrint(srH);
  440. cmSyncRecdAudioFile(srH,aFn);
  441. errLabel:
  442. if((rc = cmSyncRecdFinal(&srH)) != kOkSrRC )
  443. cmErrMsg(&err,kTestFailRC,"Sync-recd close failed.");
  444. return rc;
  445. }