Help With Network API

This topic is primarily for Brad, or anyone else familiar with the Network API:

My main question is, what address does a WebSocket client connect to in order to establish a link with Cantabile’s Network Server? Context and background below…

I recently purchased a Stream Deck and see a lot of potential using it as a controller for Cantabile. My hope is to develop a custom application that acts as an interface between the Stream Deck and Cantabile, so the buttons can be customized based on the current song configuration. For example, button one could open the GUI for the first plugin in the first rack, and always show a specific graphic to represent whatever that plugin is in the current song.

I am proficient in C# and C++, not so much in JavaScript, and barely at all when it comes to Web technologies, including WebSockets. So, my thought was to create this app in C#. I have found a .NET library (StreamDeckSharp) that does a great job of controlling and responding to the Stream Deck. To connect to Cantabile, there is a .NET class called System.Net.WebSockets.ClientWebSocket that seems to be a logical solution, using the WebSocket API. However, I am having a hard time getting it to connect.

The ClientWebSocket class can only connect using a URI that starts with “ws://” or “wss://”. All of the examples I can find regarding the Network/WebSocket API mention using “http://localhost:35007”. I can connect successfully using that address in Chrome - I get the built-in web interface. But, how do I connect using a WebSocket? I tried “ws://localhost:35007”, but the connection fails. Specifically, with this code:
_socket = new ClientWebSocket();
await _socket.ConnectAsync(new Uri(“wss://localhost:35007”), CancellationToken.None);
Debug.WriteLine(“Connected”);

The 3rd line above is never reached - the connection is never established. Maybe I am going about this all wrong, but the code above is based on several examples I have found online for using the ClientWebSocket class.

I am open to suggestions on taking a completely different approach, but the WebSocket connection seems like the simplest, cleanest way to do this from within C#. I can easily process the JSON messages if only I can get them!

Any help greatly appreciated.

Tom B

Hi Tom,

The web socket api is at the end point /api/socket so the full url you want is something like:

ws://localhost:35007/api/socket

The Network API is documented here: https://www.cantabilesoftware.com/netapi/ (but neglects to mention the socket end point. Oops :slight_smile: ).

Also, for reference the JS client side implementation is available here.

Brad

Thank you, Brad. That did the trick.

I have also played around with the JS api, and am torn on which way to go. The JS api is definitely simpler in many ways, but I am so much more proficient in C# that I am inclined to go that route. However, part of me thinks I should use this as an opportunity to finally become the last programmer on Earth to learn JavaScript! :grinning:

Tom

I’m reviving this old thread because the subject heading seemed appropriate.

I’m trying to write some client-side JavaScript using the network API. I’m starting by coding some of the examples from Bindings4 - Cantabile JavaScript API

I’m able to get some Bindings4 ‘watcher’ examples to work, but not the ones for midiPorts, OnScreen Keyboard. In the following code,
“masterLevels”, “outputGain” works as expected;
“metronome”, “tempo” works as expected;
but none of the watchers using “midiPorts”, “in.Onscreen Keyboard”, ever log a message.
I’ve tried creating different controller bar buttons to send Midi CC, PC, etc., playing notes on the on screen keyboard.

Does anyone, @Brad, have any recommendations?

Thank you - David

<script src="/lib/cantabile-js.js"></script>

<script>
// Create an instance of the Cantabile object and connect it
let C = new Cantabile();

C.connect();
C.untilConnected();
console.log("Connected to Cantabile NetAPI");

C.bindings4.open();

// Watch a source binding point using a callback function
C.bindings4.watch("masterLevels", "outputGain", null, null, function(value) {
    console.log("Master output gain changed to:", value);
})

C.bindings4.watch("metronome", "tempo", null, null, function(value) {
    console.log("metronome tempo:", value);
})

C.bindings4.watch("midiPorts", "in.Onscreen Keyboard", null, null, function(value) {
    console.log("midiPorts in.Onscreen Keyboard:", value);
})

C.bindings4.watch("midiPorts", "in.Onscreen Keyboard", null, {
       channel: 0,
       kind: "ProgramChange",
       controller: -1,
   }, 
   function(value) {
       console.log("Program Change In: ", value);
   }
)

C.bindings4.watch("midiPorts", "in.Onscreen Keyboard", null, {
    channel: -1,
    kind: "Controller",
    controller: -1,
}, function(value) {
    console.log("Control Change In: ", value);
})

// The "bindings" end point must be opened before callbacks will happen
C.bindings4.open();

// Graceful cleanup when page is closed
window.addEventListener("beforeunload", () => {
   watcher.unwatch();
   C.disconnect();
});

</script>

Followup question:
What is the difference between these two examples?

let C = new Cantabile();
let C = new CantabileApi();

Hi David,

I’ve had a look at this. There’s two main issues with your sample:

  1. My fault, the doc sample for listening to midi ports is wrong. It should be “event”, not “kind”. I think that’s a copy paste error from the old Bindings api

  2. For CC’s the controller can’t be -1, you need to explicitly specify which controller you want to listen to.

So these both work:

C.bindings4.watch("midiPorts", "in.Onscreen Keyboard", null, {
       channel: 0,
       event: "ProgramChange",
       controller: -1,
   }, 
   function(value) {
       console.log("Program Change In: ", value);
   }
)

C.bindings4.watch("midiPorts", "in.Onscreen Keyboard", null, {
    channel: -1,
    event: "Controller",
    controller: 64,
}, function(value) {
    console.log("CC64: ", value);
})

As for new Cantabile() vs new CantabileApi, use the first. Again, the samples are wrong.

Finally, you’re using C.untilConnected(); This is intended for use with async JavaScript and should be used like so…

await C.untilConnected

Usually in a browser based setup, you wouldn’t use this and just use events/callbacks to monitor the connected state.

I hope this helps. I know the docs and examples for the jsapi aren’t great but I don’t want to spend too much time on it, because I plan to reengineer the whole JavaScript API at some point (hopefully soon). Just let me know if you have questions.

Brad

1 Like

Thank you Brad! It makes me feel better knowing about “kind”.
The -1 was just one of my trial and error tests in attempting to get it to work. I did have the CC set correctly originally.
So I guess I shouldn’t spend too much time developing for this version of the js api?
While waiting for an answer I used the OnscreenKeyboard as a work-around.

FYI: I found an error in that example as well (watchController should be watch)

// Watch a controller using a callback function
C.onscreenKeyboard.watchController(1, "controller", 64, function(value) {
    console.log(value);
})

I’d continue with the current API for now. I do plan to rework it but it shouldn’t be too big a leap to switch later if necessary.

1 Like