music-transcription 0.6.3 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/examples/hip.yml +516 -0
- data/examples/make_hip.rb +63 -0
- data/examples/make_missed_connection.rb +65 -0
- data/examples/make_song1.rb +71 -0
- data/examples/make_song2.rb +58 -0
- data/examples/missed_connection.yml +305 -0
- data/examples/song1.yml +367 -0
- data/examples/song2.yml +236 -0
- data/lib/music-transcription/accent.rb +4 -15
- data/lib/music-transcription/accents.rb +12 -0
- data/lib/music-transcription/dynamic.rb +6 -15
- data/lib/music-transcription/meter.rb +15 -0
- data/lib/music-transcription/note.rb +28 -91
- data/lib/music-transcription/part.rb +9 -65
- data/lib/music-transcription/score.rb +14 -43
- data/lib/music-transcription/version.rb +1 -1
- data/lib/music-transcription.rb +2 -0
- data/spec/meter_spec.rb +25 -0
- data/spec/note_spec.rb +20 -33
- data/spec/part_spec.rb +13 -82
- data/spec/score_spec.rb +28 -16
- data/spec/spec_helper.rb +7 -2
- metadata +14 -20
- data/samples/arrangements/glissando_test.yml +0 -71
- data/samples/arrangements/hip.yml +0 -952
- data/samples/arrangements/instrument_test.yml +0 -119
- data/samples/arrangements/legato_test.yml +0 -237
- data/samples/arrangements/make_glissando_test.rb +0 -27
- data/samples/arrangements/make_hip.rb +0 -75
- data/samples/arrangements/make_instrument_test.rb +0 -34
- data/samples/arrangements/make_legato_test.rb +0 -37
- data/samples/arrangements/make_missed_connection.rb +0 -72
- data/samples/arrangements/make_portamento_test.rb +0 -27
- data/samples/arrangements/make_slur_test.rb +0 -37
- data/samples/arrangements/make_song1.rb +0 -84
- data/samples/arrangements/make_song2.rb +0 -69
- data/samples/arrangements/missed_connection.yml +0 -481
- data/samples/arrangements/portamento_test.yml +0 -71
- data/samples/arrangements/slur_test.yml +0 -237
- data/samples/arrangements/song1.yml +0 -640
- data/samples/arrangements/song2.yml +0 -429
@@ -3,39 +3,17 @@ module Transcription
|
|
3
3
|
|
4
4
|
require 'set'
|
5
5
|
|
6
|
-
# Abstraction of a musical note. The note can contain zero or more pitches,
|
7
|
-
# with links to a pitches in a following note. The note also has an accent,
|
8
|
-
# which must be one of Note::ACCENTS.
|
9
|
-
#
|
10
|
-
# @author James Tunnell
|
11
|
-
#
|
12
|
-
# @!attribute [rw] duration
|
13
|
-
# @return [Numeric] The duration (in, say note length or time), greater than 0.0.
|
14
|
-
#
|
15
|
-
# @!attribute [r] pitches
|
16
|
-
# @return [Set] The pitches that are part of the note and can link to
|
17
|
-
# pitches in a following note.
|
18
|
-
#
|
19
|
-
# @!attribute [r] links
|
20
|
-
# @return [Hash] Maps pitches in the current note to pitches in the following
|
21
|
-
# note, by some link class, like Link::Slur.
|
22
|
-
#
|
23
|
-
# @!attribute [rw] accent
|
24
|
-
# @return [Accent] The accent type, which must be one of Note::ACCENTS.
|
25
|
-
#
|
26
6
|
class Note
|
27
7
|
attr_reader :duration, :pitches, :links
|
28
8
|
attr_accessor :accent
|
29
9
|
|
30
|
-
|
31
|
-
def initialize duration, pitches = [], links: {}, accent: nil
|
10
|
+
def initialize duration, pitches = [], links: {}, accent: Accents::NONE
|
32
11
|
self.duration = duration
|
33
12
|
@pitches = Set.new(pitches).sort
|
34
13
|
@links = links
|
35
14
|
self.accent = accent
|
36
15
|
end
|
37
16
|
|
38
|
-
# Compare the equality of another Note object.
|
39
17
|
def == other
|
40
18
|
return (@duration == other.duration) &&
|
41
19
|
(self.pitches == other.pitches) &&
|
@@ -51,32 +29,15 @@ class Note
|
|
51
29
|
@duration = duration
|
52
30
|
end
|
53
31
|
|
54
|
-
# Produce an identical Note object.
|
55
32
|
def clone
|
56
33
|
Marshal.load(Marshal.dump(self))
|
57
34
|
end
|
58
35
|
|
59
|
-
def
|
60
|
-
self.clone.
|
61
|
-
end
|
62
|
-
|
63
|
-
def transpose_pitches_only! diff
|
64
|
-
self.transpose! diff, false
|
65
|
-
end
|
66
|
-
|
67
|
-
def transpose_pitches_and_links diff
|
68
|
-
self.clone.transpose_pitches_and_links! diff
|
69
|
-
end
|
70
|
-
|
71
|
-
def transpose_pitches_and_links! diff
|
72
|
-
self.transpose! diff, true
|
36
|
+
def transpose diff
|
37
|
+
self.clone.transpose! diff
|
73
38
|
end
|
74
39
|
|
75
|
-
def transpose diff
|
76
|
-
self.clone.transpose! diff, transpose_links
|
77
|
-
end
|
78
|
-
|
79
|
-
def transpose! diff, transpose_link_targets = true
|
40
|
+
def transpose! diff
|
80
41
|
unless diff.is_a?(Pitch)
|
81
42
|
diff = Pitch.make_from_semitone(diff)
|
82
43
|
end
|
@@ -84,9 +45,7 @@ class Note
|
|
84
45
|
@pitches = @pitches.map {|pitch| pitch + diff}
|
85
46
|
new_links = {}
|
86
47
|
@links.each_pair do |k,v|
|
87
|
-
|
88
|
-
v.target_pitch += diff
|
89
|
-
end
|
48
|
+
v.target_pitch += diff
|
90
49
|
new_links[k + diff] = v
|
91
50
|
end
|
92
51
|
@links = new_links
|
@@ -102,79 +61,57 @@ class Note
|
|
102
61
|
return self
|
103
62
|
end
|
104
63
|
|
105
|
-
def to_s
|
106
|
-
output = @duration.to_s
|
107
|
-
if @pitches.any?
|
108
|
-
output += "@"
|
109
|
-
@pitches[0...-1].each do |pitch|
|
110
|
-
output += pitch.to_s
|
111
|
-
if @links.has_key? pitch
|
112
|
-
output += @links[pitch].to_s
|
113
|
-
end
|
114
|
-
output += ","
|
115
|
-
end
|
116
|
-
|
117
|
-
last_pitch = @pitches[-1]
|
118
|
-
output += last_pitch.to_s
|
119
|
-
if @links.has_key? last_pitch
|
120
|
-
output += @links[last_pitch].to_s
|
121
|
-
end
|
122
|
-
end
|
123
|
-
|
124
|
-
return output
|
125
|
-
end
|
126
|
-
|
127
64
|
class Sixteenth < Note
|
128
|
-
def initialize pitches = [], links: {}, accent:
|
129
|
-
super(Rational(1,16),pitches,
|
65
|
+
def initialize pitches = [], links: {}, accent: Accents::NONE
|
66
|
+
super(Rational(1,16),pitches,links:links,accent:accent)
|
130
67
|
end
|
131
68
|
end
|
132
|
-
|
69
|
+
|
133
70
|
class DottedSixteenth < Note
|
134
|
-
def initialize pitches = [], links: {}, accent:
|
135
|
-
super(Rational(3,32),pitches,
|
71
|
+
def initialize pitches = [], links: {}, accent: Accents::NONE
|
72
|
+
super(Rational(3,32),pitches,links:links,accent:accent)
|
136
73
|
end
|
137
74
|
end
|
138
75
|
|
139
76
|
class Eighth < Note
|
140
|
-
def initialize pitches = [], links: {}, accent:
|
141
|
-
super(Rational(1,8),pitches,
|
77
|
+
def initialize pitches = [], links: {}, accent: Accents::NONE
|
78
|
+
super(Rational(1,8),pitches,links:links,accent:accent)
|
142
79
|
end
|
143
80
|
end
|
144
|
-
|
81
|
+
|
145
82
|
class DottedEighth < Note
|
146
|
-
def initialize pitches = [], links: {}, accent:
|
147
|
-
super(Rational(3,16),pitches,
|
83
|
+
def initialize pitches = [], links: {}, accent: Accents::NONE
|
84
|
+
super(Rational(3,16),pitches,links:links,accent:accent)
|
148
85
|
end
|
149
86
|
end
|
150
87
|
|
151
88
|
class Quarter < Note
|
152
|
-
def initialize pitches = [], links: {}, accent:
|
153
|
-
super(Rational(1,4),pitches,
|
89
|
+
def initialize pitches = [], links: {}, accent: Accents::NONE
|
90
|
+
super(Rational(1,4),pitches,links:links,accent:accent)
|
154
91
|
end
|
155
92
|
end
|
156
93
|
|
157
94
|
class DottedQuarter < Note
|
158
|
-
def initialize pitches = [], links: {}, accent:
|
159
|
-
super(Rational(3,8),pitches,
|
95
|
+
def initialize pitches = [], links: {}, accent: Accents::NONE
|
96
|
+
super(Rational(3,8),pitches,links:links,accent:accent)
|
160
97
|
end
|
161
98
|
end
|
162
|
-
|
99
|
+
|
163
100
|
class Half < Note
|
164
|
-
def initialize pitches = [], links: {}, accent:
|
165
|
-
super(Rational(1,2),pitches,
|
101
|
+
def initialize pitches = [], links: {}, accent: Accents::NONE
|
102
|
+
super(Rational(1,2),pitches,links:links,accent:accent)
|
166
103
|
end
|
167
104
|
end
|
168
|
-
|
105
|
+
|
169
106
|
class DottedHalf < Note
|
170
|
-
def initialize pitches = [], links: {}, accent:
|
171
|
-
super(Rational(3,4),pitches,
|
107
|
+
def initialize pitches = [], links: {}, accent: Accents::NONE
|
108
|
+
super(Rational(3,4),pitches,links:links,accent:accent)
|
172
109
|
end
|
173
110
|
end
|
174
|
-
|
111
|
+
|
175
112
|
class Whole < Note
|
176
|
-
def initialize pitches = [], links: {}, accent:
|
177
|
-
super(Rational(1,1),pitches,
|
113
|
+
def initialize pitches = [], links: {}, accent: Accents::NONE
|
114
|
+
super(Rational(1,1),pitches,links:links,accent:accent)
|
178
115
|
end
|
179
116
|
end
|
180
117
|
end
|
@@ -3,90 +3,34 @@ require 'yaml'
|
|
3
3
|
module Music
|
4
4
|
module Transcription
|
5
5
|
|
6
|
-
# Abstraction of a musical part. Contains notes and loudness_profile settings.
|
7
|
-
#
|
8
|
-
# @author James Tunnell
|
9
|
-
#
|
10
|
-
# @!attribute [r] notes
|
11
|
-
# @return [Array] The notes to be played.
|
12
|
-
#
|
13
|
-
# @!attribute [r] dynamic_profile
|
14
|
-
# @return [Profile] Dynamic values profile
|
15
|
-
#
|
16
6
|
class Part
|
17
|
-
attr_reader :
|
7
|
+
attr_reader :start_dynamic, :dynamic_changes, :notes
|
18
8
|
|
19
|
-
def initialize notes: [],
|
9
|
+
def initialize start_dynamic, notes: [], dynamic_changes: {}
|
20
10
|
@notes = notes
|
21
|
-
@
|
22
|
-
|
23
|
-
if dynamic_profile.changes_before?(0)
|
24
|
-
raise ArgumentError, "dynamic profile has changes with offset less than 0"
|
25
|
-
end
|
11
|
+
@start_dynamic = start_dynamic
|
12
|
+
@dynamic_changes = dynamic_changes
|
26
13
|
|
27
14
|
d = self.duration
|
28
|
-
|
29
|
-
|
15
|
+
badkeys = dynamic_changes.keys.select {|k| k < 0 || k > d }
|
16
|
+
if badkeys.any?
|
17
|
+
raise ArgumentError, "dynamic profile has changes outside 0..d"
|
30
18
|
end
|
31
19
|
end
|
32
20
|
|
33
|
-
# Produce an exact copy of the current object
|
34
21
|
def clone
|
35
22
|
Marshal.load(Marshal.dump(self))
|
36
23
|
end
|
37
24
|
|
38
|
-
# Compare the equality of another Part object.
|
39
25
|
def ==(other)
|
40
26
|
return (@notes == other.notes) &&
|
41
|
-
(@
|
27
|
+
(@start_dynamic == other.start_dynamic) &&
|
28
|
+
(@dynamic_changes == other.dynamic_changes)
|
42
29
|
end
|
43
30
|
|
44
|
-
# Duration of part notes.
|
45
31
|
def duration
|
46
32
|
return @notes.inject(0) { |sum, note| sum + note.duration }
|
47
33
|
end
|
48
|
-
|
49
|
-
def transpose diff
|
50
|
-
self.clone.transpose! diff
|
51
|
-
end
|
52
|
-
|
53
|
-
def transpose! diff
|
54
|
-
@notes[0...-1].each do |note|
|
55
|
-
note.transpose_pitches_and_links! diff
|
56
|
-
end
|
57
|
-
@notes[-1].transpose_pitches_only! diff
|
58
|
-
return self
|
59
|
-
end
|
60
|
-
|
61
|
-
# Add on notes and dynamic_profile from another part, producing a new
|
62
|
-
# Part object. The offsets of value changes in the dynamic profile,
|
63
|
-
# for the other part, will be considered relative from end of current part.
|
64
|
-
def append other
|
65
|
-
self.clone.append! other
|
66
|
-
end
|
67
|
-
|
68
|
-
# Add on notes and dynamic_profile from another part, producing a new
|
69
|
-
# Part object. The offsets of value changes in the dynamic profile,
|
70
|
-
# for the other part, will be considered relative from end of current part.
|
71
|
-
def append! other
|
72
|
-
@dynamic_profile.append!(other.dynamic_profile,self.duration)
|
73
|
-
@notes += other.notes.map {|x| x.clone}
|
74
|
-
return self
|
75
|
-
end
|
76
|
-
|
77
|
-
def stretch ratio
|
78
|
-
self.clone.stretch! ratio
|
79
|
-
end
|
80
|
-
|
81
|
-
def stretch! ratio
|
82
|
-
@notes.each_index do |i|
|
83
|
-
n1 = @notes[i]
|
84
|
-
@notes[i] = Note.new(n1.duration * ratio, n1.pitches, links: n1.links, accent: n1.accent)
|
85
|
-
end
|
86
|
-
|
87
|
-
@dynamic_profile.stretch! ratio
|
88
|
-
return self
|
89
|
-
end
|
90
34
|
end
|
91
35
|
|
92
36
|
end
|
@@ -1,26 +1,16 @@
|
|
1
1
|
module Music
|
2
2
|
module Transcription
|
3
3
|
|
4
|
-
# Score, containing parts and a program.
|
5
|
-
#
|
6
|
-
# @author James Tunnell
|
7
|
-
#
|
8
|
-
# @!attribute [rw] parts
|
9
|
-
# @return [Hash] Score parts, mapped to part names
|
10
|
-
#
|
11
|
-
# @!attribute [rw] program
|
12
|
-
# @return [Program] Score program (which segments are played when)
|
13
|
-
#
|
14
|
-
# @!attribute [rw] tempo_profile
|
15
|
-
# @return [Profile] Tempo values profile
|
16
|
-
#
|
17
4
|
class Score
|
18
|
-
attr_reader :parts, :program, :
|
5
|
+
attr_reader :start_meter, :start_tempo, :parts, :program, :meter_changes, :tempo_changes
|
19
6
|
|
20
|
-
def initialize
|
7
|
+
def initialize start_meter, start_tempo, meter_changes: {}, tempo_changes: {}, parts: {}, program: Program.new
|
8
|
+
@start_meter = start_meter
|
9
|
+
@start_tempo = start_tempo
|
10
|
+
@meter_changes = meter_changes
|
11
|
+
@tempo_changes = tempo_changes
|
21
12
|
@parts = parts
|
22
13
|
@program = program
|
23
|
-
@tempo_profile = tempo_profile
|
24
14
|
end
|
25
15
|
|
26
16
|
def clone
|
@@ -28,35 +18,16 @@ class Score
|
|
28
18
|
end
|
29
19
|
|
30
20
|
def ==(other)
|
31
|
-
return
|
32
|
-
|
33
|
-
|
21
|
+
return @start_meter == other.start_meter &&
|
22
|
+
@start_tempo == other.start_tempo &&
|
23
|
+
@meter_changes == other.meter_changes &&
|
24
|
+
@tempo_changes == other.tempo_changes &&
|
25
|
+
@parts == other.parts &&
|
26
|
+
@program == other.program
|
34
27
|
end
|
35
28
|
|
36
|
-
|
37
|
-
|
38
|
-
def start
|
39
|
-
sos = 0.0
|
40
|
-
|
41
|
-
@parts.each do |id,part|
|
42
|
-
sop = part.start
|
43
|
-
sos = sop if sop > sos
|
44
|
-
end
|
45
|
-
|
46
|
-
return sos
|
47
|
-
end
|
48
|
-
|
49
|
-
# Find the end of a score. The end will be at then end of whichever part ends
|
50
|
-
# last, or 0 if no parts have been added.
|
51
|
-
def end
|
52
|
-
eos = 0.0
|
53
|
-
|
54
|
-
@parts.each do |id,part|
|
55
|
-
eop = part.end
|
56
|
-
eos = eop if eop > eos
|
57
|
-
end
|
58
|
-
|
59
|
-
return eos
|
29
|
+
def duration
|
30
|
+
@parts.map {|p| p.duration }.max
|
60
31
|
end
|
61
32
|
end
|
62
33
|
|
data/lib/music-transcription.rb
CHANGED
@@ -6,6 +6,7 @@ require 'music-transcription/pitch'
|
|
6
6
|
require 'music-transcription/pitches'
|
7
7
|
require 'music-transcription/link'
|
8
8
|
require 'music-transcription/accent'
|
9
|
+
require 'music-transcription/accents'
|
9
10
|
require 'music-transcription/change'
|
10
11
|
require 'music-transcription/note'
|
11
12
|
require 'music-transcription/profile'
|
@@ -14,4 +15,5 @@ require 'music-transcription/dynamics'
|
|
14
15
|
require 'music-transcription/part'
|
15
16
|
require 'music-transcription/program'
|
16
17
|
require 'music-transcription/tempo'
|
18
|
+
require 'music-transcription/meter'
|
17
19
|
require 'music-transcription/score'
|
data/spec/meter_spec.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe Meter do
|
4
|
+
describe '#initialize' do
|
5
|
+
it 'should assign beats per measure and beat duration' do
|
6
|
+
[[4,"1/4".to_r],[3,"1/4".to_r],[6,"1/8".to_r]].each do |bpm,bd|
|
7
|
+
m = Meter.new(bpm,bd)
|
8
|
+
m.beats_per_measure.should eq bpm
|
9
|
+
m.beat_duration.should eq bd
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should derive measure duration' do
|
14
|
+
{
|
15
|
+
[4,"1/4".to_r] => "1/1".to_r,
|
16
|
+
[3,"1/4".to_r] => "3/4".to_r,
|
17
|
+
[6,"1/8".to_r] => "6/8".to_r,
|
18
|
+
[12,"1/8".to_r] => "12/8".to_r,
|
19
|
+
}.each do |bpm,bd|
|
20
|
+
m = Meter.new(bpm,bd)
|
21
|
+
m.measure_duration.should eq(bpm*bd)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/spec/note_spec.rb
CHANGED
@@ -37,46 +37,33 @@ describe Note do
|
|
37
37
|
end
|
38
38
|
|
39
39
|
describe '#transpose!' do
|
40
|
-
context '
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
end
|
47
|
-
|
48
|
-
it 'should modifiy pitches by adding pitch diff' do
|
49
|
-
@note.pitches[0].should eq E2
|
50
|
-
@note.pitches[1].should eq A2
|
51
|
-
end
|
40
|
+
context 'given pitch diff' do
|
41
|
+
before(:all) do
|
42
|
+
@note = Note::Quarter.new([C2,F2], links:{C2=>Link::Slur.new(D2)})
|
43
|
+
@diff = Pitch.new(semitone: 4)
|
44
|
+
@note.transpose! @diff
|
45
|
+
end
|
52
46
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
end
|
47
|
+
it 'should modifiy pitches by adding pitch diff' do
|
48
|
+
@note.pitches[0].should eq E2
|
49
|
+
@note.pitches[1].should eq A2
|
57
50
|
end
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
end
|
51
|
+
|
52
|
+
it 'should also affect link targets' do
|
53
|
+
@note.links.should have_key(E2)
|
54
|
+
@note.links[E2].target_pitch.should eq(Gb2)
|
63
55
|
end
|
64
56
|
end
|
65
|
-
|
66
|
-
context '
|
67
|
-
it 'should
|
68
|
-
|
69
|
-
note.transpose!(2,true)
|
70
|
-
note.links[D2].target_pitch.should eq(E2)
|
57
|
+
|
58
|
+
context 'given integer diff' do
|
59
|
+
it 'should transpose the given number of semitones' do
|
60
|
+
Note::Quarter.new([C2]).transpose!(4).pitches[0].should eq(E2)
|
71
61
|
end
|
72
62
|
end
|
73
63
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
note.transpose!(2)
|
78
|
-
note.links[D2].target_pitch.should eq E2
|
79
|
-
end
|
64
|
+
it 'should return self' do
|
65
|
+
n = Note::Quarter.new
|
66
|
+
n.transpose!(0).should eq n
|
80
67
|
end
|
81
68
|
end
|
82
69
|
|
data/spec/part_spec.rb
CHANGED
@@ -1,91 +1,22 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
2
|
|
3
3
|
describe Part do
|
4
|
-
context '
|
5
|
-
it 'should
|
6
|
-
Part.new
|
7
|
-
|
8
|
-
|
9
|
-
it "should assign dynamic profile given during construction" do
|
10
|
-
profile = Profile.new(Dynamics::FFF, { 1.0 => Change::Immediate.new(Dynamics::PP) })
|
11
|
-
part = Part.new notes:[Note.new(1.to_r)], dynamic_profile: profile
|
12
|
-
part.dynamic_profile.should eq(profile)
|
13
|
-
end
|
14
|
-
|
15
|
-
it "should assign notes given during construction" do
|
16
|
-
notes = [ Note::Quarter.new([C1,D1]) ]
|
17
|
-
part = Part.new notes: notes
|
18
|
-
part.notes.should eq(notes)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
describe '#stretch' do
|
23
|
-
before :all do
|
24
|
-
p = Part.new(
|
25
|
-
notes: [ Note::Quarter.new, Note::Whole.new, Note::Eighth.new ],
|
26
|
-
dynamic_profile: Profile.new(
|
27
|
-
Dynamics::PP,
|
28
|
-
"1/2".to_r => Change::Immediate.new(Dynamics::MP),
|
29
|
-
"3/4".to_r => Change::Immediate.new(Dynamics::FF)
|
30
|
-
)
|
31
|
-
)
|
32
|
-
@p1 = p.stretch(1)
|
33
|
-
@p2 = p.stretch("5/3".to_r)
|
34
|
-
@p3 = p.stretch("3/5".to_r)
|
35
|
-
end
|
36
|
-
|
37
|
-
it 'should multiply durations by ratio' do
|
38
|
-
@p1.notes.map {|n| n.duration }.should eq(["1/4".to_r, "1/1".to_r, "1/8".to_r])
|
39
|
-
@p2.notes.map {|n| n.duration }.should eq(["5/12".to_r, "5/3".to_r, "5/24".to_r])
|
40
|
-
@p3.notes.map {|n| n.duration }.should eq(["3/20".to_r, "3/5".to_r, "3/40".to_r])
|
4
|
+
context '#initialize' do
|
5
|
+
it 'should use empty containers for parameters not given' do
|
6
|
+
p = Part.new(Dynamics::MP)
|
7
|
+
p.notes.should be_empty
|
8
|
+
p.dynamic_changes.should be_empty
|
41
9
|
end
|
42
10
|
|
43
|
-
it
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
@p2.dynamic_profile.value_changes.should have_key("5/6".to_r)
|
48
|
-
@p2.dynamic_profile.value_changes.should have_key("15/12".to_r)
|
11
|
+
it "should assign parameters given during construction" do
|
12
|
+
p = Part.new(Dynamics::PPP)
|
13
|
+
p.start_dynamic.should eq Dynamics::PPP
|
49
14
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
describe '#append!' do
|
56
|
-
it 'should add other notes to current array' do
|
57
|
-
p1 = Part.new(notes: [Note::Eighth.new([C4])])
|
58
|
-
p2 = Part.new(notes: [Note::Eighth.new([E4])])
|
59
|
-
p1.append! p2
|
60
|
-
p1.notes.size.should be 2
|
61
|
-
p1.notes[0].pitches[0].should eq C4
|
62
|
-
p1.notes[1].pitches[0].should eq E4
|
63
|
-
end
|
64
|
-
|
65
|
-
it 'should add start dynamic from given part as immediate dynamic change' do
|
66
|
-
p1 = Part.new(notes: [Note::Eighth.new])
|
67
|
-
p2 = Part.new(notes: [Note::Eighth.new], dynamic_profile: Profile.new(Dynamics::PPP))
|
68
|
-
p1.append! p2
|
69
|
-
p1.dynamic_profile.value_changes.size.should eq 1
|
70
|
-
p1.dynamic_profile.value_changes[Rational(1,8)].should be_a Change::Immediate
|
71
|
-
p1.dynamic_profile.value_changes[Rational(1,8)].value.should eq Dynamics::PPP
|
72
|
-
end
|
73
|
-
|
74
|
-
it 'should add shifted dynamic changes from given part' do
|
75
|
-
p1 = Part.new(notes: [Note::Whole.new])
|
76
|
-
p2 = Part.new(
|
77
|
-
notes: [Note::Whole.new],
|
78
|
-
dynamic_profile: Profile.new(
|
79
|
-
Dynamics::PPP,
|
80
|
-
Rational(1,8) => Change::Gradual.new(Dynamics::PP,Rational(1,8)),
|
81
|
-
Rational(3,8) => Change::Immediate.new(Dynamics::P)
|
82
|
-
)
|
83
|
-
)
|
84
|
-
p1.append! p2
|
85
|
-
p1.dynamic_profile.value_changes.size.should eq 3
|
86
|
-
p1.dynamic_profile.value_changes[Rational(1,1)].value.should eq Dynamics::PPP
|
87
|
-
p1.dynamic_profile.value_changes[Rational(9,8)].value.should eq Dynamics::PP
|
88
|
-
p1.dynamic_profile.value_changes[Rational(11,8)].value.should eq Dynamics::P
|
15
|
+
notes = [Note::Whole.new([A2]), Note::Half.new]
|
16
|
+
dcs = { "1/2".to_r => Change::Immediate.new(Dynamics::P), 1 => Change::Gradual.new(Dynamics::MF,1) }
|
17
|
+
p = Part.new(Dynamics::FF, notes: notes, dynamic_changes: dcs)
|
18
|
+
p.notes.should eq notes
|
19
|
+
p.dynamic_changes.should eq dcs
|
89
20
|
end
|
90
21
|
end
|
91
22
|
end
|
data/spec/score_spec.rb
CHANGED
@@ -1,22 +1,34 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
2
|
|
3
3
|
describe Score do
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
4
|
+
describe '#initialize' do
|
5
|
+
it 'should use empty containers for parameters not given' do
|
6
|
+
s = Score.new(Meter.new(4,4),120)
|
7
|
+
s.parts.should be_empty
|
8
|
+
s.program.segments.should be_empty
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'should assign given parameters' do
|
12
|
+
m = Meter.new(4,"1/4".to_r)
|
13
|
+
s = Score.new(m,120)
|
14
|
+
s.start_meter.should eq m
|
15
|
+
s.start_tempo.should eq 120
|
16
|
+
|
17
|
+
parts = { "piano (LH)" => Samples::SAMPLE_PART }
|
18
|
+
program = Program.new [0...0.75, 0...0.75]
|
19
|
+
mcs = { 1 => Change::Immediate.new(Meter.new(3,"1/4".to_r)) }
|
20
|
+
tcs = { 1 => Change::Immediate.new(100) }
|
21
|
+
|
22
|
+
s = Score.new(m,120,
|
23
|
+
parts: parts,
|
24
|
+
program: program,
|
25
|
+
meter_changes: mcs,
|
26
|
+
tempo_changes: tcs
|
27
|
+
)
|
28
|
+
s.parts.should eq parts
|
29
|
+
s.program.should eq program
|
30
|
+
s.meter_changes.should eq mcs
|
31
|
+
s.tempo_changes.should eq tcs
|
14
32
|
end
|
15
|
-
|
16
|
-
it "should assign tempo profile given during construction" do
|
17
|
-
profile = Profile.new(Tempo.new(200), 0.5 => Change::Gradual.new(Tempo.new(120),0.5) )
|
18
|
-
score = Score.new tempo_profile: profile
|
19
|
-
score.tempo_profile.should eq(profile)
|
20
|
-
end
|
21
33
|
end
|
22
34
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -6,7 +6,12 @@ include Music::Transcription::Pitches
|
|
6
6
|
|
7
7
|
class Samples
|
8
8
|
SAMPLE_PART = Part.new(
|
9
|
-
|
10
|
-
|
9
|
+
Dynamics::P,
|
10
|
+
notes: [
|
11
|
+
Note::Quarter.new([ C1, D1 ]),
|
12
|
+
Note::Quarter.new([ C2, D2 ]),
|
13
|
+
Note::Whole.new([ C3, D3 ])
|
14
|
+
],
|
15
|
+
dynamic_changes: {1.0 => Change::Immediate.new(Dynamics::MP)}
|
11
16
|
)
|
12
17
|
end
|