beats 2.1.0 → 2.1.1
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 +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
|