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.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +21 -23
  3. data/README.markdown +7 -15
  4. data/Rakefile +3 -3
  5. data/bin/beats +5 -5
  6. data/lib/beats.rb +15 -15
  7. data/lib/beats/kit.rb +1 -1
  8. data/lib/beats/kit_builder.rb +5 -1
  9. data/lib/beats/song.rb +1 -1
  10. data/lib/beats/song_parser.rb +45 -23
  11. data/test/audio_engine_test.rb +1 -1
  12. data/test/audio_utils_test.rb +1 -1
  13. data/test/caching_writer_test.rb +1 -1
  14. data/test/fixtures/invalid/flow_invalid_repeat_count_prefix.txt +8 -0
  15. data/test/fixtures/invalid/flow_invalid_repeat_count_suffix.txt +8 -0
  16. data/test/fixtures/invalid/{bad_repeat_count.txt → flow_missing_repeat_count.txt} +3 -3
  17. data/test/fixtures/invalid/flow_negative_repeat_count.txt +8 -0
  18. data/test/fixtures/invalid/{bad_flow.txt → flow_non_existent_pattern.txt} +0 -0
  19. data/test/fixtures/invalid/flow_non_string_repeat_count.txt +8 -0
  20. data/test/fixtures/invalid/flow_not_an_array.txt +15 -0
  21. data/test/fixtures/invalid/flow_pattern_name_not_a_string.txt +15 -0
  22. data/test/fixtures/invalid/flow_repeat_count_is_missing_prefix.txt +8 -0
  23. data/test/fixtures/invalid/flow_section_not_a_string_or_hash.txt +15 -0
  24. data/test/fixtures/invalid/kit_filename_not_a_string.txt +17 -0
  25. data/test/fixtures/invalid/kit_not_an_array.txt +17 -0
  26. data/test/fixtures/invalid/pattern_not_an_array.txt +17 -0
  27. data/test/fixtures/invalid/pattern_referenced_pattern_name_not_a_string.txt +19 -0
  28. data/test/fixtures/invalid/pattern_unreferenced_pattern_name_not_a_string.txt +18 -0
  29. data/test/fixtures/invalid/pattern_with_incomplete_track.txt +10 -0
  30. data/test/fixtures/invalid/pattern_with_nil_track.txt +10 -0
  31. data/test/fixtures/valid/composite_sounds_trailing_comma.txt +16 -0
  32. data/test/fixtures/valid/example_alternate_array_syntax.txt +11 -0
  33. data/test/fixtures/valid/{multiple_song_header_sections.txt → multiple_copies_of_song_components.txt} +28 -4
  34. data/test/fixtures/valid/multiple_yaml_documents.txt +33 -0
  35. data/test/fixtures/valid/track_sound_has_mixed_capitalization.txt +13 -0
  36. data/test/includes.rb +2 -2
  37. data/test/integration_test.rb +3 -3
  38. data/test/kit_builder_test.rb +3 -3
  39. data/test/kit_test.rb +7 -7
  40. data/test/pattern_test.rb +176 -176
  41. data/test/song_optimizer_test.rb +1 -1
  42. data/test/song_parser_test.rb +86 -11
  43. data/test/song_swinger_test.rb +1 -1
  44. data/test/song_test.rb +1 -1
  45. data/test/sounds/MiXeD_CaPiTaLiZaTiOn.wav +0 -0
  46. data/test/sounds/bass_mono_16_11025.wav +0 -0
  47. data/test/sounds/bass_mono_16_22050.wav +0 -0
  48. data/test/sounds/bass_mono_24.wav +0 -0
  49. data/test/sounds/bass_mono_8.mp3 +0 -0
  50. data/test/track_test.rb +1 -1
  51. metadata +58 -12
@@ -0,0 +1,8 @@
1
+ # Invalid song, since flow repeat count is missing the "x" prefix
2
+ Song:
3
+ Tempo: 100
4
+ Flow:
5
+ - Verse: 2
6
+
7
+ Verse:
8
+ - test/sounds/bass_mono_8.wav: X...X...X...XX..X...X...XX..X...
@@ -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,10 @@
1
+ # Invalid song, since it contains a pattern with a track containing just a sound name not followed by a ":"
2
+
3
+ Song:
4
+ Tempo: 100
5
+ Flow:
6
+ - Verse: x1
7
+
8
+ Verse:
9
+ - test/sounds/bass_mono_8.wav: X...X...
10
+ - test/sounds/snare_mono_8.wav
@@ -0,0 +1,10 @@
1
+ # Invalid song, since it contains a pattern with a nil track
2
+
3
+ Song:
4
+ Tempo: 100
5
+ Flow:
6
+ - Verse: x1
7
+
8
+ Verse:
9
+ - test/sounds/bass_mono_8.wav: X...X...
10
+ -
@@ -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: 100
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...
@@ -1,3 +1,3 @@
1
- require 'minitest/autorun'
2
- require 'beats'
1
+ require "minitest/autorun"
2
+ require "beats"
3
3
  include Beats
@@ -1,4 +1,4 @@
1
- require 'includes'
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
- "bad_repeat_count.txt",
15
- "bad_flow.txt",
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",
@@ -1,4 +1,4 @@
1
- require 'includes'
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('mono8').class)
47
- assert_equal(Array, kit.get_sample_data('bass_mono_16.wav').class)
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
@@ -1,21 +1,21 @@
1
- require 'includes'
1
+ require "includes"
2
2
 
3
3
  class KitTest < Minitest::Test
4
4
  def test_kit_with_items
5
- kit = Kit.new({'label1' => [1,2,3], 'label2' => [4,5,6], 'label3' => [7,8,9]}, 1, 16)
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('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'))
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('foo') }
18
+ assert_raises(Kit::LabelNotFoundError) { kit.get_sample_data("foo") }
19
19
  end
20
20
 
21
21
  def test_num_channels
@@ -1,176 +1,176 @@
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
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