JupiterBroadcasting / CasterSoundboard

A soundboard for hot-keying and playing back sounds. (For podcasting)
GNU Lesser General Public License v3.0
146 stars 17 forks source link

Open Sound Control support #22

Closed guyfawcus closed 7 years ago

guyfawcus commented 7 years ago

It'd be great to be able to trigger cues using OSC commands.

For the uninitiated, Open Sound Control (OSC) is sort of next-gen MIDI that runs over a network:

Similar software also utilises OSC:

guyfawcus commented 7 years ago

This could also potentially be used for client/server communication which was brought up in issue #4 specifically @covarianttensor's comment

rikai commented 7 years ago

Mmm, can you explain what you're envisioning when you ask for support for this? I'm having difficulty understanding what it might be useful for in the context of this application as we work with arbitrary audio clips rather than midi/notations, which is what this mostly seems focused on.

covarianttensor commented 7 years ago

I'll keep it in mind. There's gonna be a big update to the sound board in about a day. I'll focus on new features after that.

guyfawcus commented 7 years ago

Sorry, didn't give much context with that, was a bit of a brain dump before I forgot!

OSC is way past MIDI in it's capabilities, it can even be used in QLC+ (Ahem, Noah 😜) to trigger lighting scenes.

To be honest, this is mainly because I'm currently working on a Python3 project (which I plan on uploading to GitHub within a week) that can take either user-input on the keyboard or a serial string from an Arduino or something (for physical buttons) then generates OSC messages which are read by the cueing software I use at work (QLab - I'm a sound engineer) to trigger sound-effects. This could then be used in conjunction with CasterSoundboard, press a button that's hooked up to a micro and one of the cues would play.

It could be as simple as '/CasterSoundboard/<tab-name>/<key-sound-is-triggered-with>/<command>' so '/CasterSoundboard/unfilter/h/play' would trigger the Obama "Hey!" clip.

covarianttensor commented 7 years ago

Okay so I've been looking into this some more and THIS IS A MUST HAVE feature. I will add an OSC server to CasterSoundboard after I add audio ducking. Can you refer me to software/scripts I can use to generate OSC messages, so I can test against CasterSoundboard?

Update: I think I already found I library I can use: https://github.com/MugenSAS/osc-cpp-qt

covarianttensor commented 7 years ago

@maelstrom59 I'm already working on adding OSC support as I type this. The soundbaord can now receive messages or bundles.

guyfawcus commented 7 years ago

Good stuff! πŸ˜€

In regards to scripts for testing, there are a few options but I usually find it easier to just do it from scratch in Python 3 using python-osc.

Install it using:

pip3 install python-osc

In-keeping with my example above:

#!/usr/bin/env python3
from pythonosc import udp_client

client = udp_client.SimpleUDPClient('127.0.0.1', 54321)
client.send_message('/CasterSoundboard/unfilter/h', 'play')

would send the play command to the h key.

