beats 1.2.4 → 1.2.5

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.
@@ -2,7 +2,7 @@ require 'includes'
2
2
 
3
3
  class MockSongOptimizer < SongOptimizer
4
4
  def clone_song_ignoring_patterns_and_flow(original_song)
5
- return super
5
+ super
6
6
  end
7
7
  end
8
8
 
@@ -35,7 +35,7 @@ Chorus:
35
35
  - hh_closed: X.XXX.XXX.XX..X.
36
36
  - sounds/tom4.wav: ...........X....
37
37
  - sounds/tom2.wav: ..............X."
38
-
38
+
39
39
  EXAMPLE_SONG_YAML_EMPTY_SUB_PATTERN = "
40
40
  Song:
41
41
  Tempo: 135
@@ -50,16 +50,16 @@ Verse:
50
50
  - snare: ..........X."
51
51
 
52
52
  def self.load_fixture(fixture_name)
53
- return SongParser.new().parse(FIXTURE_BASE_PATH, File.read("test/fixtures/#{fixture_name}"))
53
+ SongParser.new().parse(FIXTURE_BASE_PATH, File.read("test/fixtures/#{fixture_name}"))
54
54
  end
55
55
 
56
56
  def test_optimize
57
57
  parser = SongParser.new()
58
58
  original_song, kit = parser.parse(File.dirname(__FILE__) + "/..", EXAMPLE_SONG_YAML)
59
-
59
+
60
60
  optimizer = SongOptimizer.new()
61
61
  optimized_song = optimizer.optimize(original_song, 4)
62
-
62
+
63
63
  assert_equal(optimized_song.tempo, 135)
64
64
  #assert_equal(optimized_song.total_tracks, 5)
65
65
 
@@ -67,47 +67,47 @@ Verse:
67
67
  #assert_equal(original_song.sample_length, optimized_song.sample_length)
68
68
  #assert_equal(original_song.sample_length_with_overflow, optimized_song.sample_length_with_overflow)
69
69
  #assert_equal(original_song.sample_data(false), optimized_song.sample_data(false))
70
-
70
+
71
71
  # Patterns :verse_0 and :verse_4 should be removed since they are identical to :chorus_0
72
72
  assert_equal([:chorus_0, :chorus_12, :chorus_4, :chorus_8, :verse_12, :verse_8],
73
73
  optimized_song.patterns.keys.sort {|x, y| x.to_s <=> y.to_s })
74
-
74
+
75
75
  pattern = optimized_song.patterns[:verse_8]
76
76
  assert_equal(pattern.tracks.keys.sort, ["bass", "hh_closed"])
77
77
  assert_equal(pattern.tracks["bass"].rhythm, "X...")
78
78
  assert_equal(pattern.tracks["hh_closed"].rhythm, "X.X.")
79
-
79
+
80
80
  pattern = optimized_song.patterns[:verse_12]
81
81
  assert_equal(pattern.tracks.keys.sort, ["agogo", "bass", "hh_closed", "snare"])
82
82
  assert_equal(pattern.tracks["bass"].rhythm, "X...")
83
83
  assert_equal(pattern.tracks["snare"].rhythm, "..X.")
84
84
  assert_equal(pattern.tracks["hh_closed"].rhythm, "X.X.")
85
85
  assert_equal(pattern.tracks["agogo"].rhythm, "..XX")
86
-
86
+
87
87
  pattern = optimized_song.patterns[:chorus_0]
88
88
  assert_equal(pattern.tracks.keys.sort, ["bass", "hh_closed"])
89
89
  assert_equal(pattern.tracks["bass"].rhythm, "X...")
90
90
  assert_equal(pattern.tracks["hh_closed"].rhythm, "X.XX")
91
-
91
+
92
92
  pattern = optimized_song.patterns[:chorus_4]
93
93
  assert_equal(pattern.tracks.keys.sort, ["bass", "hh_closed", "snare"])
94
94
  assert_equal(pattern.tracks["bass"].rhythm, "X...")
95
95
  assert_equal(pattern.tracks["snare"].rhythm, "X...")
96
96
  assert_equal(pattern.tracks["hh_closed"].rhythm, "X.XX")
