|
@@ -18,35 +18,79 @@
|
18
|
18
|
#include "cmTakeSeqBldr.h"
|
19
|
19
|
|
20
|
20
|
|
21
|
|
-typedef struct cmNoteTsb_str
|
|
21
|
+// Map a score event to a MIDI event.
|
|
22
|
+typedef struct cmScTrkMidiTsb_str
|
22
|
23
|
{
|
23
|
24
|
unsigned mni; // midi note index as an offset from the take marker
|
24
|
25
|
unsigned scEvtIdx; // score event index this not is assoc'd with or -1 if it did not match
|
25
|
26
|
unsigned flags; // flags from cmScMatcherResult_t
|
26
|
|
-} cmNoteTsb_t;
|
|
27
|
+} cmScTrkMidiTsb_t;
|
27
|
28
|
|
28
|
|
-typedef struct cmTakeTsb_str
|
|
29
|
+
|
|
30
|
+// Score Tracking info. from a single take (time-line marker)
|
|
31
|
+typedef struct cmScTrkTakeTsb_str
|
|
32
|
+{
|
|
33
|
+ unsigned markerUid; // marker time line uid assoc'd with this take
|
|
34
|
+ cmScTrkMidiTsb_t* midiV; // midiV[midiN] score to midi file map recd. array.
|
|
35
|
+ unsigned midiN;
|
|
36
|
+ bool failFl;
|
|
37
|
+} cmScTrkTakeTsb_t;
|
|
38
|
+
|
|
39
|
+enum
|
29
|
40
|
{
|
30
|
|
- unsigned markerUid; // marker time line uid assoc'd with this take
|
31
|
|
- cmNoteTsb_t* noteV; // noteV[noteN] score to midi file map recd. array.
|
32
|
|
- unsigned noteN;
|
33
|
|
- bool failFl;
|
34
|
|
-} cmTakeTsb_t;
|
|
41
|
+ kNoteTsbFl = 0x01,
|
|
42
|
+ kPedalTsbFl = 0x02,
|
|
43
|
+ kEnableTsbFl = 0x04
|
|
44
|
+};
|
|
45
|
+
|
|
46
|
+//
|
|
47
|
+typedef struct cmMidiEvt_str
|
|
48
|
+{
|
|
49
|
+ unsigned srcId; // marker uid or -1 if this event was manually inserted
|
|
50
|
+ unsigned flags; // note | pedal | enable
|
|
51
|
+ struct cmMidiEvt_str* ref; // previous MIDI event in time
|
|
52
|
+ unsigned offsetSmp; // time offset from *ref
|
|
53
|
+ unsigned durSmp; // duration of this MIDI event
|
|
54
|
+ unsigned d0; // d0 MIDI channel msg data.
|
|
55
|
+ unsigned d1; // d1 MIDI channel msg data
|
|
56
|
+ struct cmMidiEvt_str* link; // pointer to next MIDI event in list
|
|
57
|
+} cmMidiEvt_t;
|
|
58
|
+
|
|
59
|
+// This record represents a note or pedal score event
|
|
60
|
+typedef struct cmScEvtTsb_str
|
|
61
|
+{
|
|
62
|
+ unsigned flags; // note | pedal
|
|
63
|
+ unsigned scEvtIdx; // score event index (into scH)
|
|
64
|
+ cmMidiEvt_t* evtList; // list of alternate MIDI events which may render this event
|
|
65
|
+} cmScEvtTsb_t;
|
|
66
|
+
|
|
67
|
+// This record contains all the score events and and score synchronized MIDI events
|
|
68
|
+// associated with a given take. Each call to cmTakeSeqBldrLoadTake() creates
|
|
69
|
+// one of these records.
|
|
70
|
+typedef struct cmTakeScEvtArrayTsb_str
|
|
71
|
+{
|
|
72
|
+ unsigned tlMarkerUid; // time-line marker uid associated with this take
|
|
73
|
+ cmScEvtTsb_t* scEvtV; // scEvtV[scEvtN] array of score events contained by this take
|
|
74
|
+ unsigned scEvtN; // count of score events in this take
|
|
75
|
+} cmTakeScEvtArrayTsb_t;
|
35
|
76
|
|
36
|
77
|
typedef struct
|
37
|
78
|
{
|
38
|
|
- cmCtx_t ctx;
|
39
|
|
- cmErr_t err;
|
40
|
|
- cmJsonH_t jsH;
|
41
|
|
- const cmChar_t* tlFn;
|
42
|
|
- const cmChar_t* scFn;
|
43
|
|
- const cmChar_t* tlPrefixPath;
|
44
|
|
-
|
45
|
|
- cmTakeTsb_t* takeV; // takeV[ takeN ]
|
46
|
|
- unsigned takeN;
|
|
79
|
+ cmCtx_t ctx; // application context
|
|
80
|
+ cmErr_t err; // internal error object
|
|
81
|
+ cmJsonH_t jsH; // JSON tree used to hold score tracker info.
|
|
82
|
+ const cmChar_t* tlFn; // time line filename
|
|
83
|
+ const cmChar_t* scFn; // score file name
|
|
84
|
+ const cmChar_t* tlPrefixPath; // path to time line audio and MIDI files
|
|
85
|
+ cmTlH_t tlH; // time-line handle
|
|
86
|
+ cmScH_t scH; // score handle
|
|
87
|
+
|
|
88
|
+ cmScTrkTakeTsb_t* scTrkTakeV; // score tracker scTrkTakeV[ scTrkTakeN ]
|
|
89
|
+ unsigned scTrkTakeN;
|
|
90
|
+
|
|
91
|
+ cmTakeScEvtArrayTsb_t* takes; // list of scEvt arrays used by this sequence
|
|
92
|
+ cmTakeScEvtArrayTsb_t* manual; // list of manually inserted MIDI events
|
47
|
93
|
|
48
|
|
- cmTlH_t tlH;
|
49
|
|
- cmScH_t scH;
|
50
|
94
|
|
51
|
95
|
} cmTsb_t;
|
52
|
96
|
|
|
@@ -69,10 +113,10 @@ cmTsbRC_t _cmTsbScoreTrkFree( cmTsb_t* p )
|
69
|
113
|
goto errLabel;
|
70
|
114
|
}
|
71
|
115
|
|
72
|
|
- for(i=0; i<p->takeN; ++i)
|
73
|
|
- cmMemPtrFree(&p->takeV[i].noteV);
|
|
116
|
+ for(i=0; i<p->scTrkTakeN; ++i)
|
|
117
|
+ cmMemPtrFree(&p->scTrkTakeV[i].midiV);
|
74
|
118
|
|
75
|
|
- cmMemPtrFree(&p->takeV);
|
|
119
|
+ cmMemPtrFree(&p->scTrkTakeV);
|
76
|
120
|
|
77
|
121
|
if( cmTimeLineFinalize(&p->tlH) != kOkTlRC )
|
78
|
122
|
rc = cmErrMsg(&p->err,kTimeLineFailTsbRC,"Time line object finalize failed.");
|
|
@@ -131,13 +175,13 @@ cmTsbRC_t _cmTsbLoadScoreTrkFile( cmTsb_t* p, const cmChar_t* scoreTrkFn )
|
131
|
175
|
}
|
132
|
176
|
|
133
|
177
|
// count of take records
|
134
|
|
- p->takeN = cmJsonChildCount(tkArrObj);
|
|
178
|
+ p->scTrkTakeN = cmJsonChildCount(tkArrObj);
|
135
|
179
|
|
136
|
180
|
// array of take records
|
137
|
|
- p->takeV = cmMemAllocZ(cmTakeTsb_t,p->takeN);
|
|
181
|
+ p->scTrkTakeV = cmMemAllocZ(cmScTrkTakeTsb_t,p->scTrkTakeN);
|
138
|
182
|
|
139
|
183
|
// for each take record
|
140
|
|
- for(i=0; i<p->takeN; ++i)
|
|
184
|
+ for(i=0; i<p->scTrkTakeN; ++i)
|
141
|
185
|
{
|
142
|
186
|
cmJsonNode_t* takeObj = NULL;
|
143
|
187
|
cmJsonNode_t* noteArrObj = NULL;
|
|
@@ -152,8 +196,8 @@ cmTsbRC_t _cmTsbLoadScoreTrkFile( cmTsb_t* p, const cmChar_t* scoreTrkFn )
|
152
|
196
|
|
153
|
197
|
// parse the take record
|
154
|
198
|
if((jsRC = cmJsonMemberValues( takeObj, &errMsg,
|
155
|
|
- "markerUid",kIntTId, &p->takeV[i].markerUid,
|
156
|
|
- "failFl", kIntTId, &p->takeV[i].failFl,
|
|
199
|
+ "markerUid",kIntTId, &p->scTrkTakeV[i].markerUid,
|
|
200
|
+ "failFl", kIntTId, &p->scTrkTakeV[i].failFl,
|
157
|
201
|
"array", kArrayTId, ¬eArrObj,
|
158
|
202
|
0)) != kOkJsRC )
|
159
|
203
|
{
|
|
@@ -166,13 +210,13 @@ cmTsbRC_t _cmTsbLoadScoreTrkFile( cmTsb_t* p, const cmChar_t* scoreTrkFn )
|
166
|
210
|
}
|
167
|
211
|
|
168
|
212
|
// get the count of note records
|
169
|
|
- p->takeV[i].noteN = cmJsonChildCount(noteArrObj);
|
|
213
|
+ p->scTrkTakeV[i].midiN = cmJsonChildCount(noteArrObj);
|
170
|
214
|
|
171
|
215
|
// allocate a note record array for this take
|
172
|
|
- p->takeV[i].noteV = cmMemAllocZ(cmNoteTsb_t, p->takeV[i].noteN);
|
|
216
|
+ p->scTrkTakeV[i].midiV = cmMemAllocZ(cmScTrkMidiTsb_t, p->scTrkTakeV[i].midiN);
|
173
|
217
|
|
174
|
218
|
// for each note record
|
175
|
|
- for(j=0; j<p->takeV[i].noteN; ++j)
|
|
219
|
+ for(j=0; j<p->scTrkTakeV[i].midiN; ++j)
|
176
|
220
|
{
|
177
|
221
|
cmJsonNode_t* noteObj = NULL;
|
178
|
222
|
|
|
@@ -185,9 +229,9 @@ cmTsbRC_t _cmTsbLoadScoreTrkFile( cmTsb_t* p, const cmChar_t* scoreTrkFn )
|
185
|
229
|
|
186
|
230
|
// parse the note record
|
187
|
231
|
if((jsRC = cmJsonMemberValues( noteObj, &errMsg,
|
188
|
|
- "mni", kIntTId, &p->takeV[i].noteV[j].mni,
|
189
|
|
- "scEvtIdx", kIntTId, &p->takeV[i].noteV[j].scEvtIdx,
|
190
|
|
- "flags", kIntTId, &p->takeV[i].noteV[j].flags,
|
|
232
|
+ "mni", kIntTId, &p->scTrkTakeV[i].midiV[j].mni,
|
|
233
|
+ "scEvtIdx", kIntTId, &p->scTrkTakeV[i].midiV[j].scEvtIdx,
|
|
234
|
+ "flags", kIntTId, &p->scTrkTakeV[i].midiV[j].flags,
|
191
|
235
|
0)) != kOkJsRC )
|
192
|
236
|
{
|
193
|
237
|
if( jsRC == kNodeNotFoundJsRC && errMsg != NULL )
|
|
@@ -207,6 +251,30 @@ cmTsbRC_t _cmTsbLoadScoreTrkFile( cmTsb_t* p, const cmChar_t* scoreTrkFn )
|
207
|
251
|
return rc;
|
208
|
252
|
}
|
209
|
253
|
|
|
254
|
+// Return the count of score events inside a given marker.
|
|
255
|
+unsigned _cmTsbScoreTrkMarkerEventCount( cmTsb_t* p, unsigned markUid )
|
|
256
|
+{
|
|
257
|
+ unsigned i,j;
|
|
258
|
+ unsigned minScEvtIdx = INT_MAX;
|
|
259
|
+ unsigned maxScEvtIdx = 0;
|
|
260
|
+
|
|
261
|
+ for(i=0; i<p->scTrkTakeN; ++i)
|
|
262
|
+ for(j=0; j<p->scTrkTakeV[i].midiN; ++j)
|
|
263
|
+ {
|
|
264
|
+ if( p->scTrkTakeV[i].midiV[j].scEvtIdx < minScEvtIdx )
|
|
265
|
+ minScEvtIdx = p->scTrkTakeV[i].midiV[j].scEvtIdx;
|
|
266
|
+
|
|
267
|
+ if( p->scTrkTakeV[i].midiV[j].scEvtIdx > maxScEvtIdx )
|
|
268
|
+ maxScEvtIdx = p->scTrkTakeV[i].midiV[j].scEvtIdx;
|
|
269
|
+ }
|
|
270
|
+
|
|
271
|
+ if( maxScEvtIdx < minScEvtIdx )
|
|
272
|
+ return 0;
|
|
273
|
+
|
|
274
|
+ return (maxScEvtIdx - minScEvtIdx) + 1;
|
|
275
|
+
|
|
276
|
+}
|
|
277
|
+
|
210
|
278
|
cmTsbRC_t cmTakeSeqBldrAlloc( cmCtx_t* ctx, cmTakeSeqBldrH_t* hp )
|
211
|
279
|
{
|
212
|
280
|
cmTsbRC_t rc;
|
|
@@ -286,7 +354,77 @@ cmTsbRC_t cmTakeSeqBldrInitialize( cmTakeSeqBldrH_t h, const cmChar_t* scoreTrkF
|
286
|
354
|
|
287
|
355
|
cmTsbRC_t cmTakeSeqBldrLoadTake( cmTakeSeqBldrH_t h, unsigned tlMarkUid, bool overwriteFL )
|
288
|
356
|
{
|
289
|
|
- cmTsbRC_t rc = kOkTsbRC;
|
|
357
|
+ cmTsbRC_t rc = kOkTsbRC;
|
|
358
|
+ cmTsb_t* p = _cmTsbHandleToPtr(h);
|
|
359
|
+ cmTlMarker_t* mark = NULL;
|
|
360
|
+ cmTlMidiFile_t* mf = NULL;
|
|
361
|
+ cmMidiFileH_t mfH = cmMidiFileNullHandle;
|
|
362
|
+
|
|
363
|
+ // get a pointer to the time-line marker object
|
|
364
|
+ if((mark = cmTlMarkerObjPtr( p->tlH, cmTimeLineIdToObj( p->tlH, cmInvalidId, tlMarkUid))) == NULL )
|
|
365
|
+ {
|
|
366
|
+ rc = cmErrMsg(&p->err,kInvalidArgTsbRC,"The time-line marker uid '%i' is not valid.",tlMarkUid);
|
|
367
|
+ goto errLabel;
|
|
368
|
+ }
|
|
369
|
+
|
|
370
|
+ // get the name of the MIDI file which contains the marker
|
|
371
|
+ if((mf = cmTimeLineMidiFileAtTime( p->tlH, mark->obj.seqId, mark->obj.seqSmpIdx )) == NULL )
|
|
372
|
+ {
|
|
373
|
+ rc = cmErrMsg(&p->err,kInvalidArgTsbRC,"The time-line marker '%i' does not intersect with a MIDI file.",tlMarkUid);
|
|
374
|
+ goto errLabel;
|
|
375
|
+ }
|
|
376
|
+
|
|
377
|
+ // open the MIDI file
|
|
378
|
+ if( cmMidiFileOpen( cmMidiFileName(mf->h), &mfH, &p->ctx ) != kOkMfRC )
|
|
379
|
+ {
|
|
380
|
+ rc = cmErrMsg(&p->err,kInvalidArgTsbRC,"The MIDI file '%s' could not be opened.", cmStringNullGuard(cmMidiFileName(mf->h)));
|
|
381
|
+ goto errLabel;
|
|
382
|
+ }
|
|
383
|
+
|
|
384
|
+ // convert the dtick field to absolute sample indexes
|
|
385
|
+ cmMidiFileTickToSamples( mfH, cmTimeLineSampleRate(p->tlH), true );
|
|
386
|
+
|
|
387
|
+ // calculate MIDI note and pedal durations (see cmMidiChMsg_t.durTicks)
|
|
388
|
+ cmMidiFileCalcNoteDurations( mfH );
|
|
389
|
+
|
|
390
|
+ // convert the marker beg/end sample position to be relative to the MIDI file start time
|
|
391
|
+ unsigned bsi = mark->obj.seqSmpIdx - mf->obj.seqSmpIdx;
|
|
392
|
+ unsigned esi = mark->obj.seqSmpIdx + mark->obj.durSmpCnt - mf->obj.seqSmpIdx;
|
|
393
|
+ unsigned i = 0;
|
|
394
|
+ unsigned n = cmMidiFileMsgCount(mfH);
|
|
395
|
+ const cmMidiTrackMsg_t** a = cmMidiFileMsgArray(mfH);
|
|
396
|
+
|
|
397
|
+ // seek to the first MIDI msg after bsi
|
|
398
|
+ for(i=0; i<n; ++i)
|
|
399
|
+ if( a[i]->dtick >= bsi )
|
|
400
|
+ break;
|
|
401
|
+
|
|
402
|
+ if( i == n )
|
|
403
|
+ {
|
|
404
|
+ rc = cmErrMsg(&p->err,kInvalidArgTsbRC,"No MIDI events were found in the marker.");
|
|
405
|
+ goto errLabel;
|
|
406
|
+ }
|
|
407
|
+
|
|
408
|
+ // for each MIDI message between bsi and esi
|
|
409
|
+ for(; i<n && a[i]->dtick < esi; ++i)
|
|
410
|
+ {
|
|
411
|
+ const cmMidiTrackMsg_t* m = a[i];
|
|
412
|
+ switch( m->status )
|
|
413
|
+ {
|
|
414
|
+ case kNoteOffMdId:
|
|
415
|
+ case kNoteOnMdId:
|
|
416
|
+ case kCtlMdId:
|
|
417
|
+
|
|
418
|
+ break;
|
|
419
|
+ }
|
|
420
|
+ }
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+ errLabel:
|
|
425
|
+ if( cmMidiFileClose(&mfH) != kOkMfRC )
|
|
426
|
+ rc = cmErrMsg(&p->err,kMidiFileFailTsbRC,"MIDI file close failed.");
|
|
427
|
+
|
290
|
428
|
return rc;
|
291
|
429
|
}
|
292
|
430
|
|