Monday 14 April 2014

Bitwig scripting - two weeks in

Since my last post, I thought I'd reach out and see if there were any scripting-focused Bitwig communities. I have kept an eye on the KVR forum but came across bitwigbeats.com and started talking with a few people there.

The site admin is gathering a team and looks to have plans for Push controller support, and there's plenty of people offering to test out any progress made. Being the flagship Live controller, a solid BWS mapping could bring some of the Live users over for a peek.

I also spoke with the dev from the Keith McMillen Instruments team who has started the sizable task of implementing scripts for their suite of hardware controllers. Interestingly, he was working with the Quneo initially too, and it sounds like he's made some good progress.

Possibly my downfall has been setting up an IRC channel (#bitwig-dev on freenode.net) where I've spent as much time chatting as talking code. A couple regulars have started to gather, and we've had a few visitors we've been able to get up and running too.

The time hasn't been a waste though, I've made progress on a control abstraction which will hopefully make it easier to adapt scripts for various hardware controllers. As with a few of the included scripts, I've also created a page system and am aiming to initially work on clip navigation and a mapping for the Drum Machine.

So at this stage, I'm probably going to focus on the scripting side of things. Expect an article over easter with (hopefully) my first useful script and some info on how it all works.

Until then, as always, pipe in with suggestions or questions.

Wednesday 2 April 2014

Ok, finally something technical!

Last post I promised an update on my JACK setup (and it is coming, maybe another double post after this one and a snack), but I couldn't help myself...

The Bitwig API adventure begins!

So I was going to hold off until I at least had the skeleton for my pads, but I thought I'd share something useful for all you developers.

TL;DR: Socket I/O from your control script , with a node.js app to display messages and log them to file.

 I started on the skeleton for my Quneo mapping and got sidetracked of course. First things first, I like to see what's going on so immediately looked for the the println() function . Some meandering later I came across the connectToRemoteHost() function and started to play.

This function takes a host, port and callback function as arguments. The callback function is called when it connects, and receives a RemoteConnection object to work with. Note the send(byte[] data) function requires a byte array, but we'll add some magic to handle string conversion.

I've been messing with node.js at work so thought it'd be a simple way to test the waters. Below is a short script that will listen locally on port 58008:


// logsrv.js

var net = require('net');
var fs = require('fs');
var tmpfilepath = '/tmp/logsrv.txt';


// Create our socket
var server = net.createServer(
  function (socket) {




    // Callback when data is received
    socket.on('data', function(data) {


      var messagetxt = "";




      // Convert bytes to string      for(var i = 0; i < data.length; i++) {
        messagetxt += String.fromCharCode(data[i]);
      }


      // Print message to console
      console.log(messagetxt);



      // Write message to file
      fs.writeFile(tmpfilepath, messagetxt, function(err) {
        if(err) { console.error("Write error: %s", err); }
      });
    });
  }

);


// Start server
server.listen(58008, '127.0.0.1');



Run the script with:

$ node logsrv.js

This will listen for incoming data, convert the bytestream to a string, print the message and log it to /tmp/logsrv.txt .


To set up the scratchpad control script, copy the template to your private Bitwig directory. On a default install, it will be something like:

$ cd ~/Bitwig\ Studio/Controller\ Scripts/
$ mkdir scratchpad
$ cp -R /opt/bitwig-studio/resources/controllers/template/template.js ./scratchpad/scratchpad.control.js


Make sure you use .control.js as the extension or it won't show up in the list in Bitwig.

Edit scratchpad.control.js and update the defineController() arguments to something relevant to your controller. Include a UUID generated here. You should have something like the following:

host.defineController("sherman", "scratchpad", "1.0", "1dece780-ba4d-11e3-a5e2-0800200c9a66");

We'll add our socket code in the onMidi() callback function, which will fire every time a note event is received:

function onMidi(status, data1, data2)
{

  // Create connection with callback definition
  host.connectToRemoteHost('127.0.0.1', 58008, function(conn) {
 

    var messagetxt = "midi event - " + status + " " + data1 + " " + data2;
    conn.send(messagetxt.getBytes());
    conn.disconnect();

  });

}


Wait, we need to send bytes right? Javascript doesn't actually define String.getBytes() , but we can update the String prototype by including the following:

String.prototype.getBytes = function () {
  var bytes = [];
  for (var i = 0; i < this.length; ++i) {
    bytes.push(this.charCodeAt(i));
  }
  return bytes;
};


This extends String with the new interface, which should cover our needs.

So altogether, you should have something that looks like this:

// scratchpad.control.js

loadAPI(1);

host.defineController("sherman", "scratchpad", "1.0", "1dece780-ba4d-11e3-a5e2-0800200c9a66");
host.defineMidiPorts(1, 1);

