musicality 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
|