97
-
97
+
98
98
  pattern = optimized_song.patterns[:chorus_8]
99
99
  assert_equal(pattern.tracks.keys.sort, ["bass", "hh_closed", "sounds/tom4.wav"])
100
100
  assert_equal(pattern.tracks["bass"].rhythm, "XX..")
101
101
  assert_equal(pattern.tracks["hh_closed"].rhythm, "X.XX")
102
102
  assert_equal(pattern.tracks["sounds/tom4.wav"].rhythm, "...X")
103
-
103
+
104
104
  pattern = optimized_song.patterns[:chorus_12]
105
105
  assert_equal(pattern.tracks.keys.sort, ["bass", "hh_closed", "snare", "sounds/tom2.wav"])
106
106
  assert_equal(pattern.tracks["bass"].rhythm, "X...")
107
107
  assert_equal(pattern.tracks["snare"].rhythm, "X...")
108
108
  assert_equal(pattern.tracks["hh_closed"].rhythm, "..X.")
109
109
  assert_equal(pattern.tracks["sounds/tom2.wav"].rhythm, "..X.")
110
-
110
+
111
111
  assert_equal(optimized_song.flow, [:chorus_0, :chorus_0, :verse_8, :verse_12,
112
112
  :chorus_0, :chorus_0, :verse_8, :verse_12,
113
113
  :chorus_0, :chorus_4, :chorus_8, :chorus_12,
@@ -121,54 +121,54 @@ Verse:
121
121
  :chorus_0, :chorus_4, :chorus_8, :chorus_12,
122
122
  :chorus_0, :chorus_4, :chorus_8, :chorus_12])
123
123
  end
124
-
124
+
125
125
  def test_optimize_song_nondivisible_max_pattern_length()
126
126
  parser = SongParser.new()
127
127
  original_song, kit = parser.parse(File.dirname(__FILE__) + "/..", EXAMPLE_SONG_YAML_EMPTY_SUB_PATTERN)
128
-
128
+
129
129
  optimizer = SongOptimizer.new()
130
130
  optimized_song = optimizer.optimize(original_song, 7)
131
-
131
+
132
132
  pattern = optimized_song.patterns[:verse_0]
133
133
  assert_equal(["bass"], pattern.tracks.keys.sort)
134
134
  assert_equal("X......", pattern.tracks["bass"].rhythm)
135
-
135
+
136
136
  pattern = optimized_song.patterns[:verse_7]
137
137
  assert_equal(["bass", "snare"], pattern.tracks.keys.sort)
138
138
  assert_equal(".X...", pattern.tracks["bass"].rhythm)
139
139
  assert_equal("...X.", pattern.tracks["snare"].rhythm)
140
-
140
+
141
141
  assert_equal([:verse_0, :verse_7], optimized_song.flow)
142
142
  end
143
-
143
+
144
144
  def test_pattern_collision
145
145
  original_song, kit = SongOptimizerTest.load_fixture("valid/optimize_pattern_collision.txt")
146
146
  optimizer = SongOptimizer.new()
147
147
  optimized_song = optimizer.optimize(original_song, 4)
148
-
148
+
149
149
  assert_equal([:verse2_0, :verse_0, :verse_20], optimized_song.patterns.keys.sort {|x, y| x.to_s <=> y.to_s })
150
150
  end
151
151
 
152
152
  def test_optimize_song_containing_empty_pattern()
153
153
  parser = SongParser.new()
154
154
  original_song, kit = parser.parse(File.dirname(__FILE__) + "/..", EXAMPLE_SONG_YAML_EMPTY_SUB_PATTERN)
155
-
155
+
156
156
  optimizer = SongOptimizer.new()
157
157
  optimized_song = optimizer.optimize(original_song, 4)
158
-
158
+
159
159
  pattern = optimized_song.patterns[:verse_0]
160
160
  assert_equal(["bass"], pattern.tracks.keys.sort)
161
161
  assert_equal("X...", pattern.tracks["bass"].rhythm)
162
-
162
+
163
163
  pattern = optimized_song.patterns[:verse_4]
164
164
  assert_equal(["placeholder"], pattern.tracks.keys.sort)
