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,164 @@
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
+ Under MacPorts, you may need to add
86
+ /opt/local to your make settings:
87
+
88
+ make CFLAGS="-I/opt/local/include" \
89
+ LDFLAGS="-L/opt/local/lib" \
90
+ ruby
91
+
92
+ ~
93
+
94
+ -$- THE IDEALS,
95
+
96
+ -1- ASYNCHRONOUS.
97
+ You send it a song and it plays in
98
+ the background. You'll get an event
99
+ when it finishes.
100
+
101
+ -2- SMALL.
102
+ This is just a toy, I don't want it
103
+ to be very big and comprehensive.
104
+ Just to play little tunes with a
105
+ nostalgic arcade sound rather than
106
+ the CASIO-stylings of most MIDI.
107
+
108
+ -3- CUSTOM NOTATION.
109
+ Someone told me about Nokring, iMelody,
110
+ numbered musical notation and I did
111
+ some reading. They're little languages
112
+ for texting yourself a ringtone.
113
+
114
+ <http://en.wikipedia.org/wiki/Ring_Tone_Transfer_Language>
115
+ <http://homepage.mac.com/alvinmok/ericsson/emelody.html>
116
+
117
+ Bloopsaphone uses a variation on RTTTL.
118
+
119
+ Instead of commas, I use whitespace.
120
+ A rest is simply a number. A plus sign
121
+ moves everything up an octave. A minus
122
+ down an octave.
123
+
124
+ The Simpsons' Theme, for instance, would be:
125
+
126
+ 32 + C E F# 8:A G E C - 8:A 8:F# 8:F# 8:F# 2:G
127
+
128
+ Which translates into:
129
+
130
+ * a 1/32nd note rest.
131
+ * change one octave up.
132
+ * C quarter note.
133
+ * E quarter note.
134
+ * F# quarter note.
135
+ * A eighth note.
136
+ * G quarter.
137
+ * E quarter.
138
+ * C one-quarter note.
139
+ * change one octave down.
140
+ * A eighth.
141
+ * Three F# eighths.
142
+ * G half note.
143
+
144
+ The colons are optional. They are there because you
145
+ can place an octave number after each note. Somehow
146
+ "8B6" (an eighth note of B at the sixth octave) looks
147
+ more confusing than "8:B6". I guess I figured that
148
+ the timing "8" is conceptually separate from the
149
+ actual tone "B6", even though they both comprise
150
+ the note itself.
151
+
152
+ -4- SERIALIZE SOUNDS.
153
+ To accomodate passing instruments between
154
+ ruby and c, bloopsaphone comes with a tiny
155
+ file format for describing sounds.
156
+
157
+ You can find examples of these in the sounds/
158
+ folder in this distro. Possible sound types
159
+ are 'square', 'sawtooth', 'sine' and 'noise'.
160
+ All other settings go from 0.0 to 1.0.
161
+
162
+ The 'freq' setting is only used if the sound
163
+ is played without a tune.
164
+
@@ -0,0 +1,443 @@
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 <string.h>
12
+ #include <time.h>
13
+ #include <math.h>
14
+ #include <portaudio.h>
15
+ #include <unistd.h>
16
+ #include "bloopsaphone.h"
17
+
18
+ #define SAMPLE_RATE 44100
19
+ #define rnd(n) (rand() % (n + 1))
20
+ #define tempo2frames(tempo) ((float)SAMPLE_RATE / (tempo / 60.0f))
21
+ #define PI 3.14159265f
22
+
23
+ float
24
+ frnd(float range)
25
+ {
26
+ return (float)rnd(10000) / 10000 * range;
27
+ }
28
+
29
+ void
30
+ bloops_ready(bloops *B, bloopsatrack *A, unsigned char init)
31
+ {
32
+ A->period = 100.0 / (A->P->freq * A->P->freq + 0.001);
33
+ A->maxperiod = 100.0 / (A->P->limit * A->P->limit + 0.001);
34
+ A->slide = 1.0 - pow((double)A->P->slide, 3.0) * 0.01;
35
+ A->dslide = -pow((double)A->P->dslide, 3.0) * 0.000001;
36
+ A->square = 0.5f - A->P->square * 0.5f;
37
+ A->sweep = -A->P->sweep * 0.00005f;
38
+ if (A->P->arp >= 0.0f)
39
+ A->arp = 1.0 - pow((double)A->P->arp, 2.0) * 0.9;
40
+ else
41
+ A->arp = 1.0 + pow((double)A->P->arp, 2.0) * 10.0;
42
+ A->atime = 0;
43
+ A->alimit = (int)(pow(1.0f - A->P->aspeed, 2.0f) * 20000 + 32);
44
+ if (A->P->aspeed == 1.0f)
45
+ A->alimit = 0;
46
+
47
+ if (init)
48
+ {
49
+ int i = 0;
50
+ A->phase = 0;
51
+ A->filter[0] = 0.0f;
52
+ A->filter[1] = 0.0f;
53
+ A->filter[2] = pow(A->P->lpf, 3.0f) * 0.1f;
54
+ A->filter[3] = 1.0f + A->P->lsweep * 0.0001f;
55
+ A->filter[4] = 5.0f / (1.0f + pow(A->P->resonance, 2.0f) * 20.0f) * (0.01f + A->filter[2]);
56
+ if (A->filter[4] > 0.8f) A->filter[4] = 0.8f;
57
+ A->filter[5] = 0.0f;
58
+ A->filter[6] = pow(A->P->hpf, 2.0f) * 0.1f;
59
+ A->filter[7] = 1.0 + A->P->hsweep * 0.0003f;
60
+
61
+ A->vibe = 0.0f;
62
+ A->vspeed = pow(A->P->vspeed, 2.0f) * 0.01f;
63
+ A->vdelay = A->P->vibe * 0.5f;
64
+
65
+ A->volume = 0.0f;
66
+ A->stage = 0;
67
+ A->time = 0;
68
+ A->length[0] = (int)(A->P->attack * A->P->attack * 100000.0f);
69
+ A->length[1] = (int)(A->P->sustain * A->P->sustain * 100000.0f);
70
+ A->length[2] = (int)(A->P->decay * A->P->decay * 100000.0f);
71
+
72
+ A->fphase = pow(A->P->phase, 2.0f) * 1020.0f;
73
+ if (A->P->phase < 0.0f) A->fphase = -A->fphase;
74
+ A->dphase = pow(A->P->psweep, 2.0f) * 1.0f;
75
+ if (A->P->psweep < 0.0f) A->dphase = -A->dphase;
76
+ A->iphase = abs((int)A->fphase);
77
+ A->phasex = 0;
78
+
79
+ memset(A->phaser, 0, 1024 * sizeof(float));
80
+ for (i = 0; i < 32; i++)
81
+ A->noise[i] = frnd(2.0f) - 1.0f;
82
+
83
+ A->repeat = 0;
84
+ A->limit = (int)(pow(1.0f - A->P->repeat, 2.0f) * 20000 + 32);
85
+ if (A->P->repeat == 0.0f)
86
+ A->limit = 0;
87
+ A->playing = BLOOPS_PLAY;
88
+ }
89
+ }
90
+
91
+ void
92
+ bloops_clear(bloops *B)
93
+ {
94
+ int i;
95
+ for (i = 0; i < BLOOPS_MAX_TRACKS; i++)
96
+ B->tracks[i] = NULL;
97
+ }
98
+
99
+ void
100
+ bloops_tempo(bloops *B, int tempo)
101
+ {
102
+ B->tempo = tempo;
103
+ }
104
+
105
+ void
106
+ bloops_track_at(bloops *B, bloopsatrack *track, int num)
107
+ {
108
+ B->tracks[num] = track;
109
+ }
110
+
111
+ void
112
+ bloops_play(bloops *B)
113
+ {
114
+ int i;
115
+ for (i = 0; i < BLOOPS_MAX_TRACKS; i++)
116
+ if (B->tracks[i] != NULL) {
117
+ bloops_ready(B, B->tracks[i], 1);
118
+ B->tracks[i]->frames = 0;
119
+ }
120
+ B->play = BLOOPS_PLAY;
121
+ }
122
+
123
+ int
124
+ bloops_is_done(bloops *B)
125
+ {
126
+ return B->play == BLOOPS_STOP;
127
+ }
128
+
129
+ static void
130
+ bloops_synth(bloops *B, int length, float* buffer)
131
+ {
132
+ int t, i, si;
133
+
134
+ while (length--)
135
+ {
136
+ int samplecount = 0;
137
+ int moreframes = 0;
138
+ float allsample = 0.0f;
139
+
140
+ for (t = 0; t < BLOOPS_MAX_TRACKS; t++)
141
+ {
142
+ bloopsatrack *A = B->tracks[t];
143
+ if (A == NULL)
144
+ continue;
145
+
146
+ if (A->notes)
147
+ {
148
+ int frames = 0;
149
+ for (i = 0; i < A->nlen; i++)
150
+ {
151
+ bloopsanote *note = &A->notes[i];
152
+ if (A->frames == frames)
153
+ {
154
+ float freq = bloops_note_freq(note->tone, (int)note->octave);
155
+ if (freq == 0.0f) {
156
+ A->period = 0.0f;
157
+ A->playing = BLOOPS_STOP;
158
+ } else {
159
+ bloops_ready(B, A, 1);
160
+ A->period = 100.0 / (freq * freq + 0.001);
161
+ }
162
+ }
163
+ else if (A->frames < frames)
164
+ break;
165
+ frames += (int)(tempo2frames(B->tempo) * (4.0f / note->duration));
166
+ }
167
+
168
+ if (A->frames <= frames)
169
+ moreframes++;
170
+ }
171
+ else
172
+ {
173
+ moreframes++;
174
+ }
175
+
176
+ A->frames++;
177
+
178
+ if (A->playing == BLOOPS_STOP)
179
+ continue;
180
+
181
+ samplecount++;
182
+ A->repeat++;
183
+ if (A->limit != 0 && A->repeat >= A->limit)
184
+ {
185
+ A->repeat = 0;
186
+ bloops_ready(B, A, 0);
187
+ }
188
+
189
+ A->atime++;
190
+ if (A->alimit != 0 && A->atime >= A->alimit)
191
+ {
192
+ A->alimit = 0;
193
+ A->period *= A->arp;
194
+ }
195
+
196
+ A->slide += A->dslide;
197
+ A->period *= A->slide;
198
+ if (A->period > A->maxperiod)
199
+ {
200
+ A->period = A->maxperiod;
201
+ if (A->P->limit > 0.0f)
202
+ A->playing = BLOOPS_STOP;
203
+ }
204
+
205
+ float rfperiod = A->period;
206
+ if (A->vdelay > 0.0f)
207
+ {
208
+ A->vibe += A->vspeed;
209
+ rfperiod = A->period * (1.0 + sin(A->vibe) * A->vdelay);
210
+ }
211
+
212
+ int period = (int)rfperiod;
213
+ if (period < 8) period = 8;
214
+ A->square += A->sweep;
215
+ if(A->square < 0.0f) A->square = 0.0f;
216
+ if(A->square > 0.5f) A->square = 0.5f;
217
+
218
+ A->time++;
219
+ if (A->time > A->length[A->stage])
220
+ {
221
+ A->time = 0;
222
+ A->stage++;
223
+ if (A->stage == 3)
224
+ A->playing = BLOOPS_STOP;
225
+ }
226
+
227
+ switch (A->stage) {
228
+ case 0:
229
+ A->volume = (float)A->time / A->length[0];
230
+ break;
231
+ case 1:
232
+ A->volume = 1.0f + pow(1.0f - (float)A->time / A->length[1], 1.0f) * 2.0f * A->P->punch;
233
+ break;
234
+ case 2:
235
+ A->volume = 1.0f - (float)A->time / A->length[2];
236
+ break;
237
+ }
238
+
239
+ A->fphase += A->dphase;
240
+ A->iphase = abs((int)A->fphase);
241
+ if (A->iphase > 1023) A->iphase = 1023;
242
+
243
+ if (A->filter[7] != 0.0f)
244
+ {
245
+ A->filter[6] *= A->filter[7];
246
+ if (A->filter[6] < 0.00001f) A->filter[6] = 0.00001f;
247
+ if (A->filter[6] > 0.1f) A->filter[6] = 0.1f;
248
+ }
249
+
250
+ float ssample = 0.0f;
251
+ for (si = 0; si < 8; si++)
252
+ {
253
+ float sample = 0.0f;
254
+ A->phase++;
255
+ if (A->phase >= period)
256
+ {
257
+ A->phase %= period;
258
+ if (A->P->type == BLOOPS_NOISE)
259
+ for (i = 0; i < 32; i++)
260
+ A->noise[i] = frnd(2.0f) - 1.0f;
261
+ }
262
+
263
+ float fp = (float)A->phase / period;
264
+ switch (A->P->type)
265
+ {
266
+ case BLOOPS_SQUARE:
267
+ if (fp < A->square)
268
+ sample = 0.5f;
269
+ else
270
+ sample = -0.5f;
271
+ break;
272
+ case BLOOPS_SAWTOOTH:
273
+ sample = 1.0f - fp * 2;
274
+ break;
275
+ case BLOOPS_SINE:
276
+ sample = (float)sin(fp * 2 * PI);
277
+ break;
278
+ case BLOOPS_NOISE:
279
+ sample = A->noise[A->phase * 32 / period];
280
+ break;
281
+ }
282
+
283
+ float pp = A->filter[0];
284
+ A->filter[2] *= A->filter[3];
285
+ if (A->filter[2] < 0.0f) A->filter[2] = 0.0f;
286
+ if (A->filter[2] > 0.1f) A->filter[2] = 0.1f;
287
+ if (A->P->lpf != 1.0f)
288
+ {
289
+ A->filter[1] += (sample - A->filter[0]) * A->filter[2];
290
+ A->filter[1] -= A->filter[1] * A->filter[4];
291
+ }
292
+ else
293
+ {
294
+ A->filter[0] = sample;
295
+ A->filter[1] = 0.0f;
296
+ }
297
+ A->filter[0] += A->filter[1];
298
+
299
+ A->filter[5] += A->filter[0] - pp;
300
+ A->filter[5] -= A->filter[5] * A->filter[6];
301
+ sample = A->filter[5];
302
+
303
+ A->phaser[A->phasex & 1023] = sample;
304
+ sample += A->phaser[(A->phasex - A->iphase + 1024) & 1023];
305
+ A->phasex = (A->phasex + 1) & 1023;
306
+
307
+ ssample += sample * A->volume;
308
+ }
309
+ ssample = ssample / 8 * B->volume;
310
+ ssample *= 2.0f * A->P->volume;
311
+
312
+ if (ssample > 1.0f) ssample = 1.0f;
313
+ if (ssample < -1.0f) ssample = -1.0f;
314
+ allsample += ssample;
315
+ }
316
+
317
+ if (moreframes == 0)
318
+ B->play = BLOOPS_STOP;
319
+ *buffer++ = allsample;
320
+ }
321
+ }
322
+
323
+ static int bloops_port_callback(const void *inputBuffer, void *outputBuffer,
324
+ unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo* timeInfo,
325
+ PaStreamCallbackFlags statusFlags, void *data)
326
+ {
327
+ int i;
328
+ float *out = (float*)outputBuffer;
329
+ bloops *B = (bloops *)data;
330
+
331
+ if (B->play == BLOOPS_PLAY)
332
+ bloops_synth(B, framesPerBuffer, out);
333
+ else
334
+ for(i = 0; i < framesPerBuffer; i++)
335
+ *out++ = 0.0f;
336
+
337
+ return 0;
338
+ }
339
+
340
+ bloopsaphone *
341
+ bloops_square()
342
+ {
343
+ bloopsaphone *P = (bloopsaphone *)calloc(sizeof(bloopsaphone), 1);
344
+ P->type = BLOOPS_SQUARE;
345
+ P->volume = 0.5f;
346
+ P->sustain = 0.3f;
347
+ P->decay = 0.4f;
348
+ P->freq = 0.3f;
349
+ P->lpf = 1.0f;
350
+ return P;
351
+ }
352
+
353
+ bloopsaphone *
354
+ bloops_load(char* filename)
355
+ {
356
+ bloopsaphone *P = NULL;
357
+ FILE* file = fopen(filename, "rb");
358
+ if (!file) return NULL;
359
+
360
+ int version = 0;
361
+ fread(&version, 1, sizeof(int), file);
362
+ if (version != 102)
363
+ return NULL;
364
+
365
+ P = (bloopsaphone *)malloc(sizeof(bloopsaphone));
366
+ fread(&P->type, 1, sizeof(int), file);
367
+
368
+ P->volume = 0.5f;
369
+ fread(&P->volume, 1, sizeof(float), file);
370
+ fread(&P->freq, 1, sizeof(float), file);
371
+ fread(&P->limit, 1, sizeof(float), file);
372
+ fread(&P->slide, 1, sizeof(float), file);
373
+ fread(&P->dslide, 1, sizeof(float), file);
374
+ fread(&P->square, 1, sizeof(float), file);
375
+ fread(&P->sweep, 1, sizeof(float), file);
376
+
377
+ fread(&P->vibe, 1, sizeof(float), file);
378
+ fread(&P->vspeed, 1, sizeof(float), file);
379
+ fread(&P->vdelay, 1, sizeof(float), file);
380
+
381
+ fread(&P->attack, 1, sizeof(float), file);
382
+ fread(&P->sustain, 1, sizeof(float), file);
383
+ fread(&P->decay, 1, sizeof(float), file);
384
+ fread(&P->punch, 1, sizeof(float), file);
385
+
386
+ fread(&P->resonance, 1, sizeof(float), file);
387
+ fread(&P->lpf, 1, sizeof(float), file);
388
+ fread(&P->lsweep, 1, sizeof(float), file);
389
+ fread(&P->hpf, 1, sizeof(float), file);
390
+ fread(&P->hsweep, 1, sizeof(float), file);
391
+
392
+ fread(&P->phase, 1, sizeof(float), file);
393
+ fread(&P->psweep, 1, sizeof(float), file);
394
+
395
+ fread(&P->repeat, 1, sizeof(float), file);
396
+ fread(&P->arp, 1, sizeof(float), file);
397
+ fread(&P->aspeed, 1, sizeof(float), file);
398
+
399
+ fclose(file);
400
+ return P;
401
+ }
402
+
403
+ static int bloops_open = 0;
404
+
405
+ bloops *
406
+ bloops_new()
407
+ {
408
+ bloops *B = (bloops *)malloc(sizeof(bloops));
409
+ B->volume = 0.10f;
410
+ B->tempo = 120;
411
+ B->play = BLOOPS_STOP;
412
+ bloops_clear(B);
413
+
414
+ if (!bloops_open++)
415
+ {
416
+ srand(time(NULL));
417
+ Pa_Initialize();
418
+ }
419
+
420
+ Pa_OpenDefaultStream(&B->stream, 0, 1, paFloat32,
421
+ SAMPLE_RATE, 512, bloops_port_callback, B);
422
+ Pa_StartStream(B->stream);
423
+ return B;
424
+ }
425
+
426
+ void
427
+ bloops_destroy(bloops *B)
428
+ {
429
+ Pa_StopStream(B->stream);
430
+ Pa_CloseStream(B->stream);
431
+ free((void *)B);
432
+
433
+ if (!--bloops_open)
434
+ Pa_Terminate();
435
+ }
436
+
437
+ void
438
+ bloops_track_destroy(bloopsatrack *track)
439
+ {
440
+ if (track->notes != NULL)
441
+ free(track->notes);
442
+ free(track);
443
+ }