libcm is a C development framework with an emphasis on audio signal processing applications.
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

cmMidiFilePlay.c 15KB

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