musicality 0.2.0 → 0.3.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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/ChangeLog.md +30 -0
  3. data/lib/musicality/errors.rb +1 -0
  4. data/lib/musicality/notation/conversion/change_conversion.rb +63 -3
  5. data/lib/musicality/notation/conversion/note_time_converter.rb +23 -5
  6. data/lib/musicality/notation/conversion/score_conversion.rb +60 -0
  7. data/lib/musicality/notation/conversion/score_converter.rb +105 -0
  8. data/lib/musicality/notation/model/change.rb +98 -28
  9. data/lib/musicality/notation/model/part.rb +1 -1
  10. data/lib/musicality/notation/model/score.rb +4 -4
  11. data/lib/musicality/notation/packing/change_packing.rb +35 -25
  12. data/lib/musicality/notation/packing/score_packing.rb +2 -2
  13. data/lib/musicality/notation/util/function.rb +99 -0
  14. data/lib/musicality/notation/util/piecewise_function.rb +79 -99
  15. data/lib/musicality/notation/util/transition.rb +12 -0
  16. data/lib/musicality/notation/util/value_computer.rb +12 -152
  17. data/lib/musicality/performance/conversion/score_collator.rb +35 -20
  18. data/lib/musicality/performance/midi/part_sequencer.rb +2 -5
  19. data/lib/musicality/validatable.rb +6 -1
  20. data/lib/musicality/version.rb +1 -1
  21. data/lib/musicality.rb +4 -4
  22. data/musicality.gemspec +1 -0
  23. data/spec/notation/conversion/change_conversion_spec.rb +216 -9
  24. data/spec/notation/conversion/measure_note_map_spec.rb +2 -2
  25. data/spec/notation/conversion/note_time_converter_spec.rb +91 -9
  26. data/spec/notation/conversion/{measured_score_conversion_spec.rb → score_conversion_spec.rb} +44 -9
  27. data/spec/notation/conversion/score_converter_spec.rb +246 -0
  28. data/spec/notation/model/change_spec.rb +139 -36
  29. data/spec/notation/model/part_spec.rb +3 -3
  30. data/spec/notation/model/score_spec.rb +4 -4
  31. data/spec/notation/packing/change_packing_spec.rb +222 -71
  32. data/spec/notation/packing/part_packing_spec.rb +1 -1
  33. data/spec/notation/packing/score_packing_spec.rb +3 -2
  34. data/spec/notation/util/function_spec.rb +43 -0
  35. data/spec/notation/util/transition_spec.rb +51 -0
  36. data/spec/notation/util/value_computer_spec.rb +43 -87
  37. data/spec/performance/conversion/score_collator_spec.rb +46 -7
  38. data/spec/performance/midi/part_sequencer_spec.rb +2 -1
  39. metadata +29 -14
  40. data/lib/musicality/notation/conversion/measured_score_conversion.rb +0 -70
  41. data/lib/musicality/notation/conversion/measured_score_converter.rb +0 -95
  42. data/lib/musicality/notation/conversion/unmeasured_score_conversion.rb +0 -47
  43. data/lib/musicality/notation/conversion/unmeasured_score_converter.rb +0 -64
  44. data/spec/notation/conversion/measured_score_converter_spec.rb +0 -329
  45. data/spec/notation/conversion/unmeasured_score_conversion_spec.rb +0 -71
  46. data/spec/notation/conversion/unmeasured_score_converter_spec.rb +0 -116
@@ -83,6 +83,43 @@ describe ScoreCollator do
83
83
  end
84
84
  end
85
85
 
