viking-bloopsaphone 0.4

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,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
+ }