Launchpad MIDI Step Sequencer
Click image to watch demo video.
The objective of this project was to create a step sequencer with all user interface and visual feedback provided through an Novation Launchpad.
Ever since I got this MIDI controller, I've found it had something special. Even though it couldn't detect the velocity of inputs like many other midi, there was something alluring about this minimalistic multicolored grid of buttons.
My goal with this project was to create something for the Launchpad where the user could plug it in to a computer and forget the computer is even there, interacting only with the device.
Initially this would be a rhythm based game but after experiencing issues with input timing and refresh rates with the device, a step sequencer was chosen instead. The step sequencer seemed to make sense to me due to the 8x8 at the center of the device.
The first step for this project was to design the interface of the final product. A list of requirements was laid out:
- Multiple Channels with support for a drum channel
- Live playback
- Change of instruments
- Change of tempo
- Adjusting note duration and velocity
After going through this list the current interface was designed in full to match these requirements.
The step sequencer consists of 8 pages, each representing it's own MIDI channel. The first page is MIDI channel 10 for drums, and the next 7 are channels 1 through 7. The instrument/program used for each channel can be adjusted using the up and down arrows at the top left, except for the drum channel whose instrument is fixed.
Each page allows for the placement of 8 different notes rows across 8 step columns. Each note's velocity and duration can be modified by pressing the 'user 1' or 'user 2' buttons, clicking an existing note and scaling the value by pressing a button in the rightmost column. All notes use the velocity and duration of the last created note by default.
In addition, the pitches of the 8 different notes can be chosen by clicking the 'session' button. This scale customization allows for users to use notes from within a 2 octave range. For simplicity, the note rows will always be ascending in pitch from bottom to top. In addition, creating a new page/channel will start with a copy the last used channel's scale.
When the 'mixer' button is pressed, the sequencer plays back across all channels. During this time, new notes can still be added and velocities and durations can be changing. Also, the left and right arrows of the top row allow the sequencer tempo to be sped up or slowed down.
A limitation of 8 notes and 8 beats per instrument was set to avoid the need to scroll up or down which would have proved confusing to navigate without an indicator of where the user was within the scrollable area.
The entire Step Sequencer was created in C++ using the RTMidi library that was shown in class. The Launchpad is a MIDI input and output device for the button presses and LEDs. The reference manual for how to interface with the Launchpad can be found here.
The program has a couple of helper classes.
This class is used to store messages being sent to and from the Launchpad. It's constructor contains an x and y coordinate corresponding to the location on the Launchpad and either a boolean to note whether a button was pressed or depressed for input, or a Color object from an enum for outputting a color to a specific button.
This class holds the RtMidiIn and RtMidiOut objects and is used as a proxy to send and receive messages, as well as to send a message to disable all LEDs on the device. The getMessage() method take the MIDI messages provided by RtMidi and create lpMessage objects to be returned. The sendMessage() method does the opposite: it takes an lpMessage object and converts it into a MIDI message to be sent to the Launchpad. resetLeds() sends a message to disable all device LEDs, as specified in the Launchpad programmers manual above.
The Sequencer object represents a single page or channel in the sequencer. It holds all the attributes tied to these pages like the note scale, the instrument and channel number as well as methods to play back notes by x & y coordinate, scale number or MIDI note number. A vector of these is stored in the main class and a separate object is used for the each page in the Step Sequencer.
This class does everything else. It calls RtMidi methods to set up the MIDI devices, it has a method to render the Launchpad LEDs based on the state of variables, it handles all incoming messages through the LaunchpadAdapter object and responds with direct LED or audio output or updates to Sequencer objects. Finally, the Launcher also manages timing for playback in the step sequencer and calls Sequencer object methods to play the sounds back.
It also contains a backlog of notes that were turned on with the number of steps required before turning them off. Every new step, these steps required are decreased and when they hit zero a note off is sent. This was required to allow for notes with differing durations without the implicit use of multi-threading.
In the end, the step sequencer turned out to be functional instrument that doesn't require monitor to use. The final product works well with occasional timing errors being the only thing setting it back. Even the simplistic and occasionally cheesy MIDI instruments can be replaced by linking the audio MIDI out of this software to another that handles them using nicer virtual instruments or samples.
Further improvements would be the ability to use the program with multiple Launchpads to get more note options per channel or more beats per loop but I was limited to the one controller I had.