diff options
Diffstat (limited to 'mod/gain.c')
| -rw-r--r-- | mod/gain.c | 164 |
1 files changed, 164 insertions, 0 deletions
diff --git a/mod/gain.c b/mod/gain.c new file mode 100644 index 0000000..994efcf --- /dev/null +++ b/mod/gain.c @@ -0,0 +1,164 @@ +/* + * 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 <https://www.gnu.org/licenses/>. + */ + +#include <string.h> +#include <stdlib.h> +#include <math.h> + +#include <lauxlib.h> + +#include "melisma.h" +#include "mutil.h" + +#define GAIN_TYPENAME "gain" + +struct gain +{ + ml_node *node; + float value; + int mapped; + int db; +}; + +static void proc(const ml_proc_args *args) +{ + struct gain *gain = args->userp; + + float *in = args->in[0]; + float *out = args->out[0]; + float *mapped = gain->mapped ? args->in[1] : NULL; + float v; + + for (size_t i = 0; i < args->nframes; i++) + { + v = mapped ? mapped[i] : gain->value; + + if (gain->db) + { + v = powf(10,v/20.f); + } + + out[i] = v*in[i]; + } +} + +static int l_init(lua_State *s) +{ + int db_i = lua_upvalueindex(1); + int db = lua_tointeger(s,db_i); + ml_channel *audio_src, *mapped_src = NULL; + ml_channel *audio_dst, *mapped_dst; + float value = 0; + + + if (lua_isuserdata(s,1)) + { + mapped_src = mutil_get_channel(s,1); + } + else + { + value = luaL_checknumber(s,1); + } + + audio_src = mutil_get_channel(s,2); + + struct gain *gain = calloc(1,sizeof(*gain)); + gain->value = value; + gain->mapped = mapped_src != NULL; + gain->db = db; + + ml_node *node = ml_create_node(proc,gain); + ml_label_node(node,"gain"); + gain->node = node; + + ml_add_output(node); + + audio_dst = ml_add_input(node); + ml_connect(audio_src,audio_dst); + + if (mapped_src) + { + mapped_dst = ml_add_input(node); + ml_connect(mapped_src,mapped_dst); + } + + *(void**) lua_newuserdata(s,sizeof(void*)) = gain; + luaL_setmetatable(s,GAIN_TYPENAME); + + return 1; +} + +static int l_out(lua_State *s) +{ + struct gain *gain = *(void**) luaL_checkudata(s,1,GAIN_TYPENAME); + *(void**) lua_newuserdata(s,sizeof(void*)) = ml_get_output(gain->node,0); + luaL_setmetatable(s,MELISMA_OUTPUT_CHANNEL_TYPENAME); + return 1; +} + +int luaopen_melisma_gain(lua_State *s) +{ + ml_check_version(); + +/*** Apply gain to signals. + local mgain = require 'melisma.gain' + + -- Interprets `cntl` as decibels and applies it to `input` + local input = ... + local cntl = ... + local gain = mgain.db(input,cntl) + +@classmod gain +*/ + lua_newtable(s); + int tbl = lua_gettop(s); + +/*** Creates a linear gain unit. +@static +@function linear +@tparam channel input Input signal. +@tparam number|channel gain Constant gain or a controlling signal. +@treturn gain The newly created gain unit. +*/ + lua_pushinteger(s,0); + lua_pushcclosure(s,l_init,1); + lua_setfield(s,tbl,"linear"); + +/*** Creates a decibel gain unit. +@static +@function db +@tparam channel input Input signal. +@tparam number|channel gain Constant gain in decibels or a controlling signal. +@treturn gain The newly created gain unit. +*/ + lua_pushinteger(s,1); + lua_pushcclosure(s,l_init,1); + lua_setfield(s,tbl,"db"); + + lua_pushcfunction(s,l_out); + lua_setfield(s,tbl,MUTIL_OUTPUT_CHANNEL_FUNCTION); + + luaL_newmetatable(s,GAIN_TYPENAME); + int mt = lua_gettop(s); + + lua_pushvalue(s,tbl); + lua_setfield(s,mt,"__index"); + + lua_settop(s,tbl); + return 1; +} |
