music-transcription 0.7.3 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/music-transcription.rb +2 -3
- data/lib/music-transcription/articulations.rb +16 -0
- data/lib/music-transcription/change.rb +4 -4
- data/lib/music-transcription/errors.rb +6 -6
- data/lib/music-transcription/link.rb +1 -3
- data/lib/music-transcription/meter.rb +11 -3
- data/lib/music-transcription/note.rb +23 -59
- data/lib/music-transcription/part.rb +0 -8
- data/lib/music-transcription/pitch.rb +10 -12
- data/lib/music-transcription/program.rb +2 -2
- data/lib/music-transcription/score.rb +5 -21
- data/lib/music-transcription/version.rb +1 -1
- data/spec/link_spec.rb +10 -1
- data/spec/note_spec.rb +26 -16
- data/spec/part_spec.rb +6 -6
- data/spec/spec_helper.rb +4 -3
- metadata +3 -4
- data/lib/music-transcription/accent.rb +0 -25
- data/lib/music-transcription/accents.rb +0 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2821d70c7615da2fa7bb4e67bd8920faeeeffb53
|
4
|
+
data.tar.gz: 6dc0c2d04dcc3ea530f91a182d3978d6acee9176
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a8fb5105567403e4e8de4708fab50c55db5bb5a2b47a3d2de5a0463a4d9017185e58ed424ab19c1112099c738994aa6962f8c4bbc68cdd4f7cac9181f29209c1
|
7
|
+
data.tar.gz: 69675371f0024504bdf343810cea94f0fb818ac4a348347b67ea6a2dd3e875dd8bdbb665314e02a3aab4725f27064685b002d4f2d9e6cb1215c2f99be5b8254a
|
data/lib/music-transcription.rb
CHANGED
@@ -7,11 +7,10 @@ require 'music-transcription/errors'
|
|
7
7
|
require 'music-transcription/pitch'
|
8
8
|
require 'music-transcription/pitches'
|
9
9
|
require 'music-transcription/link'
|
10
|
-
require 'music-transcription/
|
11
|
-
require 'music-transcription/accents'
|
12
|
-
require 'music-transcription/change'
|
10
|
+
require 'music-transcription/articulations'
|
13
11
|
require 'music-transcription/note'
|
14
12
|
require 'music-transcription/dynamics'
|
13
|
+
require 'music-transcription/change'
|
15
14
|
require 'music-transcription/part'
|
16
15
|
require 'music-transcription/program'
|
17
16
|
require 'music-transcription/tempo'
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Music
|
2
|
+
module Transcription
|
3
|
+
|
4
|
+
module Articulations
|
5
|
+
NORMAL = :normal
|
6
|
+
STACCATO = :staccato
|
7
|
+
STACCATISSIMO = :staccatissimo
|
8
|
+
TENUTO = :tenuto
|
9
|
+
ACCENTED = :accented
|
10
|
+
HAMMERED = :hammered
|
11
|
+
ACCENTED_TENUTO = :accented_tenuto
|
12
|
+
ACCENTED_STACCATISSIMO = :accented_staccatissimo
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
@@ -25,7 +25,7 @@ class Change
|
|
25
25
|
|
26
26
|
def ensure_zero_duration
|
27
27
|
unless @duration == 0
|
28
|
-
raise
|
28
|
+
raise NonZeroError, "immediate change duration #{self.duration} must be 0"
|
29
29
|
end
|
30
30
|
end
|
31
31
|
end
|
@@ -34,13 +34,13 @@ class Change
|
|
34
34
|
include Validatable
|
35
35
|
|
36
36
|
def initialize value, transition_duration
|
37
|
-
@check_methods = [ :
|
37
|
+
@check_methods = [ :ensure_nonnegative_duration ]
|
38
38
|
super(value, transition_duration)
|
39
39
|
end
|
40
40
|
|
41
|
-
def
|
41
|
+
def ensure_nonnegative_duration
|
42
42
|
if @duration < 0
|
43
|
-
raise
|
43
|
+
raise NegativeError, "gradual change duration #{self.duration} must be non-negative"
|
44
44
|
end
|
45
45
|
end
|
46
46
|
end
|
@@ -1,10 +1,10 @@
|
|
1
1
|
module Music
|
2
2
|
module Transcription
|
3
|
-
class
|
4
|
-
class
|
5
|
-
class
|
6
|
-
class
|
7
|
-
class
|
8
|
-
class
|
3
|
+
class NonZeroError < StandardError; end
|
4
|
+
class NegativeError < StandardError; end
|
5
|
+
class NonPositiveError < StandardError; end
|
6
|
+
class NonIntegerError < StandardError; end
|
7
|
+
class NonRationalError < StandardError; end
|
8
|
+
class NonIncreasingError < StandardError; end
|
9
9
|
end
|
10
10
|
end
|
@@ -1,9 +1,7 @@
|
|
1
1
|
module Music
|
2
2
|
module Transcription
|
3
3
|
|
4
|
-
#
|
5
|
-
#
|
6
|
-
# @author James Tunnell
|
4
|
+
# Connect one note pitch to the target pitch of the next note, via slur, legato, etc.
|
7
5
|
#
|
8
6
|
# @!attribute [rw] target_pitch
|
9
7
|
# @return [Pitch] The pitch of the note which is being connected to.
|
@@ -14,14 +14,22 @@ class Meter
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def check_beats_per_measure
|
17
|
-
unless @beats_per_measure
|
18
|
-
raise
|
17
|
+
unless @beats_per_measure > 0
|
18
|
+
raise NonPositiveError, "beats per measure #{@beats_per_measure} is not positive"
|
19
|
+
end
|
20
|
+
|
21
|
+
unless @beats_per_measure.is_a?(Integer)
|
22
|
+
raise NonIntegerError, "beats per measure #{@beats_per_measure} is not an integer"
|
19
23
|
end
|
20
24
|
end
|
21
25
|
|
22
26
|
def check_beat_duration
|
23
27
|
unless @beat_duration > 0
|
24
|
-
raise
|
28
|
+
raise NonPositiveError, "beat duration #{@beat_duration} is not positive"
|
29
|
+
end
|
30
|
+
|
31
|
+
unless @beat_duration > 0
|
32
|
+
raise NonRationalError, "beat duration #{@beat_duration} is a rational"
|
25
33
|
end
|
26
34
|
end
|
27
35
|
|
@@ -7,21 +7,23 @@ class Note
|
|
7
7
|
include Validatable
|
8
8
|
|
9
9
|
attr_reader :pitches, :links
|
10
|
-
attr_accessor :
|
10
|
+
attr_accessor :articulation, :duration
|
11
11
|
|
12
|
-
|
12
|
+
DEFAULT_ARTICULATION = Articulations::NORMAL
|
13
|
+
|
14
|
+
def initialize duration, pitches = [], links: {}, articulation: DEFAULT_ARTICULATION
|
13
15
|
self.duration = duration
|
14
16
|
@pitches = Set.new(pitches).sort
|
15
17
|
@links = links
|
16
18
|
@duration = duration
|
17
|
-
@
|
19
|
+
@articulation = articulation
|
18
20
|
|
19
21
|
@check_methods = [ :ensure_positive_duration ]
|
20
22
|
end
|
21
23
|
|
22
24
|
def ensure_positive_duration
|
23
25
|
unless @duration > 0
|
24
|
-
raise
|
26
|
+
raise NonPositiveError, "duration #{@duration} is not positive"
|
25
27
|
end
|
26
28
|
end
|
27
29
|
|
@@ -29,16 +31,12 @@ class Note
|
|
29
31
|
return (@duration == other.duration) &&
|
30
32
|
(self.pitches == other.pitches) &&
|
31
33
|
(@links.to_a.sort == other.links.to_a.sort) &&
|
32
|
-
(@
|
34
|
+
(@articulation == other.articulation)
|
33
35
|
end
|
34
36
|
|
35
37
|
def clone
|
36
38
|
Marshal.load(Marshal.dump(self))
|
37
39
|
end
|
38
|
-
|
39
|
-
def clear_links
|
40
|
-
@links = {}
|
41
|
-
end
|
42
40
|
|
43
41
|
def transpose diff
|
44
42
|
self.clone.transpose! diff
|
@@ -67,59 +65,25 @@ class Note
|
|
67
65
|
@duration *= ratio
|
68
66
|
return self
|
69
67
|
end
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
class DottedSixteenth < Note
|
78
|
-
def initialize pitches = [], links: {}, accent: Accents::NONE
|
79
|
-
super(Rational(3,32),pitches,links:links,accent:accent)
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
class Eighth < Note
|
84
|
-
def initialize pitches = [], links: {}, accent: Accents::NONE
|
85
|
-
super(Rational(1,8),pitches,links:links,accent:accent)
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
class DottedEighth < Note
|
90
|
-
def initialize pitches = [], links: {}, accent: Accents::NONE
|
91
|
-
super(Rational(3,16),pitches,links:links,accent:accent)
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
class Quarter < Note
|
96
|
-
def initialize pitches = [], links: {}, accent: Accents::NONE
|
97
|
-
super(Rational(1,4),pitches,links:links,accent:accent)
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
class DottedQuarter < Note
|
102
|
-
def initialize pitches = [], links: {}, accent: Accents::NONE
|
103
|
-
super(Rational(3,8),pitches,links:links,accent:accent)
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
class Half < Note
|
108
|
-
def initialize pitches = [], links: {}, accent: Accents::NONE
|
109
|
-
super(Rational(1,2),pitches,links:links,accent:accent)
|
68
|
+
|
69
|
+
def self.add_note_method(name, dur)
|
70
|
+
self.class.send(:define_method,name.to_sym) do |pitches = [], articulation: DEFAULT_ARTICULATION, links: {}|
|
71
|
+
Note.new(dur, pitches, articulation: articulation, links: links)
|
110
72
|
end
|
111
73
|
end
|
112
74
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
75
|
+
{
|
76
|
+
:sixteenth => Rational(1,16),
|
77
|
+
:dotted_SIXTEENTH => Rational(3,32),
|
78
|
+
:eighth => Rational(1,8),
|
79
|
+
:dotted_eighth => Rational(3,16),
|
80
|
+
:quarter => Rational(1,4),
|
81
|
+
:dotted_quarter => Rational(3,8),
|
82
|
+
:half => Rational(1,2),
|
83
|
+
:dotted_half => Rational(3,4),
|
84
|
+
:whole => Rational(1),
|
85
|
+
}.each do |meth_name, dur|
|
86
|
+
add_note_method meth_name, dur
|
123
87
|
end
|
124
88
|
end
|
125
89
|
|
@@ -41,14 +41,6 @@ class Part
|
|
41
41
|
raise RangeError, "start dynamic #{@start_dynamic} is not between 0 and 1"
|
42
42
|
end
|
43
43
|
end
|
44
|
-
|
45
|
-
#def ensure_dynamic_change_offsets
|
46
|
-
# d = self.duration
|
47
|
-
# outofrange = @dynamic_changes.keys.select {|k| !k.between?(0,d) }
|
48
|
-
# if outofrange.any?
|
49
|
-
# raise RangeError, "dynamic change offsets #{outofrange} are not between 0 and #{d}"
|
50
|
-
# end
|
51
|
-
#end
|
52
44
|
|
53
45
|
def ensure_dynamic_change_values_range
|
54
46
|
outofrange = @dynamic_changes.values.select {|v| !v.value.between?(0,1) }
|
@@ -28,9 +28,7 @@ class Pitch
|
|
28
28
|
|
29
29
|
# The base ferquency is C0
|
30
30
|
BASE_FREQ = 16.351597831287414
|
31
|
-
|
32
|
-
# A new instance of Pitch.
|
33
|
-
# @raise [NonPositiveFrequencyError] if base_freq is not > 0.
|
31
|
+
|
34
32
|
def initialize octave:0, semitone:0
|
35
33
|
@octave = octave
|
36
34
|
@semitone = semitone
|
@@ -60,9 +58,11 @@ class Pitch
|
|
60
58
|
|
61
59
|
# Set the Pitch ratio according to a total number of semitones.
|
62
60
|
# @param [Fixnum] semitone The total number of semitones to use.
|
63
|
-
# @raise [
|
61
|
+
# @raise [NonIntegerError] if semitone is not an Integer
|
64
62
|
def total_semitone= semitone
|
65
|
-
|
63
|
+
unless semitone.is_a?(Integer)
|
64
|
+
raise NonIntegerError, "semitone #{semitone} is not a Integer"
|
65
|
+
end
|
66
66
|
@octave, @semitone = 0, semitone
|
67
67
|
normalize!
|
68
68
|
end
|
@@ -76,9 +76,9 @@ class Pitch
|
|
76
76
|
|
77
77
|
# Represent the Pitch ratio according to a ratio.
|
78
78
|
# @param [Numeric] ratio The ratio to represent.
|
79
|
-
# @raise [
|
79
|
+
# @raise [NonPositiveError] unless ratio is > 0
|
80
80
|
def ratio= ratio
|
81
|
-
raise
|
81
|
+
raise NonPositiveError, "ratio #{ratio} is not > 0" unless ratio > 0
|
82
82
|
|
83
83
|
x = Math.log2 ratio
|
84
84
|
self.total_semitone = (x * SEMITONES_PER_OCTAVE).round
|
@@ -157,11 +157,9 @@ class Pitch
|
|
157
157
|
end
|
158
158
|
|
159
159
|
def self.make_from_semitone semitones
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
raise ArgumentError, "Cannot make Pitch from #{semitones}"
|
164
|
-
end
|
160
|
+
pitch = Pitch.new()
|
161
|
+
pitch.total_semitone = semitones
|
162
|
+
return pitch
|
165
163
|
end
|
166
164
|
end
|
167
165
|
|
@@ -39,14 +39,14 @@ class Program
|
|
39
39
|
def ensure_increasing_segments
|
40
40
|
non_increasing = @segments.select {|seg| seg.first >= seg.last }
|
41
41
|
if non_increasing.any?
|
42
|
-
raise
|
42
|
+
raise NonIncreasingError, "Non-increasing segments found #{non_increasing}"
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
46
|
def ensure_nonnegative_segments
|
47
47
|
negative = @segments.select {|seg| seg.first < 0 || seg.last < 0 }
|
48
48
|
if negative.any?
|
49
|
-
raise
|
49
|
+
raise NegativeError, "Segments #{negative} have negative values"
|
50
50
|
end
|
51
51
|
end
|
52
52
|
end
|
@@ -29,37 +29,21 @@ class Score
|
|
29
29
|
|
30
30
|
def check_start_tempo
|
31
31
|
unless @start_tempo > 0
|
32
|
-
raise
|
32
|
+
raise NonPositiveError, "start tempo #{@start_tempo} is not positive"
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
36
|
def check_tempo_changes
|
37
|
-
|
38
|
-
if
|
39
|
-
raise
|
37
|
+
not_positive = @tempo_changes.select {|k,v| v.value <= 0}
|
38
|
+
if not_positive.any?
|
39
|
+
raise NonPositiveError, "tempo changes #{not_positive} are not positive"
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
|
-
#def check_tempo_change_offsets
|
44
|
-
# d = self.duration
|
45
|
-
# outofrange = @tempo_changes.keys.select {|k| !k.between?(0,d) }
|
46
|
-
# if outofrange.any?
|
47
|
-
# raise RangeError, "tempo change offsets #{outofrange} are not between 0 and #{d}"
|
48
|
-
# end
|
49
|
-
#end
|
50
|
-
#
|
51
|
-
#def check_meter_change_offsets
|
52
|
-
# d = self.duration
|
53
|
-
# outofrange = @meter_changes.keys.select {|k| !k.between?(0,d) }
|
54
|
-
# if outofrange.any?
|
55
|
-
# raise RangeError, "meter change offsets #{outofrange} are not between 0 and #{d}"
|
56
|
-
# end
|
57
|
-
#end
|
58
|
-
|
59
43
|
def check_meter_changes
|
60
44
|
nonzero_duration = @meter_changes.select {|k,v| v.duration != 0 }
|
61
45
|
if nonzero_duration.any?
|
62
|
-
raise
|
46
|
+
raise NonZeroError, "meter changes #{nonzero_duration} have non-zero duration"
|
63
47
|
end
|
64
48
|
end
|
65
49
|
|
data/spec/link_spec.rb
CHANGED
@@ -15,12 +15,21 @@ describe Link do
|
|
15
15
|
it 'should return false if two links do not have the same target pitch' do
|
16
16
|
Link.new(C2).should_not eq(Link.new(F5))
|
17
17
|
end
|
18
|
+
|
19
|
+
it 'should return false if the link type is different' do
|
20
|
+
Link::Slur.new(C2).should_not eq(Link::Legato.new(D2))
|
21
|
+
end
|
18
22
|
end
|
19
23
|
|
20
24
|
describe '#clone' do
|
21
25
|
it 'should return a link with the same target pitch' do
|
22
26
|
l = Link.new(C4)
|
23
|
-
l.clone.should eq(
|
27
|
+
l.clone.target_pitch.should eq(C4)
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should return a link with the same class' do
|
31
|
+
l = Link::Slur.new(Eb2)
|
32
|
+
l.clone.class.should eq(Link::Slur)
|
24
33
|
end
|
25
34
|
end
|
26
35
|
|
data/spec/note_spec.rb
CHANGED
@@ -11,9 +11,9 @@ describe Note do
|
|
11
11
|
note.duration.should eq(2)
|
12
12
|
end
|
13
13
|
|
14
|
-
it "should assign :
|
15
|
-
note = Note.new 2,
|
16
|
-
note.
|
14
|
+
it "should assign :articulation parameter if given during construction" do
|
15
|
+
note = Note.new 2, articulation: STACCATO
|
16
|
+
note.articulation.should eq(STACCATO)
|
17
17
|
end
|
18
18
|
|
19
19
|
it 'should have no pitches if not given' do
|
@@ -36,18 +36,28 @@ describe Note do
|
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
39
|
+
{
|
40
|
+
:sixteenth => Rational(1,16),
|
41
|
+
:dotted_SIXTEENTH => Rational(3,32),
|
42
|
+
:eighth => Rational(1,8),
|
43
|
+
:dotted_eighth => Rational(3,16),
|
44
|
+
:quarter => Rational(1,4),
|
45
|
+
:dotted_quarter => Rational(3,8),
|
46
|
+
:half => Rational(1,2),
|
47
|
+
:dotted_half => Rational(3,4),
|
48
|
+
:whole => Rational(1)
|
49
|
+
}.each do |fn_name,tgt_dur|
|
50
|
+
describe ".#{fn_name}" do
|
51
|
+
it "should make a note with duration #{tgt_dur}" do
|
52
|
+
Note.send(fn_name).duration.should eq tgt_dur
|
53
|
+
end
|
44
54
|
end
|
45
55
|
end
|
46
56
|
|
47
57
|
describe '#transpose!' do
|
48
58
|
context 'given pitch diff' do
|
49
59
|
before(:all) do
|
50
|
-
@note = Note::
|
60
|
+
@note = Note::quarter([C2,F2], links:{C2=>Link::Slur.new(D2)})
|
51
61
|
@diff = Pitch.new(semitone: 4)
|
52
62
|
@note.transpose! @diff
|
53
63
|
end
|
@@ -65,32 +75,32 @@ describe Note do
|
|
65
75
|
|
66
76
|
context 'given integer diff' do
|
67
77
|
it 'should transpose the given number of semitones' do
|
68
|
-
Note::
|
78
|
+
Note::quarter([C2]).transpose!(4).pitches[0].should eq(E2)
|
69
79
|
end
|
70
80
|
end
|
71
81
|
|
72
82
|
it 'should return self' do
|
73
|
-
n = Note::
|
83
|
+
n = Note::quarter
|
74
84
|
n.transpose!(0).should eq n
|
75
85
|
end
|
76
86
|
end
|
77
87
|
|
78
88
|
describe '#stretch!' do
|
79
89
|
it 'should multiply note duration by ratio' do
|
80
|
-
note = Note::
|
90
|
+
note = Note::quarter
|
81
91
|
note.stretch!(2)
|
82
92
|
note.duration.should eq(Rational(1,2))
|
83
93
|
|
84
|
-
note = Note::
|
94
|
+
note = Note::quarter
|
85
95
|
note.stretch!(Rational(1,2))
|
86
96
|
note.duration.should eq(Rational(1,8))
|
87
|
-
note = Note::
|
97
|
+
note = Note::quarter
|
88
98
|
note.stretch!(2)
|
89
99
|
note.duration.should eq(Rational(1,2))
|
90
100
|
end
|
91
101
|
|
92
102
|
it 'should return self' do
|
93
|
-
note = Note::
|
103
|
+
note = Note::quarter
|
94
104
|
note.stretch!(1).should be note
|
95
105
|
end
|
96
106
|
end
|
@@ -103,7 +113,7 @@ describe Note do
|
|
103
113
|
n = Note.new(1,[C2,E2])
|
104
114
|
YAML.load(n.to_yaml).should eq n
|
105
115
|
|
106
|
-
n = Note.new(1,[C2],
|
116
|
+
n = Note.new(1,[C2], articulation: STACCATO)
|
107
117
|
YAML.load(n.to_yaml).should eq n
|
108
118
|
|
109
119
|
n = Note.new(1,[E2], links: {E2 => Link::Legato.new(F2)})
|
data/spec/part_spec.rb
CHANGED
@@ -12,7 +12,7 @@ describe Part do
|
|
12
12
|
p = Part.new(Dynamics::PPP)
|
13
13
|
p.start_dynamic.should eq Dynamics::PPP
|
14
14
|
|
15
|
-
notes = [Note::
|
15
|
+
notes = [Note::whole([A2]), Note::half]
|
16
16
|
dcs = { "1/2".to_r => Change::Immediate.new(Dynamics::P), 1 => Change::Gradual.new(Dynamics::MF,1) }
|
17
17
|
p = Part.new(Dynamics::FF, notes: notes, dynamic_changes: dcs)
|
18
18
|
p.notes.should eq notes
|
@@ -31,13 +31,13 @@ describe Part do
|
|
31
31
|
{ 'negative start dynamic' => [-0.01],
|
32
32
|
'start dynamic > 1' => [1.01],
|
33
33
|
#'dynamic change offsets outside 0..d' => [
|
34
|
-
# 0.5, :notes => [ Note::
|
34
|
+
# 0.5, :notes => [ Note::whole ],
|
35
35
|
# :dynamic_changes => { 1.2 => Change::Immediate.new(0.5) }],
|
36
36
|
#'dynamic change offsets outside 0..d' => [
|
37
|
-
# 0.5, :notes => [ Note::
|
37
|
+
# 0.5, :notes => [ Note::whole ],
|
38
38
|
# :dynamic_changes => { -0.2 => Change::Immediate.new(0.5) }],
|
39
39
|
'dynamic change values outside 0..1' => [
|
40
|
-
0.5, :notes => [ Note::
|
40
|
+
0.5, :notes => [ Note::whole ],
|
41
41
|
:dynamic_changes => { 0.2 => Change::Immediate.new(-0.01), 0.3 => Change::Gradual.new(1.01,0.2) }],
|
42
42
|
'notes with 0 duration' => [ 0.5, :notes => [ Note.new(0) ]],
|
43
43
|
'notes with negative duration' => [ 0.5, :notes => [ Note.new(-1) ]],
|
@@ -54,9 +54,9 @@ describe Part do
|
|
54
54
|
|
55
55
|
{
|
56
56
|
'valid notes' => [ Dynamics::PP,
|
57
|
-
:notes => [ Note::
|
57
|
+
:notes => [ Note::whole, quarter([C5]) ]],
|
58
58
|
'valid dynamic values' => [ Dynamics::MF,
|
59
|
-
:notes => [ Note::
|
59
|
+
:notes => [ Note::whole([C4]), Note::quarter ],
|
60
60
|
:dynamic_changes => {
|
61
61
|
0.5 => Change::Immediate.new(Dynamics::MP),
|
62
62
|
1.2 => Change::Gradual.new(Dynamics::FF, 0.05) } ],
|
data/spec/spec_helper.rb
CHANGED
@@ -4,14 +4,15 @@ require 'music-transcription'
|
|
4
4
|
include Music::Transcription
|
5
5
|
include Pitches
|
6
6
|
include Meters
|
7
|
+
include Articulations
|
7
8
|
|
8
9
|
class Samples
|
9
10
|
SAMPLE_PART = Part.new(
|
10
11
|
Dynamics::P,
|
11
12
|
notes: [
|
12
|
-
Note::
|
13
|
-
Note::
|
14
|
-
Note::
|
13
|
+
Note::quarter([ C1, D1 ]),
|
14
|
+
Note::quarter([ C2, D2 ]),
|
15
|
+
Note::whole([ C3, D3 ])
|
15
16
|
],
|
16
17
|
dynamic_changes: {1.0 => Change::Immediate.new(Dynamics::MP)}
|
17
18
|
)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: music-transcription
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.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
|
+
date: 2014-10-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -151,8 +151,7 @@ files:
|
|
151
151
|
- examples/song1.yml
|
152
152
|
- examples/song2.yml
|
153
153
|
- lib/music-transcription.rb
|
154
|
-
- lib/music-transcription/
|
155
|
-
- lib/music-transcription/accents.rb
|
154
|
+
- lib/music-transcription/articulations.rb
|
156
155
|
- lib/music-transcription/change.rb
|
157
156
|
- lib/music-transcription/dynamics.rb
|
158
157
|
- lib/music-transcription/errors.rb
|
@@ -1,25 +0,0 @@
|
|
1
|
-
module Music
|
2
|
-
module Transcription
|
3
|
-
|
4
|
-
# Defines a note accent (stacatto, tenuto, etc.)
|
5
|
-
#
|
6
|
-
# @author James Tunnell
|
7
|
-
#
|
8
|
-
class Accent
|
9
|
-
def ==(other)
|
10
|
-
self.class == other.class
|
11
|
-
end
|
12
|
-
|
13
|
-
def clone
|
14
|
-
self.class.new
|
15
|
-
end
|
16
|
-
|
17
|
-
[
|
18
|
-
:None, :Staccato, :Staccatissimo, :Marcato, :Martellato, :Tenuto
|
19
|
-
].each do |name|
|
20
|
-
Accent.const_set(name, Class.new(Accent))
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
end
|
25
|
-
end
|
@@ -1,12 +0,0 @@
|
|
1
|
-
module Music
|
2
|
-
module Transcription
|
3
|
-
module Accents
|
4
|
-
NONE = Accent::None
|
5
|
-
STACCATO = Accent::Staccato.new
|
6
|
-
STACCATISSIMO = Accent::Staccatissimo.new
|
7
|
-
MARCATO = Accent::Marcato.new
|
8
|
-
MARTELLATO = Accent::Martellato.new
|
9
|
-
TENUTO = Accent::Tenuto.new
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|