libcm is a C development framework with an emphasis on audio signal processing applications.
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

cmTakeSeqBldr.c 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507
  1. #include "cmPrefix.h"
  2. #include "cmGlobal.h"
  3. #include "cmFloatTypes.h"
  4. #include "cmRpt.h"
  5. #include "cmErr.h"
  6. #include "cmCtx.h"
  7. #include "cmMem.h"
  8. #include "cmMallocDebug.h"
  9. #include "cmLinkedHeap.h"
  10. #include "cmJson.h"
  11. #include "cmTime.h"
  12. #include "cmMidi.h"
  13. #include "cmMidiFile.h"
  14. #include "cmAudioFile.h"
  15. #include "cmTimeLine.h"
  16. #include "cmSymTbl.h"
  17. #include "cmScore.h"
  18. #include "cmTakeSeqBldr.h"
  19. // Score track record: Map a score event to a MIDI event.
  20. typedef struct cmScTrkMidiTsb_str
  21. {
  22. unsigned mni; // midi note index as an offset from the take marker
  23. unsigned scEvtIdx; // score event index this not is assoc'd with or -1 if it did not match
  24. unsigned flags; // flags from cmScMatcherResult_t
  25. } cmScTrkMidiTsb_t;
  26. // Score Tracking info. from a single take (time-line marker)
  27. typedef struct cmScTrkTakeTsb_str
  28. {
  29. unsigned markerUid; // marker time line uid assoc'd with this take
  30. cmScTrkMidiTsb_t* midiV; // midiV[midiN] score to midi file map recd. array.
  31. unsigned midiN;
  32. bool failFl;
  33. } cmScTrkTakeTsb_t;
  34. enum
  35. {
  36. kNoteTsbFl = 0x01,
  37. kPedalTsbFl = 0x02,
  38. kEnableTsbFl = 0x04
  39. };
  40. //
  41. typedef struct cmMidiEvt_str
  42. {
  43. unsigned srcId; // marker uid or -1 if this event was manually inserted
  44. unsigned flags; // note | pedal | enable
  45. struct cmMidiEvt_str* ref; // previous MIDI event in time
  46. unsigned offsetSmp; // time offset from *ref
  47. unsigned durSmp; // duration of this MIDI event
  48. unsigned d0; // d0 MIDI channel msg data.
  49. unsigned d1; // d1 MIDI channel msg data
  50. struct cmMidiEvt_str* link; // pointer to next MIDI event in list
  51. } cmMidiEvt_t;
  52. // This record represents a note or pedal score event
  53. typedef struct cmScEvtTsb_str
  54. {
  55. unsigned flags; // note | pedal
  56. unsigned scEvtIdx; // score event index (into scH)
  57. cmMidiEvt_t* evtList; // list of alternate MIDI events which may render this event
  58. } cmScEvtTsb_t;
  59. // This record contains all the score events and and score synchronized MIDI events
  60. // associated with a given take. Each call to cmTakeSeqBldrLoadTake() creates
  61. // one of these records.
  62. typedef struct cmTakeScEvtArrayTsb_str
  63. {
  64. unsigned tlMarkerUid; // time-line marker uid associated with this take
  65. cmScEvtTsb_t* scEvtV; // scEvtV[scEvtN] array of score events contained by this take
  66. unsigned scEvtN; // count of score events in this take
  67. } cmTakeScEvtArrayTsb_t;
  68. typedef struct
  69. {
  70. cmCtx_t ctx; // application context
  71. cmErr_t err; // internal error object
  72. cmJsonH_t jsH; // JSON tree used to hold score tracker info.
  73. const cmChar_t* tlFn; // time line filename
  74. const cmChar_t* scFn; // score file name
  75. const cmChar_t* tlPrefixPath; // path to time line audio and MIDI files
  76. cmTlH_t tlH; // time-line handle
  77. cmScH_t scH; // score handle
  78. cmScTrkTakeTsb_t* scTrkTakeV; // score tracker scTrkTakeV[ scTrkTakeN ]
  79. unsigned scTrkTakeN;
  80. cmTakeScEvtArrayTsb_t* takes; // list of scEvt arrays used by this sequence
  81. cmTakeScEvtArrayTsb_t* manual; // list of manually inserted MIDI events
  82. cmTakeScEvtArrayTsb_t* out; // render list
  83. } cmTsb_t;
  84. cmTakeSeqBldrH_t cmTakeSeqBldrNullHandle = cmSTATIC_NULL_HANDLE;
  85. cmTsb_t* _cmTsbHandleToPtr( cmTakeSeqBldrH_t h )
  86. {
  87. cmTsb_t* p = (cmTsb_t*)h.h;
  88. return p;
  89. }
  90. cmTsbRC_t _cmTsbScoreTrkFree( cmTsb_t* p )
  91. {
  92. cmTsbRC_t rc = kOkTsbRC;
  93. unsigned i;
  94. if( cmJsonFinalize(&p->jsH) != kOkJsRC )
  95. {
  96. rc = cmErrMsg(&p->err,kJsonFailTsbRC,"JSON object finalize failed.");
  97. goto errLabel;
  98. }
  99. for(i=0; i<p->scTrkTakeN; ++i)
  100. cmMemPtrFree(&p->scTrkTakeV[i].midiV);
  101. cmMemPtrFree(&p->scTrkTakeV);
  102. if( cmTimeLineFinalize(&p->tlH) != kOkTlRC )
  103. rc = cmErrMsg(&p->err,kTimeLineFailTsbRC,"Time line object finalize failed.");
  104. if( cmScoreFinalize(&p->scH) != kOkScRC )
  105. rc = cmErrMsg(&p->err,kScoreFailTsbRC,"Score finalize failed.");
  106. errLabel:
  107. return rc;
  108. }
  109. cmTsbRC_t _cmTsbFree( cmTsb_t* p )
  110. {
  111. cmTsbRC_t rc = kOkTsbRC;
  112. if((rc = _cmTsbScoreTrkFree(p)) != kOkTsbRC )
  113. goto errLabel;
  114. cmMemFree(p);
  115. errLabel:
  116. return rc;
  117. }
  118. cmTsbRC_t _cmTsbLoadScoreTrkFile( cmTsb_t* p, const cmChar_t* scoreTrkFn )
  119. {
  120. cmTsbRC_t rc = kOkTsbRC;
  121. cmJsonNode_t* tkArrObj = NULL;
  122. cmJsRC_t jsRC = kOkJsRC;
  123. const cmChar_t* errMsg = NULL;
  124. unsigned i;
  125. // initialize the TSB json object
  126. if(( rc = cmJsonInitializeFromFile(&p->jsH,scoreTrkFn,&p->ctx)) != kOkJsRC )
  127. {
  128. rc = cmErrMsg(&p->err,kJsonFailTsbRC,"The Take Sequence Builder JSON file object could not be initialized from '%s'.",cmStringNullGuard(scoreTrkFn));
  129. goto errLabel;
  130. }
  131. // parse the header
  132. if((jsRC = cmJsonMemberValues( cmJsonRoot(p->jsH), &errMsg,
  133. "timeLineFn", kStringTId, &p->tlFn,
  134. "scoreFn", kStringTId, &p->scFn,
  135. "tlPrefixPath", kStringTId, &p->tlPrefixPath,
  136. "takeArray", kArrayTId | kOptArgJsFl, &tkArrObj,
  137. NULL )) != kOkJsRC )
  138. {
  139. if( jsRC == kNodeNotFoundJsRC && errMsg != NULL )
  140. rc = cmErrMsg(&p->err,kParseFailTsbRC,"JSON file header parse failed missing required field:'%s'",errMsg);
  141. else
  142. rc = cmErrMsg(&p->err,kParseFailTsbRC,"JSON file header parse failed.");
  143. goto errLabel;
  144. }
  145. // count of take records
  146. p->scTrkTakeN = cmJsonChildCount(tkArrObj);
  147. // array of take records
  148. p->scTrkTakeV = cmMemAllocZ(cmScTrkTakeTsb_t,p->scTrkTakeN);
  149. // for each take record
  150. for(i=0; i<p->scTrkTakeN; ++i)
  151. {
  152. cmJsonNode_t* takeObj = NULL;
  153. cmJsonNode_t* noteArrObj = NULL;
  154. unsigned j;
  155. // get a pointer to the take record JSON object
  156. if((takeObj = cmJsonArrayElement(tkArrObj,i)) == NULL )
  157. {
  158. rc = cmErrMsg(&p->err,kParseFailTsbRC,"Take record header at index %i access failed.",i);
  159. goto errLabel;
  160. }
  161. // parse the take record
  162. if((jsRC = cmJsonMemberValues( takeObj, &errMsg,
  163. "markerUid",kIntTId, &p->scTrkTakeV[i].markerUid,
  164. "failFl", kIntTId, &p->scTrkTakeV[i].failFl,
  165. "array", kArrayTId, &noteArrObj,
  166. NULL)) != kOkJsRC )
  167. {
  168. if( jsRC == kNodeNotFoundJsRC && errMsg != NULL )
  169. rc = cmErrMsg(&p->err,kParseFailTsbRC,"JSON file take record parse failed missing required field:'%s'",errMsg);
  170. else
  171. rc = cmErrMsg(&p->err,kParseFailTsbRC,"JSON file take record parse failed.");
  172. goto errLabel;
  173. }
  174. // get the count of note records
  175. p->scTrkTakeV[i].midiN = cmJsonChildCount(noteArrObj);
  176. // allocate a note record array for this take
  177. p->scTrkTakeV[i].midiV = cmMemAllocZ(cmScTrkMidiTsb_t, p->scTrkTakeV[i].midiN);
  178. // for each note record
  179. for(j=0; j<p->scTrkTakeV[i].midiN; ++j)
  180. {
  181. cmJsonNode_t* noteObj = NULL;
  182. // get the note record JSON object
  183. if((noteObj = cmJsonArrayElement(noteArrObj,j)) == NULL )
  184. {
  185. rc = cmErrMsg(&p->err,kParseFailTsbRC,"Access failed for note record at index %i at take index %i.",j,i);
  186. goto errLabel;
  187. }
  188. // parse the note record
  189. if((jsRC = cmJsonMemberValues( noteObj, &errMsg,
  190. "mni", kIntTId, &p->scTrkTakeV[i].midiV[j].mni,
  191. "scEvtIdx", kIntTId, &p->scTrkTakeV[i].midiV[j].scEvtIdx,
  192. "flags", kIntTId, &p->scTrkTakeV[i].midiV[j].flags,
  193. NULL)) != kOkJsRC )
  194. {
  195. if( jsRC == kNodeNotFoundJsRC && errMsg != NULL )
  196. rc = cmErrMsg(&p->err,kParseFailTsbRC,"JSON file note record parse failed missing required field:'%s'",errMsg);
  197. else
  198. rc = cmErrMsg(&p->err,kParseFailTsbRC,"JSON file note record parse failed.");
  199. goto errLabel;
  200. }
  201. }
  202. }
  203. errLabel:
  204. if( rc != kOkTsbRC )
  205. rc = _cmTsbScoreTrkFree(p);
  206. return rc;
  207. }
  208. // Return the count of score events inside a given marker.
  209. unsigned _cmTsbScoreTrkMarkerEventCount( cmTsb_t* p, unsigned markUid )
  210. {
  211. unsigned i,j;
  212. unsigned minScEvtIdx = INT_MAX;
  213. unsigned maxScEvtIdx = 0;
  214. for(i=0; i<p->scTrkTakeN; ++i)
  215. for(j=0; j<p->scTrkTakeV[i].midiN; ++j)
  216. if( p->scTrkTakeV[i].midiV[j].scEvtIdx != cmInvalidIdx )
  217. {
  218. if( p->scTrkTakeV[i].midiV[j].scEvtIdx < minScEvtIdx )
  219. minScEvtIdx = p->scTrkTakeV[i].midiV[j].scEvtIdx;
  220. if( p->scTrkTakeV[i].midiV[j].scEvtIdx > maxScEvtIdx )
  221. maxScEvtIdx = p->scTrkTakeV[i].midiV[j].scEvtIdx;
  222. }
  223. if( maxScEvtIdx < minScEvtIdx )
  224. return 0;
  225. return (maxScEvtIdx - minScEvtIdx) + 1;
  226. }
  227. cmTsbRC_t cmTakeSeqBldrAlloc( cmCtx_t* ctx, cmTakeSeqBldrH_t* hp )
  228. {
  229. cmTsbRC_t rc;
  230. if((rc = cmTakeSeqBldrFree(hp)) != kOkTsbRC )
  231. return kOkTsbRC;
  232. cmTsb_t* p = cmMemAllocZ(cmTsb_t,1);
  233. cmErrSetup(&p->err,&ctx->rpt,"TakeSeqBldr");
  234. p->ctx = *ctx;
  235. hp->h = p;
  236. return rc;
  237. }
  238. cmTsbRC_t cmTakeSeqBldrAllocFn( cmCtx_t* ctx, cmTakeSeqBldrH_t* hp, const cmChar_t* scoreTrkFn )
  239. {
  240. cmTsbRC_t rc;
  241. if((rc = cmTakeSeqBldrAlloc(ctx,hp)) != kOkTsbRC )
  242. return rc;
  243. if((rc = cmTakeSeqBldrInitialize(*hp,scoreTrkFn)) != kOkTsbRC )
  244. return rc;
  245. return rc;
  246. }
  247. cmTsbRC_t cmTakeSeqBldrFree( cmTakeSeqBldrH_t* hp )
  248. {
  249. cmRC_t rc = kOkTsbRC;
  250. if( hp == NULL || cmTakeSeqBldrIsValid(*hp)==false )
  251. return kOkTsbRC;
  252. cmTsb_t* p = _cmTsbHandleToPtr(*hp);
  253. if((rc = _cmTsbFree(p)) != kOkTsbRC )
  254. return rc;
  255. hp->h = NULL;
  256. return rc;
  257. }
  258. bool cmTakeSeqBldrIsValid( cmTakeSeqBldrH_t h )
  259. { return h.h != NULL; }
  260. cmTsbRC_t cmTakeSeqBldrInitialize( cmTakeSeqBldrH_t h, const cmChar_t* scoreTrkFn )
  261. {
  262. cmTsbRC_t rc = kOkTsbRC;
  263. cmTsb_t* p = _cmTsbHandleToPtr(h);
  264. if(( rc = _cmTsbLoadScoreTrkFile( p, scoreTrkFn )) != kOkTsbRC )
  265. return rc;
  266. if( cmTimeLineInitializeFromFile(&p->ctx, &p->tlH, NULL, NULL, p->tlFn, p->tlPrefixPath ) != kOkTlRC )
  267. {
  268. rc = cmErrMsg(&p->err,kTimeLineFailTsbRC,"The time-line file '%s' could not be loaded.",p->tlFn);
  269. goto errLabel;
  270. }
  271. if( cmScoreInitialize(&p->ctx, &p->scH, p->scFn, 0, NULL, 0, NULL, NULL, cmSymTblNullHandle ) != kOkScRC )
  272. {
  273. rc = cmErrMsg(&p->err,kScoreFailTsbRC,"The score file '%s' could not be loaded.",p->scFn);
  274. goto errLabel;
  275. }
  276. errLabel:
  277. if( rc != kOkTsbRC )
  278. _cmTsbScoreTrkFree(p);
  279. return rc;
  280. }
  281. cmTsbRC_t cmTakeSeqBldrLoadTake( cmTakeSeqBldrH_t h, unsigned tlMarkUid, bool overwriteFL )
  282. {
  283. cmTsbRC_t rc = kOkTsbRC;
  284. cmTsb_t* p = _cmTsbHandleToPtr(h);
  285. cmTlMarker_t* mark = NULL;
  286. cmTlMidiFile_t* mf = NULL;
  287. cmMidiFileH_t mfH = cmMidiFileNullHandle;
  288. unsigned scEvtN = 0;
  289. cmScEvtTsb_t* scEvtV = NULL;
  290. // get a pointer to the time-line marker object
  291. if((mark = cmTlMarkerObjPtr( p->tlH, cmTimeLineIdToObj( p->tlH, cmInvalidId, tlMarkUid))) == NULL )
  292. {
  293. rc = cmErrMsg(&p->err,kInvalidArgTsbRC,"The time-line marker uid '%i' is not valid.",tlMarkUid);
  294. goto errLabel;
  295. }
  296. // get the count of score events in the take marker
  297. if((scEvtN = _cmTsbScoreTrkMarkerEventCount(p,tlMarkUid)) == 0 )
  298. {
  299. rc = cmErrMsg(&p->err,kInvalidArgTsbRC,"The selected take marker does not appear to contain any score events.");
  300. goto errLabel;
  301. }
  302. // allocate a score event array
  303. scEvtV = cmMemAllocZ(cmScEvtTsb_t,scEvtN);
  304. // get the name of the MIDI file which contains the marker
  305. if((mf = cmTimeLineMidiFileAtTime( p->tlH, mark->obj.seqId, mark->obj.seqSmpIdx )) == NULL )
  306. {
  307. rc = cmErrMsg(&p->err,kInvalidArgTsbRC,"The time-line marker '%i' does not intersect with a MIDI file.",tlMarkUid);
  308. goto errLabel;
  309. }
  310. // open the MIDI file
  311. if( cmMidiFileOpen( cmMidiFileName(mf->h), &mfH, &p->ctx ) != kOkMfRC )
  312. {
  313. rc = cmErrMsg(&p->err,kInvalidArgTsbRC,"The MIDI file '%s' could not be opened.", cmStringNullGuard(cmMidiFileName(mf->h)));
  314. goto errLabel;
  315. }
  316. // convert the dtick field to absolute sample indexes
  317. cmMidiFileTickToSamples( mfH, cmTimeLineSampleRate(p->tlH), true );
  318. // calculate MIDI note and pedal durations (see cmMidiChMsg_t.durTicks)
  319. cmMidiFileCalcNoteDurations( mfH );
  320. // convert the marker beg/end sample position to be relative to the MIDI file start time
  321. unsigned bsi = mark->obj.seqSmpIdx - mf->obj.seqSmpIdx;
  322. unsigned esi = mark->obj.seqSmpIdx + mark->obj.durSmpCnt - mf->obj.seqSmpIdx;
  323. unsigned i = 0;
  324. unsigned n = cmMidiFileMsgCount(mfH);
  325. const cmMidiTrackMsg_t** a = cmMidiFileMsgArray(mfH);
  326. // seek to the first MIDI msg after sample index bsi in the MIDI file
  327. for(i=0; i<n; ++i)
  328. if( a[i]->dtick >= bsi )
  329. break;
  330. // if bsi is after the file then the MIDI file finished before the marker
  331. if( i == n )
  332. {
  333. rc = cmErrMsg(&p->err,kInvalidArgTsbRC,"No MIDI events were found in the marker.");
  334. goto errLabel;
  335. }
  336. // for each MIDI message between bsi and esi
  337. for(; i<n && a[i]->dtick < esi; ++i)
  338. {
  339. const cmMidiTrackMsg_t* m = a[i];
  340. switch( m->status )
  341. {
  342. case kNoteOffMdId:
  343. case kNoteOnMdId:
  344. case kCtlMdId:
  345. break;
  346. }
  347. }
  348. errLabel:
  349. if( cmMidiFileClose(&mfH) != kOkMfRC )
  350. rc = cmErrMsg(&p->err,kMidiFileFailTsbRC,"MIDI file close failed.");
  351. if( rc != kOkTsbRC )
  352. {
  353. cmMemFree(scEvtV);
  354. }
  355. return rc;
  356. }
  357. cmTsbRC_t cmTakeSeqBldrUnloadTake( cmTakeSeqBldrH_t h, unsigned tlMarkUid )
  358. {
  359. cmTsbRC_t rc = kOkTsbRC;
  360. return rc;
  361. }
  362. cmTsbRC_t cmTakeSeqBldrInsertScoreNotes( cmTakeSeqBldrH_t h, unsigned begScEvtIdx, unsigned endScEvtId )
  363. {
  364. cmTsbRC_t rc = kOkTsbRC;
  365. return rc;
  366. }
  367. cmTsbRC_t cmTakeSeqBldrRemoveScoreNotes( cmTakeSeqBldrH_t h, unsigned begScEvtIdx, unsigned endScEvtId )
  368. {
  369. cmTsbRC_t rc = kOkTsbRC;
  370. return rc;
  371. }
  372. cmTsbRC_t cmTakeSeqBldrSelectEnable( cmTakeSeqBldrH_t h, unsigned flags, unsigned id, bool selectFl )
  373. {
  374. cmTsbRC_t rc = kOkTsbRC;
  375. return rc;
  376. }
  377. cmTsbRC_t cmTakeSeqBldrEnableNote( cmTakeSeqBldrH_t h, unsigned ssqId, bool enableFl )
  378. {
  379. cmTsbRC_t rc = kOkTsbRC;
  380. return rc;
  381. }
  382. cmTsbRC_t cmTakeSeqBldrMoveNote( cmTakeSeqBldrH_t h, unsigned ssqId, int deltaSmpIdx )
  383. {
  384. cmTsbRC_t rc = kOkTsbRC;
  385. return rc;
  386. }
  387. cmTsbRC_t cmTakeSeqBldrWriteMidiFile( cmTakeSeqBldrH_t h, const char* fn )
  388. {
  389. cmTsbRC_t rc = kOkTsbRC;
  390. return rc;
  391. }
  392. cmTsbRC_t cmTakeSeqBldrTest( cmCtx_t* ctx )
  393. {
  394. const cmChar_t* scoreTrkFn = "/home/kevin/src/cmkc/src/kc/data/assoc0.js";
  395. cmTakeSeqBldrH_t tsbH = cmTakeSeqBldrNullHandle;
  396. cmTsbRC_t tsbRC = kOkTsbRC;
  397. if((tsbRC = cmTakeSeqBldrAllocFn(ctx, &tsbH, scoreTrkFn )) != kOkTsbRC )
  398. return cmErrMsg(&ctx->err,tsbRC,"TSB Allocate and parse '%s' failed.",scoreTrkFn);
  399. if((tsbRC = cmTakeSeqBldrFree(&tsbH)) != kOkTsbRC )
  400. return cmErrMsg(&ctx->err,tsbRC,"TSB Free failed.");
  401. return tsbRC;
  402. }