beats 2.1.1 → 2.1.2

Sign up to get free protection for your applications and to get access to all the features.
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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8ae934eac074a47b7bb5f587fd96eb457f0ddabfbf18f5667e838dd01832c247
4
- data.tar.gz: ee54703a136d795679807ea67f4f5cb79532f090d0c641f924590a036a35b2d9
3
+ metadata.gz: 387b94eb7731f5bf659134c0e6ceb9d0ba01947e130a64a18517fde233a8a958
4
+ data.tar.gz: c2b0b0b67d07f471861b5b457b522739f0cdc68f6240e66ee999e935633ebfd0
5
5
  SHA512:
6
- metadata.gz: 2f2e931c783acab297d17187cadd51ebbba9cd5bd55ec9a81a6fddd15b0d95b8b03027508cbed4e3b07740a3e0d04d25e9e000b06074b916bc23cc6e4e0df3ed
7
- data.tar.gz: f6fe7883c7a9f21d62d252eec3f646aa2af5f47acda24d20dc43c8761fcf84c87716a2cbc53248fdcf31f4c7832cb5d24353ea49520ada5555a6a9cb85234898
6
+ metadata.gz: fe882750e5035fb61aed261c9d7b7e61f3d048da16ee09aaeca089c07aba44abdc15ccceec8f0cc7b7885ddd1c997f3eb19d6abc46d64891aac48996a396c35b
7
+ data.tar.gz: 5f9a9ffcc09a015fd8bdd00a6308be9af14029924ba9d65967b0fa6d5b9a37c3545155b3ad51f79d8328770310788e82cafd49bb4314272686b7f0dbc9504240
data/LICENSE CHANGED
@@ -1,24 +1,22 @@
1
- == Beats Drum Machine
1
+ Copyright (c) 2010-19 Joel Strait
2
2
 
