-
Notifications
You must be signed in to change notification settings - Fork 14
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
multichannel support #53
Comments
I'm not sure what's the best way to do this. I still need to look into Pd's multi-channel implementation. |
Ok, since I just ported multi-channels to purr-data, my understanding improved a little, but not by much. 😉 But it's good enough that I have an informed opinion about the proposed feature now. So basically you add Can you please remind me again why we should go through all that hassle? 😕 Because to me this seems to be a mere convenience for something that could easily be achieved using a combination of Sorry if this comes across as just me being too lazy to implement that feature -- which is true, I'm a lazy bum indeed, but that's also because I work on so many different projects. 😛 But IMHO, if someone is going to invest a substantial amount of time implementing this feature, it should add some real value, and not complicate things too much. Thoughts? |
TL;DR (before you read the other comments below): i'm not completely convinced that we need this. i'm also a bit ambivalent there since Lua is certainly not the right place to deal with signals of really high channel counts. so the use cases i'm think of are only applications like scope3d~, where it would just be aesthetically more pleasing to have a single input connection for 3 channels. ah - flexible channel counts would be a thing, too, of course (that are not managed in
i'm certainly not well versed when it comes to Pd's engine - but isn't the magic in that case achieved by representing multichannel signals in the same vectors, but with size
the obvious way here would probably be to also have a flat table that includes
similarly to the Pd side, this would certainly have to be defined for the signal outlets, yes. for the inlets, it could be derived in the dsp method, right?
all good - and that's definitely a good protection against feature creep here. :) |
I agree.
Ah yes, you're right, it's even documented in the t_signal declaration in m_pd.h. This makes a lot more sense now. :)
Correct. I now think that this should be doable without too much effort.
Yes, that sounds like a plan to me. Ben, do you want to give it a go yourself? |
I really hope you people get to implement this. I'm sure it'll be very very powerful, but I'm not doing a good job here selling the thing out. Or let me try it... I am currently adding MC support to most of the signal externals in ELSE. I never thought about that until MC was a reality in Pd and now I can see the benefit and it'd be great if one could code externals in lua that explore this. Just build it and they will come... |
sounds good to me and i don't see alternatives here.
yep - that sounds like the right way to adopt Pd's mechanism. a table of tables representing a multichannel signal might be more convenient, but possibly also not ideal performance-wise and overcomplicate things?
i'd be happy to - but this might be a bit over my head still (or possibly forever, haha). i'll see how far i get and let you know! before doing so, i'd really love to hear @timothyschoen's opinion on the API, too! |
I don't think that it would have much impact on the performance. The main issue is that a table of tables is not compatible with the existing API, which breaks existing code. With a flat table the user callbacks don't even need to be aware of multi-channel input and still do something sensible with the sample data. Also, many existing perform callbacks would still work fine with multi-channel data without any changes. So I think that this is the preferable solution. If we document this properly and with a nice example, then having to work with the flat tables shouldn't be much harder than with single-channel data. It's just that you need to calculate the indices in a row-major fashion (something like i = (k-1)*blocksize+j where k is the 1-based channel index). Like we did in the old days when programming in Assembler or dialects of Basic and Fortran without multi-dimensional arrays. 😉
That's the right spirit! 👍 There's a lot of examples of little Lua-callable functions and how to call Lua code from C in the code already, so I hope that you'll find your way. If not then don't hesitate to ask. |
Also, https://www.lua.org/manual/5.4/ is your friend. If you scroll down a bit to the Index, all the Lua C API functions can be found in the two columns on the right. The reference documentation can be a bit terse and hard to understand if you never used that API before, but again, just ask if you need help -- I'm well familiar with the Lua C API by now. |
I'd say that you can just go ahead with this, I'm sure that Tim will agree that it's the most sensible approach. And we can always massage the code later, or Tim might do that himself if he has ideas on how to improve the API. If we don't have any code, there's nothing to massage and this will be stuck in the idea stage forever, I'm afraid. I'm paraphrasing Alexandre's remark. 😄 |
Also, I'm currently busy with #57 and will possibly tackle #59 afterwards, so I probably won't be able to tend to this ticket myself for quite some time. And I'm doing the finishing touches of the upcoming purr-data release, which is a really big one. The winter semester is also approaching, and I still have to prepare lots of stuff for that as well. Long story short, just embark on it or it may take an eternity. 😅 |
Yep, sounds good, I think a flat table is the best way to do it. @ben-wes Good luck, let me know if you need any help! I also think we'll need a new function to set the number of output channels? |
That's actually part of the proposal above:
|
And we might also want to skip all those shiny new features for older Pd versions (testing for PD_MINOR_VERSION >= 54). |
sooo ... i fiddled around a bit to achieve some kind of result. it's certainly still far from mergeable - but maybe the api can be finalized at this point. this is missing the version checks for multichannel capabilities and probably also many other improvements and i'm honestly not sure if i can finalize it. anyway - here's a little lua test outputting the 3 channels of 2 inlets in inverted order to its 3 outlets. let me know if this looks like the right approach to you: local testmc = pd.Class:new():register("testmc")
function testmc:initialize(sel, atoms)
self.inlets = {SIGNAL, SIGNAL}
self.outlets = {SIGNAL, SIGNAL, SIGNAL}
return true
end
function testmc:dsp(samplerate, blocksize, inchans)
self.blocksize = blocksize
pd.post(string.format("samplerate: %d, blocksize: %d, inchans: %s",
samplerate, blocksize, table.concat(inchans, " ")))
self:signal_setmultiout(1, 2)
self:signal_setmultiout(2, 2)
self:signal_setmultiout(3, 2)
end
function testmc:perform(in1, in2)
out1 = {}
out2 = {}
out3 = {}
for i = 1, self.blocksize do
out1[i] = in2[i + self.blocksize * 2]
out1[i + self.blocksize] = in2[i + self.blocksize]
out2[i] = in2[i]
out2[i + self.blocksize] = in1[i + self.blocksize * 2]
out3[i] = in1[i + self.blocksize]
out3[i + self.blocksize] = in1[i]
end
return out1, out2, out3
end the current draft: |
made a few more changes and additions now. should i create a draft PR from this? |
btw, i realized that we didn't add prototypes for the newly added functions in pdlua.c ... and then was wondering if we should do that and move them all to pdlua.h? |
... here's a patch and 2 lua objects (one multichannel and one single channel) for testing this: test-lua-mc.zip |
and one more question ... the version checks here are obviously only compile time checks. do we want the version on Deken to be compatible also with older Pd versions? in that case, we'd have to deal in a different way with i'm not yet sure how to properly deal with |
Not sure what functions you're talking about (sorry, short on time, didn't look at your changeset yet), but if they are functions to be called only from Lua then they should actually be static, and not go into the header.
Good point.
Yeah, I've used that to detect purr-data which obviously uses different gui calls (JS instead of Tcl). But that's a much simpler situation which can be accommodated quite easily. If your code uses internal structs in any way, then this gets a lot messier. It's not just the nchans field; the whole layout of the struct has been changed in an incompatible way IIRC. So I'm afraid that pdlua will just crash when used with an old version thinking it has the new data structure because it has been compiled against 0.54 or later. :( I just had to deal with a similar case in purr-data where moonlib/dinlet~ was still using the old vinlet struct leading to inconsistencies which then caused crashes (kind of the reverse situation, but I think that the other way round won't fare much better). I'd have to look at the code, though, to see whether this might be case here. As I said, short on time right now, but I'll have a look asap. |
Quickly glanced at the code and yes, I think that we're in trouble. :( Not sure what to do about this. Either we need to tell people to upgrade to 0.54+, or we need to make this optional and compile the Deken version without the feature to be on the safe side for now. Or have two separate versions of pdlua on Deken, one for pd-0.53 and earlier, and one for pd-0.54 and later. Which is probably going to be a maintenance and support nightmare. :( |
ah ... nevermind - that was a stupid suggestion. but i might add the prototypes for consistency in pdlua.c then.
i'll give it a try. i had copied christof's mechanism of vstplugin for pmpd when i added multichannel support there. maybe we can do the same here if that looks good to you? see for example: https://github.com/ch-nry/pd-pmpd/blob/03c3494bafbc3af1ab8fe145221ad0cdea202124/pmpd~.c#L520-L535 |
That might do the trick. At least it's worth to give it a go. But please make sure that you actually test with both new and old pd versions. :) If that doesn't pan out -- I already started formulating the approach below before I saw your reply, so this might be our "plan B": I think that it would be a good idea to add some For the Deken version, I think that we can build the new mc version by default, but you then need to add a check which makes sure that people don't try to run that version on old pd versions. That is, check the runtime pd version at startup, and tell people in the sign-on message that they need to grab an older pdlua version if they're still running pd 0.53 or older, and then just disable the signal Lua API. Just making people's old pd versions crash if they try to do signal stuff is not an acceptable way to inform the user that something isn't working. ;-) That would require, though, that older pdlua versions remain available on Deken if we upload a new version, and that people know where to find them. Do you know whether that's possible? Otherwise, we'd have to tell people to grab the older versions on GH. In purr-data this is much easier, because pd-lua is included so I can just use whatever is available (which would be to enable mc in purr-data 2.19.4 and later). |
will do! and thanks for your thoughts on this!
old versions stay available in vanilla through deken as well - just not too obvious maybe. can't check right now and might be dependent on a deken setting. |
ddb4435 should now allow building pdlua with pre-multichannel Pd versions and running both builds with different Pd versions. Tested:
EDIT: adding here ... i'm not really sure if i properly handled all cases and also not quite sure if that's the proper way now. it becomes a bit confusing (at least for me when looking at it after a long day) with the combinations of compile time checks and runtime checks. :) |
Thanks, that's good to know! Not sure why such an important option isn't readily visible in the main dialog, but hidden away in that convoluted Preferences menu.
Yes, these things make my head hurt, too. I'll have a look asap. Unfortunately, it might take a while until I get to this, because at present I'm fighting some bugs in my upcoming purr-data 2.19.4 release, and the bugs are getting weirder -- like the fiddle~ external crashing which seems to be due to a compiler or linker bug on Arch Linux. Apparently, the Arch maintainers thought that it would be a good idea to base (and bet) their distro on an unreleased git version of gcc 14. |
OTOH, if they don't give the very latest gcc a thorough shakeout then who will? Even Debian testing and unstable are still on gcc 14.1.0 AFAICT. |
thanks for the update! hope, you're making some progress there! - i'm afraid that i can't contribute any ideas or experience in that context. about the multichannel stuff: i added a few comments in #60 ... those are the points where i'm hesitant at them moment. but obviously, the whole thing will need a review unfortunately. |
@timothyschoen @agraef : i stumbled upon one little detail here where i'd appreciate your opinions - currently, pdlua's dsp function passes the blocksize based on this line: Line 1129 in 1d8ea81
... this gets the configured blocksize of Pd. i'm now wondering whether this is what we want in all cases like a reblocked or resampled subpatch/abstraction - which can be different from Pd's blocksize. we could possibly read this local size from |
Well, in the dsp method sys_getblksize is all we got, I guess. Or is there a canvas-local block size in the Pd API that we could tap into? |
as long as there is at least 1 iolet, EDIT: i pushed 5963279 for this now. feels right to me. |
That commit doesn't seem to be in the PR anymore. Can you please update the hash so that I can take a look at what you did there?
Ok, that seems reasonable. But is that value always guaranteed to be nonzero, or do we handle that condition? |
ah, sorry - sure. it's here:
AFAICT, it is guaranteed if there's a SIGNAL in/outlet. i also checked with only a DATA iolet and without iolets and in those cases, the dsp function isn't called - which makes sense, of course. i'm not 100% sure if this is always guaranteed, since i would need to prove it with the dsp logic in pd/pdlua, which i admittedly didn't do. |
Ok, looks good to me. BTW, did you test with a float input instead of signal? |
Never mind, just tested it myself using your test patch, works fine. |
and another discussion disguised as an issue. :)
it would be nice to have multichannel support for signals in pdlua. the (probably?) obvious way might be to have a table of tables in that case, where
#in
forperform(in)
would tell the number of channels. but this is not backward compatible and possibly also not very efficient? ... although there could also be a new type likeMC_SIGNAL
for the inlets/outlets definitions.another option might be to just have a flat table with all samples for all channels? the blocksize can be obtained through the
dsp()
function - so the number of channels could be derived in other ways (maybe also written somewhere fromdsp()
function?).for outlets, the number of channels would need to be defined some way though, i guess ... not sure what the syntax there should be and what
perform()
should return.The text was updated successfully, but these errors were encountered: