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.

cmMidiScoreFollow.c 8.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. #include "cmPrefix.h"
  2. #include "cmGlobal.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 "cmFloatTypes.h"
  10. #include "cmComplexTypes.h"
  11. #include "cmFile.h"
  12. #include "cmFileSys.h"
  13. #include "cmJson.h"
  14. #include "cmSymTbl.h"
  15. #include "cmAudioFile.h"
  16. #include "cmText.h"
  17. #include "cmProcObj.h"
  18. #include "cmProcTemplate.h"
  19. #include "cmMath.h"
  20. #include "cmTime.h"
  21. #include "cmMidi.h"
  22. #include "cmMidiFile.h"
  23. #include "cmProc.h"
  24. #include "cmProc2.h"
  25. #include "cmVectOps.h"
  26. #include "cmTimeLine.h"
  27. #include "cmScore.h"
  28. #include "cmProc4.h"
  29. #include "cmMidiScoreFollow.h"
  30. #include "cmScoreMatchGraphic.h"
  31. typedef struct
  32. {
  33. cmScMatcherResult_t* rV; // rV[rN] - array of stored cmScMatcher callback records.
  34. unsigned rAllocN; //
  35. unsigned rN; //
  36. } _cmMsf_ScoreFollow_t;
  37. void _cmMsf_ReportScoreErrors( const _cmMsf_ScoreFollow_t* f, cmScH_t scH )
  38. {
  39. unsigned scoreEvtN = cmScoreEvtCount(scH);
  40. unsigned i,j;
  41. for(i=0; i<scoreEvtN; ++i)
  42. {
  43. const cmScoreEvt_t* e = cmScoreEvt(scH,i);
  44. assert(e != NULL);
  45. if( e->type == kNonEvtScId && cmIsNotFlag(e->flags,kSkipScFl) )
  46. {
  47. unsigned matchN = 0;
  48. for(j=0; j<f->rN; ++j)
  49. if( f->rV[j].scEvtIdx == i )
  50. matchN += 1;
  51. if( matchN != 1 )
  52. {
  53. const cmScoreLoc_t* l = cmScoreEvtLoc(scH,e);
  54. assert(l != NULL);
  55. printf("bar:%3i evtIdx:%5i pitch:%4s match:%i ",l->barNumb,e->index,cmMidiToSciPitch(e->pitch,NULL,0),matchN);
  56. // print the midi event associated with multiple matches.
  57. if( matchN > 1 )
  58. for(j=0; j<f->rN; ++j)
  59. if( f->rV[j].scEvtIdx == i )
  60. printf("(%i %s) ",f->rV[j].muid, cmMidiToSciPitch(f->rV[j].pitch,NULL,0) );
  61. printf("\n");
  62. }
  63. }
  64. }
  65. }
  66. void _cmMsf_ReportMidiErrors( const _cmMsf_ScoreFollow_t* f, cmScH_t scH, const cmMidiTrackMsg_t** m, unsigned mN)
  67. {
  68. unsigned i,j;
  69. unsigned lastBar = 0;
  70. // for each midi note-on msg
  71. for(i=0; i<mN; ++i)
  72. {
  73. if( (m[i]!=NULL) && cmMidiIsChStatus(m[i]->status) && cmMidiIsNoteOn(m[i]->status) && (m[i]->u.chMsgPtr->d1>0) )
  74. {
  75. unsigned matchN = 0;
  76. // find the note-on msg in the score-match result array
  77. for(j=0; j<f->rN; ++j)
  78. if( f->rV[j].muid == m[i]->uid )
  79. {
  80. if( f->rV[j].scEvtIdx != -1 )
  81. {
  82. const cmScoreEvt_t* e = cmScoreEvt(scH,f->rV[j].scEvtIdx);
  83. if( e != NULL )
  84. {
  85. const cmScoreLoc_t* l = cmScoreEvtLoc(scH,e);
  86. assert(l != NULL );
  87. lastBar = l->barNumb;
  88. }
  89. }
  90. matchN += 1;
  91. break;
  92. }
  93. if( matchN==0 )
  94. {
  95. printf("bar:%3i muid:%4i %s\n", lastBar, m[i]->uid, cmMidiToSciPitch(m[i]->u.chMsgPtr->d0,NULL,0));
  96. }
  97. }
  98. }
  99. }
  100. // Write one scScoreMatcherResult_t record to the file fH.
  101. unsigned _cmMsf_WriteMatchFileLine( cmFileH_t fH, cmScH_t scH, const cmScMatcherResult_t* r )
  102. {
  103. unsigned scUid = -1;
  104. cmChar_t buf[6];
  105. buf[0] = 0;
  106. buf[5] = 0;
  107. cmScoreLoc_t* loc = NULL;
  108. if( r->scEvtIdx > 0 && r->scEvtIdx < cmScoreEvtCount(scH))
  109. {
  110. cmScoreEvt_t* e = cmScoreEvt(scH,r->scEvtIdx);
  111. loc = cmScoreEvtLoc(scH,e);
  112. scUid = e->csvEventId;
  113. cmMidiToSciPitch(e->pitch,buf,5);
  114. }
  115. cmFilePrintf(fH,"m %3i %5i %4s %5i %4s %3i\n",
  116. loc==NULL ? 0 : loc->barNumb, // score evt bar
  117. scUid, // score event uuid
  118. buf, // score event pitch
  119. r->muid, // midi event uuid
  120. cmMidiToSciPitch(r->pitch,NULL,0), // midi event pitch
  121. r->vel); // midi event velocity
  122. return scUid;
  123. }
  124. void _cmMsf_ScoreFollowCb( struct cmScMatcher_str* p, void* arg, cmScMatcherResult_t* rp )
  125. {
  126. _cmMsf_ScoreFollow_t* r = (_cmMsf_ScoreFollow_t*)arg;
  127. r->rV[r->rN++] = *rp;
  128. }
  129. cmMsfRC_t cmMidiScoreFollowMain( cmCtx_t* ctx )
  130. {
  131. cmMsfRC_t rc = kOkMsfRC;
  132. //const cmChar_t* scoreFn = cmFsMakeUserFn("src/kc/src/kc/data","mod2e","csv",NULL);
  133. const cmChar_t* scoreFn = cmFsMakeUserFn("temp","a5","csv",NULL);
  134. const cmChar_t* midiFn = cmFsMakeUserFn("media/projects/imag_themes/scores/gen","round1-utf8_11","mid",NULL);
  135. const cmChar_t* outFn = cmFsMakeUserFn("temp","match","txt",NULL);
  136. const cmChar_t* svgFn = cmFsMakeUserFn("temp","score0","html",NULL);
  137. const cmChar_t* tlBarFn = cmFsMakeUserFn("temp",NULL,"time_line_temp","txt",NULL);
  138. double srate = 96000.0;
  139. cmScMatcher* smp = NULL;
  140. cmScH_t scH = cmScNullHandle;
  141. cmMidiFileH_t mfH = cmMidiFileNullHandle;
  142. unsigned scWndN = 10;
  143. unsigned midiWndN = 7;
  144. const cmMidiTrackMsg_t** m = NULL;
  145. unsigned mN = 0;
  146. unsigned scLocIdx = 0;
  147. cmFileH_t fH = cmFileNullHandle;
  148. cmSmgH_t smgH = cmSmgNullHandle;
  149. unsigned i;
  150. cmErr_t err;
  151. _cmMsf_ScoreFollow_t sfr;
  152. memset(&sfr,0,sizeof(sfr));
  153. cmErrSetup(&err,&ctx->rpt,"cmMidiScoreFollow");
  154. cmCtx* prCtx = cmCtxAlloc(NULL, err.rpt, cmLHeapNullHandle, cmSymTblNullHandle );
  155. // initialize the score
  156. if( cmScoreInitialize( ctx, &scH, scoreFn, srate, NULL, 0, NULL, NULL, cmSymTblNullHandle) != kOkScRC )
  157. {
  158. rc = cmErrMsg(&err,kFailMsfRC,"cmScoreInitialize() failed on %s",cmStringNullGuard(scoreFn));
  159. goto errLabel;
  160. }
  161. // setup the callback record
  162. if((sfr.rAllocN = cmScoreEvtCount( scH )*2) == 0)
  163. {
  164. rc = cmErrMsg(&err,kFailMsfRC,"The score %s appears to be empty.",cmStringNullGuard(scoreFn));
  165. goto errLabel;
  166. }
  167. sfr.rV = cmMemAllocZ(cmScMatcherResult_t,sfr.rAllocN);
  168. sfr.rN = 0;
  169. // create a matcher
  170. if((smp = cmScMatcherAlloc(prCtx, NULL, srate, scH, scWndN, midiWndN, _cmMsf_ScoreFollowCb, &sfr)) == NULL )
  171. {
  172. rc = cmErrMsg(&err,kFailMsfRC,"cmScMatcherAlloc() failed.");
  173. goto errLabel;
  174. }
  175. // open the MIDI file
  176. if( cmMidiFileOpen(ctx, &mfH, midiFn ) != kOkMfRC )
  177. {
  178. rc = cmErrMsg(&err,kFailMsfRC,"The MIDI file object could not be opened from '%s'.",cmStringNullGuard(midiFn));
  179. goto errLabel;
  180. }
  181. // get a pointer to the MIDI msg array
  182. if( (m = cmMidiFileMsgArray(mfH)) == NULL || (mN = cmMidiFileMsgCount(mfH)) == 0 )
  183. {
  184. rc = cmErrMsg(&err,kFailMsfRC,"The MIDI file object appears to be empty.");
  185. goto errLabel;
  186. }
  187. // feed each MIDI note-on to the score follower
  188. for(i=0; i<mN; ++i)
  189. if( (m[i]!=NULL) && cmMidiIsChStatus(m[i]->status) && cmMidiIsNoteOn(m[i]->status) && (m[i]->u.chMsgPtr->d1>0) )
  190. if( cmScMatcherExec( smp, m[i]->amicro * srate / 1000000.0, m[i]->uid, m[i]->status, m[i]->u.chMsgPtr->d0, m[i]->u.chMsgPtr->d1, &scLocIdx ) != cmOkRC )
  191. {
  192. rc = cmErrMsg(&err,kFailMsfRC,"The score matcher exec failed.");
  193. goto errLabel;
  194. }
  195. printf("MIDI notes:%i Score Events:%i\n",mN,cmScoreEvtCount(scH));
  196. // create the output file
  197. if( cmFileOpen(&fH,outFn,kWriteFileFl,&ctx->rpt) != kOkFileRC )
  198. {
  199. rc = cmErrMsg(&err,kFailMsfRC,"Unable to create the file '%s'.",cmStringNullGuard(outFn));
  200. goto errLabel;
  201. }
  202. // allocate the graphics object
  203. if( cmScoreMatchGraphicAlloc( ctx, &smgH, scoreFn, midiFn ) != kOkSmgRC )
  204. {
  205. rc = cmErrMsg(&err,kFailMsfRC,"Score Match Graphics allocation failed..");
  206. goto errLabel;
  207. }
  208. // for each score follower callback record
  209. for(i=0; i<sfr.rN; ++i)
  210. {
  211. // write the record to the output file
  212. unsigned scUid = _cmMsf_WriteMatchFileLine( fH, scH, sfr.rV + i );
  213. // insert the event->score match in the score match graphics object
  214. if( cmScoreMatchGraphicInsertMidi( smgH, sfr.rV[i].muid, sfr.rV[i].pitch, sfr.rV[i].vel, scUid ) != kOkSmgRC )
  215. {
  216. rc = cmErrMsg(&err,kFailMsfRC,"Score Match Graphics MIDI event insertion failed.");
  217. goto errLabel;
  218. }
  219. }
  220. //_cmMsf_ReportScoreErrors(&sfr, scH );
  221. //_cmMsf_ReportMidiErrors(&sfr, scH, m, mN);
  222. //cmScorePrint(scH,&atc->ctx->rpt);
  223. //cmMidiFilePrintMsgs( mfH, &atc->ctx->rpt );
  224. // write the tracking match file as an SVG file.
  225. cmScoreMatchGraphicWrite( smgH, svgFn );
  226. // write a cmTimeLine file which contains markers at each bar position
  227. //cmScoreMatchGraphicGenTimeLineBars(smgH, tlBarFn, srate );
  228. errLabel:
  229. cmFileClose(&fH);
  230. cmMemFree(sfr.rV);
  231. cmMidiFileClose(&mfH);
  232. cmScMatcherFree(&smp);
  233. cmScoreFinalize(&scH);
  234. cmScoreMatchGraphicFree(&smgH);
  235. cmCtxFree(&prCtx);
  236. cmFsFreeFn(scoreFn);
  237. cmFsFreeFn(midiFn);
  238. cmFsFreeFn(outFn);
  239. cmFsFreeFn(svgFn);
  240. cmFsFreeFn(tlBarFn);
  241. return rc;
  242. }