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

Spawn an own thread #103

Open
Jikstra opened this issue Dec 22, 2021 · 5 comments
Open

Spawn an own thread #103

Jikstra opened this issue Dec 22, 2021 · 5 comments

Comments

@Jikstra
Copy link

Jikstra commented Dec 22, 2021

Is there a way to spawn an own thread? I need to retrieve data from a serial device and somehow send midi messages into the run method. I tried spawning a thread in Plugin::new but it seems like this is forbidden? Using lv2_worker also seems more of a hack for my use case, as i don't want to pass data from run to a worker, but have a worker running all the time.

@prokopyl
Copy link
Member

As far as I'm aware, LV2 isn't designed at all to communicate with hardware devices, but only for DSP, so I imagine anything you'll end up doing to communicate with a device will be a hack of some sort anyway. I can't say I've ever seen an LV2 plugin communicating directly with hardware either (that's usually the host's job), but that's just from my limited personal experience.

Spawning threads in plugins is not strictly forbidden, but is indeed discouraged, as any thread you create will conflict with the host's own threadpool (that the plugin itself is running on), which will affect performance. Also, LV2 plugins are designed to run in realtime contexts to avoid Xruns, and any atomic operations or thread synchronization will also further affect performance.

Producing events from an external source also breaks the idempotence requirement of LV2 plugins, which will most definitely break offline rendering mode, and would also probably result in other unwanted behavior if used in a traditional DAW.

In general this seems like a very weird requirement to me (in an XY-problem way) since this is at odds with a lot of LV2 concepts, so what is it exactly that you want to do?

Also, is this something that you (or somebody else) have managed to do using the LV2 C API, and can't reproduce using this crate? Or is this a question about feasibility in LV2 in general?

If it's the latter I also think you should ask those questions on the LV2 mailing-list or IRC channels as well, you'll probably find better answers over there. 🙂

@Jikstra
Copy link
Author

Jikstra commented Dec 25, 2021

As far as I'm aware, LV2 isn't designed at all to communicate with hardware devices, but only for DSP, so I imagine anything you'll end up doing to communicate with a device will be a hack of some sort anyway. I can't say I've ever seen an LV2 plugin communicating directly with hardware either (that's usually the host's job), but that's just from my limited personal experience.

It will definetly be a hack, that whole idea is a hack. Basically I wrote some code to connect to a serial device and read normal midi bytes from it. I'm doing this because a lot of arduinos can not easily change their usb type, so they cannot appear as a usb midi device but show up as a serial device. I wrote a tool that is simply creating a virtual midi device and proxies all data forth and back. But I always need to start it automatically, it's a command line tool and I think it would be a lot nicer if it would be part of my carla session. This whole setup is meant for a live setup, so DAW rendering is not an requirement.

I'm just diving into LV2, and probably lv2 is not the right fit. But what appeals to me, is that it has a lot of the features of jack (so maps quite well into it) but it's a lot easier to do session managment with it, saving state... That's why I'm trying to do it on top of lv2.

Spawning threads in plugins is not strictly forbidden, but is indeed discouraged, as any thread you create will conflict with the host's own threadpool (that the plugin itself is running on), which will affect performance. Also, LV2 plugins are designed to run in realtime contexts to avoid Xruns, and any atomic operations or thread synchronization will also further affect performance.

I think what I want to do is not too much different from an ui thread or an ui worker. Not really sure how uis are handled in lv2. The ui has knobs, they can be turned and this somehow communicated with the realtime callback, of course in a non blocking way. Is there currently a way how one can spawn a ui with rust-lv2? I saw a pr that got merged, but couldn't find any of the added methods (I think it was lv2::ui) in the current docs. Maybe I didn't look at the correct places.

Also, is this something that you (or somebody else) have managed to do using the LV2 C API, and can't reproduce using this crate? Or is this a question about feasibility in LV2 in general?

If it's the latter I also think you should ask those questions on the LV2 mailing-list or IRC channels as well, you'll probably find better answers over there. slightly_smiling_face

Exploring possibilities and limitations. Thank you for your thoughts about this already :)

@prokopyl
Copy link
Member