String.prototype.getBytes = function () {
  var bytes = [];
  for (var i = 0; i < this.length; ++i) {
    bytes.push(this.charCodeAt(i));
  }
  return bytes;
};

//
// Callbacks
//

function init()
{


  println("sandbox - init()");

  host.getMidiInPort(0).setMidiCallback(onMidi);
  host.getMidiInPort(0).setSysexCallback(onSysex);

  host.showPopupNotification("scratchpad loaded");
}


function exit()
{
}


function onMidi(status, data1, data2)
{

  // Create connection with callback definition
  host.connectToRemoteHost('127.0.0.1', 58008, function(conn) {
 

    var messagetxt = "midi event - " + status + " " + data1 + " " + data2;

    println(messagetxt);
    conn.send(messagetxt.getBytes());
    conn.disconnect();

  });

}


function onSysex(data)
{
}


Now load up BWS and Show the Control Script Console from the View menu. This will show any output from the host println() function.

Make sure the node app is waiting for a connection, then open the Controllers tab of the Preferences screen. When you click Add Controller Manually you should now see the new control script in the list.

Add the device and select the midi input device. This will fire off the init() callback and a message should appear in the console. If there is a problem loading the script, an error message will be printed to help you track down what's wrong. Click OK to return to the main screen and test the script is working by playing a note.

You should see another message in the console with the midi event info. If everything worked, the node app should also display the message in its terminal. Finally, check the file at /tmp/logsrv.txt and make sure they were logged as expected.


I'll check the example files into my github repo. Feel free to copy, extend and do whatever you want with the code. If you have any questions or feedback, comment here or at github.


Logging was a useful but basic example of what the RemoteConnection offers for extending and interacting with BWS. In a Linux environment, this gives us an interface for working directly with other processes on the system, and tools such as node.js are a great place to start. A web service interface could even allow for things like "tweet that I started a new set" or even "render track and upload to soundcloud", all from a midi event!

If anyone has ideas for something a bit meatier, I can look at a more in depth example in the future. Two of my first thoughts are an OSC server, and an interface to the JACK daemon.

Oh, and if you've been working on anything cool, let me know :D I'm always keen to see what other people come up with too.

Friday 28 March 2014

The road ahead...

I've been a Linux user for a while now. I still remember ordering a magazine from the US to get a copy of Redhat 7 after realizing it would arrive before the download would finish on my dialup connection... I ended up switching to Debian for a while, then Slackware, where I finally gave up XP and started using linux fulltime.

I stuck with Slackware for a few years, I think I finally switched away sometime during uni when I started using VMWare (which didn't like rc.d), but by this stage the distro didn't matter so much. Slackware was great for learning the linux subsystems and how things fit together.


Wait, this is an audio blog right? I guess it would help if I finally got around to explaining what we're here for.

Over the next few months I plan to move my windows production setup to BWS on Linux. I thought it would be worth documenting my ideas and experiences for anyone looking to do the same.


My next post will talk about PulseAudio, JACK, and how Linux handles my audio devices. For reference, JACK is started automatically and uses my audio interface for I/O in BWS, while Pulse manages my system audio and mic (so I could skype while still making tunes, for example).

Following that, I'll start to dig into the scripting API and start building scripts for my midi gear (VCM600, Quneo and APC40). In doing so, I aim to write some higher level components and useful bits that are generic enough for other controllers.


In the meantime if anyone has any questions or suggestions for future posts, leave me a comment and I'll see what I can do.

Before we start...

I first heard of Bitwig Studio back in January 2012 while going through my daily list of blogs. Being a long time Ableton user (everything started with Live 7 and a PadKontrol), I was interested to see what it offered in comparison. Not halfway down the page I saw something I didn't expect, Linux Support.


From that moment I followed the updates near religiously, scouring every source I could find. daily.

The next update had me sold, an open javascript API (with documentation!) for control scripts.

At this stage I was running a modified version of a custom remote script for my APC40 in Live. I never really got my head around the object model and interfaces though, so I kept mainly to soft banks and some basic MIDI control. When Max4Live was announced I thought this might be what I wanted, but I never clicked with the data flow programming system.


So the two features I was most exited for weren't exactly music related...

I'm guessing I'm not the only one though. These are two game-changers for nerds like me. To be fair, BWS isn't the first or only Linux DAW with a serious API, but its familiarity to an existing Live user and the promising feature list definitely had my attention.


After an anxious wait (and some cursing about my timezone) the links finally appeared. I grabbed packages and installed first on Windows to make sure my hardware was all picked up ok, then on the clean Ubuntu 12.04 install I had waiting, with no issues on either.

In the short time I've spent with BWS so far, my initial impressions have been extremely positive. Though the forums over at KVR are a buzz with the usual release teething issues, the team seems responsive and genuine in trying to help people get up and running (shout out to Dom who I noticed has been busy since release). Especially considering this is a 1.0 release I think they should be hugely proud of what they've put out.