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/interp.c | 257 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 257 insertions(+) create mode 100644 mod/interp.c (limited to 'mod/interp.c') diff --git a/mod/interp.c b/mod/interp.c new file mode 100644 index 0000000..c5bbefb --- /dev/null +++ b/mod/interp.c @@ -0,0 +1,257 @@ +/* + * 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 +#include "melisma.h" +#include "mutil.h" + +#define INTERP_TYPENAME "interp" +#define DEFAULT_METHOD "linear" + +struct interp; +typedef float (*smp_fn)(float); + +enum { + LINEAR, + EASEIN, + EASEOUT, + SMOOTHSTEP, +}; + +struct interp +{ + ml_channel *out; + smp_fn smp; + float src; + float dst; + size_t t0; + size_t t; + int method; +}; + +static float sample(struct interp *ip) +{ + float x = ip->t0 == 0 ? 0 : (float)ip->t / (float)ip->t0; + switch (ip->method) + { + case LINEAR: + break; + case EASEIN: + x = x*x; + break; + case EASEOUT: + x = 1 -x; + x = 1 - x*x; + break; + case SMOOTHSTEP: + x = x*x*(3 - 2*x); + break; + } + return ip->src + x*(ip->dst - ip->src); +} + +static void proc(const ml_proc_args *args) +{ + struct interp *ip = args->userp; + + float *out = args->out[0]; + + for (size_t i = 0; i < args->nframes; i++) + { + out[i] = sample(ip); + + if (ip->t == ip->t0) + { + ip->t = 0; + ip->t0 = 0; + ip->src = ip->dst; + } + else + { + ip->t++; + } + } +} + +static int l_init(lua_State *s) +{ + float v = luaL_checknumber(s,1); + + struct interp *ip = calloc(1,sizeof(*ip)); + ip->src = v; + ip->dst = v; + ip->method = LINEAR; + + ml_node *node = ml_create_node(proc,ip); + ml_label_node(node,"interp"); + ip->out = ml_add_output(node); + + *(void**) lua_newuserdata(s,sizeof(void*)) = ip; + luaL_setmetatable(s,INTERP_TYPENAME); + + return 1; +} + +static int l_set(lua_State *s) +{ + struct interp *ip = *(void**) luaL_checkudata(s,1,INTERP_TYPENAME); + float v = luaL_checknumber(s,2); + + ip->src = v; + ip->dst = v; + ip->t0 = 0; + ip->t = 0; + + return 0; +} + +static int l_target(lua_State *s) +{ + int method_i = lua_upvalueindex(1); + int method = luaL_checkinteger(s,method_i); + struct interp *ip = *(void**) luaL_checkudata(s,1,INTERP_TYPENAME); + float v = luaL_checknumber(s,2); + float t = luaL_checknumber(s,3); + + size_t sr = ml_sample_rate(); + + ip->src = sample(ip); + ip->dst = v; + ip->t0 = sr*t; + ip->t = 0; + ip->method = method; + + return 0; +} + +static int l_sample(lua_State *s) +{ + struct interp *ip = *(void**) luaL_checkudata(s,1,INTERP_TYPENAME); + lua_pushnumber(s,sample(ip)); + return 1; +} + +static int l_out(lua_State *s) +{ + struct interp *ip = *(void**) luaL_checkudata(s,1,INTERP_TYPENAME); + *(void**) lua_newuserdata(s,sizeof(void*)) = ip->out; + luaL_setmetatable(s,MELISMA_OUTPUT_CHANNEL_TYPENAME); + return 1; +} + +int luaopen_melisma_interp(lua_State *s) +{ + ml_check_version(); + +/*** Interpolation. + + local minterp = require 'melisma.interp' + local mevent = require 'melisma.event' + local mgain = require 'melisma.gain' + + local interp = minterp.init(0) + + interp:linear(1,1) + + mevent.schedule(0.5, function() + print('current value:', interp:sample()) + end) + + local sig = ... + local gain = mgain.linear(sig,interp) + +@classmod interp +*/ + lua_newtable(s); + int tbl = lua_gettop(s); + +/*** Initialize an interpolation unit. +@static +@function init +@tparam number x0 Initial value. +@treturn interp The newly created interpolation unit. +*/ + lua_pushcfunction(s,l_init); + lua_setfield(s,tbl,"init"); + +/*** Set value directly. +@function set +@tparam number x New value. +*/ + lua_pushcfunction(s,l_set); + lua_setfield(s,tbl,"set"); + +/*** Interpolate using linear interpolation. +@function linear +@tparam number v Target value. +@tparam number t Interpolation time. +*/ + lua_pushnumber(s,LINEAR); + lua_pushcclosure(s,l_target,1); + lua_setfield(s,tbl,"linear"); + + +/*** Interpolate using ease-in interpolation. +@function ease_in +@tparam number v Target value. +@tparam number t Interpolation time. +*/ + lua_pushnumber(s,EASEIN); + lua_pushcclosure(s,l_target,1); + lua_setfield(s,tbl,"ease_in"); + + +/*** Interpolate using ease-out interpolation. +@function ease_out +@tparam number v Target value. +@tparam number t Interpolation time. +*/ + lua_pushnumber(s,EASEOUT); + lua_pushcclosure(s,l_target,1); + lua_setfield(s,tbl,"ease_out"); + +/*** Interpolate using smoothstep interpolation. +@function smoothstep +@tparam number v Target value. +@tparam number t Interpolation time. +*/ + lua_pushnumber(s,SMOOTHSTEP); + lua_pushcclosure(s,l_target,1); + lua_setfield(s,tbl,"smoothstep"); + +/*** Get the current value. +@function sample +@treturn number The current value. +*/ + lua_pushcfunction(s,l_sample); + lua_setfield(s,tbl,"sample"); + + lua_pushcfunction(s,l_out); + lua_setfield(s,tbl,MUTIL_OUTPUT_CHANNEL_FUNCTION); + + luaL_newmetatable(s,INTERP_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