From 53fd8b4b99a410b781634ab55335984fadedad72 Mon Sep 17 00:00:00 2001 From: Ben Wesch Date: Sun, 6 Oct 2024 10:16:13 +0200 Subject: [PATCH 01/18] add signal_setmultiout, update dsp and perform --- pd.lua | 8 +++-- pdlua.c | 105 +++++++++++++++++++++++++++++++++++++++++--------------- pdlua.h | 1 + 3 files changed, 85 insertions(+), 29 deletions(-) diff --git a/pd.lua b/pd.lua index 2c36f2b..c3fe2ef 100644 --- a/pd.lua +++ b/pd.lua @@ -77,10 +77,10 @@ pd._dispatcher = function (object, inlet, sel, atoms) end end -pd._dsp = function (object, samplerate, blocksize) +pd._dsp = function (object, samplerate, blocksize, inchans) local obj = pd._objects[object] if nil ~= obj and type(obj.dsp) == "function" then - pd._objects[object]:dsp(samplerate, blocksize) + pd._objects[object]:dsp(samplerate, blocksize, inchans) end end @@ -461,6 +461,10 @@ function pd.Class:canvas_realizedollar(s) return pd._canvas_realizedollar(self._object, s) end +function pd.Class:signal_setmultiout(n, channelcount) + return pd._signal_setmultiout(self._object, n, channelcount) +end + function pd.Class:repaint(layer) -- do some argument checking first if type(layer) == "number" then diff --git a/pdlua.c b/pdlua.c index c31fe8a..bd05294 100644 --- a/pdlua.c +++ b/pdlua.c @@ -1057,22 +1057,26 @@ static t_int *pdlua_perform(t_int *w){ for (int i = 0; i < o->siginlets; i++) { lua_newtable(__L()); - t_float *in = (t_float*)w[i + 3]; - for (int j = 0; j < nblock; j++) + t_float *in = (t_float *)(w[3 + i*2]); // FIXME: this feels wrong + int nchans = (int)(w[4 + i*2]); // FIXME: this feels wrong + int s_n = nblock * nchans; // FIXME: name? + PDLUA_DEBUG("pdlua_perform: inlet %d", i); + PDLUA_DEBUG2("pdlua_perform: nchans: %d, totalSamples: %d", nchans, s_n); + for (int j = 0; j < s_n; j++) { lua_pushinteger(__L(), j + 1); lua_pushnumber(__L(), in[j]); lua_settable(__L(), -3); } } - + if (lua_pcall(__L(), 1 + o->siginlets, o->sigoutlets, 0)) { mylua_error(__L(), o, "perform"); lua_pop(__L(), 1); /* pop the global pd */ - return w + o->siginlets + o->sigoutlets + 3; + return w + 3 + (o->siginlets + o->sigoutlets) * 2; } - + if (!lua_istable(__L(), -1)) { const char *s = "lua: perform: function should return"; @@ -1088,13 +1092,22 @@ static t_int *pdlua_perform(t_int *w){ } } lua_pop(__L(), 1 + o->sigoutlets); - return w + o->siginlets + o->sigoutlets + 3; + return w + 3 + (o->siginlets + o->sigoutlets) * 2; } - + for (int i = o->sigoutlets - 1; i >= 0; i--) { - t_float *out = (t_float*)w[i + 3 + o->siginlets]; - for (int j = 0; j < nblock; j++) + if (!lua_istable(__L(), -1)) { + pd_error(o, "pdlua_perform: expected table for outlet %d", i); + lua_pop(__L(), 1); + continue; + } + t_float *out = (t_float *)(w[3 + (o->siginlets + i)*2]); // FIXME: this feels wrong + int nchans = (int)(w[4 + (o->siginlets + i)*2]); // FIXME: this feels wrong + int totalSamples = nblock * nchans; + PDLUA_DEBUG("pdlua_perform: outlet %d", i); + PDLUA_DEBUG2("nchans: %d, totalSamples: %d", nchans, totalSamples); + for (int j = 0; j < totalSamples; j++) { lua_pushinteger(__L(), (lua_Integer)(j + 1)); lua_gettable(__L(), -2); @@ -1108,43 +1121,57 @@ static t_int *pdlua_perform(t_int *w){ } lua_pop(__L(), 1); } - lua_pop(__L(), 1); /* pop the global "pd" */ - + PDLUA_DEBUG("pdlua_perform: end. stack top %d", lua_gettop(__L())); - - return w + o->siginlets + o->sigoutlets + 3; + + return w + 3 + (o->siginlets + o->sigoutlets) * 2; } -static void pdlua_dsp(t_pdlua *x, t_signal **sp){ +static void pdlua_dsp(t_pdlua *x, t_signal **sp) { int sum = x->siginlets + x->sigoutlets; if(sum == 0) return; x->sig_warned = 0; - + PDLUA_DEBUG("pdlua_dsp: stack top %d", lua_gettop(__L())); + + x->sp = sp; // FIXME: is this the way? (setting for signal_setmultiout) + + // Call Lua _dsp function lua_getglobal(__L(), "pd"); lua_getfield (__L(), -1, "_dsp"); lua_pushlightuserdata(__L(), x); lua_pushnumber(__L(), sys_getsr()); lua_pushnumber(__L(), sys_getblksize()); - if (lua_pcall(__L(), 3, 0, 0)) - { + // Pass input channel counts as a table + lua_newtable(__L()); + for (int i = 0; i < x->siginlets; i++) { + PDLUA_DEBUG2("pdlua_dsp: inlet: %d, s_nchans: %d", i, sp[i]->s_nchans); + lua_pushinteger(__L(), i + 1); + lua_pushinteger(__L(), sp[i]->s_nchans); + lua_settable(__L(), -3); + } + + if (lua_pcall(__L(), 4, 0, 0)) { // FIXME: check again mylua_error(__L(), x, "dsp"); } lua_pop(__L(), 1); /* pop the global "pd" */ - + PDLUA_DEBUG("pdlua_dsp: end. stack top %d", lua_gettop(__L())); - - int sigvecsize = sum + 2; - t_int* sigvec = getbytes(sigvecsize * sizeof(t_int)); + + // Prepare signal vector for dsp_addv + int sigvecsize = 2 + sum * 2; // x, nblock, and (s_vec, s_nchans) for each in/outlet + t_int *sigvec = (t_int *)getbytes(sigvecsize * sizeof(t_int)); sigvec[0] = (t_int)x; - sigvec[1] = (t_int)sp[0]->s_n; - - for (int i = 0; i < sum; i++) - sigvec[i + 2] = (t_int)sp[i]->s_vec; + sigvec[1] = (t_int)sp[0]->s_n; // FIXME: check again. similar to s_length? and this should be blocksize if anything + for (int i = 0; i < sum; i++) { // FIXME: this feels wrong + sigvec[2 + i*2] = (t_int)sp[i]->s_vec; + sigvec[3 + i*2] = (t_int)sp[i]->s_nchans; + } + dsp_addv(pdlua_perform, sigvecsize, sigvec); freebytes(sigvec, sigvecsize * sizeof(t_int)); } @@ -1302,14 +1329,14 @@ static int pdlua_class_new(lua_State *L) snprintf(name_gfx, MAXPDSTRING-1, "%s:gfx", name); PDLUA_DEBUG3("pdlua_class_new: L is %p, name is %s stack top is %d", L, name, lua_gettop(L)); c = class_new(gensym((char *) name), (t_newmethod) pdlua_new, - (t_method) pdlua_free, sizeof(t_pdlua), CLASS_NOINLET, A_GIMME, 0); + (t_method) pdlua_free, sizeof(t_pdlua), CLASS_NOINLET | CLASS_MULTICHANNEL, A_GIMME, 0); if (strcmp(name, "pdlua") && strcmp(name, "pdluax")) { // Shadow class for graphics objects. This is an exact clone of the // regular (non-gui) class, except that it has a different // widgetbehavior. We only need this for the regular Lua objects, the // pdlua and pdluax built-ins don't have this. c_gfx = class_new(gensym((char *) name_gfx), (t_newmethod) pdlua_new, - (t_method) pdlua_free, sizeof(t_pdlua), CLASS_NOINLET, A_GIMME, 0); + (t_method) pdlua_free, sizeof(t_pdlua), CLASS_NOINLET | CLASS_MULTICHANNEL, A_GIMME, 0); class_sethelpsymbol(c_gfx, gensym((char *) name)); } @@ -1387,6 +1414,7 @@ static int pdlua_object_new(lua_State *L) o->canvas = canvas_getcurrent(); o->pdlua_class = c; o->pdlua_class_gfx = c_gfx; + o->sp = NULL; o->gfx.width = 80; o->gfx.height = 80; @@ -2588,6 +2616,26 @@ static int pdlua_canvas_realizedollar(lua_State *L) return 0; } +static int pdlua_signal_setmultiout(lua_State *L) +{ + if (lua_islightuserdata(L, 1) && lua_isnumber(L, 2) && lua_isnumber(L, 3)) + { + t_pdlua *x = (t_pdlua *)lua_touserdata(L, 1); + PDLUA_DEBUG("pdlua_signal_setmultiout: x = %p", x); + int outidx = lua_tointeger(L, 2) - 1; // Lua uses 1-based indexing + int nchans = lua_tointeger(L, 3); + PDLUA_DEBUG2("pdlua_signal_setmultiout: outidx = %d, nchans = %d", outidx, nchans); + + if (x && outidx >= 0 && outidx < x->sigoutlets) + { + PDLUA_DEBUG("pdlua_signal_setmultiout: nchans", nchans); + signal_setmultiout(&x->sp[x->siginlets + outidx], nchans); + return 0; + } + } + return luaL_error(L, "Invalid arguments to signal_setmultiout"); +} + /** Initialize the pd API for Lua. */ static void pdlua_init(lua_State *L) /**< Lua interpreter state. */ @@ -2689,6 +2737,9 @@ static void pdlua_init(lua_State *L) lua_pushstring(L, "_canvas_realizedollar"); lua_pushcfunction(L, pdlua_canvas_realizedollar); lua_settable(L, -3); + lua_pushstring(L, "_signal_setmultiout"); + lua_pushcfunction(L, pdlua_signal_setmultiout); + lua_settable(L, -3); lua_pushstring(L, "_error"); lua_pushcfunction(L, pdlua_error); lua_settable(L, -3); diff --git a/pdlua.h b/pdlua.h index 06c01df..02a0f83 100644 --- a/pdlua.h +++ b/pdlua.h @@ -69,6 +69,7 @@ typedef struct pdlua t_pdlua_gfx gfx; // Holds state for graphics. t_class *pdlua_class; // Holds our class pointer. t_class *pdlua_class_gfx; // Holds our gfx class pointer. + t_signal **sp; // Array of signal pointers for multichannel audio. } t_pdlua; lua_State* __L(); From de922226ed09813ae011da4fb2cad85a12719318 Mon Sep 17 00:00:00 2001 From: Ben Wesch Date: Mon, 7 Oct 2024 07:27:57 +0200 Subject: [PATCH 02/18] simplify signal management for dsp/perform --- pdlua.c | 45 +++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/pdlua.c b/pdlua.c index bd05294..d49dc1b 100644 --- a/pdlua.c +++ b/pdlua.c @@ -1047,7 +1047,9 @@ static void pdlua_menu_open(t_pdlua *o) static t_int *pdlua_perform(t_int *w){ t_pdlua *o = (t_pdlua *)(w[1]); - int nblock = (int)(w[2]); + t_signal *first_sig = (t_signal *)(w[2]); + int nblock = first_sig->s_n; + // FIXME: should we use a o->blocksize here that gets set in dsp? PDLUA_DEBUG("pdlua_perform: stack top %d", lua_gettop(__L())); lua_getglobal(__L(), "pd"); @@ -1057,12 +1059,13 @@ static t_int *pdlua_perform(t_int *w){ for (int i = 0; i < o->siginlets; i++) { lua_newtable(__L()); - t_float *in = (t_float *)(w[3 + i*2]); // FIXME: this feels wrong - int nchans = (int)(w[4 + i*2]); // FIXME: this feels wrong - int s_n = nblock * nchans; // FIXME: name? + t_signal *sig = (t_signal *)(w[2 + i]); + t_float *in = sig->s_vec; + int nchans = sig->s_nchans; + int s_n_allchans = nblock * nchans; // sum of all inlet samples PDLUA_DEBUG("pdlua_perform: inlet %d", i); - PDLUA_DEBUG2("pdlua_perform: nchans: %d, totalSamples: %d", nchans, s_n); - for (int j = 0; j < s_n; j++) + PDLUA_DEBUG2("pdlua_perform: nchans: %d, s_n_allchans: %d", nchans, s_n_allchans); + for (int j = 0; j < s_n_allchans; j++) { lua_pushinteger(__L(), j + 1); lua_pushnumber(__L(), in[j]); @@ -1074,7 +1077,7 @@ static t_int *pdlua_perform(t_int *w){ { mylua_error(__L(), o, "perform"); lua_pop(__L(), 1); /* pop the global pd */ - return w + 3 + (o->siginlets + o->sigoutlets) * 2; + return w + 2 + o->siginlets + o->sigoutlets; } if (!lua_istable(__L(), -1)) @@ -1092,7 +1095,7 @@ static t_int *pdlua_perform(t_int *w){ } } lua_pop(__L(), 1 + o->sigoutlets); - return w + 3 + (o->siginlets + o->sigoutlets) * 2; + return w + 2 + o->siginlets + o->sigoutlets; } for (int i = o->sigoutlets - 1; i >= 0; i--) @@ -1102,12 +1105,13 @@ static t_int *pdlua_perform(t_int *w){ lua_pop(__L(), 1); continue; } - t_float *out = (t_float *)(w[3 + (o->siginlets + i)*2]); // FIXME: this feels wrong - int nchans = (int)(w[4 + (o->siginlets + i)*2]); // FIXME: this feels wrong - int totalSamples = nblock * nchans; + t_signal *sig = (t_signal *)(w[2 + o->siginlets + i]); + t_float *out = sig->s_vec; + int nchans = sig->s_nchans; + int s_n_allchans = nblock * nchans; // sum of all outlet samples PDLUA_DEBUG("pdlua_perform: outlet %d", i); - PDLUA_DEBUG2("nchans: %d, totalSamples: %d", nchans, totalSamples); - for (int j = 0; j < totalSamples; j++) + PDLUA_DEBUG2("nchans: %d, s_n_allchans: %d", nchans, s_n_allchans); + for (int j = 0; j < s_n_allchans; j++) { lua_pushinteger(__L(), (lua_Integer)(j + 1)); lua_gettable(__L(), -2); @@ -1125,7 +1129,7 @@ static t_int *pdlua_perform(t_int *w){ PDLUA_DEBUG("pdlua_perform: end. stack top %d", lua_gettop(__L())); - return w + 3 + (o->siginlets + o->sigoutlets) * 2; + return w + 2 + o->siginlets + o->sigoutlets; } static void pdlua_dsp(t_pdlua *x, t_signal **sp) { @@ -1135,7 +1139,7 @@ static void pdlua_dsp(t_pdlua *x, t_signal **sp) { PDLUA_DEBUG("pdlua_dsp: stack top %d", lua_gettop(__L())); - x->sp = sp; // FIXME: is this the way? (setting for signal_setmultiout) + x->sp = sp; // FIXME: is this the way? (setting this for signal_setmultiout) // Call Lua _dsp function lua_getglobal(__L(), "pd"); @@ -1153,7 +1157,7 @@ static void pdlua_dsp(t_pdlua *x, t_signal **sp) { lua_settable(__L(), -3); } - if (lua_pcall(__L(), 4, 0, 0)) { // FIXME: check again + if (lua_pcall(__L(), 4, 0, 0)) { mylua_error(__L(), x, "dsp"); } lua_pop(__L(), 1); /* pop the global "pd" */ @@ -1161,15 +1165,12 @@ static void pdlua_dsp(t_pdlua *x, t_signal **sp) { PDLUA_DEBUG("pdlua_dsp: end. stack top %d", lua_gettop(__L())); // Prepare signal vector for dsp_addv - int sigvecsize = 2 + sum * 2; // x, nblock, and (s_vec, s_nchans) for each in/outlet + int sigvecsize = 1 + sum; // x and sp[i] for each iolet t_int *sigvec = (t_int *)getbytes(sigvecsize * sizeof(t_int)); sigvec[0] = (t_int)x; - sigvec[1] = (t_int)sp[0]->s_n; // FIXME: check again. similar to s_length? and this should be blocksize if anything - - for (int i = 0; i < sum; i++) { // FIXME: this feels wrong - sigvec[2 + i*2] = (t_int)sp[i]->s_vec; - sigvec[3 + i*2] = (t_int)sp[i]->s_nchans; + for (int i = 0; i < sum; i++) { + sigvec[1 + i] = (t_int)sp[i]; } dsp_addv(pdlua_perform, sigvecsize, sigvec); From 4ab4536e29c8a0f6fef535bdebb47b2a02412d5f Mon Sep 17 00:00:00 2001 From: Ben Wesch Date: Mon, 7 Oct 2024 07:57:34 +0200 Subject: [PATCH 03/18] check pd version for multichannel support --- pdlua.c | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/pdlua.c b/pdlua.c index d49dc1b..0c7119d 100644 --- a/pdlua.c +++ b/pdlua.c @@ -160,6 +160,10 @@ void initialise_lua_state() # error "Pd version is too new, please file a bug report" #endif +#if PD_MINOR_VERSION>=54 +# define PD_MULTICHANNEL +#endif + #ifdef UNUSED #elif defined(__GNUC__) # define UNUSED(x) UNUSED_ ## x __attribute__((unused)) @@ -1023,7 +1027,7 @@ static void pdlua_menu_open(t_pdlua *o) } //post("path = %s, name = %s, pathname = %s", path, name, pathname); lua_pop(__L(), 2); /* pop name, global "pd"*/ - + #if PD_MAJOR_VERSION==0 && PD_MINOR_VERSION<43 post("Opening %s for editing", pathname); #else @@ -1330,14 +1334,26 @@ static int pdlua_class_new(lua_State *L) snprintf(name_gfx, MAXPDSTRING-1, "%s:gfx", name); PDLUA_DEBUG3("pdlua_class_new: L is %p, name is %s stack top is %d", L, name, lua_gettop(L)); c = class_new(gensym((char *) name), (t_newmethod) pdlua_new, - (t_method) pdlua_free, sizeof(t_pdlua), CLASS_NOINLET | CLASS_MULTICHANNEL, A_GIMME, 0); + (t_method) pdlua_free, sizeof(t_pdlua), +#ifdef PD_MULTICHANNEL + CLASS_NOINLET | CLASS_MULTICHANNEL, +#else + CLASS_NOINLET, +#endif + A_GIMME, 0); if (strcmp(name, "pdlua") && strcmp(name, "pdluax")) { // Shadow class for graphics objects. This is an exact clone of the // regular (non-gui) class, except that it has a different // widgetbehavior. We only need this for the regular Lua objects, the // pdlua and pdluax built-ins don't have this. c_gfx = class_new(gensym((char *) name_gfx), (t_newmethod) pdlua_new, - (t_method) pdlua_free, sizeof(t_pdlua), CLASS_NOINLET | CLASS_MULTICHANNEL, A_GIMME, 0); + (t_method) pdlua_free, sizeof(t_pdlua), +#ifdef PD_MULTICHANNEL + CLASS_NOINLET | CLASS_MULTICHANNEL, +#else + CLASS_NOINLET, +#endif + A_GIMME, 0); class_sethelpsymbol(c_gfx, gensym((char *) name)); } @@ -2630,7 +2646,13 @@ static int pdlua_signal_setmultiout(lua_State *L) if (x && outidx >= 0 && outidx < x->sigoutlets) { PDLUA_DEBUG("pdlua_signal_setmultiout: nchans", nchans); +#ifdef PD_MULTICHANNEL signal_setmultiout(&x->sp[x->siginlets + outidx], nchans); +#else + // FIXME: does this make sense here at all? + pd_error(NULL, "lua: update Pd version for multichannel support"); +#endif + return 0; } } From 472be48959c9e31c76ad911ccde5ec96f36c6d12 Mon Sep 17 00:00:00 2001 From: Ben Wesch Date: Mon, 7 Oct 2024 08:11:31 +0200 Subject: [PATCH 04/18] default outlet channel count to 1 --- pdlua.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pdlua.c b/pdlua.c index 0c7119d..3534537 100644 --- a/pdlua.c +++ b/pdlua.c @@ -1145,13 +1145,20 @@ static void pdlua_dsp(t_pdlua *x, t_signal **sp) { x->sp = sp; // FIXME: is this the way? (setting this for signal_setmultiout) +#ifdef PD_MULTICHANNEL + // Set default channel count to 1 for all signal outlets + for (int i = x->siginlets; i < sum; i++) { + signal_setmultiout(&sp[i], 1); + } +#endif + // Call Lua _dsp function lua_getglobal(__L(), "pd"); lua_getfield (__L(), -1, "_dsp"); lua_pushlightuserdata(__L(), x); lua_pushnumber(__L(), sys_getsr()); lua_pushnumber(__L(), sys_getblksize()); - + // Pass input channel counts as a table lua_newtable(__L()); for (int i = 0; i < x->siginlets; i++) { From 8b270ec2b333977a0ab46a146e7a5bc7be0a6423 Mon Sep 17 00:00:00 2001 From: Ben Wesch Date: Mon, 7 Oct 2024 08:20:56 +0200 Subject: [PATCH 05/18] fall back to 1 for invalid signal_setmultiout channel count --- pdlua.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pdlua.c b/pdlua.c index 3534537..c6a3e92 100644 --- a/pdlua.c +++ b/pdlua.c @@ -2646,8 +2646,12 @@ static int pdlua_signal_setmultiout(lua_State *L) { t_pdlua *x = (t_pdlua *)lua_touserdata(L, 1); PDLUA_DEBUG("pdlua_signal_setmultiout: x = %p", x); - int outidx = lua_tointeger(L, 2) - 1; // Lua uses 1-based indexing + int outidx = lua_tointeger(L, 2) - 1; int nchans = lua_tointeger(L, 3); + if (nchans < 1) { + pd_error(NULL, "lua: invalid channel count: %d, setting to 1", nchans); + nchans = 1; // Ensure at least one channel + } PDLUA_DEBUG2("pdlua_signal_setmultiout: outidx = %d, nchans = %d", outidx, nchans); if (x && outidx >= 0 && outidx < x->sigoutlets) From b227521bb5875b5682bd306323bec10f6bc5791d Mon Sep 17 00:00:00 2001 From: Ben Wesch Date: Mon, 7 Oct 2024 09:14:32 +0200 Subject: [PATCH 06/18] more multichannel checks --- pdlua.c | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/pdlua.c b/pdlua.c index c6a3e92..9c538be 100644 --- a/pdlua.c +++ b/pdlua.c @@ -1039,9 +1039,9 @@ static void pdlua_menu_open(t_pdlua *o) plugdata_forward_message(o, gensym("open_textfile"), 1, &arg); #else if (nw_gui_vmess) - nw_gui_vmess("open_textfile", "s", pathname); + nw_gui_vmess("open_textfile", "s", pathname); else - sys_vgui("::pd_menucommands::menu_openfile {%s}\n", pathname); + sys_vgui("::pd_menucommands::menu_openfile {%s}\n", pathname); #endif } else { lua_pop(__L(), 2); /* pop name, global "pd"*/ @@ -1065,7 +1065,11 @@ static t_int *pdlua_perform(t_int *w){ lua_newtable(__L()); t_signal *sig = (t_signal *)(w[2 + i]); t_float *in = sig->s_vec; +#ifdef PD_MULTICHANNEL int nchans = sig->s_nchans; +#else + int nchans = 1; +#endif int s_n_allchans = nblock * nchans; // sum of all inlet samples PDLUA_DEBUG("pdlua_perform: inlet %d", i); PDLUA_DEBUG2("pdlua_perform: nchans: %d, s_n_allchans: %d", nchans, s_n_allchans); @@ -1111,7 +1115,11 @@ static t_int *pdlua_perform(t_int *w){ } t_signal *sig = (t_signal *)(w[2 + o->siginlets + i]); t_float *out = sig->s_vec; +#ifdef PD_MULTICHANNEL int nchans = sig->s_nchans; +#else + int nchans = 1; +#endif int s_n_allchans = nblock * nchans; // sum of all outlet samples PDLUA_DEBUG("pdlua_perform: outlet %d", i); PDLUA_DEBUG2("nchans: %d, s_n_allchans: %d", nchans, s_n_allchans); @@ -1147,9 +1155,8 @@ static void pdlua_dsp(t_pdlua *x, t_signal **sp) { #ifdef PD_MULTICHANNEL // Set default channel count to 1 for all signal outlets - for (int i = x->siginlets; i < sum; i++) { + for (int i = x->siginlets; i < sum; i++) signal_setmultiout(&sp[i], 1); - } #endif // Call Lua _dsp function @@ -1164,13 +1171,17 @@ static void pdlua_dsp(t_pdlua *x, t_signal **sp) { for (int i = 0; i < x->siginlets; i++) { PDLUA_DEBUG2("pdlua_dsp: inlet: %d, s_nchans: %d", i, sp[i]->s_nchans); lua_pushinteger(__L(), i + 1); +#ifdef PD_MULTICHANNEL lua_pushinteger(__L(), sp[i]->s_nchans); +#else + lua_pushinteger(__L(), 1); +#endif lua_settable(__L(), -3); } - if (lua_pcall(__L(), 4, 0, 0)) { + if (lua_pcall(__L(), 4, 0, 0)) mylua_error(__L(), x, "dsp"); - } + lua_pop(__L(), 1); /* pop the global "pd" */ PDLUA_DEBUG("pdlua_dsp: end. stack top %d", lua_gettop(__L())); @@ -2656,14 +2667,12 @@ static int pdlua_signal_setmultiout(lua_State *L) if (x && outidx >= 0 && outidx < x->sigoutlets) { - PDLUA_DEBUG("pdlua_signal_setmultiout: nchans", nchans); #ifdef PD_MULTICHANNEL + PDLUA_DEBUG("pdlua_signal_setmultiout: nchans", nchans); signal_setmultiout(&x->sp[x->siginlets + outidx], nchans); #else - // FIXME: does this make sense here at all? - pd_error(NULL, "lua: update Pd version for multichannel support"); + post("lua: Pd version without multichannel support, setting output channels to 1"); #endif - return 0; } } @@ -3123,7 +3132,7 @@ void pdlua_setup(void) nw_gui_vmess = dlsym(RTLD_DEFAULT, "gui_vmess"); #endif if (nw_gui_vmess) - post("pdlua: using JavaScript interface (nw.js)"); + post("pdlua: using JavaScript interface (nw.js)"); #endif } From 786ad4332a3d847f60d2e4af681a9ba59a9c712f Mon Sep 17 00:00:00 2001 From: Ben Wesch Date: Mon, 7 Oct 2024 09:33:17 +0200 Subject: [PATCH 07/18] minor clean-up --- pdlua.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pdlua.c b/pdlua.c index 9c538be..df4a091 100644 --- a/pdlua.c +++ b/pdlua.c @@ -1186,14 +1186,12 @@ static void pdlua_dsp(t_pdlua *x, t_signal **sp) { PDLUA_DEBUG("pdlua_dsp: end. stack top %d", lua_gettop(__L())); - // Prepare signal vector for dsp_addv int sigvecsize = 1 + sum; // x and sp[i] for each iolet t_int *sigvec = (t_int *)getbytes(sigvecsize * sizeof(t_int)); sigvec[0] = (t_int)x; - for (int i = 0; i < sum; i++) { + for (int i = 0; i < sum; i++) sigvec[1 + i] = (t_int)sp[i]; - } dsp_addv(pdlua_perform, sigvecsize, sigvec); freebytes(sigvec, sigvecsize * sizeof(t_int)); From ddb44357a02ffecdf2719f12c804724ce6b19e14 Mon Sep 17 00:00:00 2001 From: Ben Wesch Date: Mon, 7 Oct 2024 18:26:30 +0200 Subject: [PATCH 08/18] allow builds w/o multichannel, fall back for old Pd versions --- pdlua.c | 71 ++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 52 insertions(+), 19 deletions(-) diff --git a/pdlua.c b/pdlua.c index df4a091..2690907 100644 --- a/pdlua.c +++ b/pdlua.c @@ -59,6 +59,9 @@ #include "pdlua_gfx.h" +typedef void (*t_signal_setmultiout)(t_signal **, int); +static t_signal_setmultiout g_signal_setmultiout; + // This used to be in s_stuff.h, but not anymore since 0.55.1test1. int sys_trytoopenone(const char *dir, const char *name, const char* ext, char *dirresult, char **nameresult, unsigned int size, int bin); @@ -160,8 +163,12 @@ void initialise_lua_state() # error "Pd version is too new, please file a bug report" #endif -#if PD_MINOR_VERSION>=54 -# define PD_MULTICHANNEL +#ifndef PD_HAVE_MULTICHANNEL +# if PD_MINOR_VERSION>=54 +# define PD_HAVE_MULTICHANNEL 1 +# else +# define PD_HAVE_MULTICHANNEL 0 +# endif #endif #ifdef UNUSED @@ -1065,7 +1072,7 @@ static t_int *pdlua_perform(t_int *w){ lua_newtable(__L()); t_signal *sig = (t_signal *)(w[2 + i]); t_float *in = sig->s_vec; -#ifdef PD_MULTICHANNEL +#if PD_HAVE_MULTICHANNEL int nchans = sig->s_nchans; #else int nchans = 1; @@ -1115,7 +1122,7 @@ static t_int *pdlua_perform(t_int *w){ } t_signal *sig = (t_signal *)(w[2 + o->siginlets + i]); t_float *out = sig->s_vec; -#ifdef PD_MULTICHANNEL +#if PD_HAVE_MULTICHANNEL int nchans = sig->s_nchans; #else int nchans = 1; @@ -1153,11 +1160,11 @@ static void pdlua_dsp(t_pdlua *x, t_signal **sp) { x->sp = sp; // FIXME: is this the way? (setting this for signal_setmultiout) -#ifdef PD_MULTICHANNEL - // Set default channel count to 1 for all signal outlets - for (int i = x->siginlets; i < sum; i++) - signal_setmultiout(&sp[i], 1); -#endif + if (g_signal_setmultiout) { + // Set default channel count to 1 for all signal outlets + for (int i = x->siginlets; i < sum; i++) + g_signal_setmultiout(&sp[i], 1); + } // Call Lua _dsp function lua_getglobal(__L(), "pd"); @@ -1169,13 +1176,18 @@ static void pdlua_dsp(t_pdlua *x, t_signal **sp) { // Pass input channel counts as a table lua_newtable(__L()); for (int i = 0; i < x->siginlets; i++) { - PDLUA_DEBUG2("pdlua_dsp: inlet: %d, s_nchans: %d", i, sp[i]->s_nchans); lua_pushinteger(__L(), i + 1); -#ifdef PD_MULTICHANNEL - lua_pushinteger(__L(), sp[i]->s_nchans); + PDLUA_DEBUG2("pdlua_dsp: inlet: %d, s_nchans: %d", i, sp[i]->s_nchans); + if (g_signal_setmultiout) +#if PD_HAVE_MULTICHANNEL + lua_pushinteger(__L(), sp[i]->s_nchans); #else - lua_pushinteger(__L(), 1); + // Pd supports multichannel, but pdlua built without + lua_pushinteger(__L(), 1); #endif + else + // Pd doesn't support multichannel + lua_pushinteger(__L(), 1); lua_settable(__L(), -3); } @@ -1347,11 +1359,12 @@ static int pdlua_class_new(lua_State *L) // fail silently, return nothing return 0; } + snprintf(name_gfx, MAXPDSTRING-1, "%s:gfx", name); PDLUA_DEBUG3("pdlua_class_new: L is %p, name is %s stack top is %d", L, name, lua_gettop(L)); c = class_new(gensym((char *) name), (t_newmethod) pdlua_new, (t_method) pdlua_free, sizeof(t_pdlua), -#ifdef PD_MULTICHANNEL +#if PD_HAVE_MULTICHANNEL CLASS_NOINLET | CLASS_MULTICHANNEL, #else CLASS_NOINLET, @@ -1364,7 +1377,7 @@ static int pdlua_class_new(lua_State *L) // pdlua and pdluax built-ins don't have this. c_gfx = class_new(gensym((char *) name_gfx), (t_newmethod) pdlua_new, (t_method) pdlua_free, sizeof(t_pdlua), -#ifdef PD_MULTICHANNEL +#if PD_HAVE_MULTICHANNEL CLASS_NOINLET | CLASS_MULTICHANNEL, #else CLASS_NOINLET, @@ -1448,7 +1461,7 @@ static int pdlua_object_new(lua_State *L) o->pdlua_class = c; o->pdlua_class_gfx = c_gfx; o->sp = NULL; - + o->gfx.width = 80; o->gfx.height = 80; @@ -2665,11 +2678,14 @@ static int pdlua_signal_setmultiout(lua_State *L) if (x && outidx >= 0 && outidx < x->sigoutlets) { -#ifdef PD_MULTICHANNEL +#if PD_HAVE_MULTICHANNEL PDLUA_DEBUG("pdlua_signal_setmultiout: nchans", nchans); - signal_setmultiout(&x->sp[x->siginlets + outidx], nchans); + if (g_signal_setmultiout) + g_signal_setmultiout(&x->sp[x->siginlets + outidx], nchans); + else + pd_error(NULL, "lua: signal_setmultiout: Pd version without multichannel support"); #else - post("lua: Pd version without multichannel support, setting output channels to 1"); + pd_error(NULL, "lua: signal_setmultiout: pdlua built without multichannel support"); #endif return 0; } @@ -3024,6 +3040,23 @@ void pdlua_setup(void) #endif post(luaversionStr); +// multichannel handling copied from https://github.com/Spacechild1/vstplugin/blob/3f0ed8a800ea238bf204a2ead940b2d1324ac909/pd/src/vstplugin~.cpp#L4122-L4136 +#ifdef _WIN32 + // get a handle to the module containing the Pd API functions. + // NB: GetModuleHandle("pd.dll") does not cover all cases. + HMODULE module; + if (GetModuleHandleEx( + GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + (LPCSTR)&pd_typedmess, &module)) { + g_signal_setmultiout = (t_signal_setmultiout)(void *)GetProcAddress( + module, "signal_setmultiout"); + } +#else + // search recursively, starting from the main program + g_signal_setmultiout = (t_signal_setmultiout)dlsym( + dlopen(NULL, RTLD_NOW), "signal_setmultiout"); +#endif + pdlua_proxyinlet_setup(); PDLUA_DEBUG("pdlua pdlua_proxyinlet_setup done", 0); pdlua_proxyreceive_setup(); From 1afd30d022ff8a075fb750aa01368ec5452990d3 Mon Sep 17 00:00:00 2001 From: Ben Wesch Date: Mon, 7 Oct 2024 21:46:31 +0200 Subject: [PATCH 09/18] refactor signal_setmultiout method, better error handling --- pd.lua | 2 +- pdlua.c | 62 ++++++++++++++++++++++++++++++++------------------------- 2 files changed, 36 insertions(+), 28 deletions(-) diff --git a/pd.lua b/pd.lua index c3fe2ef..28b5dd1 100644 --- a/pd.lua +++ b/pd.lua @@ -462,7 +462,7 @@ function pd.Class:canvas_realizedollar(s) end function pd.Class:signal_setmultiout(n, channelcount) - return pd._signal_setmultiout(self._object, n, channelcount) + pd._signal_setmultiout(self._object, n, channelcount) end function pd.Class:repaint(layer) diff --git a/pdlua.c b/pdlua.c index 2690907..0ae05b6 100644 --- a/pdlua.c +++ b/pdlua.c @@ -1182,12 +1182,10 @@ static void pdlua_dsp(t_pdlua *x, t_signal **sp) { #if PD_HAVE_MULTICHANNEL lua_pushinteger(__L(), sp[i]->s_nchans); #else - // Pd supports multichannel, but pdlua built without - lua_pushinteger(__L(), 1); + lua_pushinteger(__L(), 1); // Pd supports multichannel, but pdlua built without #endif else - // Pd doesn't support multichannel - lua_pushinteger(__L(), 1); + lua_pushinteger(__L(), 1); // Pd doesn't support multichannel lua_settable(__L(), -3); } @@ -2664,33 +2662,43 @@ static int pdlua_canvas_realizedollar(lua_State *L) static int pdlua_signal_setmultiout(lua_State *L) { - if (lua_islightuserdata(L, 1) && lua_isnumber(L, 2) && lua_isnumber(L, 3)) + char msg[MAXPDSTRING]; + + if (!(lua_islightuserdata(L, 1) && lua_isnumber(L, 2) && lua_isnumber(L, 3))) { - t_pdlua *x = (t_pdlua *)lua_touserdata(L, 1); - PDLUA_DEBUG("pdlua_signal_setmultiout: x = %p", x); - int outidx = lua_tointeger(L, 2) - 1; - int nchans = lua_tointeger(L, 3); - if (nchans < 1) { - pd_error(NULL, "lua: invalid channel count: %d, setting to 1", nchans); - nchans = 1; // Ensure at least one channel - } - PDLUA_DEBUG2("pdlua_signal_setmultiout: outidx = %d, nchans = %d", outidx, nchans); - - if (x && outidx >= 0 && outidx < x->sigoutlets) - { + pd_error(NULL, "%s: signal_setmultiout: invalid arguments", src_info(L, msg)); + return 0; + } + + t_pdlua *x = (t_pdlua *)lua_touserdata(L, 1); + int outidx = lua_tointeger(L, 2) - 1; + int nchans = lua_tointeger(L, 3); + if (!(x && outidx >= 0 && outidx < x->sigoutlets)) + { + pd_error(NULL, "%s: signal_setmultiout: must be called from dsp method", src_info(L, msg)); + return 0; + } + + if (nchans < 1) { + pd_error(NULL, "%s: signal_setmultiout: invalid channel count: %d, setting to 1", src_info(L, msg), nchans); + nchans = 1; // Ensure at least one channel + } + #if PD_HAVE_MULTICHANNEL - PDLUA_DEBUG("pdlua_signal_setmultiout: nchans", nchans); - if (g_signal_setmultiout) - g_signal_setmultiout(&x->sp[x->siginlets + outidx], nchans); - else - pd_error(NULL, "lua: signal_setmultiout: Pd version without multichannel support"); -#else - pd_error(NULL, "lua: signal_setmultiout: pdlua built without multichannel support"); -#endif + if (g_signal_setmultiout) + if (x->sp && x->sp[x->siginlets + outidx]) + g_signal_setmultiout(&x->sp[x->siginlets + outidx], nchans); + else { + pd_error(x, "%s: signal_setmultiout: invalid signal pointer. must be called from dsp method", src_info(L, msg)); return 0; } - } - return luaL_error(L, "Invalid arguments to signal_setmultiout"); + else + pd_error(NULL, "%s: signal_setmultiout: Pd version without multichannel support", src_info(L, msg)); +#else + pd_error(NULL, "%s: signal_setmultiout: pdlua built without multichannel support", src_info(L, msg)); +#endif + + return 0; } /** Initialize the pd API for Lua. */ From 7fcd6df6ca9dd3b40e37c4ff5bdddd75b7de7e69 Mon Sep 17 00:00:00 2001 From: Ben Wesch Date: Mon, 7 Oct 2024 21:54:21 +0200 Subject: [PATCH 10/18] more error handling for signal_setmultiout --- pdlua.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/pdlua.c b/pdlua.c index 0ae05b6..ec6bade 100644 --- a/pdlua.c +++ b/pdlua.c @@ -2664,8 +2664,7 @@ static int pdlua_signal_setmultiout(lua_State *L) { char msg[MAXPDSTRING]; - if (!(lua_islightuserdata(L, 1) && lua_isnumber(L, 2) && lua_isnumber(L, 3))) - { + if (!lua_islightuserdata(L, 1) || !lua_isnumber(L, 2) || !lua_isnumber(L, 3)) { pd_error(NULL, "%s: signal_setmultiout: invalid arguments", src_info(L, msg)); return 0; } @@ -2673,11 +2672,14 @@ static int pdlua_signal_setmultiout(lua_State *L) t_pdlua *x = (t_pdlua *)lua_touserdata(L, 1); int outidx = lua_tointeger(L, 2) - 1; int nchans = lua_tointeger(L, 3); - if (!(x && outidx >= 0 && outidx < x->sigoutlets)) - { + if (!x) { pd_error(NULL, "%s: signal_setmultiout: must be called from dsp method", src_info(L, msg)); return 0; } + if (outidx < 0 || outidx >= x->sigoutlets) { + pd_error(NULL, "%s: signal_setmultiout: invalid outlet index. called outside dsp method?", src_info(L, msg)); + return 0; + } if (nchans < 1) { pd_error(NULL, "%s: signal_setmultiout: invalid channel count: %d, setting to 1", src_info(L, msg), nchans); From da811cf1ddcd1ac4d7baf58b38e342da7a65687a Mon Sep 17 00:00:00 2001 From: Ben Wesch Date: Mon, 7 Oct 2024 23:29:41 +0200 Subject: [PATCH 11/18] fix sample table passing for mc builds and non-mc pd --- pdlua.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/pdlua.c b/pdlua.c index ec6bade..d3053fd 100644 --- a/pdlua.c +++ b/pdlua.c @@ -1073,7 +1073,7 @@ static t_int *pdlua_perform(t_int *w){ t_signal *sig = (t_signal *)(w[2 + i]); t_float *in = sig->s_vec; #if PD_HAVE_MULTICHANNEL - int nchans = sig->s_nchans; + int nchans = sig->s_nchans ? sig->s_nchans : 1; #else int nchans = 1; #endif @@ -1123,7 +1123,7 @@ static t_int *pdlua_perform(t_int *w){ t_signal *sig = (t_signal *)(w[2 + o->siginlets + i]); t_float *out = sig->s_vec; #if PD_HAVE_MULTICHANNEL - int nchans = sig->s_nchans; + int nchans = sig->s_nchans ? sig->s_nchans : 1; #else int nchans = 1; #endif @@ -1177,14 +1177,14 @@ static void pdlua_dsp(t_pdlua *x, t_signal **sp) { lua_newtable(__L()); for (int i = 0; i < x->siginlets; i++) { lua_pushinteger(__L(), i + 1); - PDLUA_DEBUG2("pdlua_dsp: inlet: %d, s_nchans: %d", i, sp[i]->s_nchans); - if (g_signal_setmultiout) + if (g_signal_setmultiout) { #if PD_HAVE_MULTICHANNEL + PDLUA_DEBUG2("pdlua_dsp: inlet: %d, s_nchans: %d", i, sp[i]->s_nchans); lua_pushinteger(__L(), sp[i]->s_nchans); #else lua_pushinteger(__L(), 1); // Pd supports multichannel, but pdlua built without #endif - else + } else lua_pushinteger(__L(), 1); // Pd doesn't support multichannel lua_settable(__L(), -3); } @@ -2687,14 +2687,14 @@ static int pdlua_signal_setmultiout(lua_State *L) } #if PD_HAVE_MULTICHANNEL - if (g_signal_setmultiout) + if (g_signal_setmultiout) { if (x->sp && x->sp[x->siginlets + outidx]) g_signal_setmultiout(&x->sp[x->siginlets + outidx], nchans); else { pd_error(x, "%s: signal_setmultiout: invalid signal pointer. must be called from dsp method", src_info(L, msg)); return 0; } - else + } else pd_error(NULL, "%s: signal_setmultiout: Pd version without multichannel support", src_info(L, msg)); #else pd_error(NULL, "%s: signal_setmultiout: pdlua built without multichannel support", src_info(L, msg)); From 86b34146d48c4674412ad194a1706a613f7fd52e Mon Sep 17 00:00:00 2001 From: Ben Wesch Date: Tue, 8 Oct 2024 08:30:37 +0200 Subject: [PATCH 12/18] handle PD_MULTICHANNEL in makefile, simplify code --- Makefile | 2 ++ pdlua.c | 42 ++++++++++++++++-------------------------- 2 files changed, 18 insertions(+), 26 deletions(-) diff --git a/Makefile b/Makefile index 791024e..aa88710 100644 --- a/Makefile +++ b/Makefile @@ -18,6 +18,7 @@ pdlua_version := $(shell git describe --tags 2>/dev/null) luasrc = $(wildcard lua/onelua.c) PKG_CONFIG ?= pkg-config +PD_MULTICHANNEL ?= 1 ifeq ($(luasrc),) # compile with installed liblua @@ -40,6 +41,7 @@ endef endif cflags = ${luaflags} -DPDLUA_VERSION="$(pdlua_version)" +cflags += -DPD_MULTICHANNEL=$(PD_MULTICHANNEL) pdlua.class.sources := pdlua.c $(luasrc) pdlua.class.ldlibs := $(lualibs) diff --git a/pdlua.c b/pdlua.c index d3053fd..60a6aef 100644 --- a/pdlua.c +++ b/pdlua.c @@ -163,12 +163,14 @@ void initialise_lua_state() # error "Pd version is too new, please file a bug report" #endif -#ifndef PD_HAVE_MULTICHANNEL -# if PD_MINOR_VERSION>=54 -# define PD_HAVE_MULTICHANNEL 1 -# else -# define PD_HAVE_MULTICHANNEL 0 +#if PD_MINOR_VERSION >= 54 +# ifndef PD_MULTICHANNEL +# define PD_MULTICHANNEL 1 # endif +#else +# pragma message("building without multi-channel support; requires Pd 0.54+") +# define PD_MULTICHANNEL 0 +# define CLASS_MULTICHANNEL 0 #endif #ifdef UNUSED @@ -1072,7 +1074,7 @@ static t_int *pdlua_perform(t_int *w){ lua_newtable(__L()); t_signal *sig = (t_signal *)(w[2 + i]); t_float *in = sig->s_vec; -#if PD_HAVE_MULTICHANNEL +#if PD_MULTICHANNEL int nchans = sig->s_nchans ? sig->s_nchans : 1; #else int nchans = 1; @@ -1122,7 +1124,7 @@ static t_int *pdlua_perform(t_int *w){ } t_signal *sig = (t_signal *)(w[2 + o->siginlets + i]); t_float *out = sig->s_vec; -#if PD_HAVE_MULTICHANNEL +#if PD_MULTICHANNEL int nchans = sig->s_nchans ? sig->s_nchans : 1; #else int nchans = 1; @@ -1177,15 +1179,15 @@ static void pdlua_dsp(t_pdlua *x, t_signal **sp) { lua_newtable(__L()); for (int i = 0; i < x->siginlets; i++) { lua_pushinteger(__L(), i + 1); +#if PD_MULTICHANNEL if (g_signal_setmultiout) { -#if PD_HAVE_MULTICHANNEL PDLUA_DEBUG2("pdlua_dsp: inlet: %d, s_nchans: %d", i, sp[i]->s_nchans); lua_pushinteger(__L(), sp[i]->s_nchans); + } else + lua_pushinteger(__L(), 1); // Pd version without multichannel support #else - lua_pushinteger(__L(), 1); // Pd supports multichannel, but pdlua built without + lua_pushinteger(__L(), 1); // pdlua built without multichannel support #endif - } else - lua_pushinteger(__L(), 1); // Pd doesn't support multichannel lua_settable(__L(), -3); } @@ -1361,26 +1363,14 @@ static int pdlua_class_new(lua_State *L) snprintf(name_gfx, MAXPDSTRING-1, "%s:gfx", name); PDLUA_DEBUG3("pdlua_class_new: L is %p, name is %s stack top is %d", L, name, lua_gettop(L)); c = class_new(gensym((char *) name), (t_newmethod) pdlua_new, - (t_method) pdlua_free, sizeof(t_pdlua), -#if PD_HAVE_MULTICHANNEL - CLASS_NOINLET | CLASS_MULTICHANNEL, -#else - CLASS_NOINLET, -#endif - A_GIMME, 0); + (t_method) pdlua_free, sizeof(t_pdlua), CLASS_NOINLET | CLASS_MULTICHANNEL, A_GIMME, 0); if (strcmp(name, "pdlua") && strcmp(name, "pdluax")) { // Shadow class for graphics objects. This is an exact clone of the // regular (non-gui) class, except that it has a different // widgetbehavior. We only need this for the regular Lua objects, the // pdlua and pdluax built-ins don't have this. c_gfx = class_new(gensym((char *) name_gfx), (t_newmethod) pdlua_new, - (t_method) pdlua_free, sizeof(t_pdlua), -#if PD_HAVE_MULTICHANNEL - CLASS_NOINLET | CLASS_MULTICHANNEL, -#else - CLASS_NOINLET, -#endif - A_GIMME, 0); + (t_method) pdlua_free, sizeof(t_pdlua), CLASS_NOINLET | CLASS_MULTICHANNEL, A_GIMME, 0); class_sethelpsymbol(c_gfx, gensym((char *) name)); } @@ -2686,7 +2676,7 @@ static int pdlua_signal_setmultiout(lua_State *L) nchans = 1; // Ensure at least one channel } -#if PD_HAVE_MULTICHANNEL +#if PD_MULTICHANNEL if (g_signal_setmultiout) { if (x->sp && x->sp[x->siginlets + outidx]) g_signal_setmultiout(&x->sp[x->siginlets + outidx], nchans); From b51c9c01694c6e5d55f976c8c79995320770f93b Mon Sep 17 00:00:00 2001 From: Ben Wesch Date: Tue, 8 Oct 2024 17:20:58 +0200 Subject: [PATCH 13/18] avoid warning when building w/o multichannel --- Makefile | 5 +++-- pdlua.c | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index aa88710..7f71a2c 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,6 @@ pdlua_version := $(shell git describe --tags 2>/dev/null) luasrc = $(wildcard lua/onelua.c) PKG_CONFIG ?= pkg-config -PD_MULTICHANNEL ?= 1 ifeq ($(luasrc),) # compile with installed liblua @@ -41,7 +40,9 @@ endef endif cflags = ${luaflags} -DPDLUA_VERSION="$(pdlua_version)" -cflags += -DPD_MULTICHANNEL=$(PD_MULTICHANNEL) +ifdef PD_MULTICHANNEL + cflags += -DPD_MULTICHANNEL=$(PD_MULTICHANNEL) +endif pdlua.class.sources := pdlua.c $(luasrc) pdlua.class.ldlibs := $(lualibs) diff --git a/pdlua.c b/pdlua.c index 60a6aef..8685263 100644 --- a/pdlua.c +++ b/pdlua.c @@ -169,6 +169,7 @@ void initialise_lua_state() # endif #else # pragma message("building without multi-channel support; requires Pd 0.54+") +# undef PD_MULTICHANNEL # define PD_MULTICHANNEL 0 # define CLASS_MULTICHANNEL 0 #endif From 505303451d4189457547760390e1a692c7574ece Mon Sep 17 00:00:00 2001 From: Ben Wesch Date: Tue, 8 Oct 2024 17:26:14 +0200 Subject: [PATCH 14/18] remove unnecessary check for outlet tables --- pdlua.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/pdlua.c b/pdlua.c index 8685263..ef3169b 100644 --- a/pdlua.c +++ b/pdlua.c @@ -1118,11 +1118,6 @@ static t_int *pdlua_perform(t_int *w){ for (int i = o->sigoutlets - 1; i >= 0; i--) { - if (!lua_istable(__L(), -1)) { - pd_error(o, "pdlua_perform: expected table for outlet %d", i); - lua_pop(__L(), 1); - continue; - } t_signal *sig = (t_signal *)(w[2 + o->siginlets + i]); t_float *out = sig->s_vec; #if PD_MULTICHANNEL From 59632795544475e76de84dd1f32f7e870e470d57 Mon Sep 17 00:00:00 2001 From: Ben Wesch Date: Tue, 8 Oct 2024 20:05:12 +0200 Subject: [PATCH 15/18] handle local block size --- pdlua.c | 10 ++++------ pdlua.h | 1 + 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/pdlua.c b/pdlua.c index ef3169b..bc91dc5 100644 --- a/pdlua.c +++ b/pdlua.c @@ -1061,9 +1061,7 @@ static void pdlua_menu_open(t_pdlua *o) static t_int *pdlua_perform(t_int *w){ t_pdlua *o = (t_pdlua *)(w[1]); - t_signal *first_sig = (t_signal *)(w[2]); - int nblock = first_sig->s_n; - // FIXME: should we use a o->blocksize here that gets set in dsp? + int nblock = o->blocksize; PDLUA_DEBUG("pdlua_perform: stack top %d", lua_gettop(__L())); lua_getglobal(__L(), "pd"); @@ -1153,11 +1151,11 @@ static void pdlua_dsp(t_pdlua *x, t_signal **sp) { int sum = x->siginlets + x->sigoutlets; if(sum == 0) return; x->sig_warned = 0; + x->blocksize = (int)sp[0]->s_n; // local block size instead of sys_getblksize(); + x->sp = sp; // prepare for Lua signal_setmultiout PDLUA_DEBUG("pdlua_dsp: stack top %d", lua_gettop(__L())); - x->sp = sp; // FIXME: is this the way? (setting this for signal_setmultiout) - if (g_signal_setmultiout) { // Set default channel count to 1 for all signal outlets for (int i = x->siginlets; i < sum; i++) @@ -1169,7 +1167,7 @@ static void pdlua_dsp(t_pdlua *x, t_signal **sp) { lua_getfield (__L(), -1, "_dsp"); lua_pushlightuserdata(__L(), x); lua_pushnumber(__L(), sys_getsr()); - lua_pushnumber(__L(), sys_getblksize()); + lua_pushnumber(__L(), x->blocksize); // Pass input channel counts as a table lua_newtable(__L()); diff --git a/pdlua.h b/pdlua.h index 02a0f83..869ad89 100644 --- a/pdlua.h +++ b/pdlua.h @@ -64,6 +64,7 @@ typedef struct pdlua int siginlets; // Number of signal inlets. int sigoutlets; // Number of signal outlets. int sig_warned; // Flag for perform signal errors. + int blocksize; // Blocksize set in dsp method. t_canvas *canvas; // The canvas that the object was created on. int has_gui; // True if graphics are enabled. t_pdlua_gfx gfx; // Holds state for graphics. From 36663b052b194420b0e469978d301c04705a219a Mon Sep 17 00:00:00 2001 From: Ben Wesch Date: Tue, 8 Oct 2024 22:08:37 +0200 Subject: [PATCH 16/18] revert some formatting changes --- pdlua.c | 49 +++++++++++++++++++++++-------------------------- 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/pdlua.c b/pdlua.c index bc91dc5..69f365b 100644 --- a/pdlua.c +++ b/pdlua.c @@ -1037,7 +1037,7 @@ static void pdlua_menu_open(t_pdlua *o) } //post("path = %s, name = %s, pathname = %s", path, name, pathname); lua_pop(__L(), 2); /* pop name, global "pd"*/ - + #if PD_MAJOR_VERSION==0 && PD_MINOR_VERSION<43 post("Opening %s for editing", pathname); #else @@ -1049,9 +1049,9 @@ static void pdlua_menu_open(t_pdlua *o) plugdata_forward_message(o, gensym("open_textfile"), 1, &arg); #else if (nw_gui_vmess) - nw_gui_vmess("open_textfile", "s", pathname); + nw_gui_vmess("open_textfile", "s", pathname); else - sys_vgui("::pd_menucommands::menu_openfile {%s}\n", pathname); + sys_vgui("::pd_menucommands::menu_openfile {%s}\n", pathname); #endif } else { lua_pop(__L(), 2); /* pop name, global "pd"*/ @@ -1079,8 +1079,6 @@ static t_int *pdlua_perform(t_int *w){ int nchans = 1; #endif int s_n_allchans = nblock * nchans; // sum of all inlet samples - PDLUA_DEBUG("pdlua_perform: inlet %d", i); - PDLUA_DEBUG2("pdlua_perform: nchans: %d, s_n_allchans: %d", nchans, s_n_allchans); for (int j = 0; j < s_n_allchans; j++) { lua_pushinteger(__L(), j + 1); @@ -1088,14 +1086,14 @@ static t_int *pdlua_perform(t_int *w){ lua_settable(__L(), -3); } } - + if (lua_pcall(__L(), 1 + o->siginlets, o->sigoutlets, 0)) { mylua_error(__L(), o, "perform"); lua_pop(__L(), 1); /* pop the global pd */ - return w + 2 + o->siginlets + o->sigoutlets; + return w + o->siginlets + o->sigoutlets + 2; } - + if (!lua_istable(__L(), -1)) { const char *s = "lua: perform: function should return"; @@ -1111,9 +1109,9 @@ static t_int *pdlua_perform(t_int *w){ } } lua_pop(__L(), 1 + o->sigoutlets); - return w + 2 + o->siginlets + o->sigoutlets; + return w + o->siginlets + o->sigoutlets + 2; } - + for (int i = o->sigoutlets - 1; i >= 0; i--) { t_signal *sig = (t_signal *)(w[2 + o->siginlets + i]); @@ -1124,8 +1122,6 @@ static t_int *pdlua_perform(t_int *w){ int nchans = 1; #endif int s_n_allchans = nblock * nchans; // sum of all outlet samples - PDLUA_DEBUG("pdlua_perform: outlet %d", i); - PDLUA_DEBUG2("nchans: %d, s_n_allchans: %d", nchans, s_n_allchans); for (int j = 0; j < s_n_allchans; j++) { lua_pushinteger(__L(), (lua_Integer)(j + 1)); @@ -1140,14 +1136,15 @@ static t_int *pdlua_perform(t_int *w){ } lua_pop(__L(), 1); } - lua_pop(__L(), 1); /* pop the global "pd" */ + lua_pop(__L(), 1); /* pop the global "pd" */ + PDLUA_DEBUG("pdlua_perform: end. stack top %d", lua_gettop(__L())); - - return w + 2 + o->siginlets + o->sigoutlets; + + return w + o->siginlets + o->sigoutlets + 2; } -static void pdlua_dsp(t_pdlua *x, t_signal **sp) { +static void pdlua_dsp(t_pdlua *x, t_signal **sp){ int sum = x->siginlets + x->sigoutlets; if(sum == 0) return; x->sig_warned = 0; @@ -1175,7 +1172,6 @@ static void pdlua_dsp(t_pdlua *x, t_signal **sp) { lua_pushinteger(__L(), i + 1); #if PD_MULTICHANNEL if (g_signal_setmultiout) { - PDLUA_DEBUG2("pdlua_dsp: inlet: %d, s_nchans: %d", i, sp[i]->s_nchans); lua_pushinteger(__L(), sp[i]->s_nchans); } else lua_pushinteger(__L(), 1); // Pd version without multichannel support @@ -1186,19 +1182,21 @@ static void pdlua_dsp(t_pdlua *x, t_signal **sp) { } if (lua_pcall(__L(), 4, 0, 0)) + { mylua_error(__L(), x, "dsp"); - + } lua_pop(__L(), 1); /* pop the global "pd" */ - + PDLUA_DEBUG("pdlua_dsp: end. stack top %d", lua_gettop(__L())); - - int sigvecsize = 1 + sum; // x and sp[i] for each iolet - t_int *sigvec = (t_int *)getbytes(sigvecsize * sizeof(t_int)); + + int sigvecsize = sum + 1; + t_int* sigvec = getbytes(sigvecsize * sizeof(t_int)); sigvec[0] = (t_int)x; - for (int i = 0; i < sum; i++) - sigvec[1 + i] = (t_int)sp[i]; + for (int i = 0; i < sum; i++) + sigvec[i + 1] = (t_int)sp[i]; + dsp_addv(pdlua_perform, sigvecsize, sigvec); freebytes(sigvec, sigvecsize * sizeof(t_int)); } @@ -1353,7 +1351,6 @@ static int pdlua_class_new(lua_State *L) // fail silently, return nothing return 0; } - snprintf(name_gfx, MAXPDSTRING-1, "%s:gfx", name); PDLUA_DEBUG3("pdlua_class_new: L is %p, name is %s stack top is %d", L, name, lua_gettop(L)); c = class_new(gensym((char *) name), (t_newmethod) pdlua_new, @@ -3157,7 +3154,7 @@ void pdlua_setup(void) nw_gui_vmess = dlsym(RTLD_DEFAULT, "gui_vmess"); #endif if (nw_gui_vmess) - post("pdlua: using JavaScript interface (nw.js)"); + post("pdlua: using JavaScript interface (nw.js)"); #endif } From 408d1e714e3f43f78a00416fdfc2b6f8852b20dc Mon Sep 17 00:00:00 2001 From: Ben Wesch Date: Tue, 8 Oct 2024 22:29:09 +0200 Subject: [PATCH 17/18] init o->blocksize --- pdlua.c | 1 + 1 file changed, 1 insertion(+) diff --git a/pdlua.c b/pdlua.c index 69f365b..bc1c5cc 100644 --- a/pdlua.c +++ b/pdlua.c @@ -1436,6 +1436,7 @@ static int pdlua_object_new(lua_State *L) o->siginlets = 0; o->sigoutlets = 0; o->sig_warned = 0; + o->blocksize = 0; o->canvas = canvas_getcurrent(); o->pdlua_class = c; o->pdlua_class_gfx = c_gfx; From 9ed55f1058abe2002f3d550b61ff317278d37989 Mon Sep 17 00:00:00 2001 From: Ben Wesch Date: Wed, 9 Oct 2024 08:20:21 +0200 Subject: [PATCH 18/18] add audio stuff to graphics.txt and pdlua-help.pd --- doc/graphics.txt | 12 +++++++++++- pdlua-help.pd | 8 ++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/doc/graphics.txt b/doc/graphics.txt index abc0888..784e0e0 100644 --- a/doc/graphics.txt +++ b/doc/graphics.txt @@ -35,6 +35,7 @@ pd:Class:mouse_up(x, y) -- Mouse up callb pd:Class:mouse_move(x, y) -- Mouse move callback, called when the mouse is moved while not being down pd:Class:mouse_drag(x, y) -- Mouse drag callback, called when the mouse is moved while also being down + -- Functions you can call pd:Class:repaint(layer) -- Request a repaint for specified layer (or all layers if no parameter is set or if layer is 0). after this, the "paint" or "paint_layer_n" callback will occur @@ -59,7 +60,8 @@ g:stroke_rounded_rect(x, y, w, h, corner_radius, line_width) -- Draws the outl g:draw_line(x1, y1, x2, y2) -- Draws a line between two points g:draw_text(text, x, y, w, fontsize) -- Draws text at the specified position and size -g:fill_all() -- Fills the entire drawing area with the current color. Also will draw an object outline in the style of the host (ie. pure-data or plugdata) +g:fill_all() -- Fills the entire drawing area with the current color. Also will draw + an object outline in the style of the host (ie. pure-data or plugdata) g:translate(tx, ty) -- Translates the coordinate system by the specified amounts g:scale(sx, sy) -- Scales the coordinate system by the specified factors. This will always happen after the translation @@ -74,6 +76,14 @@ p:close_path() -- Closes the pat g:stroke_path(p, line_width) -- Draws the outline of the path with the specified line width g:fill_path(p) -- Fills the current path + +-- Audio callbacks +pd:Class:dsp(samplerate, blocksize, channel_counts) -- Called when DSP is activated and whenever the DSP graph is updated +pd:Class:perform(in1, in2, ..., in_n) -- Called for every audio block, passing tables of samples per inlet and + expecting tables of samples per outlet +pd:Class:signal_setmultiout(outlet_index, channel_count) -- Used in dsp function to achieve multichannel output for specific outlets + + -- Additional functions expandedsymbol = pd:Class:canvas_realizedollar(s) -- Expand dollar symbols in patch canvas context pd:Class:set_args(args) -- Set the object arguments to be saved in the patch file diff --git a/pdlua-help.pd b/pdlua-help.pd index a1c90a8..75bd044 100644 --- a/pdlua-help.pd +++ b/pdlua-help.pd @@ -171,7 +171,7 @@ #X connect 117 0 112 0; #X connect 118 0 110 0; #X restore 446 359 pd quickstart; -#N canvas 264 71 852 728 graphics 0; +#N canvas 264 71 851 700 graphics 0; #X obj 8 77 hello-gui; #X text 8 376 function yourclass:initialize(sel \, atoms); #X text 24 409 return true; @@ -192,9 +192,9 @@ #X text 25 543 g:fill_all(); #X text 8 323 Graphics mode is enabled automatically by defining the paint method \, see below. You can also set the size in the constructor (or in any other function) \, like this:; #X text 23 394 self:set_size(100 \, 100); -#X text 801 1407  ; -#X text 8 816 -- Callback functions you can define \; pd:Class:mouse_down(x \, y) \; pd:Class:mouse_up(x \, y) \; pd:Class:mouse_move(x \, y) \; pd:Class:mouse_drag(x \, y) \; \; \; -- Functions you can call \; pd:Class:repaint(layer) \; \; \; pd:Class:paint(g) / pd:Class:paint_layer_n(g) \; \; \; g:set_size(w \, h) \; width \, height = g:get_size(w \, h) \; \; g:set_color(r \, g \, b \, a=1.0) \; \; g:fill_ellipse(x \, y \, w \, h) \; g:stroke_ellipse(x \, y \, w \, h \, line_width) \; \; g:fill_rect(x \, y \, w \, h) \; g:stroke_rect(x \, y \, w \, h \, line_width) \; \; g:fill_rounded_rect(x \, y \, w \, h \, corner_radius) \; g:stroke_rounded_rect(x \, y \, w \, h \, corner_radius \, line_width) \; \; g:draw_line(x1 \, y1 \, x2 \, y2) \; g:draw_text(text \, x \, y \, w \, fontsize) \; \; g:fill_all() \; \; g:translate(tx \, ty) \; g:scale(sx \, sy) \; \; g:reset_transform() \; \; p = Path(x \, y) \; p:line_to(x \, y) \; p:quad_to(x1 \, y1 \, x2 \, y2) \; p:cubic_to(x1 \, y1 \, x2 \, y2 \, x3 \, y) \; p:close_path() \; \; g:stroke_path(p \, line_width) \; g:fill_path(p) \; \; \; -- Additional functions \; expandedsymbol = pd:Class:canvas_realizedollar(s) \; pd:Class:set_args(args) \; args = pd:Class:get_args() \;, f 51; -#X text 320 816 \; -- Mouse down callback \, called when the mouse is clicked \; -- Mouse up callback \, called when the mouse button is released \; -- Mouse move callback \, called when the mouse is moved while not being down \; -- Mouse drag callback \, called when the mouse is moved while also being down \; \; \; \; -- Request a repaint for specified layer (or all layers if no parameter is set or if layer is 0). after this \, the "paint" or "paint_layer_n" callback will occur \; \; -- Paint callback \, passes a graphics context object (commonly called g) for main layer or layer n (n > 1) that you can call these drawing functions on: \; \; -- Sets the size of the object \; -- Gets the size of the object \; \; -- Sets the color for the next drawing operation \; \; -- Draws a filled ellipse at the specified position and size \; -- Draws the outline of an ellipse at the specified position and size \; \; -- Draws a filled rectangle at the specified position and size \; -- Draws the outline of a rectangle at the specified position and size \; \; -- Draws a filled rounded rectangle at the specified position and size \; -- Draws the outline of a rounded rectangle at the specified position and size \; \; \; -- Draws a line between two points \; -- Draws text at the specified position and size \; \; -- Fills the entire drawing area with the current color. Also will draw an object outline in the style of the host (ie. pure-data or plugdata) \; -- Translates the coordinate system by the specified amounts \; -- Scales the coordinate system by the specified factors. This will always happen after the translation \; -- Resets current scale and translation \; \; -- Initiates a new path at the specified point \; -- Adds a line segment to the path \; -- Adds a quadratic Bezier curve to the path \; -- Adds a cubic Bezier curve to the path \; -- Closes the path \; \; -- Draws the outline of the path with the specified line width \; -- Fills the current path \; \; \; \; -- Expand dollar symbols in patch canvas context \; -- Set the object arguments to be saved in the patch file \; -- Get the object arguments \;, f 81; +#X text 814 1537  ; +#X text 8 816 -- Callback functions you can define \; pd:Class:mouse_down(x \, y) \; pd:Class:mouse_up(x \, y) \; pd:Class:mouse_move(x \, y) \; pd:Class:mouse_drag(x \, y) \; \; \; -- Functions you can call \; pd:Class:repaint(layer) \; \; \; pd:Class:paint(g) / pd:Class:paint_layer_n(g) \; \; \; g:set_size(w \, h) \; width \, height = g:get_size(w \, h) \; \; g:set_color(r \, g \, b \, a=1.0) \; \; g:fill_ellipse(x \, y \, w \, h) \; g:stroke_ellipse(x \, y \, w \, h \, line_width) \; \; g:fill_rect(x \, y \, w \, h) \; g:stroke_rect(x \, y \, w \, h \, line_width) \; \; g:fill_rounded_rect(x \, y \, w \, h \, corner_radius) \; g:stroke_rounded_rect(x \, y \, w \, h \, corner_radius \, line_width) \; \; g:draw_line(x1 \, y1 \, x2 \, y2) \; g:draw_text(text \, x \, y \, w \, fontsize) \; \; g:fill_all() \; \; g:translate(tx \, ty) \; g:scale(sx \, sy) \; \; g:reset_transform() \; \; p = Path(x \, y) \; p:line_to(x \, y) \; p:quad_to(x1 \, y1 \, x2 \, y2) \; p:cubic_to(x1 \, y1 \, x2 \, y2 \, x3 \, y) \; p:close_path() \; \; g:stroke_path(p \, line_width) \; g:fill_path(p) \; \; \; -- Audio callbacks \; pd:Class:dsp(samplerate \, blocksize \, channel_counts) \; pd:Class:perform(in1 \, in2 \, ... \, in_n) \; \; pd:Class:signal_setmultiout(outlet \, channel_count) \; \; \; -- Additional functions \; expandedsymbol = pd:Class:canvas_realizedollar(s) \; pd:Class:set_args(args) \; args = pd:Class:get_args() \;, f 53; +#X text 326 816 \; -- Mouse down callback \, called when the mouse is clicked \; -- Mouse up callback \, called when the mouse button is released \; -- Mouse move callback \, called when the mouse is moved while not being down \; -- Mouse drag callback \, called when the mouse is moved while also being down \; \; \; \; -- Request a repaint for specified layer (or all layers if no parameter is set or if layer is 0). after this \, the "paint" or "paint_layer_n" callback will occur \; \; -- Paint callback \, passes a graphics context object (commonly called g) for main layer or layer n (n > 1) that you can call these drawing functions on: \; \; -- Sets the size of the object \; -- Gets the size of the object \; \; -- Sets the color for the next drawing operation \; \; -- Draws a filled ellipse at the specified position and size \; -- Draws the outline of an ellipse at the specified position and size \; \; -- Draws a filled rectangle at the specified position and size \; -- Draws the outline of a rectangle at the specified position and size \; \; -- Draws a filled rounded rectangle at the specified position and size \; -- Draws the outline of a rounded rectangle at the specified position and size \; \; \; -- Draws a line between two points \; -- Draws text at the specified position and size \; \; -- Fills the entire drawing area with the current color. Also will draw an object outline in the style of the host (ie. pure-data or plugdata) \; -- Translates the coordinate system by the specified amounts \; -- Scales the coordinate system by the specified factors. This will always happen after the translation \; -- Resets current scale and translation \; \; -- Initiates a new path at the specified point \; -- Adds a line segment to the path \; -- Adds a quadratic Bezier curve to the path \; -- Adds a cubic Bezier curve to the path \; -- Closes the path \; \; -- Draws the outline of the path with the specified line width \; -- Fills the current path \; \; \; \; -- Called when DSP is activated and whenever the DSP graph is updated \; -- Called for every audio block \, passing tables of samples per inlet and expecting tables of samples per outlet \; -- Used in dsp function to achieve multichannel output for specific outlets \; \; \; \; -- Expand dollar symbols in patch canvas context \; -- Set the object arguments to be saved in the patch file \; -- Get the object arguments \;, f 82; #X restore 446 409 pd graphics; #X text 324 384 Details on how to create GUI objects ------->, f 18; #X obj 342 247 hello;