beats 1.2.0 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. data/LICENSE +1 -1
  2. data/README.markdown +28 -10
  3. data/bin/beats +9 -7
  4. data/lib/audioengine.rb +172 -0
  5. data/lib/audioutils.rb +73 -0
  6. data/lib/beats.rb +14 -15
  7. data/lib/beatswavefile.rb +17 -37
  8. data/lib/kit.rb +148 -71
  9. data/lib/pattern.rb +20 -117
  10. data/lib/patternexpander.rb +111 -0
  11. data/lib/song.rb +78 -132
  12. data/lib/songoptimizer.rb +29 -33
  13. data/lib/songparser.rb +70 -45
  14. data/lib/track.rb +11 -82
  15. data/test/audioengine_test.rb +261 -0
  16. data/test/audioutils_test.rb +45 -0
  17. data/test/fixtures/expected_output/example_split_mono_16-hh_closed.wav +0 -0
  18. data/test/{examples/split-agogo_high.wav → fixtures/expected_output/example_split_mono_16-hh_closed2.wav} +0 -0
  19. data/test/fixtures/expected_output/example_split_mono_8-hh_closed.wav +0 -0
  20. data/test/{examples/split-tom4.wav → fixtures/expected_output/example_split_mono_8-hh_closed2.wav} +0 -0
  21. data/test/fixtures/expected_output/example_split_stereo_16-hh_closed.wav +0 -0
  22. data/test/fixtures/expected_output/example_split_stereo_16-hh_closed2.wav +0 -0
  23. data/test/fixtures/expected_output/example_split_stereo_8-hh_closed.wav +0 -0
  24. data/test/fixtures/expected_output/example_split_stereo_8-hh_closed2.wav +0 -0
  25. data/test/fixtures/invalid/{bad_structure.txt → bad_flow.txt} +2 -2
  26. data/test/fixtures/invalid/bad_repeat_count.txt +1 -1
  27. data/test/fixtures/invalid/bad_rhythm.txt +1 -1
  28. data/test/fixtures/invalid/bad_tempo.txt +1 -1
  29. data/test/fixtures/invalid/{no_structure.txt → no_flow.txt} +1 -1
  30. data/test/fixtures/invalid/pattern_with_no_tracks.txt +1 -1
  31. data/test/fixtures/invalid/sound_in_kit_not_found.txt +1 -1
  32. data/test/fixtures/invalid/sound_in_kit_wrong_format.txt +10 -0
  33. data/test/fixtures/invalid/sound_in_track_not_found.txt +1 -1
  34. data/test/fixtures/invalid/sound_in_track_wrong_format.txt +8 -0
  35. data/test/fixtures/invalid/template.txt +1 -1
  36. data/test/fixtures/valid/example_mono_16.txt +5 -3
  37. data/test/fixtures/valid/example_mono_8.txt +5 -3
  38. data/test/fixtures/valid/example_no_kit.txt +1 -1
  39. data/test/fixtures/valid/example_stereo_16.txt +7 -4
  40. data/test/fixtures/valid/example_stereo_8.txt +5 -3
  41. data/test/fixtures/valid/example_with_empty_track.txt +1 -1
  42. data/test/fixtures/valid/example_with_kit.txt +1 -1
  43. data/test/fixtures/valid/multiple_tracks_same_sound.txt +33 -0
  44. data/test/fixtures/valid/no_tempo.txt +1 -1
  45. data/test/fixtures/valid/optimize_pattern_collision.txt +28 -0
  46. data/test/fixtures/valid/pattern_with_overflow.txt +1 -1
  47. data/test/fixtures/valid/repeats_not_specified.txt +2 -2
  48. data/test/fixtures/valid/with_structure.txt +10 -0
  49. data/test/fixtures/yaml/song_yaml.txt +5 -5
  50. data/test/includes.rb +4 -2
  51. data/test/integration.rb +3 -3
  52. data/test/kit_test.rb +136 -109
  53. data/test/pattern_test.rb +31 -131
  54. data/test/patternexpander_test.rb +142 -0
  55. data/test/song_test.rb +104 -102
  56. data/test/songoptimizer_test.rb +52 -38
  57. data/test/songparser_test.rb +79 -46
  58. data/test/sounds/composite_snare_mono_8_tom3_mono_16_mono_16.wav +0 -0
  59. data/test/sounds/composite_snare_mono_8_tom3_mono_8_mono_16.wav +0 -0
  60. data/test/sounds/composite_snare_stereo_16_tom3_mono_16_stereo_16.wav +0 -0
  61. data/test/sounds/composite_snare_stereo_8_tom3_mono_16_stereo_16.wav +0 -0
  62. data/test/track_test.rb +30 -185
  63. metadata +56 -24
  64. data/lib/songsplitter.rb +0 -38
  65. data/test/examples/combined.wav +0 -0
  66. data/test/examples/split-bass.wav +0 -0
  67. data/test/examples/split-hh_closed.wav +0 -0
  68. data/test/examples/split-snare.wav +0 -0
  69. data/test/examples/split-tom2.wav +0 -0
