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.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +1 -1
  3. data/examples/{make_hip.rb → hip.rb} +8 -2
  4. data/examples/hip_packed.yml +22 -0
  5. data/examples/{make_missed_connection.rb → missed_connection.rb} +8 -2
  6. data/examples/missed_connection.yml +2 -2
  7. data/examples/missed_connection_packed.yml +14 -0
  8. data/examples/{make_song1.rb → song1.rb} +7 -1
  9. data/examples/song1_packed.yml +19 -0
  10. data/examples/{make_song2.rb → song2.rb} +6 -1
  11. data/examples/song2_packed.yml +21 -0
  12. data/lib/music-transcription.rb +11 -3
  13. data/lib/music-transcription/model/link.rb +24 -6
  14. data/lib/music-transcription/model/meter.rb +10 -0
  15. data/lib/music-transcription/model/meters.rb +1 -1
  16. data/lib/music-transcription/model/note.rb +32 -0
  17. data/lib/music-transcription/model/pitch.rb +19 -0
  18. data/lib/music-transcription/packing/change_packing.rb +27 -0
  19. data/lib/music-transcription/packing/part_packing.rb +33 -0
  20. data/lib/music-transcription/packing/program_packing.rb +18 -0
  21. data/lib/music-transcription/packing/score_packing.rb +59 -0
  22. data/lib/music-transcription/parsing/convenience_methods.rb +10 -0
  23. data/lib/music-transcription/parsing/meter_parsing.rb +117 -9
  24. data/lib/music-transcription/parsing/meter_parsing.treetop +12 -0
  25. data/lib/music-transcription/parsing/note_node.rb +42 -0
  26. data/lib/music-transcription/parsing/note_parsing.rb +57 -180
  27. data/lib/music-transcription/parsing/note_parsing.treetop +2 -19
  28. data/lib/music-transcription/parsing/numbers/nonnegative_float_parsing.rb +178 -0
  29. data/lib/music-transcription/parsing/numbers/nonnegative_float_parsing.treetop +19 -0
  30. data/lib/music-transcription/parsing/{nonnegative_integer_parsing.rb → numbers/nonnegative_integer_parsing.rb} +9 -0
  31. data/lib/music-transcription/parsing/{nonnegative_integer_parsing.treetop → numbers/nonnegative_integer_parsing.treetop} +7 -1
  32. data/lib/music-transcription/parsing/numbers/nonnegative_rational_parsing.rb +88 -0
  33. data/lib/music-transcription/parsing/numbers/nonnegative_rational_parsing.treetop +22 -0
  34. data/lib/music-transcription/parsing/{positive_integer_parsing.rb → numbers/positive_integer_parsing.rb} +0 -0
  35. data/lib/music-transcription/parsing/{positive_integer_parsing.treetop → numbers/positive_integer_parsing.treetop} +0 -0
  36. data/lib/music-transcription/parsing/segment_parsing.rb +143 -0
  37. data/lib/music-transcription/parsing/segment_parsing.treetop +25 -0
  38. data/lib/music-transcription/version.rb +1 -1
  39. data/spec/model/link_spec.rb +44 -60
  40. data/spec/model/meter_spec.rb +18 -0
  41. data/spec/model/note_spec.rb +39 -0
  42. data/spec/model/pitch_spec.rb +32 -0
  43. data/spec/packing/change_packing_spec.rb +91 -0
  44. data/spec/packing/part_packing_spec.rb +66 -0
  45. data/spec/packing/program_packing_spec.rb +33 -0
  46. data/spec/packing/score_packing_spec.rb +122 -0
  47. data/spec/parsing/meter_parsing_spec.rb +2 -2
  48. data/spec/parsing/note_node_spec.rb +87 -0
  49. data/spec/parsing/note_parsing_spec.rb +3 -0
  50. data/spec/parsing/numbers/nonnegative_float_spec.rb +11 -0
  51. data/spec/parsing/{nonnegative_integer_spec.rb → numbers/nonnegative_integer_spec.rb} +1 -1
  52. data/spec/parsing/numbers/nonnegative_rational_spec.rb +11 -0
  53. data/spec/parsing/{positive_integer_spec.rb → numbers/positive_integer_spec.rb} +1 -1
  54. data/spec/parsing/segment_parsing_spec.rb +27 -0
  55. metadata +45 -17
  56. data/lib/music-transcription/parsing/note_nodes.rb +0 -64
  57. data/spec/parsing/note_nodes_spec.rb +0 -84
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ed4e612a8a80448e988a75202595e4ae4d845f05
4
- data.tar.gz: 63bfe1347b0faf23627be072b3ae37b7b674ce94
3
+ metadata.gz: 2990163ab7471c99ce5b1df3b4899937125d06f7
4
+ data.tar.gz: 097b4ef37645684ebcf3aac659999f742afc593f
5
5
  SHA512:
