musicality 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/.rspec +2 -0
- data/.ruby-version +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +47 -0
- data/Rakefile +65 -0
- data/bin/midify +78 -0
- data/examples/hip.rb +32 -0
- data/examples/missed_connection.rb +26 -0
- data/examples/song1.rb +33 -0
- data/examples/song2.rb +32 -0
- data/lib/musicality/errors.rb +9 -0
- data/lib/musicality/notation/conversion/change_conversion.rb +19 -0
- data/lib/musicality/notation/conversion/measure_note_map.rb +40 -0
- data/lib/musicality/notation/conversion/measured_score_conversion.rb +70 -0
- data/lib/musicality/notation/conversion/measured_score_converter.rb +95 -0
- data/lib/musicality/notation/conversion/note_time_converter.rb +68 -0
- data/lib/musicality/notation/conversion/tempo_conversion.rb +25 -0
- data/lib/musicality/notation/conversion/unmeasured_score_conversion.rb +47 -0
- data/lib/musicality/notation/conversion/unmeasured_score_converter.rb +64 -0
- data/lib/musicality/notation/model/articulations.rb +13 -0
- data/lib/musicality/notation/model/change.rb +62 -0
- data/lib/musicality/notation/model/dynamics.rb +12 -0
- data/lib/musicality/notation/model/link.rb +73 -0
- data/lib/musicality/notation/model/meter.rb +54 -0
- data/lib/musicality/notation/model/meters.rb +9 -0
- data/lib/musicality/notation/model/note.rb +120 -0
- data/lib/musicality/notation/model/part.rb +54 -0
- data/lib/musicality/notation/model/pitch.rb +163 -0
- data/lib/musicality/notation/model/pitches.rb +21 -0
- data/lib/musicality/notation/model/program.rb +53 -0
- data/lib/musicality/notation/model/score.rb +132 -0
- data/lib/musicality/notation/packing/change_packing.rb +46 -0
- data/lib/musicality/notation/packing/part_packing.rb +31 -0
- data/lib/musicality/notation/packing/program_packing.rb +16 -0
- data/lib/musicality/notation/packing/score_packing.rb +108 -0
- data/lib/musicality/notation/parsing/articulation_parsing.rb +264 -0
- data/lib/musicality/notation/parsing/articulation_parsing.treetop +59 -0
- data/lib/musicality/notation/parsing/convenience_methods.rb +74 -0
- data/lib/musicality/notation/parsing/duration_nodes.rb +21 -0
- data/lib/musicality/notation/parsing/duration_parsing.rb +205 -0
- data/lib/musicality/notation/parsing/duration_parsing.treetop +25 -0
- data/lib/musicality/notation/parsing/link_nodes.rb +35 -0
- data/lib/musicality/notation/parsing/link_parsing.rb +270 -0
- data/lib/musicality/notation/parsing/link_parsing.treetop +33 -0
- data/lib/musicality/notation/parsing/meter_parsing.rb +190 -0
- data/lib/musicality/notation/parsing/meter_parsing.treetop +29 -0
- data/lib/musicality/notation/parsing/note_node.rb +40 -0
- data/lib/musicality/notation/parsing/note_parsing.rb +229 -0
- data/lib/musicality/notation/parsing/note_parsing.treetop +28 -0
- data/lib/musicality/notation/parsing/numbers/nonnegative_float_parsing.rb +289 -0
- data/lib/musicality/notation/parsing/numbers/nonnegative_float_parsing.treetop +29 -0
- data/lib/musicality/notation/parsing/numbers/nonnegative_integer_parsing.rb +64 -0
- data/lib/musicality/notation/parsing/numbers/nonnegative_integer_parsing.treetop +17 -0
- data/lib/musicality/notation/parsing/numbers/nonnegative_rational_parsing.rb +86 -0
- data/lib/musicality/notation/parsing/numbers/nonnegative_rational_parsing.treetop +20 -0
- data/lib/musicality/notation/parsing/numbers/positive_float_parsing.rb +503 -0
- data/lib/musicality/notation/parsing/numbers/positive_float_parsing.treetop +33 -0
- data/lib/musicality/notation/parsing/numbers/positive_integer_parsing.rb +95 -0
- data/lib/musicality/notation/parsing/numbers/positive_integer_parsing.treetop +19 -0
- data/lib/musicality/notation/parsing/numbers/positive_rational_parsing.rb +84 -0
- data/lib/musicality/notation/parsing/numbers/positive_rational_parsing.treetop +19 -0
- data/lib/musicality/notation/parsing/parseable.rb +30 -0
- data/lib/musicality/notation/parsing/pitch_node.rb +23 -0
- data/lib/musicality/notation/parsing/pitch_parsing.rb +448 -0
- data/lib/musicality/notation/parsing/pitch_parsing.treetop +52 -0
- data/lib/musicality/notation/parsing/segment_parsing.rb +141 -0
- data/lib/musicality/notation/parsing/segment_parsing.treetop +23 -0
- data/lib/musicality/notation/util/interpolation.rb +16 -0
- data/lib/musicality/notation/util/piecewise_function.rb +122 -0
- data/lib/musicality/notation/util/value_computer.rb +170 -0
- data/lib/musicality/performance/conversion/glissando_converter.rb +34 -0
- data/lib/musicality/performance/conversion/note_sequence_extractor.rb +98 -0
- data/lib/musicality/performance/conversion/portamento_converter.rb +24 -0
- data/lib/musicality/performance/conversion/score_collator.rb +126 -0
- data/lib/musicality/performance/midi/midi_events.rb +34 -0
- data/lib/musicality/performance/midi/midi_util.rb +31 -0
- data/lib/musicality/performance/midi/part_sequencer.rb +123 -0
- data/lib/musicality/performance/midi/score_sequencer.rb +45 -0
- data/lib/musicality/performance/model/note_attacks.rb +19 -0
- data/lib/musicality/performance/model/note_sequence.rb +111 -0
- data/lib/musicality/performance/util/note_linker.rb +28 -0
- data/lib/musicality/performance/util/optimization.rb +31 -0
- data/lib/musicality/validatable.rb +38 -0
- data/lib/musicality/version.rb +3 -0
- data/lib/musicality.rb +81 -0
- data/musicality.gemspec +30 -0
- data/spec/musicality_spec.rb +7 -0
- data/spec/notation/conversion/change_conversion_spec.rb +40 -0
- data/spec/notation/conversion/measure_note_map_spec.rb +73 -0
- data/spec/notation/conversion/measured_score_conversion_spec.rb +141 -0
- data/spec/notation/conversion/measured_score_converter_spec.rb +329 -0
- data/spec/notation/conversion/note_time_converter_spec.rb +81 -0
- data/spec/notation/conversion/tempo_conversion_spec.rb +40 -0
- data/spec/notation/conversion/unmeasured_score_conversion_spec.rb +71 -0
- data/spec/notation/conversion/unmeasured_score_converter_spec.rb +116 -0
- data/spec/notation/model/change_spec.rb +90 -0
- data/spec/notation/model/link_spec.rb +83 -0
- data/spec/notation/model/meter_spec.rb +97 -0
- data/spec/notation/model/note_spec.rb +183 -0
- data/spec/notation/model/part_spec.rb +69 -0
- data/spec/notation/model/pitch_spec.rb +180 -0
- data/spec/notation/model/program_spec.rb +50 -0
- data/spec/notation/model/score_spec.rb +211 -0
- data/spec/notation/packing/change_packing_spec.rb +153 -0
- data/spec/notation/packing/part_packing_spec.rb +66 -0
- data/spec/notation/packing/program_packing_spec.rb +33 -0
- data/spec/notation/packing/score_packing_spec.rb +301 -0
- data/spec/notation/parsing/articulation_parsing_spec.rb +23 -0
- data/spec/notation/parsing/convenience_methods_spec.rb +99 -0
- data/spec/notation/parsing/duration_nodes_spec.rb +83 -0
- data/spec/notation/parsing/duration_parsing_spec.rb +70 -0
- data/spec/notation/parsing/link_nodes_spec.rb +30 -0
- data/spec/notation/parsing/link_parsing_spec.rb +13 -0
- data/spec/notation/parsing/meter_parsing_spec.rb +23 -0
- data/spec/notation/parsing/note_node_spec.rb +87 -0
- data/spec/notation/parsing/note_parsing_spec.rb +46 -0
- data/spec/notation/parsing/numbers/nonnegative_float_spec.rb +28 -0
- data/spec/notation/parsing/numbers/nonnegative_integer_spec.rb +11 -0
- data/spec/notation/parsing/numbers/nonnegative_rational_spec.rb +11 -0
- data/spec/notation/parsing/numbers/positive_float_spec.rb +28 -0
- data/spec/notation/parsing/numbers/positive_integer_spec.rb +28 -0
- data/spec/notation/parsing/numbers/positive_rational_spec.rb +28 -0
- data/spec/notation/parsing/pitch_node_spec.rb +38 -0
- data/spec/notation/parsing/pitch_parsing_spec.rb +14 -0
- data/spec/notation/parsing/segment_parsing_spec.rb +27 -0
- data/spec/notation/util/value_computer_spec.rb +146 -0
- data/spec/performance/conversion/glissando_converter_spec.rb +93 -0
- data/spec/performance/conversion/note_sequence_extractor_spec.rb +230 -0
- data/spec/performance/conversion/portamento_converter_spec.rb +91 -0
- data/spec/performance/conversion/score_collator_spec.rb +183 -0
- data/spec/performance/midi/midi_util_spec.rb +110 -0
- data/spec/performance/midi/part_sequencer_spec.rb +40 -0
- data/spec/performance/midi/score_sequencer_spec.rb +50 -0
- data/spec/performance/model/note_sequence_spec.rb +147 -0
- data/spec/performance/util/note_linker_spec.rb +68 -0
- data/spec/performance/util/optimization_spec.rb +73 -0
- data/spec/spec_helper.rb +43 -0
- metadata +323 -0
@@ -0,0 +1,21 @@
|
|
1
|
+
module Musicality
|
2
|
+
|
3
|
+
module Pitches
|
4
|
+
# Define pitch objects for octaves octave 0 through 9
|
5
|
+
{
|
6
|
+
:Bb => 10, :B => 11,
|
7
|
+
:Cb => 11, :C => 0,
|
8
|
+
:Db => 1, :D => 2,
|
9
|
+
:Eb => 3, :E => 4,
|
10
|
+
:Fb => 4, :F => 5,
|
11
|
+
:Gb => 6, :G => 7,
|
12
|
+
:Ab => 8, :A => 9,
|
13
|
+
}.each do |sym,pc|
|
14
|
+
(0..9).each do |octave|
|
15
|
+
obj = Pitch.new octave: octave, semitone: pc
|
16
|
+
Pitches.const_set(:"#{sym}#{octave}",obj)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Musicality
|
2
|
+
|
3
|
+
# Program defines markers (by starting note offset) and subprograms (list which markers are played).
|
4
|
+
#
|
5
|
+
# @author James Tunnell
|
6
|
+
#
|
7
|
+
class Program
|
8
|
+
include Validatable
|
9
|
+
|
10
|
+
attr_accessor :segments
|
11
|
+
|
12
|
+
def initialize segments = []
|
13
|
+
@segments = segments
|
14
|
+
end
|
15
|
+
|
16
|
+
def check_methods
|
17
|
+
[:ensure_increasing_segments, :ensure_nonnegative_segments]
|
18
|
+
end
|
19
|
+
|
20
|
+
# @return [Float] the sum of all program segment lengths
|
21
|
+
def length
|
22
|
+
segments.inject(0.0) { |length, segment| length + (segment.last - segment.first) }
|
23
|
+
end
|
24
|
+
|
25
|
+
def == other
|
26
|
+
return other.respond_to?(:segments) && @segments == other.segments
|
27
|
+
end
|
28
|
+
|
29
|
+
def include? offset
|
30
|
+
@segments.each do |segment|
|
31
|
+
if segment.include?(offset)
|
32
|
+
return true
|
33
|
+
end
|
34
|
+
end
|
35
|
+
return false
|
36
|
+
end
|
37
|
+
|
38
|
+
def ensure_increasing_segments
|
39
|
+
non_increasing = @segments.select {|seg| seg.first >= seg.last }
|
40
|
+
if non_increasing.any?
|
41
|
+
raise NonIncreasingError, "Non-increasing segments found #{non_increasing}"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def ensure_nonnegative_segments
|
46
|
+
negative = @segments.select {|seg| seg.first < 0 || seg.last < 0 }
|
47
|
+
if negative.any?
|
48
|
+
raise NegativeError, "Segments #{negative} have negative values"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
module Musicality
|
2
|
+
|
3
|
+
class Score
|
4
|
+
include Validatable
|
5
|
+
attr_accessor :parts, :program
|
6
|
+
|
7
|
+
def initialize parts: {}, program: Program.new
|
8
|
+
@parts = parts
|
9
|
+
@program = program
|
10
|
+
yield(self) if block_given?
|
11
|
+
end
|
12
|
+
|
13
|
+
def validatables
|
14
|
+
[ @program ] + @parts.values
|
15
|
+
end
|
16
|
+
|
17
|
+
def clone
|
18
|
+
Marshal.load(Marshal.dump(self))
|
19
|
+
end
|
20
|
+
|
21
|
+
def ==(other)
|
22
|
+
return @parts == other.parts && @program == other.program
|
23
|
+
end
|
24
|
+
|
25
|
+
def duration
|
26
|
+
@parts.map {|p| p.duration }.max
|
27
|
+
end
|
28
|
+
|
29
|
+
def collated?
|
30
|
+
@program.segments.size == 1 && @program.segments[0].first == 0
|
31
|
+
end
|
32
|
+
|
33
|
+
class Timed < Score
|
34
|
+
end
|
35
|
+
|
36
|
+
class TempoBased < Score
|
37
|
+
attr_accessor :start_tempo, :tempo_changes
|
38
|
+
|
39
|
+
def initialize start_tempo, tempo_changes: {}, parts: {}, program: Program.new
|
40
|
+
@start_tempo = start_tempo
|
41
|
+
@tempo_changes = tempo_changes
|
42
|
+
super(parts: parts, program: program)
|
43
|
+
|
44
|
+
yield(self) if block_given?
|
45
|
+
end
|
46
|
+
|
47
|
+
def check_methods
|
48
|
+
[:check_start_tempo, :check_tempo_changes]
|
49
|
+
end
|
50
|
+
|
51
|
+
def ==(other)
|
52
|
+
return super(other) && @start_tempo == other.start_tempo &&
|
53
|
+
@tempo_changes == other.tempo_changes
|
54
|
+
end
|
55
|
+
|
56
|
+
def check_start_tempo
|
57
|
+
if @start_tempo <= 0
|
58
|
+
raise NonPositiveError, "start tempo (#{@start_tempo}) is not positive"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def check_tempo_changes
|
63
|
+
badvalues = @tempo_changes.select {|k,v| v.value <= 0 }
|
64
|
+
if badvalues.any?
|
65
|
+
raise NonPositiveError, "tempo changes (#{badvalues}) are not positive"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# Tempo-based score without meter, bar lines, or fixed pulse (beat).
|
71
|
+
# Offsets are note-based, and tempo values are in quarter-notes-per-minute.
|
72
|
+
class Unmeasured < Score::TempoBased
|
73
|
+
end
|
74
|
+
|
75
|
+
# Tempo-based score with meter, bar lines, and a fixed pulse (beat).
|
76
|
+
# Offsets are measure-based, and tempo values are in beats-per-minute.
|
77
|
+
class Measured < Score::TempoBased
|
78
|
+
attr_accessor :start_meter, :meter_changes
|
79
|
+
|
80
|
+
def initialize start_meter, start_tempo, meter_changes: {}, tempo_changes: {}, parts: {}, program: Program.new
|
81
|
+
@start_meter = start_meter
|
82
|
+
@meter_changes = meter_changes
|
83
|
+
|
84
|
+
super(start_tempo, tempo_changes: tempo_changes,
|
85
|
+
program: program, parts: parts)
|
86
|
+
yield(self) if block_given?
|
87
|
+
end
|
88
|
+
|
89
|
+
def check_methods
|
90
|
+
super() + [:check_startmeter_type, :check_meterchange_types,
|
91
|
+
:check_meterchange_durs, :check_meterchange_offsets]
|
92
|
+
end
|
93
|
+
|
94
|
+
def validatables
|
95
|
+
super() + [ @start_meter ] + @meter_changes.values.map {|v| v.value}
|
96
|
+
end
|
97
|
+
|
98
|
+
def check_startmeter_type
|
99
|
+
unless @start_meter.is_a? Meter
|
100
|
+
raise TypeError, "start meter #{@start_meter} is not a Meter object"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def check_meterchange_types
|
105
|
+
badtypes = @meter_changes.select {|k,v| !v.value.is_a?(Meter) }
|
106
|
+
if badtypes.any?
|
107
|
+
raise TypeError, "meter change values #{nonmeter_values} are not Meter objects"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def check_meterchange_offsets
|
112
|
+
badoffsets = @meter_changes.select {|k,v| k != k.to_i }
|
113
|
+
if badoffsets.any?
|
114
|
+
raise NonIntegerError, "meter changes #{badoffsets} have non-integer offsets"
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def check_meterchange_durs
|
119
|
+
nonzero_duration = @meter_changes.select {|k,v| !v.is_a?(Change::Immediate) }
|
120
|
+
if nonzero_duration.any?
|
121
|
+
raise NonZeroError, "meter changes #{nonzero_duration} are not immediate"
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def ==(other)
|
126
|
+
return super(other) && @start_meter == other.start_meter &&
|
127
|
+
@meter_changes == other.meter_changes
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Musicality
|
2
|
+
|
3
|
+
class Change
|
4
|
+
class Immediate < Change
|
5
|
+
def pack
|
6
|
+
pack_common
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.unpack packing
|
10
|
+
new(packing["value"])
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class Gradual < Change
|
15
|
+
def pack
|
16
|
+
packing = pack_common.merge("impending" => @impending)
|
17
|
+
unless @remaining == 0
|
18
|
+
packing["remaining"] = @remaining
|
19
|
+
end
|
20
|
+
unless @elapsed == 0
|
21
|
+
packing["elapsed"] = @elapsed
|
22
|
+
end
|
23
|
+
return packing
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.unpack packing
|
27
|
+
elapsed = packing["elapsed"] || 0
|
28
|
+
remaining = packing["remaining"] || 0
|
29
|
+
new(packing["value"], packing["impending"], elapsed, remaining)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.unpack packing
|
34
|
+
type = const_get(packing["type"])
|
35
|
+
type.unpack(packing)
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def pack_common
|
41
|
+
{ "type" => self.class.to_s.split("::")[-1],
|
42
|
+
"value" => @value }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Musicality
|
2
|
+
|
3
|
+
class Part
|
4
|
+
def pack
|
5
|
+
packed_notes = notes.map {|n| n.to_s }.join(" ")
|
6
|
+
packed_dcs = Hash[ dynamic_changes.map do |offset,change|
|
7
|
+
[ offset, change.pack ]
|
8
|
+
end ]
|
9
|
+
|
10
|
+
{
|
11
|
+
'notes' => packed_notes,
|
12
|
+
'start_dynamic' => start_dynamic,
|
13
|
+
'dynamic_changes' => packed_dcs
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.unpack packing
|
18
|
+
unpacked_notes = Note.split_parse(packing["notes"])
|
19
|
+
unpacked_dcs = Hash[ packing["dynamic_changes"].map do |offset,change|
|
20
|
+
[ offset,Change.unpack(change) ]
|
21
|
+
end ]
|
22
|
+
|
23
|
+
new(
|
24
|
+
packing["start_dynamic"],
|
25
|
+
notes: unpacked_notes,
|
26
|
+
dynamic_changes: unpacked_dcs
|
27
|
+
)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
module Musicality
|
2
|
+
|
3
|
+
class Score
|
4
|
+
class Timed < Score
|
5
|
+
def pack
|
6
|
+
pack_common
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.unpack packing
|
10
|
+
score = Score.unpack_common(packing)
|
11
|
+
new(parts: score.parts, program: score.program)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class TempoBased < Score
|
16
|
+
def pack
|
17
|
+
packed_tcs = Hash[ tempo_changes.map do |offset,change|
|
18
|
+
[offset,change.pack]
|
19
|
+
end ]
|
20
|
+
|
21
|
+
pack_common.merge("start_tempo" => @start_tempo,
|
22
|
+
"tempo_changes" => packed_tcs)
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.unpack packing
|
26
|
+
score = Score.unpack_common(packing)
|
27
|
+
|
28
|
+
unpacked_tcs = Hash[ packing["tempo_changes"].map do |k,v|
|
29
|
+
[k, Change.unpack(v) ]
|
30
|
+
end ]
|
31
|
+
|
32
|
+
new(packing["start_tempo"],
|
33
|
+
tempo_changes: unpacked_tcs,
|
34
|
+
program: score.program,
|
35
|
+
parts: score.parts
|
36
|
+
)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class Unmeasured < TempoBased
|
41
|
+
def pack
|
42
|
+
super()
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.unpack packing
|
46
|
+
score = superclass.unpack(packing)
|
47
|
+
new(score.start_tempo, program: score.program,
|
48
|
+
tempo_changes: score.tempo_changes, parts: score.parts)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class Measured < TempoBased
|
53
|
+
def pack
|
54
|
+
return super().merge("start_meter" => start_meter.to_s,
|
55
|
+
"meter_changes" => Hash[ meter_changes.map do |off,change|
|
56
|
+
[off,change.pack.merge("value" => change.value.to_s)]
|
57
|
+
end ]
|
58
|
+
)
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.unpack packing
|
62
|
+
score = superclass.unpack(packing)
|
63
|
+
unpacked_start_meter = Meter.parse(packing["start_meter"])
|
64
|
+
unpacked_mcs = Hash[ packing["meter_changes"].map do |off,p|
|
65
|
+
[off, Change.unpack(p.merge("value" => Meter.parse(p["value"]))) ]
|
66
|
+
end ]
|
67
|
+
|
68
|
+
new(unpacked_start_meter, score.start_tempo,
|
69
|
+
parts: score.parts, program: score.program,
|
70
|
+
meter_changes: unpacked_mcs, tempo_changes: score.tempo_changes)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.unpack packing
|
75
|
+
type = const_get(packing["type"])
|
76
|
+
type.unpack(packing)
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def pack_common
|
82
|
+
packed_parts = Hash[
|
83
|
+
@parts.map do |name,part|
|
84
|
+
[ name, part.pack ]
|
85
|
+
end
|
86
|
+
]
|
87
|
+
packed_prog = program.pack
|
88
|
+
|
89
|
+
{ "type" => self.class.to_s.split("::")[-1],
|
90
|
+
"program" => packed_prog,
|
91
|
+
"parts" => packed_parts,
|
92
|
+
}
|
93
|
+
end
|
94
|
+
|
95
|
+
def self.unpack_common packing
|
96
|
+
unpacked_parts = Hash[ packing["parts"].map do |name,packed|
|
97
|
+
[name, Part.unpack(packed)]
|
98
|
+
end ]
|
99
|
+
|
100
|
+
unpacked_prog = Program.unpack packing["program"]
|
101
|
+
|
102
|
+
new(program: unpacked_prog,
|
103
|
+
parts: unpacked_parts
|
104
|
+
)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
@@ -0,0 +1,264 @@
|
|
1
|
+
# Autogenerated from a Treetop grammar. Edits may be lost.
|
2
|
+
|
3
|
+
|
4
|
+
module Musicality
|
5
|
+
module Parsing
|
6
|
+
|
7
|
+
module Articulation
|
8
|
+
include Treetop::Runtime
|
9
|
+
|
10
|
+
def root
|
11
|
+
@root ||= :articulation
|
12
|
+
end
|
13
|
+
|
14
|
+
def _nt_articulation
|
15
|
+
start_index = index
|
16
|
+
if node_cache[:articulation].has_key?(index)
|
17
|
+
cached = node_cache[:articulation][index]
|
18
|
+
if cached
|
19
|
+
node_cache[:articulation][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
|
20
|
+
@index = cached.interval.end
|
21
|
+
end
|
22
|
+
return cached
|
23
|
+
end
|
24
|
+
|
25
|
+
i0 = index
|
26
|
+
r1 = _nt_slur
|
27
|
+
if r1
|
28
|
+
r1 = SyntaxNode.new(input, (index-1)...index) if r1 == true
|
29
|
+
r0 = r1
|
30
|
+
else
|
31
|
+
r2 = _nt_legato
|
32
|
+
if r2
|
33
|
+
r2 = SyntaxNode.new(input, (index-1)...index) if r2 == true
|
34
|
+
r0 = r2
|
35
|
+
else
|
36
|
+
r3 = _nt_tenuto
|
37
|
+
if r3
|
38
|
+
r3 = SyntaxNode.new(input, (index-1)...index) if r3 == true
|
39
|
+
r0 = r3
|
40
|
+
else
|
41
|
+
r4 = _nt_portato
|
42
|
+
if r4
|
43
|
+
r4 = SyntaxNode.new(input, (index-1)...index) if r4 == true
|
44
|
+
r0 = r4
|
45
|
+
else
|
46
|
+
r5 = _nt_staccato
|
47
|
+
if r5
|
48
|
+
r5 = SyntaxNode.new(input, (index-1)...index) if r5 == true
|
49
|
+
r0 = r5
|
50
|
+
else
|
51
|
+
r6 = _nt_staccatissimo
|
52
|
+
if r6
|
53
|
+
r6 = SyntaxNode.new(input, (index-1)...index) if r6 == true
|
54
|
+
r0 = r6
|
55
|
+
else
|
56
|
+
@index = i0
|
57
|
+
r0 = nil
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
node_cache[:articulation][start_index] = r0
|
66
|
+
|
67
|
+
r0
|
68
|
+
end
|
69
|
+
|
70
|
+
module Slur0
|
71
|
+
def to_articulation
|
72
|
+
Musicality::Articulations::SLUR
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def _nt_slur
|
77
|
+
start_index = index
|
78
|
+
if node_cache[:slur].has_key?(index)
|
79
|
+
cached = node_cache[:slur][index]
|
80
|
+
if cached
|
81
|
+
node_cache[:slur][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
|
82
|
+
@index = cached.interval.end
|
83
|
+
end
|
84
|
+
return cached
|
85
|
+
end
|
86
|
+
|
87
|
+
if (match_len = has_terminal?("=", false, index))
|
88
|
+
r0 = instantiate_node(SyntaxNode,input, index...(index + match_len))
|
89
|
+
r0.extend(Slur0)
|
90
|
+
@index += match_len
|
91
|
+
else
|
92
|
+
terminal_parse_failure("=")
|
93
|
+
r0 = nil
|
94
|
+
end
|
95
|
+
|
96
|
+
node_cache[:slur][start_index] = r0
|
97
|
+
|
98
|
+
r0
|
99
|
+
end
|
100
|
+
|
101
|
+
module Legato0
|
102
|
+
def to_articulation
|
103
|
+
Musicality::Articulations::LEGATO
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def _nt_legato
|
108
|
+
start_index = index
|
109
|
+
if node_cache[:legato].has_key?(index)
|
110
|
+
cached = node_cache[:legato][index]
|
111
|
+
if cached
|
112
|
+
node_cache[:legato][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
|
113
|
+
@index = cached.interval.end
|
114
|
+
end
|
115
|
+
return cached
|
116
|
+
end
|
117
|
+
|
118
|
+
if (match_len = has_terminal?("|", false, index))
|
119
|
+
r0 = instantiate_node(SyntaxNode,input, index...(index + match_len))
|
120
|
+
r0.extend(Legato0)
|
121
|
+
@index += match_len
|
122
|
+
else
|
123
|
+
terminal_parse_failure("|")
|
124
|
+
r0 = nil
|
125
|
+
end
|
126
|
+
|
127
|
+
node_cache[:legato][start_index] = r0
|
128
|
+
|
129
|
+
r0
|
130
|
+
end
|
131
|
+
|
132
|
+
module Tenuto0
|
133
|
+
def to_articulation
|
134
|
+
Musicality::Articulations::TENUTO
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def _nt_tenuto
|
139
|
+
start_index = index
|
140
|
+
if node_cache[:tenuto].has_key?(index)
|
141
|
+
cached = node_cache[:tenuto][index]
|
142
|
+
if cached
|
143
|
+
node_cache[:tenuto][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
|
144
|
+
@index = cached.interval.end
|
145
|
+
end
|
146
|
+
return cached
|
147
|
+
end
|
148
|
+
|
149
|
+
if (match_len = has_terminal?("_", false, index))
|
150
|
+
r0 = instantiate_node(SyntaxNode,input, index...(index + match_len))
|
151
|
+
r0.extend(Tenuto0)
|
152
|
+
@index += match_len
|
153
|
+
else
|
154
|
+
terminal_parse_failure("_")
|
155
|
+
r0 = nil
|
156
|
+
end
|
157
|
+
|
158
|
+
node_cache[:tenuto][start_index] = r0
|
159
|
+
|
160
|
+
r0
|
161
|
+
end
|
162
|
+
|
163
|
+
module Portato0
|
164
|
+
def to_articulation
|
165
|
+
Musicality::Articulations::PORTATO
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def _nt_portato
|
170
|
+
start_index = index
|
171
|
+
if node_cache[:portato].has_key?(index)
|
172
|
+
cached = node_cache[:portato][index]
|
173
|
+
if cached
|
174
|
+
node_cache[:portato][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
|
175
|
+
@index = cached.interval.end
|
176
|
+
end
|
177
|
+
return cached
|
178
|
+
end
|
179
|
+
|
180
|
+
if (match_len = has_terminal?("%", false, index))
|
181
|
+
r0 = instantiate_node(SyntaxNode,input, index...(index + match_len))
|
182
|
+
r0.extend(Portato0)
|
183
|
+
@index += match_len
|
184
|
+
else
|
185
|
+
terminal_parse_failure("%")
|
186
|
+
r0 = nil
|
187
|
+
end
|
188
|
+
|
189
|
+
node_cache[:portato][start_index] = r0
|
190
|
+
|
191
|
+
r0
|
192
|
+
end
|
193
|
+
|
194
|
+
module Staccato0
|
195
|
+
def to_articulation
|
196
|
+
Musicality::Articulations::STACCATO
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
def _nt_staccato
|
201
|
+
start_index = index
|
202
|
+
if node_cache[:staccato].has_key?(index)
|
203
|
+
cached = node_cache[:staccato][index]
|
204
|
+
if cached
|
205
|
+
node_cache[:staccato][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
|
206
|
+
@index = cached.interval.end
|
207
|
+
end
|
208
|
+
return cached
|
209
|
+
end
|
210
|
+
|
211
|
+
if (match_len = has_terminal?(".", false, index))
|
212
|
+
r0 = instantiate_node(SyntaxNode,input, index...(index + match_len))
|
213
|
+
r0.extend(Staccato0)
|
214
|
+
@index += match_len
|
215
|
+
else
|
216
|
+
terminal_parse_failure(".")
|
217
|
+
r0 = nil
|
218
|
+
end
|
219
|
+
|
220
|
+
node_cache[:staccato][start_index] = r0
|
221
|
+
|
222
|
+
r0
|
223
|
+
end
|
224
|
+
|
225
|
+
module Staccatissimo0
|
226
|
+
def to_articulation
|
227
|
+
Musicality::Articulations::STACCATISSIMO
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
def _nt_staccatissimo
|
232
|
+
start_index = index
|
233
|
+
if node_cache[:staccatissimo].has_key?(index)
|
234
|
+
cached = node_cache[:staccatissimo][index]
|
235
|
+
if cached
|
236
|
+
node_cache[:staccatissimo][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
|
237
|
+
@index = cached.interval.end
|
238
|
+
end
|
239
|
+
return cached
|
240
|
+
end
|
241
|
+
|
242
|
+
if (match_len = has_terminal?("'", false, index))
|
243
|
+
r0 = instantiate_node(SyntaxNode,input, index...(index + match_len))
|
244
|
+
r0.extend(Staccatissimo0)
|
245
|
+
@index += match_len
|
246
|
+
else
|
247
|
+
terminal_parse_failure("'")
|
248
|
+
r0 = nil
|
249
|
+
end
|
250
|
+
|
251
|
+
node_cache[:staccatissimo][start_index] = r0
|
252
|
+
|
253
|
+
r0
|
254
|
+
end
|
255
|
+
|
256
|
+
end
|
257
|
+
|
258
|
+
class ArticulationParser < Treetop::Runtime::CompiledParser
|
259
|
+
include Articulation
|
260
|
+
end
|
261
|
+
|
262
|
+
|
263
|
+
end
|
264
|
+
end
|