libcm is a C development framework with an emphasis on audio signal processing applications.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

cmTakeSeqBldr.c 42KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620
  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 "cmFile.h"
  18. #include "cmScore.h"
  19. #include "cmTakeSeqBldr.h"
  20. // Score track record: Map a score event to a MIDI event.
  21. typedef struct cmScTrkMidiTsb_str
  22. {
  23. unsigned mni; // MIDI note index as an offset from the take marker
  24. unsigned muid; // MIDI file msg unique id
  25. unsigned scEvtIdx; // score event index this note/pedal is assoc'd with or -1 if it did not match
  26. unsigned flags; // flags from cmScMatcherResult_t
  27. } cmScTrkMidiTsb_t;
  28. // Score Tracking info. from a single take (time-line marker)
  29. typedef struct cmScTrkTakeTsb_str
  30. {
  31. unsigned tlMarkerUid; // marker time line uid assoc'd with this take
  32. cmScTrkMidiTsb_t* midiV; // midiV[midiN] score to midi file map recd. array.
  33. unsigned midiN; // count of records in midiV[]
  34. unsigned minMuid; // min MIDI muid in midiV[]
  35. unsigned maxMuid; // max MIDI muid in midiV[]
  36. unsigned minScEvtIdx; // min scEvtIdx in midiV[]
  37. unsigned maxScEvtIdx; // max scEvtIdx in midiV[]
  38. bool failFl;
  39. } cmScTrkTakeTsb_t;
  40. //
  41. typedef struct cmMidiTsb_str
  42. {
  43. unsigned rid; // unique id among all object in this take
  44. unsigned srcId; // marker uid or -1 if this event was manually inserted
  45. unsigned scEvtIdx; // score event assocd with this midi event
  46. unsigned flags; // copy of cmScTrkMidiTsb_t.flags or 0 if not assoc'd with a score event
  47. struct cmMidiTsb_str* ref; // previous MIDI event in time
  48. unsigned offsetSmp; // time offset from *ref
  49. unsigned durSmp; // duration of this MIDI event
  50. unsigned status; // MIDI status value
  51. unsigned d0; // d0 MIDI channel msg data byte 0.
  52. unsigned d1; // d1 MIDI channel msg data byte 1.
  53. struct cmMidiTsb_str* link; // pointer to next MIDI event in list
  54. } cmMidiTsb_t;
  55. // This record contains all the score events and and score synchronized MIDI events
  56. // associated with a given take. Each call to cmTakeSeqBldrLoadTake() creates
  57. // one of these records.
  58. typedef struct cmTakeTsb_str
  59. {
  60. unsigned tlMarkerUid; // time-line marker uid associated with this take
  61. cmScTrkTakeTsb_t* stt; // score tracking info
  62. cmMidiTsb_t* midi; // midi events contained by this take
  63. struct cmTakeTsb_str* link;
  64. } cmTakeTsb_t;
  65. typedef struct
  66. {
  67. cmCtx_t ctx; // application context
  68. cmErr_t err; // internal error object
  69. cmJsonH_t jsH; // JSON tree used to hold score tracker info.
  70. const cmChar_t* tlFn; // time line filename
  71. const cmChar_t* scFn; // score file name
  72. const cmChar_t* tlPrefixPath; // path to time line audio and MIDI files
  73. cmTlH_t tlH; // time-line handle
  74. cmScH_t scH; // score handle
  75. cmScTrkTakeTsb_t* scTrkTakeV; // score tracker file info scTrkTakeV[ scTrkTakeN ]
  76. unsigned scTrkTakeN; // (one record per take)
  77. cmTakeTsb_t* takes; // list of loaded takes
  78. cmTakeTsb_t* manual; // list of manually inserted MIDI events
  79. cmTakeTsb_t* out; // render list
  80. cmMidiTsb_t* evt; // next event to play from the render list
  81. unsigned absPlaySmp; // absolute sample index of the clock
  82. unsigned absEvtSmp; // absolute sample index of the next event to play
  83. unsigned nextRid;
  84. cmMidiTsb_t* rend;
  85. } cmTsb_t;
  86. cmTakeSeqBldrH_t cmTakeSeqBldrNullHandle = cmSTATIC_NULL_HANDLE;
  87. cmTsb_t* _cmTsbHandleToPtr( cmTakeSeqBldrH_t h )
  88. {
  89. cmTsb_t* p = (cmTsb_t*)h.h;
  90. return p;
  91. }
  92. //-------------------------------------------------------------------------------------------------------------------------
  93. typedef struct cmOrderTsb_str
  94. {
  95. cmTakeTsb_t* t;
  96. unsigned minScEvtIdx; // take begin
  97. unsigned maxScEvtIdx; // take end
  98. unsigned begScEvtIdx; // render begin (render beg/end is always contained
  99. unsigned endScEvtIdx; // render end withing min/maxScEvtIdx)
  100. struct cmOrderTsb_str* link;
  101. } cmOrderTsb_t;
  102. // Notes:
  103. // 1) begScEvtIdx - endScEvtIdx is always contained within minScEvtIdx-maxScEvtIdx.
  104. // 2) If begScEvtIdx == cmInvalidIdx then the take was entirely contained inside the
  105. // previous take and is therefore skipped over.
  106. // Free a linked list of cmOrderTsb_t records
  107. void _cmTakeSeqBldrFreeOrder( cmOrderTsb_t* o )
  108. {
  109. while(o!=NULL)
  110. {
  111. cmOrderTsb_t* o0 = o->link;
  112. cmMemFree(o);
  113. o = o0;
  114. }
  115. }
  116. // Create a linked list of cmOrderTsb_t records from the p->takes list.
  117. cmOrderTsb_t* _cmTakeSeqBldrAllocOrder( cmTsb_t* p )
  118. {
  119. cmOrderTsb_t* o = NULL;
  120. cmOrderTsb_t* o0 = NULL;
  121. cmTakeTsb_t* t = p->takes;
  122. // create a list of order records - one per take
  123. for(; t!=NULL; t=t->link)
  124. if( t->stt != NULL )
  125. {
  126. cmOrderTsb_t* o1 = cmMemAllocZ(cmOrderTsb_t,1);
  127. o1->t = t;
  128. o1->minScEvtIdx = t->stt->minScEvtIdx;
  129. o1->maxScEvtIdx = t->stt->maxScEvtIdx;
  130. o1->begScEvtIdx = o1->minScEvtIdx;
  131. o1->endScEvtIdx = o1->maxScEvtIdx;
  132. if( o0 == NULL )
  133. o = o1;
  134. else
  135. o0->link = o1;
  136. o0 = o1;
  137. }
  138. return o;
  139. }
  140. // Sort a list of cmOrderTsb_t records on minScEvtIdx.
  141. cmOrderTsb_t* _cmTakeSeqBldrSortOrder( cmOrderTsb_t* o )
  142. {
  143. cmOrderTsb_t* beg = NULL;
  144. cmOrderTsb_t* end = NULL;
  145. cmOrderTsb_t* m = NULL;
  146. // while elements remain on the unordered list
  147. while( o != NULL )
  148. {
  149. cmOrderTsb_t* c0 = NULL;
  150. cmOrderTsb_t* c1 = o->link;
  151. m = o;
  152. // set m to point to the recd with the min minScEvtIdx
  153. for(; c1!=NULL; c1=c1->link )
  154. if( c1->minScEvtIdx < m->minScEvtIdx )
  155. m = c1;
  156. // for each recd on the unordered list
  157. for(c1=o; c1!=NULL; c1=c1->link)
  158. {
  159. // if this is the min record
  160. if( c1==m )
  161. {
  162. // remove the min recd from this list
  163. if( c0 == NULL )
  164. o = c1->link;
  165. else
  166. c0->link = c1->link;
  167. c1->link = NULL;
  168. break;
  169. }
  170. c0 = c1;
  171. }
  172. // c1 now points to the min record
  173. assert(c1==m);
  174. if( end == NULL )
  175. beg = end = m;
  176. else
  177. {
  178. end->link = m;
  179. end = m;
  180. }
  181. }
  182. return beg;
  183. }
  184. // Set the begScEvtIdx and endScEvtIdx fields to settle conflicts
  185. // between overlapping takes.
  186. void _cmTakeSeqBldrSetBegEndOrder( cmOrderTsb_t* o )
  187. {
  188. if( o == NULL )
  189. return;
  190. // Earlier takes have priority over later takes so the first
  191. // take has highest priority and therefore it's beg=min and end=max.
  192. o->begScEvtIdx = o->minScEvtIdx;
  193. o->endScEvtIdx = o->maxScEvtIdx;
  194. cmOrderTsb_t* o0 = o;
  195. cmOrderTsb_t* o1 = o->link;
  196. while( o1!=NULL)
  197. {
  198. bool skipFl = false;
  199. // if this take begins after the previous take
  200. if( o1->minScEvtIdx > o0->endScEvtIdx )
  201. {
  202. o1->begScEvtIdx = o1->minScEvtIdx;
  203. o1->endScEvtIdx = o1->maxScEvtIdx;
  204. }
  205. else
  206. {
  207. // if this take is entirely contained in the previous take
  208. // then this take is skipped.
  209. if( o0->endScEvtIdx > o1->maxScEvtIdx )
  210. {
  211. o1->begScEvtIdx = cmInvalidIdx;
  212. o1->endScEvtIdx = cmInvalidIdx;
  213. skipFl = true;
  214. }
  215. else // this take overlaps with the previous take
  216. {
  217. o1->begScEvtIdx = o0->endScEvtIdx + 1;
  218. o1->endScEvtIdx = o1->maxScEvtIdx;
  219. }
  220. }
  221. if( skipFl )
  222. {
  223. // the current take is being skipped so do not change o0
  224. o1 = o1->link;
  225. }
  226. else
  227. {
  228. // advance to the next take
  229. o0 = o1;
  230. o1 = o0->link;
  231. }
  232. }
  233. }
  234. void _cmTakeSeqBldrPrintOrder( cmOrderTsb_t* o )
  235. {
  236. int i;
  237. for(i=0; o!=NULL; o=o->link,++i)
  238. printf("%i : %p min:%i max:%i beg:%i end:%i\n",i, o->t, o->minScEvtIdx, o->maxScEvtIdx, o->begScEvtIdx, o->endScEvtIdx);
  239. printf("\n");
  240. }
  241. //-------------------------------------------------------------------------------------------------------------------------
  242. typedef struct
  243. {
  244. cmScoreEvt_t* sep;
  245. unsigned absSmp;
  246. } cmTsbTempo_t;
  247. void _cmTsbTempoPrint( cmTsbTempo_t* r, unsigned rN )
  248. {
  249. unsigned i;
  250. for(i=0; i<rN; ++i)
  251. printf("%s %i\n",cmScEvtTypeIdToLabel(r[i].sep->type),r[i].absSmp);
  252. printf("\n");
  253. }
  254. cmTsbTempo_t* _cmTsbTempoFind( cmTsbTempo_t* r, unsigned rN, unsigned scEvtIdx )
  255. {
  256. unsigned i;
  257. for(i=0; i<rN; ++i)
  258. if( r[i].sep->index == scEvtIdx )
  259. return r + i;
  260. return NULL;
  261. }
  262. // Starting at r[ri] calc the tempo for the next two bars.
  263. unsigned _cmTsbTempoCalc( cmTsbTempo_t* r, unsigned rN, unsigned ri, double srate )
  264. {
  265. unsigned durBarCnt = 2;
  266. unsigned barIdx = 0;
  267. double beats = 0;
  268. unsigned abs0Smp = -1;
  269. unsigned abs1Smp = -1;
  270. for(; ri<rN; ++ri)
  271. {
  272. // if this is a note event
  273. if( r[ri].sep->type == kNonEvtScId )
  274. {
  275. beats += r[ri].sep->frac;
  276. if( abs0Smp == -1 )
  277. abs0Smp = r[ri].absSmp;
  278. if( r[ri].absSmp != -1 )
  279. abs1Smp = r[ri].absSmp;
  280. }
  281. // if this is a bar event
  282. if( r[ri].sep->type == kBarEvtScId )
  283. {
  284. barIdx += 1;
  285. if( barIdx == durBarCnt )
  286. break;
  287. }
  288. }
  289. double durSmp = abs1Smp - abs0Smp;
  290. if( durSmp == 0 )
  291. return 0;
  292. return (unsigned)round(beats / (durSmp / (srate * 60.0)));
  293. }
  294. cmTsbRC_t _cmTsbCalcTempo( cmTsb_t* p, cmTakeTsb_t* t, unsigned* begBpSRef, unsigned* endBpSRef )
  295. {
  296. unsigned i,j;
  297. assert( begBpSRef != NULL && endBpSRef != NULL );
  298. *begBpSRef = 0;
  299. *endBpSRef = 0;
  300. if( t->stt == NULL )
  301. return cmErrMsg(&p->err,kMissingScTrkTsbRC,"The tempo of takes without score-tracking results cannot be calculated.");
  302. if( t->midi == NULL )
  303. return cmErrMsg(&p->err,kInvalidArgTsbRC,"The tempo of a take without MIDI events cannot be estimated.");
  304. // allocate an array to with one record per score event in the take
  305. unsigned rN = t->stt->maxScEvtIdx - t->stt->minScEvtIdx + 1;
  306. cmTsbTempo_t r[ rN ];
  307. // assign a score event pointer to each record
  308. for(i=0; i<rN; ++i)
  309. {
  310. r[i].sep = cmScoreEvt(p->scH,t->stt->minScEvtIdx + i);
  311. assert( r[i].sep != NULL );
  312. r[i].absSmp = -1;
  313. }
  314. // use MIDI events with score information to assign absolute
  315. // time information to as many score events as possible.
  316. unsigned absSmp = 0;
  317. cmMidiTsb_t* m = t->midi;
  318. while( m!=NULL )
  319. {
  320. cmTsbTempo_t* r0 = NULL;
  321. if( m->scEvtIdx != cmInvalidIdx && (r0 = _cmTsbTempoFind(r,rN,m->scEvtIdx)) != NULL )
  322. {
  323. assert( r0->sep->type == kNonEvtScId );
  324. r0->absSmp = absSmp;
  325. }
  326. m = m->link;
  327. if( m != NULL )
  328. absSmp += m->offsetSmp;
  329. }
  330. unsigned barCnt = 0;
  331. // assign an absolute time to each bar recd
  332. for(i=0; i<rN; ++i)
  333. if( r[i].sep->type == kBarEvtScId )
  334. {
  335. barCnt += 1; // count the number of bars found
  336. // search ahead from the bar to the next note
  337. unsigned j;
  338. for(j=i; j<rN; ++j)
  339. if( r[j].sep->type == kNonEvtScId )
  340. {
  341. r[i].absSmp = r[j].absSmp; // the bar time is the same as the next
  342. break;
  343. }
  344. }
  345. //_cmTsbTempoPrint(r,rN);
  346. unsigned tempoV[ barCnt ];
  347. for(i=0,j=0; i<rN && j<barCnt; ++i)
  348. {
  349. tempoV[j++] = _cmTsbTempoCalc(r, rN, i, cmTimeLineSampleRate(p->tlH) );
  350. for(; i<rN; ++i)
  351. if( r[i].sep->type == kBarEvtScId )
  352. break;
  353. }
  354. if( j > 0 )
  355. {
  356. *begBpSRef = tempoV[0];
  357. *endBpSRef = tempoV[j-1];
  358. }
  359. printf("Bars:%i ",barCnt);
  360. for(i=0; i<j; ++i)
  361. printf("%i ",tempoV[i]);
  362. printf("\n");
  363. return kOkTsbRC;
  364. }
  365. //-------------------------------------------------------------------------------------------------------------------------
  366. void _cmTsbPrintMidi( cmTakeTsb_t* t )
  367. {
  368. if( t==NULL )
  369. return;
  370. cmMidiTsb_t* m = t->midi;
  371. for(; m!=NULL; m=m->link)
  372. printf("mark:%4i sei:%4i fl:0x%x offs:%8i dur:%8i\n", m->srcId, m->scEvtIdx,m->flags,m->offsetSmp,m->durSmp);
  373. printf("\n");
  374. }
  375. void _cmTsbTakePrint( cmTakeTsb_t* t )
  376. {
  377. for(; t!=NULL; t=t->link)
  378. {
  379. printf("Take:%i %p\n", t->tlMarkerUid, t->stt);
  380. _cmTsbPrintMidi(t);
  381. }
  382. }
  383. cmTsbRC_t _cmTsbScoreTrkFree( cmTsb_t* p )
  384. {
  385. cmTsbRC_t rc = kOkTsbRC;
  386. unsigned i;
  387. if( cmJsonFinalize(&p->jsH) != kOkJsRC )
  388. {
  389. rc = cmErrMsg(&p->err,kJsonFailTsbRC,"JSON object finalize failed.");
  390. goto errLabel;
  391. }
  392. if( p->scTrkTakeV != NULL )
  393. {
  394. for(i=0; i<p->scTrkTakeN; ++i)
  395. cmMemPtrFree(&p->scTrkTakeV[i].midiV);
  396. cmMemPtrFree(&p->scTrkTakeV);
  397. }
  398. if( cmTimeLineFinalize(&p->tlH) != kOkTlRC )
  399. rc = cmErrMsg(&p->err,kTimeLineFailTsbRC,"Time line object finalize failed.");
  400. if( cmScoreFinalize(&p->scH) != kOkScRC )
  401. rc = cmErrMsg(&p->err,kScoreFailTsbRC,"Score finalize failed.");
  402. errLabel:
  403. return rc;
  404. }
  405. // Free a take record. Note that the record must be unlinked
  406. // from p->takes (See _cmTakeTsbUnlink().) prior to calling this function.
  407. void _cmTsbTakeFree( cmTsb_t* p, cmTakeTsb_t** tp )
  408. {
  409. if( tp==NULL || *tp==NULL )
  410. return;
  411. cmMidiTsb_t* m = (*tp)->midi;
  412. while( m != NULL )
  413. {
  414. cmMidiTsb_t* nm = m->link;
  415. cmMemFree(m);
  416. m = nm;
  417. }
  418. cmMemPtrFree(tp);
  419. }
  420. void _cmTsbRendTakeFree( cmTsb_t* p, cmTakeTsb_t** t )
  421. {
  422. _cmTsbTakeFree(p,t);
  423. p->evt = NULL;
  424. p->absPlaySmp = 0;
  425. p->absEvtSmp = 0;
  426. p->nextRid = 0;
  427. p->rend = NULL;
  428. }
  429. // Unlink a 'take' record from p->takes.
  430. cmTakeTsb_t* _cmTsbTakeUnlink( cmTsb_t* p, cmTakeTsb_t* t )
  431. {
  432. cmTakeTsb_t* t0 = NULL;
  433. cmTakeTsb_t* t1 = p->takes;
  434. while( t1 != NULL )
  435. {
  436. if( t1 == t )
  437. {
  438. if( t0 == NULL )
  439. p->takes = t->link;
  440. else
  441. t0->link = t->link;
  442. return t;
  443. }
  444. }
  445. return NULL;
  446. }
  447. cmTsbRC_t _cmTsbFree( cmTsb_t* p )
  448. {
  449. cmTsbRC_t rc = kOkTsbRC;
  450. if((rc = _cmTsbScoreTrkFree(p)) != kOkTsbRC )
  451. goto errLabel;
  452. //_cmTsbTakePrint(p->takes);
  453. //_cmTsbTakePrint(p->out);
  454. cmTakeTsb_t* t = p->takes;
  455. while( t != NULL )
  456. {
  457. cmTakeTsb_t* nt = t->link;
  458. _cmTsbTakeFree(p,&t);
  459. t = nt;
  460. }
  461. _cmTsbRendTakeFree(p,&p->out);
  462. cmMemFree(p);
  463. errLabel:
  464. return rc;
  465. }
  466. cmScTrkTakeTsb_t* _cmTsbMarkerIdToScTrkTake( cmTsb_t* p, unsigned markerUid )
  467. {
  468. unsigned i;
  469. for(i=0; p->scTrkTakeN; ++i)
  470. if( p->scTrkTakeV[i].tlMarkerUid == markerUid )
  471. return p->scTrkTakeV + i;
  472. return NULL;
  473. }
  474. cmScTrkMidiTsb_t* _cmTsbMuidToScTrkMidi( cmScTrkTakeTsb_t* t, unsigned muid )
  475. {
  476. unsigned i;
  477. for(i=0; i<t->midiN; ++i)
  478. if( t->midiV[i].muid == muid )
  479. return t->midiV + i;
  480. return NULL;
  481. }
  482. cmTsbRC_t _cmTsbLoadScoreTrkFile( cmTsb_t* p, const cmChar_t* scoreTrkFn )
  483. {
  484. cmTsbRC_t rc = kOkTsbRC;
  485. cmJsonNode_t* tkArrObj = NULL;
  486. cmJsRC_t jsRC = kOkJsRC;
  487. const cmChar_t* errMsg = NULL;
  488. unsigned i;
  489. // initialize the TSB json object
  490. if(( rc = cmJsonInitializeFromFile(&p->jsH,scoreTrkFn,&p->ctx)) != kOkJsRC )
  491. {
  492. rc = cmErrMsg(&p->err,kJsonFailTsbRC,"The Take Sequence Builder JSON file object could not be initialized from '%s'.",cmStringNullGuard(scoreTrkFn));
  493. goto errLabel;
  494. }
  495. // parse the header
  496. if((jsRC = cmJsonMemberValues( cmJsonRoot(p->jsH), &errMsg,
  497. "timeLineFn", kStringTId, &p->tlFn,
  498. "scoreFn", kStringTId, &p->scFn,
  499. "tlPrefixPath", kStringTId, &p->tlPrefixPath,
  500. "takeArray", kArrayTId | kOptArgJsFl, &tkArrObj,
  501. NULL )) != kOkJsRC )
  502. {
  503. if( jsRC == kNodeNotFoundJsRC && errMsg != NULL )
  504. rc = cmErrMsg(&p->err,kParseFailTsbRC,"JSON file header parse failed missing required field:'%s'",errMsg);
  505. else
  506. rc = cmErrMsg(&p->err,kParseFailTsbRC,"JSON file header parse failed.");
  507. goto errLabel;
  508. }
  509. // count of take records
  510. p->scTrkTakeN = cmJsonChildCount(tkArrObj);
  511. // array of take records
  512. p->scTrkTakeV = cmMemAllocZ(cmScTrkTakeTsb_t,p->scTrkTakeN);
  513. // for each take record
  514. for(i=0; i<p->scTrkTakeN; ++i)
  515. {
  516. cmJsonNode_t* takeObj = NULL;
  517. cmJsonNode_t* noteArrObj = NULL;
  518. cmScTrkTakeTsb_t* t = p->scTrkTakeV + i;
  519. unsigned j;
  520. // get a pointer to the take record JSON object
  521. if((takeObj = cmJsonArrayElement(tkArrObj,i)) == NULL )
  522. {
  523. rc = cmErrMsg(&p->err,kParseFailTsbRC,"Take record header at index %i access failed.",i);
  524. goto errLabel;
  525. }
  526. // parse the take record
  527. if((jsRC = cmJsonMemberValues( takeObj, &errMsg,
  528. "markerUid",kIntTId, &t->tlMarkerUid,
  529. "failFl", kIntTId, &t->failFl,
  530. "array", kArrayTId, &noteArrObj,
  531. NULL)) != kOkJsRC )
  532. {
  533. if( jsRC == kNodeNotFoundJsRC && errMsg != NULL )
  534. rc = cmErrMsg(&p->err,kParseFailTsbRC,"JSON file take record parse failed missing required field:'%s'",errMsg);
  535. else
  536. rc = cmErrMsg(&p->err,kParseFailTsbRC,"JSON file take record parse failed.");
  537. goto errLabel;
  538. }
  539. // get the count of note records
  540. t->midiN = cmJsonChildCount(noteArrObj);
  541. // allocate a note record array for this take
  542. t->midiV = cmMemAllocZ(cmScTrkMidiTsb_t, t->midiN);
  543. t->minMuid = INT_MAX;
  544. t->maxMuid = 0;
  545. t->minScEvtIdx = INT_MAX;
  546. t->maxScEvtIdx = 0;
  547. // for each note record
  548. for(j=0; j<t->midiN; ++j)
  549. {
  550. cmJsonNode_t* noteObj = NULL;
  551. // get the note record JSON object
  552. if((noteObj = cmJsonArrayElement(noteArrObj,j)) == NULL )
  553. {
  554. rc = cmErrMsg(&p->err,kParseFailTsbRC,"Access failed for note record at index %i at take index %i.",j,i);
  555. goto errLabel;
  556. }
  557. // parse the note record
  558. if((jsRC = cmJsonMemberValues( noteObj, &errMsg,
  559. "mni", kIntTId, &t->midiV[j].mni,
  560. "muid", kIntTId, &t->midiV[j].muid,
  561. "scEvtIdx", kIntTId, &t->midiV[j].scEvtIdx,
  562. "flags", kIntTId, &t->midiV[j].flags,
  563. NULL)) != kOkJsRC )
  564. {
  565. if( jsRC == kNodeNotFoundJsRC && errMsg != NULL )
  566. rc = cmErrMsg(&p->err,kParseFailTsbRC,"JSON file note record parse failed missing required field:'%s'",errMsg);
  567. else
  568. rc = cmErrMsg(&p->err,kParseFailTsbRC,"JSON file note record parse failed.");
  569. goto errLabel;
  570. }
  571. if( t->midiV[j].muid < t->minMuid )
  572. t->minMuid = t->midiV[j].muid;
  573. if( t->midiV[j].muid > t->maxMuid )
  574. t->maxMuid = t->midiV[j].muid;
  575. unsigned scEvtIdx = t->midiV[j].scEvtIdx;
  576. if( scEvtIdx!=0 && scEvtIdx!=cmInvalidIdx && scEvtIdx < t->minScEvtIdx )
  577. t->minScEvtIdx = scEvtIdx;
  578. if( scEvtIdx!=0 && scEvtIdx!=cmInvalidIdx && scEvtIdx > t->maxScEvtIdx )
  579. t->maxScEvtIdx = t->midiV[j].scEvtIdx;
  580. }
  581. }
  582. errLabel:
  583. if( rc != kOkTsbRC )
  584. rc = _cmTsbScoreTrkFree(p);
  585. return rc;
  586. }
  587. cmTsbRC_t _cmTakeSeqBldrRender( cmTsb_t* p )
  588. {
  589. cmTsbRC_t rc = kOkTsbRC;
  590. unsigned takeCnt = 0;
  591. unsigned midiCnt = 0;
  592. // delete the previous output rendering
  593. _cmTsbRendTakeFree(p,&p->out);
  594. // allocate a take order list
  595. cmOrderTsb_t* o = _cmTakeSeqBldrAllocOrder( p );
  596. // sort the list by minScEvtIdx
  597. o = _cmTakeSeqBldrSortOrder(o);
  598. // assign beg/endScEvtIdx values to each take
  599. _cmTakeSeqBldrSetBegEndOrder(o);
  600. //_cmTakeSeqBldrPrintOrder(o);
  601. // if the render take has not yet been allocated
  602. p->out = cmMemAllocZ(cmTakeTsb_t,1);
  603. p->out->tlMarkerUid = cmInvalidId;
  604. p->nextRid = 0;
  605. cmMidiTsb_t* m0 = NULL;
  606. cmOrderTsb_t* t = o;
  607. for(; t!=NULL; t=t->link)
  608. {
  609. // skip takes whose begScEvtIdx is not valid
  610. if( t->begScEvtIdx == cmInvalidIdx )
  611. continue;
  612. takeCnt += 1;
  613. //_cmTsbPrintMidi(t->t);
  614. // advance to the MIDI event assoc'd with t->begScEvtIdx
  615. cmMidiTsb_t* m = t->t->midi;
  616. for(; m!=NULL; m=m->link)
  617. if( m->scEvtIdx >= t->begScEvtIdx )
  618. break;
  619. // copy the MIDI events from the take into the render list
  620. for(; m!=NULL; m=m->link)
  621. {
  622. midiCnt += 1;
  623. // allocate a MIDI record to hold this render event
  624. cmMidiTsb_t* m1 = cmMemAllocZ(cmMidiTsb_t,1);
  625. // copy in the MIDI record
  626. *m1 = *m;
  627. //memcpy(m1,m,sizeof(cmMidiTsb_t));
  628. m1->link= NULL; // NULLify the copied link
  629. m1->ref = m0; // set prev. link
  630. m1->rid = p->nextRid++; // set the unique id
  631. // if this is not the first MIDI event ...
  632. if( m0 != NULL )
  633. m0->link = m1; // then set next link on the previous record
  634. else //
  635. { // otherwise
  636. p->out->midi = m1; // 1) set the render take event list
  637. m1->offsetSmp = 0; // 2) the first event always starts at time zero.
  638. p->evt = m1; // 3) set the first event to play
  639. p->absPlaySmp = 0; // 4) set the current play clock to 0
  640. p->absEvtSmp = 0; // 5) set the current next play event time to 0
  641. }
  642. m0 = m1;
  643. // if this is the last event in this take
  644. if( m0->scEvtIdx == t->endScEvtIdx )
  645. break;
  646. }
  647. }
  648. // free the take order list
  649. _cmTakeSeqBldrFreeOrder(o);
  650. printf("rendered takes:%i events:%i\n",takeCnt,midiCnt);
  651. //_cmTsbPrintMidi(p->out);
  652. return rc;
  653. }
  654. cmTsbRC_t cmTakeSeqBldrAlloc( cmCtx_t* ctx, cmTakeSeqBldrH_t* hp )
  655. {
  656. cmTsbRC_t rc;
  657. if((rc = cmTakeSeqBldrFree(hp)) != kOkTsbRC )
  658. return kOkTsbRC;
  659. cmTsb_t* p = cmMemAllocZ(cmTsb_t,1);
  660. cmErrSetup(&p->err,&ctx->rpt,"TakeSeqBldr");
  661. p->ctx = *ctx;
  662. hp->h = p;
  663. return rc;
  664. }
  665. cmTsbRC_t cmTakeSeqBldrAllocFn( cmCtx_t* ctx, cmTakeSeqBldrH_t* hp, const cmChar_t* scoreTrkFn )
  666. {
  667. cmTsbRC_t rc;
  668. if((rc = cmTakeSeqBldrAlloc(ctx,hp)) != kOkTsbRC )
  669. return rc;
  670. if((rc = cmTakeSeqBldrInitialize(*hp,scoreTrkFn)) != kOkTsbRC )
  671. return rc;
  672. return rc;
  673. }
  674. cmTsbRC_t cmTakeSeqBldrFree( cmTakeSeqBldrH_t* hp )
  675. {
  676. cmRC_t rc = kOkTsbRC;
  677. if( hp == NULL || cmTakeSeqBldrIsValid(*hp)==false )
  678. return kOkTsbRC;
  679. cmTsb_t* p = _cmTsbHandleToPtr(*hp);
  680. if((rc = _cmTsbFree(p)) != kOkTsbRC )
  681. return rc;
  682. hp->h = NULL;
  683. return rc;
  684. }
  685. bool cmTakeSeqBldrIsValid( cmTakeSeqBldrH_t h )
  686. { return h.h != NULL; }
  687. cmTsbRC_t cmTakeSeqBldrInitialize( cmTakeSeqBldrH_t h, const cmChar_t* scoreTrkFn )
  688. {
  689. cmTsbRC_t rc = kOkTsbRC;
  690. cmTsb_t* p = _cmTsbHandleToPtr(h);
  691. if(( rc = _cmTsbLoadScoreTrkFile( p, scoreTrkFn )) != kOkTsbRC )
  692. return rc;
  693. if( cmTimeLineInitializeFromFile(&p->ctx, &p->tlH, NULL, NULL, p->tlFn, p->tlPrefixPath ) != kOkTlRC )
  694. {
  695. rc = cmErrMsg(&p->err,kTimeLineFailTsbRC,"The time-line file '%s' could not be loaded.",p->tlFn);
  696. goto errLabel;
  697. }
  698. if( cmScoreInitialize(&p->ctx, &p->scH, p->scFn, 0, NULL, 0, NULL, NULL, cmSymTblNullHandle ) != kOkScRC )
  699. {
  700. rc = cmErrMsg(&p->err,kScoreFailTsbRC,"The score file '%s' could not be loaded.",p->scFn);
  701. goto errLabel;
  702. }
  703. errLabel:
  704. if( rc != kOkTsbRC )
  705. _cmTsbScoreTrkFree(p);
  706. return rc;
  707. }
  708. cmTakeTsb_t* _cmTsbMarkerUidToTake( cmTsb_t* p, unsigned tlMarkerUid )
  709. {
  710. cmTakeTsb_t* t = p->takes;
  711. for(; t != NULL; t=t->link)
  712. if( t->tlMarkerUid == tlMarkerUid )
  713. return t;
  714. return NULL;
  715. }
  716. cmTsbRC_t cmTakeSeqBldrLoadTake( cmTakeSeqBldrH_t h, unsigned tlMarkUid, bool overwriteFL )
  717. {
  718. cmTsbRC_t rc = kOkTsbRC;
  719. cmTsb_t* p = _cmTsbHandleToPtr(h);
  720. cmTlMarker_t* mark = NULL;
  721. cmTlMidiFile_t* mf = NULL;
  722. cmMidiFileH_t mfH = cmMidiFileNullHandle;
  723. cmScTrkTakeTsb_t* stt = NULL;
  724. // verify that the requested take has not already been loaded
  725. if( _cmTsbMarkerUidToTake( p, tlMarkUid ) != NULL )
  726. {
  727. rc = cmErrMsg(&p->err,kInvalidArgTsbRC,"The take indicated by marker id %i has already been loaded.",tlMarkUid );
  728. goto errLabel;
  729. }
  730. // find the score tracked take for the requested marker
  731. if((stt = _cmTsbMarkerIdToScTrkTake(p,tlMarkUid )) == NULL )
  732. {
  733. rc = cmErrMsg(&p->err,kInvalidArgTsbRC,"The score tracked take indicated by marker id %i could not be found.", tlMarkUid );
  734. goto errLabel;
  735. }
  736. // get a pointer to the time-line marker object
  737. if((mark = cmTlMarkerObjPtr( p->tlH, cmTimeLineIdToObj( p->tlH, cmInvalidId, tlMarkUid))) == NULL )
  738. {
  739. rc = cmErrMsg(&p->err,kInvalidArgTsbRC,"The time-line marker uid '%i' is not valid.",tlMarkUid);
  740. goto errLabel;
  741. }
  742. // get the name of the MIDI file which contains the marker
  743. if((mf = cmTimeLineMidiFileAtTime( p->tlH, mark->obj.seqId, mark->obj.seqSmpIdx )) == NULL )
  744. {
  745. rc = cmErrMsg(&p->err,kInvalidArgTsbRC,"The time-line marker '%i' does not intersect with a MIDI file.",tlMarkUid);
  746. goto errLabel;
  747. }
  748. // open the MIDI file
  749. if( cmMidiFileOpen( &p->ctx, &mfH, cmMidiFileName(mf->h) ) != kOkMfRC )
  750. {
  751. rc = cmErrMsg(&p->err,kInvalidArgTsbRC,"The MIDI file '%s' could not be opened.", cmStringNullGuard(cmMidiFileName(mf->h)));
  752. goto errLabel;
  753. }
  754. // convert the dtick field to delta samples
  755. //cmMidiFileTickToSamples( mfH, cmTimeLineSampleRate(p->tlH), false );
  756. // calculate MIDI note and pedal durations (see cmMidiChMsg_t.durTicks)
  757. cmMidiFileCalcNoteDurations( mfH );
  758. unsigned i = 0;
  759. unsigned n = cmMidiFileMsgCount(mfH);
  760. const cmMidiTrackMsg_t** a = cmMidiFileMsgArray(mfH);
  761. double srate = cmTimeLineSampleRate(p->tlH);
  762. // allocate and link a new take render record
  763. cmTakeTsb_t* t = cmMemAllocZ(cmTakeTsb_t,1);
  764. t->tlMarkerUid = tlMarkUid;
  765. t->stt = stt;
  766. t->link = p->takes;
  767. p->takes = t;
  768. unsigned rid = 0;
  769. cmMidiTsb_t* m0 = NULL;
  770. const cmMidiTrackMsg_t* mf0 = NULL;
  771. // for each MIDI message in the file
  772. for(i=0; i<n; ++i)
  773. {
  774. const cmMidiTrackMsg_t* mf1 = a[i];
  775. // we are only interested in rendering notes and control msgs
  776. switch( mf1->status )
  777. {
  778. case kNoteOnMdId:
  779. case kNoteOffMdId:
  780. case kCtlMdId:
  781. break;
  782. default:
  783. continue;
  784. }
  785. // if this MIDI message is inside the tracked region of the take
  786. if( stt->minMuid > mf1->uid || mf1->uid > stt->maxMuid )
  787. continue;
  788. // get a pointer to the tracking map for the given MIDI file event.
  789. // (Note that since control messages are not tracked so this function may return NULL.)
  790. cmScTrkMidiTsb_t* stm = _cmTsbMuidToScTrkMidi(stt, mf1->uid );
  791. // create a MIDI render event
  792. cmMidiTsb_t* m1 = cmMemAllocZ(cmMidiTsb_t,1);
  793. m1->rid = rid++;
  794. m1->srcId = tlMarkUid;
  795. m1->scEvtIdx = stm != NULL ? stm->scEvtIdx : cmInvalidIdx;
  796. m1->flags = stm != NULL ? stm->flags : 0;
  797. m1->ref = m0;
  798. m1->offsetSmp = mf0 == NULL ? 0 : round(mf1->amicro * srate / 1000000.0);
  799. m1->durSmp = mf1->u.chMsgPtr->durMicros * srate / 1000000.0;
  800. m1->d0 = mf1->u.chMsgPtr->d0;
  801. m1->d1 = mf1->u.chMsgPtr->d1;
  802. m1->status = mf1->status;
  803. m1->link = NULL;
  804. //printf("0x%x %f %f\n",m1->status,m1->offsetSmp/96000.0,m1->durSmp/96000.0);
  805. if( m0 != NULL )
  806. m0->link = m1;
  807. if( t->midi == NULL )
  808. t->midi = m1;
  809. m0 = m1;
  810. mf0 = mf1;
  811. }
  812. //unsigned begBpS, endBpS;
  813. //_cmTsbCalcTempo(p, t, &begBpS, &endBpS );
  814. // render the new output sequence
  815. if((rc = _cmTakeSeqBldrRender(p)) != kOkTsbRC)
  816. cmErrMsg(&p->err,rc,"Take sequence builder rendering failed.");
  817. errLabel:
  818. if( cmMidiFileClose(&mfH) != kOkMfRC )
  819. rc = cmErrMsg(&p->err,kMidiFileFailTsbRC,"MIDI file close failed.");
  820. return rc;
  821. }
  822. cmTsbRC_t cmTakeSeqBldrUnloadTake( cmTakeSeqBldrH_t h, unsigned tlMarkUid )
  823. {
  824. cmTsbRC_t rc = kOkTsbRC;
  825. cmTsb_t* p = _cmTsbHandleToPtr(h);
  826. cmTakeTsb_t* t;
  827. if((t = _cmTsbMarkerUidToTake(p, tlMarkUid )) == NULL )
  828. return cmErrMsg(&p->err,kInvalidArgTsbRC,"The take indicated by marker id %i could not be found.",tlMarkUid);
  829. t = _cmTsbTakeUnlink(p,t);
  830. assert( t != NULL );
  831. _cmTsbTakeFree(p,&t);
  832. // render the new output sequence
  833. if((rc = _cmTakeSeqBldrRender(p)) != kOkTsbRC)
  834. cmErrMsg(&p->err,rc,"Take sequence builder rendering failed.");
  835. return rc;
  836. }
  837. double cmTakeSeqBldrSampleRate( cmTakeSeqBldrH_t h )
  838. {
  839. cmTsb_t* p = _cmTsbHandleToPtr(h);
  840. if( cmTimeLineIsValid( p->tlH ) )
  841. return cmTimeLineSampleRate(p->tlH );
  842. return 0;
  843. }
  844. cmScH_t cmTakeSeqBldrScoreHandle( cmTakeSeqBldrH_t h )
  845. {
  846. cmTsb_t* p = _cmTsbHandleToPtr(h);
  847. return p->scH;
  848. }
  849. unsigned cmTakeSeqBldrScTrkTakeCount( cmTakeSeqBldrH_t h )
  850. {
  851. cmTsb_t* p = _cmTsbHandleToPtr(h);
  852. return p->scTrkTakeN;
  853. }
  854. cmTsbRC_t cmTakeSeqBldrScTrkTake( cmTakeSeqBldrH_t h, unsigned idx, cmTksbScTrkTake_t* ref )
  855. {
  856. cmTsb_t* p = _cmTsbHandleToPtr(h);
  857. assert( idx < p->scTrkTakeN );
  858. ref->minScEvtIdx = p->scTrkTakeV[ idx ].minScEvtIdx;
  859. ref->maxScEvtIdx = p->scTrkTakeV[ idx ].maxScEvtIdx;
  860. ref->tlMarkerUid = p->scTrkTakeV[idx].tlMarkerUid;
  861. return kOkTsbRC;
  862. }
  863. const cmChar_t* cmTakeSeqBldrScTrkTakeText( cmTakeSeqBldrH_t h, unsigned tlMarkerUid )
  864. {
  865. cmTsb_t* p = _cmTsbHandleToPtr(h);
  866. cmTlObj_t* tlObj = NULL;
  867. cmTlMarker_t* tlMark = NULL;
  868. if((tlObj = cmTimeLineIdToObj( p->tlH, cmInvalidId, tlMarkerUid )) == NULL )
  869. {
  870. cmErrMsg(&p->err,kTimeLineFailTsbRC,"Unable to locate the time line record for score-track-take uid: %i.",tlMarkerUid);
  871. return NULL;
  872. }
  873. if((tlMark = cmTimeLineMarkerObjPtr( p->tlH, tlObj)) == NULL )
  874. {
  875. cmErrMsg(&p->err,kTimeLineFailTsbRC,"Unable to cast a time line marker object for score-track-take %i.",tlMarkerUid);
  876. return NULL;
  877. }
  878. return tlMark->text;
  879. }
  880. cmTsbRC_t cmTakeSeqBldrPlaySeekLoc( cmTakeSeqBldrH_t h, unsigned scLocIdx )
  881. {
  882. cmTsb_t* p = _cmTsbHandleToPtr(h);
  883. if( p->out == NULL )
  884. return cmErrMsg(&p->err,kRenderSeqEmptyTsbRC,"Seek faied. The render sequence is empty.");
  885. if( scLocIdx == cmInvalidIdx )
  886. p->evt = p->out->midi;
  887. else
  888. {
  889. cmScoreLoc_t* loc;
  890. if(( loc = cmScoreLoc(p->scH, scLocIdx )) == NULL )
  891. return cmErrMsg(&p->err,kInvalidArgTsbRC,"Seek failed. The requested score location (%i) could not be found.",scLocIdx);
  892. if( loc->evtCnt == 0 || loc->evtArray[0]==NULL )
  893. return cmErrMsg(&p->err,kInvalidArgTsbRC,"Seek failed. The requested score location (%i) has no associated event.",scLocIdx);
  894. unsigned scEvtIdx = loc->evtArray[0]->index;
  895. cmMidiTsb_t* m = p->out->midi;
  896. for(; m!=NULL; m=m->link)
  897. if( m->scEvtIdx >= scEvtIdx )
  898. {
  899. p->evt = m;
  900. break;
  901. }
  902. if( m == NULL )
  903. return cmErrMsg(&p->err,kInvalidArgTsbRC,"Seek failed. The requested score event index (%i) is out of range.",scEvtIdx );
  904. }
  905. return kOkTsbRC;
  906. }
  907. cmTsbRC_t cmTakeSeqBldrPlayExec( cmTakeSeqBldrH_t h, unsigned deltaSmp, cmTakeSeqBldrPlayFunc_t cbFunc, void* cbArg )
  908. {
  909. cmTsb_t* p = _cmTsbHandleToPtr(h);
  910. if( p->evt == NULL )
  911. return kOkTsbRC;
  912. // advance the play clock
  913. p->absPlaySmp += deltaSmp;
  914. // if the next event time has elapsed
  915. while( p->evt != NULL && p->absEvtSmp <= p->absPlaySmp )
  916. {
  917. // make the event callback
  918. cmTksbEvent_t e;
  919. e.smpIdx = p->absEvtSmp;
  920. e.status = p->evt->status;
  921. e.d0 = p->evt->d0;
  922. e.d1 = p->evt->d1;
  923. cbFunc(cbArg,&e);
  924. do
  925. {
  926. // advance the current play event
  927. p->evt = p->evt->link;
  928. // the last event was not encountered and the events offset time is legal
  929. if( p->evt != NULL && p->evt->offsetSmp != cmInvalidIdx )
  930. {
  931. // advance the event absolute time
  932. p->absEvtSmp += p->evt->offsetSmp;
  933. break;
  934. }
  935. }while( p->evt != NULL );
  936. }
  937. return kOkTsbRC;
  938. }
  939. void cmTakeSeqBldrRendReset( cmTakeSeqBldrH_t h )
  940. {
  941. cmTsb_t* p = _cmTsbHandleToPtr(h);
  942. if( p->out == NULL )
  943. return;
  944. p->rend = p->out->midi;
  945. }
  946. void _cmTakeSeqBldrMidiToRend( cmTksbRend_t* r, const cmMidiTsb_t* m )
  947. {
  948. r->rid = m->rid;
  949. r->srcId = m->srcId;
  950. r->scEvtIdx = m->scEvtIdx;
  951. r->flags = m->flags;
  952. r->offsetSmp = m->offsetSmp;
  953. r->durSmp = m->durSmp;
  954. r->evt.smpIdx = 0;
  955. r->evt.status = m->status;
  956. r->evt.d0 = m->d0;
  957. r->evt.d1 = m->d1;
  958. }
  959. cmMidiTsb_t* _cmTakeSeqBldrRidToMidi( cmTsb_t* p, unsigned rid )
  960. {
  961. cmMidiTsb_t* m;
  962. if( p->out == NULL )
  963. return NULL;
  964. for(m=p->out->midi; m!=NULL; m=m->link)
  965. if( m->rid == rid )
  966. return m;
  967. return NULL;
  968. }
  969. bool cmTakeSeqBldrRendNext(cmTakeSeqBldrH_t h, cmTksbRend_t* r)
  970. {
  971. cmTsb_t* p = _cmTsbHandleToPtr(h);
  972. cmMidiTsb_t* m = p->rend;
  973. if( m == NULL )
  974. return false;
  975. p->rend = p->rend->link;
  976. _cmTakeSeqBldrMidiToRend(r,m);
  977. return true;
  978. }
  979. cmTsbRC_t cmTakeSeqBldrRendInfo( cmTakeSeqBldrH_t h, unsigned rid, cmTksbRend_t* r )
  980. {
  981. cmTsb_t* p = _cmTsbHandleToPtr(h);
  982. cmMidiTsb_t* m;
  983. if((m = _cmTakeSeqBldrRidToMidi(p, rid )) == NULL )
  984. return cmErrMsg(&p->err,kInvalidArgTsbRC,"Unable to locate the MIDI render record associated with rid:%i.",rid);
  985. _cmTakeSeqBldrMidiToRend(r,m);
  986. return kOkTsbRC;
  987. }
  988. cmTsbRC_t cmTakeSeqBldrRendDelete( cmTakeSeqBldrH_t h, unsigned rid )
  989. {
  990. cmTsb_t* p = _cmTsbHandleToPtr(h);
  991. if( p->out == NULL || p->out->midi == NULL )
  992. return kOkTsbRC;
  993. cmMidiTsb_t* m1 = p->out->midi;
  994. for(; m1!=NULL; m1=m1->link)
  995. if( m1->rid == rid )
  996. {
  997. // if there is no recd before m1
  998. if( m1->ref == NULL )
  999. {
  1000. p->out->midi = m1->link;
  1001. // if there is a record after m1
  1002. if( p->out->midi != NULL )
  1003. {
  1004. p->out->midi->ref = NULL;
  1005. p->out->midi->offsetSmp = 0; // the first record never has a time offset
  1006. }
  1007. }
  1008. else // there is a record before m1
  1009. {
  1010. m1->ref->link = m1->link;
  1011. // if there is a record after m1
  1012. if( m1->link != NULL )
  1013. {
  1014. m1->link->ref = m1->ref;
  1015. m1->link->offsetSmp += m1->offsetSmp; // absorb m1's offset time into the next revent
  1016. }
  1017. }
  1018. cmMemFree(m1);
  1019. break;
  1020. }
  1021. _cmTsbTakePrint(p->out);
  1022. return kOkTsbRC;
  1023. }
  1024. cmTsbRC_t cmTakeSeqBldrRendInsert( cmTakeSeqBldrH_t h, const cmTksbEvent_t* e, unsigned durSmp, unsigned* ridRef)
  1025. {
  1026. cmTsb_t* p = _cmTsbHandleToPtr(h);
  1027. if( ridRef != NULL )
  1028. *ridRef = cmInvalidId;
  1029. cmMidiTsb_t* nm = cmMemAllocZ(cmMidiTsb_t,1);
  1030. nm->rid = p->nextRid++;
  1031. nm->srcId = cmInvalidId;
  1032. nm->scEvtIdx = cmInvalidId;
  1033. nm->flags = 0;
  1034. nm->durSmp = durSmp;
  1035. nm->status = e->status;
  1036. nm->d0 = e->d0;
  1037. nm->d1 = e->d1;
  1038. if( p->out == NULL )
  1039. {
  1040. p->out = cmMemAllocZ(cmTakeTsb_t,1);
  1041. p->out->tlMarkerUid = cmInvalidId;
  1042. }
  1043. if( p->out->midi == NULL )
  1044. {
  1045. p->out->midi = nm;
  1046. goto doneLabel;
  1047. }
  1048. cmMidiTsb_t* m0 = NULL;
  1049. cmMidiTsb_t* m1 = p->out->midi;
  1050. unsigned absSmpIdx = m1==NULL ? 0 : m1->offsetSmp;
  1051. for(; m1!=NULL; m1=m1->link)
  1052. {
  1053. // absSmpIdx is the absolute time of m1
  1054. // if m1 is the recd just after the new record
  1055. if( absSmpIdx > e->smpIdx )
  1056. {
  1057. // the record prior to the new record is m0
  1058. nm->ref = m0;
  1059. // the reocrd after the new record is m1
  1060. nm->link = m1;
  1061. // the new record is before m1
  1062. m1->ref = nm;
  1063. // if the new record is first on the list
  1064. if( m0 == NULL )
  1065. {
  1066. p->out->midi = nm;
  1067. // TODO: without giving more information there is no way
  1068. // to give the old first event an offset relative to the new
  1069. // first event - so the both events will be scheduled at
  1070. // time zero.
  1071. nm->offsetSmp = 0;
  1072. }
  1073. else // the new record is between m0 and m1
  1074. {
  1075. m0->link = nm;
  1076. // offset from new record to m1
  1077. unsigned dsi = absSmpIdx - e->smpIdx;
  1078. // m1's time offset is being reduced
  1079. assert( m1->offsetSmp >= dsi );
  1080. // the offset to the new record from m0
  1081. nm->offsetSmp = m1->offsetSmp - dsi;
  1082. m1->offsetSmp = dsi;
  1083. }
  1084. break;
  1085. }
  1086. // if m1 is not the last element on the list
  1087. if( m1->link != NULL )
  1088. absSmpIdx += m1->link->offsetSmp;
  1089. else
  1090. {
  1091. // insert the new event at the end of the list
  1092. nm->ref = m1;
  1093. nm->link = NULL;
  1094. m1->link = nm;
  1095. assert( e->smpIdx > absSmpIdx );
  1096. nm->offsetSmp = e->smpIdx - absSmpIdx;
  1097. break;
  1098. }
  1099. m0 = m1;
  1100. }
  1101. doneLabel:
  1102. if( ridRef != NULL )
  1103. *ridRef = nm->rid;
  1104. //_cmTsbTakePrint(p->out);
  1105. return kOkTsbRC;
  1106. }
  1107. cmTsbRC_t cmTakeSeqBldrWrite( cmTakeSeqBldrH_t h, const cmChar_t* fn )
  1108. {
  1109. cmTsbRC_t rc = kOkTsbRC;
  1110. cmJsonH_t jsH = cmJsonNullHandle;
  1111. cmJsonNode_t* arr;
  1112. cmMidiTsb_t* m;
  1113. cmTsb_t* p = _cmTsbHandleToPtr(h);
  1114. if( p->out == NULL )
  1115. return rc;
  1116. // allocate a JSON tree
  1117. if( cmJsonInitialize(&jsH,&p->ctx) != kOkJsRC )
  1118. {
  1119. rc = cmErrMsg(&p->err,kJsonFailTsbRC,"JSON write tree allocate failed.");
  1120. goto errLabel;
  1121. }
  1122. // insert the root object
  1123. if( cmJsonCreateObject(jsH, NULL ) != kOkJsRC )
  1124. {
  1125. rc = cmErrMsg(&p->err,kJsonFailTsbRC,"JSON root object allocate failed.");
  1126. goto errLabel;
  1127. }
  1128. // create the header record
  1129. if( cmJsonInsertPairs(jsH, cmJsonRoot(jsH),
  1130. "tlMarkerUid", kIntTId, p->out->tlMarkerUid,
  1131. "tlFileName", kStringTId, cmTimeLineFileName(p->tlH),
  1132. NULL) != kOkJsRC )
  1133. {
  1134. rc = cmErrMsg(&p->err,kJsonFailTsbRC,"JSON header record create failed.");
  1135. goto errLabel;
  1136. }
  1137. // create the MIDI event array
  1138. if((arr = cmJsonInsertPairArray(jsH, cmJsonRoot(jsH), "midi")) == NULL )
  1139. {
  1140. rc = cmErrMsg(&p->err,kJsonFailTsbRC,"Create the MIDI event array.");
  1141. goto errLabel;
  1142. }
  1143. // fill the MIDI array
  1144. for(m=p->out->midi; m!=NULL; m=m->link)
  1145. {
  1146. if( cmJsonCreateFilledObject(jsH, arr,
  1147. "rid", kIntTId, m->rid,
  1148. "srcId", kIntTId, m->srcId,
  1149. "scEvtIdx", kIntTId, m->scEvtIdx,
  1150. "flags", kIntTId, m->flags,
  1151. "offsetSmp", kIntTId, m->offsetSmp,
  1152. "durSmp", kIntTId, m->durSmp,
  1153. "status", kIntTId, m->status,
  1154. "d0", kIntTId, m->d0,
  1155. "d1", kIntTId, m->d1,
  1156. NULL) == NULL )
  1157. {
  1158. rc = cmErrMsg(&p->err,kJsonFailTsbRC,"JSON MIDI record create failed.");
  1159. goto errLabel;
  1160. }
  1161. }
  1162. // write the tree
  1163. if( cmJsonWrite( jsH, cmJsonRoot(jsH), fn ) != kOkJsRC )
  1164. {
  1165. rc = cmErrMsg(&p->err,kJsonFailTsbRC,"JSON write to '%s' failed.",cmStringNullGuard(fn));
  1166. }
  1167. errLabel:
  1168. if( cmJsonFinalize(&jsH) != kOkJsRC )
  1169. rc = cmErrMsg(&p->err,kJsonFailTsbRC,"JSON tree finalize failed.");
  1170. return rc;
  1171. }
  1172. cmTsbRC_t cmTakeSeqBldrRead( cmTakeSeqBldrH_t h, const cmChar_t* fn )
  1173. {
  1174. cmTsbRC_t rc = kOkTsbRC;
  1175. cmTsb_t* p = _cmTsbHandleToPtr(h);
  1176. cmJsonH_t jsH = cmJsonNullHandle;
  1177. const cmChar_t* jsErrLabel = NULL;
  1178. cmTakeTsb_t* t = NULL;
  1179. cmMidiTsb_t* m0 = NULL;
  1180. cmJsonNode_t* arr = NULL;
  1181. cmJsRC_t jsRC;
  1182. if( cmJsonInitializeFromFile( &jsH, fn, &p->ctx ) != kOkJsRC )
  1183. {
  1184. rc = cmErrMsg(&p->err,kJsonFailTsbRC,"Unable to parse the JSON file '%s'.",cmStringNullGuard(fn));
  1185. goto errLabel;
  1186. }
  1187. t = cmMemAllocZ(cmTakeTsb_t,1);
  1188. if((jsRC = cmJsonMemberValues( cmJsonRoot(jsH), &jsErrLabel,
  1189. "tlMarkerUid", kIntTId, &t->tlMarkerUid,
  1190. "midi", kArrayTId, &arr,
  1191. NULL)) != kOkJsRC )
  1192. {
  1193. if( jsRC == kNodeNotFoundJsRC && jsErrLabel != NULL )
  1194. rc = cmErrMsg(&p->err,kParseFailTsbRC,"JSON file MIDI render header failed missing required field:'%s'",cmStringNullGuard(jsErrLabel));
  1195. else
  1196. rc = cmErrMsg(&p->err,kParseFailTsbRC,"JSON file MIDI render header parse failed.");
  1197. goto errLabel;
  1198. }
  1199. unsigned n = cmJsonChildCount(arr);
  1200. unsigned i;
  1201. for(i=0; i<n; ++i)
  1202. {
  1203. const cmJsonNode_t* e = cmJsonArrayElementC(arr, i );
  1204. cmMidiTsb_t* m = cmMemAllocZ(cmMidiTsb_t,1);
  1205. if((jsRC = cmJsonMemberValues( e, &jsErrLabel,
  1206. "rid", kIntTId, &m->rid,
  1207. "srcId", kIntTId, &m->srcId,
  1208. "scEvtIdx", kIntTId, &m->scEvtIdx,
  1209. "flags", kIntTId, &m->flags,
  1210. "offsetSmp", kIntTId, &m->offsetSmp,
  1211. "durSmp", kIntTId, &m->durSmp,
  1212. "status", kIntTId, &m->status,
  1213. "d0", kIntTId, &m->d0,
  1214. "d1", kIntTId, &m->d1,
  1215. NULL)) != kOkJsRC )
  1216. {
  1217. if( jsRC == kNodeNotFoundJsRC && jsErrLabel != NULL )
  1218. rc = cmErrMsg(&p->err,kParseFailTsbRC,"JSON file MIDI render element failed missing required field:'%s' on index:%i",cmStringNullGuard(jsErrLabel),i);
  1219. else
  1220. rc = cmErrMsg(&p->err,kParseFailTsbRC,"JSON file MIDI render element parse failed on index: %i.", i);
  1221. goto errLabel;
  1222. }
  1223. m->ref = m0;
  1224. if( m0 == NULL )
  1225. t->midi = m;
  1226. else
  1227. m0->link = m;
  1228. }
  1229. errLabel:
  1230. if( cmJsonFinalize(&jsH) != kOkJsRC )
  1231. rc = cmErrMsg(&p->err,kJsonFailTsbRC,"JSON finalize failed.");
  1232. if( rc != kOkTsbRC )
  1233. _cmTsbTakeFree(p,&t);
  1234. else
  1235. {
  1236. _cmTsbRendTakeFree(p,&p->out);
  1237. p->out = t;
  1238. }
  1239. return rc;
  1240. }
  1241. cmTsbRC_t cmTakeSeqBldrTest( cmCtx_t* ctx )
  1242. {
  1243. const cmChar_t* scoreTrkFn = "/home/kevin/src/cmkc/src/kc/data/takeSeqBldr0.js";
  1244. cmTakeSeqBldrH_t tsbH = cmTakeSeqBldrNullHandle;
  1245. cmTsbRC_t tsbRC = kOkTsbRC;
  1246. unsigned markerIdV[] = { 2200, 2207 };
  1247. unsigned markerN = sizeof(markerIdV)/sizeof(markerIdV[0]);
  1248. unsigned i;
  1249. if((tsbRC = cmTakeSeqBldrAllocFn(ctx, &tsbH, scoreTrkFn )) != kOkTsbRC )
  1250. return cmErrMsg(&ctx->err,tsbRC,"TSB Allocate and parse '%s' failed.",scoreTrkFn);
  1251. cmRptPrintf(&ctx->rpt, "TakeSeqBldr Allocation Completed.");
  1252. for(i=0; i<markerN; ++i)
  1253. {
  1254. if((tsbRC = cmTakeSeqBldrLoadTake(tsbH,markerIdV[i],false)) != kOkTsbRC )
  1255. cmErrMsg(&ctx->err,tsbRC,"TSB load take failed.");
  1256. cmRptPrintf(&ctx->rpt, "TakeSeqBldr Load Take %i Completed.",markerIdV[i]);
  1257. }
  1258. if((tsbRC = cmTakeSeqBldrFree(&tsbH)) != kOkTsbRC )
  1259. return cmErrMsg(&ctx->err,tsbRC,"TSB Free failed.");
  1260. return tsbRC;
  1261. }