165
165
  assert_equal("....", pattern.tracks["placeholder"].rhythm)
166
-
166
+
167
167
  pattern = optimized_song.patterns[:verse_8]
168
168
  assert_equal(["bass", "snare"], pattern.tracks.keys.sort)
169
169
  assert_equal("X...", pattern.tracks["bass"].rhythm)
170
170
  assert_equal("..X.", pattern.tracks["snare"].rhythm)
171
-
171
+
172
172
  assert_equal([:verse_0, :verse_4, :verse_8], optimized_song.flow)
173
173
  end
174
174
  end
@@ -12,7 +12,7 @@ class SongParserTest < Test::Unit::TestCase
12
12
  :example_with_empty_track,
13
13
  :multiple_tracks_same_sound,
14
14
  :with_structure]
15
-
15
+
16
16
  INVALID_FIXTURES = [:bad_repeat_count,
17
17
  :bad_flow,
18
18
  :bad_tempo,
@@ -25,7 +25,7 @@ class SongParserTest < Test::Unit::TestCase
25
25
  :sound_in_track_wrong_format]
26
26
 
27
27
  def self.load_fixture(fixture_name)
28
- return SongParser.new().parse(FIXTURE_BASE_PATH, File.read("test/fixtures/#{fixture_name}"))
28
+ SongParser.new().parse(FIXTURE_BASE_PATH, File.read("test/fixtures/#{fixture_name}"))
29
29
  end
30
30
 
31
31
  def self.generate_test_data
@@ -37,20 +37,20 @@ class SongParserTest < Test::Unit::TestCase
37
37
  test_songs[fixture_name] = song
38
38
  test_kits[fixture_name] = kit
39
39
  end
40
-
40
+
41
41
  return test_songs, test_kits
42
42
  end
43
43
 
44
44
  # TODO: Add somes tests to validate the Kits
45
45
  def test_valid_parse
46
46
  test_songs, test_kits = SongParserTest.generate_test_data()
47
-
47
+
48
48
  assert_equal(120, test_songs[:no_tempo].tempo)
49
49
  assert_equal([:verse], test_songs[:no_tempo].flow)
50
-
50
+
51
51
  assert_equal(100, test_songs[:repeats_not_specified].tempo)
52
52
  assert_equal([:verse], test_songs[:repeats_not_specified].flow)
53
-
53
+
54
54
  # These two songs should be the same, except that one uses a kit in the song header
55
55
  # and the other doesn't.
56
56
  [:example_no_kit, :example_with_kit].each do |song_key|
@@ -68,13 +68,13 @@ class SongParserTest < Test::Unit::TestCase
68
68
  assert_equal(5, song.patterns[:chorus].tracks.length)
69
69
  assert_equal(1, song.patterns[:bridge].tracks.length)
70
70
  end
71
-
71
+
72
72
  song = test_songs[:example_with_empty_track]
73
73
  assert_equal(1, song.patterns.length)
74
74
  assert_equal(2, song.patterns[:verse].tracks.length)
75
75
  assert_equal("........", song.patterns[:verse].tracks["test/sounds/bass_mono_8.wav"].rhythm)
76
76
  assert_equal("X...X...", song.patterns[:verse].tracks["test/sounds/snare_mono_8.wav"].rhythm)
77
-
77
+
78
78
  song = test_songs[:multiple_tracks_same_sound]
79
79
  assert_equal(2, song.patterns.length)
80
80
  assert_equal(7, song.patterns[:verse].tracks.length)
@@ -87,21 +87,21 @@ class SongParserTest < Test::Unit::TestCase
87
87
  assert_equal("..............X.", song.patterns[:verse].tracks["snare"].rhythm)
88
88
  assert_equal("X.XXX.XXX.X.X.X.", song.patterns[:verse].tracks["hh_closed"].rhythm)
89
89
  assert_equal("..............XX", song.patterns[:verse].tracks["agogo"].rhythm)
90
-
90
+
91
91
  song = test_songs[:with_structure]
92
92
  assert_equal([:verse, :verse], song.flow)
93
93
  assert_equal(1, song.patterns.length)
94
94
  assert_equal(1, song.patterns[:verse].tracks.length)