@@ -3,24 +3,23 @@ class InvalidRhythmError < RuntimeError; end
3
3
  class Track
4
4
  REST = "."
5
5
  BEAT = "X"
6
+ BARLINE = "|"
6
7
 
7
- def initialize(name, wave_data, rhythm)
8
+ def initialize(name, rhythm)
8
9
  # TODO: Add validation for input parameters
9
-
10
- @wave_data = wave_data
11
10
  @name = name
12
- @sample_data = nil
13
- @overflow = nil
14
11
  self.rhythm = rhythm
15
12
  end
16
13
 
14
+ # TODO: What to have this invoked when setting like this?
15
+ # track.rhythm[x..y] = whatever
17
16
  def rhythm=(rhythm)
18
- @rhythm = rhythm
17
+ @rhythm = rhythm.delete(BARLINE)
19
18
  beats = []
20
19
 
21
20
  beat_length = 0
22
21
  #rhythm.each_char{|ch|
23
- rhythm.each_byte do |ch|
22
+ @rhythm.each_byte do |ch|
24
23
  ch = ch.chr
25
24
  if ch == BEAT
26
25
  beats << beat_length
@@ -39,82 +38,12 @@ class Track
39
38
  beats = [0]
40
39
  end
41
40
  @beats = beats
42
-
43
- # Remove any cached sample data
44
- @sample_data = nil
45
- @overflow = nil
46
- end
47
-
48
- def intro_sample_length(tick_sample_length)
49
- return @beats[0] * tick_sample_length.floor
50
41
  end
51
42
 
52
- def sample_length(tick_sample_length)
53
- total_ticks = @beats.inject(0) {|sum, n| sum + n}
54
- return (total_ticks * tick_sample_length).floor
55
- end
56
-
57
- def sample_length_with_overflow(tick_sample_length)
58
- temp_sample_length = sample_length(tick_sample_length)
59
-
60
- unless @beats == [0]
61
- beat_sample_length = @beats.last * tick_sample_length
62
- if(@wave_data.length > beat_sample_length)
63
- temp_sample_length += @wave_data.length - beat_sample_length.floor
64
- end
65
- end
66
-
67
- return temp_sample_length.floor
68
- end
69
-
70
- def tick_count
43
+ def step_count
71
44
  return @rhythm.length
72
45
  end
