music-transcription 0.11.0 → 0.13.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Rakefile +1 -1
- data/examples/{make_hip.rb → hip.rb} +8 -2
- data/examples/hip_packed.yml +22 -0
- data/examples/{make_missed_connection.rb → missed_connection.rb} +8 -2
- data/examples/missed_connection.yml +2 -2
- data/examples/missed_connection_packed.yml +14 -0
- data/examples/{make_song1.rb → song1.rb} +7 -1
- data/examples/song1_packed.yml +19 -0
- data/examples/{make_song2.rb → song2.rb} +6 -1
- data/examples/song2_packed.yml +21 -0
- data/lib/music-transcription.rb +11 -3
- data/lib/music-transcription/model/link.rb +24 -6
- data/lib/music-transcription/model/meter.rb +10 -0
- data/lib/music-transcription/model/meters.rb +1 -1
- data/lib/music-transcription/model/note.rb +32 -0
- data/lib/music-transcription/model/pitch.rb +19 -0
- data/lib/music-transcription/packing/change_packing.rb +27 -0
- data/lib/music-transcription/packing/part_packing.rb +33 -0
- data/lib/music-transcription/packing/program_packing.rb +18 -0
- data/lib/music-transcription/packing/score_packing.rb +59 -0
- data/lib/music-transcription/parsing/convenience_methods.rb +10 -0
- data/lib/music-transcription/parsing/meter_parsing.rb +117 -9
- data/lib/music-transcription/parsing/meter_parsing.treetop +12 -0
- data/lib/music-transcription/parsing/note_node.rb +42 -0
- data/lib/music-transcription/parsing/note_parsing.rb +57 -180
- data/lib/music-transcription/parsing/note_parsing.treetop +2 -19
- data/lib/music-transcription/parsing/numbers/nonnegative_float_parsing.rb +178 -0
- data/lib/music-transcription/parsing/numbers/nonnegative_float_parsing.treetop +19 -0
- data/lib/music-transcription/parsing/{nonnegative_integer_parsing.rb → numbers/nonnegative_integer_parsing.rb} +9 -0
- data/lib/music-transcription/parsing/{nonnegative_integer_parsing.treetop → numbers/nonnegative_integer_parsing.treetop} +7 -1
- data/lib/music-transcription/parsing/numbers/nonnegative_rational_parsing.rb +88 -0
- data/lib/music-transcription/parsing/numbers/nonnegative_rational_parsing.treetop +22 -0
- data/lib/music-transcription/parsing/{positive_integer_parsing.rb → numbers/positive_integer_parsing.rb} +0 -0
- data/lib/music-transcription/parsing/{positive_integer_parsing.treetop → numbers/positive_integer_parsing.treetop} +0 -0
- data/lib/music-transcription/parsing/segment_parsing.rb +143 -0
- data/lib/music-transcription/parsing/segment_parsing.treetop +25 -0
- data/lib/music-transcription/version.rb +1 -1
- data/spec/model/link_spec.rb +44 -60
- data/spec/model/meter_spec.rb +18 -0
- data/spec/model/note_spec.rb +39 -0
- data/spec/model/pitch_spec.rb +32 -0
- data/spec/packing/change_packing_spec.rb +91 -0
- data/spec/packing/part_packing_spec.rb +66 -0
- data/spec/packing/program_packing_spec.rb +33 -0
- data/spec/packing/score_packing_spec.rb +122 -0
- data/spec/parsing/meter_parsing_spec.rb +2 -2
- data/spec/parsing/note_node_spec.rb +87 -0
- data/spec/parsing/note_parsing_spec.rb +3 -0
- data/spec/parsing/numbers/nonnegative_float_spec.rb +11 -0
- data/spec/parsing/{nonnegative_integer_spec.rb → numbers/nonnegative_integer_spec.rb} +1 -1
- data/spec/parsing/numbers/nonnegative_rational_spec.rb +11 -0
- data/spec/parsing/{positive_integer_spec.rb → numbers/positive_integer_spec.rb} +1 -1
- data/spec/parsing/segment_parsing_spec.rb +27 -0
- metadata +45 -17
- data/lib/music-transcription/parsing/note_nodes.rb +0 -64
- data/spec/parsing/note_nodes_spec.rb +0 -84
@@ -0,0 +1,25 @@
|
|
1
|
+
module Music
|
2
|
+
module Transcription
|
3
|
+
module Parsing
|
4
|
+
|
5
|
+
grammar Segment
|
6
|
+
include NonnegativeInteger
|
7
|
+
include NonnegativeFloat
|
8
|
+
include NonnegativeRational
|
9
|
+
|
10
|
+
rule range
|
11
|
+
first:nonnegative_number ([.] 2..3) last:nonnegative_number {
|
12
|
+
def to_range
|
13
|
+
first.to_num...last.to_num
|
14
|
+
end
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
rule nonnegative_number
|
19
|
+
nonnegative_float / nonnegative_rational / nonnegative_integer
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/spec/model/link_spec.rb
CHANGED
@@ -1,73 +1,51 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
3
|
+
{
|
4
|
+
Link::Glissando => Link::Portamento,
|
5
|
+
Link::Portamento => Link::Glissando,
|
6
|
+
Link::Slur => Link::Legato,
|
7
|
+
Link::Legato => Link::Slur
|
8
|
+
}.each do |klass,klass2|
|
9
|
+
describe klass do
|
10
|
+
describe '#initialize' do
|
11
|
+
it 'should assign the given pitch to :target_pitch' do
|
12
|
+
klass.new(C2).target_pitch.should eq(C2)
|
13
|
+
end
|
13
14
|
end
|
14
15
|
|
15
|
-
|
16
|
-
|
16
|
+
describe '#==' do
|
17
|
+
it 'should return true if two links have the same target pitch' do
|
18
|
+
klass.new(C2).should eq(klass.new(C2))
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should return false if two links do not have the same target pitch' do
|
22
|
+
klass.new(C2).should_not eq(klass.new(F5))
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should return false if the link type is different' do
|
26
|
+
klass.new(C2).should_not eq(klass2.new(C2))
|
27
|
+
end
|
17
28
|
end
|
18
29
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
describe '#clone' do
|
25
|
-
it 'should return a link equal to original' do
|
26
|
-
l = Link::Glissando.new(C4)
|
27
|
-
l.clone.should eq l
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
describe '#to_yaml' do
|
32
|
-
it 'should produce YAML that can be loaded' do
|
33
|
-
l = Link::Glissando.new(C5)
|
34
|
-
YAML.load(l.to_yaml).should eq l
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
describe Link::Portamento do
|
40
|
-
describe '#initialize' do
|
41
|
-
it 'should assign the given pitch to :target_pitch' do
|
42
|
-
Link::Portamento.new(C2).target_pitch.should eq(C2)
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
describe '#==' do
|
47
|
-
it 'should return true if two links have the same target pitch' do
|
48
|
-
Link::Portamento.new(C2).should eq(Link::Portamento.new(C2))
|
30
|
+
describe '#clone' do
|
31
|
+
it 'should return a link equal to original' do
|
32
|
+
l = klass.new(C4)
|
33
|
+
l.clone.should eq l
|
34
|
+
end
|
49
35
|
end
|
50
36
|
|
51
|
-
|
52
|
-
|
37
|
+
describe '#to_yaml' do
|
38
|
+
it 'should produce YAML that can be loaded' do
|
39
|
+
l = klass.new(C5)
|
40
|
+
YAML.load(l.to_yaml).should eq l
|
41
|
+
end
|
53
42
|
end
|
54
43
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
describe '#clone' do
|
61
|
-
it 'should return a link equal to original' do
|
62
|
-
l = Link::Portamento.new(C4)
|
63
|
-
l.clone.should eq l
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
describe '#to_yaml' do
|
68
|
-
it 'should produce YAML that can be loaded' do
|
69
|
-
l = Link::Portamento.new(C5)
|
70
|
-
YAML.load(l.to_yaml).should eq l
|
44
|
+
describe '#to_s' do
|
45
|
+
it 'should produce string that include link char and target pitch str' do
|
46
|
+
l = klass.new(C3)
|
47
|
+
l.to_s.should eq(l.link_char + "C3")
|
48
|
+
end
|
71
49
|
end
|
72
50
|
end
|
73
51
|
end
|
@@ -96,4 +74,10 @@ describe Link::Tie do
|
|
96
74
|
YAML.load(l.to_yaml).should eq l
|
97
75
|
end
|
98
76
|
end
|
77
|
+
|
78
|
+
describe '#to_s' do
|
79
|
+
it 'should return =' do
|
80
|
+
Link::Tie.new.to_s.should eq("=")
|
81
|
+
end
|
82
|
+
end
|
99
83
|
end
|
data/spec/model/meter_spec.rb
CHANGED
@@ -48,6 +48,24 @@ describe Meter do
|
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
51
|
+
describe '#to_s' do
|
52
|
+
context 'beat duration with 1 in denominator' do
|
53
|
+
it 'should return string of fraction: beats_per_measure / beat_duration.denom' do
|
54
|
+
FOUR_FOUR.to_s.should eq("4/4")
|
55
|
+
TWO_FOUR.to_s.should eq("2/4")
|
56
|
+
THREE_FOUR.to_s.should eq("3/4")
|
57
|
+
TWO_TWO.to_s.should eq("2/2")
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context 'beat duration with >1 in denominator' do
|
62
|
+
it 'should return beats_per_measure * beat_dur fraction' do
|
63
|
+
SIX_EIGHT.to_s.should eq("2*3/8")
|
64
|
+
Meter.new(3,"3/8".to_r).to_s.should eq("3*3/8")
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
51
69
|
describe '#valid?' do
|
52
70
|
{
|
53
71
|
'4/4 meter' => [4,'1/4'.to_r],
|
data/spec/model/note_spec.rb
CHANGED
@@ -122,6 +122,45 @@ describe Note do
|
|
122
122
|
end
|
123
123
|
end
|
124
124
|
|
125
|
+
describe '#to_s' do
|
126
|
+
before :all do
|
127
|
+
@note_parser = Parsing::NoteParser.new
|
128
|
+
end
|
129
|
+
|
130
|
+
context
|
131
|
+
it 'should produce string that when parsed produces an equal note' do
|
132
|
+
durations = ["1/8".to_r,"1".to_r,"5/3".to_r]
|
133
|
+
include Articulations
|
134
|
+
articulations = [NORMAL, SLUR, LEGATO, TENUTO, PORTATO, STACCATO, STACCATISSIMO ]
|
135
|
+
pitches_links_sets = [
|
136
|
+
[[],{}],
|
137
|
+
[[C2],{}],
|
138
|
+
[[A5,D6],{ A5 => Link::Tie.new }],
|
139
|
+
[[C5,E6,Gb2],{ C5 => Link::Glissando.new(D5) }],
|
140
|
+
[[A5,D6],{ A5 => Link::Legato.new(B5), D6 => Link::Slur.new(E6) }],
|
141
|
+
[[C5,E6,Gb2],{ C5 => Link::Portamento.new(D5), Gb2 => Link::Tie.new }],
|
142
|
+
]
|
143
|
+
|
144
|
+
notes = []
|
145
|
+
durations.each do |d|
|
146
|
+
articulations.each do |art|
|
147
|
+
pitches_links_sets.each do |pitches_links_set|
|
148
|
+
pitches,links = pitches_links_set
|
149
|
+
[true,false].each do |acc|
|
150
|
+
notes.push Note.new(d, pitches, articulation: art, links: links, accented: acc)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
notes.each do |note|
|
157
|
+
str = note.to_s
|
158
|
+
note2 = @note_parser.parse(str).to_note
|
159
|
+
note2.should eq(note)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
125
164
|
describe '#to_yaml' do
|
126
165
|
it 'should produce YAML that can be loaded' do
|
127
166
|
n = Note.new(1,[C2])
|
data/spec/model/pitch_spec.rb
CHANGED
@@ -108,6 +108,38 @@ describe Pitch do
|
|
108
108
|
end
|
109
109
|
end
|
110
110
|
|
111
|
+
describe '#to_s' do
|
112
|
+
context 'on-letter semitones' do
|
113
|
+
it 'should return the semitone letter + octave number' do
|
114
|
+
{ C0 => "C0", D1 => "D1", E7 => "E7",
|
115
|
+
F8 => "F8", G3 => "G3", A4 => "A4",
|
116
|
+
B5 => "B5", C2 => "C2" }.each do |p,s|
|
117
|
+
p.to_s.should eq s
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
context 'off-letter semitones' do
|
123
|
+
context 'sharpit set false' do
|
124
|
+
it 'should return semitone letter + "b" + octave number' do
|
125
|
+
{ Db0 => "Db0", Eb1 => "Eb1", Gb7 => "Gb7",
|
126
|
+
Ab4 => "Ab4", Bb1 => "Bb1" }.each do |p,s|
|
127
|
+
p.to_s(false).should eq s
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
context 'sharpit set true' do
|
133
|
+
it 'should return semitone letter + "#" + octave number' do
|
134
|
+
{ Db0 => "C#0", Eb1 => "D#1", Gb7 => "F#7",
|
135
|
+
Ab4 => "G#4", Bb1 => "A#1" }.each do |p,s|
|
136
|
+
p.to_s(true).should eq s
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
111
143
|
describe '.make_from_freq' do
|
112
144
|
it 'should make a pitch whose freq is approximately the given freq' do
|
113
145
|
[16.35, 440.0, 987.77].each do |given_freq|
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe Change::Immediate do
|
4
|
+
describe '#pack' do
|
5
|
+
before :all do
|
6
|
+
@c = Change::Immediate.new(0.5)
|
7
|
+
@a = @c.pack
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'should return an Array' do
|
11
|
+
@a.should be_a Array
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should return an Array of size 1' do
|
15
|
+
@a.size.should eq 1
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'should put the change value at index 0' do
|
19
|
+
@a[0].should eq @c.value
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe Change::Gradual do
|
25
|
+
describe '#pack' do
|
26
|
+
before :all do
|
27
|
+
@c = Change::Gradual.new(0.3,1.5)
|
28
|
+
@a = @c.pack
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should return an Array' do
|
32
|
+
@a.should be_a Array
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should return an Array of size 1' do
|
36
|
+
@a.size.should eq 2
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should put the change value at index 0' do
|
40
|
+
@a[0].should eq @c.value
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should put the duration at index 1' do
|
44
|
+
@a[1].should eq @c.duration
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe Change do
|
50
|
+
describe '.unpack' do
|
51
|
+
context 'given a packed immediate change' do
|
52
|
+
before :all do
|
53
|
+
@c = Change::Immediate.new(0.5)
|
54
|
+
@a = @c.pack
|
55
|
+
@c2 = Change.unpack(@a)
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should return a Change::Immediate' do
|
59
|
+
@c2.should be_a Change::Immediate
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'should successfully unpack the change value' do
|
63
|
+
@c2.value.should eq @c.value
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'should successfully unpack the change duration' do
|
67
|
+
@c2.duration.should eq @c.duration
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context 'given a packed gradual change' do
|
72
|
+
before :all do
|
73
|
+
@c = Change::Gradual.new(0.3,1.5)
|
74
|
+
@a = @c.pack
|
75
|
+
@c2 = Change.unpack(@a)
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'should return a Change::Gradual' do
|
79
|
+
@c2.should be_a Change::Gradual
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'should successfully unpack the change value' do
|
83
|
+
@c2.value.should eq @c.value
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'should successfully unpack the change duration' do
|
87
|
+
@c2.duration.should eq @c.duration
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe Part do
|
4
|
+
before :all do
|
5
|
+
@p = Part.new(
|
6
|
+
Dynamics::MP,
|
7
|
+
notes: Parsing::notes("/4Bb2 /8 /8F3= /2F3 /4Bb2 /8 /8F3= /2F3"),
|
8
|
+
dynamic_changes: {
|
9
|
+
1 => Change::Immediate.new(Dynamics::PP),
|
10
|
+
2 => Change::Gradual.new(Dynamics::FF, 2.0)
|
11
|
+
}
|
12
|
+
)
|
13
|
+
|
14
|
+
@h = @p.pack
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#pack' do
|
18
|
+
it 'should produce a hash' do
|
19
|
+
@h.should be_a Hash
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should return a hash with keys: "notes", "start_dynamic", and "dynamic_changes"' do
|
23
|
+
@h.keys.should include("notes")
|
24
|
+
@h.keys.should include("start_dynamic")
|
25
|
+
@h.keys.should include("dynamic_changes")
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should pack notes into a string' do
|
29
|
+
@h['notes'].should be_a String
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should pack start dynamic as plain numeric value' do
|
33
|
+
@h['start_dynamic'].should be_a Numeric
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'should pack dynamic changes as whatver type Change#pack returns' do
|
37
|
+
@h['dynamic_changes'].each do |offset,packed_v|
|
38
|
+
change_v = @p.dynamic_changes[offset]
|
39
|
+
t = change_v.pack.class
|
40
|
+
packed_v.should be_a t
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe '.unpack' do
|
46
|
+
before :all do
|
47
|
+
@p2 = Part.unpack @h
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should return a Part' do
|
51
|
+
@p2.should be_a Part
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'should successfuly unpack the notes' do
|
55
|
+
@p2.notes.should eq @p.notes
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should successfuly unpack the start dynamic' do
|
59
|
+
@p2.start_dynamic.should eq @p.start_dynamic
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'should successfuly unpack the dynamic changes' do
|
63
|
+
@p2.dynamic_changes.should eq @p.dynamic_changes
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe Program do
|
4
|
+
before :all do
|
5
|
+
@p = Program.new([0...5,3.0...6.5,("1/2".to_r)...("3/2".to_r)])
|
6
|
+
@a = @p.pack
|
7
|
+
@p2 = Program.unpack(@a)
|
8
|
+
end
|
9
|
+
|
10
|
+
describe '#pack' do
|
11
|
+
it 'should return an Array' do
|
12
|
+
@a.should be_a Array
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should return an array with same size as # of segments' do
|
16
|
+
@a.size.should eq @p.segments.size
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should return an array of strings' do
|
20
|
+
@a.each {|x| x.should be_a String }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '#unpack' do
|
25
|
+
it 'should return a Program' do
|
26
|
+
@p2.should be_a Program
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should successfully unpack program segments' do
|
30
|
+
@p2.segments.should eq @p.segments
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|