95
95
  assert_equal("X...X...", song.patterns[:verse].tracks["test/sounds/bass_mono_8.wav"].rhythm)
96
96
  end
97
-
97
+
98
98
  def test_invalid_parse
99
99
  INVALID_FIXTURES.each do |fixture|
100
100
  assert_raise(SongParseError) do
101
101
  song = SongParserTest.load_fixture("invalid/#{fixture}.txt")
102
102
  end
103
103
  end
104
-
104
+
105
105
  assert_raise(InvalidRhythmError) do
106
106
  song = SongParserTest.load_fixture("invalid/bad_rhythm.txt")
107
107
  end
Binary file
Binary file
@@ -3,45 +3,45 @@ require 'includes'
3
3
  class TrackTest < Test::Unit::TestCase
4
4
  def generate_test_data
5
5
  test_tracks = {}
6
-
6
+
7
7
  test_tracks[:blank] = Track.new("bass", "")
8
8
  test_tracks[:solo] = Track.new("bass", "X")
9
9
  test_tracks[:with_overflow] = Track.new("bass", "...X")
10
10
  test_tracks[:with_barlines] = Track.new("bass", "|X.X.|X.X.|")
11
11
  test_tracks[:placeholder] = Track.new("bass", "....")
12
12
  test_tracks[:complicated] = Track.new("bass", "..X...X...X...X.X...X...X...X...")
13
-
14
- return test_tracks
13
+
14
+ test_tracks
15
15
  end
16
-
16
+
17
17
  def test_initialize
18
18
  test_tracks = generate_test_data()
19
-
19
+
20
20
  assert_equal([0], test_tracks[:blank].beats)
21
21
  assert_equal("bass", test_tracks[:blank].name)
22
22
  assert_equal("", test_tracks[:blank].rhythm)
23
-
23
+
24
24
  assert_equal([0, 1], test_tracks[:solo].beats)
25
25
  assert_equal("bass", test_tracks[:solo].name)
26
26
  assert_equal("X", test_tracks[:solo].rhythm)
27
-
27
+
28
28
  assert_equal([3, 1], test_tracks[:with_overflow].beats)
29
29
  assert_equal("...X", test_tracks[:with_overflow].rhythm)
30
-
30
+
31
31
  assert_equal([0, 2, 2, 2, 2], test_tracks[:with_barlines].beats)
32
32
  # Bar lines should be removed from rhythm:
33
33
  assert_equal("X.X.X.X.", test_tracks[:with_barlines].rhythm)
34
-
34
+
35
35
  assert_equal([4], test_tracks[:placeholder].beats)
36
36
  assert_equal("....", test_tracks[:placeholder].rhythm)
37
-
37
+
38
38
  assert_equal([2, 4, 4, 4, 2, 4, 4, 4, 4], test_tracks[:complicated].beats)
39
39
  assert_equal("..X...X...X...X.X...X...X...X...", test_tracks[:complicated].rhythm)
40
40
  end
41
-
41
+
42
42
  def test_step_count
43
43
  test_tracks = generate_test_data()
44
-
44
+
45
45
  assert_equal(0, test_tracks[:blank].step_count())
46
46
  assert_equal(1, test_tracks[:solo].step_count())
47
47
  assert_equal(4, test_tracks[:with_overflow].step_count())
metadata CHANGED
@@ -1,52 +1,51 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: beats
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.4
5
- prerelease:
4
+ version: 1.2.5
6
5
  platform: ruby
7
6
  authors:
8
7
  - Joel Strait
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2012-12-23 00:00:00.000000000 Z
11
+ date: 2013-12-31 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: wavefile
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
17
  - - '='
20
18
  - !ruby/object:Gem::Version
21
- version: 0.4.0
19
+ version: 0.6.0
22
20
  type: :runtime
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
24
  - - '='
28
25
  - !ruby/object:Gem::Version
29
- version: 0.4.0
26
+ version: 0.6.0
30
27
  description: A command-line drum machine. Feed it a song notated in YAML, and it will
31
28
  produce a precision-milled Wave file of impeccable timing and feel.
32
29
  email: joel dot strait at Google's popular web mail service
