2016-02-04 16:33:42 +00:00
# include "cmPrefix.h"
# include "cmGlobal.h"
# include "cmFloatTypes.h"
2016-02-25 00:09:29 +00:00
# include "cmComplexTypes.h"
2016-02-04 16:33:42 +00:00
# include "cmRpt.h"
# include "cmErr.h"
# include "cmCtx.h"
# include "cmMem.h"
# include "cmMallocDebug.h"
# include "cmLinkedHeap.h"
# include "cmXml.h"
# include "cmText.h"
2016-07-14 23:08:13 +00:00
# include "cmFileSys.h"
2016-02-04 16:33:42 +00:00
# include "cmXScore.h"
2016-02-04 22:55:22 +00:00
# include "cmTime.h"
# include "cmMidi.h"
2016-02-19 00:10:05 +00:00
# include "cmMidiFile.h"
2016-02-09 02:46:07 +00:00
# include "cmLex.h"
# include "cmCsv.h"
2016-04-20 22:53:24 +00:00
# include "cmSymTbl.h"
# include "cmScore.h"
2016-02-04 16:33:42 +00:00
2016-07-14 23:08:13 +00:00
2016-02-25 00:09:29 +00:00
# include "cmFile.h"
# include "cmSymTbl.h"
# include "cmAudioFile.h"
# include "cmAudioFile.h"
# include "cmProcObj.h"
# include "cmProcTemplate.h"
# include "cmProc.h"
# include "cmProc2.h"
# include "cmProc5.h"
2016-07-14 23:08:13 +00:00
# include "cmSvgWriter.h"
2016-02-04 16:33:42 +00:00
cmXsH_t cmXsNullHandle = cmSTATIC_NULL_HANDLE ;
2016-02-04 22:55:22 +00:00
enum
{
2016-08-03 21:03:17 +00:00
kSectionXsFl = 0x0000001 , // rvalue holds section number
kBarXsFl = 0x0000002 ,
kRestXsFl = 0x0000004 ,
kGraceXsFl = 0x0000008 ,
kDotXsFl = 0x0000010 ,
kChordXsFl = 0x0000020 ,
kDynXsFl = 0x0000040 ,
kEvenXsFl = 0x0000080 ,
kTempoXsFl = 0x0000100 ,
kHeelXsFl = 0x0000200 ,
kTieBegXsFl = 0x0000400 ,
kTieEndXsFl = 0x0000800 ,
kTieProcXsFl = 0x0001000 ,
kDampDnXsFl = 0x0002000 ,
kDampUpXsFl = 0x0004000 ,
kDampUpDnXsFl = 0x0008000 ,
kSostDnXsFl = 0x0010000 ,
kSostUpXsFl = 0x0020000 ,
kMetronomeXsFl = 0x0040000 , // duration holds BPM
kOnsetXsFl = 0x0080000 , // this is a sounding note
kBegGroupXsFl = 0x0100000 ,
kEndGroupXsFl = 0x0200000 ,
kBegGraceXsFl = 0x0400000 , // beg grace note group
kEndGraceXsFl = 0x0800000 , // end grace note group
kAddGraceXsFl = 0x1000000 , // end grace note group operator flag - add time
kSubGraceXsFl = 0x2000000 , // " " " " " " - subtract time
kFirstGraceXsFl = 0x4000000 , // " " " " " " - sync to first note
2016-07-27 23:05:42 +00:00
2016-02-04 22:55:22 +00:00
} ;
2016-02-12 00:23:42 +00:00
struct cmXsMeas_str ;
struct cmXsVoice_str ;
2016-02-04 22:55:22 +00:00
2016-02-04 16:33:42 +00:00
typedef struct cmXsNote_str
{
2016-03-02 23:52:40 +00:00
unsigned uid ; // unique id of this note record
2016-04-07 17:39:24 +00:00
unsigned flags ; // See k???XsFl
2016-02-18 00:00:15 +00:00
unsigned pitch ; // midi pitch
2016-02-19 00:10:05 +00:00
unsigned velocity ; // midi velocity
2016-03-30 23:18:44 +00:00
unsigned dynamics ; // dynamic level 1=pppp 9=fff
2016-06-16 16:53:59 +00:00
unsigned vel ; // score specified MIDI velocity
2016-02-18 00:00:15 +00:00
cmChar_t step ; // A-G
unsigned octave ; // sci pitch octave
int alter ; // +n=sharps,-n=flats
2016-02-25 00:09:29 +00:00
unsigned staff ; // 1=treble 2=bass
2016-04-07 17:39:24 +00:00
unsigned tick ; //
2016-02-18 00:00:15 +00:00
unsigned duration ; // duration in ticks
2016-03-31 23:07:48 +00:00
double secs ; // absolute time in seconds
double dsecs ; // delta time in seconds since previous event
unsigned locIdx ; // location index (chords share the same location index)
2016-02-18 00:00:15 +00:00
double rvalue ; // 1/rvalue = rythmic value (1/0.5 double whole 1/1 whole 1/2 half 1/4=quarter note, 1/8=eighth note, ...)
const cmChar_t * tvalue ; // text value
2016-02-12 00:23:42 +00:00
2016-03-31 23:07:48 +00:00
unsigned evenGroupId ; // eveness group id
unsigned dynGroupId ; // dynamics group id
unsigned tempoGroupId ; // tempo group id
2016-07-27 23:05:42 +00:00
unsigned graceGroupId ; // grace note group id
2016-03-31 23:07:48 +00:00
2016-04-07 17:39:24 +00:00
struct cmXsVoice_str * voice ; // voice this note belongs to
2016-02-19 00:10:05 +00:00
struct cmXsMeas_str * meas ; // measure this note belongs to
2016-02-12 00:23:42 +00:00
2016-02-18 00:00:15 +00:00
const cmXmlNode_t * xmlNode ; // note xml ptr
2016-04-07 17:39:24 +00:00
2016-07-14 23:08:13 +00:00
struct cmXsNote_str * tied ; // subsequent note tied to this note
2016-07-27 23:05:42 +00:00
struct cmXsNote_str * grace ; // grace note groups link backward in time from the anchor note
2016-04-07 17:39:24 +00:00
struct cmXsNote_str * mlink ; // measure note list
2016-02-18 00:00:15 +00:00
struct cmXsNote_str * slink ; // time sorted event list
2016-04-07 17:39:24 +00:00
2016-02-04 16:33:42 +00:00
} cmXsNote_t ;
typedef struct cmXsVoice_str
{
unsigned id ; // Voice id
2016-04-07 17:39:24 +00:00
cmXsNote_t * noteL ; // List of notes in this voice
2016-02-04 16:33:42 +00:00
struct cmXsVoice_str * link ; // Link to other voices in this measure
} cmXsVoice_t ;
2016-02-19 00:10:05 +00:00
typedef struct cmXsSpan_str
{
2016-07-14 23:08:13 +00:00
unsigned staff ;
2016-02-19 00:10:05 +00:00
unsigned number ;
struct cmXsMeas_str * meas ;
unsigned tick0 ;
unsigned tick1 ;
int pitch_offset ;
struct cmXsSpan_str * link ;
} cmXsSpan_t ;
2016-02-04 16:33:42 +00:00
typedef struct cmXsMeas_str
{
2016-02-09 02:46:07 +00:00
unsigned number ; // Measure number
unsigned divisions ; // ticks-per-quarter-note
unsigned beats ; // beats per measure
unsigned beat_type ; // whole/half/quarter/eighth ...
2016-04-07 17:39:24 +00:00
cmXsVoice_t * voiceL ; // List of voices in this measure
2016-02-09 02:46:07 +00:00
cmXsNote_t * noteL ; // List of time sorted notes in this measure
2016-04-07 17:39:24 +00:00
2016-02-04 16:33:42 +00:00
struct cmXsMeas_str * link ; // Link to other measures in this part.
} cmXsMeas_t ;
typedef struct cmXsPart_str
{
const cmChar_t * idStr ; // Id of this part
cmXsMeas_t * measL ; // List of measures in this part.
struct cmXsPart_str * link ; // Link to other parts in this score
} cmXsPart_t ;
typedef struct
{
2016-02-09 02:46:07 +00:00
cmErr_t err ;
cmXmlH_t xmlH ;
cmLHeapH_t lhH ;
cmXsPart_t * partL ;
2016-02-12 00:23:42 +00:00
cmCsvH_t csvH ;
2016-04-07 17:39:24 +00:00
2016-02-19 00:10:05 +00:00
cmXsSpan_t * spanL ;
2016-03-02 23:52:40 +00:00
unsigned nextUid ;
2016-02-04 16:33:42 +00:00
} cmXScore_t ;
cmXScore_t * _cmXScoreHandleToPtr ( cmXsH_t h )
{
cmXScore_t * p = ( cmXScore_t * ) h . h ;
assert ( p ! = NULL ) ;
return p ;
}
cmXsRC_t _cmXScoreFinalize ( cmXScore_t * p )
{
cmXsRC_t rc = kOkXsRC ;
// release the XML file
2016-02-09 02:46:07 +00:00
cmXmlFree ( & p - > xmlH ) ;
2016-02-04 16:33:42 +00:00
// release the local linked heap memory
2016-02-09 02:46:07 +00:00
cmLHeapDestroy ( & p - > lhH ) ;
// release the CSV output object
cmCsvFinalize ( & p - > csvH ) ;
2016-04-07 17:39:24 +00:00
2016-02-04 16:33:42 +00:00
cmMemFree ( p ) ;
2016-04-07 17:39:24 +00:00
2016-02-04 16:33:42 +00:00
return rc ;
}
2016-02-07 04:36:53 +00:00
cmXsRC_t _cmXScoreMissingNode ( cmXScore_t * p , const cmXmlNode_t * parent , const cmChar_t * label )
2016-02-04 16:33:42 +00:00
{
2016-02-07 04:36:53 +00:00
return cmErrMsg ( & p - > err , kSyntaxErrorXsRC , " Missing XML node '%s'. Parent line:%i " , label , parent - > line ) ;
2016-02-04 16:33:42 +00:00
}
cmXsRC_t _cmXScoreMissingAttribute ( cmXScore_t * p , const cmXmlNode_t * np , const cmChar_t * attrLabel )
{
return cmErrMsg ( & p - > err , kSyntaxErrorXsRC , " Missing XML attribute '%s' from node '%s'. " , attrLabel , np - > label ) ;
}
2016-02-07 04:36:53 +00:00
cmXsVoice_t * _cmXScoreIdToVoice ( cmXsMeas_t * meas , unsigned voiceId )
{
cmXsVoice_t * v = meas - > voiceL ;
for ( ; v ! = NULL ; v = v - > link )
if ( v - > id = = voiceId )
return v ;
return NULL ;
}
cmXsRC_t _cmXScorePushNote ( cmXScore_t * p , cmXsMeas_t * meas , unsigned voiceId , cmXsNote_t * note )
{
cmXsVoice_t * v ;
// get the voice recd
if ( ( v = _cmXScoreIdToVoice ( meas , voiceId ) ) = = NULL )
{
// the voice recd doesn't exist for this voiceId - allocate one
v = cmLhAllocZ ( p - > lhH , cmXsVoice_t , 1 ) ;
v - > id = voiceId ;
// add the voice record to the meas->voiceL
if ( meas - > voiceL = = NULL )
meas - > voiceL = v ;
else
{
cmXsVoice_t * vp = meas - > voiceL ;
while ( vp - > link ! = NULL )
vp = vp - > link ;
2016-04-07 17:39:24 +00:00
vp - > link = v ;
2016-02-07 04:36:53 +00:00
}
}
// add the note record to the end of meas->voiceL
if ( v - > noteL = = NULL )
v - > noteL = note ;
else
{
cmXsNote_t * n = v - > noteL ;
while ( n - > mlink ! = NULL )
n = n - > mlink ;
n - > mlink = note ;
}
2016-02-12 00:23:42 +00:00
note - > voice = v ;
2016-03-02 23:52:40 +00:00
note - > uid = p - > nextUid + + ;
2016-02-12 00:23:42 +00:00
2016-02-07 04:36:53 +00:00
return kOkXsRC ;
}
2016-06-16 22:04:28 +00:00
void _cmXScoreInsertNoteBefore ( cmXsNote_t * note , cmXsNote_t * nn )
{
// insert the new note into the voice list before 'note'
cmXsNote_t * n0 = NULL ;
cmXsNote_t * n1 = note - > voice - > noteL ;
for ( ; n1 ! = NULL ; n1 = n1 - > mlink )
{
if ( n1 - > uid = = note - > uid )
{
if ( n0 = = NULL )
note - > voice - > noteL = nn ;
else
n0 - > mlink = nn ;
nn - > mlink = note ;
break ;
}
n0 = n1 ;
}
assert ( n1 ! = NULL ) ;
// insert the new note into the time sorted note list before 'note'
n0 = NULL ;
n1 = note - > meas - > noteL ;
for ( ; n1 ! = NULL ; n1 = n1 - > slink )
{
if ( n1 - > tick > = nn - > tick )
{
if ( n0 = = NULL )
note - > meas - > noteL = nn ;
else
n0 - > slink = nn ;
nn - > slink = n1 ;
break ;
}
n0 = n1 ;
}
assert ( n1 ! = NULL ) ;
}
void _cmXScoreInsertNoteAfter ( cmXsNote_t * note , cmXsNote_t * nn )
{
// insert the new note into the voice list after 'note'
cmXsNote_t * n0 = note - > voice - > noteL ;
for ( ; n0 ! = NULL ; n0 = n0 - > mlink )
if ( n0 - > uid = = note - > uid )
{
nn - > mlink = note - > mlink ;
note - > mlink = nn ;
break ;
}
assert ( n0 ! = NULL ) ;
// insert the new note into the time sorted note list after 'note'
n0 = note - > meas - > noteL ;
for ( ; n0 ! = NULL ; n0 = n0 - > slink )
{
if ( n0 - > tick > = nn - > tick )
{
nn - > slink = n0 - > slink ;
n0 - > slink = nn ;
break ;
}
// if n0 is the last ele in the list
if ( n0 - > slink = = NULL )
{
n0 - > slink = nn ;
nn - > slink = NULL ;
break ;
}
}
assert ( n0 ! = NULL ) ;
}
2016-02-04 16:33:42 +00:00
cmXsRC_t _cmXScoreParsePartList ( cmXScore_t * p )
{
cmXsRC_t rc = kOkXsRC ;
cmXsPart_t * lastPartPtr = NULL ;
const cmXmlNode_t * xnp ;
// find the 'part-list'
if ( ( xnp = cmXmlSearch ( cmXmlRoot ( p - > xmlH ) , " part-list " , NULL , 0 ) ) = = NULL )
2016-02-07 04:36:53 +00:00
return _cmXScoreMissingNode ( p , cmXmlRoot ( p - > xmlH ) , " part-list " ) ;
2016-04-07 17:39:24 +00:00
2016-02-04 16:33:42 +00:00
const cmXmlNode_t * cnp = xnp - > children ;
// for each child of the 'part-list'
for ( ; cnp ! = NULL ; cnp = cnp - > sibling )
if ( cmTextCmp ( cnp - > label , " score-part " ) = = 0 )
{
const cmXmlAttr_t * a ;
// find the 'score-part' id
if ( ( a = cmXmlFindAttrib ( cnp , " id " ) ) = = NULL )
return _cmXScoreMissingAttribute ( p , cnp , " id " ) ;
// allocate a new part record
cmXsPart_t * pp = cmLhAllocZ ( p - > lhH , cmXsPart_t , 1 ) ;
pp - > idStr = a - > value ; // set the part id
// link the new part record to the end of the part list
if ( lastPartPtr = = NULL )
p - > partL = pp ;
else
lastPartPtr - > link = pp ;
2016-04-07 17:39:24 +00:00
lastPartPtr = pp ;
2016-02-04 16:33:42 +00:00
}
return rc ;
}
2016-02-18 00:00:15 +00:00
cmXsRC_t _cmXScoreParsePitch ( cmXScore_t * p , const cmXmlNode_t * nnp , cmXsNote_t * np )
2016-02-04 22:55:22 +00:00
{
cmXsRC_t rc = kOkXsRC ;
unsigned octave = 0 ;
double alter = 0 ;
const cmChar_t * step = NULL ;
if ( ( step = cmXmlNodeValue ( nnp , " pitch " , " step " , NULL ) ) = = NULL )
2016-02-07 04:36:53 +00:00
return _cmXScoreMissingNode ( p , nnp , " step " ) ;
2016-04-07 17:39:24 +00:00
2016-02-04 22:55:22 +00:00
if ( ( rc = cmXmlNodeUInt ( nnp , & octave , " pitch " , " octave " , NULL ) ) ! = kOkXmlRC )
2016-02-07 04:36:53 +00:00
return _cmXScoreMissingNode ( p , nnp , " octave " ) ;
2016-04-07 17:39:24 +00:00
2016-02-04 22:55:22 +00:00
cmXmlNodeDouble ( nnp , & alter , " pitch " , " alter " , NULL ) ;
2016-07-14 23:08:13 +00:00
int acc = alter ;
unsigned midi = cmSciPitchToMidiPitch ( * step , acc , octave ) ;
2016-02-04 22:55:22 +00:00
2016-02-26 00:10:54 +00:00
np - > pitch = midi ;
np - > step = * step ;
2016-02-18 00:00:15 +00:00
np - > octave = octave ;
np - > alter = alter ;
2016-02-19 00:10:05 +00:00
np - > flags | = kOnsetXsFl ;
2016-02-04 22:55:22 +00:00
2016-04-07 17:39:24 +00:00
return rc ;
2016-02-04 22:55:22 +00:00
}
2016-02-11 00:03:17 +00:00
cmXsRC_t _cmXScoreParseNoteRValue ( cmXScore_t * p , const cmXmlNode_t * nnp , const cmChar_t * label , double * rvalueRef )
2016-02-04 22:55:22 +00:00
{
typedef struct map_str
{
2016-02-11 00:03:17 +00:00
double rvalue ;
2016-02-04 22:55:22 +00:00
const cmChar_t * label ;
} map_t ;
map_t mapV [ ] =
{
2016-02-11 00:03:17 +00:00
{ - 1.0 , " measure " } , // whole measure rest
{ 0.5 , " breve " } , // double whole
{ 1.0 , " whole " } ,
{ 2.0 , " half " } ,
{ 4.0 , " quarter " } ,
{ 8.0 , " eighth " } ,
{ 16.0 , " 16th " } ,
{ 32.0 , " 32nd " } ,
{ 64.0 , " 64th " } ,
{ 128.0 , " 128th " } ,
{ 0.0 , " " }
2016-02-04 22:55:22 +00:00
} ;
2016-02-07 04:36:53 +00:00
const cmChar_t * str ;
2016-02-11 00:03:17 +00:00
// get the XML rvalue label
2016-02-07 04:36:53 +00:00
if ( ( str = cmXmlNodeValue ( nnp , label , NULL ) ) = = NULL )
2016-02-09 02:46:07 +00:00
{
if ( ( nnp = cmXmlSearch ( nnp , " rest " , NULL , 0 ) ) ! = NULL )
{
const cmXmlAttr_t * a ;
if ( ( a = cmXmlFindAttrib ( nnp , " measure " ) ) ! = NULL & & cmTextCmp ( a - > value , " yes " ) = = 0 )
2016-02-11 00:03:17 +00:00
{
* rvalueRef = - 1 ;
return kOkXsRC ;
}
2016-02-09 02:46:07 +00:00
}
2016-02-11 00:03:17 +00:00
return cmErrMsg ( & p - > err , kSyntaxErrorXsRC , " The 'beat-unit' metronome value is missing on line %i. " , nnp - > line ) ;
2016-02-09 02:46:07 +00:00
}
2016-04-07 17:39:24 +00:00
2016-02-07 04:36:53 +00:00
unsigned i ;
2016-02-11 00:03:17 +00:00
// lookup the rvalue numeric value from the mapV[] table
2016-02-07 04:36:53 +00:00
for ( i = 0 ; mapV [ i ] . rvalue ! = 0 ; + + i )
if ( cmTextCmp ( mapV [ i ] . label , str ) = = 0 )
2016-02-11 00:03:17 +00:00
{
* rvalueRef = mapV [ i ] . rvalue ;
return kOkXsRC ;
}
2016-04-07 17:39:24 +00:00
2016-02-11 00:03:17 +00:00
// the rvalue label was not found
return cmErrMsg ( & p - > err , kSyntaxErrorXsRC , " Unknown rvalue type='%s'. " , str ) ;
2016-02-04 22:55:22 +00:00
}
2016-02-07 04:36:53 +00:00
cmXsRC_t _cmXScoreParseColor ( cmXScore_t * p , const cmXmlNode_t * nnp , cmXsNote_t * note )
2016-02-04 22:55:22 +00:00
{
2016-02-07 04:36:53 +00:00
cmXsRC_t rc = kOkXsRC ;
const cmXmlAttr_t * a ;
2016-02-04 16:33:42 +00:00
2016-02-07 04:36:53 +00:00
typedef struct map_str
2016-02-04 22:55:22 +00:00
{
2016-02-07 04:36:53 +00:00
unsigned value ;
const cmChar_t * label ;
} map_t ;
2016-02-04 22:55:22 +00:00
2016-02-07 04:36:53 +00:00
map_t mapV [ ] =
2016-02-04 22:55:22 +00:00
{
2016-02-11 00:03:17 +00:00
{ kEvenXsFl , " #0000FF " } , // blue (even)
{ kTempoXsFl , " #00FF00 " } , // green (tempo)
{ kDynXsFl , " #FF0000 " } , // red (dynamics)
{ kTempoXsFl | kEvenXsFl , " #00FFFF " } , // green + blue (turquoise)
2016-04-07 17:39:24 +00:00
{ kDynXsFl | kEvenXsFl , " #FF00FF " } , // red + blue
2016-02-11 00:03:17 +00:00
{ kDynXsFl | kEvenXsFl , " #FF0CF7 " } , // magenta (even+dyn)
{ kDynXsFl | kTempoXsFl , " #FF7F00 " } , // red + green (brown)
{ kTempoXsFl | kEvenXsFl | kDynXsFl , " #996633 " } , // (purple)
{ kDynXsFl , " #FF6A03 " } , // 176 orange (dynamics)
2016-04-07 17:39:24 +00:00
{ kEvenXsFl , " #2F00E8 " } , // 1001 blue (even)
2016-02-11 00:03:17 +00:00
{ kTempoXsFl , " #01CD1F " } , // 1196 green (tempo)
{ kEvenXsFl , " #3600E8 " } , // 1627 blue (even)
{ kDynXsFl | kTempoXsFl , " #9E8F15 " } , // 8827 brown (dyn + tempo)
{ kEvenXsFl , " #2E00E6 " } , // 5393 blue (even)
{ kEvenXsFl , " #2C00DD " } , // 5895 blue (even)
{ kDynXsFl , " #FF5B03 " } , // 6498 orange (dyn)
{ kDynXsFl , " #FF6104 " } , // 6896 orange
{ kEvenXsFl , " #2A00E6 " } , // 7781 blue
2016-04-07 17:39:24 +00:00
{ kEvenXsFl , " #2300DD " } , // 8300 blue (even)
2016-02-11 00:03:17 +00:00
{ kTempoXsFl , " #03CD22 " } , // 10820 green (tempo)
{ kEvenXsFl , " #3400DB " } , // 11627 blue (dyn)
{ - 1 , " " }
2016-02-07 04:36:53 +00:00
} ;
2016-02-11 00:03:17 +00:00
/*
orange # FF6A03
magenta # FF0CF7
blue # 2F 00E8
green # 01 CD1F
gold # 9E8 F15
green # 03 CD22
*/
2016-04-07 17:39:24 +00:00
2016-02-07 04:36:53 +00:00
if ( ( a = cmXmlFindAttrib ( nnp , " color " ) ) ! = NULL )
{
unsigned i ;
2016-02-11 00:03:17 +00:00
for ( i = 0 ; mapV [ i ] . value ! = - 1 ; + + i )
2016-02-07 04:36:53 +00:00
if ( cmTextCmp ( a - > value , mapV [ i ] . label ) = = 0 )
{
note - > flags + = mapV [ i ] . value ;
break ;
}
2016-02-04 22:55:22 +00:00
2016-02-11 00:03:17 +00:00
if ( mapV [ i ] . value = = - 1 )
cmErrMsg ( & p - > err , kSyntaxErrorXsRC , " The note color '%s' was not found on line %i. " , a - > value , nnp - > line ) ;
2016-02-04 22:55:22 +00:00
}
2016-02-07 04:36:53 +00:00
return rc ;
2016-02-04 22:55:22 +00:00
}
2016-03-30 15:47:41 +00:00
// On input tick0Ref is set to the tick of the previous event.
// On input tickRef is set to the tick of this event.
// On output tick0Ref is set to the tick of this event.
// On output tickRef is set to the tick of the next event.
2016-02-18 00:00:15 +00:00
cmXsRC_t _cmXScoreParseNote ( cmXScore_t * p , cmXsMeas_t * meas , const cmXmlNode_t * nnp , unsigned * tick0Ref , unsigned * tickRef )
2016-02-04 22:55:22 +00:00
{
cmXsRC_t rc = kOkXsRC ;
cmXsNote_t * note = cmLhAllocZ ( p - > lhH , cmXsNote_t , 1 ) ;
2016-02-07 04:36:53 +00:00
unsigned voiceId ;
2016-02-04 22:55:22 +00:00
2016-02-12 00:23:42 +00:00
note - > meas = meas ;
note - > xmlNode = nnp ;
2016-02-04 22:55:22 +00:00
// get the voice id for this node
if ( cmXmlNodeUInt ( nnp , & voiceId , " voice " , NULL ) ! = kOkXmlRC )
2016-02-07 04:36:53 +00:00
return _cmXScoreMissingNode ( p , nnp , " voice " ) ;
2016-02-04 22:55:22 +00:00
// if this note has a pitch
2016-02-07 04:36:53 +00:00
if ( cmXmlNodeHasChild ( nnp , " pitch " , NULL ) )
2016-02-18 00:00:15 +00:00
if ( ( rc = _cmXScoreParsePitch ( p , nnp , note ) ) ! = kOkXsRC )
2016-02-04 22:55:22 +00:00
return rc ;
2016-04-07 17:39:24 +00:00
2016-02-19 00:10:05 +00:00
cmXmlNodeUInt ( nnp , & note - > duration , " duration " , NULL ) ; // get the note duration
cmXmlNodeUInt ( nnp , & note - > staff , " staff " , NULL ) ; // get th staff number
2016-04-07 17:39:24 +00:00
2016-02-04 22:55:22 +00:00
// is 'rest'
2016-02-07 04:36:53 +00:00
if ( cmXmlNodeHasChild ( nnp , " rest " , NULL ) )
2016-02-04 22:55:22 +00:00
note - > flags | = kRestXsFl ;
// is 'grace'
2016-02-07 04:36:53 +00:00
if ( cmXmlNodeHasChild ( nnp , " grace " , NULL ) )
2016-02-04 22:55:22 +00:00
note - > flags | = kGraceXsFl ;
// is 'dot'
2016-02-07 04:36:53 +00:00
if ( cmXmlNodeHasChild ( nnp , " dot " , NULL ) )
2016-02-04 22:55:22 +00:00
note - > flags | = kDotXsFl ;
2016-02-07 04:36:53 +00:00
// is 'chord'
if ( cmXmlNodeHasChild ( nnp , " chord " , NULL ) )
note - > flags | = kChordXsFl ;
2016-02-18 00:00:15 +00:00
// is this is first note in a tied pair
if ( cmXmlNodeHasChildWithAttrAndValue ( nnp , " tie " , " type " , " start " , NULL ) )
note - > flags | = kTieBegXsFl ;
2016-04-07 17:39:24 +00:00
2016-02-18 00:00:15 +00:00
// is this is second note in a tied pair
if ( cmXmlNodeHasChildWithAttrAndValue ( nnp , " tie " , " type " , " stop " , NULL ) )
note - > flags | = kTieEndXsFl ;
2016-04-07 17:39:24 +00:00
2016-02-11 00:03:17 +00:00
// has 'heel' mark
if ( cmXmlNodeHasChild ( nnp , " notations " , " technical " , " heel " , NULL ) )
note - > flags | = kHeelXsFl ;
2016-04-07 17:39:24 +00:00
2016-02-07 04:36:53 +00:00
// set color coded flags
if ( ( rc = _cmXScoreParseColor ( p , nnp , note ) ) ! = kOkXsRC )
return rc ;
2016-04-07 17:39:24 +00:00
2016-02-07 04:36:53 +00:00
// get the note's rythmic value
2016-02-11 00:03:17 +00:00
if ( ( rc = _cmXScoreParseNoteRValue ( p , nnp , " type " , & note - > rvalue ) ) ! = kOkXsRC )
return rc ;
2016-02-07 04:36:53 +00:00
2016-04-07 17:39:24 +00:00
// if this is a chord note
2016-02-18 00:00:15 +00:00
if ( cmIsFlag ( note - > flags , kChordXsFl ) )
{
note - > tick = * tick0Ref ; // then use the onset time from the previous note and do not advance time
}
else
{
* tick0Ref = * tickRef ;
note - > tick = * tickRef ;
2016-02-07 04:36:53 +00:00
* tickRef + = note - > duration ;
2016-02-18 00:00:15 +00:00
}
2016-04-07 17:39:24 +00:00
2016-02-07 04:36:53 +00:00
return _cmXScorePushNote ( p , meas , voiceId , note ) ;
}
2016-02-12 00:23:42 +00:00
cmXsRC_t _cmXScorePushNonNote ( cmXScore_t * p , cmXsMeas_t * meas , const cmXmlNode_t * noteXmlNode , unsigned tick , unsigned duration , double rvalue , const cmChar_t * tvalue , unsigned flags )
2016-02-07 04:36:53 +00:00
{
cmXsNote_t * note = cmLhAllocZ ( p - > lhH , cmXsNote_t , 1 ) ;
unsigned voiceId = 0 ; // non-note's are always assigned to voiceId=0;
2016-04-07 17:39:24 +00:00
2016-02-07 04:36:53 +00:00
note - > tick = tick ;
note - > flags = flags ;
note - > rvalue = rvalue ;
2016-02-09 02:46:07 +00:00
note - > tvalue = tvalue ;
2016-02-07 04:36:53 +00:00
note - > duration = duration ;
2016-02-12 00:23:42 +00:00
note - > meas = meas ;
note - > xmlNode = noteXmlNode ;
2016-04-07 17:39:24 +00:00
2016-02-07 04:36:53 +00:00
return _cmXScorePushNote ( p , meas , voiceId , note ) ;
}
2016-02-19 00:10:05 +00:00
cmXsSpan_t * _cmXScoreFindOpenOctaveShift ( cmXScore_t * p , unsigned staff , unsigned number )
{
cmXsSpan_t * s = p - > spanL ;
for ( ; s ! = NULL ; s = s - > link )
if ( s - > tick1 = = - 1 & & s - > staff = = staff & & s - > number = = number )
return s ;
return NULL ;
}
cmXsRC_t _cmXScorePushOctaveShift ( cmXScore_t * p , cmXsMeas_t * meas , unsigned staff , unsigned span_number , const cmChar_t * type_str , unsigned tick )
{
assert ( meas ! = NULL ) ;
2016-04-07 17:39:24 +00:00
2016-02-19 00:10:05 +00:00
cmXsSpan_t * s ;
if ( cmTextCmp ( type_str , " stop " ) = = 0 )
{
if ( ( s = _cmXScoreFindOpenOctaveShift ( p , staff , span_number ) ) = = NULL )
return cmErrWarnMsg ( & p - > err , kUnterminatedOctaveShiftXsrRC , " An illegal octave shift was encounted in meas %i. \n " , meas - > number ) ;
2016-04-07 17:39:24 +00:00
2016-02-19 00:10:05 +00:00
s - > tick1 = tick ;
}
else
{
2016-03-31 23:07:48 +00:00
s = cmLhAllocZ ( p - > lhH , cmXsSpan_t , 1 ) ;
s - > staff = staff ;
s - > meas = meas ;
s - > number = span_number ;
s - > tick0 = tick ;
s - > tick1 = - 1 ;
2016-02-19 00:10:05 +00:00
s - > pitch_offset = cmTextCmp ( type_str , " up " ) = = 0 ? - 12 : 12 ;
2016-03-31 23:07:48 +00:00
s - > link = p - > spanL ;
2016-04-07 17:39:24 +00:00
p - > spanL = s ;
2016-02-19 00:10:05 +00:00
}
return kOkXsRC ;
}
2016-02-07 04:36:53 +00:00
cmXsRC_t _cmXScoreParseDirection ( cmXScore_t * p , cmXsMeas_t * meas , const cmXmlNode_t * dnp , unsigned tick )
{
2016-02-11 00:03:17 +00:00
cmXsRC_t rc = kOkXsRC ;
const cmXmlNode_t * np = NULL ;
const cmXmlAttr_t * a = NULL ;
2016-02-07 04:36:53 +00:00
unsigned flags = 0 ;
int offset = 0 ;
2016-02-11 00:03:17 +00:00
double rvalue = 0 ;
2016-02-09 02:46:07 +00:00
const cmChar_t * tvalue = NULL ;
2016-02-07 04:36:53 +00:00
unsigned duration = 0 ;
2016-02-18 00:00:15 +00:00
bool pushFl = true ;
2016-02-19 00:10:05 +00:00
unsigned staff = 0 ;
2016-04-07 17:39:24 +00:00
2016-02-19 00:10:05 +00:00
cmXmlNodeInt ( dnp , & offset , " offset " , NULL ) ;
cmXmlNodeUInt ( dnp , & staff , " staff " , NULL ) ;
2016-04-07 17:39:24 +00:00
2016-02-07 04:36:53 +00:00
// if this is a metronome direction
if ( ( np = cmXmlSearch ( dnp , " metronome " , NULL , 0 ) ) ! = NULL )
{
2016-04-07 17:39:24 +00:00
2016-02-07 04:36:53 +00:00
if ( cmXmlNodeUInt ( np , & duration , " per-minute " , NULL ) ! = kOkXmlRC )
return cmErrMsg ( & p - > err , kSyntaxErrorXsRC , " The 'per-minute' metronome value is missing on line %i. " , np - > line ) ;
2016-02-11 00:03:17 +00:00
if ( ( rc = _cmXScoreParseNoteRValue ( p , np , " beat-unit " , & rvalue ) ) ! = kOkXsRC )
return rc ;
2016-04-07 17:39:24 +00:00
2016-02-07 04:36:53 +00:00
flags = kMetronomeXsFl ;
}
else
// if this is a pedal direction
if ( ( np = cmXmlSearch ( dnp , " pedal " , NULL , 0 ) ) ! = NULL )
{
2016-04-07 17:39:24 +00:00
2016-02-07 04:36:53 +00:00
if ( ( a = cmXmlFindAttrib ( np , " type " ) ) = = NULL )
return _cmXScoreMissingAttribute ( p , np , " type " ) ;
if ( cmTextCmp ( a - > value , " start " ) = = 0 )
2016-07-20 23:16:00 +00:00
flags = kDampDnXsFl ;
2016-02-07 04:36:53 +00:00
else
if ( cmTextCmp ( a - > value , " change " ) = = 0 )
2016-07-20 23:16:00 +00:00
flags = kDampUpDnXsFl ;
2016-02-07 04:36:53 +00:00
else
if ( cmTextCmp ( a - > value , " stop " ) = = 0 )
2016-07-20 23:16:00 +00:00
flags = kDampUpXsFl ;
2016-02-07 04:36:53 +00:00
else
return cmErrMsg ( & p - > err , kSyntaxErrorXsRC , " Unrecognized pedal type:'%s'. " , cmStringNullGuard ( a - > value ) ) ;
}
else
2016-04-07 17:39:24 +00:00
2016-02-07 04:36:53 +00:00
// if this is a 'words' direction
if ( ( np = cmXmlSearch ( dnp , " words " , NULL , 0 ) ) ! = NULL )
{
if ( ( a = cmXmlFindAttrib ( np , " enclosure " ) ) ! = NULL & & cmTextCmp ( a - > value , " rectangle " ) = = 0 )
{
2016-02-09 02:46:07 +00:00
if ( cmTextIsEmpty ( tvalue = np - > dataStr ) )
return cmErrMsg ( & p - > err , kSyntaxErrorXsRC , " Section number is blank or missing on line %i. " , np - > line ) ;
2016-02-07 04:36:53 +00:00
flags = kSectionXsFl ;
}
}
2016-02-18 00:00:15 +00:00
else
2016-02-19 00:10:05 +00:00
2016-04-07 17:39:24 +00:00
// if this is an 'octave-shift' direction
2016-02-19 00:10:05 +00:00
if ( ( np = cmXmlSearch ( dnp , " octave-shift " , NULL , 0 ) ) ! = NULL )
{
unsigned span_number = - 1 ;
if ( cmXmlAttrUInt ( np , " number " , & span_number ) ! = kOkXmlRC )
return cmErrMsg ( & p - > err , kSyntaxErrorXsRC , " Octave-shift is missing a 'number' attribute. " ) ;
2016-04-07 17:39:24 +00:00
2016-02-19 00:10:05 +00:00
if ( ( a = cmXmlFindAttrib ( np , " type " ) ) = = NULL )
return cmErrMsg ( & p - > err , kSyntaxErrorXsRC , " Octave-shift is missing a 'type' attribute. " ) ;
2016-04-07 17:39:24 +00:00
2016-02-19 00:10:05 +00:00
rc = _cmXScorePushOctaveShift ( p , meas , staff , span_number , a - > value , tick + offset ) ;
pushFl = false ;
}
else
2016-02-18 00:00:15 +00:00
{
pushFl = false ;
}
2016-02-07 04:36:53 +00:00
2016-02-18 00:00:15 +00:00
if ( pushFl )
2016-04-07 17:39:24 +00:00
rc = _cmXScorePushNonNote ( p , meas , dnp , tick + offset , duration , rvalue , tvalue , flags ) ;
2016-02-18 00:00:15 +00:00
return rc ;
2016-02-04 22:55:22 +00:00
}
2016-02-07 04:36:53 +00:00
2016-03-30 15:47:41 +00:00
// On input tickRef is set to the absolute tick of the bar line and on output it is set
// to the absolute tick of the next bar line.
cmXsRC_t _cmXScoreParseMeasure ( cmXScore_t * p , cmXsPart_t * pp , const cmXmlNode_t * mnp , unsigned * tickRef )
2016-02-04 16:33:42 +00:00
{
2016-02-07 04:36:53 +00:00
cmXsRC_t rc = kOkXsRC ;
2016-04-07 17:39:24 +00:00
const cmXmlNode_t * np = NULL ;
2016-03-30 15:47:41 +00:00
unsigned tick = * tickRef ;
2016-02-18 00:00:15 +00:00
unsigned tick0 = 0 ;
2016-02-19 00:10:05 +00:00
cmXsMeas_t * m = NULL ;
2016-02-04 16:33:42 +00:00
2016-02-04 22:55:22 +00:00
// allocate the 'measure' record
2016-02-04 16:33:42 +00:00
cmXsMeas_t * meas = cmLhAllocZ ( p - > lhH , cmXsMeas_t , 1 ) ;
// get measure number
if ( cmXmlAttrUInt ( mnp , " number " , & meas - > number ) ! = kOkXmlRC )
return _cmXScoreMissingAttribute ( p , mnp , " number " ) ;
if ( pp - > measL = = NULL )
pp - > measL = meas ;
else
{
2016-02-19 00:10:05 +00:00
m = pp - > measL ;
2016-02-04 16:33:42 +00:00
while ( m - > link ! = NULL )
m = m - > link ;
2016-04-07 17:39:24 +00:00
2016-02-19 00:10:05 +00:00
m - > link = meas ;
meas - > divisions = m - > divisions ;
meas - > beats = m - > beats ;
meas - > beat_type = m - > beat_type ;
2016-02-04 16:33:42 +00:00
}
2016-04-07 17:39:24 +00:00
2016-02-04 16:33:42 +00:00
// get measure attributes node
2016-02-09 02:46:07 +00:00
if ( ( np = cmXmlSearch ( mnp , " attributes " , NULL , 0 ) ) ! = NULL )
{
cmXmlNodeUInt ( np , & meas - > divisions , " divisions " , NULL ) ;
cmXmlNodeUInt ( np , & meas - > beats , " time " , " beats " , NULL ) ;
cmXmlNodeUInt ( np , & meas - > beat_type , " time " , " beat-type " , NULL ) ;
}
2016-04-07 17:39:24 +00:00
2016-02-07 04:36:53 +00:00
// store the bar line
2016-02-12 00:23:42 +00:00
if ( ( rc = _cmXScorePushNonNote ( p , meas , mnp , tick , 0 , 0 , NULL , kBarXsFl ) ) ! = kOkXsRC )
2016-02-07 04:36:53 +00:00
return rc ;
2016-02-04 22:55:22 +00:00
np = mnp - > children ;
2016-02-07 04:36:53 +00:00
// for each child of the 'meas' XML node
for ( ; rc = = kOkXsRC & & np ! = NULL ; np = np - > sibling )
{
// if this is a 'note' node
if ( cmTextCmp ( np - > label , " note " ) = = 0 )
2016-02-04 22:55:22 +00:00
{
2016-02-18 00:00:15 +00:00
rc = _cmXScoreParseNote ( p , meas , np , & tick0 , & tick ) ;
2016-02-04 22:55:22 +00:00
}
else
2016-02-07 04:36:53 +00:00
// if this is a 'backup' node
if ( cmTextCmp ( np - > label , " backup " ) = = 0 )
2016-02-04 22:55:22 +00:00
{
2016-02-07 04:36:53 +00:00
unsigned backup ;
cmXmlNodeUInt ( np , & backup , " duration " , NULL ) ;
2016-02-19 00:10:05 +00:00
if ( backup > tick )
tick = 0 ;
else
tick - = backup ;
2016-04-07 17:39:24 +00:00
2016-02-18 00:00:15 +00:00
tick0 = tick ;
2016-02-04 22:55:22 +00:00
}
2016-02-07 04:36:53 +00:00
else
// if this is a 'direction' node
if ( cmTextCmp ( np - > label , " direction " ) = = 0 )
{
rc = _cmXScoreParseDirection ( p , meas , np , tick ) ;
}
2016-04-07 17:39:24 +00:00
2016-02-07 04:36:53 +00:00
}
2016-03-24 23:21:40 +00:00
2016-03-30 15:47:41 +00:00
* tickRef = tick ;
2016-02-04 16:33:42 +00:00
return rc ;
}
cmXsRC_t _cmXScoreParsePart ( cmXScore_t * p , cmXsPart_t * pp )
{
cmXsRC_t rc = kOkXsRC ;
const cmXmlNode_t * xnp ;
2016-03-24 23:21:40 +00:00
cmXmlAttr_t partAttr = { " id " , pp - > idStr } ;
2016-03-30 15:47:41 +00:00
unsigned barTick = 0 ;
2016-02-04 16:33:42 +00:00
// find the 'part'
if ( ( xnp = cmXmlSearch ( cmXmlRoot ( p - > xmlH ) , " part " , & partAttr , 1 ) ) = = NULL )
2016-02-07 04:36:53 +00:00
return cmErrMsg ( & p - > err , kSyntaxErrorXsRC , " The part '%s' was not found. " , pp - > idStr ) ;
2016-02-04 16:33:42 +00:00
// for each child of this part - find each measure
const cmXmlNode_t * cnp = xnp - > children ;
for ( ; cnp ! = NULL ; cnp = cnp - > sibling )
if ( cmTextCmp ( cnp - > label , " measure " ) = = 0 )
2016-03-30 15:47:41 +00:00
if ( ( rc = _cmXScoreParseMeasure ( p , pp , cnp , & barTick ) ) ! = kOkXsRC )
2016-02-04 16:33:42 +00:00
return rc ;
2016-04-07 17:39:24 +00:00
2016-02-04 16:33:42 +00:00
return rc ;
}
2016-02-25 00:09:29 +00:00
// Insert note 'np' into the sorted note list based at 's0'.
// Return a pointer to the base of the list after the insertion.
2016-02-07 04:36:53 +00:00
cmXsNote_t * _cmXScoreInsertSortedNote ( cmXsNote_t * s0 , cmXsNote_t * np )
{
2016-03-31 23:07:48 +00:00
assert ( np ! = NULL ) ;
// np->slink is not NULL if the list is being resorted
np - > slink = NULL ;
// this list is empty so np is the first element on the list
2016-02-07 04:36:53 +00:00
if ( s0 = = NULL )
return np ;
2016-03-31 23:07:48 +00:00
// np is before the first element on the list
2016-02-07 04:36:53 +00:00
if ( np - > tick < s0 - > tick )
{
np - > slink = s0 ;
return np ;
}
cmXsNote_t * s1 = s0 ;
cmXsNote_t * s2 = s0 - > slink ;
while ( s2 ! = NULL )
{
if ( s2 - > tick > np - > tick )
{
s1 - > slink = np ;
np - > slink = s2 ;
return s0 ;
}
2016-04-07 17:39:24 +00:00
2016-02-07 04:36:53 +00:00
s1 = s2 ;
s2 = s2 - > slink ;
}
s1 - > slink = np ;
2016-04-07 17:39:24 +00:00
2016-02-07 04:36:53 +00:00
return s0 ;
}
void _cmXScoreSort ( cmXScore_t * p )
{
2016-02-25 00:09:29 +00:00
// for each part
2016-02-07 04:36:53 +00:00
cmXsPart_t * pp = p - > partL ;
for ( ; pp ! = NULL ; pp = pp - > link )
{
2016-02-25 00:09:29 +00:00
// for each measure in this part
2016-02-07 04:36:53 +00:00
cmXsMeas_t * mp = pp - > measL ;
for ( ; mp ! = NULL ; mp = mp - > link )
2016-03-31 23:07:48 +00:00
{
// explicitely set noteL to NULL to in case we are re-sorting
mp - > noteL = NULL ;
2016-04-07 17:39:24 +00:00
2016-02-07 04:36:53 +00:00
// for each voice in this measure
cmXsVoice_t * vp = mp - > voiceL ;
for ( ; vp ! = NULL ; vp = vp - > link )
{
2016-02-25 00:09:29 +00:00
// for each note in this measure
2016-02-07 04:36:53 +00:00
cmXsNote_t * np = vp - > noteL ;
for ( ; np ! = NULL ; np = np - > mlink )
2016-04-07 17:39:24 +00:00
mp - > noteL = _cmXScoreInsertSortedNote ( mp - > noteL , np ) ;
}
2016-02-07 04:36:53 +00:00
}
}
}
2016-07-20 23:16:00 +00:00
// Set the cmXsNode_t.secs and dsecs.
2016-03-31 23:07:48 +00:00
void _cmXScoreSetAbsoluteTime ( cmXScore_t * p )
{
double tpqn = 0 ; // ticks per quarter note
double tps = 0 ; // ticks per second
unsigned metro_tick = 0 ;
double metro_sec = 0 ;
double sec0 = 0 ;
2016-04-07 17:39:24 +00:00
2016-03-31 23:07:48 +00:00
cmXsPart_t * pp = p - > partL ;
for ( ; pp ! = NULL ; pp = pp - > link )
{
cmXsMeas_t * mp = pp - > measL ;
2016-04-07 17:39:24 +00:00
for ( ; mp ! = NULL ; mp = mp - > link )
2016-03-31 23:07:48 +00:00
{
if ( mp - > divisions ! = 0 )
tpqn = mp - > divisions ;
2016-04-07 17:39:24 +00:00
2016-03-31 23:07:48 +00:00
cmXsNote_t * np = mp - > noteL ;
2016-04-07 17:39:24 +00:00
2016-03-31 23:07:48 +00:00
for ( ; np ! = NULL ; np = np - > slink )
{
// Seconds are calculated as:
// dticks = np->tick - metro_tick; // where metro_tick is the absolute tick of the last metro event
// secs = (dticks/tps) + metro_secs; // where metro_secs is the absoute time of the last metro event
2016-04-07 17:39:24 +00:00
2016-07-20 23:16:00 +00:00
unsigned dticks = np - > tick - metro_tick ;
double secs = tps = = 0 ? 0 : ( dticks / tps ) + metro_sec ;
2016-03-31 23:07:48 +00:00
double dsecs = secs - sec0 ;
2016-04-07 17:39:24 +00:00
//
2016-03-31 23:07:48 +00:00
if ( cmIsFlag ( np - > flags , kMetronomeXsFl ) )
{
2016-07-20 23:16:00 +00:00
double bpm = np - > duration ; // beats per minute
double bps = bpm / 60.0 ; // beats per second
tps = bps * tpqn ; // ticks per second
metro_tick = np - > tick ; // store tick of the last metronome marker
metro_sec = secs ; // store time of the last metronome marker
2016-03-31 23:07:48 +00:00
}
2016-07-20 23:16:00 +00:00
if ( cmIsFlag ( np - > flags , kBarXsFl | kDampDnXsFl | kDampUpXsFl | kDampUpDnXsFl | kSostDnXsFl | kSostUpXsFl | kOnsetXsFl | kSectionXsFl ) )
2016-03-31 23:07:48 +00:00
{
np - > secs = secs ;
np - > dsecs = dsecs ;
sec0 = secs ;
}
}
}
2016-04-07 17:39:24 +00:00
}
2016-03-31 23:07:48 +00:00
}
// All notes in a[aN] are on the same tick
unsigned _cmXsSpreadGraceNotes ( cmXsNote_t * * a , unsigned aN )
{
unsigned i ;
bool barFl = false ;
// set barFl to true if a bar marker is included in the notes
for ( i = 0 ; i < aN ; + + i )
if ( cmIsFlag ( a [ i ] - > flags , kBarXsFl ) )
barFl = true ;
// spread any grace notes by one tick
unsigned nextGraceTick = UINT_MAX ;
for ( i = 0 ; i < aN ; + + i )
if ( cmIsFlag ( a [ i ] - > flags , kGraceXsFl ) )
{
if ( nextGraceTick = = UINT_MAX )
nextGraceTick = a [ i ] - > tick + 1 ;
else
{
a [ i ] - > tick = nextGraceTick ;
nextGraceTick + = 1 ;
}
}
2016-04-07 17:39:24 +00:00
// if this tick group includes the bar ...
2016-03-31 23:07:48 +00:00
if ( barFl & & nextGraceTick ! = UINT_MAX )
{
// ... then move all non-grace note events (except the bar marker) after
// the grace notes
for ( i = 0 ; i < aN ; + + i )
if ( cmIsNotFlag ( a [ i ] - > flags , kGraceXsFl ) & & cmIsNotFlag ( a [ i ] - > flags , kBarXsFl ) )
a [ i ] - > tick = nextGraceTick ;
}
2016-04-07 17:39:24 +00:00
2016-03-31 23:07:48 +00:00
return nextGraceTick = = UINT_MAX ? 0 : nextGraceTick ;
}
void _cmXScoreSpreadGraceNotes ( cmXScore_t * p )
{
cmXsPart_t * pp = p - > partL ;
for ( ; pp ! = NULL ; pp = pp - > link )
{
// tick1 is the location of the minimum current tick
// (or 0 if it should be ignored)
2016-04-07 17:39:24 +00:00
unsigned tick1 = 0 ;
2016-03-31 23:07:48 +00:00
cmXsMeas_t * mp = pp - > measL ;
for ( ; mp ! = NULL ; mp = mp - > link )
{
cmXsNote_t * np = mp - > noteL ;
unsigned aN = 128 ;
cmXsNote_t * a [ aN ] ;
unsigned ai = 0 ;
// The first event in a measure may have been forced ahead
// by spreading at the end of the previous measure
if ( tick1 > np - > tick )
np - > tick = tick1 ;
else
tick1 = 0 ;
// tick0 is the tick of the current tick group we are examining
// A tick group is a group of events that share the same tick.
unsigned tick0 = np - > tick ;
2016-04-07 17:39:24 +00:00
2016-07-20 23:16:00 +00:00
// for each note
2016-03-31 23:07:48 +00:00
for ( ; np ! = NULL ; np = np - > slink )
{
2016-07-20 23:16:00 +00:00
// if this event is the first of a new tick group (then a[] holds a group completed on the previous note)
2016-03-31 23:07:48 +00:00
if ( np - > tick ! = tick0 )
{
// if there is more than one event in the completed tick group ...
if ( ai > 1 )
tick1 = _cmXsSpreadGraceNotes ( a , ai ) ; // ... then process the group
2016-04-07 17:39:24 +00:00
2016-03-31 23:07:48 +00:00
ai = 0 ; // empty the tick group array
tick0 = np - > tick ; // update the current group's common tick
}
// if the min. current tick is ahead of this event then move the event ahead
if ( tick1 > np - > tick )
np - > tick = tick1 ;
else
tick1 = 0 ; // otherwise disable tick1
// add this event to the tick group
assert ( ai < aN ) ;
a [ ai + + ] = np ;
2016-04-07 17:39:24 +00:00
2016-03-31 23:07:48 +00:00
}
// if there are events in the group array then process them
if ( ai > 1 )
tick1 = _cmXsSpreadGraceNotes ( a , ai ) ;
}
}
}
2016-03-17 23:30:21 +00:00
bool _cmXScoreFindTiedNote ( cmXScore_t * p , cmXsMeas_t * mp , cmXsNote_t * n0p , bool rptFl )
2016-02-18 00:00:15 +00:00
{
2016-03-17 23:30:21 +00:00
cmXsNote_t * nnp = n0p - > slink ; // begin w/ note following np
unsigned measNumb = mp - > number ;
cmChar_t acc = n0p - > alter = = - 1 ? ' b ' : ( n0p - > alter = = 1 ? ' # ' : ' ' ) ;
2016-02-18 00:00:15 +00:00
2016-03-15 14:08:44 +00:00
if ( rptFl )
2016-03-17 23:30:21 +00:00
printf ( " %i %i %s " , n0p - > meas - > number , n0p - > tick , cmMidiToSciPitch ( n0p - > pitch , NULL , 0 ) ) ;
2016-04-07 17:39:24 +00:00
2016-03-17 23:30:21 +00:00
while ( 1 )
2016-02-18 00:00:15 +00:00
{
2016-03-17 23:30:21 +00:00
// if we are at the end of a measure advance to the next measure
2016-02-18 00:00:15 +00:00
if ( nnp = = NULL )
2016-03-17 23:30:21 +00:00
{
mp = mp - > link ;
2016-02-18 00:00:15 +00:00
nnp = mp - > noteL ;
2016-03-10 22:50:33 +00:00
2016-07-14 23:08:13 +00:00
// if a measure was completed and no end note was found ... then the tie is unterminated
// (a tie must be continued in every measure which it passes through)
2016-03-17 23:30:21 +00:00
if ( mp - > number > measNumb + 1 )
break ;
}
2016-04-07 17:39:24 +00:00
2016-03-17 23:30:21 +00:00
// for each note starting at nnp
for ( ; nnp ! = NULL ; nnp = nnp - > slink )
{
// if this note is tied to the originating note (np)
if ( nnp - > voice - > id = = n0p - > voice - > id & & nnp - > step = = n0p - > step & & nnp - > octave = = n0p - > octave )
{
nnp - > flags | = kTieProcXsFl ;
nnp - > flags = cmClrFlag ( nnp - > flags , kOnsetXsFl ) ;
2016-07-14 23:08:13 +00:00
n0p - > tied = nnp ;
2016-03-17 23:30:21 +00:00
if ( rptFl )
printf ( " ---> %i %i %s " , nnp - > meas - > number , nnp - > tick , cmMidiToSciPitch ( nnp - > pitch , NULL , 0 ) ) ;
// if this note is not tied to a subsequent note
if ( cmIsNotFlag ( nnp - > flags , kTieBegXsFl ) )
return true ;
2016-07-14 23:08:13 +00:00
n0p = nnp ;
2016-03-17 23:30:21 +00:00
// record the measure number of the last note with a tie-start
measNumb = mp - > number ;
}
2016-04-07 17:39:24 +00:00
}
2016-03-17 23:30:21 +00:00
}
2016-04-07 17:39:24 +00:00
2016-03-17 23:30:21 +00:00
cmErrWarnMsg ( & p - > err , kUnterminatedTieXsRC , " The tied %c%c%i in measure %i was not terminated. " , n0p - > step , acc , n0p - > octave , measNumb ) ;
return false ;
}
2016-02-25 00:09:29 +00:00
void _cmXScoreResolveTiesAndLoc ( cmXScore_t * p )
2016-02-18 00:00:15 +00:00
{
2016-02-25 00:09:29 +00:00
unsigned n = 0 ;
unsigned m = 0 ;
2016-03-15 14:08:44 +00:00
bool rptFl = false ;
2016-02-18 00:00:15 +00:00
cmXsPart_t * pp = p - > partL ;
2016-04-07 17:39:24 +00:00
2016-03-10 22:50:33 +00:00
// for each part
2016-02-18 00:00:15 +00:00
for ( ; pp ! = NULL ; pp = pp - > link )
{
2016-02-25 00:09:29 +00:00
unsigned locIdx = 1 ;
2016-02-18 00:00:15 +00:00
cmXsMeas_t * mp = pp - > measL ;
2016-03-10 22:50:33 +00:00
// for each measure
2016-02-18 00:00:15 +00:00
for ( ; mp ! = NULL ; mp = mp - > link )
{
2016-02-25 00:09:29 +00:00
cmXsNote_t * n0 = NULL ;
2016-02-18 00:00:15 +00:00
cmXsNote_t * np = mp - > noteL ;
2016-03-10 22:50:33 +00:00
// for each note in this measure
2016-02-18 00:00:15 +00:00
for ( ; np ! = NULL ; np = np - > slink )
2016-02-25 00:09:29 +00:00
{
// if this note begins a tie and has not yet been processed
// (A note that continues a tie and therefore has a kTieBegXsFl set
// may have already been processed by an earlier tied note.)
2016-02-18 00:00:15 +00:00
if ( cmIsFlag ( np - > flags , kTieBegXsFl ) & & cmIsNotFlag ( np - > flags , kTieProcXsFl ) )
{
2016-03-15 14:08:44 +00:00
if ( _cmXScoreFindTiedNote ( p , mp , np , rptFl ) )
2016-02-18 00:00:15 +00:00
m + = 1 ;
2016-04-07 17:39:24 +00:00
2016-03-15 14:08:44 +00:00
if ( rptFl )
printf ( " \n " ) ;
2016-04-07 17:39:24 +00:00
2016-02-18 00:00:15 +00:00
n + = 1 ;
2016-02-25 00:09:29 +00:00
}
2016-07-14 23:08:13 +00:00
// Validate the tie state of the current note.
2016-03-15 14:08:44 +00:00
if ( cmIsFlag ( np - > flags , kTieEndXsFl ) & & cmIsFlag ( np - > flags , kOnsetXsFl ) )
{
cmChar_t acc = np - > alter = = - 1 ? ' b ' : ( np - > alter = = 1 ? ' # ' : ' ' ) ;
cmErrWarnMsg ( & p - > err , kUnterminatedTieXsRC , " The tied %c%c%i in measure %i marked as a tied note but is also marked to sound. " , np - > step , acc , np - > octave , mp - > number ) ;
}
2016-07-14 23:08:13 +00:00
//
// Set the score location of notes marked for onset and bar lines.
//
2016-03-24 23:21:40 +00:00
if ( cmIsFlag ( np - > flags , kOnsetXsFl | kBarXsFl ) )
2016-02-25 00:09:29 +00:00
{
2016-07-14 23:08:13 +00:00
// if this note does not share the same location as the previous 'located' note then increment the 'loc' index
2016-03-24 23:21:40 +00:00
if ( cmIsFlag ( np - > flags , kBarXsFl ) | | ( n0 ! = NULL & & n0 - > tick ! = np - > tick ) )
2016-02-25 00:09:29 +00:00
locIdx + = 1 ;
np - > locIdx = locIdx ;
n0 = np ;
}
2016-04-07 17:39:24 +00:00
}
2016-02-18 00:00:15 +00:00
}
}
2016-07-14 23:08:13 +00:00
printf ( " Tied notes found:%i Not found:%i \n " , m , n - m ) ;
2016-02-18 00:00:15 +00:00
}
2016-02-19 00:10:05 +00:00
cmXsRC_t _cmXScoreResolveOctaveShift ( cmXScore_t * p )
{
2016-04-07 17:39:24 +00:00
2016-02-19 00:10:05 +00:00
const cmXsSpan_t * s ;
for ( s = p - > spanL ; s ! = NULL ; s = s - > link )
{
if ( s - > tick1 = = - 1 )
{
cmErrWarnMsg ( & p - > err , kSyntaxErrorXsRC , " An unterminated octave shift span was encountered in measure %i staff=%i. " , s - > meas - > number , s - > staff ) ;
}
else
{
cmXsMeas_t * m = p - > partL - > measL ;
for ( ; m ! = NULL ; m = m - > link )
if ( m - > number = = s - > meas - > number )
break ;
assert ( m ! = NULL ) ;
cmXsNote_t * note = m - > noteL ;
for ( ; note ! = NULL ; note = note - > slink )
if ( note - > staff = = s - > staff & & s - > tick0 < = note - > tick & & note - > tick < s - > tick1 )
note - > pitch + = s - > pitch_offset ;
}
}
return kOkXsRC ;
}
2016-03-24 23:21:40 +00:00
// The identical pitch may be notated to play simultaneously on different voices.
// As performed on the piano this will equate to a single sounding note.
// This function clears the onset flag on all except one of the duplicated notes.
void _cmXScoreRemoveDuplicateNotes ( cmXScore_t * p )
{
cmXsPart_t * pp = p - > partL ;
2016-04-07 17:39:24 +00:00
2016-03-24 23:21:40 +00:00
// for each part
for ( ; pp ! = NULL ; pp = pp - > link )
{
cmXsMeas_t * mp = pp - > measL ;
// for each measure
for ( ; mp ! = NULL ; mp = mp - > link )
{
cmXsNote_t * np = mp - > noteL ;
// for each note in this measure
for ( ; np ! = NULL ; np = np - > slink )
if ( cmIsFlag ( np - > flags , kOnsetXsFl ) )
{
cmXsNote_t * n0p = mp - > noteL ;
for ( ; n0p ! = NULL ; n0p = n0p - > slink )
if ( n0p ! = np & & cmIsFlag ( n0p - > flags , kOnsetXsFl ) & & np - > locIdx = = n0p - > locIdx & & np - > pitch = = n0p - > pitch )
n0p - > flags = cmClrFlag ( n0p - > flags , kOnsetXsFl ) ;
2016-04-07 17:39:24 +00:00
2016-03-24 23:21:40 +00:00
}
}
}
}
2016-03-31 23:07:48 +00:00
void _cmXScoreSetMeasGroups ( cmXScore_t * p , unsigned flag )
2016-02-19 00:10:05 +00:00
{
2016-03-31 23:07:48 +00:00
unsigned sectionId = 0 ;
cmXsNote_t * n0 = NULL ;
2016-04-07 17:39:24 +00:00
2016-03-31 23:07:48 +00:00
cmXsPart_t * pp = p - > partL ;
2016-04-07 17:39:24 +00:00
2016-03-31 23:07:48 +00:00
// for each part
for ( ; pp ! = NULL ; pp = pp - > link )
2016-02-19 00:10:05 +00:00
{
2016-03-31 23:07:48 +00:00
cmXsMeas_t * mp = pp - > measL ;
2016-02-19 00:10:05 +00:00
2016-03-31 23:07:48 +00:00
// for each measure
for ( ; mp ! = NULL ; mp = mp - > link )
2016-02-19 00:10:05 +00:00
{
2016-03-31 23:07:48 +00:00
cmXsNote_t * np = mp - > noteL ;
2016-02-19 00:10:05 +00:00
2016-03-31 23:07:48 +00:00
// for each note in this measure
for ( ; np ! = NULL ; np = np - > slink )
{
2016-04-07 17:39:24 +00:00
// if this note has a heel marker and we are looking for evenness events
if ( cmIsFlag ( flag , kEvenXsFl ) & & cmIsFlag ( np - > flags , kHeelXsFl ) )
{
np - > flags = cmSetFlag ( np - > flags , kBegGroupXsFl | kEndGroupXsFl ) ;
np - > evenGroupId = sectionId + 1 ;
}
// if this note is of the type we are looking for
2016-03-31 23:07:48 +00:00
if ( cmIsFlag ( np - > flags , flag ) )
2016-04-07 17:39:24 +00:00
{
if ( n0 = = NULL )
np - > flags = cmSetFlag ( np - > flags , kBegGroupXsFl ) ;
n0 = np ;
}
// if this is a section marker
2016-03-31 23:07:48 +00:00
if ( cmIsFlag ( np - > flags , kSectionXsFl ) )
{
if ( n0 ! = NULL )
{
2016-04-07 17:39:24 +00:00
np - > flags = cmSetFlag ( np - > flags , kEndGroupXsFl ) ;
2016-03-31 23:07:48 +00:00
switch ( flag )
{
2016-04-07 17:39:24 +00:00
case kEvenXsFl : n0 - > evenGroupId = sectionId + 1 ; break ;
case kDynXsFl : n0 - > dynGroupId = sectionId + 1 ; break ;
case kTempoXsFl : n0 - > tempoGroupId = sectionId + 1 ; break ;
2016-03-31 23:07:48 +00:00
}
}
2016-02-19 00:10:05 +00:00
2016-03-31 23:07:48 +00:00
if ( cmIsFlag ( np - > flags , kSectionXsFl ) )
sectionId = np - > tvalue = = NULL ? 0 : strtol ( np - > tvalue , NULL , 10 ) ;
2016-04-07 17:39:24 +00:00
2016-03-31 23:07:48 +00:00
n0 = NULL ;
}
}
}
}
2016-02-19 00:10:05 +00:00
}
2016-03-31 23:07:48 +00:00
2016-02-26 00:10:54 +00:00
cmXsRC_t _cmXScoreWriteScorePlotFile ( cmXScore_t * p , const cmChar_t * fn )
{
cmXsRC_t rc = kOkXsRC ;
cmFileH_t fH = cmFileNullHandle ;
double ticks_per_sec = 0 ;
double onset_secs = 0 ;
2016-04-07 17:39:24 +00:00
2016-02-26 00:10:54 +00:00
if ( cmFileOpen ( & fH , fn , kWriteFileFl , p - > err . rpt ) ! = kOkFileRC )
return cmErrMsg ( & p - > err , kFileFailXsRC , " Unable to create the file '%s'. " , cmStringNullGuard ( fn ) ) ;
2016-04-07 17:39:24 +00:00
2016-02-26 00:10:54 +00:00
cmXsPart_t * pp = p - > partL ;
for ( ; pp ! = NULL ; pp = pp - > link )
{
cmXsMeas_t * mp = pp - > measL ;
for ( ; mp ! = NULL ; mp = mp - > link )
{
cmFilePrintf ( fH , " b %f %i %s B \n " , onset_secs , mp - > number , " bar " ) ;
cmXsNote_t * np = mp - > noteL ;
unsigned tick0 = 0 ;
for ( ; np ! = NULL ; np = np - > slink )
{
if ( cmIsFlag ( np - > flags , kMetronomeXsFl ) )
{
double bps = np - > duration / 60.0 ;
// t b t
// - = - -
// s s b
2016-04-07 17:39:24 +00:00
ticks_per_sec = bps * mp - > divisions ;
2016-02-26 00:10:54 +00:00
}
else
{
if ( cmIsFlag ( np - > flags , kOnsetXsFl ) )
{
onset_secs + = ( np - > tick - tick0 ) / ticks_per_sec ;
tick0 = np - > tick ;
2016-03-02 23:52:40 +00:00
cmFilePrintf ( fH , " n %f %f %i %s %s \n " , onset_secs , np - > duration / ticks_per_sec , np - > uid , cmMidiToSciPitch ( np - > pitch , NULL , 0 ) , cmIsFlag ( np - > flags , kGraceXsFl ) ? " G " : " N " ) ;
2016-02-26 00:10:54 +00:00
}
}
}
onset_secs + = ( mp - > divisions * mp - > beats - tick0 ) / ticks_per_sec ;
}
}
cmFileClose ( & fH ) ;
return rc ;
}
2016-06-16 16:53:59 +00:00
// Force the bar event to be the first event in the measure.
void _cmXScoreFixBarLines ( cmXScore_t * p )
{
cmXsPart_t * pp = p - > partL ;
for ( ; pp ! = NULL ; pp = pp - > link )
{
cmXsMeas_t * mp = pp - > measL ;
for ( ; mp ! = NULL ; mp = mp - > link )
{
cmXsNote_t * np = mp - > noteL ;
cmXsNote_t * ep = NULL ;
for ( ; np ! = NULL ; np = np - > slink )
{
if ( cmIsFlag ( np - > flags , kBarXsFl ) )
{
if ( ep ! = NULL )
np - > tick = ep - > tick ;
break ;
}
if ( ep = = NULL )
ep = np ;
}
}
}
}
2016-07-20 23:16:00 +00:00
// Assign pedal down durations to pedal down events.
cmXsRC_t _cmXScoreProcessPedals ( cmXScore_t * p )
{
cmXsRC_t rc = kOkXsRC ;
cmXsPart_t * pp = p - > partL ;
for ( ; pp ! = NULL ; pp = pp - > link )
{
cmXsNote_t * dnp = NULL ; // pointer to last damper down event
cmXsNote_t * snp = NULL ; // pointer to last sostenuto down event
cmXsMeas_t * mp = pp - > measL ;
for ( ; mp ! = NULL ; mp = mp - > link )
{
cmXsNote_t * np = mp - > noteL ;
for ( ; np ! = NULL ; np = np - > slink )
{
unsigned x = np - > flags & ( kDampDnXsFl | kDampUpXsFl | kDampUpDnXsFl | kSostUpXsFl | kSostDnXsFl ) ;
switch ( x )
{
case 0 :
break ;
case kDampDnXsFl :
if ( dnp ! = NULL )
cmErrWarnMsg ( & p - > err , kPedalStateErrorXsRc , " Damper down not preceded by damper up in measure:%i. " , mp - > number ) ;
else
dnp = np ;
break ;
case kDampUpXsFl :
if ( dnp = = NULL )
cmErrWarnMsg ( & p - > err , kPedalStateErrorXsRc , " Damper up not preceded by damper down in measure:%i. " , mp - > number ) ;
else
{
dnp - > duration = np - > tick - dnp - > tick ;
dnp = NULL ;
}
break ;
case kDampUpDnXsFl :
if ( dnp = = NULL )
cmErrWarnMsg ( & p - > err , kPedalStateErrorXsRc , " Damper up/down not preceded by damper down in measure:%i. " , mp - > number ) ;
else
{
dnp - > duration = np - > tick - dnp - > tick ;
dnp = np ;
}
break ;
case kSostDnXsFl :
if ( snp ! = NULL )
cmErrWarnMsg ( & p - > err , kPedalStateErrorXsRc , " Sostenuto down not preceded by sostenuto up in measure:%i. " , mp - > number ) ;
else
snp = np ;
break ;
case kSostUpXsFl :
if ( snp = = NULL )
cmErrWarnMsg ( & p - > err , kPedalStateErrorXsRc , " Sostenuto up not preceded by sostenuto down in measure:%i. " , mp - > number ) ;
else
{
snp - > duration = np - > tick - snp - > tick ;
snp = NULL ;
}
break ;
default :
{
assert ( 0 ) ;
}
} // switch
} // for notes
} // for measures
if ( dnp ! = NULL )
cmErrWarnMsg ( & p - > err , kPedalStateErrorXsRc , " Damper left down at the end of a part. " ) ;
if ( snp ! = NULL )
cmErrWarnMsg ( & p - > err , kPedalStateErrorXsRc , " Sostenuto left down at the end of a part. " ) ;
}
return rc ;
}
2016-08-03 21:03:17 +00:00
void _cmXScoreInsertTime ( cmXScore_t * p , cmXsMeas_t * mp , cmXsNote_t * np , unsigned expand_ticks )
{
for ( ; mp ! = NULL ; mp = mp - > link )
{
if ( np = = NULL )
np = mp - > noteL ;
for ( ; np ! = NULL ; np = np - > slink )
np - > tick + = expand_ticks ;
}
}
// Insert the grace notes in between the first and last note in the group
// by inserting time between the first and last note.
void _cmXScoreGraceInsertTime ( cmXScore_t * p , unsigned graceGroupId , cmXsNote_t * aV [ ] , unsigned aN )
{
cmXsNote_t * np = NULL ;
unsigned expand_ticks = 0 ;
unsigned ticks = aV [ aN - 1 ] - > tick ;
unsigned i ;
for ( i = 0 ; i < aN ; + + i )
if ( cmIsFlag ( aV [ i ] - > flags , kGraceXsFl ) & & aV [ i ] - > graceGroupId = = graceGroupId )
{
aV [ i ] - > tick = ticks ;
ticks + = aV [ i ] - > duration ;
expand_ticks + = aV [ i ] - > duration ;
np = aV [ i ] ;
}
np = np - > slink ;
if ( np ! = NULL )
_cmXScoreInsertTime ( p , np - > meas , np , expand_ticks ) ;
}
// Insert the grace notes in between the first and last note in the group
// but do not insert any additional time betwee the first and last note.
// In effect time is removed from the first note and taken by the grace notes.
void _cmXScoreGraceOverlayTime ( cmXScore_t * p , unsigned graceGroupId , cmXsNote_t * aV [ ] , unsigned aN )
{
assert ( aN > = 3 ) ;
unsigned t = aV [ aN - 1 ] - > tick ;
int i = ( int ) aN - 2 ;
for ( ; i > 0 ; - - i )
if ( cmIsFlag ( aV [ i ] - > flags , kGraceXsFl ) & & aV [ i ] - > graceGroupId = = graceGroupId )
{
aV [ i ] - > tick = t - aV [ i ] - > duration ;
t = aV [ i ] - > tick ;
}
}
2016-07-27 23:05:42 +00:00
// Adjust the locations of grace notes. Note that this must be done
// after reordering so that we can be sure that the order in time of
// the notes in each group has been set prior to building the
// grace note groups - which must be in reverse time order.
cmXsRC_t _cmXScoreProcessGraceNotes ( cmXScore_t * p )
{
cmXsRC_t rc = kOkXsRC ;
unsigned graceGroupId = 1 ;
for ( ; 1 ; + + graceGroupId )
{
2016-08-03 21:03:17 +00:00
cmXsNote_t * gn0p = NULL ;
cmXsNote_t * gn1p = NULL ;
unsigned gN = 0 ;
2016-07-27 23:05:42 +00:00
cmXsPart_t * pp = p - > partL ;
double ticksPerSec = 0 ;
for ( ; pp ! = NULL ; pp = pp - > link )
{
cmXsMeas_t * mp = pp - > measL ;
for ( ; mp ! = NULL ; mp = mp - > link )
{
cmXsNote_t * np = mp - > noteL ;
for ( ; np ! = NULL ; np = np - > slink )
{
// notice change of tempo
if ( cmIsFlag ( np - > flags , kMetronomeXsFl ) )
{
// ticks/sec = ticks/qn * qn/sec
ticksPerSec = mp - > divisions * np - > duration / 60.0 ;
}
// if this note is part of the grace note group we are searching for
if ( np - > graceGroupId = = graceGroupId )
{
2016-08-03 21:03:17 +00:00
// track the first note in the grace note list
if ( gn0p = = NULL )
gn0p = np ;
// add the note to the end of the grace note list
if ( gn1p ! = NULL )
gn1p - > grace = np ;
2016-07-27 23:05:42 +00:00
2016-08-03 21:03:17 +00:00
// track the last note in the grace note list
gn1p = np ;
2016-07-27 23:05:42 +00:00
// set each grace note to have 1/20 of a second duration
if ( cmIsFlag ( np - > flags , kGraceXsFl ) )
np - > duration = floor ( ticksPerSec / 20.0 ) ;
2016-08-03 21:03:17 +00:00
gN + = 1 ;
2016-07-27 23:05:42 +00:00
}
2016-08-03 21:03:17 +00:00
} //
2016-07-27 23:05:42 +00:00
}
}
// no records were found for this grace id - we're done
2016-08-03 21:03:17 +00:00
if ( gn0p = = NULL )
break ;
// grace note groups must have at least 3 members
if ( gN < 3 )
{
rc = cmErrMsg ( & p - > err , kSyntaxErrorXsRC , " The grace not group ending in meas %i has fewer than 3 (%i) members. " , gn1p - > meas - > number , gN ) ;
2016-07-27 23:05:42 +00:00
break ;
2016-08-03 21:03:17 +00:00
}
2016-07-27 23:05:42 +00:00
2016-08-03 21:03:17 +00:00
// gn0p is now set to the first note in th group
// gn1p is now set to the last note in the group
// verify that the first note is marked with kBegGraceXsFl
if ( cmIsNotFlag ( gn0p - > flags , kBegGraceXsFl ) )
2016-07-27 23:05:42 +00:00
{
2016-08-03 21:03:17 +00:00
rc = cmErrMsg ( & p - > err , kSyntaxErrorXsRC , " The first note in a grace note group in meas %i is not marked with a 'b'. " , gn0p - > meas - > number ) ;
break ;
}
// verify that the last note is marked with kEndGraceXsFl
if ( cmIsNotFlag ( gn1p - > flags , kEndGraceXsFl ) )
{
rc = cmErrMsg ( & p - > err , kSyntaxErrorXsRC , " The last note in a grace note group in meas %i is not marked with a valid operator character. " , gn1p - > meas - > number ) ;
break ;
}
// count the total number of events between gn0p and gn1p
cmXsNote_t * n0p = NULL ;
cmXsNote_t * n1p = gn0p ;
cmXsMeas_t * mp = gn0p - > meas ;
unsigned aN = 0 ;
for ( ; n0p ! = gn1p ; n1p = n1p - > slink )
{
// if we are crossing a measure boundary
if ( n1p = = NULL )
2016-07-27 23:05:42 +00:00
{
2016-08-03 21:03:17 +00:00
mp = mp - > link ;
assert ( mp ! = NULL ) ;
n1p = mp - > noteL ;
2016-07-27 23:05:42 +00:00
}
2016-08-03 21:03:17 +00:00
if ( 1 )
{
bool fl = n0p ! = NULL & & n0p - > tick < n1p - > tick ;
unsigned type = n1p - > flags & ( kBegGraceXsFl | kEndGraceXsFl | kAddGraceXsFl | kSubGraceXsFl | kFirstGraceXsFl ) ;
printf ( " %3i 0x%08x %i %5i %i \n " , n1p - > graceGroupId , type , n1p - > tick , n1p - > duration , fl ) ;
}
2016-07-27 23:05:42 +00:00
2016-08-03 21:03:17 +00:00
+ + aN ;
n0p = n1p ;
}
// create a vector of pointers to all events between gn0p and gn1p
cmXsNote_t * aV [ aN ] ;
unsigned i ;
n1p = gn0p ;
n0p = NULL ;
mp = gn0p - > meas ;
for ( i = 0 ; n0p ! = gn1p ; n1p = n1p - > slink )
{
// if we are crossing a measure boundardy
if ( n1p = = NULL )
{
mp = mp - > link ;
assert ( mp ! = NULL ) ;
n1p = mp - > noteL ;
}
2016-07-27 23:05:42 +00:00
2016-08-03 21:03:17 +00:00
assert ( i < aN ) ;
aV [ i + + ] = n1p ;
n0p = n1p ;
}
switch ( gn1p - > flags & ( kAddGraceXsFl | kSubGraceXsFl | kFirstGraceXsFl ) )
{
case kAddGraceXsFl :
_cmXScoreGraceInsertTime ( p , graceGroupId , aV , aN ) ;
break ;
case kSubGraceXsFl :
_cmXScoreGraceOverlayTime ( p , graceGroupId , aV , aN ) ;
break ;
case kFirstGraceXsFl :
break ;
default :
{ assert ( 0 ) ; }
2016-07-27 23:05:42 +00:00
}
}
return rc ;
}
2016-07-14 23:08:13 +00:00
cmXsRC_t cmXScoreInitialize ( cmCtx_t * ctx , cmXsH_t * hp , const cmChar_t * xmlFn )
2016-02-04 16:33:42 +00:00
{
cmXsRC_t rc = kOkXsRC ;
if ( ( rc = cmXScoreFinalize ( hp ) ) ! = kOkXsRC )
return rc ;
cmXScore_t * p = cmMemAllocZ ( cmXScore_t , 1 ) ;
cmErrSetup ( & p - > err , & ctx - > rpt , " XScore " ) ;
// create a local linked heap
if ( cmLHeapIsValid ( p - > lhH = cmLHeapCreate ( 8196 , ctx ) ) = = false )
return cmErrMsg ( & p - > err , kLHeapFailXsRC , " Lheap create failed. " ) ;
// open the music xml file
if ( cmXmlAlloc ( ctx , & p - > xmlH , xmlFn ) ! = kOkXmlRC )
{
rc = cmErrMsg ( & p - > err , kXmlFailXsRC , " Unable to open the MusicXML file '%s'. " , cmStringNullGuard ( xmlFn ) ) ;
goto errLabel ;
}
2016-04-07 17:39:24 +00:00
//cmXmlPrint(p->xmlH,&ctx->rpt);
2016-02-09 02:46:07 +00:00
2016-02-04 16:33:42 +00:00
// parse the part-list
if ( ( rc = _cmXScoreParsePartList ( p ) ) ! = kOkXsRC )
goto errLabel ;
2016-02-07 04:36:53 +00:00
// parse each score 'part'
2016-02-04 16:33:42 +00:00
cmXsPart_t * pp = p - > partL ;
for ( ; pp ! = NULL ; pp = pp - > link )
if ( ( rc = _cmXScoreParsePart ( p , pp ) ) ! = kOkXsRC )
goto errLabel ;
2016-02-07 04:36:53 +00:00
// fill in the note->slink chain to link the notes in each measure in time order
2016-02-09 02:46:07 +00:00
_cmXScoreSort ( p ) ;
2016-03-31 23:07:48 +00:00
_cmXScoreSpreadGraceNotes ( p ) ;
2016-04-07 17:39:24 +00:00
2016-03-31 23:07:48 +00:00
_cmXScoreSort ( p ) ;
2016-04-07 17:39:24 +00:00
2016-03-31 23:07:48 +00:00
_cmXScoreSetAbsoluteTime ( p ) ;
2016-02-25 00:09:29 +00:00
_cmXScoreResolveTiesAndLoc ( p ) ;
2016-02-18 00:00:15 +00:00
2016-03-24 23:21:40 +00:00
_cmXScoreRemoveDuplicateNotes ( p ) ;
2016-03-31 23:07:48 +00:00
_cmXScoreSetMeasGroups ( p , kEvenXsFl ) ;
_cmXScoreSetMeasGroups ( p , kDynXsFl ) ;
_cmXScoreSetMeasGroups ( p , kTempoXsFl ) ;
2016-04-07 17:39:24 +00:00
2016-02-19 00:10:05 +00:00
//_cmXScoreResolveOctaveShift(p);
2016-02-09 02:46:07 +00:00
// CSV output initialize failed.
if ( cmCsvInitialize ( & p - > csvH , ctx ) ! = kOkCsvRC )
rc = cmErrMsg ( & p - > err , kCsvFailXsRC , " CSV output object create failed. " ) ;
2016-04-07 17:39:24 +00:00
2016-02-04 16:33:42 +00:00
errLabel :
if ( rc ! = kOkXsRC )
_cmXScoreFinalize ( p ) ;
else
hp - > h = p ;
2016-04-07 17:39:24 +00:00
2016-02-04 16:33:42 +00:00
return rc ;
}
cmXsRC_t cmXScoreFinalize ( cmXsH_t * hp )
{
cmXsRC_t rc = kOkXsRC ;
2016-04-07 17:39:24 +00:00
2016-02-04 16:33:42 +00:00
if ( hp = = NULL | | cmXScoreIsValid ( * hp ) = = false )
return kOkXsRC ;
cmXScore_t * p = _cmXScoreHandleToPtr ( * hp ) ;
if ( ( rc = _cmXScoreFinalize ( p ) ) ! = kOkXsRC )
return rc ;
hp - > h = NULL ;
2016-04-07 17:39:24 +00:00
return rc ;
2016-02-04 16:33:42 +00:00
}
bool cmXScoreIsValid ( cmXsH_t h )
{ return h . h ! = NULL ; }
2016-04-06 21:35:09 +00:00
//-------------------------------------------------------------------------------------------
2016-06-09 22:52:29 +00:00
2016-04-06 21:35:09 +00:00
typedef struct
{
2016-06-16 16:53:59 +00:00
unsigned idx ; // Fields from the reordering input file which are
unsigned voice ; // used to match the reorder record to
unsigned locIdx ; // the associated a cmXsNode_t record.
unsigned tick ; //
unsigned durtn ; //
float rval ; //
unsigned midi ; //
2016-07-27 23:05:42 +00:00
cmXsNote_t * note ; // The cmXsNote_t* associated with this cmXsReorder_t record
2016-06-16 16:53:59 +00:00
2016-08-03 21:03:17 +00:00
unsigned dynIdx ; // cmInvalidIdx=ignore otherwise index into _cmXScoreDynMarkArray[]
unsigned newFlags ; // 0=ignore | kSostUp/DnXsFl | kDampUp/DnXsFl | kTieEndXsFl
unsigned newTick ; // 0=ignore >0 new tick value
unsigned graceFlags ; // 0=ignore See kXXXGraceXsFl
2016-07-27 23:05:42 +00:00
unsigned graceGroupId ; // 0=ignore >0=grace note group id
2016-08-03 21:03:17 +00:00
unsigned pitch ; // 0=ignore >0 new pitch
2016-04-06 21:35:09 +00:00
} cmXsReorder_t ;
2016-06-16 16:53:59 +00:00
typedef struct _cmXScoreDynMark_str
{
const cmChar_t * mark ; //
unsigned id ; // (1-17) maps to velocity
unsigned dyn ; // pppp - fff (1-9) as used by cmScore
int adjust ; // {-1,0,+1}
unsigned vel ; // associated MIDI velocity
} _cmXScoreDynMark_t ;
_cmXScoreDynMark_t _cmXScoreDynMarkArray [ ] =
{
{ " pppp- " , 1 , 1 , - 1 , 3 } ,
{ " pppp " , 2 , 1 , 0 , 10 } ,
{ " pppp+ " , 3 , 1 , 1 , 22 } ,
{ " ppp- " , 3 , 2 , - 1 , 22 } ,
{ " ppp " , 4 , 2 , 0 , 29 } ,
{ " ppp+ " , 5 , 2 , 1 , 36 } ,
{ " pp- " , 5 , 3 , - 1 , 36 } ,
{ " pp " , 6 , 3 , 0 , 43 } ,
{ " pp+ " , 7 , 3 , 1 , 50 } ,
{ " p- " , 7 , 4 , - 1 , 50 } ,
{ " p " , 8 , 4 , 0 , 57 } ,
{ " p+ " , 9 , 4 , 1 , 64 } ,
{ " mp- " , 9 , 5 , - 1 , 64 } ,
{ " mp " , 10 , 5 , 0 , 71 } ,
{ " mp+ " , 11 , 5 , 1 , 78 } ,
{ " mf- " , 11 , 6 , - 1 , 78 } ,
{ " mf " , 12 , 6 , 0 , 85 } ,
{ " mf+ " , 13 , 6 , 1 , 92 } ,
{ " f- " , 13 , 7 , - 1 , 92 } ,
{ " f " , 14 , 7 , 0 , 99 } ,
{ " f+ " , 15 , 7 , 1 , 106 } ,
{ " ff " , 16 , 8 , 0 , 113 } ,
{ " ff+ " , 17 , 8 , 1 , 120 } ,
{ " fff " , 18 , 9 , 0 , 127 } ,
{ NULL , 0 , 0 , 0 , 0 }
} ;
cmXsNote_t * _cmXsReorderFindNote ( cmXScore_t * p , unsigned measNumb , const cmXsReorder_t * r , unsigned iii )
2016-04-06 21:35:09 +00:00
{
cmXsPart_t * pp = p - > partL ;
for ( ; pp ! = NULL ; pp = pp - > link )
{
cmXsMeas_t * mp = pp - > measL ;
for ( ; mp ! = NULL ; mp = mp - > link )
2016-04-07 17:39:24 +00:00
if ( mp - > number = = measNumb )
{
2016-06-09 22:52:29 +00:00
cmXsNote_t * np = mp - > noteL ;
int index = 0 ;
2016-04-27 17:38:09 +00:00
for ( ; np ! = NULL ; np = np - > slink , + + index )
{
2016-04-06 21:35:09 +00:00
if ( np - > voice - > id = = r - > voice & &
np - > locIdx = = r - > locIdx & &
np - > tick = = r - > tick & &
np - > duration = = r - > durtn & &
np - > rvalue = = r - > rval & &
2016-04-26 21:30:49 +00:00
np - > pitch = = r - > midi & &
2016-06-16 16:53:59 +00:00
index = = r - > idx )
2016-04-06 21:35:09 +00:00
{
return np ;
}
2016-04-27 17:38:09 +00:00
}
2016-04-06 21:35:09 +00:00
}
}
2016-06-09 22:52:29 +00:00
cmErrMsg ( & p - > err , kSyntaxErrorXsRC , " Reorder note not found meas:%i index:%i. " , measNumb , iii ) ;
2016-04-06 21:35:09 +00:00
return NULL ;
}
2016-06-23 23:16:41 +00:00
void _cmXScoreInsertPedalEvent ( cmXScore_t * p , const cmXsReorder_t * r , unsigned flags )
{
// Create a new score event record
cmXsNote_t * nn = cmLhAllocZ ( p - > lhH , cmXsNote_t , 1 ) ;
nn - > uid = p - > nextUid + + ;
nn - > voice = r - > note - > voice ;
nn - > meas = r - > note - > meas ;
nn - > flags = flags ;
// Pedal down events occur after the event they are attached to
2016-07-20 23:16:00 +00:00
if ( cmIsFlag ( flags , kSostDnXsFl | kDampDnXsFl ) )
2016-06-23 23:16:41 +00:00
{
nn - > tick = r - > note - > tick + 1 ;
_cmXScoreInsertNoteAfter ( r - > note , nn ) ;
}
else
{
// Pedal up events occur before the event they are attached to
2016-07-20 23:16:00 +00:00
if ( cmIsFlag ( flags , kSostUpXsFl | kDampUpXsFl ) )
2016-06-23 23:16:41 +00:00
{
nn - > tick = r - > note - > tick = = 0 ? 0 : r - > note - > tick - 1 ;
_cmXScoreInsertNoteBefore ( r - > note , nn ) ;
}
else
{ assert ( 0 ) ; }
}
}
2016-04-06 21:35:09 +00:00
cmXsRC_t _cmXScoreReorderMeas ( cmXScore_t * p , unsigned measNumb , cmXsReorder_t * rV , unsigned rN )
{
unsigned i ;
2016-04-07 17:39:24 +00:00
2016-04-06 21:35:09 +00:00
if ( rN = = 0 )
return kOkXsRC ;
// set the 'note' field on each cmXsReorder_t record
for ( i = 0 ; i < rN ; + + i )
2016-06-16 16:53:59 +00:00
if ( ( rV [ i ] . note = _cmXsReorderFindNote ( p , measNumb , rV + i , i ) ) = = NULL )
2016-04-06 21:35:09 +00:00
return kSyntaxErrorXsRC ;
2016-06-16 22:04:28 +00:00
2016-04-20 22:53:24 +00:00
cmXsMeas_t * mp = rV [ 0 ] . note - > meas ;
2016-04-06 21:35:09 +00:00
cmXsNote_t * n0p = NULL ;
assert ( mp - > number = = measNumb ) ;
2016-04-07 17:39:24 +00:00
2016-04-06 21:35:09 +00:00
// Reassign the slink of the cmXsNote_t records in this measure
// according to their order in rV[].
for ( i = 0 ; i < rN ; + + i )
{
if ( n0p = = NULL )
mp - > noteL = rV [ i ] . note ;
else
n0p - > slink = rV [ i ] . note ;
2016-06-16 16:53:59 +00:00
// if a new tick was specified
2016-06-09 22:52:29 +00:00
if ( rV [ i ] . newTick ! = 0 )
rV [ i ] . note - > tick = rV [ i ] . newTick ;
2016-06-16 22:04:28 +00:00
// if a dynamic or velocity mark was included
2016-06-16 16:53:59 +00:00
if ( rV [ i ] . dynIdx ! = cmInvalidIdx )
{
rV [ i ] . note - > dynamics = _cmXScoreDynMarkArray [ rV [ i ] . dynIdx ] . dyn ;
rV [ i ] . note - > vel = _cmXScoreDynMarkArray [ rV [ i ] . dynIdx ] . vel ;
}
2016-06-16 22:04:28 +00:00
// if the tie end flag was set
if ( cmIsFlag ( rV [ i ] . newFlags , kTieEndXsFl ) )
{
rV [ i ] . note - > flags | = kTieEndXsFl ;
rV [ i ] . note - > flags = cmClrFlag ( rV [ i ] . note - > flags , kOnsetXsFl ) ;
rV [ i ] . newFlags = cmClrFlag ( rV [ i ] . newFlags , kTieEndXsFl ) ;
}
// if a new note value was specified
if ( rV [ i ] . pitch ! = 0 )
rV [ i ] . note - > pitch = rV [ i ] . pitch ;
2016-08-03 21:03:17 +00:00
rV [ i ] . note - > flags | = rV [ i ] . graceFlags ;
rV [ i ] . note - > graceGroupId = rV [ i ] . graceGroupId ;
2016-06-16 16:53:59 +00:00
2016-06-16 22:04:28 +00:00
n0p = rV [ i ] . note ;
2016-04-06 21:35:09 +00:00
n0p - > slink = NULL ;
}
2016-04-07 17:39:24 +00:00
2016-06-16 22:04:28 +00:00
// Insert new note records for pedal up/dn events.
for ( i = 0 ; i < rN ; + + i )
{
if ( rV [ i ] . newFlags ! = 0 )
{
2016-07-20 23:16:00 +00:00
if ( cmIsFlag ( rV [ i ] . newFlags , kDampDnXsFl ) )
_cmXScoreInsertPedalEvent ( p , rV + i , kDampDnXsFl ) ;
2016-06-16 22:04:28 +00:00
2016-06-23 23:16:41 +00:00
if ( cmIsFlag ( rV [ i ] . newFlags , kSostDnXsFl ) )
_cmXScoreInsertPedalEvent ( p , rV + i , kSostDnXsFl ) ;
2016-07-20 23:16:00 +00:00
if ( cmIsFlag ( rV [ i ] . newFlags , kDampUpXsFl ) )
_cmXScoreInsertPedalEvent ( p , rV + i , kDampUpXsFl ) ;
2016-06-23 23:16:41 +00:00
if ( cmIsFlag ( rV [ i ] . newFlags , kSostUpXsFl ) )
_cmXScoreInsertPedalEvent ( p , rV + i , kSostUpXsFl ) ;
2016-06-16 22:04:28 +00:00
}
}
2016-04-07 17:39:24 +00:00
return kOkXsRC ;
2016-04-06 21:35:09 +00:00
}
2016-06-16 16:53:59 +00:00
cmXsRC_t _cmXScoreReorderParseDyn ( cmXScore_t * p , const cmChar_t * b , unsigned lineNumb , unsigned * dynIdxRef )
2016-06-09 22:52:29 +00:00
{
cmXsRC_t rc = kOkXsRC ;
const cmChar_t * s ;
2016-06-16 16:53:59 +00:00
* dynIdxRef = cmInvalidIdx ;
// locate the '!' which indicates the start of a dynamic marking
2016-06-09 22:52:29 +00:00
if ( ( s = strchr ( b , ' ! ' ) ) = = NULL )
return rc ;
2016-06-16 16:53:59 +00:00
+ + s ; // increment past the '!'
2016-06-09 22:52:29 +00:00
if ( * s = = 0 )
return cmErrMsg ( & p - > err , kSyntaxErrorXsRC , " Unexpected end-of-line on dynamics parsing on line:%i. " , lineNumb ) ;
2016-06-16 16:53:59 +00:00
// some dynamic markings are surrounded by parenthesis (to indicate a dynamic level with greater uncertainty)
2016-06-09 22:52:29 +00:00
if ( * s = = ' ( ' )
2016-06-16 16:53:59 +00:00
+ + s ; // skip the paren.
2016-06-09 22:52:29 +00:00
if ( * s = = 0 )
return cmErrMsg ( & p - > err , kSyntaxErrorXsRC , " Unexpected end-of-line on dynamics parsing on line:%i. " , lineNumb ) ;
unsigned i = 0 ;
unsigned j = 0 ;
unsigned n = 6 ;
bool doneFl = false ;
cmChar_t mark [ n + 1 ] ;
memset ( mark , 0 , n + 1 ) ;
2016-06-16 16:53:59 +00:00
for ( i = 0 ; j < n & & doneFl = = false ; + + i )
2016-06-09 22:52:29 +00:00
{
switch ( s [ i ] )
{
case ' m ' :
case ' p ' :
case ' f ' :
case ' + ' :
case ' - ' :
mark [ j + + ] = s [ i ] ;
break ;
2016-06-16 16:53:59 +00:00
case ' ) ' : // ending paren.
case 0 : // end of string
case ' ' : // end of mark
case ' \n ' : // end of line
default : // anything else
2016-06-09 22:52:29 +00:00
doneFl = true ;
break ;
}
}
if ( ! doneFl )
return cmErrMsg ( & p - > err , kSyntaxErrorXsRC , " Illegal dynamic mark (%s) syntax on line : % i . " ,mark,lineNumb) ;
2016-06-16 16:53:59 +00:00
// look up the dynamic mark in the reference table
2016-06-09 22:52:29 +00:00
for ( j = 0 ; _cmXScoreDynMarkArray [ j ] . mark ! = NULL ; + + j )
2016-06-16 16:53:59 +00:00
if ( strcmp ( mark , _cmXScoreDynMarkArray [ j ] . mark ) = = 0 )
2016-06-09 22:52:29 +00:00
break ;
2016-06-16 16:53:59 +00:00
if ( _cmXScoreDynMarkArray [ j ] . mark = = NULL )
2016-06-09 22:52:29 +00:00
return cmErrMsg ( & p - > err , kSyntaxErrorXsRC , " The dynamic mark '%s' is not legal on line:%i. " , mark , lineNumb ) ;
2016-06-16 16:53:59 +00:00
* dynIdxRef = j ;
2016-06-09 22:52:29 +00:00
return rc ;
}
2016-06-16 22:04:28 +00:00
cmXsRC_t _cmXScoreReorderParseFlags ( cmXScore_t * p , const cmChar_t * b , unsigned line , unsigned * newFlagsRef )
2016-06-09 22:52:29 +00:00
{
cmXsRC_t rc = kOkXsRC ;
const cmChar_t * s ;
2016-06-16 22:04:28 +00:00
bool doneFl = false ;
unsigned i = 0 ;
* newFlagsRef = 0 ;
// tilde indicates a pedal event
2016-06-09 22:52:29 +00:00
if ( ( s = strchr ( b , ' ~ ' ) ) = = NULL )
return rc ;
2016-06-16 22:04:28 +00:00
do
2016-06-09 22:52:29 +00:00
{
2016-06-16 22:04:28 +00:00
+ + s ;
2016-06-09 22:52:29 +00:00
2016-06-16 22:04:28 +00:00
switch ( * s )
{
case ' d ' :
* newFlagsRef | = kSostDnXsFl ; // sostenuto pedal down just after this note onset
break ;
case ' u ' :
2016-06-23 23:16:41 +00:00
* newFlagsRef | = kSostUpXsFl ; // sostenuto pedal up just before this event
2016-06-16 22:04:28 +00:00
break ;
2016-06-23 23:16:41 +00:00
case ' x ' :
* newFlagsRef | = ( kSostUpXsFl | kSostDnXsFl ) ; // sostenuto pedal up just before this event and sost down just after it.
break ;
2016-06-16 22:04:28 +00:00
case ' D ' :
2016-07-20 23:16:00 +00:00
* newFlagsRef | = kDampDnXsFl ; // damper pedal down
2016-06-16 22:04:28 +00:00
break ;
2016-06-09 22:52:29 +00:00
2016-06-16 22:04:28 +00:00
case ' U ' :
2016-07-20 23:16:00 +00:00
* newFlagsRef | = kDampUpXsFl ; // damper pedal up
2016-06-16 22:04:28 +00:00
break ;
case ' _ ' :
* newFlagsRef | = kTieEndXsFl ; // set tie end flag
break ;
default :
if ( i = = 0 )
return cmErrMsg ( & p - > err , kSyntaxErrorXsRC , " Unexpected flag marking '%c' on line %i. " , * s , line ) ;
doneFl = true ;
}
+ + i ;
} while ( ! doneFl ) ;
2016-06-09 22:52:29 +00:00
return rc ;
}
cmXsRC_t _cmXScoreReorderParseTick ( cmXScore_t * p , const cmChar_t * b , unsigned line , unsigned * tickRef )
{
cmXsRC_t rc = kOkXsRC ;
const cmChar_t * s ;
if ( ( s = strchr ( b , ' @ ' ) ) = = NULL )
return rc ;
+ + s ;
if ( ! isdigit ( * s ) )
return cmErrMsg ( & p - > err , kSyntaxErrorXsRC , " Unexpected tick reorder value '%c' on line %i. " , * s , line ) ;
if ( sscanf ( s , " %i " , tickRef ) ! = 1 )
return cmErrMsg ( & p - > err , kSyntaxErrorXsRC , " tick reorder parse failed on line %i. " , line ) ;
return rc ;
}
2016-08-03 21:03:17 +00:00
cmXsRC_t _cmXScoreReorderParseGrace ( cmXScore_t * p , const cmChar_t * b , unsigned line , cmXsReorder_t * r , unsigned * graceGroupIdRef )
2016-07-27 23:05:42 +00:00
{
cmXsRC_t rc = kOkXsRC ;
const cmChar_t * s ;
if ( ( s = strchr ( b , ' % ' ) ) = = NULL )
return rc ;
+ + s ;
2016-08-03 21:03:17 +00:00
r - > graceGroupId = * graceGroupIdRef ;
while ( 1 )
2016-07-27 23:05:42 +00:00
{
2016-08-03 21:03:17 +00:00
switch ( * s )
{
case ' b ' : r - > graceFlags | = kBegGraceXsFl ; break ;
case ' a ' : r - > graceFlags | = kAddGraceXsFl | kEndGraceXsFl ; break ;
case ' s ' : r - > graceFlags | = kSubGraceXsFl | kEndGraceXsFl ; break ;
case ' f ' : r - > graceFlags | = kFirstGraceXsFl | kEndGraceXsFl ; break ;
case ' g ' : break ;
case ' % ' :
* graceGroupIdRef + = 1 ;
+ + s ;
continue ;
default :
{ assert ( 0 ) ; }
}
break ;
2016-07-27 23:05:42 +00:00
}
return rc ;
}
2016-06-16 22:04:28 +00:00
cmXsRC_t _cmXScoreReorderParsePitch ( cmXScore_t * p , const cmChar_t * b , unsigned line , unsigned * pitchRef )
{
cmXsRC_t rc = kOkXsRC ;
cmChar_t * s ;
cmChar_t buf [ 4 ] ;
unsigned i , j ;
memset ( buf , 0 , sizeof ( buf ) ) ;
* pitchRef = 0 ;
if ( ( s = strchr ( b , ' $ ' ) ) = = NULL )
return rc ;
+ + s ;
j = 2 ;
for ( i = 0 ; i < j & & s [ i ] ; + + i , + + s )
{
buf [ i ] = * s ;
if ( i = = 1 & & ( * s = = ' # ' | | * s = = ' b ' ) )
j = 3 ;
if ( i = = 0 & & strchr ( " ABCDEFG " , * s ) = = NULL )
return cmErrMsg ( & p - > err , kSyntaxErrorXsRC , " Illegal pitch letter ('%c') specification line % i . " ,*s,line) ;
if ( i = = 1 & & ! isdigit ( * s ) & & * s ! = ' # ' & & * s ! = ' b ' )
return cmErrMsg ( & p - > err , kSyntaxErrorXsRC , " Illegal pitch level ('%c') specification line % i . " ,*s,line) ;
if ( i = = 2 & & ! isdigit ( * s ) )
return cmErrMsg ( & p - > err , kSyntaxErrorXsRC , " Illegal pitch octave ('%c') specification line % i . " ,*s,line) ;
}
unsigned pitch = cmSciPitchToMidi ( buf ) ;
if ( pitch < kInvalidMidiByte )
* pitchRef = pitch ;
else
rc = cmErrMsg ( & p - > err , kSyntaxErrorXsRC , " Pitch conversion from '%s' failed on line %i. " , buf , line ) ;
2016-07-27 23:05:42 +00:00
return rc ;
2016-06-16 22:04:28 +00:00
}
2016-04-06 21:35:09 +00:00
cmXsRC_t cmXScoreReorder ( cmXsH_t h , const cmChar_t * fn )
{
typedef enum { kFindMeasStId , kFindEventStId , kReadEventStId } stateId_t ;
2016-07-27 23:05:42 +00:00
cmXsRC_t rc = kOkXsRC ;
cmXScore_t * p = _cmXScoreHandleToPtr ( h ) ;
cmFileH_t fH = cmFileNullHandle ;
cmChar_t * b = NULL ;
unsigned bN = 0 ;
unsigned ln = 0 ;
stateId_t stateId = kFindMeasStId ;
unsigned rN = 1024 ;
unsigned ri = 0 ;
unsigned measNumb = 0 ;
unsigned graceGroupId = 1 ;
2016-04-06 21:35:09 +00:00
cmXsReorder_t rV [ rN ] ;
2016-04-26 21:30:49 +00:00
2016-04-06 21:35:09 +00:00
if ( cmFileOpen ( & fH , fn , kReadFileFl , p - > err . rpt ) ! = kOkFileRC )
{
rc = cmErrMsg ( & p - > err , kFileFailXsRC , " The reordering file '%s' could not be opened. " , cmStringNullGuard ( fn ) ) ;
return rc ;
}
2016-04-07 17:39:24 +00:00
2016-04-06 21:35:09 +00:00
for ( ; cmFileGetLineAuto ( fH , & b , & bN ) = = kOkFileRC ; + + ln )
{
switch ( stateId )
{
2016-04-20 22:53:24 +00:00
case kFindEventStId : // scanning past labels to an event line
2016-04-06 21:35:09 +00:00
{
unsigned voice , loc ;
if ( sscanf ( b , " %i %i " , & voice , & loc ) ! = 2 )
continue ;
2016-04-07 17:39:24 +00:00
2016-04-06 21:35:09 +00:00
stateId = kReadEventStId ;
}
// fall through
2016-04-07 17:39:24 +00:00
2016-04-06 21:35:09 +00:00
case kReadEventStId :
{
cmXsReorder_t r ;
char pitchStr [ 4 ] ;
2016-04-27 17:38:09 +00:00
memset ( & r , 0 , sizeof ( r ) ) ;
2016-04-26 21:30:49 +00:00
2016-04-20 22:53:24 +00:00
// parse an event line
2016-06-09 22:52:29 +00:00
if ( sscanf ( b , " %i %i %i %i %i %f " , & r . idx , & r . voice , & r . locIdx , & r . tick , & r . durtn , & r . rval ) = = 6 )
2016-04-06 21:35:09 +00:00
{
2016-04-27 17:38:09 +00:00
assert ( strlen ( b ) > = 52 ) ;
2016-06-09 22:52:29 +00:00
int PC = 39 ; // text file column where first pitch char occurs
if ( b [ PC ] = = ' ' )
r . midi = 0 ;
else
2016-04-06 21:35:09 +00:00
{
2016-06-09 22:52:29 +00:00
pitchStr [ 0 ] = b [ PC + 0 ] ;
pitchStr [ 1 ] = b [ PC + 1 ] ;
pitchStr [ 2 ] = b [ PC + 2 ] ;
2016-04-27 17:38:09 +00:00
pitchStr [ 3 ] = 0 ;
if ( ! isdigit ( pitchStr [ 2 ] ) )
r . midi = 0 ;
else
2016-04-06 21:35:09 +00:00
{
2016-04-27 17:38:09 +00:00
if ( pitchStr [ 1 ] = = ' ' )
{
pitchStr [ 1 ] = pitchStr [ 2 ] ;
pitchStr [ 2 ] = 0 ;
}
2016-04-06 21:35:09 +00:00
2016-04-27 17:38:09 +00:00
r . midi = cmSciPitchToMidi ( pitchStr ) ;
}
2016-06-09 22:52:29 +00:00
2016-04-06 21:35:09 +00:00
}
2016-04-07 17:39:24 +00:00
2016-04-27 17:38:09 +00:00
2016-06-16 16:53:59 +00:00
// parse the dynamic marking following a '!'
if ( ( rc = _cmXScoreReorderParseDyn ( p , b , ln + 1 , & r . dynIdx ) ) ! = kOkXsRC )
2016-06-09 22:52:29 +00:00
goto errLabel ;
2016-04-27 17:38:09 +00:00
2016-06-16 22:04:28 +00:00
// parse the flag edits
if ( ( rc = _cmXScoreReorderParseFlags ( p , b , ln + 1 , & r . newFlags ) ) ! = kOkXsRC )
2016-06-09 22:52:29 +00:00
goto errLabel ;
2016-06-16 16:53:59 +00:00
// parse the @newtick marker
2016-06-09 22:52:29 +00:00
if ( ( rc = _cmXScoreReorderParseTick ( p , b , ln + 1 , & r . newTick ) ) ! = kOkXsRC )
2016-06-16 22:04:28 +00:00
goto errLabel ;
2016-07-27 23:05:42 +00:00
// parse the %grace note marker
2016-08-03 21:03:17 +00:00
if ( ( rc = _cmXScoreReorderParseGrace ( p , b , ln + 1 , & r , & graceGroupId ) ) ! = kOkXsRC )
2016-07-27 23:05:42 +00:00
goto errLabel ;
2016-06-16 22:04:28 +00:00
// parse the $pitch marker
if ( ( rc = _cmXScoreReorderParsePitch ( p , b , ln + 1 , & r . pitch ) ) ! = kOkXsRC )
goto errLabel ;
2016-07-27 23:05:42 +00:00
// process grace notes - these need to be processed separate from
// the _cmXScoreReorderMeas() because grace notes may cross measure boundaries.
2016-08-03 21:03:17 +00:00
/*
2016-07-27 23:05:42 +00:00
if ( r . graceType ! = 0 )
{
r . graceGroupId = graceGroupId ;
// if this is an end of a grace note group
if ( r . graceType ! = ' g ' & & r . graceType ! = ' b ' )
{
graceGroupId + = 1 ;
}
}
2016-08-03 21:03:17 +00:00
*/
2016-04-20 22:53:24 +00:00
// store the record
2016-04-06 21:35:09 +00:00
assert ( ri < rN ) ;
rV [ ri + + ] = r ;
2016-04-07 17:39:24 +00:00
2016-04-06 21:35:09 +00:00
continue ;
}
2016-04-20 22:53:24 +00:00
// the end of the measure was encountered -
// reorder the measure based on the cmXsReorder_t in rV[ri]
2016-06-09 22:52:29 +00:00
if ( ( rc = _cmXScoreReorderMeas ( p , measNumb , rV , ri ) ) ! = kOkXsRC )
goto errLabel ;
2016-04-06 21:35:09 +00:00
ri = 0 ;
stateId = kFindMeasStId ;
// fall through
}
2016-04-07 17:39:24 +00:00
2016-04-20 22:53:24 +00:00
case kFindMeasStId : // scanning for a bar-line
2016-04-06 21:35:09 +00:00
{
char colon ;
if ( sscanf ( b , " %i %c " , & measNumb , & colon ) = = 2 & & colon = = ' : ' )
{
//printf("meas: %i \n",measNumb);
stateId = kFindEventStId ;
2016-04-07 17:39:24 +00:00
2016-04-06 21:35:09 +00:00
}
}
break ;
}
2016-04-07 17:39:24 +00:00
2016-04-06 21:35:09 +00:00
}
2016-06-16 16:53:59 +00:00
// the ticks may have changed so the 'secs' and 'dsecs' must be updated
_cmXScoreSetAbsoluteTime ( p ) ;
2016-06-09 22:52:29 +00:00
2016-06-16 16:53:59 +00:00
// the bar lines should be the first event in the measure
_cmXScoreFixBarLines ( p ) ;
2016-06-09 22:52:29 +00:00
2016-08-03 21:03:17 +00:00
// resort to force the links to be correct
2016-06-16 16:53:59 +00:00
_cmXScoreSort ( p ) ;
2016-07-27 23:05:42 +00:00
2016-08-03 21:03:17 +00:00
// process the grace notes.
2016-07-27 23:05:42 +00:00
_cmXScoreProcessGraceNotes ( p ) ;
2016-08-03 21:03:17 +00:00
// inserting grace notes may have left the score unsorted
_cmXScoreSort ( p ) ;
2016-06-09 22:52:29 +00:00
errLabel :
2016-04-06 21:35:09 +00:00
cmFileClose ( & fH ) ;
cmMemFree ( b ) ;
return rc ;
}
2016-06-09 22:52:29 +00:00
2016-06-16 16:53:59 +00:00
2016-02-07 04:36:53 +00:00
2016-02-09 02:46:07 +00:00
/*
2016-04-07 17:39:24 +00:00
kMidiFileIdColScIdx = 0 ,
2016-02-09 02:46:07 +00:00
kTypeLabelColScIdx = 3 ,
kDSecsColScIdx = 4 ,
kSecsColScIdx = 5 ,
kD0ColScIdx = 9 ,
kD1ColScIdx = 10 ,
kPitchColScIdx = 11 ,
kBarColScIdx = 13 ,
kSkipColScIdx = 14 ,
kEvenColScIdx = 15 ,
kGraceColScIdx = 16 ,
kTempoColScIdx = 17 ,
kFracColScIdx = 18 ,
kDynColScIdx = 19 ,
kSectionColScIdx = 20 ,
kRecdPlayColScIdx = 21 ,
kRemarkColScIdx = 22
*/
cmXsRC_t _cmXScoreWriteCsvHdr ( cmXScore_t * p )
2016-02-04 16:33:42 +00:00
{
2016-02-09 02:46:07 +00:00
const cmChar_t * s [ ] =
{
" id " , " trk " , " evt " , " opcode " , " dticks " , " micros " , " status " ,
" meta " , " ch " , " d0 " , " d1 " , " arg0 " , " arg1 " , " bar " , " skip " ,
" even " , " grace " , " tempo " , " t frac " , " dyn " , " section " , " play_recd " , " remark " , NULL
} ;
2016-02-04 16:33:42 +00:00
2016-02-09 02:46:07 +00:00
cmCsvCell_t * lcp = NULL ;
2016-04-07 17:39:24 +00:00
2016-02-09 02:46:07 +00:00
if ( cmCsvAppendRow ( p - > csvH , & lcp , cmCsvInsertSymText ( p - > csvH , s [ 0 ] ) , 0 , 0 ) ! = kOkCsvRC )
return cmErrMsg ( & p - > err , kCsvFailXsRC , " CSV append row failed. " ) ;
2016-04-07 17:39:24 +00:00
2016-02-09 02:46:07 +00:00
unsigned i ;
for ( i = 1 ; s [ i ] ! = NULL ; + + i )
{
2016-03-02 23:52:40 +00:00
if ( cmCsvInsertIdentColAfter ( p - > csvH , lcp , & lcp , s [ i ] , 0 ) ! = kOkCsvRC )
2016-02-09 02:46:07 +00:00
return cmErrMsg ( & p - > err , kCsvFailXsRC , " CSV error inserting CSV title %i. \n " , i ) ;
}
return kOkXsRC ;
}
cmXsRC_t _cmXScoreWriteCsvBlankCols ( cmXScore_t * p , unsigned cnt , cmCsvCell_t * * leftCellPtrPtr )
{
unsigned i ;
for ( i = 0 ; i < cnt ; + + i )
2016-03-02 23:52:40 +00:00
if ( cmCsvInsertIdentColAfter ( p - > csvH , * leftCellPtrPtr , leftCellPtrPtr , " " , 0 ) ! = kOkCsvRC )
2016-02-09 02:46:07 +00:00
return cmErrMsg ( & p - > err , kCsvFailXsRC , " CSV output failed on blank column. " ) ;
2016-04-07 17:39:24 +00:00
return kOkCsvRC ;
2016-02-09 02:46:07 +00:00
}
2016-04-07 17:39:24 +00:00
const cmChar_t * _cmXScoreTranslateDynamics ( cmXScore_t * p , const cmXsNote_t * np , cmChar_t * buf , unsigned bufN )
2016-03-30 23:18:44 +00:00
{
if ( cmIsFlag ( np - > flags , kDynXsFl ) & & np - > dynamics ! = 0 )
{
2016-04-07 17:39:24 +00:00
const cmChar_t * dynStr = NULL ;
2016-03-30 23:18:44 +00:00
switch ( np - > dynamics )
{
2016-04-07 17:39:24 +00:00
case 1 : dynStr = " pppp " ; break ;
case 2 : dynStr = " ppp " ; break ;
case 3 : dynStr = " pp " ; break ;
case 4 : dynStr = " p " ; break ;
case 5 : dynStr = " mp " ; break ;
case 6 : dynStr = " mf " ; break ;
case 7 : dynStr = " f " ; break ;
case 8 : dynStr = " ff " ; break ;
case 9 : dynStr = " fff " ; break ;
default :
cmErrMsg ( & p - > err , kSyntaxErrorXsRC , " An invalid dynamic value (%i) was encountered. " , np - > dynamics ) ;
goto errLabel ;
2016-03-30 23:18:44 +00:00
}
2016-04-07 17:39:24 +00:00
if ( np - > dynGroupId = = 0 )
snprintf ( buf , bufN , " %s " , dynStr ) ;
else
snprintf ( buf , bufN , " %s %i " , dynStr , np - > dynGroupId ) ;
return buf ;
2016-03-30 23:18:44 +00:00
}
2016-04-07 17:39:24 +00:00
errLabel :
2016-03-30 23:18:44 +00:00
return " " ;
}
2016-04-07 17:39:24 +00:00
const cmChar_t * cmXsFormatMeasurementCsvField ( unsigned flags , unsigned fl , char abbrev , unsigned sectionId , char * buf , unsigned bufN )
{
assert ( bufN > 1 ) ;
buf [ 0 ] = ' ' ;
buf [ 1 ] = 0 ;
if ( cmIsFlag ( flags , fl ) )
{
if ( sectionId ! = 0 )
2016-04-14 23:37:26 +00:00
snprintf ( buf , bufN - 1 , " %c %i%c " , abbrev , sectionId , cmIsFlag ( flags , kHeelXsFl ) ? ' * ' : ' ' ) ;
2016-04-07 17:39:24 +00:00
else
buf [ 0 ] = abbrev ;
}
return buf ;
}
2016-02-09 02:46:07 +00:00
cmXsRC_t _cmXScoreWriteCsvRow (
cmXScore_t * p ,
unsigned rowIdx ,
2016-03-02 23:52:40 +00:00
unsigned uid ,
2016-02-09 02:46:07 +00:00
unsigned bar ,
const cmChar_t * sectionStr ,
const cmChar_t * opCodeStr ,
double dsecs ,
double secs ,
unsigned d0 ,
unsigned d1 ,
2016-03-02 23:52:40 +00:00
unsigned pitch , // set to -1 if the pitch is not valid
2016-02-09 02:46:07 +00:00
double frac ,
2016-03-30 23:18:44 +00:00
const cmChar_t * dynStr ,
2016-04-07 17:39:24 +00:00
unsigned flags ,
const cmChar_t * evenStr ,
const cmChar_t * tempoStr )
2016-02-09 02:46:07 +00:00
{
cmXsRC_t rc = kOkXsRC ;
cmCsvCell_t * lcp = NULL ;
// append an empty row to the CSV object
if ( cmCsvAppendRow ( p - > csvH , & lcp , cmCsvInsertSymUInt ( p - > csvH , rowIdx ) , 0 , 0 ) ! = kOkCsvRC )
{
rc = cmErrMsg ( & p - > err , kCsvFailXsRC , " CSV append row failed. " ) ;
goto errLabel ;
}
/*
// col 0 : blanks
if ( cmCsvInsertUIntColAfter ( p - > csvH , lcp , & lcp , rowIdx , 0 ) ! = kOkCsvRC )
{
rc = cmErrMsg ( & p - > err , kCsvFailXsRC , " CSV output row index failed. " ) ;
goto errLabel ;
}
*/
2016-03-02 23:52:40 +00:00
// col 1 : track (always 1)
if ( cmCsvInsertUIntColAfter ( p - > csvH , lcp , & lcp , 1 , 0 ) ! = kOkCsvRC )
{
rc = cmErrMsg ( & p - > err , kCsvFailXsRC , " CSV insert failed on 'd0'. " ) ;
2016-02-09 02:46:07 +00:00
goto errLabel ;
2016-03-02 23:52:40 +00:00
}
2016-02-09 02:46:07 +00:00
2016-03-02 23:52:40 +00:00
// col 2 : evt (set to event uid, or blank if uid == -1)
if ( uid = = - 1 )
{
if ( ( rc = _cmXScoreWriteCsvBlankCols ( p , 1 , & lcp ) ) ! = kOkXsRC )
goto errLabel ;
2016-04-07 17:39:24 +00:00
}
2016-03-02 23:52:40 +00:00
else
if ( cmCsvInsertUIntColAfter ( p - > csvH , lcp , & lcp , uid , 0 ) ! = kOkCsvRC )
{
rc = cmErrMsg ( & p - > err , kCsvFailXsRC , " CSV insert failed on 'd0'. " ) ;
goto errLabel ;
}
2016-04-07 17:39:24 +00:00
2016-02-09 02:46:07 +00:00
// col 3 : output the opcode
2016-03-02 23:52:40 +00:00
if ( cmCsvInsertIdentColAfter ( p - > csvH , lcp , & lcp , opCodeStr , 0 ) ! = kOkCsvRC )
2016-02-09 02:46:07 +00:00
{
rc = cmErrMsg ( & p - > err , kCsvFailXsRC , " CSV insert failed on opcode label. " ) ;
goto errLabel ;
}
// col 4 : dsecs
if ( cmCsvInsertDoubleColAfter ( p - > csvH , lcp , & lcp , dsecs , 0 ) ! = kOkCsvRC )
{
rc = cmErrMsg ( & p - > err , kCsvFailXsRC , " CSV insert failed on 'dsecs'. " ) ;
goto errLabel ;
}
// col 5 : secs
if ( cmCsvInsertDoubleColAfter ( p - > csvH , lcp , & lcp , secs , 0 ) ! = kOkCsvRC )
{
rc = cmErrMsg ( & p - > err , kCsvFailXsRC , " CSV insert failed on 'secs'. " ) ;
goto errLabel ;
}
2016-04-07 17:39:24 +00:00
2016-02-09 02:46:07 +00:00
// cols 6,7,8 blanks
if ( ( rc = _cmXScoreWriteCsvBlankCols ( p , 3 , & lcp ) ) ! = kOkXsRC )
goto errLabel ;
// col 9 : d0
if ( cmCsvInsertUIntColAfter ( p - > csvH , lcp , & lcp , d0 , 0 ) ! = kOkCsvRC )
{
rc = cmErrMsg ( & p - > err , kCsvFailXsRC , " CSV insert failed on 'd0'. " ) ;
goto errLabel ;
}
// col 10 : d1
if ( cmCsvInsertUIntColAfter ( p - > csvH , lcp , & lcp , d1 , 0 ) ! = kOkCsvRC )
{
rc = cmErrMsg ( & p - > err , kCsvFailXsRC , " CSV insert failed on 'd1'. " ) ;
goto errLabel ;
}
// col 11 : pitch
2016-03-02 23:52:40 +00:00
if ( pitch = = - 1 )
2016-02-09 02:46:07 +00:00
{
2016-03-02 23:52:40 +00:00
if ( ( rc = _cmXScoreWriteCsvBlankCols ( p , 1 , & lcp ) ) ! = kOkXsRC )
goto errLabel ;
}
else
{
if ( cmCsvInsertIdentColAfter ( p - > csvH , lcp , & lcp , cmMidiToSciPitch ( pitch , NULL , 0 ) , 0 ) ! = kOkCsvRC )
{
rc = cmErrMsg ( & p - > err , kCsvFailXsRC , " CSV insert failed on 'pitch'. " ) ;
goto errLabel ;
}
2016-02-09 02:46:07 +00:00
}
2016-04-07 17:39:24 +00:00
2016-02-09 02:46:07 +00:00
// col 12 : blanks
if ( ( rc = _cmXScoreWriteCsvBlankCols ( p , 1 + ( cmIsFlag ( flags , kBarXsFl ) ? 0 : 1 ) , & lcp ) ) ! = kOkXsRC )
goto errLabel ;
// col 13 : bar number
if ( cmIsFlag ( flags , kBarXsFl ) )
{
if ( cmCsvInsertUIntColAfter ( p - > csvH , lcp , & lcp , bar , 0 ) ! = kOkCsvRC )
{
rc = cmErrMsg ( & p - > err , kCsvFailXsRC , " CSV insert failed on 'pitch'. " ) ;
goto errLabel ;
}
}
// col 14 : skip (blank for now)
if ( ( rc = _cmXScoreWriteCsvBlankCols ( p , 1 , & lcp ) ) ! = kOkXsRC )
goto errLabel ;
2016-04-07 17:39:24 +00:00
2016-03-02 23:52:40 +00:00
// col 15: even (all grace notes are 'even' notes
2016-04-20 22:53:24 +00:00
if ( cmCsvInsertQTextColAfter ( p - > csvH , lcp , & lcp , evenStr , 0 ) ! = kOkCsvRC )
2016-02-09 02:46:07 +00:00
{
rc = cmErrMsg ( & p - > err , kCsvFailXsRC , " CSV insert failed on eveness flag label. " ) ;
goto errLabel ;
}
2016-04-07 17:39:24 +00:00
// col 16: grace
2016-03-02 23:52:40 +00:00
if ( cmCsvInsertIdentColAfter ( p - > csvH , lcp , & lcp , cmIsFlag ( flags , kGraceXsFl ) ? " g " : " " , 0 ) ! = kOkCsvRC )
2016-02-09 02:46:07 +00:00
{
rc = cmErrMsg ( & p - > err , kCsvFailXsRC , " CSV insert failed on eveness flag label. " ) ;
goto errLabel ;
}
2016-04-07 17:39:24 +00:00
2016-02-09 02:46:07 +00:00
// col 17: tempo
2016-04-23 20:06:54 +00:00
if ( cmCsvInsertQTextColAfter ( p - > csvH , lcp , & lcp , tempoStr , 0 ) ! = kOkCsvRC )
2016-02-09 02:46:07 +00:00
{
rc = cmErrMsg ( & p - > err , kCsvFailXsRC , " CSV insert failed on eveness flag label. " ) ;
goto errLabel ;
}
2016-04-07 17:39:24 +00:00
2016-02-09 02:46:07 +00:00
// col 18: frac
if ( frac = = 0 )
{
if ( ( rc = _cmXScoreWriteCsvBlankCols ( p , 1 , & lcp ) ) ! = kOkXsRC )
goto errLabel ;
}
else
{
if ( cmCsvInsertDoubleColAfter ( p - > csvH , lcp , & lcp , 1.0 / frac , 0 ) ! = kOkCsvRC )
{
rc = cmErrMsg ( & p - > err , kCsvFailXsRC , " CSV insert failed on 't frac'. " ) ;
goto errLabel ;
}
}
2016-04-07 17:39:24 +00:00
2016-03-30 23:18:44 +00:00
// col 19: dynamic marking
2016-04-23 20:06:54 +00:00
if ( cmCsvInsertQTextColAfter ( p - > csvH , lcp , & lcp , dynStr , 0 ) ! = kOkCsvRC )
2016-03-30 23:18:44 +00:00
{
rc = cmErrMsg ( & p - > err , kCsvFailXsRC , " CSV insert failed on 'dynamics'. " ) ;
2016-04-07 17:39:24 +00:00
goto errLabel ;
2016-03-30 23:18:44 +00:00
}
//if((rc = _cmXScoreWriteCsvBlankCols(p,1,&lcp)) != kOkXsRC )
// goto errLabel;
2016-04-07 17:39:24 +00:00
2016-02-09 02:46:07 +00:00
// col 20: section
2016-04-07 17:39:24 +00:00
if ( cmCsvInsertIdentColAfter ( p - > csvH , lcp , & lcp , sectionStr ! = NULL ? sectionStr : " " , 0 ) ! = kOkCsvRC )
2016-02-09 02:46:07 +00:00
{
rc = cmErrMsg ( & p - > err , kCsvFailXsRC , " CSV insert failed on eveness flag label. " ) ;
goto errLabel ;
}
2016-04-07 17:39:24 +00:00
2016-02-09 02:46:07 +00:00
// col 21, 22 : recd-play, remark (blank for now)
if ( ( rc = _cmXScoreWriteCsvBlankCols ( p , 2 , & lcp ) ) ! = kOkXsRC )
goto errLabel ;
2016-04-07 17:39:24 +00:00
2016-02-09 02:46:07 +00:00
errLabel :
return rc ;
2016-04-07 17:39:24 +00:00
}
2016-03-30 23:18:44 +00:00
2016-02-09 02:46:07 +00:00
cmXsRC_t cmXScoreWriteCsv ( cmXsH_t h , const cmChar_t * csvFn )
{
cmXsRC_t rc = kOkXsRC ;
cmXScore_t * p = _cmXScoreHandleToPtr ( h ) ;
unsigned rowIdx = 1 ;
const cmChar_t * sectionIdStr = NULL ;
2016-04-07 17:39:24 +00:00
2016-02-09 02:46:07 +00:00
if ( ! cmCsvIsValid ( p - > csvH ) )
return cmErrMsg ( & p - > err , kCsvFailXsRC , " The CSV output object is not initialized. " ) ;
if ( ( rc = _cmXScoreWriteCsvHdr ( p ) ) ! = kOkXsRC )
goto errLabel ;
2016-04-07 17:39:24 +00:00
2016-02-09 02:46:07 +00:00
cmXsPart_t * pp = p - > partL ;
for ( ; pp ! = NULL ; pp = pp - > link )
{
cmXsMeas_t * mp = pp - > measL ;
for ( ; mp ! = NULL ; mp = mp - > link )
{
2016-04-07 17:39:24 +00:00
cmXsNote_t * np = mp - > noteL ;
2016-02-09 02:46:07 +00:00
for ( ; np ! = NULL ; np = np - > slink )
{
// if this is a section event
if ( cmIsFlag ( np - > flags , kSectionXsFl ) )
sectionIdStr = np - > tvalue ;
// if this is a bar event
2016-03-31 23:07:48 +00:00
if ( cmIsFlag ( np - > flags , kBarXsFl ) )
2016-02-09 02:46:07 +00:00
{
2016-04-07 17:39:24 +00:00
_cmXScoreWriteCsvRow ( p , rowIdx , - 1 , mp - > number , sectionIdStr , " bar " , np - > dsecs , np - > secs , 0 , 0 , - 1 , 0 , " " , np - > flags , " " , " " ) ;
2016-03-31 23:07:48 +00:00
sectionIdStr = NULL ;
2016-02-09 02:46:07 +00:00
}
else
{
2016-03-31 23:07:48 +00:00
// if this is a pedal event
2016-07-20 23:16:00 +00:00
if ( cmIsFlag ( np - > flags , kDampDnXsFl | kDampUpXsFl | kDampUpDnXsFl | kSostDnXsFl | kSostUpXsFl ) )
2016-02-09 02:46:07 +00:00
{
2016-06-16 22:04:28 +00:00
unsigned d0 = cmIsFlag ( np - > flags , kSostDnXsFl | kSostUpXsFl ) ? 66 : 64 ; // pedal MIDI ctl id
2016-07-20 23:16:00 +00:00
unsigned d1 = cmIsFlag ( np - > flags , kDampDnXsFl | kSostDnXsFl ) ? 64 : 0 ; // pedal-dn: d1>=64 pedal-up:<64
2016-04-07 17:39:24 +00:00
_cmXScoreWriteCsvRow ( p , rowIdx , - 1 , mp - > number , sectionIdStr , " ctl " , np - > dsecs , np - > secs , d0 , d1 , - 1 , 0 , " " , np - > flags , " " , " " ) ;
2016-02-09 02:46:07 +00:00
sectionIdStr = NULL ;
2016-04-26 21:30:49 +00:00
2016-07-20 23:16:00 +00:00
if ( cmIsFlag ( np - > flags , kDampUpDnXsFl ) )
2016-04-26 21:30:49 +00:00
{
rowIdx + = 1 ;
double millisecond = 0.0 ;
_cmXScoreWriteCsvRow ( p , rowIdx , - 1 , mp - > number , sectionIdStr , " ctl " , millisecond , np - > secs + millisecond , d0 , 64 , - 1 , 0 , " " , np - > flags , " " , " " ) ;
}
2016-02-09 02:46:07 +00:00
}
2016-04-07 17:39:24 +00:00
else
2016-03-31 23:07:48 +00:00
{
// if this is a sounding note event
if ( cmIsFlag ( np - > flags , kOnsetXsFl ) )
{
2016-04-07 17:39:24 +00:00
unsigned bufN = 128 ;
cmChar_t ebuf [ bufN + 1 ] ; ebuf [ bufN ] = 0 ;
cmChar_t dbuf [ bufN + 1 ] ; dbuf [ bufN ] = 0 ;
cmChar_t tbuf [ bufN + 1 ] ; tbuf [ bufN ] = 0 ;
2016-03-31 23:07:48 +00:00
double frac = np - > rvalue + ( cmIsFlag ( np - > flags , kDotXsFl ) ? ( np - > rvalue / 2 ) : 0 ) ;
2016-04-07 17:39:24 +00:00
const cmChar_t * dyn = _cmXScoreTranslateDynamics ( p , np , dbuf , bufN ) ;
2016-06-16 16:53:59 +00:00
unsigned vel = np - > vel = = 0 ? 60 : np - > vel ;
2016-04-07 17:39:24 +00:00
//
2016-06-16 16:53:59 +00:00
_cmXScoreWriteCsvRow ( p , rowIdx , np - > uid , mp - > number , sectionIdStr , " non " , np - > dsecs , np - > secs , np - > pitch , vel , np - > pitch , frac , dyn , np - > flags ,
2016-04-07 17:39:24 +00:00
cmXsFormatMeasurementCsvField ( np - > flags , kEvenXsFl , ' e ' , np - > evenGroupId , ebuf , bufN ) ,
cmXsFormatMeasurementCsvField ( np - > flags , kTempoXsFl , ' t ' , np - > tempoGroupId , tbuf , bufN ) ) ;
2016-03-31 23:07:48 +00:00
sectionIdStr = NULL ;
}
}
}
2016-04-07 17:39:24 +00:00
2016-02-09 02:46:07 +00:00
rowIdx + = 1 ;
}
2016-04-07 17:39:24 +00:00
}
2016-02-09 02:46:07 +00:00
}
2016-03-31 23:07:48 +00:00
// Section labels are output on the next bar/pedal/note
// but what if there is no bar pedal note-on after the section label
if ( sectionIdStr ! = NULL )
cmErrMsg ( & p - > err , kSyntaxErrorXsRC , " The section label '%s' was ignored because it was not followed by a score event. " , sectionIdStr ) ;
2016-02-09 02:46:07 +00:00
if ( cmCsvWrite ( p - > csvH , csvFn ) ! = kOkCsvRC )
rc = cmErrMsg ( & p - > err , kCsvFailXsRC , " The CSV output write failed on file '%s'. " , csvFn ) ;
errLabel :
return rc ;
}
2016-03-30 23:18:44 +00:00
void _cmXScoreReportTitle ( cmRpt_t * rpt )
{
2016-04-27 17:38:09 +00:00
cmRptPrintf ( rpt , " idx voc loc tick durtn rval flags \n " ) ;
2016-06-16 22:04:28 +00:00
cmRptPrintf ( rpt , " --- --- ----- ------- ----- ---- --- --------------- \n " ) ;
2016-03-30 23:18:44 +00:00
}
2016-04-07 17:39:24 +00:00
2016-04-27 17:38:09 +00:00
void _cmXScoreReportNote ( cmRpt_t * rpt , const cmXsNote_t * note , unsigned index )
2016-03-30 23:18:44 +00:00
{
const cmChar_t * B = cmIsFlag ( note - > flags , kBarXsFl ) ? " | " : " - " ;
const cmChar_t * R = cmIsFlag ( note - > flags , kRestXsFl ) ? " R " : " - " ;
const cmChar_t * G = cmIsFlag ( note - > flags , kGraceXsFl ) ? " G " : " - " ;
2016-03-31 23:07:48 +00:00
const cmChar_t * D = cmIsFlag ( note - > flags , kDotXsFl ) ? " . " : " - " ;
2016-03-30 23:18:44 +00:00
const cmChar_t * C = cmIsFlag ( note - > flags , kChordXsFl ) ? " C " : " - " ;
const cmChar_t * e = cmIsFlag ( note - > flags , kEvenXsFl ) ? " e " : " - " ;
const cmChar_t * d = cmIsFlag ( note - > flags , kDynXsFl ) ? " d " : " - " ;
const cmChar_t * t = cmIsFlag ( note - > flags , kTempoXsFl ) ? " t " : " - " ;
2016-07-20 23:16:00 +00:00
const cmChar_t * P = cmIsFlag ( note - > flags , kDampDnXsFl ) ? " V " : " - " ;
2016-06-16 22:04:28 +00:00
const cmChar_t * s = cmIsFlag ( note - > flags , kSostDnXsFl ) ? " { " : " - " ;
2016-03-30 23:18:44 +00:00
const cmChar_t * S = cmIsFlag ( note - > flags , kSectionXsFl ) ? " S " : " - " ;
const cmChar_t * H = cmIsFlag ( note - > flags , kHeelXsFl ) ? " H " : " - " ;
const cmChar_t * T0 = cmIsFlag ( note - > flags , kTieBegXsFl ) ? " T " : " - " ;
const cmChar_t * T1 = cmIsFlag ( note - > flags , kTieEndXsFl ) ? " _ " : " - " ;
2016-03-31 23:07:48 +00:00
const cmChar_t * O = cmIsFlag ( note - > flags , kOnsetXsFl ) ? " * " : " - " ;
2016-07-20 23:16:00 +00:00
P = cmIsFlag ( note - > flags , kDampUpXsFl ) ? " ^ " : P ;
P = cmIsFlag ( note - > flags , kDampUpDnXsFl ) ? " X " : P ;
2016-06-16 22:04:28 +00:00
s = cmIsFlag ( note - > flags , kSostUpXsFl ) ? " } " : s ;
2016-03-30 23:18:44 +00:00
//const cmChar_t* N = note->pitch==0 ? " " : cmMidiToSciPitch( note->pitch, NULL, 0 );
cmChar_t N [ ] = { ' \0 ' , ' \0 ' , ' \0 ' , ' \0 ' } ;
cmChar_t acc = note - > alter = = - 1 ? ' b ' : ( note - > alter = = 1 ? ' # ' : ' ' ) ;
snprintf ( N , 4 , " %c%c%1i " , note - > step , acc , note - > octave ) ;
2016-04-07 17:39:24 +00:00
2016-06-16 22:04:28 +00:00
cmRptPrintf ( rpt , " %3i %3i %5i %7i %5i %4.1f %3s %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s " ,
2016-04-27 17:38:09 +00:00
index ,
2016-03-30 23:18:44 +00:00
note - > voice - > id ,
note - > locIdx ,
note - > tick ,
note - > duration ,
note - > rvalue ,
2016-06-16 22:04:28 +00:00
N , B , R , G , D , C , e , d , t , P , s , S , H , T0 , T1 , O ) ;
2016-03-30 23:18:44 +00:00
if ( cmIsFlag ( note - > flags , kSectionXsFl ) )
cmRptPrintf ( rpt , " %s " , cmStringNullGuard ( note - > tvalue ) ) ;
if ( cmIsFlag ( note - > flags , kMetronomeXsFl ) )
cmRptPrintf ( rpt , " %i bpm " , note - > duration ) ;
2016-03-31 23:07:48 +00:00
if ( note - > evenGroupId ! = 0 )
cmRptPrintf ( rpt , " e=%i " , note - > evenGroupId ) ;
2016-04-07 17:39:24 +00:00
2016-03-31 23:07:48 +00:00
if ( note - > dynGroupId ! = 0 )
cmRptPrintf ( rpt , " d=%i " , note - > dynGroupId ) ;
2016-04-07 17:39:24 +00:00
2016-03-31 23:07:48 +00:00
if ( note - > tempoGroupId ! = 0 )
cmRptPrintf ( rpt , " t=%i " , note - > tempoGroupId ) ;
2016-08-03 21:03:17 +00:00
if ( note - > graceGroupId ! = 0 )
cmRptPrintf ( rpt , " g=%i " , note - > graceGroupId ) ;
2016-03-30 23:18:44 +00:00
}
2016-02-09 02:46:07 +00:00
void cmXScoreReport ( cmXsH_t h , cmRpt_t * rpt , bool sortFl )
{
cmXScore_t * p = _cmXScoreHandleToPtr ( h ) ;
2016-02-04 16:33:42 +00:00
cmXsPart_t * pp = p - > partL ;
2016-04-07 17:39:24 +00:00
2016-02-04 16:33:42 +00:00
for ( ; pp ! = NULL ; pp = pp - > link )
{
cmRptPrintf ( rpt , " Part:%s \n " , pp - > idStr ) ;
const cmXsMeas_t * meas = pp - > measL ;
for ( ; meas ! = NULL ; meas = meas - > link )
2016-02-07 04:36:53 +00:00
{
2016-04-27 17:38:09 +00:00
unsigned idx = 0 ;
2016-02-19 00:10:05 +00:00
cmRptPrintf ( rpt , " %i : div:%i beat:%i beat-type:%i (%i) \n " , meas - > number , meas - > divisions , meas - > beats , meas - > beat_type , meas - > divisions * meas - > beats ) ;
2016-02-07 04:36:53 +00:00
2016-03-30 23:18:44 +00:00
_cmXScoreReportTitle ( rpt ) ;
2016-02-07 04:36:53 +00:00
if ( sortFl )
{
2016-04-07 17:39:24 +00:00
2016-02-07 04:36:53 +00:00
const cmXsNote_t * note = meas - > noteL ;
2016-04-06 21:35:09 +00:00
unsigned t0 = 0 ;
unsigned t1 = 0 ;
2016-04-27 17:38:09 +00:00
for ( ; note ! = NULL ; note = note - > slink , + + idx )
2016-02-19 00:10:05 +00:00
{
2016-04-27 17:38:09 +00:00
_cmXScoreReportNote ( rpt , note , idx ) ;
2016-04-06 21:35:09 +00:00
t1 = note - > slink = = NULL ? note - > tick : note - > slink - > tick ;
if ( ! ( t0 < = note - > tick & & note - > tick < = t1 ) )
{
2016-04-07 17:39:24 +00:00
cmRptPrintf ( rpt , " + " ) ;
2016-04-06 21:35:09 +00:00
}
t0 = note - > tick ;
2016-04-07 17:39:24 +00:00
2016-02-19 00:10:05 +00:00
if ( note - > slink ! = NULL | | note - > voice - > id = = 0 )
cmRptPrintf ( rpt , " \n " ) ;
else
2016-04-07 17:39:24 +00:00
cmRptPrintf ( rpt , " %i \n " , note - > tick + note - > duration ) ;
2016-02-19 00:10:05 +00:00
}
2016-04-07 17:39:24 +00:00
2016-02-07 04:36:53 +00:00
}
else
{
2016-04-07 17:39:24 +00:00
2016-02-07 04:36:53 +00:00
const cmXsVoice_t * v = meas - > voiceL ;
for ( ; v ! = NULL ; v = v - > link )
2016-04-07 17:39:24 +00:00
{
2016-02-07 04:36:53 +00:00
const cmXsNote_t * note = v - > noteL ;
2016-04-07 17:39:24 +00:00
2016-02-07 04:36:53 +00:00
cmRptPrintf ( rpt , " voice:%i \n " , v - > id ) ;
2016-04-07 17:39:24 +00:00
2016-02-07 04:36:53 +00:00
for ( ; note ! = NULL ; note = note - > mlink )
2016-02-19 00:10:05 +00:00
{
2016-04-27 17:38:09 +00:00
_cmXScoreReportNote ( rpt , note , idx ) ;
2016-02-19 00:10:05 +00:00
if ( note - > mlink ! = NULL | | note - > voice - > id = = 0 )
cmRptPrintf ( rpt , " \n " ) ;
else
cmRptPrintf ( rpt , " %i \n " , note - > tick + note - > duration ) ;
}
2016-02-07 04:36:53 +00:00
}
2016-02-19 00:10:05 +00:00
}
2016-02-07 04:36:53 +00:00
}
2016-04-07 17:39:24 +00:00
}
2016-02-04 16:33:42 +00:00
}
2016-07-27 23:05:42 +00:00
cmXsRC_t _cmXsWriteMidiFile ( cmCtx_t * ctx , cmXsH_t h , const cmChar_t * dir , const cmChar_t * fn )
{
cmXsRC_t rc = kOkXsRC ;
cmXScore_t * p = _cmXScoreHandleToPtr ( h ) ;
if ( p - > partL = = NULL | | p - > partL - > measL = = NULL )
return rc ;
cmMidiFileH_t mfH = cmMidiFileNullHandle ;
unsigned trkN = 2 ;
unsigned ticksPerQN = p - > partL - > measL - > divisions ;
const cmChar_t * outFn = cmFsMakeFn ( dir , fn , " mid " , NULL ) ;
if ( cmMidiFileCreate ( ctx , & mfH , trkN , ticksPerQN ) ! = kOkMfRC )
return cmErrMsg ( & p - > err , kMidiFailXsRC , " Unable to create the MIDI file object. " ) ;
cmXsPart_t * pp = p - > partL ;
// for each part
for ( ; pp ! = NULL ; pp = pp - > link )
{
cmXsMeas_t * mp = pp - > measL ;
// for each measure
for ( ; mp ! = NULL ; mp = mp - > link )
{
cmXsNote_t * np = mp - > noteL ;
if ( mp - > divisions ! = ticksPerQN )
cmErrWarnMsg ( & p - > err , kMidiFailXsRC , " The 'tick per quarter note' (divisions) field in measure %i does not match the value in the first measure (%i). " , mp - > divisions , ticksPerQN ) ;
// for each note in this measure
for ( ; np ! = NULL ; np = np - > slink )
{
switch ( np - > flags & ( kOnsetXsFl | kMetronomeXsFl | kDampDnXsFl | kDampUpDnXsFl | kSostDnXsFl ) )
{
case kOnsetXsFl :
if ( cmMidiFileInsertTrackChMsg ( mfH , 1 , np - > tick , kNoteOnMdId , np - > pitch , np - > vel ) ! = kOkMfRC
| | cmMidiFileInsertTrackChMsg ( mfH , 1 , np - > tick + np - > duration , kNoteOffMdId , np - > pitch , 0 ) ! = kOkMfRC )
{
rc = kMidiFailXsRC ;
}
break ;
case kDampDnXsFl :
case kDampUpDnXsFl :
case kSostDnXsFl :
{
cmMidiByte_t d0 = cmIsFlag ( np - > flags , kSostDnXsFl ) ? kSostenutoCtlMdId : kSustainCtlMdId ;
if ( ( cmMidiFileInsertTrackChMsg ( mfH , 1 , np - > tick , kCtlMdId , d0 , 127 ) ! = kOkMfRC )
| | ( cmMidiFileInsertTrackChMsg ( mfH , 1 , np - > tick + np - > duration , kCtlMdId , d0 , 0 ) ! = kOkMfRC ) )
{
rc = kMidiFailXsRC ;
}
}
break ;
case kMetronomeXsFl :
if ( cmMidFileInsertTrackTempoMsg ( mfH , 0 , np - > tick , np - > duration ) ! = kOkMfRC )
rc = kMidiFailXsRC ;
break ;
case 0 :
break ;
default :
{ assert ( 0 ) ; }
}
if ( rc ! = kOkXsRC )
{
rc = cmErrMsg ( & p - > err , rc , " MIDI message insert failed on '%s'. " , cmStringNullGuard ( outFn ) ) ;
goto errLabel ;
}
}
}
}
if ( cmMidiFileWrite ( mfH , outFn ) ! = kOkMfRC )
{
rc = cmErrMsg ( & p - > err , kMidiFailXsRC , " MIDI file write failed on '%s'. " , cmStringNullGuard ( outFn ) ) ;
goto errLabel ;
}
errLabel :
cmFsFreeFn ( outFn ) ;
if ( cmMidiFileClose ( & mfH ) ! = kOkMfRC )
{
rc = cmErrMsg ( & p - > err , kMidiFailXsRC , " Unable to create the MIDI file object. " ) ;
goto errLabel ;
}
return rc ;
}
typedef struct cmXsSvgEvt_str
2016-07-14 23:08:13 +00:00
{
unsigned flags ; // k???XsFl
unsigned tick ; // start tick
unsigned durTicks ; // dur-ticks
unsigned voice ; // score voice number
unsigned d0 ; // MIDI d0 (barNumb)
unsigned d1 ; // MIDI d1
2016-07-27 23:05:42 +00:00
struct cmXsSvgEvt_str * link ;
} cmXsSvgEvt_t ;
2016-07-14 23:08:13 +00:00
typedef struct cmXsMidiFile_str
{
2016-07-27 23:05:42 +00:00
cmXsSvgEvt_t * elist ;
cmXsSvgEvt_t * eol ;
2016-07-14 23:08:13 +00:00
unsigned pitch_min ;
unsigned pitch_max ;
} cmXsMidiFile_t ;
cmXsRC_t _cmXsWriteMidiSvg ( cmCtx_t * ctx , cmXScore_t * p , cmXsMidiFile_t * mf , const cmChar_t * dir , const cmChar_t * fn )
{
cmXsRC_t rc = kOkXsRC ;
cmSvgH_t svgH = cmSvgNullHandle ;
2016-07-27 23:05:42 +00:00
cmXsSvgEvt_t * e = mf - > elist ;
2016-07-14 23:08:13 +00:00
unsigned noteHeight = 10 ;
const cmChar_t * svgFn = cmFsMakeFn ( dir , fn , " html " , NULL ) ;
const cmChar_t * cssFn = cmFsMakeFn ( NULL , fn , " css " , NULL ) ;
2016-07-20 23:16:00 +00:00
cmChar_t * t0 = NULL ; // temporary dynamic string
2016-07-14 23:08:13 +00:00
// create the SVG writer
if ( cmSvgWriterAlloc ( ctx , & svgH ) ! = kOkSvgRC )
{
rc = cmErrMsg ( & p - > err , kSvgFailXsRC , " Unable to create the MIDI SVG output file '%s'. " , cmStringNullGuard ( svgFn ) ) ;
goto errLabel ;
}
// for each MIDI file element
for ( ; e ! = NULL & & rc = = kOkXsRC ; e = e - > link )
{
2016-07-20 23:16:00 +00:00
switch ( e - > flags & ( kOnsetXsFl | kBarXsFl | kDampDnXsFl | kDampUpDnXsFl | kSostDnXsFl ) )
2016-07-14 23:08:13 +00:00
{
2016-07-20 23:16:00 +00:00
// if this is a note
case kOnsetXsFl :
{
const cmChar_t * classLabel = " note " ;
2016-07-14 23:08:13 +00:00
2016-07-20 23:16:00 +00:00
t0 = cmTsPrintfP ( t0 , " note_%i%s " , e - > voice , cmIsFlag ( e - > flags , kGraceXsFl ) ? " _g " : " " ) ;
2016-07-14 23:08:13 +00:00
2016-07-20 23:16:00 +00:00
if ( cmIsFlag ( e - > flags , kGraceXsFl ) )
classLabel = " grace " ;
if ( cmSvgWriterRect ( svgH , e - > tick , e - > d0 * noteHeight , e - > durTicks , noteHeight - 1 , t0 ) ! = kOkSvgRC )
rc = kSvgFailXsRC ;
else
if ( cmSvgWriterText ( svgH , e - > tick + e - > durTicks / 2 , e - > d0 * noteHeight + noteHeight / 2 , cmMidiToSciPitch ( e - > d0 , NULL , 0 ) , " pitch " ) ! = kOkSvgRC )
rc = kSvgFailXsRC ;
}
break ;
2016-07-14 23:08:13 +00:00
2016-07-20 23:16:00 +00:00
// if this is a bar
case kBarXsFl :
{
if ( cmSvgWriterLine ( svgH , e - > tick , 0 , e - > tick , 127 * noteHeight , " bar " ) ! = kOkSvgRC )
rc = kSvgFailXsRC ;
else
{
if ( cmSvgWriterText ( svgH , e - > tick , 10 , t0 = cmTsPrintfP ( t0 , " %i " , e - > d0 ) , " text " ) ! = kOkSvgRC )
rc = kSvgFailXsRC ;
}
}
break ;
// if this is a pedal event
case kDampDnXsFl :
case kDampUpDnXsFl :
case kSostDnXsFl :
{
const cmChar_t * classLabel = cmIsFlag ( e - > flags , kSostDnXsFl ) ? " sost " : " damp " ;
unsigned y = ( 128 + cmIsFlag ( e - > flags , kSostDnXsFl ) ? 1 : 0 ) * noteHeight ;
cmSvgWriterRect ( svgH , e - > tick , y , e - > durTicks , noteHeight - 1 , classLabel ) ;
}
break ;
2016-07-14 23:08:13 +00:00
}
}
2016-07-20 23:16:00 +00:00
2016-07-14 23:08:13 +00:00
if ( rc ! = kOkXsRC )
cmErrMsg ( & p - > err , kSvgFailXsRC , " SVG element insert failed. " ) ;
if ( rc = = kOkXsRC )
if ( cmSvgWriterWrite ( svgH , cssFn , svgFn ) ! = kOkSvgRC )
rc = cmErrMsg ( & p - > err , kSvgFailXsRC , " SVG file write to '%s' failed. " , cmStringNullGuard ( svgFn ) ) ;
errLabel :
cmSvgWriterFree ( & svgH ) ;
cmFsFreeFn ( svgFn ) ;
cmFsFreeFn ( cssFn ) ;
cmMemFree ( t0 ) ;
return rc ;
}
2016-07-20 23:16:00 +00:00
2016-07-27 23:05:42 +00:00
void _cmXsPushSvgEvent ( cmXScore_t * p , cmXsMidiFile_t * mf , unsigned flags , unsigned tick , unsigned durTick , unsigned voice , unsigned d0 , unsigned d1 )
2016-07-14 23:08:13 +00:00
{
2016-07-27 23:05:42 +00:00
cmXsSvgEvt_t * e = cmLhAllocZ ( p - > lhH , cmXsSvgEvt_t , 1 ) ;
2016-07-14 23:08:13 +00:00
e - > flags = flags ;
e - > tick = tick ;
e - > durTicks = durTick ;
e - > voice = voice ;
2016-07-20 23:16:00 +00:00
e - > d0 = d0 ; // note=pitch bar=number pedal=ctl# metronome=BPM
2016-07-14 23:08:13 +00:00
e - > d1 = d1 ;
if ( mf - > eol ! = NULL )
mf - > eol - > link = e ;
else
mf - > elist = e ;
// track the min/max pitch
if ( cmIsFlag ( flags , kOnsetXsFl ) )
{
mf - > pitch_min = mf - > eol = = NULL ? d0 : cmMin ( mf - > pitch_min , d0 ) ;
mf - > pitch_max = mf - > eol = = NULL ? d0 : cmMin ( mf - > pitch_max , d0 ) ;
}
mf - > eol = e ;
}
2016-02-26 00:10:54 +00:00
2016-07-27 23:05:42 +00:00
cmXsRC_t _cmXScoreGenSvg ( cmCtx_t * ctx , cmXsH_t h , const cmChar_t * dir , const cmChar_t * fn )
2016-02-18 00:00:15 +00:00
{
cmXScore_t * p = _cmXScoreHandleToPtr ( h ) ;
cmXsPart_t * pp = p - > partL ;
2016-07-27 23:05:42 +00:00
2016-07-14 23:08:13 +00:00
cmXsMidiFile_t mf ;
memset ( & mf , 0 , sizeof ( mf ) ) ;
2016-02-18 00:00:15 +00:00
for ( ; pp ! = NULL ; pp = pp - > link )
{
const cmXsMeas_t * meas = pp - > measL ;
for ( ; meas ! = NULL ; meas = meas - > link )
{
const cmXsNote_t * note = meas - > noteL ;
for ( ; note ! = NULL ; note = note - > slink )
{
2016-07-20 23:16:00 +00:00
// if this is a metronome marker
if ( cmIsFlag ( note - > flags , kMetronomeXsFl ) )
{
// set BPM as d0
2016-07-27 23:05:42 +00:00
_cmXsPushSvgEvent ( p , & mf , note - > flags , note - > tick , 0 , 0 , note - > duration , 0 ) ;
2016-07-20 23:16:00 +00:00
continue ;
}
2016-07-14 23:08:13 +00:00
// if this is a sounding note
if ( cmIsFlag ( note - > flags , kOnsetXsFl ) )
{
unsigned d0 = cmSciPitchToMidiPitch ( note - > step , note - > alter , note - > octave ) ;
unsigned durTick = note - > duration ;
if ( note - > tied ! = NULL )
{
cmXsNote_t * tn = note - > tied ;
for ( ; tn ! = NULL ; tn = tn - > tied )
durTick + = tn - > duration ;
}
2016-07-27 23:05:42 +00:00
_cmXsPushSvgEvent ( p , & mf , note - > flags , note - > tick , durTick , note - > voice - > id , d0 , note - > vel ) ;
2016-07-14 23:08:13 +00:00
continue ;
}
2016-04-07 17:39:24 +00:00
2016-07-14 23:08:13 +00:00
// if this is a bar event
if ( cmIsFlag ( note - > flags , kBarXsFl ) )
{
2016-07-27 23:05:42 +00:00
_cmXsPushSvgEvent ( p , & mf , note - > flags , note - > tick , 0 , 0 , note - > meas - > number , 0 ) ;
2016-07-14 23:08:13 +00:00
continue ;
}
2016-07-20 23:16:00 +00:00
// if this is a pedal event
if ( cmIsFlag ( note - > flags , kDampDnXsFl | kDampUpDnXsFl | kSostDnXsFl ) )
{
unsigned d0 = cmIsFlag ( note - > flags , kSostDnXsFl ) ? kSostenutoCtlMdId : kSustainCtlMdId ;
2016-07-27 23:05:42 +00:00
_cmXsPushSvgEvent ( p , & mf , note - > flags , note - > tick , note - > duration , 0 , d0 , 127 ) ;
2016-07-20 23:16:00 +00:00
continue ;
}
2016-07-14 23:08:13 +00:00
2016-02-18 00:00:15 +00:00
}
}
2016-04-07 17:39:24 +00:00
}
2016-07-14 23:08:13 +00:00
2016-07-20 23:16:00 +00:00
2016-07-14 23:08:13 +00:00
return _cmXsWriteMidiSvg ( ctx , p , & mf , dir , fn ) ;
2016-02-18 00:00:15 +00:00
}
2016-02-04 16:33:42 +00:00
2016-04-23 20:06:54 +00:00
cmXsRC_t cmXScoreTest (
cmCtx_t * ctx ,
const cmChar_t * xmlFn ,
2016-07-14 23:08:13 +00:00
const cmChar_t * reorderFn ,
const cmChar_t * csvOutFn ,
const cmChar_t * midiOutFn )
2016-02-04 16:33:42 +00:00
{
cmXsRC_t rc ;
cmXsH_t h = cmXsNullHandle ;
2016-04-07 17:39:24 +00:00
2016-07-14 23:08:13 +00:00
if ( ( rc = cmXScoreInitialize ( ctx , & h , xmlFn ) ) ! = kOkXsRC )
2016-02-04 16:33:42 +00:00
return cmErrMsg ( & ctx - > err , rc , " XScore alloc failed. " ) ;
2016-06-09 22:52:29 +00:00
if ( reorderFn ! = NULL )
cmXScoreReorder ( h , reorderFn ) ;
2016-07-27 23:05:42 +00:00
// assign durations to pedal down events
_cmXScoreProcessPedals ( _cmXScoreHandleToPtr ( h ) ) ;
2016-06-16 16:53:59 +00:00
2016-07-14 23:08:13 +00:00
if ( csvOutFn ! = NULL )
2016-04-20 22:53:24 +00:00
{
cmScH_t scH = cmScNullHandle ;
2016-04-23 20:06:54 +00:00
double srate = 44100.0 ;
2016-04-20 22:53:24 +00:00
2016-07-14 23:08:13 +00:00
cmXScoreWriteCsv ( h , csvOutFn ) ;
2016-04-23 20:06:54 +00:00
cmSymTblH_t stH = cmSymTblCreate ( cmSymTblNullHandle , 0 , ctx ) ;
2016-04-20 22:53:24 +00:00
2016-07-14 23:08:13 +00:00
if ( cmScoreInitialize ( ctx , & scH , csvOutFn , srate , NULL , 0 , NULL , NULL , stH ) ! = kOkScRC )
2016-04-20 22:53:24 +00:00
cmErrMsg ( & ctx - > err , kFileFailXsRC , " The generated CSV file could not be parsed. " ) ;
else
2016-04-23 20:06:54 +00:00
{
2016-04-26 21:30:49 +00:00
//cmScorePrintSets(scH,&ctx->rpt);
//cmScorePrint(scH,&ctx->rpt);
2016-04-23 20:06:54 +00:00
2016-04-20 22:53:24 +00:00
cmScoreFinalize ( & scH ) ;
2016-04-23 20:06:54 +00:00
}
2016-07-14 23:08:13 +00:00
cmSymTblDestroy ( & stH ) ;
}
if ( midiOutFn ! = NULL )
{
cmFileSysPathPart_t * pp = cmFsPathParts ( midiOutFn ) ;
2016-07-27 23:05:42 +00:00
_cmXScoreGenSvg ( ctx , h , pp - > dirStr , pp - > fnStr ) ;
_cmXsWriteMidiFile ( ctx , h , pp - > dirStr , pp - > fnStr ) ;
2016-07-14 23:08:13 +00:00
cmFsFreePathParts ( pp ) ;
2016-04-20 22:53:24 +00:00
}
2016-08-03 21:03:17 +00:00
cmXScoreReport ( h , & ctx - > rpt , true ) ;
2016-04-07 17:39:24 +00:00
2016-02-04 16:33:42 +00:00
return cmXScoreFinalize ( & h ) ;
}