AtlatestRepositorysigil-dsp

sigil-dsp / tree / src / cdsp.c

1/*
2 * dsp.c - Sigil DSP Module
3 *
4 * Native bindings for SoundPipe DSP primitives.
5 * Provides oscillators, filters, envelopes, effects, noise generators,
6 * and utility modules for audio synthesis.
7 */
8
9#include <sigil/sigil.h>
11#include <stdlib.h>
12#include <string.h>
13#include <math.h>
15#include "soundpipe.h"
16#include "ks.h"
18/* ============================================================
19 * GLOBAL STATE
20 * ============================================================ */
22static sp_data *g_sp = NULL;
24/* Type tags for foreign objects */
25static Value tt_osc = SIGIL_UNDEFINED;
26static Value tt_posc3 = SIGIL_UNDEFINED;
27static Value tt_blsaw = SIGIL_UNDEFINED;
28static Value tt_blsquare = SIGIL_UNDEFINED;
29static Value tt_bltriangle = SIGIL_UNDEFINED;
30static Value tt_fosc = SIGIL_UNDEFINED;
31static Value tt_phasor = SIGIL_UNDEFINED;
32static Value tt_moogladder = SIGIL_UNDEFINED;
33static Value tt_butlp = SIGIL_UNDEFINED;
34static Value tt_buthp = SIGIL_UNDEFINED;
35static Value tt_lpf18 = SIGIL_UNDEFINED;
36static Value tt_tone = SIGIL_UNDEFINED;
37static Value tt_atone = SIGIL_UNDEFINED;
38static Value tt_dcblock = SIGIL_UNDEFINED;
39static Value tt_adsr = SIGIL_UNDEFINED;
40static Value tt_tadsr = SIGIL_UNDEFINED;
41static Value tt_tenv = SIGIL_UNDEFINED;
42static Value tt_line = SIGIL_UNDEFINED;
43static Value tt_expon = SIGIL_UNDEFINED;
44static Value tt_port = SIGIL_UNDEFINED;
45static Value tt_revsc = SIGIL_UNDEFINED;
46static Value tt_zitarev = SIGIL_UNDEFINED;
47static Value tt_delay = SIGIL_UNDEFINED;
48static Value tt_vdelay = SIGIL_UNDEFINED;
49static Value tt_noise = SIGIL_UNDEFINED;
50static Value tt_pinknoise = SIGIL_UNDEFINED;
51static Value tt_brown = SIGIL_UNDEFINED;
52static Value tt_saturator = SIGIL_UNDEFINED;
53static Value tt_clip = SIGIL_UNDEFINED;
54static Value tt_peaklim = SIGIL_UNDEFINED;
55static Value tt_pan2 = SIGIL_UNDEFINED;
56static Value tt_metro = SIGIL_UNDEFINED;
57static Value tt_clock = SIGIL_UNDEFINED;
58static Value tt_tgate = SIGIL_UNDEFINED;
59static Value tt_randh = SIGIL_UNDEFINED;
60static Value tt_randi = SIGIL_UNDEFINED;
61static Value tt_scale = SIGIL_UNDEFINED;
62static Value tt_clamp = SIGIL_UNDEFINED;
63static Value tt_crossfade = SIGIL_UNDEFINED;
64static Value tt_wavout = SIGIL_UNDEFINED;
65static Value tt_ftbl = SIGIL_UNDEFINED;
66static Value tt_ks = SIGIL_UNDEFINED;
68/* ============================================================
69 * HELPERS
70 * ============================================================ */
72#define ENSURE_TAG(vm, tag, name) \
73 if (sigil_is_undefined(tag)) { \
74 tag = sigil_intern_symbol(vm, name, strlen(name)); \
75 }
77#define CHECK_SP(vm, funcname) \
78 if (!g_sp) { \
79 sigil__vm_set_error(vm, SIGIL_ERR_RUNTIME, funcname ": DSP not initialized, call dsp-init first"); \
80 return SIGIL_FALSE; \
81 }
83/* Wrapper struct for wavetable oscillator (owns ftbl) */
84typedef struct {
85 sp_osc *osc;
86 sp_ftbl *ft;
87} dsp_osc;
89/* Wrapper struct for posc3 (owns ftbl) */
90typedef struct {
91 sp_posc3 *osc;
92 sp_ftbl *ft;
93} dsp_posc3;
95/* Wrapper struct for fosc (owns ftbl) */
96typedef struct {
97 sp_fosc *osc;
98 sp_ftbl *ft;
99} dsp_fosc;
101/* ============================================================
102 * DESTRUCTORS
103 * ============================================================ */
105static void destroy_osc(void *data) {
106 dsp_osc *d = (dsp_osc *)data;
107 if (d->osc) sp_osc_destroy(&d->osc);
108 if (d->ft) sp_ftbl_destroy(&d->ft);
109 free(d);
112static void destroy_posc3(void *data) {
113 dsp_posc3 *d = (dsp_posc3 *)data;
114 if (d->osc) sp_posc3_destroy(&d->osc);
115 if (d->ft) sp_ftbl_destroy(&d->ft);
116 free(d);
119static void destroy_blsaw(void *data) { sp_blsaw *p = (sp_blsaw *)data; sp_blsaw_destroy(&p); }
120static void destroy_blsquare(void *data) { sp_blsquare *p = (sp_blsquare *)data; sp_blsquare_destroy(&p); }
121static void destroy_bltriangle(void *data) { sp_bltriangle *p = (sp_bltriangle *)data; sp_bltriangle_destroy(&p); }
123static void destroy_fosc(void *data) {
124 dsp_fosc *d = (dsp_fosc *)data;
125 if (d->osc) sp_fosc_destroy(&d->osc);
126 if (d->ft) sp_ftbl_destroy(&d->ft);
127 free(d);
130static void destroy_phasor(void *data) { sp_phasor *p = (sp_phasor *)data; sp_phasor_destroy(&p); }
131static void destroy_moogladder(void *data) { sp_moogladder *p = (sp_moogladder *)data; sp_moogladder_destroy(&p); }
132static void destroy_butlp(void *data) { sp_butlp *p = (sp_butlp *)data; sp_butlp_destroy(&p); }
133static void destroy_buthp(void *data) { sp_buthp *p = (sp_buthp *)data; sp_buthp_destroy(&p); }
134static void destroy_lpf18(void *data) { sp_lpf18 *p = (sp_lpf18 *)data; sp_lpf18_destroy(&p); }
135static void destroy_tone(void *data) { sp_tone *p = (sp_tone *)data; sp_tone_destroy(&p); }
136static void destroy_atone(void *data) { sp_atone *p = (sp_atone *)data; sp_atone_destroy(&p); }
137static void destroy_dcblock(void *data) { sp_dcblock *p = (sp_dcblock *)data; sp_dcblock_destroy(&p); }
138static void destroy_adsr(void *data) { sp_adsr *p = (sp_adsr *)data; sp_adsr_destroy(&p); }
139static void destroy_tadsr(void *data) { sp_tadsr *p = (sp_tadsr *)data; sp_tadsr_destroy(&p); }
140static void destroy_tenv(void *data) { sp_tenv *p = (sp_tenv *)data; sp_tenv_destroy(&p); }
141static void destroy_line(void *data) { sp_line *p = (sp_line *)data; sp_line_destroy(&p); }
142static void destroy_expon(void *data) { sp_expon *p = (sp_expon *)data; sp_expon_destroy(&p); }
143static void destroy_port(void *data) { sp_port *p = (sp_port *)data; sp_port_destroy(&p); }
144static void destroy_revsc(void *data) { sp_revsc *p = (sp_revsc *)data; sp_revsc_destroy(&p); }
145static void destroy_zitarev(void *data) { sp_zitarev *p = (sp_zitarev *)data; sp_zitarev_destroy(&p); }
146static void destroy_delay(void *data) { sp_delay *p = (sp_delay *)data; sp_delay_destroy(&p); }
147static void destroy_vdelay(void *data) { sp_vdelay *p = (sp_vdelay *)data; sp_vdelay_destroy(&p); }
148static void destroy_noise(void *data) { sp_noise *p = (sp_noise *)data; sp_noise_destroy(&p); }
149static void destroy_pinknoise(void *data) { sp_pinknoise *p = (sp_pinknoise *)data; sp_pinknoise_destroy(&p); }
150static void destroy_brown(void *data) { sp_brown *p = (sp_brown *)data; sp_brown_destroy(&p); }
151static void destroy_saturator(void *data) { sp_saturator *p = (sp_saturator *)data; sp_saturator_destroy(&p); }
152static void destroy_clip(void *data) { sp_clip *p = (sp_clip *)data; sp_clip_destroy(&p); }
153static void destroy_peaklim(void *data) { sp_peaklim *p = (sp_peaklim *)data; sp_peaklim_destroy(&p); }
154static void destroy_pan2(void *data) { sp_pan2 *p = (sp_pan2 *)data; sp_pan2_destroy(&p); }
155static void destroy_metro(void *data) { sp_metro *p = (sp_metro *)data; sp_metro_destroy(&p); }
156static void destroy_clock(void *data) { sp_clock *p = (sp_clock *)data; sp_clock_destroy(&p); }
157static void destroy_tgate(void *data) { sp_tgate *p = (sp_tgate *)data; sp_tgate_destroy(&p); }
158static void destroy_randh(void *data) { sp_randh *p = (sp_randh *)data; sp_randh_destroy(&p); }
159static void destroy_randi(void *data) { sp_randi *p = (sp_randi *)data; sp_randi_destroy(&p); }
160static void destroy_scale(void *data) { sp_scale *p = (sp_scale *)data; sp_scale_destroy(&p); }
161static void destroy_clamp(void *data) { sp_clamp *p = (sp_clamp *)data; sp_clamp_destroy(&p); }
162static void destroy_crossfade(void *data) { sp_crossfade *p = (sp_crossfade *)data; sp_crossfade_destroy(&p); }
163static void destroy_wavout(void *data) { sp_wavout *p = (sp_wavout *)data; sp_wavout_destroy(&p); }
164static void destroy_ftbl(void *data) { sp_ftbl *p = (sp_ftbl *)data; sp_ftbl_destroy(&p); }
165static void destroy_ks(void *data) { sp_ks *p = (sp_ks *)data; sp_ks_destroy(&p); }
167/* ============================================================
168 * TYPE GETTERS
169 * ============================================================ */
171#define DEF_GETTER(name, sp_type, tag, label) \
172 static sp_type *get_##name(SigilVM *vm, Value v) { \
173 ENSURE_TAG(vm, tag, "dsp-" label); \
174 if (!sigil_is_foreign_type(v, tag)) return NULL; \
175 return (sp_type *)sigil_foreign_get_data(v); \
176 }
178/* Wrapper getters return the wrapper struct */
179static dsp_osc *get_osc(SigilVM *vm, Value v) {
180 ENSURE_TAG(vm, tt_osc, "dsp-osc");
181 if (!sigil_is_foreign_type(v, tt_osc)) return NULL;
182 return (dsp_osc *)sigil_foreign_get_data(v);
184static dsp_posc3 *get_posc3(SigilVM *vm, Value v) {
185 ENSURE_TAG(vm, tt_posc3, "dsp-posc3");
186 if (!sigil_is_foreign_type(v, tt_posc3)) return NULL;
187 return (dsp_posc3 *)sigil_foreign_get_data(v);
189static dsp_fosc *get_fosc(SigilVM *vm, Value v) {
190 ENSURE_TAG(vm, tt_fosc, "dsp-fosc");
191 if (!sigil_is_foreign_type(v, tt_fosc)) return NULL;
192 return (dsp_fosc *)sigil_foreign_get_data(v);
195DEF_GETTER(blsaw, sp_blsaw, tt_blsaw, "blsaw")
196DEF_GETTER(blsquare, sp_blsquare, tt_blsquare, "blsquare")
197DEF_GETTER(bltriangle, sp_bltriangle, tt_bltriangle, "bltriangle")
198DEF_GETTER(phasor, sp_phasor, tt_phasor, "phasor")
199DEF_GETTER(moogladder, sp_moogladder, tt_moogladder, "moogladder")
200DEF_GETTER(butlp, sp_butlp, tt_butlp, "butlp")
201DEF_GETTER(buthp, sp_buthp, tt_buthp, "buthp")
202DEF_GETTER(lpf18, sp_lpf18, tt_lpf18, "lpf18")
203DEF_GETTER(tone, sp_tone, tt_tone, "tone")
204DEF_GETTER(atone, sp_atone, tt_atone, "atone")
205DEF_GETTER(dcblock, sp_dcblock, tt_dcblock, "dcblock")
206DEF_GETTER(adsr, sp_adsr, tt_adsr, "adsr")
207DEF_GETTER(tadsr, sp_tadsr, tt_tadsr, "tadsr")
208DEF_GETTER(tenv, sp_tenv, tt_tenv, "tenv")
209DEF_GETTER(line, sp_line, tt_line, "line")
210DEF_GETTER(expon, sp_expon, tt_expon, "expon")
211DEF_GETTER(port, sp_port, tt_port, "port")
212DEF_GETTER(revsc, sp_revsc, tt_revsc, "revsc")
213DEF_GETTER(zitarev, sp_zitarev, tt_zitarev, "zitarev")
214DEF_GETTER(delay, sp_delay, tt_delay, "delay")
215DEF_GETTER(vdelay, sp_vdelay, tt_vdelay, "vdelay")
216DEF_GETTER(noise, sp_noise, tt_noise, "noise")
217DEF_GETTER(pinknoise, sp_pinknoise, tt_pinknoise, "pinknoise")
218DEF_GETTER(brown, sp_brown, tt_brown, "brown")
219DEF_GETTER(saturator, sp_saturator, tt_saturator, "saturator")
220DEF_GETTER(clip, sp_clip, tt_clip, "clip")
221DEF_GETTER(peaklim, sp_peaklim, tt_peaklim, "peaklim")
222DEF_GETTER(pan2, sp_pan2, tt_pan2, "pan2")
223DEF_GETTER(metro, sp_metro, tt_metro, "metro")
224DEF_GETTER(clock_mod, sp_clock, tt_clock, "clock")
225DEF_GETTER(tgate, sp_tgate, tt_tgate, "tgate")
226DEF_GETTER(randh, sp_randh, tt_randh, "randh")
227DEF_GETTER(randi, sp_randi, tt_randi, "randi")
228DEF_GETTER(scale, sp_scale, tt_scale, "scale")
229DEF_GETTER(clamp, sp_clamp, tt_clamp, "clamp")
230DEF_GETTER(crossfade, sp_crossfade, tt_crossfade, "crossfade")
231DEF_GETTER(wavout, sp_wavout, tt_wavout, "wavout")
232DEF_GETTER(ks, sp_ks, tt_ks, "ks")
234/* ============================================================
235 * CORE — dsp-init, dsp-shutdown, dsp-sample-rate
236 * ============================================================ */
238/* (dsp-init [sample-rate]) */
239/* Helper to get a number as double from either fixnum or flonum */
240static double as_number(Value v)
242 if (sigil_is_fixnum(v)) return (double)sigil_as_fixnum(v);
243 return sigil_as_flonum(v);
246static Value native_dsp_init(SigilVM *vm, int argc, Value *args)
248 (void)vm;
249 if (g_sp) return SIGIL_TRUE;
251 sp_create(&g_sp);
252 if (argc > 0) {
253 g_sp->sr = (int)as_number(args[0]);
254 }
255 return SIGIL_TRUE;
258/* (dsp-shutdown) */
259static Value native_dsp_shutdown(SigilVM *vm, int argc, Value *args)
261 (void)vm; (void)argc; (void)args;
262 if (g_sp) {
263 sp_destroy(&g_sp);
264 g_sp = NULL;
265 }
266 return SIGIL_NIL;
269/* (dsp-sample-rate) -> fixnum */
270static Value native_dsp_sample_rate(SigilVM *vm, int argc, Value *args)
272 (void)argc; (void)args;
273 CHECK_SP(vm, "dsp-sample-rate");
274 return sigil_fixnum(g_sp->sr);
277/* ============================================================
278 * FUNCTION TABLES
279 * ============================================================ */
281/* (make-ftbl size) -> <ftbl> */
282static Value native_make_ftbl(SigilVM *vm, int argc, Value *args)
284 CHECK_SP(vm, "make-ftbl");
285 size_t size = (size_t)sigil_as_flonum(args[0]);
286 sp_ftbl *ft;
287 sp_ftbl_create(g_sp, &ft, size);
288 ENSURE_TAG(vm, tt_ftbl, "dsp-ftbl");
289 return sigil_make_foreign(vm, tt_ftbl, ft, destroy_ftbl,
290 sizeof(sp_ftbl) + size * sizeof(SPFLOAT));
293/* (ftbl-gen-sine ftbl) */
294static Value native_ftbl_gen_sine(SigilVM *vm, int argc, Value *args)
296 CHECK_SP(vm, "ftbl-gen-sine");
297 ENSURE_TAG(vm, tt_ftbl, "dsp-ftbl");
298 if (!sigil_is_foreign_type(args[0], tt_ftbl)) {
299 sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "ftbl-gen-sine: expected ftbl");
300 return SIGIL_FALSE;
301 }
302 sp_ftbl *ft = (sp_ftbl *)sigil_foreign_get_data(args[0]);
303 sp_gen_sine(g_sp, ft);
304 return SIGIL_NIL;
307/* (ftbl-gen-triangle ftbl) */
308static Value native_ftbl_gen_triangle(SigilVM *vm, int argc, Value *args)
310 CHECK_SP(vm, "ftbl-gen-triangle");
311 ENSURE_TAG(vm, tt_ftbl, "dsp-ftbl");
312 if (!sigil_is_foreign_type(args[0], tt_ftbl)) {
313 sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "ftbl-gen-triangle: expected ftbl");
314 return SIGIL_FALSE;
315 }
316 sp_ftbl *ft = (sp_ftbl *)sigil_foreign_get_data(args[0]);
317 sp_gen_triangle(g_sp, ft);
318 return SIGIL_NIL;
321/* (ftbl-gen-line ftbl argstring) */
322static Value native_ftbl_gen_line(SigilVM *vm, int argc, Value *args)
324 CHECK_SP(vm, "ftbl-gen-line");
325 ENSURE_TAG(vm, tt_ftbl, "dsp-ftbl");
326 if (!sigil_is_foreign_type(args[0], tt_ftbl)) {
327 sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "ftbl-gen-line: expected ftbl");
328 return SIGIL_FALSE;
329 }
330 if (!sigil_is_string(args[1])) {
331 sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "ftbl-gen-line: expected string");
332 return SIGIL_FALSE;
333 }
334 sp_ftbl *ft = (sp_ftbl *)sigil_foreign_get_data(args[0]);
335 SigilString *s = (SigilString *)sigil_as_ptr(args[1]);
336 sp_gen_line(g_sp, ft, s->data);
337 return SIGIL_NIL;
340/* ============================================================
341 * OSCILLATORS
342 * ============================================================ */
344/* (make-osc freq amp [phase]) -> <osc>
345 * Wavetable oscillator with sine wave table */
346static Value native_make_osc(SigilVM *vm, int argc, Value *args)
348 CHECK_SP(vm, "make-osc");
349 SPFLOAT freq = (SPFLOAT)sigil_as_flonum(args[0]);
350 SPFLOAT amp = (SPFLOAT)sigil_as_flonum(args[1]);
351 SPFLOAT phase = argc > 2 ? (SPFLOAT)sigil_as_flonum(args[2]) : 0.0f;
353 sp_ftbl *ft;
354 sp_ftbl_create(g_sp, &ft, 4096);
355 sp_gen_sine(g_sp, ft);
357 sp_osc *osc;
358 sp_osc_create(&osc);
359 sp_osc_init(g_sp, osc, ft, phase);
360 osc->freq = freq;
361 osc->amp = amp;
363 dsp_osc *d = malloc(sizeof(dsp_osc));
364 d->osc = osc;
365 d->ft = ft;
367 ENSURE_TAG(vm, tt_osc, "dsp-osc");
368 return sigil_make_foreign(vm, tt_osc, d, destroy_osc,
369 sizeof(dsp_osc) + sizeof(sp_osc) + 4096 * sizeof(SPFLOAT));
372/* (make-osc-ft ftbl freq amp [phase]) -> <osc>
373 * Wavetable oscillator with custom function table */
374static Value native_make_osc_ft(SigilVM *vm, int argc, Value *args)
376 CHECK_SP(vm, "make-osc-ft");
377 ENSURE_TAG(vm, tt_ftbl, "dsp-ftbl");
378 if (!sigil_is_foreign_type(args[0], tt_ftbl)) {
379 sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "make-osc-ft: expected ftbl as first argument");
380 return SIGIL_FALSE;
381 }
382 /* Note: the ftbl is NOT owned by this osc — caller manages its lifetime */
383 sp_ftbl *ft = (sp_ftbl *)sigil_foreign_get_data(args[0]);
384 SPFLOAT freq = (SPFLOAT)sigil_as_flonum(args[1]);
385 SPFLOAT amp = (SPFLOAT)sigil_as_flonum(args[2]);
386 SPFLOAT phase = argc > 3 ? (SPFLOAT)sigil_as_flonum(args[3]) : 0.0f;
388 sp_osc *osc;
389 sp_osc_create(&osc);
390 sp_osc_init(g_sp, osc, ft, phase);
391 osc->freq = freq;
392 osc->amp = amp;
394 dsp_osc *d = malloc(sizeof(dsp_osc));
395 d->osc = osc;
396 d->ft = NULL; /* not owned */
398 ENSURE_TAG(vm, tt_osc, "dsp-osc");
399 return sigil_make_foreign(vm, tt_osc, d, destroy_osc,
400 sizeof(dsp_osc) + sizeof(sp_osc));
403/* (osc-compute osc) -> flonum */
404static Value native_osc_compute(SigilVM *vm, int argc, Value *args)
406 dsp_osc *d = get_osc(vm, args[0]);
407 if (!d) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "osc-compute: expected osc"); return SIGIL_FALSE; }
408 SPFLOAT in = 0, out = 0;
409 sp_osc_compute(g_sp, d->osc, &in, &out);
410 return sigil_flonum((double)out);
413/* (osc-set-freq! osc freq) */
414static Value native_osc_set_freq(SigilVM *vm, int argc, Value *args)
416 dsp_osc *d = get_osc(vm, args[0]);
417 if (!d) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "osc-set-freq!: expected osc"); return SIGIL_FALSE; }
418 d->osc->freq = (SPFLOAT)sigil_as_flonum(args[1]);
419 return SIGIL_NIL;
422/* (osc-set-amp! osc amp) */
423static Value native_osc_set_amp(SigilVM *vm, int argc, Value *args)
425 dsp_osc *d = get_osc(vm, args[0]);
426 if (!d) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "osc-set-amp!: expected osc"); return SIGIL_FALSE; }
427 d->osc->amp = (SPFLOAT)sigil_as_flonum(args[1]);
428 return SIGIL_NIL;
431/* (make-posc3 freq amp [phase]) -> <posc3> */
432static Value native_make_posc3(SigilVM *vm, int argc, Value *args)
434 CHECK_SP(vm, "make-posc3");
435 SPFLOAT freq = (SPFLOAT)sigil_as_flonum(args[0]);
436 SPFLOAT amp = (SPFLOAT)sigil_as_flonum(args[1]);
438 sp_ftbl *ft;
439 sp_ftbl_create(g_sp, &ft, 4096);
440 sp_gen_sine(g_sp, ft);
442 sp_posc3 *osc;
443 sp_posc3_create(&osc);
444 sp_posc3_init(g_sp, osc, ft);
445 osc->freq = freq;
446 osc->amp = amp;
448 dsp_posc3 *d = malloc(sizeof(dsp_posc3));
449 d->osc = osc;
450 d->ft = ft;
452 ENSURE_TAG(vm, tt_posc3, "dsp-posc3");
453 return sigil_make_foreign(vm, tt_posc3, d, destroy_posc3,
454 sizeof(dsp_posc3) + sizeof(sp_posc3) + 4096 * sizeof(SPFLOAT));
457/* (posc3-compute osc) -> flonum */
458static Value native_posc3_compute(SigilVM *vm, int argc, Value *args)
460 dsp_posc3 *d = get_posc3(vm, args[0]);
461 if (!d) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "posc3-compute: expected posc3"); return SIGIL_FALSE; }
462 SPFLOAT in = 0, out = 0;
463 sp_posc3_compute(g_sp, d->osc, &in, &out);
464 return sigil_flonum((double)out);
467/* (posc3-set-freq! osc freq) */
468static Value native_posc3_set_freq(SigilVM *vm, int argc, Value *args)
470 dsp_posc3 *d = get_posc3(vm, args[0]);
471 if (!d) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "posc3-set-freq!: expected posc3"); return SIGIL_FALSE; }
472 d->osc->freq = (SPFLOAT)sigil_as_flonum(args[1]);
473 return SIGIL_NIL;
476/* (posc3-set-amp! osc amp) */
477static Value native_posc3_set_amp(SigilVM *vm, int argc, Value *args)
479 dsp_posc3 *d = get_posc3(vm, args[0]);
480 if (!d) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "posc3-set-amp!: expected posc3"); return SIGIL_FALSE; }
481 d->osc->amp = (SPFLOAT)sigil_as_flonum(args[1]);
482 return SIGIL_NIL;
485/* (make-blsaw freq amp) -> <blsaw> */
486static Value native_make_blsaw(SigilVM *vm, int argc, Value *args)
488 CHECK_SP(vm, "make-blsaw");
489 SPFLOAT freq = (SPFLOAT)sigil_as_flonum(args[0]);
490 SPFLOAT amp = (SPFLOAT)sigil_as_flonum(args[1]);
492 sp_blsaw *p;
493 sp_blsaw_create(&p);
494 sp_blsaw_init(g_sp, p);
495 *p->freq = freq;
496 *p->amp = amp;
498 ENSURE_TAG(vm, tt_blsaw, "dsp-blsaw");
499 return sigil_make_foreign(vm, tt_blsaw, p, destroy_blsaw, sizeof(sp_blsaw));
502/* (blsaw-compute osc) -> flonum */
503static Value native_blsaw_compute(SigilVM *vm, int argc, Value *args)
505 sp_blsaw *p = get_blsaw(vm, args[0]);
506 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "blsaw-compute: expected blsaw"); return SIGIL_FALSE; }
507 SPFLOAT in = 0, out = 0;
508 sp_blsaw_compute(g_sp, p, &in, &out);
509 return sigil_flonum((double)out);
512/* (blsaw-set-freq! osc freq) */
513static Value native_blsaw_set_freq(SigilVM *vm, int argc, Value *args)
515 sp_blsaw *p = get_blsaw(vm, args[0]);
516 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "blsaw-set-freq!: expected blsaw"); return SIGIL_FALSE; }
517 *p->freq = (SPFLOAT)sigil_as_flonum(args[1]);
518 return SIGIL_NIL;
521/* (blsaw-set-amp! osc amp) */
522static Value native_blsaw_set_amp(SigilVM *vm, int argc, Value *args)
524 sp_blsaw *p = get_blsaw(vm, args[0]);
525 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "blsaw-set-amp!: expected blsaw"); return SIGIL_FALSE; }
526 *p->amp = (SPFLOAT)sigil_as_flonum(args[1]);
527 return SIGIL_NIL;
530/* (make-blsquare freq amp [width]) -> <blsquare> */
531static Value native_make_blsquare(SigilVM *vm, int argc, Value *args)
533 CHECK_SP(vm, "make-blsquare");
534 SPFLOAT freq = (SPFLOAT)sigil_as_flonum(args[0]);
535 SPFLOAT amp = (SPFLOAT)sigil_as_flonum(args[1]);
536 SPFLOAT width = argc > 2 ? (SPFLOAT)sigil_as_flonum(args[2]) : 0.5f;
538 sp_blsquare *p;
539 sp_blsquare_create(&p);
540 sp_blsquare_init(g_sp, p);
541 *p->freq = freq;
542 *p->amp = amp;
543 *p->width = width;
545 ENSURE_TAG(vm, tt_blsquare, "dsp-blsquare");
546 return sigil_make_foreign(vm, tt_blsquare, p, destroy_blsquare, sizeof(sp_blsquare));
549/* (blsquare-compute osc) -> flonum */
550static Value native_blsquare_compute(SigilVM *vm, int argc, Value *args)
552 sp_blsquare *p = get_blsquare(vm, args[0]);
553 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "blsquare-compute: expected blsquare"); return SIGIL_FALSE; }
554 SPFLOAT in = 0, out = 0;
555 sp_blsquare_compute(g_sp, p, &in, &out);
556 return sigil_flonum((double)out);
559/* (blsquare-set-freq! osc freq) */
560static Value native_blsquare_set_freq(SigilVM *vm, int argc, Value *args)
562 sp_blsquare *p = get_blsquare(vm, args[0]);
563 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "blsquare-set-freq!: expected blsquare"); return SIGIL_FALSE; }
564 *p->freq = (SPFLOAT)sigil_as_flonum(args[1]);
565 return SIGIL_NIL;
568/* (blsquare-set-amp! osc amp) */
569static Value native_blsquare_set_amp(SigilVM *vm, int argc, Value *args)
571 sp_blsquare *p = get_blsquare(vm, args[0]);
572 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "blsquare-set-amp!: expected blsquare"); return SIGIL_FALSE; }
573 *p->amp = (SPFLOAT)sigil_as_flonum(args[1]);
574 return SIGIL_NIL;
577/* (make-bltriangle freq amp) -> <bltriangle> */
578static Value native_make_bltriangle(SigilVM *vm, int argc, Value *args)
580 CHECK_SP(vm, "make-bltriangle");
581 SPFLOAT freq = (SPFLOAT)sigil_as_flonum(args[0]);
582 SPFLOAT amp = (SPFLOAT)sigil_as_flonum(args[1]);
584 sp_bltriangle *p;
585 sp_bltriangle_create(&p);
586 sp_bltriangle_init(g_sp, p);
587 *p->freq = freq;
588 *p->amp = amp;
590 ENSURE_TAG(vm, tt_bltriangle, "dsp-bltriangle");
591 return sigil_make_foreign(vm, tt_bltriangle, p, destroy_bltriangle, sizeof(sp_bltriangle));
594/* (bltriangle-compute osc) -> flonum */
595static Value native_bltriangle_compute(SigilVM *vm, int argc, Value *args)
597 sp_bltriangle *p = get_bltriangle(vm, args[0]);
598 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "bltriangle-compute: expected bltriangle"); return SIGIL_FALSE; }
599 SPFLOAT in = 0, out = 0;
600 sp_bltriangle_compute(g_sp, p, &in, &out);
601 return sigil_flonum((double)out);
604/* (bltriangle-set-freq! osc freq) */
605static Value native_bltriangle_set_freq(SigilVM *vm, int argc, Value *args)
607 sp_bltriangle *p = get_bltriangle(vm, args[0]);
608 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "bltriangle-set-freq!: expected bltriangle"); return SIGIL_FALSE; }
609 *p->freq = (SPFLOAT)sigil_as_flonum(args[1]);
610 return SIGIL_NIL;
613/* (bltriangle-set-amp! osc amp) */
614static Value native_bltriangle_set_amp(SigilVM *vm, int argc, Value *args)
616 sp_bltriangle *p = get_bltriangle(vm, args[0]);
617 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "bltriangle-set-amp!: expected bltriangle"); return SIGIL_FALSE; }
618 *p->amp = (SPFLOAT)sigil_as_flonum(args[1]);
619 return SIGIL_NIL;
622/* (make-fosc freq car mod indx amp) -> <fosc> */
623static Value native_make_fosc(SigilVM *vm, int argc, Value *args)
625 CHECK_SP(vm, "make-fosc");
626 SPFLOAT freq = (SPFLOAT)sigil_as_flonum(args[0]);
627 SPFLOAT car = (SPFLOAT)sigil_as_flonum(args[1]);
628 SPFLOAT mod = (SPFLOAT)sigil_as_flonum(args[2]);
629 SPFLOAT indx = (SPFLOAT)sigil_as_flonum(args[3]);
630 SPFLOAT amp = (SPFLOAT)sigil_as_flonum(args[4]);
632 sp_ftbl *ft;
633 sp_ftbl_create(g_sp, &ft, 4096);
634 sp_gen_sine(g_sp, ft);
636 sp_fosc *osc;
637 sp_fosc_create(&osc);
638 sp_fosc_init(g_sp, osc, ft);
639 osc->freq = freq;
640 osc->car = car;
641 osc->mod = mod;
642 osc->indx = indx;
643 osc->amp = amp;
645 dsp_fosc *d = malloc(sizeof(dsp_fosc));
646 d->osc = osc;
647 d->ft = ft;
649 ENSURE_TAG(vm, tt_fosc, "dsp-fosc");
650 return sigil_make_foreign(vm, tt_fosc, d, destroy_fosc,
651 sizeof(dsp_fosc) + sizeof(sp_fosc) + 4096 * sizeof(SPFLOAT));
654/* (fosc-compute osc) -> flonum */
655static Value native_fosc_compute(SigilVM *vm, int argc, Value *args)
657 dsp_fosc *d = get_fosc(vm, args[0]);
658 if (!d) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "fosc-compute: expected fosc"); return SIGIL_FALSE; }
659 SPFLOAT in = 0, out = 0;
660 sp_fosc_compute(g_sp, d->osc, &in, &out);
661 return sigil_flonum((double)out);
664/* (fosc-set-freq! osc freq) */
665static Value native_fosc_set_freq(SigilVM *vm, int argc, Value *args)
667 dsp_fosc *d = get_fosc(vm, args[0]);
668 if (!d) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "fosc-set-freq!: expected fosc"); return SIGIL_FALSE; }
669 d->osc->freq = (SPFLOAT)sigil_as_flonum(args[1]);
670 return SIGIL_NIL;
673/* (fosc-set-index! osc indx) */
674static Value native_fosc_set_index(SigilVM *vm, int argc, Value *args)
676 dsp_fosc *d = get_fosc(vm, args[0]);
677 if (!d) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "fosc-set-index!: expected fosc"); return SIGIL_FALSE; }
678 d->osc->indx = (SPFLOAT)sigil_as_flonum(args[1]);
679 return SIGIL_NIL;
682/* (fosc-set-amp! osc amp) */
683static Value native_fosc_set_amp(SigilVM *vm, int argc, Value *args)
685 dsp_fosc *d = get_fosc(vm, args[0]);
686 if (!d) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "fosc-set-amp!: expected fosc"); return SIGIL_FALSE; }
687 d->osc->amp = (SPFLOAT)sigil_as_flonum(args[1]);
688 return SIGIL_NIL;
691/* (make-phasor freq [phase]) -> <phasor> */
692static Value native_make_phasor(SigilVM *vm, int argc, Value *args)
694 CHECK_SP(vm, "make-phasor");
695 SPFLOAT freq = (SPFLOAT)sigil_as_flonum(args[0]);
696 SPFLOAT phase = argc > 1 ? (SPFLOAT)sigil_as_flonum(args[1]) : 0.0f;
698 sp_phasor *p;
699 sp_phasor_create(&p);
700 sp_phasor_init(g_sp, p, phase);
701 p->freq = freq;
703 ENSURE_TAG(vm, tt_phasor, "dsp-phasor");
704 return sigil_make_foreign(vm, tt_phasor, p, destroy_phasor, sizeof(sp_phasor));
707/* (phasor-compute phasor) -> flonum */
708static Value native_phasor_compute(SigilVM *vm, int argc, Value *args)
710 sp_phasor *p = get_phasor(vm, args[0]);
711 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "phasor-compute: expected phasor"); return SIGIL_FALSE; }
712 SPFLOAT in = 0, out = 0;
713 sp_phasor_compute(g_sp, p, &in, &out);
714 return sigil_flonum((double)out);
717/* (phasor-set-freq! phasor freq) */
718static Value native_phasor_set_freq(SigilVM *vm, int argc, Value *args)
720 sp_phasor *p = get_phasor(vm, args[0]);
721 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "phasor-set-freq!: expected phasor"); return SIGIL_FALSE; }
722 p->freq = (SPFLOAT)sigil_as_flonum(args[1]);
723 return SIGIL_NIL;
726/* ============================================================
727 * FILTERS
728 * ============================================================ */
730/* (make-moogladder freq res) -> <moogladder> */
731static Value native_make_moogladder(SigilVM *vm, int argc, Value *args)
733 CHECK_SP(vm, "make-moogladder");
734 sp_moogladder *p;
735 sp_moogladder_create(&p);
736 sp_moogladder_init(g_sp, p);
737 p->freq = (SPFLOAT)sigil_as_flonum(args[0]);
738 p->res = (SPFLOAT)sigil_as_flonum(args[1]);
739 ENSURE_TAG(vm, tt_moogladder, "dsp-moogladder");
740 return sigil_make_foreign(vm, tt_moogladder, p, destroy_moogladder, sizeof(sp_moogladder));
743/* (make-butlp freq) -> <butlp> */
744static Value native_make_butlp(SigilVM *vm, int argc, Value *args)
746 CHECK_SP(vm, "make-butlp");
747 sp_butlp *p;
748 sp_butlp_create(&p);
749 sp_butlp_init(g_sp, p);
750 p->freq = (SPFLOAT)sigil_as_flonum(args[0]);
751 ENSURE_TAG(vm, tt_butlp, "dsp-butlp");
752 return sigil_make_foreign(vm, tt_butlp, p, destroy_butlp, sizeof(sp_butlp));
755/* (make-buthp freq) -> <buthp> */
756static Value native_make_buthp(SigilVM *vm, int argc, Value *args)
758 CHECK_SP(vm, "make-buthp");
759 sp_buthp *p;
760 sp_buthp_create(&p);
761 sp_buthp_init(g_sp, p);
762 p->freq = (SPFLOAT)sigil_as_flonum(args[0]);
763 ENSURE_TAG(vm, tt_buthp, "dsp-buthp");
764 return sigil_make_foreign(vm, tt_buthp, p, destroy_buthp, sizeof(sp_buthp));
767/* (make-lpf18 cutoff res dist) -> <lpf18> */
768static Value native_make_lpf18(SigilVM *vm, int argc, Value *args)
770 CHECK_SP(vm, "make-lpf18");
771 sp_lpf18 *p;
772 sp_lpf18_create(&p);
773 sp_lpf18_init(g_sp, p);
774 p->cutoff = (SPFLOAT)sigil_as_flonum(args[0]);
775 p->res = (SPFLOAT)sigil_as_flonum(args[1]);
776 p->dist = (SPFLOAT)sigil_as_flonum(args[2]);
777 ENSURE_TAG(vm, tt_lpf18, "dsp-lpf18");
778 return sigil_make_foreign(vm, tt_lpf18, p, destroy_lpf18, sizeof(sp_lpf18));
781/* (make-tone freq) -> <tone> */
782static Value native_make_tone(SigilVM *vm, int argc, Value *args)
784 CHECK_SP(vm, "make-tone");
785 sp_tone *p;
786 sp_tone_create(&p);
787 sp_tone_init(g_sp, p);
788 p->hp = (SPFLOAT)sigil_as_flonum(args[0]);
789 ENSURE_TAG(vm, tt_tone, "dsp-tone");
790 return sigil_make_foreign(vm, tt_tone, p, destroy_tone, sizeof(sp_tone));
793/* (make-atone freq) -> <atone> */
794static Value native_make_atone(SigilVM *vm, int argc, Value *args)
796 CHECK_SP(vm, "make-atone");
797 sp_atone *p;
798 sp_atone_create(&p);
799 sp_atone_init(g_sp, p);
800 p->hp = (SPFLOAT)sigil_as_flonum(args[0]);
801 ENSURE_TAG(vm, tt_atone, "dsp-atone");
802 return sigil_make_foreign(vm, tt_atone, p, destroy_atone, sizeof(sp_atone));
805/* (make-dcblock) -> <dcblock> */
806static Value native_make_dcblock(SigilVM *vm, int argc, Value *args)
808 CHECK_SP(vm, "make-dcblock");
809 sp_dcblock *p;
810 sp_dcblock_create(&p);
811 sp_dcblock_init(g_sp, p);
812 ENSURE_TAG(vm, tt_dcblock, "dsp-dcblock");
813 return sigil_make_foreign(vm, tt_dcblock, p, destroy_dcblock, sizeof(sp_dcblock));
816/* Generic filter compute: (filter-compute filter input) -> flonum */
817static Value native_filter_compute(SigilVM *vm, int argc, Value *args)
819 SPFLOAT in = (SPFLOAT)sigil_as_flonum(args[1]);
820 SPFLOAT out = 0;
822 /* Try each filter type */
823 sp_moogladder *ml; sp_butlp *bl; sp_buthp *bh; sp_lpf18 *l18;
824 sp_tone *tn; sp_atone *atn; sp_dcblock *dc;
826 if ((ml = get_moogladder(vm, args[0]))) { sp_moogladder_compute(g_sp, ml, &in, &out); }
827 else if ((bl = get_butlp(vm, args[0]))) { sp_butlp_compute(g_sp, bl, &in, &out); }
828 else if ((bh = get_buthp(vm, args[0]))) { sp_buthp_compute(g_sp, bh, &in, &out); }
829 else if ((l18 = get_lpf18(vm, args[0]))) { sp_lpf18_compute(g_sp, l18, &in, &out); }
830 else if ((tn = get_tone(vm, args[0]))) { sp_tone_compute(g_sp, tn, &in, &out); }
831 else if ((atn = get_atone(vm, args[0]))) { sp_atone_compute(g_sp, atn, &in, &out); }
832 else if ((dc = get_dcblock(vm, args[0]))) { sp_dcblock_compute(g_sp, dc, &in, &out); }
833 else {
834 sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "filter-compute: expected filter object");
835 return SIGIL_FALSE;
836 }
837 return sigil_flonum((double)out);
840/* (filter-set-freq! filter freq) */
841static Value native_filter_set_freq(SigilVM *vm, int argc, Value *args)
843 SPFLOAT freq = (SPFLOAT)sigil_as_flonum(args[1]);
844 sp_moogladder *ml; sp_butlp *bl; sp_buthp *bh; sp_lpf18 *l18;
845 sp_tone *tn; sp_atone *atn;
847 if ((ml = get_moogladder(vm, args[0]))) { ml->freq = freq; }
848 else if ((bl = get_butlp(vm, args[0]))) { bl->freq = freq; }
849 else if ((bh = get_buthp(vm, args[0]))) { bh->freq = freq; }
850 else if ((l18 = get_lpf18(vm, args[0]))) { l18->cutoff = freq; }
851 else if ((tn = get_tone(vm, args[0]))) { tn->hp = freq; }
852 else if ((atn = get_atone(vm, args[0]))) { atn->hp = freq; }
853 else {
854 sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "filter-set-freq!: expected filter object");
855 return SIGIL_FALSE;
856 }
857 return SIGIL_NIL;
860/* (filter-set-res! filter res) — for moogladder, lpf18 */
861static Value native_filter_set_res(SigilVM *vm, int argc, Value *args)
863 SPFLOAT res = (SPFLOAT)sigil_as_flonum(args[1]);
864 sp_moogladder *ml; sp_lpf18 *l18;
866 if ((ml = get_moogladder(vm, args[0]))) { ml->res = res; }
867 else if ((l18 = get_lpf18(vm, args[0]))) { l18->res = res; }
868 else {
869 sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "filter-set-res!: expected resonant filter");
870 return SIGIL_FALSE;
871 }
872 return SIGIL_NIL;
875/* ============================================================
876 * ENVELOPES
877 * ============================================================ */
879/* (make-adsr atk dec sus rel) -> <adsr> */
880static Value native_make_adsr(SigilVM *vm, int argc, Value *args)
882 CHECK_SP(vm, "make-adsr");
883 sp_adsr *p;
884 sp_adsr_create(&p);
885 sp_adsr_init(g_sp, p);
886 p->atk = (SPFLOAT)sigil_as_flonum(args[0]);
887 p->dec = (SPFLOAT)sigil_as_flonum(args[1]);
888 p->sus = (SPFLOAT)sigil_as_flonum(args[2]);
889 p->rel = (SPFLOAT)sigil_as_flonum(args[3]);
890 ENSURE_TAG(vm, tt_adsr, "dsp-adsr");
891 return sigil_make_foreign(vm, tt_adsr, p, destroy_adsr, sizeof(sp_adsr));
894/* (adsr-compute adsr gate) -> flonum */
895static Value native_adsr_compute(SigilVM *vm, int argc, Value *args)
897 sp_adsr *p = get_adsr(vm, args[0]);
898 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "adsr-compute: expected adsr"); return SIGIL_FALSE; }
899 SPFLOAT gate = (SPFLOAT)sigil_as_flonum(args[1]);
900 SPFLOAT out = 0;
901 sp_adsr_compute(g_sp, p, &gate, &out);
902 return sigil_flonum((double)out);
905/* (make-tadsr atk dec sus rel) -> <tadsr> */
906static Value native_make_tadsr(SigilVM *vm, int argc, Value *args)
908 CHECK_SP(vm, "make-tadsr");
909 sp_tadsr *p;
910 sp_tadsr_create(&p);
911 sp_tadsr_init(g_sp, p);
912 p->atk = (SPFLOAT)sigil_as_flonum(args[0]);
913 p->dec = (SPFLOAT)sigil_as_flonum(args[1]);
914 p->sus = (SPFLOAT)sigil_as_flonum(args[2]);
915 p->rel = (SPFLOAT)sigil_as_flonum(args[3]);
916 ENSURE_TAG(vm, tt_tadsr, "dsp-tadsr");
917 return sigil_make_foreign(vm, tt_tadsr, p, destroy_tadsr, sizeof(sp_tadsr));
920/* (tadsr-compute tadsr trigger) -> flonum */
921static Value native_tadsr_compute(SigilVM *vm, int argc, Value *args)
923 sp_tadsr *p = get_tadsr(vm, args[0]);
924 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "tadsr-compute: expected tadsr"); return SIGIL_FALSE; }
925 SPFLOAT trig = (SPFLOAT)sigil_as_flonum(args[1]);
926 SPFLOAT out = 0;
927 sp_tadsr_compute(g_sp, p, &trig, &out);
928 return sigil_flonum((double)out);
931/* (make-tenv atk hold rel) -> <tenv> */
932static Value native_make_tenv(SigilVM *vm, int argc, Value *args)
934 CHECK_SP(vm, "make-tenv");
935 sp_tenv *p;
936 sp_tenv_create(&p);
937 sp_tenv_init(g_sp, p);
938 p->atk = (SPFLOAT)sigil_as_flonum(args[0]);
939 p->hold = (SPFLOAT)sigil_as_flonum(args[1]);
940 p->rel = (SPFLOAT)sigil_as_flonum(args[2]);
941 ENSURE_TAG(vm, tt_tenv, "dsp-tenv");
942 return sigil_make_foreign(vm, tt_tenv, p, destroy_tenv, sizeof(sp_tenv));
945/* (tenv-compute tenv trigger) -> flonum */
946static Value native_tenv_compute(SigilVM *vm, int argc, Value *args)
948 sp_tenv *p = get_tenv(vm, args[0]);
949 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "tenv-compute: expected tenv"); return SIGIL_FALSE; }
950 SPFLOAT trig = (SPFLOAT)sigil_as_flonum(args[1]);
951 SPFLOAT out = 0;
952 sp_tenv_compute(g_sp, p, &trig, &out);
953 return sigil_flonum((double)out);
956/* (make-line a dur b) -> <line> */
957static Value native_make_line(SigilVM *vm, int argc, Value *args)
959 CHECK_SP(vm, "make-line");
960 sp_line *p;
961 sp_line_create(&p);
962 sp_line_init(g_sp, p);
963 p->a = (SPFLOAT)sigil_as_flonum(args[0]);
964 p->dur = (SPFLOAT)sigil_as_flonum(args[1]);
965 p->b = (SPFLOAT)sigil_as_flonum(args[2]);
966 ENSURE_TAG(vm, tt_line, "dsp-line");
967 return sigil_make_foreign(vm, tt_line, p, destroy_line, sizeof(sp_line));
970/* (line-compute line trigger) -> flonum */
971static Value native_line_compute(SigilVM *vm, int argc, Value *args)
973 sp_line *p = get_line(vm, args[0]);
974 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "line-compute: expected line"); return SIGIL_FALSE; }
975 SPFLOAT trig = (SPFLOAT)sigil_as_flonum(args[1]);
976 SPFLOAT out = 0;
977 sp_line_compute(g_sp, p, &trig, &out);
978 return sigil_flonum((double)out);
981/* (make-expon a dur b) -> <expon> */
982static Value native_make_expon(SigilVM *vm, int argc, Value *args)
984 CHECK_SP(vm, "make-expon");
985 sp_expon *p;
986 sp_expon_create(&p);
987 sp_expon_init(g_sp, p);
988 p->a = (SPFLOAT)sigil_as_flonum(args[0]);
989 p->dur = (SPFLOAT)sigil_as_flonum(args[1]);
990 p->b = (SPFLOAT)sigil_as_flonum(args[2]);
991 ENSURE_TAG(vm, tt_expon, "dsp-expon");
992 return sigil_make_foreign(vm, tt_expon, p, destroy_expon, sizeof(sp_expon));
995/* (expon-compute expon trigger) -> flonum */
996static Value native_expon_compute(SigilVM *vm, int argc, Value *args)
998 sp_expon *p = get_expon(vm, args[0]);
999 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "expon-compute: expected expon"); return SIGIL_FALSE; }
1000 SPFLOAT trig = (SPFLOAT)sigil_as_flonum(args[1]);
1001 SPFLOAT out = 0;
1002 sp_expon_compute(g_sp, p, &trig, &out);
1003 return sigil_flonum((double)out);
1006/* (make-port smoothing) -> <port> */
1007static Value native_make_port(SigilVM *vm, int argc, Value *args)
1009 CHECK_SP(vm, "make-port");
1010 sp_port *p;
1011 sp_port_create(&p);
1012 sp_port_init(g_sp, p);
1013 p->smooth = (SPFLOAT)sigil_as_flonum(args[0]);
1014 ENSURE_TAG(vm, tt_port, "dsp-port");
1015 return sigil_make_foreign(vm, tt_port, p, destroy_port, sizeof(sp_port));
1018/* (port-compute port input) -> flonum */
1019static Value native_port_compute(SigilVM *vm, int argc, Value *args)
1021 sp_port *p = get_port(vm, args[0]);
1022 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "port-compute: expected port"); return SIGIL_FALSE; }
1023 SPFLOAT in = (SPFLOAT)sigil_as_flonum(args[1]);
1024 SPFLOAT out = 0;
1025 sp_port_compute(g_sp, p, &in, &out);
1026 return sigil_flonum((double)out);
1029/* ============================================================
1030 * REVERB & DELAY
1031 * ============================================================ */
1033/* (make-revsc feedback lpfreq) -> <revsc> */
1034static Value native_make_revsc(SigilVM *vm, int argc, Value *args)
1036 CHECK_SP(vm, "make-revsc");
1037 sp_revsc *p;
1038 sp_revsc_create(&p);
1039 sp_revsc_init(g_sp, p);
1040 p->feedback = (SPFLOAT)sigil_as_flonum(args[0]);
1041 p->lpfreq = (SPFLOAT)sigil_as_flonum(args[1]);
1042 ENSURE_TAG(vm, tt_revsc, "dsp-revsc");
1043 return sigil_make_foreign(vm, tt_revsc, p, destroy_revsc, sizeof(sp_revsc));
1046/* (revsc-compute revsc in-l in-r) -> (values out-l out-r) */
1047static Value native_revsc_compute(SigilVM *vm, int argc, Value *args)
1049 sp_revsc *p = get_revsc(vm, args[0]);
1050 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "revsc-compute: expected revsc"); return SIGIL_FALSE; }
1051 SPFLOAT inL = (SPFLOAT)sigil_as_flonum(args[1]);
1052 SPFLOAT inR = (SPFLOAT)sigil_as_flonum(args[2]);
1053 SPFLOAT outL = 0, outR = 0;
1054 sp_revsc_compute(g_sp, p, &inL, &inR, &outL, &outR);
1055 /* Return as a pair for simplicity */
1056 Value l = sigil_flonum((double)outL);
1057 Value r = sigil_flonum((double)outR);
1058 return sigil_cons(vm,l, r);
1061/* (revsc-set-feedback! revsc feedback) */
1062static Value native_revsc_set_feedback(SigilVM *vm, int argc, Value *args)
1064 sp_revsc *p = get_revsc(vm, args[0]);
1065 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "revsc-set-feedback!: expected revsc"); return SIGIL_FALSE; }
1066 p->feedback = (SPFLOAT)sigil_as_flonum(args[1]);
1067 return SIGIL_NIL;
1070/* (revsc-set-lpfreq! revsc lpfreq) */
1071static Value native_revsc_set_lpfreq(SigilVM *vm, int argc, Value *args)
1073 sp_revsc *p = get_revsc(vm, args[0]);
1074 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "revsc-set-lpfreq!: expected revsc"); return SIGIL_FALSE; }
1075 p->lpfreq = (SPFLOAT)sigil_as_flonum(args[1]);
1076 return SIGIL_NIL;
1079/* (make-zitarev) -> <zitarev> */
1080static Value native_make_zitarev(SigilVM *vm, int argc, Value *args)
1082 CHECK_SP(vm, "make-zitarev");
1083 sp_zitarev *p;
1084 sp_zitarev_create(&p);
1085 sp_zitarev_init(g_sp, p);
1086 ENSURE_TAG(vm, tt_zitarev, "dsp-zitarev");
1087 return sigil_make_foreign(vm, tt_zitarev, p, destroy_zitarev, sizeof(sp_zitarev));
1090/* (zitarev-compute zitarev in-l in-r) -> (out-l . out-r) */
1091static Value native_zitarev_compute(SigilVM *vm, int argc, Value *args)
1093 sp_zitarev *p = get_zitarev(vm, args[0]);
1094 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "zitarev-compute: expected zitarev"); return SIGIL_FALSE; }
1095 SPFLOAT inL = (SPFLOAT)sigil_as_flonum(args[1]);
1096 SPFLOAT inR = (SPFLOAT)sigil_as_flonum(args[2]);
1097 SPFLOAT outL = 0, outR = 0;
1098 sp_zitarev_compute(g_sp, p, &inL, &inR, &outL, &outR);
1099 Value l = sigil_flonum((double)outL);
1100 Value r = sigil_flonum((double)outR);
1101 return sigil_cons(vm,l, r);
1104/* (zitarev-set-mix! zitarev mix) */
1105static Value native_zitarev_set_mix(SigilVM *vm, int argc, Value *args)
1107 sp_zitarev *p = get_zitarev(vm, args[0]);
1108 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "zitarev-set-mix!: expected zitarev"); return SIGIL_FALSE; }
1109 *p->mix = (SPFLOAT)sigil_as_flonum(args[1]);
1110 return SIGIL_NIL;
1113/* (zitarev-set-level! zitarev level) */
1114static Value native_zitarev_set_level(SigilVM *vm, int argc, Value *args)
1116 sp_zitarev *p = get_zitarev(vm, args[0]);
1117 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "zitarev-set-level!: expected zitarev"); return SIGIL_FALSE; }
1118 *p->level = (SPFLOAT)sigil_as_flonum(args[1]);
1119 return SIGIL_NIL;
1122/* (make-delay time feedback) -> <delay> */
1123static Value native_make_delay(SigilVM *vm, int argc, Value *args)
1125 CHECK_SP(vm, "make-delay");
1126 SPFLOAT time = (SPFLOAT)sigil_as_flonum(args[0]);
1127 SPFLOAT feedback = (SPFLOAT)sigil_as_flonum(args[1]);
1128 sp_delay *p;
1129 sp_delay_create(&p);
1130 sp_delay_init(g_sp, p, time);
1131 p->feedback = feedback;
1132 ENSURE_TAG(vm, tt_delay, "dsp-delay");
1133 return sigil_make_foreign(vm, tt_delay, p, destroy_delay,
1134 sizeof(sp_delay) + (size_t)(time * g_sp->sr) * sizeof(SPFLOAT));
1137/* (delay-compute delay input) -> flonum */
1138static Value native_delay_compute(SigilVM *vm, int argc, Value *args)
1140 sp_delay *p = get_delay(vm, args[0]);
1141 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "delay-compute: expected delay"); return SIGIL_FALSE; }
1142 SPFLOAT in = (SPFLOAT)sigil_as_flonum(args[1]);
1143 SPFLOAT out = 0;
1144 sp_delay_compute(g_sp, p, &in, &out);
1145 return sigil_flonum((double)out);
1148/* (delay-set-feedback! delay feedback) */
1149static Value native_delay_set_feedback(SigilVM *vm, int argc, Value *args)
1151 sp_delay *p = get_delay(vm, args[0]);
1152 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "delay-set-feedback!: expected delay"); return SIGIL_FALSE; }
1153 p->feedback = (SPFLOAT)sigil_as_flonum(args[1]);
1154 return SIGIL_NIL;
1157/* (make-vdelay maxdel feedback) -> <vdelay> */
1158static Value native_make_vdelay(SigilVM *vm, int argc, Value *args)
1160 CHECK_SP(vm, "make-vdelay");
1161 SPFLOAT maxdel = (SPFLOAT)sigil_as_flonum(args[0]);
1162 SPFLOAT feedback = (SPFLOAT)sigil_as_flonum(args[1]);
1163 sp_vdelay *p;
1164 sp_vdelay_create(&p);
1165 sp_vdelay_init(g_sp, p, maxdel);
1166 p->feedback = feedback;
1167 ENSURE_TAG(vm, tt_vdelay, "dsp-vdelay");
1168 return sigil_make_foreign(vm, tt_vdelay, p, destroy_vdelay,
1169 sizeof(sp_vdelay) + (size_t)(maxdel * g_sp->sr) * sizeof(SPFLOAT));
1172/* (vdelay-compute vdelay input) -> flonum */
1173static Value native_vdelay_compute(SigilVM *vm, int argc, Value *args)
1175 sp_vdelay *p = get_vdelay(vm, args[0]);
1176 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "vdelay-compute: expected vdelay"); return SIGIL_FALSE; }
1177 SPFLOAT in = (SPFLOAT)sigil_as_flonum(args[1]);
1178 SPFLOAT out = 0;
1179 sp_vdelay_compute(g_sp, p, &in, &out);
1180 return sigil_flonum((double)out);
1183/* (vdelay-set-del! vdelay del) */
1184static Value native_vdelay_set_del(SigilVM *vm, int argc, Value *args)
1186 sp_vdelay *p = get_vdelay(vm, args[0]);
1187 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "vdelay-set-del!: expected vdelay"); return SIGIL_FALSE; }
1188 p->del = (SPFLOAT)sigil_as_flonum(args[1]);
1189 return SIGIL_NIL;
1192/* (vdelay-set-feedback! vdelay feedback) */
1193static Value native_vdelay_set_feedback(SigilVM *vm, int argc, Value *args)
1195 sp_vdelay *p = get_vdelay(vm, args[0]);
1196 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "vdelay-set-feedback!: expected vdelay"); return SIGIL_FALSE; }
1197 p->feedback = (SPFLOAT)sigil_as_flonum(args[1]);
1198 return SIGIL_NIL;
1201/* ============================================================
1202 * NOISE
1203 * ============================================================ */
1205/* (make-noise amp) -> <noise> */
1206static Value native_make_noise(SigilVM *vm, int argc, Value *args)
1208 CHECK_SP(vm, "make-noise");
1209 sp_noise *p;
1210 sp_noise_create(&p);
1211 sp_noise_init(g_sp, p);
1212 p->amp = (SPFLOAT)sigil_as_flonum(args[0]);
1213 ENSURE_TAG(vm, tt_noise, "dsp-noise");
1214 return sigil_make_foreign(vm, tt_noise, p, destroy_noise, sizeof(sp_noise));
1217/* (noise-compute noise) -> flonum */
1218static Value native_noise_compute(SigilVM *vm, int argc, Value *args)
1220 sp_noise *p = get_noise(vm, args[0]);
1221 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "noise-compute: expected noise"); return SIGIL_FALSE; }
1222 SPFLOAT in = 0, out = 0;
1223 sp_noise_compute(g_sp, p, &in, &out);
1224 return sigil_flonum((double)out);
1227/* (make-pinknoise amp) -> <pinknoise> */
1228static Value native_make_pinknoise(SigilVM *vm, int argc, Value *args)
1230 CHECK_SP(vm, "make-pinknoise");
1231 sp_pinknoise *p;
1232 sp_pinknoise_create(&p);
1233 sp_pinknoise_init(g_sp, p);
1234 p->amp = (SPFLOAT)sigil_as_flonum(args[0]);
1235 ENSURE_TAG(vm, tt_pinknoise, "dsp-pinknoise");
1236 return sigil_make_foreign(vm, tt_pinknoise, p, destroy_pinknoise, sizeof(sp_pinknoise));
1239/* (pinknoise-compute pinknoise) -> flonum */
1240static Value native_pinknoise_compute(SigilVM *vm, int argc, Value *args)
1242 sp_pinknoise *p = get_pinknoise(vm, args[0]);
1243 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "pinknoise-compute: expected pinknoise"); return SIGIL_FALSE; }
1244 SPFLOAT in = 0, out = 0;
1245 sp_pinknoise_compute(g_sp, p, &in, &out);
1246 return sigil_flonum((double)out);
1249/* (make-brown) -> <brown> */
1250static Value native_make_brown(SigilVM *vm, int argc, Value *args)
1252 CHECK_SP(vm, "make-brown");
1253 sp_brown *p;
1254 sp_brown_create(&p);
1255 sp_brown_init(g_sp, p);
1256 ENSURE_TAG(vm, tt_brown, "dsp-brown");
1257 return sigil_make_foreign(vm, tt_brown, p, destroy_brown, sizeof(sp_brown));
1260/* (brown-compute brown) -> flonum */
1261static Value native_brown_compute(SigilVM *vm, int argc, Value *args)
1263 sp_brown *p = get_brown(vm, args[0]);
1264 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "brown-compute: expected brown"); return SIGIL_FALSE; }
1265 SPFLOAT in = 0, out = 0;
1266 sp_brown_compute(g_sp, p, &in, &out);
1267 return sigil_flonum((double)out);
1270/* ============================================================
1271 * KARPLUS-STRONG
1272 * ============================================================ */
1274/* (make-ks freq decay excite-gain) -> <ks> */
1275static Value native_make_ks(SigilVM *vm, int argc, Value *args)
1277 (void)argc;
1278 CHECK_SP(vm, "make-ks");
1279 SPFLOAT freq = (SPFLOAT)sigil_as_flonum(args[0]);
1280 SPFLOAT decay = (SPFLOAT)sigil_as_flonum(args[1]);
1281 SPFLOAT gain = (SPFLOAT)sigil_as_flonum(args[2]);
1282 sp_ks *p;
1283 sp_ks_create(&p);
1284 sp_ks_init(g_sp, p, freq, decay, gain);
1285 ENSURE_TAG(vm, tt_ks, "dsp-ks");
1286 return sigil_make_foreign(vm, tt_ks, p, destroy_ks,
1287 sizeof(sp_ks) + (size_t)p->bufcap * sizeof(SPFLOAT));
1290/* (ks-compute ks gate) -> flonum */
1291static Value native_ks_compute(SigilVM *vm, int argc, Value *args)
1293 (void)argc;
1294 sp_ks *p = get_ks(vm, args[0]);
1295 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "ks-compute: expected ks"); return SIGIL_FALSE; }
1296 SPFLOAT gate = (SPFLOAT)sigil_as_flonum(args[1]);
1297 SPFLOAT out = 0.0f;
1298 sp_ks_compute(g_sp, p, &gate, &out);
1299 return sigil_flonum((double)out);
1302/* (ks-trigger! ks) — manual excitation */
1303static Value native_ks_trigger(SigilVM *vm, int argc, Value *args)
1305 (void)argc;
1306 sp_ks *p = get_ks(vm, args[0]);
1307 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "ks-trigger!: expected ks"); return SIGIL_FALSE; }
1308 sp_ks_trigger(g_sp, p);
1309 return SIGIL_NIL;
1312/* (ks-set-freq! ks freq) */
1313static Value native_ks_set_freq(SigilVM *vm, int argc, Value *args)
1315 (void)argc;
1316 sp_ks *p = get_ks(vm, args[0]);
1317 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "ks-set-freq!: expected ks"); return SIGIL_FALSE; }
1318 p->freq = (SPFLOAT)sigil_as_flonum(args[1]);
1319 sp_ks_retune(g_sp, p);
1320 return SIGIL_NIL;
1323/* (ks-set-decay! ks decay) */
1324static Value native_ks_set_decay(SigilVM *vm, int argc, Value *args)
1326 (void)argc;
1327 sp_ks *p = get_ks(vm, args[0]);
1328 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "ks-set-decay!: expected ks"); return SIGIL_FALSE; }
1329 p->decay = (SPFLOAT)sigil_as_flonum(args[1]);
1330 sp_ks_retune(g_sp, p);
1331 return SIGIL_NIL;
1334/* (ks-set-excite-gain! ks gain) */
1335static Value native_ks_set_excite_gain(SigilVM *vm, int argc, Value *args)
1337 (void)argc;
1338 sp_ks *p = get_ks(vm, args[0]);
1339 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "ks-set-excite-gain!: expected ks"); return SIGIL_FALSE; }
1340 p->excite_gain = (SPFLOAT)sigil_as_flonum(args[1]);
1341 return SIGIL_NIL;
1344/* ============================================================
1345 * DYNAMICS / WAVESHAPING
1346 * ============================================================ */
1348/* (make-saturator drive dcoffset) -> <saturator> */
1349static Value native_make_saturator(SigilVM *vm, int argc, Value *args)
1351 CHECK_SP(vm, "make-saturator");
1352 sp_saturator *p;
1353 sp_saturator_create(&p);
1354 sp_saturator_init(g_sp, p);
1355 p->drive = (SPFLOAT)sigil_as_flonum(args[0]);
1356 p->dcoffset = (SPFLOAT)sigil_as_flonum(args[1]);
1357 ENSURE_TAG(vm, tt_saturator, "dsp-saturator");
1358 return sigil_make_foreign(vm, tt_saturator, p, destroy_saturator, sizeof(sp_saturator));
1361/* (saturator-compute saturator input) -> flonum */
1362static Value native_saturator_compute(SigilVM *vm, int argc, Value *args)
1364 sp_saturator *p = get_saturator(vm, args[0]);
1365 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "saturator-compute: expected saturator"); return SIGIL_FALSE; }
1366 SPFLOAT in = (SPFLOAT)sigil_as_flonum(args[1]);
1367 SPFLOAT out = 0;
1368 sp_saturator_compute(g_sp, p, &in, &out);
1369 return sigil_flonum((double)out);
1372/* (make-clip limit) -> <clip> */
1373static Value native_make_clip(SigilVM *vm, int argc, Value *args)
1375 CHECK_SP(vm, "make-clip");
1376 sp_clip *p;
1377 sp_clip_create(&p);
1378 sp_clip_init(g_sp, p);
1379 p->lim = (SPFLOAT)sigil_as_flonum(args[0]);
1380 ENSURE_TAG(vm, tt_clip, "dsp-clip");
1381 return sigil_make_foreign(vm, tt_clip, p, destroy_clip, sizeof(sp_clip));
1384/* (clip-compute clip input) -> flonum */
1385static Value native_clip_compute(SigilVM *vm, int argc, Value *args)
1387 sp_clip *p = get_clip(vm, args[0]);
1388 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "clip-compute: expected clip"); return SIGIL_FALSE; }
1389 SPFLOAT in = (SPFLOAT)sigil_as_flonum(args[1]);
1390 SPFLOAT out = 0;
1391 sp_clip_compute(g_sp, p, &in, &out);
1392 return sigil_flonum((double)out);
1395/* (make-peaklim atk rel thresh) -> <peaklim> */
1396static Value native_make_peaklim(SigilVM *vm, int argc, Value *args)
1398 CHECK_SP(vm, "make-peaklim");
1399 sp_peaklim *p;
1400 sp_peaklim_create(&p);
1401 sp_peaklim_init(g_sp, p);
1402 p->atk = (SPFLOAT)sigil_as_flonum(args[0]);
1403 p->rel = (SPFLOAT)sigil_as_flonum(args[1]);
1404 p->thresh = (SPFLOAT)sigil_as_flonum(args[2]);
1405 ENSURE_TAG(vm, tt_peaklim, "dsp-peaklim");
1406 return sigil_make_foreign(vm, tt_peaklim, p, destroy_peaklim, sizeof(sp_peaklim));
1409/* (peaklim-compute peaklim input) -> flonum */
1410static Value native_peaklim_compute(SigilVM *vm, int argc, Value *args)
1412 sp_peaklim *p = get_peaklim(vm, args[0]);
1413 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "peaklim-compute: expected peaklim"); return SIGIL_FALSE; }
1414 SPFLOAT in = (SPFLOAT)sigil_as_flonum(args[1]);
1415 SPFLOAT out = 0;
1416 sp_peaklim_compute(g_sp, p, &in, &out);
1417 return sigil_flonum((double)out);
1420/* ============================================================
1421 * SPATIAL — PAN2
1422 * ============================================================ */
1424/* (make-pan2 [pan]) -> <pan2> */
1425static Value native_make_pan2(SigilVM *vm, int argc, Value *args)
1427 CHECK_SP(vm, "make-pan2");
1428 sp_pan2 *p;
1429 sp_pan2_create(&p);
1430 sp_pan2_init(g_sp, p);
1431 p->pan = argc > 0 ? (SPFLOAT)sigil_as_flonum(args[0]) : 0.5f;
1432 ENSURE_TAG(vm, tt_pan2, "dsp-pan2");
1433 return sigil_make_foreign(vm, tt_pan2, p, destroy_pan2, sizeof(sp_pan2));
1436/* (pan2-compute pan2 input) -> (left . right) */
1437static Value native_pan2_compute(SigilVM *vm, int argc, Value *args)
1439 sp_pan2 *p = get_pan2(vm, args[0]);
1440 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "pan2-compute: expected pan2"); return SIGIL_FALSE; }
1441 SPFLOAT in = (SPFLOAT)sigil_as_flonum(args[1]);
1442 SPFLOAT outL = 0, outR = 0;
1443 sp_pan2_compute(g_sp, p, &in, &outL, &outR);
1444 Value l = sigil_flonum((double)outL);
1445 Value r = sigil_flonum((double)outR);
1446 return sigil_cons(vm,l, r);
1449/* (pan2-set-pan! pan2 pan) */
1450static Value native_pan2_set_pan(SigilVM *vm, int argc, Value *args)
1452 sp_pan2 *p = get_pan2(vm, args[0]);
1453 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "pan2-set-pan!: expected pan2"); return SIGIL_FALSE; }
1454 p->pan = (SPFLOAT)sigil_as_flonum(args[1]);
1455 return SIGIL_NIL;
1458/* ============================================================
1459 * TIMING
1460 * ============================================================ */
1462/* (make-metro freq) -> <metro> */
1463static Value native_make_metro(SigilVM *vm, int argc, Value *args)
1465 CHECK_SP(vm, "make-metro");
1466 sp_metro *p;
1467 sp_metro_create(&p);
1468 sp_metro_init(g_sp, p);
1469 p->freq = (SPFLOAT)sigil_as_flonum(args[0]);
1470 ENSURE_TAG(vm, tt_metro, "dsp-metro");
1471 return sigil_make_foreign(vm, tt_metro, p, destroy_metro, sizeof(sp_metro));
1474/* (metro-compute metro) -> flonum (0 or 1) */
1475static Value native_metro_compute(SigilVM *vm, int argc, Value *args)
1477 sp_metro *p = get_metro(vm, args[0]);
1478 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "metro-compute: expected metro"); return SIGIL_FALSE; }
1479 SPFLOAT in = 0, out = 0;
1480 sp_metro_compute(g_sp, p, &in, &out);
1481 return sigil_flonum((double)out);
1484/* (metro-set-freq! metro freq) */
1485static Value native_metro_set_freq(SigilVM *vm, int argc, Value *args)
1487 sp_metro *p = get_metro(vm, args[0]);
1488 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "metro-set-freq!: expected metro"); return SIGIL_FALSE; }
1489 p->freq = (SPFLOAT)sigil_as_flonum(args[1]);
1490 return SIGIL_NIL;
1493/* (make-clock bpm subdiv) -> <clock> */
1494static Value native_make_clock(SigilVM *vm, int argc, Value *args)
1496 CHECK_SP(vm, "make-clock");
1497 sp_clock *p;
1498 sp_clock_create(&p);
1499 sp_clock_init(g_sp, p);
1500 p->bpm = (SPFLOAT)sigil_as_flonum(args[0]);
1501 p->subdiv = (SPFLOAT)sigil_as_flonum(args[1]);
1502 ENSURE_TAG(vm, tt_clock, "dsp-clock");
1503 return sigil_make_foreign(vm, tt_clock, p, destroy_clock, sizeof(sp_clock));
1506/* (clock-compute clock trigger) -> flonum (0 or 1) */
1507static Value native_clock_compute(SigilVM *vm, int argc, Value *args)
1509 sp_clock *p = get_clock_mod(vm, args[0]);
1510 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "clock-compute: expected clock"); return SIGIL_FALSE; }
1511 SPFLOAT trig = (SPFLOAT)sigil_as_flonum(args[1]);
1512 SPFLOAT out = 0;
1513 sp_clock_compute(g_sp, p, &trig, &out);
1514 return sigil_flonum((double)out);
1517/* (clock-set-bpm! clock bpm) */
1518static Value native_clock_set_bpm(SigilVM *vm, int argc, Value *args)
1520 sp_clock *p = get_clock_mod(vm, args[0]);
1521 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "clock-set-bpm!: expected clock"); return SIGIL_FALSE; }
1522 p->bpm = (SPFLOAT)sigil_as_flonum(args[1]);
1523 return SIGIL_NIL;
1526/* (make-tgate time) -> <tgate> */
1527static Value native_make_tgate(SigilVM *vm, int argc, Value *args)
1529 CHECK_SP(vm, "make-tgate");
1530 sp_tgate *p;
1531 sp_tgate_create(&p);
1532 sp_tgate_init(g_sp, p);
1533 p->time = (SPFLOAT)sigil_as_flonum(args[0]);
1534 ENSURE_TAG(vm, tt_tgate, "dsp-tgate");
1535 return sigil_make_foreign(vm, tt_tgate, p, destroy_tgate, sizeof(sp_tgate));
1538/* (tgate-compute tgate trigger) -> flonum (0 or 1) */
1539static Value native_tgate_compute(SigilVM *vm, int argc, Value *args)
1541 sp_tgate *p = get_tgate(vm, args[0]);
1542 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "tgate-compute: expected tgate"); return SIGIL_FALSE; }
1543 SPFLOAT trig = (SPFLOAT)sigil_as_flonum(args[1]);
1544 SPFLOAT out = 0;
1545 sp_tgate_compute(g_sp, p, &trig, &out);
1546 return sigil_flonum((double)out);
1549/* ============================================================
1550 * MODULATION — RANDOM
1551 * ============================================================ */
1553/* (make-randh min max freq) -> <randh> */
1554static Value native_make_randh(SigilVM *vm, int argc, Value *args)
1556 CHECK_SP(vm, "make-randh");
1557 sp_randh *p;
1558 sp_randh_create(&p);
1559 sp_randh_init(g_sp, p);
1560 p->min = (SPFLOAT)sigil_as_flonum(args[0]);
1561 p->max = (SPFLOAT)sigil_as_flonum(args[1]);
1562 p->freq = (SPFLOAT)sigil_as_flonum(args[2]);
1563 ENSURE_TAG(vm, tt_randh, "dsp-randh");
1564 return sigil_make_foreign(vm, tt_randh, p, destroy_randh, sizeof(sp_randh));
1567/* (randh-compute randh) -> flonum */
1568static Value native_randh_compute(SigilVM *vm, int argc, Value *args)
1570 sp_randh *p = get_randh(vm, args[0]);
1571 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "randh-compute: expected randh"); return SIGIL_FALSE; }
1572 SPFLOAT in = 0, out = 0;
1573 sp_randh_compute(g_sp, p, &in, &out);
1574 return sigil_flonum((double)out);
1577/* (make-randi min max freq) -> <randi> */
1578static Value native_make_randi(SigilVM *vm, int argc, Value *args)
1580 CHECK_SP(vm, "make-randi");
1581 sp_randi *p;
1582 sp_randi_create(&p);
1583 sp_randi_init(g_sp, p);
1584 p->min = (SPFLOAT)sigil_as_flonum(args[0]);
1585 p->max = (SPFLOAT)sigil_as_flonum(args[1]);
1586 p->cps = (SPFLOAT)sigil_as_flonum(args[2]);
1587 ENSURE_TAG(vm, tt_randi, "dsp-randi");
1588 return sigil_make_foreign(vm, tt_randi, p, destroy_randi, sizeof(sp_randi));
1591/* (randi-compute randi) -> flonum */
1592static Value native_randi_compute(SigilVM *vm, int argc, Value *args)
1594 sp_randi *p = get_randi(vm, args[0]);
1595 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "randi-compute: expected randi"); return SIGIL_FALSE; }
1596 SPFLOAT in = 0, out = 0;
1597 sp_randi_compute(g_sp, p, &in, &out);
1598 return sigil_flonum((double)out);
1601/* ============================================================
1602 * UTILITY
1603 * ============================================================ */
1605/* (make-scale min max) -> <scale> */
1606static Value native_make_scale(SigilVM *vm, int argc, Value *args)
1608 CHECK_SP(vm, "make-scale");
1609 sp_scale *p;
1610 sp_scale_create(&p);
1611 sp_scale_init(g_sp, p);
1612 p->min = (SPFLOAT)sigil_as_flonum(args[0]);
1613 p->max = (SPFLOAT)sigil_as_flonum(args[1]);
1614 ENSURE_TAG(vm, tt_scale, "dsp-scale");
1615 return sigil_make_foreign(vm, tt_scale, p, destroy_scale, sizeof(sp_scale));
1618/* (scale-compute scale input) -> flonum */
1619static Value native_scale_compute(SigilVM *vm, int argc, Value *args)
1621 sp_scale *p = get_scale(vm, args[0]);
1622 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "scale-compute: expected scale"); return SIGIL_FALSE; }
1623 SPFLOAT in = (SPFLOAT)sigil_as_flonum(args[1]);
1624 SPFLOAT out = 0;
1625 sp_scale_compute(g_sp, p, &in, &out);
1626 return sigil_flonum((double)out);
1629/* (make-clamp min max) -> <clamp> */
1630static Value native_make_clamp(SigilVM *vm, int argc, Value *args)
1632 CHECK_SP(vm, "make-clamp");
1633 sp_clamp *p;
1634 sp_clamp_create(&p);
1635 sp_clamp_init(g_sp, p);
1636 p->min = (SPFLOAT)sigil_as_flonum(args[0]);
1637 p->max = (SPFLOAT)sigil_as_flonum(args[1]);
1638 ENSURE_TAG(vm, tt_clamp, "dsp-clamp");
1639 return sigil_make_foreign(vm, tt_clamp, p, destroy_clamp, sizeof(sp_clamp));
1642/* (clamp-compute clamp input) -> flonum */
1643static Value native_clamp_compute(SigilVM *vm, int argc, Value *args)
1645 sp_clamp *p = get_clamp(vm, args[0]);
1646 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "clamp-compute: expected clamp"); return SIGIL_FALSE; }
1647 SPFLOAT in = (SPFLOAT)sigil_as_flonum(args[1]);
1648 SPFLOAT out = 0;
1649 sp_clamp_compute(g_sp, p, &in, &out);
1650 return sigil_flonum((double)out);
1653/* (make-crossfade [pos]) -> <crossfade> */
1654static Value native_make_crossfade(SigilVM *vm, int argc, Value *args)
1656 CHECK_SP(vm, "make-crossfade");
1657 sp_crossfade *p;
1658 sp_crossfade_create(&p);
1659 sp_crossfade_init(g_sp, p);
1660 p->pos = argc > 0 ? (SPFLOAT)sigil_as_flonum(args[0]) : 0.5f;
1661 ENSURE_TAG(vm, tt_crossfade, "dsp-crossfade");
1662 return sigil_make_foreign(vm, tt_crossfade, p, destroy_crossfade, sizeof(sp_crossfade));
1665/* (crossfade-compute crossfade in1 in2) -> flonum */
1666static Value native_crossfade_compute(SigilVM *vm, int argc, Value *args)
1668 sp_crossfade *p = get_crossfade(vm, args[0]);
1669 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "crossfade-compute: expected crossfade"); return SIGIL_FALSE; }
1670 SPFLOAT in1 = (SPFLOAT)sigil_as_flonum(args[1]);
1671 SPFLOAT in2 = (SPFLOAT)sigil_as_flonum(args[2]);
1672 SPFLOAT out = 0;
1673 sp_crossfade_compute(g_sp, p, &in1, &in2, &out);
1674 return sigil_flonum((double)out);
1677/* (crossfade-set-pos! crossfade pos) */
1678static Value native_crossfade_set_pos(SigilVM *vm, int argc, Value *args)
1680 sp_crossfade *p = get_crossfade(vm, args[0]);
1681 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "crossfade-set-pos!: expected crossfade"); return SIGIL_FALSE; }
1682 p->pos = (SPFLOAT)sigil_as_flonum(args[1]);
1683 return SIGIL_NIL;
1686/* ============================================================
1687 * WAV OUTPUT
1688 * ============================================================ */
1690/* (make-wavout filename) -> <wavout> */
1691static Value native_make_wavout(SigilVM *vm, int argc, Value *args)
1693 CHECK_SP(vm, "make-wavout");
1694 if (!sigil_is_string(args[0])) {
1695 sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "make-wavout: expected string filename");
1696 return SIGIL_FALSE;
1698 SigilString *s = (SigilString *)sigil_as_ptr(args[0]);
1699 sp_wavout *p;
1700 sp_wavout_create(&p);
1701 sp_wavout_init(g_sp, p, s->data);
1702 ENSURE_TAG(vm, tt_wavout, "dsp-wavout");
1703 return sigil_make_foreign(vm, tt_wavout, p, destroy_wavout, 0);
1706/* (wavout-compute wavout sample) -> flonum */
1707static Value native_wavout_compute(SigilVM *vm, int argc, Value *args)
1709 sp_wavout *p = get_wavout(vm, args[0]);
1710 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "wavout-compute: expected wavout"); return SIGIL_FALSE; }
1711 SPFLOAT in = (SPFLOAT)sigil_as_flonum(args[1]);
1712 SPFLOAT out = 0;
1713 sp_wavout_compute(g_sp, p, &in, &out);
1714 return sigil_flonum((double)out);
1717/* ============================================================
1718 * MODULE INITIALIZATION
1719 * ============================================================ */
1721void sigil__init_sigil_dsp_module(SigilVM *vm)
1723 /* === (sigil dsp core) === */
1724 SigilModule *mod = sigil_begin_module(vm, "(sigil dsp core)");
1725 if (!mod) return;
1727 sigil_module_register_native(vm, "dsp-init", native_dsp_init,
1728 SIGIL_ARITY_RANGE(0, 1), "Initialize DSP engine");
1729 sigil_module_register_native(vm, "dsp-shutdown", native_dsp_shutdown,
1730 SIGIL_ARITY_EXACT(0), "Shutdown DSP engine");
1731 sigil_module_register_native(vm, "dsp-sample-rate", native_dsp_sample_rate,
1732 SIGIL_ARITY_EXACT(0), "Get sample rate");
1734 /* Function tables */
1735 sigil_module_register_native(vm, "make-ftbl", native_make_ftbl,
1736 SIGIL_ARITY_EXACT(1), "Create function table");
1737 sigil_module_register_native(vm, "ftbl-gen-sine", native_ftbl_gen_sine,
1738 SIGIL_ARITY_EXACT(1), "Fill table with sine wave");
1739 sigil_module_register_native(vm, "ftbl-gen-triangle", native_ftbl_gen_triangle,
1740 SIGIL_ARITY_EXACT(1), "Fill table with triangle wave");
1741 sigil_module_register_native(vm, "ftbl-gen-line", native_ftbl_gen_line,
1742 SIGIL_ARITY_EXACT(2), "Fill table with line segments");
1744 sigil_module_export(vm, "dsp-init");
1745 sigil_module_export(vm, "dsp-shutdown");
1746 sigil_module_export(vm, "dsp-sample-rate");
1747 sigil_module_export(vm, "make-ftbl");
1748 sigil_module_export(vm, "ftbl-gen-sine");
1749 sigil_module_export(vm, "ftbl-gen-triangle");
1750 sigil_module_export(vm, "ftbl-gen-line");
1751 sigil_end_module(vm);
1753 /* === (sigil dsp osc) === */
1754 mod = sigil_begin_module(vm, "(sigil dsp osc)");
1755 if (!mod) return;
1757 sigil_module_register_native(vm, "make-osc", native_make_osc,
1758 SIGIL_ARITY_RANGE(2, 3), "Create wavetable osc (freq amp [phase])");
1759 sigil_module_register_native(vm, "make-osc-ft", native_make_osc_ft,
1760 SIGIL_ARITY_RANGE(3, 4), "Create wavetable osc with custom ftbl");
1761 sigil_module_register_native(vm, "osc-compute", native_osc_compute,
1762 SIGIL_ARITY_EXACT(1), "Compute one sample from osc");
1763 sigil_module_register_native(vm, "osc-set-freq!", native_osc_set_freq,
1764 SIGIL_ARITY_EXACT(2), "Set osc frequency");
1765 sigil_module_register_native(vm, "osc-set-amp!", native_osc_set_amp,
1766 SIGIL_ARITY_EXACT(2), "Set osc amplitude");
1768 sigil_module_register_native(vm, "make-posc3", native_make_posc3,
1769 SIGIL_ARITY_RANGE(2, 3), "Create cubic interp wavetable osc");
1770 sigil_module_register_native(vm, "posc3-compute", native_posc3_compute,
1771 SIGIL_ARITY_EXACT(1), "Compute one sample from posc3");
1772 sigil_module_register_native(vm, "posc3-set-freq!", native_posc3_set_freq,
1773 SIGIL_ARITY_EXACT(2), "Set posc3 frequency");
1774 sigil_module_register_native(vm, "posc3-set-amp!", native_posc3_set_amp,
1775 SIGIL_ARITY_EXACT(2), "Set posc3 amplitude");
1777 sigil_module_register_native(vm, "make-blsaw", native_make_blsaw,
1778 SIGIL_ARITY_EXACT(2), "Create bandlimited sawtooth (freq amp)");
1779 sigil_module_register_native(vm, "blsaw-compute", native_blsaw_compute,
1780 SIGIL_ARITY_EXACT(1), "Compute one sample from blsaw");
1781 sigil_module_register_native(vm, "blsaw-set-freq!", native_blsaw_set_freq,
1782 SIGIL_ARITY_EXACT(2), "Set blsaw frequency");
1783 sigil_module_register_native(vm, "blsaw-set-amp!", native_blsaw_set_amp,
1784 SIGIL_ARITY_EXACT(2), "Set blsaw amplitude");
1786 sigil_module_register_native(vm, "make-blsquare", native_make_blsquare,
1787 SIGIL_ARITY_RANGE(2, 3), "Create bandlimited square (freq amp [width])");
1788 sigil_module_register_native(vm, "blsquare-compute", native_blsquare_compute,
1789 SIGIL_ARITY_EXACT(1), "Compute one sample from blsquare");
1790 sigil_module_register_native(vm, "blsquare-set-freq!", native_blsquare_set_freq,
1791 SIGIL_ARITY_EXACT(2), "Set blsquare frequency");
1792 sigil_module_register_native(vm, "blsquare-set-amp!", native_blsquare_set_amp,
1793 SIGIL_ARITY_EXACT(2), "Set blsquare amplitude");
1795 sigil_module_register_native(vm, "make-bltriangle", native_make_bltriangle,
1796 SIGIL_ARITY_EXACT(2), "Create bandlimited triangle (freq amp)");
1797 sigil_module_register_native(vm, "bltriangle-compute", native_bltriangle_compute,
1798 SIGIL_ARITY_EXACT(1), "Compute one sample from bltriangle");
1799 sigil_module_register_native(vm, "bltriangle-set-freq!", native_bltriangle_set_freq,
1800 SIGIL_ARITY_EXACT(2), "Set bltriangle frequency");
1801 sigil_module_register_native(vm, "bltriangle-set-amp!", native_bltriangle_set_amp,
1802 SIGIL_ARITY_EXACT(2), "Set bltriangle amplitude");
1804 sigil_module_register_native(vm, "make-fosc", native_make_fosc,
1805 SIGIL_ARITY_EXACT(5), "Create FM osc (freq car mod indx amp)");
1806 sigil_module_register_native(vm, "fosc-compute", native_fosc_compute,
1807 SIGIL_ARITY_EXACT(1), "Compute one sample from fosc");
1808 sigil_module_register_native(vm, "fosc-set-freq!", native_fosc_set_freq,
1809 SIGIL_ARITY_EXACT(2), "Set fosc frequency");
1810 sigil_module_register_native(vm, "fosc-set-index!", native_fosc_set_index,
1811 SIGIL_ARITY_EXACT(2), "Set fosc modulation index");
1812 sigil_module_register_native(vm, "fosc-set-amp!", native_fosc_set_amp,
1813 SIGIL_ARITY_EXACT(2), "Set fosc amplitude");
1815 sigil_module_register_native(vm, "make-phasor", native_make_phasor,
1816 SIGIL_ARITY_RANGE(1, 2), "Create phasor (freq [phase])");
1817 sigil_module_register_native(vm, "phasor-compute", native_phasor_compute,
1818 SIGIL_ARITY_EXACT(1), "Compute one sample from phasor");
1819 sigil_module_register_native(vm, "phasor-set-freq!", native_phasor_set_freq,
1820 SIGIL_ARITY_EXACT(2), "Set phasor frequency");
1822 sigil_module_register_native(vm, "make-ks", native_make_ks,
1823 SIGIL_ARITY_EXACT(3), "Create Karplus-Strong (freq decay excite-gain)");
1824 sigil_module_register_native(vm, "ks-compute", native_ks_compute,
1825 SIGIL_ARITY_EXACT(2), "Compute Karplus-Strong (ks gate) -> sample");
1826 sigil_module_register_native(vm, "ks-trigger!", native_ks_trigger,
1827 SIGIL_ARITY_EXACT(1), "Excite Karplus-Strong with noise burst");
1828 sigil_module_register_native(vm, "ks-set-freq!", native_ks_set_freq,
1829 SIGIL_ARITY_EXACT(2), "Set KS fundamental frequency");
1830 sigil_module_register_native(vm, "ks-set-decay!", native_ks_set_decay,
1831 SIGIL_ARITY_EXACT(2), "Set KS -60 dB decay time");
1832 sigil_module_register_native(vm, "ks-set-excite-gain!", native_ks_set_excite_gain,
1833 SIGIL_ARITY_EXACT(2), "Set KS excitation amplitude");
1835 /* Export all osc bindings */
1836 const char *osc_exports[] = {
1837 "make-osc", "make-osc-ft", "osc-compute", "osc-set-freq!", "osc-set-amp!",
1838 "make-posc3", "posc3-compute", "posc3-set-freq!", "posc3-set-amp!",
1839 "make-blsaw", "blsaw-compute", "blsaw-set-freq!", "blsaw-set-amp!",
1840 "make-blsquare", "blsquare-compute", "blsquare-set-freq!", "blsquare-set-amp!",
1841 "make-bltriangle", "bltriangle-compute", "bltriangle-set-freq!", "bltriangle-set-amp!",
1842 "make-fosc", "fosc-compute", "fosc-set-freq!", "fosc-set-index!", "fosc-set-amp!",
1843 "make-phasor", "phasor-compute", "phasor-set-freq!",
1844 "make-ks", "ks-compute", "ks-trigger!",
1845 "ks-set-freq!", "ks-set-decay!", "ks-set-excite-gain!",
1846 NULL
1847 };
1848 for (int i = 0; osc_exports[i]; i++) sigil_module_export(vm, osc_exports[i]);
1849 sigil_end_module(vm);
1851 /* === (sigil dsp filter) === */
1852 mod = sigil_begin_module(vm, "(sigil dsp filter)");
1853 if (!mod) return;
1855 sigil_module_register_native(vm, "make-moogladder", native_make_moogladder,
1856 SIGIL_ARITY_EXACT(2), "Create Moog ladder filter (freq res)");
1857 sigil_module_register_native(vm, "make-butlp", native_make_butlp,
1858 SIGIL_ARITY_EXACT(1), "Create Butterworth lowpass (freq)");
1859 sigil_module_register_native(vm, "make-buthp", native_make_buthp,
1860 SIGIL_ARITY_EXACT(1), "Create Butterworth highpass (freq)");
1861 sigil_module_register_native(vm, "make-lpf18", native_make_lpf18,
1862 SIGIL_ARITY_EXACT(3), "Create 3-pole resonant LP (cutoff res dist)");
1863 sigil_module_register_native(vm, "make-tone", native_make_tone,
1864 SIGIL_ARITY_EXACT(1), "Create 1st-order lowpass (freq)");
1865 sigil_module_register_native(vm, "make-atone", native_make_atone,
1866 SIGIL_ARITY_EXACT(1), "Create 1st-order highpass (freq)");
1867 sigil_module_register_native(vm, "make-dcblock", native_make_dcblock,
1868 SIGIL_ARITY_EXACT(0), "Create DC blocking filter");
1869 sigil_module_register_native(vm, "filter-compute", native_filter_compute,
1870 SIGIL_ARITY_EXACT(2), "Compute filter (filter input) -> output");
1871 sigil_module_register_native(vm, "filter-set-freq!", native_filter_set_freq,
1872 SIGIL_ARITY_EXACT(2), "Set filter cutoff frequency");
1873 sigil_module_register_native(vm, "filter-set-res!", native_filter_set_res,
1874 SIGIL_ARITY_EXACT(2), "Set filter resonance");
1876 const char *filter_exports[] = {
1877 "make-moogladder", "make-butlp", "make-buthp", "make-lpf18",
1878 "make-tone", "make-atone", "make-dcblock",
1879 "filter-compute", "filter-set-freq!", "filter-set-res!",
1880 NULL
1881 };
1882 for (int i = 0; filter_exports[i]; i++) sigil_module_export(vm, filter_exports[i]);
1883 sigil_end_module(vm);
1885 /* === (sigil dsp env) === */
1886 mod = sigil_begin_module(vm, "(sigil dsp env)");
1887 if (!mod) return;
1889 sigil_module_register_native(vm, "make-adsr", native_make_adsr,
1890 SIGIL_ARITY_EXACT(4), "Create ADSR envelope (atk dec sus rel)");
1891 sigil_module_register_native(vm, "adsr-compute", native_adsr_compute,
1892 SIGIL_ARITY_EXACT(2), "Compute ADSR (adsr gate) -> env value");
1893 sigil_module_register_native(vm, "make-tadsr", native_make_tadsr,
1894 SIGIL_ARITY_EXACT(4), "Create triggered ADSR (atk dec sus rel)");
1895 sigil_module_register_native(vm, "tadsr-compute", native_tadsr_compute,
1896 SIGIL_ARITY_EXACT(2), "Compute triggered ADSR");
1897 sigil_module_register_native(vm, "make-tenv", native_make_tenv,
1898 SIGIL_ARITY_EXACT(3), "Create triggered envelope (atk hold rel)");
1899 sigil_module_register_native(vm, "tenv-compute", native_tenv_compute,
1900 SIGIL_ARITY_EXACT(2), "Compute triggered envelope");
1901 sigil_module_register_native(vm, "make-line", native_make_line,
1902 SIGIL_ARITY_EXACT(3), "Create line segment (a dur b)");
1903 sigil_module_register_native(vm, "line-compute", native_line_compute,
1904 SIGIL_ARITY_EXACT(2), "Compute line segment");
1905 sigil_module_register_native(vm, "make-expon", native_make_expon,
1906 SIGIL_ARITY_EXACT(3), "Create exponential curve (a dur b)");
1907 sigil_module_register_native(vm, "expon-compute", native_expon_compute,
1908 SIGIL_ARITY_EXACT(2), "Compute exponential curve");
1909 sigil_module_register_native(vm, "make-port", native_make_port,
1910 SIGIL_ARITY_EXACT(1), "Create portamento (smoothing)");
1911 sigil_module_register_native(vm, "port-compute", native_port_compute,
1912 SIGIL_ARITY_EXACT(2), "Compute portamento");
1914 const char *env_exports[] = {
1915 "make-adsr", "adsr-compute", "make-tadsr", "tadsr-compute",
1916 "make-tenv", "tenv-compute", "make-line", "line-compute",
1917 "make-expon", "expon-compute", "make-port", "port-compute",
1918 NULL
1919 };
1920 for (int i = 0; env_exports[i]; i++) sigil_module_export(vm, env_exports[i]);
1921 sigil_end_module(vm);
1923 /* === (sigil dsp fx) === */
1924 mod = sigil_begin_module(vm, "(sigil dsp fx)");
1925 if (!mod) return;
1927 sigil_module_register_native(vm, "make-revsc", native_make_revsc,
1928 SIGIL_ARITY_EXACT(2), "Create stereo reverb (feedback lpfreq)");
1929 sigil_module_register_native(vm, "revsc-compute", native_revsc_compute,
1930 SIGIL_ARITY_EXACT(3), "Compute reverb (revsc inL inR) -> (outL . outR)");
1931 sigil_module_register_native(vm, "revsc-set-feedback!", native_revsc_set_feedback,
1932 SIGIL_ARITY_EXACT(2), "Set reverb feedback");
1933 sigil_module_register_native(vm, "revsc-set-lpfreq!", native_revsc_set_lpfreq,
1934 SIGIL_ARITY_EXACT(2), "Set reverb LP frequency");
1936 sigil_module_register_native(vm, "make-zitarev", native_make_zitarev,
1937 SIGIL_ARITY_EXACT(0), "Create Zita reverb");
1938 sigil_module_register_native(vm, "zitarev-compute", native_zitarev_compute,
1939 SIGIL_ARITY_EXACT(3), "Compute Zita reverb");
1940 sigil_module_register_native(vm, "zitarev-set-mix!", native_zitarev_set_mix,
1941 SIGIL_ARITY_EXACT(2), "Set Zita reverb mix");
1942 sigil_module_register_native(vm, "zitarev-set-level!", native_zitarev_set_level,
1943 SIGIL_ARITY_EXACT(2), "Set Zita reverb level");
1945 sigil_module_register_native(vm, "make-delay", native_make_delay,
1946 SIGIL_ARITY_EXACT(2), "Create delay (time feedback)");
1947 sigil_module_register_native(vm, "delay-compute", native_delay_compute,
1948 SIGIL_ARITY_EXACT(2), "Compute delay (delay input) -> output");
1949 sigil_module_register_native(vm, "delay-set-feedback!", native_delay_set_feedback,
1950 SIGIL_ARITY_EXACT(2), "Set delay feedback");
1952 sigil_module_register_native(vm, "make-vdelay", native_make_vdelay,
1953 SIGIL_ARITY_EXACT(2), "Create variable delay (maxdel feedback)");
1954 sigil_module_register_native(vm, "vdelay-compute", native_vdelay_compute,
1955 SIGIL_ARITY_EXACT(2), "Compute variable delay");
1956 sigil_module_register_native(vm, "vdelay-set-del!", native_vdelay_set_del,
1957 SIGIL_ARITY_EXACT(2), "Set variable delay time");
1958 sigil_module_register_native(vm, "vdelay-set-feedback!", native_vdelay_set_feedback,
1959 SIGIL_ARITY_EXACT(2), "Set variable delay feedback");
1961 const char *fx_exports[] = {
1962 "make-revsc", "revsc-compute", "revsc-set-feedback!", "revsc-set-lpfreq!",
1963 "make-zitarev", "zitarev-compute", "zitarev-set-mix!", "zitarev-set-level!",
1964 "make-delay", "delay-compute", "delay-set-feedback!",
1965 "make-vdelay", "vdelay-compute", "vdelay-set-del!", "vdelay-set-feedback!",
1966 NULL
1967 };
1968 for (int i = 0; fx_exports[i]; i++) sigil_module_export(vm, fx_exports[i]);
1969 sigil_end_module(vm);
1971 /* === (sigil dsp noise) === */
1972 mod = sigil_begin_module(vm, "(sigil dsp noise)");
1973 if (!mod) return;
1975 sigil_module_register_native(vm, "make-noise", native_make_noise,
1976 SIGIL_ARITY_EXACT(1), "Create white noise (amp)");
1977 sigil_module_register_native(vm, "noise-compute", native_noise_compute,
1978 SIGIL_ARITY_EXACT(1), "Compute one noise sample");
1979 sigil_module_register_native(vm, "make-pinknoise", native_make_pinknoise,
1980 SIGIL_ARITY_EXACT(1), "Create pink noise (amp)");
1981 sigil_module_register_native(vm, "pinknoise-compute", native_pinknoise_compute,
1982 SIGIL_ARITY_EXACT(1), "Compute one pinknoise sample");
1983 sigil_module_register_native(vm, "make-brown", native_make_brown,
1984 SIGIL_ARITY_EXACT(0), "Create brown noise");
1985 sigil_module_register_native(vm, "brown-compute", native_brown_compute,
1986 SIGIL_ARITY_EXACT(1), "Compute one brown noise sample");
1988 const char *noise_exports[] = {
1989 "make-noise", "noise-compute",
1990 "make-pinknoise", "pinknoise-compute",
1991 "make-brown", "brown-compute",
1992 NULL
1993 };
1994 for (int i = 0; noise_exports[i]; i++) sigil_module_export(vm, noise_exports[i]);
1995 sigil_end_module(vm);
1997 /* === (sigil dsp util) === */
1998 mod = sigil_begin_module(vm, "(sigil dsp util)");
1999 if (!mod) return;
2001 /* Dynamics */
2002 sigil_module_register_native(vm, "make-saturator", native_make_saturator,
2003 SIGIL_ARITY_EXACT(2), "Create saturator (drive dcoffset)");
2004 sigil_module_register_native(vm, "saturator-compute", native_saturator_compute,
2005 SIGIL_ARITY_EXACT(2), "Compute saturator");
2006 sigil_module_register_native(vm, "make-clip", native_make_clip,
2007 SIGIL_ARITY_EXACT(1), "Create hard clipper (limit)");
2008 sigil_module_register_native(vm, "clip-compute", native_clip_compute,
2009 SIGIL_ARITY_EXACT(2), "Compute clipper");
2010 sigil_module_register_native(vm, "make-peaklim", native_make_peaklim,
2011 SIGIL_ARITY_EXACT(3), "Create peak limiter (atk rel thresh)");
2012 sigil_module_register_native(vm, "peaklim-compute", native_peaklim_compute,
2013 SIGIL_ARITY_EXACT(2), "Compute peak limiter");
2015 /* Panning */
2016 sigil_module_register_native(vm, "make-pan2", native_make_pan2,
2017 SIGIL_ARITY_RANGE(0, 1), "Create panner ([pan])");
2018 sigil_module_register_native(vm, "pan2-compute", native_pan2_compute,
2019 SIGIL_ARITY_EXACT(2), "Compute pan2 (pan2 input) -> (L . R)");
2020 sigil_module_register_native(vm, "pan2-set-pan!", native_pan2_set_pan,
2021 SIGIL_ARITY_EXACT(2), "Set pan position");
2023 /* Timing */
2024 sigil_module_register_native(vm, "make-metro", native_make_metro,
2025 SIGIL_ARITY_EXACT(1), "Create metronome (freq)");
2026 sigil_module_register_native(vm, "metro-compute", native_metro_compute,
2027 SIGIL_ARITY_EXACT(1), "Compute metronome");
2028 sigil_module_register_native(vm, "metro-set-freq!", native_metro_set_freq,
2029 SIGIL_ARITY_EXACT(2), "Set metronome frequency");
2030 sigil_module_register_native(vm, "make-clock", native_make_clock,
2031 SIGIL_ARITY_EXACT(2), "Create clock (bpm subdiv)");
2032 sigil_module_register_native(vm, "clock-compute", native_clock_compute,
2033 SIGIL_ARITY_EXACT(2), "Compute clock");
2034 sigil_module_register_native(vm, "clock-set-bpm!", native_clock_set_bpm,
2035 SIGIL_ARITY_EXACT(2), "Set clock BPM");
2036 sigil_module_register_native(vm, "make-tgate", native_make_tgate,
2037 SIGIL_ARITY_EXACT(1), "Create triggered gate (time)");
2038 sigil_module_register_native(vm, "tgate-compute", native_tgate_compute,
2039 SIGIL_ARITY_EXACT(2), "Compute triggered gate");
2041 /* Random */
2042 sigil_module_register_native(vm, "make-randh", native_make_randh,
2043 SIGIL_ARITY_EXACT(3), "Create random hold (min max freq)");
2044 sigil_module_register_native(vm, "randh-compute", native_randh_compute,
2045 SIGIL_ARITY_EXACT(1), "Compute random hold");
2046 sigil_module_register_native(vm, "make-randi", native_make_randi,
2047 SIGIL_ARITY_EXACT(3), "Create random interp (min max freq)");
2048 sigil_module_register_native(vm, "randi-compute", native_randi_compute,
2049 SIGIL_ARITY_EXACT(1), "Compute random interp");
2051 /* Scaling */
2052 sigil_module_register_native(vm, "make-scale", native_make_scale,
2053 SIGIL_ARITY_EXACT(2), "Create scaler (min max)");
2054 sigil_module_register_native(vm, "scale-compute", native_scale_compute,
2055 SIGIL_ARITY_EXACT(2), "Compute scale");
2056 sigil_module_register_native(vm, "make-clamp", native_make_clamp,
2057 SIGIL_ARITY_EXACT(2), "Create clamp (min max)");
2058 sigil_module_register_native(vm, "clamp-compute", native_clamp_compute,
2059 SIGIL_ARITY_EXACT(2), "Compute clamp");
2060 sigil_module_register_native(vm, "make-crossfade", native_make_crossfade,
2061 SIGIL_ARITY_RANGE(0, 1), "Create crossfade ([pos])");
2062 sigil_module_register_native(vm, "crossfade-compute", native_crossfade_compute,
2063 SIGIL_ARITY_EXACT(3), "Compute crossfade (xf in1 in2)");
2064 sigil_module_register_native(vm, "crossfade-set-pos!", native_crossfade_set_pos,
2065 SIGIL_ARITY_EXACT(2), "Set crossfade position");
2067 /* WAV output */
2068 sigil_module_register_native(vm, "make-wavout", native_make_wavout,
2069 SIGIL_ARITY_EXACT(1), "Create WAV file writer (filename)");
2070 sigil_module_register_native(vm, "wavout-compute", native_wavout_compute,
2071 SIGIL_ARITY_EXACT(2), "Write sample to WAV");
2073 const char *util_exports[] = {
2074 "make-saturator", "saturator-compute",
2075 "make-clip", "clip-compute",
2076 "make-peaklim", "peaklim-compute",
2077 "make-pan2", "pan2-compute", "pan2-set-pan!",
2078 "make-metro", "metro-compute", "metro-set-freq!",
2079 "make-clock", "clock-compute", "clock-set-bpm!",
2080 "make-tgate", "tgate-compute",
2081 "make-randh", "randh-compute",
2082 "make-randi", "randi-compute",
2083 "make-scale", "scale-compute",
2084 "make-clamp", "clamp-compute",
2085 "make-crossfade", "crossfade-compute", "crossfade-set-pos!",
2086 "make-wavout", "wavout-compute",
2087 NULL
2088 };
2089 for (int i = 0; util_exports[i]; i++) sigil_module_export(vm, util_exports[i]);
2090 sigil_end_module(vm);