33
30
  executables:
34
31
  - beats
35
- extensions: []
32
+ extensions:
33
+ - ext/mkrf_conf.rb
36
34
  extra_rdoc_files: []
37
35
  files:
38
36
  - LICENSE
39
37
  - README.markdown
40
38
  - Rakefile
41
- - lib/audioengine.rb
42
- - lib/audioutils.rb
39
+ - lib/beats/audioengine.rb
40
+ - lib/beats/audioutils.rb
41
+ - lib/beats/beatsrunner.rb
42
+ - lib/beats/kit.rb
43
+ - lib/beats/pattern.rb
44
+ - lib/beats/song.rb
45
+ - lib/beats/songoptimizer.rb
46
+ - lib/beats/songparser.rb
47
+ - lib/beats/track.rb
43
48
  - lib/beats.rb
44
- - lib/kit.rb
45
- - lib/pattern.rb
46
- - lib/song.rb
47
- - lib/songoptimizer.rb
48
- - lib/songparser.rb
49
- - lib/track.rb
50
49
  - lib/wavefile/cachingwriter.rb
51
50
  - bin/beats
52
51
  - test/audioengine_test.rb
@@ -104,6 +103,7 @@ files:
104
103
  - test/fixtures/valid/example_stereo_8.txt
105
104
  - test/fixtures/valid/example_with_empty_track.txt
106
105
  - test/fixtures/valid/example_with_kit.txt
106
+ - test/fixtures/valid/foo.txt
107
107
  - test/fixtures/valid/multiple_tracks_same_sound.txt
108
108
  - test/fixtures/valid/no_tempo.txt
109
109
  - test/fixtures/valid/optimize_pattern_collision.txt
@@ -126,6 +126,8 @@ files:
126
126
  - test/sounds/agogo_low_mono_8.wav
127
127
  - test/sounds/agogo_low_stereo_16.wav
128
128
  - test/sounds/agogo_low_stereo_8.wav
129
+ - test/sounds/bass.wav
130
+ - test/sounds/bass2.wav
129
131
  - test/sounds/bass2_mono_16.wav
130
132
  - test/sounds/bass2_mono_8.wav
131
133
  - test/sounds/bass2_stereo_16.wav
@@ -205,29 +207,29 @@ files:
205
207
  - test/sounds/tom4_stereo_8.wav
206
208
  - test/sounds/tone.wav
207
209
  - test/track_test.rb
210
+ - ext/mkrf_conf.rb
208
211
  homepage: http://beatsdrummachine.com/
209
212
  licenses: []
213
+ metadata: {}
210
214
  post_install_message:
211
215
  rdoc_options: []
212
216
  require_paths:
213
217
  - lib
214
218
  required_ruby_version: !ruby/object:Gem::Requirement
215
- none: false
216
219
  requirements:
217
- - - ! '>='
220
+ - - '>='
218
221
  - !ruby/object:Gem::Version
219
222
  version: '0'
220
223
  required_rubygems_version: !ruby/object:Gem::Requirement
221
- none: false
222
224
  requirements:
223
- - - ! '>='
225
+ - - '>='
224
226
  - !ruby/object:Gem::Version
225
227
  version: '0'
226
228
  requirements: []
227
229
  rubyforge_project:
228
- rubygems_version: 1.8.24
230
+ rubygems_version: 2.1.11
229
231
  signing_key:
230
- specification_version: 3
232
+ specification_version: 4
231
233
  summary: A command-line drum machine. Feed it a song notated in YAML, and it will
232
234
  produce a precision-milled Wave file of impeccable timing and feel.
233
235
  test_files:
@@ -286,6 +288,7 @@ test_files:
286
288
  - test/fixtures/valid/example_stereo_8.txt
287
289
  - test/fixtures/valid/example_with_empty_track.txt
288
290
  - test/fixtures/valid/example_with_kit.txt
291
+ - test/fixtures/valid/foo.txt
289
292
  - test/fixtures/valid/multiple_tracks_same_sound.txt
290
293
  - test/fixtures/valid/no_tempo.txt
291
294
  - test/fixtures/valid/optimize_pattern_collision.txt
