From 039847a29868d335c08fe0a587260dffc71ca5c4 Mon Sep 17 00:00:00 2001 From: Ludvig Gunne Lindström Date: Fri, 17 Jul 2026 11:43:24 +0200 Subject: init --- mod/delay.c | 233 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 233 insertions(+) create mode 100644 mod/delay.c (limited to 'mod/delay.c') diff --git a/mod/delay.c b/mod/delay.c new file mode 100644 index 0000000..44441cf --- /dev/null +++ b/mod/delay.c @@ -0,0 +1,233 @@ +/* + * melisma - extensible and scriptable audio engine + * Copyright (C) 2026 Ludvig Gunne Lindström + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include "melisma.h" +#include "mutil.h" + +#define DELAY_TYPENAME "delay" + +#define MAX_DELAY 2.00f +#define DEFAULT_DELAY 0.25f +#define DEFAULT_GAIN 1.00f +#define DEFAULT_FEEDBACK 0.25f +#define DEFAULT_WETDRYMIX 0.50f + +#define CLAMP(v,min,max) ((v)<(min)?(min):((v)>(max)?(max):(v))) + +struct delay +{ + ml_channel *out; + float feedback; + float gain; + float wetdrymix; + float delay; + size_t len; + size_t write; + float *buffer; +}; + +static void proc(const ml_proc_args *args) +{ + struct delay *dl = args->userp; + + size_t sr = ml_sample_rate(); + size_t offset = sr*dl->delay; + + const float *in = args->in[0]; + float *out = args->out[0]; + + size_t read; + float dry,wet; + + for (size_t i = 0; i < args->nframes; i++) + { + read = dl->write + dl->len - offset; + + if (read >= dl->len) + { + read -= dl->len; + } + + dry = in[i]; + wet = dl->buffer[read]*dl->gain; + + out[i] = wet*dl->wetdrymix + dry*(1 - dl->wetdrymix); + dl->buffer[dl->write++] = dry + wet*dl->feedback; + + if (dl->write == dl->len) + { + dl->write = 0; + } + } +} + +static void clamp(struct delay *dl) +{ + dl->delay = CLAMP(dl->delay,0,MAX_DELAY); + dl->gain = CLAMP(dl->gain,0,1); + dl->feedback = CLAMP(dl->feedback,0,1); + dl->wetdrymix = CLAMP(dl->wetdrymix,0,1); +} + +static int l_init(lua_State *s) +{ + ml_channel *src = mutil_get_channel(s,1); + + struct delay *dl = calloc(1,sizeof(*dl)); + + dl->delay = DEFAULT_DELAY; + dl->gain = DEFAULT_GAIN; + dl->feedback = DEFAULT_FEEDBACK; + dl->wetdrymix = DEFAULT_WETDRYMIX; + + size_t sr = ml_sample_rate(); + dl->len = sr*MAX_DELAY; + dl->buffer = calloc(1,dl->len*sizeof(dl->buffer[0])); + + clamp(dl); + + ml_node *node = ml_create_node(proc,dl); + ml_label_node(node,"delay"); + ml_channel *dst = ml_add_input(node); + ml_connect(src,dst); + + dl->out = ml_add_output(node); + + *(void**) lua_newuserdata(s,sizeof(void*)) = dl; + luaL_setmetatable(s,DELAY_TYPENAME); + + return 1; +} + +static int l_out(lua_State *s) +{ + struct delay *dl = *(void**) luaL_checkudata(s,1,DELAY_TYPENAME); + *(void**) lua_newuserdata(s,sizeof(void*)) = dl->out; + luaL_setmetatable(s,MELISMA_OUTPUT_CHANNEL_TYPENAME); + return 1; +} + +static int l_delay(lua_State *s) +{ + struct delay *dl = *(void**) luaL_checkudata(s,1,DELAY_TYPENAME); + float v = luaL_checknumber(s,2); + dl->delay = v; + clamp(dl); + *(void**) lua_newuserdata(s,sizeof(void*)) = dl; + luaL_setmetatable(s,DELAY_TYPENAME); + return 1; +} + +static int l_gain(lua_State *s) +{ + struct delay *dl = *(void**) luaL_checkudata(s,1,DELAY_TYPENAME); + float v = luaL_checknumber(s,2); + dl->gain = v; + clamp(dl); + *(void**) lua_newuserdata(s,sizeof(void*)) = dl; + luaL_setmetatable(s,DELAY_TYPENAME); + return 1; +} + +static int l_feedback(lua_State *s) +{ + struct delay *dl = *(void**) luaL_checkudata(s,1,DELAY_TYPENAME); + float v = luaL_checknumber(s,2); + dl->feedback = v; + clamp(dl); + *(void**) lua_newuserdata(s,sizeof(void*)) = dl; + luaL_setmetatable(s,DELAY_TYPENAME); + return 1; +} + +static int l_wetdrymix(lua_State *s) +{ + struct delay *dl = *(void**) luaL_checkudata(s,1,DELAY_TYPENAME); + float v = luaL_checknumber(s,2); + dl->wetdrymix = v; + clamp(dl); + *(void**) lua_newuserdata(s,sizeof(void*)) = dl; + luaL_setmetatable(s,DELAY_TYPENAME); + return 1; +} + +int luaopen_melisma_delay(lua_State *s) +{ + ml_check_version(); + +/*** Digital delay line. +@classmod delay +*/ + lua_newtable(s); + int tbl = lua_gettop(s); + +/*** Create a delay line. +@static +@function init +@tparam channel input Input signal. +@treturn delay Newly created delay line. +*/ + lua_pushcfunction(s,l_init); + lua_setfield(s,tbl,"init"); + +/*** Set delay. +@function delay +@tparam number d delay in seconds. +@treturn delay +*/ + lua_pushcfunction(s,l_delay); + lua_setfield(s,tbl,"delay"); + +/*** Set gain. +@function gain +@tparam number g Gain (**TODO**: Use decibels). +@treturn delay +*/ + lua_pushcfunction(s,l_gain); + lua_setfield(s,tbl,"gain"); + +/*** Set feedback. +@function feedback +@tparam number f Feedback. +@treturn delay +*/ + lua_pushcfunction(s,l_feedback); + lua_setfield(s,tbl,"feedback"); + +/*** Set wet/dry mix. +@function wetdrymix +@tparam number wd Wet/dry mix. +@treturn delay +*/ + lua_pushcfunction(s,l_wetdrymix); + lua_setfield(s,tbl,"wetdrymix"); + + lua_pushcfunction(s,l_out); + lua_setfield(s,tbl,MUTIL_OUTPUT_CHANNEL_FUNCTION); + + luaL_newmetatable(s,DELAY_TYPENAME); + int mt = lua_gettop(s); + + lua_pushvalue(s,tbl); + lua_setfield(s,mt,"__index"); + + lua_settop(s,tbl); + return 1; +} -- cgit v1.3.1