mirror of
https://github.com/microtherion/VocalEasel.git
synced 2024-12-22 19:23:59 +00:00
Read harmony elements
This commit is contained in:
parent
81cfec4fea
commit
48798ae5a1
|
@ -28,6 +28,75 @@ SYLL = {
|
||||||
'middle' => 3
|
'middle' => 3
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CHORD = {
|
||||||
|
#
|
||||||
|
# Triads
|
||||||
|
#
|
||||||
|
'major' => VL::Chord::Maj,
|
||||||
|
'minor' => VL::Chord::Min,
|
||||||
|
'augmented' => VL::Chord::Aug,
|
||||||
|
'diminished' => VL::Chord::Dim,
|
||||||
|
#
|
||||||
|
# 7ths
|
||||||
|
#
|
||||||
|
'dominant' => VL::Chord::Dom7,
|
||||||
|
'major-seventh' => VL::Chord::Maj7,
|
||||||
|
'minor-seventh' => VL::Chord::Min7,
|
||||||
|
'diminished-seventh' => VL::Chord::Dim7,
|
||||||
|
'augmented-seventh' => VL::Chord::Aug7,
|
||||||
|
'half-diminished' => VL::Chord::M7b5,
|
||||||
|
'major-minor' => VL::Chord::MMin7,
|
||||||
|
#
|
||||||
|
# 6ths
|
||||||
|
#
|
||||||
|
'major-sixth' => VL::Chord::Maj6,
|
||||||
|
'minor-sixth' => VL::Chord::Min6,
|
||||||
|
#
|
||||||
|
# 9ths
|
||||||
|
#
|
||||||
|
'dominant-ninth' => VL::Chord::Dom9,
|
||||||
|
'major-ninth' => VL::Chord::Maj9,
|
||||||
|
'minor-ninth' => VL::Chord::Min9,
|
||||||
|
#
|
||||||
|
# 11ths
|
||||||
|
#
|
||||||
|
'dominant-11th' => VL::Chord::Dom11,
|
||||||
|
'major-11th' => VL::Chord::Maj11,
|
||||||
|
'minor-11th' => VL::Chord::Min11,
|
||||||
|
#
|
||||||
|
# 13ths
|
||||||
|
#
|
||||||
|
'dominant-13th' => VL::Chord::Dom13,
|
||||||
|
'major-13th' => VL::Chord::Maj13,
|
||||||
|
'minor-13th' => VL::Chord::Min13,
|
||||||
|
#
|
||||||
|
# Suspended
|
||||||
|
#
|
||||||
|
'suspended-second' => VL::Chord::Sus2,
|
||||||
|
'suspended-fourth' => VL::Chord::Sus4,
|
||||||
|
#
|
||||||
|
# Varia
|
||||||
|
#
|
||||||
|
'other' => VL::Unison,
|
||||||
|
'none' => 0
|
||||||
|
}
|
||||||
|
|
||||||
|
DEGREE = [
|
||||||
|
[VL::Unison, VL::Unison],
|
||||||
|
[VL::Min2nd+VL::Maj2nd, VL::Maj2nd],
|
||||||
|
[VL::Min3rd+VL::Maj3rd, VL::Maj3rd],
|
||||||
|
[VL::Fourth, VL::Fourth],
|
||||||
|
[VL::Fifth, VL::Fifth],
|
||||||
|
[VL::Aug5th+VL::Dim7th, VL::Dim7th],
|
||||||
|
[VL::Min7th+VL::Maj7th, VL::Min7th],
|
||||||
|
[VL::Octave, VL::Octave],
|
||||||
|
[VL::Min9th+VL::Maj9th, VL::Maj9th],
|
||||||
|
[VL::Aug9th+VL::Dim11th, VL::Dim11th],
|
||||||
|
[VL::Eleventh, VL::Eleventh],
|
||||||
|
[VL::Aug11th+VL::Dim13th, VL::Dim13th],
|
||||||
|
[VL::Min13th+VL::Maj13th, VL::Maj13th]
|
||||||
|
];
|
||||||
|
|
||||||
class MusicXMLListener
|
class MusicXMLListener
|
||||||
include REXML::StreamListener
|
include REXML::StreamListener
|
||||||
|
|
||||||
|
@ -72,7 +141,9 @@ class MusicXMLListener
|
||||||
@measNo = -1
|
@measNo = -1
|
||||||
when 'measure' then
|
when 'measure' then
|
||||||
@notes= []
|
@notes= []
|
||||||
|
@harm = []
|
||||||
@chord= false
|
@chord= false
|
||||||
|
@at = 0
|
||||||
if a = attrs['number']
|
if a = attrs['number']
|
||||||
@measNo = a.to_i-1
|
@measNo = a.to_i-1
|
||||||
else
|
else
|
||||||
|
@ -117,11 +188,21 @@ class MusicXMLListener
|
||||||
when 'beat-type' then
|
when 'beat-type' then
|
||||||
@kind = 'prop'
|
@kind = 'prop'
|
||||||
@key = 'timeDenom'
|
@key = 'timeDenom'
|
||||||
|
when 'harmony' then
|
||||||
|
@note = { 'pitch' => VL::NoPitch, 'steps' => 0, 'root' => VL::NoPitch,
|
||||||
|
'at' => @at }
|
||||||
|
when 'degree' then
|
||||||
|
@degree_value = 0
|
||||||
|
@degree_alter = 0
|
||||||
|
@degree_type = "alter"
|
||||||
when 'note' then
|
when 'note' then
|
||||||
@note = { 'pitch' => 0, 'durNum' => 0, 'durDenom' => 0 }
|
@note = { 'pitch' => 0, 'durNum' => 0, 'durDenom' => 0 }
|
||||||
when 'rest' then
|
when 'rest' then
|
||||||
@note['pitch'] = -128
|
@note['pitch'] = VL::NoPitch
|
||||||
when 'mode', 'step', 'alter', 'octave', 'duration', 'syllabic', 'text' then
|
when 'mode', 'step', 'alter', 'octave', 'duration', 'syllabic', 'text',
|
||||||
|
'root-step', 'root-alter', 'bass-step', 'bass-alter', 'kind',
|
||||||
|
'degree-value', 'degree-alter', 'degree-type'
|
||||||
|
then
|
||||||
@kind = tag
|
@kind = tag
|
||||||
when 'tie' then
|
when 'tie' then
|
||||||
@note['tied'] ||= 0
|
@note['tied'] ||= 0
|
||||||
|
@ -146,12 +227,12 @@ class MusicXMLListener
|
||||||
|
|
||||||
def makeChords(chords)
|
def makeChords(chords)
|
||||||
chords.each do |chord|
|
chords.each do |chord|
|
||||||
chord['root'] = -128
|
chord['root'] = VL::NoPitch
|
||||||
st = [*chord['pitch']].sort
|
st = [*chord['pitch']].sort
|
||||||
pitch = st[0]
|
pitch = st[0]
|
||||||
if pitch > 0 && pitch < 60
|
if pitch > 0 && pitch < 60
|
||||||
chord['root'] = st.shift
|
chord['root'] = st.shift
|
||||||
pitch = st[0] || -128
|
pitch = st[0] || VL::NoPitch
|
||||||
end
|
end
|
||||||
steps = 0
|
steps = 0
|
||||||
if pitch > 0
|
if pitch > 0
|
||||||
|
@ -166,6 +247,24 @@ class MusicXMLListener
|
||||||
return chords
|
return chords
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def makeHarmChords(harm)
|
||||||
|
chords = []
|
||||||
|
durDenom = @prop['divisions']*4
|
||||||
|
if harm[0]['at'] > 0
|
||||||
|
chords.push({'pitch' => VL::NoPitch, 'steps' => 0, 'root' => VL::NoPitch,
|
||||||
|
'durNum' => harm[0]['at'], 'durDenom' => durDenom})
|
||||||
|
end
|
||||||
|
(0..harm.length-1).each do |i|
|
||||||
|
chord = harm[i]
|
||||||
|
nextAt = i+1 < harm.length ? harm[i+1]['at'] : @at
|
||||||
|
chord['durNum'] = nextAt - chord.delete('at')
|
||||||
|
chord['durDenom'] = durDenom
|
||||||
|
chords.push(chord)
|
||||||
|
end
|
||||||
|
|
||||||
|
return chords
|
||||||
|
end
|
||||||
|
|
||||||
def makeVolta(number)
|
def makeVolta(number)
|
||||||
volta = 0
|
volta = 0
|
||||||
number.split(/,\s*/).each do |v|
|
number.split(/,\s*/).each do |v|
|
||||||
|
@ -184,20 +283,38 @@ class MusicXMLListener
|
||||||
when 'textProp' then
|
when 'textProp' then
|
||||||
OUTPUT[@key] = @text
|
OUTPUT[@key] = @text
|
||||||
when 'dateProp' then
|
when 'dateProp' then
|
||||||
OUTPUT[@key] = Time.parse(@text)
|
begin
|
||||||
|
OUTPUT[@key] = Time.parse(@text)
|
||||||
|
rescue
|
||||||
|
OUTPUT[@key] = Time.now
|
||||||
|
end
|
||||||
when 'prop' then
|
when 'prop' then
|
||||||
@prop[@key] = @text.to_i
|
@prop[@key] = @text.to_i
|
||||||
when 'mode' then
|
when 'mode' then
|
||||||
@prop['mode'] = @text == 'minor' ? -1 : 1
|
@prop['mode'] = @text == 'minor' ? -1 : 1
|
||||||
when 'step' then
|
when 'step' then
|
||||||
@note['pitch'] += PITCH[@text]
|
@note['pitch'] += PITCH[@text]
|
||||||
when 'alter' then
|
when 'alter', 'root-alter' then
|
||||||
@note['pitch'] += @text.to_i
|
@note['pitch'] += @text.to_i
|
||||||
when 'octave' then
|
when 'octave' then
|
||||||
@note['pitch'] += (@text.to_i+1)*12
|
@note['pitch'] += (@text.to_i+1)*12
|
||||||
when 'duration' then
|
when 'duration' then
|
||||||
@note['durNum'] = @text.to_i
|
@note['durNum'] = @text.to_i
|
||||||
@note['durDenom'] = @prop['divisions']*4
|
@note['durDenom'] = @prop['divisions']*4
|
||||||
|
when 'root-step' then
|
||||||
|
@note['pitch'] = PITCH[@text]
|
||||||
|
when 'bass-step' then
|
||||||
|
@note['root'] = PITCH[@text]
|
||||||
|
when 'bass-alter' then
|
||||||
|
@note['root'] += @text.to_i
|
||||||
|
when 'kind' then
|
||||||
|
@note['steps'] = CHORD[@text]
|
||||||
|
when 'degree-value' then
|
||||||
|
@degree_value = @text.to_i-1
|
||||||
|
when 'degree-alter' then
|
||||||
|
@degree_alter = @text.to_i
|
||||||
|
when 'degree-type' then
|
||||||
|
@degree_type = @text
|
||||||
when 'syllabic' then
|
when 'syllabic' then
|
||||||
@lyric['kind'] = SYLL[@text]
|
@lyric['kind'] = SYLL[@text]
|
||||||
when 'text' then
|
when 'text' then
|
||||||
|
@ -220,7 +337,35 @@ class MusicXMLListener
|
||||||
@chord = false
|
@chord = false
|
||||||
else
|
else
|
||||||
@notes.push(@note)
|
@notes.push(@note)
|
||||||
|
@at += @note['durNum']
|
||||||
end
|
end
|
||||||
|
when 'degree' then
|
||||||
|
oldSteps = @note['steps']
|
||||||
|
oldValue = @degree_value
|
||||||
|
case @degree_type
|
||||||
|
when 'subtract' then
|
||||||
|
@note['steps'] &= ~DEGREE[@degree_value][0]
|
||||||
|
when 'add' then
|
||||||
|
@degree_value = DEGREE[@degree_value][1]
|
||||||
|
if @degree_alter < 0
|
||||||
|
@degree_value >>= -@degree_alter
|
||||||
|
elsif @degree_alter > 0
|
||||||
|
@degree_value <<= @degree_alter
|
||||||
|
end
|
||||||
|
@note['steps'] |= @degree_value
|
||||||
|
when 'alter' then
|
||||||
|
@degree_value = @note['steps'] & DEGREE[@degree_value][0]
|
||||||
|
@note['steps'] ^= @degree_value
|
||||||
|
if @degree_alter < 0
|
||||||
|
@degree_value >>= -@degree_alter
|
||||||
|
elsif @degree_alter > 0
|
||||||
|
@degree_value <<= @degree_alter
|
||||||
|
end
|
||||||
|
@note['steps'] |= @degree_value
|
||||||
|
end
|
||||||
|
# $stderr.puts "#{@degree_type} #{oldValue} #{@degree_alter} (#{oldSteps} -> #{@note['steps']})"
|
||||||
|
when 'harmony' then
|
||||||
|
@harm.push(@note)
|
||||||
when 'barline' then
|
when 'barline' then
|
||||||
case @type
|
case @type
|
||||||
when 'start' then
|
when 'start' then
|
||||||
|
@ -233,12 +378,14 @@ class MusicXMLListener
|
||||||
'last' => @type == 'discontinue'
|
'last' => @type == 'discontinue'
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
if @times > 0
|
if @times
|
||||||
@meas['end-repeat'] = {
|
if @times > 0
|
||||||
'times' => @times
|
@meas['end-repeat'] = {
|
||||||
}
|
'times' => @times
|
||||||
elsif @times == 0
|
}
|
||||||
@meas['begin-repeat'] = {}
|
else @times == 0
|
||||||
|
@meas['begin-repeat'] = {}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
when 'measure' then
|
when 'measure' then
|
||||||
|
@ -247,6 +394,9 @@ class MusicXMLListener
|
||||||
@meas['chords'] = makeChords(@notes)
|
@meas['chords'] = makeChords(@notes)
|
||||||
else
|
else
|
||||||
@meas['melody'] = @notes
|
@meas['melody'] = @notes
|
||||||
|
unless @harm.empty?
|
||||||
|
@meas['chords'] = makeHarmChords(@harm)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
when 'part' then
|
when 'part' then
|
||||||
OUTPUT['properties'] = @props unless @part == 'HARM'
|
OUTPUT['properties'] = @props unless @part == 'HARM'
|
||||||
|
|
|
@ -8,4 +8,75 @@ class VL
|
||||||
TiedWithNext = 1
|
TiedWithNext = 1
|
||||||
TiedWithPrev = 2
|
TiedWithPrev = 2
|
||||||
InChord = 4
|
InChord = 4
|
||||||
|
|
||||||
|
Unison = 1<<0
|
||||||
|
Min2nd = 1<<1
|
||||||
|
Maj2nd = 1<<2
|
||||||
|
Min3rd = 1<<3
|
||||||
|
Maj3rd = 1<<4
|
||||||
|
Fourth = 1<<5
|
||||||
|
Dim5th = 1<<6
|
||||||
|
Fifth = 1<<7
|
||||||
|
Aug5th = 1<<8
|
||||||
|
Dim7th = 1<<9
|
||||||
|
Min7th = 1<<10
|
||||||
|
Maj7th = 1<<11
|
||||||
|
Octave = 1<<12
|
||||||
|
Min9th = 1<<13
|
||||||
|
Maj9th = 1<<14
|
||||||
|
Aug9th = 1<<15
|
||||||
|
Dim11th = 1<<16
|
||||||
|
Eleventh = 1<<17
|
||||||
|
Aug11th = 1<<18
|
||||||
|
Dim13th = 1<<19
|
||||||
|
Min13th = 1<<20
|
||||||
|
Maj13th = 1<<21
|
||||||
|
|
||||||
|
class Chord
|
||||||
|
#
|
||||||
|
# Triads
|
||||||
|
#
|
||||||
|
Maj = VL::Unison+VL::Maj3rd+VL::Fifth
|
||||||
|
Min = VL::Unison+VL::Min3rd+VL::Fifth
|
||||||
|
Aug = VL::Unison+VL::Maj3rd+VL::Aug5th
|
||||||
|
Dim = VL::Unison+VL::Min3rd+VL::Dim5th
|
||||||
|
#
|
||||||
|
# 7ths
|
||||||
|
#
|
||||||
|
Dom7 = Maj+VL::Min7th
|
||||||
|
Maj7 = Maj+VL::Maj7th
|
||||||
|
Min7 = Min+VL::Min7th
|
||||||
|
Dim7 = Dim+VL::Dim7th
|
||||||
|
Aug7 = Aug+VL::Min7th
|
||||||
|
M7b5 = Dim+VL::Min7th
|
||||||
|
MMin7 = Min+VL::Maj7th
|
||||||
|
#
|
||||||
|
# 6ths
|
||||||
|
#
|
||||||
|
Maj6 = Maj+VL::Dim7th
|
||||||
|
Min6 = Min+VL::Dim7th
|
||||||
|
#
|
||||||
|
# 9ths
|
||||||
|
#
|
||||||
|
Dom9 = Dom7+VL::Maj9th
|
||||||
|
Maj9 = Maj7+VL::Maj9th
|
||||||
|
Min9 = Min7+VL::Maj9th
|
||||||
|
#
|
||||||
|
# 11ths
|
||||||
|
#
|
||||||
|
Dom11 = Dom9+VL::Eleventh
|
||||||
|
Maj11 = Maj9+VL::Eleventh
|
||||||
|
Min11 = Min9+VL::Eleventh
|
||||||
|
#
|
||||||
|
# 13ths
|
||||||
|
#
|
||||||
|
Dom13 = Dom11+VL::Maj13th
|
||||||
|
Maj13 = Maj11+VL::Maj13th
|
||||||
|
Min13 = Min11+VL::Maj13th
|
||||||
|
#
|
||||||
|
# Suspended
|
||||||
|
#
|
||||||
|
Sus4 = VL::Unison+VL::Fourth+VL::Fifth
|
||||||
|
Sus2 = VL::Unison+VL::Maj2nd+VL::Fifth
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue
Block a user