Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/multichannel #60

Merged
merged 18 commits into from
Oct 9, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions pd.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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)
pd._signal_setmultiout(self._object, n, channelcount)
end

function pd.Class:repaint(layer)
-- do some argument checking first
if type(layer) == "number" then
Expand Down
201 changes: 167 additions & 34 deletions pdlua.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -160,6 +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
# endif
#endif

#ifdef UNUSED
#elif defined(__GNUC__)
# define UNUSED(x) UNUSED_ ## x __attribute__((unused))
Expand Down Expand Up @@ -1023,7 +1034,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
Expand All @@ -1035,9 +1046,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"*/
Expand All @@ -1047,7 +1058,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?
ben-wes marked this conversation as resolved.
Show resolved Hide resolved

PDLUA_DEBUG("pdlua_perform: stack top %d", lua_gettop(__L()));
lua_getglobal(__L(), "pd");
Expand All @@ -1057,22 +1070,31 @@ 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_signal *sig = (t_signal *)(w[2 + i]);
t_float *in = sig->s_vec;
#if PD_HAVE_MULTICHANNEL
int nchans = sig->s_nchans;
ben-wes marked this conversation as resolved.
Show resolved Hide resolved
#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);
for (int j = 0; j < s_n_allchans; 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 + 2 + o->siginlets + o->sigoutlets;
}

if (!lua_istable(__L(), -1))
{
const char *s = "lua: perform: function should return";
Expand All @@ -1088,13 +1110,27 @@ static t_int *pdlua_perform(t_int *w){
}
}
lua_pop(__L(), 1 + o->sigoutlets);
return w + o->siginlets + o->sigoutlets + 3;
return w + 2 + o->siginlets + o->sigoutlets;
}

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;
}
agraef marked this conversation as resolved.
Show resolved Hide resolved
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;
#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);
for (int j = 0; j < s_n_allchans; j++)
{
lua_pushinteger(__L(), (lua_Integer)(j + 1));
lua_gettable(__L(), -2);
Expand All @@ -1108,43 +1144,65 @@ 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 + 2 + o->siginlets + o->sigoutlets;
}

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 this for signal_setmultiout)
ben-wes marked this conversation as resolved.
Show resolved Hide resolved

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");
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++) {
lua_pushinteger(__L(), i + 1);
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
#endif
else
lua_pushinteger(__L(), 1); // Pd doesn't support multichannel
lua_settable(__L(), -3);
}

if (lua_pcall(__L(), 3, 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()));
int sigvecsize = sum + 2;
t_int* sigvec = getbytes(sigvecsize * sizeof(t_int));

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;

for (int i = 0; i < sum; i++)
sigvec[i + 2] = (t_int)sp[i]->s_vec;
sigvec[1 + i] = (t_int)sp[i];

dsp_addv(pdlua_perform, sigvecsize, sigvec);
freebytes(sigvec, sigvecsize * sizeof(t_int));
}
Expand Down Expand Up @@ -1299,17 +1357,30 @@ 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), CLASS_NOINLET, A_GIMME, 0);
(t_method) pdlua_free, sizeof(t_pdlua),
#if PD_HAVE_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, A_GIMME, 0);
(t_method) pdlua_free, sizeof(t_pdlua),
#if PD_HAVE_MULTICHANNEL
CLASS_NOINLET | CLASS_MULTICHANNEL,
#else
CLASS_NOINLET,
#endif
A_GIMME, 0);
class_sethelpsymbol(c_gfx, gensym((char *) name));
}

Expand Down Expand Up @@ -1387,7 +1458,8 @@ 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;

Expand Down Expand Up @@ -2588,6 +2660,47 @@ static int pdlua_canvas_realizedollar(lua_State *L)
return 0;
}

static int pdlua_signal_setmultiout(lua_State *L)
{
char msg[MAXPDSTRING];

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;
}

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));
ben-wes marked this conversation as resolved.
Show resolved Hide resolved
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
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
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. */
static void pdlua_init(lua_State *L)
/**< Lua interpreter state. */
Expand Down Expand Up @@ -2689,6 +2802,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);
Expand Down Expand Up @@ -2932,6 +3048,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();
Expand Down Expand Up @@ -3038,7 +3171,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

}
Expand Down
1 change: 1 addition & 0 deletions pdlua.h
Original file line number Diff line number Diff line change
Expand Up @@ -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();