music-transcription 0.15.0 → 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -116,12 +116,12 @@ module Articulation
116
116
  return cached
117
117
  end
118
118
 
119
- if (match_len = has_terminal?("-", false, index))
119
+ if (match_len = has_terminal?("|", false, index))
120
120
  r0 = instantiate_node(SyntaxNode,input, index...(index + match_len))
121
121
  r0.extend(Legato0)
122
122
  @index += match_len
123
123
  else
124
- terminal_parse_failure("-")
124
+ terminal_parse_failure("|")
125
125
  r0 = nil
126
126
  end
127
127
 
@@ -16,7 +16,7 @@ grammar Articulation
16
16
  end
17
17
 
18
18
  rule legato
19
- "-" {
19
+ "|" {
20
20
  def to_articulation
21
21
  Music::Transcription::Articulations::LEGATO
22
22
  end
@@ -149,11 +149,11 @@ module Link
149
149
  end
150
150
 
151
151
  i0, s0 = index, []
152
- if (match_len = has_terminal?("-", false, index))
152
+ if (match_len = has_terminal?("|", false, index))
153
153
  r1 = true
154
154
  @index += match_len
155
155
  else
156
- terminal_parse_failure("-")
156
+ terminal_parse_failure("|")
157
157
  r1 = nil
158
158
  end
159
159
  s0 << r1
@@ -18,7 +18,7 @@ grammar Link
18
18
  end
19
19
 
20
20
  rule legato_link
21
- "-" target:pitch <LegatoNode>
21
+ "|" target:pitch <LegatoNode>
22
22
  end
23
23
 
24
24
  rule glissando
@@ -11,8 +11,13 @@ module Parsing
11
11
  when "b" then -1
12
12
  end
13
13
  end
14
- oct = octn.text_value.to_i
15
- Music::Transcription::Pitch.new(semitone: sem, octave: oct)
14
+ oct = octave.to_i
15
+ ncents = 0
16
+ unless cents.empty?
17
+ ncents = cents.to_i
18
+ end
19
+
20
+ Music::Transcription::Pitch.new(semitone: sem, octave: oct, cent: ncents)
16
21
  end
17
22
  end
18
23
  end
@@ -12,6 +12,8 @@ module Pitch
12
12
  @root ||= :pitch
13
13
  end
14
14
 
15
+ include NonnegativeInteger
16
+
15
17
  module Pitch0
16
18
  def pitch_letter
17
19
  elements[0]
@@ -21,9 +23,13 @@ module Pitch
21
23
  elements[1]
22
24
  end
23
25
 
24
- def octn
26
+ def octave
25
27
  elements[2]
26
28
  end
29
+
30
+ def cents
31
+ elements[3]
32
+ end
27
33
  end
28
34
 
29
35
  def _nt_pitch
@@ -55,14 +61,17 @@ module Pitch
55
61
  end
56
62
  s0 << r2
57
63
  if r2
58
- if has_terminal?(@regexps[gr = '\A[0-9]'] ||= Regexp.new(gr), :regexp, index)
59
- r4 = true
60
- @index += 1
61
- else
62
- terminal_parse_failure('[0-9]')
63
- r4 = nil
64
- end
64
+ r4 = _nt_octave
65
65
  s0 << r4
66
+ if r4
67
+ r6 = _nt_cent
68
+ if r6
69
+ r5 = r6
70
+ else
71
+ r5 = instantiate_node(SyntaxNode,input, index...index)
72
+ end
73
+ s0 << r5
74
+ end
66
75
  end
67
76
  end
68
77
  if s0.last
@@ -78,6 +87,92 @@ module Pitch
78
87
  r0
79
88
  end
80
89
 
90
+ module Octave0
91
+ def n
92
+ elements[0]
93
+ end
94
+ end
95
+
96
+ module Octave1
97
+ def to_i; n.to_i; end
98
+ end
99
+
100
+ def _nt_octave
101
+ start_index = index
102
+ if node_cache[:octave].has_key?(index)
103
+ cached = node_cache[:octave][index]
104
+ if cached
105
+ node_cache[:octave][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
106
+ @index = cached.interval.end
107
+ end
108
+ return cached
109
+ end
110
+
111
+ i0, s0 = index, []
112
+ r1 = _nt_nonnegative_integer
113
+ s0 << r1
114
+ if s0.last
115
+ r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
116
+ r0.extend(Octave0)
117
+ r0.extend(Octave1)
118
+ else
119
+ @index = i0
120
+ r0 = nil
121
+ end
122
+
123
+ node_cache[:octave][start_index] = r0
124
+
125
+ r0
126
+ end
127
+
128
+ module Cent0
129
+ def n
130
+ elements[1]
131
+ end
132
+ end
133
+
134
+ module Cent1
135
+ def to_i; text_value.to_i; end
136
+ end
137
+
138
+ def _nt_cent
139
+ start_index = index
140
+ if node_cache[:cent].has_key?(index)
141
+ cached = node_cache[:cent][index]
142
+ if cached
143
+ node_cache[:cent][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
144
+ @index = cached.interval.end
145
+ end
146
+ return cached
147
+ end
148
+
149
+ i0, s0 = index, []
150
+ if has_terminal?(@regexps[gr = '\A[+-]'] ||= Regexp.new(gr), :regexp, index)
151
+ r1 = true
152
+ @index += 1
153
+ else
154
+ terminal_parse_failure('[+-]')
155
+ r1 = nil
156
+ end
157
+ s0 << r1
158
+ if r1
159
+ r2 = _nt_nonnegative_integer
160
+ s0 << r2
161
+ end
162
+ if s0.last
163
+ r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
164
+ r0.extend(Cent0)
165
+ r0.extend(Cent1)
166
+ else
167
+ @index = i0
168
+ r0 = nil
169
+ end
170
+
171
+ node_cache[:cent][start_index] = r0
172
+
173
+ r0
174
+ end
175
+
81
176
  def _nt_pitch_letter
82
177
  start_index = index
83
178
  if node_cache[:pitch_letter].has_key?(index)
@@ -3,8 +3,17 @@ module Transcription
3
3
  module Parsing
4
4
 
5
5
  grammar Pitch
6
+ include NonnegativeInteger
6
7
  rule pitch
7
- pitch_letter mod:[#b]? octn:[0-9] <PitchNode>
8
+ pitch_letter mod:[#b]? octave cents:cent? <PitchNode>
9
+ end
10
+
11
+ rule octave
12
+ n:nonnegative_integer { def to_i; n.to_i; end }
13
+ end
14
+
15
+ rule cent
16
+ [+-] n:nonnegative_integer { def to_i; text_value.to_i; end }
8
17
  end
9
18
 
10
19
  rule pitch_letter
@@ -2,6 +2,6 @@
2
2
  module Music
3
3
  module Transcription
4
4
  # music-transcription version
5
- VERSION = "0.15.0"
5
+ VERSION = "0.16.0"
6
6
  end
7
7
  end
@@ -138,7 +138,8 @@ describe Note do
138
138
 
139
139
  notes.each do |note|
140
140
  str = note.to_s
141
- note2 = @note_parser.parse(str).to_note
141
+ res = @note_parser.parse(str)
142
+ note2 = res.to_note
142
143
  note2.should eq(note)
143
144
  end
144
145
  end
@@ -5,15 +5,19 @@ describe Pitch do
5
5
  before :each do
6
6
  @cases =
7
7
  [
8
- { octave: 1, semitone: 0, :ratio => 2.0, :total_semitone => 12 },
9
- { octave: 2, semitone: 0, :ratio => 4.0, :total_semitone => 24 },
10
- { octave: 1, semitone: 6, :ratio => 2.828, :total_semitone => 18 },
11
- { octave: 2, semitone: 6, :ratio => 5.657, :total_semitone => 30 },
12
- { octave: 3, semitone: 7, :ratio => 11.986, :total_semitone => 43 },
13
- { octave: -1, semitone: 0, :ratio => 0.5, :total_semitone => -12 },
14
- { octave: -2, semitone: 0, :ratio => 0.25, :total_semitone => -24 },
15
- { octave: -2, semitone: 7, :ratio => 0.375, :total_semitone => -17 },
16
- { octave: -1, semitone: 9, :ratio => 0.841, :total_semitone => -3 },
8
+ { :octave => 1, :semitone => 0, :cent => 0, :ratio => 2.0, :total_cents => 1200 },
9
+ { :octave => 2, :semitone => 0, :cent => 0, :ratio => 4.0, :total_cents => 2400 },
10
+ { :octave => 1, :semitone => 6, :cent => 0, :ratio => 2.828, :total_cents => 1800 },
11
+ { :octave => 2, :semitone => 6, :cent => 0, :ratio => 5.657, :total_cents => 3000 },
12
+ { :octave => 3, :semitone => 7, :cent => 0, :ratio => 11.986, :total_cents => 4300 },
13
+ { :octave => -1, :semitone => 0, :cent => 0, :ratio => 0.5, :total_cents => -1200 },
14
+ { :octave => -2, :semitone => 0, :cent => 0, :ratio => 0.25, :total_cents => -2400 },
15
+ { :octave => -2, :semitone => 7, :cent => 0, :ratio => 0.37458, :total_cents => -1700 },
16
+ { :octave => -1, :semitone => 9, :cent => 0, :ratio => 0.841, :total_cents => -300 },
17
+ { :octave => 5, :semitone => 0, :cent => 20, :ratio => 32.372, :total_cents => 6020 },
18
+ { :octave => 3, :semitone => 3, :cent => 95, :ratio => 10.0503, :total_cents => 3995 },
19
+ { :octave => -3, :semitone => 2, :cent => -20, :ratio => 0.1387, :total_cents => -3420 },
20
+ { :octave => -5, :semitone => -2, :cent => -77, :ratio => 0.02663, :total_cents => -6277 }
17
21
  ]
18
22
  end
19
23
 
@@ -22,30 +26,38 @@ describe Pitch do
22
26
  end
23
27
 
24
28
  it "should take keyword args" do
25
- obj = Pitch.new octave: 4, semitone: 3
29
+ obj = Pitch.new octave: 4, semitone: 3, cent: 5
26
30
  obj.octave.should eq(4)
27
31
  obj.semitone.should eq(3)
32
+ obj.cent.should eq(5)
28
33
  end
29
34
 
30
- it "should use default octave and semitone if none is given" do
35
+ it "should use default octave, semitone, and cent if none is given" do
31
36
  p = Pitch.new
32
37
  p.ratio.should be_within(0.01).of(1.0)
33
- p.total_semitone.should eq(0)
38
+ p.total_cents.should eq(0)
34
39
  end
35
40
 
36
41
  it "should use the octave and semitone given during construction" do
37
42
  @cases.each do |case_data|
38
- p = Pitch.new octave: case_data[:octave], semitone: case_data[:semitone]
43
+ p = Pitch.new octave: case_data[:octave], semitone: case_data[:semitone], cent: case_data[:cent]
39
44
  p.ratio.should be_within(0.01).of case_data[:ratio]
40
- p.total_semitone.should be case_data[:total_semitone]
45
+ p.total_cents.should be case_data[:total_cents]
41
46
  end
42
47
  end
43
48
 
44
49
  describe '#diff' do
45
50
  it 'should return the difference between the given pitch, in semitones' do
46
- C5.diff(C4).should eq(12)
47
- C5.diff(D5).should eq(-2)
48
- D5.diff(C5).should eq(2)
51
+ [
52
+ [C5,C4,12],
53
+ [C5,D5,-2],
54
+ [D5,C5,2],
55
+ [C5,Pitch.new(octave:5, cent:-5),5/100.0],
56
+ [A5,Pitch.new(octave:5, semitone: 5, cent: 22),378/100.0],
57
+ [A5,Pitch.new(octave:5, semitone: 11, cent: 85),-285/100.0],
58
+ ].each do |a,b,c|
59
+ a.diff(b).should eq(c)
60
+ end
49
61
  end
50
62
  end
51
63
 
@@ -62,22 +74,7 @@ describe Pitch do
62
74
  it 'should return a Pitch with given ratio' do
63
75
  @cases.each do |case_data|
64
76
  p = Pitch.from_ratio case_data[:ratio]
65
-
66
- p.octave.should eq case_data[:octave]
67
- p.semitone.should eq case_data[:semitone]
68
- p.total_semitone.should eq case_data[:total_semitone]
69
- end
70
- end
71
- end
72
-
73
- describe '.from_semitones' do
74
- it 'should return a Pitch with given total semitones' do
75
- @cases.each do |case_data|
76
- p = Pitch.from_semitones case_data[:total_semitone]
77
-
78
- p.octave.should eq case_data[:octave]
79
- p.semitone.should eq case_data[:semitone]
80
- p.total_semitone.should eq case_data[:total_semitone]
77
+ p.total_cents.should eq case_data[:total_cents]
81
78
  end
82
79
  end
83
80
  end
@@ -146,9 +143,19 @@ describe Pitch do
146
143
  { Db0 => "C#0", Eb1 => "D#1", Gb7 => "F#7",
147
144
  Ab4 => "G#4", Bb1 => "A#1" }.each do |p,s|
148
145
  p.to_s(true).should eq s
149
- end
146
+ end
150
147
  end
151
148
  end
152
149
  end
150
+
151
+ context 'non-zero cent value' do
152
+ it 'should append +n (n = cent value)' do
153
+ { C0.transpose(0.01) => "C0+1", E1.transpose(0.15) => "E1+15",
154
+ G5.transpose(-0.55) => "Gb5+45"
155
+ }.each do |p,s|
156
+ p.to_s.should eq s
157
+ end
158
+ end
159
+ end
153
160
  end
154
161
  end
@@ -5,7 +5,7 @@ describe Parsing::ArticulationParser do
5
5
 
6
6
  {
7
7
  '=' => SLUR,
8
- '-' => LEGATO,
8
+ '|' => LEGATO,
9
9
  '_' => TENUTO,
10
10
  '%' => PORTATO,
11
11
  '.' => STACCATO,
@@ -7,7 +7,7 @@ describe Parsing::LinkNode do
7
7
  '=C4' => Link::Slur.new(C4),
8
8
  '/Db2' => Link::Portamento.new(Db2),
9
9
  '~C#2' => Link::Glissando.new(Db2),
10
- '-Db2' => Link::Legato.new(Db2),
10
+ '|Db2' => Link::Legato.new(Db2),
11
11
  }.each do |str,tgt|
12
12
  res = parser.parse(str)
13
13
  context str do
@@ -5,19 +5,9 @@ describe Parsing::LinkParser do
5
5
  @parser = Parsing::LinkParser.new
6
6
  end
7
7
 
8
- it 'should parse a "=C2"' do
9
- @parser.parse("=C2").should_not be nil
10
- end
11
-
12
- it 'should parse a "-C2"' do
13
- @parser.parse("-C2").should_not be nil
14
- end
15
-
16
- it 'should parse a "~C2"' do
17
- @parser.parse("~C2").should_not be nil
18
- end
19
-
20
- it 'should parse a "/C2"' do
21
- @parser.parse("/C2").should_not be nil
8
+ ["=C2","|C2","~C2","/C2"].each do |str|
9
+ it "should parse #{str}" do
10
+ @parser.should parse(str)
11
+ end
22
12
  end
23
13
  end
@@ -60,7 +60,7 @@ describe Parsing::NoteNode do
60
60
 
61
61
  context 'polyphonic note' do
62
62
  {
63
- '/2C2,D2,E2-F2' => Note.new(Rational(1,2),[C2,D2,E2],links:{E2=>Link::Legato.new(F2)}),
63
+ '/2C2,D2,E2|F2' => Note.new(Rational(1,2),[C2,D2,E2],links:{E2=>Link::Legato.new(F2)}),
64
64
  '4/2.D#6,G4' => Note.new(Rational(4,2),[Eb6,G4], articulation:STACCATO),
65
65
  '28_Eb7,D7,G7' => Note.new(Rational(28,1),[Eb7,D7,G7],articulation:TENUTO),
66
66
  '56/33B1,B2,B3,B4,B5!' => Note.new(Rational(56,33),[B1,B2,B3,B4,B5], accented: true),
@@ -12,17 +12,17 @@ describe Parsing::NoteParser do
12
12
  'duration + articulation + accent' => ['1/4.!','/2%!','2/3=!'],
13
13
  'single pitch' => ['/4C2','5/3Db3','/33E#8'],
14
14
  'multiple pitches' => ['/4C2,C3,c5','5/3Db3,Bb2,E5','/33E#8,F1,B9'],
15
- 'with articulation' => ['/4.C2',"5/3'Db3,Bb2,E5",'/33=F3','5-B2','/2_D3,F4'],
15
+ 'with articulation' => ['/4.C2',"5/3'Db3,Bb2,E5",'/33=F3','5|B2','/2_D3,F4'],
16
16
  'with accent' => ['/4C2!','3/2Db3,Bb4!'],
17
- 'with links' => ['/2C2=','/2C2=D2','/4D4-E4,G4~A5'],
17
+ 'with links' => ['/2C2=','/2C2=D2','/4D4|E4,G4~A5'],
18
18
  'with single pitch + articulation + link + accent' => [
19
- '3/4.D2=!','5/8=F2=!','/8Db4-Db5!','/3_G4~B4!'],
19
+ '3/4.D2=!','5/8=F2=!','/8Db4|Db5!','/3_G4~B4!'],
20
20
  'with multiple pitches + articulation + links + accent' => [
21
- '5/4.D2=,G4-A4,C3~D3!','5/8-F2=D4,B4/A4!'],
21
+ '5/4.D2=,G4|A4,C3~D3!','5/8|F2=D4,B4/A4!'],
22
22
  }
23
23
  invalid_cases = {
24
- 'single pitch' => ['/4C22','5/3Hb-3','/33E-2'],
25
- 'multiple pitches' => ['/4C20,C3,c5','5/3Db3,Bb-1,E5','/33H8,F1,B9'],
24
+ 'single pitch' => ['5/3Hb|3','/33E|2'],
25
+ 'multiple pitches' => ['5/3Db3,Bb|1,E5','/33H8,F1,B9'],
26
26
  'with articulation' => ['/4[C2',"5/3>Db3"],
27
27
  'with accent' => ['/4C2['],
28
28
  'with links' => ['/2C2)'],