73
-
74
- def sample_data(tick_sample_length)
75
- actual_sample_length = sample_length(tick_sample_length)
76
- full_sample_length = sample_length_with_overflow(tick_sample_length)
77
-
78
- if @sample_data == nil
79
- fill_value = (@wave_data.first.class == Array) ? [0, 0] : 0
80
- output_data = [].fill(fill_value, 0, full_sample_length)
81
-
82
- if full_sample_length > 0
83
- remainder = 0.0
84
- offset = @beats[0] * tick_sample_length
85
- remainder += (@beats[0] * tick_sample_length) - (@beats[0] * tick_sample_length).floor
86
-
87
- @beats[1...(@beats.length)].each do |beat_length|
88
- beat_sample_length = beat_length * tick_sample_length
89
-
90
- remainder += beat_sample_length - beat_sample_length.floor
91
- if remainder >= 1.0
92
- beat_sample_length += 1
93
- remainder -= 1.0
94
- end
95
-
96
- output_data[offset...(offset + wave_data.length)] = wave_data
97
- offset += beat_sample_length.floor
98
- end
99
-
100
- if full_sample_length > actual_sample_length
101
- @sample_data = output_data[0...offset]
102
- @overflow = output_data[actual_sample_length...full_sample_length]
103
- else
104
- @sample_data = output_data
105
- @overflow = []
106
- end
107
- else
108
- @sample_data = []
109
- @overflow = []
110
- end
111
- end
112
-
113
- primary_sample_data = @sample_data.dup
114
-
115
- return {:primary => primary_sample_data, :overflow => @overflow}
116
- end
117
-
118
- attr_accessor :name, :wave_data
119
- attr_reader :rhythm
120
- end
46
+
47
+ attr_accessor :name
48
+ attr_reader :rhythm, :beats
49
+ end
@@ -0,0 +1,261 @@
1
+ $:.unshift File.join(File.dirname(__FILE__),'..','lib')
2
+
3
+ require 'test/includes'
4
+
5
+ # Make private methods public for testing
6
+ class MockAudioEngine < AudioEngine
7
+ def generate_track_sample_data(track, sound)
8
+ super
9
+ end
10
+
11
+ def composite_pattern_tracks(pattern)
12
+ super
13
+ end
14
+
15
+ attr_accessor :step_sample_length
16
+ end
17
+
18
+ # Allow setting sample data directly, instead of loading from a file
19
+ class MockKit < Kit
20
+ attr_accessor :sound_bank, :num_channels
21
+ end
22
+
23
+ class AudioEngineTest < Test::Unit::TestCase
24
+ FIXTURES = [:repeats_not_specified,
25
+ :pattern_with_overflow,
26
+ :example_no_kit,
27
+ :example_with_kit]
28
+
29
+ def load_fixtures
30
+ test_engines = {}
31
+ base_path = File.dirname(__FILE__) + "/.."
32
+ song_parser = SongParser.new()
33
+
34
+ test_engines[:blank] = AudioEngine.new(Song.new(), Kit.new(base_path, {}))
35
+
36
+ FIXTURES.each do |fixture_name|
37
+ song, kit = song_parser.parse(base_path, File.read("test/fixtures/valid/#{fixture_name}.txt"))
38
+ test_engines[fixture_name] = AudioEngine.new(song, kit)
39
+ end
40
+
41
+ return test_engines
42
+ end
43
+
44
+ def test_initialize
45
+ test_engines = load_fixtures()
46
+
47
+ assert_equal(5512.5, test_engines[:blank].step_sample_length)
48
+ assert_equal(6615.0, test_engines[:repeats_not_specified].step_sample_length)
49
+ end
50
+
51
+
52
+ # S Sample data for a sound. Unrealistically short for clarity.
53
+ # SL Sound when step sample length is longer than full sound length
54
+ # SS Sound when step sample length is less than full sound length
55
+ # SO Sound overflow when step sample length is less than full sound length
56
+ # TE A step with no sound, with length equal to S
57
+ # TL A step with no sound, longer than full sound length
58
+ # TS A step with no sound, shorter than full sound length
59
+ # Z A zero sample
60
+
61
+ MONO_KIT = MockKit.new(".", {})
62
+ MONO_KIT.sound_bank = { "S" => [-100, 200, 300, -400],
63
+ "SL" => [-100, 200, 300, -400, 0, 0],
64
+ "SS" => [-100, 200],
65
+ "SO" => [300, -400],
66
+ "TE" => [0, 0, 0, 0],
67
+ "TL" => [0, 0, 0, 0, 0, 0],
68
+ "TS" => [0, 0],
69
+ "Z" => [0] }
70
+ MONO_KIT.num_channels = 1
71
+
72
+ STEREO_KIT = MockKit.new(".", {})
73
+ STEREO_KIT.sound_bank = { "S" => [[-100, 800], [200, -700], [300, -600], [-400, 400]],
74
+ "SL" => [[-100, 800], [200, -700], [300, -600], [-400, 400], [0, 0], [0, 0]],
75
+ "SS" => [[-100, 800], [200, -700]],
76
+ "SO" => [[300, -600], [-400, 400]],
77
+ "TE" => [[0, 0], [0, 0], [0, 0], [0, 0]],
78
+ "TL" => [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]],
79
+ "TS" => [[0, 0], [0, 0]],
80
+ "Z" => [[0, 0]] }
81
+ STEREO_KIT.num_channels = 2
82
+
83
+
84
+ # These tests use unrealistically short sounds and step sample lengths, to make tests easier to work with.
85
+ def test_generate_track_sample_data
86
+ [MONO_KIT, STEREO_KIT].each do |kit|
87
+ s = kit.get_sample_data("S")
88
+ sl = kit.get_sample_data("SL")
89
+ ss = kit.get_sample_data("SS")
90
+ so = kit.get_sample_data("SO")
91
+ te = kit.get_sample_data("TE")
92
+ tl = kit.get_sample_data("TL")
93
+ ts = kit.get_sample_data("TS")
94
+ z = kit.get_sample_data("Z")
95
+
96
+ # 1.) Tick sample length is equal to the length of the sound sample data.
97
+ # When this is the case, overflow should never occur.
98
+ # In practice, this will probably not occur often, but these tests act as a form of sanity check.
99
+ helper_generate_track_sample_data kit, "", 4, []
100
+ helper_generate_track_sample_data kit, "X", 4, s
101
+ helper_generate_track_sample_data kit, "X.", 4, s + te
102
+ helper_generate_track_sample_data kit, ".X", 4, te + s
103
+ helper_generate_track_sample_data kit, "...X.", 4, (te * 3) + s + te
104
+ helper_generate_track_sample_data kit, ".X.XX.", 4, te + s + te + s + s + te
105
+ helper_generate_track_sample_data kit, "...", 4, te * 3
106
+
107
+ # 2A.) Tick sample length is longer than the sound sample data. This is similar to (1), except that there should
108
+ # be some extra silence after the end of each trigger.
109
+ # Like (1), overflow should never occur.
110
+ helper_generate_track_sample_data kit, "", 6, []
111
+ helper_generate_track_sample_data kit, "X", 6, sl
112
+ helper_generate_track_sample_data kit, "X.", 6, sl + tl
113
+ helper_generate_track_sample_data kit, ".X", 6, tl + sl
114
+ helper_generate_track_sample_data kit, "...X.", 6, (tl * 3) + sl + tl
115
+ helper_generate_track_sample_data kit, ".X.XX.", 6, tl + sl + tl + sl + sl + tl
116
+ helper_generate_track_sample_data kit, "...", 6, (te + ts) * 3
117
+
118
+ # 2B.) Tick sample length is longer than the sound sample data, but not by an integer amount.
119
+ #
120
+ # Each step of 5.83 samples should end on the following boundaries:
121
+ # Tick: 1, 2, 3, 4, 5, 6
122
+ # Raw: 0.0, 5.83, 11.66, 17.49, 23.32, 29.15, 34.98
123
+ # Quantized: 0, 5, 11, 17, 23, 29, 34
124
+ helper_generate_track_sample_data kit, "", 5.83, []
125
+ helper_generate_track_sample_data kit, "X", 5.83, sl[0..4]
126
+ helper_generate_track_sample_data kit, "X.", 5.83, sl[0..4] + tl
127
+ helper_generate_track_sample_data kit, ".X", 5.83, tl[0..4] + sl
128
+ helper_generate_track_sample_data kit, "...X.", 5.83, (z * 17) + sl + tl
129
+ helper_generate_track_sample_data kit, ".X.XX.", 5.83, tl[0..4] + sl + tl + sl + sl + tl[0..4]
130
+ helper_generate_track_sample_data kit, "...", 5.83, z * 17
131
+
132
+ # 3A.) Tick sample length is shorter than the sound sample data. Overflow will now occur!
133
+ helper_generate_track_sample_data kit, "", 2, [], []
134
+ helper_generate_track_sample_data kit, "X", 2, ss, so
135
+ helper_generate_track_sample_data kit, "X.", 2, s, []
136
+ helper_generate_track_sample_data kit, ".X", 2, ts + ss, so
137
+ helper_generate_track_sample_data kit, "...X.", 2, (ts * 3) + s, []
138
+ helper_generate_track_sample_data kit, ".X.XX.", 2, ts + s + ss + s, []
139
+ helper_generate_track_sample_data kit, "...", 2, z * 6, []
140
+
141
+ # 3B.) Tick sample length is shorter than sound sample data, such that a beat other than the final one
142
+ # would extend past the end of the rhythm if not cut off. Make sure that the sample data array doesn't
143
+ # inadvertently lengthen as a result.
144
+ #helper_generate_track_sample_data kit, "XX", 1, [-100, -100], [200, 300, -400]
145
+
146
+ # 3C.) Tick sample length is shorter than the sound sample data, but not by an integer amount.
147
+ #
148
+ # Each step of 1.83 samples should end on the following boundaries:
149
+ # Tick: 1, 2, 3, 4, 5, 6
150
+ # Raw: 0.0, 1.83, 3.66, 5.49, 7.32, 9.15, 10.98
151
+ # Quantized: 0, 1, 3, 5, 7, 9, 10
152
+ helper_generate_track_sample_data kit, "", 1.83, []
153
+ helper_generate_track_sample_data kit, "X", 1.83, s[0..0], s[1..3]
154
+ helper_generate_track_sample_data kit, "X.", 1.83, s[0..2], s[3..3]
155
+ helper_generate_track_sample_data kit, ".X", 1.83, z + s[0..1], s[2..3]
156
+ helper_generate_track_sample_data kit, "...X.", 1.83, (z * 5) + s, []
157
+ helper_generate_track_sample_data kit, ".X.XX.", 1.83, z + s + ss + s[0..2], s[3..3]
158
+ helper_generate_track_sample_data kit, "...", 1.83, z * 5, []
159
+ end
160
+ end
161
+
162
+ def helper_generate_track_sample_data(kit, rhythm, step_sample_length, expected_primary, expected_overflow = [])
163
+ track = Track.new("foo", rhythm)
164
+ engine = MockAudioEngine.new(Song.new(), kit)
165
+ engine.step_sample_length = step_sample_length
166
+ actual = engine.generate_track_sample_data(track, kit.get_sample_data("S"))
167
+
168
+ assert_equal(Hash, actual.class)
169
+ assert_equal(["overflow", "primary"], actual.keys.map{|key| key.to_s}.sort)
170
+ assert_equal(expected_primary, actual[:primary])
171
+ assert_equal(expected_overflow, actual[:overflow])
172
+ end
173
+
174
+ def test_composite_pattern_tracks
175
+ no_overflow_pattern = Pattern.new("no_overflow")
176
+ no_overflow_pattern.track "S", "X..."
177
+ no_overflow_pattern.track "SO", "X.X."
178
+ no_overflow_pattern.track "S", "X.XX"
179
+
180
+ overflow_pattern = Pattern.new("overflow")
181
+ overflow_pattern.track "S", "X..X"
182
+ overflow_pattern.track "SO", "XX.X"
183
+ overflow_pattern.track "SL", ".X.X"
184
+
185
+
186
+ # Simple case, no overflow (stereo)
187
+ engine = MockAudioEngine.new(Song.new(), MONO_KIT)
188
+ engine.step_sample_length = 4
189
+ primary, overflow = engine.composite_pattern_tracks(no_overflow_pattern)
190
+ assert_equal([
191
+ -100 + 300 + -100, 200 + -400 + 200, 300 + 0 + 300, -400 + 0 + -400,
192
+ 0 + 0 + 0, 0 + 0 + 0, 0 + 0 + 0, 0 + 0 + 0,
193
+ 0 + 300 + -100, 0 + -400 + 200, 0 + 0 + 300, 0 + 0 + -400,
194
+ 0 + 0 + -100, 0 + 0 + 200, 0 + 0 + 300, 0 + 0 + -400,
195
+ ],
196
+ primary)
197
+ assert_equal({"S" => [], "SO" => [], "S2" => []}, overflow)
198
+
199
+
200
+ # Simple case, no overflow (stereo)
201
+ engine = MockAudioEngine.new(Song.new(), STEREO_KIT)
202
+ engine.step_sample_length = 4
203
+ primary, overflow = engine.composite_pattern_tracks(no_overflow_pattern)
204
+ assert_equal([
205
+ [-100 + 300 + -100, 800 + -600 + 800],
206
+ [200 + -400 + 200, -700 + 400 + -700],
207
+ [300 + 0 + 300, -600 + 0 + -600],
208
+ [-400 + 0 + -400, 400 + 0 + 400],
209
+ [0 + 0 + 0, 0 + 0 + 0],
210
+ [0 + 0 + 0, 0 + 0 + 0],
211
+ [0 + 0 + 0, 0 + 0 + 0],
212
+ [0 + 0 + 0, 0 + 0 + 0],
213
+ [0 + 300 + -100, 0 + -600 + 800],
214
+ [0 + -400 + 200, 0 + 400 + -700],
215
+ [0 + 0 + 300, 0 + 0 + -600],
216
+ [0 + 0 + -400, 0 + 0 + 400],
217
+ [0 + 0 + -100, 0 + 0 + 800],
218
+ [0 + 0 + 200, 0 + 0 + -700],
219
+ [0 + 0 + 300, 0 + 0 + -600],
220
+ [0 + 0 + -400, 0 + 0 + 400],
221
+ ],
222
+ primary)
223
+ assert_equal({"S" => [], "SO" => [], "S2" => []}, overflow)
224
+
225
+
226
+ # Some overflow (mono)
227
+ engine = MockAudioEngine.new(Song.new(), MONO_KIT)
228
+ engine.step_sample_length = 3
229
+ primary, overflow = engine.composite_pattern_tracks(overflow_pattern)
230
+ assert_equal([
231
+ -100 + 300 + 0, 200 + -400 + 0, 300 + 0 + 0,
232
+ -400 + 300 + -100, 0 + -400 + 200, 0 + 0 + 300,
233
+ 0 + 0 + -400, 0 + 0 + 0, 0 + 0 + 0,
234
+ -100 + 300 + -100, 200 + -400 + 200, 300 + 0 + 300,
235
+ ],
236
+ primary)
237
+ assert_equal({"S" => [-400], "SO" => [], "SL" => [-400, 0, 0]}, overflow)
238
+
239
+
240
+ # Some overflow (stereo)
241
+ engine = MockAudioEngine.new(Song.new(), STEREO_KIT)
242
+ engine.step_sample_length = 3
243
+ primary, overflow = engine.composite_pattern_tracks(overflow_pattern)
244
+ assert_equal([
245
+ [-100 + 300 + 0, 800 + -600 + 0],
246
+ [200 + -400 + 0, -700 + 400 + 0],
247
+ [300 + 0 + 0, -600 + 0 + 0],
248
+ [-400 + 300 + -100, 400 + -600 + 800],
249
+ [0 + -400 + 200, 0 + 400 + -700],
250
+ [0 + 0 + 300, 0 + 0 + -600],
251
+ [0 + 0 + -400, 0 + 0 + 400],
252
+ [0 + 0 + 0, 0 + 0 + 0],
253
+ [0 + 0 + 0, 0 + 0 + 0],
254
+ [-100 + 300 + -100, 800 + -600 + 800],
255
+ [200 + -400 + 200, -700 + 400 + -700],
256
+ [300 + 0 + 300, -600 + 0 + -600],
257
+ ],
258
+ primary)
259
+ assert_equal({"S" => [[-400, 400]], "SO" => [], "SL" => [[-400, 400], [0, 0], [0, 0]]}, overflow)
260
+ end
261
+ end
@@ -0,0 +1,45 @@
1
+ $:.unshift File.join(File.dirname(__FILE__),'..','lib')
2
+
3
+ require 'test/includes'
4
+
5
+ class AudioUtilsTest < Test::Unit::TestCase
6
+ def test_composite
7
+ # Mono empty arrays
8
+ assert_equal([], AudioUtils.composite([], 1))
9
+ assert_equal([], AudioUtils.composite([[]], 1))
10
+ assert_equal([], AudioUtils.composite([[], [], [], []], 1))
11
+
12
+ # Stereo empty arrays
13
+ assert_equal([], AudioUtils.composite([], 2))
14
+ assert_equal([], AudioUtils.composite([[]], 2))
15
+ assert_equal([], AudioUtils.composite([[], [], [], []], 2))
16
+
17
+ # Mono
18
+ assert_equal([10, 20, 30, 40], AudioUtils.composite([[10, 20, 30, 40]], 1))
19
+ assert_equal([10, 20, 30, 40], AudioUtils.composite([[10, 20, 30, 40], []], 1))
20
+ assert_equal([30, 50, 70, -10], AudioUtils.composite([[10, 20, 30, 40], [20, 30, 40, -50]], 1))
21
+ assert_equal([70, 80, 60], AudioUtils.composite([[20, 30], [10], [40, 50, 60]], 1))
22
+
23
+ # Stereo
24
+ assert_equal([[10, 20], [30, 40]], AudioUtils.composite([[[10, 20], [30, 40]]], 2))
25
+ assert_equal([[10, 20], [30, 40]], AudioUtils.composite([[[10, 20], [30, 40]], []], 2))
26
+ assert_equal([[30, 50], [70, -10]], AudioUtils.composite([[[10, 20], [30, 40]], [[20, 30], [40, -50]]], 2))
27
+ assert_equal([[90, 120], [120, 140], [100, 110]], AudioUtils.composite([[[20, 30], [40, 50]], [[10, 20]], [[60, 70], [80, 90], [100, 110]]], 2))
28
+ end
29
+
30
+ def test_scale
31
+ assert_equal([], AudioUtils.scale([], 1, 5))
32
+ assert_equal([], AudioUtils.scale([], 2, 5))
33
+ assert_equal([100, 200, 300, 400, 500], AudioUtils.scale([100, 200, 300, 400, 500], 1, 1))
34
+ assert_equal([20, 40, 60, 80, 100], AudioUtils.scale([100, 200, 300, 400, 500], 1, 5))
35
+
36
+ assert_equal([[100, 200], [300, 400], [500, 600]], AudioUtils.scale([[100, 200], [300, 400], [500, 600]], 2, 1))
37
+ assert_equal([[20, 40], [60, 80], [100, 120]], AudioUtils.scale([[100, 200], [300, 400], [500, 600]], 2, 5))
38
+ end
39
+
40
+ def test_step_sample_length
41
+ assert_equal(6615.0, AudioUtils.step_sample_length(44100, 100))
42
+ assert_equal(3307.5, AudioUtils.step_sample_length(44100, 200))
43
+ assert_equal(3307.5, AudioUtils.step_sample_length(22050, 100))
44
+ end
45
+ end
@@ -1,7 +1,7 @@
1
- # Invalid song, since the structure references a non-existent pattern
1
+ # Invalid song, since the flow references a non-existent pattern
2
2
  Song:
3
3
  Tempo: 100
4
- Structure:
4
+ Flow:
5
5
  - Verse: x2
6
6
  - Chorus: x1
7
7