Building a custom MIDI controller part 1 - Prototyping
I've been interested in DJing and music production since my early teens, ever since I met a friend's dad who had a MIDI powered studio in his home which included a software sequencer running on an Atari ST. I started to collect vinyl when I was about 14, and although I haven't bought much in the last 10 years or so I've always had a soft spot for collecting music. Occasionally I will indulge myself by buying a collection of mp3s from my favourite online retailer, and use Traktor Scratch along with a Numark Mixtrack Pro 2 to record myself something to listen to while I code away my days. During the most recent of these sessions I found myself pondering just how the Numark MIDI controller worked, and with a little bit of research realised that it wouldn't be too hard to build myself my own custom controller. Don't get me wrong, the Mixtrack Pro is a capable piece of kit - even if it's not the Pioneer knobs the cool kids are twiddling today - but there's a certain satisfaction in building and using your own equipment.
So what do I mean when I say 'MIDI controller'? If you came here through a search engine then chances are you already know, but for those who don't here is a brief run down: MIDI is a serial data protocol used to transmit note, velocity and other data between pieces of electronic equipment, originally designed so that a single MIDI keyboard could be used to control multiple synthesisers or sound generators, or so that a sequencer (either in software or hardware) could transmit pre-arranged note data to multiple pieces of equipment. In this context my MIDI controller is used to send commands to the mixer faders, volume and EQ controls and special effects parameters of the Traktor DJ software. It is a digital representation, if you will, of the traditional turntable setup, with a hardware interface sending data, via MIDI, to the computer. It looks like this:
Building something which replicated the entire controller was somewhat ambitious, even if the theory is relatively easy to understand, so I decided my own controller should reproduce only a subset of the controls I most use on the Mixtrack Pro.
Where to start?
So the first thing to do was understand the basic representation provided by MIDI data. This is actually quite straight forward - there are digital commands, on/off data which represents when a note on a keyboard has been pressed or released, and contains a value representing the note itself, such as A#3. These are employed by the Mixtrack as button pushes, for instance pressing and releasing the play button will send a note on and note off signal for B3. This is mapped inside Traktor, so that it knows that it's expected to play or pause the music.
Then there are analogue inputs. MIDI allows for analogue controls such as pitch bend, volume, pan and many custom assignable parameters, which are sent every time a control is moved or updated. The first bit of a MIDI byte is the status bit, indicating what type of data is being sent - therefore the control data is mapped to the values 0 - 127 in the remaining 7 bits. When a control is moved its current value is sampled then sent as MIDI data along with a descriptor of the control to which it belongs.
The design.
The next step of the plan was to figure out how to best implement this in hardware. The first thing that sprang to mind was the trusty ATMega microcontroller (I use these almost every day at work) so the Arduino platform proved to be ideal. These microcontrollers not only have digital inputs I could use for the buttons, but also analogue to digital converters (ADCs) which can be used to sample the analogue sliders and knobs. An Arduino neatly packages this all up, not only on a ready made board, but also in software support. There are many great libraries available for it and I thought I could safely assume there would be a MIDI library to save me much of the work. As it turns out, I was correct :)
The Arduino Uno board is probably the most popular of all the Arduinos, certainly popular enough that I had at least one spare lying around. It also has another advantage, in that the board connects to the PC for regular development via a USB socket. The Uno actually has a second microcontroller onboard which provides an interface to the UART of the main microcontroller so that data can be sent via RS232 over USB. As it happens this second microcontroller is programmable via the 6 pin ISP header on the board using an AVR ISP MKII programmer. The HIDUINO project provides software which can be uploaded to this microcontroller to make it act like an HID MIDI device, and HIDs are compatible with windows, linux and macOS. This is fantastically useful as it means that I didn't have to use a traditional five pin DIN MIDI connector when plugging the Arduino into my PC and I could still power the device from a single connection. The only drawback is that with this software you can no longer use the USB socket to upload sketches via the Arduino IDE. This was of little consequence to me, fortunately, as I could use the aforementioned ISP programmer via the onboard header pins, along with Atmel Studio 7, but this is a caveat to watch out for if you do not own a dedicated programmer.
Peripheral hardware.
So, with the ability to sample digital and analogue inputs and send the results over a MIDI connection in place, the next thing to tackle was which hardware I should be using to create the MIDI signals. I mentioned at the start of the post that I was only going to implement a subset of controls, so I looked at what I used most and made a list:
The rotary knobs, however, I ordered from Amazon. It's important to note that both the knobs and the sliders use a linear scale, not an logarithmic one which is most commonly found in audio equipment. The old mixer used VCA (voltage controlled) signals which is why they were linear. If you're not aware of the difference between linear and logarithmic sliders, then it's definitely worth checking out this page, which explains why logarithmic sliders are used in audio hardware. As this is a controller we want to control the software in a linear fashion - the DJ software (Traktor in this case) implements the logarithmic scale itself.
For buttons I wanted something a bit fancy, and the best looking ones I could find were actually designed for an arcade cabinet. Arcadeworld UK do a great selection, and even have buttons which include LEDs - so of course I had to have those ;)
A note on the power:
It's always worth taking into consideration just how much current a project like this is likely to draw. As I'm powering the Arduino via the USB port, the supply can provide a maximum current of 500mA. A quick bit of maths (and good old Ohm's law) will let us calculate just how much current is going to be drawn:
5v / 10kOhm for each of the rotary controls and two channel sliders = 0.5mA * 9 = 4.5mA
The crossfader is 50kOhm (for no reason other than that's just what I happened to have) so 0.1mA
This gives a total of 4.6mA, plus the 6 LEDs which are rated at 5mA according to the button's spec sheet. One of these per channel is fine (an Arduino channel is rated 40mA max) and assuming all 6 LEDs are on at once this gives us a total of 30mA current draw for the LEDs. Add that to our analogue inputs to get 34.6mA and we find that this is well within the 500mA PSU limit. Hooray!
Hurdle number one.
Now, while I was getting carried away ordering all these parts, I'd also started to assemble something of a prototype using the sliders and some old clicky buttons I had lying around. This is where I hit the first snag. While the Uno has more than 6 digital inputs, it doesn't have enough to also light up those fancy button LEDs. It certainly doesn't have 10 analogue inputs. Hum. The solution to this was the Sparkfun 8 way multiplexer, which allows up to 8 signals to be used on a single Arduino input. This works by sending a binary value between 0 - 7 over 3 connections (each one representing a single bit) to the board to select which input to listen to, which is then forwarded to a common connection to the Uno. With only four connections to this board I had enough digital inputs for the buttons, with two to spare, and enough remaining digital connections on the Arduino itself to drive the LEDs. The 16 way bigger brother of this board provided plenty of inputs for the analogue controls, with six spare in case I ever decide to upgrade in the future...
Both boards are operated in the same way so they can share the same set of selection pins when connecting to the Arduino.
Phew.
With the prototype up and running it turned out to be pretty close to the final design. Here is the schematic for the overall circuit, and a fritzing wiring diagram:
The fritzing project file is included in the project repository.
Sending useful data.
With everything wired together, albeit in a bit of a jumbled mess on my desk, I could start work on the software. I already had the Hiduino software loaded up on the USB controller, so I fired up Atmel Studio 7, created a new project and linked it to the Arduino core library. I decided to use the Arduino core partially for convenience, but also because I wanted to use this MIDI library which greatly simplified sending MIDI data without having to implement the protocol myself. To decide which data to send I figured I might as well copy the MIDI layout of the Mixtrack Pro - after all Traktor was already set up to use it, and it is also a known reliable mapping. Figuring out what data was output by which control was made considerably easier by a utility called MIDI OX, which you can download here. This simple program outputs any incoming MIDI data to a console style window so you can easily see what's being sent and received over a MIDI connection. It was also at this point I discovered that, when sending back data via MIDI OX, the LEDs were controlled by note on/off events. Having realised this I made sure to handle incoming MIDI data on the Arduino. With all the mappings now worked out I made sure to note them all in the source code, as I'm pretty sure my future self will be very grateful.
I won't list the entire source code here, this post is already long enough, but you can download it via the repository. The overall technique though is as follows:
for each analogue input:
select the input via the multiplexer
sample the current value of the input
if the input value has changed since the last sample, send it via a MIDI signal
repeat for each digital input
handle any incoming MIDI signals and forward them to LEDs as necessary.
The premise is pretty simple, and made easy to implement thanks to the existing libraries (and the hard work of their authors!). With the prototype up and running, and my excitement mounting, it was time to turn my hand to designing the enclosure which will be the topic of my next post which you can find here.
Source code and Fritzing diagrams.
So what do I mean when I say 'MIDI controller'? If you came here through a search engine then chances are you already know, but for those who don't here is a brief run down: MIDI is a serial data protocol used to transmit note, velocity and other data between pieces of electronic equipment, originally designed so that a single MIDI keyboard could be used to control multiple synthesisers or sound generators, or so that a sequencer (either in software or hardware) could transmit pre-arranged note data to multiple pieces of equipment. In this context my MIDI controller is used to send commands to the mixer faders, volume and EQ controls and special effects parameters of the Traktor DJ software. It is a digital representation, if you will, of the traditional turntable setup, with a hardware interface sending data, via MIDI, to the computer. It looks like this:
Building something which replicated the entire controller was somewhat ambitious, even if the theory is relatively easy to understand, so I decided my own controller should reproduce only a subset of the controls I most use on the Mixtrack Pro.
Where to start?
So the first thing to do was understand the basic representation provided by MIDI data. This is actually quite straight forward - there are digital commands, on/off data which represents when a note on a keyboard has been pressed or released, and contains a value representing the note itself, such as A#3. These are employed by the Mixtrack as button pushes, for instance pressing and releasing the play button will send a note on and note off signal for B3. This is mapped inside Traktor, so that it knows that it's expected to play or pause the music.
Then there are analogue inputs. MIDI allows for analogue controls such as pitch bend, volume, pan and many custom assignable parameters, which are sent every time a control is moved or updated. The first bit of a MIDI byte is the status bit, indicating what type of data is being sent - therefore the control data is mapped to the values 0 - 127 in the remaining 7 bits. When a control is moved its current value is sampled then sent as MIDI data along with a descriptor of the control to which it belongs.
The design.
The next step of the plan was to figure out how to best implement this in hardware. The first thing that sprang to mind was the trusty ATMega microcontroller (I use these almost every day at work) so the Arduino platform proved to be ideal. These microcontrollers not only have digital inputs I could use for the buttons, but also analogue to digital converters (ADCs) which can be used to sample the analogue sliders and knobs. An Arduino neatly packages this all up, not only on a ready made board, but also in software support. There are many great libraries available for it and I thought I could safely assume there would be a MIDI library to save me much of the work. As it turns out, I was correct :)
The Arduino Uno board is probably the most popular of all the Arduinos, certainly popular enough that I had at least one spare lying around. It also has another advantage, in that the board connects to the PC for regular development via a USB socket. The Uno actually has a second microcontroller onboard which provides an interface to the UART of the main microcontroller so that data can be sent via RS232 over USB. As it happens this second microcontroller is programmable via the 6 pin ISP header on the board using an AVR ISP MKII programmer. The HIDUINO project provides software which can be uploaded to this microcontroller to make it act like an HID MIDI device, and HIDs are compatible with windows, linux and macOS. This is fantastically useful as it means that I didn't have to use a traditional five pin DIN MIDI connector when plugging the Arduino into my PC and I could still power the device from a single connection. The only drawback is that with this software you can no longer use the USB socket to upload sketches via the Arduino IDE. This was of little consequence to me, fortunately, as I could use the aforementioned ISP programmer via the onboard header pins, along with Atmel Studio 7, but this is a caveat to watch out for if you do not own a dedicated programmer.
Peripheral hardware.
So, with the ability to sample digital and analogue inputs and send the results over a MIDI connection in place, the next thing to tackle was which hardware I should be using to create the MIDI signals. I mentioned at the start of the post that I was only going to implement a subset of controls, so I looked at what I used most and made a list:
- EQ - high, middle and bass
- Master volume
- Channel Volume
- Crossfader
- Special effects
- Play/Pause button
- Cue button
The rotary knobs, however, I ordered from Amazon. It's important to note that both the knobs and the sliders use a linear scale, not an logarithmic one which is most commonly found in audio equipment. The old mixer used VCA (voltage controlled) signals which is why they were linear. If you're not aware of the difference between linear and logarithmic sliders, then it's definitely worth checking out this page, which explains why logarithmic sliders are used in audio hardware. As this is a controller we want to control the software in a linear fashion - the DJ software (Traktor in this case) implements the logarithmic scale itself.
For buttons I wanted something a bit fancy, and the best looking ones I could find were actually designed for an arcade cabinet. Arcadeworld UK do a great selection, and even have buttons which include LEDs - so of course I had to have those ;)
A note on the power:
It's always worth taking into consideration just how much current a project like this is likely to draw. As I'm powering the Arduino via the USB port, the supply can provide a maximum current of 500mA. A quick bit of maths (and good old Ohm's law) will let us calculate just how much current is going to be drawn:
5v / 10kOhm for each of the rotary controls and two channel sliders = 0.5mA * 9 = 4.5mA
The crossfader is 50kOhm (for no reason other than that's just what I happened to have) so 0.1mA
This gives a total of 4.6mA, plus the 6 LEDs which are rated at 5mA according to the button's spec sheet. One of these per channel is fine (an Arduino channel is rated 40mA max) and assuming all 6 LEDs are on at once this gives us a total of 30mA current draw for the LEDs. Add that to our analogue inputs to get 34.6mA and we find that this is well within the 500mA PSU limit. Hooray!
Hurdle number one.
Now, while I was getting carried away ordering all these parts, I'd also started to assemble something of a prototype using the sliders and some old clicky buttons I had lying around. This is where I hit the first snag. While the Uno has more than 6 digital inputs, it doesn't have enough to also light up those fancy button LEDs. It certainly doesn't have 10 analogue inputs. Hum. The solution to this was the Sparkfun 8 way multiplexer, which allows up to 8 signals to be used on a single Arduino input. This works by sending a binary value between 0 - 7 over 3 connections (each one representing a single bit) to the board to select which input to listen to, which is then forwarded to a common connection to the Uno. With only four connections to this board I had enough digital inputs for the buttons, with two to spare, and enough remaining digital connections on the Arduino itself to drive the LEDs. The 16 way bigger brother of this board provided plenty of inputs for the analogue controls, with six spare in case I ever decide to upgrade in the future...
Both boards are operated in the same way so they can share the same set of selection pins when connecting to the Arduino.
Phew.
With the prototype up and running it turned out to be pretty close to the final design. Here is the schematic for the overall circuit, and a fritzing wiring diagram:
The fritzing project file is included in the project repository.
Sending useful data.
With everything wired together, albeit in a bit of a jumbled mess on my desk, I could start work on the software. I already had the Hiduino software loaded up on the USB controller, so I fired up Atmel Studio 7, created a new project and linked it to the Arduino core library. I decided to use the Arduino core partially for convenience, but also because I wanted to use this MIDI library which greatly simplified sending MIDI data without having to implement the protocol myself. To decide which data to send I figured I might as well copy the MIDI layout of the Mixtrack Pro - after all Traktor was already set up to use it, and it is also a known reliable mapping. Figuring out what data was output by which control was made considerably easier by a utility called MIDI OX, which you can download here. This simple program outputs any incoming MIDI data to a console style window so you can easily see what's being sent and received over a MIDI connection. It was also at this point I discovered that, when sending back data via MIDI OX, the LEDs were controlled by note on/off events. Having realised this I made sure to handle incoming MIDI data on the Arduino. With all the mappings now worked out I made sure to note them all in the source code, as I'm pretty sure my future self will be very grateful.
I won't list the entire source code here, this post is already long enough, but you can download it via the repository. The overall technique though is as follows:
for each analogue input:
select the input via the multiplexer
sample the current value of the input
if the input value has changed since the last sample, send it via a MIDI signal
repeat for each digital input
handle any incoming MIDI signals and forward them to LEDs as necessary.
The premise is pretty simple, and made easy to implement thanks to the existing libraries (and the hard work of their authors!). With the prototype up and running, and my excitement mounting, it was time to turn my hand to designing the enclosure which will be the topic of my next post which you can find here.
Source code and Fritzing diagrams.
Comments
Post a Comment