music-transcription 0.17.1 → 0.19.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.rb +4 -4
- data/examples/missed_connection.rb +3 -3
- data/examples/song1.rb +6 -6
- data/examples/song2.rb +3 -3
- data/lib/music-transcription.rb +8 -2
- data/lib/music-transcription/model/change.rb +9 -3
- data/lib/music-transcription/model/measure_score.rb +62 -0
- data/lib/music-transcription/model/meter.rb +5 -1
- data/lib/music-transcription/model/note.rb +4 -1
- data/lib/music-transcription/model/note_score.rb +60 -0
- data/lib/music-transcription/model/part.rb +4 -1
- data/lib/music-transcription/model/program.rb +5 -2
- data/lib/music-transcription/model/tempo.rb +13 -23
- data/lib/music-transcription/packing/measure_score_packing.rb +34 -0
- data/lib/music-transcription/packing/{score_packing.rb → note_score_packing.rb} +12 -21
- data/lib/music-transcription/packing/part_packing.rb +1 -1
- data/lib/music-transcription/packing/program_packing.rb +1 -1
- data/lib/music-transcription/parsing/convenience_methods.rb +37 -58
- data/lib/music-transcription/parsing/numbers/nonnegative_float_parsing.rb +172 -59
- data/lib/music-transcription/parsing/numbers/nonnegative_float_parsing.treetop +13 -1
- data/lib/music-transcription/parsing/numbers/positive_float_parsing.rb +505 -0
- data/lib/music-transcription/parsing/numbers/positive_float_parsing.treetop +35 -0
- data/lib/music-transcription/parsing/numbers/positive_integer_parsing.rb +2 -0
- data/lib/music-transcription/parsing/numbers/positive_integer_parsing.treetop +2 -0
- data/lib/music-transcription/parsing/numbers/positive_rational_parsing.rb +86 -0
- data/lib/music-transcription/parsing/numbers/positive_rational_parsing.treetop +21 -0
- data/lib/music-transcription/parsing/parseable.rb +32 -0
- data/lib/music-transcription/parsing/tempo_parsing.rb +396 -0
- data/lib/music-transcription/parsing/tempo_parsing.treetop +49 -0
- data/lib/music-transcription/validatable.rb +5 -18
- data/lib/music-transcription/version.rb +1 -1
- data/spec/model/measure_score_spec.rb +85 -0
- data/spec/model/note_score_spec.rb +68 -0
- data/spec/model/tempo_spec.rb +15 -15
- data/spec/packing/{score_packing_spec.rb → measure_score_packing_spec.rb} +13 -7
- data/spec/packing/note_score_packing_spec.rb +100 -0
- data/spec/packing/part_packing_spec.rb +1 -1
- data/spec/parsing/convenience_methods_spec.rb +80 -81
- data/spec/parsing/numbers/nonnegative_float_spec.rb +19 -2
- data/spec/parsing/numbers/positive_float_spec.rb +28 -0
- data/spec/parsing/numbers/positive_integer_spec.rb +13 -2
- data/spec/parsing/numbers/positive_rational_spec.rb +28 -0
- data/spec/parsing/tempo_parsing_spec.rb +21 -0
- metadata +27 -8
- data/lib/music-transcription/model/score.rb +0 -68
- data/spec/model/score_spec.rb +0 -69
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f5b490ee5916154f2376f07568ccd234ff6fb82a
|
4
|
+
data.tar.gz: 2a2fff2f3b7e94deacc2438d510f1d803cf6efcb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 797d3b962ded3291e92d8ba49a390072663d1ae37b0e7e957b3acc562d2d1793d569a7c117fd750de3bb99e03ed4d935cc0b13338c95e669993128acbd246902
|
7
|
+
data.tar.gz: e0ae19e64aa43b0dc274d61601db32cbcccf87b33226c312e4f76407f7e87b7694a4f678c32de08bf17a714da5aa2e113a7ca15b470dbc2e8b48dbab07360489
|
data/examples/hip.rb
CHANGED
@@ -10,14 +10,14 @@ include Parsing
|
|
10
10
|
score = Score.new(FOUR_FOUR,120) do |s|
|
11
11
|
s.program = Program.new([0...2, 0...2,2...4,0...2])
|
12
12
|
s.parts["lead"] = Part.new(Dynamics::MF) do |p|
|
13
|
-
riff =
|
14
|
-
|
13
|
+
riff = "/6Bb3 /4 /12Db4= /6Db4= /36Db4 /36Eb4 /36Db4 /6Ab3 /12Db4 \
|
14
|
+
/6Bb3 /4 /12Db4= /4Db4= /8=Db4 /8C4".to_notes
|
15
15
|
p.notes = riff + riff.map {|n| n.transpose(2) }
|
16
16
|
end
|
17
17
|
|
18
18
|
s.parts["bass"] = Part.new(Dynamics::MP) do |p|
|
19
|
-
riff =
|
20
|
-
|
19
|
+
riff = "/6Bb2 /4 /3Ab2 /6F2 /12Ab2 \
|
20
|
+
/6Bb2 /4 /3Ab2 /4Ab2".to_notes
|
21
21
|
p.notes = riff + riff.map {|n| n.transpose(2) }
|
22
22
|
end
|
23
23
|
end
|
@@ -10,9 +10,9 @@ include Parsing
|
|
10
10
|
score = Score.new(FOUR_FOUR, 120) do |s|
|
11
11
|
s.program = Program.new([0...2,0...6])
|
12
12
|
s.parts["bass"] = Part.new(Dynamics::MF) do |p|
|
13
|
-
p.notes =
|
14
|
-
p.notes +=
|
15
|
-
p.notes +=
|
13
|
+
p.notes = "/4Eb2 /4 /4Bb2 /4 /4Eb2 /8 /8B2 /4Bb2 /4Ab2".to_notes
|
14
|
+
p.notes += "/4Bb2 /8 /8F3= /2F3 /4Bb2 /8 /8F3= /2F3".to_notes
|
15
|
+
p.notes += "/4B2 /8 /8Gb3= /2Gb3 /8 /8Gb3= /2Gb3".to_notes
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
data/examples/song1.rb
CHANGED
@@ -11,15 +11,15 @@ score = Score.new(FOUR_FOUR, 120) do |s|
|
|
11
11
|
s.program = Program.new([ 0...4.0, 0...4.0 ])
|
12
12
|
|
13
13
|
s.parts[1] = Part.new(Dynamics::MF) do |p|
|
14
|
-
p.notes =
|
15
|
-
|
16
|
-
|
17
|
-
|
14
|
+
p.notes = "3/8C2 /4Eb2 5/16F2 /16Eb2 \
|
15
|
+
/8 /4C2 /4Eb2 3/8 \
|
16
|
+
3/8C2 /4Eb2 5/16F2 /16Eb2 \
|
17
|
+
/8 /4C2 /4Eb2".to_notes
|
18
18
|
end
|
19
19
|
|
20
20
|
s.parts[2] = Part.new(Dynamics::MF) do |p|
|
21
|
-
p.notes =
|
22
|
-
|
21
|
+
p.notes = "/8 /8Bb3 /8Bb3 /8Bb3 /8Bb3 /4C4 /4A3 /8G3 /8F3 5/16=G3 /16=F3 /8E3 /8 \
|
22
|
+
/8 /8Bb3 /8Bb3 /8Bb3 /8Bb3 /4C4 /8A3 /8E4 /8=E4 /8=D4 /8C4".to_notes
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
data/examples/song2.rb
CHANGED
@@ -10,15 +10,15 @@ score = Score.new(FOUR_FOUR, 120) do |s|
|
|
10
10
|
s.program = Program.new([0...4.0, 0...4.0 ])
|
11
11
|
|
12
12
|
s.parts[1] = Part.new(Dynamics::MF) do |p|
|
13
|
-
p.notes =
|
13
|
+
p.notes = "1C4 1Bb3 1Ab3 /2G3 /2Bb3".to_notes
|
14
14
|
end
|
15
15
|
|
16
16
|
s.parts[2] = Part.new(Dynamics::MF) do |p|
|
17
|
-
p.notes =
|
17
|
+
p.notes = "3/8E5 1D5 1C5 5/8C5 /2C5 /2D5".to_notes
|
18
18
|
end
|
19
19
|
|
20
20
|
s.parts[3] = Part.new(Dynamics::MF) do |p|
|
21
|
-
p.notes =
|
21
|
+
p.notes = "/8 /4G5 /2F5 /4 /4F5 /2Eb5 /4 /4Eb5 /2Eb5 /8 /2Eb5 /2F5".to_notes
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
data/lib/music-transcription.rb
CHANGED
@@ -14,13 +14,16 @@ require 'music-transcription/model/change'
|
|
14
14
|
require 'music-transcription/model/part'
|
15
15
|
require 'music-transcription/model/program'
|
16
16
|
require 'music-transcription/model/tempo'
|
17
|
+
require 'music-transcription/model/note_score'
|
17
18
|
require 'music-transcription/model/meter'
|
18
19
|
require 'music-transcription/model/meters'
|
19
|
-
require 'music-transcription/model/
|
20
|
+
require 'music-transcription/model/measure_score'
|
20
21
|
|
21
22
|
require 'treetop'
|
22
23
|
require 'music-transcription/parsing/numbers/nonnegative_integer_parsing'
|
23
24
|
require 'music-transcription/parsing/numbers/positive_integer_parsing'
|
25
|
+
require 'music-transcription/parsing/numbers/positive_float_parsing'
|
26
|
+
require 'music-transcription/parsing/numbers/positive_rational_parsing'
|
24
27
|
require 'music-transcription/parsing/numbers/nonnegative_float_parsing'
|
25
28
|
require 'music-transcription/parsing/numbers/nonnegative_rational_parsing'
|
26
29
|
require 'music-transcription/parsing/pitch_parsing'
|
@@ -34,9 +37,12 @@ require 'music-transcription/parsing/note_parsing'
|
|
34
37
|
require 'music-transcription/parsing/note_node'
|
35
38
|
require 'music-transcription/parsing/meter_parsing'
|
36
39
|
require 'music-transcription/parsing/segment_parsing'
|
40
|
+
require 'music-transcription/parsing/tempo_parsing'
|
41
|
+
require 'music-transcription/parsing/parseable'
|
37
42
|
require 'music-transcription/parsing/convenience_methods'
|
38
43
|
|
39
44
|
require 'music-transcription/packing/change_packing'
|
40
45
|
require 'music-transcription/packing/part_packing'
|
41
46
|
require 'music-transcription/packing/program_packing'
|
42
|
-
require 'music-transcription/packing/
|
47
|
+
require 'music-transcription/packing/note_score_packing'
|
48
|
+
require 'music-transcription/packing/measure_score_packing'
|
@@ -17,12 +17,15 @@ class Change
|
|
17
17
|
|
18
18
|
class Immediate < Change
|
19
19
|
include Validatable
|
20
|
-
|
21
|
-
@@check_methods = [ :ensure_zero_duration ]
|
20
|
+
|
22
21
|
def initialize value
|
23
22
|
super(value,0)
|
24
23
|
end
|
25
24
|
|
25
|
+
def check_methods
|
26
|
+
[ :ensure_zero_duration ]
|
27
|
+
end
|
28
|
+
|
26
29
|
def ensure_zero_duration
|
27
30
|
unless @duration == 0
|
28
31
|
raise NonZeroError, "immediate change duration #{self.duration} must be 0"
|
@@ -33,11 +36,14 @@ class Change
|
|
33
36
|
class Gradual < Change
|
34
37
|
include Validatable
|
35
38
|
|
36
|
-
@@check_methods = [ :ensure_nonnegative_duration ]
|
37
39
|
def initialize value, transition_duration
|
38
40
|
super(value, transition_duration)
|
39
41
|
end
|
40
42
|
|
43
|
+
def check_methods
|
44
|
+
[ :ensure_nonnegative_duration ]
|
45
|
+
end
|
46
|
+
|
41
47
|
def ensure_nonnegative_duration
|
42
48
|
if @duration < 0
|
43
49
|
raise NegativeError, "gradual change duration #{self.duration} must be non-negative"
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Music
|
2
|
+
module Transcription
|
3
|
+
|
4
|
+
class MeasureScore < NoteScore
|
5
|
+
attr_accessor :start_meter, :meter_changes
|
6
|
+
|
7
|
+
def initialize start_meter, start_tempo, meter_changes: {}, tempo_changes: {}, parts: {}, program: Program.new
|
8
|
+
@start_meter = start_meter
|
9
|
+
@meter_changes = meter_changes
|
10
|
+
|
11
|
+
super(start_tempo, tempo_changes: tempo_changes, program: program, parts: parts)
|
12
|
+
yield(self) if block_given?
|
13
|
+
end
|
14
|
+
|
15
|
+
def check_methods
|
16
|
+
super() + [:check_startmeter_type, :check_meterchange_types, :check_meterchange_durs]
|
17
|
+
end
|
18
|
+
|
19
|
+
def validatables
|
20
|
+
super() + [ @start_meter ] + @meter_changes.values +
|
21
|
+
@meter_changes.values.map {|v| v.value}
|
22
|
+
end
|
23
|
+
|
24
|
+
def valid_tempo_types
|
25
|
+
super() + [ Tempo::BPM ]
|
26
|
+
end
|
27
|
+
|
28
|
+
def check_startmeter_type
|
29
|
+
unless @start_meter.is_a? Meter
|
30
|
+
raise TypeError, "start meter #{@start_meter} is not a Meter object"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def check_meterchange_types
|
35
|
+
badtypes = @meter_changes.select {|k,v| !v.value.is_a?(Meter) }
|
36
|
+
if badtypes.any?
|
37
|
+
raise TypeError, "meter change values #{nonmeter_values} are not Meter objects"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def check_meterchange_durs
|
42
|
+
nonzero_duration = @meter_changes.select {|k,v| v.duration != 0 }
|
43
|
+
if nonzero_duration.any?
|
44
|
+
raise NonZeroError, "meter changes #{nonzero_duration} have non-zero duration"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def ==(other)
|
49
|
+
return super() && @start_meter == other.start_meter &&
|
50
|
+
@meter_changes == other.meter_changes
|
51
|
+
end
|
52
|
+
|
53
|
+
# Convert to NoteScore object by first converting measure-based offsets to
|
54
|
+
# note-based offsets, and eliminating the use of meters. Also, tempo is
|
55
|
+
# converted from beats-per-minute to notes-per-minute.
|
56
|
+
def to_note_score
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
@@ -3,15 +3,19 @@ module Transcription
|
|
3
3
|
|
4
4
|
class Meter
|
5
5
|
include Validatable
|
6
|
+
|
6
7
|
attr_reader :measure_duration, :beat_duration, :beats_per_measure
|
7
8
|
|
8
|
-
@@check_methods = [ :check_beats_per_measure, :check_beat_duration ]
|
9
9
|
def initialize beats_per_measure, beat_duration
|
10
10
|
@beats_per_measure = beats_per_measure
|
11
11
|
@beat_duration = beat_duration
|
12
12
|
@measure_duration = beats_per_measure * beat_duration
|
13
13
|
end
|
14
14
|
|
15
|
+
def check_methods
|
16
|
+
[ :check_beats_per_measure, :check_beat_duration ]
|
17
|
+
end
|
18
|
+
|
15
19
|
def check_beats_per_measure
|
16
20
|
unless @beats_per_measure > 0
|
17
21
|
raise NonPositiveError, "beats per measure #{@beats_per_measure} is not positive"
|
@@ -11,7 +11,6 @@ class Note
|
|
11
11
|
|
12
12
|
DEFAULT_ARTICULATION = Articulations::NORMAL
|
13
13
|
|
14
|
-
@@check_methods = [ :ensure_positive_duration ]
|
15
14
|
def initialize duration, pitches = [], articulation: DEFAULT_ARTICULATION, accented: false, links: {}
|
16
15
|
@duration = duration
|
17
16
|
@pitches = Set.new(pitches).sort
|
@@ -20,6 +19,10 @@ class Note
|
|
20
19
|
@links = links
|
21
20
|
end
|
22
21
|
|
22
|
+
def check_methods
|
23
|
+
[ :ensure_positive_duration ]
|
24
|
+
end
|
25
|
+
|
23
26
|
def ensure_positive_duration
|
24
27
|
unless @duration > 0
|
25
28
|
raise NonPositiveError, "duration #{@duration} is not positive"
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Music
|
2
|
+
module Transcription
|
3
|
+
|
4
|
+
class NoteScore
|
5
|
+
include Validatable
|
6
|
+
|
7
|
+
attr_accessor :start_tempo, :parts, :program, :tempo_changes
|
8
|
+
|
9
|
+
def initialize start_tempo, tempo_changes: {}, parts: {}, program: Program.new
|
10
|
+
@start_tempo = start_tempo
|
11
|
+
@tempo_changes = tempo_changes
|
12
|
+
@parts = parts
|
13
|
+
@program = program
|
14
|
+
|
15
|
+
yield(self) if block_given?
|
16
|
+
end
|
17
|
+
|
18
|
+
def check_methods
|
19
|
+
[ :check_start_tempo_type, :check_tempo_change_types ]
|
20
|
+
end
|
21
|
+
|
22
|
+
def validatables
|
23
|
+
[ @program ] + @tempo_changes.values + @parts.values
|
24
|
+
end
|
25
|
+
|
26
|
+
def valid_tempo_types
|
27
|
+
[ Tempo::QNPM, Tempo::NPM, Tempo::NPS ]
|
28
|
+
end
|
29
|
+
|
30
|
+
def check_start_tempo_type
|
31
|
+
unless valid_tempo_types.include?(@start_tempo.class)
|
32
|
+
raise TypeError, "type of start tempo #{@start_tempo} is not one of valid tempo types: #{valid_tempo_types}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def check_tempo_change_types
|
37
|
+
baddtypes = @tempo_changes.select {|k,v| !valid_tempo_types.include?(v.value.class) }
|
38
|
+
if baddtypes.any?
|
39
|
+
raise NonPositiveError, "type of tempo change values #{baddtypes} are not one of valid tempo types: #{valid_tempo_types}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def clone
|
44
|
+
Marshal.load(Marshal.dump(self))
|
45
|
+
end
|
46
|
+
|
47
|
+
def ==(other)
|
48
|
+
return @start_tempo == other.start_tempo &&
|
49
|
+
@tempo_changes == other.tempo_changes &&
|
50
|
+
@parts == other.parts &&
|
51
|
+
@program == other.program
|
52
|
+
end
|
53
|
+
|
54
|
+
def duration
|
55
|
+
@parts.map {|p| p.duration }.max
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
@@ -8,7 +8,6 @@ class Part
|
|
8
8
|
|
9
9
|
attr_accessor :start_dynamic, :dynamic_changes, :notes
|
10
10
|
|
11
|
-
@@check_methods = [:ensure_start_dynamic, :ensure_dynamic_change_values_range ]
|
12
11
|
def initialize start_dynamic, notes: [], dynamic_changes: {}
|
13
12
|
@notes = notes
|
14
13
|
@start_dynamic = start_dynamic
|
@@ -17,6 +16,10 @@ class Part
|
|
17
16
|
yield(self) if block_given?
|
18
17
|
end
|
19
18
|
|
19
|
+
def check_methods
|
20
|
+
[:ensure_start_dynamic, :ensure_dynamic_change_values_range ]
|
21
|
+
end
|
22
|
+
|
20
23
|
def validatables
|
21
24
|
@notes + @dynamic_changes.values
|
22
25
|
end
|
@@ -9,12 +9,15 @@ class Program
|
|
9
9
|
include Validatable
|
10
10
|
|
11
11
|
attr_accessor :segments
|
12
|
-
|
13
|
-
@@check_methods = [:ensure_increasing_segments, :ensure_nonnegative_segments]
|
12
|
+
|
14
13
|
def initialize segments = []
|
15
14
|
@segments = segments
|
16
15
|
end
|
17
16
|
|
17
|
+
def check_methods
|
18
|
+
[:ensure_increasing_segments, :ensure_nonnegative_segments]
|
19
|
+
end
|
20
|
+
|
18
21
|
# @return [Float] the sum of all program segment lengths
|
19
22
|
def length
|
20
23
|
segments.inject(0.0) { |length, segment| length + (segment.last - segment.first) }
|
@@ -1,37 +1,27 @@
|
|
1
1
|
module Music
|
2
2
|
module Transcription
|
3
3
|
|
4
|
-
# Represent the musical tempo, with beats ber minute and beat duration.
|
5
4
|
class Tempo
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
@beats_per_minute = beats_per_minute
|
11
|
-
@beat_duration = beat_duration
|
5
|
+
attr_reader :value
|
6
|
+
def initialize value
|
7
|
+
raise NonPositiveError, "Given tempo value #{value} is not positive" if value <= 0
|
8
|
+
@value = value
|
12
9
|
end
|
13
10
|
|
14
11
|
def ==(other)
|
15
|
-
|
16
|
-
(other.beat_duration == @beat_duration)
|
17
|
-
end
|
18
|
-
|
19
|
-
def notes_per_second
|
20
|
-
(@beats_per_minute * @beat_duration) / 60.0
|
12
|
+
self.class == other.class && self.value == other.value
|
21
13
|
end
|
22
14
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
if other.is_a? Tempo
|
29
|
-
notes_per_second <=> other.notes_per_second
|
30
|
-
else
|
31
|
-
notes_per_second <=> other
|
15
|
+
[ :qnpm, :bpm, :npm, :nps ].each do |sym|
|
16
|
+
klass = Class.new(Tempo) do
|
17
|
+
def to_s
|
18
|
+
"#{@value}#{self.class::PRINT_SYM}"
|
19
|
+
end
|
32
20
|
end
|
21
|
+
klass.const_set(:PRINT_SYM,sym)
|
22
|
+
Tempo.const_set(sym.upcase,klass)
|
33
23
|
end
|
34
24
|
end
|
35
25
|
|
36
26
|
end
|
37
|
-
end
|
27
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Music
|
2
|
+
module Transcription
|
3
|
+
|
4
|
+
class MeasureScore
|
5
|
+
def pack
|
6
|
+
hash = super()
|
7
|
+
hash["start_meter"] = start_meter.to_s
|
8
|
+
hash["meter_changes"] = Hash[ meter_changes.map do |offset,change|
|
9
|
+
a = change.pack
|
10
|
+
a[0] = a[0].to_s
|
11
|
+
[offset,a]
|
12
|
+
end ]
|
13
|
+
return hash
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.unpack packing
|
17
|
+
unpacked_start_meter = Meter.parse(packing["start_meter"])
|
18
|
+
unpacked_mcs = Hash[ packing["meter_changes"].map do |k,v|
|
19
|
+
v = v.clone
|
20
|
+
v[0] = Meter.parse(v[0])
|
21
|
+
[k, Change.from_ary(v) ]
|
22
|
+
end ]
|
23
|
+
|
24
|
+
note_score = NoteScore.unpack(packing)
|
25
|
+
|
26
|
+
new(unpacked_start_meter, note_score.start_tempo,
|
27
|
+
meter_changes: unpacked_mcs, tempo_changes: note_score.tempo_changes,
|
28
|
+
program: note_score.program, parts: note_score.parts
|
29
|
+
)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
@@ -1,19 +1,15 @@
|
|
1
1
|
module Music
|
2
2
|
module Transcription
|
3
3
|
|
4
|
-
class
|
4
|
+
class NoteScore
|
5
5
|
def pack
|
6
|
-
|
7
|
-
|
6
|
+
packed_starttempo = start_tempo.to_s
|
7
|
+
packed_tcs = Hash[ tempo_changes.map do |offset,change|
|
8
8
|
a = change.pack
|
9
9
|
a[0] = a[0].to_s
|
10
10
|
[offset,a]
|
11
11
|
end ]
|
12
12
|
|
13
|
-
packed_tcs = Hash[ tempo_changes.map do |k,v|
|
14
|
-
[k,v.to_ary]
|
15
|
-
end ]
|
16
|
-
|
17
13
|
packed_parts = Hash[
|
18
14
|
@parts.map do |name,part|
|
19
15
|
[ name, part.pack ]
|
@@ -21,9 +17,7 @@ class Score
|
|
21
17
|
]
|
22
18
|
packed_prog = program.pack
|
23
19
|
|
24
|
-
{ "
|
25
|
-
"meter_changes" => packed_mcs,
|
26
|
-
"start_tempo" => start_tempo,
|
20
|
+
{ "start_tempo" => packed_starttempo,
|
27
21
|
"tempo_changes" => packed_tcs,
|
28
22
|
"program" => packed_prog,
|
29
23
|
"parts" => packed_parts,
|
@@ -31,27 +25,24 @@ class Score
|
|
31
25
|
end
|
32
26
|
|
33
27
|
def self.unpack packing
|
34
|
-
|
35
|
-
|
28
|
+
unpacked_starttempo = Tempo.parse(packing["start_tempo"])
|
29
|
+
unpacked_tcs = Hash[ packing["tempo_changes"].map do |k,v|
|
36
30
|
v = v.clone
|
37
|
-
v[0] =
|
31
|
+
v[0] = Tempo.parse(v[0])
|
38
32
|
[k, Change.from_ary(v) ]
|
39
33
|
end ]
|
40
34
|
|
41
|
-
unpacked_tcs = Hash[ packing["tempo_changes"].map do |k,v|
|
42
|
-
[k, Change.from_ary(v)]
|
43
|
-
end ]
|
44
|
-
|
45
35
|
unpacked_parts = Hash[ packing["parts"].map do |name,packed|
|
46
36
|
[name, Part.unpack(packed)]
|
47
37
|
end ]
|
48
38
|
|
49
39
|
unpacked_prog = Program.unpack packing["program"]
|
50
40
|
|
51
|
-
new(
|
52
|
-
|
53
|
-
program: unpacked_prog,
|
54
|
-
|
41
|
+
new(unpacked_starttempo,
|
42
|
+
tempo_changes: unpacked_tcs,
|
43
|
+
program: unpacked_prog,
|
44
|
+
parts: unpacked_parts
|
45
|
+
)
|
55
46
|
end
|
56
47
|
end
|
57
48
|
|