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