musicality 0.9.0 → 0.10.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 +7 -1
- data/lib/musicality.rb +0 -1
- data/lib/musicality/composition/dsl/score_methods.rb +5 -6
- data/lib/musicality/notation/conversion/score_conversion.rb +3 -34
- data/lib/musicality/notation/conversion/score_converter.rb +6 -17
- data/lib/musicality/notation/model/mark.rb +0 -12
- data/lib/musicality/notation/model/marks.rb +0 -3
- data/lib/musicality/notation/model/note.rb +0 -8
- data/lib/musicality/notation/model/score.rb +14 -35
- data/lib/musicality/notation/model/symbols.rb +0 -2
- data/lib/musicality/notation/parsing/mark_parsing.rb +0 -58
- data/lib/musicality/notation/parsing/mark_parsing.treetop +0 -12
- data/lib/musicality/notation/parsing/note_node.rb +22 -27
- data/lib/musicality/notation/parsing/note_parsing.rb +59 -189
- data/lib/musicality/notation/parsing/note_parsing.treetop +2 -9
- data/lib/musicality/performance/conversion/note_sequence_extractor.rb +1 -20
- data/lib/musicality/performance/model/note_sequence.rb +1 -1
- data/lib/musicality/printing/lilypond/note_engraving.rb +3 -3
- data/lib/musicality/printing/lilypond/part_engraver.rb +16 -12
- data/lib/musicality/project/create_tasks.rb +1 -1
- data/lib/musicality/project/load_config.rb +16 -1
- data/lib/musicality/project/project.rb +2 -2
- data/lib/musicality/version.rb +1 -1
- data/musicality.gemspec +0 -1
- data/spec/composition/dsl/score_methods_spec.rb +73 -0
- data/spec/notation/conversion/score_conversion_spec.rb +0 -100
- data/spec/notation/conversion/score_converter_spec.rb +33 -33
- data/spec/notation/model/note_spec.rb +2 -2
- data/spec/notation/model/score_spec.rb +17 -87
- data/spec/notation/parsing/note_node_spec.rb +2 -2
- data/spec/notation/parsing/note_parsing_spec.rb +3 -3
- data/spec/performance/conversion/note_sequence_extractor_spec.rb +23 -0
- data/spec/performance/model/note_sequence_spec.rb +50 -6
- metadata +4 -19
- data/lib/musicality/notation/conversion/measure_note_map.rb +0 -40
- data/spec/notation/conversion/measure_note_map_spec.rb +0 -73
@@ -2,9 +2,10 @@ module Musicality
|
|
2
2
|
|
3
3
|
class Project
|
4
4
|
CONFIG_FILE_NAME = "config.yml"
|
5
|
+
BASE_SCORES_DIR = "scores"
|
5
6
|
|
6
7
|
DEFAULT_CONFIG = {
|
7
|
-
:
|
8
|
+
:scores => File.join(BASE_SCORES_DIR, "**", "*.score"),
|
8
9
|
:tempo_sample_rate => 200,
|
9
10
|
:audio_sample_rate => 44100,
|
10
11
|
:audio_sample_format => "int16"
|
@@ -35,6 +36,20 @@ class Project
|
|
35
36
|
else
|
36
37
|
DEFAULT_CONFIG
|
37
38
|
end
|
39
|
+
|
40
|
+
# overrides from ENV
|
41
|
+
config.keys.each do |k|
|
42
|
+
k_str = k.to_s
|
43
|
+
if ENV.has_key? k_str
|
44
|
+
case k
|
45
|
+
when :tempo_sample_rate, :audio_sample_rate
|
46
|
+
config[k] = ENV[k_str].to_i
|
47
|
+
else
|
48
|
+
config[k] = ENV[k_str]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
38
53
|
check_config config
|
39
54
|
return config
|
40
55
|
end
|
@@ -24,7 +24,7 @@ class Project
|
|
24
24
|
unless Dir.exists?(dest_dir)
|
25
25
|
raise "directory #{dest_dir} could not be created"
|
26
26
|
end
|
27
|
-
end
|
27
|
+
end
|
28
28
|
end
|
29
29
|
|
30
30
|
def create_gemfile
|
@@ -56,7 +56,7 @@ class Project
|
|
56
56
|
end
|
57
57
|
|
58
58
|
def create_scores_dir
|
59
|
-
scores_dir = File.join(dest_dir, Project::
|
59
|
+
scores_dir = File.join(dest_dir, Project::BASE_SCORES_DIR)
|
60
60
|
Dir.mkdir(scores_dir)
|
61
61
|
end
|
62
62
|
end
|
data/lib/musicality/version.rb
CHANGED
data/musicality.gemspec
CHANGED
@@ -0,0 +1,73 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
|
+
|
3
|
+
describe Score do
|
4
|
+
describe '#notes' do
|
5
|
+
before :all do
|
6
|
+
@equal_duration_note_sets = [
|
7
|
+
[ Note.quarter, Note.half ],
|
8
|
+
[ Note.eighth, Note.half, Note.eighth ],
|
9
|
+
"/2 /12 /12 /12".to_notes,
|
10
|
+
]
|
11
|
+
end
|
12
|
+
|
13
|
+
context 'given two parts' do
|
14
|
+
before :all do
|
15
|
+
@notes_a = "/4C2 /4Bb2 /2D4".to_notes
|
16
|
+
@notes_b = "/8 /8F3 3/4D2".to_notes
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'neither part in score already' do
|
20
|
+
it 'should create both parts with given notes and add to score' do
|
21
|
+
s = Score::Timed.new
|
22
|
+
s.notes("A" => @notes_a, "B" => @notes_b)
|
23
|
+
expect(s.parts).to include("A")
|
24
|
+
expect(s.parts).to include("B")
|
25
|
+
expect(s.parts["A"].notes).to eq(@notes_a)
|
26
|
+
expect(s.parts["B"].notes).to eq(@notes_b)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'both parts in score already' do
|
31
|
+
it 'should add onto part notes' do
|
32
|
+
s = Score::Timed.new(parts: {
|
33
|
+
"A" => Part.new(Dynamics::MP),
|
34
|
+
"B" => Part.new(Dynamics::MF)
|
35
|
+
})
|
36
|
+
s.notes("A" => @notes_a, "B" => @notes_b)
|
37
|
+
expect(s.parts["A"].notes).to eq(@notes_a)
|
38
|
+
expect(s.parts["B"].notes).to eq(@notes_b)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'one part in score already' do
|
43
|
+
it 'should create the other part with an initial rest and add to score' do
|
44
|
+
first_note_a = Note.whole(C2)
|
45
|
+
s = Score::Timed.new(parts: {
|
46
|
+
"A" => Part.new(Dynamics::MP, notes: [ first_note_a ]),
|
47
|
+
})
|
48
|
+
s.notes("A" => @notes_a, "B" => @notes_b)
|
49
|
+
expect(s.parts["A"].notes).to eq([first_note_a] + @notes_a)
|
50
|
+
expect(s.parts["B"].notes).to eq([Note.whole] + @notes_b)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context 'given equal duration note sets' do
|
55
|
+
it 'should not raise DurationMismatchError' do
|
56
|
+
@equal_duration_note_sets.combination(2).each do |note_sets|
|
57
|
+
s = Score::Timed.new
|
58
|
+
expect { s.notes("A" => note_sets[0], "B" => note_sets[1]) }.to_not raise_error
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context 'given different duration note sets' do
|
64
|
+
it 'should raise DurationMismatchError' do
|
65
|
+
note_set_a = "/4 /4 /8".to_notes
|
66
|
+
note_set_b = "/4 /4 /8 /16".to_notes
|
67
|
+
s = Score::Timed.new
|
68
|
+
expect { s.notes("A" => note_set_a, "B" => note_set_b) }.to raise_error(DurationMismatchError)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -31,106 +31,6 @@ describe Score::Tempo do
|
|
31
31
|
)
|
32
32
|
end
|
33
33
|
|
34
|
-
describe '#measure_offsets' do
|
35
|
-
before(:all){ @moffs = @score.measure_offsets }
|
36
|
-
|
37
|
-
it 'should return an already-sorted array' do
|
38
|
-
@moffs.should eq @moffs.sort
|
39
|
-
end
|
40
|
-
|
41
|
-
it 'should start with offset from start tempo/meter/dynamic' do
|
42
|
-
@moffs[0].should eq(0)
|
43
|
-
end
|
44
|
-
|
45
|
-
it 'should include offsets from tempo changes' do
|
46
|
-
@score.tempo_changes.each do |moff,change|
|
47
|
-
@moffs.should include(moff)
|
48
|
-
@moffs.should include(moff + change.duration)
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
it 'should include offsets from meter changes' do
|
53
|
-
@score.meter_changes.keys.each {|moff| @moffs.should include(moff) }
|
54
|
-
end
|
55
|
-
|
56
|
-
it "should include offsets from each part's dynamic changes" do
|
57
|
-
@score.parts.values.each do |part|
|
58
|
-
part.dynamic_changes.each do |moff,change|
|
59
|
-
@moffs.should include(moff)
|
60
|
-
change.offsets(moff).each {|offset| @moffs.should include(offset) }
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
it 'should include offsets from program segments' do
|
66
|
-
@score.program.each do |seg|
|
67
|
-
@moffs.should include(seg.first)
|
68
|
-
@moffs.should include(seg.last)
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
describe '#measure_durations' do
|
74
|
-
before(:all){ @mdurs = @score.measure_durations }
|
75
|
-
|
76
|
-
it 'should return a Hash' do
|
77
|
-
@mdurs.should be_a Hash
|
78
|
-
end
|
79
|
-
|
80
|
-
context 'no meter change at offset 0' do
|
81
|
-
it 'should have size of meter_changes.size + 1' do
|
82
|
-
@mdurs.size.should eq(@score.meter_changes.size + 1)
|
83
|
-
end
|
84
|
-
|
85
|
-
it 'should begin with offset 0' do
|
86
|
-
@mdurs.keys.min.should eq(0)
|
87
|
-
end
|
88
|
-
|
89
|
-
it 'should map start meter to offset 0' do
|
90
|
-
@mdurs[0.to_r].should eq(@score.start_meter.measure_duration)
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
context 'meter change at offset 0' do
|
95
|
-
before :all do
|
96
|
-
@change = Change::Immediate.new(THREE_FOUR)
|
97
|
-
@score2 = Score::Tempo.new(FOUR_FOUR, 120, meter_changes: { 0 => @change })
|
98
|
-
@mdurs2 = @score2.measure_durations
|
99
|
-
end
|
100
|
-
|
101
|
-
it 'should have same size as meter changes' do
|
102
|
-
@mdurs2.size.should eq(@score2.meter_changes.size)
|
103
|
-
end
|
104
|
-
|
105
|
-
it 'should begin with offset 0' do
|
106
|
-
@mdurs2.keys.min.should eq(0)
|
107
|
-
end
|
108
|
-
|
109
|
-
it 'should begin with meter change at offset 0, instead of start meter' do
|
110
|
-
@mdurs2[0].should eq(@change.end_value.measure_duration)
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
context 'no meter changes' do
|
115
|
-
before :all do
|
116
|
-
@score3 = Score::Tempo.new(FOUR_FOUR, 120)
|
117
|
-
@mdurs3 = @score3.measure_durations
|
118
|
-
end
|
119
|
-
|
120
|
-
it 'should have size 1' do
|
121
|
-
@mdurs3.size.should eq(1)
|
122
|
-
end
|
123
|
-
|
124
|
-
it 'should begin with offset 0' do
|
125
|
-
@mdurs3.keys.min.should eq(0)
|
126
|
-
end
|
127
|
-
|
128
|
-
it 'should begin with start meter' do
|
129
|
-
@mdurs3[0.to_r].should eq(@score3.start_meter.measure_duration)
|
130
|
-
end
|
131
|
-
end
|
132
|
-
end
|
133
|
-
|
134
34
|
describe '#to_timed' do
|
135
35
|
it 'should use ScoreConverter#convert_score' do
|
136
36
|
nscore1 = @score.to_timed(200)
|
@@ -25,7 +25,7 @@ describe ScoreConverter do
|
|
25
25
|
parts.keys.sort.should eq(@score.parts.keys.sort)
|
26
26
|
end
|
27
27
|
|
28
|
-
it 'should convert part dynamic change offsets from
|
28
|
+
it 'should convert part dynamic change offsets from note-based to time-based' do
|
29
29
|
parts = ScoreConverter.new(@score,200).convert_parts
|
30
30
|
parts.should have_key("simple")
|
31
31
|
part = parts["simple"]
|
@@ -36,36 +36,36 @@ describe ScoreConverter do
|
|
36
36
|
change.end_value.should eq(@changeB.end_value)
|
37
37
|
change.duration.should eq(4)
|
38
38
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
39
|
+
@score.start_meter = THREE_FOUR
|
40
|
+
parts = ScoreConverter.new(@score,200).convert_parts
|
41
|
+
parts.should have_key("simple")
|
42
|
+
part = parts["simple"]
|
43
|
+
part.dynamic_changes.keys.sort.should eq([2,6])
|
44
|
+
change = part.dynamic_changes[2.0]
|
45
|
+
change.end_value.should eq(@changeA.end_value)
|
46
|
+
change.duration.should eq(0)
|
47
|
+
change = part.dynamic_changes[6.0]
|
48
|
+
change.end_value.should eq(@changeB.end_value)
|
49
|
+
change.duration.should eq(4)
|
50
50
|
end
|
51
51
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
52
|
+
context 'gradual changes with positive elapsed and/or remaining' do
|
53
|
+
it 'should change elapsed and remaining so they reflect time-based offsets' do
|
54
|
+
score = Score::Tempo.new(THREE_FOUR,120, parts: {
|
55
|
+
"abc" => Part.new(Dynamics::P, dynamic_changes: {
|
56
|
+
2 => Change::Gradual.linear(Dynamics::F,2).to_trimmed(1, 3),
|
57
|
+
7 => Change::Gradual.linear(Dynamics::F,1).to_trimmed(4, 5)
|
58
|
+
})
|
59
|
+
})
|
60
|
+
converter = ScoreConverter.new(score, 200)
|
61
|
+
parts = converter.convert_parts
|
62
|
+
dcs = parts["abc"].dynamic_changes
|
63
|
+
|
64
|
+
dcs.keys.should eq([4, 14])
|
65
|
+
dcs[4.0].should eq(Change::Gradual.linear(Dynamics::F,4).to_trimmed(2,6))
|
66
|
+
dcs[14.0].should eq(Change::Gradual.linear(Dynamics::F,2).to_trimmed(8,10))
|
67
|
+
end
|
68
|
+
end
|
69
69
|
end
|
70
70
|
|
71
71
|
describe '#convert_program' do
|
@@ -81,7 +81,7 @@ describe ScoreConverter do
|
|
81
81
|
prog.size.should eq(@score.program.size)
|
82
82
|
end
|
83
83
|
|
84
|
-
it 'should convert program segments offsets from
|
84
|
+
it 'should convert program segments offsets from note-based to time-based' do
|
85
85
|
prog = ScoreConverter.new(@score,200).convert_program
|
86
86
|
prog.size.should eq(2)
|
87
87
|
prog[0].first.should eq(0)
|
@@ -93,9 +93,9 @@ describe ScoreConverter do
|
|
93
93
|
prog = ScoreConverter.new(@score,200).convert_program
|
94
94
|
prog.size.should eq(2)
|
95
95
|
prog[0].first.should eq(0)
|
96
|
-
prog[0].last.should eq(
|
97
|
-
prog[1].first.should eq(
|
98
|
-
prog[1].last.should eq(
|
96
|
+
prog[0].last.should eq(8)
|
97
|
+
prog[1].first.should eq(4)
|
98
|
+
prog[1].last.should eq(10)
|
99
99
|
end
|
100
100
|
end
|
101
101
|
|
@@ -25,7 +25,7 @@ describe Note do
|
|
25
25
|
|
26
26
|
it 'should assign :marks if given' do
|
27
27
|
[
|
28
|
-
[], [BEGIN_SLUR,
|
28
|
+
[], [BEGIN_SLUR], [END_SLUR]
|
29
29
|
].each do |marks|
|
30
30
|
Note.quarter(marks: marks).marks.should eq(marks)
|
31
31
|
end
|
@@ -131,7 +131,7 @@ describe Note do
|
|
131
131
|
pitches,links = pitches_links_set
|
132
132
|
if pitches.any?
|
133
133
|
articulations.each do |art|
|
134
|
-
[[],[BEGIN_SLUR],[END_SLUR
|
134
|
+
[[],[BEGIN_SLUR],[END_SLUR]].each do |marks|
|
135
135
|
notes.push Note.new(d, pitches, articulation: art, links: links, marks: marks)
|
136
136
|
end
|
137
137
|
end
|
@@ -64,32 +64,7 @@ describe Score do
|
|
64
64
|
end
|
65
65
|
end
|
66
66
|
end
|
67
|
-
|
68
|
-
describe '#max_part_duration' do
|
69
|
-
context 'no parts' do
|
70
|
-
it 'should return 0' do
|
71
|
-
Score.new.max_part_duration.should eq(0)
|
72
|
-
end
|
73
|
-
end
|
74
67
|
|
75
|
-
context 'one part' do
|
76
|
-
it 'should return the part duration' do
|
77
|
-
Score.new(parts: {"part1" => Part.new(Dynamics::PP,
|
78
|
-
notes: "/4 /4 /2 1".to_notes)
|
79
|
-
}).max_part_duration.should eq(2)
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
context 'two parts' do
|
84
|
-
it 'should return the part duration of the longer part' do
|
85
|
-
Score.new(parts: {"part1" => Part.new(Dynamics::PP,
|
86
|
-
notes: "/4 /4 /2 1".to_notes), "part2" => Part.new(Dynamics::MP,
|
87
|
-
notes: "4".to_notes)
|
88
|
-
}).max_part_duration.should eq(4)
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
68
|
describe '#valid?' do
|
94
69
|
context 'non-Range objects' do
|
95
70
|
it 'should return false' do
|
@@ -163,69 +138,26 @@ describe Score::Tempo do
|
|
163
138
|
end
|
164
139
|
end
|
165
140
|
|
166
|
-
describe '#
|
167
|
-
context 'with no
|
168
|
-
|
169
|
-
|
170
|
-
Score::Tempo.new(TWO_FOUR, 120).measures_long.should eq(0)
|
171
|
-
end
|
172
|
-
end
|
173
|
-
|
174
|
-
context 'with one part' do
|
175
|
-
it 'should return the duration of the part, in measures' do
|
176
|
-
Score::Tempo.new(TWO_FOUR, 120, parts: {
|
177
|
-
"abc" => Part.new(Dynamics::MF, notes: "/4 /4 /2 3/4".to_notes)
|
178
|
-
}).measures_long.should eq(3.5)
|
179
|
-
end
|
180
|
-
end
|
181
|
-
|
182
|
-
context 'with two parts' do
|
183
|
-
it 'should return the duration of the longest part, in measures' do
|
184
|
-
Score::Tempo.new(TWO_FOUR, 120, parts: {
|
185
|
-
"abc" => Part.new(Dynamics::MF, notes: "/4 /4 /2 3/4".to_notes),
|
186
|
-
"def" => Part.new(Dynamics::MF, notes: "/4 /4 /2 1".to_notes)
|
187
|
-
}).measures_long.should eq(4)
|
188
|
-
end
|
141
|
+
describe '#duration' do
|
142
|
+
context 'with no parts' do
|
143
|
+
it 'should return 0' do
|
144
|
+
Score::Tempo.new(TWO_FOUR, 120).duration.should eq(0)
|
189
145
|
end
|
190
146
|
end
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
2 => Change::Immediate.new(FOUR_FOUR),
|
197
|
-
},
|
198
|
-
parts: {
|
199
|
-
"abc" => Part.new(Dynamics::MF, notes: "/4 /4 /2 3/4".to_notes),
|
200
|
-
"def" => Part.new(Dynamics::MF, notes: "/4 /4 /2 1".to_notes)
|
201
|
-
}
|
202
|
-
).measures_long.should eq(3)
|
203
|
-
|
204
|
-
Score::Tempo.new(TWO_FOUR, 120,
|
205
|
-
meter_changes: {
|
206
|
-
2 => Change::Immediate.new(FOUR_FOUR),
|
207
|
-
4 => Change::Immediate.new(SIX_EIGHT),
|
208
|
-
},
|
209
|
-
parts: {
|
210
|
-
"abc" => Part.new(Dynamics::MF, notes: "/4 /4 /2 3/4".to_notes),
|
211
|
-
"def" => Part.new(Dynamics::MF, notes: "/4 /4 /2 1 /2".to_notes)
|
212
|
-
}
|
213
|
-
).measures_long.should eq(3.5)
|
147
|
+
context 'with one part' do
|
148
|
+
it 'should return the duration of the part, in notes' do
|
149
|
+
Score::Tempo.new(TWO_FOUR, 120, parts: {
|
150
|
+
"abc" => Part.new(Dynamics::MF, notes: "/4 /4 /2 3/4".to_notes)
|
151
|
+
}).duration.should eq(1.75)
|
214
152
|
end
|
215
153
|
end
|
216
154
|
|
217
|
-
context '
|
218
|
-
it 'should
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
})
|
224
|
-
|
225
|
-
{ 1 => 2, 1.5 => 2.5, 2 => 3, 3 => 4, 3.75 => 5
|
226
|
-
}.each do |note_dur, meas_dur|
|
227
|
-
score.measures_long(note_dur).should eq(meas_dur)
|
228
|
-
end
|
155
|
+
context 'with two parts' do
|
156
|
+
it 'should return the duration of the longest part, in notes' do
|
157
|
+
Score::Tempo.new(TWO_FOUR, 120, parts: {
|
158
|
+
"abc" => Part.new(Dynamics::MF, notes: "/4 /4 /2 3/4".to_notes),
|
159
|
+
"def" => Part.new(Dynamics::MF, notes: "/4 /4 /2 1".to_notes)
|
160
|
+
}).duration.should eq(2)
|
229
161
|
end
|
230
162
|
end
|
231
163
|
end
|
@@ -258,8 +190,6 @@ describe Score::Tempo do
|
|
258
190
|
:meter_changes => { 1 => Change::Immediate.new(5) } ],
|
259
191
|
'non-immediate meter change' => [ FOUR_FOUR, 120,
|
260
192
|
:meter_changes => { 1 => Change::Gradual.linear(TWO_FOUR,1) } ],
|
261
|
-
'non-integer meter change offset' => [ FOUR_FOUR, 120,
|
262
|
-
:meter_changes => { 1.1 => Change::Immediate.new(TWO_FOUR) } ],
|
263
193
|
'invalid part' => [ FOUR_FOUR, 120, :parts => { "piano" => Part.new(-0.1) }],
|
264
194
|
'invalid program' => [ FOUR_FOUR, 120, :program => [2..0] ],
|
265
195
|
}.each do |context_str,args|
|
@@ -314,12 +244,12 @@ describe Score::Timed do
|
|
314
244
|
end
|
315
245
|
end
|
316
246
|
|
317
|
-
describe '#
|
247
|
+
describe '#duration' do
|
318
248
|
it 'should return the duration of the longest part' do
|
319
249
|
Score::Timed.new(parts: {
|
320
250
|
"abc" => Part.new(Dynamics::MF, notes: "/4 /4 /2 3/4".to_notes),
|
321
251
|
"def" => Part.new(Dynamics::MF, notes: "/4 /4 /2 1".to_notes)
|
322
|
-
}).
|
252
|
+
}).duration.should eq(2)
|
323
253
|
end
|
324
254
|
end
|
325
255
|
|