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"
17#include "fm4.h"
18#include "fx4.h"
20/* ============================================================
21 * GLOBAL STATE
22 * ============================================================ */
24static sp_data *g_sp = NULL;
26/* Type tags for foreign objects */
27static Value tt_osc = SIGIL_UNDEFINED;
28static Value tt_posc3 = SIGIL_UNDEFINED;
29static Value tt_blsaw = SIGIL_UNDEFINED;
30static Value tt_blsquare = SIGIL_UNDEFINED;
31static Value tt_bltriangle = SIGIL_UNDEFINED;
32static Value tt_fosc = SIGIL_UNDEFINED;
33static Value tt_phasor = SIGIL_UNDEFINED;
34static Value tt_moogladder = SIGIL_UNDEFINED;
35static Value tt_butlp = SIGIL_UNDEFINED;
36static Value tt_buthp = SIGIL_UNDEFINED;
37static Value tt_lpf18 = SIGIL_UNDEFINED;
38static Value tt_tone = SIGIL_UNDEFINED;
39static Value tt_atone = SIGIL_UNDEFINED;
40static Value tt_dcblock = SIGIL_UNDEFINED;
41static Value tt_adsr = SIGIL_UNDEFINED;
42static Value tt_tadsr = SIGIL_UNDEFINED;
43static Value tt_tenv = SIGIL_UNDEFINED;
44static Value tt_line = SIGIL_UNDEFINED;
45static Value tt_expon = SIGIL_UNDEFINED;
46static Value tt_port = SIGIL_UNDEFINED;
47static Value tt_revsc = SIGIL_UNDEFINED;
48static Value tt_zitarev = SIGIL_UNDEFINED;
49static Value tt_delay = SIGIL_UNDEFINED;
50static Value tt_vdelay = SIGIL_UNDEFINED;
51static Value tt_noise = SIGIL_UNDEFINED;
52static Value tt_pinknoise = SIGIL_UNDEFINED;
53static Value tt_brown = SIGIL_UNDEFINED;
54static Value tt_saturator = SIGIL_UNDEFINED;
55static Value tt_clip = SIGIL_UNDEFINED;
56static Value tt_peaklim = SIGIL_UNDEFINED;
57static Value tt_pan2 = SIGIL_UNDEFINED;
58static Value tt_metro = SIGIL_UNDEFINED;
59static Value tt_clock = SIGIL_UNDEFINED;
60static Value tt_tgate = SIGIL_UNDEFINED;
61static Value tt_randh = SIGIL_UNDEFINED;
62static Value tt_randi = SIGIL_UNDEFINED;
63static Value tt_scale = SIGIL_UNDEFINED;
64static Value tt_clamp = SIGIL_UNDEFINED;
65static Value tt_crossfade = SIGIL_UNDEFINED;
66static Value tt_wavout = SIGIL_UNDEFINED;
67static Value tt_ftbl = SIGIL_UNDEFINED;
68static Value tt_ks = SIGIL_UNDEFINED;
69static Value tt_fm4 = SIGIL_UNDEFINED;
70static Value tt_chorus = SIGIL_UNDEFINED;
71static Value tt_crush = SIGIL_UNDEFINED;
72static Value tt_comp = SIGIL_UNDEFINED;
74/* ============================================================
75 * HELPERS
76 * ============================================================ */
78#define ENSURE_TAG(vm, tag, name) \
79 if (sigil_is_undefined(tag)) { \
80 tag = sigil_intern_symbol(vm, name, strlen(name)); \
81 }
83#define CHECK_SP(vm, funcname) \
84 if (!g_sp) { \
85 sigil__vm_set_error(vm, SIGIL_ERR_RUNTIME, funcname ": DSP not initialized, call dsp-init first"); \
86 return SIGIL_FALSE; \
87 }
89/* Wrapper struct for wavetable oscillator (owns ftbl) */
90typedef struct {
91 sp_osc *osc;
92 sp_ftbl *ft;
93} dsp_osc;
95/* Wrapper struct for posc3 (owns ftbl) */
96typedef struct {
97 sp_posc3 *osc;
98 sp_ftbl *ft;
99} dsp_posc3;
101/* Wrapper struct for fosc (owns ftbl) */
102typedef struct {
103 sp_fosc *osc;
104 sp_ftbl *ft;
105} dsp_fosc;
107/* ============================================================
108 * DESTRUCTORS
109 * ============================================================ */
111static void destroy_osc(void *data) {
112 dsp_osc *d = (dsp_osc *)data;
113 if (d->osc) sp_osc_destroy(&d->osc);
114 if (d->ft) sp_ftbl_destroy(&d->ft);
115 free(d);
118static void destroy_posc3(void *data) {
119 dsp_posc3 *d = (dsp_posc3 *)data;
120 if (d->osc) sp_posc3_destroy(&d->osc);
121 if (d->ft) sp_ftbl_destroy(&d->ft);
122 free(d);
125static void destroy_blsaw(void *data) { sp_blsaw *p = (sp_blsaw *)data; sp_blsaw_destroy(&p); }
126static void destroy_blsquare(void *data) { sp_blsquare *p = (sp_blsquare *)data; sp_blsquare_destroy(&p); }
127static void destroy_bltriangle(void *data) { sp_bltriangle *p = (sp_bltriangle *)data; sp_bltriangle_destroy(&p); }
129static void destroy_fosc(void *data) {
130 dsp_fosc *d = (dsp_fosc *)data;
131 if (d->osc) sp_fosc_destroy(&d->osc);
132 if (d->ft) sp_ftbl_destroy(&d->ft);
133 free(d);
136static void destroy_phasor(void *data) { sp_phasor *p = (sp_phasor *)data; sp_phasor_destroy(&p); }
137static void destroy_moogladder(void *data) { sp_moogladder *p = (sp_moogladder *)data; sp_moogladder_destroy(&p); }
138static void destroy_butlp(void *data) { sp_butlp *p = (sp_butlp *)data; sp_butlp_destroy(&p); }
139static void destroy_buthp(void *data) { sp_buthp *p = (sp_buthp *)data; sp_buthp_destroy(&p); }
140static void destroy_lpf18(void *data) { sp_lpf18 *p = (sp_lpf18 *)data; sp_lpf18_destroy(&p); }
141static void destroy_tone(void *data) { sp_tone *p = (sp_tone *)data; sp_tone_destroy(&p); }
142static void destroy_atone(void *data) { sp_atone *p = (sp_atone *)data; sp_atone_destroy(&p); }
143static void destroy_dcblock(void *data) { sp_dcblock *p = (sp_dcblock *)data; sp_dcblock_destroy(&p); }
144static void destroy_adsr(void *data) { sp_adsr *p = (sp_adsr *)data; sp_adsr_destroy(&p); }
145static void destroy_tadsr(void *data) { sp_tadsr *p = (sp_tadsr *)data; sp_tadsr_destroy(&p); }
146static void destroy_tenv(void *data) { sp_tenv *p = (sp_tenv *)data; sp_tenv_destroy(&p); }
147static void destroy_line(void *data) { sp_line *p = (sp_line *)data; sp_line_destroy(&p); }
148static void destroy_expon(void *data) { sp_expon *p = (sp_expon *)data; sp_expon_destroy(&p); }
149static void destroy_port(void *data) { sp_port *p = (sp_port *)data; sp_port_destroy(&p); }
150static void destroy_revsc(void *data) { sp_revsc *p = (sp_revsc *)data; sp_revsc_destroy(&p); }
151static void destroy_zitarev(void *data) { sp_zitarev *p = (sp_zitarev *)data; sp_zitarev_destroy(&p); }
152static void destroy_delay(void *data) { sp_delay *p = (sp_delay *)data; sp_delay_destroy(&p); }
153static void destroy_vdelay(void *data) { sp_vdelay *p = (sp_vdelay *)data; sp_vdelay_destroy(&p); }
154static void destroy_noise(void *data) { sp_noise *p = (sp_noise *)data; sp_noise_destroy(&p); }
155static void destroy_pinknoise(void *data) { sp_pinknoise *p = (sp_pinknoise *)data; sp_pinknoise_destroy(&p); }
156static void destroy_brown(void *data) { sp_brown *p = (sp_brown *)data; sp_brown_destroy(&p); }
157static void destroy_saturator(void *data) { sp_saturator *p = (sp_saturator *)data; sp_saturator_destroy(&p); }
158static void destroy_clip(void *data) { sp_clip *p = (sp_clip *)data; sp_clip_destroy(&p); }
159static void destroy_peaklim(void *data) { sp_peaklim *p = (sp_peaklim *)data; sp_peaklim_destroy(&p); }
160static void destroy_pan2(void *data) { sp_pan2 *p = (sp_pan2 *)data; sp_pan2_destroy(&p); }
161static void destroy_metro(void *data) { sp_metro *p = (sp_metro *)data; sp_metro_destroy(&p); }
162static void destroy_clock(void *data) { sp_clock *p = (sp_clock *)data; sp_clock_destroy(&p); }
163static void destroy_tgate(void *data) { sp_tgate *p = (sp_tgate *)data; sp_tgate_destroy(&p); }
164static void destroy_randh(void *data) { sp_randh *p = (sp_randh *)data; sp_randh_destroy(&p); }
165static void destroy_randi(void *data) { sp_randi *p = (sp_randi *)data; sp_randi_destroy(&p); }
166static void destroy_scale(void *data) { sp_scale *p = (sp_scale *)data; sp_scale_destroy(&p); }
167static void destroy_clamp(void *data) { sp_clamp *p = (sp_clamp *)data; sp_clamp_destroy(&p); }
168static void destroy_crossfade(void *data) { sp_crossfade *p = (sp_crossfade *)data; sp_crossfade_destroy(&p); }
169static void destroy_wavout(void *data) { sp_wavout *p = (sp_wavout *)data; sp_wavout_destroy(&p); }
170static void destroy_ftbl(void *data) { sp_ftbl *p = (sp_ftbl *)data; sp_ftbl_destroy(&p); }
171static void destroy_ks(void *data) { sp_ks *p = (sp_ks *)data; sp_ks_destroy(&p); }
172static void destroy_fm4(void *data) { sp_fm4 *p = (sp_fm4 *)data; sp_fm4_destroy(&p); }
173static void destroy_chorus(void *data) { sp_chorus *p = (sp_chorus *)data; sp_chorus_destroy(&p); }
174static void destroy_crush(void *data) { sp_crush *p = (sp_crush *)data; sp_crush_destroy(&p); }
175static void destroy_comp(void *data) { sp_comp *p = (sp_comp *)data; sp_comp_destroy(&p); }
177/* ============================================================
178 * TYPE GETTERS
179 * ============================================================ */
181#define DEF_GETTER(name, sp_type, tag, label) \
182 static sp_type *get_##name(SigilVM *vm, Value v) { \
183 ENSURE_TAG(vm, tag, "dsp-" label); \
184 if (!sigil_is_foreign_type(v, tag)) return NULL; \
185 return (sp_type *)sigil_foreign_get_data(v); \
186 }
188/* Wrapper getters return the wrapper struct */
189static dsp_osc *get_osc(SigilVM *vm, Value v) {
190 ENSURE_TAG(vm, tt_osc, "dsp-osc");
191 if (!sigil_is_foreign_type(v, tt_osc)) return NULL;
192 return (dsp_osc *)sigil_foreign_get_data(v);
194static dsp_posc3 *get_posc3(SigilVM *vm, Value v) {
195 ENSURE_TAG(vm, tt_posc3, "dsp-posc3");
196 if (!sigil_is_foreign_type(v, tt_posc3)) return NULL;
197 return (dsp_posc3 *)sigil_foreign_get_data(v);
199static dsp_fosc *get_fosc(SigilVM *vm, Value v) {
200 ENSURE_TAG(vm, tt_fosc, "dsp-fosc");
201 if (!sigil_is_foreign_type(v, tt_fosc)) return NULL;
202 return (dsp_fosc *)sigil_foreign_get_data(v);
205DEF_GETTER(blsaw, sp_blsaw, tt_blsaw, "blsaw")
206DEF_GETTER(blsquare, sp_blsquare, tt_blsquare, "blsquare")
207DEF_GETTER(bltriangle, sp_bltriangle, tt_bltriangle, "bltriangle")
208DEF_GETTER(phasor, sp_phasor, tt_phasor, "phasor")
209DEF_GETTER(moogladder, sp_moogladder, tt_moogladder, "moogladder")
210DEF_GETTER(butlp, sp_butlp, tt_butlp, "butlp")
211DEF_GETTER(buthp, sp_buthp, tt_buthp, "buthp")
212DEF_GETTER(lpf18, sp_lpf18, tt_lpf18, "lpf18")
213DEF_GETTER(tone, sp_tone, tt_tone, "tone")
214DEF_GETTER(atone, sp_atone, tt_atone, "atone")
215DEF_GETTER(dcblock, sp_dcblock, tt_dcblock, "dcblock")
216DEF_GETTER(adsr, sp_adsr, tt_adsr, "adsr")
217DEF_GETTER(tadsr, sp_tadsr, tt_tadsr, "tadsr")
218DEF_GETTER(tenv, sp_tenv, tt_tenv, "tenv")
219DEF_GETTER(line, sp_line, tt_line, "line")
220DEF_GETTER(expon, sp_expon, tt_expon, "expon")
221DEF_GETTER(port, sp_port, tt_port, "port")
222DEF_GETTER(revsc, sp_revsc, tt_revsc, "revsc")
223DEF_GETTER(zitarev, sp_zitarev, tt_zitarev, "zitarev")
224DEF_GETTER(delay, sp_delay, tt_delay, "delay")
225DEF_GETTER(vdelay, sp_vdelay, tt_vdelay, "vdelay")
226DEF_GETTER(noise, sp_noise, tt_noise, "noise")
227DEF_GETTER(pinknoise, sp_pinknoise, tt_pinknoise, "pinknoise")
228DEF_GETTER(brown, sp_brown, tt_brown, "brown")
229DEF_GETTER(saturator, sp_saturator, tt_saturator, "saturator")
230DEF_GETTER(clip, sp_clip, tt_clip, "clip")
231DEF_GETTER(peaklim, sp_peaklim, tt_peaklim, "peaklim")
232DEF_GETTER(pan2, sp_pan2, tt_pan2, "pan2")
233DEF_GETTER(metro, sp_metro, tt_metro, "metro")
234DEF_GETTER(clock_mod, sp_clock, tt_clock, "clock")
235DEF_GETTER(tgate, sp_tgate, tt_tgate, "tgate")
236DEF_GETTER(randh, sp_randh, tt_randh, "randh")
237DEF_GETTER(randi, sp_randi, tt_randi, "randi")
238DEF_GETTER(scale, sp_scale, tt_scale, "scale")
239DEF_GETTER(clamp, sp_clamp, tt_clamp, "clamp")
240DEF_GETTER(crossfade, sp_crossfade, tt_crossfade, "crossfade")
241DEF_GETTER(wavout, sp_wavout, tt_wavout, "wavout")
242DEF_GETTER(ks, sp_ks, tt_ks, "ks")
243DEF_GETTER(fm4, sp_fm4, tt_fm4, "fm4")
244DEF_GETTER(chorus, sp_chorus, tt_chorus, "chorus")
245DEF_GETTER(crush, sp_crush, tt_crush, "crush")
246DEF_GETTER(comp, sp_comp, tt_comp, "comp")
248/* ============================================================
249 * CORE — dsp-init, dsp-shutdown, dsp-sample-rate
250 * ============================================================ */
252/* (dsp-init [sample-rate]) */
253/* Helper to get a number as double from either fixnum or flonum */
254static double as_number(Value v)
256 if (sigil_is_fixnum(v)) return (double)sigil_as_fixnum(v);
257 return sigil_as_flonum(v);
260/* (dsp-init [sample-rate])
261 *
262 * Creates the shared soundpipe context on the first call. A later call
263 * WITH a rate sets the context's rate for the nodes built from then on
264 * (nodes cache it at init: the fm4 voice, the chorus and the comp): a
265 * program that renders at more than one rate in its life (crash the
266 * stack's live soundtrack reopens its player at a lower rate when a
267 * device is slow, 2026-09-21) calls (dsp-init sr) before it builds a
268 * graph for that rate. Nodes already built keep the rate they were built
269 * at. A call without a rate changes nothing once the context exists. */
270static Value native_dsp_init(SigilVM *vm, int argc, Value *args)
272 (void)vm;
273 if (!g_sp) sp_create(&g_sp);
274 if (argc > 0) {
275 int sr = (int)as_number(args[0]);
276 if (sr > 0) g_sp->sr = sr;
277 }
278 return SIGIL_TRUE;
281/* (dsp-shutdown) */
282static Value native_dsp_shutdown(SigilVM *vm, int argc, Value *args)
284 (void)vm; (void)argc; (void)args;
285 if (g_sp) {
286 sp_destroy(&g_sp);
287 g_sp = NULL;
288 }
289 return SIGIL_NIL;
292/* (dsp-sample-rate) -> fixnum */
293static Value native_dsp_sample_rate(SigilVM *vm, int argc, Value *args)
295 (void)argc; (void)args;
296 CHECK_SP(vm, "dsp-sample-rate");
297 return sigil_fixnum(g_sp->sr);
300/* ============================================================
301 * FUNCTION TABLES
302 * ============================================================ */
304/* (make-ftbl size) -> <ftbl> */
305static Value native_make_ftbl(SigilVM *vm, int argc, Value *args)
307 CHECK_SP(vm, "make-ftbl");
308 size_t size = (size_t)sigil_as_flonum(args[0]);
309 sp_ftbl *ft;
310 sp_ftbl_create(g_sp, &ft, size);
311 ENSURE_TAG(vm, tt_ftbl, "dsp-ftbl");
312 return sigil_make_foreign(vm, tt_ftbl, ft, destroy_ftbl,
313 sizeof(sp_ftbl) + size * sizeof(SPFLOAT));
316/* (ftbl-gen-sine ftbl) */
317static Value native_ftbl_gen_sine(SigilVM *vm, int argc, Value *args)
319 CHECK_SP(vm, "ftbl-gen-sine");
320 ENSURE_TAG(vm, tt_ftbl, "dsp-ftbl");
321 if (!sigil_is_foreign_type(args[0], tt_ftbl)) {
322 sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "ftbl-gen-sine: expected ftbl");
323 return SIGIL_FALSE;
324 }
325 sp_ftbl *ft = (sp_ftbl *)sigil_foreign_get_data(args[0]);
326 sp_gen_sine(g_sp, ft);
327 return SIGIL_NIL;
330/* (ftbl-gen-triangle ftbl) */
331static Value native_ftbl_gen_triangle(SigilVM *vm, int argc, Value *args)
333 CHECK_SP(vm, "ftbl-gen-triangle");
334 ENSURE_TAG(vm, tt_ftbl, "dsp-ftbl");
335 if (!sigil_is_foreign_type(args[0], tt_ftbl)) {
336 sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "ftbl-gen-triangle: expected ftbl");
337 return SIGIL_FALSE;
338 }
339 sp_ftbl *ft = (sp_ftbl *)sigil_foreign_get_data(args[0]);
340 sp_gen_triangle(g_sp, ft);
341 return SIGIL_NIL;
344/* (ftbl-gen-line ftbl argstring) */
345static Value native_ftbl_gen_line(SigilVM *vm, int argc, Value *args)
347 CHECK_SP(vm, "ftbl-gen-line");
348 ENSURE_TAG(vm, tt_ftbl, "dsp-ftbl");
349 if (!sigil_is_foreign_type(args[0], tt_ftbl)) {
350 sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "ftbl-gen-line: expected ftbl");
351 return SIGIL_FALSE;
352 }
353 if (!sigil_is_string(args[1])) {
354 sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "ftbl-gen-line: expected string");
355 return SIGIL_FALSE;
356 }
357 sp_ftbl *ft = (sp_ftbl *)sigil_foreign_get_data(args[0]);
358 SigilString *s = (SigilString *)sigil_as_ptr(args[1]);
359 sp_gen_line(g_sp, ft, s->data);
360 return SIGIL_NIL;
363/* ============================================================
364 * OSCILLATORS
365 * ============================================================ */
367/* (make-osc freq amp [phase]) -> <osc>
368 * Wavetable oscillator with sine wave table */
369static Value native_make_osc(SigilVM *vm, int argc, Value *args)
371 CHECK_SP(vm, "make-osc");
372 SPFLOAT freq = (SPFLOAT)sigil_as_flonum(args[0]);
373 SPFLOAT amp = (SPFLOAT)sigil_as_flonum(args[1]);
374 SPFLOAT phase = argc > 2 ? (SPFLOAT)sigil_as_flonum(args[2]) : 0.0f;
376 sp_ftbl *ft;
377 sp_ftbl_create(g_sp, &ft, 4096);
378 sp_gen_sine(g_sp, ft);
380 sp_osc *osc;
381 sp_osc_create(&osc);
382 sp_osc_init(g_sp, osc, ft, phase);
383 osc->freq = freq;
384 osc->amp = amp;
386 dsp_osc *d = malloc(sizeof(dsp_osc));
387 d->osc = osc;
388 d->ft = ft;
390 ENSURE_TAG(vm, tt_osc, "dsp-osc");
391 return sigil_make_foreign(vm, tt_osc, d, destroy_osc,
392 sizeof(dsp_osc) + sizeof(sp_osc) + 4096 * sizeof(SPFLOAT));
395/* (make-osc-ft ftbl freq amp [phase]) -> <osc>
396 * Wavetable oscillator with custom function table */
397static Value native_make_osc_ft(SigilVM *vm, int argc, Value *args)
399 CHECK_SP(vm, "make-osc-ft");
400 ENSURE_TAG(vm, tt_ftbl, "dsp-ftbl");
401 if (!sigil_is_foreign_type(args[0], tt_ftbl)) {
402 sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "make-osc-ft: expected ftbl as first argument");
403 return SIGIL_FALSE;
404 }
405 /* Note: the ftbl is NOT owned by this osc — caller manages its lifetime */
406 sp_ftbl *ft = (sp_ftbl *)sigil_foreign_get_data(args[0]);
407 SPFLOAT freq = (SPFLOAT)sigil_as_flonum(args[1]);
408 SPFLOAT amp = (SPFLOAT)sigil_as_flonum(args[2]);
409 SPFLOAT phase = argc > 3 ? (SPFLOAT)sigil_as_flonum(args[3]) : 0.0f;
411 sp_osc *osc;
412 sp_osc_create(&osc);
413 sp_osc_init(g_sp, osc, ft, phase);
414 osc->freq = freq;
415 osc->amp = amp;
417 dsp_osc *d = malloc(sizeof(dsp_osc));
418 d->osc = osc;
419 d->ft = NULL; /* not owned */
421 ENSURE_TAG(vm, tt_osc, "dsp-osc");
422 return sigil_make_foreign(vm, tt_osc, d, destroy_osc,
423 sizeof(dsp_osc) + sizeof(sp_osc));
426/* (osc-compute osc) -> flonum */
427static Value native_osc_compute(SigilVM *vm, int argc, Value *args)
429 dsp_osc *d = get_osc(vm, args[0]);
430 if (!d) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "osc-compute: expected osc"); return SIGIL_FALSE; }
431 SPFLOAT in = 0, out = 0;
432 sp_osc_compute(g_sp, d->osc, &in, &out);
433 return sigil_flonum((double)out);
436/* (osc-set-freq! osc freq) */
437static Value native_osc_set_freq(SigilVM *vm, int argc, Value *args)
439 dsp_osc *d = get_osc(vm, args[0]);
440 if (!d) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "osc-set-freq!: expected osc"); return SIGIL_FALSE; }
441 d->osc->freq = (SPFLOAT)sigil_as_flonum(args[1]);
442 return SIGIL_NIL;
445/* (osc-set-amp! osc amp) */
446static Value native_osc_set_amp(SigilVM *vm, int argc, Value *args)
448 dsp_osc *d = get_osc(vm, args[0]);
449 if (!d) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "osc-set-amp!: expected osc"); return SIGIL_FALSE; }
450 d->osc->amp = (SPFLOAT)sigil_as_flonum(args[1]);
451 return SIGIL_NIL;
454/* (make-posc3 freq amp [phase]) -> <posc3> */
455static Value native_make_posc3(SigilVM *vm, int argc, Value *args)
457 CHECK_SP(vm, "make-posc3");
458 SPFLOAT freq = (SPFLOAT)sigil_as_flonum(args[0]);
459 SPFLOAT amp = (SPFLOAT)sigil_as_flonum(args[1]);
461 sp_ftbl *ft;
462 sp_ftbl_create(g_sp, &ft, 4096);
463 sp_gen_sine(g_sp, ft);
465 sp_posc3 *osc;
466 sp_posc3_create(&osc);
467 sp_posc3_init(g_sp, osc, ft);
468 osc->freq = freq;
469 osc->amp = amp;
471 dsp_posc3 *d = malloc(sizeof(dsp_posc3));
472 d->osc = osc;
473 d->ft = ft;
475 ENSURE_TAG(vm, tt_posc3, "dsp-posc3");
476 return sigil_make_foreign(vm, tt_posc3, d, destroy_posc3,
477 sizeof(dsp_posc3) + sizeof(sp_posc3) + 4096 * sizeof(SPFLOAT));
480/* (posc3-compute osc) -> flonum */
481static Value native_posc3_compute(SigilVM *vm, int argc, Value *args)
483 dsp_posc3 *d = get_posc3(vm, args[0]);
484 if (!d) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "posc3-compute: expected posc3"); return SIGIL_FALSE; }
485 SPFLOAT in = 0, out = 0;
486 sp_posc3_compute(g_sp, d->osc, &in, &out);
487 return sigil_flonum((double)out);
490/* (posc3-set-freq! osc freq) */
491static Value native_posc3_set_freq(SigilVM *vm, int argc, Value *args)
493 dsp_posc3 *d = get_posc3(vm, args[0]);
494 if (!d) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "posc3-set-freq!: expected posc3"); return SIGIL_FALSE; }
495 d->osc->freq = (SPFLOAT)sigil_as_flonum(args[1]);
496 return SIGIL_NIL;
499/* (posc3-set-amp! osc amp) */
500static Value native_posc3_set_amp(SigilVM *vm, int argc, Value *args)
502 dsp_posc3 *d = get_posc3(vm, args[0]);
503 if (!d) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "posc3-set-amp!: expected posc3"); return SIGIL_FALSE; }
504 d->osc->amp = (SPFLOAT)sigil_as_flonum(args[1]);
505 return SIGIL_NIL;
508/* (make-blsaw freq amp) -> <blsaw> */
509static Value native_make_blsaw(SigilVM *vm, int argc, Value *args)
511 CHECK_SP(vm, "make-blsaw");
512 SPFLOAT freq = (SPFLOAT)sigil_as_flonum(args[0]);
513 SPFLOAT amp = (SPFLOAT)sigil_as_flonum(args[1]);
515 sp_blsaw *p;
516 sp_blsaw_create(&p);
517 sp_blsaw_init(g_sp, p);
518 *p->freq = freq;
519 *p->amp = amp;
521 ENSURE_TAG(vm, tt_blsaw, "dsp-blsaw");
522 return sigil_make_foreign(vm, tt_blsaw, p, destroy_blsaw, sizeof(sp_blsaw));
525/* (blsaw-compute osc) -> flonum */
526static Value native_blsaw_compute(SigilVM *vm, int argc, Value *args)
528 sp_blsaw *p = get_blsaw(vm, args[0]);
529 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "blsaw-compute: expected blsaw"); return SIGIL_FALSE; }
530 SPFLOAT in = 0, out = 0;
531 sp_blsaw_compute(g_sp, p, &in, &out);
532 return sigil_flonum((double)out);
535/* (blsaw-set-freq! osc freq) */
536static Value native_blsaw_set_freq(SigilVM *vm, int argc, Value *args)
538 sp_blsaw *p = get_blsaw(vm, args[0]);
539 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "blsaw-set-freq!: expected blsaw"); return SIGIL_FALSE; }
540 *p->freq = (SPFLOAT)sigil_as_flonum(args[1]);
541 return SIGIL_NIL;
544/* (blsaw-set-amp! osc amp) */
545static Value native_blsaw_set_amp(SigilVM *vm, int argc, Value *args)
547 sp_blsaw *p = get_blsaw(vm, args[0]);
548 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "blsaw-set-amp!: expected blsaw"); return SIGIL_FALSE; }
549 *p->amp = (SPFLOAT)sigil_as_flonum(args[1]);
550 return SIGIL_NIL;
553/* (make-blsquare freq amp [width]) -> <blsquare> */
554static Value native_make_blsquare(SigilVM *vm, int argc, Value *args)
556 CHECK_SP(vm, "make-blsquare");
557 SPFLOAT freq = (SPFLOAT)sigil_as_flonum(args[0]);
558 SPFLOAT amp = (SPFLOAT)sigil_as_flonum(args[1]);
559 SPFLOAT width = argc > 2 ? (SPFLOAT)sigil_as_flonum(args[2]) : 0.5f;
561 sp_blsquare *p;
562 sp_blsquare_create(&p);
563 sp_blsquare_init(g_sp, p);
564 *p->freq = freq;
565 *p->amp = amp;
566 *p->width = width;
568 ENSURE_TAG(vm, tt_blsquare, "dsp-blsquare");
569 return sigil_make_foreign(vm, tt_blsquare, p, destroy_blsquare, sizeof(sp_blsquare));
572/* (blsquare-compute osc) -> flonum */
573static Value native_blsquare_compute(SigilVM *vm, int argc, Value *args)
575 sp_blsquare *p = get_blsquare(vm, args[0]);
576 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "blsquare-compute: expected blsquare"); return SIGIL_FALSE; }
577 SPFLOAT in = 0, out = 0;
578 sp_blsquare_compute(g_sp, p, &in, &out);
579 return sigil_flonum((double)out);
582/* (blsquare-set-freq! osc freq) */
583static Value native_blsquare_set_freq(SigilVM *vm, int argc, Value *args)
585 sp_blsquare *p = get_blsquare(vm, args[0]);
586 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "blsquare-set-freq!: expected blsquare"); return SIGIL_FALSE; }
587 *p->freq = (SPFLOAT)sigil_as_flonum(args[1]);
588 return SIGIL_NIL;
591/* (blsquare-set-amp! osc amp) */
592static Value native_blsquare_set_amp(SigilVM *vm, int argc, Value *args)
594 sp_blsquare *p = get_blsquare(vm, args[0]);
595 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "blsquare-set-amp!: expected blsquare"); return SIGIL_FALSE; }
596 *p->amp = (SPFLOAT)sigil_as_flonum(args[1]);
597 return SIGIL_NIL;
600/* (make-bltriangle freq amp) -> <bltriangle> */
601static Value native_make_bltriangle(SigilVM *vm, int argc, Value *args)
603 CHECK_SP(vm, "make-bltriangle");
604 SPFLOAT freq = (SPFLOAT)sigil_as_flonum(args[0]);
605 SPFLOAT amp = (SPFLOAT)sigil_as_flonum(args[1]);
607 sp_bltriangle *p;
608 sp_bltriangle_create(&p);
609 sp_bltriangle_init(g_sp, p);
610 *p->freq = freq;
611 *p->amp = amp;
613 ENSURE_TAG(vm, tt_bltriangle, "dsp-bltriangle");
614 return sigil_make_foreign(vm, tt_bltriangle, p, destroy_bltriangle, sizeof(sp_bltriangle));
617/* (bltriangle-compute osc) -> flonum */
618static Value native_bltriangle_compute(SigilVM *vm, int argc, Value *args)
620 sp_bltriangle *p = get_bltriangle(vm, args[0]);
621 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "bltriangle-compute: expected bltriangle"); return SIGIL_FALSE; }
622 SPFLOAT in = 0, out = 0;
623 sp_bltriangle_compute(g_sp, p, &in, &out);
624 return sigil_flonum((double)out);
627/* (bltriangle-set-freq! osc freq) */
628static Value native_bltriangle_set_freq(SigilVM *vm, int argc, Value *args)
630 sp_bltriangle *p = get_bltriangle(vm, args[0]);
631 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "bltriangle-set-freq!: expected bltriangle"); return SIGIL_FALSE; }
632 *p->freq = (SPFLOAT)sigil_as_flonum(args[1]);
633 return SIGIL_NIL;
636/* (bltriangle-set-amp! osc amp) */
637static Value native_bltriangle_set_amp(SigilVM *vm, int argc, Value *args)
639 sp_bltriangle *p = get_bltriangle(vm, args[0]);
640 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "bltriangle-set-amp!: expected bltriangle"); return SIGIL_FALSE; }
641 *p->amp = (SPFLOAT)sigil_as_flonum(args[1]);
642 return SIGIL_NIL;
645/* (make-fosc freq car mod indx amp) -> <fosc> */
646static Value native_make_fosc(SigilVM *vm, int argc, Value *args)
648 CHECK_SP(vm, "make-fosc");
649 SPFLOAT freq = (SPFLOAT)sigil_as_flonum(args[0]);
650 SPFLOAT car = (SPFLOAT)sigil_as_flonum(args[1]);
651 SPFLOAT mod = (SPFLOAT)sigil_as_flonum(args[2]);
652 SPFLOAT indx = (SPFLOAT)sigil_as_flonum(args[3]);
653 SPFLOAT amp = (SPFLOAT)sigil_as_flonum(args[4]);
655 sp_ftbl *ft;
656 sp_ftbl_create(g_sp, &ft, 4096);
657 sp_gen_sine(g_sp, ft);
659 sp_fosc *osc;
660 sp_fosc_create(&osc);
661 sp_fosc_init(g_sp, osc, ft);
662 osc->freq = freq;
663 osc->car = car;
664 osc->mod = mod;
665 osc->indx = indx;
666 osc->amp = amp;
668 dsp_fosc *d = malloc(sizeof(dsp_fosc));
669 d->osc = osc;
670 d->ft = ft;
672 ENSURE_TAG(vm, tt_fosc, "dsp-fosc");
673 return sigil_make_foreign(vm, tt_fosc, d, destroy_fosc,
674 sizeof(dsp_fosc) + sizeof(sp_fosc) + 4096 * sizeof(SPFLOAT));
677/* (fosc-compute osc) -> flonum */
678static Value native_fosc_compute(SigilVM *vm, int argc, Value *args)
680 dsp_fosc *d = get_fosc(vm, args[0]);
681 if (!d) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "fosc-compute: expected fosc"); return SIGIL_FALSE; }
682 SPFLOAT in = 0, out = 0;
683 sp_fosc_compute(g_sp, d->osc, &in, &out);
684 return sigil_flonum((double)out);
687/* (fosc-set-freq! osc freq) */
688static Value native_fosc_set_freq(SigilVM *vm, int argc, Value *args)
690 dsp_fosc *d = get_fosc(vm, args[0]);
691 if (!d) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "fosc-set-freq!: expected fosc"); return SIGIL_FALSE; }
692 d->osc->freq = (SPFLOAT)sigil_as_flonum(args[1]);
693 return SIGIL_NIL;
696/* (fosc-set-index! osc indx) */
697static Value native_fosc_set_index(SigilVM *vm, int argc, Value *args)
699 dsp_fosc *d = get_fosc(vm, args[0]);
700 if (!d) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "fosc-set-index!: expected fosc"); return SIGIL_FALSE; }
701 d->osc->indx = (SPFLOAT)sigil_as_flonum(args[1]);
702 return SIGIL_NIL;
705/* (fosc-set-amp! osc amp) */
706static Value native_fosc_set_amp(SigilVM *vm, int argc, Value *args)
708 dsp_fosc *d = get_fosc(vm, args[0]);
709 if (!d) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "fosc-set-amp!: expected fosc"); return SIGIL_FALSE; }
710 d->osc->amp = (SPFLOAT)sigil_as_flonum(args[1]);
711 return SIGIL_NIL;
714/* (make-phasor freq [phase]) -> <phasor> */
715static Value native_make_phasor(SigilVM *vm, int argc, Value *args)
717 CHECK_SP(vm, "make-phasor");
718 SPFLOAT freq = (SPFLOAT)sigil_as_flonum(args[0]);
719 SPFLOAT phase = argc > 1 ? (SPFLOAT)sigil_as_flonum(args[1]) : 0.0f;
721 sp_phasor *p;
722 sp_phasor_create(&p);
723 sp_phasor_init(g_sp, p, phase);
724 p->freq = freq;
726 ENSURE_TAG(vm, tt_phasor, "dsp-phasor");
727 return sigil_make_foreign(vm, tt_phasor, p, destroy_phasor, sizeof(sp_phasor));
730/* (phasor-compute phasor) -> flonum */
731static Value native_phasor_compute(SigilVM *vm, int argc, Value *args)
733 sp_phasor *p = get_phasor(vm, args[0]);
734 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "phasor-compute: expected phasor"); return SIGIL_FALSE; }
735 SPFLOAT in = 0, out = 0;
736 sp_phasor_compute(g_sp, p, &in, &out);
737 return sigil_flonum((double)out);
740/* (phasor-set-freq! phasor freq) */
741static Value native_phasor_set_freq(SigilVM *vm, int argc, Value *args)
743 sp_phasor *p = get_phasor(vm, args[0]);
744 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "phasor-set-freq!: expected phasor"); return SIGIL_FALSE; }
745 p->freq = (SPFLOAT)sigil_as_flonum(args[1]);
746 return SIGIL_NIL;
749/* ============================================================
750 * FILTERS
751 * ============================================================ */
753/* (make-moogladder freq res) -> <moogladder> */
754static Value native_make_moogladder(SigilVM *vm, int argc, Value *args)
756 CHECK_SP(vm, "make-moogladder");
757 sp_moogladder *p;
758 sp_moogladder_create(&p);
759 sp_moogladder_init(g_sp, p);
760 p->freq = (SPFLOAT)sigil_as_flonum(args[0]);
761 p->res = (SPFLOAT)sigil_as_flonum(args[1]);
762 ENSURE_TAG(vm, tt_moogladder, "dsp-moogladder");
763 return sigil_make_foreign(vm, tt_moogladder, p, destroy_moogladder, sizeof(sp_moogladder));
766/* (make-butlp freq) -> <butlp> */
767static Value native_make_butlp(SigilVM *vm, int argc, Value *args)
769 CHECK_SP(vm, "make-butlp");
770 sp_butlp *p;
771 sp_butlp_create(&p);
772 sp_butlp_init(g_sp, p);
773 p->freq = (SPFLOAT)sigil_as_flonum(args[0]);
774 ENSURE_TAG(vm, tt_butlp, "dsp-butlp");
775 return sigil_make_foreign(vm, tt_butlp, p, destroy_butlp, sizeof(sp_butlp));
778/* (make-buthp freq) -> <buthp> */
779static Value native_make_buthp(SigilVM *vm, int argc, Value *args)
781 CHECK_SP(vm, "make-buthp");
782 sp_buthp *p;
783 sp_buthp_create(&p);
784 sp_buthp_init(g_sp, p);
785 p->freq = (SPFLOAT)sigil_as_flonum(args[0]);
786 ENSURE_TAG(vm, tt_buthp, "dsp-buthp");
787 return sigil_make_foreign(vm, tt_buthp, p, destroy_buthp, sizeof(sp_buthp));
790/* (make-lpf18 cutoff res dist) -> <lpf18> */
791static Value native_make_lpf18(SigilVM *vm, int argc, Value *args)
793 CHECK_SP(vm, "make-lpf18");
794 sp_lpf18 *p;
795 sp_lpf18_create(&p);
796 sp_lpf18_init(g_sp, p);
797 p->cutoff = (SPFLOAT)sigil_as_flonum(args[0]);
798 p->res = (SPFLOAT)sigil_as_flonum(args[1]);
799 p->dist = (SPFLOAT)sigil_as_flonum(args[2]);
800 ENSURE_TAG(vm, tt_lpf18, "dsp-lpf18");
801 return sigil_make_foreign(vm, tt_lpf18, p, destroy_lpf18, sizeof(sp_lpf18));
804/* (make-tone freq) -> <tone> */
805static Value native_make_tone(SigilVM *vm, int argc, Value *args)
807 CHECK_SP(vm, "make-tone");
808 sp_tone *p;
809 sp_tone_create(&p);
810 sp_tone_init(g_sp, p);
811 p->hp = (SPFLOAT)sigil_as_flonum(args[0]);
812 ENSURE_TAG(vm, tt_tone, "dsp-tone");
813 return sigil_make_foreign(vm, tt_tone, p, destroy_tone, sizeof(sp_tone));
816/* (make-atone freq) -> <atone> */
817static Value native_make_atone(SigilVM *vm, int argc, Value *args)
819 CHECK_SP(vm, "make-atone");
820 sp_atone *p;
821 sp_atone_create(&p);
822 sp_atone_init(g_sp, p);
823 p->hp = (SPFLOAT)sigil_as_flonum(args[0]);
824 ENSURE_TAG(vm, tt_atone, "dsp-atone");
825 return sigil_make_foreign(vm, tt_atone, p, destroy_atone, sizeof(sp_atone));
828/* (make-dcblock) -> <dcblock> */
829static Value native_make_dcblock(SigilVM *vm, int argc, Value *args)
831 CHECK_SP(vm, "make-dcblock");
832 sp_dcblock *p;
833 sp_dcblock_create(&p);
834 sp_dcblock_init(g_sp, p);
835 ENSURE_TAG(vm, tt_dcblock, "dsp-dcblock");
836 return sigil_make_foreign(vm, tt_dcblock, p, destroy_dcblock, sizeof(sp_dcblock));
839/* Generic filter compute: (filter-compute filter input) -> flonum */
840static Value native_filter_compute(SigilVM *vm, int argc, Value *args)
842 SPFLOAT in = (SPFLOAT)sigil_as_flonum(args[1]);
843 SPFLOAT out = 0;
845 /* Try each filter type */
846 sp_moogladder *ml; sp_butlp *bl; sp_buthp *bh; sp_lpf18 *l18;
847 sp_tone *tn; sp_atone *atn; sp_dcblock *dc;
849 if ((ml = get_moogladder(vm, args[0]))) { sp_moogladder_compute(g_sp, ml, &in, &out); }
850 else if ((bl = get_butlp(vm, args[0]))) { sp_butlp_compute(g_sp, bl, &in, &out); }
851 else if ((bh = get_buthp(vm, args[0]))) { sp_buthp_compute(g_sp, bh, &in, &out); }
852 else if ((l18 = get_lpf18(vm, args[0]))) { sp_lpf18_compute(g_sp, l18, &in, &out); }
853 else if ((tn = get_tone(vm, args[0]))) { sp_tone_compute(g_sp, tn, &in, &out); }
854 else if ((atn = get_atone(vm, args[0]))) { sp_atone_compute(g_sp, atn, &in, &out); }
855 else if ((dc = get_dcblock(vm, args[0]))) { sp_dcblock_compute(g_sp, dc, &in, &out); }
856 else {
857 sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "filter-compute: expected filter object");
858 return SIGIL_FALSE;
859 }
860 return sigil_flonum((double)out);
863/* (filter-set-freq! filter freq) */
864static Value native_filter_set_freq(SigilVM *vm, int argc, Value *args)
866 SPFLOAT freq = (SPFLOAT)sigil_as_flonum(args[1]);
867 sp_moogladder *ml; sp_butlp *bl; sp_buthp *bh; sp_lpf18 *l18;
868 sp_tone *tn; sp_atone *atn;
870 if ((ml = get_moogladder(vm, args[0]))) { ml->freq = freq; }
871 else if ((bl = get_butlp(vm, args[0]))) { bl->freq = freq; }
872 else if ((bh = get_buthp(vm, args[0]))) { bh->freq = freq; }
873 else if ((l18 = get_lpf18(vm, args[0]))) { l18->cutoff = freq; }
874 else if ((tn = get_tone(vm, args[0]))) { tn->hp = freq; }
875 else if ((atn = get_atone(vm, args[0]))) { atn->hp = freq; }
876 else {
877 sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "filter-set-freq!: expected filter object");
878 return SIGIL_FALSE;
879 }
880 return SIGIL_NIL;
883/* (filter-set-res! filter res) — for moogladder, lpf18 */
884static Value native_filter_set_res(SigilVM *vm, int argc, Value *args)
886 SPFLOAT res = (SPFLOAT)sigil_as_flonum(args[1]);
887 sp_moogladder *ml; sp_lpf18 *l18;
889 if ((ml = get_moogladder(vm, args[0]))) { ml->res = res; }
890 else if ((l18 = get_lpf18(vm, args[0]))) { l18->res = res; }
891 else {
892 sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "filter-set-res!: expected resonant filter");
893 return SIGIL_FALSE;
894 }
895 return SIGIL_NIL;
898/* ============================================================
899 * ENVELOPES
900 * ============================================================ */
902/* (make-adsr atk dec sus rel) -> <adsr> */
903static Value native_make_adsr(SigilVM *vm, int argc, Value *args)
905 CHECK_SP(vm, "make-adsr");
906 sp_adsr *p;
907 sp_adsr_create(&p);
908 sp_adsr_init(g_sp, p);
909 p->atk = (SPFLOAT)sigil_as_flonum(args[0]);
910 p->dec = (SPFLOAT)sigil_as_flonum(args[1]);
911 p->sus = (SPFLOAT)sigil_as_flonum(args[2]);
912 p->rel = (SPFLOAT)sigil_as_flonum(args[3]);
913 ENSURE_TAG(vm, tt_adsr, "dsp-adsr");
914 return sigil_make_foreign(vm, tt_adsr, p, destroy_adsr, sizeof(sp_adsr));
917/* (adsr-compute adsr gate) -> flonum */
918static Value native_adsr_compute(SigilVM *vm, int argc, Value *args)
920 sp_adsr *p = get_adsr(vm, args[0]);
921 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "adsr-compute: expected adsr"); return SIGIL_FALSE; }
922 SPFLOAT gate = (SPFLOAT)sigil_as_flonum(args[1]);
923 SPFLOAT out = 0;
924 sp_adsr_compute(g_sp, p, &gate, &out);
925 return sigil_flonum((double)out);
928/* (make-tadsr atk dec sus rel) -> <tadsr> */
929static Value native_make_tadsr(SigilVM *vm, int argc, Value *args)
931 CHECK_SP(vm, "make-tadsr");
932 sp_tadsr *p;
933 sp_tadsr_create(&p);
934 sp_tadsr_init(g_sp, p);
935 p->atk = (SPFLOAT)sigil_as_flonum(args[0]);
936 p->dec = (SPFLOAT)sigil_as_flonum(args[1]);
937 p->sus = (SPFLOAT)sigil_as_flonum(args[2]);
938 p->rel = (SPFLOAT)sigil_as_flonum(args[3]);
939 ENSURE_TAG(vm, tt_tadsr, "dsp-tadsr");
940 return sigil_make_foreign(vm, tt_tadsr, p, destroy_tadsr, sizeof(sp_tadsr));
943/* (tadsr-compute tadsr trigger) -> flonum */
944static Value native_tadsr_compute(SigilVM *vm, int argc, Value *args)
946 sp_tadsr *p = get_tadsr(vm, args[0]);
947 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "tadsr-compute: expected tadsr"); return SIGIL_FALSE; }
948 SPFLOAT trig = (SPFLOAT)sigil_as_flonum(args[1]);
949 SPFLOAT out = 0;
950 sp_tadsr_compute(g_sp, p, &trig, &out);
951 return sigil_flonum((double)out);
954/* (make-tenv atk hold rel) -> <tenv> */
955static Value native_make_tenv(SigilVM *vm, int argc, Value *args)
957 CHECK_SP(vm, "make-tenv");
958 sp_tenv *p;
959 sp_tenv_create(&p);
960 sp_tenv_init(g_sp, p);
961 p->atk = (SPFLOAT)sigil_as_flonum(args[0]);
962 p->hold = (SPFLOAT)sigil_as_flonum(args[1]);
963 p->rel = (SPFLOAT)sigil_as_flonum(args[2]);
964 ENSURE_TAG(vm, tt_tenv, "dsp-tenv");
965 return sigil_make_foreign(vm, tt_tenv, p, destroy_tenv, sizeof(sp_tenv));
968/* (tenv-compute tenv trigger) -> flonum */
969static Value native_tenv_compute(SigilVM *vm, int argc, Value *args)
971 sp_tenv *p = get_tenv(vm, args[0]);
972 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "tenv-compute: expected tenv"); return SIGIL_FALSE; }
973 SPFLOAT trig = (SPFLOAT)sigil_as_flonum(args[1]);
974 SPFLOAT out = 0;
975 sp_tenv_compute(g_sp, p, &trig, &out);
976 return sigil_flonum((double)out);
979/* (make-line a dur b) -> <line> */
980static Value native_make_line(SigilVM *vm, int argc, Value *args)
982 CHECK_SP(vm, "make-line");
983 sp_line *p;
984 sp_line_create(&p);
985 sp_line_init(g_sp, p);
986 p->a = (SPFLOAT)sigil_as_flonum(args[0]);
987 p->dur = (SPFLOAT)sigil_as_flonum(args[1]);
988 p->b = (SPFLOAT)sigil_as_flonum(args[2]);
989 ENSURE_TAG(vm, tt_line, "dsp-line");
990 return sigil_make_foreign(vm, tt_line, p, destroy_line, sizeof(sp_line));
993/* (line-compute line trigger) -> flonum */
994static Value native_line_compute(SigilVM *vm, int argc, Value *args)
996 sp_line *p = get_line(vm, args[0]);
997 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "line-compute: expected line"); return SIGIL_FALSE; }
998 SPFLOAT trig = (SPFLOAT)sigil_as_flonum(args[1]);
999 SPFLOAT out = 0;
1000 sp_line_compute(g_sp, p, &trig, &out);
1001 return sigil_flonum((double)out);
1004/* (make-expon a dur b) -> <expon> */
1005static Value native_make_expon(SigilVM *vm, int argc, Value *args)
1007 CHECK_SP(vm, "make-expon");
1008 sp_expon *p;
1009 sp_expon_create(&p);
1010 sp_expon_init(g_sp, p);
1011 p->a = (SPFLOAT)sigil_as_flonum(args[0]);
1012 p->dur = (SPFLOAT)sigil_as_flonum(args[1]);
1013 p->b = (SPFLOAT)sigil_as_flonum(args[2]);
1014 ENSURE_TAG(vm, tt_expon, "dsp-expon");
1015 return sigil_make_foreign(vm, tt_expon, p, destroy_expon, sizeof(sp_expon));
1018/* (expon-compute expon trigger) -> flonum */
1019static Value native_expon_compute(SigilVM *vm, int argc, Value *args)
1021 sp_expon *p = get_expon(vm, args[0]);
1022 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "expon-compute: expected expon"); return SIGIL_FALSE; }
1023 SPFLOAT trig = (SPFLOAT)sigil_as_flonum(args[1]);
1024 SPFLOAT out = 0;
1025 sp_expon_compute(g_sp, p, &trig, &out);
1026 return sigil_flonum((double)out);
1029/* (make-port smoothing) -> <port> */
1030static Value native_make_port(SigilVM *vm, int argc, Value *args)
1032 CHECK_SP(vm, "make-port");
1033 sp_port *p;
1034 sp_port_create(&p);
1035 sp_port_init(g_sp, p);
1036 p->smooth = (SPFLOAT)sigil_as_flonum(args[0]);
1037 ENSURE_TAG(vm, tt_port, "dsp-port");
1038 return sigil_make_foreign(vm, tt_port, p, destroy_port, sizeof(sp_port));
1041/* (port-compute port input) -> flonum */
1042static Value native_port_compute(SigilVM *vm, int argc, Value *args)
1044 sp_port *p = get_port(vm, args[0]);
1045 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "port-compute: expected port"); return SIGIL_FALSE; }
1046 SPFLOAT in = (SPFLOAT)sigil_as_flonum(args[1]);
1047 SPFLOAT out = 0;
1048 sp_port_compute(g_sp, p, &in, &out);
1049 return sigil_flonum((double)out);
1052/* ============================================================
1053 * REVERB & DELAY
1054 * ============================================================ */
1056/* (make-revsc feedback lpfreq) -> <revsc> */
1057static Value native_make_revsc(SigilVM *vm, int argc, Value *args)
1059 CHECK_SP(vm, "make-revsc");
1060 sp_revsc *p;
1061 sp_revsc_create(&p);
1062 sp_revsc_init(g_sp, p);
1063 p->feedback = (SPFLOAT)sigil_as_flonum(args[0]);
1064 p->lpfreq = (SPFLOAT)sigil_as_flonum(args[1]);
1065 ENSURE_TAG(vm, tt_revsc, "dsp-revsc");
1066 return sigil_make_foreign(vm, tt_revsc, p, destroy_revsc, sizeof(sp_revsc));
1069/* (revsc-compute revsc in-l in-r) -> (values out-l out-r) */
1070static Value native_revsc_compute(SigilVM *vm, int argc, Value *args)
1072 sp_revsc *p = get_revsc(vm, args[0]);
1073 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "revsc-compute: expected revsc"); return SIGIL_FALSE; }
1074 SPFLOAT inL = (SPFLOAT)sigil_as_flonum(args[1]);
1075 SPFLOAT inR = (SPFLOAT)sigil_as_flonum(args[2]);
1076 SPFLOAT outL = 0, outR = 0;
1077 sp_revsc_compute(g_sp, p, &inL, &inR, &outL, &outR);
1078 /* Return as a pair for simplicity */
1079 Value l = sigil_flonum((double)outL);
1080 Value r = sigil_flonum((double)outR);
1081 return sigil_cons(vm,l, r);
1084/* (revsc-set-feedback! revsc feedback) */
1085static Value native_revsc_set_feedback(SigilVM *vm, int argc, Value *args)
1087 sp_revsc *p = get_revsc(vm, args[0]);
1088 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "revsc-set-feedback!: expected revsc"); return SIGIL_FALSE; }
1089 p->feedback = (SPFLOAT)sigil_as_flonum(args[1]);
1090 return SIGIL_NIL;
1093/* (revsc-set-lpfreq! revsc lpfreq) */
1094static Value native_revsc_set_lpfreq(SigilVM *vm, int argc, Value *args)
1096 sp_revsc *p = get_revsc(vm, args[0]);
1097 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "revsc-set-lpfreq!: expected revsc"); return SIGIL_FALSE; }
1098 p->lpfreq = (SPFLOAT)sigil_as_flonum(args[1]);
1099 return SIGIL_NIL;
1102/* (make-zitarev) -> <zitarev> */
1103static Value native_make_zitarev(SigilVM *vm, int argc, Value *args)
1105 CHECK_SP(vm, "make-zitarev");
1106 sp_zitarev *p;
1107 sp_zitarev_create(&p);
1108 sp_zitarev_init(g_sp, p);
1109 ENSURE_TAG(vm, tt_zitarev, "dsp-zitarev");
1110 return sigil_make_foreign(vm, tt_zitarev, p, destroy_zitarev, sizeof(sp_zitarev));
1113/* (zitarev-compute zitarev in-l in-r) -> (out-l . out-r) */
1114static Value native_zitarev_compute(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-compute: expected zitarev"); return SIGIL_FALSE; }
1118 SPFLOAT inL = (SPFLOAT)sigil_as_flonum(args[1]);
1119 SPFLOAT inR = (SPFLOAT)sigil_as_flonum(args[2]);
1120 SPFLOAT outL = 0, outR = 0;
1121 sp_zitarev_compute(g_sp, p, &inL, &inR, &outL, &outR);
1122 Value l = sigil_flonum((double)outL);
1123 Value r = sigil_flonum((double)outR);
1124 return sigil_cons(vm,l, r);
1127/* (zitarev-set-mix! zitarev mix) */
1128static Value native_zitarev_set_mix(SigilVM *vm, int argc, Value *args)
1130 sp_zitarev *p = get_zitarev(vm, args[0]);
1131 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "zitarev-set-mix!: expected zitarev"); return SIGIL_FALSE; }
1132 *p->mix = (SPFLOAT)sigil_as_flonum(args[1]);
1133 return SIGIL_NIL;
1136/* (zitarev-set-level! zitarev level) */
1137static Value native_zitarev_set_level(SigilVM *vm, int argc, Value *args)
1139 sp_zitarev *p = get_zitarev(vm, args[0]);
1140 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "zitarev-set-level!: expected zitarev"); return SIGIL_FALSE; }
1141 *p->level = (SPFLOAT)sigil_as_flonum(args[1]);
1142 return SIGIL_NIL;
1145/* (make-delay time feedback) -> <delay> */
1146static Value native_make_delay(SigilVM *vm, int argc, Value *args)
1148 CHECK_SP(vm, "make-delay");
1149 SPFLOAT time = (SPFLOAT)sigil_as_flonum(args[0]);
1150 SPFLOAT feedback = (SPFLOAT)sigil_as_flonum(args[1]);
1151 sp_delay *p;
1152 sp_delay_create(&p);
1153 sp_delay_init(g_sp, p, time);
1154 p->feedback = feedback;
1155 ENSURE_TAG(vm, tt_delay, "dsp-delay");
1156 return sigil_make_foreign(vm, tt_delay, p, destroy_delay,
1157 sizeof(sp_delay) + (size_t)(time * g_sp->sr) * sizeof(SPFLOAT));
1160/* (delay-compute delay input) -> flonum */
1161static Value native_delay_compute(SigilVM *vm, int argc, Value *args)
1163 sp_delay *p = get_delay(vm, args[0]);
1164 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "delay-compute: expected delay"); return SIGIL_FALSE; }
1165 SPFLOAT in = (SPFLOAT)sigil_as_flonum(args[1]);
1166 SPFLOAT out = 0;
1167 sp_delay_compute(g_sp, p, &in, &out);
1168 return sigil_flonum((double)out);
1171/* (delay-set-feedback! delay feedback) */
1172static Value native_delay_set_feedback(SigilVM *vm, int argc, Value *args)
1174 sp_delay *p = get_delay(vm, args[0]);
1175 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "delay-set-feedback!: expected delay"); return SIGIL_FALSE; }
1176 p->feedback = (SPFLOAT)sigil_as_flonum(args[1]);
1177 return SIGIL_NIL;
1180/* (make-vdelay maxdel feedback) -> <vdelay> */
1181static Value native_make_vdelay(SigilVM *vm, int argc, Value *args)
1183 CHECK_SP(vm, "make-vdelay");
1184 SPFLOAT maxdel = (SPFLOAT)sigil_as_flonum(args[0]);
1185 SPFLOAT feedback = (SPFLOAT)sigil_as_flonum(args[1]);
1186 sp_vdelay *p;
1187 sp_vdelay_create(&p);
1188 sp_vdelay_init(g_sp, p, maxdel);
1189 p->feedback = feedback;
1190 ENSURE_TAG(vm, tt_vdelay, "dsp-vdelay");
1191 return sigil_make_foreign(vm, tt_vdelay, p, destroy_vdelay,
1192 sizeof(sp_vdelay) + (size_t)(maxdel * g_sp->sr) * sizeof(SPFLOAT));
1195/* (vdelay-compute vdelay input) -> flonum */
1196static Value native_vdelay_compute(SigilVM *vm, int argc, Value *args)
1198 sp_vdelay *p = get_vdelay(vm, args[0]);
1199 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "vdelay-compute: expected vdelay"); return SIGIL_FALSE; }
1200 SPFLOAT in = (SPFLOAT)sigil_as_flonum(args[1]);
1201 SPFLOAT out = 0;
1202 sp_vdelay_compute(g_sp, p, &in, &out);
1203 return sigil_flonum((double)out);
1206/* (vdelay-set-del! vdelay del) */
1207static Value native_vdelay_set_del(SigilVM *vm, int argc, Value *args)
1209 sp_vdelay *p = get_vdelay(vm, args[0]);
1210 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "vdelay-set-del!: expected vdelay"); return SIGIL_FALSE; }
1211 p->del = (SPFLOAT)sigil_as_flonum(args[1]);
1212 return SIGIL_NIL;
1215/* (vdelay-set-feedback! vdelay feedback) */
1216static Value native_vdelay_set_feedback(SigilVM *vm, int argc, Value *args)
1218 sp_vdelay *p = get_vdelay(vm, args[0]);
1219 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "vdelay-set-feedback!: expected vdelay"); return SIGIL_FALSE; }
1220 p->feedback = (SPFLOAT)sigil_as_flonum(args[1]);
1221 return SIGIL_NIL;
1224/* ============================================================
1225 * NOISE
1226 * ============================================================ */
1228/* (make-noise amp) -> <noise> */
1229static Value native_make_noise(SigilVM *vm, int argc, Value *args)
1231 CHECK_SP(vm, "make-noise");
1232 sp_noise *p;
1233 sp_noise_create(&p);
1234 sp_noise_init(g_sp, p);
1235 p->amp = (SPFLOAT)sigil_as_flonum(args[0]);
1236 ENSURE_TAG(vm, tt_noise, "dsp-noise");
1237 return sigil_make_foreign(vm, tt_noise, p, destroy_noise, sizeof(sp_noise));
1240/* (noise-compute noise) -> flonum */
1241static Value native_noise_compute(SigilVM *vm, int argc, Value *args)
1243 sp_noise *p = get_noise(vm, args[0]);
1244 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "noise-compute: expected noise"); return SIGIL_FALSE; }
1245 SPFLOAT in = 0, out = 0;
1246 sp_noise_compute(g_sp, p, &in, &out);
1247 return sigil_flonum((double)out);
1250/* (make-pinknoise amp) -> <pinknoise> */
1251static Value native_make_pinknoise(SigilVM *vm, int argc, Value *args)
1253 CHECK_SP(vm, "make-pinknoise");
1254 sp_pinknoise *p;
1255 sp_pinknoise_create(&p);
1256 sp_pinknoise_init(g_sp, p);
1257 p->amp = (SPFLOAT)sigil_as_flonum(args[0]);
1258 ENSURE_TAG(vm, tt_pinknoise, "dsp-pinknoise");
1259 return sigil_make_foreign(vm, tt_pinknoise, p, destroy_pinknoise, sizeof(sp_pinknoise));
1262/* (pinknoise-compute pinknoise) -> flonum */
1263static Value native_pinknoise_compute(SigilVM *vm, int argc, Value *args)
1265 sp_pinknoise *p = get_pinknoise(vm, args[0]);
1266 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "pinknoise-compute: expected pinknoise"); return SIGIL_FALSE; }
1267 SPFLOAT in = 0, out = 0;
1268 sp_pinknoise_compute(g_sp, p, &in, &out);
1269 return sigil_flonum((double)out);
1272/* (make-brown) -> <brown> */
1273static Value native_make_brown(SigilVM *vm, int argc, Value *args)
1275 CHECK_SP(vm, "make-brown");
1276 sp_brown *p;
1277 sp_brown_create(&p);
1278 sp_brown_init(g_sp, p);
1279 ENSURE_TAG(vm, tt_brown, "dsp-brown");
1280 return sigil_make_foreign(vm, tt_brown, p, destroy_brown, sizeof(sp_brown));
1283/* (brown-compute brown) -> flonum */
1284static Value native_brown_compute(SigilVM *vm, int argc, Value *args)
1286 sp_brown *p = get_brown(vm, args[0]);
1287 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "brown-compute: expected brown"); return SIGIL_FALSE; }
1288 SPFLOAT in = 0, out = 0;
1289 sp_brown_compute(g_sp, p, &in, &out);
1290 return sigil_flonum((double)out);
1293/* ============================================================
1294 * KARPLUS-STRONG
1295 * ============================================================ */
1297/* (make-ks freq decay excite-gain) -> <ks> */
1298static Value native_make_ks(SigilVM *vm, int argc, Value *args)
1300 (void)argc;
1301 CHECK_SP(vm, "make-ks");
1302 SPFLOAT freq = (SPFLOAT)sigil_as_flonum(args[0]);
1303 SPFLOAT decay = (SPFLOAT)sigil_as_flonum(args[1]);
1304 SPFLOAT gain = (SPFLOAT)sigil_as_flonum(args[2]);
1305 sp_ks *p;
1306 sp_ks_create(&p);
1307 sp_ks_init(g_sp, p, freq, decay, gain);
1308 ENSURE_TAG(vm, tt_ks, "dsp-ks");
1309 return sigil_make_foreign(vm, tt_ks, p, destroy_ks,
1310 sizeof(sp_ks) + (size_t)p->bufcap * sizeof(SPFLOAT));
1313/* (ks-compute ks gate) -> flonum */
1314static Value native_ks_compute(SigilVM *vm, int argc, Value *args)
1316 (void)argc;
1317 sp_ks *p = get_ks(vm, args[0]);
1318 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "ks-compute: expected ks"); return SIGIL_FALSE; }
1319 SPFLOAT gate = (SPFLOAT)sigil_as_flonum(args[1]);
1320 SPFLOAT out = 0.0f;
1321 sp_ks_compute(g_sp, p, &gate, &out);
1322 return sigil_flonum((double)out);
1325/* (ks-trigger! ks) — manual excitation */
1326static Value native_ks_trigger(SigilVM *vm, int argc, Value *args)
1328 (void)argc;
1329 sp_ks *p = get_ks(vm, args[0]);
1330 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "ks-trigger!: expected ks"); return SIGIL_FALSE; }
1331 sp_ks_trigger(g_sp, p);
1332 return SIGIL_NIL;
1335/* (ks-set-freq! ks freq) */
1336static Value native_ks_set_freq(SigilVM *vm, int argc, Value *args)
1338 (void)argc;
1339 sp_ks *p = get_ks(vm, args[0]);
1340 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "ks-set-freq!: expected ks"); return SIGIL_FALSE; }
1341 p->freq = (SPFLOAT)sigil_as_flonum(args[1]);
1342 sp_ks_retune(g_sp, p);
1343 return SIGIL_NIL;
1346/* (ks-set-decay! ks decay) */
1347static Value native_ks_set_decay(SigilVM *vm, int argc, Value *args)
1349 (void)argc;
1350 sp_ks *p = get_ks(vm, args[0]);
1351 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "ks-set-decay!: expected ks"); return SIGIL_FALSE; }
1352 p->decay = (SPFLOAT)sigil_as_flonum(args[1]);
1353 sp_ks_retune(g_sp, p);
1354 return SIGIL_NIL;
1357/* (ks-set-excite-gain! ks gain) */
1358static Value native_ks_set_excite_gain(SigilVM *vm, int argc, Value *args)
1360 (void)argc;
1361 sp_ks *p = get_ks(vm, args[0]);
1362 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "ks-set-excite-gain!: expected ks"); return SIGIL_FALSE; }
1363 p->excite_gain = (SPFLOAT)sigil_as_flonum(args[1]);
1364 return SIGIL_NIL;
1367/* ============================================================
1368 * FM4 — four-operator FM voice (fm4.h)
1369 * ============================================================ */
1371#define FM4_GET(vm, args, name) \
1372 sp_fm4 *p = get_fm4(vm, args[0]); \
1373 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, name ": expected fm4"); return SIGIL_FALSE; }
1375/* (make-fm4 algorithm feedback vel-depth vel-curve) */
1376static Value native_make_fm4(SigilVM *vm, int argc, Value *args)
1378 (void)argc;
1379 CHECK_SP(vm, "make-fm4");
1380 int alg = (int)as_number(args[0]);
1381 if (alg < 0 || alg >= SP_FM4_ALGORITHMS) {
1382 sigil__vm_set_error(vm, SIGIL_ERR_RUNTIME, "make-fm4: algorithm out of range 0..7");
1383 return SIGIL_FALSE;
1385 sp_fm4 *p;
1386 sp_fm4_create(&p);
1387 sp_fm4_init(g_sp, p);
1388 sp_fm4_set_algorithm(p, alg);
1389 p->feedback = (SPFLOAT)as_number(args[1]);
1390 p->vel_depth = (SPFLOAT)as_number(args[2]);
1391 p->vel_curve = (SPFLOAT)as_number(args[3]);
1392 ENSURE_TAG(vm, tt_fm4, "dsp-fm4");
1393 return sigil_make_foreign(vm, tt_fm4, p, destroy_fm4, sizeof(sp_fm4));
1396/* (fm4-set-op! fm4 op ratio level attack decay sustain release) — op is 0..3 */
1397static Value native_fm4_set_op(SigilVM *vm, int argc, Value *args)
1399 (void)argc;
1400 FM4_GET(vm, args, "fm4-set-op!");
1401 int op = (int)as_number(args[1]);
1402 if (op < 0 || op >= SP_FM4_OPS) {
1403 sigil__vm_set_error(vm, SIGIL_ERR_RUNTIME, "fm4-set-op!: operator out of range 0..3");
1404 return SIGIL_FALSE;
1406 sp_fm4_set_op(p, op,
1407 (SPFLOAT)as_number(args[2]), (SPFLOAT)as_number(args[3]),
1408 (SPFLOAT)as_number(args[4]), (SPFLOAT)as_number(args[5]),
1409 (SPFLOAT)as_number(args[6]), (SPFLOAT)as_number(args[7]));
1410 return SIGIL_NIL;
1413/* (fm4-set-algorithm! fm4 algorithm) */
1414static Value native_fm4_set_algorithm(SigilVM *vm, int argc, Value *args)
1416 (void)argc;
1417 FM4_GET(vm, args, "fm4-set-algorithm!");
1418 if (sp_fm4_set_algorithm(p, (int)as_number(args[1])) != SP_OK) {
1419 sigil__vm_set_error(vm, SIGIL_ERR_RUNTIME, "fm4-set-algorithm!: algorithm out of range 0..7");
1420 return SIGIL_FALSE;
1422 return SIGIL_NIL;
1425/* (fm4-set-feedback! fm4 feedback) */
1426static Value native_fm4_set_feedback(SigilVM *vm, int argc, Value *args)
1428 (void)argc;
1429 FM4_GET(vm, args, "fm4-set-feedback!");
1430 p->feedback = (SPFLOAT)as_number(args[1]);
1431 return SIGIL_NIL;
1434/* (fm4-compute fm4 freq gate index velocity) -> flonum */
1435static Value native_fm4_compute(SigilVM *vm, int argc, Value *args)
1437 (void)argc;
1438 FM4_GET(vm, args, "fm4-compute");
1439 SPFLOAT out = 0.0f;
1440 sp_fm4_compute(g_sp, p,
1441 (SPFLOAT)as_number(args[1]), (SPFLOAT)as_number(args[2]),
1442 (SPFLOAT)as_number(args[3]), (SPFLOAT)as_number(args[4]),
1443 &out);
1444 return sigil_flonum((double)out);
1447/* (fm4-algorithm-name algorithm) -> string */
1448static Value native_fm4_algorithm_name(SigilVM *vm, int argc, Value *args)
1450 (void)argc;
1451 int alg = (int)as_number(args[0]);
1452 if (alg < 0 || alg >= SP_FM4_ALGORITHMS) return SIGIL_FALSE;
1453 const char *name = sp_fm4_algorithm_table()[alg].name;
1454 return sigil_make_string(vm, name, strlen(name));
1457/* ============================================================
1458 * FATTENERS — chorus, crush, compressor (fx4.h)
1459 * ============================================================ */
1461/* (make-chorus) */
1462static Value native_make_chorus(SigilVM *vm, int argc, Value *args)
1464 (void)argc; (void)args;
1465 CHECK_SP(vm, "make-chorus");
1466 sp_chorus *p;
1467 sp_chorus_create(&p);
1468 sp_chorus_init(g_sp, p);
1469 ENSURE_TAG(vm, tt_chorus, "dsp-chorus");
1470 return sigil_make_foreign(vm, tt_chorus, p, destroy_chorus,
1471 sizeof(sp_chorus) + (size_t)p->bufsize * sizeof(SPFLOAT));
1474/* (chorus-compute chorus in rate depth-ms mix) -> flonum */
1475static Value native_chorus_compute(SigilVM *vm, int argc, Value *args)
1477 (void)argc;
1478 sp_chorus *p = get_chorus(vm, args[0]);
1479 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "chorus-compute: expected chorus"); return SIGIL_FALSE; }
1480 SPFLOAT out = 0.0f;
1481 sp_chorus_compute(g_sp, p, (SPFLOAT)as_number(args[1]),
1482 (SPFLOAT)as_number(args[2]), (SPFLOAT)as_number(args[3]),
1483 (SPFLOAT)as_number(args[4]), &out);
1484 return sigil_flonum((double)out);
1487/* (make-crush) */
1488static Value native_make_crush(SigilVM *vm, int argc, Value *args)
1490 (void)argc; (void)args;
1491 CHECK_SP(vm, "make-crush");
1492 sp_crush *p;
1493 sp_crush_create(&p);
1494 sp_crush_init(g_sp, p);
1495 ENSURE_TAG(vm, tt_crush, "dsp-crush");
1496 return sigil_make_foreign(vm, tt_crush, p, destroy_crush, sizeof(sp_crush));
1499/* (crush-compute crush in drive bits rate-div) -> flonum */
1500static Value native_crush_compute(SigilVM *vm, int argc, Value *args)
1502 (void)argc;
1503 sp_crush *p = get_crush(vm, args[0]);
1504 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "crush-compute: expected crush"); return SIGIL_FALSE; }
1505 SPFLOAT out = 0.0f;
1506 sp_crush_compute(g_sp, p, (SPFLOAT)as_number(args[1]),
1507 (SPFLOAT)as_number(args[2]), (SPFLOAT)as_number(args[3]),
1508 (SPFLOAT)as_number(args[4]), &out);
1509 return sigil_flonum((double)out);
1512/* (make-comp attack release makeup-db) */
1513static Value native_make_comp(SigilVM *vm, int argc, Value *args)
1515 (void)argc;
1516 CHECK_SP(vm, "make-comp");
1517 sp_comp *p;
1518 sp_comp_create(&p);
1519 sp_comp_init(g_sp, p, (SPFLOAT)as_number(args[0]),
1520 (SPFLOAT)as_number(args[1]), (SPFLOAT)as_number(args[2]));
1521 ENSURE_TAG(vm, tt_comp, "dsp-comp");
1522 return sigil_make_foreign(vm, tt_comp, p, destroy_comp, sizeof(sp_comp));
1525/* (comp-compute comp in threshold-db ratio) -> flonum */
1526static Value native_comp_compute(SigilVM *vm, int argc, Value *args)
1528 (void)argc;
1529 sp_comp *p = get_comp(vm, args[0]);
1530 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "comp-compute: expected comp"); return SIGIL_FALSE; }
1531 SPFLOAT out = 0.0f;
1532 sp_comp_compute(g_sp, p, (SPFLOAT)as_number(args[1]),
1533 (SPFLOAT)as_number(args[2]), (SPFLOAT)as_number(args[3]), &out);
1534 return sigil_flonum((double)out);
1537/* ============================================================
1538 * DYNAMICS / WAVESHAPING
1539 * ============================================================ */
1541/* (make-saturator drive dcoffset) -> <saturator> */
1542static Value native_make_saturator(SigilVM *vm, int argc, Value *args)
1544 CHECK_SP(vm, "make-saturator");
1545 sp_saturator *p;
1546 sp_saturator_create(&p);
1547 sp_saturator_init(g_sp, p);
1548 p->drive = (SPFLOAT)sigil_as_flonum(args[0]);
1549 p->dcoffset = (SPFLOAT)sigil_as_flonum(args[1]);
1550 ENSURE_TAG(vm, tt_saturator, "dsp-saturator");
1551 return sigil_make_foreign(vm, tt_saturator, p, destroy_saturator, sizeof(sp_saturator));
1554/* (saturator-compute saturator input) -> flonum */
1555static Value native_saturator_compute(SigilVM *vm, int argc, Value *args)
1557 sp_saturator *p = get_saturator(vm, args[0]);
1558 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "saturator-compute: expected saturator"); return SIGIL_FALSE; }
1559 SPFLOAT in = (SPFLOAT)sigil_as_flonum(args[1]);
1560 SPFLOAT out = 0;
1561 sp_saturator_compute(g_sp, p, &in, &out);
1562 return sigil_flonum((double)out);
1565/* (make-clip limit) -> <clip> */
1566static Value native_make_clip(SigilVM *vm, int argc, Value *args)
1568 CHECK_SP(vm, "make-clip");
1569 sp_clip *p;
1570 sp_clip_create(&p);
1571 sp_clip_init(g_sp, p);
1572 p->lim = (SPFLOAT)sigil_as_flonum(args[0]);
1573 ENSURE_TAG(vm, tt_clip, "dsp-clip");
1574 return sigil_make_foreign(vm, tt_clip, p, destroy_clip, sizeof(sp_clip));
1577/* (clip-compute clip input) -> flonum */
1578static Value native_clip_compute(SigilVM *vm, int argc, Value *args)
1580 sp_clip *p = get_clip(vm, args[0]);
1581 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "clip-compute: expected clip"); return SIGIL_FALSE; }
1582 SPFLOAT in = (SPFLOAT)sigil_as_flonum(args[1]);
1583 SPFLOAT out = 0;
1584 sp_clip_compute(g_sp, p, &in, &out);
1585 return sigil_flonum((double)out);
1588/* (make-peaklim atk rel thresh) -> <peaklim> */
1589static Value native_make_peaklim(SigilVM *vm, int argc, Value *args)
1591 CHECK_SP(vm, "make-peaklim");
1592 sp_peaklim *p;
1593 sp_peaklim_create(&p);
1594 sp_peaklim_init(g_sp, p);
1595 p->atk = (SPFLOAT)sigil_as_flonum(args[0]);
1596 p->rel = (SPFLOAT)sigil_as_flonum(args[1]);
1597 p->thresh = (SPFLOAT)sigil_as_flonum(args[2]);
1598 ENSURE_TAG(vm, tt_peaklim, "dsp-peaklim");
1599 return sigil_make_foreign(vm, tt_peaklim, p, destroy_peaklim, sizeof(sp_peaklim));
1602/* (peaklim-compute peaklim input) -> flonum */
1603static Value native_peaklim_compute(SigilVM *vm, int argc, Value *args)
1605 sp_peaklim *p = get_peaklim(vm, args[0]);
1606 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "peaklim-compute: expected peaklim"); return SIGIL_FALSE; }
1607 SPFLOAT in = (SPFLOAT)sigil_as_flonum(args[1]);
1608 SPFLOAT out = 0;
1609 sp_peaklim_compute(g_sp, p, &in, &out);
1610 return sigil_flonum((double)out);
1613/* ============================================================
1614 * SPATIAL — PAN2
1615 * ============================================================ */
1617/* (make-pan2 [pan]) -> <pan2> */
1618static Value native_make_pan2(SigilVM *vm, int argc, Value *args)
1620 CHECK_SP(vm, "make-pan2");
1621 sp_pan2 *p;
1622 sp_pan2_create(&p);
1623 sp_pan2_init(g_sp, p);
1624 p->pan = argc > 0 ? (SPFLOAT)sigil_as_flonum(args[0]) : 0.5f;
1625 ENSURE_TAG(vm, tt_pan2, "dsp-pan2");
1626 return sigil_make_foreign(vm, tt_pan2, p, destroy_pan2, sizeof(sp_pan2));
1629/* (pan2-compute pan2 input) -> (left . right) */
1630static Value native_pan2_compute(SigilVM *vm, int argc, Value *args)
1632 sp_pan2 *p = get_pan2(vm, args[0]);
1633 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "pan2-compute: expected pan2"); return SIGIL_FALSE; }
1634 SPFLOAT in = (SPFLOAT)sigil_as_flonum(args[1]);
1635 SPFLOAT outL = 0, outR = 0;
1636 sp_pan2_compute(g_sp, p, &in, &outL, &outR);
1637 Value l = sigil_flonum((double)outL);
1638 Value r = sigil_flonum((double)outR);
1639 return sigil_cons(vm,l, r);
1642/* (pan2-set-pan! pan2 pan) */
1643static Value native_pan2_set_pan(SigilVM *vm, int argc, Value *args)
1645 sp_pan2 *p = get_pan2(vm, args[0]);
1646 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "pan2-set-pan!: expected pan2"); return SIGIL_FALSE; }
1647 p->pan = (SPFLOAT)sigil_as_flonum(args[1]);
1648 return SIGIL_NIL;
1651/* ============================================================
1652 * TIMING
1653 * ============================================================ */
1655/* (make-metro freq) -> <metro> */
1656static Value native_make_metro(SigilVM *vm, int argc, Value *args)
1658 CHECK_SP(vm, "make-metro");
1659 sp_metro *p;
1660 sp_metro_create(&p);
1661 sp_metro_init(g_sp, p);
1662 p->freq = (SPFLOAT)sigil_as_flonum(args[0]);
1663 ENSURE_TAG(vm, tt_metro, "dsp-metro");
1664 return sigil_make_foreign(vm, tt_metro, p, destroy_metro, sizeof(sp_metro));
1667/* (metro-compute metro) -> flonum (0 or 1) */
1668static Value native_metro_compute(SigilVM *vm, int argc, Value *args)
1670 sp_metro *p = get_metro(vm, args[0]);
1671 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "metro-compute: expected metro"); return SIGIL_FALSE; }
1672 SPFLOAT in = 0, out = 0;
1673 sp_metro_compute(g_sp, p, &in, &out);
1674 return sigil_flonum((double)out);
1677/* (metro-set-freq! metro freq) */
1678static Value native_metro_set_freq(SigilVM *vm, int argc, Value *args)
1680 sp_metro *p = get_metro(vm, args[0]);
1681 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "metro-set-freq!: expected metro"); return SIGIL_FALSE; }
1682 p->freq = (SPFLOAT)sigil_as_flonum(args[1]);
1683 return SIGIL_NIL;
1686/* (make-clock bpm subdiv) -> <clock> */
1687static Value native_make_clock(SigilVM *vm, int argc, Value *args)
1689 CHECK_SP(vm, "make-clock");
1690 sp_clock *p;
1691 sp_clock_create(&p);
1692 sp_clock_init(g_sp, p);
1693 p->bpm = (SPFLOAT)sigil_as_flonum(args[0]);
1694 p->subdiv = (SPFLOAT)sigil_as_flonum(args[1]);
1695 ENSURE_TAG(vm, tt_clock, "dsp-clock");
1696 return sigil_make_foreign(vm, tt_clock, p, destroy_clock, sizeof(sp_clock));
1699/* (clock-compute clock trigger) -> flonum (0 or 1) */
1700static Value native_clock_compute(SigilVM *vm, int argc, Value *args)
1702 sp_clock *p = get_clock_mod(vm, args[0]);
1703 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "clock-compute: expected clock"); return SIGIL_FALSE; }
1704 SPFLOAT trig = (SPFLOAT)sigil_as_flonum(args[1]);
1705 SPFLOAT out = 0;
1706 sp_clock_compute(g_sp, p, &trig, &out);
1707 return sigil_flonum((double)out);
1710/* (clock-set-bpm! clock bpm) */
1711static Value native_clock_set_bpm(SigilVM *vm, int argc, Value *args)
1713 sp_clock *p = get_clock_mod(vm, args[0]);
1714 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "clock-set-bpm!: expected clock"); return SIGIL_FALSE; }
1715 p->bpm = (SPFLOAT)sigil_as_flonum(args[1]);
1716 return SIGIL_NIL;
1719/* (make-tgate time) -> <tgate> */
1720static Value native_make_tgate(SigilVM *vm, int argc, Value *args)
1722 CHECK_SP(vm, "make-tgate");
1723 sp_tgate *p;
1724 sp_tgate_create(&p);
1725 sp_tgate_init(g_sp, p);
1726 p->time = (SPFLOAT)sigil_as_flonum(args[0]);
1727 ENSURE_TAG(vm, tt_tgate, "dsp-tgate");
1728 return sigil_make_foreign(vm, tt_tgate, p, destroy_tgate, sizeof(sp_tgate));
1731/* (tgate-compute tgate trigger) -> flonum (0 or 1) */
1732static Value native_tgate_compute(SigilVM *vm, int argc, Value *args)
1734 sp_tgate *p = get_tgate(vm, args[0]);
1735 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "tgate-compute: expected tgate"); return SIGIL_FALSE; }
1736 SPFLOAT trig = (SPFLOAT)sigil_as_flonum(args[1]);
1737 SPFLOAT out = 0;
1738 sp_tgate_compute(g_sp, p, &trig, &out);
1739 return sigil_flonum((double)out);
1742/* ============================================================
1743 * MODULATION — RANDOM
1744 * ============================================================ */
1746/* (make-randh min max freq) -> <randh> */
1747static Value native_make_randh(SigilVM *vm, int argc, Value *args)
1749 CHECK_SP(vm, "make-randh");
1750 sp_randh *p;
1751 sp_randh_create(&p);
1752 sp_randh_init(g_sp, p);
1753 p->min = (SPFLOAT)sigil_as_flonum(args[0]);
1754 p->max = (SPFLOAT)sigil_as_flonum(args[1]);
1755 p->freq = (SPFLOAT)sigil_as_flonum(args[2]);
1756 ENSURE_TAG(vm, tt_randh, "dsp-randh");
1757 return sigil_make_foreign(vm, tt_randh, p, destroy_randh, sizeof(sp_randh));
1760/* (randh-compute randh) -> flonum */
1761static Value native_randh_compute(SigilVM *vm, int argc, Value *args)
1763 sp_randh *p = get_randh(vm, args[0]);
1764 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "randh-compute: expected randh"); return SIGIL_FALSE; }
1765 SPFLOAT in = 0, out = 0;
1766 sp_randh_compute(g_sp, p, &in, &out);
1767 return sigil_flonum((double)out);
1770/* (make-randi min max freq) -> <randi> */
1771static Value native_make_randi(SigilVM *vm, int argc, Value *args)
1773 CHECK_SP(vm, "make-randi");
1774 sp_randi *p;
1775 sp_randi_create(&p);
1776 sp_randi_init(g_sp, p);
1777 p->min = (SPFLOAT)sigil_as_flonum(args[0]);
1778 p->max = (SPFLOAT)sigil_as_flonum(args[1]);
1779 p->cps = (SPFLOAT)sigil_as_flonum(args[2]);
1780 ENSURE_TAG(vm, tt_randi, "dsp-randi");
1781 return sigil_make_foreign(vm, tt_randi, p, destroy_randi, sizeof(sp_randi));
1784/* (randi-compute randi) -> flonum */
1785static Value native_randi_compute(SigilVM *vm, int argc, Value *args)
1787 sp_randi *p = get_randi(vm, args[0]);
1788 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "randi-compute: expected randi"); return SIGIL_FALSE; }
1789 SPFLOAT in = 0, out = 0;
1790 sp_randi_compute(g_sp, p, &in, &out);
1791 return sigil_flonum((double)out);
1794/* ============================================================
1795 * UTILITY
1796 * ============================================================ */
1798/* (make-scale min max) -> <scale> */
1799static Value native_make_scale(SigilVM *vm, int argc, Value *args)
1801 CHECK_SP(vm, "make-scale");
1802 sp_scale *p;
1803 sp_scale_create(&p);
1804 sp_scale_init(g_sp, p);
1805 p->min = (SPFLOAT)sigil_as_flonum(args[0]);
1806 p->max = (SPFLOAT)sigil_as_flonum(args[1]);
1807 ENSURE_TAG(vm, tt_scale, "dsp-scale");
1808 return sigil_make_foreign(vm, tt_scale, p, destroy_scale, sizeof(sp_scale));
1811/* (scale-compute scale input) -> flonum */
1812static Value native_scale_compute(SigilVM *vm, int argc, Value *args)
1814 sp_scale *p = get_scale(vm, args[0]);
1815 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "scale-compute: expected scale"); return SIGIL_FALSE; }
1816 SPFLOAT in = (SPFLOAT)sigil_as_flonum(args[1]);
1817 SPFLOAT out = 0;
1818 sp_scale_compute(g_sp, p, &in, &out);
1819 return sigil_flonum((double)out);
1822/* (make-clamp min max) -> <clamp> */
1823static Value native_make_clamp(SigilVM *vm, int argc, Value *args)
1825 CHECK_SP(vm, "make-clamp");
1826 sp_clamp *p;
1827 sp_clamp_create(&p);
1828 sp_clamp_init(g_sp, p);
1829 p->min = (SPFLOAT)sigil_as_flonum(args[0]);
1830 p->max = (SPFLOAT)sigil_as_flonum(args[1]);
1831 ENSURE_TAG(vm, tt_clamp, "dsp-clamp");
1832 return sigil_make_foreign(vm, tt_clamp, p, destroy_clamp, sizeof(sp_clamp));
1835/* (clamp-compute clamp input) -> flonum */
1836static Value native_clamp_compute(SigilVM *vm, int argc, Value *args)
1838 sp_clamp *p = get_clamp(vm, args[0]);
1839 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "clamp-compute: expected clamp"); return SIGIL_FALSE; }
1840 SPFLOAT in = (SPFLOAT)sigil_as_flonum(args[1]);
1841 SPFLOAT out = 0;
1842 sp_clamp_compute(g_sp, p, &in, &out);
1843 return sigil_flonum((double)out);
1846/* (make-crossfade [pos]) -> <crossfade> */
1847static Value native_make_crossfade(SigilVM *vm, int argc, Value *args)
1849 CHECK_SP(vm, "make-crossfade");
1850 sp_crossfade *p;
1851 sp_crossfade_create(&p);
1852 sp_crossfade_init(g_sp, p);
1853 p->pos = argc > 0 ? (SPFLOAT)sigil_as_flonum(args[0]) : 0.5f;
1854 ENSURE_TAG(vm, tt_crossfade, "dsp-crossfade");
1855 return sigil_make_foreign(vm, tt_crossfade, p, destroy_crossfade, sizeof(sp_crossfade));
1858/* (crossfade-compute crossfade in1 in2) -> flonum */
1859static Value native_crossfade_compute(SigilVM *vm, int argc, Value *args)
1861 sp_crossfade *p = get_crossfade(vm, args[0]);
1862 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "crossfade-compute: expected crossfade"); return SIGIL_FALSE; }
1863 SPFLOAT in1 = (SPFLOAT)sigil_as_flonum(args[1]);
1864 SPFLOAT in2 = (SPFLOAT)sigil_as_flonum(args[2]);
1865 SPFLOAT out = 0;
1866 sp_crossfade_compute(g_sp, p, &in1, &in2, &out);
1867 return sigil_flonum((double)out);
1870/* (crossfade-set-pos! crossfade pos) */
1871static Value native_crossfade_set_pos(SigilVM *vm, int argc, Value *args)
1873 sp_crossfade *p = get_crossfade(vm, args[0]);
1874 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "crossfade-set-pos!: expected crossfade"); return SIGIL_FALSE; }
1875 p->pos = (SPFLOAT)sigil_as_flonum(args[1]);
1876 return SIGIL_NIL;
1879/* ============================================================
1880 * WAV OUTPUT
1881 * ============================================================ */
1883/* (make-wavout filename) -> <wavout> */
1884static Value native_make_wavout(SigilVM *vm, int argc, Value *args)
1886 CHECK_SP(vm, "make-wavout");
1887 if (!sigil_is_string(args[0])) {
1888 sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "make-wavout: expected string filename");
1889 return SIGIL_FALSE;
1891 SigilString *s = (SigilString *)sigil_as_ptr(args[0]);
1892 sp_wavout *p;
1893 sp_wavout_create(&p);
1894 sp_wavout_init(g_sp, p, s->data);
1895 ENSURE_TAG(vm, tt_wavout, "dsp-wavout");
1896 return sigil_make_foreign(vm, tt_wavout, p, destroy_wavout, 0);
1899/* (wavout-compute wavout sample) -> flonum */
1900static Value native_wavout_compute(SigilVM *vm, int argc, Value *args)
1902 sp_wavout *p = get_wavout(vm, args[0]);
1903 if (!p) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "wavout-compute: expected wavout"); return SIGIL_FALSE; }
1904 SPFLOAT in = (SPFLOAT)sigil_as_flonum(args[1]);
1905 SPFLOAT out = 0;
1906 sp_wavout_compute(g_sp, p, &in, &out);
1907 return sigil_flonum((double)out);
1910/* ============================================================
1911 * MODULE INITIALIZATION
1912 * ============================================================ */
1914void sigil__init_sigil_dsp_module(SigilVM *vm)
1916 /* === (sigil dsp core) === */
1917 SigilModule *mod = sigil_begin_module(vm, "(sigil dsp core)");
1918 if (!mod) return;
1920 sigil_module_register_native(vm, "dsp-init", native_dsp_init,
1921 SIGIL_ARITY_RANGE(0, 1), "Initialize DSP engine");
1922 sigil_module_register_native(vm, "dsp-shutdown", native_dsp_shutdown,
1923 SIGIL_ARITY_EXACT(0), "Shutdown DSP engine");
1924 sigil_module_register_native(vm, "dsp-sample-rate", native_dsp_sample_rate,
1925 SIGIL_ARITY_EXACT(0), "Get sample rate");
1927 /* Function tables */
1928 sigil_module_register_native(vm, "make-ftbl", native_make_ftbl,
1929 SIGIL_ARITY_EXACT(1), "Create function table");
1930 sigil_module_register_native(vm, "ftbl-gen-sine", native_ftbl_gen_sine,
1931 SIGIL_ARITY_EXACT(1), "Fill table with sine wave");
1932 sigil_module_register_native(vm, "ftbl-gen-triangle", native_ftbl_gen_triangle,
1933 SIGIL_ARITY_EXACT(1), "Fill table with triangle wave");
1934 sigil_module_register_native(vm, "ftbl-gen-line", native_ftbl_gen_line,
1935 SIGIL_ARITY_EXACT(2), "Fill table with line segments");
1937 sigil_module_export(vm, "dsp-init");
1938 sigil_module_export(vm, "dsp-shutdown");
1939 sigil_module_export(vm, "dsp-sample-rate");
1940 sigil_module_export(vm, "make-ftbl");
1941 sigil_module_export(vm, "ftbl-gen-sine");
1942 sigil_module_export(vm, "ftbl-gen-triangle");
1943 sigil_module_export(vm, "ftbl-gen-line");
1944 sigil_end_module(vm);
1946 /* === (sigil dsp osc) === */
1947 mod = sigil_begin_module(vm, "(sigil dsp osc)");
1948 if (!mod) return;
1950 sigil_module_register_native(vm, "make-osc", native_make_osc,
1951 SIGIL_ARITY_RANGE(2, 3), "Create wavetable osc (freq amp [phase])");
1952 sigil_module_register_native(vm, "make-osc-ft", native_make_osc_ft,
1953 SIGIL_ARITY_RANGE(3, 4), "Create wavetable osc with custom ftbl");
1954 sigil_module_register_native(vm, "osc-compute", native_osc_compute,
1955 SIGIL_ARITY_EXACT(1), "Compute one sample from osc");
1956 sigil_module_register_native(vm, "osc-set-freq!", native_osc_set_freq,
1957 SIGIL_ARITY_EXACT(2), "Set osc frequency");
1958 sigil_module_register_native(vm, "osc-set-amp!", native_osc_set_amp,
1959 SIGIL_ARITY_EXACT(2), "Set osc amplitude");
1961 sigil_module_register_native(vm, "make-posc3", native_make_posc3,
1962 SIGIL_ARITY_RANGE(2, 3), "Create cubic interp wavetable osc");
1963 sigil_module_register_native(vm, "posc3-compute", native_posc3_compute,
1964 SIGIL_ARITY_EXACT(1), "Compute one sample from posc3");
1965 sigil_module_register_native(vm, "posc3-set-freq!", native_posc3_set_freq,
1966 SIGIL_ARITY_EXACT(2), "Set posc3 frequency");
1967 sigil_module_register_native(vm, "posc3-set-amp!", native_posc3_set_amp,
1968 SIGIL_ARITY_EXACT(2), "Set posc3 amplitude");
1970 sigil_module_register_native(vm, "make-blsaw", native_make_blsaw,
1971 SIGIL_ARITY_EXACT(2), "Create bandlimited sawtooth (freq amp)");
1972 sigil_module_register_native(vm, "blsaw-compute", native_blsaw_compute,
1973 SIGIL_ARITY_EXACT(1), "Compute one sample from blsaw");
1974 sigil_module_register_native(vm, "blsaw-set-freq!", native_blsaw_set_freq,
1975 SIGIL_ARITY_EXACT(2), "Set blsaw frequency");
1976 sigil_module_register_native(vm, "blsaw-set-amp!", native_blsaw_set_amp,
1977 SIGIL_ARITY_EXACT(2), "Set blsaw amplitude");
1979 sigil_module_register_native(vm, "make-blsquare", native_make_blsquare,
1980 SIGIL_ARITY_RANGE(2, 3), "Create bandlimited square (freq amp [width])");
1981 sigil_module_register_native(vm, "blsquare-compute", native_blsquare_compute,
1982 SIGIL_ARITY_EXACT(1), "Compute one sample from blsquare");
1983 sigil_module_register_native(vm, "blsquare-set-freq!", native_blsquare_set_freq,
1984 SIGIL_ARITY_EXACT(2), "Set blsquare frequency");
1985 sigil_module_register_native(vm, "blsquare-set-amp!", native_blsquare_set_amp,
1986 SIGIL_ARITY_EXACT(2), "Set blsquare amplitude");
1988 sigil_module_register_native(vm, "make-bltriangle", native_make_bltriangle,
1989 SIGIL_ARITY_EXACT(2), "Create bandlimited triangle (freq amp)");
1990 sigil_module_register_native(vm, "bltriangle-compute", native_bltriangle_compute,
1991 SIGIL_ARITY_EXACT(1), "Compute one sample from bltriangle");
1992 sigil_module_register_native(vm, "bltriangle-set-freq!", native_bltriangle_set_freq,
1993 SIGIL_ARITY_EXACT(2), "Set bltriangle frequency");
1994 sigil_module_register_native(vm, "bltriangle-set-amp!", native_bltriangle_set_amp,
1995 SIGIL_ARITY_EXACT(2), "Set bltriangle amplitude");
1997 sigil_module_register_native(vm, "make-fosc", native_make_fosc,
1998 SIGIL_ARITY_EXACT(5), "Create FM osc (freq car mod indx amp)");
1999 sigil_module_register_native(vm, "fosc-compute", native_fosc_compute,
2000 SIGIL_ARITY_EXACT(1), "Compute one sample from fosc");
2001 sigil_module_register_native(vm, "fosc-set-freq!", native_fosc_set_freq,
2002 SIGIL_ARITY_EXACT(2), "Set fosc frequency");
2003 sigil_module_register_native(vm, "fosc-set-index!", native_fosc_set_index,
2004 SIGIL_ARITY_EXACT(2), "Set fosc modulation index");
2005 sigil_module_register_native(vm, "fosc-set-amp!", native_fosc_set_amp,
2006 SIGIL_ARITY_EXACT(2), "Set fosc amplitude");
2008 sigil_module_register_native(vm, "make-phasor", native_make_phasor,
2009 SIGIL_ARITY_RANGE(1, 2), "Create phasor (freq [phase])");
2010 sigil_module_register_native(vm, "phasor-compute", native_phasor_compute,
2011 SIGIL_ARITY_EXACT(1), "Compute one sample from phasor");
2012 sigil_module_register_native(vm, "phasor-set-freq!", native_phasor_set_freq,
2013 SIGIL_ARITY_EXACT(2), "Set phasor frequency");
2015 sigil_module_register_native(vm, "make-ks", native_make_ks,
2016 SIGIL_ARITY_EXACT(3), "Create Karplus-Strong (freq decay excite-gain)");
2017 sigil_module_register_native(vm, "ks-compute", native_ks_compute,
2018 SIGIL_ARITY_EXACT(2), "Compute Karplus-Strong (ks gate) -> sample");
2019 sigil_module_register_native(vm, "ks-trigger!", native_ks_trigger,
2020 SIGIL_ARITY_EXACT(1), "Excite Karplus-Strong with noise burst");
2021 sigil_module_register_native(vm, "ks-set-freq!", native_ks_set_freq,
2022 SIGIL_ARITY_EXACT(2), "Set KS fundamental frequency");
2023 sigil_module_register_native(vm, "ks-set-decay!", native_ks_set_decay,
2024 SIGIL_ARITY_EXACT(2), "Set KS -60 dB decay time");
2025 sigil_module_register_native(vm, "ks-set-excite-gain!", native_ks_set_excite_gain,
2026 SIGIL_ARITY_EXACT(2), "Set KS excitation amplitude");
2028 sigil_module_register_native(vm, "make-fm4", native_make_fm4,
2029 SIGIL_ARITY_EXACT(4), "Create 4-op FM voice (algorithm feedback vel-depth vel-curve)");
2030 sigil_module_register_native(vm, "fm4-set-op!", native_fm4_set_op,
2031 SIGIL_ARITY_EXACT(8), "Set operator (fm4 op ratio level attack decay sustain release)");
2032 sigil_module_register_native(vm, "fm4-set-algorithm!", native_fm4_set_algorithm,
2033 SIGIL_ARITY_EXACT(2), "Set fm4 algorithm 0..7");
2034 sigil_module_register_native(vm, "fm4-set-feedback!", native_fm4_set_feedback,
2035 SIGIL_ARITY_EXACT(2), "Set fm4 operator-4 feedback 0..1");
2036 sigil_module_register_native(vm, "fm4-compute", native_fm4_compute,
2037 SIGIL_ARITY_EXACT(5), "Compute fm4 (fm4 freq gate index velocity) -> sample");
2038 sigil_module_register_native(vm, "fm4-algorithm-name", native_fm4_algorithm_name,
2039 SIGIL_ARITY_EXACT(1), "Name of fm4 algorithm 0..7");
2041 /* Export all osc bindings */
2042 const char *osc_exports[] = {
2043 "make-osc", "make-osc-ft", "osc-compute", "osc-set-freq!", "osc-set-amp!",
2044 "make-posc3", "posc3-compute", "posc3-set-freq!", "posc3-set-amp!",
2045 "make-blsaw", "blsaw-compute", "blsaw-set-freq!", "blsaw-set-amp!",
2046 "make-blsquare", "blsquare-compute", "blsquare-set-freq!", "blsquare-set-amp!",
2047 "make-bltriangle", "bltriangle-compute", "bltriangle-set-freq!", "bltriangle-set-amp!",
2048 "make-fosc", "fosc-compute", "fosc-set-freq!", "fosc-set-index!", "fosc-set-amp!",
2049 "make-phasor", "phasor-compute", "phasor-set-freq!",
2050 "make-ks", "ks-compute", "ks-trigger!",
2051 "ks-set-freq!", "ks-set-decay!", "ks-set-excite-gain!",
2052 "make-fm4", "fm4-set-op!", "fm4-set-algorithm!", "fm4-set-feedback!",
2053 "fm4-compute", "fm4-algorithm-name",
2054 NULL
2055 };
2056 for (int i = 0; osc_exports[i]; i++) sigil_module_export(vm, osc_exports[i]);
2057 sigil_end_module(vm);
2059 /* === (sigil dsp filter) === */
2060 mod = sigil_begin_module(vm, "(sigil dsp filter)");
2061 if (!mod) return;
2063 sigil_module_register_native(vm, "make-moogladder", native_make_moogladder,
2064 SIGIL_ARITY_EXACT(2), "Create Moog ladder filter (freq res)");
2065 sigil_module_register_native(vm, "make-butlp", native_make_butlp,
2066 SIGIL_ARITY_EXACT(1), "Create Butterworth lowpass (freq)");
2067 sigil_module_register_native(vm, "make-buthp", native_make_buthp,
2068 SIGIL_ARITY_EXACT(1), "Create Butterworth highpass (freq)");
2069 sigil_module_register_native(vm, "make-lpf18", native_make_lpf18,
2070 SIGIL_ARITY_EXACT(3), "Create 3-pole resonant LP (cutoff res dist)");
2071 sigil_module_register_native(vm, "make-tone", native_make_tone,
2072 SIGIL_ARITY_EXACT(1), "Create 1st-order lowpass (freq)");
2073 sigil_module_register_native(vm, "make-atone", native_make_atone,
2074 SIGIL_ARITY_EXACT(1), "Create 1st-order highpass (freq)");
2075 sigil_module_register_native(vm, "make-dcblock", native_make_dcblock,
2076 SIGIL_ARITY_EXACT(0), "Create DC blocking filter");
2077 sigil_module_register_native(vm, "filter-compute", native_filter_compute,
2078 SIGIL_ARITY_EXACT(2), "Compute filter (filter input) -> output");
2079 sigil_module_register_native(vm, "filter-set-freq!", native_filter_set_freq,
2080 SIGIL_ARITY_EXACT(2), "Set filter cutoff frequency");
2081 sigil_module_register_native(vm, "filter-set-res!", native_filter_set_res,
2082 SIGIL_ARITY_EXACT(2), "Set filter resonance");
2084 const char *filter_exports[] = {
2085 "make-moogladder", "make-butlp", "make-buthp", "make-lpf18",
2086 "make-tone", "make-atone", "make-dcblock",
2087 "filter-compute", "filter-set-freq!", "filter-set-res!",
2088 NULL
2089 };
2090 for (int i = 0; filter_exports[i]; i++) sigil_module_export(vm, filter_exports[i]);
2091 sigil_end_module(vm);
2093 /* === (sigil dsp env) === */
2094 mod = sigil_begin_module(vm, "(sigil dsp env)");
2095 if (!mod) return;
2097 sigil_module_register_native(vm, "make-adsr", native_make_adsr,
2098 SIGIL_ARITY_EXACT(4), "Create ADSR envelope (atk dec sus rel)");
2099 sigil_module_register_native(vm, "adsr-compute", native_adsr_compute,
2100 SIGIL_ARITY_EXACT(2), "Compute ADSR (adsr gate) -> env value");
2101 sigil_module_register_native(vm, "make-tadsr", native_make_tadsr,
2102 SIGIL_ARITY_EXACT(4), "Create triggered ADSR (atk dec sus rel)");
2103 sigil_module_register_native(vm, "tadsr-compute", native_tadsr_compute,
2104 SIGIL_ARITY_EXACT(2), "Compute triggered ADSR");
2105 sigil_module_register_native(vm, "make-tenv", native_make_tenv,
2106 SIGIL_ARITY_EXACT(3), "Create triggered envelope (atk hold rel)");
2107 sigil_module_register_native(vm, "tenv-compute", native_tenv_compute,
2108 SIGIL_ARITY_EXACT(2), "Compute triggered envelope");
2109 sigil_module_register_native(vm, "make-line", native_make_line,
2110 SIGIL_ARITY_EXACT(3), "Create line segment (a dur b)");
2111 sigil_module_register_native(vm, "line-compute", native_line_compute,
2112 SIGIL_ARITY_EXACT(2), "Compute line segment");
2113 sigil_module_register_native(vm, "make-expon", native_make_expon,
2114 SIGIL_ARITY_EXACT(3), "Create exponential curve (a dur b)");
2115 sigil_module_register_native(vm, "expon-compute", native_expon_compute,
2116 SIGIL_ARITY_EXACT(2), "Compute exponential curve");
2117 sigil_module_register_native(vm, "make-port", native_make_port,
2118 SIGIL_ARITY_EXACT(1), "Create portamento (smoothing)");
2119 sigil_module_register_native(vm, "port-compute", native_port_compute,
2120 SIGIL_ARITY_EXACT(2), "Compute portamento");
2122 const char *env_exports[] = {
2123 "make-adsr", "adsr-compute", "make-tadsr", "tadsr-compute",
2124 "make-tenv", "tenv-compute", "make-line", "line-compute",
2125 "make-expon", "expon-compute", "make-port", "port-compute",
2126 NULL
2127 };
2128 for (int i = 0; env_exports[i]; i++) sigil_module_export(vm, env_exports[i]);
2129 sigil_end_module(vm);
2131 /* === (sigil dsp fx) === */
2132 mod = sigil_begin_module(vm, "(sigil dsp fx)");
2133 if (!mod) return;
2135 sigil_module_register_native(vm, "make-revsc", native_make_revsc,
2136 SIGIL_ARITY_EXACT(2), "Create stereo reverb (feedback lpfreq)");
2137 sigil_module_register_native(vm, "revsc-compute", native_revsc_compute,
2138 SIGIL_ARITY_EXACT(3), "Compute reverb (revsc inL inR) -> (outL . outR)");
2139 sigil_module_register_native(vm, "revsc-set-feedback!", native_revsc_set_feedback,
2140 SIGIL_ARITY_EXACT(2), "Set reverb feedback");
2141 sigil_module_register_native(vm, "revsc-set-lpfreq!", native_revsc_set_lpfreq,
2142 SIGIL_ARITY_EXACT(2), "Set reverb LP frequency");
2144 sigil_module_register_native(vm, "make-zitarev", native_make_zitarev,
2145 SIGIL_ARITY_EXACT(0), "Create Zita reverb");
2146 sigil_module_register_native(vm, "zitarev-compute", native_zitarev_compute,
2147 SIGIL_ARITY_EXACT(3), "Compute Zita reverb");
2148 sigil_module_register_native(vm, "zitarev-set-mix!", native_zitarev_set_mix,
2149 SIGIL_ARITY_EXACT(2), "Set Zita reverb mix");
2150 sigil_module_register_native(vm, "zitarev-set-level!", native_zitarev_set_level,
2151 SIGIL_ARITY_EXACT(2), "Set Zita reverb level");
2153 sigil_module_register_native(vm, "make-delay", native_make_delay,
2154 SIGIL_ARITY_EXACT(2), "Create delay (time feedback)");
2155 sigil_module_register_native(vm, "delay-compute", native_delay_compute,
2156 SIGIL_ARITY_EXACT(2), "Compute delay (delay input) -> output");
2157 sigil_module_register_native(vm, "delay-set-feedback!", native_delay_set_feedback,
2158 SIGIL_ARITY_EXACT(2), "Set delay feedback");
2160 sigil_module_register_native(vm, "make-vdelay", native_make_vdelay,
2161 SIGIL_ARITY_EXACT(2), "Create variable delay (maxdel feedback)");
2162 sigil_module_register_native(vm, "vdelay-compute", native_vdelay_compute,
2163 SIGIL_ARITY_EXACT(2), "Compute variable delay");
2164 sigil_module_register_native(vm, "vdelay-set-del!", native_vdelay_set_del,
2165 SIGIL_ARITY_EXACT(2), "Set variable delay time");
2166 sigil_module_register_native(vm, "vdelay-set-feedback!", native_vdelay_set_feedback,
2167 SIGIL_ARITY_EXACT(2), "Set variable delay feedback");
2169 sigil_module_register_native(vm, "make-chorus", native_make_chorus,
2170 SIGIL_ARITY_EXACT(0), "Create chorus");
2171 sigil_module_register_native(vm, "chorus-compute", native_chorus_compute,
2172 SIGIL_ARITY_EXACT(5), "Compute chorus (chorus in rate depth-ms mix) -> sample");
2173 sigil_module_register_native(vm, "make-crush", native_make_crush,
2174 SIGIL_ARITY_EXACT(0), "Create crush (drive + bit depth + rate reduction)");
2175 sigil_module_register_native(vm, "crush-compute", native_crush_compute,
2176 SIGIL_ARITY_EXACT(5), "Compute crush (crush in drive bits rate-div) -> sample");
2177 sigil_module_register_native(vm, "make-comp", native_make_comp,
2178 SIGIL_ARITY_EXACT(3), "Create compressor (attack release makeup-db)");
2179 sigil_module_register_native(vm, "comp-compute", native_comp_compute,
2180 SIGIL_ARITY_EXACT(4), "Compute compressor (comp in threshold-db ratio) -> sample");
2182 const char *fx_exports[] = {
2183 "make-revsc", "revsc-compute", "revsc-set-feedback!", "revsc-set-lpfreq!",
2184 "make-zitarev", "zitarev-compute", "zitarev-set-mix!", "zitarev-set-level!",
2185 "make-delay", "delay-compute", "delay-set-feedback!",
2186 "make-vdelay", "vdelay-compute", "vdelay-set-del!", "vdelay-set-feedback!",
2187 "make-chorus", "chorus-compute", "make-crush", "crush-compute",
2188 "make-comp", "comp-compute",
2189 NULL
2190 };
2191 for (int i = 0; fx_exports[i]; i++) sigil_module_export(vm, fx_exports[i]);
2192 sigil_end_module(vm);
2194 /* === (sigil dsp noise) === */
2195 mod = sigil_begin_module(vm, "(sigil dsp noise)");
2196 if (!mod) return;
2198 sigil_module_register_native(vm, "make-noise", native_make_noise,
2199 SIGIL_ARITY_EXACT(1), "Create white noise (amp)");
2200 sigil_module_register_native(vm, "noise-compute", native_noise_compute,
2201 SIGIL_ARITY_EXACT(1), "Compute one noise sample");
2202 sigil_module_register_native(vm, "make-pinknoise", native_make_pinknoise,
2203 SIGIL_ARITY_EXACT(1), "Create pink noise (amp)");
2204 sigil_module_register_native(vm, "pinknoise-compute", native_pinknoise_compute,
2205 SIGIL_ARITY_EXACT(1), "Compute one pinknoise sample");
2206 sigil_module_register_native(vm, "make-brown", native_make_brown,
2207 SIGIL_ARITY_EXACT(0), "Create brown noise");
2208 sigil_module_register_native(vm, "brown-compute", native_brown_compute,
2209 SIGIL_ARITY_EXACT(1), "Compute one brown noise sample");
2211 const char *noise_exports[] = {
2212 "make-noise", "noise-compute",
2213 "make-pinknoise", "pinknoise-compute",
2214 "make-brown", "brown-compute",
2215 NULL
2216 };
2217 for (int i = 0; noise_exports[i]; i++) sigil_module_export(vm, noise_exports[i]);
2218 sigil_end_module(vm);
2220 /* === (sigil dsp util) === */
2221 mod = sigil_begin_module(vm, "(sigil dsp util)");
2222 if (!mod) return;
2224 /* Dynamics */
2225 sigil_module_register_native(vm, "make-saturator", native_make_saturator,
2226 SIGIL_ARITY_EXACT(2), "Create saturator (drive dcoffset)");
2227 sigil_module_register_native(vm, "saturator-compute", native_saturator_compute,
2228 SIGIL_ARITY_EXACT(2), "Compute saturator");
2229 sigil_module_register_native(vm, "make-clip", native_make_clip,
2230 SIGIL_ARITY_EXACT(1), "Create hard clipper (limit)");
2231 sigil_module_register_native(vm, "clip-compute", native_clip_compute,
2232 SIGIL_ARITY_EXACT(2), "Compute clipper");
2233 sigil_module_register_native(vm, "make-peaklim", native_make_peaklim,
2234 SIGIL_ARITY_EXACT(3), "Create peak limiter (atk rel thresh)");
2235 sigil_module_register_native(vm, "peaklim-compute", native_peaklim_compute,
2236 SIGIL_ARITY_EXACT(2), "Compute peak limiter");
2238 /* Panning */
2239 sigil_module_register_native(vm, "make-pan2", native_make_pan2,
2240 SIGIL_ARITY_RANGE(0, 1), "Create panner ([pan])");
2241 sigil_module_register_native(vm, "pan2-compute", native_pan2_compute,
2242 SIGIL_ARITY_EXACT(2), "Compute pan2 (pan2 input) -> (L . R)");
2243 sigil_module_register_native(vm, "pan2-set-pan!", native_pan2_set_pan,
2244 SIGIL_ARITY_EXACT(2), "Set pan position");
2246 /* Timing */
2247 sigil_module_register_native(vm, "make-metro", native_make_metro,
2248 SIGIL_ARITY_EXACT(1), "Create metronome (freq)");
2249 sigil_module_register_native(vm, "metro-compute", native_metro_compute,
2250 SIGIL_ARITY_EXACT(1), "Compute metronome");
2251 sigil_module_register_native(vm, "metro-set-freq!", native_metro_set_freq,
2252 SIGIL_ARITY_EXACT(2), "Set metronome frequency");
2253 sigil_module_register_native(vm, "make-clock", native_make_clock,
2254 SIGIL_ARITY_EXACT(2), "Create clock (bpm subdiv)");
2255 sigil_module_register_native(vm, "clock-compute", native_clock_compute,
2256 SIGIL_ARITY_EXACT(2), "Compute clock");
2257 sigil_module_register_native(vm, "clock-set-bpm!", native_clock_set_bpm,
2258 SIGIL_ARITY_EXACT(2), "Set clock BPM");
2259 sigil_module_register_native(vm, "make-tgate", native_make_tgate,
2260 SIGIL_ARITY_EXACT(1), "Create triggered gate (time)");
2261 sigil_module_register_native(vm, "tgate-compute", native_tgate_compute,
2262 SIGIL_ARITY_EXACT(2), "Compute triggered gate");
2264 /* Random */
2265 sigil_module_register_native(vm, "make-randh", native_make_randh,
2266 SIGIL_ARITY_EXACT(3), "Create random hold (min max freq)");
2267 sigil_module_register_native(vm, "randh-compute", native_randh_compute,
2268 SIGIL_ARITY_EXACT(1), "Compute random hold");
2269 sigil_module_register_native(vm, "make-randi", native_make_randi,
2270 SIGIL_ARITY_EXACT(3), "Create random interp (min max freq)");
2271 sigil_module_register_native(vm, "randi-compute", native_randi_compute,
2272 SIGIL_ARITY_EXACT(1), "Compute random interp");
2274 /* Scaling */
2275 sigil_module_register_native(vm, "make-scale", native_make_scale,
2276 SIGIL_ARITY_EXACT(2), "Create scaler (min max)");
2277 sigil_module_register_native(vm, "scale-compute", native_scale_compute,
2278 SIGIL_ARITY_EXACT(2), "Compute scale");
2279 sigil_module_register_native(vm, "make-clamp", native_make_clamp,
2280 SIGIL_ARITY_EXACT(2), "Create clamp (min max)");
2281 sigil_module_register_native(vm, "clamp-compute", native_clamp_compute,
2282 SIGIL_ARITY_EXACT(2), "Compute clamp");
2283 sigil_module_register_native(vm, "make-crossfade", native_make_crossfade,
2284 SIGIL_ARITY_RANGE(0, 1), "Create crossfade ([pos])");
2285 sigil_module_register_native(vm, "crossfade-compute", native_crossfade_compute,
2286 SIGIL_ARITY_EXACT(3), "Compute crossfade (xf in1 in2)");
2287 sigil_module_register_native(vm, "crossfade-set-pos!", native_crossfade_set_pos,
2288 SIGIL_ARITY_EXACT(2), "Set crossfade position");
2290 /* WAV output */
2291 sigil_module_register_native(vm, "make-wavout", native_make_wavout,
2292 SIGIL_ARITY_EXACT(1), "Create WAV file writer (filename)");
2293 sigil_module_register_native(vm, "wavout-compute", native_wavout_compute,
2294 SIGIL_ARITY_EXACT(2), "Write sample to WAV");
2296 const char *util_exports[] = {
2297 "make-saturator", "saturator-compute",
2298 "make-clip", "clip-compute",
2299 "make-peaklim", "peaklim-compute",
2300 "make-pan2", "pan2-compute", "pan2-set-pan!",
2301 "make-metro", "metro-compute", "metro-set-freq!",
2302 "make-clock", "clock-compute", "clock-set-bpm!",
2303 "make-tgate", "tgate-compute",
2304 "make-randh", "randh-compute",
2305 "make-randi", "randi-compute",
2306 "make-scale", "scale-compute",
2307 "make-clamp", "clamp-compute",
2308 "make-crossfade", "crossfade-compute", "crossfade-set-pos!",
2309 "make-wavout", "wavout-compute",
2310 NULL
2311 };
2312 for (int i = 0; util_exports[i]; i++) sigil_module_export(vm, util_exports[i]);
2313 sigil_end_module(vm);