@@ -308,6 +311,8 @@ test_files:
308
311
  - test/sounds/agogo_low_mono_8.wav
309
312
  - test/sounds/agogo_low_stereo_16.wav
310
313
  - test/sounds/agogo_low_stereo_8.wav
314
+ - test/sounds/bass.wav
315
+ - test/sounds/bass2.wav
311
316
  - test/sounds/bass2_mono_16.wav
312
317
  - test/sounds/bass2_mono_8.wav
313
318
  - test/sounds/bass2_stereo_16.wav
@@ -1,161 +0,0 @@
1
- # This class actually generates the output audio data that is saved to disk.
2
- #
3
- # To produce audio data, it needs two things: a Song and a Kit. The Song tells
4
- # it which sounds to trigger and when, while the Kit provides the sample data
5
- # for each of these sounds.
6
- #
7
- # Example usage, assuming song and kit are already defined:
8
- #
9
- # engine = AudioEngine.new(song, kit)
10
- # engine.write_to_file("my_song.wav")
11
- #
12
- class AudioEngine
13
- SAMPLE_RATE = 44100
14
- PACK_CODE = "s*" # All output sample data is assumed to be 16-bit
15
-
16
- def initialize(song, kit)
17
- @song = song
18
- @kit = kit
19
-
20
- @step_sample_length = AudioUtils.step_sample_length(SAMPLE_RATE, @song.tempo)
21
- @composited_pattern_cache = {}
22
- end
23
-
24
- def write_to_file(output_file_name)
25
- packed_pattern_cache = {}
26
- num_tracks_in_song = @song.total_tracks
27
-
28
- # Open output wave file and prepare it for writing sample data.
29
- format = WaveFile::Format.new(@kit.num_channels, @kit.bits_per_sample, SAMPLE_RATE)
30
- writer = WaveFile::CachingWriter.new(output_file_name, format)
31
-
32
- # Generate each pattern's sample data, or pull it from cache, and append it to the wave file.
33
- incoming_overflow = {}
34
- @song.flow.each do |pattern_name|
35
- key = [pattern_name, incoming_overflow.hash]
36
- unless packed_pattern_cache.member?(key)
37
- sample_data = generate_pattern_sample_data(@song.patterns[pattern_name], incoming_overflow)
38
-
39
- packed_pattern_cache[key] = { :primary => WaveFile::Buffer.new(sample_data[:primary], format),
40
- :overflow => WaveFile::Buffer.new(sample_data[:overflow], format) }
41
- end
42
-
43
- writer.write(packed_pattern_cache[key][:primary])
44
- incoming_overflow = packed_pattern_cache[key][:overflow].samples
45
- end
46
-
47
- # Write any remaining overflow from the final pattern
48
- final_overflow_composite = AudioUtils.composite(incoming_overflow.values, format.channels)
49
- final_overflow_composite = AudioUtils.scale(final_overflow_composite, format.channels, num_tracks_in_song)
50
- writer.write(WaveFile::Buffer.new(final_overflow_composite, format))
51
-
52
- writer.close()
53
-
54
- return WaveFile::Reader.info(output_file_name).duration
55
- end
56
-
57
- attr_reader :step_sample_length
58
-
59
- private
60
-
61
- # Generates the sample data for a single track, using the specified sound's sample data.
62
- def generate_track_sample_data(track, sound)
63
- beats = track.beats
64
- if beats == [0]
65
- return {:primary => [], :overflow => []} # Is this really what should happen? Why throw away overflow?
66
- end
67
-
68
- fill_value = (@kit.num_channels == 1) ? 0 : [0, 0]
69
- primary_sample_data = [].fill(fill_value, 0, AudioUtils.step_start_sample(track.step_count, @step_sample_length))
70
-
71
- step_index = beats[0]
72
- beat_sample_length = 0
73
- beats[1...(beats.length)].each do |beat_step_length|
74
- start_sample = AudioUtils.step_start_sample(step_index, @step_sample_length)
75
- end_sample = [(start_sample + sound.length), primary_sample_data.length].min
76
- beat_sample_length = end_sample - start_sample
77
-
78
- primary_sample_data[start_sample...end_sample] = sound[0...beat_sample_length]
79
-
80
- step_index += beat_step_length
81
- end
82
-
83
- overflow_sample_data = (sound == [] || beats.length == 1) ? [] : sound[beat_sample_length...(sound.length)]
84
-
85
- return {:primary => primary_sample_data, :overflow => overflow_sample_data}
86
- end
87
-
88
- # Composites the sample data for each of the pattern's tracks, and returns the overflow sample data
89
- # from tracks whose last sound trigger extends past the end of the pattern. This overflow can be
90
- # used by the next pattern to avoid sounds cutting off when the pattern changes.
91
- def generate_pattern_sample_data(pattern, incoming_overflow)
92
- # Unless cached, composite each track's sample data.
93
- if @composited_pattern_cache[pattern] == nil
94
- primary_sample_data, overflow_sample_data = composite_pattern_tracks(pattern)
95
- @composited_pattern_cache[pattern] = {:primary => primary_sample_data.dup, :overflow => overflow_sample_data.dup}
96
- else
97
- primary_sample_data = @composited_pattern_cache[pattern][:primary].dup
98
- overflow_sample_data = @composited_pattern_cache[pattern][:overflow].dup
99
- end
100
-
101
- # Composite overflow from the previous pattern onto this pattern, to prevent sounds from cutting off.
102
- primary_sample_data, overflow_sample_data = handle_incoming_overflow(pattern,
103
- incoming_overflow,
104
- primary_sample_data,
105
- overflow_sample_data)
106
- primary_sample_data = AudioUtils.scale(primary_sample_data, @kit.num_channels, @song.total_tracks)
107
-
108
- return {:primary => primary_sample_data, :overflow => overflow_sample_data}
109
- end
110
-
111
- def composite_pattern_tracks(pattern)
112
- overflow_sample_data = {}
113
-
114
- raw_track_sample_arrays = []
115
- pattern.tracks.each do |track_name, track|
116
- temp = generate_track_sample_data(track, @kit.get_sample_data(track.name))
117
- raw_track_sample_arrays << temp[:primary]
118
- overflow_sample_data[track_name] = temp[:overflow]
119
- end
120
-
121
- primary_sample_data = AudioUtils.composite(raw_track_sample_arrays, @kit.num_channels)
122
- return primary_sample_data, overflow_sample_data
123
- end
124
-
125
- # Applies sound overflow (i.e. long sounds such as cymbal crash which extend past the last step)
126
- # from the previous pattern in the flow to the current pattern. This prevents sounds from being
127
- # cut off when the pattern changes.
128
- #
129
- # It would probably be shorter and conceptually simpler to deal with incoming overflow in
130
- # generate_track_sample_data() instead of this method. (In fact, this method would go away).
131
- # However, doing it this way allows for caching composited pattern sample data, and
132
- # applying incoming overflow to the composite. This allows each pattern to only be composited once,
133
- # regardless of the incoming overflow that each performance of it receives. If incoming overflow
134
- # was handled at the Track level we couldn't do that.
135
- def handle_incoming_overflow(pattern, incoming_overflow, primary_sample_data, overflow_sample_data)
136
- pattern_track_names = pattern.tracks.keys
137
- sample_arrays = [primary_sample_data]
138
-
139
- incoming_overflow.each do |incoming_track_name, incoming_sample_data|
140
- end_sample = incoming_sample_data.length
141
-
142
- if pattern_track_names.member?(incoming_track_name)
143
- track = pattern.tracks[incoming_track_name]
144
-
145
- if track.beats.length > 1
146
- intro_length = (pattern.tracks[incoming_track_name].beats[0] * step_sample_length).floor
147
- end_sample = [end_sample, intro_length].min
148
- end
149
- end
150
-
151
- if end_sample > primary_sample_data.length
152
- end_sample = primary_sample_data.length
153
- overflow_sample_data[incoming_track_name] = incoming_sample_data[(primary_sample_data.length)...(incoming_sample_data.length)]
154
- end
155
-
156
- sample_arrays << incoming_sample_data[0...end_sample]
157
- end
158
-
159
- return AudioUtils.composite(sample_arrays, @kit.num_channels), overflow_sample_data
160
- end
161
- end