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.

cmTimeLine.c 40KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542
  1. #include "cmGlobal.h"
  2. #include "cmFloatTypes.h"
  3. #include "cmRpt.h"
  4. #include "cmErr.h"
  5. #include "cmCtx.h"
  6. #include "cmMem.h"
  7. #include "cmMallocDebug.h"
  8. #include "cmLinkedHeap.h"
  9. #include "cmJson.h"
  10. #include "cmAudioFile.h"
  11. #include "cmMidi.h"
  12. #include "cmMidiFile.h"
  13. #include "cmTimeLine.h"
  14. // id's used to track the type of a serialized object
  15. enum
  16. {
  17. kMsgTlId,
  18. kObjTlId
  19. };
  20. //
  21. typedef struct _cmTlMsg_str
  22. {
  23. unsigned typeId; // always set to kMsgTlId
  24. cmTlUiMsgTypeId_t msgId; //
  25. double srate;
  26. unsigned seqCnt;
  27. unsigned seqId;
  28. } _cmTlMsg_t;
  29. typedef struct _cmTlObj_str
  30. {
  31. void* mem;
  32. unsigned memByteCnt;
  33. cmTlObj_t* obj;
  34. struct _cmTlObj_str* prev;
  35. struct _cmTlObj_str* next;
  36. } _cmTlObj_t;
  37. typedef struct
  38. {
  39. _cmTlObj_t* first;
  40. _cmTlObj_t* last;
  41. } _cmTlSeq_t;
  42. typedef struct
  43. {
  44. cmErr_t err;
  45. cmCtx_t ctx;
  46. cmLHeapH_t lH;
  47. double srate;
  48. unsigned nextSeqId;
  49. cmTlCb_t cbFunc;
  50. void* cbArg;
  51. unsigned nextUId;
  52. char* tmpBuf;
  53. unsigned seqCnt;
  54. _cmTlSeq_t* seq; // seq[seqCnt]
  55. } _cmTl_t;
  56. typedef struct
  57. {
  58. char label[8];
  59. unsigned id;
  60. } _cmTlId_t;
  61. _cmTlId_t _cmTlIdArray[] =
  62. {
  63. { "mf", kMidiFileTlId },
  64. { "me", kMidiEvtTlId },
  65. { "af", kAudioFileTlId },
  66. { "ae", kAudioEvtTlId },
  67. { "mk", kMarkerTlId },
  68. { "", cmInvalidId }
  69. };
  70. cmTlH_t cmTimeLineNullHandle = cmSTATIC_NULL_HANDLE;
  71. _cmTl_t* _cmTlHandleToPtr( cmTlH_t h )
  72. {
  73. _cmTl_t* p = (_cmTl_t*)h.h;
  74. assert( p != NULL );
  75. return p;
  76. }
  77. _cmTlId_t* _cmTlIdLabelToRecd( _cmTl_t* p, const cmChar_t* label )
  78. {
  79. unsigned i;
  80. if( label != NULL )
  81. for(i=0; _cmTlIdArray[i].id != cmInvalidId; ++i)
  82. if( strcmp(_cmTlIdArray[i].label,label) == 0 )
  83. return _cmTlIdArray + i;
  84. return NULL;
  85. }
  86. _cmTlId_t* _cmTlIdToRecd( _cmTl_t* p, unsigned id )
  87. {
  88. unsigned i;
  89. for(i=0; _cmTlIdArray[i].id != cmInvalidId; ++i)
  90. if( _cmTlIdArray[i].id == id )
  91. return _cmTlIdArray + i;
  92. return NULL;
  93. }
  94. const cmChar_t* _cmTlIdToLabel( _cmTl_t* p, unsigned id )
  95. {
  96. _cmTlId_t* rp;
  97. if((rp = _cmTlIdToRecd(p,id)) != NULL )
  98. return rp->label;
  99. return "";
  100. }
  101. unsigned _cmTlIdLabelToId( _cmTl_t* p, const cmChar_t* label )
  102. {
  103. _cmTlId_t* rp;
  104. if((rp = _cmTlIdLabelToRecd(p,label)) != NULL )
  105. return rp->id;
  106. return cmInvalidId;
  107. }
  108. // cast a generic object to a midi file object
  109. cmTlMidiFile_t* _cmTlMidiFileObjPtr( _cmTl_t* p, cmTlObj_t* op, bool errFl )
  110. {
  111. if( op->typeId != kMidiFileTlId )
  112. {
  113. if( errFl && p != NULL)
  114. cmErrMsg(&p->err,kTypeCvtFailTlRC,"A time line object type promotion failed.");
  115. return NULL;
  116. }
  117. return (cmTlMidiFile_t*)op;
  118. }
  119. // cast a generic object to a midi event object
  120. cmTlMidiEvt_t* _cmTlMidiEvtObjPtr( _cmTl_t* p, cmTlObj_t* op, bool errFl )
  121. {
  122. if( op->typeId != kMidiEvtTlId )
  123. {
  124. if( errFl && p != NULL )
  125. cmErrMsg(&p->err,kTypeCvtFailTlRC,"A time line object type promotion failed.");
  126. return NULL;
  127. }
  128. return (cmTlMidiEvt_t*)op;
  129. }
  130. // case a generic object to an audio file object
  131. cmTlAudioFile_t* _cmTlAudioFileObjPtr( _cmTl_t* p, cmTlObj_t* op, bool errFl )
  132. {
  133. if( op->typeId != kAudioFileTlId )
  134. {
  135. if( errFl && p != NULL)
  136. cmErrMsg(&p->err,kTypeCvtFailTlRC,"A time line object type promotion failed.");
  137. return NULL;
  138. }
  139. return (cmTlAudioFile_t*)op;
  140. }
  141. // cast a generic object an audio event object to
  142. cmTlAudioEvt_t* _cmTlAudioEvtObjPtr( _cmTl_t* p, cmTlObj_t* op, bool errFl )
  143. {
  144. if( op->typeId != kAudioEvtTlId )
  145. {
  146. if( errFl && p != NULL)
  147. cmErrMsg(&p->err,kTypeCvtFailTlRC,"A time line object type promotion failed.");
  148. return NULL;
  149. }
  150. return (cmTlAudioEvt_t*)op;
  151. }
  152. // cast a generic object to a marker object
  153. cmTlMarker_t* _cmTlMarkerObjPtr( _cmTl_t* p, cmTlObj_t* op, bool errFl )
  154. {
  155. if( op->typeId != kMarkerTlId )
  156. {
  157. if( errFl && p != NULL)
  158. cmErrMsg(&p->err,kTypeCvtFailTlRC,"A time line object type promotion failed.");
  159. return NULL;
  160. }
  161. return (cmTlMarker_t*)op;
  162. }
  163. cmTlMidiFile_t* _cmTimeLineMidiFileObjPtr( _cmTl_t* p, cmTlObj_t* op )
  164. { return _cmTlMidiFileObjPtr(p,op,true); }
  165. cmTlMidiEvt_t* _cmTimeLineMidiEvtObjPtr( _cmTl_t* p, cmTlObj_t* op )
  166. { return _cmTlMidiEvtObjPtr(p,op,true); }
  167. cmTlAudioFile_t* _cmTimeLineAudioFileObjPtr( _cmTl_t* p, cmTlObj_t* op )
  168. { return _cmTlAudioFileObjPtr(p,op,true);}
  169. cmTlAudioEvt_t* _cmTimeLineAudioEvtObjPtr( _cmTl_t* p, cmTlObj_t* op )
  170. { return _cmTlAudioEvtObjPtr(p,op,true);}
  171. cmTlMarker_t* _cmTimeLineMarkerObjPtr( _cmTl_t* p, cmTlObj_t* op )
  172. { return _cmTlMarkerObjPtr(p,op,true);}
  173. // Locate a record which matches 'name' and (optionally) 'seqId'
  174. _cmTlObj_t* _cmTlFindRecd( _cmTl_t* p, unsigned seqId, const cmChar_t* name )
  175. {
  176. if( name == NULL )
  177. return NULL;
  178. unsigned i;
  179. for(i=0; i<p->seqCnt; ++i)
  180. if( seqId==cmInvalidId || seqId == i )
  181. {
  182. _cmTlObj_t* op = p->seq[i].first;
  183. while(op != NULL)
  184. {
  185. if( strcmp(op->obj->name,name) == 0 )
  186. return op;
  187. op = op->next;
  188. }
  189. }
  190. return NULL;
  191. }
  192. // Returns true if 'op' is a child of 'ref'.
  193. bool _cmTlIsChild( _cmTlObj_t* ref, _cmTlObj_t* op )
  194. {
  195. // if 'op' is not active then it can't be a child
  196. if( op->obj == NULL )
  197. return false;
  198. // if 'ref' is NULL then match obj's which do not have a parent
  199. if( ref == NULL )
  200. return op->obj->ref == NULL;
  201. // if 'ref' is the parent of 'op'.
  202. return op->obj->ref == ref->obj;
  203. }
  204. // calc the absolute start time of this object by adding the
  205. // time time offsets of all ancestors.
  206. int _cmTlStartTime( const cmTlObj_t* obj )
  207. {
  208. assert( obj != NULL );
  209. int t = 0;
  210. do
  211. {
  212. t += obj->begSmpIdx;
  213. obj=obj->ref;
  214. }while( obj != NULL);
  215. return t;
  216. }
  217. // Locate the closest record which is before 'np'.
  218. // When multiple records have the same distance to 'np' then the last one inserted
  219. // is taken as the closest. This way records with equal time values will be
  220. // secondarily sequenced on their order of insertion.
  221. _cmTlObj_t* _cmTlFindRecdBefore( _cmTl_t* p, const _cmTlObj_t* np )
  222. {
  223. assert( np->obj!=NULL && np->obj->seqId < p->seqCnt );
  224. // calc the absolute time of this object
  225. //int absSmpIdx = _cmTlStartTime(np->obj);
  226. int rsi;
  227. _cmTlObj_t* rp = NULL;
  228. _cmTlObj_t* op = p->seq[np->obj->seqId].first;
  229. //printf("type:%i %i\n",np->obj->typeId,absSmpIdx);
  230. // for each object in the list ...
  231. while( op != NULL )
  232. {
  233. int csi;
  234. //if( op!=np && op->obj!=NULL && (csi = _cmTlStartTime(op->obj)) <= absSmpIdx )
  235. if( (op!=np) && (op->obj!=NULL) && ((csi = op->obj->seqSmpIdx) <= np->obj->seqSmpIdx) )
  236. {
  237. if( rp == NULL || csi >= rsi )
  238. {
  239. rp = op;
  240. rsi = csi;
  241. }
  242. }
  243. op = op->next;
  244. }
  245. return rp;
  246. }
  247. // Mark 'op' and all children of 'op' for deletion.
  248. // Note that this function is recursive.
  249. cmTlRC_t _cmTlDeleteDependentRecds( _cmTl_t* p, _cmTlObj_t* op )
  250. {
  251. assert( op->obj!=NULL && op->obj->seqId < p->seqCnt );
  252. cmTlRC_t rc = kOkTlRC;
  253. _cmTlObj_t* dp = p->seq[op->obj->seqId].first;
  254. // mark all recd's that are children of 'op' for deletion
  255. while( dp != NULL )
  256. {
  257. // if 'dp' is a child of 'op'.
  258. if( _cmTlIsChild(op,dp) )
  259. if(( rc = _cmTlDeleteDependentRecds(p,dp)) != kOkTlRC )
  260. return rc;
  261. dp = dp->next;
  262. }
  263. // release any resources held by 'op'.
  264. switch(op->obj->typeId)
  265. {
  266. case kMidiFileTlId:
  267. {
  268. cmTlMidiFile_t* mp = _cmTimeLineMidiFileObjPtr(p,op->obj);
  269. cmMidiFileClose(&mp->h);
  270. }
  271. break;
  272. case kMidiEvtTlId:
  273. break;
  274. case kAudioFileTlId:
  275. {
  276. cmTlAudioFile_t* ap = _cmTimeLineAudioFileObjPtr(p,op->obj);
  277. cmAudioFileDelete(&ap->h);
  278. }
  279. break;
  280. case kAudioEvtTlId:
  281. break;
  282. case kMarkerTlId:
  283. break;
  284. default:
  285. return cmErrMsg(&p->err,kUnknownRecdTypeTlRC,"An unknown time line object type (%i) was encounterned during object deletion.",op->obj->typeId);
  286. }
  287. // mark 'op' as deleted by setting op->obj to NULL
  288. op->obj = NULL;
  289. return kOkTlRC;
  290. }
  291. // Delete 'op' and all of its dependents.
  292. cmTlRC_t _cmTlDeleteRecd( _cmTl_t* p, _cmTlObj_t* op )
  293. {
  294. if( op == NULL )
  295. return kOkTlRC;
  296. assert( op->obj!=NULL && op->obj->seqId < p->seqCnt );
  297. cmTlRC_t rc;
  298. _cmTlSeq_t* s = p->seq + op->obj->seqId;
  299. // mark this object and any objecs which are dependent on it for deletion
  300. if((rc =_cmTlDeleteDependentRecds(p,op)) != kOkTlRC )
  301. return rc;
  302. // unlink and delete and records marked for deletion
  303. op = s->first;
  304. while( op )
  305. {
  306. _cmTlObj_t* tp = op->next;
  307. // if this object is marked for deletion unlink it from this master list
  308. if( op->obj == NULL )
  309. {
  310. if( op->next != NULL )
  311. op->next->prev = op->prev;
  312. else
  313. {
  314. assert( s->last == op );
  315. s->last = op->prev;
  316. }
  317. if( op->prev != NULL )
  318. op->prev->next = op->next;
  319. else
  320. {
  321. assert( s->first == op );
  322. s->first = op->next;
  323. }
  324. // free the record
  325. cmLhFreePtr(p->lH,(void**)&op->mem);
  326. }
  327. op = tp;
  328. }
  329. return rc;
  330. }
  331. // Insert 'op' after 'bp'.
  332. // If 'bp' is NULL then 'op' is inserted as p->seq[op->seqId].first.
  333. void _cmTlInsertAfter( _cmTl_t* p, _cmTlObj_t* bp, _cmTlObj_t* op )
  334. {
  335. assert( op->obj!=NULL && op->obj->seqId < p->seqCnt );
  336. _cmTlSeq_t* s = p->seq + op->obj->seqId;
  337. op->prev = bp;
  338. // if op is being inserted at the beginning of the list
  339. if( bp == NULL )
  340. {
  341. op->next = s->first;
  342. if( s->first != NULL )
  343. s->first->prev = op;
  344. s->first = op;
  345. }
  346. else // op is being inserted in the middle or end of the list
  347. {
  348. op->next = bp->next;
  349. if( bp->next != NULL )
  350. bp->next->prev = op;
  351. else
  352. {
  353. // insertion at end
  354. assert( bp == s->last );
  355. s->last = op;
  356. }
  357. bp->next = op;
  358. }
  359. if( s->last == NULL )
  360. s->last = op;
  361. if( s->first == NULL )
  362. s->first = op;
  363. }
  364. // Allocate an object record
  365. cmTlRC_t _cmTlAllocRecd2(
  366. _cmTl_t* p,
  367. const cmChar_t* nameStr, // NULL, empty or unique name
  368. _cmTlObj_t* refPtr, // parent object
  369. int begSmpIdx, // start time
  370. unsigned durSmpCnt, // duration
  371. unsigned typeId, // object type id
  372. unsigned seqId, // owning seq
  373. unsigned recdByteCnt, // byte for type specific data to follow _cmTlObj_t data
  374. _cmTlObj_t** opp ) // return pointer
  375. {
  376. *opp = NULL;
  377. if( nameStr == NULL )
  378. nameStr = "";
  379. // get the length of the recd name field
  380. unsigned nameByteCnt = strlen(nameStr)+1;
  381. // verify that the name was not already used by another recd
  382. if( nameByteCnt>1 && _cmTlFindRecd(p,seqId,nameStr) != NULL )
  383. return cmErrMsg(&p->err,kDuplNameTlRC,"The object identifier '%s' was already used.",nameStr);
  384. assert( refPtr==NULL || refPtr->obj !=NULL );
  385. if( refPtr != NULL && refPtr->obj->seqId != seqId )
  386. return cmErrMsg(&p->err,kInvalidSeqIdTlRC,"The sequence id of the reference object (%i) does not match the sequence id (%i) of the new object (label:%s).",refPtr->obj->seqId,seqId,cmStringNullGuard(nameStr));
  387. if( seqId >= p->seqCnt )
  388. {
  389. // assume the sequence id's arrive in increasing order
  390. assert( seqId == p->seqCnt );
  391. assert( refPtr == NULL );
  392. p->seqCnt = seqId+1;
  393. p->seq = cmMemResizePZ(_cmTlSeq_t,p->seq,p->seqCnt);
  394. }
  395. // calc the total size of the recd. memory layout: [name /0 _cmTlObj_t cmTlObj_t <type specific fields> ]
  396. unsigned byteCnt = sizeof(unsigned) + sizeof(unsigned) + nameByteCnt + sizeof(_cmTlObj_t) + recdByteCnt;
  397. void* mem = cmLHeapAllocZ( p->lH, byteCnt );
  398. unsigned* tidPtr = (unsigned*)mem;
  399. unsigned* parentIdPtr = tidPtr + 1;
  400. cmChar_t* name = (cmChar_t*)(parentIdPtr + 1);
  401. _cmTlObj_t* op = (_cmTlObj_t*)(name + nameByteCnt);
  402. cmTlObj_t* tp = (cmTlObj_t*)(op+1);
  403. // The entire object is contained in mem[]
  404. // Memory Layout:
  405. // kObjTlId parentId name[] \0 _cmTlObj_t cmTlObj_t [recdByteCnt - sizeof(cmTlObj_t)]
  406. strcpy(name,nameStr);
  407. // the first element in the mem[] buffer must be kObjTlId - this allows
  408. // mem[] to be used directly as the serialized version of the buffer.
  409. *tidPtr = kObjTlId;
  410. *parentIdPtr = refPtr==NULL ? cmInvalidId : refPtr->obj->uid;
  411. tp->reserved = op;
  412. tp->seqId = seqId;
  413. tp->name = name;
  414. tp->uid = p->nextUId++;
  415. tp->typeId = typeId;
  416. tp->ref = refPtr==NULL ? NULL : refPtr->obj;
  417. tp->begSmpIdx = refPtr==NULL ? 0 : begSmpIdx;
  418. tp->durSmpCnt = durSmpCnt;
  419. tp->seqSmpIdx = refPtr==NULL ? 0 : refPtr->obj->seqSmpIdx + begSmpIdx;
  420. tp->flags = 0;
  421. tp->text = NULL;
  422. op->obj = tp;
  423. op->mem = mem;
  424. op->memByteCnt = byteCnt;
  425. op->next = NULL;
  426. op->prev = NULL;
  427. //if( seqId == 4 )
  428. // printf("seq:%i id:%i type:%i accum:%i ref:%i offs:%i %f\n",seqId, tp->uid, tp->typeId, tp->seqSmpIdx, refPtr==NULL?-1:refPtr->obj->uid, begSmpIdx, begSmpIdx/(96000.0*60.0) );
  429. _cmTlInsertAfter(p, _cmTlFindRecdBefore(p,op), op );
  430. *opp = op;
  431. return kOkTlRC;
  432. }
  433. cmTlRC_t _cmTlAllocRecd( _cmTl_t* p, const cmChar_t* nameStr, const cmChar_t* refIdStr, int begSmpIdx, unsigned durSmpCnt, unsigned typeId, unsigned seqId, unsigned recdByteCnt, _cmTlObj_t** opp )
  434. {
  435. // locate the obj recd that this recd is part of (the parent recd)
  436. _cmTlObj_t* refPtr = _cmTlFindRecd(p,seqId,refIdStr);
  437. // if this obj has a parent but it was not found
  438. if( refPtr == NULL && refIdStr!=NULL && strlen(refIdStr)>0 )
  439. return cmErrMsg(&p->err,kRefNotFoundTlRC,"Reference identifier '%s' not found for object '%s'.",refIdStr,cmStringNullGuard(nameStr));
  440. return _cmTlAllocRecd2(p,nameStr,refPtr,begSmpIdx,durSmpCnt,typeId,seqId,recdByteCnt,opp);
  441. }
  442. void _cmTlNotifyListener( _cmTl_t* p, cmTlUiMsgTypeId_t msgTypeId, _cmTlObj_t* op, unsigned seqId )
  443. {
  444. if( p->cbFunc == NULL )
  445. return;
  446. switch( msgTypeId )
  447. {
  448. case kInitMsgTlId:
  449. case kFinalMsgTlId:
  450. case kDoneMsgTlId:
  451. {
  452. _cmTlMsg_t m;
  453. m.typeId = kMsgTlId;
  454. m.msgId = msgTypeId;
  455. m.srate = p->srate;
  456. m.seqCnt = p->seqCnt;
  457. m.seqId = seqId;
  458. p->cbFunc( p->cbArg, &m, sizeof(m) );
  459. }
  460. break;
  461. case kInsertMsgTlId:
  462. if( op != NULL )
  463. p->cbFunc( p->cbArg, op->mem, op->memByteCnt );
  464. break;
  465. default:
  466. { assert(0); }
  467. }
  468. }
  469. cmTlRC_t _cmTlAllocAudioFileRecd( _cmTl_t* p, const cmChar_t* nameStr, const cmChar_t* refIdStr, int begSmpIdx, unsigned seqId, const cmChar_t* fn )
  470. {
  471. cmAudioFileH_t afH = cmNullAudioFileH;
  472. cmAudioFileInfo_t info;
  473. cmRC_t afRC = cmOkRC;
  474. cmTlRC_t rc;
  475. _cmTlObj_t* op = NULL;
  476. unsigned recdByteCnt = sizeof(cmTlAudioFile_t) + strlen(fn) + 1;
  477. if( cmAudioFileIsValid( afH = cmAudioFileNewOpen(fn, &info, &afRC, p->err.rpt )) == false )
  478. return cmErrMsg(&p->err,kAudioFileFailTlRC,"The time line audio file '%s' could not be opened.",cmStringNullGuard(fn));
  479. if((rc = _cmTlAllocRecd(p,nameStr,refIdStr,begSmpIdx,info.frameCnt,kAudioFileTlId,seqId,recdByteCnt,&op)) != kOkTlRC )
  480. goto errLabel;
  481. assert(op != NULL && fn != NULL );
  482. cmTlAudioFile_t* ap = _cmTimeLineAudioFileObjPtr(p,op->obj);
  483. char* cp = (char*)ap;
  484. assert(ap != NULL );
  485. ap->h = afH;
  486. ap->info = info;
  487. ap->fn = cp + sizeof(cmTlAudioFile_t);
  488. strcpy(ap->fn,fn); // copy the file name into the extra memory
  489. assert( ap->fn + strlen(fn) + 1 == cp + recdByteCnt );
  490. op->obj->text = ap->fn;
  491. // notify listeners that an new object was created by sending a kObjTlId msg
  492. // notifiy any listeners that a midi file object was created
  493. //_cmTlNotifyListener(p, kInsertMsgTlId, op );
  494. errLabel:
  495. if( rc != kOkTlRC )
  496. cmAudioFileDelete(&afH);
  497. return rc;
  498. }
  499. cmTlRC_t _cmTlProcMidiFile( _cmTl_t* p, _cmTlObj_t* op, cmMidiFileH_t mfH )
  500. {
  501. cmTlRC_t rc = kOkTlRC;
  502. cmTlMidiFile_t* mfp = _cmTimeLineMidiFileObjPtr(p,op->obj);
  503. unsigned mn = cmMidiFileMsgCount(mfH);
  504. const cmMidiTrackMsg_t** mapp = cmMidiFileMsgArray(mfH);
  505. unsigned mi = 0;
  506. double accum = 0;
  507. _cmTlObj_t* refOp = op;
  508. bool fl = false;
  509. unsigned dtick = 0;
  510. mfp->noteOnCnt = 0;
  511. // for each midi message
  512. for(; mi<mn; ++mi)
  513. {
  514. _cmTlObj_t* meop = NULL;
  515. const cmMidiTrackMsg_t* mp = mapp[mi];
  516. dtick = mp->dtick;
  517. if( fl )
  518. {
  519. dtick = 0;
  520. fl = mp->dtick == 0;
  521. }
  522. accum += dtick * p->srate / 1000000;
  523. //int begSmpIdx = floor(accum_micros * p->srate / 1000000);
  524. int begSmpIdx = floor( dtick * p->srate / 1000000 );
  525. int durSmpCnt = 0;
  526. unsigned midiTrkMsgByteCnt = cmMidiFilePackTrackMsgBufByteCount( mp );
  527. unsigned recdByteCnt = sizeof(cmTlMidiEvt_t) + midiTrkMsgByteCnt;
  528. //if( mfp->obj.seqId==4 && mi<=25 )
  529. // printf("%s: bsi:%9i acc:%f smp acc:%f min %s\n", mp->status == kNoteOnMdId?"non":" ", begSmpIdx, accum, accum / (p->srate * 60),cmStringNullGuard(mfp->obj.name));
  530. // count the note-on messages
  531. if( mp->status == kNoteOnMdId )
  532. {
  533. durSmpCnt = floor(mp->u.chMsgPtr->durTicks * p->srate / 1000000 );
  534. ++mfp->noteOnCnt;
  535. }
  536. // allocate the generic time-line object record
  537. if((rc = _cmTlAllocRecd2(p, NULL, refOp, begSmpIdx, durSmpCnt, kMidiEvtTlId, mfp->obj.seqId, recdByteCnt, &meop)) != kOkTlRC )
  538. goto errLabel;
  539. assert( meop != NULL );
  540. cmTlMidiEvt_t* mep = _cmTimeLineMidiEvtObjPtr(p,meop->obj);
  541. char* cp = (char*)mep;
  542. assert( mep != NULL );
  543. // Set the cmTlMidiEvt_t.msg cmMidiTrkMsg_t pointer to point to the
  544. // extra memory allocated just past the cmTlMidiEvt_t recd.
  545. mep->msg = (cmMidiTrackMsg_t*)(cp + sizeof(cmTlMidiEvt_t));
  546. mep->midiFileObjId = mfp->obj.uid;
  547. // Do not write MIDI objects that are part of a MIDI file. They will be automatically
  548. // loaded when the time line is loaded and therefore do not need to be save
  549. // explicitely in the time line data.
  550. meop->obj->flags = cmSetFlag(meop->obj->flags,kNoWriteTlFl);
  551. // Pack the cmMidiTrackMsg_t record into the extra memory
  552. cmMidiFilePackTrackMsg( mp, (char*)mep->msg, midiTrkMsgByteCnt );
  553. // verify that the memory allocation was calculated correctly
  554. assert( cp + recdByteCnt == ((char*)mep->msg) + midiTrkMsgByteCnt);
  555. // notify any listeners that a new midi event was created by sending a kObjTlId msg.
  556. //_cmTlNotifyListener(p, kInsertMsgTlId, meop );
  557. // this midi event is the ref. for the next midi evt
  558. refOp = meop;
  559. }
  560. errLabel:
  561. return rc;
  562. }
  563. cmTlRC_t _cmTlAllocMidiFileRecd( _cmTl_t* p, const cmChar_t* nameStr, const cmChar_t* refIdStr, int begSmpIdx, unsigned seqId, const cmChar_t* fn )
  564. {
  565. cmMidiFileH_t mfH = cmMidiFileNullHandle;
  566. cmTlRC_t rc = kOkTlRC;
  567. _cmTlObj_t* op = NULL;
  568. // open the midi file
  569. if( cmMidiFileOpen(fn, &mfH, &p->ctx ) != kOkMfRC )
  570. return cmErrMsg(&p->err,kMidiFileFailTlRC,"The time line midi file '%s' could not be opened.",cmStringNullGuard(fn));
  571. // force the first msg to occurr one quarter note into the file
  572. cmMidiFileSetDelay(mfH, cmMidiFileTicksPerQN(mfH) );
  573. unsigned durSmpCnt = floor(cmMidiFileDurSecs(mfH)*p->srate);
  574. // convert the midi file from ticks to microseconds
  575. cmMidiFileTickToMicros(mfH);
  576. // assign note durations to all note-on msg's
  577. cmMidiFileCalcNoteDurations(mfH);
  578. unsigned recdByteCnt = sizeof(cmTlMidiFile_t) + strlen(fn) + 1;
  579. // allocate the midi file time line object
  580. if((rc = _cmTlAllocRecd(p,nameStr,refIdStr,begSmpIdx,durSmpCnt,kMidiFileTlId,seqId,recdByteCnt,&op)) != kOkTlRC )
  581. goto errLabel;
  582. assert( op != NULL && fn != NULL );
  583. cmTlMidiFile_t* mp = _cmTimeLineMidiFileObjPtr(p,op->obj);
  584. char* cp = (char*)mp;
  585. assert(mp != NULL );
  586. mp->h = mfH;
  587. mp->fn = cp + sizeof(cmTlMidiFile_t);
  588. strcpy(mp->fn,fn); // copy the filename into the extra memory
  589. assert( mp->fn + strlen(mp->fn) + 1 == cp + recdByteCnt );
  590. op->obj->text = mp->fn;
  591. // notifiy any listeners that a midi file object was created
  592. //_cmTlNotifyListener(p, kInsertMsgTlId, op );
  593. // insert the events in the midi file as individual time line objects
  594. if((rc = _cmTlProcMidiFile(p, op, mfH)) != kOkTlRC )
  595. goto errLabel;
  596. errLabel:
  597. if( rc != kOkTlRC )
  598. {
  599. cmMidiFileClose(&mfH);
  600. _cmTlDeleteRecd(p, op);
  601. }
  602. return rc;
  603. }
  604. cmTlRC_t _cmTlAllocMarkerRecd( _cmTl_t* p, const cmChar_t* nameStr, const cmChar_t* refIdStr, int begSmpIdx, unsigned durSmpCnt, unsigned seqId, const cmChar_t* text )
  605. {
  606. cmTlRC_t rc = kOkTlRC;
  607. _cmTlObj_t* op = NULL;
  608. const cmChar_t* textStr = text==NULL ? "" : text;
  609. // add memory at the end of the the cmTlMarker_t record to hold the text string.
  610. unsigned recdByteCnt = sizeof(cmTlMarker_t) + strlen(textStr) + 1;
  611. if((rc = _cmTlAllocRecd(p,nameStr,refIdStr,begSmpIdx,durSmpCnt,kMarkerTlId,seqId,recdByteCnt,&op)) != kOkTlRC )
  612. goto errLabel;
  613. assert(op != NULL);
  614. cmTlMarker_t* mp = _cmTimeLineMarkerObjPtr(p,op->obj);
  615. assert(mp != NULL );
  616. // copy the marker text string into the memory just past the cmTlMarker_t recd.
  617. cmChar_t* tp = (cmChar_t*)(mp + 1);
  618. strcpy(tp,textStr);
  619. mp->text = tp;
  620. op->obj->text = tp;
  621. // notify listeners
  622. //_cmTlNotifyListener(p, kInsertMsgTlId, op );
  623. errLabel:
  624. if( op != NULL && rc != kOkTlRC )
  625. _cmTlDeleteRecd(p,op);
  626. return rc;
  627. }
  628. cmTlRC_t _cmTlAllocAudioEvtRecd( _cmTl_t* p, const cmChar_t* nameStr, const cmChar_t* refIdStr, int begSmpIdx, unsigned durSmpCnt, unsigned seqId, const cmChar_t* text )
  629. {
  630. cmTlRC_t rc = kOkTlRC;
  631. _cmTlObj_t* op = NULL;
  632. const cmChar_t* textStr = text==NULL ? "" : text;
  633. unsigned recdByteCnt = sizeof(cmTlAudioEvt_t) + strlen(textStr) + 1;
  634. if((rc = _cmTlAllocRecd(p,nameStr,refIdStr,begSmpIdx,durSmpCnt,kAudioEvtTlId,seqId,recdByteCnt,&op)) != kOkTlRC )
  635. goto errLabel;
  636. assert(op != NULL);
  637. cmTlAudioEvt_t* mp = _cmTimeLineAudioEvtObjPtr(p,op->obj);
  638. assert(mp != NULL );
  639. // copy the marker text string into the memory just past the cmTlAudioEvt_t recd.
  640. cmChar_t* tp = (cmChar_t*)(mp + 1);
  641. strcpy(tp,textStr);
  642. mp->text = tp;
  643. op->obj->text = tp;
  644. // notify listeners
  645. //_cmTlNotifyListener(p, kInsertMsgTlId, op );
  646. errLabel:
  647. if( op != NULL && rc != kOkTlRC )
  648. _cmTlDeleteRecd(p,op);
  649. return rc;
  650. }
  651. cmTlRC_t _cmTlAllocRecdFromJson(_cmTl_t* p,const cmChar_t* nameStr, const cmChar_t* typeIdStr,const cmChar_t* refIdStr, int begSmpIdx, unsigned durSmpCnt, unsigned seqId, const cmChar_t* textStr)
  652. {
  653. cmTlRC_t rc = kOkTlRC;
  654. unsigned typeId = _cmTlIdLabelToId(p,typeIdStr);
  655. switch( typeId )
  656. {
  657. case kAudioFileTlId: rc = _cmTlAllocAudioFileRecd(p,nameStr,refIdStr,begSmpIdx, seqId,textStr); break;
  658. case kMidiFileTlId: rc = _cmTlAllocMidiFileRecd( p,nameStr,refIdStr,begSmpIdx, seqId,textStr); break;
  659. case kMarkerTlId: rc = _cmTlAllocMarkerRecd( p,nameStr,refIdStr,begSmpIdx,durSmpCnt,seqId,textStr); break;
  660. case kAudioEvtTlId: rc = _cmTlAllocAudioEvtRecd( p,nameStr,refIdStr,begSmpIdx,durSmpCnt,seqId,textStr); break;
  661. default:
  662. rc = cmErrMsg(&p->err,kParseFailTlRC,"'%s' is not a valid 'objArray' record type.",cmStringNullGuard(typeIdStr));
  663. }
  664. return rc;
  665. }
  666. cmTlRC_t _cmTimeLineFinalize( _cmTl_t* p )
  667. {
  668. cmTlRC_t rc = kOkTlRC;
  669. unsigned i;
  670. for(i=0; i<p->seqCnt; ++i)
  671. while( p->seq[i].first != NULL )
  672. {
  673. if((rc = _cmTlDeleteRecd(p,p->seq[i].first)) != kOkTlRC )
  674. goto errLabel;
  675. }
  676. cmLHeapDestroy(&p->lH);
  677. //_cmTlNotifyListener(p, kFinalMsgTlId, NULL );
  678. cmMemPtrFree(&p->tmpBuf);
  679. cmMemPtrFree(&p);
  680. return kOkTlRC;
  681. errLabel:
  682. return cmErrMsg(&p->err,kFinalizeFailTlRC,"Finalize failed.");
  683. }
  684. cmTlRC_t cmTimeLineInitialize( cmCtx_t* ctx, cmTlH_t* hp, cmTlCb_t cbFunc, void* cbArg )
  685. {
  686. cmTlRC_t rc;
  687. if((rc = cmTimeLineFinalize(hp)) != kOkTlRC )
  688. return rc;
  689. _cmTl_t* p = cmMemAllocZ( _cmTl_t, 1 );
  690. cmErrSetup(&p->err,&ctx->rpt,"Time Line");
  691. p->ctx = *ctx;
  692. p->cbFunc = cbFunc;
  693. p->cbArg = cbArg;
  694. if(cmLHeapIsValid( p->lH = cmLHeapCreate( 8192, ctx )) == false )
  695. {
  696. rc = cmErrMsg(&p->err,kLHeapFailTlRC,"The linked heap allocation failed.");
  697. goto errLabel;
  698. }
  699. hp->h = p;
  700. return rc;
  701. errLabel:
  702. _cmTimeLineFinalize(p);
  703. return rc;
  704. }
  705. cmTlRC_t cmTimeLineInitializeFromFile( cmCtx_t* ctx, cmTlH_t* hp, cmTlCb_t cbFunc, void* cbArg, const cmChar_t* fn )
  706. {
  707. cmTlRC_t rc;
  708. if((rc = cmTimeLineInitialize(ctx,hp,cbFunc,cbArg)) != kOkTlRC )
  709. return rc;
  710. //_cmTl_t* p = _cmTlHandleToPtr(*hp);
  711. //_cmTlNotifyListener(p, kInitMsgTlId, NULL );
  712. return cmTimeLineReadJson(*hp,fn);
  713. }
  714. cmTlRC_t cmTimeLineFinalize( cmTlH_t* hp )
  715. {
  716. cmTlRC_t rc;
  717. if( hp == NULL || cmTimeLineIsValid(*hp) == false )
  718. return kOkTlRC;
  719. _cmTl_t* p = _cmTlHandleToPtr(*hp);
  720. if((rc = _cmTimeLineFinalize(p)) != kOkTlRC )
  721. return rc;
  722. hp->h = NULL;
  723. return rc;
  724. }
  725. bool cmTimeLineIsValid( cmTlH_t h )
  726. { return h.h != NULL; }
  727. double cmTimeLineSampleRate( cmTlH_t h )
  728. {
  729. _cmTl_t* p = _cmTlHandleToPtr(h);
  730. return p->srate;
  731. }
  732. cmTlObj_t* cmTimeLineNextObj( cmTlH_t h, cmTlObj_t* tp, unsigned seqId )
  733. {
  734. _cmTl_t* p = _cmTlHandleToPtr(h);
  735. _cmTlObj_t* op;
  736. assert( seqId < p->seqCnt );
  737. if( seqId >= p->seqCnt )
  738. return NULL;
  739. // if tp is NULL then start at the begin of the obj list ...
  740. if( tp == NULL )
  741. op = p->seq[seqId].first;
  742. else
  743. {
  744. // ... otherwise advance to the obj after tp
  745. op = (_cmTlObj_t*)tp->reserved;
  746. assert( op != NULL );
  747. op = op->next;
  748. }
  749. // if the list is empty
  750. if( op == NULL )
  751. return NULL;
  752. // return the next object which matches seqId or
  753. // the next object if seqId == cmInvalidId
  754. for(; op != NULL; op = op->next)
  755. if( (seqId == cmInvalidId) || (op->obj->seqId == seqId) )
  756. return op->obj;
  757. return NULL;
  758. }
  759. cmTlObj_t* cmTimeLineNextTypeObj( cmTlH_t h, cmTlObj_t* p, unsigned seqId, unsigned typeId )
  760. {
  761. cmTlObj_t* tp = p;
  762. while( (tp = cmTimeLineNextObj(h,tp,seqId)) != NULL )
  763. if( typeId == cmInvalidId || tp->typeId == typeId )
  764. return tp;
  765. return NULL;
  766. }
  767. cmTlMidiFile_t* cmTlNextMidiFileObjPtr( cmTlH_t h, cmTlObj_t* op, unsigned seqId )
  768. {
  769. if((op = cmTimeLineNextTypeObj(h, op, seqId, kMidiFileTlId )) == NULL )
  770. return NULL;
  771. return cmTimeLineMidiFileObjPtr(h,op);
  772. }
  773. cmTlAudioFile_t* cmTlNextAudioFileObjPtr( cmTlH_t h, cmTlObj_t* op, unsigned seqId )
  774. {
  775. if((op = cmTimeLineNextTypeObj(h, op, seqId, kAudioFileTlId )) == NULL )
  776. return NULL;
  777. return cmTimeLineAudioFileObjPtr(h,op);
  778. }
  779. cmTlMidiEvt_t* cmTlNextMidiEvtObjPtr( cmTlH_t h, cmTlObj_t* op, unsigned seqId )
  780. {
  781. if((op = cmTimeLineNextTypeObj(h, op, seqId, kMidiEvtTlId )) == NULL )
  782. return NULL;
  783. return cmTimeLineMidiEvtObjPtr(h,op);
  784. }
  785. cmTlAudioEvt_t* cmTlNextAudioEvtObjPtr( cmTlH_t h, cmTlObj_t* op, unsigned seqId )
  786. {
  787. if((op = cmTimeLineNextTypeObj(h, op, seqId, kAudioEvtTlId )) == NULL )
  788. return NULL;
  789. return cmTimeLineAudioEvtObjPtr(h,op);
  790. }
  791. cmTlMarker_t* cmTlNextMarkerObjPtr( cmTlH_t h, cmTlObj_t* op, unsigned seqId )
  792. {
  793. if((op = cmTimeLineNextTypeObj(h, op, seqId, kMarkerTlId )) == NULL )
  794. return NULL;
  795. return cmTimeLineMarkerObjPtr(h,op);
  796. }
  797. cmTlMidiFile_t* cmTimeLineMidiFileObjPtr( cmTlH_t h, cmTlObj_t* op )
  798. {
  799. _cmTl_t* p = _cmTlHandleToPtr(h);
  800. return _cmTimeLineMidiFileObjPtr(p,op);
  801. }
  802. cmTlAudioFile_t* cmTimeLineAudioFileObjPtr( cmTlH_t h, cmTlObj_t* op )
  803. {
  804. _cmTl_t* p = _cmTlHandleToPtr(h);
  805. return _cmTimeLineAudioFileObjPtr(p,op);
  806. }
  807. cmTlMidiEvt_t* cmTimeLineMidiEvtObjPtr( cmTlH_t h, cmTlObj_t* op )
  808. {
  809. _cmTl_t* p = _cmTlHandleToPtr(h);
  810. return _cmTimeLineMidiEvtObjPtr(p,op);
  811. }
  812. cmTlAudioEvt_t* cmTimeLineAudioEvtObjPtr( cmTlH_t h, cmTlObj_t* op )
  813. {
  814. _cmTl_t* p = _cmTlHandleToPtr(h);
  815. return _cmTimeLineAudioEvtObjPtr(p,op);
  816. }
  817. cmTlMarker_t* cmTimeLineMarkerObjPtr( cmTlH_t h, cmTlObj_t* op )
  818. {
  819. _cmTl_t* p = _cmTlHandleToPtr(h);
  820. return _cmTimeLineMarkerObjPtr(p,op);
  821. }
  822. cmTlMidiFile_t* cmTlMidiFileObjPtr( cmTlH_t h, cmTlObj_t* op )
  823. {
  824. _cmTl_t* p = _cmTlHandleToPtr(h);
  825. return _cmTlMidiFileObjPtr(p,op,false);
  826. }
  827. cmTlAudioFile_t* cmTlAudioFileObjPtr( cmTlH_t h, cmTlObj_t* op )
  828. {
  829. _cmTl_t* p = _cmTlHandleToPtr(h);
  830. return _cmTlAudioFileObjPtr(p,op,false);
  831. }
  832. cmTlMidiEvt_t* cmTlMidiEvtObjPtr( cmTlH_t h, cmTlObj_t* op )
  833. {
  834. _cmTl_t* p = _cmTlHandleToPtr(h);
  835. return _cmTlMidiEvtObjPtr(p,op,false);
  836. }
  837. cmTlAudioEvt_t* cmTlAudioEvtObjPtr( cmTlH_t h, cmTlObj_t* op )
  838. {
  839. _cmTl_t* p = _cmTlHandleToPtr(h);
  840. return _cmTlAudioEvtObjPtr(p,op,false);
  841. }
  842. cmTlMarker_t* cmTlMarkerObjPtr( cmTlH_t h, cmTlObj_t* op )
  843. {
  844. _cmTl_t* p = _cmTlHandleToPtr(h);
  845. return _cmTlMarkerObjPtr(p,op,false);
  846. }
  847. cmTlRC_t cmTimeLineInsert( cmTlH_t h, const cmChar_t* nameStr, unsigned typeId,
  848. const cmChar_t* fn, int begSmpIdx, unsigned durSmpCnt, const cmChar_t* refObjNameStr, unsigned seqId )
  849. {
  850. _cmTl_t* p = _cmTlHandleToPtr(h);
  851. return _cmTlAllocRecdFromJson(p, nameStr, _cmTlIdToLabel(p,typeId), refObjNameStr, begSmpIdx, durSmpCnt, seqId, fn);
  852. }
  853. cmTlObj_t* _cmTimeLineFindFile( _cmTl_t* p, const cmChar_t* fn, unsigned typeId )
  854. {
  855. unsigned i;
  856. for(i=0; i<p->seqCnt; ++i)
  857. {
  858. _cmTlObj_t* op = p->seq[i].first;
  859. for(; op != NULL; op=op->next )
  860. if( op->obj->typeId == typeId )
  861. {
  862. const cmChar_t* objFn = NULL;
  863. switch( typeId )
  864. {
  865. case kAudioFileTlId:
  866. objFn = ((cmTlAudioFile_t*)(op->obj))->fn;
  867. break;
  868. case kMidiFileTlId:
  869. objFn = ((cmTlMidiFile_t*)(op->obj))->fn;
  870. break;
  871. default:
  872. { assert(0); }
  873. }
  874. if( strcmp(objFn,fn) == 0 )
  875. return op->obj;
  876. }
  877. }
  878. return NULL;
  879. }
  880. cmTlAudioFile_t* cmTimeLineFindAudioFile( cmTlH_t h, const cmChar_t* fn )
  881. {
  882. _cmTl_t* p = _cmTlHandleToPtr(h);
  883. cmTlObj_t* op;
  884. if((op = _cmTimeLineFindFile(p,fn,kAudioFileTlId)) != NULL )
  885. return _cmTlAudioFileObjPtr(p,op,true);
  886. return NULL;
  887. }
  888. cmTlMidiFile_t* cmTimeLineFindMidiFile( cmTlH_t h, const cmChar_t* fn )
  889. {
  890. _cmTl_t* p = _cmTlHandleToPtr(h);
  891. cmTlObj_t* op;
  892. if((op = _cmTimeLineFindFile(p,fn,kMidiFileTlId)) != NULL )
  893. return _cmTlMidiFileObjPtr(p,op,true);
  894. return NULL;
  895. }
  896. cmTlRC_t _cmTlParseErr( cmErr_t* err, const cmChar_t* errLabelPtr, unsigned idx, const cmChar_t* fn )
  897. {
  898. cmTlRC_t rc;
  899. if( errLabelPtr != NULL )
  900. rc = cmErrMsg(err,kParseFailTlRC,"The required time line configuration field %s was not found in the record at index %i in '%s'.",cmStringNullGuard(errLabelPtr),idx,cmStringNullGuard(fn));
  901. else
  902. rc = cmErrMsg(err,kParseFailTlRC,"The time_line configuration parse failed on the record at index %i in '%s'.",idx,cmStringNullGuard(fn));
  903. return rc;
  904. }
  905. cmTlRC_t cmTimeLineReadJson( cmTlH_t h, const cmChar_t* ifn )
  906. {
  907. cmTlRC_t rc = kOkTlRC;
  908. cmJsonH_t jsH = cmJsonNullHandle;
  909. cmJsonNode_t* jnp;
  910. const cmChar_t* errLabelPtr;
  911. int i;
  912. _cmTl_t* p = _cmTlHandleToPtr(h);
  913. // open the json file
  914. if( cmJsonInitializeFromFile(&jsH, ifn, &p->ctx ) != kOkJsRC )
  915. return cmErrMsg(&p->err,kJsonFailTlRC,"JSON file initialization failed.");
  916. if((jnp = cmJsonFindValue(jsH,"time_line",cmJsonRoot(jsH),kObjectTId)) == NULL)
  917. {
  918. rc = cmErrMsg(&p->err,kParseFailTlRC,"The JSON 'time_line' object was not found in '%s'.",cmStringNullGuard(ifn));
  919. goto errLabel;
  920. }
  921. if( cmJsonMemberValues(jnp,&errLabelPtr,
  922. "srate",kRealTId,&p->srate,
  923. "objArray",kArrayTId,&jnp,
  924. NULL) != kOkJsRC )
  925. {
  926. rc = cmErrMsg(&p->err,kParseFailTlRC,"The JSON 'time_line' object header parse failed in '%s'.",cmStringNullGuard(ifn));
  927. goto errLabel;
  928. }
  929. for(i=0; i<cmJsonChildCount(jnp); ++i)
  930. {
  931. const cmJsonNode_t* rp = cmJsonArrayElementC(jnp,i);
  932. const cmChar_t* nameStr;
  933. const cmChar_t* typeIdStr;
  934. const cmChar_t* refIdStr;
  935. int begSmpIdx;
  936. unsigned durSmpCnt;
  937. unsigned seqId;
  938. const cmChar_t* textStr;
  939. if( cmJsonMemberValues(rp,&errLabelPtr,
  940. "label",kStringTId,&nameStr,
  941. "type", kStringTId,&typeIdStr,
  942. "ref", kStringTId,&refIdStr,
  943. "offset",kIntTId,&begSmpIdx,
  944. "smpCnt",kIntTId,&durSmpCnt,
  945. "trackId",kIntTId,&seqId,
  946. "textStr",kStringTId,&textStr,
  947. NULL) != kOkJsRC )
  948. {
  949. rc = _cmTlParseErr(&p->err, errLabelPtr, i, ifn );
  950. goto errLabel;
  951. }
  952. if((rc = _cmTlAllocRecdFromJson(p,nameStr,typeIdStr,refIdStr,begSmpIdx,durSmpCnt,seqId,textStr)) != kOkTlRC )
  953. goto errLabel;
  954. }
  955. errLabel:
  956. if( rc != kOkTlRC )
  957. _cmTimeLineFinalize(p);
  958. cmJsonFinalize(&jsH);
  959. return rc;
  960. }
  961. unsigned cmTimeLineSeqCount( cmTlH_t h )
  962. {
  963. _cmTl_t* p = _cmTlHandleToPtr(h);
  964. return p->seqCnt;
  965. }
  966. cmTlRC_t cmTimeLineSeqNotify( cmTlH_t h, unsigned seqId )
  967. {
  968. cmTlRC_t rc = kOkTlRC;
  969. _cmTl_t* p = _cmTlHandleToPtr(h);
  970. assert( seqId < p->seqCnt );
  971. _cmTlNotifyListener(p,kInitMsgTlId,NULL,seqId);
  972. _cmTlObj_t* op = p->seq[seqId].first;
  973. for(; op!=NULL; op=op->next)
  974. if( op->obj->seqId == seqId )
  975. _cmTlNotifyListener(p, kInsertMsgTlId, op, cmInvalidId );
  976. _cmTlNotifyListener(p,kDoneMsgTlId,NULL,seqId);
  977. return rc;
  978. }
  979. void _cmTimeLinePrintObj(_cmTl_t* p, _cmTlObj_t* op, cmRpt_t* rpt )
  980. {
  981. cmRptPrintf(rpt,"%2i %5i %9i %9i %s %10s ",op->obj->seqId, op->obj->uid, op->obj->seqSmpIdx, op->obj->begSmpIdx, _cmTlIdToLabel(p,op->obj->typeId),op->obj->name);
  982. switch(op->obj->typeId )
  983. {
  984. case kMidiFileTlId:
  985. {
  986. cmTlMidiFile_t* rp = _cmTimeLineMidiFileObjPtr(p,op->obj);
  987. cmRptPrintf(rpt,"%s ", cmMidiFileName(rp->h));
  988. }
  989. break;
  990. case kMidiEvtTlId:
  991. {
  992. cmTlMidiEvt_t* rp = _cmTimeLineMidiEvtObjPtr(p,op->obj);
  993. if( op->obj->ref != NULL )
  994. cmRptPrintf(rpt,"%s ",op->obj->ref->name);
  995. cmRptPrintf(rpt,"%s ",cmMidiStatusToLabel(rp->msg->status));
  996. }
  997. break;
  998. case kAudioFileTlId:
  999. {
  1000. cmTlAudioFile_t* rp = _cmTimeLineAudioFileObjPtr(p,op->obj);
  1001. cmRptPrintf(rpt,"%s ", cmAudioFileName(rp->h));
  1002. }
  1003. break;
  1004. case kAudioEvtTlId:
  1005. {
  1006. cmTlAudioEvt_t* rp = _cmTimeLineAudioEvtObjPtr(p,op->obj);
  1007. cmRptPrintf(rpt,"%s",rp->text);
  1008. }
  1009. break;
  1010. case kMarkerTlId:
  1011. {
  1012. cmTlMarker_t* rp = _cmTimeLineMarkerObjPtr(p,op->obj);
  1013. cmRptPrintf(rpt,"%s ", rp->text );
  1014. }
  1015. break;
  1016. }
  1017. cmRptPrint(rpt,"\n");
  1018. }
  1019. cmTlRC_t _cmTimeLineWriteRecd( _cmTl_t* p, cmJsonH_t jsH, cmJsonNode_t* np, cmTlObj_t* obj )
  1020. {
  1021. cmTlRC_t rc = kOkTlRC;
  1022. // if this recd is writable and has not been previously written
  1023. if( cmIsNotFlag(obj->flags,kNoWriteTlFl) && cmIsNotFlag(obj->flags,kReservedTlFl) )
  1024. {
  1025. const cmChar_t* refStr = NULL;
  1026. if( obj->ref != NULL )
  1027. {
  1028. refStr = obj->ref->name;
  1029. // if the ref obj was not previously written then write it (recursively) first
  1030. if( cmIsNotFlag(obj->ref->flags, kReservedTlFl) )
  1031. if((rc = _cmTimeLineWriteRecd(p,jsH,np,obj->ref)) != kOkTlRC )
  1032. return rc;
  1033. }
  1034. // create the time-line recd
  1035. if( cmJsonInsertPairs( jsH, cmJsonCreateObject(jsH,np),
  1036. "label", kStringTId, obj->name,
  1037. "type", kStringTId, _cmTlIdToLabel(p, obj->typeId ),
  1038. "ref", kStringTId, refStr==NULL ? "" : refStr,
  1039. "offset", kIntTId, obj->begSmpIdx,
  1040. "smpCnt", kIntTId, obj->durSmpCnt,
  1041. "trackId", kIntTId, obj->seqId,
  1042. "textStr", kStringTId, obj->text==NULL ? "" : obj->text,
  1043. NULL ) != kOkJsRC )
  1044. {
  1045. return cmErrMsg(&p->err,kJsonFailTlRC,"A time line insertion failed on label:%s type:%s text:%s.",cmStringNullGuard(obj->name),cmStringNullGuard(_cmTlIdToLabel(p, obj->typeId )), cmStringNullGuard(obj->text));
  1046. }
  1047. obj->flags = cmSetFlag(obj->flags, kReservedTlFl);
  1048. }
  1049. return rc;
  1050. }
  1051. cmTlRC_t cmTimeLineWrite( cmTlH_t h, const cmChar_t* fn )
  1052. {
  1053. cmTlRC_t rc = kOkTlRC;
  1054. _cmTl_t* p = _cmTlHandleToPtr(h);
  1055. cmJsonH_t jsH = cmJsonNullHandle;
  1056. cmJsonNode_t* np;
  1057. // initialize a JSON tree
  1058. if( cmJsonInitialize(&jsH,&p->ctx) != kOkJsRC )
  1059. return cmErrMsg(&p->err,kJsonFailTlRC,"JSON object initialization failed.");
  1060. // create the root object
  1061. if((np = cmJsonCreateObject(jsH,NULL)) == NULL )
  1062. {
  1063. rc = cmErrMsg(&p->err,kJsonFailTlRC,"Time line root object create failed while creating the time line file '%s.",cmStringNullGuard(fn));
  1064. goto errLabel;
  1065. }
  1066. // create the time-line object
  1067. if((np = cmJsonInsertPairObject(jsH, np, "time_line" )) == NULL )
  1068. {
  1069. rc = cmErrMsg(&p->err,kJsonFailTlRC,"'time_line' object created failed while creating the time line file '%s.",cmStringNullGuard(fn));
  1070. goto errLabel;
  1071. }
  1072. // write the sample rate
  1073. if( cmJsonInsertPairReal(jsH, np, "srate", p->srate ) != kOkJsRC )
  1074. {
  1075. rc = cmErrMsg(&p->err,kJsonFailTlRC,"'sample rate' output failed while creating the time line file '%s.",cmStringNullGuard(fn));
  1076. goto errLabel;
  1077. }
  1078. // create the time-line object array
  1079. if((np = cmJsonInsertPairArray(jsH, np, "objArray")) == NULL )
  1080. {
  1081. rc = cmErrMsg(&p->err,kJsonFailTlRC,"'objArray' output failed while creating the time line file '%s.",cmStringNullGuard(fn));
  1082. goto errLabel;
  1083. }
  1084. unsigned i;
  1085. // write each time-line object
  1086. for(i=0; i<p->seqCnt; ++i)
  1087. {
  1088. _cmTlObj_t* op = p->seq[i].first;
  1089. for(; op != NULL; op=op->next )
  1090. if((rc = _cmTimeLineWriteRecd(p,jsH,np,op->obj)) != kOkTlRC )
  1091. goto errLabel;
  1092. }
  1093. if( cmJsonErrorCode(jsH) == kOkJsRC )
  1094. if( cmJsonWrite(jsH,NULL,fn) != kOkJsRC )
  1095. {
  1096. rc = cmErrMsg(&p->err,kJsonFailTlRC,"JSON write failed.");
  1097. goto errLabel;
  1098. }
  1099. errLabel:
  1100. if( cmJsonFinalize(&jsH) != kOkJsRC )
  1101. {
  1102. rc = cmErrMsg(&p->err,kJsonFailTlRC,"JSON finalize failed.");
  1103. goto errLabel;
  1104. }
  1105. return rc;
  1106. }
  1107. cmTlRC_t cmTimeLinePrint( cmTlH_t h, cmRpt_t* rpt )
  1108. {
  1109. _cmTl_t* p = _cmTlHandleToPtr(h);
  1110. unsigned i;
  1111. for(i=0; i<p->seqCnt; ++i)
  1112. {
  1113. _cmTlObj_t* op = p->seq[i].first;
  1114. while(op != NULL)
  1115. {
  1116. _cmTimeLinePrintObj(p,op,rpt);
  1117. op = op->next;
  1118. }
  1119. }
  1120. return kOkTlRC;
  1121. }
  1122. cmTlRC_t cmTimeLinePrintFn( cmCtx_t* ctx, const cmChar_t* fn, cmRpt_t* rpt )
  1123. {
  1124. cmTlRC_t rc;
  1125. cmTlH_t h = cmTimeLineNullHandle;
  1126. if((rc = cmTimeLineInitializeFromFile(ctx,&h,NULL,NULL,fn)) != kOkTlRC )
  1127. return rc;
  1128. cmTimeLinePrint(h,rpt);
  1129. return cmTimeLineFinalize(&h);
  1130. }
  1131. cmTlRC_t cmTimeLineTest( cmCtx_t* ctx, const cmChar_t* jsFn )
  1132. {
  1133. cmTlRC_t rc = kOkTlRC;
  1134. cmTlH_t tlH = cmTimeLineNullHandle;
  1135. if((rc = cmTimeLineInitialize(ctx,&tlH,NULL,NULL)) != kOkTlRC )
  1136. return rc;
  1137. if((rc = cmTimeLineReadJson(tlH,jsFn)) != kOkTlRC )
  1138. goto errLabel;
  1139. if((rc = cmTimeLineInsert(tlH,"Mark",kMarkerTlId,"My Marker",10,0,NULL,0)) != kOkTlRC )
  1140. goto errLabel;
  1141. cmTimeLinePrint(tlH, &ctx->rpt );
  1142. errLabel:
  1143. cmTimeLineFinalize(&tlH);
  1144. return rc;
  1145. }
  1146. cmTlRC_t _cmTimeLineDecodeObj( const void* msg, unsigned msgByteCnt, cmTlUiMsg_t* r )
  1147. {
  1148. cmTlRC_t rc = kOkTlRC;
  1149. unsigned* typeIdPtr = (unsigned*)msg;
  1150. unsigned* parentIdPtr = typeIdPtr + 1;
  1151. const char* text = (const char*)(parentIdPtr + 1);
  1152. _cmTlObj_t* op = (_cmTlObj_t*)(text + strlen(text) + 1);
  1153. cmTlObj_t* tp = (cmTlObj_t*)(op + 1 );
  1154. r->msgId = kInsertMsgTlId;
  1155. r->objId = tp->uid;
  1156. r->parentObjId = *parentIdPtr;
  1157. r->seqId = tp->seqId;
  1158. r->typeId = tp->typeId;
  1159. r->begSmpIdx = tp->begSmpIdx;
  1160. r->durSmpCnt = tp->durSmpCnt;
  1161. r->label = strlen(text)==0 ? NULL : text;
  1162. r->srate = 0;
  1163. r->midiTrkMsg = NULL;
  1164. r->textStr = NULL;
  1165. switch( tp->typeId )
  1166. {
  1167. case kMidiFileTlId:
  1168. r->textStr = _cmTimeLineMidiFileObjPtr(NULL,tp)->fn;
  1169. break;
  1170. case kMidiEvtTlId:
  1171. {
  1172. cmTlMidiEvt_t* mep;
  1173. if((mep = _cmTimeLineMidiEvtObjPtr(NULL,tp)) != NULL )
  1174. {
  1175. r->midiFileObjId = mep->midiFileObjId;
  1176. r->midiTrkMsg = mep->msg;
  1177. }
  1178. }
  1179. break;
  1180. case kAudioFileTlId:
  1181. r->textStr = _cmTimeLineAudioFileObjPtr(NULL,tp)->fn;
  1182. break;
  1183. case kAudioEvtTlId:
  1184. break;
  1185. case kMarkerTlId:
  1186. r->textStr = _cmTimeLineMarkerObjPtr(NULL,tp)->text;
  1187. break;
  1188. default:
  1189. { assert(0); }
  1190. }
  1191. return rc;
  1192. }
  1193. cmTlRC_t cmTimeLineDecode( const void* msg, unsigned msgByteCnt, cmTlUiMsg_t* r )
  1194. {
  1195. cmTlRC_t rc = kOkTlRC;
  1196. _cmTlMsg_t* m = (_cmTlMsg_t*)msg;
  1197. switch( m->typeId )
  1198. {
  1199. case kMsgTlId:
  1200. r->msgId = m->msgId;
  1201. r->srate = m->srate;
  1202. r->seqCnt= m->seqCnt;
  1203. r->seqId = m->seqId;
  1204. break;
  1205. case kObjTlId:
  1206. r->msgId = kInsertMsgTlId;
  1207. rc = _cmTimeLineDecodeObj(msg,msgByteCnt,r);
  1208. break;
  1209. default:
  1210. assert(0);
  1211. }
  1212. return rc;
  1213. }