-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
[WIP] OSC support via liblo #2064
Conversation
@harryhaaren I'm still fairly new to threading and networking in C++, so thanks for bringing up the blocking issues. If you or anyone else could give me some guidance as to what would be involved in creating a dedicated OSC thread, that would be appreciated. Would that interfere with the other Mixxx controller threads? In that case, I'm wondering if it would be better to switch libraries to liblo and use it to create a non-blocking server: In response to your question about making the socket "block forever" - that's what happens if you set the timeout to -1. I tried this yesterday and everything seemed to work fine (but I didn't test with any hardware controllers plugged in - I'm guessing it might have blocked those) until I closed the application, at which point Mixxx just froze and stopped responding. |
I think mapping each group/key to a ControlProxy makes sense, but I'm wondering if there would be the same performance overhead if the map grew large from many different controls being addressed. Could you explain why ControlProxy is such a heavyweight class? |
Re threads; example is Ctlra controller, creates one thread for any amount of Ctlra controllers: https://github.com/openAVproductions/mixxx/blob/ctlra/src/controllers/ctlra/ctlraenumerator.cpp#L16 I expect the design to map best to using on thread per OSC socket, because that allows blocking the thread on the socket read - causing it to go to sleep until the kernel wakes it up because it has work to do. I see no advantage to switching to LibLO per-se, using -1 to achieve non-blocking is just a good as using a library that supports it using API calls. Re; threads and interfering with other Mixxx threads; Re: ControlProxy creation; Creating any class is usually expensive - in this case the ControlProxy is "linked" to a ControlObject, which has to be found. I believe the current implementation is using a HashTable, so the the "Group/Key" pair get hashed and then looked up (this isn't too expensive usually). What can be particularly expensive is calling |
Left a comment in the Zulip on caching ControlProxy* instances in a hash table after they're used once - this will make the commonly used controls more performant to access, and the not-used controls will not consume any memory: https://mixxx.zulipchat.com/#narrow/stream/109171-development/topic/OSC.20Support |
Did you considered to use liblo instead of oscpkt? The other draft implementations is using this, and IMHO we should use only one if there is no technical limitation. |
The required Hash table lookup is the heavy part. The object itself avoids to doing it over and over again because it just wraps a shared pointer to the control and adds the capability of receive the valueChange signal. I have created a PR introducing a light version of ControlProxy without the valueChange hook #1717 but this does not relrase you from the hash table lookup and the required string math. |
This PR: |
@daschuer I'm a little confused about the end of your message "we should use only one if there is no technical limitation." Are you talking about using only one thread? Or are you saying we should switch to liblo? |
Yes, #2062 uses liblo, so maybe it suits for all OSC topics. But I am unsure because I have never worked with it. At least it is under active development http://liblo.sourceforge.net/NEWS.html
That looks easy, hopfuly. liblo is setup in #2062 for Ubuntu. We need to add it to the Windows and Mac CI to be able to successful build it. I think your approach an #2062 can live side by side. Do we need here a preferences page as well? Maybe we can use one common page for client ant server.
Since the Hash Table scales well for many entries, I expect only a minor benefit if you set up your own hash table. With the single parameter solution, we have the chance to get entirely rid of the hash table and save the ControlProxy inside the callback. |
Should this relay become a controller? We have a model for each hardware controller in Mixxx to allow a mapping via GUI or Java. In the OSC receive case we offer a fixed API anyone can use. Is there a need for a OSC controllrt model? |
I just rewrote the majority of OscController.cpp to use liblo rather than oscpkt. There's no more polling now and liblo takes care of creating its own non-blocking thread. All controls are now addressable via their own path, so you can send messages like "/[Master]/crossfader 0.3" (path, double). If possible, types of value (e.g. int, float, bool, etc.) are automatically converted to double (so you can send "/[Channel1]/play true" and Deck 1 will start playing). If you want to query a parameter, you send "/[Master]/crossfader '?'" (path, string) - just like the Resolume docs @daschuer linked me to on Zulip. |
The switch to liblo is done. I'm confused by the idea of saving the ControlProxy inside the callback? To clarify, there is only one generic callback that is invoked for all possible OSC paths (which map to key/value pairs).
-------- Original Message --------
…On Mar 25, 2019, 7:01 PM, Daniel Schürmann wrote:
> Or are you saying we should switch to liblo?
Yes, [#2062](#2062) uses liblo, so maybe it suits for all OSC topics. But I am unsure because I have never worked with it. At least it is under active development http://liblo.sourceforge.net/NEWS.html
> The reason I mentioned liblo earlier is that I think it can handle threading for us ...
That looks easy, hopfuly.
liblo is setup in [#2062](#2062) for Ubuntu. We need to add it to the Windows and Mac CI to be able to successful build it.
I think your approach an [#2062](#2062) can live side by side. Do we need here a preferences page as well? Maybe we can use one common page for client ant server.
> Also, if the hash table lookup is the heavy part of using the ControlProxy class, then wouldn't the caching proposal by ***@***.***(https://github.com/harryhaaren) be just as slow?
Since the Hash Table scales well for many entries, I expect only a minor benefit if you set up your own hash table. With the single parameter solution, we have the chance to get entirely rid of the hash table and save the ControlProxy inside the callback.
—
You are receiving this because you authored the thread.
Reply to this email directly, [view it on GitHub](#2064 (comment)), or [mute the thread](https://github.com/notifications/unsubscribe-auth/AhnMpNHP6Z6kB6yjMuLksuvcUy7L-6PBks5vaWNtgaJpZM4cFmaZ).
|
I tried creating a controller because that's what @be suggested in Zulip and also because I was already familiar with Mixxx's controller system. I had the same concern, and if you think it would be better to integrate OSC separately from the controller system then that sounds good to me. I'll try to do something like the other OSC PR you mentioned earlier if it's more like what you want.
-------- Original Message --------
…On Mar 25, 2019, 7:19 PM, Daniel Schürmann wrote:
Should this relay become a controller? We have a model for each hardware controller in Mixxx to allow a mapping via GUI or Java.
In the OSC receive case we offer a fixed API anyone can use. Is there a need for a OSC controllrt model?
—
You are receiving this because you authored the thread.
Reply to this email directly, [view it on GitHub](#2064 (comment)), or [mute the thread](https://github.com/notifications/unsubscribe-auth/AhnMpHrpmo_m6D82k0oHK3FQcTI35b4yks5vaWeQgaJpZM4cFmaZ).
|
I think the controller model, does actually not fit well here. |
Makes sense. So then where in your opinion would be the best place to set up an OSC server? Would I just create an OscServer class and then initialize it at some point (maybe after regular controllers) in the mixxx.cpp file? Or should it be more abstracted than that?
-------- Original Message --------
…On Mar 26, 2019, 5:00 PM, Daniel Schürmann wrote:
I think the controller model, does actually not fit well here.
The OSC Server is represented as a controller here. Normally the clients are represented as a controller.
In this case it would be TouchOSC, but we cannot do it because Mixxx is not aware any OSC controller.
—
You are receiving this because you authored the thread.
Reply to this email directly, [view it on GitHub](#2064 (comment)), or [mute the thread](https://github.com/notifications/unsubscribe-auth/AhnMpMancnxuYKm8JqXwPDmVAOwhy_dPks5vaph_gaJpZM4cFmaZ).
|
I am sorry I have not yet found the time to test you branch. Here only some thoughts that might be wrong: If we look at TouchOSC it is a OSC Server and Client at the same time. A control has a unique single Unique address, that can be auto generated from the structure in TouchOSC or adopt the namespace of the controlled Application. In this case of a fully customizable Controller, it is reasonable to use the Mixxx Namespace for it. It is somehow like Here I have an idea from your implementation: How about using the temporary ControlProxy to register a OSC callback for all following OSC commands and register a value change callback at the control proxy to send OSC commands back. If we think of more complicated control task where for example one control on the controller will control two controls of Mixxx, the a OSC Controller would be handy. This can be implemented by copying the HID controller implementation where every hid command is parsed by a script. We may also consider to use a Xml mapping file that would allow to map the controller namespace to the Mixxx Namespace. |
IMHO Mixxx can make use of both interfaces one for easy interfacing other customizable applications and one for map other static applications, that requires a mapping just like midi controllers. I am fine with merging a solution that is somewhere on the way to it. |
Is it Ok to have the co namespace in the OSC address root? I am unsure. |
Both PR target different issue. This here is a OSC Server, the other one is a OSC Client. Can you have a look to the other PR and descide how a integrated solution will look like. If we have an idea about it we can tweak both towards it and merge them finally. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Question about how OSCEnumerator is structured. Comments inline. #2062 looks like it integrates OSC endpoints more deeply with Mixxx, rather than the "arm's length" approach the Controller subsystem offers. I've never used OSC so can't speak to which is more appropriate for the DJ use case, though my gut feeling is the deeper integration would be more flexible.
@@ -58,6 +58,13 @@ def configure(self, build, conf): | |||
def sources(self, build): | |||
return ['controllers/midi/portmidienumerator.cpp', 'controllers/midi/portmidicontroller.cpp'] | |||
|
|||
# TODO: how to make this optional? (see OSC in features.py) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Answer: Check to see how HSS1394 does it. I believe you would move this liblo stuff to features.py, so it's only acted on if the feature is turned on.
@@ -1529,7 +1536,7 @@ def depends(self, build): | |||
return [SoundTouch, ReplayGain, Ebur128Mit, PortAudio, PortMIDI, Qt, TestHeaders, | |||
FidLib, SndFile, FLAC, OggVorbis, OpenGL, TagLib, ProtoBuf, | |||
Chromaprint, RubberBand, SecurityFramework, CoreServices, IOKit, | |||
QtScriptByteArray, Reverb, FpClassify, PortAudioRingBuffer] | |||
QtScriptByteArray, Reverb, FpClassify, PortAudioRingBuffer, LibLO] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto.
@@ -401,6 +401,11 @@ void DlgControllerLearning::visit(BulkController* pBulkController) { | |||
Q_UNUSED(pBulkController); | |||
} | |||
|
|||
void DlgControllerLearning::visit(OscController* pOscController) { | |||
qWarning() << "ERROR: DlgControllerLearning does not OSC devices."; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"does not yet support OSC devices"
#include "controllers/osc/oscenumerator.h" | ||
|
||
OscEnumerator::OscEnumerator() { | ||
m_devices.push_back(new OscController()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems strange to me. I understand this to mean that when an OSCEnumerator is instantiated, it creates a single OSCController. The intent of the controller subsystem is to query available controllers in queryDevices() using whatever API is required to see what OSC endpoints are on the network and return that list there. That allows users to hit a 're-scan' button in the GUI which would call queryDevices() again to get an updated list. As well, there is supposed to be one OSCController instance per endpoint. Does OSC not fit this paradigm?
Thanks for explaining. Hopefully in the next two or three days I'll have some time to come up with an integrated solution.
-------- Original Message --------
…On Mar 27, 2019, 3:30 PM, Daniel Schürmann wrote:
Both PR target different issue. This here is a OSC Server, the other one is a OSC Client.
For TouchOSC for instane we need both.
Can you have a look to the other PR and descide how a integrated solution will look like. If we have an idea about it we can tweak both towards it and merge them finally.
—
You are receiving this because you authored the thread.
Reply to this email directly, [view it on GitHub](#2064 (comment)), or [mute the thread](https://github.com/notifications/unsubscribe-auth/AhnMpL38-mBa3G4kwmElz1DAkrKipJ2qks5va9T1gaJpZM4cFmaZ).
|
1. Ok, I'll examine HSS1394 again. I was actually talking about making liblo optional, not the sources, but you're right - I should have listed those sources in features.py not depends.py. Sorry for the misleading comment I left in that script.
2. Good question. The reason only 1 OscController exists is that this controller really represents a server (UDP socket) that Mixxx OSC cilents connect to. I believe even if you have many OSC clients connected to an application, they all talk to the same single server. I'm hopefully going to look into integrating my server code here with Jakob Braun's OSC client implementation (so Mixxx can act as both). When that's done, these files will probably disappear and the structure of this feature will change significantly.
-------- Original Message --------
…On Mar 27, 2019, 6:51 PM, Sean M. Pappalardo wrote:
@Pegasus-RPG requested changes on this pull request.
Question about how OSCEnumerator is structured. Comments inline.
---------------------------------------------------------------
In [build/depends.py](#2064 (comment)):
> @@ -58,6 +58,13 @@ def configure(self, build, conf):
def sources(self, build):
return ['controllers/midi/portmidienumerator.cpp', 'controllers/midi/portmidicontroller.cpp']
+# TODO: how to make this optional? (see OSC in features.py)
Answer: Check to see how HSS1394 does it. I believe you would move this liblo stuff to features.py, so it's only acted on if the feature is turned on.
---------------------------------------------------------------
In [build/depends.py](#2064 (comment)):
> @@ -1529,7 +1536,7 @@ def depends(self, build):
return [SoundTouch, ReplayGain, Ebur128Mit, PortAudio, PortMIDI, Qt, TestHeaders,
FidLib, SndFile, FLAC, OggVorbis, OpenGL, TagLib, ProtoBuf,
Chromaprint, RubberBand, SecurityFramework, CoreServices, IOKit,
- QtScriptByteArray, Reverb, FpClassify, PortAudioRingBuffer]
+ QtScriptByteArray, Reverb, FpClassify, PortAudioRingBuffer, LibLO]
Ditto.
---------------------------------------------------------------
In [src/controllers/dlgcontrollerlearning.cpp](#2064 (comment)):
> @@ -401,6 +401,11 @@ void DlgControllerLearning::visit(BulkController* pBulkController) {
Q_UNUSED(pBulkController);
}
+void DlgControllerLearning::visit(OscController* pOscController) {
+ qWarning() << "ERROR: DlgControllerLearning does not OSC devices.";
"does not yet support OSC devices"
---------------------------------------------------------------
In [src/controllers/osc/oscenumerator.cpp](#2064 (comment)):
> @@ -0,0 +1,16 @@
+#include "controllers/osc/oscenumerator.h"
+
+OscEnumerator::OscEnumerator() {
+ m_devices.push_back(new OscController());
This seems strange to me. I understand this to mean that when an OSCEnumerator is instantiated, it creates a single OSCController. The intent of the controller subsystem is to query available controllers in queryDevices() using whatever API is required to see what OSC endpoints are on the network and return that list there. That allows users to hit a 're-scan' button in the GUI which would call queryDevices() again to get an updated list. As well, there is supposed to be one OSCController instance per endpoint. Does OSC not fit this paradigm?
—
You are receiving this because you authored the thread.
Reply to this email directly, [view it on GitHub](#2064 (review)), or [mute the thread](https://github.com/notifications/unsubscribe-auth/AhnMpK0hEs0KMYGKJcspq8JWy8FHfUhDks5vbAPpgaJpZM4cFmaZ).
|
Do we agree to the following future implementation?
I think all this can be done one by one. |
@daschuer I definitely agree on impementation ideas 1 & 2 above. I think 3-5 would be wonderful features to have but are probably too involved for me to implement given the amount of time I have right now. Am I correct in assuming that the other OSC support PR adds feature 2? If so, would it be best for me just to fork your daschuer/mixxx repository and try to build feature 1 into its osc_support branch? |
Great, it is reasonable to to it step by step so go ahead. Please just take the rout that suits best to you. |
@daschuer I'm almost done with the new OSC server implementation that can exist alongside the existing OSC client we've been discussing. Since I built this into your fork of Mixxx that has the osc_support branch, would it be better for me to create a PR on your fork rather than this repository? |
If you are willing to take responds for all OSC branches, It is probably reasonable that you issue your own PR which includes @jakobbraun work. We can then close #2062 with a reference to your PR. |
But for which repo should I issue the PR? mixxxdj/mixxx or daschuer/mixxx or jakobbraun/mixxx?
-------- Original Message --------
…On Apr 7, 2019, 3:06 PM, Daniel Schürmann wrote:
If you are willing to take responds for all OSC branches, It is probably reasonable that you issue your own PR which includes ***@***.***(https://github.com/jakobbraun) work. We can then close [#2062](#2062) with a reference to your PR.
—
You are receiving this because you authored the thread.
Reply to this email directly, [view it on GitHub](#2064 (comment)), or [mute the thread](https://github.com/notifications/unsubscribe-auth/AhnMpAIf1njXCmlGLLu5xSbI7Rxjf4g-ks5vek_OgaJpZM4cFmaZ).
|
mixxxdj/mixxx |
This PR is marked as stale because it has been open 90 days with no activity. |
This pull request adds OSC support (provided by the oscpkt library) to Mixxx. This has been discussed under the "OSC Support" topic of the development stream on Mixxx's Zulip instance. Please feel free to provide any further feedback and ideas of changes that should be incorporated here.
Thanks!