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.

cmTakeSeqBldr.c 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  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. // 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. } cmTsb_t;
  83. cmTakeSeqBldrH_t cmTakeSeqBldrNullHandle = cmSTATIC_NULL_HANDLE;
  84. cmTsb_t* _cmTsbHandleToPtr( cmTakeSeqBldrH_t h )
  85. {
  86. cmTsb_t* p = (cmTsb_t*)h.h;
  87. return p;
  88. }
  89. cmTsbRC_t _cmTsbScoreTrkFree( cmTsb_t* p )
  90. {
  91. cmTsbRC_t rc = kOkTsbRC;
  92. unsigned i;
  93. if( cmJsonFinalize(&p->jsH) != kOkJsRC )
  94. {
  95. rc = cmErrMsg(&p->err,kJsonFailTsbRC,"JSON object finalize failed.");
  96. goto errLabel;
  97. }
  98. for(i=0; i<p->scTrkTakeN; ++i)
  99. cmMemPtrFree(&p->scTrkTakeV[i].midiV);
  100. cmMemPtrFree(&p->scTrkTakeV);
  101. if( cmTimeLineFinalize(&p->tlH) != kOkTlRC )
  102. rc = cmErrMsg(&p->err,kTimeLineFailTsbRC,"Time line object finalize failed.");
  103. if( cmScoreFinalize(&p->scH) != kOkScRC )
  104. rc = cmErrMsg(&p->err,kScoreFailTsbRC,"Score finalize failed.");
  105. errLabel:
  106. return rc;
  107. }
  108. cmTsbRC_t _cmTsbFree( cmTsb_t* p )
  109. {
  110. cmTsbRC_t rc = kOkTsbRC;
  111. if((rc = _cmTsbScoreTrkFree(p)) != kOkTsbRC )
  112. goto errLabel;
  113. cmMemFree(p);
  114. errLabel:
  115. return rc;
  116. }
  117. cmTsbRC_t _cmTsbLoadScoreTrkFile( cmTsb_t* p, const cmChar_t* scoreTrkFn )
  118. {
  119. cmTsbRC_t rc = kOkTsbRC;
  120. cmJsonNode_t* tkArrObj = NULL;
  121. cmJsRC_t jsRC = kOkJsRC;
  122. const cmChar_t* errMsg = NULL;
  123. unsigned i;
  124. // initialize the TSB json object
  125. if(( rc = cmJsonInitializeFromFile(&p->jsH,scoreTrkFn,&p->ctx)) != kOkJsRC )
  126. {
  127. rc = cmErrMsg(&p->err,kJsonFailTsbRC,"The Take Sequence Builder JSON file object could not be initialized from '%s'.",cmStringNullGuard(scoreTrkFn));
  128. goto errLabel;
  129. }
  130. // parse the header
  131. if((jsRC = cmJsonMemberValues( cmJsonRoot(p->jsH), &errMsg,
  132. "timeLineFn", kStringTId, &p->tlFn,
  133. "scoreFn", kStringTId, &p->scFn,
  134. "tlPrefixPath", kStringTId, &p->tlPrefixPath,
  135. "takeArray", kArrayTId | kOptArgJsFl, &tkArrObj,
  136. 0 )) != kOkJsRC )
  137. {
  138. if( jsRC == kNodeNotFoundJsRC && errMsg != NULL )
  139. rc = cmErrMsg(&p->err,kParseFailTsbRC,"JSON file header parse failed missing required field:'%s'",errMsg);
  140. else
  141. rc = cmErrMsg(&p->err,kParseFailTsbRC,"JSON file header parse failed.");
  142. goto errLabel;
  143. }
  144. // count of take records
  145. p->scTrkTakeN = cmJsonChildCount(tkArrObj);
  146. // array of take records
  147. p->scTrkTakeV = cmMemAllocZ(cmScTrkTakeTsb_t,p->scTrkTakeN);
  148. // for each take record
  149. for(i=0; i<p->scTrkTakeN; ++i)
  150. {
  151. cmJsonNode_t* takeObj = NULL;
  152. cmJsonNode_t* noteArrObj = NULL;
  153. unsigned j;
  154. // get a pointer to the take record JSON object
  155. if((takeObj = cmJsonArrayElement(tkArrObj,i)) == NULL )
  156. {
  157. rc = cmErrMsg(&p->err,kParseFailTsbRC,"Take record header at index %i access failed.",i);
  158. goto errLabel;
  159. }
  160. // parse the take record
  161. if((jsRC = cmJsonMemberValues( takeObj, &errMsg,
  162. "markerUid",kIntTId, &p->scTrkTakeV[i].markerUid,
  163. "failFl", kIntTId, &p->scTrkTakeV[i].failFl,
  164. "array", kArrayTId, &noteArrObj,
  165. 0)) != kOkJsRC )
  166. {
  167. if( jsRC == kNodeNotFoundJsRC && errMsg != NULL )
  168. rc = cmErrMsg(&p->err,kParseFailTsbRC,"JSON file take record parse failed missing required field:'%s'",errMsg);
  169. else
  170. rc = cmErrMsg(&p->err,kParseFailTsbRC,"JSON file take record parse failed.");
  171. goto errLabel;
  172. }
  173. // get the count of note records
  174. p->scTrkTakeV[i].midiN = cmJsonChildCount(noteArrObj);
  175. // allocate a note record array for this take
  176. p->scTrkTakeV[i].midiV = cmMemAllocZ(cmScTrkMidiTsb_t, p->scTrkTakeV[i].midiN);
  177. // for each note record
  178. for(j=0; j<p->scTrkTakeV[i].midiN; ++j)
  179. {
  180. cmJsonNode_t* noteObj = NULL;
  181. // get the note record JSON object
  182. if((noteObj = cmJsonArrayElement(noteArrObj,j)) == NULL )
  183. {
  184. rc = cmErrMsg(&p->err,kParseFailTsbRC,"Access failed for note record at index %i at take index %i.",j,i);
  185. goto errLabel;
  186. }
  187. // parse the note record
  188. if((jsRC = cmJsonMemberValues( noteObj, &errMsg,
  189. "mni", kIntTId, &p->scTrkTakeV[i].midiV[j].mni,
  190. "scEvtIdx", kIntTId, &p->scTrkTakeV[i].midiV[j].scEvtIdx,
  191. "flags", kIntTId, &p->scTrkTakeV[i].midiV[j].flags,
  192. 0)) != kOkJsRC )
  193. {
  194. if( jsRC == kNodeNotFoundJsRC && errMsg != NULL )
  195. rc = cmErrMsg(&p->err,kParseFailTsbRC,"JSON file note record parse failed missing required field:'%s'",errMsg);
  196. else
  197. rc = cmErrMsg(&p->err,kParseFailTsbRC,"JSON file note record parse failed.");
  198. goto errLabel;
  199. }
  200. }
  201. }
  202. errLabel:
  203. if( rc != kOkTsbRC )
  204. rc = _cmTsbScoreTrkFree(p);
  205. return rc;
  206. }
  207. // Return the count of score events inside a given marker.
  208. unsigned _cmTsbScoreTrkMarkerEventCount( cmTsb_t* p, unsigned markUid )
  209. {
  210. unsigned i,j;
  211. unsigned minScEvtIdx = INT_MAX;
  212. unsigned maxScEvtIdx = 0;
  213. for(i=0; i<p->scTrkTakeN; ++i)
  214. for(j=0; j<p->scTrkTakeV[i].midiN; ++j)
  215. {
  216. if( p->scTrkTakeV[i].midiV[j].scEvtIdx < minScEvtIdx )
  217. minScEvtIdx = p->scTrkTakeV[i].midiV[j].scEvtIdx;
  218. if( p->scTrkTakeV[i].midiV[j].scEvtIdx > maxScEvtIdx )
  219. maxScEvtIdx = p->scTrkTakeV[i].midiV[j].scEvtIdx;
  220. }
  221. if( maxScEvtIdx < minScEvtIdx )
  222. return 0;
  223. return (maxScEvtIdx - minScEvtIdx) + 1;
  224. }
  225. cmTsbRC_t cmTakeSeqBldrAlloc( cmCtx_t* ctx, cmTakeSeqBldrH_t* hp )
  226. {
  227. cmTsbRC_t rc;
  228. if((rc = cmTakeSeqBldrFree(hp)) != kOkTsbRC )
  229. return kOkTsbRC;
  230. cmTsb_t* p = cmMemAllocZ(cmTsb_t,1);
  231. cmErrSetup(&p->err,&ctx->rpt,"TakeSeqBldr");
  232. p->ctx = *ctx;
  233. hp->h = p;
  234. return rc;
  235. }
  236. cmTsbRC_t cmTakeSeqBldrAllocFn( cmCtx_t* ctx, cmTakeSeqBldrH_t* hp, const cmChar_t* scoreTrkFn )
  237. {
  238. cmTsbRC_t rc;
  239. if((rc = cmTakeSeqBldrAlloc(ctx,hp)) != kOkTsbRC )
  240. return rc;
  241. if((rc = cmTakeSeqBldrInitialize(*hp,scoreTrkFn)) != kOkTsbRC )
  242. return rc;
  243. return rc;
  244. }
  245. cmTsbRC_t cmTakeSeqBldrFree( cmTakeSeqBldrH_t* hp )
  246. {
  247. cmRC_t rc = kOkTsbRC;
  248. if( hp == NULL || cmTakeSeqBldrIsValid(*hp)==false )
  249. return kOkTsbRC;
  250. cmTsb_t* p = _cmTsbHandleToPtr(*hp);
  251. if((rc = _cmTsbFree(p)) != kOkTsbRC )
  252. return rc;
  253. hp->h = NULL;
  254. return rc;
  255. }
  256. bool cmTakeSeqBldrIsValid( cmTakeSeqBldrH_t h )
  257. { return h.h != NULL; }
  258. cmTsbRC_t cmTakeSeqBldrInitialize( cmTakeSeqBldrH_t h, const cmChar_t* scoreTrkFn )
  259. {
  260. cmTsbRC_t rc = kOkTsbRC;
  261. cmTsb_t* p = _cmTsbHandleToPtr(h);
  262. if(( rc = _cmTsbLoadScoreTrkFile( p, scoreTrkFn )) != kOkTsbRC )
  263. return rc;
  264. if( cmTimeLineInitializeFromFile(&p->ctx, &p->tlH, NULL, NULL, p->tlFn, p->tlPrefixPath ) != kOkTlRC )
  265. {
  266. rc = cmErrMsg(&p->err,kTimeLineFailTsbRC,"The time-line file '%s' could not be loaded.",p->tlFn);
  267. goto errLabel;
  268. }
  269. if( cmScoreInitialize(&p->ctx, &p->scH, p->scFn, 0, NULL, 0, NULL, NULL, cmSymTblNullHandle ) != kOkScRC )
  270. {
  271. rc = cmErrMsg(&p->err,kScoreFailTsbRC,"The score file '%s' could not be loaded.",p->scFn);
  272. goto errLabel;
  273. }
  274. errLabel:
  275. if( rc != kOkTsbRC )
  276. _cmTsbScoreTrkFree(p);
  277. return rc;
  278. }
  279. cmTsbRC_t cmTakeSeqBldrLoadTake( cmTakeSeqBldrH_t h, unsigned tlMarkUid, bool overwriteFL )
  280. {
  281. cmTsbRC_t rc = kOkTsbRC;
  282. cmTsb_t* p = _cmTsbHandleToPtr(h);
  283. cmTlMarker_t* mark = NULL;
  284. cmTlMidiFile_t* mf = NULL;
  285. cmMidiFileH_t mfH = cmMidiFileNullHandle;
  286. // get a pointer to the time-line marker object
  287. if((mark = cmTlMarkerObjPtr( p->tlH, cmTimeLineIdToObj( p->tlH, cmInvalidId, tlMarkUid))) == NULL )
  288. {
  289. rc = cmErrMsg(&p->err,kInvalidArgTsbRC,"The time-line marker uid '%i' is not valid.",tlMarkUid);
  290. goto errLabel;
  291. }
  292. // get the name of the MIDI file which contains the marker
  293. if((mf = cmTimeLineMidiFileAtTime( p->tlH, mark->obj.seqId, mark->obj.seqSmpIdx )) == NULL )
  294. {
  295. rc = cmErrMsg(&p->err,kInvalidArgTsbRC,"The time-line marker '%i' does not intersect with a MIDI file.",tlMarkUid);
  296. goto errLabel;
  297. }
  298. // open the MIDI file
  299. if( cmMidiFileOpen( cmMidiFileName(mf->h), &mfH, &p->ctx ) != kOkMfRC )
  300. {
  301. rc = cmErrMsg(&p->err,kInvalidArgTsbRC,"The MIDI file '%s' could not be opened.", cmStringNullGuard(cmMidiFileName(mf->h)));
  302. goto errLabel;
  303. }
  304. // convert the dtick field to absolute sample indexes
  305. cmMidiFileTickToSamples( mfH, cmTimeLineSampleRate(p->tlH), true );
  306. // calculate MIDI note and pedal durations (see cmMidiChMsg_t.durTicks)
  307. cmMidiFileCalcNoteDurations( mfH );
  308. // convert the marker beg/end sample position to be relative to the MIDI file start time
  309. unsigned bsi = mark->obj.seqSmpIdx - mf->obj.seqSmpIdx;
  310. unsigned esi = mark->obj.seqSmpIdx + mark->obj.durSmpCnt - mf->obj.seqSmpIdx;
  311. unsigned i = 0;
  312. unsigned n = cmMidiFileMsgCount(mfH);
  313. const cmMidiTrackMsg_t** a = cmMidiFileMsgArray(mfH);
  314. // seek to the first MIDI msg after bsi
  315. for(i=0; i<n; ++i)
  316. if( a[i]->dtick >= bsi )
  317. break;
  318. if( i == n )
  319. {
  320. rc = cmErrMsg(&p->err,kInvalidArgTsbRC,"No MIDI events were found in the marker.");
  321. goto errLabel;
  322. }
  323. // for each MIDI message between bsi and esi
  324. for(; i<n && a[i]->dtick < esi; ++i)
  325. {
  326. const cmMidiTrackMsg_t* m = a[i];
  327. switch( m->status )
  328. {
  329. case kNoteOffMdId:
  330. case kNoteOnMdId:
  331. case kCtlMdId:
  332. break;
  333. }
  334. }
  335. errLabel:
  336. if( cmMidiFileClose(&mfH) != kOkMfRC )
  337. rc = cmErrMsg(&p->err,kMidiFileFailTsbRC,"MIDI file close failed.");
  338. return rc;
  339. }
  340. cmTsbRC_t cmTakeSeqBldrUnloadTake( cmTakeSeqBldrH_t h, unsigned tlMarkUid )
  341. {
  342. cmTsbRC_t rc = kOkTsbRC;
  343. return rc;
  344. }
  345. cmTsbRC_t cmTakeSeqBldrInsertScoreNotes( cmTakeSeqBldrH_t h, unsigned begScEvtIdx, unsigned endScEvtId )
  346. {
  347. cmTsbRC_t rc = kOkTsbRC;
  348. return rc;
  349. }
  350. cmTsbRC_t cmTakeSeqBldrRemoveScoreNotes( cmTakeSeqBldrH_t h, unsigned begScEvtIdx, unsigned endScEvtId )
  351. {
  352. cmTsbRC_t rc = kOkTsbRC;
  353. return rc;
  354. }
  355. cmTsbRC_t cmTakeSeqBldrSelectEnable( cmTakeSeqBldrH_t h, unsigned flags, unsigned id, bool selectFl )
  356. {
  357. cmTsbRC_t rc = kOkTsbRC;
  358. return rc;
  359. }
  360. cmTsbRC_t cmTakeSeqBldrEnableNote( cmTakeSeqBldrH_t h, unsigned ssqId, bool enableFl )
  361. {
  362. cmTsbRC_t rc = kOkTsbRC;
  363. return rc;
  364. }
  365. cmTsbRC_t cmTakeSeqBldrMoveNote( cmTakeSeqBldrH_t h, unsigned ssqId, int deltaSmpIdx )
  366. {
  367. cmTsbRC_t rc = kOkTsbRC;
  368. return rc;
  369. }
  370. cmTsbRC_t cmTakeSeqBldrWriteMidiFile( cmTakeSeqBldrH_t h, const char* fn )
  371. {
  372. cmTsbRC_t rc = kOkTsbRC;
  373. return rc;
  374. }
  375. cmTsbRC_t cmTakeSeqBldrTest( cmCtx_t* ctx )
  376. {
  377. const cmChar_t* scoreTrkFn = "/home/kevin/src/cmkc/src/kc/data/assoc0.js";
  378. cmTakeSeqBldrH_t tsbH = cmTakeSeqBldrNullHandle;
  379. cmTsbRC_t tsbRC = kOkTsbRC;
  380. if((tsbRC = cmTakeSeqBldrAllocFn(ctx, &tsbH, scoreTrkFn )) != kOkTsbRC )
  381. return cmErrMsg(&ctx->err,tsbRC,"TSB Allocate and parse '%s' failed.",scoreTrkFn);
  382. if((tsbRC = cmTakeSeqBldrFree(&tsbH)) != kOkTsbRC )
  383. return cmErrMsg(&ctx->err,tsbRC,"TSB Free failed.");
  384. return tsbRC;
  385. }