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.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726
  1. #include "cmPrefix.h"
  2. #include "cmGlobal.h"
  3. #include "cmFloatTypes.h"
  4. #include "cmComplexTypes.h"
  5. #include "cmRpt.h"
  6. #include "cmErr.h"
  7. #include "cmCtx.h"
  8. #include "cmMem.h"
  9. #include "cmMallocDebug.h"
  10. #include "cmLinkedHeap.h"
  11. #include "cmFile.h"
  12. #include "cmSymTbl.h"
  13. #include "cmJson.h"
  14. #include "cmPrefs.h"
  15. #include "cmDspValue.h"
  16. #include "cmMsgProtocol.h"
  17. #include "cmThread.h"
  18. #include "cmUdpPort.h"
  19. #include "cmUdpNet.h"
  20. #include "cmAudioSys.h"
  21. #include "cmDspCtx.h"
  22. #include "cmDspClass.h"
  23. #include "cmDspUi.h"
  24. #include "cmOp.h"
  25. #include "cmMath.h"
  26. #include "cmAudioFile.h"
  27. #include "cmFileSys.h"
  28. #include "cmProcObj.h"
  29. #include "cmProcTemplateMain.h"
  30. #include "cmProc.h"
  31. #include "cmMidi.h"
  32. #include "cmProc2.h"
  33. #include "cmVectOpsTemplateMain.h"
  34. #include "cmAudioFile.h"
  35. #include "cmMidiFile.h"
  36. #include "cmTimeLine.h"
  37. #include "cmScore.h"
  38. #include "cmProc4.h"
  39. enum
  40. {
  41. kWndSmpCntKrId,
  42. kHopFactKrId,
  43. kModeKrId,
  44. kThreshKrId,
  45. kLwrSlopeKrId,
  46. kUprSlopeKrId,
  47. kOffsetKrId,
  48. kInvertKrId,
  49. kAudioInKrId,
  50. kAudioOutKrId
  51. };
  52. typedef struct
  53. {
  54. cmDspInst_t inst;
  55. cmCtx* ctx;
  56. cmSpecDist_t* sdp;
  57. } cmDspKr_t;
  58. cmDspClass_t _cmKrDC;
  59. //==========================================================================================================================================
  60. cmDspInst_t* _cmDspKrAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
  61. {
  62. cmDspVarArg_t args[] =
  63. {
  64. { "wndn", kWndSmpCntKrId, 0, 0, kInDsvFl | kUIntDsvFl | kReqArgDsvFl, "Window sample count" },
  65. { "hopf", kHopFactKrId, 0, 0, kInDsvFl | kUIntDsvFl | kOptArgDsvFl, "Hop factor" },
  66. { "mode", kModeKrId, 0, 0, kInDsvFl | kUIntDsvFl | kOptArgDsvFl, "Mode 0=bypass 1=basic 2=spec cnt 3=amp env" },
  67. { "thrh", kThreshKrId, 0, 0, kInDsvFl | kDoubleDsvFl | kOptArgDsvFl, "Threshold" },
  68. { "lwrs", kLwrSlopeKrId, 0, 0, kInDsvFl | kDoubleDsvFl | kOptArgDsvFl, "Lower Slope"},
  69. { "uprs", kUprSlopeKrId, 0, 0, kInDsvFl | kDoubleDsvFl | kOptArgDsvFl, "Upper Slope"},
  70. { "offs", kOffsetKrId, 0, 0, kInDsvFl | kDoubleDsvFl | kOptArgDsvFl, "Offset"},
  71. { "invt", kInvertKrId, 0, 0, kInDsvFl | kUIntDsvFl | kOptArgDsvFl, "Invert"},
  72. { "in", kAudioInKrId, 0, 0, kInDsvFl | kAudioBufDsvFl, "Audio Input" },
  73. { "out", kAudioOutKrId, 0, 1, kOutDsvFl | kAudioBufDsvFl, "Audio Output" },
  74. { NULL, 0, 0, 0, 0 }
  75. };
  76. cmDspKr_t* p = cmDspInstAlloc(cmDspKr_t,ctx,classPtr,args,instSymId,id,storeSymId,va_cnt,vl);
  77. unsigned defWndSmpCnt = cmDspDefaultUInt(&p->inst,kWndSmpCntKrId);
  78. unsigned wndSmpCnt = cmNextPowerOfTwo( defWndSmpCnt );
  79. cmDspSetDefaultUInt( ctx,&p->inst, kWndSmpCntKrId, defWndSmpCnt, wndSmpCnt );
  80. cmDspSetDefaultUInt( ctx,&p->inst, kHopFactKrId, 0, 4 );
  81. cmDspSetDefaultUInt( ctx,&p->inst, kModeKrId, 0, kBasicModeSdId );
  82. cmDspSetDefaultDouble( ctx,&p->inst, kThreshKrId, 0, 60.0 );
  83. cmDspSetDefaultDouble( ctx,&p->inst, kLwrSlopeKrId, 0, 2.0 );
  84. cmDspSetDefaultDouble( ctx,&p->inst, kUprSlopeKrId, 0, 0.0 );
  85. cmDspSetDefaultDouble( ctx,&p->inst, kOffsetKrId, 0, 30.0);
  86. cmDspSetDefaultUInt( ctx,&p->inst, kInvertKrId, 0, 0 );
  87. //_cmDspKrCmInit(ctx,p); // initialize the cm library
  88. p->ctx = cmCtxAlloc(NULL,ctx->rpt,ctx->lhH,ctx->stH);
  89. return &p->inst;
  90. }
  91. cmDspRC_t _cmDspKrFree(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  92. {
  93. cmDspRC_t rc = kOkDspRC;
  94. cmDspKr_t* p = (cmDspKr_t*)inst;
  95. cmSpecDistFree(&p->sdp);
  96. cmCtxFree(&p->ctx);
  97. //_cmDspKrCmFinal(ctx,p); // finalize the cm library
  98. return rc;
  99. }
  100. cmDspRC_t _cmDspKrSetup(cmDspCtx_t* ctx, cmDspKr_t* p )
  101. {
  102. cmDspRC_t rc = kOkDspRC;
  103. unsigned wndSmpCnt = cmDspUInt(&p->inst,kWndSmpCntKrId);
  104. unsigned hopFact = cmDspUInt(&p->inst,kHopFactKrId);
  105. unsigned olaWndTypeId = kHannWndId;
  106. cmSpecDistFree(&p->sdp);
  107. p->sdp = cmSpecDistAlloc(p->ctx, NULL, cmDspSamplesPerCycle(ctx), cmDspSampleRate(ctx), wndSmpCnt, hopFact, olaWndTypeId);
  108. assert(p->sdp != NULL );
  109. if((rc = cmDspZeroAudioBuf(ctx,&p->inst,kAudioOutKrId)) != kOkDspRC )
  110. return rc;
  111. return rc;
  112. }
  113. cmDspRC_t _cmDspKrReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  114. {
  115. cmDspKr_t* p = (cmDspKr_t*)inst;
  116. cmDspRC_t rc;
  117. if((rc = cmDspApplyAllDefaults(ctx,inst)) != kOkDspRC )
  118. return rc;
  119. return _cmDspKrSetup(ctx,p);
  120. }
  121. cmDspRC_t _cmDspKrExec(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  122. {
  123. cmDspKr_t* p = (cmDspKr_t*)inst;
  124. cmDspRC_t rc = kOkDspRC;
  125. unsigned iChIdx = 0;
  126. const cmSample_t* ip = cmDspAudioBuf(ctx,inst,kAudioInKrId,iChIdx);
  127. unsigned iSmpCnt = cmDspVarRows(inst,kAudioInKrId);
  128. unsigned oChIdx = 0;
  129. cmSample_t* op = cmDspAudioBuf(ctx,inst,kAudioOutKrId,oChIdx);
  130. unsigned oSmpCnt = cmDspVarRows(inst,kAudioOutKrId);
  131. const cmSample_t* sp;
  132. cmSpecDistExec(p->sdp,ip,iSmpCnt);
  133. if((sp = cmSpecDistOut(p->sdp)) != NULL )
  134. vs_Copy(op,sp,oSmpCnt);
  135. return rc;
  136. }
  137. cmDspRC_t _cmDspKrRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  138. {
  139. cmDspKr_t* p = (cmDspKr_t*)inst;
  140. cmDspRC_t rc = kOkDspRC;
  141. cmDspSetEvent(ctx,inst,evt);
  142. switch( evt->dstVarId )
  143. {
  144. case kWndSmpCntKrId:
  145. case kHopFactKrId:
  146. _cmDspKrSetup(ctx,p);
  147. printf("wsn:%i hsn:%i\n",p->sdp->wndSmpCnt,p->sdp->hopSmpCnt);
  148. break;
  149. case kModeKrId:
  150. p->sdp->mode = cmDspUInt(inst,kModeKrId);
  151. printf("mode:%i\n",p->sdp->mode);
  152. break;
  153. case kThreshKrId:
  154. p->sdp->thresh = cmDspDouble(inst,kThreshKrId);
  155. break;
  156. case kUprSlopeKrId:
  157. p->sdp->uprSlope = cmDspDouble(inst,kUprSlopeKrId);
  158. printf("upr slope:%f\n",p->sdp->uprSlope);
  159. break;
  160. case kLwrSlopeKrId:
  161. p->sdp->lwrSlope = cmDspDouble(inst,kLwrSlopeKrId);
  162. printf("upr slope:%f\n",p->sdp->lwrSlope);
  163. break;
  164. case kOffsetKrId:
  165. p->sdp->offset = cmDspDouble(inst,kOffsetKrId);
  166. break;
  167. case kInvertKrId:
  168. p->sdp->invertFl = cmDspUInt(inst,kInvertKrId)!=0;
  169. break;
  170. default:
  171. { assert(0); }
  172. }
  173. return rc;
  174. }
  175. struct cmDspClass_str* cmKrClassCons( cmDspCtx_t* ctx )
  176. {
  177. cmDspClassSetup(&_cmKrDC,ctx,"Kr",
  178. NULL,
  179. _cmDspKrAlloc,
  180. _cmDspKrFree,
  181. _cmDspKrReset,
  182. _cmDspKrExec,
  183. _cmDspKrRecv,
  184. NULL,NULL,
  185. "Fourier based non-linear transformer.");
  186. return &_cmKrDC;
  187. }
  188. //==========================================================================================================================================
  189. // Time Line UI Object
  190. enum
  191. {
  192. kTlFileTlId,
  193. kAudPathTlId,
  194. kSelTlId,
  195. kAudFnTlId,
  196. kMidiFnTlId,
  197. kBegAudSmpIdxTlId,
  198. kEndAudSmpIdxTlId,
  199. kBegMidiSmpIdxTlId,
  200. kEndMidiSmpIdxTlId
  201. };
  202. cmDspClass_t _cmTimeLineDC;
  203. typedef struct
  204. {
  205. cmDspInst_t inst;
  206. cmTlH_t tlH;
  207. } cmDspTimeLine_t;
  208. cmDspInst_t* _cmDspTimeLineAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
  209. {
  210. cmDspVarArg_t args[] =
  211. {
  212. { "tlfile", kTlFileTlId, 0, 0, kInDsvFl | kStrzDsvFl | kReqArgDsvFl, "Time line file." },
  213. { "path", kAudPathTlId, 0, 0, kInDsvFl | kStrzDsvFl | kReqArgDsvFl, "Audio path" },
  214. { "sel", kSelTlId, 0, 0, kInDsvFl | kInDsvFl | kOutDsvFl | kUIntDsvFl, "Selected marker id."},
  215. { "afn", kAudFnTlId, 0, 0, kOutDsvFl | kStrzDsvFl, "Selected Audio file." },
  216. { "mfn", kMidiFnTlId, 0, 0, kOutDsvFl | kStrzDsvFl, "Selected MIDI file." },
  217. { "absi", kBegAudSmpIdxTlId, 0, 0, kOutDsvFl | kIntDsvFl, "Begin audio sample index."},
  218. { "aesi", kEndAudSmpIdxTlId, 0, 0, kOutDsvFl | kIntDsvFl, "End audio sample index."},
  219. { "mbsi", kBegMidiSmpIdxTlId, 0, 0, kOutDsvFl | kIntDsvFl, "Begin MIDI sample index."},
  220. { "mesi", kEndMidiSmpIdxTlId, 0, 0, kOutDsvFl | kIntDsvFl, "End MIDI sample index."},
  221. { NULL, 0, 0, 0, 0 }
  222. };
  223. cmDspTimeLine_t* p = cmDspInstAlloc(cmDspTimeLine_t,ctx,classPtr,args,instSymId,id,storeSymId,va_cnt,vl);
  224. cmDspSetDefaultUInt( ctx, &p->inst, kSelTlId, 0, cmInvalidId);
  225. cmDspSetDefaultStrcz(ctx, &p->inst, kAudFnTlId, NULL, "");
  226. cmDspSetDefaultStrcz(ctx, &p->inst, kMidiFnTlId, NULL, "");
  227. cmDspSetDefaultInt( ctx, &p->inst, kBegAudSmpIdxTlId, 0, cmInvalidIdx);
  228. cmDspSetDefaultInt( ctx, &p->inst, kEndAudSmpIdxTlId, 0, cmInvalidIdx);
  229. cmDspSetDefaultInt( ctx, &p->inst, kBegMidiSmpIdxTlId, 0, cmInvalidIdx);
  230. cmDspSetDefaultInt( ctx, &p->inst, kEndMidiSmpIdxTlId, 0, cmInvalidIdx);
  231. // create the UI control
  232. cmDspUiTimeLineCreate(ctx,&p->inst,kTlFileTlId,kAudPathTlId,kSelTlId);
  233. p->tlH = cmTimeLineNullHandle;
  234. return &p->inst;
  235. }
  236. cmDspRC_t _cmDspTimeLineFree(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  237. {
  238. cmDspRC_t rc = kOkDspRC;
  239. cmDspTimeLine_t* p = (cmDspTimeLine_t*)inst;
  240. if( cmTimeLineFinalize(&p->tlH) != kOkTlRC )
  241. return cmErrMsg(&inst->classPtr->err, kInstFinalFailDspRC, "Time-line finalize failed.");
  242. return rc;
  243. }
  244. cmDspRC_t _cmDspTimeLineReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  245. {
  246. cmDspRC_t rc = kOkDspRC;
  247. cmDspTimeLine_t* p = (cmDspTimeLine_t*)inst;
  248. cmDspApplyAllDefaults(ctx,inst);
  249. const cmChar_t* tlFn;
  250. if((tlFn = cmDspStrcz(inst, kTlFileTlId )) != NULL )
  251. if( cmTimeLineInitializeFromFile(ctx->cmCtx, &p->tlH, NULL, NULL, tlFn ) != kOkTlRC )
  252. rc = cmErrMsg(&inst->classPtr->err, kInstResetFailDspRC, "Time-line file open failed.");
  253. return rc;
  254. }
  255. cmDspRC_t _cmDspTimeLineRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  256. {
  257. cmDspTimeLine_t* p = (cmDspTimeLine_t*)inst;
  258. switch( evt->dstVarId )
  259. {
  260. case kSelTlId:
  261. {
  262. unsigned markerId;
  263. cmDspSetEvent(ctx,inst,evt);
  264. // get the id of the selected marker
  265. if((markerId = cmDspUInt(inst,kSelTlId)) != cmInvalidId )
  266. {
  267. // get the marker object
  268. cmTlObj_t* op;
  269. if((op = cmTimeLineIdToObj(p->tlH, cmInvalidId, markerId )) != NULL )
  270. {
  271. assert(op->typeId == kMarkerTlId);
  272. cmDspSetInt(ctx, inst, kBegAudSmpIdxTlId, op->begSmpIdx );
  273. cmDspSetInt(ctx, inst, kEndAudSmpIdxTlId, op->begSmpIdx + op->durSmpCnt );
  274. // locate the audio file assoc'd with the marker
  275. cmTlAudioFile_t* afp;
  276. if((afp = cmTimeLineAudioFileAtTime(p->tlH,op->seqId,op->seqSmpIdx)) != NULL)
  277. cmDspSetStrcz(ctx, inst, kAudFnTlId, afp->fn );
  278. // locate the midi file assoc'd with the marker
  279. cmTlMidiFile_t* mfp;
  280. if((mfp = cmTimeLineMidiFileAtTime(p->tlH,op->seqId,op->seqSmpIdx)) != NULL )
  281. {
  282. cmDspSetInt(ctx, inst, kBegMidiSmpIdxTlId, op->seqSmpIdx - mfp->obj.seqSmpIdx );
  283. cmDspSetInt(ctx, inst, kEndMidiSmpIdxTlId, op->seqSmpIdx + op->durSmpCnt - mfp->obj.seqSmpIdx );
  284. cmDspSetStrcz(ctx, inst, kMidiFnTlId, mfp->fn );
  285. }
  286. }
  287. }
  288. }
  289. break;
  290. default:
  291. {assert(0);}
  292. }
  293. return kOkDspRC;
  294. }
  295. struct cmDspClass_str* cmTimeLineClassCons( cmDspCtx_t* ctx )
  296. {
  297. cmDspClassSetup(&_cmTimeLineDC,ctx,"TimeLine",
  298. NULL,
  299. _cmDspTimeLineAlloc,
  300. _cmDspTimeLineFree,
  301. _cmDspTimeLineReset,
  302. NULL,
  303. _cmDspTimeLineRecv,
  304. NULL,NULL,
  305. "Time Line control.");
  306. return &_cmTimeLineDC;
  307. }
  308. //==========================================================================================================================================
  309. // MIDI File Player
  310. enum
  311. {
  312. kFnMfId,
  313. kSelMfId,
  314. kBsiMfId,
  315. kEsiMfId,
  316. kStatusMfId,
  317. kD0MfId,
  318. kD1MfId
  319. };
  320. cmDspClass_t _cmMidiFilePlayDC;
  321. typedef struct
  322. {
  323. cmDspInst_t inst;
  324. cmMidiFileH_t mfH;
  325. unsigned curMsgIdx; // current midi file msg index
  326. int csi; // current sample index
  327. int bsi; // starting sample index
  328. int esi; // ending sample index
  329. unsigned startSymId;
  330. unsigned stopSymId;
  331. unsigned contSymId;
  332. } cmDspMidiFilePlay_t;
  333. /*
  334. 'bsi' and 'esi' give the starting and ending sample for MIDI file playback.
  335. These indexes are relative to the start of the file.
  336. When the player recieves a 'start' msg it sets the current sample index
  337. 'si' to 'bsi' and begins scanning for the next note to play.
  338. On each call to the _cmDspMidiFilePlayExec() msgs that fall in the interval
  339. si:si+sPc-1 will be transmitted. (where sPc are the number of samples per DSP cycle).
  340. */
  341. cmDspInst_t* _cmDspMidiFilePlayAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
  342. {
  343. cmDspVarArg_t args[] =
  344. {
  345. { "fn", kFnMfId, 0, 0, kInDsvFl | kStrzDsvFl, "File name"},
  346. { "sel", kSelMfId, 0, 0, kInDsvFl | kSymDsvFl, "start | stop | continue" },
  347. { "bsi", kBsiMfId, 0, 0, kInDsvFl | kIntDsvFl, "Starting sample." },
  348. { "esi", kEsiMfId, 0, 0, kInDsvFl | kIntDsvFl, "Ending sample."},
  349. { "status", kStatusMfId, 0, 0, kOutDsvFl | kIntDsvFl, "Status value output" },
  350. { "d0", kD0MfId, 0, 0, kOutDsvFl | kUIntDsvFl, "Data byte 0" },
  351. { "d1", kD1MfId, 0, 0, kOutDsvFl | kUIntDsvFl, "Data byte 1" },
  352. { NULL, 0, 0, 0, 0 }
  353. };
  354. cmDspMidiFilePlay_t* p = cmDspInstAlloc(cmDspMidiFilePlay_t,ctx,classPtr,args,instSymId,id,storeSymId,va_cnt,vl);
  355. p->startSymId = cmSymTblRegisterStaticSymbol(ctx->stH,"start");
  356. p->stopSymId = cmSymTblRegisterStaticSymbol(ctx->stH,"stop");
  357. p->contSymId = cmSymTblRegisterStaticSymbol(ctx->stH,"continue");
  358. p->mfH = cmMidiFileNullHandle;
  359. cmDspSetDefaultStrcz( ctx, &p->inst, kFnMfId, NULL, "");
  360. cmDspSetDefaultSymbol(ctx, &p->inst, kSelMfId, p->stopSymId);
  361. cmDspSetDefaultInt( ctx, &p->inst, kBsiMfId, 0, 0);
  362. cmDspSetDefaultInt( ctx, &p->inst, kEsiMfId, 0, 0);
  363. cmDspSetDefaultUInt( ctx, &p->inst, kStatusMfId, 0, 0);
  364. cmDspSetDefaultUInt( ctx, &p->inst, kD0MfId, 0, 0);
  365. cmDspSetDefaultUInt( ctx, &p->inst, kD1MfId, 0, 0);
  366. return &p->inst;
  367. }
  368. cmDspRC_t _cmDspMidiFilePlayFree(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  369. {
  370. cmDspMidiFilePlay_t* p = (cmDspMidiFilePlay_t*)inst;
  371. if( cmMidiFileClose(&p->mfH) )
  372. return cmErrMsg(&inst->classPtr->err, kInstFinalFailDspRC, "MIDI file close failed.");
  373. return kOkDspRC;
  374. }
  375. // return the index of the msg following smpIdx
  376. unsigned _cmDspMidiFilePlaySeekMsgIdx( cmDspCtx_t* ctx, cmDspMidiFilePlay_t* p, unsigned smpIdx )
  377. {
  378. unsigned i;
  379. unsigned n = cmMidiFileMsgCount(p->mfH);
  380. const cmMidiTrackMsg_t** a = cmMidiFileMsgArray(p->mfH);
  381. for(i=0; i<n; ++i)
  382. if( a[i]->dtick > smpIdx )
  383. break;
  384. return i==n ? cmInvalidIdx : i;
  385. }
  386. cmDspRC_t _cmDspMidiFilePlayOpen(cmDspCtx_t* ctx, cmDspInst_t* inst )
  387. {
  388. cmDspRC_t rc = kOkDspRC;
  389. const cmChar_t* fn = cmDspStrcz(inst,kFnMfId);
  390. cmDspMidiFilePlay_t* p = (cmDspMidiFilePlay_t*)inst;
  391. if( fn==NULL || strlen(fn)==0 )
  392. return rc;
  393. if( cmMidiFileOpen( fn, &p->mfH, ctx->cmCtx ) != kOkFileRC )
  394. rc = cmErrMsg(&inst->classPtr->err, kInstResetFailDspRC, "MIDI file open failed.");
  395. else
  396. {
  397. p->curMsgIdx = 0;
  398. p->bsi = cmDspInt(inst,kBsiMfId);
  399. p->esi = cmDspInt(inst,kEsiMfId);
  400. p->csi = 0;
  401. // force the first msg to occurr one quarter note into the file
  402. cmMidiFileSetDelay(p->mfH, cmMidiFileTicksPerQN(p->mfH) );
  403. // convert midi msg times to absolute time in samples
  404. cmMidiFileTickToSamples(p->mfH,cmDspSampleRate(ctx),true);
  405. }
  406. return rc;
  407. }
  408. cmDspRC_t _cmDspMidiFilePlayReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  409. {
  410. cmDspApplyAllDefaults(ctx,inst);
  411. return _cmDspMidiFilePlayOpen(ctx,inst);
  412. }
  413. cmDspRC_t _cmDspMidiFilePlayExec(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  414. {
  415. cmDspRC_t rc = kOkDspRC;
  416. cmDspMidiFilePlay_t* p = (cmDspMidiFilePlay_t*)inst;
  417. unsigned sPc = cmDspSamplesPerCycle(ctx);
  418. if( cmDspSymbol(inst,kSelMfId) != p->stopSymId )
  419. {
  420. const cmMidiTrackMsg_t** mpp = cmMidiFileMsgArray(p->mfH);
  421. unsigned msgN = cmMidiFileMsgCount(p->mfH);
  422. for(; p->curMsgIdx < msgN && p->csi <= mpp[p->curMsgIdx]->dtick && mpp[p->curMsgIdx]->dtick < (p->csi + sPc); ++p->curMsgIdx )
  423. {
  424. const cmMidiTrackMsg_t* mp = mpp[p->curMsgIdx];
  425. switch( mp->status )
  426. {
  427. case kNoteOnMdId:
  428. case kCtlMdId:
  429. cmDspSetUInt(ctx,inst, kD1MfId, mp->u.chMsgPtr->d1);
  430. cmDspSetUInt(ctx,inst, kD0MfId, mp->u.chMsgPtr->d0);
  431. cmDspSetUInt(ctx,inst, kStatusMfId, mp->status);
  432. break;
  433. }
  434. }
  435. }
  436. p->csi += sPc;
  437. return rc;
  438. }
  439. cmDspRC_t _cmDspMidiFilePlayRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  440. {
  441. cmDspMidiFilePlay_t* p = (cmDspMidiFilePlay_t*)inst;
  442. cmDspSetEvent(ctx,inst,evt);
  443. switch(evt->dstVarId)
  444. {
  445. case kFnMfId:
  446. _cmDspMidiFilePlayOpen(ctx, inst );
  447. break;
  448. case kSelMfId:
  449. {
  450. if( cmDspSymbol(inst,kSelMfId)==p->startSymId )
  451. {
  452. p->csi = cmDspInt(inst,kBsiMfId);
  453. p->curMsgIdx = _cmDspMidiFilePlaySeekMsgIdx(ctx, p, p->csi );
  454. }
  455. break;
  456. }
  457. }
  458. return kOkDspRC;
  459. }
  460. struct cmDspClass_str* cmMidiFilePlayClassCons( cmDspCtx_t* ctx )
  461. {
  462. cmDspClassSetup(&_cmMidiFilePlayDC,ctx,"MidiFilePlay",
  463. NULL,
  464. _cmDspMidiFilePlayAlloc,
  465. _cmDspMidiFilePlayFree,
  466. _cmDspMidiFilePlayReset,
  467. _cmDspMidiFilePlayExec,
  468. _cmDspMidiFilePlayRecv,
  469. NULL,NULL,
  470. "Time tagged text file.");
  471. return &_cmMidiFilePlayDC;
  472. }
  473. //==========================================================================================================================================
  474. enum
  475. {
  476. kFnSfId,
  477. kWndCntSfId,
  478. kWndMsSfId,
  479. kIndexSfId,
  480. kStatusSfId,
  481. kD0SfId,
  482. kD1SfId,
  483. kOutSfId
  484. };
  485. cmDspClass_t _cmScFolDC;
  486. typedef struct
  487. {
  488. cmDspInst_t inst;
  489. cmScFol* sfp;
  490. cmScH_t scH;
  491. } cmDspScFol_t;
  492. cmDspInst_t* _cmDspScFolAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
  493. {
  494. cmDspVarArg_t args[] =
  495. {
  496. { "fn", kFnSfId, 0, 0, kInDsvFl | kStrzDsvFl | kReqArgDsvFl, "Score file." },
  497. { "wndcnt",kWndCntSfId, 0, 0, kInDsvFl | kUIntDsvFl, "Event window element count." },
  498. { "wndms", kWndMsSfId, 0, 0, kInDsvFl | kUIntDsvFl, "Event window length milliseconds."},
  499. { "index", kIndexSfId, 0, 0, kInDsvFl | kUIntDsvFl, "Tracking start location."},
  500. { "status",kStatusSfId, 0, 0, kInDsvFl | kUIntDsvFl, "MIDI status byte"},
  501. { "d0", kD0SfId, 0, 0, kInDsvFl | kUIntDsvFl, "MIDI data byte 0"},
  502. { "d1", kD1SfId, 0, 0, kInDsvFl | kUIntDsvFl, "MIDI data byte 1"},
  503. { "out", kOutSfId, 0, 0, kOutDsvFl| kUIntDsvFl, "Current score index."},
  504. { NULL, 0, 0, 0, 0, NULL }
  505. };
  506. cmDspScFol_t* p;
  507. if((p = cmDspInstAlloc(cmDspScFol_t,ctx,classPtr,args,instSymId,id,storeSymId,va_cnt,vl)) == NULL )
  508. return NULL;
  509. p->sfp = cmScFolAlloc(ctx->cmProcCtx, NULL, 0, 0, 0, cmScNullHandle );
  510. cmDspSetDefaultUInt( ctx, &p->inst, kWndCntSfId, 0, 10);
  511. cmDspSetDefaultUInt( ctx, &p->inst, kWndMsSfId, 0, 5000);
  512. cmDspSetDefaultUInt( ctx, &p->inst, kIndexSfId, 0, 0);
  513. cmDspSetDefaultUInt( ctx, &p->inst, kOutSfId, 0, 0);
  514. return &p->inst;
  515. }
  516. cmDspRC_t _cmDspScFolFree(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  517. {
  518. cmDspScFol_t* p = (cmDspScFol_t*)inst;
  519. cmScFolFree(&p->sfp);
  520. cmScoreFinalize(&p->scH);
  521. return kOkDspRC;
  522. }
  523. cmDspRC_t _cmDspScFolOpenScore( cmDspCtx_t* ctx, cmDspInst_t* inst )
  524. {
  525. const cmChar_t* fn;
  526. cmDspScFol_t* p = (cmDspScFol_t*)inst;
  527. if((fn = cmDspStrcz(inst,kFnSfId)) == NULL || strlen(fn)==0 )
  528. return cmErrMsg(&inst->classPtr->err, kInvalidArgDspRC, "No score file name supplied.");
  529. if( cmScoreInitialize(ctx->cmCtx, &p->scH, fn, NULL, NULL ) != kOkScRC )
  530. return cmErrMsg(&inst->classPtr->err, kSubSysFailDspRC, "Unable to open the score '%s'.",fn);
  531. return kOkDspRC;
  532. }
  533. cmDspRC_t _cmDspScFolReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  534. {
  535. cmDspRC_t rc = kOkDspRC;
  536. cmDspScFol_t* p = (cmDspScFol_t*)inst;
  537. rc = cmDspApplyAllDefaults(ctx,inst);
  538. if((rc = _cmDspScFolOpenScore(ctx,inst)) != kOkDspRC )
  539. return rc;
  540. if( cmScoreIsValid(p->scH) )
  541. if( cmScFolInit(p->sfp, cmDspSampleRate(ctx), cmDspUInt(inst,kWndCntSfId), cmDspUInt(inst,kWndMsSfId), p->scH) != cmOkRC )
  542. rc = cmErrMsg(&inst->classPtr->err, kSubSysFailDspRC, "Internal score follower allocation failed.");
  543. return rc;
  544. }
  545. cmDspRC_t _cmDspScFolRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
  546. {
  547. cmDspRC_t rc = kOkDspRC;
  548. cmDspScFol_t* p = (cmDspScFol_t*)inst;
  549. if((rc = cmDspSetEvent(ctx,inst,evt)) == kOkDspRC && p->sfp != NULL )
  550. {
  551. switch( evt->dstVarId )
  552. {
  553. case kIndexSfId:
  554. if( cmScoreIsValid(p->scH) )
  555. if( cmScFolReset( p->sfp, cmDspUInt(inst,kIndexSfId) ) != cmOkRC )
  556. cmErrMsg(&inst->classPtr->err, kSubSysFailDspRC, "Score follower reset to score index '%i' failed.");
  557. break;
  558. case kStatusSfId:
  559. if( cmScoreIsValid(p->scH))
  560. {
  561. unsigned idx = cmScFolExec(p->sfp, ctx->cycleCnt, cmDspUInt(inst,kStatusSfId), cmDspUInt(inst,kD0SfId), cmDspUInt(inst,kD1SfId));
  562. if( idx != cmInvalidIdx )
  563. cmDspSetUInt(ctx,inst,kOutSfId,idx);
  564. }
  565. break;
  566. case kFnSfId:
  567. _cmDspScFolOpenScore(ctx,inst);
  568. break;
  569. }
  570. }
  571. return rc;
  572. }
  573. struct cmDspClass_str* cmScFolClassCons( cmDspCtx_t* ctx )
  574. {
  575. cmDspClassSetup(&_cmScFolDC,ctx,"ScFol",
  576. NULL,
  577. _cmDspScFolAlloc,
  578. _cmDspScFolFree,
  579. _cmDspScFolReset,
  580. NULL,
  581. _cmDspScFolRecv,
  582. NULL,NULL,
  583. "Score Follower");
  584. return &_cmScFolDC;
  585. }