beats 1.2.0 → 1.2.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|