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