beats 2.1.0 → 2.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/LICENSE +2 -2
- data/README.markdown +17 -80
- data/bin/beats +2 -7
- data/lib/beats.rb +12 -6
- data/lib/beats/{audioengine.rb → audio_engine.rb} +7 -8
- data/lib/beats/{audioutils.rb → audio_utils.rb} +35 -17
- data/lib/beats/{beatsrunner.rb → beats_runner.rb} +2 -2
- data/lib/beats/kit.rb +6 -5
- data/lib/beats/kit_builder.rb +12 -8
- data/lib/beats/pattern.rb +7 -16
- data/lib/beats/song.rb +7 -8
- data/lib/beats/{songoptimizer.rb → song_optimizer.rb} +11 -8
- data/lib/beats/{songparser.rb → song_parser.rb} +46 -48
- data/lib/beats/track.rb +14 -17
- data/lib/beats/transforms/song_swinger.rb +7 -5
- data/lib/wavefile/{cachingwriter.rb → caching_writer.rb} +2 -2
- data/test/{audioengine_test.rb → audio_engine_test.rb} +49 -46
- data/test/audio_utils_test.rb +86 -0
- data/test/{cachingwriter_test.rb → caching_writer_test.rb} +0 -0
- data/test/fixtures/invalid/sound_in_kit_wrong_format.txt +1 -1
- data/test/fixtures/invalid/sound_in_track_wrong_format.txt +1 -1
- data/test/fixtures/valid/empty_kit.txt +14 -0
- data/test/fixtures/valid/example_song_header_different_capitalization.txt +21 -0
- data/test/fixtures/valid/multiple_patterns_same_name.txt +31 -0
- data/test/fixtures/valid/multiple_song_header_sections.txt +29 -0
- data/test/includes.rb +0 -9
- data/test/kit_builder_test.rb +20 -0
- data/test/kit_test.rb +7 -0
- data/test/pattern_test.rb +86 -74
- data/test/{songoptimizer_test.rb → song_optimizer_test.rb} +5 -8
- data/test/{songparser_test.rb → song_parser_test.rb} +109 -13
- data/test/song_swinger_test.rb +6 -4
- data/test/song_test.rb +32 -14
- data/test/sounds/agogo_high_stereo_16.wav +0 -0
- data/test/sounds/agogo_low_stereo_16.wav +0 -0
- data/test/sounds/bass2_stereo_16.wav +0 -0
- data/test/sounds/bass_stereo_16.wav +0 -0
- data/test/sounds/clave_high_stereo_16.wav +0 -0
- data/test/sounds/clave_low_stereo_16.wav +0 -0
- data/test/sounds/conga_high_stereo_16.wav +0 -0
- data/test/sounds/conga_low_stereo_16.wav +0 -0
- data/test/sounds/cowbell_high_stereo_16.wav +0 -0
- data/test/sounds/cowbell_low_stereo_16.wav +0 -0
- data/test/sounds/hh_closed_stereo_16.wav +0 -0
- data/test/sounds/hh_open_stereo_16.wav +0 -0
- data/test/sounds/ride_stereo_16.wav +0 -0
- data/test/sounds/rim_stereo_16.wav +0 -0
- data/test/sounds/snare2_stereo_16.wav +0 -0
- data/test/sounds/snare_stereo_16.wav +0 -0
- data/test/sounds/tom1_stereo_16.wav +0 -0
- data/test/sounds/tom2_stereo_16.wav +0 -0
- data/test/sounds/tom3_stereo_16.wav +0 -0
- data/test/sounds/tom4_stereo_16.wav +0 -0
- data/test/track_test.rb +51 -5
- metadata +184 -176
- data/test/audioutils_test.rb +0 -46
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 8ae934eac074a47b7bb5f587fd96eb457f0ddabfbf18f5667e838dd01832c247
|
4
|
+
data.tar.gz: ee54703a136d795679807ea67f4f5cb79532f090d0c641f924590a036a35b2d9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2f2e931c783acab297d17187cadd51ebbba9cd5bd55ec9a81a6fddd15b0d95b8b03027508cbed4e3b07740a3e0d04d25e9e000b06074b916bc23cc6e4e0df3ed
|
7
|
+
data.tar.gz: f6fe7883c7a9f21d62d252eec3f646aa2af5f47acda24d20dc43c8761fcf84c87716a2cbc53248fdcf31f4c7832cb5d24353ea49520ada5555a6a9cb85234898
|
data/LICENSE
CHANGED
data/README.markdown
CHANGED
@@ -27,23 +27,23 @@ Beats is a command-line drum machine written in pure Ruby. Feed it a song notate
|
|
27
27
|
- cowbell: ....XX.X..X.X...
|
28
28
|
- deep: .............X..
|
29
29
|
|
30
|
-
And [here's what it sounds like](
|
30
|
+
And [here's what it sounds like](https://beatsdrummachine.com/media/beat.mp3) after getting the Beats treatment. What a glorious groove!
|
31
31
|
|
32
|
-
For more, check out [beatsdrummachine.com](
|
32
|
+
For more, check out [beatsdrummachine.com](https://beatsdrummachine.com)
|
33
33
|
|
34
34
|
|
35
35
|
Installation
|
36
36
|
------------
|
37
37
|
|
38
|
-
To install the latest stable version (2.1.
|
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:
|
39
39
|
|
40
40
|
gem install beats
|
41
41
|
|
42
|
-
Note
|
42
|
+
Note: if you're installing using the default version of Ruby that comes with macOS, you might get a file permission error. If that happens, use `sudo gem install beats` instead. If you're using a version manager such as rbenv, chruby, or RVM, plain `gem install beats` should work fine.
|
43
43
|
|
44
44
|
Once installed, you can then run Beats from the command-line using the `beats` command.
|
45
45
|
|
46
|
-
Beats is not very useful unless you have some sounds to use with it. You can download some example sounds from [
|
46
|
+
Beats is not very useful unless you have some sounds to use with it. You can download some example sounds from [https://beatsdrummachine.com](https://beatsdrummachine.com/download#drum-kits).
|
47
47
|
|
48
48
|
|
49
49
|
Usage
|
@@ -51,87 +51,25 @@ Usage
|
|
51
51
|
|
52
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).
|
53
53
|
|
54
|
-
Check out [this tutorial at beatsdrummachine.com](
|
54
|
+
Check out [this tutorial at beatsdrummachine.com](https://beatsdrummachine.com/tutorial/) to see an example of how to create a beat from sratch.
|
55
55
|
|
56
56
|
|
57
|
-
What's New
|
58
|
-
|
57
|
+
What's New in v2.1.1
|
58
|
+
--------------------
|
59
59
|
|
60
|
-
The latest version of Beats is 2.1.
|
60
|
+
The latest version of Beats is 2.1.1, released on June 29, 2018. It contains these changes:
|
61
61
|
|
62
|
-
|
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:
|
63
65
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
- bass: bass.wav # A traditional non-composite sound
|
68
|
-
- combo_snare: [clap.wav, 808_snare.wav] # A composite sound
|
69
|
-
|
70
|
-
The `combo_snare` sound above is a composite sound made by combining `clap.wav` and `808_snare.wav` together. It can then be used in a pattern:
|
71
|
-
|
72
|
-
Verse:
|
73
|
-
- bass: X.......X.......
|
74
|
-
- combo_snare: ....X.......X...
|
75
|
-
|
76
|
-
This is equivalent to the following song:
|
77
|
-
|
78
|
-
Kit:
|
79
|
-
- bass: bass.wav
|
80
|
-
- clap: clap.wav
|
81
|
-
- snare: 808_snare.wav
|
82
|
-
|
83
|
-
Verse:
|
84
|
-
- bass: X.......X.......
|
85
|
-
- clap: ....X.......X...
|
86
|
-
- snare: ....X.......X...
|
87
|
-
|
88
|
-
When using the `-s` command-line option to write each track to its own *.wav file, each sub-sound in a composite sound will be written to its own file. For example, this song:
|
89
|
-
|
90
|
-
Kit:
|
91
|
-
- combo_snare: [clap.wav, 808_snare.wav]
|
92
|
-
|
93
|
-
Verse:
|
94
|
-
- combo_snare: X...X...X...X...
|
95
|
-
|
96
|
-
...will be written to two different files, `combo_snare-clap.wav` and `combo_snare-808_snare.wav`, when using the `-s` option.
|
97
|
-
|
98
|
-
Finally, when defining a track in a pattern, multiple sounds can be given in an array as a composite sound. Kit sounds and non-Kit sounds can be used together:
|
99
|
-
|
100
|
-
Kit:
|
101
|
-
- bass: bass.wav
|
102
|
-
- combo_snare: [clap.wav, 808_snare.wav]
|
103
|
-
|
104
|
-
Verse:
|
105
|
-
- [bass, combo_snare, other_sound.wav]: X...X...X...X...
|
106
|
-
|
107
|
-
This is a equivalent to:
|
108
|
-
|
109
|
-
Kit:
|
110
|
-
- bass: bass.wav
|
111
|
-
- clap: clap.wav
|
112
|
-
- snare: 808_snare.wav
|
113
|
-
|
114
|
-
Verse:
|
115
|
-
- bass: X...X...X...X...
|
116
|
-
- clap: X...X...X...X...
|
117
|
-
- snare: X...X...X...X...
|
118
|
-
- other_sound.wav: X...X...X...X...
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
What's Slightly Less New
|
123
|
-
------------------------
|
66
|
+
Kit:
|
67
|
+
- sound: [sound1.wav, sound2.wav]
|
68
|
+
- sound: sound3.wav
|
124
69
|
|
125
|
-
|
70
|
+
`sound` will now be bound to `sound3.wav`, not `[sound1.wav, sound2.wav]`.
|
126
71
|
|
127
|
-
|
128
|
-
* Wave files using `WAVEFORMATEXTENSIBLE` format can now be used, due to upgrading the WaveFile gem dependency to v0.8.1 behind the scenes.
|
129
|
-
* Installing the gem is now simpler, since it no longer requires installing the legacy `syck` YAML parser via an extension.
|
130
|
-
* A `Fixnum is deprecated` message is no longer shown when using Ruby 2.4
|
131
|
-
* **Backwards incompatible changes**:
|
132
|
-
* Song files containing a `Structure` section are no longer supported. A `Flow` section should be used instead. Support for the `Structure` section has been deprecated since v1.2.1 (released in 2011).
|
133
|
-
* Track rhythms can no longer start with a `|` character. For example, `|X...X...` is no longer a valid rhythm. However, bar lines are still allowed to appear elsewhere in the rhythm. For example, `X...X...|X...X...|` _is_ a valid rhythm. The reason for this change is that a rhythm starting with `|` is parsed as a YAML scalar block now that Beats is using the Psych YAML library behind the scenes. The fact that the old Syck YAML library didn't treat rhythms starting with a `|` as a YAML scalar block appears to have been a bug in Syck?
|
134
|
-
* The minimum supported Ruby version is now 1.9.3, instead of 1.8.7
|
72
|
+
For info about previous releases, visit https://github.com/jstrait/beats/releases.
|
135
73
|
|
136
74
|
|
137
75
|
Local Development
|
@@ -160,4 +98,3 @@ Contact me (Joel Strait) by sending a GitHub message or opening a GitHub issue.
|
|
160
98
|
License
|
161
99
|
-------
|
162
100
|
Beats Drum Machine is released under the MIT license.
|
163
|
-
|
data/bin/beats
CHANGED
@@ -2,13 +2,8 @@
|
|
2
2
|
|
3
3
|
start_time = Time.now
|
4
4
|
|
5
|
-
$:.unshift File.dirname(__FILE__) + "/../lib"
|
6
|
-
gem "wavefile", "=0.8.1"
|
7
5
|
require "optparse"
|
8
|
-
require "yaml"
|
9
|
-
require "wavefile"
|
10
6
|
require "beats"
|
11
|
-
require "wavefile/cachingwriter"
|
12
7
|
|
13
8
|
include Beats
|
14
9
|
|
@@ -79,9 +74,9 @@ begin
|
|
79
74
|
input_file_name = ARGV[0]
|
80
75
|
output_file_name = ARGV[1]
|
81
76
|
|
82
|
-
|
77
|
+
beats_runner = BeatsRunner.new(input_file_name, output_file_name, options)
|
83
78
|
|
84
|
-
output =
|
79
|
+
output = beats_runner.run()
|
85
80
|
duration = output[:duration]
|
86
81
|
puts "#{duration.minutes}:#{duration.seconds.to_s.rjust(2, '0')} of audio written in #{Time.now - start_time} seconds."
|
87
82
|
rescue => error
|
data/lib/beats.rb
CHANGED
@@ -1,15 +1,21 @@
|
|
1
|
-
require '
|
2
|
-
|
3
|
-
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
gem "wavefile", "=0.8.1"
|
4
|
+
require 'wavefile'
|
5
|
+
require 'wavefile/caching_writer'
|
6
|
+
|
7
|
+
require 'beats/audio_engine'
|
8
|
+
require 'beats/audio_utils'
|
9
|
+
require 'beats/beats_runner'
|
4
10
|
require 'beats/kit'
|
5
11
|
require 'beats/kit_builder'
|
6
12
|
require 'beats/pattern'
|
7
13
|
require 'beats/song'
|
8
|
-
require 'beats/
|
9
|
-
require 'beats/
|
14
|
+
require 'beats/song_parser'
|
15
|
+
require 'beats/song_optimizer'
|
10
16
|
require 'beats/track'
|
11
17
|
require 'beats/transforms/song_swinger'
|
12
18
|
|
13
19
|
module Beats
|
14
|
-
VERSION = "2.1.
|
20
|
+
VERSION = "2.1.1"
|
15
21
|
end
|
@@ -12,7 +12,6 @@ module Beats
|
|
12
12
|
#
|
13
13
|
class AudioEngine
|
14
14
|
SAMPLE_RATE = 44100
|
15
|
-
PACK_CODE = "s*" # All output sample data is assumed to be 16-bit
|
16
15
|
|
17
16
|
def initialize(song, kit)
|
18
17
|
@song = song
|
@@ -37,8 +36,8 @@ module Beats
|
|
37
36
|
unless packed_pattern_cache.member?(key)
|
38
37
|
sample_data = generate_pattern_sample_data(@song.patterns[pattern_name], incoming_overflow)
|
39
38
|
|
40
|
-
packed_pattern_cache[key] = { :
|
41
|
-
:
|
39
|
+
packed_pattern_cache[key] = { primary: WaveFile::Buffer.new(sample_data[:primary], format),
|
40
|
+
overflow: WaveFile::Buffer.new(sample_data[:overflow], format) }
|
42
41
|
end
|
43
42
|
|
44
43
|
writer.write(packed_pattern_cache[key][:primary])
|
@@ -63,10 +62,10 @@ module Beats
|
|
63
62
|
def generate_track_sample_data(track, sound)
|
64
63
|
trigger_step_lengths = track.trigger_step_lengths
|
65
64
|
if trigger_step_lengths == [0]
|
66
|
-
return {:
|
65
|
+
return {primary: [], overflow: []} # Is this really what should happen? Why throw away overflow?
|
67
66
|
end
|
68
67
|
|
69
|
-
fill_value = (@kit.num_channels == 1) ? 0 :
|
68
|
+
fill_value = (@kit.num_channels == 1) ? 0 : Array.new(@kit.num_channels, 0)
|
70
69
|
primary_sample_data = [].fill(fill_value, 0, AudioUtils.step_start_sample(track.step_count, @step_sample_length))
|
71
70
|
|
72
71
|
step_index = trigger_step_lengths[0]
|
@@ -83,7 +82,7 @@ module Beats
|
|
83
82
|
|
84
83
|
overflow_sample_data = (sound == [] || trigger_step_lengths.length == 1) ? [] : sound[trigger_sample_length...(sound.length)]
|
85
84
|
|
86
|
-
{:
|
85
|
+
{primary: primary_sample_data, overflow: overflow_sample_data}
|
87
86
|
end
|
88
87
|
|
89
88
|
# Composites the sample data for each of the pattern's tracks, and returns the overflow sample data
|
@@ -93,7 +92,7 @@ module Beats
|
|
93
92
|
# Unless cached, composite each track's sample data.
|
94
93
|
if @composited_pattern_cache[pattern].nil?
|
95
94
|
primary_sample_data, overflow_sample_data = composite_pattern_tracks(pattern)
|
96
|
-
@composited_pattern_cache[pattern] = {:
|
95
|
+
@composited_pattern_cache[pattern] = {primary: primary_sample_data.dup, overflow: overflow_sample_data.dup}
|
97
96
|
else
|
98
97
|
primary_sample_data = @composited_pattern_cache[pattern][:primary].dup
|
99
98
|
overflow_sample_data = @composited_pattern_cache[pattern][:overflow].dup
|
@@ -106,7 +105,7 @@ module Beats
|
|
106
105
|
overflow_sample_data)
|
107
106
|
primary_sample_data = AudioUtils.scale(primary_sample_data, @kit.num_channels, @song.total_tracks)
|
108
107
|
|
109
|
-
{:
|
108
|
+
{primary: primary_sample_data, overflow: overflow_sample_data}
|
110
109
|
end
|
111
110
|
|
112
111
|
def composite_pattern_tracks(pattern)
|
@@ -7,6 +7,10 @@ module Beats
|
|
7
7
|
# of the longest input array.
|
8
8
|
# WARNING: Incoming arrays can be modified.
|
9
9
|
def self.composite(sample_arrays, num_channels)
|
10
|
+
if num_channels < 1
|
11
|
+
raise ArgumentError, "`num_channels` must be 1 or greater"
|
12
|
+
end
|
13
|
+
|
10
14
|
if sample_arrays == []
|
11
15
|
return []
|
12
16
|
end
|
@@ -16,14 +20,26 @@ module Beats
|
|
16
20
|
|
17
21
|
composited_output = sample_arrays.slice!(0)
|
18
22
|
sample_arrays.each do |sample_array|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
23
|
+
if num_channels == 1
|
24
|
+
sample_array.length.times {|i| composited_output[i] += sample_array[i] }
|
25
|
+
elsif num_channels == 2
|
26
|
+
sample_array.length.times do |i|
|
27
|
+
# Setting composited_output[i][<channel_index>] won't necessary work,
|
28
|
+
# because each sub array might point to the same array, causing more
|
29
|
+
# samples to be set than expected. For example, a sample buffer initialized
|
30
|
+
# using `[].fill([0, 0], 0, 1000)` will result in an array where each index
|
31
|
+
# is a pointer to the same array instance.
|
32
|
+
composited_output[i] = [composited_output[i][0] + sample_array[i][0],
|
33
|
+
composited_output[i][1] + sample_array[i][1]]
|
34
|
+
end
|
35
|
+
elsif num_channels > 2
|
36
|
+
sample_array.each_with_index do |sub_array, i|
|
37
|
+
composited_sub_array = []
|
38
|
+
sub_array.each_with_index do |sample, j|
|
39
|
+
composited_sub_array << composited_output[i][j] + sample
|
26
40
|
end
|
41
|
+
|
42
|
+
composited_output[i] = composited_sub_array
|
27
43
|
end
|
28
44
|
end
|
29
45
|
end
|
@@ -35,21 +51,23 @@ module Beats
|
|
35
51
|
# Scales the amplitude of the incoming sample array by *scale* amount. Can be used in conjunction
|
36
52
|
# with composite() to make sure composited sample arrays don't have an amplitude greater than 1.0.
|
37
53
|
def self.scale(sample_array, num_channels, scale)
|
38
|
-
if
|
54
|
+
if num_channels < 1
|
55
|
+
raise ArgumentError, "`num_channels` must be 1 or greater"
|
56
|
+
end
|
57
|
+
|
58
|
+
if scale == 1 || sample_array == []
|
39
59
|
return sample_array
|
40
60
|
end
|
41
61
|
|
42
|
-
if
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
62
|
+
if num_channels == 1
|
63
|
+
sample_array.map {|sample| sample / scale }
|
64
|
+
elsif num_channels == 2
|
65
|
+
sample_array.map {|sample_frame| [sample_frame[0] / scale, sample_frame[1] / scale]}
|
66
|
+
elsif num_channels > 2
|
67
|
+
sample_array.map do |sample_frame|
|
68
|
+
sample_frame.map {|sample| sample / scale }
|
49
69
|
end
|
50
70
|
end
|
51
|
-
|
52
|
-
sample_array
|
53
71
|
end
|
54
72
|
|
55
73
|
|
@@ -18,7 +18,7 @@ module Beats
|
|
18
18
|
|
19
19
|
def run
|
20
20
|
base_path = @options[:base_path] || File.dirname(@input_file_name)
|
21
|
-
song, kit = SongParser.
|
21
|
+
song, kit = SongParser.parse(base_path, File.read(@input_file_name))
|
22
22
|
|
23
23
|
song = normalize_for_pattern_option(song)
|
24
24
|
songs_to_generate = normalize_for_split_option(song)
|
@@ -29,7 +29,7 @@ module Beats
|
|
29
29
|
AudioEngine.new(optimized_song, kit).write_to_file(output_file_name)
|
30
30
|
end
|
31
31
|
|
32
|
-
{:
|
32
|
+
{duration: durations.last}
|
33
33
|
end
|
34
34
|
|
35
35
|
private
|
data/lib/beats/kit.rb
CHANGED
@@ -6,15 +6,16 @@ module Beats
|
|
6
6
|
|
7
7
|
def initialize(items, num_channels, bits_per_sample)
|
8
8
|
@items = items
|
9
|
+
@labels = @items.keys.freeze
|
9
10
|
@num_channels = num_channels
|
10
11
|
@bits_per_sample = bits_per_sample
|
11
12
|
end
|
12
13
|
|
13
|
-
attr_reader :num_channels, :bits_per_sample
|
14
|
+
attr_reader :labels, :num_channels, :bits_per_sample
|
14
15
|
|
15
16
|
# Returns the sample data for a sound contained in the Kit. If the all sounds in the
|
16
|
-
# kit are mono, then this will be a flat Array of
|
17
|
-
# Otherwise, this will be an Array of
|
17
|
+
# kit are mono, then this will be a flat Array of Integers between -32768 and 32767.
|
18
|
+
# Otherwise, this will be an Array of Integers pairs between -32768 and 32767.
|
18
19
|
#
|
19
20
|
# label - The name of the sound to get sample data for. If the sound was defined in
|
20
21
|
# the Kit section of a song file, this will generally be a descriptive label
|
@@ -23,11 +24,11 @@ module Beats
|
|
23
24
|
#
|
24
25
|
# Examples
|
25
26
|
#
|
26
|
-
# # If @num_channels is 1, a flat Array of
|
27
|
+
# # If @num_channels is 1, a flat Array of Integers:
|
27
28
|
# get_sample_data("bass")
|
28
29
|
# # => [154, 7023, 8132, 2622, -132, 34, ..., -6702]
|
29
30
|
#
|
30
|
-
# # If @num_channels is 2, a Array of
|
31
|
+
# # If @num_channels is 2, a Array of Integers pairs:
|
31
32
|
# get_sample_data("snare")
|
32
33
|
# # => [[57, 1265], [-452, 10543], [-2531, 12643], [-6372, 11653], ..., [5482, 25673]]
|
33
34
|
#
|
data/lib/beats/kit_builder.rb
CHANGED
@@ -20,19 +20,22 @@ module Beats
|
|
20
20
|
def add_item(label, filenames)
|
21
21
|
if filenames.is_a?(Array)
|
22
22
|
if filenames.empty?
|
23
|
-
raise SoundFileNotFoundError, "Kit sound '#{label}'
|
23
|
+
raise SoundFileNotFoundError, "Kit sound '#{label}' is an empty composite sound (i.e. \"[]\"), which is not valid."
|
24
24
|
end
|
25
25
|
|
26
|
+
@composite_replacements[label] = []
|
27
|
+
|
26
28
|
filenames.each do |filename|
|
27
29
|
unless filename.is_a?(String)
|
28
30
|
raise SoundFileNotFoundError, "The Kit sound '#{label}' contains an invalid file: '#{filename}'"
|
29
31
|
end
|
30
|
-
|
32
|
+
composite_replacement = "#{label}-#{File.basename(filename, ".*")}"
|
33
|
+
@labels_to_filenames[composite_replacement] = absolute_file_name(filename)
|
34
|
+
@composite_replacements[label] << composite_replacement
|
31
35
|
end
|
32
|
-
|
33
|
-
@composite_replacements[label] = filenames.map {|filename| "#{label}-#{File.basename(filename, ".*")}" }
|
34
36
|
else
|
35
37
|
@labels_to_filenames[label] = absolute_file_name(filenames)
|
38
|
+
@composite_replacements.delete(label)
|
36
39
|
end
|
37
40
|
end
|
38
41
|
|
@@ -80,10 +83,11 @@ module Beats
|
|
80
83
|
sample_buffer = buffer
|
81
84
|
end
|
82
85
|
rescue Errno::ENOENT
|
83
|
-
raise SoundFileNotFoundError, "Sound file
|
84
|
-
rescue
|
85
|
-
raise InvalidSoundFormatError, "Sound file
|
86
|
-
|
86
|
+
raise SoundFileNotFoundError, "Sound file `#{filename}` not found."
|
87
|
+
rescue WaveFile::UnsupportedFormatError
|
88
|
+
raise InvalidSoundFormatError, "Sound file `#{filename}` is not a supported *.wav format. Beats can use *.wav files with a sample format of 8/16/24/32 PCM or floating point."
|
89
|
+
rescue WaveFile::InvalidFormatError
|
90
|
+
raise InvalidSoundFormatError, "Sound file `#{filename}` is either not a sound file, or is in an unsupported format. Beats can use *.wav files with a sample format of 8/16/24/32 PCM or floating point."
|
87
91
|
end
|
88
92
|
|
89
93
|
sample_buffer
|
data/lib/beats/pattern.rb
CHANGED
@@ -5,28 +5,19 @@ module Beats
|
|
5
5
|
# This object is like sheet music; the AudioEngine is responsible creating actual
|
6
6
|
# audio data for a Pattern (with the help of a Kit).
|
7
7
|
class Pattern
|
8
|
-
def initialize(name)
|
8
|
+
def initialize(name, tracks=[])
|
9
9
|
@name = name
|
10
10
|
@tracks = {}
|
11
|
-
end
|
12
11
|
|
13
|
-
|
14
|
-
def track(name, rhythm)
|
15
|
-
track_key = unique_track_name(name)
|
16
|
-
new_track = Track.new(name, rhythm)
|
17
|
-
@tracks[track_key] = new_track
|
12
|
+
longest_track_length = tracks.map {|track| track.rhythm.length }.max
|
18
13
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
@tracks.values.each do |track|
|
24
|
-
if track.rhythm.length < longest_track_length
|
25
|
-
track.rhythm += "." * (longest_track_length - track.rhythm.length)
|
26
|
-
end
|
14
|
+
tracks.each do |track|
|
15
|
+
track_key = unique_track_name(track.name)
|
16
|
+
new_track = Track.new(track.name, track.rhythm.ljust(longest_track_length, Track::REST))
|
17
|
+
@tracks[track_key] = new_track
|
27
18
|
end
|
28
19
|
|
29
|
-
|
20
|
+
@tracks.freeze
|
30
21
|
end
|
31
22
|
|
32
23
|
def step_count
|