It will definetly be a hack, that whole idea is a hack. Basically I wrote some code to connect to a serial device and read normal midi bytes from it. I'm doing this because a lot of arduinos can not easily change their usb type, so they cannot appear as a usb midi device but show up as a serial device. I wrote a tool that is simply creating a virtual midi device and proxies all data forth and back. But I always need to start it automatically, it's a command line tool and I think it would be a lot nicer if it would be part of my carla session. This whole setup is meant for a live setup, so DAW rendering is not an requirement.

Aah, I see what you mean now!

I am no multi-threaded programming expert, but if you don't mind a bit of hacking here and there, I think the easiest way to do this would be to spawn a new "listener" thread in Plugin::new, and create a lock-free channel or queue, with the sending half sent to that thread and the receiving half stored in your plugin instance.

Your listener thread can live freely listening for hardware events, and push them into the channel/queue whenever it receives some.

In the meantime, the host will regularly call the Plugin::run method whenever it wants to do some processing, and in there you can receive everything that's in the channel/queue and push these out into an output Atom+MIDI sequence port.

I think that'd be easiest, however be wary that this will probably introduce some undefined (and maybe variable) amount of latency, since the host is in charge of scheduling calls to run and isn't aware of your external device.

I'm just diving into LV2, and probably lv2 is not the right fit. But what appeals to me, is that it has a lot of the features of jack (so maps quite well into it) but it's a lot easier to do session managment with it, saving state... That's why I'm trying to do it on top of lv2.

I see! I'll have to warn you though, that the rust-lv2 libraries are still unstable WIPs, so a lot of LV2 features don't have safe wrappers yet, and that includes session and state management. (And not much of what is there is properly documented yet). You might get stuck on some things if you only want to deal with safe Rust wrappers, although you technically can integrate custom wrappers in rust-lv2 for APIs that don't exist yet (if you don't mind hacking around and dealing with some C FFI that is).

I think what I want to do is not too much different from an ui thread or an ui worker. Not really sure how uis are handled in lv2. The ui has knobs, they can be turned and this somehow communicated with the realtime callback, of course in a non blocking way. Is there currently a way how one can spawn a ui with rust-lv2? I saw a pr that got merged, but couldn't find any of the added methods (I think it was lv2::ui) in the current docs. Maybe I didn't look at the correct places.

You haven't missed much, LV2 UI is still a Work In Progress, and got pushed back quite a bit due to some work that was needed in underlying APIs (Atom and Option) to make the whole thing safe yet still usable. LV2 is quite a complex beast. 🙂

@Jikstra
Copy link
Author

Jikstra commented Dec 26, 2021

I think that'd be easiest, however be wary that this will probably introduce some undefined (and maybe variable) amount of latency, since the host is in charge of scheduling calls to run and isn't aware of your external device.

Hmm but shouldn't that be fine if i take care of the run method not taking too long and especially not blocking? I didn't exactly get your hint about the threadpool, and that an lv2 plugin spawning a thread can cause problems in a host. Do you mean this?

I actually tried this already, but I couldn't share anything with the plugin because of Send or something not being in the Plugin trait. But maybe I oversaw something. I will try it out and report back :)

I see! I'll have to warn you though, that the rust-lv2 libraries are still unstable WIPs, so a lot of LV2 features don't have safe wrappers yet, and that includes session and state management. (And not much of what is there is properly documented yet). You might get stuck on some things if you only want to deal with safe Rust wrappers, although you technically can integrate custom wrappers in rust-lv2 for APIs that don't exist yet (if you don't mind hacking around and dealing with some C FFI that is).

If I get this far I'm more then happy to contribute a PR helping with those things :)

@Be-ing
Copy link

Be-ing commented Dec 27, 2021

connect to a serial device and read normal midi bytes from it. I'm doing this because a lot of arduinos can not easily change their usb type, so they cannot appear as a usb midi device but show up as a serial device. I wrote a tool that is simply creating a virtual midi device and proxies all data forth and back. But I always need to start it automatically, it's a command line tool and I think it would be a lot nicer if it would be part of my carla session.

Have you tried writing a udev rule to start this program automatically when the device is plugged in?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants