Phase 3: Transport on an iPhone
Pulling all these lessons together, it's time to make an interface!
The DAW I've been using partly for cost reasons, but mostly because I don't like DAW's enough to spend $$$$$ on a new one is Cakewalk Sonar X1.
In Sonar, you have the ability to set up custom mappings for midi interfaces, using a series of notes and controller messages to control core DAW functions.
I started with the basic idea that it'd be SUPER HANDY to have transport controls (play, stop, record, fast forward, rewind) for recording. I've started a recording project for my friend Andy Quitmeyer's PhD project where I had to lay down drums without a recording assistant. Having a little iPhone transport control was sooo helpful, eliminating the RECORD - RUN BEHIND THE DRUMS - PUT ON HEADPHONES - PLAY DRUMS - DROP A STICK - STAND UP - STOP THE RECORDING - UNDO THE RECORDING - AND REPEAT cycle of endless frustration.
For this design, I'm using Entypo web fonts and some simple little buttons. Here's a screen grab of the transport from my iPhone 4s:
Simple enough!
I mapped out the notes in Sonar, and keep a reference of the the transport controls and their note mappings in my Node code:
var map = { methods: { noteOff: 8, noteOn: 9, keyPressure: "A", controller: "B", programChange: "C", channelPressure: "D", pitch: "E" }, transport: { "tostart": { pitch: 63, velocity: 127, channel: 1 }, "toend": { pitch: 64, velocity: 127, channel: 1 }, "rewind": { pitch: 65, velocity: 127, channel: 1 }, "fastforward": { pitch: 66, velocity: 127, channel: 1 }, "play": { pitch: 60, velocity: 127, channel: 1 }, "stop": { pitch: 61, velocity: 127, channel: 1 }, "record": { pitch: 62, velocity: 127, channel: 1 }, "test": { pitch: 60, velocity: 127, channel: 1 } } }
By keeping this reference object, I'm able to quickly keep track of what human-language commands are mapped to what midi controls. For the transport, all commands are mapped to midi notes.
In the browser, I'm sending tiny socket messages with this human-readable code as a message
//an example button html: <button class="play">▶</button> //button event handlers $("button").on(eventstring(), function(e){ if(e.type.indexOf("start") > -1 || e.type.indexOf("down") > -1){ //shows a visual highlight when you tap the button $(this).addClass("active"); }else{ //removes the highlight $(this).removeClass("active"); //calls the send message function sendMessage("transport", $(this).attr("class")); } }) //and here's the function to send out the socket message var sendMessage = function(message, data){ socket.emit(message, data); }
Now on the server, I wanted to separate the messages for transport controls (navigating the timeline) vs. channel navigation (finding your tracks) vs. channel conducting ('playing' levels, pans, effects). I set up a socket listener exclusively for transport which uses the functions outlined in my previous post.
socket.on("transport", function(data){ var messages = noteOn(map.transport[data].pitch, map.transport[data].velocity, map.transport[data].channel); messages = messages.concat( noteOff(map.transport[data].pitch, map.transport[data].channel) ) serialSender.sendMessages(messages); });
And there you have it, instantly useful and not so terribly complex :)


