To emulate the server side (only for completeness of this example, you can obviously skip this step if you've already got one going in CasterSoundboard!):

#!/usr/bin/env python3
from pythonosc import dispatcher
from pythonosc import osc_server

def do_thing(addr, val):
    print('Address:', addr)
    print('Value:', val)

    if addr == '/CasterSoundboard/unfilter/h' and val == 'play':
        print('Hey!')

dispatcher = dispatcher.Dispatcher()
dispatcher.map('/CasterSoundboard/unfilter/h', do_thing)

server = osc_server.ThreadingOSCUDPServer(('127.0.0.1', 54321), dispatcher)
server.serve_forever()

would print:

Address: /CasterSoundboard/unfilter/h
Command: play
Hey!

when it received a message from the first script.

guyfawcus commented 7 years ago

Just watched the latest JB video - Studio Tour Quickie // VLOG 45 and noticed the Linux powered sound desk Chris has been talking about, it's the Behringer X32.

I'm already somewhat familiar with it as I've come across it before at work and knew about X32 Edit (the controller software) because of it's Linux compatibility but I did a bit more research and guess what protocol the software uses to control the desk... 😜

Also offered is a separate remote editor running on host computers that will allow for complete editing control of the X32 via Ethernet. Check out behringer. com for more information.

Tip: The X32 remote communication is OSC-based (open sound control) and we will share the protocol on our website, allowing developers to design their own control software. Stay tuned to behringer.com for details on the OSC protocol.

Yup, that's right, you could (implementation dependant) potentially control the desk with CasterSoundboard and vice versa! Something like, when a clip is playing, mute the Mumble room channel, or, configure a button on the desk to trigger a clip.

At the very least, you could grab the OSC bindings for your favourite programming language and control the soundboard and the desk using similar commands. Wink wink JBot @rikai @ChrisLAS @dominickm

There isn't much in the manual about the commands but the MUSIC Group wiki (which could do with some work...) has a load of information about it: http://behringerwiki.music-group.com/index.php?title=OSC_Remote_Protocol

covarianttensor commented 7 years ago

I've made CasterSoundBoard print all osc direct message commands sent to it, we can find out what messages are sent that way. Right now, I have one-way OSC control working, currently working on two-way control to make things like TouchOSC work with CasterSoundboard.

I'm not done, but would you be willing to test what I have so far @maelstrom59 ? I'll commit what I have to the development branch.

OSC Message Format (ONE-WAY): /castersoundboard/board/<board-name>/player/<player-name>/modify/<interface-name> <value>

Examples: ADDRESS: /castersoundboard/board/No Name/player/,/modify/track_position VALUE (float): 0.5 ADDRESS: /castersoundboard/board/No Name/player/W/modify/volume VALUE (float): 0.9 ADDRESS: /castersoundboard/board/No Name/player/G/modify/loop_state VALUE (int): 0 ADDRESS: /castersoundboard/board/No Name/player/G/modify/loop_state VALUE(int): 1 ADDRESS: /castersoundboard/board/No Name/player/D/modify/play_state/play VALUE (int): 1 ADDRESS: /castersoundboard/board/No Name/player/D/modify/play_state/pause VALUE (int): 1 ADDRESS: /castersoundboard/board/No Name/player/D/modify/play_state/stop VALUE (int): 1

Code Example:

#!/usr/bin/env python3
from pythonosc import udp_client

client = udp_client.SimpleUDPClient('127.0.0.1', 5051)
client.send_message('/castersoundboard/board/No Name/player/,/modify/track_position', 0.5)

I will make the port changeable, but for now it's listening on port 5051 for OSC packets. Please don't judge what I have so far, as I am not done. I am just letting you preview it so I can get feedback from you. I will most likely be done with the full implementation in about two to three days from now.

Please build from the development branch and test @maelstrom59

Please be aware OSC commands print to the bottom status bar of Caster Soundboard and stay there for only half a second.

I will research the Behringer X32 and see how I can have it interface with CasterSoundboard.

guyfawcus commented 7 years ago

This is awesome! Don't worry about me judging, I'm still pretty new to lower-level languages so you could probably hide a lot without me noticing πŸ˜›

I'd love to test it out but unfortunately it's failing to compile ColorConversion.cpp for some reason:

Click to expand error test ``` g++ -c -pipe -O2 -Wall -W -D_REENTRANT -fPIC -DQT_NO_DEBUG -DQT_MULTIMEDIA_LIB -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -I. -isystem /usr/include/qt5 -isystem /usr/include/qt5/QtMultimedia -isystem /usr/include/qt5/QtWidgets -isystem /usr/include/qt5/QtGui -isystem /usr/include/qt5/QtNetwork -isystem /usr/include/qt5/QtCore -I. -I/usr/lib64/qt5/mkspecs/linux-g++ -o ColorConversion.o ColorConversion.cpp In file included from ColorConversion.cpp:1:0: ColorConversion.h: In function β€˜HSL RGBtoHSL(const RGB&)’: ColorConversion.h:24:9: warning: variable β€˜hsl’ set but not used [-Wunused-but-set-variable] HSL hsl; ^~~ ColorConversion.h:90:1: warning: no return statement in function returning non-void [-Wreturn-type] } ^ ColorConversion.h: In function β€˜RGB HSLtoRGB(const HSL&)’: ColorConversion.h:96:9: warning: variable β€˜rgb’ set but not used [-Wunused-but-set-variable] RGB rgb; ^~~ ColorConversion.h:102:10: warning: unused variable β€˜h’ [-Wunused-variable] uint h = hsl.h; ^ ColorConversion.h:167:1: warning: no return statement in function returning non-void [-Wreturn-type] } ^ ColorConversion.cpp: In function β€˜int* getRGB_fromHLS(int, int, int)’: ColorConversion.cpp:12:37: error: call of overloaded β€˜abs(double)’ is ambiguous double C = (1.0 - abs(2.0*L_-1.0)) * S_; ^ In file included from /usr/include/c++/6.3.1/cstdlib:75:0, from /usr/include/c++/6.3.1/stdlib.h:36, from ColorConversion.h:3, from ColorConversion.cpp:1: /usr/include/stdlib.h:735:12: note: candidate: int abs(int) extern int abs (int __x) __THROW __attribute__ ((__const__)) __wur; ^~~ In file included from /usr/include/c++/6.3.1/stdlib.h:36:0, from ColorConversion.h:3, from ColorConversion.cpp:1: /usr/include/c++/6.3.1/cstdlib:185:3: note: candidate: __int128 std::abs(__int128) abs(__GLIBCXX_TYPE_INT_N_0 __x) { return __x >= 0 ? __x : -__x; } ^~~ /usr/include/c++/6.3.1/cstdlib:180:3: note: candidate: long long int std::abs(long long int) abs(long long __x) { return __builtin_llabs (__x); } ^~~ /usr/include/c++/6.3.1/cstdlib:172:3: note: candidate: long int std::abs(long int) abs(long __i) { return __builtin_labs(__i); } ^~~ ColorConversion.cpp:13:53: error: call of overloaded β€˜abs(double)’ is ambiguous double X = C * (1.0 - abs(fmodf(H/60.0, 2.0)-1.0)); ^ In file included from /usr/include/c++/6.3.1/cstdlib:75:0, from /usr/include/c++/6.3.1/stdlib.h:36, from ColorConversion.h:3, from ColorConversion.cpp:1: /usr/include/stdlib.h:735:12: note: candidate: int abs(int) extern int abs (int __x) __THROW __attribute__ ((__const__)) __wur; ^~~ In file included from /usr/include/c++/6.3.1/stdlib.h:36:0, from ColorConversion.h:3, from ColorConversion.cpp:1: /usr/include/c++/6.3.1/cstdlib:185:3: note: candidate: __int128 std::abs(__int128) abs(__GLIBCXX_TYPE_INT_N_0 __x) { return __x >= 0 ? __x : -__x; } ^~~ /usr/include/c++/6.3.1/cstdlib:180:3: note: candidate: long long int std::abs(long long int) abs(long long __x) { return __builtin_llabs (__x); } ^~~ /usr/include/c++/6.3.1/cstdlib:172:3: note: candidate: long int std::abs(long int) abs(long __i) { return __builtin_labs(__i); } ^~~ Makefile:1260: recipe for target 'ColorConversion.o' failed make: *** [ColorConversion.o] Error 1 ```

Not sure why, considering it hasn't changed from the master branch. I've tried fixing it, got as far as this wiki page and started going down a rabbit hole. I'll keep trying to figure it out but would you mind having a look if you get the chance, I cant wait to try this!

guyfawcus commented 7 years ago

Got it working! Albeit via an extremely hacky method - I just deleted the lines in the generated Makefile that referenced ColorConversion.cpp and ColorConversion.o under SOURCES and OBJECTS.

Definitely something that still needs investigating by someone with more knowledge in C++ than I, but in the meantime, I've got some stuff to play around with!

covarianttensor commented 7 years ago

Hmm, weird, I've made no changes to that ColorConversion file.

rikai commented 7 years ago

I suspect this is being caused by differences in GCC versions handling things differently.

guyfawcus commented 7 years ago

With a file loaded into cue 1, I tried all of your examples:

client.send_message('/castersoundboard/board/No Name/player/1/modify/track_position', 0.5)

client.send_message('/castersoundboard/board/No Name/player/1/modify/volume', 0.8)

client.send_message('/castersoundboard/board/No Name/player/1/modify/loop_state', 1)
client.send_message('/castersoundboard/board/No Name/player/1/modify/loop_state', 0)

client.send_message('/castersoundboard/board/No Name/player/1/modify/play_state/play', 1)
client.send_message('/castersoundboard/board/No Name/player/1/modify/play_state/pause', 1)
client.send_message('/castersoundboard/board/No Name/player/1/modify/play_state/stop', 1)

it's a great start for sure, I've already integrated it into the application I mentioned earlier (still planning to release it in a few days).

Just a couple of small issues though:

EDIT: Formatting and resume idea

covarianttensor commented 7 years ago

track_position will be renamed to track_position_percent and I will add resume option to api.

covarianttensor commented 7 years ago

Full open sound control implemented. Will document api in wiki soon. Can control with touchOSC. Sample layout added to repo.

guyfawcus commented 7 years ago

Amazing! Thanks again for all your work on this πŸ‘ I'll get cracking on the wiki as soon as I can as well.