beats 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +24 -0
- data/README.markdown +27 -0
- data/bin/beats +109 -0
- data/lib/kit.rb +40 -0
- data/lib/pattern.rb +122 -0
- data/lib/song.rb +294 -0
- data/lib/track.rb +118 -0
- data/test/includes.rb +8 -0
- data/test/kit_test.rb +121 -0
- data/test/pattern_test.rb +153 -0
- data/test/song_test.rb +225 -0
- data/test/sounds/bass_mono_16.wav +0 -0
- data/test/sounds/bass_mono_8.wav +0 -0
- data/test/sounds/bass_stereo_16.wav +0 -0
- data/test/sounds/hh_closed_mono_8.wav +0 -0
- data/test/sounds/hh_open_mono_8.wav +0 -0
- data/test/sounds/ride_mono_8.wav +0 -0
- data/test/sounds/snare_mono_8.wav +0 -0
- data/test/track_test.rb +203 -0
- metadata +93 -0
data/lib/track.rb
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
class Track
|
2
|
+
REST = "."
|
3
|
+
BEAT = "X"
|
4
|
+
|
5
|
+
def initialize(name, wave_data, pattern)
|
6
|
+
@wave_data = wave_data
|
7
|
+
@name = name
|
8
|
+
@sample_data = nil
|
9
|
+
@overflow = nil
|
10
|
+
self.pattern = pattern
|
11
|
+
end
|
12
|
+
|
13
|
+
def pattern=(pattern)
|
14
|
+
@pattern = pattern
|
15
|
+
beats = []
|
16
|
+
|
17
|
+
beat_length = 0
|
18
|
+
#pattern.each_char{|ch|
|
19
|
+
pattern.each_byte{|ch|
|
20
|
+
ch = ch.chr
|
21
|
+
if ch == BEAT
|
22
|
+
beats << beat_length
|
23
|
+
beat_length = 1
|
24
|
+
else
|
25
|
+
beat_length += 1
|
26
|
+
end
|
27
|
+
}
|
28
|
+
|
29
|
+
if(beat_length > 0)
|
30
|
+
beats << beat_length
|
31
|
+
end
|
32
|
+
if(beats == [])
|
33
|
+
beats = [0]
|
34
|
+
end
|
35
|
+
@beats = beats
|
36
|
+
|
37
|
+
# Remove any cached sample data
|
38
|
+
@sample_data = nil
|
39
|
+
@overflow = nil
|
40
|
+
end
|
41
|
+
|
42
|
+
def sample_length(tick_sample_length)
|
43
|
+
total_ticks = @beats.inject(0) {|sum, n| sum + n}
|
44
|
+
return (total_ticks * tick_sample_length).floor
|
45
|
+
end
|
46
|
+
|
47
|
+
def sample_length_with_overflow(tick_sample_length)
|
48
|
+
temp_sample_length = sample_length(tick_sample_length)
|
49
|
+
|
50
|
+
if(@beats != [0])
|
51
|
+
beat_sample_length = @beats.last * tick_sample_length
|
52
|
+
if(@wave_data.length > beat_sample_length)
|
53
|
+
temp_sample_length += @wave_data.length - beat_sample_length.floor
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
return temp_sample_length.floor
|
58
|
+
end
|
59
|
+
|
60
|
+
def sample_data(tick_sample_length, incoming_overflow = nil)
|
61
|
+
actual_sample_length = sample_length(tick_sample_length)
|
62
|
+
full_sample_length = sample_length_with_overflow(tick_sample_length)
|
63
|
+
|
64
|
+
if(@sample_data == nil)
|
65
|
+
fill_value = (@wave_data.first.class == Array) ? [0, 0] : 0
|
66
|
+
output_data = [].fill(fill_value, 0, full_sample_length)
|
67
|
+
|
68
|
+
if(full_sample_length > 0)
|
69
|
+
remainder = 0.0
|
70
|
+
offset = @beats[0] * tick_sample_length
|
71
|
+
remainder += (@beats[0] * tick_sample_length) - (@beats[0] * tick_sample_length).floor
|
72
|
+
|
73
|
+
@beats[1...(@beats.length)].each {|beat_length|
|
74
|
+
beat_sample_length = beat_length * tick_sample_length
|
75
|
+
|
76
|
+
remainder += beat_sample_length - beat_sample_length.floor
|
77
|
+
if(remainder >= 1.0)
|
78
|
+
beat_sample_length += 1
|
79
|
+
remainder -= 1.0
|
80
|
+
end
|
81
|
+
|
82
|
+
output_data[offset...(offset + wave_data.length)] = wave_data
|
83
|
+
offset += beat_sample_length.floor
|
84
|
+
}
|
85
|
+
|
86
|
+
if(full_sample_length > actual_sample_length)
|
87
|
+
@sample_data = output_data[0...offset]
|
88
|
+
@overflow = output_data[actual_sample_length...full_sample_length]
|
89
|
+
else
|
90
|
+
@sample_data = output_data
|
91
|
+
@overflow = []
|
92
|
+
end
|
93
|
+
else
|
94
|
+
@sample_data = []
|
95
|
+
@overflow = []
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
primary_sample_data = @sample_data.dup
|
100
|
+
|
101
|
+
if(incoming_overflow != nil && incoming_overflow != [])
|
102
|
+
# TO DO: Add check for when incoming overflow is longer than
|
103
|
+
# track full length to prevent track from lengthening.
|
104
|
+
intro_length = @beats.first * tick_sample_length.floor
|
105
|
+
|
106
|
+
if(incoming_overflow.length <= intro_length)
|
107
|
+
primary_sample_data[0...incoming_overflow.length] = incoming_overflow
|
108
|
+
else
|
109
|
+
primary_sample_data[0...intro_length] = incoming_overflow[0...intro_length]
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
return {:primary => primary_sample_data, :overflow => @overflow}
|
114
|
+
end
|
115
|
+
|
116
|
+
attr_accessor :name, :wave_data
|
117
|
+
attr_reader :pattern
|
118
|
+
end
|
data/test/includes.rb
ADDED
data/test/kit_test.rb
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__),'..','lib')
|
2
|
+
|
3
|
+
require 'test/includes'
|
4
|
+
|
5
|
+
class SongTest < Test::Unit::TestCase
|
6
|
+
MIN_SAMPLE_8BIT = 0
|
7
|
+
MAX_SAMPLE_8BIT = 255
|
8
|
+
|
9
|
+
def test_add
|
10
|
+
# Test adding sounds with progressively higher bits per sample and num channels.
|
11
|
+
# Verify that kit.bits_per_sample and kit.num_channels is ratcheted up.
|
12
|
+
kit = Kit.new()
|
13
|
+
assert_equal(kit.bits_per_sample, 0)
|
14
|
+
assert_equal(kit.num_channels, 0)
|
15
|
+
assert_equal(kit.size, 0)
|
16
|
+
kit.add("mono8", "test/sounds/bass_mono_8.wav")
|
17
|
+
assert_equal(kit.bits_per_sample, 8)
|
18
|
+
assert_equal(kit.num_channels, 1)
|
19
|
+
assert_equal(kit.size, 1)
|
20
|
+
kit.add("mono16", "test/sounds/bass_mono_16.wav")
|
21
|
+
assert_equal(kit.bits_per_sample, 16)
|
22
|
+
assert_equal(kit.num_channels, 1)
|
23
|
+
assert_equal(kit.size, 2)
|
24
|
+
kit.add("stereo16", "test/sounds/bass_stereo_16.wav")
|
25
|
+
assert_equal(kit.bits_per_sample, 16)
|
26
|
+
assert_equal(kit.num_channels, 2)
|
27
|
+
assert_equal(kit.size, 3)
|
28
|
+
|
29
|
+
# Test adding sounds with progressively lower bits per sample and num channels.
|
30
|
+
# Verify that kit.bits_per_sample and kit.num_channels doesn't change.
|
31
|
+
kit = Kit.new()
|
32
|
+
assert_equal(kit.bits_per_sample, 0)
|
33
|
+
assert_equal(kit.num_channels, 0)
|
34
|
+
kit.add("stereo16", "test/sounds/bass_stereo_16.wav")
|
35
|
+
assert_equal(kit.bits_per_sample, 16)
|
36
|
+
assert_equal(kit.num_channels, 2)
|
37
|
+
kit.add("mono16", "test/sounds/bass_mono_16.wav")
|
38
|
+
assert_equal(kit.bits_per_sample, 16)
|
39
|
+
assert_equal(kit.num_channels, 2)
|
40
|
+
kit.add("mono8", "test/sounds/bass_mono_8.wav")
|
41
|
+
assert_equal(kit.bits_per_sample, 16)
|
42
|
+
assert_equal(kit.num_channels, 2)
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_get_sample_data
|
46
|
+
kit = Kit.new()
|
47
|
+
|
48
|
+
assert_raise(StandardError) { kit.get_sample_data("nonexistant") }
|
49
|
+
|
50
|
+
# Test adding sounds with progressively higher bits per sample and num channels.
|
51
|
+
# Verify that sample data bits per sample and num channels is ratcheted up.
|
52
|
+
kit.add("mono8", "test/sounds/bass_mono_8.wav")
|
53
|
+
sample_data = kit.get_sample_data("mono8")
|
54
|
+
assert(sample_data.max <= MAX_SAMPLE_8BIT)
|
55
|
+
assert(sample_data.min >= MIN_SAMPLE_8BIT)
|
56
|
+
all_are_fixnums = true
|
57
|
+
sample_data.each {|sample|
|
58
|
+
all_are_fixnums &&= sample.class == Fixnum
|
59
|
+
}
|
60
|
+
assert(all_are_fixnums)
|
61
|
+
|
62
|
+
kit.add("mono16", "test/sounds/bass_mono_16.wav")
|
63
|
+
sample_data = kit.get_sample_data("mono8")
|
64
|
+
assert(sample_data.max > MAX_SAMPLE_8BIT)
|
65
|
+
assert(sample_data.min < MIN_SAMPLE_8BIT)
|
66
|
+
all_are_fixnums = true
|
67
|
+
sample_data.each {|sample|
|
68
|
+
all_are_fixnums &&= sample.class == Fixnum
|
69
|
+
}
|
70
|
+
assert(all_are_fixnums)
|
71
|
+
|
72
|
+
kit.add("stereo16", "test/sounds/bass_stereo_16.wav")
|
73
|
+
sample_data = kit.get_sample_data("stereo16")
|
74
|
+
assert(sample_data.flatten.max > MAX_SAMPLE_8BIT)
|
75
|
+
assert(sample_data.flatten.min < MIN_SAMPLE_8BIT)
|
76
|
+
all_are_arrays = true
|
77
|
+
sample_data.each {|sample|
|
78
|
+
all_are_arrays &&= sample.class == Array
|
79
|
+
}
|
80
|
+
assert(all_are_arrays)
|
81
|
+
assert(sample_data.first.length == 2)
|
82
|
+
|
83
|
+
|
84
|
+
# Test adding sounds with progressively lower bits per sample and num channels.
|
85
|
+
# Verify that sample data bits per sample and num channels doesn't go down.
|
86
|
+
kit = Kit.new()
|
87
|
+
|
88
|
+
kit.add("stereo16", "test/sounds/bass_stereo_16.wav")
|
89
|
+
sample_data = kit.get_sample_data("stereo16")
|
90
|
+
assert(sample_data.flatten.max > MAX_SAMPLE_8BIT)
|
91
|
+
assert(sample_data.flatten.min < MIN_SAMPLE_8BIT)
|
92
|
+
all_are_arrays = true
|
93
|
+
sample_data.each {|sample|
|
94
|
+
all_are_arrays &&= sample.class == Array
|
95
|
+
}
|
96
|
+
assert(all_are_arrays)
|
97
|
+
assert(sample_data.first.length == 2)
|
98
|
+
|
99
|
+
kit.add("mono16", "test/sounds/bass_mono_16.wav")
|
100
|
+
sample_data = kit.get_sample_data("mono16")
|
101
|
+
assert(sample_data.flatten.max > MAX_SAMPLE_8BIT)
|
102
|
+
assert(sample_data.flatten.min < MIN_SAMPLE_8BIT)
|
103
|
+
all_are_arrays = true
|
104
|
+
sample_data.each {|sample|
|
105
|
+
all_are_arrays &&= sample.class == Array
|
106
|
+
}
|
107
|
+
assert(all_are_arrays)
|
108
|
+
assert(sample_data.first.length == 2)
|
109
|
+
|
110
|
+
kit.add("mono8", "test/sounds/bass_mono_8.wav")
|
111
|
+
sample_data = kit.get_sample_data("mono8")
|
112
|
+
assert(sample_data.flatten.max > MAX_SAMPLE_8BIT)
|
113
|
+
assert(sample_data.flatten.min < MIN_SAMPLE_8BIT)
|
114
|
+
all_are_arrays = true
|
115
|
+
sample_data.each {|sample|
|
116
|
+
all_are_arrays &&= sample.class == Array
|
117
|
+
}
|
118
|
+
assert(all_are_arrays)
|
119
|
+
assert(sample_data.first.length == 2)
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__),'..','lib')
|
2
|
+
|
3
|
+
require 'test/includes'
|
4
|
+
|
5
|
+
class PatternTest < Test::Unit::TestCase
|
6
|
+
SAMPLE_RATE = 44100
|
7
|
+
SECONDS_IN_MINUTE = 60.0
|
8
|
+
|
9
|
+
def generate_test_data
|
10
|
+
kit = Kit.new()
|
11
|
+
kit.add("bass.wav", "test/sounds/bass_mono_8.wav")
|
12
|
+
kit.add("snare.wav", "test/sounds/snare_mono_8.wav")
|
13
|
+
kit.add("hh_closed.wav", "test/sounds/hh_closed_mono_8.wav")
|
14
|
+
kit.add("hh_open.wav", "test/sounds/hh_open_mono_8.wav")
|
15
|
+
|
16
|
+
test_patterns = []
|
17
|
+
|
18
|
+
p1 = Pattern.new :blank
|
19
|
+
test_patterns << p1
|
20
|
+
|
21
|
+
p2 = Pattern.new :verse
|
22
|
+
p2.track "bass.wav", kit.get_sample_data("bass.wav"), "X...X...X...XX..X...X...XX..X..."
|
23
|
+
p2.track "snare.wav", kit.get_sample_data("snare.wav"), "..X...X...X...X.X...X...X...X..."
|
24
|
+
p2.track "hh_closed.wav", kit.get_sample_data("hh_closed.wav"), "X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X."
|
25
|
+
p2.track "hh_open.wav", kit.get_sample_data("hh_open.wav"), "X...............X..............X"
|
26
|
+
test_patterns << p2
|
27
|
+
|
28
|
+
p3 = Pattern.new :staircase
|
29
|
+
p3.track "bass.wav", kit.get_sample_data("bass.wav"), "X..."
|
30
|
+
p3.track "snare.wav", kit.get_sample_data("snare.wav"), "X.."
|
31
|
+
p3.track "hh_closed.wav", kit.get_sample_data("hh_closed.wav"), "X."
|
32
|
+
test_patterns << p3
|
33
|
+
|
34
|
+
return test_patterns
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_initialize
|
38
|
+
test_patterns = generate_test_data()
|
39
|
+
|
40
|
+
pattern = test_patterns.shift()
|
41
|
+
assert_equal(pattern.name, :blank)
|
42
|
+
assert_equal(pattern.tracks.length, 0)
|
43
|
+
|
44
|
+
pattern = test_patterns.shift()
|
45
|
+
assert_equal(pattern.name, :verse)
|
46
|
+
assert_equal(pattern.tracks.length, 4)
|
47
|
+
|
48
|
+
pattern = test_patterns.shift()
|
49
|
+
assert_equal(pattern.name, :staircase)
|
50
|
+
assert_equal(pattern.tracks.length, 3)
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_sample_length
|
54
|
+
test_patterns = generate_test_data()
|
55
|
+
|
56
|
+
tick_sample_length = 13860.0
|
57
|
+
assert_equal(test_patterns[0].sample_length(tick_sample_length), 0)
|
58
|
+
assert_equal(test_patterns[1].sample_length(tick_sample_length), tick_sample_length * 32)
|
59
|
+
assert_equal(test_patterns[2].sample_length(tick_sample_length), tick_sample_length * 4)
|
60
|
+
|
61
|
+
tick_sample_length = 6681.81818181818
|
62
|
+
assert_equal(test_patterns[0].sample_length(tick_sample_length), 0)
|
63
|
+
assert_equal(test_patterns[1].sample_length(tick_sample_length), (tick_sample_length * 32).floor)
|
64
|
+
assert_equal(test_patterns[2].sample_length(tick_sample_length), (tick_sample_length * 4).floor)
|
65
|
+
|
66
|
+
tick_sample_length = 16134.1463414634
|
67
|
+
assert_equal(test_patterns[0].sample_length(tick_sample_length), 0)
|
68
|
+
assert_equal(test_patterns[1].sample_length(tick_sample_length), (tick_sample_length * 32).floor)
|
69
|
+
assert_equal(test_patterns[2].sample_length(tick_sample_length), (tick_sample_length * 4).floor)
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_sample_data
|
73
|
+
tick_sample_lengths = [
|
74
|
+
13860.0,
|
75
|
+
(SAMPLE_RATE * SECONDS_IN_MINUTE) / 200 / 4, # 3307.50
|
76
|
+
(SAMPLE_RATE * SECONDS_IN_MINUTE) / 99 / 4 # 6681.81818181818
|
77
|
+
]
|
78
|
+
|
79
|
+
tick_sample_lengths.each{|tick_sample_length| helper_test_sample_data(tick_sample_length) }
|
80
|
+
end
|
81
|
+
|
82
|
+
def helper_test_sample_data(tick_sample_length)
|
83
|
+
|
84
|
+
test_patterns = generate_test_data()
|
85
|
+
|
86
|
+
# Combined
|
87
|
+
test_patterns.each{|test_pattern|
|
88
|
+
sample_data = test_pattern.sample_data(tick_sample_length, 1, test_pattern.tracks.length, {})
|
89
|
+
assert_equal(sample_data.class, Hash)
|
90
|
+
assert_equal(sample_data.keys.map{|key| key.to_s}.sort, ["overflow", "primary"])
|
91
|
+
|
92
|
+
primary_sample_length = test_pattern.sample_length(tick_sample_length)
|
93
|
+
full_sample_length = test_pattern.sample_length_with_overflow(tick_sample_length)
|
94
|
+
assert_equal(sample_data[:primary].length, primary_sample_length)
|
95
|
+
assert_equal(sample_data[:overflow].length, test_pattern.tracks.length)
|
96
|
+
sample_data[:overflow].values.each {|track_overflow|
|
97
|
+
assert_equal(track_overflow.class, Array)
|
98
|
+
}
|
99
|
+
# To do: add test to verify that longest overflow == full_sample_length - primary_sample_length
|
100
|
+
}
|
101
|
+
|
102
|
+
#Split
|
103
|
+
track_samples = test_patterns[0].sample_data(tick_sample_length, 1, 0, {}, true)
|
104
|
+
assert_equal(track_samples.class, Hash)
|
105
|
+
assert_equal(track_samples.keys.map{|key| key.to_s}.sort, ["overflow", "primary"])
|
106
|
+
|
107
|
+
track_samples = test_patterns[1].sample_data(tick_sample_length, 1, 4, {}, true)
|
108
|
+
assert_equal(track_samples.class, Hash)
|
109
|
+
assert_equal(track_samples[:primary].keys.map{|key| key.to_s}.sort,
|
110
|
+
["bass.wav", "hh_closed.wav", "hh_open.wav", "snare.wav"])
|
111
|
+
primary = track_samples[:primary]
|
112
|
+
primary.keys.each{|name|
|
113
|
+
assert_equal(primary[name].length, test_patterns[1].sample_length(tick_sample_length))
|
114
|
+
}
|
115
|
+
overflow = track_samples[:overflow]
|
116
|
+
longest_overflow = find_longest_overflow(overflow)
|
117
|
+
overflow.keys.each{|name|
|
118
|
+
assert_equal(overflow[name].class, Array)
|
119
|
+
#assert_lessthan(overflow[name].length, test_patterns[1].sample_length_with_overflow(tick_sample_length) - test_patterns[1].sample_length(tick_sample_length))
|
120
|
+
}
|
121
|
+
assert_equal(overflow[longest_overflow].length, test_patterns[1].sample_length_with_overflow(tick_sample_length) - test_patterns[1].sample_length(tick_sample_length))
|
122
|
+
|
123
|
+
track_samples = test_patterns[2].sample_data(tick_sample_length, 1, 3, {}, true)
|
124
|
+
assert_equal(track_samples.class, Hash)
|
125
|
+
assert_equal(track_samples[:primary].keys.map{|key| key.to_s}.sort,
|
126
|
+
["bass.wav", "hh_closed.wav", "snare.wav"])
|
127
|
+
primary = track_samples[:primary]
|
128
|
+
primary.keys.each{|name|
|
129
|
+
assert_equal(primary[name].length, test_patterns[2].sample_length(tick_sample_length))
|
130
|
+
}
|
131
|
+
overflow = track_samples[:overflow]
|
132
|
+
longest_overflow = find_longest_overflow(overflow)
|
133
|
+
overflow.keys.each{|name|
|
134
|
+
assert_equal(overflow[name].class, Array)
|
135
|
+
}
|
136
|
+
assert_equal(overflow[longest_overflow].length, test_patterns[2].sample_length_with_overflow(tick_sample_length) - test_patterns[2].sample_length(tick_sample_length))
|
137
|
+
primary.keys.each{|name|
|
138
|
+
assert_equal(primary[name].length, (tick_sample_length * 4).floor)
|
139
|
+
assert_equal(primary[name].length, test_patterns[2].sample_length(tick_sample_length))
|
140
|
+
}
|
141
|
+
end
|
142
|
+
|
143
|
+
def find_longest_overflow(overflow)
|
144
|
+
longest_overflow = overflow.keys.first
|
145
|
+
overflow.keys.each {|name|
|
146
|
+
if(overflow[name].length > overflow[longest_overflow].length)
|
147
|
+
longest_overflow = name
|
148
|
+
end
|
149
|
+
}
|
150
|
+
|
151
|
+
return longest_overflow
|
152
|
+
end
|
153
|
+
end
|
data/test/song_test.rb
ADDED
@@ -0,0 +1,225 @@
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__),'..','lib')
|
2
|
+
|
3
|
+
require 'test/includes'
|
4
|
+
|
5
|
+
class MockSong < Song
|
6
|
+
attr_reader :patterns
|
7
|
+
attr_accessor :kit
|
8
|
+
end
|
9
|
+
|
10
|
+
class SongTest < Test::Unit::TestCase
|
11
|
+
DEFAULT_TEMPO = 120
|
12
|
+
|
13
|
+
def generate_test_data
|
14
|
+
kit = Kit.new()
|
15
|
+
kit.add("bass.wav", "test/sounds/bass_mono_8.wav")
|
16
|
+
kit.add("snare.wav", "test/sounds/snare_mono_8.wav")
|
17
|
+
kit.add("hh_closed.wav", "test/sounds/hh_closed_mono_8.wav")
|
18
|
+
kit.add("ride.wav", "test/sounds/ride_mono_8.wav")
|
19
|
+
|
20
|
+
test_songs = {}
|
21
|
+
|
22
|
+
test_songs[:blank] = MockSong.new(File.dirname(__FILE__) + "/..")
|
23
|
+
|
24
|
+
test_songs[:no_structure] = MockSong.new(File.dirname(__FILE__) + "/..")
|
25
|
+
verse = test_songs[:no_structure].pattern :verse
|
26
|
+
verse.track "bass.wav", kit.get_sample_data("bass.wav"), "X.......X......."
|
27
|
+
verse.track "snare.wav", kit.get_sample_data("snare.wav"), "....X.......X..."
|
28
|
+
verse.track "hh_closed.wav", kit.get_sample_data("hh_closed.wav"), "X.X.X.X.X.X.X.X."
|
29
|
+
|
30
|
+
repeats_not_specified_yaml = "
|
31
|
+
Song:
|
32
|
+
Tempo: 100
|
33
|
+
Structure:
|
34
|
+
- Verse
|
35
|
+
|
36
|
+
Verse:
|
37
|
+
- test/sounds/bass_mono_8.wav: X"
|
38
|
+
test_songs[:repeats_not_specified] = MockSong.new(File.dirname(__FILE__) + "/..", repeats_not_specified_yaml)
|
39
|
+
|
40
|
+
overflow_yaml = "
|
41
|
+
Song:
|
42
|
+
Tempo: 100
|
43
|
+
Structure:
|
44
|
+
- Verse: x2
|
45
|
+
|
46
|
+
Verse:
|
47
|
+
- test/sounds/snare_mono_8.wav: ...X"
|
48
|
+
test_songs[:overflow] = MockSong.new(File.dirname(__FILE__) + "/..", overflow_yaml)
|
49
|
+
|
50
|
+
test_songs[:from_code] = MockSong.new(File.dirname(__FILE__) + "/..")
|
51
|
+
verse = test_songs[:from_code].pattern :verse
|
52
|
+
verse.track "bass.wav", kit.get_sample_data("bass.wav"), "X.......X......."
|
53
|
+
verse.track "snare.wav", kit.get_sample_data("snare.wav"), "....X.......X..."
|
54
|
+
verse.track "hh_closed.wav", kit.get_sample_data("hh_closed.wav"), "X.X.X.X.X.X.X.X."
|
55
|
+
chorus = test_songs[:from_code].pattern :chorus
|
56
|
+
chorus.track "bass.wav", kit.get_sample_data("bass.wav"), "X......."
|
57
|
+
chorus.track "snare.wav", kit.get_sample_data("snare.wav"), "....X..X"
|
58
|
+
chorus.track "ride.wav", kit.get_sample_data("ride.wav"), "X.....X."
|
59
|
+
test_songs[:from_code].structure = [:verse, :chorus, :verse, :chorus, :chorus]
|
60
|
+
test_songs[:from_code].kit = kit
|
61
|
+
|
62
|
+
valid_yaml_string = "# An example song
|
63
|
+
|
64
|
+
Song:
|
65
|
+
Tempo: 99
|
66
|
+
Structure:
|
67
|
+
- Verse: x2
|
68
|
+
- Chorus: x2
|
69
|
+
- Verse: x2
|
70
|
+
- Chorus: x4
|
71
|
+
- Bridge: x1
|
72
|
+
- Chorus: x4
|
73
|
+
|
74
|
+
Verse:
|
75
|
+
- test/sounds/bass_mono_8.wav: X...X...X...XX..X...X...XX..X...
|
76
|
+
- test/sounds/snare_mono_8.wav: ..X...X...X...X.X...X...X...X...
|
77
|
+
# Here is a comment
|
78
|
+
- test/sounds/hh_closed_mono_8.wav: X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.
|
79
|
+
- test/sounds/hh_open_mono_8.wav: X...............X..............X
|
80
|
+
# Here is another comment
|
81
|
+
Chorus:
|
82
|
+
- test/sounds/bass_mono_8.wav: X...X...XXXXXXXXX...X...X...X...
|
83
|
+
- test/sounds/snare_mono_8.wav: ...................X...X...X...X
|
84
|
+
- test/sounds/hh_closed_mono_8.wav: X.X.XXX.X.X.XXX.X.X.XXX.X.X.XXX. # It's comment time
|
85
|
+
- test/sounds/hh_open_mono_8.wav: ........X.......X.......X.......
|
86
|
+
- test/sounds/ride_mono_8.wav: ....X...................X.......
|
87
|
+
|
88
|
+
|
89
|
+
Bridge:
|
90
|
+
- test/sounds/hh_closed_mono_8.wav: XX.XXX.XXX.XXX.XXX.XXX.XXX.XXX.X"
|
91
|
+
|
92
|
+
test_songs[:from_valid_yaml_string] = MockSong.new(File.dirname(__FILE__) + "/..", valid_yaml_string)
|
93
|
+
|
94
|
+
return test_songs
|
95
|
+
end
|
96
|
+
|
97
|
+
def test_initialize
|
98
|
+
test_songs = generate_test_data
|
99
|
+
|
100
|
+
assert_equal(test_songs[:blank].structure, [])
|
101
|
+
assert_equal(test_songs[:blank].tick_sample_length, (Song::SAMPLE_RATE * Song::SECONDS_PER_MINUTE) / DEFAULT_TEMPO / 4.0)
|
102
|
+
assert_equal(test_songs[:no_structure].structure, [])
|
103
|
+
assert_equal(test_songs[:no_structure].tick_sample_length, (Song::SAMPLE_RATE * Song::SECONDS_PER_MINUTE) / DEFAULT_TEMPO / 4.0)
|
104
|
+
assert_equal(test_songs[:from_code].structure, [:verse, :chorus, :verse, :chorus, :chorus])
|
105
|
+
assert_equal(test_songs[:from_code].tick_sample_length, (Song::SAMPLE_RATE * Song::SECONDS_PER_MINUTE) / DEFAULT_TEMPO / 4.0)
|
106
|
+
|
107
|
+
assert_equal(test_songs[:from_valid_yaml_string].structure, [:verse, :verse, :chorus, :chorus, :verse, :verse, :chorus, :chorus, :chorus, :chorus, :bridge, :chorus, :chorus, :chorus, :chorus])
|
108
|
+
assert_equal(test_songs[:from_valid_yaml_string].tempo, 99)
|
109
|
+
assert_equal(test_songs[:from_valid_yaml_string].tick_sample_length, (Song::SAMPLE_RATE * Song::SECONDS_PER_MINUTE) / 99 / 4.0)
|
110
|
+
assert_equal(test_songs[:from_valid_yaml_string].patterns.keys.map{|key| key.to_s}.sort, ["bridge", "chorus", "verse"])
|
111
|
+
assert_equal(test_songs[:from_valid_yaml_string].patterns[:verse].tracks.length, 4)
|
112
|
+
assert_equal(test_songs[:from_valid_yaml_string].patterns[:chorus].tracks.length, 5)
|
113
|
+
assert_equal(test_songs[:from_valid_yaml_string].patterns[:bridge].tracks.length, 1)
|
114
|
+
end
|
115
|
+
|
116
|
+
def test_invalid_initialize
|
117
|
+
invalid_tempo_yaml_string = "# Invalid tempo song
|
118
|
+
Song:
|
119
|
+
Tempo: 100a
|
120
|
+
Structure:
|
121
|
+
- Verse: x2
|
122
|
+
|
123
|
+
Verse:
|
124
|
+
- test/sounds/bass_mono_8.wav: X...X...X...XX..X...X...XX..X..."
|
125
|
+
assert_raise(SongParseError) { song = MockSong.new(File.dirname(__FILE__) + "/..", invalid_tempo_yaml_string) }
|
126
|
+
|
127
|
+
invalid_structure_yaml_string = "# Invalid structure song
|
128
|
+
Song:
|
129
|
+
Tempo: 100
|
130
|
+
Structure:
|
131
|
+
- Verse: x2
|
132
|
+
- Chorus: x1
|
133
|
+
|
134
|
+
Verse:
|
135
|
+
- test/sounds/bass_mono_8.wav: X...X...X...XX..X...X...XX..X..."
|
136
|
+
assert_raise(SongParseError) { song = MockSong.new(File.dirname(__FILE__) + "/..", invalid_structure_yaml_string) }
|
137
|
+
|
138
|
+
invalid_repeats_yaml_string = " # Invalid structure song
|
139
|
+
Song:
|
140
|
+
Tempo: 100
|
141
|
+
Structure:
|
142
|
+
- Verse: x2a
|
143
|
+
|
144
|
+
Verse:
|
145
|
+
- test/sounds/bass_mono_8.wav: X...X...X...XX..X...X...XX..X..."
|
146
|
+
assert_raise(SongParseError) { song = MockSong.new(File.dirname(__FILE__) + "/..", invalid_repeats_yaml_string) }
|
147
|
+
end
|
148
|
+
|
149
|
+
def test_total_tracks
|
150
|
+
test_songs = generate_test_data()
|
151
|
+
|
152
|
+
assert_equal(test_songs[:blank].total_tracks, 0)
|
153
|
+
assert_equal(test_songs[:no_structure].total_tracks, 3)
|
154
|
+
assert_equal(test_songs[:from_code].total_tracks, 3)
|
155
|
+
assert_equal(test_songs[:repeats_not_specified].total_tracks, 1)
|
156
|
+
assert_equal(test_songs[:overflow].total_tracks, 1)
|
157
|
+
assert_equal(test_songs[:from_valid_yaml_string].total_tracks, 5)
|
158
|
+
end
|
159
|
+
|
160
|
+
def test_sample_length
|
161
|
+
test_songs = generate_test_data()
|
162
|
+
|
163
|
+
assert_equal(test_songs[:blank].sample_length, 0)
|
164
|
+
assert_equal(test_songs[:no_structure].sample_length, 0)
|
165
|
+
|
166
|
+
assert_equal(test_songs[:from_code].sample_length,
|
167
|
+
(test_songs[:from_code].tick_sample_length * 16 * 2) +
|
168
|
+
(test_songs[:from_code].tick_sample_length * 8 * 3))
|
169
|
+
|
170
|
+
assert_equal(test_songs[:repeats_not_specified].sample_length,
|
171
|
+
test_songs[:repeats_not_specified].tick_sample_length)
|
172
|
+
|
173
|
+
assert_equal(test_songs[:overflow].sample_length, test_songs[:overflow].tick_sample_length * 8)
|
174
|
+
end
|
175
|
+
|
176
|
+
def test_sample_length_with_overflow
|
177
|
+
test_songs = generate_test_data()
|
178
|
+
|
179
|
+
assert_equal(test_songs[:blank].sample_length_with_overflow, 0)
|
180
|
+
assert_equal(test_songs[:no_structure].sample_length_with_overflow, 0)
|
181
|
+
|
182
|
+
snare_overflow =
|
183
|
+
(test_songs[:from_code].kit.get_sample_data("snare.wav").length -
|
184
|
+
test_songs[:from_code].tick_sample_length).ceil
|
185
|
+
assert_equal(test_songs[:from_code].sample_length_with_overflow, test_songs[:from_code].sample_length + snare_overflow)
|
186
|
+
|
187
|
+
assert_equal(test_songs[:repeats_not_specified].sample_length_with_overflow,
|
188
|
+
test_songs[:repeats_not_specified].tick_sample_length)
|
189
|
+
|
190
|
+
snare_overflow =
|
191
|
+
(test_songs[:overflow].kit.get_sample_data("test/sounds/snare_mono_8.wav").length -
|
192
|
+
test_songs[:overflow].tick_sample_length).ceil
|
193
|
+
assert_equal(test_songs[:overflow].sample_length_with_overflow,
|
194
|
+
(test_songs[:overflow].tick_sample_length * 8) + snare_overflow)
|
195
|
+
|
196
|
+
snare_overflow =
|
197
|
+
(test_songs[:from_valid_yaml_string].kit.get_sample_data("test/sounds/snare_mono_8.wav").length -
|
198
|
+
test_songs[:from_valid_yaml_string].tick_sample_length).ceil
|
199
|
+
assert_equal(test_songs[:from_valid_yaml_string].sample_length_with_overflow, test_songs[:from_valid_yaml_string].sample_length + snare_overflow)
|
200
|
+
end
|
201
|
+
|
202
|
+
def test_sample_data
|
203
|
+
test_songs = generate_test_data()
|
204
|
+
|
205
|
+
test_songs.values.each {|song|
|
206
|
+
sample_data = song.sample_data("", false)
|
207
|
+
assert_equal(sample_data.class, Array)
|
208
|
+
assert_equal(sample_data.length, song.sample_length_with_overflow)
|
209
|
+
sample_data = song.sample_data("", true)
|
210
|
+
assert_equal(sample_data.class, Hash)
|
211
|
+
}
|
212
|
+
|
213
|
+
[:from_code, :repeats_not_specified, :overflow, :from_valid_yaml_string].each {|key|
|
214
|
+
assert_equal(test_songs[key].sample_data("verse", false).class, Array)
|
215
|
+
assert_equal(test_songs[key].sample_data("verse", true).class, Hash)
|
216
|
+
}
|
217
|
+
|
218
|
+
snare_sample_data = test_songs[:overflow].kit.get_sample_data("test/sounds/snare_mono_8.wav")
|
219
|
+
expected = [].fill(0, 0, test_songs[:overflow].tick_sample_length * 4)
|
220
|
+
expected[0...(snare_sample_data.length)] = snare_sample_data
|
221
|
+
expected += snare_sample_data
|
222
|
+
expected = [].fill(0, 0, test_songs[:overflow].tick_sample_length * 3) + expected
|
223
|
+
assert_equal(test_songs[:overflow].sample_data("", false)[0], expected[0])
|
224
|
+
end
|
225
|
+
end
|