bloops 0.5

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/c/notation.rl ADDED
@@ -0,0 +1,549 @@
1
+ //
2
+ // notation.rl
3
+ // the musical notation parser
4
+ //
5
+ // (c) 2009 why the lucky stiff
6
+ // See COPYING for the license
7
+ //
8
+ #include <stdio.h>
9
+ #include <stdlib.h>
10
+ #include <string.h>
11
+ #include <math.h>
12
+ #include <sys/stat.h>
13
+ #include "bloopsaphone.h"
14
+
15
+ #define ATOI(X,N) ({ \
16
+ char *Ap = X; \
17
+ int Ai = 0; \
18
+ size_t Al = N; \
19
+ while (Al--) { \
20
+ if ((*Ap >= '0') && (*Ap <= '9')) { \
21
+ Ai = (Ai * 10) + (*Ap - '0'); \
22
+ Ap++; \
23
+ } \
24
+ else break; \
25
+ } \
26
+ Ai; \
27
+ })
28
+
29
+ #define NOTE S->notes[S->nlen]
30
+
31
+ #define NEXT() \
32
+ NOTE.duration = len; \
33
+ NOTE.octave = oct; \
34
+ mod = 0; \
35
+ tone = 0; \
36
+ len = 4; \
37
+ fxval = 0; \
38
+ fxmod = 0; \
39
+ S->nlen++
40
+
41
+ %%{
42
+ machine bloopnotes;
43
+
44
+ action Alen {
45
+ len = ATOI(ts, p - ts);
46
+ }
47
+
48
+ action Aoct {
49
+ oct = ATOI(p - 1, 1);
50
+ }
51
+
52
+ action Anote {
53
+ switch (tone) {
54
+ case 'a': case 'A':
55
+ if (mod == 'b') NOTE.tone = 'a';
56
+ else if (mod == '#') NOTE.tone = 'b';
57
+ else NOTE.tone = 'A';
58
+ break;
59
+ case 'b': case 'B':
60
+ if (mod == 'b') NOTE.tone = 'b';
61
+ else if (mod == '#') NOTE.tone = 'C';
62
+ else NOTE.tone = 'B';
63
+ break;
64
+ case 'c': case 'C':
65
+ if (mod == 'b') NOTE.tone = 'B';
66
+ else if (mod == '#') NOTE.tone = 'd';
67
+ else NOTE.tone = 'C';
68
+ break;
69
+ case 'd': case 'D':
70
+ if (mod == 'b') NOTE.tone = 'd';
71
+ else if (mod == '#') NOTE.tone = 'e';
72
+ else NOTE.tone = 'D';
73
+ break;
74
+ case 'e': case 'E':
75
+ if (mod == 'b') NOTE.tone = 'e';
76
+ else if (mod == '#') NOTE.tone = 'F';
77
+ else NOTE.tone = 'E';
78
+ break;
79
+ case 'f': case 'F':
80
+ if (mod == 'b') NOTE.tone = 'E';
81
+ else if (mod == '#') NOTE.tone = 'g';
82
+ else NOTE.tone = 'F';
83
+ break;
84
+ case 'g': case 'G':
85
+ if (mod == 'b') NOTE.tone = 'g';
86
+ else if (mod == '#') NOTE.tone = 'a';
87
+ else NOTE.tone = 'G';
88
+ break;
89
+ }
90
+ }
91
+
92
+ action Afx {
93
+ bloopsafx *fx = (bloopsafx *)malloc(sizeof(bloopsafx));
94
+ fx->next = NOTE.FX;
95
+ fx->cmd = fxcmd;
96
+ fx->val = fxval;
97
+ fx->mod = fxmod;
98
+ fxval = fxmod = 0;
99
+ NOTE.FX = fx;
100
+ }
101
+
102
+ action fxval1 {
103
+ fxval = atoi(p-1) * 1.0f;
104
+ }
105
+
106
+ action fxval2 {
107
+ fxval += ATOI(pf, p - pf) * pow(0.1f, p - pf);
108
+ }
109
+
110
+ dec = digit+ %fxval1 ("." %{ pf = p; } digit+ )? %fxval2;
111
+ float = ("-" dec %{ fxval *= -1.0f; } | dec);
112
+ fxcmd = "volume" %{ fxcmd = BLOOPS_FX_VOLUME; } |
113
+ "punch" %{ fxcmd = BLOOPS_FX_PUNCH; } |
114
+ "attack" %{ fxcmd = BLOOPS_FX_ATTACK; } |
115
+ "sustain" %{ fxcmd = BLOOPS_FX_SUSTAIN; } |
116
+ "decay" %{ fxcmd = BLOOPS_FX_DECAY; } |
117
+ "square" %{ fxcmd = BLOOPS_FX_SQUARE; } |
118
+ "sweep" %{ fxcmd = BLOOPS_FX_SWEEP; } |
119
+ "vibe" %{ fxcmd = BLOOPS_FX_VIBE; } |
120
+ "vspeed" %{ fxcmd = BLOOPS_FX_VSPEED; } |
121
+ "vdelay" %{ fxcmd = BLOOPS_FX_VDELAY; } |
122
+ "lpf" %{ fxcmd = BLOOPS_FX_LPF; } |
123
+ "lsweep" %{ fxcmd = BLOOPS_FX_LSWEEP; } |
124
+ "resonance" %{ fxcmd = BLOOPS_FX_RESONANCE; } |
125
+ "hpf" %{ fxcmd = BLOOPS_FX_HPF; } |
126
+ "hsweep" %{ fxcmd = BLOOPS_FX_HSWEEP; } |
127
+ "arp" %{ fxcmd = BLOOPS_FX_ARP; } |
128
+ "aspeed" %{ fxcmd = BLOOPS_FX_ASPEED; } |
129
+ "phase" %{ fxcmd = BLOOPS_FX_PHASE; } |
130
+ "psweep" %{ fxcmd = BLOOPS_FX_PSWEEP; } |
131
+ "repeat" %{ fxcmd = BLOOPS_FX_REPEAT; };
132
+
133
+ len = [1-9] [0-9]? ":"? %Alen;
134
+ up = "+" %{ len = 1; } len?;
135
+ down = "-" %{ len = 1; } len?;
136
+ mod = [b#] %{ mod = p[-1]; };
137
+ oct = [1-8] %Aoct;
138
+ fxmod = ( ("+"|"-") %{ fxmod = p[-1]; } (":"|space+) )?;
139
+ fx = ("[" fxcmd (":"|space*) fxmod float "]" %Afx );
140
+ note = len? [a-gA-G] %{ tone = p[-1]; } mod? oct? fx* %Anote;
141
+
142
+ main := |*
143
+ len => {
144
+ NOTE.tone = 0;
145
+ NEXT();
146
+ };
147
+ note => { NEXT(); };
148
+ up => { oct++; len = 4; };
149
+ down => { oct--; len = 4; };
150
+ space;
151
+ *|;
152
+
153
+ write data nofinal;
154
+ }%%
155
+
156
+ extern void _bloops_track_add(bloops *B, bloopsatrack *track);
157
+
158
+ bloopsatrack *
159
+ bloops_track(bloops *B, bloopsaphone *P, char *track, int tracklen)
160
+ {
161
+ int cs, act, oct = 4, len = 4;
162
+ bloopsatrack *S = (bloopsatrack *)malloc(sizeof(bloopsatrack));
163
+ char tone, mod, fxmod, *p, *pe, *pf, *ts, *te, *eof = 0;
164
+ bloopsafxcmd fxcmd = (bloopsafxcmd)0;
165
+ float fxval = 0;
166
+
167
+ S->refcount = 1;
168
+ S->nlen = 0;
169
+ S->capa = 1024;
170
+ S->notes = (bloopsanote *)calloc(sizeof(bloopsanote), 1024);
171
+
172
+ p = track;
173
+ pe = track + tracklen + 1;
174
+
175
+ %% write init;
176
+ %% write exec;
177
+
178
+ memcpy(&S->params, &P->params, sizeof(bloopsaparams));
179
+
180
+ _bloops_track_add(B, S);
181
+
182
+ return S;
183
+ }
184
+
185
+ bloopsatrack *
186
+ bloops_track2(bloops *B, bloopsaphone *P, char *track)
187
+ {
188
+ return bloops_track(B, P, track, strlen(track));
189
+ }
190
+
191
+ char *
192
+ bloops_track_str(bloopsatrack *track)
193
+ {
194
+ int bufsize = sizeof(char) * (track->nlen * 6 + 1024);
195
+ char *str = (char *)malloc(bufsize), *ptr = str;
196
+ int i, adv = 0;
197
+
198
+ for (i = 0; i < track->nlen; i++)
199
+ {
200
+ if (ptr - str + adv + sizeof(char) * 256 > bufsize) {
201
+ char *new_str;
202
+ bufsize += sizeof(char) * 1024;
203
+ new_str = realloc(str, bufsize);
204
+ if (new_str == NULL) {
205
+ free(str);
206
+ return NULL;
207
+ }
208
+ }
209
+
210
+ if (ptr > str)
211
+ strcat(ptr++, " ");
212
+
213
+ if (track->notes[i].duration != 4)
214
+ {
215
+ adv = sprintf(ptr, "%d:", (int)track->notes[i].duration);
216
+ ptr += adv;
217
+ }
218
+
219
+ if (track->notes[i].tone)
220
+ {
221
+ char tone[3] = "\0\0\0";
222
+ tone[0] = track->notes[i].tone;
223
+ switch (tone[0]) {
224
+ case 'a': tone[0] = 'A'; tone[1] = 'b'; break;
225
+ case 'b': tone[0] = 'B'; tone[1] = 'b'; break;
226
+ case 'd': tone[0] = 'C'; tone[1] = '#'; break;
227
+ case 'e': tone[0] = 'E'; tone[1] = 'b'; break;
228
+ case 'g': tone[0] = 'F'; tone[1] = '#'; break;
229
+ }
230
+ adv = sprintf(ptr, "%s", tone);
231
+ ptr += adv;
232
+
233
+ adv = sprintf(ptr, "%d", (int)track->notes[i].octave);
234
+ ptr += adv;
235
+ bloopsafx *fx = (bloopsafx *)track->notes[i].FX;
236
+ while (fx) {
237
+ if (fx->mod == 0)
238
+ adv = sprintf(ptr, "[%s %0.3f]", bloops_fxcmd_name(fx->cmd), fx->val);
239
+ else
240
+ adv = sprintf(ptr, "[%s %c %0.3f]", bloops_fxcmd_name(fx->cmd), fx->mod, fx->val);
241
+ ptr += adv;
242
+ fx = (bloopsafx *)fx->next;
243
+ }
244
+ }
245
+ }
246
+
247
+ return str;
248
+ }
249
+
250
+ char *
251
+ bloops_fxcmd_name(bloopsafxcmd fxcmd) {
252
+ char *fxname = "\0";
253
+ switch (fxcmd) {
254
+ case BLOOPS_FX_VOLUME: fxname = "volume"; break;
255
+ case BLOOPS_FX_PUNCH: fxname = "punch"; break;
256
+ case BLOOPS_FX_ATTACK: fxname = "attack"; break;
257
+ case BLOOPS_FX_SUSTAIN: fxname = "sustain"; break;
258
+ case BLOOPS_FX_DECAY: fxname = "decay"; break;
259
+ case BLOOPS_FX_SQUARE: fxname = "square"; break;
260
+ case BLOOPS_FX_SWEEP: fxname = "sweep"; break;
261
+ case BLOOPS_FX_VIBE: fxname = "vibe"; break;
262
+ case BLOOPS_FX_VSPEED: fxname = "vspeed"; break;
263
+ case BLOOPS_FX_VDELAY: fxname = "vdelay"; break;
264
+ case BLOOPS_FX_LPF: fxname = "lpf"; break;
265
+ case BLOOPS_FX_LSWEEP: fxname = "lsweep"; break;
266
+ case BLOOPS_FX_RESONANCE: fxname = "resonance"; break;
267
+ case BLOOPS_FX_HPF: fxname = "hpf"; break;
268
+ case BLOOPS_FX_HSWEEP: fxname = "hsweep"; break;
269
+ case BLOOPS_FX_ARP: fxname = "arp"; break;
270
+ case BLOOPS_FX_ASPEED: fxname = "aspeed"; break;
271
+ case BLOOPS_FX_PHASE: fxname = "phase"; break;
272
+ case BLOOPS_FX_PSWEEP: fxname = "psweep"; break;
273
+ case BLOOPS_FX_REPEAT: fxname = "repeat"; break;
274
+ }
275
+ return fxname;
276
+ }
277
+
278
+ float
279
+ bloops_note_freq(char note, int octave)
280
+ {
281
+ switch (note)
282
+ {
283
+ case 'A': // A
284
+ if (octave <= 0) return 0.0;
285
+ else if (octave == 1) return 0.121;
286
+ else if (octave == 2) return 0.175;
287
+ else if (octave == 3) return 0.248;
288
+ else if (octave == 4) return 0.353;
289
+ else if (octave == 5) return 0.500;
290
+ break;
291
+
292
+ case 'b': // A# or Bb
293
+ if (octave <= 0) return 0.0;
294
+ else if (octave == 1) return 0.125;
295
+ else if (octave == 2) return 0.181;
296
+ else if (octave == 3) return 0.255;
297
+ else if (octave == 4) return 0.364;
298
+ else if (octave == 5) return 0.515;
299
+ break;
300
+
301
+ case 'B': // B
302
+ if (octave <= 0) return 0.0;
303
+ else if (octave == 1) return 0.129;
304
+ else if (octave == 2) return 0.187;
305
+ else if (octave == 3) return 0.263;
306
+ else if (octave == 4) return 0.374;
307
+ else if (octave == 5) return 0.528;
308
+ break;
309
+
310
+ case 'C': // C
311
+ if (octave <= 1) return 0.0;
312
+ else if (octave == 2) return 0.133;
313
+ else if (octave == 3) return 0.192;
314
+ else if (octave == 4) return 0.271;
315
+ else if (octave == 5) return 0.385;
316
+ else if (octave == 6) return 0.544;
317
+ break;
318
+
319
+ case 'd': // C# or Db
320
+ if (octave <= 1) return 0.0;
321
+ else if (octave == 2) return 0.138;
322
+ else if (octave == 3) return 0.198;
323
+ else if (octave == 4) return 0.279;
324
+ else if (octave == 5) return 0.395;
325
+ else if (octave == 6) return 0.559;
326
+ break;
327
+
328
+ case 'D': // D
329
+ if (octave <= 1) return 0.0;
330
+ else if (octave == 2) return 0.143;
331
+ else if (octave == 3) return 0.202;
332
+ else if (octave == 4) return 0.287;
333
+ else if (octave == 5) return 0.406;
334
+ else if (octave == 6) return 0.575;
335
+ break;
336
+
337
+ case 'e': // D# or Eb
338
+ if (octave <= 1) return 0.0;
339
+ else if (octave == 2) return 0.148;
340
+ else if (octave == 3) return 0.208;
341
+ else if (octave == 4) return 0.296;
342
+ else if (octave == 5) return 0.418;
343
+ else if (octave == 6) return 0.593;
344
+ break;
345
+
346
+ case 'E': // E
347
+ if (octave <= 1) return 0.0;
348
+ else if (octave == 2) return 0.152;
349
+ else if (octave == 3) return 0.214;
350
+ else if (octave == 4) return 0.305;
351
+ else if (octave == 5) return 0.429;
352
+ else if (octave == 6) return 0.608;
353
+ break;
354
+
355
+ case 'F': // F
356
+ if (octave <= 1) return 0.0;
357
+ else if (octave == 2) return 0.155;
358
+ else if (octave == 3) return 0.220;
359
+ else if (octave == 4) return 0.314;
360
+ else if (octave == 5) return 0.441;
361
+ break;
362
+
363
+ case 'g': // F# or Gb
364
+ if (octave <= 1) return 0.0;
365
+ else if (octave == 2) return 0.160;
366
+ else if (octave == 3) return 0.227;
367
+ else if (octave == 4) return 0.323;
368
+ else if (octave == 5) return 0.454;
369
+ break;
370
+
371
+ case 'G': // G
372
+ if (octave <= 1) return 0.0;
373
+ else if (octave == 2) return 0.164;
374
+ else if (octave == 3) return 0.234;
375
+ else if (octave == 4) return 0.332;
376
+ else if (octave == 5) return 0.468;
377
+ break;
378
+
379
+ case 'a': // G# or Ab
380
+ if (octave <= 1) return 0.117;
381
+ else if (octave == 2) return 0.170;
382
+ else if (octave == 3) return 0.242;
383
+ else if (octave == 4) return 0.343;
384
+ else if (octave == 5) return 0.485;
385
+ break;
386
+ }
387
+
388
+ return 0.0;
389
+ }
390
+
391
+ #define KEY(name) key = (void *)&P->params.name
392
+
393
+ %%{
394
+ machine bloopserial;
395
+
396
+ action ival {
397
+ ival = ATOI(ts, p - ts);
398
+ }
399
+
400
+ action fval1 {
401
+ fval = ATOI(ts, p - ts) * 1.0f;
402
+ }
403
+
404
+ action fval2 {
405
+ fval = ATOI(pf, p - pf) * pow(0.1f, p - pf);
406
+ }
407
+
408
+ dec = [0-9]+ %fval1 "." %{ pf = p; } [0-9]+ %fval2;
409
+ float = ("-" dec %{ fval *= -1.0f; } | dec);
410
+ key = "volume" %{ KEY(volume); } |
411
+ "arp" %{ KEY(arp); } |
412
+ "aspeed" %{ KEY(aspeed); } |
413
+ "attack" %{ KEY(attack); } |
414
+ "decay" %{ KEY(decay); } |
415
+ "dslide" %{ KEY(dslide); } |
416
+ "freq" %{ KEY(freq); } |
417
+ "hpf" %{ KEY(hpf); } |
418
+ "hsweep" %{ KEY(hsweep); } |
419
+ "limit" %{ KEY(limit); } |
420
+ "lpf" %{ KEY(lpf); } |
421
+ "lsweep" %{ KEY(lsweep); } |
422
+ "phase" %{ KEY(phase); } |
423
+ "psweep" %{ KEY(psweep); } |
424
+ "repeat" %{ KEY(repeat); } |
425
+ "resonance" %{ KEY(resonance); } |
426
+ "slide" %{ KEY(slide); } |
427
+ "square" %{ KEY(square); } |
428
+ "sustain" %{ KEY(sustain); } |
429
+ "sweep" %{ KEY(sweep); } |
430
+ "punch" %{ KEY(punch); } |
431
+ "vibe" %{ KEY(vibe); } |
432
+ "vspeed" %{ KEY(vspeed); } |
433
+ "vdelay" %{ KEY(vdelay); } |
434
+ "volume" %{ KEY(volume); };
435
+
436
+ main := |*
437
+ key space+ float space* => { *((float *)key) = fval; };
438
+ "type" space+ "square" => { P->params.type = BLOOPS_SQUARE; };
439
+ "type" space+ "sawtooth" => { P->params.type = BLOOPS_SAWTOOTH; };
440
+ "type" space+ "sine" => { P->params.type = BLOOPS_SINE; };
441
+ "type" space+ "noise" => { P->params.type = BLOOPS_NOISE; };
442
+ space+;
443
+ *|;
444
+
445
+ write data nofinal;
446
+ }%%
447
+
448
+ bloopsaphone *
449
+ bloops_sound_file(bloops *B, char *fname)
450
+ {
451
+ FILE *fp;
452
+ struct stat stats;
453
+ int cs, act, len;
454
+ float fval;
455
+ void *key;
456
+ char *str, *p, *pe, *pf, *ts, *te, *eof = 0;
457
+ bloopsaphone *P;
458
+
459
+ if (stat(fname, &stats) == -1)
460
+ return NULL;
461
+
462
+ fp = fopen(fname, "rb");
463
+ if (!fp)
464
+ return NULL;
465
+
466
+ len = stats.st_size;
467
+ str = (char *)malloc(stats.st_size + 1);
468
+ if (fread(str, 1, stats.st_size, fp) != stats.st_size)
469
+ goto done;
470
+
471
+ p = str;
472
+ pe = str + len + 1;
473
+ p[len] = '\0';
474
+
475
+ P = bloops_square();
476
+ %% write init;
477
+ %% write exec;
478
+
479
+ done:
480
+ fclose(fp);
481
+ return P;
482
+ }
483
+
484
+ char *
485
+ bloops_sound_str(bloopsaphone *P)
486
+ {
487
+ char *lines = (char *)malloc(4096), *str = lines;
488
+ bloopsaphone *sq = bloops_square();
489
+ if (P->params.type == BLOOPS_SQUARE)
490
+ str += sprintf(str, "type square\n");
491
+ else if (P->params.type == BLOOPS_SAWTOOTH)
492
+ str += sprintf(str, "type sawtooth\n");
493
+ else if (P->params.type == BLOOPS_SINE)
494
+ str += sprintf(str, "type sine\n");
495
+ else if (P->params.type == BLOOPS_NOISE)
496
+ str += sprintf(str, "type noise\n");
497
+
498
+ if (P->params.volume != sq->params.volume)
499
+ str += sprintf(str, "volume %0.3f\n", P->params.volume);
500
+ if (P->params.punch != sq->params.punch)
501
+ str += sprintf(str, "punch %0.3f\n", P->params.punch);
502
+ if (P->params.attack != sq->params.attack)
503
+ str += sprintf(str, "attack %0.3f\n", P->params.attack);
504
+ if (P->params.sustain != sq->params.sustain)
505
+ str += sprintf(str, "sustain %0.3f\n", P->params.sustain);
506
+ if (P->params.decay != sq->params.decay)
507
+ str += sprintf(str, "decay %0.3f\n", P->params.decay);
508
+ if (P->params.freq != sq->params.freq)
509
+ str += sprintf(str, "freq %0.3f\n", P->params.freq);
510
+ if (P->params.limit != sq->params.limit)
511
+ str += sprintf(str, "limit %0.3f\n", P->params.limit);
512
+ if (P->params.slide != sq->params.slide)
513
+ str += sprintf(str, "slide %0.3f\n", P->params.slide);
514
+ if (P->params.dslide != sq->params.dslide)
515
+ str += sprintf(str, "dslide %0.3f\n", P->params.dslide);
516
+ if (P->params.square != sq->params.square)
517
+ str += sprintf(str, "square %0.3f\n", P->params.square);
518
+ if (P->params.sweep != sq->params.sweep)
519
+ str += sprintf(str, "sweep %0.3f\n", P->params.sweep);
520
+ if (P->params.vibe != sq->params.vibe)
521
+ str += sprintf(str, "vibe %0.3f\n", P->params.vibe);
522
+ if (P->params.vspeed != sq->params.vspeed)
523
+ str += sprintf(str, "vspeed %0.3f\n", P->params.vspeed);
524
+ if (P->params.vdelay != sq->params.vdelay)
525
+ str += sprintf(str, "vdelay %0.3f\n", P->params.vdelay);
526
+ if (P->params.lpf != sq->params.lpf)
527
+ str += sprintf(str, "lpf %0.3f\n", P->params.lpf);
528
+ if (P->params.lsweep != sq->params.lsweep)
529
+ str += sprintf(str, "lsweep %0.3f\n", P->params.lsweep);
530
+ if (P->params.resonance != sq->params.resonance)
531
+ str += sprintf(str, "resonance %0.3f\n", P->params.resonance);
532
+ if (P->params.hpf != sq->params.hpf)
533
+ str += sprintf(str, "hpf %0.3f\n", P->params.hpf);
534
+ if (P->params.hsweep != sq->params.hsweep)
535
+ str += sprintf(str, "hsweep %0.3f\n", P->params.hsweep);
536
+ if (P->params.arp != sq->params.arp)
537
+ str += sprintf(str, "arp %0.3f\n", P->params.arp);
538
+ if (P->params.aspeed != sq->params.aspeed)
539
+ str += sprintf(str, "aspeed %0.3f\n", P->params.aspeed);
540
+ if (P->params.phase != sq->params.phase)
541
+ str += sprintf(str, "phase %0.3f\n", P->params.phase);
542
+ if (P->params.psweep != sq->params.psweep)
543
+ str += sprintf(str, "psweep %0.3f\n", P->params.psweep);
544
+ if (P->params.repeat != sq->params.repeat)
545
+ str += sprintf(str, "repeat %0.3f\n", P->params.repeat);
546
+
547
+ bloops_sound_destroy(sq);
548
+ return lines;
549
+ }
@@ -0,0 +1,15 @@
1
+ require 'mkmf'
2
+ require 'fileutils'
3
+
4
+ $CFLAGS << " -I../../c #{ENV['CFLAGS']}"
5
+ $LIBS << " -lm -lportaudio #{ENV['LDFLAGS']}"
6
+
7
+ %w[notation.c bloopsaphone-internal.h bloopsaphone.c bloopsaphone.h].each do |fn|
8
+ fn = "../../c/#{fn}"
9
+ abort "!! ERROR !!\n** #{fn} not found; type 'make ruby' in the top directory\n\n" \
10
+ unless File.exist? fn
11
+ FileUtils.cp(fn, ".")
12
+ end
13
+
14
+ have_library("portaudio")
15
+ create_makefile("bloops")