|
@@ -37,8 +37,10 @@ enum
|
37
|
37
|
|
38
|
38
|
enum
|
39
|
39
|
{
|
|
40
|
+ kMidiFileIdColScIdx= 0,
|
40
|
41
|
kTypeLabelColScIdx = 3,
|
41
|
|
- kDSecsColScIdx = 5,
|
|
42
|
+ kDSecsColScIdx = 4,
|
|
43
|
+ kSecsColScIdx = 5,
|
42
|
44
|
kPitchColScIdx = 11,
|
43
|
45
|
kBarColScIdx = 13,
|
44
|
46
|
kSkipColScIdx = 14,
|
|
@@ -63,6 +65,8 @@ typedef struct
|
63
|
65
|
cmScCb_t cbFunc;
|
64
|
66
|
void* cbArg;
|
65
|
67
|
cmChar_t* fn;
|
|
68
|
+ cmScoreLoc_t* loc;
|
|
69
|
+ unsigned locCnt;
|
66
|
70
|
} cmSc_t;
|
67
|
71
|
|
68
|
72
|
cmScEvtRef_t _cmScEvtRefArray[] =
|
|
@@ -176,6 +180,11 @@ cmScRC_t _cmScFinalize( cmSc_t* p )
|
176
|
180
|
if( cmCsvFinalize(&p->cH) != kOkCsvRC )
|
177
|
181
|
return rc;
|
178
|
182
|
|
|
183
|
+ unsigned i;
|
|
184
|
+ for(i=0; i<p->locCnt; ++i)
|
|
185
|
+ cmMemFree(p->loc[i].evtArray);
|
|
186
|
+ cmMemFree(p->loc);
|
|
187
|
+
|
179
|
188
|
cmMemFree(p->fn);
|
180
|
189
|
cmMemFree(p->array);
|
181
|
190
|
cmMemFree(p);
|
|
@@ -188,7 +197,7 @@ cmScRC_t _cmScParseBar( cmSc_t* p, unsigned rowIdx, cmScoreEvt_t* s, int* barNum
|
188
|
197
|
return cmErrMsg(&p->err,kSyntaxErrScRC,"Unable to parse the bar number.");
|
189
|
198
|
|
190
|
199
|
s->type = kBarEvtScId;
|
191
|
|
- s->dsecs = 0;
|
|
200
|
+ s->secs = 0;
|
192
|
201
|
s->barNumb = *barNumb;
|
193
|
202
|
return kOkScRC;
|
194
|
203
|
}
|
|
@@ -201,7 +210,8 @@ cmScRC_t _cmScParseNoteOn( cmSc_t* p, unsigned rowIdx, cmScoreEvt_t* s, int barN
|
201
|
210
|
const cmChar_t* sciPitch;
|
202
|
211
|
cmMidiByte_t midiPitch;
|
203
|
212
|
const cmChar_t* attr;
|
204
|
|
- double dsecs;
|
|
213
|
+ double secs;
|
|
214
|
+ double durSecs;
|
205
|
215
|
|
206
|
216
|
if((sciPitch = cmCsvCellText(p->cH,rowIdx,kPitchColScIdx)) == NULL )
|
207
|
217
|
return cmErrMsg(&p->err,kSyntaxErrScRC,"Expected a scientific pitch value");
|
|
@@ -210,8 +220,8 @@ cmScRC_t _cmScParseNoteOn( cmSc_t* p, unsigned rowIdx, cmScoreEvt_t* s, int barN
|
210
|
220
|
return cmErrMsg(&p->err,kSyntaxErrScRC,"Unable to convert the scientific pitch '%s' to a MIDI value. ");
|
211
|
221
|
|
212
|
222
|
// it is possible that note delta-secs field is empty - so default to 0
|
213
|
|
- if((dsecs = cmCsvCellDouble(p->cH, rowIdx, kDSecsColScIdx )) == DBL_MAX) // Returns DBL_MAX on error.
|
214
|
|
- dsecs = 0;
|
|
223
|
+ if((secs = cmCsvCellDouble(p->cH, rowIdx, kSecsColScIdx )) == DBL_MAX) // Returns DBL_MAX on error.
|
|
224
|
+ flags += kInvalidScFl;
|
215
|
225
|
|
216
|
226
|
if((attr = cmCsvCellText(p->cH,rowIdx,kSkipColScIdx)) != NULL && *attr == 's' )
|
217
|
227
|
flags += kSkipScFl;
|
|
@@ -230,14 +240,19 @@ cmScRC_t _cmScParseNoteOn( cmSc_t* p, unsigned rowIdx, cmScoreEvt_t* s, int barN
|
230
|
240
|
flags += kDynScFl;
|
231
|
241
|
}
|
232
|
242
|
|
|
243
|
+ // Returns DBL_MAX on error.
|
|
244
|
+ if((durSecs = cmCsvCellDouble(p->cH, rowIdx, kDSecsColScIdx )) == DBL_MAX)
|
|
245
|
+ durSecs = 0.25;
|
|
246
|
+
|
|
247
|
+
|
233
|
248
|
s->type = kNonEvtScId;
|
234
|
|
- s->dsecs = dsecs;
|
|
249
|
+ s->secs = secs;
|
235
|
250
|
s->pitch = midiPitch;
|
236
|
251
|
s->flags = flags;
|
237
|
252
|
s->dynVal = dynVal;
|
238
|
253
|
s->barNumb = barNumb;
|
239
|
254
|
s->barNoteIdx = barNoteIdx;
|
240
|
|
-
|
|
255
|
+ s->durSecs = durSecs;
|
241
|
256
|
return rc;
|
242
|
257
|
}
|
243
|
258
|
|
|
@@ -248,6 +263,7 @@ cmScRC_t _cmScParseFile( cmSc_t* p, cmCtx_t* ctx, const cmChar_t* fn )
|
248
|
263
|
int barEvtIdx = cmInvalidIdx;
|
249
|
264
|
int barNumb = 0;
|
250
|
265
|
double secs;
|
|
266
|
+ double cur_secs = 0;
|
251
|
267
|
|
252
|
268
|
if( cmCsvInitialize(&p->cH, ctx ) != kOkCsvRC )
|
253
|
269
|
{
|
|
@@ -295,23 +311,29 @@ cmScRC_t _cmScParseFile( cmSc_t* p, cmCtx_t* ctx, const cmChar_t* fn )
|
295
|
311
|
|
296
|
312
|
switch(tid)
|
297
|
313
|
{
|
298
|
|
- case kBarEvtScId:
|
299
|
|
- // parse bar lines
|
|
314
|
+ case kBarEvtScId: // parse bar lines
|
300
|
315
|
if((rc = _cmScParseBar(p,i,p->array+j,&barNumb)) == kOkScRC )
|
301
|
316
|
{
|
302
|
317
|
barNoteIdx = 0;
|
303
|
318
|
barEvtIdx = j;
|
|
319
|
+ p->array[j].index = j;
|
304
|
320
|
++j;
|
305
|
321
|
}
|
306
|
322
|
break;
|
307
|
323
|
|
308
|
|
- case kNonEvtScId:
|
309
|
|
- // parse note-on events
|
|
324
|
+ case kNonEvtScId: // parse note-on events
|
310
|
325
|
if((rc = _cmScParseNoteOn(p, i, p->array + j, barNumb, barNoteIdx )) == kOkScRC )
|
311
|
326
|
{
|
312
|
|
- secs = p->array[j].dsecs;
|
|
327
|
+ secs = p->array[j].secs;
|
|
328
|
+
|
|
329
|
+ if( p->array[j].secs == DBL_MAX )
|
|
330
|
+ p->array[j].secs = cur_secs;
|
|
331
|
+
|
313
|
332
|
if( cmIsFlag(p->array[j].flags,kSkipScFl) == false )
|
|
333
|
+ {
|
|
334
|
+ p->array[j].index = j;
|
314
|
335
|
++j;
|
|
336
|
+ }
|
315
|
337
|
|
316
|
338
|
++barNoteIdx;
|
317
|
339
|
}
|
|
@@ -319,16 +341,24 @@ cmScRC_t _cmScParseFile( cmSc_t* p, cmCtx_t* ctx, const cmChar_t* fn )
|
319
|
341
|
|
320
|
342
|
default:
|
321
|
343
|
// Returns DBL_MAX on error.
|
322
|
|
- secs = cmCsvCellDouble(p->cH, i, kDSecsColScIdx );
|
|
344
|
+ secs = cmCsvCellDouble(p->cH, i, kSecsColScIdx );
|
323
|
345
|
break;
|
324
|
346
|
}
|
325
|
347
|
|
|
348
|
+ if( secs != DBL_MAX )
|
|
349
|
+ cur_secs = secs;
|
|
350
|
+
|
326
|
351
|
// the bar lines don't have times so set the time of the bar line to the
|
327
|
352
|
// time of the first event in the bar.
|
328
|
353
|
if( barEvtIdx != cmInvalidIdx && secs != DBL_MAX )
|
329
|
354
|
{
|
330
|
355
|
assert( p->array[ barEvtIdx ].type == kBarEvtScId );
|
331
|
|
- p->array[ barEvtIdx ].dsecs = secs;
|
|
356
|
+ p->array[ barEvtIdx ].secs = secs;
|
|
357
|
+
|
|
358
|
+ // handle the case where the previous bar had no events
|
|
359
|
+ if( p->array[ barEvtIdx-1].type == kBarEvtScId )
|
|
360
|
+ p->array[ barEvtIdx-1].secs = secs;
|
|
361
|
+
|
332
|
362
|
barEvtIdx = cmInvalidIdx;
|
333
|
363
|
}
|
334
|
364
|
|
|
@@ -347,6 +377,76 @@ cmScRC_t _cmScParseFile( cmSc_t* p, cmCtx_t* ctx, const cmChar_t* fn )
|
347
|
377
|
return rc;
|
348
|
378
|
}
|
349
|
379
|
|
|
380
|
+// This function does not currently work because there is no
|
|
381
|
+// guarantee that all the time values (secs field) have been filled in
|
|
382
|
+/// with valid times and that all event records have a valid 'type' id.
|
|
383
|
+cmScRC_t _cmScoreInitLocArray( cmSc_t* p )
|
|
384
|
+{
|
|
385
|
+ cmScRC_t rc = kOkScRC;
|
|
386
|
+ double minDSecs = 0;
|
|
387
|
+ unsigned barNumb = 0;
|
|
388
|
+
|
|
389
|
+ if( p->cnt==0)
|
|
390
|
+ return rc;
|
|
391
|
+
|
|
392
|
+ p->locCnt = 1;
|
|
393
|
+
|
|
394
|
+ // count the number of unique time locations in the score
|
|
395
|
+ int i,j,k;
|
|
396
|
+ double secs = p->array[0].secs;
|
|
397
|
+ for(i=0; i<p->cnt; ++i)
|
|
398
|
+ {
|
|
399
|
+ assert( p->array[i].secs >= secs );
|
|
400
|
+
|
|
401
|
+ if( p->array[i].secs - secs <= minDSecs )
|
|
402
|
+ {
|
|
403
|
+ p->locCnt += 1;
|
|
404
|
+ secs = p->array[i].secs;
|
|
405
|
+ }
|
|
406
|
+ }
|
|
407
|
+
|
|
408
|
+ // allocate the loc. array
|
|
409
|
+ p->loc = cmMemAllocZ(cmScoreLoc_t,p->locCnt);
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+ // fill in the location array
|
|
414
|
+ for(i=0,k=0; i<p->cnt; ++k)
|
|
415
|
+ {
|
|
416
|
+ j = i+1;
|
|
417
|
+
|
|
418
|
+ assert(p->array[j].secs > p->array[i].secs );
|
|
419
|
+
|
|
420
|
+ // get the count of events at this location
|
|
421
|
+ while( j<p->cnt && p->array[j].secs - p->array[i].secs < minDSecs )
|
|
422
|
+ ++j;
|
|
423
|
+
|
|
424
|
+ assert(k<p->locCnt);
|
|
425
|
+
|
|
426
|
+ p->loc[k].evtCnt = j-i;
|
|
427
|
+ p->loc[k].evtArray = cmMemAllocZ(cmScoreEvt_t*,p->loc[k].evtCnt);
|
|
428
|
+
|
|
429
|
+ // fill in the location record event pointers
|
|
430
|
+ for(j=0; j<p->loc[k].evtCnt; ++j)
|
|
431
|
+ {
|
|
432
|
+ p->loc[k].evtArray[j] = p->array + (i + j);
|
|
433
|
+
|
|
434
|
+ if( p->array[i+j].type == kBarEvtScId )
|
|
435
|
+ barNumb = p->array[i+j].barNumb;
|
|
436
|
+ }
|
|
437
|
+
|
|
438
|
+ // fill in the location record
|
|
439
|
+ p->loc[k].secs = p->array[i].secs;
|
|
440
|
+ p->loc[k].evtIdx = i;
|
|
441
|
+ p->loc[k].barNumb = barNumb;
|
|
442
|
+
|
|
443
|
+ i += p->loc[k].evtCnt;
|
|
444
|
+
|
|
445
|
+ }
|
|
446
|
+
|
|
447
|
+ return rc;
|
|
448
|
+}
|
|
449
|
+
|
350
|
450
|
cmScRC_t cmScoreInitialize( cmCtx_t* ctx, cmScH_t* hp, const cmChar_t* fn, cmScCb_t cbFunc, void* cbArg )
|
351
|
451
|
{
|
352
|
452
|
cmScRC_t rc = kOkScRC;
|
|
@@ -360,6 +460,10 @@ cmScRC_t cmScoreInitialize( cmCtx_t* ctx, cmScH_t* hp, const cmChar_t* fn, cmScC
|
360
|
460
|
if((rc = _cmScParseFile(p,ctx,fn)) != kOkScRC )
|
361
|
461
|
goto errLabel;
|
362
|
462
|
|
|
463
|
+ // See note at function
|
|
464
|
+ //if((rc = _cmScoreInitLocArray(p)) != kOkScRC )
|
|
465
|
+ // goto errLabel;
|
|
466
|
+
|
363
|
467
|
p->cbFunc = cbFunc;
|
364
|
468
|
p->cbArg = cbArg;
|
365
|
469
|
p->fn = cmMemAllocStr(fn);
|
|
@@ -416,6 +520,24 @@ cmScoreEvt_t* cmScoreEvt( cmScH_t h, unsigned idx )
|
416
|
520
|
return p->array + idx;
|
417
|
521
|
}
|
418
|
522
|
|
|
523
|
+unsigned cmScoreLocCount( cmScH_t h )
|
|
524
|
+{
|
|
525
|
+ cmSc_t* p = _cmScHandleToPtr(h);
|
|
526
|
+ return p->locCnt;
|
|
527
|
+}
|
|
528
|
+
|
|
529
|
+cmScoreLoc_t* cmScoreLoc( cmScH_t h, unsigned idx )
|
|
530
|
+{
|
|
531
|
+ cmSc_t* p = _cmScHandleToPtr(h);
|
|
532
|
+ if( idx >= p->locCnt )
|
|
533
|
+ {
|
|
534
|
+ cmErrMsg(&p->err,kInvalidIdxScRC,"%i is an invalid index for %i location records.",idx,p->locCnt);
|
|
535
|
+ return NULL;
|
|
536
|
+ }
|
|
537
|
+ return p->loc + idx;
|
|
538
|
+}
|
|
539
|
+
|
|
540
|
+
|
419
|
541
|
cmScRC_t cmScoreSeqNotify( cmScH_t h )
|
420
|
542
|
{
|
421
|
543
|
cmScRC_t rc = kOkScRC;
|
|
@@ -707,3 +829,111 @@ void cmScoreTest( cmCtx_t* ctx, const cmChar_t* fn )
|
707
|
829
|
|
708
|
830
|
cmScoreFinalize(&h);
|
709
|
831
|
}
|
|
832
|
+
|
|
833
|
+// 1. Fix absolute message time which was incorrect on original score file.
|
|
834
|
+// 2.
|
|
835
|
+void cmScoreFix( cmCtx_t* ctx )
|
|
836
|
+{
|
|
837
|
+ const cmChar_t* mfn = "/home/kevin/src/cmgv/src/gv/data/ImaginaryThemes.mid";
|
|
838
|
+ const cmChar_t* crfn = "/home/kevin/src/cmgv/src/gv/data/mod0a.txt";
|
|
839
|
+ const cmChar_t* cwfn = "/home/kevin/src/cmgv/src/gv/data/mod1.csv";
|
|
840
|
+ cmMidiFileH_t mfH = cmMidiFileNullHandle;
|
|
841
|
+ cmCsvH_t csvH = cmCsvNullHandle;
|
|
842
|
+ const cmMidiTrackMsg_t** msg = NULL;
|
|
843
|
+ double secs = 0.0;
|
|
844
|
+ int ci,mi,crn,mn;
|
|
845
|
+ bool errFl = true;
|
|
846
|
+ unsigned handCnt = 0;
|
|
847
|
+ unsigned midiMissCnt = 0;
|
|
848
|
+
|
|
849
|
+ if( cmCsvInitialize(&csvH,ctx) != kOkCsvRC )
|
|
850
|
+ goto errLabel;
|
|
851
|
+
|
|
852
|
+ if( cmCsvLexRegisterMatcher(csvH, cmCsvLexNextAvailId(csvH), _cmScLexSciPitchMatcher ) != kOkCsvRC )
|
|
853
|
+ goto errLabel;
|
|
854
|
+
|
|
855
|
+ if( cmCsvParseFile(csvH, crfn, 0 ) != kOkCsvRC )
|
|
856
|
+ goto errLabel;
|
|
857
|
+
|
|
858
|
+ if( cmMidiFileOpen(mfn,&mfH,ctx) != kOkMfRC )
|
|
859
|
+ goto errLabel;
|
|
860
|
+
|
|
861
|
+ cmMidiFileTickToMicros(mfH);
|
|
862
|
+
|
|
863
|
+ cmMidiFileCalcNoteDurations(mfH);
|
|
864
|
+
|
|
865
|
+ mn = cmMidiFileMsgCount(mfH);
|
|
866
|
+
|
|
867
|
+ msg = cmMidiFileMsgArray(mfH);
|
|
868
|
+
|
|
869
|
+ crn = cmCsvRowCount(csvH);
|
|
870
|
+
|
|
871
|
+ // for each row in the score file
|
|
872
|
+ for(ci=1,mi=0; ci<crn && cmCsvLastRC(csvH)==kOkCsvRC; ++ci)
|
|
873
|
+ {
|
|
874
|
+ unsigned id;
|
|
875
|
+
|
|
876
|
+ // zero the duration column
|
|
877
|
+ if( cmCsvCellPtr(csvH, ci, kDSecsColScIdx ) != NULL )
|
|
878
|
+ cmCsvSetCellUInt( csvH, ci, kDSecsColScIdx, 0 );
|
|
879
|
+
|
|
880
|
+ // get the MIDI file event id for this row
|
|
881
|
+ if((id = cmCsvCellUInt(csvH,ci,kMidiFileIdColScIdx)) == UINT_MAX)
|
|
882
|
+ {
|
|
883
|
+ // this is a hand-entered event - so it has no event id
|
|
884
|
+ ++handCnt;
|
|
885
|
+
|
|
886
|
+ }
|
|
887
|
+ else
|
|
888
|
+ {
|
|
889
|
+ for(; mi<mn; ++mi)
|
|
890
|
+ {
|
|
891
|
+ const cmMidiTrackMsg_t* m = msg[mi];
|
|
892
|
+
|
|
893
|
+ assert( mi+1 <= id );
|
|
894
|
+ secs += m->dtick/1000000.0;
|
|
895
|
+
|
|
896
|
+ if( mi+1 != id )
|
|
897
|
+ {
|
|
898
|
+ if( m->status == kNoteOnMdId && m->u.chMsgPtr->d1>0 )
|
|
899
|
+ {
|
|
900
|
+ // this MIDI note-on does not have a corresponding score event
|
|
901
|
+ ++midiMissCnt;
|
|
902
|
+ }
|
|
903
|
+ }
|
|
904
|
+ else
|
|
905
|
+ {
|
|
906
|
+ cmCsvSetCellDouble( csvH, ci, kSecsColScIdx, secs );
|
|
907
|
+ ++mi;
|
|
908
|
+
|
|
909
|
+ if( m->status == kNoteOnMdId )
|
|
910
|
+ cmCsvSetCellDouble( csvH, ci, kDSecsColScIdx, m->u.chMsgPtr->durTicks/1000000.0 );
|
|
911
|
+ break;
|
|
912
|
+ }
|
|
913
|
+
|
|
914
|
+
|
|
915
|
+ }
|
|
916
|
+
|
|
917
|
+ if( mi==mn)
|
|
918
|
+ printf("done on row:%i\n",ci);
|
|
919
|
+ }
|
|
920
|
+ }
|
|
921
|
+
|
|
922
|
+ if( cmCsvLastRC(csvH) != kOkCsvRC )
|
|
923
|
+ goto errLabel;
|
|
924
|
+
|
|
925
|
+ if( cmCsvWrite(csvH,cwfn) != kOkCsvRC )
|
|
926
|
+ goto errLabel;
|
|
927
|
+
|
|
928
|
+ errFl = false;
|
|
929
|
+
|
|
930
|
+ errLabel:
|
|
931
|
+ if( errFl )
|
|
932
|
+ printf("Score fix failed.\n");
|
|
933
|
+ else
|
|
934
|
+ printf("Score fix done! hand:%i miss:%i\n",handCnt,midiMissCnt);
|
|
935
|
+ cmMidiFileClose(&mfH);
|
|
936
|
+
|
|
937
|
+ cmCsvFinalize(&csvH);
|
|
938
|
+
|
|
939
|
+}
|