mdaines-soxophone 0.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.
data/COPYING ADDED
@@ -0,0 +1,25 @@
1
+ "Soxophone" is (c) 2009 Michael Daines, but the concept
2
+ and notation scanner is based on "Bloopsaphone", which is
3
+ copyright (c) 2009 why the lucky stiff and is also released
4
+ under the MIT license.
5
+
6
+ Permission is hereby granted, free of charge, to any person
7
+ obtaining a copy of this software and associated documentation
8
+ files (the "Software"), to deal in the Software without restriction,
9
+ including without limitation the rights to use, copy, modify, merge,
10
+ publish, distribute, sublicense, and/or sell copies of the Software,
11
+ and to permit persons to whom the Software is furnished to do so,
12
+ subject to the following 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
18
+ ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
19
+ TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
20
+ PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
21
+ SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
22
+ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
23
+ OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25
+ SOFTWARE.
data/README ADDED
@@ -0,0 +1,167 @@
1
+ !!!SOXOPHONE!!!
2
+ =========
3
+
4
+ This is a small synthesis and
5
+ sequencing library that runs on
6
+ top of SoX. You write little
7
+ Ruby programs and it arranges
8
+ a bunch of command-line
9
+ stuff to make your songs.
10
+
11
+ The notation and general idea
12
+ were borrowed from _why's
13
+ Bloopsaphone.
14
+
15
+
16
+ A quick demo
17
+ ------------
18
+
19
+ 1. You need to install SoX:
20
+
21
+ http://sox.sourceforge.net/
22
+
23
+ You can probably use the version
24
+ provided by your package manager
25
+ (I use MacPorts).
26
+
27
+ 2. Run the example:
28
+
29
+ ruby soxo.rb
30
+
31
+ You'll get an MP3 you can play
32
+ in your music player.
33
+
34
+ open soxo.mp3
35
+
36
+
37
+ The basics of Soxophone
38
+ -----------------------
39
+
40
+ (Follow along with the "boop.rb"
41
+ example.)
42
+
43
+
44
+ 1. Before anything, you need a
45
+ Soxophone block, like this:
46
+
47
+ Soxophone.play do |x|
48
+
49
+ end
50
+
51
+ This means that Soxophone will
52
+ ask SoX to play your song with
53
+ the "play" command. You can
54
+ also write your song to a file,
55
+ like this:
56
+
57
+ Soxophone.write("tune.mp3") do |x|
58
+
59
+ end
60
+
61
+
62
+ 2. Then, you define sounds.
63
+
64
+ To define a sound, ask the object
65
+ the block yields for a sound:
66
+
67
+ Soxophone.play do |x|
68
+ boop = x.sound :sine
69
+ end
70
+
71
+ All the SoX synth sounds, like
72
+ sine waves, square waves, and so
73
+ on are supported.
74
+
75
+ You can set attributes on your
76
+ sounds to change them. For example,
77
+ to add a simple envelope:
78
+
79
+ boop = x.sound :sine
80
+ boop.release = 0.75
81
+
82
+ This means the "boop" sound will
83
+ start fading out three quarters
84
+ of the way from the end.
85
+
86
+ You can add a few other effects,
87
+ like a phaser effect and a
88
+ tremolo effect. See "effects.rb"
89
+ for an example.
90
+
91
+ Soxophone also allows you to use
92
+ sampled sound files. To do this,
93
+ you ask for a sample:
94
+
95
+ tri = x.sample "triangle.mp3"
96
+
97
+ Samples can have effects too, and
98
+ you can play chords with them
99
+ (see below).
100
+
101
+ Samples are pitch-shifted relative
102
+ to middle A. If you're including
103
+ a drum sample, you want to just
104
+ write a middle A (or A4, or
105
+ just "a") so the pitch won't
106
+ be shifted.
107
+
108
+
109
+ 3. Next, you define tunes.
110
+
111
+ Just ask to use one of your sounds
112
+ in a tune:
113
+
114
+ x.tune boop, "a b c"
115
+
116
+ Here, we're asking Soxophone to
117
+ play three quarter notes with the
118
+ "boop" sound.
119
+
120
+ You can write as many tunes as you
121
+ want and Soxophone will mix
122
+ them all together.
123
+
124
+ Notes look like this:
125
+
126
+ a (quarter note at middle A)
127
+ a5 (quarter note one octave higher than middle A)
128
+ 2:c (half note at middle C)
129
+ 1e3 (whole note at E but one octave lower)
130
+ c# (quarter note at C sharp)
131
+ eb (quarter note at E flat)
132
+ eb3 (quarter note at E flat but one octave lower)
133
+
134
+ So, a note is written like this:
135
+
136
+ - The duration (as the lower half
137
+ of a fraction of a bar)
138
+ - An optional ":"
139
+ - The note name
140
+ - An optional sharp or flat
141
+ - The octave
142
+
143
+ You can write a plus or minus to
144
+ move notes up or down:
145
+
146
+ - a + a + a (A3, A4, and A5)
147
+
148
+ You can also write chords. Just
149
+ surround notes with parentheses:
150
+
151
+ a c e (a c e)
152
+
153
+ 4. Then, run your composition through
154
+ Ruby:
155
+
156
+ ruby boop.rb
157
+
158
+ If you asked Soxophone to play your
159
+ composition, it'll play through your
160
+ speakers. If you asked to write
161
+ a file, the file will appear.
162
+
163
+ Make sure you try the examples!
164
+
165
+
166
+ ------
167
+
data/Rakefile ADDED
@@ -0,0 +1,3 @@
1
+ task :default do
2
+ `ragel -R lib/soxophone/notation.rl -o lib/soxophone/notation.rb`
3
+ end
data/examples/boop.rb ADDED
@@ -0,0 +1,11 @@
1
+ $LOAD_PATH << File.dirname(__FILE__) + "/../lib"
2
+ require "soxophone"
3
+
4
+ Soxophone.play do |x|
5
+
6
+ boop = x.sound :sine
7
+ boop.release = 0.75
8
+
9
+ x.tune boop, "a b c"
10
+
11
+ end
@@ -0,0 +1,7 @@
1
+ $LOAD_PATH << File.dirname(__FILE__) + "/../lib"
2
+ require "soxophone"
3
+
4
+ Soxophone.play do |x|
5
+ s = x.sound :sine
6
+ x.tune s, "(1:a c e) (1:c e g) - (1:e g b)"
7
+ end
Binary file
@@ -0,0 +1,25 @@
1
+ $LOAD_PATH << File.dirname(__FILE__) + "/../lib"
2
+ require "soxophone"
3
+
4
+ Soxophone.play do |x|
5
+ s = x.sound(:square)
6
+ s.release = 0.5
7
+
8
+ x.tune s, "a b c"
9
+ end
10
+
11
+ Soxophone.play do |x|
12
+ s = x.sound(:square)
13
+ s.release = 0.5
14
+ s.tremolo = 10
15
+
16
+ x.tune s, "a b c"
17
+ end
18
+
19
+ Soxophone.play do |x|
20
+ s = x.sound(:square)
21
+ s.release = 0.5
22
+ s.phaser = true
23
+
24
+ x.tune s, "a b c"
25
+ end
@@ -0,0 +1,26 @@
1
+ $LOAD_PATH << File.dirname(__FILE__) + "/../lib"
2
+ require "soxophone"
3
+
4
+ Soxophone.play do |x|
5
+ s = x.sound :sine
6
+ s.attack = 0.5
7
+ s.release = 0.5
8
+
9
+ x.tune s, "1a 1a 1a 8"
10
+ end
11
+
12
+ Soxophone.play do |x|
13
+ s = x.sound :sine
14
+ s.attack = 0
15
+ s.release = 0.25
16
+
17
+ x.tune s, "1a 1a 1a 8"
18
+ end
19
+
20
+ Soxophone.play do |x|
21
+ s = x.sound :sine
22
+ s.attack = 1
23
+ s.release = 0
24
+
25
+ x.tune s, "1a 1a 1a 8"
26
+ end
@@ -0,0 +1,23 @@
1
+ $LOAD_PATH << File.dirname(__FILE__) + "/../lib"
2
+ require "soxophone"
3
+
4
+ def inspect_notation(n)
5
+ puts "#{n} =>"
6
+ Soxophone::Notation.instructions(n).each do |ins|
7
+ puts ins.inspect
8
+ end
9
+ puts
10
+ end
11
+
12
+ inspect_notation('a b c')
13
+ inspect_notation('a 8b 1:c')
14
+ inspect_notation('a bb c#')
15
+ inspect_notation('a + b - c')
16
+ inspect_notation('a5 b2 c3')
17
+
18
+ inspect_notation('(a b)')
19
+ inspect_notation('(a b) c')
20
+ inspect_notation('(a b) (c d)')
21
+ inspect_notation('g3 (a b) c (d e) f')
22
+
23
+ inspect_notation('a b !!!')
@@ -0,0 +1,23 @@
1
+ $LOAD_PATH << File.dirname(__FILE__) + "/../lib"
2
+ require "soxophone"
3
+
4
+ Soxophone.play do |x|
5
+ c = x.sample("crash.mp3")
6
+ c.gain = -9
7
+
8
+ x.tune c, "1a 4a"
9
+ end
10
+
11
+ Soxophone.play do |x|
12
+ t = x.sample("triangle.mp3")
13
+ t.gain = 5
14
+
15
+ x.tune t, "1a 16a 16a 16a 16a"
16
+ end
17
+
18
+ Soxophone.play do |x|
19
+ t = x.sample("triangle.mp3")
20
+ t.gain = 5
21
+
22
+ x.tune t, "a b c d e f g + a - 4 (a c e)"
23
+ end
Binary file
data/lib/soxophone.rb ADDED
@@ -0,0 +1,22 @@
1
+ module Soxophone
2
+
3
+ def self.play
4
+ s = Base.new
5
+ yield s
6
+ system "play #{"-m" if s.tunes.size > 1} #{s.paths.map { |p| "-t sox #{p} " }.join(" ")}"
7
+ end
8
+
9
+ def self.write(path)
10
+ s = Base.new
11
+ yield s
12
+ system "sox #{"-m" if s.tunes.size > 1} #{s.paths.map { |p| "-t sox #{p} " }.join(" ")} #{path}"
13
+ end
14
+
15
+ end
16
+
17
+ require "soxophone/notation"
18
+ require "soxophone/effects"
19
+ require "soxophone/sound"
20
+ require "soxophone/sample"
21
+ require "soxophone/tune"
22
+ require "soxophone/base"
@@ -0,0 +1,39 @@
1
+ require "tempfile"
2
+
3
+ module Soxophone
4
+
5
+ class Base
6
+
7
+ def initialize
8
+ @tunes = []
9
+ end
10
+
11
+ attr_accessor :tunes
12
+
13
+ def sound(kind)
14
+ Sound.new(kind)
15
+ end
16
+
17
+ def sample(path)
18
+ Sample.new(path)
19
+ end
20
+
21
+ def tune(sound, notation)
22
+ tune = Tune.new(sound)
23
+ tune.instructions = Notation.instructions(notation)
24
+ @tunes << tune
25
+ end
26
+
27
+ def paths
28
+ @tunes.map do |tune|
29
+ tmp = Tempfile.new("soxo")
30
+ system tune.cmd(tmp.path)
31
+ tmp.close
32
+
33
+ tmp.path
34
+ end
35
+ end
36
+
37
+ end
38
+
39
+ end
@@ -0,0 +1,41 @@
1
+ module Soxophone
2
+
3
+ module Effects
4
+
5
+ attr_reader :tremolo
6
+ attr_reader :phaser
7
+ attr_reader :gain
8
+
9
+ def tremolo=(tremolo)
10
+ unless tremolo.is_a?(Numeric)
11
+ raise "Tremolo must be a number"
12
+ end
13
+ @tremolo = tremolo
14
+ end
15
+
16
+ def phaser=(phaser)
17
+ unless [true, false].include?(phaser)
18
+ raise "Phaser must be true or false"
19
+ end
20
+ @phaser = phaser
21
+ end
22
+
23
+ def gain=(gain)
24
+ unless gain.is_a?(Numeric)
25
+ raise "Gain must be a number"
26
+ end
27
+ @gain = gain
28
+ end
29
+
30
+ def effectopts
31
+ fx = []
32
+ fx << "tremolo #{tremolo}" if tremolo
33
+ fx << "phaser 0.6 0.66 3 0.6 2 -t" if phaser
34
+ fx << "gain #{gain}" if gain
35
+
36
+ fx.compact.join(" ")
37
+ end
38
+
39
+ end
40
+
41
+ end
@@ -0,0 +1,504 @@
1
+ # line 1 "lib/soxophone/notation.rl"
2
+ # :$: BLOOPSAPHONE :$:
3
+ # Copyright (c) 2009 why the lucky stiff
4
+ # Based on sfxr (c) 2007 Tomas Pettersson
5
+ # (Also released under the MIT license)
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person
8
+ # obtaining a copy of this software and associated documentation
9
+ # files (the "Software"), to deal in the Software without restriction,
10
+ # including without limitation the rights to use, copy, modify, merge,
11
+ # publish, distribute, sublicense, and/or sell copies of the Software,
12
+ # and to permit persons to whom the Software is furnished to do so,
13
+ # subject to the following conditions:
14
+ #
15
+ # The above copyright notice and this permission notice shall be
16
+ # included in all copies or substantial portions of the Software.
17
+ #
18
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
19
+ # ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
20
+ # TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
21
+ # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
22
+ # SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
23
+ # OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
24
+ # OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ # SOFTWARE.
27
+
28
+ module Soxophone
29
+
30
+ class Rest
31
+ attr_accessor :duration
32
+ end
33
+
34
+ class Note
35
+ attr_accessor :tone, :duration, :octave
36
+ end
37
+
38
+ class Chord
39
+ def initialize
40
+ @notes = []
41
+ end
42
+ def duration
43
+ notes.map { |n| n.duration }.min
44
+ end
45
+ attr_accessor :notes
46
+ end
47
+
48
+ module Notation
49
+
50
+ NUMBERS = {
51
+ 'A' => 0,
52
+ 'B' => 2,
53
+ 'C' => 3,
54
+ 'D' => 5,
55
+ 'E' => 7,
56
+ 'F' => 8,
57
+ 'G' => 10
58
+ }
59
+
60
+
61
+ # line 62 "lib/soxophone/notation.rb"
62
+ class << self
63
+ attr_accessor :_bloopnotes_actions
64
+ private :_bloopnotes_actions, :_bloopnotes_actions=
65
+ end
66
+ self._bloopnotes_actions = [
67
+ 0, 1, 0, 1, 1, 1, 2, 1,
68
+ 3, 1, 5, 1, 6, 1, 7, 1,
69
+ 8, 1, 9, 1, 10, 2, 0, 11,
70
+ 2, 0, 13, 2, 0, 14, 2, 1,
71
+ 13, 2, 2, 14, 2, 3, 12, 2,
72
+ 4, 12, 2, 5, 12
73
+ ]
74
+
75
+ class << self
76
+ attr_accessor :_bloopnotes_key_offsets
77
+ private :_bloopnotes_key_offsets, :_bloopnotes_key_offsets=
78
+ end
79
+ self._bloopnotes_key_offsets = [
80
+ 0, 0, 13, 15, 18, 19, 19, 21,
81
+ 24, 25, 25, 32, 37, 41, 45, 47
82
+ ]
83
+
84
+ class << self
85
+ attr_accessor :_bloopnotes_trans_keys
86
+ private :_bloopnotes_trans_keys, :_bloopnotes_trans_keys=
87
+ end
88
+ self._bloopnotes_trans_keys = [
89
+ 32, 40, 41, 43, 45, 9, 13, 49,
90
+ 57, 65, 71, 97, 103, 49, 57, 58,
91
+ 48, 57, 58, 49, 57, 58, 48, 57,
92
+ 58, 58, 48, 57, 65, 71, 97, 103,
93
+ 58, 65, 71, 97, 103, 65, 71, 97,
94
+ 103, 35, 98, 49, 56, 49, 56, 0
95
+ ]
96
+
97
+ class << self
98
+ attr_accessor :_bloopnotes_single_lengths
99
+ private :_bloopnotes_single_lengths, :_bloopnotes_single_lengths=
100
+ end
101
+ self._bloopnotes_single_lengths = [
102
+ 0, 5, 0, 1, 1, 0, 0, 1,
103
+ 1, 0, 1, 1, 0, 2, 0, 0
104
+ ]
105
+
106
+ class << self
107
+ attr_accessor :_bloopnotes_range_lengths
108
+ private :_bloopnotes_range_lengths, :_bloopnotes_range_lengths=
109
+ end
110
+ self._bloopnotes_range_lengths = [
111
+ 0, 4, 1, 1, 0, 0, 1, 1,
112
+ 0, 0, 3, 2, 2, 1, 1, 0
113
+ ]
114
+
115
+ class << self
116
+ attr_accessor :_bloopnotes_index_offsets
117
+ private :_bloopnotes_index_offsets, :_bloopnotes_index_offsets=
118
+ end
119
+ self._bloopnotes_index_offsets = [
120
+ 0, 0, 10, 12, 15, 17, 18, 20,
121
+ 23, 25, 26, 31, 35, 38, 42, 44
122
+ ]
123
+
124
+ class << self
125
+ attr_accessor :_bloopnotes_trans_targs
126
+ private :_bloopnotes_trans_targs, :_bloopnotes_trans_targs=
127
+ end
128
+ self._bloopnotes_trans_targs = [
129
+ 1, 1, 1, 2, 6, 1, 10, 13,
130
+ 13, 0, 3, 1, 5, 4, 1, 5,
131
+ 1, 1, 7, 1, 9, 8, 1, 9,
132
+ 1, 1, 12, 11, 13, 13, 1, 12,
133
+ 13, 13, 1, 13, 13, 1, 14, 14,
134
+ 15, 1, 15, 1, 1, 1, 1, 1,
135
+ 1, 1, 1, 1, 1, 1, 1, 1,
136
+ 1, 1, 1, 0
137
+ ]
138
+
139
+ class << self
140
+ attr_accessor :_bloopnotes_trans_actions
141
+ private :_bloopnotes_trans_actions, :_bloopnotes_trans_actions=
142
+ end
143
+ self._bloopnotes_trans_actions = [
144
+ 19, 15, 17, 0, 0, 19, 0, 0,
145
+ 0, 0, 3, 30, 0, 0, 24, 0,
146
+ 24, 24, 5, 33, 0, 0, 27, 0,
147
+ 27, 27, 0, 0, 1, 1, 21, 0,
148
+ 1, 1, 21, 1, 1, 21, 9, 9,
149
+ 9, 42, 7, 36, 39, 30, 24, 24,
150
+ 24, 33, 27, 27, 27, 21, 21, 21,
151
+ 42, 36, 39, 0
152
+ ]
153
+
154
+ class << self
155
+ attr_accessor :_bloopnotes_to_state_actions
156
+ private :_bloopnotes_to_state_actions, :_bloopnotes_to_state_actions=
157
+ end
158
+ self._bloopnotes_to_state_actions = [
159
+ 0, 11, 0, 0, 0, 0, 0, 0,
160
+ 0, 0, 0, 0, 0, 0, 0, 0
161
+ ]
162
+
163
+ class << self
164
+ attr_accessor :_bloopnotes_from_state_actions
165
+ private :_bloopnotes_from_state_actions, :_bloopnotes_from_state_actions=
166
+ end
167
+ self._bloopnotes_from_state_actions = [
168
+ 0, 13, 0, 0, 0, 0, 0, 0,
169
+ 0, 0, 0, 0, 0, 0, 0, 0
170
+ ]
171
+
172
+ class << self
173
+ attr_accessor :_bloopnotes_eof_trans
174
+ private :_bloopnotes_eof_trans, :_bloopnotes_eof_trans=
175
+ end
176
+ self._bloopnotes_eof_trans = [
177
+ 0, 0, 46, 49, 49, 49, 50, 53,
178
+ 53, 53, 56, 56, 56, 57, 58, 59
179
+ ]
180
+
181
+ class << self
182
+ attr_accessor :bloopnotes_start
183
+ end
184
+ self.bloopnotes_start = 1;
185
+ class << self
186
+ attr_accessor :bloopnotes_error
187
+ end
188
+ self.bloopnotes_error = 0;
189
+
190
+ class << self
191
+ attr_accessor :bloopnotes_en_main
192
+ end
193
+ self.bloopnotes_en_main = 1;
194
+
195
+ # line 130 "lib/soxophone/notation.rl"
196
+
197
+
198
+ def self.instructions(data)
199
+ oct = 4
200
+ len = 4
201
+ tone = nil
202
+ mod = nil
203
+ instructions = []
204
+
205
+ data += " "
206
+ eof = -1
207
+
208
+
209
+ # line 210 "lib/soxophone/notation.rb"
210
+ begin
211
+ p ||= 0
212
+ pe ||= data.length
213
+ cs = bloopnotes_start
214
+ ts = nil
215
+ te = nil
216
+ act = 0
217
+ end
218
+
219
+ # line 220 "lib/soxophone/notation.rb"
220
+ begin
221
+ _klen, _trans, _keys, _acts, _nacts = nil
222
+ _goto_level = 0
223
+ _resume = 10
224
+ _eof_trans = 15
225
+ _again = 20
226
+ _test_eof = 30
227
+ _out = 40
228
+ while true
229
+ _trigger_goto = false
230
+ if _goto_level <= 0
231
+ if p == pe
232
+ _goto_level = _test_eof
233
+ next
234
+ end
235
+ if cs == 0
236
+ _goto_level = _out
237
+ next
238
+ end
239
+ end
240
+ if _goto_level <= _resume
241
+ _acts = _bloopnotes_from_state_actions[cs]
242
+ _nacts = _bloopnotes_actions[_acts]
243
+ _acts += 1
244
+ while _nacts > 0
245
+ _nacts -= 1
246
+ _acts += 1
247
+ case _bloopnotes_actions[_acts - 1]
248
+ when 7 then
249
+ # line 1 "lib/soxophone/notation.rl"
250
+ begin
251
+ ts = p
252
+ end
253
+ # line 1 "lib/soxophone/notation.rl"
254
+ # line 255 "lib/soxophone/notation.rb"
255
+ end # from state action switch
256
+ end
257
+ if _trigger_goto
258
+ next
259
+ end
260
+ _keys = _bloopnotes_key_offsets[cs]
261
+ _trans = _bloopnotes_index_offsets[cs]
262
+ _klen = _bloopnotes_single_lengths[cs]
263
+ _break_match = false
264
+
265
+ begin
266
+ if _klen > 0
267
+ _lower = _keys
268
+ _upper = _keys + _klen - 1
269
+
270
+ loop do
271
+ break if _upper < _lower
272
+ _mid = _lower + ( (_upper - _lower) >> 1 )
273
+
274
+ if data[p] < _bloopnotes_trans_keys[_mid]
275
+ _upper = _mid - 1
276
+ elsif data[p] > _bloopnotes_trans_keys[_mid]
277
+ _lower = _mid + 1
278
+ else
279
+ _trans += (_mid - _keys)
280
+ _break_match = true
281
+ break
282
+ end
283
+ end # loop
284
+ break if _break_match
285
+ _keys += _klen
286
+ _trans += _klen
287
+ end
288
+ _klen = _bloopnotes_range_lengths[cs]
289
+ if _klen > 0
290
+ _lower = _keys
291
+ _upper = _keys + (_klen << 1) - 2
292
+ loop do
293
+ break if _upper < _lower
294
+ _mid = _lower + (((_upper-_lower) >> 1) & ~1)
295
+ if data[p] < _bloopnotes_trans_keys[_mid]
296
+ _upper = _mid - 2
297
+ elsif data[p] > _bloopnotes_trans_keys[_mid+1]
298
+ _lower = _mid + 2
299
+ else
300
+ _trans += ((_mid - _keys) >> 1)
301
+ _break_match = true
302
+ break
303
+ end
304
+ end # loop
305
+ break if _break_match
306
+ _trans += _klen
307
+ end
308
+ end while false
309
+ end
310
+ if _goto_level <= _eof_trans
311
+ cs = _bloopnotes_trans_targs[_trans]
312
+ if _bloopnotes_trans_actions[_trans] != 0
313
+ _acts = _bloopnotes_trans_actions[_trans]
314
+ _nacts = _bloopnotes_actions[_acts]
315
+ _acts += 1
316
+ while _nacts > 0
317
+ _nacts -= 1
318
+ _acts += 1
319
+ case _bloopnotes_actions[_acts - 1]
320
+ when 0 then
321
+ # line 62 "lib/soxophone/notation.rl"
322
+ begin
323
+ len = data[ts..p].to_i end
324
+ # line 62 "lib/soxophone/notation.rl"
325
+ when 1 then
326
+ # line 63 "lib/soxophone/notation.rl"
327
+ begin
328
+ len = 1; end
329
+ # line 63 "lib/soxophone/notation.rl"
330
+ when 2 then
331
+ # line 64 "lib/soxophone/notation.rl"
332
+ begin
333
+ len = 1; end
334
+ # line 64 "lib/soxophone/notation.rl"
335
+ when 3 then
336
+ # line 65 "lib/soxophone/notation.rl"
337
+ begin
338
+ mod = data[p-1..p-1] end
339
+ # line 65 "lib/soxophone/notation.rl"
340
+ when 4 then
341
+ # line 66 "lib/soxophone/notation.rl"
342
+ begin
343
+ oct = data[p-1..p-1].to_i end
344
+ # line 66 "lib/soxophone/notation.rl"
345
+ when 5 then
346
+ # line 67 "lib/soxophone/notation.rl"
347
+ begin
348
+ tone = data[p-1..p-1] end
349
+ # line 67 "lib/soxophone/notation.rl"
350
+ when 8 then
351
+ # line 72 "lib/soxophone/notation.rl"
352
+ begin
353
+ te = p+1
354
+ begin
355
+ chord = Chord.new
356
+ end
357
+ end
358
+ # line 72 "lib/soxophone/notation.rl"
359
+ when 9 then
360
+ # line 75 "lib/soxophone/notation.rl"
361
+ begin
362
+ te = p+1
363
+ begin
364
+ if chord
365
+ instructions << chord
366
+ chord = nil
367
+ end
368
+ end
369
+ end
370
+ # line 75 "lib/soxophone/notation.rl"
371
+ when 10 then
372
+ # line 125 "lib/soxophone/notation.rl"
373
+ begin
374
+ te = p+1
375
+ end
376
+ # line 125 "lib/soxophone/notation.rl"
377
+ when 11 then
378
+ # line 81 "lib/soxophone/notation.rl"
379
+ begin
380
+ te = p
381
+ p = p - 1; begin
382
+ unless chord
383
+ rest = Rest.new
384
+ rest.duration = len
385
+
386
+ instructions << rest
387
+ end
388
+
389
+ mod = nil;
390
+ tone = nil;
391
+ len = 4;
392
+ end
393
+ end
394
+ # line 81 "lib/soxophone/notation.rl"
395
+ when 12 then
396
+ # line 93 "lib/soxophone/notation.rl"
397
+ begin
398
+ te = p
399
+ p = p - 1; begin
400
+ note = Note.new
401
+
402
+ num = NUMBERS[tone.upcase]
403
+ if mod == 'b'
404
+ num -= 1
405
+ elsif mod == '#'
406
+ num += 1
407
+ end
408
+
409
+ note.tone = num
410
+ note.duration = len
411
+ note.octave = oct
412
+
413
+ if chord
414
+ chord.notes << note
415
+ else
416
+ instructions << note
417
+ end
418
+
419
+ mod = nil
420
+ tone = nil
421
+ len = 4
422
+ end
423
+ end
424
+ # line 93 "lib/soxophone/notation.rl"
425
+ when 13 then
426
+ # line 117 "lib/soxophone/notation.rl"
427
+ begin
428
+ te = p
429
+ p = p - 1; begin
430
+ oct += 1
431
+ len = 4
432
+ end
433
+ end
434
+ # line 117 "lib/soxophone/notation.rl"
435
+ when 14 then
436
+ # line 121 "lib/soxophone/notation.rl"
437
+ begin
438
+ te = p
439
+ p = p - 1; begin
440
+ oct -= 1
441
+ len = 4
442
+ end
443
+ end
444
+ # line 121 "lib/soxophone/notation.rl"
445
+ # line 446 "lib/soxophone/notation.rb"
446
+ end # action switch
447
+ end
448
+ end
449
+ if _trigger_goto
450
+ next
451
+ end
452
+ end
453
+ if _goto_level <= _again
454
+ _acts = _bloopnotes_to_state_actions[cs]
455
+ _nacts = _bloopnotes_actions[_acts]
456
+ _acts += 1
457
+ while _nacts > 0
458
+ _nacts -= 1
459
+ _acts += 1
460
+ case _bloopnotes_actions[_acts - 1]
461
+ when 6 then
462
+ # line 1 "lib/soxophone/notation.rl"
463
+ begin
464
+ ts = nil; end
465
+ # line 1 "lib/soxophone/notation.rl"
466
+ # line 467 "lib/soxophone/notation.rb"
467
+ end # to state action switch
468
+ end
469
+ if _trigger_goto
470
+ next
471
+ end
472
+ if cs == 0
473
+ _goto_level = _out
474
+ next
475
+ end
476
+ p += 1
477
+ if p != pe
478
+ _goto_level = _resume
479
+ next
480
+ end
481
+ end
482
+ if _goto_level <= _test_eof
483
+ if p == eof
484
+ if _bloopnotes_eof_trans[cs] > 0
485
+ _trans = _bloopnotes_eof_trans[cs] - 1;
486
+ _goto_level = _eof_trans
487
+ next;
488
+ end
489
+ end
490
+ end
491
+ if _goto_level <= _out
492
+ break
493
+ end
494
+ end
495
+ end
496
+ # line 145 "lib/soxophone/notation.rl"
497
+
498
+
499
+ instructions
500
+ end
501
+
502
+ end
503
+
504
+ end
@@ -0,0 +1,152 @@
1
+ # :$: BLOOPSAPHONE :$:
2
+ # Copyright (c) 2009 why the lucky stiff
3
+ # Based on sfxr (c) 2007 Tomas Pettersson
4
+ # (Also released under the MIT license)
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person
7
+ # obtaining a copy of this software and associated documentation
8
+ # files (the "Software"), to deal in the Software without restriction,
9
+ # including without limitation the rights to use, copy, modify, merge,
10
+ # publish, distribute, sublicense, and/or sell copies of the Software,
11
+ # and to permit persons to whom the Software is furnished to do so,
12
+ # subject to the following 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
18
+ # ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
19
+ # TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
20
+ # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
21
+ # SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
22
+ # OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
23
+ # OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25
+ # SOFTWARE.
26
+
27
+ module Soxophone
28
+
29
+ class Rest
30
+ attr_accessor :duration
31
+ end
32
+
33
+ class Note
34
+ attr_accessor :tone, :duration, :octave
35
+ end
36
+
37
+ class Chord
38
+ def initialize
39
+ @notes = []
40
+ end
41
+ def duration
42
+ notes.map { |n| n.duration }.min
43
+ end
44
+ attr_accessor :notes
45
+ end
46
+
47
+ module Notation
48
+
49
+ NUMBERS = {
50
+ 'A' => 0,
51
+ 'B' => 2,
52
+ 'C' => 3,
53
+ 'D' => 5,
54
+ 'E' => 7,
55
+ 'F' => 8,
56
+ 'G' => 10
57
+ }
58
+
59
+ %%{
60
+ machine bloopnotes;
61
+
62
+ len = [1-9] [0-9]? ":"? %{ len = data[ts..p].to_i };
63
+ up = "+" %{ len = 1; } len?;
64
+ down = "-" %{ len = 1; } len?;
65
+ mod = [b#] %{ mod = data[p-1..p-1] };
66
+ oct = [1-8] %{ oct = data[p-1..p-1].to_i };
67
+ note = len? [a-gA-G] %{ tone = data[p-1..p-1] } mod? oct?;
68
+ startchord = "(";
69
+ endchord = ")";
70
+
71
+ main := |*
72
+ startchord => {
73
+ chord = Chord.new
74
+ };
75
+ endchord => {
76
+ if chord
77
+ instructions << chord
78
+ chord = nil
79
+ end
80
+ };
81
+ len => {
82
+ unless chord
83
+ rest = Rest.new
84
+ rest.duration = len
85
+
86
+ instructions << rest
87
+ end
88
+
89
+ mod = nil;
90
+ tone = nil;
91
+ len = 4;
92
+ };
93
+ note => {
94
+ note = Note.new
95
+
96
+ num = NUMBERS[tone.upcase]
97
+ if mod == 'b'
98
+ num -= 1
99
+ elsif mod == '#'
100
+ num += 1
101
+ end
102
+
103
+ note.tone = num
104
+ note.duration = len
105
+ note.octave = oct
106
+
107
+ if chord
108
+ chord.notes << note
109
+ else
110
+ instructions << note
111
+ end
112
+
113
+ mod = nil
114
+ tone = nil
115
+ len = 4
116
+ };
117
+ up => {
118
+ oct += 1
119
+ len = 4
120
+ };
121
+ down => {
122
+ oct -= 1
123
+ len = 4
124
+ };
125
+ space;
126
+ *|;
127
+
128
+ write data nofinal;
129
+
130
+ }%%
131
+
132
+ def self.instructions(data)
133
+ oct = 4
134
+ len = 4
135
+ tone = nil
136
+ mod = nil
137
+ instructions = []
138
+
139
+ data += " "
140
+ eof = -1
141
+
142
+ %%{
143
+ write init;
144
+ write exec;
145
+ }%%
146
+
147
+ instructions
148
+ end
149
+
150
+ end
151
+
152
+ end
@@ -0,0 +1,47 @@
1
+ module Soxophone
2
+
3
+ class Sample
4
+
5
+ include Effects
6
+
7
+ def initialize(path)
8
+ self.path = path
9
+ end
10
+
11
+ attr_reader :path
12
+
13
+ def path=(path)
14
+ unless File.file?(path)
15
+ raise "Sample path must be a file"
16
+ end
17
+ @path = path
18
+ end
19
+
20
+ def notecmd(freq, duration)
21
+ if duration > length
22
+ "sox #{path} -r 48k -c1 -p pad 0 #{duration - length} pitch #{freq*100}"
23
+ else
24
+ "sox #{path} -r 48k -c1 -p trim 0 #{duration} pitch #{freq*100}"
25
+ end
26
+ end
27
+
28
+ def chordcmd(freqs, duration)
29
+ cmds = freqs.map { |f| notecmd(f, duration) }
30
+ "sox -m #{cmds.map { |c| "\"|#{c}\"" }.join(" ")} -p"
31
+ end
32
+
33
+ private
34
+
35
+ def length
36
+ if instance_variable_defined?(:@length)
37
+ return @length
38
+ end
39
+
40
+ rate = `soxi -r #{path}`.strip.to_i
41
+ samples = `soxi -s #{path}`.strip.to_i
42
+ @length = samples.quo(rate)
43
+ end
44
+
45
+ end
46
+
47
+ end
@@ -0,0 +1,65 @@
1
+ module Soxophone
2
+
3
+ class Sound
4
+
5
+ include Effects
6
+
7
+ KINDS = %w(
8
+ sine square triangle sawtooth trapezium
9
+ exp noise whitenoise pinknoise brownnoise
10
+ )
11
+
12
+ def initialize(kind)
13
+ self.kind = kind
14
+ self.attack = 0
15
+ self.release = 0
16
+ end
17
+
18
+ attr_reader :kind
19
+ attr_reader :attack
20
+ attr_reader :release
21
+
22
+ def kind=(kind)
23
+ unless KINDS.include?(kind.to_s)
24
+ raise "Kind must be one of #{KINDS.join(", ")}"
25
+ end
26
+ @kind = kind
27
+ end
28
+
29
+ def attack=(attack)
30
+ unless attack.is_a?(Numeric)
31
+ raise "Attack must be a number"
32
+ end
33
+ @attack = attack
34
+ end
35
+
36
+ def release=(release)
37
+ unless release.is_a?(Numeric)
38
+ raise "Release must be a number"
39
+ end
40
+ @release = release
41
+ end
42
+
43
+ def notecmd(freq, duration)
44
+ env = envelope(duration)
45
+ "sox -n -p synth #{duration} #{kind} %#{freq} #{env}"
46
+ end
47
+
48
+ def chordcmd(freqs, duration)
49
+ env = envelope(duration)
50
+ "sox -n -c1 -p synth #{duration} #{freqs.map { |f| "#{kind} %#{f}" }.join(" ")} #{env}"
51
+ end
52
+
53
+ private
54
+
55
+ def envelope(duration)
56
+ env = []
57
+ env << "fade #{attack * duration}" if attack > 0
58
+ env << "fade 0 #{duration} #{release * duration}" if release > 0
59
+
60
+ env.compact.join(" ")
61
+ end
62
+
63
+ end
64
+
65
+ end
@@ -0,0 +1,44 @@
1
+ module Soxophone
2
+
3
+ class Tune
4
+ def initialize(sound)
5
+ @instructions = []
6
+ @sound = sound
7
+ end
8
+
9
+ attr_accessor :instructions, :sound
10
+
11
+ def cmd(out_path)
12
+ "sox #{cmds.map { |c| "\"|#{c.gsub('"', '\"')}\"" }.join(" ")} -t sox #{out_path} #{sound.effectopts}"
13
+ end
14
+
15
+ private
16
+
17
+ def cmds
18
+ @instructions.map do |ins|
19
+ duration = 1.0 / ins.duration
20
+
21
+ case ins
22
+ when Chord
23
+ freqs = ins.notes.map { |n| n.tone + ((n.octave - 4) * 12) }
24
+
25
+ if freqs.size == 1
26
+ sound.notecmd(freqs.first, duration)
27
+ else
28
+ sound.chordcmd(freqs, duration)
29
+ end
30
+
31
+ when Note
32
+ freq = ins.tone + ((ins.octave - 4) * 12)
33
+ sound.notecmd(freq, duration)
34
+
35
+ when Rest
36
+ "sox -n -p trim 0 #{duration}"
37
+
38
+ end
39
+ end
40
+
41
+ end
42
+ end
43
+
44
+ end
data/soxo.rb ADDED
@@ -0,0 +1,40 @@
1
+ $LOAD_PATH << File.dirname(__FILE__) + "/lib"
2
+ require "soxophone"
3
+
4
+ Soxophone.write("soxo.mp3") do |x|
5
+
6
+ toot = x.sound :sine
7
+ toot.attack = 0
8
+ toot.release = 0.25
9
+ toot.tremolo = 100
10
+
11
+ squp = x.sound :square
12
+ squp.gain = -9
13
+ squp.attack = 0
14
+ squp.release = 0.5
15
+
16
+ pish = x.sound :noise
17
+ pish.attack = 0
18
+ pish.release = 0.9
19
+
20
+ x.tune toot, %q*
21
+ 8c 8e 8g + 2c 8 4a - (2d f) 4
22
+ 8c 8e 8g + 2b 8 4a - (2c e) 4
23
+ 8c 8e 8g + 2a 8-4g (2b d) 4
24
+ 1c
25
+ *
26
+
27
+ x.tune squp, %q*
28
+ 1c2 1g2
29
+ 1e2 1a3
30
+ 1g2 1d3
31
+ 1c2
32
+ *
33
+
34
+ x.tune pish, %q*
35
+ 1a 4a 4 4a 4
36
+ 1a 4a 4 4a 4
37
+ 1a 4a 4 4a 4
38
+ *
39
+
40
+ end
metadata ADDED
@@ -0,0 +1,73 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mdaines-soxophone
3
+ version: !ruby/object:Gem::Version
4
+ version: "0.2"
5
+ platform: ruby
6
+ authors:
7
+ - Michael Daines
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-02-19 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: a little synth and sequencer
17
+ email: michael@mdaines.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README
24
+ - COPYING
25
+ files:
26
+ - COPYING
27
+ - README
28
+ - examples/boop.rb
29
+ - examples/chords.rb
30
+ - examples/effects.rb
31
+ - examples/envelope.rb
32
+ - examples/notation.rb
33
+ - examples/samples.rb
34
+ - examples/crash.mp3
35
+ - examples/triangle.mp3
36
+ - lib/soxophone.rb
37
+ - lib/soxophone/base.rb
38
+ - lib/soxophone/effects.rb
39
+ - lib/soxophone/notation.rb
40
+ - lib/soxophone/notation.rl
41
+ - lib/soxophone/sample.rb
42
+ - lib/soxophone/sound.rb
43
+ - lib/soxophone/tune.rb
44
+ - Rakefile
45
+ - soxo.rb
46
+ has_rdoc: false
47
+ homepage: http://github.com/mdaines/soxophone
48
+ post_install_message:
49
+ rdoc_options: []
50
+
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: "0"
58
+ version:
59
+ required_rubygems_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: "0"
64
+ version:
65
+ requirements: []
66
+
67
+ rubyforge_project:
68
+ rubygems_version: 1.2.0
69
+ signing_key:
70
+ specification_version: 2
71
+ summary: a little synth and sequencer
72
+ test_files: []
73
+