beats 2.1.1 → 2.1.2
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.
- checksums.yaml +4 -4
- data/LICENSE +21 -23
- data/README.markdown +7 -15
- data/Rakefile +3 -3
- data/bin/beats +5 -5
- data/lib/beats.rb +15 -15
- data/lib/beats/kit.rb +1 -1
- data/lib/beats/kit_builder.rb +5 -1
- data/lib/beats/song.rb +1 -1
- data/lib/beats/song_parser.rb +45 -23
- data/test/audio_engine_test.rb +1 -1
- data/test/audio_utils_test.rb +1 -1
- data/test/caching_writer_test.rb +1 -1
- data/test/fixtures/invalid/flow_invalid_repeat_count_prefix.txt +8 -0
- data/test/fixtures/invalid/flow_invalid_repeat_count_suffix.txt +8 -0
- data/test/fixtures/invalid/{bad_repeat_count.txt → flow_missing_repeat_count.txt} +3 -3
- data/test/fixtures/invalid/flow_negative_repeat_count.txt +8 -0
- data/test/fixtures/invalid/{bad_flow.txt → flow_non_existent_pattern.txt} +0 -0
- data/test/fixtures/invalid/flow_non_string_repeat_count.txt +8 -0
- data/test/fixtures/invalid/flow_not_an_array.txt +15 -0
- data/test/fixtures/invalid/flow_pattern_name_not_a_string.txt +15 -0
- data/test/fixtures/invalid/flow_repeat_count_is_missing_prefix.txt +8 -0
- data/test/fixtures/invalid/flow_section_not_a_string_or_hash.txt +15 -0
- data/test/fixtures/invalid/kit_filename_not_a_string.txt +17 -0
- data/test/fixtures/invalid/kit_not_an_array.txt +17 -0
- data/test/fixtures/invalid/pattern_not_an_array.txt +17 -0
- data/test/fixtures/invalid/pattern_referenced_pattern_name_not_a_string.txt +19 -0
- data/test/fixtures/invalid/pattern_unreferenced_pattern_name_not_a_string.txt +18 -0
- data/test/fixtures/invalid/pattern_with_incomplete_track.txt +10 -0
- data/test/fixtures/invalid/pattern_with_nil_track.txt +10 -0
- data/test/fixtures/valid/composite_sounds_trailing_comma.txt +16 -0
- data/test/fixtures/valid/example_alternate_array_syntax.txt +11 -0
- data/test/fixtures/valid/{multiple_song_header_sections.txt → multiple_copies_of_song_components.txt} +28 -4
- data/test/fixtures/valid/multiple_yaml_documents.txt +33 -0
- data/test/fixtures/valid/track_sound_has_mixed_capitalization.txt +13 -0
- data/test/includes.rb +2 -2
- data/test/integration_test.rb +3 -3
- data/test/kit_builder_test.rb +3 -3
- data/test/kit_test.rb +7 -7
- data/test/pattern_test.rb +176 -176
- data/test/song_optimizer_test.rb +1 -1
- data/test/song_parser_test.rb +86 -11
- data/test/song_swinger_test.rb +1 -1
- data/test/song_test.rb +1 -1
- data/test/sounds/MiXeD_CaPiTaLiZaTiOn.wav +0 -0
- data/test/sounds/bass_mono_16_11025.wav +0 -0
- data/test/sounds/bass_mono_16_22050.wav +0 -0
- data/test/sounds/bass_mono_24.wav +0 -0
- data/test/sounds/bass_mono_8.mp3 +0 -0
- data/test/track_test.rb +1 -1
- metadata +58 -12
@@ -0,0 +1,15 @@
|
|
1
|
+
# Invalid song, since one of the flow sections is not a Hash
|
2
|
+
Song:
|
3
|
+
Tempo: 100
|
4
|
+
Flow:
|
5
|
+
- Verse: x2
|
6
|
+
- 6 # Invalid, since this is not a pattern name String, or a Hash
|
7
|
+
- Verse: x2
|
8
|
+
- Chorus: x2
|
9
|
+
|
10
|
+
|
11
|
+
Verse:
|
12
|
+
- test/sounds/bass_mono_8.wav: X...X...
|
13
|
+
|
14
|
+
Chorus:
|
15
|
+
- test/sounds/snare_mono_8.wav: X...X...
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# Invalid song, the 'snare' Kit sound has an invalid filename
|
2
|
+
Song:
|
3
|
+
Tempo: 120
|
4
|
+
Flow:
|
5
|
+
- Verse: x2
|
6
|
+
Kit:
|
7
|
+
- bass: test/sounds/bass_mono_8.wav
|
8
|
+
- snare: 3
|
9
|
+
- hh_closed: test/sounds/hh_closed_mono_8.wav
|
10
|
+
- agogo: test/sounds/agogo_high_mono_8.wav
|
11
|
+
|
12
|
+
Verse:
|
13
|
+
- bass: X...X...X...X...
|
14
|
+
- snare: ..............X.
|
15
|
+
- hh_closed: X.XXX.XX........
|
16
|
+
- hh_closed: ........X.X.X.X.
|
17
|
+
- agogo: ..............XX
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# Invalid song, since the Kit is not an array (i.e., each line is not prefixed with "-")
|
2
|
+
Song:
|
3
|
+
Tempo: 120
|
4
|
+
Flow:
|
5
|
+
- Verse: x2
|
6
|
+
Kit:
|
7
|
+
bass: ../../sounds/bass_mono_8.wav
|
8
|
+
snare: ../../sounds/snare_mono_8.wav
|
9
|
+
hh_closed: ../../sounds/hh_closed_mono_8.wav
|
10
|
+
agogo: ../../sounds/agogo_high_mono_8.wav
|
11
|
+
|
12
|
+
Verse:
|
13
|
+
- bass: X...X...X...X...
|
14
|
+
- snare: ..............X.
|
15
|
+
- hh_closed: X.XXX.XX........
|
16
|
+
- hh_closed: ........X.X.X.X.
|
17
|
+
- agogo: ..............XX
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# Invalid song, since a Pattern is not an array (i.e., each line is not prefixed with "-")
|
2
|
+
Song:
|
3
|
+
Tempo: 120
|
4
|
+
Flow:
|
5
|
+
- Verse: x2
|
6
|
+
Kit:
|
7
|
+
- bass: ../../sounds/bass_mono_8.wav
|
8
|
+
- snare: ../../sounds/snare_mono_8.wav
|
9
|
+
- hh_closed: ../../sounds/hh_closed_mono_8.wav
|
10
|
+
- agogo: ../../sounds/agogo_high_mono_8.wav
|
11
|
+
|
12
|
+
Verse:
|
13
|
+
bass: X...X...X...X...
|
14
|
+
snare: ..............X.
|
15
|
+
hh_closed: X.XXX.XX........
|
16
|
+
hh_closed: ........X.X.X.X.
|
17
|
+
agogo: ..............XX
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# Invalid song, a pattern has a name which will not be parsed as a String, and it's referenced in the flow
|
2
|
+
Song:
|
3
|
+
Tempo: 120
|
4
|
+
Flow:
|
5
|
+
- Verse: x2
|
6
|
+
- 4: x2
|
7
|
+
Kit:
|
8
|
+
- bass: ../../sounds/bass_mono_8.wav
|
9
|
+
- snare: ../../sounds/snare_mono_8.wav
|
10
|
+
- hh_closed: ../../sounds/hh_closed_mono_8.wav
|
11
|
+
- agogo: ../../sounds/agogo_high_mono_8.wav
|
12
|
+
|
13
|
+
Verse:
|
14
|
+
- bass: X...X...X...X...
|
15
|
+
- snare: ..............X.
|
16
|
+
|
17
|
+
4: # Invalid pattern name!
|
18
|
+
- hh_closed: X.XXX.XX........
|
19
|
+
- agogo: ..............XX
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# Invalid song, a pattern has a name which will not be parsed as a String, but it's not referenced in the flow
|
2
|
+
Song:
|
3
|
+
Tempo: 120
|
4
|
+
Flow:
|
5
|
+
- Verse: x2
|
6
|
+
Kit:
|
7
|
+
- bass: ../../sounds/bass_mono_8.wav
|
8
|
+
- snare: ../../sounds/snare_mono_8.wav
|
9
|
+
- hh_closed: ../../sounds/hh_closed_mono_8.wav
|
10
|
+
- agogo: ../../sounds/agogo_high_mono_8.wav
|
11
|
+
|
12
|
+
Verse:
|
13
|
+
- bass: X...X...X...X...
|
14
|
+
- snare: ..............X.
|
15
|
+
|
16
|
+
4: # Invalid pattern name!
|
17
|
+
- hh_closed: X.XXX.XX........
|
18
|
+
- agogo: ..............XX
|
@@ -0,0 +1,16 @@
|
|
1
|
+
Song:
|
2
|
+
Tempo: 100
|
3
|
+
Kit:
|
4
|
+
- bass: test/sounds/bass_mono_8.wav
|
5
|
+
- snare: [test/sounds/snare_mono_8.wav,] # Trailing commas are valid in YAML
|
6
|
+
- hihat: [test/sounds/hh_closed_mono_8.wav, test/sounds/hh_open_mono_8.wav,]
|
7
|
+
Flow:
|
8
|
+
- Verse: x2
|
9
|
+
- Chorus: x2
|
10
|
+
|
11
|
+
Verse:
|
12
|
+
- hihat: X...X...
|
13
|
+
- [snare,]: ..X...X.
|
14
|
+
Chorus:
|
15
|
+
- bass: XXXXXXXX
|
16
|
+
- [snare, hihat,]: .X.X.X.X
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# A song that uses alternate YAML syntax to define arrays.
|
2
|
+
# This syntax is not recommended (in particular it's hard to read),
|
3
|
+
# but it will be parsed as valid YAML.
|
4
|
+
|
5
|
+
Song:
|
6
|
+
Tempo: 120
|
7
|
+
Kit: [bass: test/sounds/bass_mono_8.wav, snare: test/sounds/snare_mono_8.wav, hhclosed: test/sounds/hh_closed_mono_8.wav, hhopen: test/sounds/hh_open_mono_8.wav]
|
8
|
+
Flow: [Verse: x2, Chorus: x2, Verse: x2, Chorus: x4]
|
9
|
+
|
10
|
+
Verse: [bass: X...X...X...X..., snare: ....X.......X..., hhclosed: X.X.X.X.X.X.X..., hhopen: ..............X.]
|
11
|
+
Chorus: [bass: X...X...XXXXXXXX, snare: X...X...X...X..., hhopen: ........X......., test/sounds/ride_mono_8.wav: ....X...........]
|
@@ -1,29 +1,53 @@
|
|
1
|
+
# This example includes multiple copies of song components such as the Flow, Kit, etc.
|
2
|
+
# In general, multiple copies of the same song component are allowed (due to the Song
|
3
|
+
# header being a YAML hash), but the final definition should be chosen as the winner.
|
4
|
+
|
5
|
+
|
1
6
|
# This song header is ignored, because it is overridden by a subsequent song
|
2
7
|
# header at the end of the file
|
3
8
|
Song:
|
4
|
-
Tempo:
|
9
|
+
Tempo: 90
|
5
10
|
Flow:
|
6
11
|
- Verse: x1
|
7
12
|
- Chorus: x2
|
8
|
-
- Verse: x1
|
9
|
-
- Chorus: x2
|
10
13
|
Kit:
|
11
14
|
- bass: test/sounds/bass_mono_8.wav
|
12
15
|
- snare: test/sounds/snare_mono_8.wav
|
13
16
|
|
14
17
|
Verse:
|
15
18
|
- bass: X...X...X...X...
|
16
|
-
- snare: ..............X.
|
17
19
|
|
18
20
|
Chorus:
|
19
21
|
- bass: X...X...XX..X...
|
20
22
|
- snare: ....X.......X...
|
21
23
|
|
24
|
+
|
25
|
+
|
26
|
+
|
27
|
+
|
22
28
|
# This is the actual song header that should be used
|
23
29
|
Song:
|
30
|
+
Kit:
|
31
|
+
- bass2: test/sounds/tom2_mono_8.wav
|
32
|
+
- snare2: test/sounds/tom4_mono_8.wav
|
24
33
|
Tempo: 200
|
34
|
+
Flow:
|
35
|
+
- Verse: x4
|
25
36
|
Flow:
|
26
37
|
- Chorus: x4
|
38
|
+
Tempo: 100
|
39
|
+
Tempo: 120
|
27
40
|
Kit:
|
28
41
|
- bass: test/sounds/ride_mono_8.wav
|
29
42
|
- snare: test/sounds/snare2_mono_8.wav
|
43
|
+
Flow:
|
44
|
+
- Verse: x2
|
45
|
+
- Chorus: x2
|
46
|
+
|
47
|
+
Verse:
|
48
|
+
- bass: X.......X...X...
|
49
|
+
- snare: ....X.......X...
|
50
|
+
|
51
|
+
Chorus:
|
52
|
+
- bass: X.X.X.X.X.X.X.X.
|
53
|
+
- snare: X...X...X...X...
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# This file contains multiple YAML documents (demarcated by "---")
|
2
|
+
# The first document should be chosen as the song to use, and the
|
3
|
+
# second document should be ignored.
|
4
|
+
|
5
|
+
---
|
6
|
+
Song:
|
7
|
+
Tempo: 150
|
8
|
+
Flow:
|
9
|
+
- Verse: x2
|
10
|
+
Kit:
|
11
|
+
- bass: test/sounds/bass_mono_8.wav
|
12
|
+
|
13
|
+
Verse:
|
14
|
+
- bass: X...X...X...X...
|
15
|
+
|
16
|
+
|
17
|
+
---
|
18
|
+
Song:
|
19
|
+
Tempo: 200
|
20
|
+
Flow:
|
21
|
+
- Verse: x3
|
22
|
+
- Chorus: x1
|
23
|
+
Kit:
|
24
|
+
- snare: test/sounds/snare_mono_8.wav
|
25
|
+
- tom: test/sounds/tom4_mono_8.wav
|
26
|
+
|
27
|
+
Verse:
|
28
|
+
- snare: X...X...........
|
29
|
+
- tom: ........X...X...
|
30
|
+
|
31
|
+
Chorus:
|
32
|
+
- snare: X.X.X.X.X.X.X.X.
|
33
|
+
- tom: XXXXXXXXXXXXXXXX
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# "MiXeD_CaPiTaLiZaTiOn.wav" should be the valid name for a track sound.
|
2
|
+
# That is, there's no requirement that file names have all lowercase or uppercase letters.
|
3
|
+
|
4
|
+
Song:
|
5
|
+
Tempo: 120
|
6
|
+
Flow:
|
7
|
+
- Verse: x1
|
8
|
+
Kit:
|
9
|
+
- bass: test/sounds/MiXeD_CaPiTaLiZaTiOn.wav
|
10
|
+
|
11
|
+
Verse:
|
12
|
+
- bass: X...X...........
|
13
|
+
- test/sounds/MiXeD_CaPiTaLiZaTiOn.wav: ........X...X...
|
data/test/includes.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "minitest/autorun"
|
2
|
+
require "beats"
|
3
3
|
include Beats
|
data/test/integration_test.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "includes"
|
2
2
|
|
3
3
|
class IntegrationTest < Minitest::Test
|
4
4
|
TRACK_NAMES = ["bass", "snare", "hh_closed", "hh_closed2", "agogo", "tom4", "tom2"]
|
@@ -11,8 +11,8 @@ class IntegrationTest < Minitest::Test
|
|
11
11
|
|
12
12
|
def test_bad_song_errors
|
13
13
|
invalid_fixtures = ["bad_tempo.txt",
|
14
|
-
"
|
15
|
-
"
|
14
|
+
"flow_invalid_repeat_count_suffix.txt",
|
15
|
+
"flow_non_existent_pattern.txt",
|
16
16
|
"no_header.txt",
|
17
17
|
"no_flow.txt",
|
18
18
|
"sound_in_kit_not_found.txt",
|
data/test/kit_builder_test.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "includes"
|
2
2
|
|
3
3
|
class KitBuilderTest < Minitest::Test
|
4
4
|
def test_has_label?
|
@@ -43,8 +43,8 @@ class KitBuilderTest < Minitest::Test
|
|
43
43
|
assert_equal(Kit, kit.class)
|
44
44
|
assert_equal(2, kit.num_channels)
|
45
45
|
assert_equal(16, kit.bits_per_sample)
|
46
|
-
assert_equal(Array, kit.get_sample_data(
|
47
|
-
assert_equal(Array, kit.get_sample_data(
|
46
|
+
assert_equal(Array, kit.get_sample_data("mono8").class)
|
47
|
+
assert_equal(Array, kit.get_sample_data("bass_mono_16.wav").class)
|
48
48
|
end
|
49
49
|
|
50
50
|
def test_build_kit_no_sounds
|
data/test/kit_test.rb
CHANGED
@@ -1,21 +1,21 @@
|
|
1
|
-
require
|
1
|
+
require "includes"
|
2
2
|
|
3
3
|
class KitTest < Minitest::Test
|
4
4
|
def test_kit_with_items
|
5
|
-
kit = Kit.new({
|
5
|
+
kit = Kit.new({"label1" => [1,2,3], "label2" => [4,5,6], "label3" => [7,8,9]}, 1, 16)
|
6
6
|
|
7
7
|
assert_equal(["label1", "label2", "label3"], kit.labels)
|
8
|
-
assert_equal([1,2,3], kit.get_sample_data(
|
9
|
-
assert_equal([4,5,6], kit.get_sample_data(
|
10
|
-
assert_raises(Kit::LabelNotFoundError) { kit.get_sample_data(
|
11
|
-
assert_equal([7,8,9], kit.get_sample_data(
|
8
|
+
assert_equal([1,2,3], kit.get_sample_data("label1"))
|
9
|
+
assert_equal([4,5,6], kit.get_sample_data("label2"))
|
10
|
+
assert_raises(Kit::LabelNotFoundError) { kit.get_sample_data("nope") }
|
11
|
+
assert_equal([7,8,9], kit.get_sample_data("label3"))
|
12
12
|
end
|
13
13
|
|
14
14
|
def test_kit_with_no_items
|
15
15
|
kit = Kit.new({}, 1, 16)
|
16
16
|
|
17
17
|
assert_equal([], kit.labels)
|
18
|
-
assert_raises(Kit::LabelNotFoundError) { kit.get_sample_data(
|
18
|
+
assert_raises(Kit::LabelNotFoundError) { kit.get_sample_data("foo") }
|
19
19
|
end
|
20
20
|
|
21
21
|
def test_num_channels
|
data/test/pattern_test.rb
CHANGED
@@ -1,176 +1,176 @@
|
|
1
|
-
require
|
2
|
-
|
3
|
-
class PatternTest < Minitest::Test
|
4
|
-
SAMPLE_RATE = 44100
|
5
|
-
SECONDS_IN_MINUTE = 60.0
|
6
|
-
|
7
|
-
def generate_test_data
|
8
|
-
test_patterns = {}
|
9
|
-
|
10
|
-
pattern = Pattern.new :blank
|
11
|
-
test_patterns[:blank] = pattern
|
12
|
-
|
13
|
-
verse_tracks = [
|
14
|
-
Track.new("bass.wav", "X...X...X...XX..X...X...XX..X..."),
|
15
|
-
Track.new("snare.wav", "..X...X...X...X.X...X...X...X..."),
|
16
|
-
Track.new("hh_closed.wav", "X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X."),
|
17
|
-
Track.new("hh_open.wav", "X...............X..............X"),
|
18
|
-
]
|
19
|
-
pattern = Pattern.new(:verse, verse_tracks)
|
20
|
-
test_patterns[:verse] = pattern
|
21
|
-
|
22
|
-
staircase_tracks = [
|
23
|
-
Track.new("bass.wav", "X..."),
|
24
|
-
Track.new("snare.wav", "X.."),
|
25
|
-
Track.new("hh_closed.wav", "X."),
|
26
|
-
]
|
27
|
-
pattern = Pattern.new(:staircase, staircase_tracks)
|
28
|
-
test_patterns[:staircase] = pattern
|
29
|
-
|
30
|
-
test_patterns
|
31
|
-
end
|
32
|
-
|
33
|
-
def test_initialize
|
34
|
-
test_patterns = generate_test_data
|
35
|
-
|
36
|
-
pattern = test_patterns[:blank]
|
37
|
-
assert_equal(pattern.name, :blank)
|
38
|
-
assert_equal(pattern.tracks.length, 0)
|
39
|
-
|
40
|
-
pattern = test_patterns[:verse]
|
41
|
-
assert_equal(pattern.name, :verse)
|
42
|
-
assert_equal(pattern.tracks.length, 4)
|
43
|
-
|
44
|
-
pattern = test_patterns[:staircase]
|
45
|
-
assert_equal(pattern.name, :staircase)
|
46
|
-
assert_equal(pattern.tracks.length, 3)
|
47
|
-
|
48
|
-
tracks = [
|
49
|
-
Track.new("track1", "X...X..."),
|
50
|
-
Track.new("track2", "X..."),
|
51
|
-
]
|
52
|
-
pattern = Pattern.new(:tracks_provided_in_constructor, tracks)
|
53
|
-
assert_equal(pattern.name, :tracks_provided_in_constructor)
|
54
|
-
assert_equal(pattern.tracks.length, 2)
|
55
|
-
assert_pattern_tracks(pattern, {"track1" => {name: "track1", rhythm: "X...X..."},
|
56
|
-
"track2" => {name: "track2", rhythm: "X......."}})
|
57
|
-
|
58
|
-
tracks = [
|
59
|
-
Track.new("my_sound", "X...X..."),
|
60
|
-
Track.new("my_other_sound", "X..."),
|
61
|
-
Track.new("my_sound", ".X.........."),
|
62
|
-
Track.new("my_sound2", "..X........."),
|
63
|
-
Track.new("my_sound", ".."),
|
64
|
-
]
|
65
|
-
pattern = Pattern.new("whatevs", tracks)
|
66
|
-
|
67
|
-
assert_pattern_tracks(pattern, {"my_sound" => {name: "my_sound", rhythm: "X...X......."},
|
68
|
-
"my_other_sound" => {name: "my_other_sound", rhythm: "X..........."},
|
69
|
-
"my_sound2" => {name: "my_sound", rhythm: ".X.........."},
|
70
|
-
"my_sound22" => {name: "my_sound2", rhythm: "..X........."},
|
71
|
-
"my_sound3" => {name: "my_sound", rhythm: "............"},})
|
72
|
-
end
|
73
|
-
|
74
|
-
def test_track_array_is_frozen
|
75
|
-
tracks = [
|
76
|
-
Track.new("my_sound1", "X...X..."),
|
77
|
-
Track.new("my_sound2", "X.X.X.X."),
|
78
|
-
Track.new("my_sound3", "XXXXXXXX"),
|
79
|
-
]
|
80
|
-
pattern = Pattern.new("whatevs", tracks)
|
81
|
-
|
82
|
-
assert_raises(RuntimeError) { pattern.tracks["my_sound4"] = Track.new("my_sound4", "X...X...") }
|
83
|
-
end
|
84
|
-
|
85
|
-
def test_track_unique_name_already_taken
|
86
|
-
tracks = [
|
87
|
-
Track.new("my_sound2", "X...X..."),
|
88
|
-
Track.new("my_sound", "X.X.X.X."),
|
89
|
-
Track.new("my_sound", "XXXXXXXX"),
|
90
|
-
]
|
91
|
-
pattern = Pattern.new("whatevs", tracks)
|
92
|
-
|
93
|
-
assert_pattern_tracks(pattern, {"my_sound2" => {name: "my_sound2", rhythm: "X...X..."},
|
94
|
-
"my_sound" => {name: "my_sound", rhythm: "X.X.X.X."},
|
95
|
-
# The first attempt at a unique name would be "my_sound2", but that is already taken
|
96
|
-
"my_sound3" => {name: "my_sound", rhythm: "XXXXXXXX"}})
|
97
|
-
end
|
98
|
-
|
99
|
-
def test_step_count
|
100
|
-
test_patterns = generate_test_data
|
101
|
-
|
102
|
-
assert_equal(0, test_patterns[:blank].step_count)
|
103
|
-
assert_equal(32, test_patterns[:verse].step_count)
|
104
|
-
assert_equal(4, test_patterns[:staircase].step_count)
|
105
|
-
end
|
106
|
-
|
107
|
-
def test_same_tracks_as?
|
108
|
-
left_tracks = [
|
109
|
-
Track.new("bass", "X...X..."),
|
110
|
-
Track.new("snare", "..X...X."),
|
111
|
-
Track.new("hh_closed", "X.X.X.X."),
|
112
|
-
]
|
113
|
-
left_pattern = Pattern.new("left", left_tracks)
|
114
|
-
|
115
|
-
right_tracks = [
|
116
|
-
Track.new("bass", "X...X..."),
|
117
|
-
Track.new("snare", "..X...X."),
|
118
|
-
Track.new("hh_closed", "X.X.X.X."),
|
119
|
-
]
|
120
|
-
right_pattern = Pattern.new("right", right_tracks)
|
121
|
-
assert(left_pattern.same_tracks_as?(right_pattern))
|
122
|
-
assert(right_pattern.same_tracks_as?(left_pattern))
|
123
|
-
|
124
|
-
# Now switch up the order. Left and right should still be equal.
|
125
|
-
right_tracks = [
|
126
|
-
Track.new("snare", "..X...X."),
|
127
|
-
Track.new("hh_closed", "X.X.X.X."),
|
128
|
-
Track.new("bass", "X...X..."),
|
129
|
-
]
|
130
|
-
right_pattern = Pattern.new("right", right_tracks)
|
131
|
-
assert(left_pattern.same_tracks_as?(right_pattern))
|
132
|
-
assert(right_pattern.same_tracks_as?(left_pattern))
|
133
|
-
|
134
|
-
# Now compare the pattern with same rhythms but different track names. Should not be equal.
|
135
|
-
different_names_tracks = [
|
136
|
-
Track.new("tom", "X...X..."),
|
137
|
-
Track.new("cymbal", "..X...X."),
|
138
|
-
Track.new("hh_open", "X.X.X.X."),
|
139
|
-
]
|
140
|
-
different_names_pattern = Pattern.new("different_names", different_names_tracks)
|
141
|
-
assert_equal(false, left_pattern.same_tracks_as?(different_names_pattern))
|
142
|
-
assert_equal(false, different_names_pattern.same_tracks_as?(left_pattern))
|
143
|
-
|
144
|
-
# Now compare the pattern with same track names but different rhythms. Should not be equal.
|
145
|
-
different_beats_tracks = [
|
146
|
-
Track.new("bass", "X...X..."),
|
147
|
-
Track.new("snare", "..X...X."),
|
148
|
-
Track.new("hh_closed", "X.XXX.X."),
|
149
|
-
]
|
150
|
-
different_beats_pattern = Pattern.new("different_beats", different_beats_tracks)
|
151
|
-
assert_equal(false, left_pattern.same_tracks_as?(different_beats_pattern))
|
152
|
-
assert_equal(false, different_beats_pattern.same_tracks_as?(left_pattern))
|
153
|
-
|
154
|
-
# Now compare a pattern with the same tracks, but with one extra one as well. Should not be equal.
|
155
|
-
something_extra_tracks = [
|
156
|
-
Track.new("bass", "X...X..."),
|
157
|
-
Track.new("snare", "..X...X."),
|
158
|
-
Track.new("hh_closed", "X.X.X.X."),
|
159
|
-
Track.new("extra", "X..X..X."),
|
160
|
-
]
|
161
|
-
something_extra = Pattern.new("something_extra", something_extra_tracks)
|
162
|
-
assert_equal(false, left_pattern.same_tracks_as?(something_extra))
|
163
|
-
assert_equal(false, something_extra.same_tracks_as?(left_pattern))
|
164
|
-
end
|
165
|
-
|
166
|
-
private
|
167
|
-
|
168
|
-
def assert_pattern_tracks(pattern, expected_pattern_structure)
|
169
|
-
assert_equal(expected_pattern_structure.keys, pattern.tracks.keys)
|
170
|
-
|
171
|
-
expected_pattern_structure.each do |pattern_key, expected_track|
|
172
|
-
assert_equal(expected_track[:name], pattern.tracks[pattern_key].name)
|
173
|
-
assert_equal(expected_track[:rhythm], pattern.tracks[pattern_key].rhythm)
|
174
|
-
end
|
175
|
-
end
|
176
|
-
end
|
1
|
+
require "includes"
|
2
|
+
|
3
|
+
class PatternTest < Minitest::Test
|
4
|
+
SAMPLE_RATE = 44100
|
5
|
+
SECONDS_IN_MINUTE = 60.0
|
6
|
+
|
7
|
+
def generate_test_data
|
8
|
+
test_patterns = {}
|
9
|
+
|
10
|
+
pattern = Pattern.new :blank
|
11
|
+
test_patterns[:blank] = pattern
|
12
|
+
|
13
|
+
verse_tracks = [
|
14
|
+
Track.new("bass.wav", "X...X...X...XX..X...X...XX..X..."),
|
15
|
+
Track.new("snare.wav", "..X...X...X...X.X...X...X...X..."),
|
16
|
+
Track.new("hh_closed.wav", "X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X."),
|
17
|
+
Track.new("hh_open.wav", "X...............X..............X"),
|
18
|
+
]
|
19
|
+
pattern = Pattern.new(:verse, verse_tracks)
|
20
|
+
test_patterns[:verse] = pattern
|
21
|
+
|
22
|
+
staircase_tracks = [
|
23
|
+
Track.new("bass.wav", "X..."),
|
24
|
+
Track.new("snare.wav", "X.."),
|
25
|
+
Track.new("hh_closed.wav", "X."),
|
26
|
+
]
|
27
|
+
pattern = Pattern.new(:staircase, staircase_tracks)
|
28
|
+
test_patterns[:staircase] = pattern
|
29
|
+
|
30
|
+
test_patterns
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_initialize
|
34
|
+
test_patterns = generate_test_data
|
35
|
+
|
36
|
+
pattern = test_patterns[:blank]
|
37
|
+
assert_equal(pattern.name, :blank)
|
38
|
+
assert_equal(pattern.tracks.length, 0)
|
39
|
+
|
40
|
+
pattern = test_patterns[:verse]
|
41
|
+
assert_equal(pattern.name, :verse)
|
42
|
+
assert_equal(pattern.tracks.length, 4)
|
43
|
+
|
44
|
+
pattern = test_patterns[:staircase]
|
45
|
+
assert_equal(pattern.name, :staircase)
|
46
|
+
assert_equal(pattern.tracks.length, 3)
|
47
|
+
|
48
|
+
tracks = [
|
49
|
+
Track.new("track1", "X...X..."),
|
50
|
+
Track.new("track2", "X..."),
|
51
|
+
]
|
52
|
+
pattern = Pattern.new(:tracks_provided_in_constructor, tracks)
|
53
|
+
assert_equal(pattern.name, :tracks_provided_in_constructor)
|
54
|
+
assert_equal(pattern.tracks.length, 2)
|
55
|
+
assert_pattern_tracks(pattern, {"track1" => {name: "track1", rhythm: "X...X..."},
|
56
|
+
"track2" => {name: "track2", rhythm: "X......."}})
|
57
|
+
|
58
|
+
tracks = [
|
59
|
+
Track.new("my_sound", "X...X..."),
|
60
|
+
Track.new("my_other_sound", "X..."),
|
61
|
+
Track.new("my_sound", ".X.........."),
|
62
|
+
Track.new("my_sound2", "..X........."),
|
63
|
+
Track.new("my_sound", ".."),
|
64
|
+
]
|
65
|
+
pattern = Pattern.new("whatevs", tracks)
|
66
|
+
|
67
|
+
assert_pattern_tracks(pattern, {"my_sound" => {name: "my_sound", rhythm: "X...X......."},
|
68
|
+
"my_other_sound" => {name: "my_other_sound", rhythm: "X..........."},
|
69
|
+
"my_sound2" => {name: "my_sound", rhythm: ".X.........."},
|
70
|
+
"my_sound22" => {name: "my_sound2", rhythm: "..X........."},
|
71
|
+
"my_sound3" => {name: "my_sound", rhythm: "............"},})
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_track_array_is_frozen
|
75
|
+
tracks = [
|
76
|
+
Track.new("my_sound1", "X...X..."),
|
77
|
+
Track.new("my_sound2", "X.X.X.X."),
|
78
|
+
Track.new("my_sound3", "XXXXXXXX"),
|
79
|
+
]
|
80
|
+
pattern = Pattern.new("whatevs", tracks)
|
81
|
+
|
82
|
+
assert_raises(RuntimeError) { pattern.tracks["my_sound4"] = Track.new("my_sound4", "X...X...") }
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_track_unique_name_already_taken
|
86
|
+
tracks = [
|
87
|
+
Track.new("my_sound2", "X...X..."),
|
88
|
+
Track.new("my_sound", "X.X.X.X."),
|
89
|
+
Track.new("my_sound", "XXXXXXXX"),
|
90
|
+
]
|
91
|
+
pattern = Pattern.new("whatevs", tracks)
|
92
|
+
|
93
|
+
assert_pattern_tracks(pattern, {"my_sound2" => {name: "my_sound2", rhythm: "X...X..."},
|
94
|
+
"my_sound" => {name: "my_sound", rhythm: "X.X.X.X."},
|
95
|
+
# The first attempt at a unique name would be "my_sound2", but that is already taken
|
96
|
+
"my_sound3" => {name: "my_sound", rhythm: "XXXXXXXX"}})
|
97
|
+
end
|
98
|
+
|
99
|
+
def test_step_count
|
100
|
+
test_patterns = generate_test_data
|
101
|
+
|
102
|
+
assert_equal(0, test_patterns[:blank].step_count)
|
103
|
+
assert_equal(32, test_patterns[:verse].step_count)
|
104
|
+
assert_equal(4, test_patterns[:staircase].step_count)
|
105
|
+
end
|
106
|
+
|
107
|
+
def test_same_tracks_as?
|
108
|
+
left_tracks = [
|
109
|
+
Track.new("bass", "X...X..."),
|
110
|
+
Track.new("snare", "..X...X."),
|
111
|
+
Track.new("hh_closed", "X.X.X.X."),
|
112
|
+
]
|
113
|
+
left_pattern = Pattern.new("left", left_tracks)
|
114
|
+
|
115
|
+
right_tracks = [
|
116
|
+
Track.new("bass", "X...X..."),
|
117
|
+
Track.new("snare", "..X...X."),
|
118
|
+
Track.new("hh_closed", "X.X.X.X."),
|
119
|
+
]
|
120
|
+
right_pattern = Pattern.new("right", right_tracks)
|
121
|
+
assert(left_pattern.same_tracks_as?(right_pattern))
|
122
|
+
assert(right_pattern.same_tracks_as?(left_pattern))
|
123
|
+
|
124
|
+
# Now switch up the order. Left and right should still be equal.
|
125
|
+
right_tracks = [
|
126
|
+
Track.new("snare", "..X...X."),
|
127
|
+
Track.new("hh_closed", "X.X.X.X."),
|
128
|
+
Track.new("bass", "X...X..."),
|
129
|
+
]
|
130
|
+
right_pattern = Pattern.new("right", right_tracks)
|
131
|
+
assert(left_pattern.same_tracks_as?(right_pattern))
|
132
|
+
assert(right_pattern.same_tracks_as?(left_pattern))
|
133
|
+
|
134
|
+
# Now compare the pattern with same rhythms but different track names. Should not be equal.
|
135
|
+
different_names_tracks = [
|
136
|
+
Track.new("tom", "X...X..."),
|
137
|
+
Track.new("cymbal", "..X...X."),
|
138
|
+
Track.new("hh_open", "X.X.X.X."),
|
139
|
+
]
|
140
|
+
different_names_pattern = Pattern.new("different_names", different_names_tracks)
|
141
|
+
assert_equal(false, left_pattern.same_tracks_as?(different_names_pattern))
|
142
|
+
assert_equal(false, different_names_pattern.same_tracks_as?(left_pattern))
|
143
|
+
|
144
|
+
# Now compare the pattern with same track names but different rhythms. Should not be equal.
|
145
|
+
different_beats_tracks = [
|
146
|
+
Track.new("bass", "X...X..."),
|
147
|
+
Track.new("snare", "..X...X."),
|
148
|
+
Track.new("hh_closed", "X.XXX.X."),
|
149
|
+
]
|
150
|
+
different_beats_pattern = Pattern.new("different_beats", different_beats_tracks)
|
151
|
+
assert_equal(false, left_pattern.same_tracks_as?(different_beats_pattern))
|
152
|
+
assert_equal(false, different_beats_pattern.same_tracks_as?(left_pattern))
|
153
|
+
|
154
|
+
# Now compare a pattern with the same tracks, but with one extra one as well. Should not be equal.
|
155
|
+
something_extra_tracks = [
|
156
|
+
Track.new("bass", "X...X..."),
|
157
|
+
Track.new("snare", "..X...X."),
|
158
|
+
Track.new("hh_closed", "X.X.X.X."),
|
159
|
+
Track.new("extra", "X..X..X."),
|
160
|
+
]
|
161
|
+
something_extra = Pattern.new("something_extra", something_extra_tracks)
|
162
|
+
assert_equal(false, left_pattern.same_tracks_as?(something_extra))
|
163
|
+
assert_equal(false, something_extra.same_tracks_as?(left_pattern))
|
164
|
+
end
|
165
|
+
|
166
|
+
private
|
167
|
+
|
168
|
+
def assert_pattern_tracks(pattern, expected_pattern_structure)
|
169
|
+
assert_equal(expected_pattern_structure.keys, pattern.tracks.keys)
|
170
|
+
|
171
|
+
expected_pattern_structure.each do |pattern_key, expected_track|
|
172
|
+
assert_equal(expected_track[:name], pattern.tracks[pattern_key].name)
|
173
|
+
assert_equal(expected_track[:rhythm], pattern.tracks[pattern_key].rhythm)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|