mirror of
https://github.com/microtherion/VocalEasel.git
synced 2024-12-22 11:14:00 +00:00
Improve MusicXML export, especially tuplets and ties
This commit is contained in:
parent
9d198f5cec
commit
ec63fe8a7d
|
@ -221,7 +221,9 @@ class MusicXMLListener
|
||||||
'root-step', 'root-alter', 'bass-step', 'bass-alter', 'kind',
|
'root-step', 'root-alter', 'bass-step', 'bass-alter', 'kind',
|
||||||
'degree-value', 'degree-alter', 'degree-type', 'accidental'
|
'degree-value', 'degree-alter', 'degree-type', 'accidental'
|
||||||
@kind = tag
|
@kind = tag
|
||||||
when 'tie'
|
when 'notation'
|
||||||
|
@note['tied'] = 0
|
||||||
|
when 'tie', 'tied'
|
||||||
@note['tied'] ||= 0
|
@note['tied'] ||= 0
|
||||||
case attrs['type']
|
case attrs['type']
|
||||||
when 'start'
|
when 'start'
|
||||||
|
@ -348,6 +350,10 @@ class MusicXMLListener
|
||||||
else
|
else
|
||||||
@duration = @text.to_i
|
@duration = @text.to_i
|
||||||
end
|
end
|
||||||
|
when 'actual-notes'
|
||||||
|
@note['actualNotes'] = @text.to_i
|
||||||
|
when 'normal-notes'
|
||||||
|
@note['normalNotes'] = @text.to_i
|
||||||
when 'accidental'
|
when 'accidental'
|
||||||
case @text
|
case @text
|
||||||
when 'sharp'
|
when 'sharp'
|
||||||
|
|
|
@ -51,38 +51,47 @@ def _part_list
|
||||||
part_list = REXML::Element.new('part-list')
|
part_list = REXML::Element.new('part-list')
|
||||||
melody = REXML::Element.new('score-part')
|
melody = REXML::Element.new('score-part')
|
||||||
melody.add_attribute('id', 'MELO')
|
melody.add_attribute('id', 'MELO')
|
||||||
melody.add_element newTextElement('part-name', 'Song')
|
partname = newTextElement('part-name', 'Song')
|
||||||
|
partname.add_attribute('print-object', 'no')
|
||||||
|
melody.add_element(partname)
|
||||||
part_list.add_element(melody)
|
part_list.add_element(melody)
|
||||||
|
|
||||||
return part_list
|
return part_list
|
||||||
end
|
end
|
||||||
|
|
||||||
|
$LAST_DIVISIONS = nil
|
||||||
|
$LAST_KEY = nil
|
||||||
|
$LAST_MODE = nil
|
||||||
|
$LAST_TIME_NUM = nil
|
||||||
|
$LAST_TIME_DENOM= nil
|
||||||
|
$SEEN_CLEF = false
|
||||||
|
|
||||||
def _attributes(prop)
|
def _attributes(prop)
|
||||||
$DIVISIONS = prop['divisions']
|
$DIVISIONS = prop['divisions']
|
||||||
attr = REXML::Element.new('attributes')
|
attr = REXML::Element.new('attributes')
|
||||||
attr.add_element newTextElement('divisions', prop['divisions'])
|
if $LAST_DIVISIONS != prop['divisions']
|
||||||
|
attr.add_element newTextElement('divisions', $LAST_DIVISIONS = prop['divisions'])
|
||||||
|
end
|
||||||
|
if prop['key'] != $LAST_KEY || prop['mode'] != $LAST_MODE
|
||||||
key = REXML::Element.new('key')
|
key = REXML::Element.new('key')
|
||||||
key.add_element newTextElement('fifths', prop['key'])
|
key.add_element newTextElement('fifths', $LAST_KEY = prop['key'])
|
||||||
key.add_element newTextElement('mode', prop['mode'] > 0 ? "major" : "minor")
|
key.add_element newTextElement('mode', ($LAST_MODE = prop['mode']) > 0 ? "major" : "minor")
|
||||||
attr.add_element(key)
|
attr.add_element(key)
|
||||||
|
end
|
||||||
|
if prop['timeNum'] != $LAST_TIME_NUM || prop['timeDenom'] != $LAST_TIME_DENOM
|
||||||
time = REXML::Element.new('time')
|
time = REXML::Element.new('time')
|
||||||
time.add_element newTextElement('beats', prop['timeNum'])
|
time.add_element newTextElement('beats', $LAST_TIME_NUM = prop['timeNum'])
|
||||||
time.add_element newTextElement('beat-type', prop['timeDenom'])
|
time.add_element newTextElement('beat-type', $LAST_TIME_DENOM = prop['timeDenom'])
|
||||||
attr.add_element(time)
|
attr.add_element(time)
|
||||||
|
end
|
||||||
|
if !$SEEN_CLEF
|
||||||
|
$SEEN_CLEF = true
|
||||||
clef = REXML::Element.new('clef')
|
clef = REXML::Element.new('clef')
|
||||||
clef.add_element newTextElement('sign', 'G')
|
clef.add_element newTextElement('sign', 'G')
|
||||||
clef.add_element newTextElement('line', 2)
|
clef.add_element newTextElement('line', 2)
|
||||||
attr.add_element(clef)
|
attr.add_element(clef)
|
||||||
return attr
|
end
|
||||||
end
|
return attr.has_elements? ? attr : nil
|
||||||
|
|
||||||
def _groove(groove)
|
|
||||||
dir = REXML::Element.new('direction')
|
|
||||||
dty = REXML::Element.new('direction-type')
|
|
||||||
dty.add_element newTextElement('words', groove)
|
|
||||||
dir.add_element(dty)
|
|
||||||
|
|
||||||
return dir
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def _tempo(tempo)
|
def _tempo(tempo)
|
||||||
|
@ -98,6 +107,16 @@ def _tempo(tempo)
|
||||||
return dir
|
return dir
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def _groove(groove)
|
||||||
|
dir = REXML::Element.new('direction')
|
||||||
|
dir.add_attribute('placement', 'above')
|
||||||
|
dty = REXML::Element.new('direction-type')
|
||||||
|
dty.add_element newTextElement('words', groove)
|
||||||
|
dir.add_element(dty)
|
||||||
|
|
||||||
|
return dir
|
||||||
|
end
|
||||||
|
|
||||||
STEPS = ' BC D EF G A B C '
|
STEPS = ' BC D EF G A B C '
|
||||||
|
|
||||||
def _pitch(name, pitch, accidental, prefix="")
|
def _pitch(name, pitch, accidental, prefix="")
|
||||||
|
@ -106,14 +125,20 @@ def _pitch(name, pitch, accidental, prefix="")
|
||||||
stp = (pitch%12)+2
|
stp = (pitch%12)+2
|
||||||
step = STEPS[stp]
|
step = STEPS[stp]
|
||||||
if step == 0x20
|
if step == 0x20
|
||||||
abort "Pitch #{pitch} not representable"
|
if $LAST_KEY > 0
|
||||||
|
step = STEPS[stp-1]
|
||||||
|
accidental = 1
|
||||||
|
else
|
||||||
|
step = STEPS[stp+1]
|
||||||
|
accidental = -1
|
||||||
|
end
|
||||||
end
|
end
|
||||||
if prefix.length > 0
|
if prefix.length > 0
|
||||||
prefix += "-"
|
prefix += "-"
|
||||||
end
|
end
|
||||||
pitch= REXML::Element.new(name)
|
pitch= REXML::Element.new(name)
|
||||||
pitch.add_element newTextElement(prefix+'step', step.chr)
|
pitch.add_element newTextElement(prefix+'step', step.chr)
|
||||||
if accidental != 0
|
if accidental && accidental != 0
|
||||||
pitch.add_element newTextElement(prefix+'alter', accidental)
|
pitch.add_element newTextElement(prefix+'alter', accidental)
|
||||||
end
|
end
|
||||||
if prefix.length == 0
|
if prefix.length == 0
|
||||||
|
@ -143,7 +168,7 @@ def _accidental(visual)
|
||||||
accidental
|
accidental
|
||||||
end
|
end
|
||||||
|
|
||||||
def _note(pitch, dur, visual, tied)
|
def _note(pitch, dur, visual, tuplet, tied)
|
||||||
accidental = _accidental(visual)
|
accidental = _accidental(visual)
|
||||||
note = REXML::Element.new('note')
|
note = REXML::Element.new('note')
|
||||||
if pitch == VL::NoPitch
|
if pitch == VL::NoPitch
|
||||||
|
@ -152,21 +177,49 @@ def _note(pitch, dur, visual, tied)
|
||||||
note.add_element(_pitch('pitch', pitch, accidental))
|
note.add_element(_pitch('pitch', pitch, accidental))
|
||||||
end
|
end
|
||||||
note.add_element newTextElement('duration', dur)
|
note.add_element newTextElement('duration', dur)
|
||||||
|
notations = nil
|
||||||
if (tied & VL::TiedWithPrev) != 0
|
if (tied & VL::TiedWithPrev) != 0
|
||||||
note.add_element 'tie', {'type' => 'stop' }
|
note.add_element 'tie', {'type' => 'stop' }
|
||||||
|
unless notations
|
||||||
|
notations = REXML::Element.new('notations')
|
||||||
|
end
|
||||||
|
notations.add_element 'tied', {'type' => 'stop'}
|
||||||
end
|
end
|
||||||
if (tied & VL::TiedWithNext) != 0
|
if (tied & VL::TiedWithNext) != 0
|
||||||
note.add_element 'tie', {'type' => 'start' }
|
note.add_element 'tie', {'type' => 'start' }
|
||||||
|
unless notations
|
||||||
|
notations = REXML::Element.new('notations')
|
||||||
|
end
|
||||||
|
notations.add_element 'tied', {'type' => 'start'}
|
||||||
end
|
end
|
||||||
note.add_element newTextElement('voice', 1)
|
note.add_element newTextElement('voice', 1)
|
||||||
note.add_element newTextElement('type', TYPE[visual & 7])
|
note.add_element newTextElement('type', TYPE[visual & 7])
|
||||||
if accidental
|
if accidental
|
||||||
note.add_element newTextElement('accidental', ACC[accidental+2])
|
note.add_element newTextElement('accidental', ACC[accidental+2])
|
||||||
end
|
end
|
||||||
|
if tuplet
|
||||||
|
unless notations
|
||||||
|
notations = REXML::Element.new('notations')
|
||||||
|
end
|
||||||
|
notations.add_element 'tuplet', {'type' => (tuplet>0 ? 'start' : 'stop')}
|
||||||
|
end
|
||||||
|
if notations
|
||||||
|
note.add_element notations
|
||||||
|
end
|
||||||
|
|
||||||
return note
|
return note
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def _timeMod(note)
|
||||||
|
timeMod = REXML::Element.new('time-modification')
|
||||||
|
timeMod.add_element newTextElement('actual-notes', note['actualNotes'])
|
||||||
|
timeMod.add_element newTextElement('normal-notes', note['normalNotes'])
|
||||||
|
if note['normalType']
|
||||||
|
timeMod.add_element newTextElement('normal-type', TYPE[note['normalType']])
|
||||||
|
end
|
||||||
|
return timeMod
|
||||||
|
end
|
||||||
|
|
||||||
CHORD = {
|
CHORD = {
|
||||||
#
|
#
|
||||||
# Triads
|
# Triads
|
||||||
|
@ -350,27 +403,18 @@ def _melody
|
||||||
lastGroove = groove
|
lastGroove = groove
|
||||||
m.add_element(_groove(lastGroove))
|
m.add_element(_groove(lastGroove))
|
||||||
end
|
end
|
||||||
m.add_element(_attributes(INPUT['properties'][lastProp]))
|
if attr = _attributes(INPUT['properties'][lastProp])
|
||||||
|
m.add_element(attr)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
if meas['new-page']
|
if meas['new-page']
|
||||||
m.add_element 'print', {'new-page' => 'yes'}
|
m.add_element 'print', {'new-page' => 'yes'}
|
||||||
elsif meas['new-system']
|
elsif meas['new-system']
|
||||||
m.add_element 'print', {'new-system' => 'yes'}
|
m.add_element 'print', {'new-system' => 'yes'}
|
||||||
end
|
end
|
||||||
if meas['coda']
|
coda = meas['coda']
|
||||||
m.add_element 'sound', {'coda' => 'A'}
|
eEnding = nil
|
||||||
end
|
|
||||||
if meas['begin-repeat']
|
|
||||||
barline = REXML::Element.new('barline')
|
|
||||||
barline.add_attribute('location', 'left')
|
|
||||||
barline.add_element newTextElement('bar-style',
|
|
||||||
(repeat[measNum-1] & 2) != 0 ? 'heavy-heavy' : 'heavy-light')
|
|
||||||
barline.add_element 'repeat', {'direction' => 'forward'}
|
|
||||||
m.add_element(barline)
|
|
||||||
end
|
|
||||||
if ending = meas['begin-ending']
|
if ending = meas['begin-ending']
|
||||||
barline = REXML::Element.new('barline')
|
|
||||||
barline.add_attribute('location', 'left')
|
|
||||||
volta = ending['volta']
|
volta = ending['volta']
|
||||||
num = nil
|
num = nil
|
||||||
(0..7).each do |i|
|
(0..7).each do |i|
|
||||||
|
@ -382,9 +426,37 @@ def _melody
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
barline.add_element 'ending', {'type' => 'start', 'number' => num}
|
eEnding = REXML::Element.new('ending')
|
||||||
|
eEnding.add_attributes({'type' => 'start', 'number' => num})
|
||||||
|
end
|
||||||
|
eBarstyle = nil
|
||||||
|
eRepeat = nil
|
||||||
|
if meas['begin-repeat']
|
||||||
|
eBarstyle = newTextElement('bar-style',
|
||||||
|
(repeat[measNum-1] & 2) != 0 ? 'heavy-heavy' : 'heavy-light')
|
||||||
|
eRepeat = REXML::Element.new('repeat')
|
||||||
|
eRepeat.add_attribute('direction', 'forward')
|
||||||
|
end
|
||||||
|
if eEnding || eBarstyle || eRepeat || coda
|
||||||
|
barline = REXML::Element.new('barline')
|
||||||
|
barline.add_attribute('location', 'left')
|
||||||
|
if eBarstyle
|
||||||
|
barline.add_element(eBarstyle)
|
||||||
|
end
|
||||||
|
if coda
|
||||||
|
barline.add_element 'coda'
|
||||||
|
end
|
||||||
|
if eEnding
|
||||||
|
barline.add_element(eEnding)
|
||||||
|
end
|
||||||
|
if eRepeat
|
||||||
|
barline.add_element(eRepeat)
|
||||||
|
end
|
||||||
m.add_element(barline)
|
m.add_element(barline)
|
||||||
end
|
end
|
||||||
|
if coda
|
||||||
|
m.add_element 'sound', {'coda' => 'A'}
|
||||||
|
end
|
||||||
noteAt = 0
|
noteAt = 0
|
||||||
chordAt= 0
|
chordAt= 0
|
||||||
chords = meas['chords']
|
chords = meas['chords']
|
||||||
|
@ -412,7 +484,10 @@ def _melody
|
||||||
m.add_element(bk)
|
m.add_element(bk)
|
||||||
tempAt = noteAt
|
tempAt = noteAt
|
||||||
end
|
end
|
||||||
n = _note(note['pitch'], dur, note['visual'], note['tied'] || 0)
|
n = _note(note['pitch'], dur, note['visual'], note['tuplet'], note['tied'] || 0)
|
||||||
|
if note['actualNotes']
|
||||||
|
n.add_element _timeMod(note)
|
||||||
|
end
|
||||||
stanza = 1
|
stanza = 1
|
||||||
note['lyrics'].each do |syll|
|
note['lyrics'].each do |syll|
|
||||||
if syll['text']
|
if syll['text']
|
||||||
|
@ -427,18 +502,17 @@ def _melody
|
||||||
noteAt += dur
|
noteAt += dur
|
||||||
m.add_element(n)
|
m.add_element(n)
|
||||||
end
|
end
|
||||||
|
eRepeat = nil
|
||||||
|
eBarstyle = nil
|
||||||
if r = meas['end-repeat']
|
if r = meas['end-repeat']
|
||||||
barline = REXML::Element.new('barline')
|
eBarstyle = newTextElement('bar-style',
|
||||||
barline.add_attribute('location', 'right')
|
|
||||||
barline.add_element newTextElement('bar-style',
|
|
||||||
(repeat[measNum+1] & 1) != 0 ? 'heavy-heavy' : 'light-heavy')
|
(repeat[measNum+1] & 1) != 0 ? 'heavy-heavy' : 'light-heavy')
|
||||||
barline.add_element 'repeat', {'direction' => 'backward', 'times' => r['times'].to_s}
|
eRepeat = REXML::Element.new('repeat')
|
||||||
m.add_element(barline)
|
eRepeat.add_attributes({'direction' => 'backward', 'times' => r['times'].to_s})
|
||||||
end
|
end
|
||||||
|
eEnding = nil
|
||||||
if ending = meas['end-ending']
|
if ending = meas['end-ending']
|
||||||
barline = REXML::Element.new('barline')
|
eBarstyle ||= newTextElement('bar-style',
|
||||||
barline.add_attribute('location', 'right')
|
|
||||||
barline.add_element newTextElement('bar-style',
|
|
||||||
(repeat[measNum+1] & 1) != 0 ? 'heavy-heavy' : 'light-heavy')
|
(repeat[measNum+1] & 1) != 0 ? 'heavy-heavy' : 'light-heavy')
|
||||||
volta = ending['volta']
|
volta = ending['volta']
|
||||||
num = nil
|
num = nil
|
||||||
|
@ -452,13 +526,34 @@ def _melody
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
type = ending['last'] ? "discontinue" : "stop"
|
type = ending['last'] ? "discontinue" : "stop"
|
||||||
barline.add_element 'ending', {'type', type, 'number', num}
|
eEnding = REXML::Element.new('ending')
|
||||||
barline.add_element 'repeat', {'direction' => 'backward'}
|
eEnding.add_attributes({'type', type, 'number', num})
|
||||||
m.add_element(barline)
|
if !ending['last'] && !eRepeat
|
||||||
|
eRepeat = REXML::Element.new('repeat')
|
||||||
|
eRepeat.add_attribute('direction', 'backward')
|
||||||
end
|
end
|
||||||
if meas['tocoda']
|
end
|
||||||
|
if coda = meas['tocoda']
|
||||||
m.add_element 'sound', {'tocoda' => 'A'}
|
m.add_element 'sound', {'tocoda' => 'A'}
|
||||||
end
|
end
|
||||||
|
if coda || eEnding || eRepeat || eBarstyle
|
||||||
|
barline = REXML::Element.new('barline')
|
||||||
|
barline.add_attribute('location', 'right')
|
||||||
|
if eBarstyle
|
||||||
|
barline.add_element(eBarstyle)
|
||||||
|
end
|
||||||
|
if coda
|
||||||
|
barline.add_element 'coda'
|
||||||
|
end
|
||||||
|
if eEnding
|
||||||
|
barline.add_element(eEnding)
|
||||||
|
end
|
||||||
|
if eRepeat
|
||||||
|
barline.add_element(eRepeat)
|
||||||
|
end
|
||||||
|
m.add_element(barline)
|
||||||
|
end
|
||||||
|
|
||||||
melody.add_element(m)
|
melody.add_element(m)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -148,6 +148,7 @@ struct VLNote {
|
||||||
};
|
};
|
||||||
static int TupletNum(uint16_t visual) { return visual >> 12; }
|
static int TupletNum(uint16_t visual) { return visual >> 12; }
|
||||||
static int TupletDenom(uint16_t visual) { return (visual >> 8) & 0x0F; }
|
static int TupletDenom(uint16_t visual) { return (visual >> 8) & 0x0F; }
|
||||||
|
static uint16_t Tuplet(int num, int denom) { return (num << 12) | (denom << 8); }
|
||||||
VLNote(VLFraction dur=0, int pitch=kNoPitch, uint16_t visual=0);
|
VLNote(VLFraction dur=0, int pitch=kNoPitch, uint16_t visual=0);
|
||||||
VLNote(std::string name);
|
VLNote(std::string name);
|
||||||
std::string Name(uint16_t accidentals=0) const;
|
std::string Name(uint16_t accidentals=0) const;
|
||||||
|
|
|
@ -39,9 +39,14 @@ protected:
|
||||||
NSMutableArray * fMeasures;
|
NSMutableArray * fMeasures;
|
||||||
NSMutableArray * fNotes;
|
NSMutableArray * fNotes;
|
||||||
NSMutableArray * fChords;
|
NSMutableArray * fChords;
|
||||||
|
NSMutableArray * fNotesInTuplet;
|
||||||
bool fPerfOrder;
|
bool fPerfOrder;
|
||||||
const VLSong * fSong;
|
const VLSong * fSong;
|
||||||
VLVisualFilter fVisFilter;
|
VLVisualFilter fVisFilter;
|
||||||
|
int fInTuplet;
|
||||||
|
uint16_t fTuplet;
|
||||||
|
uint16_t fTupletNote;
|
||||||
|
VLFraction fTupletDur;
|
||||||
};
|
};
|
||||||
|
|
||||||
NSArray * VLPlistVisitor::EncodeProperties(const std::vector<VLProperties> & properties)
|
NSArray * VLPlistVisitor::EncodeProperties(const std::vector<VLProperties> & properties)
|
||||||
|
@ -83,8 +88,11 @@ void VLPlistVisitor::VisitMeasure(size_t m, VLProperties & p, VLMeasure & meas)
|
||||||
fChords= [NSMutableArray arrayWithCapacity:1];
|
fChords= [NSMutableArray arrayWithCapacity:1];
|
||||||
|
|
||||||
fVisFilter.ResetWithKey(p.fKey);
|
fVisFilter.ResetWithKey(p.fKey);
|
||||||
|
fInTuplet = 0;
|
||||||
VisitNotes(meas, p, true);
|
VisitNotes(meas, p, true);
|
||||||
VisitChords(meas);
|
VisitChords(meas);
|
||||||
|
if (fInTuplet)
|
||||||
|
[[fNotesInTuplet lastObject] setObject:[NSNumber numberWithInt:-1] forKey:@"tuplet"];
|
||||||
|
|
||||||
NSMutableDictionary * md =
|
NSMutableDictionary * md =
|
||||||
[NSMutableDictionary dictionaryWithObjectsAndKeys:
|
[NSMutableDictionary dictionaryWithObjectsAndKeys:
|
||||||
|
@ -142,8 +150,8 @@ void VLPlistVisitor::VisitNote(VLLyricsNote & n)
|
||||||
: [NSDictionary dictionary]];
|
: [NSDictionary dictionary]];
|
||||||
|
|
||||||
int grid = n.fPitch==VLNote::kNoPitch ? 0 : VLPitchToGrid(n.fPitch, n.fVisual, 0);
|
int grid = n.fPitch==VLNote::kNoPitch ? 0 : VLPitchToGrid(n.fPitch, n.fVisual, 0);
|
||||||
NSDictionary * nd =
|
NSMutableDictionary * nd =
|
||||||
[NSDictionary dictionaryWithObjectsAndKeys:
|
[NSMutableDictionary dictionaryWithObjectsAndKeys:
|
||||||
[NSNumber numberWithInt:n.fDuration.fNum], @"durNum",
|
[NSNumber numberWithInt:n.fDuration.fNum], @"durNum",
|
||||||
[NSNumber numberWithInt:n.fDuration.fDenom], @"durDenom",
|
[NSNumber numberWithInt:n.fDuration.fDenom], @"durDenom",
|
||||||
[NSNumber numberWithInt:n.fPitch], @"pitch",
|
[NSNumber numberWithInt:n.fPitch], @"pitch",
|
||||||
|
@ -151,6 +159,62 @@ void VLPlistVisitor::VisitNote(VLLyricsNote & n)
|
||||||
[NSNumber numberWithInt:fVisFilter(grid, n.fVisual)], @"visual",
|
[NSNumber numberWithInt:fVisFilter(grid, n.fVisual)], @"visual",
|
||||||
ly, @"lyrics",
|
ly, @"lyrics",
|
||||||
nil];
|
nil];
|
||||||
|
if (uint16_t newTuplet = n.fVisual & VLNote::kTupletMask) {
|
||||||
|
if (!fInTuplet || newTuplet != fTuplet) {
|
||||||
|
if (fInTuplet) {
|
||||||
|
[[fNotesInTuplet lastObject] setObject:[NSNumber numberWithInt:-1] forKey:@"tuplet"];
|
||||||
|
fInTuplet = 0;
|
||||||
|
}
|
||||||
|
fNotesInTuplet = [NSMutableArray array];
|
||||||
|
fTuplet = newTuplet;
|
||||||
|
fTupletNote = n.fVisual & VLNote::kNoteHeadMask;
|
||||||
|
fTupletDur = 0;
|
||||||
|
[nd setObject:[NSNumber numberWithInt:1] forKey:@"tuplet"];
|
||||||
|
}
|
||||||
|
int tupletNum = VLNote::TupletNum(fTuplet);
|
||||||
|
int tupletDenom = VLNote::TupletDenom(fTuplet);
|
||||||
|
[nd setObject:[NSNumber numberWithInt:tupletNum] forKey:@"actualNotes"];
|
||||||
|
[nd setObject:[NSNumber numberWithInt:tupletDenom] forKey:@"normalNotes"];
|
||||||
|
++fInTuplet;
|
||||||
|
fTupletDur += n.fDuration;
|
||||||
|
if (fTuplet == VLNote::kTriplet) {
|
||||||
|
uint16_t newNote = n.fVisual & VLNote::kNoteHeadMask;
|
||||||
|
if (newNote == fTupletNote) {
|
||||||
|
if (fInTuplet == 3)
|
||||||
|
fInTuplet = 0;
|
||||||
|
} else if (fInTuplet == 2 && newNote == fTupletNote-1) {
|
||||||
|
//
|
||||||
|
// 8th, 4th triplet
|
||||||
|
//
|
||||||
|
[nd setObject:[NSNumber numberWithInt:fTupletNote] forKey:@"normalType"];
|
||||||
|
fInTuplet = 0;
|
||||||
|
} else if (fInTuplet == 3 && newNote == fTupletNote-1) {
|
||||||
|
//
|
||||||
|
// 8th 8th 4th
|
||||||
|
//
|
||||||
|
for (NSMutableDictionary * prevNote in fNotesInTuplet) {
|
||||||
|
[prevNote setObject:[NSNumber numberWithInt:newNote] forKey:@"normalType"];
|
||||||
|
}
|
||||||
|
fInTuplet = 2;
|
||||||
|
fTupletNote = newNote;
|
||||||
|
} else {
|
||||||
|
//
|
||||||
|
// 4th 4th 8th
|
||||||
|
//
|
||||||
|
[nd setObject:[NSNumber numberWithInt:fTupletNote] forKey:@"normalType"];
|
||||||
|
if (fTupletDur.fNum == 1 && !(fTupletDur.fDenom&(fTupletDur.fDenom-1)))
|
||||||
|
fInTuplet = 0;
|
||||||
|
}
|
||||||
|
} else if (fInTuplet == tupletNum)
|
||||||
|
fInTuplet = 0;
|
||||||
|
if (!fInTuplet)
|
||||||
|
[nd setObject:[NSNumber numberWithInt:-1] forKey:@"tuplet"];
|
||||||
|
else
|
||||||
|
[fNotesInTuplet addObject:nd];
|
||||||
|
} else if (fInTuplet) {
|
||||||
|
[[fNotesInTuplet lastObject] setObject:[NSNumber numberWithInt:-1] forKey:@"tuplet"];
|
||||||
|
fInTuplet = 0;
|
||||||
|
}
|
||||||
[fNotes addObject:nd];
|
[fNotes addObject:nd];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -255,10 +319,14 @@ enum {
|
||||||
[[ndict objectForKey:@"durDenom"] intValue],
|
[[ndict objectForKey:@"durDenom"] intValue],
|
||||||
true);
|
true);
|
||||||
note.fPitch = [[ndict objectForKey:@"pitch"] intValue];
|
note.fPitch = [[ndict objectForKey:@"pitch"] intValue];
|
||||||
note.fVisual |= [[ndict objectForKey:@"visual"] intValue]
|
note.fVisual = [[ndict objectForKey:@"visual"] intValue]
|
||||||
& VLNote::kAccidentalsMask;
|
& VLNote::kAccidentalsMask;
|
||||||
note.fTied = 0;
|
note.fTied = 0;
|
||||||
|
|
||||||
|
if ([ndict objectForKey:@"actualNotes"])
|
||||||
|
note.fVisual |= VLNote::Tuplet([[ndict objectForKey:@"actualNotes"] intValue],
|
||||||
|
[[ndict objectForKey:@"normalNotes"] intValue]);
|
||||||
|
|
||||||
if ([[ndict objectForKey:@"tied"] intValue] & VLNote::kTiedWithPrev) {
|
if ([[ndict objectForKey:@"tied"] intValue] & VLNote::kTiedWithPrev) {
|
||||||
if (at != 0) {
|
if (at != 0) {
|
||||||
//
|
//
|
||||||
|
|
Loading…
Reference in New Issue
Block a user