6
- metadata.gz: 8498194327951219f0011f4ad48e68eef2a5a10fcd8781da4f17b4fd40528f7e593d075a18389bf815392c074a6b94d9d8c1dbb7508bb6a693a71e54b64155a8
7
- data.tar.gz: b268880e97c17b46128a7f350bbda0fc64f0a4ddbfbd7b9f884d46a32ed0d41ef3091b02899b8fcab16091b0855cb935d9993a1aa717a35960949e44289af86e
6
+ metadata.gz: 15b1e4dca0a5359ef7cf0bb975da7217c592abae21a770b4004c2037dc12bfe5583ebca5d73517ff928f1061fdddde92a146614bcc73f00acd2f8f37fb9e81d6
7
+ data.tar.gz: 8453d7fecb15559942a1c8efb42bb0404718b96599e1515497b012f94cc23b5e81aca96307d6921386b9c2856dde122fbdbcb3a3bc5a6718bced2a773793d5a8
data/Rakefile CHANGED
@@ -38,7 +38,7 @@ task :make_examples do
38
38
  Dir.chdir examples_dir
39
39
 
40
40
  examples = []
41
- Dir.glob('**/make*.rb') do |file|
41
+ Dir.glob('**/*.rb') do |file|
42
42
  examples.push File.expand_path(file)
43
43
  end
44
44
 
@@ -22,6 +22,12 @@ score = Score.new(FOUR_FOUR,120) do |s|
22
22
  end
23
23
  end
24
24
 
25
- File.open('hip.yml','w') do |file|
25
+ name = File.basename(__FILE__,".rb")
26
+
27
+ File.open("#{name}.yml", "w") do |file|
26
28
  file.write score.to_yaml
27
- end
29
+ end
30
+
31
+ File.open("#{name}_packed.yml", "w") do |file|
32
+ file.write score.pack.to_yaml
33
+ end
@@ -0,0 +1,22 @@
1
+ ---
2
+ start_meter: 4/4
3
+ meter_changes: {}
4
+ start_tempo: 120
5
+ tempo_changes: {}
6
+ program:
7
+ - 0...2
8
+ - 0...2
9
+ - 2...4
10
+ - 0...2
11
+ parts:
12
+ lead:
13
+ notes: /6Bb3 /4 /12Db4= /6Db4= /36Db4 /36Eb4 /36Db4 /6Ab3 /12Db4 /6Bb3 /4 /12Db4=
14
+ /4Db4= /8=Db4 /8C4 /6C4 /4 /12Eb4= /6Eb4= /36Eb4 /36F4 /36Eb4 /6Bb3 /12Eb4 /6C4
15
+ /4 /12Eb4= /4Eb4= /8=Eb4 /8D4
16
+ start_dynamic: 0.625
17
+ dynamic_changes: {}
18
+ bass:
19
+ notes: /6Bb2 /4 /3Ab2 /6F2 /12Ab2 /6Bb2 /4 /3Ab2 /4Ab2 /6C3 /4 /3Bb2 /6G2 /12Bb2
20
+ /6C3 /4 /3Bb2 /4Bb2
21
+ start_dynamic: 0.5
22
+ dynamic_changes: {}
@@ -8,7 +8,7 @@ include Meters
8
8
  include Parsing
9
9
 
10
10
  score = Score.new(FOUR_FOUR, 120) do |s|
11
- s.program = Program.new([0..2,0..6])
11
+ s.program = Program.new([0...2,0...6])
12
12
  s.parts["bass"] = Part.new(Dynamics::MF) do |p|
13
13
  p.notes = notes("/4Eb2 /4 /4Bb2 /4 /4Eb2 /8 /8B2 /4Bb2 /4Ab2")
14
14
  p.notes += notes("/4Bb2 /8 /8F3= /2F3 /4Bb2 /8 /8F3= /2F3")
@@ -16,6 +16,12 @@ score = Score.new(FOUR_FOUR, 120) do |s|
16
16
  end
17
17
  end
18
18
 
