viking-bloopsaphone 0.4

Sign up to get free protection for your applications and to get access to all the features.
data/COPYING ADDED
@@ -0,0 +1,27 @@
1
+
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
+
data/README ADDED
@@ -0,0 +1,172 @@
1
+
2
+ |~~ |~~
3
+ | |
4
+ :$: bloopsaphone :$:
5
+ `''''''''''''''''`
6
+
7
+ for writing chiptune-style songs
8
+ in c or ruby. you know: the sounds
9
+ of ataris, gameboys and the like.
10
+
11
+ ~
12
+
13
+ -$- ABOUT
14
+
15
+ this is a small c library for sending
16
+ chiptunes through portaudio, which is
17
+ a rather light cross-platform audio lib.
18
+ <http://www.portaudio.com/>
19
+
20
+ right now i'm only including ruby
21
+ bindings. you are welcome to contribute
22
+ code to hook up to other languages,
23
+ though.
24
+
25
+ i wrote this for h-ety h.
26
+ <http://hacketyhack.net/>
27
+
28
+ i don't have binaries ready for this yet,
29
+ so if you're on windows or os x, you may
30
+ have to wait until HH comes out at ART
31
+ AND CODE. the tribulations you face.
32
+
33
+ ~
34
+
35
+ -$- THE BLOOPSAPHONE THEME SONG
36
+ (in ruby)
37
+
38
+ require 'bloops'
39
+
40
+ # the bloops o' phone
41
+ b = Bloops.new
42
+ b.tempo = 320
43
+
44
+ # melodious
45
+ s1 = b.sound Bloops::SQUARE
46
+ s1.punch = 0.5
47
+ s1.sustain = 0.4
48
+ s1.decay = 0.2
49
+ s1.arp = 0.4
50
+ s1.aspeed = 0.6
51
+ s1.repeat = 0.6
52
+ s1.phase = 0.2
53
+ s1.psweep = 0.2
54
+
55
+ # beats
56
+ s2 = b.sound Bloops::NOISE
57
+ s2.punch = 0.5
58
+ s2.sustain = 0.2
59
+ s2.decay = 0.4
60
+ s2.slide = -0.4
61
+ s2.phase = 0.2
62
+ s2.psweep = 0.2
63
+
64
+ # the tracks
65
+ b.tune s1, "f#5 c6 e4 b6 g5 d6 4 f#5 e5 c5 b6 c6 d6 4 "
66
+ b.tune s2, "4 c6 4 b5 4 4 e4 4 c6 4 b5 4 4 e4"
67
+
68
+ # and away we go
69
+ b.play
70
+ sleep 1 while !b.stopped?
71
+
72
+ ~
73
+
74
+ -$- BUILDING FOR RUBY
75
+
76
+ If Ruby is in your path and PortAudio 1.9
77
+ or greater is installed:
78
+
79
+ make ruby
80
+
81
+ To install PortAudio 1.9 under Ubuntu:
82
+
83
+ aptitude install portaudio19-dev
84
+
85
+ To build from source isn't too bad.
86
+ Download PortAudio 1.9 and build it.
87
+ <http://www.portaudio.com/archives/pa_stable_v19_20071207.tar.gz>
88
+
89
+ Like this:
90
+
91
+ $ tar xzvf pa_stable_v19_20071207.tar.gz
92
+ $ cd portaudio
93
+ $ ./configure
94
+ $ make
95
+ $ sudo make install
96
+
97
+ Then go back to Bloopsaphone and do
98
+ a `make ruby`.
99
+
100
+ ~
101
+
102
+ -$- THE IDEALS,
103
+
104
+ -1- ASYNCHRONOUS.
105
+ You send it a song and it plays in
106
+ the background. You'll get an event
107
+ when it finishes.
108
+
109
+ -2- SMALL.
110
+ This is just a toy, I don't want it
111
+ to be very big and comprehensive.
112
+ Just to play little tunes with a
113
+ nostalgic arcade sound rather than
114
+ the CASIO-stylings of most MIDI.
115
+
116
+ -3- CUSTOM NOTATION.
117
+ Someone told me about Nokring, iMelody,
118
+ numbered musical notation and I did
119
+ some reading. They're little languages
120
+ for texting yourself a ringtone.
121
+
122
+ <http://en.wikipedia.org/wiki/Ring_Tone_Transfer_Language>
123
+ <http://homepage.mac.com/alvinmok/ericsson/emelody.html>
124
+
125
+ Bloopsaphone uses a variation on RTTTL.
126
+
127
+ Instead of commas, I use whitespace.
128
+ A rest is simply a number. A plus sign
129
+ moves everything up an octave. A minus
130
+ down an octave.
131
+
132
+ The Simpsons' Theme, for instance, would be:
133
+
134
+ 32 + C E F# 8:A G E C - 8:A 8:F# 8:F# 8:F# 2:G
135
+
136
+ Which translates into:
137
+
138
+ * a 1/32nd note rest.
139
+ * change one octave up.
140
+ * C quarter note.
141
+ * E quarter note.
142
+ * F# quarter note.
143
+ * A eighth note.
144
+ * G quarter.
145
+ * E quarter.
146
+ * C one-quarter note.
147
+ * change one octave down.
148
+ * A eighth.
149
+ * Three F# eighths.
150
+ * G half note.
151
+
152
+ The colons are optional. They are there because you
153
+ can place an octave number after each note. Somehow
154
+ "8B6" (an eighth note of B at the sixth octave) looks
155
+ more confusing than "8:B6". I guess I figured that
156
+ the timing "8" is conceptually separate from the
157
+ actual tone "B6", even though they both comprise
158
+ the note itself.
159
+
160
+ -4- SERIALIZE SOUNDS.
161
+ To accomodate passing instruments between
162
+ ruby and c, bloopsaphone comes with a tiny
163
+ file format for describing sounds.
164
+
165
+ You can find examples of these in the sounds/
166
+ folder in this distro. Possible sound types
167
+ are 'square', 'sawtooth', 'sine' and 'noise'.
168
+ All other settings go from 0.0 to 1.0.
169
+
170
+ The 'freq' setting is only used if the sound
171
+ is played without a tune.
172
+
@@ -0,0 +1,596 @@
1
+ //
2
+ // bloopsaphone.c
3
+ // the chiptune maker for portaudio
4
+ // (with bindings for ruby)
5
+ //
6
+ // (c) 2009 why the lucky stiff
7
+ // See COPYING for the license
8
+ //
9
+ #include <stdio.h>
10
+ #include <stdlib.h>
11
+ #include <stddef.h>
12
+ #include <string.h>
13
+ #include <time.h>
14
+ #include <math.h>
15
+ #include <portaudio.h>
16
+ #include <unistd.h>
17
+ #include "bloopsaphone.h"
18
+ #include "threads.h"
19
+
20
+ #ifdef PaStream
21
+ #error ** Looks like you're linking against PortAudio 1.8!
22
+ #error ** Bloopsaphone needs PortAudio 1.9 or greater.
23
+ #error ** On Ubuntu, try: aptitude install portaudio19-dev.
24
+ #endif
25
+
26
+ #define SAMPLE_RATE 44100
27
+ #define rnd(n) (rand() % (n + 1))
28
+ #define tempo2frames(tempo) ((float)SAMPLE_RATE / (tempo / 60.0f))
29
+ #define PI 3.14159265f
30
+
31
+ #define FX(F, V) ({ \
32
+ if (F->mod == '+') V += F->val; \
33
+ else if (F->mod == '-') V -= F->val; \
34
+ else V = F->val; \
35
+ if (V > 1.0f) \
36
+ V = 1.0f; \
37
+ else if (V < 0.0f) \
38
+ V = 0.0f; \
39
+ })
40
+
41
+ static bloopsalock LOCK;
42
+ static bloopsmix *MIXER = NULL;
43
+
44
+ static void bloops_synth(int, float *);
45
+ static int bloops_port_callback(const void *, void *,
46
+ unsigned long, const PaStreamCallbackTimeInfo *,
47
+ PaStreamCallbackFlags, void *);
48
+ static void bloops_set_track_at(bloops *B, bloopsatrack *track, int num);
49
+
50
+ float
51
+ frnd(float range)
52
+ {
53
+ return (float)rnd(10000) / 10000 * range;
54
+ }
55
+
56
+ static void
57
+ bloops_remove(bloops *B)
58
+ {
59
+ int i;
60
+ if (MIXER == NULL) return;
61
+ for (i = 0; i < BLOOPS_MAX_CHANNELS; i++) {
62
+ if (MIXER->B[i] == B) {
63
+ MIXER->B[i] = NULL;
64
+ bloops_destroy(B);
65
+ }
66
+ }
67
+ }
68
+
69
+ static void
70
+ bloops_reset_voice(bloopsavoice *A)
71
+ {
72
+ A->period = 100.0 / (A->params.freq * A->params.freq + 0.001);
73
+ A->maxperiod = 100.0 / (A->params.limit * A->params.limit + 0.001);
74
+ A->slide = 1.0 - pow((double)A->params.slide, 3.0) * 0.01;
75
+ A->dslide = -pow((double)A->params.dslide, 3.0) * 0.000001;
76
+ A->square = 0.5f - A->params.square * 0.5f;
77
+ A->sweep = -A->params.sweep * 0.00005f;
78
+ if (A->params.arp >= 0.0f)
79
+ A->arp = 1.0 - pow((double)A->params.arp, 2.0) * 0.9;
80
+ else
81
+ A->arp = 1.0 + pow((double)A->params.arp, 2.0) * 10.0;
82
+ A->atime = 0;
83
+ A->alimit = (int)(pow(1.0f - A->params.aspeed, 2.0f) * 20000 + 32);
84
+ if (A->params.aspeed == 1.0f)
85
+ A->alimit = 0;
86
+ }
87
+
88
+ static void
89
+ bloops_start_voice(bloopsavoice *A) {
90
+ int i = 0;
91
+ A->phase = 0;
92
+ A->filter[0] = 0.0f;
93
+ A->filter[1] = 0.0f;
94
+ A->filter[2] = pow(A->params.lpf, 3.0f) * 0.1f;
95
+ A->filter[3] = 1.0f + A->params.lsweep * 0.0001f;
96
+ A->filter[4] = 5.0f / (1.0f + pow(A->params.resonance, 2.0f) * 20.0f) * (0.01f + A->filter[2]);
97
+ if (A->filter[4] > 0.8f) A->filter[4] = 0.8f;
98
+ A->filter[5] = 0.0f;
99
+ A->filter[6] = pow(A->params.hpf, 2.0f) * 0.1f;
100
+ A->filter[7] = 1.0 + A->params.hsweep * 0.0003f;
101
+
102
+ A->vibe = 0.0f;
103
+ A->vspeed = pow(A->params.vspeed, 2.0f) * 0.01f;
104
+ A->vdelay = A->params.vibe * 0.5f;
105
+
106
+ A->volume = 0.0f;
107
+ A->stage = 0;
108
+ A->time = 0;
109
+ A->length[0] = (int)(A->params.attack * A->params.attack * 100000.0f);
110
+ A->length[1] = (int)(A->params.sustain * A->params.sustain * 100000.0f);
111
+ A->length[2] = (int)(A->params.decay * A->params.decay * 100000.0f);
112
+
113
+ A->fphase = pow(A->params.phase, 2.0f) * 1020.0f;
114
+ if (A->params.phase < 0.0f) A->fphase = -A->fphase;
115
+ A->dphase = pow(A->params.psweep, 2.0f) * 1.0f;
116
+ if (A->params.psweep < 0.0f) A->dphase = -A->dphase;
117
+ A->iphase = abs((int)A->fphase);
118
+ A->phasex = 0;
119
+
120
+ memset(A->phaser, 0, 1024 * sizeof(float));
121
+ for (i = 0; i < 32; i++)
122
+ A->noise[i] = frnd(2.0f) - 1.0f;
123
+
124
+ A->repeat = 0;
125
+ A->limit = (int)(pow(1.0f - A->params.repeat, 2.0f) * 20000 + 32);
126
+ if (A->params.repeat == 0.0f)
127
+ A->limit = 0;
128
+ A->state = BLOOPS_PLAY;
129
+ }
130
+
131
+ void
132
+ bloops_clear(bloops *B)
133
+ {
134
+ int i;
135
+ for (i = 0; i < BLOOPS_MAX_TRACKS; i++) {
136
+ bloops_set_track_at(B, NULL, i);
137
+ }
138
+ }
139
+
140
+ void
141
+ bloops_tempo(bloops *B, int tempo)
142
+ {
143
+ B->tempo = tempo;
144
+ }
145
+
146
+ void
147
+ bloops_set_track_at(bloops *B, bloopsatrack *track, int num)
148
+ {
149
+ bloopsavoice *voice;
150
+ bloopsatrack *old_track;
151
+ voice = &B->voices[num];
152
+ old_track = voice->track;
153
+ voice->track = track;
154
+ if (track != NULL) {
155
+ bloops_track_ref(track);
156
+ }
157
+ if (old_track != NULL) {
158
+ bloops_track_destroy(old_track);
159
+ }
160
+ voice->state = BLOOPS_STOP;
161
+ if (track != NULL) {
162
+ memcpy(&voice->params, &track->params, sizeof(bloopsaparams));
163
+ }
164
+ voice->frames = 0;
165
+ voice->nextnote[0] = 0;
166
+ voice->nextnote[1] = 0;
167
+ }
168
+
169
+ void
170
+ _bloops_track_add(bloops *B, bloopsatrack *track) {
171
+ int i;
172
+ for (i = 0; i < BLOOPS_MAX_TRACKS; i++) {
173
+ if (B->voices[i].track == NULL) {
174
+ bloops_set_track_at(B, track, i);
175
+ break;
176
+ }
177
+ }
178
+ }
179
+
180
+ int
181
+ bloops_is_done(bloops *B)
182
+ {
183
+ return B->state == BLOOPS_STOP;
184
+ }
185
+
186
+ static void
187
+ bloops_synth(int length, float* buffer)
188
+ {
189
+ int bi, t, i, si;
190
+
191
+ while (length--)
192
+ {
193
+ int samplecount = 0;
194
+ float allsample = 0.0f;
195
+
196
+ for (bi = 0; bi < BLOOPS_MAX_CHANNELS; bi++)
197
+ {
198
+ int moreframes = 0;
199
+ bloops *B = MIXER->B[bi];
200
+ if (B == NULL)
201
+ continue;
202
+ for (t = 0; t < BLOOPS_MAX_TRACKS; t++)
203
+ {
204
+ bloopsavoice *A = &B->voices[t];
205
+ bloopsatrack *track = A->track;
206
+ if (track == NULL)
207
+ continue;
208
+
209
+ if (track->notes)
210
+ {
211
+ if (A->frames == A->nextnote[0])
212
+ {
213
+ if (A->nextnote[1] < track->nlen)
214
+ {
215
+ bloopsanote *note = &track->notes[A->nextnote[1]];
216
+ float freq = A->params.freq;
217
+ if (note->tone != 'n')
218
+ freq = bloops_note_freq(note->tone, (int)note->octave);
219
+ if (freq == 0.0f) {
220
+ A->period = 0.0f;
221
+ A->state = BLOOPS_STOP;
222
+ } else {
223
+ bloopsanote *note = &track->notes[A->nextnote[1]];
224
+ bloopsafx *fx = note->FX;
225
+ while (fx) {
226
+ switch (fx->cmd) {
227
+ case BLOOPS_FX_VOLUME: FX(fx, A->params.volume); break;
228
+ case BLOOPS_FX_PUNCH: FX(fx, A->params.punch); break;
229
+ case BLOOPS_FX_ATTACK: FX(fx, A->params.attack); break;
230
+ case BLOOPS_FX_SUSTAIN: FX(fx, A->params.sustain); break;
231
+ case BLOOPS_FX_DECAY: FX(fx, A->params.decay); break;
232
+ case BLOOPS_FX_SQUARE: FX(fx, A->params.square); break;
233
+ case BLOOPS_FX_SWEEP: FX(fx, A->params.sweep); break;
234
+ case BLOOPS_FX_VIBE: FX(fx, A->params.vibe); break;
235
+ case BLOOPS_FX_VSPEED: FX(fx, A->params.vspeed); break;
236
+ case BLOOPS_FX_VDELAY: FX(fx, A->params.vdelay); break;
237
+ case BLOOPS_FX_LPF: FX(fx, A->params.lpf); break;
238
+ case BLOOPS_FX_LSWEEP: FX(fx, A->params.lsweep); break;
239
+ case BLOOPS_FX_RESONANCE: FX(fx, A->params.resonance); break;
240
+ case BLOOPS_FX_HPF: FX(fx, A->params.hpf); break;
241
+ case BLOOPS_FX_HSWEEP: FX(fx, A->params.hsweep); break;
242
+ case BLOOPS_FX_ARP: FX(fx, A->params.arp); break;
243
+ case BLOOPS_FX_ASPEED: FX(fx, A->params.aspeed); break;
244
+ case BLOOPS_FX_PHASE: FX(fx, A->params.phase); break;
245
+ case BLOOPS_FX_PSWEEP: FX(fx, A->params.psweep); break;
246
+ case BLOOPS_FX_REPEAT: FX(fx, A->params.repeat); break;
247
+ }
248
+ fx = fx->next;
249
+ }
250
+
251
+ bloops_reset_voice(A);
252
+ bloops_start_voice(A);
253
+ A->period = 100.0 / (freq * freq + 0.001);
254
+ }
255
+
256
+ A->nextnote[0] += (int)(tempo2frames(B->tempo) * (4.0f / note->duration));
257
+ }
258
+ A->nextnote[1]++;
259
+ }
260
+
261
+ if (A->nextnote[1] <= track->nlen)
262
+ moreframes++;
263
+ }
264
+ else
265
+ {
266
+ moreframes++;
267
+ }
268
+
269
+ A->frames++;
270
+
271
+ if (A->state == BLOOPS_STOP)
272
+ continue;
273
+
274
+ samplecount++;
275
+ A->repeat++;
276
+ if (A->limit != 0 && A->repeat >= A->limit)
277
+ {
278
+ A->repeat = 0;
279
+ bloops_reset_voice(A);
280
+ }
281
+
282
+ A->atime++;
283
+ if (A->alimit != 0 && A->atime >= A->alimit)
284
+ {
285
+ A->alimit = 0;
286
+ A->period *= A->arp;
287
+ }
288
+
289
+ A->slide += A->dslide;
290
+ A->period *= A->slide;
291
+ if (A->period > A->maxperiod)
292
+ {
293
+ A->period = A->maxperiod;
294
+ if (A->params.limit > 0.0f)
295
+ A->state = BLOOPS_STOP;
296
+ }
297
+
298
+ float rfperiod = A->period;
299
+ if (A->vdelay > 0.0f)
300
+ {
301
+ A->vibe += A->vspeed;
302
+ rfperiod = A->period * (1.0 + sin(A->vibe) * A->vdelay);
303
+ }
304
+
305
+ int period = (int)rfperiod;
306
+ if (period < 8) period = 8;
307
+ A->square += A->sweep;
308
+ if(A->square < 0.0f) A->square = 0.0f;
309
+ if(A->square > 0.5f) A->square = 0.5f;
310
+
311
+ A->time++;
312
+ while (A->time >= A->length[A->stage])
313
+ {
314
+ A->time = 0;
315
+ A->stage++;
316
+ if (A->stage == 3)
317
+ A->state = BLOOPS_STOP;
318
+ }
319
+
320
+ switch (A->stage) {
321
+ case 0:
322
+ A->volume = (float)A->time / A->length[0];
323
+ break;
324
+ case 1:
325
+ A->volume = 1.0f + (1.0f - (float)A->time / A->length[1]) * 2.0f * A->params.punch;
326
+ break;
327
+ case 2:
328
+ A->volume = 1.0f - (float)A->time / A->length[2];
329
+ break;
330
+ }
331
+
332
+ A->fphase += A->dphase;
333
+ A->iphase = abs((int)A->fphase);
334
+ if (A->iphase > 1023) A->iphase = 1023;
335
+
336
+ if (A->filter[7] != 0.0f)
337
+ {
338
+ A->filter[6] *= A->filter[7];
339
+ if (A->filter[6] < 0.00001f) A->filter[6] = 0.00001f;
340
+ if (A->filter[6] > 0.1f) A->filter[6] = 0.1f;
341
+ }
342
+
343
+ float ssample = 0.0f;
344
+ for (si = 0; si < 8; si++)
345
+ {
346
+ float sample = 0.0f;
347
+ A->phase++;
348
+ if (A->phase >= period)
349
+ {
350
+ A->phase %= period;
351
+ if (A->params.type == BLOOPS_NOISE)
352
+ for (i = 0; i < 32; i++)
353
+ A->noise[i] = frnd(2.0f) - 1.0f;
354
+ }
355
+
356
+ float fp = (float)A->phase / period;
357
+ switch (A->params.type)
358
+ {
359
+ case BLOOPS_SQUARE:
360
+ if (fp < A->square)
361
+ sample = 0.5f;
362
+ else
363
+ sample = -0.5f;
364
+ break;
365
+ case BLOOPS_SAWTOOTH:
366
+ sample = 1.0f - fp * 2;
367
+ break;
368
+ case BLOOPS_SINE:
369
+ sample = (float)sin(fp * 2 * PI);
370
+ break;
371
+ case BLOOPS_NOISE:
372
+ sample = A->noise[A->phase * 32 / period];
373
+ break;
374
+ }
375
+
376
+ float pp = A->filter[0];
377
+ A->filter[2] *= A->filter[3];
378
+ if (A->filter[2] < 0.0f) A->filter[2] = 0.0f;
379
+ if (A->filter[2] > 0.1f) A->filter[2] = 0.1f;
380
+ if (A->params.lpf != 1.0f)
381
+ {
382
+ A->filter[1] += (sample - A->filter[0]) * A->filter[2];
383
+ A->filter[1] -= A->filter[1] * A->filter[4];
384
+ }
385
+ else
386
+ {
387
+ A->filter[0] = sample;
388
+ A->filter[1] = 0.0f;
389
+ }
390
+ A->filter[0] += A->filter[1];
391
+
392
+ A->filter[5] += A->filter[0] - pp;
393
+ A->filter[5] -= A->filter[5] * A->filter[6];
394
+ sample = A->filter[5];
395
+
396
+ A->phaser[A->phasex & 1023] = sample;
397
+ sample += A->phaser[(A->phasex - A->iphase + 1024) & 1023];
398
+ A->phasex = (A->phasex + 1) & 1023;
399
+
400
+ ssample += sample * A->volume;
401
+ }
402
+ ssample = ssample / 8 * B->volume;
403
+ ssample *= 2.0f * A->params.volume;
404
+
405
+ if (ssample > 1.0f) ssample = 1.0f;
406
+ if (ssample < -1.0f) ssample = -1.0f;
407
+ allsample += ssample;
408
+ }
409
+ if (moreframes == 0)
410
+ B->state = BLOOPS_STOP;
411
+ }
412
+
413
+ *buffer++ = allsample;
414
+ }
415
+ }
416
+
417
+ static int bloops_port_callback(const void *inputBuffer, void *outputBuffer,
418
+ unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo* timeInfo,
419
+ PaStreamCallbackFlags statusFlags, void *data)
420
+ {
421
+ float *out = (float*)outputBuffer;
422
+ bloops_synth(framesPerBuffer, out);
423
+ return paContinue;
424
+ }
425
+
426
+ void
427
+ bloops_play(bloops *B)
428
+ {
429
+ int i;
430
+
431
+ for (i = 0; i < BLOOPS_MAX_TRACKS; i++) {
432
+ bloopsavoice *A;
433
+ A = &B->voices[i];
434
+ if (A->track != NULL) {
435
+ memcpy(&A->params, &A->track->params, sizeof(bloopsaparams));
436
+ bloops_reset_voice(A);
437
+ bloops_start_voice(A);
438
+ A->frames = 0;
439
+ A->nextnote[0] = 0;
440
+ A->nextnote[1] = 0;
441
+ }
442
+ }
443
+
444
+ bloops_remove(B);
445
+ for (i = 0; i < BLOOPS_MAX_CHANNELS; i++) {
446
+ if (MIXER->B[i] == NULL || MIXER->B[i]->state == BLOOPS_STOP) {
447
+ bloops_ref(B);
448
+ if (MIXER->B[i] != NULL) {
449
+ bloops_destroy(MIXER->B[i]);
450
+ }
451
+ MIXER->B[i] = B;
452
+ break;
453
+ }
454
+ }
455
+
456
+ B->state = BLOOPS_PLAY;
457
+ if (MIXER->stream == NULL) {
458
+ Pa_OpenDefaultStream(&MIXER->stream, 0, 1, paFloat32,
459
+ SAMPLE_RATE, 512, bloops_port_callback, B);
460
+ Pa_StartStream(MIXER->stream);
461
+ }
462
+ }
463
+
464
+ void
465
+ bloops_stop(bloops *B)
466
+ {
467
+ int i, stopall = 1;
468
+ B->state = BLOOPS_STOP;
469
+ for (i = 0; i < BLOOPS_MAX_CHANNELS; i++)
470
+ if (MIXER->B[i] != NULL && MIXER->B[i]->state != BLOOPS_STOP)
471
+ stopall = 0;
472
+
473
+ if (stopall)
474
+ {
475
+ Pa_StopStream(MIXER->stream);
476
+ Pa_CloseStream(MIXER->stream);
477
+ MIXER->stream = NULL;
478
+ }
479
+ }
480
+
481
+ bloopsaphone *
482
+ bloops_square()
483
+ {
484
+ bloopsaphone *P = (bloopsaphone *)calloc(sizeof(bloopsaphone), 1);
485
+ P->refcount = 1;
486
+ P->params.type = BLOOPS_SQUARE;
487
+ P->params.volume = 0.5f;
488
+ P->params.sustain = 0.3f;
489
+ P->params.decay = 0.4f;
490
+ P->params.freq = 0.3f;
491
+ P->params.lpf = 1.0f;
492
+ return P;
493
+ }
494
+
495
+ static int bloops_open = 0;
496
+
497
+ bloops *
498
+ bloops_new()
499
+ {
500
+ int i;
501
+ bloops *B = (bloops *)malloc(sizeof(bloops));
502
+ B->refcount = 1;
503
+ B->volume = 0.10f;
504
+ B->tempo = 120;
505
+ B->state = BLOOPS_STOP;
506
+ for (i = 0; i < BLOOPS_MAX_TRACKS; i++) {
507
+ B->voices[i].track = NULL;
508
+ }
509
+
510
+ if (MIXER == NULL)
511
+ MIXER = (bloopsmix *)calloc(sizeof(bloopsmix), 1);
512
+
513
+ if (!bloops_open++)
514
+ {
515
+ srand(time(NULL));
516
+ bloops_lock_init(&LOCK);
517
+ Pa_Initialize();
518
+ }
519
+
520
+ return B;
521
+ }
522
+
523
+ void
524
+ bloops_ref(bloops *B)
525
+ {
526
+ B->refcount++;
527
+ }
528
+
529
+ void
530
+ bloops_destroy(bloops *B)
531
+ {
532
+ if (--B->refcount) {
533
+ return;
534
+ }
535
+
536
+ bloops_remove(B);
537
+ free((void *)B);
538
+
539
+ if (!--bloops_open)
540
+ {
541
+ Pa_Terminate();
542
+ bloops_lock_finalize(&LOCK);
543
+ if (MIXER != NULL)
544
+ free(MIXER);
545
+ MIXER = NULL;
546
+ }
547
+ }
548
+
549
+ static void bloops_notes_destroy(bloopsanote *notes, int nlen)
550
+ {
551
+ bloopsafx *fx, *n;
552
+ int i;
553
+
554
+ for (i = 0; i < nlen; i++) {
555
+ n = fx = notes[i].FX;
556
+ while ((fx = n)) {
557
+ n = fx->next;
558
+ free(fx);
559
+ }
560
+ }
561
+
562
+ free(notes);
563
+ }
564
+
565
+ void
566
+ bloops_track_ref(bloopsatrack *track)
567
+ {
568
+ track->refcount++;
569
+ }
570
+
571
+ void
572
+ bloops_track_destroy(bloopsatrack *track)
573
+ {
574
+ if (--track->refcount) {
575
+ return;
576
+ }
577
+ if (track->notes != NULL) {
578
+ bloops_notes_destroy(track->notes, track->nlen);
579
+ }
580
+ free(track);
581
+ }
582
+
583
+ void bloops_sound_copy(bloopsaphone *dest, bloopsaphone const *src) {
584
+ memcpy(&dest->params, &src->params, sizeof(bloopsaparams));
585
+ }
586
+
587
+ void bloops_sound_ref(bloopsaphone *sound) {
588
+ sound->refcount++;
589
+ }
590
+
591
+ void bloops_sound_destroy(bloopsaphone *sound) {
592
+ if (--sound->refcount) {
593
+ return;
594
+ }
595
+ free(sound);
596
+ }