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
@@ -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
|
|