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
@@ -6,14 +6,22 @@ grammar Note
|
|
6
6
|
include Articulation
|
7
7
|
include Link
|
8
8
|
include Duration
|
9
|
+
include Mark
|
9
10
|
|
10
11
|
rule note
|
12
|
+
begin_marks:(
|
13
|
+
(first:begin_triplet second:begin_slur?) /
|
14
|
+
(first:begin_slur second:begin_triplet?)
|
15
|
+
)?
|
11
16
|
duration
|
12
17
|
more:(
|
13
18
|
first_pl:pitch_link
|
14
19
|
more_pl:("," pl:pitch_link)*
|
15
20
|
art:articulation?
|
16
|
-
|
21
|
+
)?
|
22
|
+
end_marks:(
|
23
|
+
(first:end_triplet second:end_slur?) /
|
24
|
+
(first:end_slur second:end_triplet?)
|
17
25
|
)?
|
18
26
|
<NoteNode>
|
19
27
|
end
|
@@ -21,10 +29,6 @@ grammar Note
|
|
21
29
|
rule pitch_link
|
22
30
|
pitch the_link:link?
|
23
31
|
end
|
24
|
-
|
25
|
-
rule accent
|
26
|
-
"!"
|
27
|
-
end
|
28
32
|
end
|
29
33
|
|
30
34
|
end
|
@@ -45,8 +45,13 @@ module NonnegativeInteger
|
|
45
45
|
break
|
46
46
|
end
|
47
47
|
end
|
48
|
-
|
49
|
-
|
48
|
+
if s0.empty?
|
49
|
+
@index = i0
|
50
|
+
r0 = nil
|
51
|
+
else
|
52
|
+
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
|
53
|
+
r0.extend(NonnegativeInteger0)
|
54
|
+
end
|
50
55
|
|
51
56
|
node_cache[:nonnegative_integer][start_index] = r0
|
52
57
|
|
@@ -14,9 +14,6 @@ module PositiveInteger
|
|
14
14
|
include NonnegativeInteger
|
15
15
|
|
16
16
|
module PositiveInteger0
|
17
|
-
def nonnegative_integer
|
18
|
-
elements[2]
|
19
|
-
end
|
20
17
|
end
|
21
18
|
|
22
19
|
module PositiveInteger1
|
@@ -66,7 +63,12 @@ module PositiveInteger
|
|
66
63
|
end
|
67
64
|
s0 << r3
|
68
65
|
if r3
|
69
|
-
|
66
|
+
r5 = _nt_nonnegative_integer
|
67
|
+
if r5
|
68
|
+
r4 = r5
|
69
|
+
else
|
70
|
+
r4 = instantiate_node(SyntaxNode,input, index...index)
|
71
|
+
end
|
70
72
|
s0 << r4
|
71
73
|
end
|
72
74
|
end
|
@@ -13,15 +13,41 @@ class Function
|
|
13
13
|
return perc * (end_domain.last - end_domain.first) + end_domain.first
|
14
14
|
end
|
15
15
|
|
16
|
+
attr_reader :domain
|
17
|
+
def initialize domain = (DOMAIN_MIN...DOMAIN_MAX), memoize: true, &at_block
|
18
|
+
raise ArgumentError unless domain.last > domain.first
|
19
|
+
@domain = domain
|
20
|
+
raise ArgumentError unless block_given?
|
21
|
+
raise ArgumentError unless at_block.arity == 1
|
22
|
+
@at_block = at_block
|
23
|
+
|
24
|
+
@memoize = memoize
|
25
|
+
@memoized = {}
|
26
|
+
end
|
27
|
+
|
28
|
+
def at(x)
|
29
|
+
raise DomainError unless @domain.include?(x)
|
30
|
+
if @memoize
|
31
|
+
if @memoized.has_key? x
|
32
|
+
@memoized[x]
|
33
|
+
else
|
34
|
+
@memoized[x] = @at_block.call(x)
|
35
|
+
end
|
36
|
+
else
|
37
|
+
@at_block.call(x)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def ==(other)
|
42
|
+
@domain == other.domain
|
43
|
+
end
|
44
|
+
|
16
45
|
class Constant < Function
|
17
46
|
attr_reader :value
|
18
47
|
|
19
48
|
def initialize value
|
20
49
|
@value = value
|
21
|
-
|
22
|
-
|
23
|
-
def at(x)
|
24
|
-
@value
|
50
|
+
super() {|x| @value }
|
25
51
|
end
|
26
52
|
|
27
53
|
def ==(other)
|
@@ -35,10 +61,8 @@ class Function
|
|
35
61
|
def initialize p1,p2
|
36
62
|
@slope = (p2[1] - p1[1])/(p2[0] - p1[0]).to_f
|
37
63
|
@intercept = p1[1] - @slope * p1[0]
|
38
|
-
|
39
|
-
|
40
|
-
def at(x)
|
41
|
-
x * @slope + @intercept
|
64
|
+
|
65
|
+
super() {|x| x * @slope + @intercept }
|
42
66
|
end
|
43
67
|
|
44
68
|
def ==(other)
|
@@ -59,24 +83,23 @@ class Function
|
|
59
83
|
SIGM_RANGE = Sigmoid.sigm(SIGM_DOMAIN.first)..Sigmoid.sigm(SIGM_DOMAIN.last)
|
60
84
|
SIGM_SPAN = SIGM_RANGE.last - SIGM_RANGE.first
|
61
85
|
|
62
|
-
attr_reader :y0, :dy
|
86
|
+
attr_reader :y0, :dy
|
63
87
|
def initialize p0, p1
|
64
88
|
@y0, y1 = p0[1], p1[1]
|
65
89
|
@dy = y1 - @y0
|
66
|
-
@
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
return y
|
90
|
+
@external_domain = p0[0]..p1[0]
|
91
|
+
|
92
|
+
super() do |x|
|
93
|
+
x_ = Function.transform_domains(@external_domain, SIGM_DOMAIN, x)
|
94
|
+
y_ = (Sigmoid.sigm(x_) - SIGM_RANGE.first) / SIGM_SPAN
|
95
|
+
@y0 + y_ * @dy
|
96
|
+
end
|
74
97
|
end
|
75
98
|
|
76
99
|
#def from(y)
|
77
100
|
# y2 = (y - @y0) / @dy
|
78
101
|
# x2 = Sigmoid.inv_sigm(y2 * SIGM_SPAN + SIGM_RANGE.first)
|
79
|
-
# x = Function.transform_domains(SIGM_DOMAIN, @
|
102
|
+
# x = Function.transform_domains(SIGM_DOMAIN, @external_domain, x2)
|
80
103
|
# return x
|
81
104
|
#end
|
82
105
|
|
@@ -0,0 +1,156 @@
|
|
1
|
+
# Requires that an including class can be instantiated entirely by keyword
|
2
|
+
# args that map these symbols to values
|
3
|
+
module Packable
|
4
|
+
PACKED_CLASS_KEY = :packed_class
|
5
|
+
|
6
|
+
def self.included(base)
|
7
|
+
base.extend(ClassMethods)
|
8
|
+
base.class_variable_set(:@@special_packing, {})
|
9
|
+
base.class_variable_set(:@@special_unpacking, {})
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
def unpack packing
|
14
|
+
args = []
|
15
|
+
kwargs = {}
|
16
|
+
|
17
|
+
init_params.each do |name,type|
|
18
|
+
if (type == :req || type == :keyreq) && !packing.has_key?(name)
|
19
|
+
raise ArgumentError, "Packing does not have required key #{name}"
|
20
|
+
end
|
21
|
+
|
22
|
+
val = packing[name]
|
23
|
+
if type == :keyrest
|
24
|
+
raise "Expected this to be a Hash" unless val.is_a? Hash
|
25
|
+
end
|
26
|
+
|
27
|
+
val2 = if unpacks_specially?(name)
|
28
|
+
unpack_specially(name,val)
|
29
|
+
else
|
30
|
+
Packable.unpack_val(val)
|
31
|
+
end
|
32
|
+
|
33
|
+
case type
|
34
|
+
when :req, :opt
|
35
|
+
args.push val2
|
36
|
+
when :key, :keyreq
|
37
|
+
kwargs[name] = val2
|
38
|
+
when :keyrest
|
39
|
+
kwargs.merge!(val2)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
if args.any?
|
44
|
+
if kwargs.any?
|
45
|
+
obj = new(*args,**kwargs)
|
46
|
+
else
|
47
|
+
obj = new(*args)
|
48
|
+
end
|
49
|
+
elsif kwargs.any?
|
50
|
+
obj = new(**kwargs)
|
51
|
+
else
|
52
|
+
obj = new
|
53
|
+
end
|
54
|
+
|
55
|
+
block_given? ? yield(obj) : obj
|
56
|
+
end
|
57
|
+
|
58
|
+
def init_params
|
59
|
+
params = instance_method(:initialize).parameters
|
60
|
+
Hash[ params.map {|pair| pair.reverse } ]
|
61
|
+
end
|
62
|
+
|
63
|
+
def special_packing sym, &block
|
64
|
+
class_variable_get(:@@special_packing)[sym] = block
|
65
|
+
end
|
66
|
+
|
67
|
+
def special_unpacking sym, &block
|
68
|
+
class_variable_get(:@@special_unpacking)[sym] = block
|
69
|
+
end
|
70
|
+
|
71
|
+
def packs_specially? sym
|
72
|
+
class_variable_get(:@@special_packing).has_key?(sym)
|
73
|
+
end
|
74
|
+
|
75
|
+
def unpacks_specially? sym
|
76
|
+
class_variable_get(:@@special_unpacking).has_key?(sym)
|
77
|
+
end
|
78
|
+
|
79
|
+
def pack_specially sym, val
|
80
|
+
class_variable_get(:@@special_packing)[sym].call(val)
|
81
|
+
end
|
82
|
+
|
83
|
+
def unpack_specially sym, val
|
84
|
+
class_variable_get(:@@special_unpacking)[sym].call(val)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def init_params
|
89
|
+
self.class.init_params
|
90
|
+
end
|
91
|
+
|
92
|
+
def class_str
|
93
|
+
self.class.to_s
|
94
|
+
end
|
95
|
+
|
96
|
+
def pack
|
97
|
+
packing = { PACKED_CLASS_KEY => class_str }
|
98
|
+
init_params.keys.each do |name|
|
99
|
+
val = self.send(name)
|
100
|
+
val2 = if self.class.packs_specially?(name)
|
101
|
+
self.class.pack_specially(name,val)
|
102
|
+
else
|
103
|
+
Packable.pack_val(val)
|
104
|
+
end
|
105
|
+
packing[name] = val2
|
106
|
+
end
|
107
|
+
packing
|
108
|
+
end
|
109
|
+
|
110
|
+
def recover_class klass_str
|
111
|
+
Kernel.const_get(klass_str)
|
112
|
+
end
|
113
|
+
module_function :recover_class
|
114
|
+
|
115
|
+
private
|
116
|
+
|
117
|
+
def unpack_val val
|
118
|
+
if val.is_a? Array
|
119
|
+
val.map {|v| Packable.unpack_val v}
|
120
|
+
elsif val.is_a? Hash
|
121
|
+
if val.packed_class?
|
122
|
+
val.unpack
|
123
|
+
else
|
124
|
+
Hash[ val.map {|k,v| [ Packable.unpack_val(k), Packable.unpack_val(v) ]} ]
|
125
|
+
end
|
126
|
+
else
|
127
|
+
val
|
128
|
+
end
|
129
|
+
end
|
130
|
+
module_function :unpack_val
|
131
|
+
|
132
|
+
def pack_val val
|
133
|
+
if val.is_a?(Packable)
|
134
|
+
val.pack
|
135
|
+
elsif val.is_a? Array
|
136
|
+
val.map {|v| pack_val v}
|
137
|
+
elsif val.is_a? Hash
|
138
|
+
Hash[ val.map {|k,v| [ Packable.pack_val(k), Packable.pack_val(v) ]} ]
|
139
|
+
else
|
140
|
+
val
|
141
|
+
end
|
142
|
+
end
|
143
|
+
module_function :pack_val
|
144
|
+
end
|
145
|
+
|
146
|
+
class Hash
|
147
|
+
def packed_class?
|
148
|
+
has_key?(Packable::PACKED_CLASS_KEY) &&
|
149
|
+
Packable.recover_class(fetch(Packable::PACKED_CLASS_KEY)).included_modules.include?(Packable)
|
150
|
+
end
|
151
|
+
|
152
|
+
def unpack
|
153
|
+
raise "Not a packed class" unless packed_class?
|
154
|
+
Packable.recover_class(fetch(Packable::PACKED_CLASS_KEY)).unpack self
|
155
|
+
end
|
156
|
+
end
|
@@ -22,11 +22,11 @@ class GlissandoConverter
|
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
|
-
def self.glissando_elements(start_pitch, target_pitch, duration,
|
25
|
+
def self.glissando_elements(start_pitch, target_pitch, duration, attack)
|
26
26
|
pitches = glissando_pitches(start_pitch, target_pitch)
|
27
27
|
subdur = Rational(duration, pitches.size)
|
28
28
|
pitches.map do |pitch|
|
29
|
-
|
29
|
+
NoteSequence::Element.new(subdur, pitch, attack)
|
30
30
|
end
|
31
31
|
end
|
32
32
|
end
|
@@ -1,97 +1,250 @@
|
|
1
1
|
module Musicality
|
2
2
|
|
3
3
|
class NoteSequenceExtractor
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
4
|
+
attr_reader :notes
|
5
|
+
def initialize notes
|
6
|
+
prepare_notes(notes)
|
7
|
+
mark_slurring
|
8
|
+
remove_bad_links
|
9
|
+
calculate_offsets
|
10
|
+
establish_maps
|
11
|
+
# now, ready to extract sequences!
|
12
|
+
end
|
13
|
+
|
14
|
+
def extract_sequences cents_per_step = 10
|
15
|
+
return [] if @notes.empty?
|
16
|
+
|
17
|
+
next_seqs = seqs_from_note(@notes.size-1, cents_per_step)
|
18
|
+
return next_seqs if @notes.one?
|
19
|
+
|
20
|
+
complete_seqs = []
|
21
|
+
(@notes.size-2).downto(0) do |i|
|
22
|
+
cur_seqs = seqs_from_note(i, cents_per_step)
|
23
|
+
map = @maps[i]
|
24
|
+
|
25
|
+
next_seqs.each do |next_seq|
|
26
|
+
p1 = nil
|
27
|
+
p2 = next_seq.elements.first.pitch
|
28
|
+
if p1 = map[:ties].key(p2)
|
29
|
+
cur_seq = cur_seqs.find {|x| x.elements.first.pitch == p1 }
|
30
|
+
NoteSequenceExtractor.tie_seqs(cur_seq, next_seq)
|
31
|
+
elsif p1 = map[:slurs].key(p2)
|
32
|
+
cur_seq = cur_seqs.find {|x| x.elements.first.pitch == p1 }
|
33
|
+
NoteSequenceExtractor.slur_seqs(cur_seq, next_seq)
|
34
|
+
elsif p1 = map[:full_glissandos].key(p2)
|
35
|
+
cur_seq = cur_seqs.find {|x| x.elements.first.pitch == p1 }
|
36
|
+
NoteSequenceExtractor.glissando_seqs(cur_seq, next_seq)
|
37
|
+
elsif p1 = map[:full_portamentos].key(p2)
|
38
|
+
cur_seq = cur_seqs.find {|x| x.elements.first.pitch == p1 }
|
39
|
+
NoteSequenceExtractor.portamento_seqs(cur_seq, next_seq, cents_per_step)
|
19
40
|
else
|
20
|
-
|
21
|
-
end
|
22
|
-
elsif (link.is_a?(Link::Slur) ||
|
23
|
-
link.is_a?(Link::Legato))
|
24
|
-
unless next_note.pitches.include? link.target_pitch
|
25
|
-
note.links.delete pitch
|
41
|
+
complete_seqs.push next_seq
|
26
42
|
end
|
43
|
+
end
|
44
|
+
next_seqs = cur_seqs
|
45
|
+
end
|
46
|
+
complete_seqs += next_seqs
|
47
|
+
return complete_seqs
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def self.tie_seqs(cur_seq, next_seq)
|
53
|
+
cur_seq.elements.last.duration += next_seq.elements.first.duration
|
54
|
+
cur_seq.elements += next_seq.elements[1..-1]
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.slur_seqs(cur_seq, next_seq)
|
58
|
+
if next_seq.elements.first.attack == Attack::NORMAL
|
59
|
+
next_seq.elements.first.attack = Attack::NONE
|
60
|
+
end
|
61
|
+
cur_seq.separation = next_seq.separation
|
62
|
+
cur_seq.elements += next_seq.elements
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.glissando_seqs(cur_seq, next_seq)
|
66
|
+
cur_seq.elements = GlissandoConverter.glissando_elements(cur_seq.last_pitch,
|
67
|
+
next_seq.first_pitch, cur_seq.full_duration, cur_seq.last_attack)
|
68
|
+
cur_seq.separation = next_seq.separation
|
69
|
+
cur_seq.elements += next_seq.elements
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.portamento_seqs(cur_seq, next_seq, cents_per_step)
|
73
|
+
cur_seq.elements = PortamentoConverter.portamento_elements(cur_seq.last_pitch,
|
74
|
+
next_seq.first_pitch, cents_per_step, cur_seq.full_duration, cur_seq.last_attack)
|
75
|
+
cur_seq.separation = Separation::NONE
|
76
|
+
cur_seq.elements += next_seq.elements
|
77
|
+
end
|
78
|
+
|
79
|
+
def seqs_from_note(idx, cents_per_step)
|
80
|
+
map = @maps[idx]
|
81
|
+
note = @notes[idx]
|
82
|
+
attack = NoteSequenceExtractor.note_attack(note.articulation)
|
83
|
+
separation = NoteSequenceExtractor.note_separation(note.articulation, @slurring_flags[idx])
|
84
|
+
offset = @offsets[idx]
|
85
|
+
note.pitches.map do |p|
|
86
|
+
if map[:half_glissandos].has_key?(p)
|
87
|
+
NoteSequence.new(offset, separation,
|
88
|
+
GlissandoConverter.glissando_elements(
|
89
|
+
p, map[:half_glissandos][p], note.duration, attack))
|
90
|
+
elsif map[:half_portamentos].has_key?(p)
|
91
|
+
NoteSequence.new(offset, Separation::NONE,
|
92
|
+
PortamentoConverter.portamento_elements(
|
93
|
+
p, map[:half_portamentos][p], cents_per_step, note.duration, attack))
|
94
|
+
else
|
95
|
+
NoteSequence.new(offset, separation,
|
96
|
+
[NoteSequence::Element.new(note.duration, p, attack)])
|
27
97
|
end
|
28
98
|
end
|
29
99
|
end
|
30
100
|
|
31
|
-
def self.
|
32
|
-
case
|
33
|
-
when Articulations::
|
34
|
-
|
35
|
-
|
36
|
-
when Articulations::
|
37
|
-
|
38
|
-
|
101
|
+
def self.note_attack articulation
|
102
|
+
case articulation
|
103
|
+
when Articulations::NORMAL then Attack::NORMAL
|
104
|
+
when Articulations::TENUTO then Attack::TENUTO
|
105
|
+
when Articulations::ACCENT then Attack::ACCENT
|
106
|
+
when Articulations::MARCATO then Attack::ACCENT
|
107
|
+
when Articulations::PORTATO then Attack::NORMAL
|
108
|
+
when Articulations::STACCATO then Attack::NORMAL
|
109
|
+
when Articulations::STACCATISSIMO then Attack::NORMAL
|
39
110
|
end
|
40
111
|
end
|
41
112
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
113
|
+
def self.note_separation articulation, under_slur
|
114
|
+
if under_slur
|
115
|
+
case articulation
|
116
|
+
when Articulations::NORMAL then Separation::NONE
|
117
|
+
when Articulations::TENUTO then Separation::NONE
|
118
|
+
when Articulations::ACCENT then Separation::NONE
|
119
|
+
when Articulations::MARCATO then Separation::PORTATO
|
120
|
+
when Articulations::PORTATO then Separation::NORMAL
|
121
|
+
when Articulations::STACCATO then Separation::PORTATO
|
122
|
+
when Articulations::STACCATISSIMO then Separation::STACCATO
|
123
|
+
end
|
124
|
+
else
|
125
|
+
case articulation
|
126
|
+
when Articulations::NORMAL then Separation::NORMAL
|
127
|
+
when Articulations::TENUTO then Separation::TENUTO
|
128
|
+
when Articulations::ACCENT then Separation::NORMAL
|
129
|
+
when Articulations::MARCATO then Separation::NORMAL
|
130
|
+
when Articulations::PORTATO then Separation::PORTATO
|
131
|
+
when Articulations::STACCATO then Separation::STACCATO
|
132
|
+
when Articulations::STACCATISSIMO then Separation::STACCATISSIMO
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def prepare_notes notes
|
138
|
+
in_triplet = false
|
139
|
+
@notes = Array.new(notes.size) do |i|
|
140
|
+
note = notes[i]
|
141
|
+
|
142
|
+
if note.begins_triplet?
|
143
|
+
in_triplet = true
|
144
|
+
end
|
145
|
+
|
146
|
+
new_note = in_triplet ? note.resize(note.duration * Rational(2,3)) : note.clone
|
147
|
+
|
148
|
+
if note.ends_triplet?
|
149
|
+
in_triplet = false
|
150
|
+
end
|
46
151
|
|
47
|
-
|
48
|
-
(@notes.size-1).times do |i|
|
49
|
-
NoteSequenceExtractor.fixup_links(@notes[i], @notes[i+1])
|
50
|
-
NoteSequenceExtractor.replace_articulation(@notes[i], @notes[i+1])
|
152
|
+
new_note
|
51
153
|
end
|
52
|
-
@notes.pop
|
53
154
|
end
|
54
155
|
|
55
|
-
def
|
56
|
-
|
57
|
-
|
156
|
+
def mark_slurring
|
157
|
+
@slurring_flags = []
|
158
|
+
under_slur = false
|
159
|
+
|
160
|
+
@slurring_flags = Array.new(@notes.size) do |i|
|
161
|
+
note = @notes[i]
|
58
162
|
|
59
|
-
|
60
|
-
|
61
|
-
|
163
|
+
if note.begins_slur? && !note.ends_slur?
|
164
|
+
under_slur = true
|
165
|
+
end
|
166
|
+
flag = under_slur
|
62
167
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
link = note.links[pitch]
|
168
|
+
if note.ends_slur? && !note.begins_slur?
|
169
|
+
under_slur = false
|
170
|
+
end
|
171
|
+
flag
|
172
|
+
end
|
173
|
+
end
|
70
174
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
175
|
+
def remove_bad_links
|
176
|
+
@notes.each_with_index do |n,i|
|
177
|
+
# create a dummy note (with no pitches) for checking links from the last note
|
178
|
+
n2 = (i == (@notes.size-1)) ? Note.quarter : @notes[i+1]
|
179
|
+
n.links.delete_if do |p,l|
|
180
|
+
!n.pitches.include?(p) || (l.is_a?(Link::Tie) && !n2.pitches.include?(p))
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def calculate_offsets
|
186
|
+
offset = 0.to_r
|
187
|
+
@offsets = Array.new(@notes.size) do |i|
|
188
|
+
cur_offset = offset
|
189
|
+
offset += @notes[i].duration
|
190
|
+
cur_offset
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def self.no_separation?(articulation, under_slur)
|
195
|
+
under_slur && (
|
196
|
+
articulation == Articulations::NORMAL ||
|
197
|
+
articulation == Articulations::TENUTO ||
|
198
|
+
articulation == Articulations::ACCENT
|
199
|
+
)
|
200
|
+
end
|
201
|
+
|
202
|
+
def establish_maps
|
203
|
+
@maps = []
|
204
|
+
|
205
|
+
@notes.each_index do |i|
|
206
|
+
map = { :ties => {}, :slurs => {}, :full_glissandos => {},
|
207
|
+
:full_portamentos => {}, :half_glissandos => {},
|
208
|
+
:half_portamentos => {}}
|
209
|
+
note = @notes[i]
|
210
|
+
|
211
|
+
# Create a dummy note (with no pitches) for the last note to "link" to.
|
212
|
+
# This will allow half glissandos and half portamentos from the last note
|
213
|
+
next_note = (i == (@notes.size-1)) ? Note.quarter : @notes[i+1]
|
214
|
+
|
215
|
+
no_separation = NoteSequenceExtractor.no_separation?(note.articulation, @slurring_flags[i])
|
216
|
+
|
217
|
+
linked = note.pitches & note.links.keys
|
218
|
+
linked.each do |p|
|
219
|
+
l = note.links[p]
|
220
|
+
if l.is_a?(Link::Tie)
|
221
|
+
map[:ties][p] = p
|
222
|
+
elsif l.is_a?(Link::Glissando)
|
223
|
+
if next_note.pitches.include?(l.target_pitch)
|
224
|
+
map[:full_glissandos][p] = l.target_pitch
|
80
225
|
else
|
81
|
-
|
82
|
-
|
226
|
+
map[:half_glissandos][p] = l.target_pitch
|
227
|
+
end
|
228
|
+
elsif l.is_a?(Link::Portamento)
|
229
|
+
if next_note.pitches.include?(l.target_pitch)
|
230
|
+
map[:full_portamentos][p] = l.target_pitch
|
231
|
+
else
|
232
|
+
map[:half_portamentos][p] = l.target_pitch
|
83
233
|
end
|
84
|
-
|
85
|
-
j += 1
|
86
|
-
break if j >= @notes.size || !@notes[j].pitches.include?(link.target_pitch)
|
87
234
|
end
|
88
|
-
|
89
|
-
sequences.push(NoteSequence.from_elements(offset,elements))
|
90
235
|
end
|
91
|
-
|
236
|
+
|
237
|
+
if(no_separation)
|
238
|
+
unlinked = note.pitches - linked
|
239
|
+
target_pitches = note.links.map {|p,l| l.is_a?(Link::Tie) ? p : l.target_pitch }
|
240
|
+
untargeted = next_note.pitches - target_pitches
|
241
|
+
Optimization.linking(unlinked, untargeted).each do |pitch,tgt_pitch|
|
242
|
+
map[:slurs][pitch] = tgt_pitch
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
@maps.push map
|
92
247
|
end
|
93
|
-
|
94
|
-
return sequences
|
95
248
|
end
|
96
249
|
end
|
97
250
|
|