19
- File.open("missed_connection.yml", "w") do |file|
19
+ name = File.basename(__FILE__,".rb")
20
+
21
+ File.open("#{name}.yml", "w") do |file|
20
22
  file.write score.to_yaml
21
23
  end
24
+
25
+ File.open("#{name}_packed.yml", "w") do |file|
26
+ file.write score.pack.to_yaml
27
+ end
@@ -267,8 +267,8 @@ program: !ruby/object:Music::Transcription::Program
267
267
  - !ruby/range
268
268
  begin: 0
269
269
  end: 2
270
- excl: false
270
+ excl: true
271
271
  - !ruby/range
272
272
  begin: 0
273
273
  end: 6
274
- excl: false
274
+ excl: true
@@ -0,0 +1,14 @@
1
+ ---
2
+ start_meter: 4/4
3
+ meter_changes: {}
4
+ start_tempo: 120
5
+ tempo_changes: {}
6
+ program:
7
+ - 0...2
8
+ - 0...6
9
+ parts:
10
+ bass:
11
+ notes: /4Eb2 /4 /4Bb2 /4 /4Eb2 /8 /8B2 /4Bb2 /4Ab2 /4Bb2 /8 /8F3= /2F3 /4Bb2 /8
12
+ /8F3= /2F3 /4B2 /8 /8Gb3= /2Gb3 /8 /8Gb3= /2Gb3
13
+ start_dynamic: 0.625
14
+ dynamic_changes: {}
@@ -23,6 +23,12 @@ score = Score.new(FOUR_FOUR, 120) do |s|
23
23
  end
24
24
  end
25
25
 
26
- File.open("song1.yml", "w") do |file|
26
+ name = File.basename(__FILE__,".rb")
27
+
28
+ File.open("#{name}.yml", "w") do |file|
27
29
  file.write score.to_yaml
28
30
  end
31
+
32
+ File.open("#{name}_packed.yml", "w") do |file|
33
+ file.write score.pack.to_yaml
34
+ end
@@ -0,0 +1,19 @@
1
+ ---
2
+ start_meter: 4/4
3
+ meter_changes: {}
4
+ start_tempo: 120
5
+ tempo_changes: {}
6
+ program:
7
+ - 0...4.0
8
+ - 0...4.0
9
+ parts:
10
+ 1:
11
+ notes: 3/8C2 /4Eb2 5/16F2 /16Eb2 /8 /4C2 /4Eb2 3/8 3/8C2 /4Eb2 5/16F2 /16Eb2 /8
12
+ /4C2 /4Eb2
13
+ start_dynamic: 0.625
14
+ dynamic_changes: {}
15
+ 2:
16
+ notes: /8 /8Bb3 /8Bb3 /8Bb3 /8Bb3 /4C4 /4A3 /8G3 /8F3 5/16=G3 /16=F3 /8E3 /8 /8
17
+ /8Bb3 /8Bb3 /8Bb3 /8Bb3 /4C4 /8A3 /8E4 /8=E4 /8=D4 /8C4
18
+ start_dynamic: 0.625
19
+ dynamic_changes: {}
@@ -22,7 +22,12 @@ score = Score.new(FOUR_FOUR, 120) do |s|
22
22
  end
23
23
  end
24
24
 
25
- File.open("song2.yml", "w") do |file|
25
+ name = File.basename(__FILE__,".rb")
26
+
27
+ File.open("#{name}.yml", "w") do |file|
26
28
  file.write score.to_yaml
27
29
  end
28
30
 
31
+ File.open("#{name}_packed.yml", "w") do |file|
32
+ file.write score.pack.to_yaml
33
+ end
@@ -0,0 +1,21 @@
1
+ ---
2
+ start_meter: 4/4
3
+ meter_changes: {}
4
+ start_tempo: 120
5
+ tempo_changes: {}
6
+ program:
7
+ - 0...4.0
8
+ - 0...4.0
9
+ parts:
10
+ 1:
11
+ notes: 1C4 1Bb3 1Ab3 /2G3 /2Bb3
12
+ start_dynamic: 0.625
13
+ dynamic_changes: {}
14
+ 2:
15
+ notes: 3/8E5 1D5 1C5 5/8C5 /2C5 /2D5
16
+ start_dynamic: 0.625
17
+ dynamic_changes: {}
18
+ 3:
19
+ notes: ''
20
+ start_dynamic: 0.625
21
+ dynamic_changes: {}
@@ -19,8 +19,10 @@ require 'music-transcription/model/meters'
19
19
  require 'music-transcription/model/score'
