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.
- data/LICENSE +1 -1
- data/README.markdown +28 -10
- data/bin/beats +9 -7
- data/lib/audioengine.rb +172 -0
- data/lib/audioutils.rb +73 -0
- data/lib/beats.rb +14 -15
- data/lib/beatswavefile.rb +17 -37
- data/lib/kit.rb +148 -71
- data/lib/pattern.rb +20 -117
- data/lib/patternexpander.rb +111 -0
- data/lib/song.rb +78 -132
- data/lib/songoptimizer.rb +29 -33
- data/lib/songparser.rb +70 -45
- data/lib/track.rb +11 -82
- data/test/audioengine_test.rb +261 -0
- data/test/audioutils_test.rb +45 -0
- data/test/fixtures/expected_output/example_split_mono_16-hh_closed.wav +0 -0
- data/test/{examples/split-agogo_high.wav → fixtures/expected_output/example_split_mono_16-hh_closed2.wav} +0 -0
- data/test/fixtures/expected_output/example_split_mono_8-hh_closed.wav +0 -0
- data/test/{examples/split-tom4.wav → fixtures/expected_output/example_split_mono_8-hh_closed2.wav} +0 -0
- data/test/fixtures/expected_output/example_split_stereo_16-hh_closed.wav +0 -0
- data/test/fixtures/expected_output/example_split_stereo_16-hh_closed2.wav +0 -0
- data/test/fixtures/expected_output/example_split_stereo_8-hh_closed.wav +0 -0
- data/test/fixtures/expected_output/example_split_stereo_8-hh_closed2.wav +0 -0
- data/test/fixtures/invalid/{bad_structure.txt → bad_flow.txt} +2 -2
- data/test/fixtures/invalid/bad_repeat_count.txt +1 -1
- data/test/fixtures/invalid/bad_rhythm.txt +1 -1
- data/test/fixtures/invalid/bad_tempo.txt +1 -1
- data/test/fixtures/invalid/{no_structure.txt → no_flow.txt} +1 -1
- data/test/fixtures/invalid/pattern_with_no_tracks.txt +1 -1
- data/test/fixtures/invalid/sound_in_kit_not_found.txt +1 -1
- data/test/fixtures/invalid/sound_in_kit_wrong_format.txt +10 -0
- data/test/fixtures/invalid/sound_in_track_not_found.txt +1 -1
- data/test/fixtures/invalid/sound_in_track_wrong_format.txt +8 -0
- data/test/fixtures/invalid/template.txt +1 -1
- data/test/fixtures/valid/example_mono_16.txt +5 -3
- data/test/fixtures/valid/example_mono_8.txt +5 -3
- data/test/fixtures/valid/example_no_kit.txt +1 -1
- data/test/fixtures/valid/example_stereo_16.txt +7 -4
- data/test/fixtures/valid/example_stereo_8.txt +5 -3
- data/test/fixtures/valid/example_with_empty_track.txt +1 -1
- data/test/fixtures/valid/example_with_kit.txt +1 -1
- data/test/fixtures/valid/multiple_tracks_same_sound.txt +33 -0
- data/test/fixtures/valid/no_tempo.txt +1 -1
- data/test/fixtures/valid/optimize_pattern_collision.txt +28 -0
- data/test/fixtures/valid/pattern_with_overflow.txt +1 -1
- data/test/fixtures/valid/repeats_not_specified.txt +2 -2
- data/test/fixtures/valid/with_structure.txt +10 -0
- data/test/fixtures/yaml/song_yaml.txt +5 -5
- data/test/includes.rb +4 -2
- data/test/integration.rb +3 -3
- data/test/kit_test.rb +136 -109
- data/test/pattern_test.rb +31 -131
- data/test/patternexpander_test.rb +142 -0
- data/test/song_test.rb +104 -102
- data/test/songoptimizer_test.rb +52 -38
- data/test/songparser_test.rb +79 -46
- data/test/sounds/composite_snare_mono_8_tom3_mono_16_mono_16.wav +0 -0
- data/test/sounds/composite_snare_mono_8_tom3_mono_8_mono_16.wav +0 -0
- data/test/sounds/composite_snare_stereo_16_tom3_mono_16_stereo_16.wav +0 -0
- data/test/sounds/composite_snare_stereo_8_tom3_mono_16_stereo_16.wav +0 -0
- data/test/track_test.rb +30 -185
- metadata +56 -24
- data/lib/songsplitter.rb +0 -38
- data/test/examples/combined.wav +0 -0
- data/test/examples/split-bass.wav +0 -0
- data/test/examples/split-hh_closed.wav +0 -0
- data/test/examples/split-snare.wav +0 -0
- data/test/examples/split-tom2.wav +0 -0
data/lib/track.rb
CHANGED
@@ -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,
|
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
|
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
|
-
|
75
|
-
|
76
|
-
|
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
|
Binary file
|
Binary file
|
Binary file
|
data/test/{examples/split-tom4.wav → fixtures/expected_output/example_split_mono_8-hh_closed2.wav}
RENAMED
Binary file
|
Binary file
|
Binary file
|
Binary file
|