libcm is a C development framework with an emphasis on audio signal processing applications.
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

cmMidiFilePlay.c 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553
  1. #include <sys/time.h> // gettimeofday()
  2. #include "cmPrefix.h"
  3. #include "cmGlobal.h"
  4. #include "cmRpt.h"
  5. #include "cmErr.h"
  6. #include "cmCtx.h"
  7. #include "cmMem.h"
  8. #include "cmMallocDebug.h"
  9. #include "cmFile.h"
  10. #include "cmTime.h"
  11. #include "cmMidi.h"
  12. #include "cmMidiPort.h"
  13. #include "cmMidiFile.h"
  14. #include "cmMidiFilePlay.h"
  15. #include "cmThread.h" // cmSleepUs()
  16. #include "cmTime.h"
  17. typedef struct
  18. {
  19. cmErr_t err;
  20. cmCtx_t ctx;
  21. cmMfpCallback_t cbFunc;
  22. void* userCbPtr;
  23. void* printDataPtr;
  24. unsigned memBlockByteCnt;
  25. cmMidiFileH_t mfH; // midi file handle
  26. bool closeFileFl; // true mfH should be closed when this midi file player is closed
  27. unsigned ticksPerQN; // global for file
  28. unsigned microsPerTick; // set via tempo
  29. unsigned etime; // usecs elapsed since transmitting prev msg
  30. unsigned mtime; // usecs to wait before transmitting next msg
  31. unsigned msgN; // count of pointers in msgV[]
  32. unsigned msgIdx; // index into msgV[] of next msg to transmit
  33. const cmMidiTrackMsg_t** msgV; // array of msg pointers
  34. } cmMfp_t;
  35. cmMfpH_t cmMfpNullHandle = cmSTATIC_NULL_HANDLE;
  36. #define _cmMfpError( mfp, rc ) _cmMfpOnError(mfp, rc, __LINE__,__FILE__,__FUNCTION__ )
  37. // note: mfp may be NULL
  38. cmMfpRC_t _cmMfpOnError( cmMfp_t* mfp, cmMfpRC_t rc, int line, const char* fn, const char* func )
  39. {
  40. return cmErrMsg(&mfp->err,rc,"rc:%i %i %s %s\n",rc,line,func,fn);
  41. }
  42. cmMfp_t* _cmMfpHandleToPtr( cmMfpH_t h )
  43. {
  44. cmMfp_t* p = (cmMfp_t*)h.h;
  45. assert(p != NULL);
  46. return p;
  47. }
  48. void _cmMfpUpdateMicrosPerTick( cmMfp_t* mfp, unsigned microsPerQN )
  49. {
  50. mfp->microsPerTick = microsPerQN / mfp->ticksPerQN;
  51. printf("microsPerTick: %i bpm:%i ticksPerQN:%i\n", mfp->microsPerTick,microsPerQN,mfp->ticksPerQN);
  52. }
  53. cmMfpRC_t cmMfpCreate( cmMfpH_t* hp, cmMfpCallback_t cbFunc, void* userCbPtr, cmCtx_t* ctx )
  54. {
  55. cmMfp_t* p = cmMemAllocZ( cmMfp_t, 1 );
  56. cmErrSetup(&p->err,&ctx->rpt,"MIDI File Player");
  57. p->ctx = *ctx;
  58. p->cbFunc = cbFunc;
  59. p->userCbPtr = userCbPtr;
  60. p->mfH.h = NULL;
  61. p->closeFileFl = false;
  62. p->ticksPerQN = 0;
  63. p->microsPerTick = 0;
  64. p->etime = 0;
  65. p->msgN = 0;
  66. p->msgV = NULL;
  67. p->msgIdx = 0;
  68. hp->h = p;
  69. return kOkMfpRC;
  70. }
  71. cmMfpRC_t cmMfpDestroy( cmMfpH_t* hp )
  72. {
  73. if( hp == NULL )
  74. return kOkMfpRC;
  75. if( cmMfpIsValid(*hp) )
  76. {
  77. cmMfp_t* p = _cmMfpHandleToPtr(*hp);
  78. if( cmMidiFileIsNull(p->mfH)==false && p->closeFileFl==true )
  79. cmMidiFileClose(&p->mfH);
  80. cmMemFree(p);
  81. hp->h = NULL;
  82. }
  83. return kOkMfpRC;
  84. }
  85. bool cmMfpIsValid( cmMfpH_t h )
  86. { return h.h != NULL; }
  87. cmMfpRC_t cmMfpLoadFile( cmMfpH_t h, const char* fn )
  88. {
  89. cmMfpRC_t rc = kOkMfpRC;
  90. cmMfp_t* p = _cmMfpHandleToPtr(h);
  91. cmMidiFileH_t mfH = cmMidiFileNullHandle;
  92. if((rc = cmMidiFileOpen( fn, &mfH, &p->ctx )) != kOkMfRC )
  93. return _cmMfpError(p,kFileOpenFailMfpRC);
  94. if((rc= cmMfpLoadHandle( h, mfH )) == kOkMfpRC )
  95. p->closeFileFl = true;
  96. return rc;
  97. }
  98. cmMfpRC_t cmMfpLoadHandle( cmMfpH_t h, cmMidiFileH_t mfH )
  99. {
  100. cmMfp_t* p = _cmMfpHandleToPtr(h);
  101. // if a file has already been assigned to this player
  102. if( (cmMidiFileIsNull(p->mfH) == false) && p->closeFileFl)
  103. {
  104. // close the existing file
  105. cmMidiFileClose(&p->mfH);
  106. }
  107. // get the count of msg's in the new midi file
  108. if((p->msgN = cmMidiFileMsgCount(mfH)) == cmInvalidCnt )
  109. return _cmMfpError(p,kInvalidFileMfpRC);
  110. // get a pointer to the first mesage
  111. if((p->msgV = cmMidiFileMsgArray(mfH)) == NULL )
  112. return _cmMfpError(p,kInvalidFileMfpRC);
  113. // get the count of ticks per qn
  114. if((p->ticksPerQN = cmMidiFileTicksPerQN( mfH )) == 0 )
  115. return _cmMfpError(p,kSmpteTickNotImplMfpRC);
  116. // set the initial tempo to 120
  117. _cmMfpUpdateMicrosPerTick(p,60000000/120);
  118. p->msgIdx = 0;
  119. p->mfH = mfH;
  120. p->etime = 0;
  121. p->mtime = 0;
  122. p->closeFileFl= false;
  123. return kOkMfpRC;
  124. }
  125. cmMfpRC_t cmMfpSeek( cmMfpH_t h, unsigned offsUsecs )
  126. {
  127. cmMfp_t* p = _cmMfpHandleToPtr(h);
  128. unsigned msgOffsUsecs = 0;
  129. unsigned msgIdx;
  130. unsigned newMicrosPerTick;
  131. // if the requested offset is past the end of the file then return EOF
  132. if((msgIdx = cmMidiFileSeekUsecs( p->mfH, offsUsecs, &msgOffsUsecs, &newMicrosPerTick )) == cmInvalidIdx )
  133. {
  134. p->msgIdx = p->msgN;
  135. return _cmMfpError(p,kEndOfFileMfpRC);
  136. }
  137. if( msgIdx < p->msgIdx )
  138. p->msgIdx = 0;
  139. p->mtime = msgOffsUsecs;
  140. p->etime = 0;
  141. p->microsPerTick = newMicrosPerTick;
  142. p->msgIdx = msgIdx;
  143. assert(p->mtime >= 0);
  144. return kOkMfpRC;
  145. }
  146. // p 0 1 n 2
  147. // v v v v v
  148. // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  149. // 012345678901234567890123456780
  150. // 0 1 2
  151. //
  152. // p = 3 = prev msg sent
  153. // n = 19 = next msg to send
  154. // 0 = 6 = call to cmMfpClock()
  155. // 1 = 12 = call to cmMfpClock()
  156. // 2 = 22 = call to cmMfpClock()
  157. //
  158. // dusecs etime mtime
  159. // 0 n/a 3 13
  160. // 1 6 9 7
  161. // 2 10 19 -3
  162. //
  163. cmMfpRC_t cmMfpClock( cmMfpH_t h, unsigned dusecs )
  164. {
  165. cmMfp_t* p = _cmMfpHandleToPtr(h);
  166. if( p->msgIdx >= p->msgN )
  167. return kEndOfFileMfpRC;
  168. // get a pointer to the next msg to send
  169. const cmMidiTrackMsg_t* mp = p->msgV[p->msgIdx];
  170. // p->etime is the interval of time between when the last msg was
  171. // sent and the end of the time window for this mfpClock() cycle
  172. p->etime += dusecs;
  173. // if the elapsed time (etime) since the last msg is greater or equal
  174. // to the delta time to the next msg (mtime)
  175. while( p->etime >= p->mtime )
  176. {
  177. // send the current message
  178. p->cbFunc( p->userCbPtr, p->mtime, mp );
  179. unsigned amicro = mp->amicro;
  180. ++(p->msgIdx);
  181. if( p->msgIdx >= p->msgN )
  182. break;
  183. // get the next msg to send
  184. mp = p->msgV[p->msgIdx];
  185. // we probably went past the actual mtime - so update etime
  186. // with the delta usecs from the msg just sent and the current time
  187. p->etime -= p->mtime;
  188. // calc the delta usecs from the message just sent to the next msg to send
  189. p->mtime = mp->amicro - amicro;
  190. }
  191. return p->msgIdx >= p->msgN ? kEndOfFileMfpRC : kOkMfpRC;
  192. }
  193. void mfpPrint( void* userDataPtr, const char* fmt, va_list vl )
  194. {
  195. vprintf(fmt,vl);
  196. }
  197. // this assumes that the seconds have been normalized to a recent start time
  198. // so as to avoid overflow
  199. unsigned _cmMfpElapsedMicroSecs( const struct timespec* t0, const struct timespec* t1 )
  200. {
  201. // convert seconds to usecs
  202. long u0 = t0->tv_sec * 1000000;
  203. long u1 = t1->tv_sec * 1000000;
  204. // convert nanoseconds to usec
  205. u0 += t0->tv_nsec / 1000;
  206. u1 += t1->tv_nsec / 1000;
  207. // take diff between t1 and t0
  208. return u1 - u0;
  209. }
  210. void _cmMfpTestTimer()
  211. {
  212. useconds_t suspendUsecs = 15 * 1000;
  213. struct timespec t0,t1,t2;
  214. unsigned accum = 0;
  215. unsigned i;
  216. unsigned n = 4000;
  217. // t0 will be the base time which all other times will be
  218. // set relative to.
  219. cmTimeGet(&t0);
  220. t2 = t0;
  221. t2.tv_sec = 0;
  222. for(i=0; i<n; ++i)
  223. {
  224. cmSleepUs(suspendUsecs);
  225. cmTimeGet(&t1);
  226. t1.tv_sec -= t0.tv_sec;
  227. unsigned d0usec = _cmMfpElapsedMicroSecs(&t0,&t1);
  228. unsigned d1usec = _cmMfpElapsedMicroSecs(&t2,&t1);
  229. accum += d1usec;
  230. if( i == n-1 )
  231. printf("%i %i %i\n",d0usec,d1usec,accum);
  232. t2 = t1;
  233. }
  234. }
  235. // midi file player callback test function
  236. void _cmMfpCallbackTest( void* userCbPtr, unsigned dmicros, const cmMidiTrackMsg_t* msgPtr )
  237. {
  238. if( kNoteOffMdId <= msgPtr->status && msgPtr->status <= kPbendMdId )
  239. cmMpDeviceSend( 0, 0, msgPtr->status+msgPtr->u.chMsgPtr->ch, msgPtr->u.chMsgPtr->d0,msgPtr->u.chMsgPtr->d1);
  240. //printf("%i 0x%x 0x%x %i\n",msgPtr->tick,msgPtr->status,msgPtr->metaId,msgPtr->trkIdx);
  241. }
  242. // midi port callback test function
  243. void _cmMpCallbackTest( const cmMidiPacket_t* pktArray, unsigned pktCnt )
  244. {}
  245. cmMfpRC_t cmMfpTest( const char* fn, cmCtx_t* ctx )
  246. {
  247. cmMfpH_t mfpH = cmMfpNullHandle;
  248. cmMfpRC_t rc;
  249. useconds_t suspendUsecs = 15 * 1000;
  250. struct timespec t0,t1,base;
  251. //unsigned i;
  252. //unsigned n = 4000;
  253. unsigned mdParserBufByteCnt = 1024;
  254. printf("Initializing MIDI Devices...\n");
  255. cmMpInitialize( ctx, _cmMpCallbackTest, NULL, mdParserBufByteCnt,"app" );
  256. //mdReport();
  257. printf("Creating Player...\n");
  258. if((rc = cmMfpCreate( &mfpH, _cmMfpCallbackTest, NULL, ctx )) != kOkMfpRC )
  259. return rc;
  260. printf("Loading MIDI file...\n");
  261. if((rc = cmMfpLoadFile( mfpH, fn )) != kOkMfpRC )
  262. goto errLabel;
  263. if((rc = cmMfpSeek( mfpH, 60 * 1000000 )) != kOkMfpRC )
  264. goto errLabel;
  265. cmTimeGet(&base);
  266. t0 = base;
  267. t0.tv_sec = 0;
  268. //for(i=0; i<n; ++i)
  269. while(rc != kEndOfFileMfpRC)
  270. {
  271. cmSleepUs(suspendUsecs);
  272. cmTimeGet(&t1);
  273. t1.tv_sec -= base.tv_sec;
  274. unsigned dusecs = _cmMfpElapsedMicroSecs(&t0,&t1);
  275. rc = cmMfpClock( mfpH, dusecs );
  276. //printf("%i %i\n",dusecs,rc);
  277. t0 = t1;
  278. }
  279. errLabel:
  280. cmMfpDestroy(&mfpH);
  281. cmMpFinalize();
  282. return rc;
  283. }
  284. //------------------------------------------------------------------------------------------------------------
  285. #include "cmFloatTypes.h"
  286. #include "cmComplexTypes.h"
  287. #include "cmLinkedHeap.h"
  288. #include "cmSymTbl.h"
  289. #include "cmAudioFile.h"
  290. #include "cmProcObj.h"
  291. #include "cmProcTemplateMain.h"
  292. #include "cmVectOps.h"
  293. #include "cmProc.h"
  294. #include "cmProc2.h"
  295. enum
  296. {
  297. kOkMfptRC = cmOkRC,
  298. kMfpFailMfptRC,
  299. kAudioFileFailMfptRC,
  300. kProcObjFailMfptRC
  301. };
  302. typedef struct
  303. {
  304. cmErr_t* err;
  305. cmMidiSynth* msp;
  306. } _cmMfpTest2CbData_t;
  307. // Called by the MIDI file player to send a msg to the MIDI synth.
  308. void _cmMfpCb( void* userCbPtr, unsigned dmicros, const cmMidiTrackMsg_t* msgPtr )
  309. {
  310. if( kNoteOffMdId <= msgPtr->status && msgPtr->status <= kPbendMdId )
  311. {
  312. cmMidiPacket_t pkt;
  313. cmMidiMsg msg;
  314. _cmMfpTest2CbData_t* d = (_cmMfpTest2CbData_t*)userCbPtr;
  315. msg.timeStamp.tv_sec = 0;
  316. msg.timeStamp.tv_nsec = 0;
  317. msg.status = msgPtr->status + msgPtr->u.chMsgPtr->ch;
  318. msg.d0 = msgPtr->u.chMsgPtr->d0;
  319. msg.d1 = msgPtr->u.chMsgPtr->d1;
  320. pkt.cbDataPtr = NULL;
  321. pkt.devIdx = cmInvalidIdx;
  322. pkt.portIdx = cmInvalidIdx;
  323. pkt.msgArray = &msg;
  324. pkt.sysExMsg = NULL;
  325. pkt.msgCnt = 1;
  326. if( cmMidiSynthOnMidi( d->msp, &pkt, 1 ) != cmOkRC )
  327. cmErrMsg(d->err,kProcObjFailMfptRC,"Synth. MIDI receive failed.");
  328. }
  329. }
  330. // Called by the MIDI synth to send a msg to the voice bank.
  331. int _cmMidiSynthCb( struct cmMidiVoice_str* voicePtr, unsigned sel, cmSample_t* outChArray[], unsigned outChCnt )
  332. {
  333. return cmWtVoiceBankExec( ((cmWtVoiceBank*)voicePtr->pgm.cbDataPtr), voicePtr, sel, outChArray, outChCnt );
  334. }
  335. // BUG BUG BUG: THIS FUNCTION IS NOT TESTED!!!!!
  336. cmRC_t cmMfpTest2( const char* midiFn, const char* audioFn, cmCtx_t* ctx )
  337. {
  338. cmRC_t rc = kOkMfptRC;
  339. cmMfpH_t mfpH = cmMfpNullHandle;
  340. _cmMfpTest2CbData_t cbData;
  341. cmErr_t err;
  342. cmAudioFileH_t afH = cmNullAudioFileH;
  343. cmRC_t afRC = kOkAfRC;
  344. double afSrate = 44100;
  345. unsigned afBits = 16;
  346. unsigned afChCnt = 1;
  347. cmCtx* cctx;
  348. cmMidiSynth* msp;
  349. cmWtVoiceBank* vbp;
  350. unsigned msPgmCnt = 127;
  351. cmMidiSynthPgm msPgmArray[ msPgmCnt ];
  352. unsigned msVoiceCnt = 36;
  353. unsigned procSmpCnt = 64;
  354. unsigned i;
  355. cmErrSetup(&err,&ctx->rpt,"MFP Test 2");
  356. // create the MIDI file player
  357. if( cmMfpCreate(&mfpH, _cmMfpCb, &cbData, ctx ) != kOkMfpRC )
  358. return cmErrMsg(&err,kMfpFailMfptRC,"MIDI file player create failed.");
  359. // create an output audio file
  360. if( cmAudioFileIsValid( afH = cmAudioFileNewCreate(audioFn, afSrate, afBits, afChCnt, &afRC, &ctx->rpt))==false)
  361. {
  362. rc = cmErrMsg(&err,kAudioFileFailMfptRC,"The audio file create failed.");
  363. goto errLabel;
  364. }
  365. // load the midi file into the player
  366. if( cmMfpLoadFile( mfpH, midiFn ) != kOkMfpRC )
  367. {
  368. rc = cmErrMsg(&err,kMfpFailMfptRC,"MIDI file load failed.");
  369. goto errLabel;
  370. }
  371. // create the proc obj context
  372. if((cctx = cmCtxAlloc(NULL, &ctx->rpt, cmLHeapNullHandle, cmSymTblNullHandle )) == NULL)
  373. {
  374. rc = cmErrMsg(&err,kProcObjFailMfptRC,"cmCtx allocate failed.");
  375. goto errLabel;
  376. }
  377. // create the voice bank
  378. if((vbp = cmWtVoiceBankAlloc(cctx, NULL, afSrate, procSmpCnt, msVoiceCnt, afChCnt )) == NULL)
  379. {
  380. rc = cmErrMsg(&err,kProcObjFailMfptRC,"WT voice bank allocate failed.");
  381. goto errLabel;
  382. }
  383. // a MIDI synth
  384. if((msp = cmMidiSynthAlloc(cctx, NULL, msPgmArray, msPgmCnt, msVoiceCnt, procSmpCnt, afChCnt, afSrate )) == NULL )
  385. {
  386. rc = cmErrMsg(&err,kProcObjFailMfptRC,"MIDI synth allocate failed.");
  387. goto errLabel;
  388. }
  389. cbData.msp = msp;
  390. cbData.err = &err;
  391. // load all of the the MIDI pgm recds with the same settings
  392. for(i=0; i<msPgmCnt; ++i)
  393. {
  394. msPgmArray[i].pgm = i;
  395. msPgmArray[i].cbPtr = _cmMidiSynthCb; // Call this function to update voices using this pgm
  396. msPgmArray[i].cbDataPtr = vbp; // Voice bank containing the voice states.
  397. }
  398. unsigned dusecs = floor((double)procSmpCnt * 1000000. / afSrate);
  399. while(rc != kEndOfFileMfpRC)
  400. {
  401. // update the MFP's current time and call _cmMfpCb() for MIDI msgs whose time has elapsed
  402. rc = cmMfpClock( mfpH, dusecs );
  403. // check for MFP errors
  404. if(rc!=kOkMfpRC && rc!=kEndOfFileMfpRC)
  405. {
  406. cmErrMsg(&err,kMfpFailMfptRC,"MIDI file player exec failed.");
  407. goto errLabel;
  408. }
  409. // generate audio based on the current state of the synth voices
  410. if( cmMidiSynthExec(msp, NULL, 0 ) != cmOkRC )
  411. {
  412. cmErrMsg(&err,kProcObjFailMfptRC,"MIDI synth exec. failed.");
  413. goto errLabel;
  414. }
  415. // write the last frame of synth. generated audio to the output file
  416. if( cmAudioFileWriteSample(afH, procSmpCnt, msp->outChCnt, msp->outChArray ) != kOkAfRC )
  417. {
  418. cmErrMsg(&err,kProcObjFailMfptRC,"Audio file write failed.");
  419. goto errLabel;
  420. }
  421. }
  422. errLabel:
  423. if( cmMidiSynthFree(&msp) != cmOkRC )
  424. cmErrMsg(&err,kProcObjFailMfptRC,"MIDI synth. free failed.");
  425. if( cmWtVoiceBankFree(&vbp) != cmOkRC )
  426. cmErrMsg(&err,kProcObjFailMfptRC,"WT voice free failed.");
  427. if( cmCtxFree(&cctx) != cmOkRC )
  428. cmErrMsg(&err,kProcObjFailMfptRC,"cmCtx free failed.");
  429. if( cmAudioFileDelete(&afH) )
  430. cmErrMsg(&err,kAudioFileFailMfptRC,"The audio file close failed.");
  431. if( cmMfpDestroy(&mfpH) != kOkMfpRC )
  432. cmErrMsg(&err,kMfpFailMfptRC,"MIDI file player destroy failed.");
  433. return rc;
  434. }