86
+ context 'part contains trimmed gradual changes' do
87
+ it 'should exclude the change when it is not at all in a program segment' do
88
+ score = Score::Measured.new(FOUR_FOUR, 120,
89
+ parts: { 1 => Part.new(Dynamics::FF, dynamic_changes: {
90
+ 2 => Change::Gradual.linear(Dynamics::PP,5).trim(1,0)
91
+ }) },
92
+ program: Program.new([7...9])
93
+ )
94
+ collator = ScoreCollator.new(score)
95
+ parts = collator.collate_parts
96
+ dcs = parts[1].dynamic_changes
97
+ dcs.size.should eq(1)
98
+ dcs[0.to_r].should eq(Change::Immediate.new(Dynamics::PP))
99
+
100
+ score.program = Program.new([0...1])
101
+ collator = ScoreCollator.new(score)
102
+ parts = collator.collate_parts
103
+ dcs = parts[1].dynamic_changes
104
+ dcs.size.should eq(1)
105
+ dcs[0.to_r].should eq(Change::Immediate.new(Dynamics::FF))
106
+ end
107
+
108
+ it 'should trim the change further if needed' do
109
+ score = Score::Measured.new(FOUR_FOUR, 120,
110
+ parts: { 1 => Part.new(Dynamics::FF, dynamic_changes: {
111
+ 2 => Change::Gradual.linear(Dynamics::PP,5).trim(1,1)
112
+ }) },
113
+ program: Program.new([3...4])
114
+ )
115
+ collator = ScoreCollator.new(score)
116
+ parts = collator.collate_parts
117
+ dcs = parts[1].dynamic_changes
118
+ dcs.size.should eq(1)
119
+ dcs[0.to_r].should eq(Change::Gradual.linear(Dynamics::PP,5).trim(2,2))
120
+ end
121
+ end
122
+
86
123
  it 'should preserve links' do
87
124
  notes = Note.split_parse("1Db4~Bb4")