20
20
 
21
21
  require 'treetop'
22
- require 'music-transcription/parsing/nonnegative_integer_parsing'
23
- require 'music-transcription/parsing/positive_integer_parsing'
22
+ require 'music-transcription/parsing/numbers/nonnegative_integer_parsing'
23
+ require 'music-transcription/parsing/numbers/positive_integer_parsing'
24
+ require 'music-transcription/parsing/numbers/nonnegative_float_parsing'
25
+ require 'music-transcription/parsing/numbers/nonnegative_rational_parsing'
24
26
  require 'music-transcription/parsing/pitch_parsing'
25
27
  require 'music-transcription/parsing/pitch_node'
26
28
  require 'music-transcription/parsing/duration_parsing'
@@ -29,6 +31,12 @@ require 'music-transcription/parsing/articulation_parsing'
29
31
  require 'music-transcription/parsing/link_parsing'
30
32
  require 'music-transcription/parsing/link_nodes'
31
33
  require 'music-transcription/parsing/note_parsing'
32
- require 'music-transcription/parsing/note_nodes'
34
+ require 'music-transcription/parsing/note_node'
33
35
  require 'music-transcription/parsing/meter_parsing'
36
+ require 'music-transcription/parsing/segment_parsing'
34
37
  require 'music-transcription/parsing/convenience_methods'
38
+
39
+ require 'music-transcription/packing/change_packing'
40
+ require 'music-transcription/packing/part_packing'
41
+ require 'music-transcription/packing/program_packing'
42
+ require 'music-transcription/packing/score_packing'
@@ -21,13 +21,16 @@ class Link
21
21
  def transpose! diff
22
22
  # do nothing, of course
23
23
  end
24
+
25
+ def to_s; "="; end
24
26
  end
25
27
 
26
28
  class TargetedLink < Link
27
- attr_accessor :target_pitch
29
+ attr_accessor :target_pitch, :link_char
28
30
 
29
- def initialize target_pitch
31
+ def initialize target_pitch, link_char
30
32
  @target_pitch = target_pitch
33
+ @link_char = link_char
31
34
  end
32
35
 
33
36
  def ==(other)
@@ -37,12 +40,27 @@ class Link
37
40
  def transpose! diff
38
41
  @target_pitch += diff
39
42
  end
43
+
44
+ def to_s
45
+ @link_char + @target_pitch.to_s
46
+ end
47
+ end
48
+
49
+ class Glissando < TargetedLink
50
+ def initialize(target_pitch); super(target_pitch,"~"); end
40
51
  end
41
52
 
42
- class Glissando < TargetedLink; end
43
- class Portamento < TargetedLink; end
44
- class Slur < TargetedLink; end
45
- class Legato < TargetedLink; end
53
+ class Portamento < TargetedLink
54
+ def initialize(target_pitch); super(target_pitch,"/"); end
55
+ end
56
+
57
+ class Slur < TargetedLink
58
+ def initialize(target_pitch); super(target_pitch,"="); end
59
+ end
60
+
61
+ class Legato < TargetedLink
62
+ def initialize(target_pitch); super(target_pitch,"-"); end
63
+ end
46
64
  end
47
65
 
48
66
  end
@@ -36,6 +36,16 @@ class Meter
36
36
  return (@beats_per_measure == other.beats_per_measure &&
37
37
  @beat_duration == other.beat_duration)
38
38
  end
39
+
40
+ def to_s
41
+ if beat_duration.numerator == 1
42
+ num = beats_per_measure * beat_duration.numerator
43
+ den = beat_duration.denominator
44
+ "#{num}/#{den}"
45
+ else
46
+ "#{beats_per_measure}*#{beat_duration}"
47
+ end
48
+ end
39
49
  end
40
50
 
41
51
  end
@@ -5,7 +5,7 @@ module Meters
5
5
  TWO_FOUR = Meter.new(2,"1/4".to_r)
6
6
  THREE_FOUR = Meter.new(3,"1/4".to_r)
7
7
  FOUR_FOUR = Meter.new(4,"1/4".to_r)
8
- SIX_EIGHT = Meter.new(6,"1/8".to_r)
8
+ SIX_EIGHT = Meter.new(2,"3/8".to_r)
9
9
  end
10
10
  end
11
11
  end
