libcm is a C development framework with an emphasis on audio signal processing applications.
Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

cmSyncRecd.c 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  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. typedef enum
  13. {
  14. kInvalidSrId,
  15. kMidiSrId,
  16. kAudioSrId
  17. } cmSrTypeId_t;
  18. typedef struct cmSrMidi_str
  19. {
  20. cmTimeSpec_t timestamp;
  21. unsigned status;
  22. unsigned d0;
  23. unsigned d1;
  24. } cmSrMidi_t;
  25. typedef struct cmSrAudio_str
  26. {
  27. cmTimeSpec_t timestamp;
  28. unsigned smpIdx;
  29. } cmSrAudio_t;
  30. typedef struct cmSrRecd_str
  31. {
  32. cmSrTypeId_t tid;
  33. union
  34. {
  35. cmSrMidi_t m;
  36. cmSrAudio_t a;
  37. } u;
  38. } cmSrRecd_t;
  39. enum
  40. {
  41. kReadSrFl = 0x01, // This is a read (not a write) file
  42. kFileUUSrId = 0xf00d
  43. };
  44. typedef struct cmSr_str
  45. {
  46. cmErr_t err;
  47. cmFileH_t fH;
  48. cmAudioFileH_t afH;
  49. unsigned flags; // See kXXXSrFl
  50. cmSrRecd_t* cache; // cache[cn]
  51. cmSrAudio_t* map; //
  52. unsigned cn; // count of records in cache[].
  53. unsigned ci; // next cache recd index
  54. unsigned fn; // count of recds written to file
  55. long offs;
  56. } cmSr_t;
  57. cmSr_t* _cmSrHtoP( cmSyncRecdH_t h )
  58. {
  59. cmSr_t* p = (cmSr_t*)h.h;
  60. assert(p!=NULL);
  61. return p;
  62. }
  63. cmSrRC_t _cmSrWriteCache( cmSr_t* p )
  64. {
  65. if( cmFileWrite(p->fH,p->cache,p->ci * sizeof(cmSrRecd_t)) != kOkFileRC )
  66. return cmErrMsg(&p->err,kFileFailSrRC,"File write failed.");
  67. p->fn += p->ci;
  68. p->ci = 0;
  69. return kOkSrRC;
  70. }
  71. cmSrRC_t _cmSrFinal( cmSr_t* p )
  72. {
  73. cmSrRC_t rc = kOkSrRC;
  74. // write any remaining cache records
  75. if( cmIsFlag(p->flags,kReadSrFl) == false )
  76. {
  77. if((rc = _cmSrWriteCache(p)) == kOkSrRC )
  78. {
  79. if(cmFileSeek(p->fH,kBeginFileFl,p->offs) != kOkFileRC )
  80. rc = cmErrMsg(&p->err,kFileFailSrRC, "File seek fail on file offset positioning.");
  81. else
  82. if(cmFileWriteUInt(p->fH,&p->fn,1) != kOkFileRC )
  83. rc = cmErrMsg(&p->err,kFileFailSrRC, "File write failed on record count.");
  84. }
  85. }
  86. // release the audio file object
  87. if( cmAudioFileIsValid(p->afH) )
  88. if( cmAudioFileDelete(&p->afH) != kOkAfRC )
  89. return cmErrMsg(&p->err,kAudioFileFailSrRC,"Audio file object delete failed.");
  90. // release the sync-recd file object
  91. if( cmFileIsValid(p->fH) )
  92. if( cmFileClose(&p->fH) != kOkFileRC )
  93. return cmErrMsg(&p->err,kFileFailSrRC,"File close failed.");
  94. cmMemFree(p->cache);
  95. cmMemFree(p->map);
  96. cmMemFree(p);
  97. return rc;
  98. }
  99. cmSr_t* _cmSrAlloc( cmCtx_t* ctx, unsigned flags )
  100. {
  101. cmSr_t* p = cmMemAllocZ(cmSr_t,1);
  102. cmErrSetup(&p->err,&ctx->rpt,"SyncRecd");
  103. p->cn = 2048;
  104. p->cache = cmMemAllocZ(cmSrRecd_t,p->cn);
  105. return p;
  106. }
  107. cmSrRC_t cmSyncRecdCreate( cmCtx_t* ctx, cmSyncRecdH_t* hp, const cmChar_t* srFn, const cmChar_t* audioFn, double srate, unsigned chCnt, unsigned bits )
  108. {
  109. cmSrRC_t rc = kOkSrRC;
  110. cmRC_t afRC = kOkAfRC;
  111. assert( audioFn != NULL );
  112. if((rc = cmSyncRecdFinal(hp)) != kOkSrRC )
  113. return rc;
  114. cmSr_t* p = _cmSrAlloc(ctx,0);
  115. if( cmFileOpen(&p->fH,srFn,kWriteFileFl,&ctx->rpt) != kOkFileRC )
  116. {
  117. rc = cmErrMsg(&p->err,kFileFailSrRC,"Unable to create the sync-recd file '%s'.",cmStringNullGuard(srFn));
  118. goto errLabel;
  119. }
  120. if( cmAudioFileIsValid(p->afH = cmAudioFileNewCreate(audioFn,srate,bits,chCnt,&afRC,&ctx->rpt))==false)
  121. {
  122. rc = cmErrMsg(&p->err,kAudioFileFailSrRC,"Unable to create the sync-recd audio file '%s'.",cmStringNullGuard(audioFn));
  123. goto errLabel;
  124. }
  125. unsigned fileUUId = kFileUUSrId;
  126. unsigned audioFnCnt = strlen(audioFn)+1;
  127. if( cmFileWriteUInt(p->fH,&fileUUId,1) != kOkFileRC )
  128. {
  129. rc = cmErrMsg(&p->err,kFileFailSrRC,"File write failed on UUID.");
  130. goto errLabel;
  131. }
  132. if( cmFileWriteUInt(p->fH,&audioFnCnt,1) != kOkFileRC )
  133. {
  134. rc = cmErrMsg(&p->err,kFileFailSrRC,"File write failed on audio file length write count.");
  135. goto errLabel;
  136. }
  137. if( cmFileWriteChar(p->fH,audioFn,audioFnCnt) != kOkFileRC )
  138. {
  139. rc = cmErrMsg(&p->err,kFileFailSrRC,"File write failed on audio file string.");
  140. goto errLabel;
  141. }
  142. if( cmFileTell(p->fH,&p->offs) != kOkFileRC )
  143. {
  144. rc = cmErrMsg(&p->err,kFileFailSrRC,"Unable to determine file offset.");
  145. goto errLabel;
  146. }
  147. if( cmFileWriteUInt(p->fH,&p->fn,1) != kOkFileRC )
  148. {
  149. rc = cmErrMsg(&p->err,kFileFailSrRC,"File write failed on initial record count.");
  150. goto errLabel;
  151. }
  152. hp->h = p;
  153. errLabel:
  154. if( rc != kOkSrRC )
  155. _cmSrFinal(p);
  156. return rc;
  157. }
  158. cmSrRC_t cmSyncRecdOpen( cmCtx_t* ctx, cmSyncRecdH_t* hp, const cmChar_t* srFn )
  159. {
  160. cmSrRC_t rc = kOkSrRC;
  161. cmRC_t afRC = kOkAfRC;
  162. unsigned fileUUId = cmInvalidId;
  163. unsigned audioFnCnt = 0;
  164. cmChar_t* audioFn = NULL;
  165. cmAudioFileInfo_t afInfo;
  166. unsigned i;
  167. unsigned acnt = 0;
  168. unsigned mcnt = 0;
  169. unsigned* tiV = NULL;
  170. if((rc = cmSyncRecdFinal(hp)) != kOkSrRC )
  171. return rc;
  172. cmSr_t* p = _cmSrAlloc(ctx,kReadSrFl);
  173. if( cmFileOpen(&p->fH,srFn,kReadFileFl,&ctx->rpt) != kOkFileRC )
  174. {
  175. rc = cmErrMsg(&p->err,kFileFailSrRC,"Unable to open the sync-recd file '%s'.",cmStringNullGuard(srFn));
  176. goto errLabel;
  177. }
  178. if( cmFileReadUInt(p->fH,&fileUUId,1) != kOkFileRC )
  179. {
  180. rc = cmErrMsg(&p->err,kFileFailSrRC,"File read failed on UUId.");
  181. goto errLabel;
  182. }
  183. if( cmFileReadUInt(p->fH,&audioFnCnt,1) != kOkFileRC )
  184. {
  185. rc = cmErrMsg(&p->err,kFileFailSrRC,"File read failed on audio file name count.");
  186. goto errLabel;
  187. }
  188. audioFn = cmMemAllocZ(cmChar_t,audioFnCnt);
  189. if( cmFileReadChar(p->fH,audioFn,audioFnCnt) != kOkFileRC )
  190. {
  191. rc = cmErrMsg(&p->err,kFileFailSrRC,"File read failed on audio file string.");
  192. goto errLabel;
  193. }
  194. if( cmFileReadUInt(p->fH,&p->fn,1) != kOkFileRC )
  195. {
  196. rc = cmErrMsg(&p->err,kFileFailSrRC,"File read failed on record count.");
  197. goto errLabel;
  198. }
  199. // store the file offset to the first recd
  200. if( cmFileTell(p->fH,&p->offs) != kOkFileRC )
  201. {
  202. rc = cmErrMsg(&p->err,kFileFailSrRC,"Unable to determine the current file offset.");
  203. goto errLabel;
  204. }
  205. // read each file - and count the types
  206. for(i=0; i<p->fn; ++i)
  207. {
  208. cmSrRecd_t r;
  209. if( cmFileRead(p->fH,&r,sizeof(r)) != kOkFileRC )
  210. {
  211. rc = cmErrMsg(&p->err,kFileFailSrRC,"Unable to read the record at index %i.");
  212. goto errLabel;
  213. }
  214. switch(r.tid)
  215. {
  216. case kMidiSrId:
  217. mcnt += 1;
  218. break;
  219. case kAudioSrId:
  220. acnt += 1;
  221. break;
  222. default:
  223. { assert(0); }
  224. }
  225. }
  226. // rewind to the begining of the records
  227. if( cmFileSeek(p->fH,kBeginFileFl,p->offs) != kOkFileRC )
  228. {
  229. rc = cmErrMsg(&p->err,kFileFailSrRC,"Unable to seek to first recd offset.");
  230. goto errLabel;
  231. }
  232. // allocate space to hold the MIDI records
  233. p->cn = mcnt;
  234. p->ci = 0;
  235. p->cache = cmMemResizeZ(cmSrRecd_t,p->cache,p->cn);
  236. p->map = cmMemAllocZ(cmSrAudio_t,p->cn);
  237. tiV = cmMemAllocZ(unsigned,p->cn);
  238. for(i=0; p->ci<p->cn && i<p->fn; ++i)
  239. {
  240. if( cmFileRead(p->fH,p->cache + p->ci,sizeof(cmSrRecd_t)) != kOkFileRC )
  241. {
  242. rc = cmErrMsg(&p->err,kFileFailSrRC,"Unable to read the record at index %i.");
  243. goto errLabel;
  244. }
  245. if( p->cache[p->ci].tid == kMidiSrId )
  246. p->ci += 1;
  247. }
  248. // assert that all the MIDI records were read
  249. assert( p->ci == p->cn);
  250. // rewind to the first recd
  251. if( cmFileSeek(p->fH,kBeginFileFl,p->offs) != kOkFileRC )
  252. {
  253. rc = cmErrMsg(&p->err,kFileFailSrRC,"Unable to seek to first recd offset.");
  254. goto errLabel;
  255. }
  256. // for each recd in the file
  257. for(i=0; i<p->fn; ++i)
  258. {
  259. cmSrRecd_t r;
  260. if( cmFileRead(p->fH,&r,sizeof(r)) != kOkFileRC )
  261. {
  262. rc = cmErrMsg(&p->err,kFileFailSrRC,"Unable to read the record at index %i.");
  263. goto errLabel;
  264. }
  265. // if this is an audio record
  266. if( r.tid == kAudioSrId )
  267. {
  268. unsigned j;
  269. // for each midi record
  270. for(j=0; j<p->cn; ++j)
  271. {
  272. // measure the time interval between this midi and this audio recd
  273. unsigned time_interval_micros = cmTimeAbsElapsedMicros(&r.u.a.timestamp,&p->cache[j].u.m.timestamp);
  274. // if the audio recd is closer to this midi recd than prior audio records ...
  275. if( tiV[j] < time_interval_micros || i==0 )
  276. {
  277. // ... then store the audio time stamp in the map
  278. tiV[j] = time_interval_micros;
  279. p->map->timestamp = r.u.a.timestamp;
  280. p->map->smpIdx = r.u.a.smpIdx;
  281. }
  282. }
  283. }
  284. }
  285. // open the audio file
  286. if( cmAudioFileIsValid(p->afH = cmAudioFileNewOpen(audioFn,&afInfo,&afRC,&ctx->rpt ))==false)
  287. {
  288. rc = cmErrMsg(&p->err,kAudioFileFailSrRC,"Unable to open the sync-recd audio file '%s'.",cmStringNullGuard(audioFn));
  289. goto errLabel;
  290. }
  291. p->flags = cmSetFlag(p->flags,kReadSrFl);
  292. hp->h = p;
  293. errLabel:
  294. cmMemFree(tiV);
  295. cmMemFree(audioFn);
  296. if( rc != kOkSrRC )
  297. _cmSrFinal(p);
  298. return rc;
  299. }
  300. cmSrRC_t cmSyncRecdFinal( cmSyncRecdH_t* hp )
  301. {
  302. cmSrRC_t rc = kOkSrRC;
  303. if( hp==NULL || cmSyncRecdIsValid(*hp)==false)
  304. return rc;
  305. cmSr_t* p = _cmSrHtoP(*hp);
  306. if((rc = _cmSrFinal(p)) != kOkSrRC )
  307. return rc;
  308. hp->h = NULL;
  309. return rc;
  310. }
  311. bool cmSyncRecdIsValid( cmSyncRecdH_t h )
  312. { return h.h != NULL; }
  313. cmSrRC_t cmSyncRecdMidiWrite( cmSyncRecdH_t h, const cmTimeSpec_t* timestamp, unsigned status, unsigned d0, unsigned d1 )
  314. {
  315. cmSrRC_t rc = kOkSrRC;
  316. cmSr_t* p = _cmSrHtoP(h);
  317. cmSrRecd_t* rp = p->cache + p->ci;
  318. rp->tid = kMidiSrId;
  319. rp->u.m.timestamp = *timestamp;
  320. rp->u.m.status = status;
  321. rp->u.m.d0 = d0;
  322. rp->u.m.d1 = d1;
  323. p->ci += 1;
  324. if( p->ci == p->cn )
  325. rc = _cmSrWriteCache(p);
  326. return rc;
  327. }
  328. cmSrRC_t cmSyncRecdAudioWrite( cmSyncRecdH_t h, const cmTimeSpec_t* timestamp, unsigned smpIdx, const cmSample_t* ch[], unsigned chCnt, unsigned frmCnt )
  329. {
  330. cmSrRC_t rc = kOkSrRC;
  331. cmSr_t* p = _cmSrHtoP(h);
  332. cmSrRecd_t* rp = p->cache + p->ci;
  333. rp->tid = kAudioSrId;
  334. rp->u.a.timestamp = *timestamp;
  335. rp->u.a.smpIdx = smpIdx;
  336. p->ci += 1;
  337. if( p->ci == p->cn )
  338. if((rc = _cmSrWriteCache(p)) != kOkSrRC )
  339. goto errLabel;
  340. if( cmAudioFileWriteSample(p->afH,frmCnt,chCnt,(cmSample_t**)ch) != kOkAfRC )
  341. rc = cmErrMsg(&p->err,kAudioFileFailSrRC,"Audio file write failed.");
  342. errLabel:
  343. return rc;
  344. }