3
- # Copyright (c) 2010-18 Joel Strait
4
- #
5
- # Permission is hereby granted, free of charge, to any person
6
- # obtaining a copy of this software and associated documentation
7
- # files (the "Software"), to deal in the Software without
8
- # restriction, including without limitation the rights to use,
9
- # copy, modify, merge, publish, distribute, sublicense, and/or sell
10
- # copies of the Software, and to permit persons to whom the
11
- # Software is furnished to do so, subject to the following
12
- # conditions:
13
- #
14
- # The above copyright notice and this permission notice shall be
15
- # included in all copies or substantial portions of the Software.
16
- #
17
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
- # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19
- # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
- # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21
- # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22
- # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23
- # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24
- # OTHER DEALINGS IN THE SOFTWARE.
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
@@ -35,7 +35,7 @@ For more, check out [beatsdrummachine.com](https://beatsdrummachine.com)
35
35
  Installation
36
36
  ------------
37
37
 
38
- To install the latest stable version (2.1.1) from [rubygems.org](https://rubygems.org/gems/beats), run the following from the command line:
38
+ To install the latest stable version (2.1.2) from [rubygems.org](https://rubygems.org/gems/beats), run the following from the command line:
39
39
 
40
40
  gem install beats
41
41
 
@@ -49,25 +49,17 @@ Beats is not very useful unless you have some sounds to use with it. You can dow
49
49
  Usage
50
50
  -----
51
51
 
52
- Beats runs from the command-line. Run `beats -h` to see the available options. For more detailed instructions, visit [https://github.com/jstrait/beats/wiki/Usage](https://github.com/jstrait/beats/wiki/Usage) on the [Beats Wiki](https://github.com/jstrait/beats/wiki).
52
+ Beats runs from the command-line. Run `beats -h` to see the available options. For more detailed instructions, visit <https://beatsdrummachine.com/usage/>.
53
53
 
54
- Check out [this tutorial at beatsdrummachine.com](https://beatsdrummachine.com/tutorial/) to see an example of how to create a beat from sratch.
54
+ Check out [this tutorial at beatsdrummachine.com](https://beatsdrummachine.com/tutorial/) to see an example of how to create a beat from scratch.
55
55
 
56
56
 
57
- What's New in v2.1.1
57
+ What's New in v2.1.2
58
58
  --------------------
59
59
 
60
- The latest version of Beats is 2.1.1, released on June 29, 2018. It contains these changes:
60
+ The latest version of Beats is 2.1.2, released on December 18, 2019. It contains these changes:
61
61
 
62
- * Several error messages are improved to be more accurate or specific.
63
- * **Bug fix**: Songs can now use *.wav files with more than 2 channels. Previously, using a sound with more than 2 channels would cause a fatal `Invalid sample data array in AudioUtils.normalize()` error.
64
- * **Bug fix**: If a sound is defined multiple times in a Kit, the final definition should be used as the winner. However, previously this did not occur if the earlier definition was for a composite sound. For example, with this Kit:
65
-
66
- Kit:
67
- - sound: [sound1.wav, sound2.wav]
68
- - sound: sound3.wav
69
-
70
- `sound` will now be bound to `sound3.wav`, not `[sound1.wav, sound2.wav]`.
62
+ * Several confusing/unhelpful errors shown due to an error in an input file have been improved. For example, if a pattern has the invalid name "4", the error message will now be `Pattern name '4' is not valid. It must be a value that will be parsed from YAML as a String.`, instead of `undefined method 'downcase' for 4:Integer`.
71
63
 
72
64
  For info about previous releases, visit https://github.com/jstrait/beats/releases.
73
65
 
@@ -92,7 +84,7 @@ To run the tests:
92
84
  Found a Bug? Have a Suggestion? Want to Contribute?
93
85
  ---------------------------------------------------
94
86
 
95
- Contact me (Joel Strait) by sending a GitHub message or opening a GitHub issue.
87
+ Contact me (Joel Strait) by opening a GitHub issue.
96
88
 
97
89
 
98
90
  License
data/Rakefile CHANGED
@@ -1,6 +1,6 @@
1
- require 'rake/testtask'
1
+ require "rake/testtask"
2
2
 
3
3
  Rake::TestTask.new do |t|
4
- t.libs << 'lib' << 'test'
5
- t.pattern = 'test/**/*_test.rb'
4
+ t.libs << "lib" << "test"
5
+ t.pattern = "test/**/*_test.rb"
6
6
  end
data/bin/beats CHANGED
@@ -15,24 +15,24 @@ def parse_options
15
15
  optparse = OptionParser.new do |opts|
16
16
  opts.banner = "usage: beats [options] input_file [output_file]"
17
17
 
18
- opts.on('-s', '--split', "Save each track to an individual wave file") do
18
+ opts.on("-s", "--split", "Save each track to an individual wave file") do
19
19
  options[:split] = true
20
20
  end
21
21
 
22
- opts.on('-p', '--pattern PATTERN_NAME', "Output a single pattern instead of the whole song" ) do |p|
22
+ opts.on("-p", "--pattern PATTERN_NAME", "Output a single pattern instead of the whole song") do |p|
23
23
  options[:pattern] = p
24
24
  end
25
25
 
26
- opts.on('--path BASE_PATH', "The base path used to load sound files with relative paths.") do |base_path|
26
+ opts.on("--path BASE_PATH", "The base path used to load sound files with relative paths.") do |base_path|
27
27
  options[:base_path] = base_path
28
28
  end
29
29
 
30
- opts.on('-v', '--version', "Display version number and exit") do
30
+ opts.on("-v", "--version", "Display version number and exit") do
31
31
  puts "Beats Drum Machine #{Beats::VERSION}"
32
32
  exit
33
33
  end
34
34
 
35
- opts.on( '-h', '--help', "Display this screen and exit" ) do
35
+ opts.on("-h", "--help", "Display this screen and exit") do
36
36
  puts opts
37
37
  exit
38
38
  end
@@ -1,21 +1,21 @@
1
- require 'yaml'
1
+ require "yaml"
2
2
 
3
3
  gem "wavefile", "=0.8.1"
4
- require 'wavefile'
5
- require 'wavefile/caching_writer'
4
+ require "wavefile"
5
+ require "wavefile/caching_writer"
6
6
 
7
- require 'beats/audio_engine'
8
- require 'beats/audio_utils'
9
- require 'beats/beats_runner'
10
- require 'beats/kit'
11
- require 'beats/kit_builder'
12
- require 'beats/pattern'
13
- require 'beats/song'
14
- require 'beats/song_parser'
15
- require 'beats/song_optimizer'
16
- require 'beats/track'
17
- require 'beats/transforms/song_swinger'
7
+ require "beats/audio_engine"
8
+ require "beats/audio_utils"
9
+ require "beats/beats_runner"
10
+ require "beats/kit"
11
+ require "beats/kit_builder"
12
+ require "beats/pattern"
13
+ require "beats/song"
14
+ require "beats/song_parser"
15
+ require "beats/song_optimizer"
16
+ require "beats/track"
17
+ require "beats/transforms/song_swinger"
18
18
 
19
19
  module Beats
20
- VERSION = "2.1.1"
20
+ VERSION = "2.1.2"
21
21
  end
@@ -2,7 +2,7 @@ module Beats
2
2
  class Kit
3
3
  class LabelNotFoundError < RuntimeError; end
4
4
 
5
- PLACEHOLDER_TRACK_NAME = 'empty_track_placeholder_name_234hkj32hjk4hjkhds23'
5
+ PLACEHOLDER_TRACK_NAME = "empty_track_placeholder_name_234hkj32hjk4hjkhds23"
6
6
 
7
7
  def initialize(items, num_channels, bits_per_sample)
8
8
  @items = items
@@ -27,13 +27,17 @@ module Beats
27
27
 
28
28
  filenames.each do |filename|
29
29
  unless filename.is_a?(String)
30
- raise SoundFileNotFoundError, "The Kit sound '#{label}' contains an invalid file: '#{filename}'"
30
+ raise SoundFileNotFoundError, "Kit sound '#{label}' contains an invalid filename: '#{filename}'. It must be a value that will be parsed from YAML as a String."
31
31
  end
32
32
  composite_replacement = "#{label}-#{File.basename(filename, ".*")}"
33
33
  @labels_to_filenames[composite_replacement] = absolute_file_name(filename)
34
34
  @composite_replacements[label] << composite_replacement
35
35
  end
36
36
  else
37
+ unless filenames.is_a?(String)
38
+ raise SoundFileNotFoundError, "Kit sound '#{label}' has an invalid filename: '#{filenames}'. It must be a value that will be parsed from YAML as a String."
39
+ end
40
+
37
41
  @labels_to_filenames[label] = absolute_file_name(filenames)
38
42
  @composite_replacements.delete(label)
39
43
  end
@@ -50,7 +50,7 @@ module Beats
50
50
  end
51
51
 
52
52
  def tempo=(new_tempo)
53
- unless (new_tempo.is_a?(Integer) || new_tempo.class == Float) && new_tempo > 0
53
+ unless (new_tempo.is_a?(Integer) || new_tempo.is_a?(Float)) && new_tempo > 0
54
54
  raise InvalidTempoError, "Invalid tempo: '#{new_tempo}'. Tempo must be a number greater than 0."
55
55
  end
56
56
 
@@ -86,11 +86,9 @@ Song:
86
86
  raw_song_definition = YAML.load(raw_yaml_string)
87
87
  rescue Psych::SyntaxError => detail
88
88
  raise ParseError, "Syntax error in YAML file: #{detail}"
89
- rescue ArgumentError => detail
90
- raise ParseError, "Syntax error in YAML file: #{detail}"
91
89
  end
92
90
 
93
- header_keys = raw_song_definition.keys.select {|key| key.downcase == "song" }
91
+ header_keys = raw_song_definition.keys.select {|key| key.is_a?(String) && key.downcase == "song" }
94
92
 
95
93
  if header_keys.empty?
96
94
  raise ParseError, NO_SONG_HEADER_ERROR_MSG
@@ -115,6 +113,10 @@ Song:
115
113
  def self.add_kit_sounds_from_kit(kit_builder, raw_kit)
116
114
  return if raw_kit.nil?
117
115
 
116
+ unless raw_kit.is_a?(Array)
117
+ raise ParseError, "Kit is not an array. Make sure each sound in the Kit is placed on new indented line prefixed with a '-'"
118
+ end
119
+
118
120
  # Add sounds defined in the Kit section of the song header
119
121
  # Converts [{a=>1}, {b=>2}, {c=>3}] from raw YAML to {a=>1, b=>2, c=>3}
120
122
  raw_kit.each do |kit_item|
@@ -137,14 +139,26 @@ Song:
137
139
 
138
140
  def self.add_patterns_to_song(song, kit_builder, raw_patterns)
139
141
  raw_patterns.each do |pattern_name, raw_tracks|
142
+ if !pattern_name.is_a?(String)
143
+ raise ParseError, "Pattern name '#{pattern_name}' is not valid. It must be a value that will be parsed from YAML as a String."
144
+ end
145
+
140
146
  if raw_tracks.nil?
141
147
  # TODO: Possibly allow if pattern not referenced in the Flow, or has 0 repeats?
142
148
  raise ParseError, "Pattern '#{pattern_name}' has no tracks. It needs at least one."
143
149
  end
144
150
 
151
+ if !raw_tracks.is_a?(Array)
152
+ raise ParseError, "Tracks in pattern '#{pattern_name}' are not an Array. Make sure each track is placed on new indented line prefixed with a '-'"
153
+ end
154
+
145
155
  tracks = []
146
156
 
147
- raw_tracks.each do |raw_track|
157
+ raw_tracks.each_with_index do |raw_track, index|
158
+ if !raw_track.is_a?(Hash)
159
+ raise ParseError, "Track ##{index + 1} in pattern '#{pattern_name}' is incomplete. Must be in form '- <kit/file name>: <rhythm>'"
160
+ end
161
+
148
162
  track_names = raw_track.keys.first
149
163
  rhythm = raw_track.values.first
150
164
 
@@ -158,7 +172,7 @@ Song:
158
172
 
159
173
  track_names.map! do |track_name|
160
174
  unless track_name.is_a?(String)
161
- raise ParseError, "'#{track_name}' in pattern '#{pattern_name}' is not a valid track sound"
175
+ raise ParseError, "'#{track_name}' in pattern '#{pattern_name}' is not a valid filename/kit sound. It must be a value that will be parsed from YAML as a String."
162
176
  end
163
177
  kit_builder.composite_replacements[track_name] || track_name
164
178
  end
@@ -177,34 +191,42 @@ Song:
177
191
  def self.set_song_flow(song, raw_flow)
178
192
  flow = []
179
193
 
194
+ if !raw_flow.is_a?(Array)
195
+ raise ParseError, "Song flow is not an array. Make sure each section of the flow is placed on new indented line prefixed with a '-'"
196
+ end
197
+
180
198
  raw_flow.each do |pattern_item|
181
- if pattern_item.class == String
182
- pattern_item = {pattern_item => "x1"}
199
+ if !pattern_item.is_a?(Hash)
200
+ if pattern_item.is_a?(String)
201
+ pattern_item = {pattern_item => "x1"}
202
+ else
203
+ raise ParseError, "'#{pattern_item}' is invalid flow section; must be in form '- <pattern name>: <repeat count>'"
204
+ end
183
205
  end
184
206
 
185
207
  pattern_name = pattern_item.keys.first
208
+ if !pattern_name.is_a?(String)
209
+ raise ParseError, "Pattern name '#{pattern_name}' in flow is not valid. It must be a value that will be parsed from YAML as a String."
210
+ end
186
211
  pattern_name_sym = pattern_name.downcase.to_sym
187
212
 
188
- # Convert the number of repeats from a String such as "x4" into an integer such as 4.
189
- multiples_str = pattern_item[pattern_name]
190
- multiples_str.slice!(0)
191
- multiples = multiples_str.to_i
213
+ repeat_count_str = pattern_item[pattern_name]
192
214
 
193
- unless multiples_str.match(/[^0-9]/).nil?
215
+ unless repeat_count_str.is_a?(String) && repeat_count_str.match(/^x[0-9]+$/) != nil
194
216
  raise ParseError,
195
- "'#{multiples_str}' is an invalid number of repeats for pattern '#{pattern_name}'. Number of repeats should be a whole number."
196
- else
197
- if multiples < 0
198
- raise ParseError, "'#{multiples_str}' is an invalid number of repeats for pattern '#{pattern_name}'. Must be 0 or greater."
199
- elsif multiples > 0 && !song.patterns.has_key?(pattern_name_sym)
200
- # This test is purposefully designed to only throw an error if the number of repeats is greater
201
- # than 0. This allows you to specify an undefined pattern in the flow with "x0" repeats.
202
- # This can be convenient for defining the flow before all patterns have been added to the song file.
203
- raise ParseError, "Song flow includes non-existent pattern: '#{pattern_name}'"
204
- end
217
+ "'#{repeat_count_str}' is an invalid number of repeats for pattern '#{pattern_name}'. Number of repeats must be a whole number >= 0, prefixed with 'x'."
218
+ end
219
+
220
+ repeat_count = repeat_count_str[1..-1].to_i
221
+
222
+ if repeat_count > 0 && !song.patterns.has_key?(pattern_name_sym)
223
+ # This test is purposefully designed to only throw an error if the number of repeats is greater
224
+ # than 0. This allows you to specify an undefined pattern in the flow with "x0" repeats.
225
+ # This can be convenient for defining the flow before all patterns have been added to the song file.
226
+ raise ParseError, "Song flow includes non-existent pattern: '#{pattern_name}'"
205
227
  end
206
228
 
207
- multiples.times { flow << pattern_name_sym }
229
+ repeat_count.times { flow << pattern_name_sym }
208
230
  end
209
231
 
210
232
  song.flow = flow
@@ -1,4 +1,4 @@
1
- require 'includes'
1
+ require "includes"
2
2
 
3
3
  # Make private methods public for testing
4
4
  class MockAudioEngine < AudioEngine
@@ -1,4 +1,4 @@
1
- require 'includes'
1
+ require "includes"
2
2
 
3
3
  class AudioUtilsTest < Minitest::Test
4
4
  def test_composite
@@ -1,4 +1,4 @@
1
- require 'includes'
1
+ require "includes"
2
2
 
3
3
  # Basic tests for CachingWriter; the integration tests test it more thoroughly.
4
4
  class CachingWriterTest < Minitest::Test
@@ -0,0 +1,8 @@
1
+ # Invalid song, since the number of repeats for pattern Verse has an invalid prefix
2
+ Song:
3
+ Tempo: 100
4
+ Flow:
5
+ - Verse: ax2
6
+
7
+ Verse:
8
+ - test/sounds/bass_mono_8.wav: X...X...X...XX..X...X...XX..X...
@@ -0,0 +1,8 @@
1
+ # Invalid song, since the number of repeats for pattern Verse has an invalid suffix
2
+ Song:
3
+ Tempo: 100
4
+ Flow:
5
+ - Verse: x2a
6
+
7
+ Verse:
8
+ - test/sounds/bass_mono_8.wav: X...X...X...XX..X...X...XX..X...
@@ -1,8 +1,8 @@
1
- # Invalid song, since the number of repeats for pattern Verse is not a number
1
+ # Invalid song, since the number of repeats for pattern Verse is missing
2
2
  Song:
3
3
  Tempo: 100
4
4
  Flow:
5
- - Verse: x2a
5
+ - Verse:
6
6
 
7
7
  Verse:
8
- - test/sounds/bass_mono_8.wav: X...X...X...XX..X...X...XX..X...
8
+ - test/sounds/bass_mono_8.wav: X...X...X...XX..X...X...XX..X...
@@ -0,0 +1,8 @@
1
+ # Invalid song, since the flow repeat count is less than 0
2
+ Song:
3
+ Tempo: 100
4
+ Flow:
5
+ - Verse: x-2
6
+
7
+ Verse:
8
+ - test/sounds/bass_mono_8.wav: X...X...X...XX..X...X...XX..X...
@@ -0,0 +1,8 @@
1
+ # Invalid song, since the flow repeat count is not a String like "x4"
2
+ Song:
3
+ Tempo: 100
4
+ Flow:
5
+ - Verse: [1, 2, 3]
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 the flow is not an array (i.e., each line is not prefixed with "-")
2
+ Song:
3
+ Tempo: 100
4
+ Flow:
5
+ Verse: x2
6
+ Chorus: x2
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,15 @@
1
+ # Invalid song, a pattern used in the flow has a name which will not be parsed as a String
2
+ Song:
3
+ Tempo: 120
4
+ Flow:
5
+ - Verse: x2
6
+ - 4: x2 # Invalid pattern name!
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.