@@ -65,6 +65,38 @@ class Note
65
65
  @duration *= ratio
66
66
  return self
67
67
  end
68
+
69
+ def to_s
70
+ d = @duration.to_r
71
+ if d.denominator == 1
72
+ dur_str = "#{d.numerator}"
73
+ elsif d.numerator == 1
74
+ dur_str = "/#{d.denominator}"
75
+ else
76
+ dur_str = d.to_s
77
+ end
78
+
79
+ art_str = case @articulation
80
+ when Articulations::SLUR then "="
81
+ when Articulations::LEGATO then "-"
82
+ when Articulations::TENUTO then "_"
83
+ when Articulations::PORTATO then "%"
84
+ when Articulations::STACCATO then "."
85
+ when Articulations::STACCATISSIMO then "'"
86
+ else ""
87
+ end
88
+
89
+ pitch_links_str = @pitches.map do |p|
90
+ if @links.has_key?(p)
91
+ p.to_s + @links[p].to_s
92
+ else
93
+ p.to_s
94
+ end
95
+ end.join(",")
96
+
97
+ acc_str = @accented ? "!" : ""
98
+ return dur_str + art_str + pitch_links_str + acc_str
99
+ end
68
100
 
69
101
  def self.add_note_method(name, dur)
70
102
  self.class.send(:define_method,name.to_sym) do |pitches = [], articulation: DEFAULT_ARTICULATION, links: {}, accented: false|
@@ -150,6 +150,25 @@ class Pitch
150
150
  return self
151
151
  end
152
152
 
153
+ def to_s(sharpit = false)
154
+ letter = case semitone
155
+ when 0 then "C"
156
+ when 1 then sharpit ? "C#" : "Db"
157
+ when 2 then "D"
158
+ when 3 then sharpit ? "D#" : "Eb"
159
+ when 4 then "E"
160
+ when 5 then "F"
161
+ when 6 then sharpit ? "F#" : "Gb"
162
+ when 7 then "G"
163
+ when 8 then sharpit ? "G#" : "Ab"
164
+ when 9 then "A"
165
+ when 10 then sharpit ? "A#" : "Bb"
166
+ when 11 then "B"
167
+ end
168
+
169
+ return letter + octave.to_s
170
+ end
171
+
153
172
  def self.make_from_freq(freq)
154
173
  pitch = Pitch.new()
155
174
  pitch.ratio = freq / BASE_FREQ
@@ -0,0 +1,27 @@
1
+ module Music
2
+ module Transcription
3
+
4
+ class Change
5
+ class Immediate < Change
6
+ def pack
7
+ [ @value ]
8
+ end
9
+ end
10
+
11
+ class Gradual < Change
12
+ def pack
13
+ [ @value, @duration ]
14
+ end
15
+ end
16
+
17
+ def self.unpack packing
18
+ case packing.size
19
+ when 1 then Immediate.new(*packing)
20
+ when 2 then Gradual.new(*packing)
21
+ else raise ArgumentError, "bad array size"
22
+ end
23
+ end
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,33 @@
1
+ module Music
2
+ module Transcription
3
+
4
+ class Part
5
+ def pack
6
+ packed_notes = notes.map {|n| n.to_s }.join(" ")
7
+ packed_dcs = Hash[ dynamic_changes.map do |offset,change|
8
+ [ offset, change.pack ]
9
+ end ]
10
+
11
+ {
12
+ 'notes' => packed_notes,
13
+ 'start_dynamic' => start_dynamic,
14
+ 'dynamic_changes' => packed_dcs
15
+ }
16
+ end
17
+
18
+ def self.unpack packing
19
+ unpacked_notes = Parsing::notes(packing["notes"])
20
+ unpacked_dcs = Hash[ packing["dynamic_changes"].map do |offset,change|
21
+ [ offset,Change.unpack(change) ]
22
+ end ]
23
+
24
+ new(
25
+ packing["start_dynamic"],
26
+ notes: unpacked_notes,
27
+ dynamic_changes: unpacked_dcs
28
+ )
29
+ end
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,18 @@
1
+ module Music
2
+ module Transcription
3
+
4
+ class Program
5
+ def pack
6
+ @segments.map do |seg|
7
+ seg.to_s
8
+ end
9
+ end
10
+
11
+ def self.unpack packing
12
+ segments = packing.map {|str| Parsing::segment(str) }
13
+ new segments
14
+ end
15
+ end
16
+
17
+ end
18
+ end