88
125
  score = Score::Measured.new(
@@ -106,7 +143,7 @@ describe ScoreCollator do
106
143
  before :all do
107
144
  @change0 = Change::Immediate.new(120)
108
145
  @change1 = Change::Immediate.new(200)
109
- @change2 = Change::Gradual.new(100,1)
146
+ @change2 = Change::Gradual.linear(100,1)
110
147
  end
111
148
 
112
149
  context 'tempo change starts at end of program segment' do
@@ -134,8 +171,8 @@ describe ScoreCollator do
134
171
  @tcs[0.to_r].should be_a Change::Immediate
135
172
  end
136
173
 
137
- it 'should be used as starting tempo change value' do
138
- @tcs[0.to_r].value.should eq @change2.value
174
+ it 'should be used as starting tempo change end_value' do
175
+ @tcs[0.to_r].end_value.should eq @change2.end_value
139
176
  end
140
177
  end
141
178
 
@@ -146,8 +183,9 @@ describe ScoreCollator do
146
183
  collator = ScoreCollator.new(score)
147
184
  tcs = collator.collate_tempo_changes
148
185
  tcs.size.should eq 1
149
- tcs[0.to_r].value.should eq @change2.value
150
- tcs[0.to_r].duration.should eq(0.5)
186
+ tcs[0.to_r].should be_a Change::Gradual::Trimmed
187
+ tcs[0.to_r].end_value.should eq @change2.end_value
188
+ tcs[0.to_r].remaining.should eq(0.5)
151
189
  end
152
190
  end
153
191
 
@@ -160,8 +198,9 @@ describe ScoreCollator do
160
198
  tcs.size.should eq 3
161
199
  tcs[0.to_r].should eq @change0
162
200
  tcs[1.to_r].should eq @change1
163
- tcs[2.to_r].value.should eq @change2.value
164
- tcs[2.to_r].duration.should eq(0.5)
201
+ tcs[2.to_r].should be_a Change::Gradual::Trimmed
202
+ tcs[2.to_r].end_value.should eq @change2.end_value
203
+ tcs[2.to_r].remaining.should eq(0.5)
165
204
  end
166
205
  end
167
206
  end
@@ -3,7 +3,8 @@ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
3
3
  describe PartSequencer do
4
4
  describe '#make_midi_track' do
5
5
  before :all do
6
- part = Part.new(Dynamics::PP, notes: "/4C4 /4D4 /8 /8D4 /8E4 /2C4".to_notes * 2)
6
+ part = Part.new(Dynamics::PP, notes: "/4C4 /4D4 /8 /8D4 /8E4 /2C4".to_notes * 2,
7
+ dynamic_changes: { 1 => Change::Gradual.linear(Dynamics::FF,2) })
7
8
  @midi_seq = MIDI::Sequence.new
8
9
  @part_name = "mypart"
9
10
  @channel = 2
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: musicality
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - James Tunnell
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-11-24 00:00:00.000000000 Z
11
+ date: 2014-12-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - ~>
53
53
  - !ruby/object:Gem::Version
54
54
  version: '2.9'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pry
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: treetop
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -109,6 +123,7 @@ files:
109
123
  - .gitignore
110
124
  - .rspec
111
125
  - .ruby-version
126
+ - ChangeLog.md
112
127
  - Gemfile
113
128
  - LICENSE.txt
114
129
  - README.md
@@ -122,12 +137,10 @@ files:
122
137
  - lib/musicality/errors.rb
123
138
  - lib/musicality/notation/conversion/change_conversion.rb
124
139
  - lib/musicality/notation/conversion/measure_note_map.rb
125
- - lib/musicality/notation/conversion/measured_score_conversion.rb
126
- - lib/musicality/notation/conversion/measured_score_converter.rb
127
140
  - lib/musicality/notation/conversion/note_time_converter.rb
141
+ - lib/musicality/notation/conversion/score_conversion.rb
142
+ - lib/musicality/notation/conversion/score_converter.rb
128
143
  - lib/musicality/notation/conversion/tempo_conversion.rb
129
- - lib/musicality/notation/conversion/unmeasured_score_conversion.rb
130
- - lib/musicality/notation/conversion/unmeasured_score_converter.rb
131
144
  - lib/musicality/notation/model/articulations.rb
132
145
  - lib/musicality/notation/model/change.rb
133
146
  - lib/musicality/notation/model/dynamics.rb
@@ -176,8 +189,10 @@ files:
176
189
  - lib/musicality/notation/parsing/pitch_parsing.treetop
177
190
  - lib/musicality/notation/parsing/segment_parsing.rb
178
191
  - lib/musicality/notation/parsing/segment_parsing.treetop
192
+ - lib/musicality/notation/util/function.rb
179
193
  - lib/musicality/notation/util/interpolation.rb
180
194
  - lib/musicality/notation/util/piecewise_function.rb
195
+ - lib/musicality/notation/util/transition.rb
181
196
  - lib/musicality/notation/util/value_computer.rb
182
197
  - lib/musicality/performance/conversion/glissando_converter.rb
183
198
  - lib/musicality/performance/conversion/note_sequence_extractor.rb
@@ -197,12 +212,10 @@ files:
197
212
  - spec/musicality_spec.rb
198
213
  - spec/notation/conversion/change_conversion_spec.rb
199
214
  - spec/notation/conversion/measure_note_map_spec.rb
200
- - spec/notation/conversion/measured_score_conversion_spec.rb
201
- - spec/notation/conversion/measured_score_converter_spec.rb
202
215
  - spec/notation/conversion/note_time_converter_spec.rb
216
+ - spec/notation/conversion/score_conversion_spec.rb
217
+ - spec/notation/conversion/score_converter_spec.rb
203
218
  - spec/notation/conversion/tempo_conversion_spec.rb
204
- - spec/notation/conversion/unmeasured_score_conversion_spec.rb
205
- - spec/notation/conversion/unmeasured_score_converter_spec.rb
206
219
  - spec/notation/model/change_spec.rb
207
220
  - spec/notation/model/link_spec.rb
208
221
  - spec/notation/model/meter_spec.rb
@@ -233,6 +246,8 @@ files:
233
246
  - spec/notation/parsing/pitch_node_spec.rb
234
247
  - spec/notation/parsing/pitch_parsing_spec.rb
235
248
  - spec/notation/parsing/segment_parsing_spec.rb
249
+ - spec/notation/util/function_spec.rb
250
+ - spec/notation/util/transition_spec.rb
236
251
  - spec/notation/util/value_computer_spec.rb
237
252
  - spec/performance/conversion/glissando_converter_spec.rb
238
253
  - spec/performance/conversion/note_sequence_extractor_spec.rb
@@ -273,12 +288,10 @@ test_files:
273
288
  - spec/musicality_spec.rb
274
289
  - spec/notation/conversion/change_conversion_spec.rb
275
290
  - spec/notation/conversion/measure_note_map_spec.rb
276
- - spec/notation/conversion/measured_score_conversion_spec.rb
277
- - spec/notation/conversion/measured_score_converter_spec.rb
278
291
  - spec/notation/conversion/note_time_converter_spec.rb
292
+ - spec/notation/conversion/score_conversion_spec.rb
293
+ - spec/notation/conversion/score_converter_spec.rb
279
294
  - spec/notation/conversion/tempo_conversion_spec.rb
280
- - spec/notation/conversion/unmeasured_score_conversion_spec.rb
281
- - spec/notation/conversion/unmeasured_score_converter_spec.rb
282
295
  - spec/notation/model/change_spec.rb
283
296
  - spec/notation/model/link_spec.rb
284
297
  - spec/notation/model/meter_spec.rb
@@ -309,6 +322,8 @@ test_files:
309
322
  - spec/notation/parsing/pitch_node_spec.rb
310
323
  - spec/notation/parsing/pitch_parsing_spec.rb
311
324
  - spec/notation/parsing/segment_parsing_spec.rb
325
+ - spec/notation/util/function_spec.rb
326
+ - spec/notation/util/transition_spec.rb
312
327
  - spec/notation/util/value_computer_spec.rb
313
328
  - spec/performance/conversion/glissando_converter_spec.rb
314
329
  - spec/performance/conversion/note_sequence_extractor_spec.rb
@@ -1,70 +0,0 @@
1
- module Musicality
2
-
3
- class Score
4
- class Measured < TempoBased
5
- # Convert to unmeasured score by converting measure-based offsets to
6
- # note-based offsets, and eliminating the use of meters. Also, tempo is
7
- # coverted from beats-per-minute to quarter-notes per minute.
8
- def to_unmeasured
9
- MeasuredScoreConverter.new(self).convert_score
10
- end
11
-
12
- def to_timed tempo_sample_rate
13
- unmeasured = MeasuredScoreConverter.new(self).convert_score
14
- unmeasured.to_timed(tempo_sample_rate)
15
- end
16
-
17
- def measure_note_map
18
- Conversion::measure_note_map(measure_offsets,measure_durations)
19
- end
20
-
21
- def measure_offsets
22
- moffs = Set.new([0.to_r])
23
-
24
- @tempo_changes.each do |moff,change|
25
- moffs += change.offsets(moff)
26
- end
27
-
28
- @meter_changes.keys.each {|moff| moffs.add(moff) }
29
-
30
- @parts.values.each do |part|
31
- part.dynamic_changes.each do |moff,change|
32
- moffs += change.offsets(moff)
33
- end
34
- end
35
-
36
- @program.segments.each do |seg|
37
- moffs.add(seg.first)
38
- moffs.add(seg.last)
39
- end
40
-
41
- return moffs.sort
42
- end
43
-
44
- def beat_durations
45
- bdurs = @meter_changes.map do |offset,change|
46
- [ offset, change.value.beat_duration ]
47
- end.sort
48
-
49
- if bdurs.empty? || bdurs[0][0] != 0
50
- bdurs.unshift([0,@start_meter.beat_duration])
51
- end
52
-
53
- return bdurs
54
- end
55
-
56
- def measure_durations
57
- mdurs = @meter_changes.map do |offset,change|
58
- [ offset, change.value.measure_duration ]
59
- end.sort
60
-
61
- if mdurs.empty? || mdurs[0][0] != 0
62
- mdurs.unshift([0,@start_meter.measure_duration])
63
- end
64
-
65
- return Hash[ mdurs ]
66
- end
67
- end
68
- end
69
-
70
- end
@@ -1,95 +0,0 @@
1
- module Musicality
2
-
3
- # Converts measured score to unmeasured by converting measure-based offsets
4
- # and durations to note-based offsets, and eliminating the use of meters.
5
- # Also, tempo is coverted from beats-per-minute to quarter-notes per minute.
6
- class MeasuredScoreConverter
7
- def initialize score
8
- unless score.valid?
9
- raise NotValidError, "The given score can not be converted because \
10
- it is invalid, with these errors: #{score.errors}"
11
- end
12
-
13
- @score = score
14
- @mnoff_map = score.measure_note_map
15
- end
16
-
17
- def convert_score
18
- Score::Unmeasured.new(convert_start_tempo,
19
- parts: convert_parts, program: convert_program,
20
- tempo_changes: convert_tempo_changes)
21
- end
22
-
23
- def convert_parts
24
- Hash[ @score.parts.map do |name,part|
25
- new_dcs = Hash[ part.dynamic_changes.map do |moff,change|
26
- case change
27
- when Change::Immediate
28
- [@mnoff_map[moff],change.clone]
29
- when Change::Gradual
30
- noff1, noff2, noff3, noff4 = change.offsets(moff).map {|x| @mnoff_map[x] }
31
- [noff2, Change::Gradual.new(change.value,
32
- noff3-noff2, noff2-noff1, noff4-noff3)]
33
- end
34
- end ]
35
- new_notes = part.notes.map {|n| n.clone }
36
- [name, Part.new(part.start_dynamic,
37
- notes: new_notes, dynamic_changes: new_dcs)]
38
- end ]
39
- end
40
-
41
- def convert_program
42
- Program.new(
43
- @score.program.segments.map do |seg|
44
- @mnoff_map[seg.first]...@mnoff_map[seg.last]
45
- end
46
- )
47
- end
48
-
49
- def convert_start_tempo
50
- Tempo::BPM.to_qnpm(@score.start_tempo, @score.start_meter.beat_duration)
51
- end
52
-
53
- def convert_tempo_changes
54
- tcs = {}
55
- bdurs = @score.beat_durations
56
-
57
- @score.tempo_changes.each do |moff,change|
58
- bdur = bdurs.select {|x,y| x <= moff}.max[1]
59
- tempo = change.value
60
-
61
- case change
62
- when Change::Immediate
63
- tcs[@mnoff_map[moff]] = Change::Immediate.new(Tempo::BPM.to_qnpm(tempo,bdur))
64
- when Change::Gradual
65
- start_moff, end_moff = moff, moff + change.duration
66
- start_noff, end_noff = @mnoff_map[start_moff], @mnoff_map[end_moff]
67
-
68
- initial_moff, final_moff = start_moff - change.elapsed, end_moff + change.remaining
69
- initial_noff, final_noff = @mnoff_map[initial_moff], @mnoff_map[final_moff]
70
-
71
- more_bdurs = bdurs.select {|x,y| x > start_moff && x < end_moff }
72
- cur_noff, cur_bdur = start_noff, bdur
73
-
74
- more_bdurs.each do |next_moff, next_bdur|
75
- next_noff = @mnoff_map[next_moff]
76
- elapsed = cur_noff - initial_noff
77
- impending = next_noff - cur_noff
78
- remaining = final_noff - next_noff
79
- tempo2 = Tempo::BPM.to_qnpm(tempo, cur_bdur)
80
- tcs[cur_noff] = Change::Gradual.new(tempo2, impending, elapsed, remaining)
81
- cur_noff, cur_bdur = next_noff, next_bdur
82
- end
83
- elapsed = cur_noff - initial_noff
84
- impending = end_noff - cur_noff
85
- remaining = final_noff - end_noff
86
- tempo2 = Tempo::BPM.to_qnpm(tempo, cur_bdur)
87
- tcs[cur_noff] = Change::Gradual.new(tempo2, impending, elapsed, remaining)
88
- end
89
- end
90
-
91
- return tcs
92
- end
93
- end
94
-
95
- end
@@ -1,47 +0,0 @@
1
- module Musicality
2
-
3
- class Score
4
- class Unmeasured < TempoBased
5
- # Convert to unmeasured score by converting measure-based offsets to
6
- # note-based offsets, and eliminating the use of meters. Also, tempo is
7
- # coverted from beats-per-minute to quarter-notes per minute.
8
- def to_timed tempo_sample_rate
9
- UnmeasuredScoreConverter.new(self,tempo_sample_rate).convert_score
10
- end
11
-
12
- def note_time_map tempo_sample_rate
13
- tempo_computer = ValueComputer.new(@start_tempo, @tempo_changes)
14
- ntc = NoteTimeConverter.new(tempo_computer, tempo_sample_rate)
15
- ntc.note_time_map(note_offsets)
16
- end
17
-
18
- def note_offsets
19
- noffs = Set.new([0.to_r])
20
-
21
- @tempo_changes.each do |noff,change|
22
- noffs += change.offsets(noff)
23
- end
24
-
25
- @parts.values.each do |part|
26
- noff = 0.to_r
27
- part.notes.each do |note|
28
- noff += note.duration
29
- noffs.add(noff)
30
- end
31
-
32
- part.dynamic_changes.each do |noff,change|
33
- noffs += change.offsets(noff)
34
- end
35
- end
36
-
37
- @program.segments.each do |seg|
38
- noffs.add(seg.first)
39
- noffs.add(seg.last)
40
- end
41
-
42
- return noffs.sort
43
- end
44
- end
45
- end
46
-
47
- end
@@ -1,64 +0,0 @@
1
- require 'set'
2
-
3
- module Musicality
4
-
5
- # Converts unmeasured score to timed score, by converting note-based offsets
6
- # and durations to time-based, and eliminating the use of tempo.
7
- class UnmeasuredScoreConverter
8
-
9
- def initialize score, tempo_sample_rate
10
- unless score.valid?
11
- raise NotValidError, "The given score can not be converted because \
12
- it is invalid, with these errors: #{score.errors}"
13
- end
14
-
15
- @score = score
16
- @note_time_map = score.note_time_map(tempo_sample_rate)
17
- end
18
-
19
- def convert_score
20
- Score::Timed.new(parts: convert_parts, program: convert_program)
21
- end
22
-
23
- # Convert note-based offsets & durations to time-based.
24
- def convert_parts
25
- Hash[ @score.parts.map do |name,part|
26
- offset = 0.to_r
27
-
28
- new_notes = part.notes.map do |note|
29
- starttime = @note_time_map[offset]
30
- endtime = @note_time_map[offset + note.duration]
31
- offset += note.duration
32
- newnote = note.clone
33
- newnote.duration = endtime - starttime
34
- newnote
35
- end
36
-
37
- new_dcs = Hash[ part.dynamic_changes.map do |noff,change|
38
- case change
39
- when Change::Immediate
40
- [@note_time_map[noff],change.clone]
41
- when Change::Gradual
42
- toff1, toff2, toff3, toff4 = change.offsets(noff).map {|x| @note_time_map[x] }
43
- [toff2, Change::Gradual.new(change.value,
44
- toff3-toff2, toff2-toff1, toff4-toff3)]
45
- end
46
- end ]
47
-
48
- [name, Part.new(part.start_dynamic,
49
- notes: new_notes, dynamic_changes: new_dcs)]
50
- end]
51
- end
52
-
53
- # Convert note-based offsets & durations to time-based.
54
- def convert_program
55
- newsegments = @score.program.segments.map do |segment|
56
- first = @note_time_map[segment.first]
57
- last = @note_time_map[segment.last]
58
- first...last
59
- end
60
- Program.new(newsegments)
61
- end
62
- end
63
-
64
- end