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.

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. }