musicality 0.8.0 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/ChangeLog.md +27 -1
- data/README.md +153 -10
- data/bin/collidify +102 -0
- data/bin/lilify +57 -29
- data/bin/midify +64 -24
- data/bin/musicality +39 -0
- data/examples/composition/auto_counterpoint.rb +4 -5
- data/examples/composition/part_generator.rb +8 -2
- data/examples/composition/scale_exercise.rb +1 -1
- data/examples/notation/notes.rb +27 -0
- data/examples/notation/parts.rb +51 -0
- data/examples/notation/scores.rb +38 -0
- data/examples/notation/twinkle.rb +34 -0
- data/examples/notation/twinkle.score +33 -0
- data/lib/musicality.rb +46 -11
- data/lib/musicality/composition/dsl/score_dsl.rb +2 -2
- data/lib/musicality/composition/dsl/score_methods.rb +10 -7
- data/lib/musicality/notation/conversion/change_conversion.rb +1 -1
- data/lib/musicality/notation/conversion/note_time_converter.rb +6 -23
- data/lib/musicality/notation/conversion/score_conversion.rb +15 -15
- data/lib/musicality/notation/conversion/score_converter.rb +50 -67
- data/lib/musicality/notation/model/articulations.rb +3 -2
- data/lib/musicality/notation/model/change.rb +15 -6
- data/lib/musicality/notation/model/dynamics.rb +11 -8
- data/lib/musicality/notation/model/instrument.rb +61 -0
- data/lib/musicality/notation/model/instruments.rb +111 -0
- data/lib/musicality/notation/model/key.rb +137 -0
- data/lib/musicality/notation/model/keys.rb +37 -0
- data/lib/musicality/notation/model/link.rb +6 -19
- data/lib/musicality/notation/model/mark.rb +43 -0
- data/lib/musicality/notation/model/marks.rb +11 -0
- data/lib/musicality/notation/model/meter.rb +4 -0
- data/lib/musicality/notation/model/note.rb +42 -28
- data/lib/musicality/notation/model/part.rb +18 -5
- data/lib/musicality/notation/model/pitch.rb +13 -4
- data/lib/musicality/notation/model/score.rb +104 -66
- data/lib/musicality/notation/model/symbols.rb +16 -11
- data/lib/musicality/notation/parsing/articulation_parsing.rb +38 -38
- data/lib/musicality/notation/parsing/articulation_parsing.treetop +14 -14
- data/lib/musicality/notation/parsing/link_parsing.rb +6 -6
- data/lib/musicality/notation/parsing/link_parsing.treetop +3 -3
- data/lib/musicality/notation/parsing/mark_parsing.rb +138 -0
- data/lib/musicality/notation/parsing/mark_parsing.treetop +31 -0
- data/lib/musicality/notation/parsing/note_node.rb +19 -12
- data/lib/musicality/notation/parsing/note_parsing.rb +218 -87
- data/lib/musicality/notation/parsing/note_parsing.treetop +9 -5
- data/lib/musicality/notation/parsing/numbers/nonnegative_integer_parsing.rb +7 -2
- data/lib/musicality/notation/parsing/numbers/nonnegative_integer_parsing.treetop +1 -1
- data/lib/musicality/notation/parsing/numbers/positive_integer_parsing.rb +6 -4
- data/lib/musicality/notation/parsing/numbers/positive_integer_parsing.treetop +1 -1
- data/lib/musicality/notation/util/function.rb +41 -18
- data/lib/musicality/packable.rb +156 -0
- data/lib/musicality/performance/conversion/glissando_converter.rb +2 -2
- data/lib/musicality/performance/conversion/note_sequence_extractor.rb +223 -70
- data/lib/musicality/performance/conversion/portamento_converter.rb +5 -2
- data/lib/musicality/performance/conversion/score_collator.rb +70 -64
- data/lib/musicality/performance/midi/midi_events.rb +3 -3
- data/lib/musicality/performance/midi/midi_settings.rb +127 -0
- data/lib/musicality/performance/midi/midi_util.rb +8 -2
- data/lib/musicality/performance/midi/part_sequencer.rb +19 -18
- data/lib/musicality/performance/midi/score_sequencer.rb +13 -9
- data/lib/musicality/performance/midi/score_sequencing.rb +5 -5
- data/lib/musicality/performance/model/attack.rb +8 -0
- data/lib/musicality/performance/model/duration_functions.rb +23 -0
- data/lib/musicality/performance/model/note_sequence.rb +52 -95
- data/lib/musicality/performance/model/separation.rb +10 -0
- data/lib/musicality/performance/supercollider/add_actions.rb +13 -0
- data/lib/musicality/performance/supercollider/bundle.rb +18 -0
- data/lib/musicality/performance/supercollider/conductor.rb +125 -0
- data/lib/musicality/performance/supercollider/group.rb +71 -0
- data/lib/musicality/performance/supercollider/message.rb +26 -0
- data/lib/musicality/performance/supercollider/node.rb +122 -0
- data/lib/musicality/performance/supercollider/performer.rb +123 -0
- data/lib/musicality/performance/supercollider/score_conducting.rb +17 -0
- data/lib/musicality/performance/supercollider/server.rb +8 -0
- data/lib/musicality/performance/supercollider/synth.rb +43 -0
- data/lib/musicality/performance/supercollider/synthdef.rb +57 -0
- data/lib/musicality/performance/supercollider/synthdef_settings.rb +23 -0
- data/lib/musicality/performance/supercollider/synthdefs.rb +1654 -0
- data/lib/musicality/{composition/model/pitch_class.rb → pitch_class.rb} +1 -1
- data/lib/musicality/{composition/model/pitch_classes.rb → pitch_classes.rb} +9 -9
- data/lib/musicality/printing/lilypond/clef.rb +12 -0
- data/lib/musicality/printing/lilypond/key_engraving.rb +9 -0
- data/lib/musicality/printing/lilypond/lilypond_settings.rb +105 -0
- data/lib/musicality/printing/lilypond/meter_engraving.rb +1 -1
- data/lib/musicality/printing/lilypond/note_engraving.rb +112 -30
- data/lib/musicality/printing/lilypond/part_engraver.rb +114 -3
- data/lib/musicality/printing/lilypond/pitch_class_engraving.rb +22 -0
- data/lib/musicality/printing/lilypond/pitch_engraving.rb +2 -15
- data/lib/musicality/printing/lilypond/score_engraver.rb +44 -73
- data/lib/musicality/printing/lilypond/score_engraving.rb +3 -3
- data/lib/musicality/project/create_tasks.rb +31 -0
- data/lib/musicality/project/file_cleaner.rb +19 -0
- data/lib/musicality/project/file_raker.rb +107 -0
- data/lib/musicality/project/load_config.rb +43 -0
- data/lib/musicality/project/project.rb +64 -0
- data/lib/musicality/version.rb +1 -1
- data/musicality.gemspec +3 -0
- data/spec/composition/util/random_sampler_spec.rb +1 -1
- data/spec/notation/conversion/measure_note_map_spec.rb +1 -1
- data/spec/notation/conversion/note_time_converter_spec.rb +5 -85
- data/spec/notation/conversion/score_conversion_spec.rb +6 -41
- data/spec/notation/conversion/score_converter_spec.rb +19 -137
- data/spec/notation/model/change_spec.rb +55 -0
- data/spec/notation/model/key_spec.rb +171 -0
- data/spec/notation/model/link_spec.rb +34 -5
- data/spec/notation/model/meter_spec.rb +15 -0
- data/spec/notation/model/note_spec.rb +33 -27
- data/spec/notation/model/part_spec.rb +53 -4
- data/spec/notation/model/pitch_spec.rb +15 -0
- data/spec/notation/model/score_spec.rb +64 -72
- data/spec/notation/parsing/link_nodes_spec.rb +3 -3
- data/spec/notation/parsing/link_parsing_spec.rb +6 -6
- data/spec/notation/parsing/note_node_spec.rb +34 -9
- data/spec/notation/parsing/note_parsing_spec.rb +11 -9
- data/spec/notation/parsing/numbers/nonnegative_integer_spec.rb +4 -0
- data/spec/notation/parsing/pitch_node_spec.rb +0 -1
- data/spec/notation/util/value_computer_spec.rb +2 -2
- data/spec/performance/conversion/glissando_converter_spec.rb +9 -9
- data/spec/performance/conversion/note_sequence_extractor_spec.rb +48 -53
- data/spec/performance/conversion/portamento_converter_spec.rb +11 -9
- data/spec/performance/conversion/score_collator_spec.rb +59 -63
- data/spec/performance/midi/midi_util_spec.rb +22 -8
- data/spec/performance/midi/part_sequencer_spec.rb +2 -2
- data/spec/performance/midi/score_sequencer_spec.rb +12 -10
- data/spec/performance/midi/score_sequencing_spec.rb +2 -2
- data/spec/performance/model/note_sequence_spec.rb +41 -134
- data/spec/printing/note_engraving_spec.rb +204 -0
- data/spec/printing/score_engraver_spec.rb +40 -0
- data/spec/spec_helper.rb +1 -0
- metadata +69 -23
- data/examples/notation/hip.rb +0 -32
- data/examples/notation/missed_connection.rb +0 -26
- data/examples/notation/song1.rb +0 -33
- data/examples/notation/song2.rb +0 -32
- data/lib/musicality/notation/model/links.rb +0 -11
- data/lib/musicality/notation/packing/change_packing.rb +0 -56
- data/lib/musicality/notation/packing/part_packing.rb +0 -31
- data/lib/musicality/notation/packing/score_packing.rb +0 -123
- data/lib/musicality/performance/model/note_attacks.rb +0 -19
- data/lib/musicality/performance/util/note_linker.rb +0 -28
- data/spec/notation/packing/change_packing_spec.rb +0 -304
- data/spec/notation/packing/part_packing_spec.rb +0 -66
- data/spec/notation/packing/score_packing_spec.rb +0 -255
- data/spec/performance/util/note_linker_spec.rb +0 -68
@@ -1,17 +1,20 @@
|
|
1
|
-
require 'yaml'
|
2
|
-
|
3
1
|
module Musicality
|
4
2
|
|
5
3
|
class Part
|
4
|
+
include Packable
|
6
5
|
include Validatable
|
7
6
|
|
8
|
-
|
7
|
+
special_packing(:notes) {|notes| notes.map {|n| n.to_s }.join(" ") }
|
8
|
+
special_unpacking(:notes) {|notes_str| Note.split_parse(notes_str) }
|
9
|
+
|
10
|
+
attr_accessor :start_dynamic, :dynamic_changes, :notes, :settings
|
9
11
|
|
10
|
-
def initialize start_dynamic, notes: [], dynamic_changes: {}
|
12
|
+
def initialize start_dynamic, notes: [], dynamic_changes: {}, settings: []
|
11
13
|
@notes = notes
|
12
14
|
@start_dynamic = start_dynamic
|
13
15
|
@dynamic_changes = dynamic_changes
|
14
|
-
|
16
|
+
@settings = settings
|
17
|
+
|
15
18
|
yield(self) if block_given?
|
16
19
|
end
|
17
20
|
|
@@ -49,6 +52,16 @@ class Part
|
|
49
52
|
raise RangeError, "dynamic change values #{outofrange} are not between 0 and 1"
|
50
53
|
end
|
51
54
|
end
|
55
|
+
|
56
|
+
def transpose interval
|
57
|
+
p = self.clone
|
58
|
+
p.notes.each {|n| n.transpose!(interval) }
|
59
|
+
return p
|
60
|
+
end
|
61
|
+
|
62
|
+
def find_settings settings_class
|
63
|
+
settings.find {|s| s.is_a? settings_class }
|
64
|
+
end
|
52
65
|
end
|
53
66
|
|
54
67
|
end
|
@@ -19,11 +19,13 @@ module Musicality
|
|
19
19
|
#
|
20
20
|
class Pitch
|
21
21
|
include Comparable
|
22
|
+
include Packable
|
23
|
+
|
22
24
|
attr_reader :octave, :semitone, :cent, :total_cents
|
23
25
|
|
24
26
|
#The default number of semitones per octave is 12, corresponding to
|
25
27
|
# the twelve-tone equal temperment tuning system.
|
26
|
-
SEMITONES_PER_OCTAVE =
|
28
|
+
SEMITONES_PER_OCTAVE = PitchClass::MOD
|
27
29
|
CENTS_PER_SEMITONE = 100
|
28
30
|
CENTS_PER_OCTAVE = SEMITONES_PER_OCTAVE * CENTS_PER_SEMITONE
|
29
31
|
|
@@ -116,8 +118,12 @@ class Pitch
|
|
116
118
|
Pitch.new(cent: @total_cents)
|
117
119
|
end
|
118
120
|
|
119
|
-
def
|
120
|
-
|
121
|
+
def natural?
|
122
|
+
[0,2,4,5,7,9,11].include?(semitone)
|
123
|
+
end
|
124
|
+
|
125
|
+
def self.pc_str semitone, sharpit
|
126
|
+
case semitone
|
121
127
|
when 0 then "C"
|
122
128
|
when 1 then sharpit ? "C#" : "Db"
|
123
129
|
when 2 then "D"
|
@@ -131,7 +137,10 @@ class Pitch
|
|
131
137
|
when 10 then sharpit ? "A#" : "Bb"
|
132
138
|
when 11 then "B"
|
133
139
|
end
|
134
|
-
|
140
|
+
end
|
141
|
+
|
142
|
+
def to_s(sharpit = false)
|
143
|
+
letter = Pitch.pc_str(semitone, sharpit)
|
135
144
|
if @cent == 0
|
136
145
|
return letter + octave.to_s
|
137
146
|
elsif @cent > 0
|
@@ -1,8 +1,16 @@
|
|
1
1
|
module Musicality
|
2
2
|
|
3
3
|
class Score
|
4
|
+
include Packable
|
4
5
|
include Validatable
|
5
|
-
|
6
|
+
|
7
|
+
special_packing(:program){|p| p.map {|range| range.to_s }}
|
8
|
+
special_unpacking(:program){|p| p.map {|str| parse_numeric_range(str) }}
|
9
|
+
|
10
|
+
special_packing(:sections){|s| Hash[ s.map {|name,range| [name,range.to_s] } ]}
|
11
|
+
special_unpacking(:sections){|s| Hash[ s.map {|name,str| [name,parse_numeric_range(str)] } ]}
|
12
|
+
|
13
|
+
attr_accessor :parts, :sections, :program, :start_key, :key_changes
|
6
14
|
attr_writer :title, :composer
|
7
15
|
|
8
16
|
def title value = nil
|
@@ -20,25 +28,33 @@ class Score
|
|
20
28
|
@composer = value
|
21
29
|
end
|
22
30
|
end
|
23
|
-
|
24
|
-
def initialize parts: {}, program: [], title: nil, composer: nil, sections: {}
|
31
|
+
|
32
|
+
def initialize parts: {}, program: [], title: nil, composer: nil, sections: {}, start_key: Keys::C_MAJOR, key_changes: {}
|
25
33
|
@parts = parts
|
26
34
|
@program = program
|
27
35
|
@title = title
|
28
36
|
@composer = composer
|
29
37
|
@sections = sections
|
38
|
+
@start_key = start_key
|
39
|
+
@key_changes = key_changes
|
30
40
|
yield(self) if block_given?
|
31
41
|
end
|
32
42
|
|
33
43
|
def validatables; @parts.values; end
|
34
|
-
def check_methods
|
44
|
+
def check_methods
|
45
|
+
[:check_program, :check_parts, :check_start_key, :check_key_changes ]
|
46
|
+
end
|
35
47
|
|
36
48
|
def clone
|
37
49
|
Marshal.load(Marshal.dump(self))
|
38
50
|
end
|
39
51
|
|
40
52
|
def ==(other)
|
41
|
-
return
|
53
|
+
return self.class == other.class &&
|
54
|
+
@parts == other.parts && @program == other.program &&
|
55
|
+
@program == other.program && @sections == other.sections &&
|
56
|
+
@title == other.title && @composer == other.composer &&
|
57
|
+
@start_key == other.start_key && @key_changes == other.key_changes
|
42
58
|
end
|
43
59
|
|
44
60
|
def max_part_duration
|
@@ -55,72 +71,24 @@ class Score
|
|
55
71
|
end
|
56
72
|
alias duration seconds_long
|
57
73
|
end
|
58
|
-
|
59
|
-
|
60
|
-
|
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 Tempo < Score
|
78
|
+
attr_accessor :start_tempo, :tempo_changes, :start_meter, :meter_changes
|
61
79
|
|
62
80
|
# See Score#initialize for remaining kwargs
|
63
|
-
def initialize start_tempo, tempo_changes: {},
|
81
|
+
def initialize start_meter, start_tempo, tempo_changes: {}, meter_changes: {}, parts: {}, program: [], title: nil, composer: nil, sections: {}, start_key: Keys::C_MAJOR, key_changes: {}
|
64
82
|
@start_tempo = start_tempo
|
65
83
|
@tempo_changes = tempo_changes
|
66
|
-
super(**kwargs)
|
67
|
-
end
|
68
|
-
|
69
|
-
def check_methods
|
70
|
-
super() + [:check_start_tempo, :check_tempo_changes]
|
71
|
-
end
|
72
|
-
|
73
|
-
def ==(other)
|
74
|
-
return super(other) && @start_tempo == other.start_tempo &&
|
75
|
-
@tempo_changes == other.tempo_changes
|
76
|
-
end
|
77
|
-
|
78
|
-
def notes_long
|
79
|
-
max_part_duration
|
80
|
-
end
|
81
|
-
|
82
|
-
private
|
83
|
-
|
84
|
-
def check_start_tempo
|
85
|
-
if @start_tempo <= 0
|
86
|
-
raise NonPositiveError, "Start tempo (#{@start_tempo}) is not positive"
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
def check_tempo_changes
|
91
|
-
badtypes = @tempo_changes.select {|k,v| !v.end_value.is_a?(Numeric) }
|
92
|
-
if badtypes.any?
|
93
|
-
raise TypeError, "Found non-numeric tempo change values: #{badtypes}"
|
94
|
-
end
|
95
|
-
|
96
|
-
badvalues = @tempo_changes.select {|k,v| v.end_value <= 0 }
|
97
|
-
if badvalues.any?
|
98
|
-
raise NonPositiveError, "Found non-positive tempo changes values: #{badvalues}"
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
# Tempo-based score without meter, bar lines, or fixed pulse (beat).
|
104
|
-
# Offsets are note-based, and tempo values are in quarter-notes-per-minute.
|
105
|
-
class Unmeasured < Score::TempoBased
|
106
|
-
alias duration notes_long
|
107
|
-
end
|
108
|
-
|
109
|
-
# Tempo-based score with meter, bar lines, and a fixed pulse (beat).
|
110
|
-
# Offsets are measure-based, and tempo values are in beats-per-minute.
|
111
|
-
class Measured < Score::TempoBased
|
112
|
-
attr_accessor :start_meter, :meter_changes
|
113
|
-
|
114
|
-
# See Score::TempoBased#initialize for remaining kwargs
|
115
|
-
def initialize start_meter, start_tempo, meter_changes: {}, **kwargs
|
116
84
|
@start_meter = start_meter
|
117
85
|
@meter_changes = meter_changes
|
118
86
|
|
119
|
-
super(
|
87
|
+
super(parts: parts, program: program, title: title, composer: composer, sections: sections, start_key: start_key, key_changes: key_changes)
|
120
88
|
end
|
121
89
|
|
122
90
|
def check_methods
|
123
|
-
super() + [:check_start_meter, :check_meter_changes]
|
91
|
+
super() + [:check_start_tempo, :check_tempo_changes, :check_start_meter, :check_meter_changes]
|
124
92
|
end
|
125
93
|
|
126
94
|
def validatables
|
@@ -128,10 +96,17 @@ class Score
|
|
128
96
|
end
|
129
97
|
|
130
98
|
def ==(other)
|
131
|
-
return super(other) &&
|
132
|
-
|
99
|
+
return super(other) &&
|
100
|
+
@start_tempo == other.start_tempo &&
|
101
|
+
@tempo_changes == other.tempo_changes
|
102
|
+
@start_meter == other.start_meter &&
|
103
|
+
@meter_changes == other.meter_changes
|
133
104
|
end
|
134
105
|
|
106
|
+
def notes_long
|
107
|
+
max_part_duration
|
108
|
+
end
|
109
|
+
|
135
110
|
def measures_long note_dur = self.notes_long
|
136
111
|
noff_end = note_dur
|
137
112
|
noff_prev = 0.to_r
|
@@ -157,6 +132,24 @@ class Score
|
|
157
132
|
|
158
133
|
private
|
159
134
|
|
135
|
+
def check_start_tempo
|
136
|
+
if @start_tempo <= 0
|
137
|
+
raise NonPositiveError, "Start tempo (#{@start_tempo}) is not positive"
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def check_tempo_changes
|
142
|
+
badtypes = @tempo_changes.select {|k,v| !v.end_value.is_a?(Numeric) }
|
143
|
+
if badtypes.any?
|
144
|
+
raise TypeError, "Found non-numeric tempo change values: #{badtypes}"
|
145
|
+
end
|
146
|
+
|
147
|
+
badvalues = @tempo_changes.select {|k,v| v.end_value <= 0 }
|
148
|
+
if badvalues.any?
|
149
|
+
raise NonPositiveError, "Found non-positive tempo changes values: #{badvalues}"
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
160
153
|
def check_start_meter
|
161
154
|
unless @start_meter.is_a? Meter
|
162
155
|
raise TypeError, "Start meter #{@start_meter} is not a Meter object"
|
@@ -166,14 +159,14 @@ class Score
|
|
166
159
|
def check_meter_changes
|
167
160
|
badtypes = @meter_changes.select {|k,v| !v.end_value.is_a?(Meter) }
|
168
161
|
if badtypes.any?
|
169
|
-
raise TypeError, "Found meter change values that are not Meter objects: #{
|
162
|
+
raise TypeError, "Found meter change values that are not Meter objects: #{badtypes}"
|
170
163
|
end
|
171
|
-
|
164
|
+
|
172
165
|
badoffsets = @meter_changes.select {|k,v| k != k.to_i }
|
173
166
|
if badoffsets.any?
|
174
167
|
raise NonIntegerError, "Found meter changes at non-integer offsets: #{badoffsets}"
|
175
168
|
end
|
176
|
-
|
169
|
+
|
177
170
|
nonzero_duration = @meter_changes.select {|k,v| !v.is_a?(Change::Immediate) }
|
178
171
|
if nonzero_duration.any?
|
179
172
|
raise NonZeroError, "Found meter changes that are not immediate: #{nonzero_duration}"
|
@@ -206,6 +199,51 @@ class Score
|
|
206
199
|
raise TypeError, "Non-Part part value(s) found: #{non_parts}"
|
207
200
|
end
|
208
201
|
end
|
202
|
+
|
203
|
+
def check_start_key
|
204
|
+
unless @start_key.is_a? Key
|
205
|
+
raise TypeError, "Start key #{@start_key} is not a Key object"
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
def check_key_changes
|
210
|
+
badtypes = @key_changes.select {|k,v| !v.end_value.is_a?(Key) }
|
211
|
+
if badtypes.any?
|
212
|
+
raise TypeError, "Found key change values that are not Key objects: #{badtypes}"
|
213
|
+
end
|
214
|
+
|
215
|
+
badoffsets = @key_changes.select {|k,v| k != k.to_i }
|
216
|
+
if badoffsets.any?
|
217
|
+
raise NonIntegerError, "Found key changes at non-integer offsets: #{badoffsets}"
|
218
|
+
end
|
219
|
+
|
220
|
+
nonzero_duration = @key_changes.select {|k,v| !v.is_a?(Change::Immediate) }
|
221
|
+
if nonzero_duration.any?
|
222
|
+
raise NonZeroError, "Found key changes that are not immediate: #{nonzero_duration}"
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
private
|
227
|
+
|
228
|
+
def self.parse_numeric str
|
229
|
+
if str.include? "."
|
230
|
+
str.to_f
|
231
|
+
elsif str.include? "/"
|
232
|
+
str.to_r
|
233
|
+
else
|
234
|
+
str.to_i
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
def self.parse_numeric_range str
|
239
|
+
result = str.match /(\d+([\.\/]\d+)?)([\.]{2,3})(\d+([\.\/]\d+)?)/
|
240
|
+
raise ArgumentError, "string #{str} is not a numeric range" if result.nil?
|
241
|
+
|
242
|
+
dots = result.values_at(3)
|
243
|
+
l_num = parse_numeric(result.values_at(1)[0])
|
244
|
+
r_num = parse_numeric(result.values_at(4)[0])
|
245
|
+
Range.new l_num, r_num, dots.size == 3
|
246
|
+
end
|
209
247
|
end
|
210
248
|
|
211
249
|
end
|
@@ -1,22 +1,27 @@
|
|
1
1
|
module Musicality
|
2
2
|
|
3
|
+
# These symbols all need to match the notation parsers
|
4
|
+
|
3
5
|
ARTICULATION_SYMBOLS = {
|
4
|
-
Articulations::
|
5
|
-
Articulations::
|
6
|
-
Articulations::
|
7
|
-
Articulations::PORTATO => "
|
6
|
+
Articulations::TENUTO => "-",
|
7
|
+
Articulations::ACCENT => ">",
|
8
|
+
Articulations::MARCATO => "^",
|
9
|
+
Articulations::PORTATO => "_",
|
8
10
|
Articulations::STACCATO => ".",
|
9
|
-
Articulations::STACCATISSIMO => "
|
11
|
+
Articulations::STACCATISSIMO => "!",
|
10
12
|
}
|
11
13
|
|
12
14
|
LINK_SYMBOLS = {
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
Links::SLUR => ARTICULATION_SYMBOLS[Articulations::SLUR],
|
17
|
-
Links::LEGATO => ARTICULATION_SYMBOLS[Articulations::LEGATO],
|
15
|
+
Link::Tie => "~",
|
16
|
+
Link::Glissando => ";",
|
17
|
+
Link::Portamento => ":",
|
18
18
|
}
|
19
19
|
|
20
|
-
|
20
|
+
MARK_SYMBOLS = {
|
21
|
+
Mark::Slur::Begin => "(",
|
22
|
+
Mark::Slur::End => ")",
|
23
|
+
Mark::Triplet::Begin => "[",
|
24
|
+
Mark::Triplet::End => "]",
|
25
|
+
}
|
21
26
|
|
22
27
|
end
|
@@ -23,17 +23,17 @@ module Articulation
|
|
23
23
|
end
|
24
24
|
|
25
25
|
i0 = index
|
26
|
-
r1 =
|
26
|
+
r1 = _nt_tenuto
|
27
27
|
if r1
|
28
28
|
r1 = SyntaxNode.new(input, (index-1)...index) if r1 == true
|
29
29
|
r0 = r1
|
30
30
|
else
|
31
|
-
r2 =
|
31
|
+
r2 = _nt_accent
|
32
32
|
if r2
|
33
33
|
r2 = SyntaxNode.new(input, (index-1)...index) if r2 == true
|
34
34
|
r0 = r2
|
35
35
|
else
|
36
|
-
r3 =
|
36
|
+
r3 = _nt_marcato
|
37
37
|
if r3
|
38
38
|
r3 = SyntaxNode.new(input, (index-1)...index) if r3 == true
|
39
39
|
r0 = r3
|
@@ -67,95 +67,95 @@ module Articulation
|
|
67
67
|
r0
|
68
68
|
end
|
69
69
|
|
70
|
-
module
|
70
|
+
module Tenuto0
|
71
71
|
def to_articulation
|
72
|
-
Musicality::Articulations::
|
72
|
+
Musicality::Articulations::TENUTO
|
73
73
|
end
|
74
74
|
end
|
75
75
|
|
76
|
-
def
|
76
|
+
def _nt_tenuto
|
77
77
|
start_index = index
|
78
|
-
if node_cache[:
|
79
|
-
cached = node_cache[:
|
78
|
+
if node_cache[:tenuto].has_key?(index)
|
79
|
+
cached = node_cache[:tenuto][index]
|
80
80
|
if cached
|
81
|
-
node_cache[:
|
81
|
+
node_cache[:tenuto][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
|
82
82
|
@index = cached.interval.end
|
83
83
|
end
|
84
84
|
return cached
|
85
85
|
end
|
86
86
|
|
87
|
-
if (match_len = has_terminal?("
|
87
|
+
if (match_len = has_terminal?("-", false, index))
|
88
88
|
r0 = instantiate_node(SyntaxNode,input, index...(index + match_len))
|
89
|
-
r0.extend(
|
89
|
+
r0.extend(Tenuto0)
|
90
90
|
@index += match_len
|
91
91
|
else
|
92
|
-
terminal_parse_failure("
|
92
|
+
terminal_parse_failure('"-"')
|
93
93
|
r0 = nil
|
94
94
|
end
|
95
95
|
|
96
|
-
node_cache[:
|
96
|
+
node_cache[:tenuto][start_index] = r0
|
97
97
|
|
98
98
|
r0
|
99
99
|
end
|
100
100
|
|
101
|
-
module
|
101
|
+
module Accent0
|
102
102
|
def to_articulation
|
103
|
-
Musicality::Articulations::
|
103
|
+
Musicality::Articulations::ACCENT
|
104
104
|
end
|
105
105
|
end
|
106
106
|
|
107
|
-
def
|
107
|
+
def _nt_accent
|
108
108
|
start_index = index
|
109
|
-
if node_cache[:
|
110
|
-
cached = node_cache[:
|
109
|
+
if node_cache[:accent].has_key?(index)
|
110
|
+
cached = node_cache[:accent][index]
|
111
111
|
if cached
|
112
|
-
node_cache[:
|
112
|
+
node_cache[:accent][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
|
113
113
|
@index = cached.interval.end
|
114
114
|
end
|
115
115
|
return cached
|
116
116
|
end
|
117
117
|
|
118
|
-
if (match_len = has_terminal?("
|
118
|
+
if (match_len = has_terminal?(">", false, index))
|
119
119
|
r0 = instantiate_node(SyntaxNode,input, index...(index + match_len))
|
120
|
-
r0.extend(
|
120
|
+
r0.extend(Accent0)
|
121
121
|
@index += match_len
|
122
122
|
else
|
123
|
-
terminal_parse_failure("
|
123
|
+
terminal_parse_failure('">"')
|
124
124
|
r0 = nil
|
125
125
|
end
|
126
126
|
|
127
|
-
node_cache[:
|
127
|
+
node_cache[:accent][start_index] = r0
|
128
128
|
|
129
129
|
r0
|
130
130
|
end
|
131
131
|
|
132
|
-
module
|
132
|
+
module Marcato0
|
133
133
|
def to_articulation
|
134
|
-
Musicality::Articulations::
|
134
|
+
Musicality::Articulations::MARCATO
|
135
135
|
end
|
136
136
|
end
|
137
137
|
|
138
|
-
def
|
138
|
+
def _nt_marcato
|
139
139
|
start_index = index
|
140
|
-
if node_cache[:
|
141
|
-
cached = node_cache[:
|
140
|
+
if node_cache[:marcato].has_key?(index)
|
141
|
+
cached = node_cache[:marcato][index]
|
142
142
|
if cached
|
143
|
-
node_cache[:
|
143
|
+
node_cache[:marcato][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
|
144
144
|
@index = cached.interval.end
|
145
145
|
end
|
146
146
|
return cached
|
147
147
|
end
|
148
148
|
|
149
|
-
if (match_len = has_terminal?("
|
149
|
+
if (match_len = has_terminal?("^", false, index))
|
150
150
|
r0 = instantiate_node(SyntaxNode,input, index...(index + match_len))
|
151
|
-
r0.extend(
|
151
|
+
r0.extend(Marcato0)
|
152
152
|
@index += match_len
|
153
153
|
else
|
154
|
-
terminal_parse_failure("
|
154
|
+
terminal_parse_failure('"^"')
|
155
155
|
r0 = nil
|
156
156
|
end
|
157
157
|
|
158
|
-
node_cache[:
|
158
|
+
node_cache[:marcato][start_index] = r0
|
159
159
|
|
160
160
|
r0
|
161
161
|
end
|
@@ -177,12 +177,12 @@ module Articulation
|
|
177
177
|
return cached
|
178
178
|
end
|
179
179
|
|
180
|
-
if (match_len = has_terminal?("
|
180
|
+
if (match_len = has_terminal?("_", false, index))
|
181
181
|
r0 = instantiate_node(SyntaxNode,input, index...(index + match_len))
|
182
182
|
r0.extend(Portato0)
|
183
183
|
@index += match_len
|
184
184
|
else
|
185
|
-
terminal_parse_failure("
|
185
|
+
terminal_parse_failure('"_"')
|
186
186
|
r0 = nil
|
187
187
|
end
|
188
188
|
|
@@ -213,7 +213,7 @@ module Articulation
|
|
213
213
|
r0.extend(Staccato0)
|
214
214
|
@index += match_len
|
215
215
|
else
|
216
|
-
terminal_parse_failure(".")
|
216
|
+
terminal_parse_failure('"."')
|
217
217
|
r0 = nil
|
218
218
|
end
|
219
219
|
|
@@ -239,12 +239,12 @@ module Articulation
|
|
239
239
|
return cached
|
240
240
|
end
|
241
241
|
|
242
|
-
if (match_len = has_terminal?("
|
242
|
+
if (match_len = has_terminal?("!", false, index))
|
243
243
|
r0 = instantiate_node(SyntaxNode,input, index...(index + match_len))
|
244
244
|
r0.extend(Staccatissimo0)
|
245
245
|
@index += match_len
|
246
246
|
else
|
247
|
-
terminal_parse_failure("'
|
247
|
+
terminal_parse_failure('"!"')
|
248
248
|
r0 = nil
|
249
249
|
end
|
250
250
|
|