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

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