You are on page 1of 7

16/05/2021 Dual PS2 Keyboard MIDI controller

Dual PS2-Keyboard MIDI controller


After having a lot of fun hooking up an old MIDI keyboard to my PC with VST-host plus a bunch of VST plugins
for all sort of instruments I was lacking a few buttons to start extra effects. So I started wondering whether I can use
old PS2 keyboards to generate MIDI commands with a microcontroller to convert PS2 keystrokes to MIDI
sequences. I choose an Atmel328p that I flashed with the Arduino UNO bootloader for this task. I also connected a
FTDI UM232R USB to serial converter to the Atmel, so to the host PC this setup looks like an Arduino UNO and I
can use the nice Arduino development software including all libraries for programming. The PS2 keyboards are
connected to the digital pins of the Atmel using pins that support interrupts. The MIDI output is using the RS232
Serial TX line of the Atmel. But let me first describe first the hardware and then the software in a bit more detail.

Hardware

The hardware is rather simple. I flashed the Atmel chip using a three-resistor programmer hooked up to the parallel
port which essentially turns it into an Arduino UNO without the peripherial stuff. Then I connect the RX and TX lines
of an FTDI USB to serial converter to the appropriate pins on the Atmel and connect the FTDI DTR line via a 100
nF capacitor and a 22k pullup resistor to the Reset line of the Atmel. I also connect an LED to pin 19 on the Atmel
(D13 on UNO) via a suitable current limiting resistor which is useful for debugging. Now the contraption appears to
the host PC as an Arduino UNO.

Next in line was connecting the the PS2 keyboards, that I had lying around. At first I had planned to only use a single
keyboard, but it turned out that it was rather difficult to obtain individual PS2 connectors. It feels just wrong to cut
apart perfectly functional PS2 to USB keyboard and mouse adapters. In the end I used a desoldered PS2 connector
from a damaged motherboard. This connector has two individual PS2 connectors, one for the mouse and one for the
keyboard. So, instead of leaving one connector unused I decided to double the number of keys that I can punch and
hook up two keyboards. The connection is rather straight forward. On the PS2 connector there are ground and 5V
lines as well as one clock- and one data-line. The same is of course true for the other keyboard connector. I then
followed the Arduino PS2 keyboard description and hooked up the clock lines to the external interrupt pins (pin 4
and 5 of the Atmel, which corresponds to pin D2 and D3 on the Arduino UNO board) and the data lines to pin 6
and 11 on the Atmel (D4 and D5 on the UNO), respectively.

The MIDI messages are sent via a DIN 5-pin connector to external MIDI consumers. There are just three pins
connected, Ground, 5V and the TX signal from pin number 3 on the Atmel (D1 on UNO). Note that presently the
system has an old-style MIDI interface, rather than a more modern USB MIDI interface. An idea is to use the
LUFA or V-USB framework in the future, but presently it's the old stuff that is implemented.

I also connected three analog input pins 23-25 on the Atmel (A0-A2 on UNO) to a 100k potentiometer that can
vary the voltage on the input pins between zero and 5 Volts to change the MIDI velocity or the speed of the
arpeggiator. There was too little space in the box that I had, but if I build it again, I would connected all six analog
inputs. The function of the three potentiometers is described in the following table.
Pot Function
A0 (left) Key velocity, first keyboard
A1 (middle) Key velocity, second keyboard
A2 (right) Speed of the arpeggiator

In this configuration the Atmel chip has quite a few unused IO pins and I used those to add a 16x2 LCD display that
I hooked up in the 4-wire mode and use the LiquidCrystal library for the Arduino to interface it to my project. In
the main program I just add

#include <LiquidCrystal.h>
LiquidCrystal lcd(12,11,10,9,8,7)
ziemann.web.cern.ch/ziemann/gadget/midi_keyboard/ 1/7
16/05/2021 Dual PS2 Keyboard MIDI controller

to the declaration area of the code and can henceforth lcd.setCursor(line,column) to write something to the specified
position with lcd.print(...). Initially did not foresee a LCD display and the retrofitted version is hanging a bit losely
above the box with the hardware. What I had foreseen is to make the unused IO-pins accessible via a DSUB-25
connector at the back of the box. And that is where the LCD is connected.

Software

The software was a bit tricky. I could not use the Arduino library for PS2-keyboard, because it only returns a single
key and has some blocking loops inside. For a MIDI keyboard we need key-down and the key-release events and
preferably even for multiple keys and multiple keyboards. So, we need to avoid blocking completely. In the end I
used a number of interleaved ringbuffers that are filled by one function and consumed by another. The different
functions are then executed in a round-robin fashion in the runtime loop of the Arduino software.

At the lowest level there are interrupt handlers that take care of the clock lines of the PS2 keyborad and assemble
individual bytes which are put in two ringbuffers, one for each of the two keyboards. The first functions in the main
Arduino loop check whether new bytes are available and then build key events from those bytes.

Here we need to briefly discuss how PS2 keyboard communicate keystrokes. Once a key is pressed it sends a byte
that depends on the key. A table that links the keys to the bytes is available here. If a normal key is released the the
same byte is send again, preceded by 0xF0. Thus a 0xF0 byte signals key release. That's good! What makes life a
bit complicated is the fact that there are extended codes which are always preceded by 0xE0, even in key-release
events. So we have to keep track of the preceding keys and once we know which key and what type of event
(down or release) happened we put an event in the key-event ring-buffer. In the software a key event is a structure
that contains the byte that characterizes the key and a status byte that contains information from which of the two
keybords that event originates and whether some special keys (ALT, CTRL, SHIFT_LEFT, SHIFT_RIGHT) was
pressed. The latter are basically normal keys, but I intercept them in the event assembly function and set the
appropriate bits in the status byte.

In the software I define a key_event structure that contains the key that was pressed and a status byte. The status
byte contains the information whether the event was a key-down or key-release event, and whether the above
mentioned special keys were pressed simultaneously, and whether the key comes from the first or second keyborad.
The bits in the status byte have the following meaning

Bit number Test Meaning


0 0x01 PRESSED
1 0x02 MODIFIER
2 0x04 SHIFT_L
3 0x08 SHIFT_R
4 0x10 ALT
5 0x20 CTRL
If set, take note-value literally, without table lookup, used mostly
6 0x40
for arpeggio
7 0x80 0=kbd0, 1=kbd1

Thus, after the key_event is assembled it is placed in a ring buffer named keybuf[], that is consumed later in the
loop. The key-events are written to position keyhead in the buffer and are consumed at position keytail. later in the
program we just check whether keyhead is different from keytail, which indicates that a new key-event is available
and needs to be handled.

ziemann.web.cern.ch/ziemann/gadget/midi_keyboard/ 2/7
16/05/2021 Dual PS2 Keyboard MIDI controller

Notes

The normal keyboard keys are used to emulate a piano keyboard. The key at the lower left, next to the 'Shift' key
produces a 'C' on the keyboard. The bottom row of keys, just above the space bar, are assigned to all the white
keys on a normal piano keyboard. The black keys are located on the second row at the appropriate locations. The
key 'A' corresponds to 'C#' and 'S' to 'D#'. Then there is an unassigned key, the letter 'D', because there is no black
key between an 'E' and 'F' on a piano-keyboard. The keyboard rows starting with 'Q' and '1' are set with the
equivalent keys, just shifted up by two octaves.

The notes can be easily transposed up by one half-tone by pressing the 'Up' arrow key and down by one half-tone
with the 'Down' arrow. Using 'CTRL-Up' transposes up by one octave and 'CTRL-Down' transposes down.

Chords

It is possible to play chords or combination of notes instead of single notes. This is controlled by the mode variable,
which is an array with one entry for each keyboard. The mode variable is incremented using the 'Cursor Up' and
'Cursor Down' arrows and the current value is displayed on the LCD. The following table explains the chords that
are available.
mode Action
0 Chords are turned OFF
1-12 Base note plus half-tone 'mode'
13 Minor chord
14 Major chord
101-112 Arpeggio: base plus half-tone
113 Arpeggio: half-tones 0+4+7+4
114 Arpeggio: walking bass
115 Arpeggio: half-tones 0+7+4+7
116 Arpeggio: waltz
... More to come...

Note that also broken chords are available, but this is sub-optimal. It is better to use the arpeggio engine discussed
below. This mode can only use a single keyboard and is blocking the other one.

In the code the mode variable is interpreted immediately after the a MIDI command is sent and additional MIDI
commands are automatically added. If mode is below 100 simple note or chord completions are implemented. So
pressing a single key will cause a major chord, interval such as an octave to sound. For modes below 100 all keys
are also turned off when the initially pressed key is released. The Arpeggio modes above 100 repeat indefinitely until
another key is pressed on either keyboard.

Arpeggio

The arpeggio uses delayed events and is enabled with the 'Insert' key which turns on the autoplay engine and the
'Delete' key turns it off. The arpeggio pattern can be chosen with the keypad numbers. See the table describing the
special key actions below to see what is presently implemented.

The arpeggiator has a second level of complexity in the sense that the base note is shifted after the arpeggio pattern
has completed one turn. In this way a blues can be easily implemented, which is based on shifting the base note of a
chord to the fourth or fifth at the appropriate time. This mode is enabled by 'CTRL-Insert' and diabled by 'CTRL-

ziemann.web.cern.ch/ziemann/gadget/midi_keyboard/ 3/7
16/05/2021 Dual PS2 Keyboard MIDI controller

Delete'. The only implemented pattern is a blues and the controlling variable is use_meta[] which has two array
elements, one for each keyboard.

If the normal autoplay mode is enabled, the present time is recorded. The base note is recorded and the code near
the start of the loop() is activated, because attime() is non-zero. There the contents of the key-event ringbuffer is
manipulated. First we check whether the present time is later than the requested time. Then the previous key-event is
turned off and then the next even in the sequence is placed in the ringbuffer and the attime() variable updated to
schedule the execution until after the attime has expired. The length of the delay can be adjusted with the right-most
potentiometer (A2).

Limitation

In the arpeggiator, all notes have the same duration.

The number of simultaneously accepted keys is limited, but that depends mostly on the keyboards. The simple
consumer-keyboards cannot handle more than two or three simultaneously pressed keys.

Learn mode

I implemented a learn mode, mostly in order to be able to record events with non-standard time intervals. The learn
mode is toggled with the 'PageUp' key and if it is enabled, at present up to 200 key-events from both keyboards can
be recorded in a buffer, including the time elapsed between events. Pressing 'PageUp' again turns off the learn mode.
The duration of the last event is given by the time between the last event and the 'PageUp' keypress, that turns the
learn mode off. In the program this is implememented by writing the event that enters the section, where the key-
events are interpreted and just written into the buffer, while simultaneously recording the time elapsed since the
previous event. Here the events that correspond to entering the learn or replay modes are masked.

The recorded key-events can be played back by enabling the replay mode by pressing 'PageDown'. Then the system
loops over the recorded events. The replay mode is turned off by pressing 'PageDown' a second time.

Note that the learn mode is very versatile. It records events from both keyboard and even mode change, arpeggios
or other meta-like events.

User Manual

Most of the key-strokes cause note events, that are sent to the MIDI device, but a selection is changing the state of
the system, such as turning on and off learn mode or arpeggiators. These special commands are collected inthe
following table.

The special key actions

Key pressed Modifier Key Action


Left arrow - Transpose down by one half-tone
. CTRL Transpose down by one octave
Right arrow - Transpose up by one half-tone
. CTRL Transpose up by one octave
Home - Turn transpose back to default
Up arrow - Increment mode (arpeggio and special effects) by one
. CTRL Increment mode by 100
Down arrow - Decrement mode by one
ziemann.web.cern.ch/ziemann/gadget/midi_keyboard/ 4/7
16/05/2021 Dual PS2 Keyboard MIDI controller

. CTRL Decrement mode by 100 (or set to 0)


Insert - Toggle Autoplay
Delete - Toggle Meta loop
Page Up - Toggle Learn mode
Page Down - Toggle Replay mode
Key F1-F10 - Select MIDI channel 1 to 10
Key F11 - Decrement MIDI channel by 1 (1 is rock-bottom)
Key F12 - Increment MIDI channel by 1 (wrap 16->1)
ESC - All notes OFF
NumLock - Program autoplay Keypad0 to interval defined by mode[]
Keypad 0 - Autoplay, arpeggio=Two notes, program with NumLock
Keypad 1 - Autoplay, arpeggio=walking bass line
Keypad 2 - Autoplay, arpeggio=broken major chord
Keypad 3 - Autoplay, arpeggio=plus fifth
Keypad 4 - Autoplay, arpeggio=plus octave
Keypad 5 - Autoplay, arpeggio=plus fifth and sixth
Keypad 6 - Autoplay, arpeggio=plus minor and major third
Keypad 7 ff - Available for added functionality

The state of the system is displayed on the small 16x2 LCD. There is one line for each attached keyboard. The
format for each line is
C2M5amT2-80

The number following the initial 'C' is the MIDI channel on which the messages are sent. This number can be
changed by the F1-F12 keys. The number following the 'M' is the mode that is used for the arpeggios and can be
changed by the up and down arrow keys. The small letter 'a' denotes that the system is in the real autoplay arpeggio
mode that can be toggled by 'Insert'. If autoplay is not enabled a hyphen is displayed. The small 'm' indicates that the
meta loop is active, a hyphen indicates that it is not active. This mode is toggled by the 'Delete' key. The number
following the letter 'T' indicates the transpose in number of half-tones. In the example above we shift up by two half-
tones. The transpose variable can be adjusted by the left and right arrow keys. The following hyphen is replaced by
the letter 'L' if the system is in learn mode and by the letter 'R', if in replay mode. The final numbers indicate the
number of events in the replay buffer that was acquired during the previous learn mode.

Prototype

I built a prototype system into a small box shown below. On the left image the LCD shows the message right after
booting. Also visible are the two connectors of the cables to the keyboards. The three potentiometers are equipped
with small pieces of wood that I use as knobs. Initially that was a quick-fix, but proved useful, because you can 'feel'
the knobs without looking. The retro-fitted LCD-display is dangling somewhat losely above the device and is rather
fragile. In a real system this needs to be fixed in the box, of course. Behind the small hole in the lower left of the box
is a reset button, that reboots the system, should it hang. The image in the center shows the system in operation. The
upper keyboard has Channel 2 selected, arpeggio running in a meta loop. Actually, I ran a blues in the meta loop and
a walking bass in the arpeggiator. Just pick up your harmonica and start playing along. The lower keyboard uses
MIDI channel 1 and responds to normal key-presses. The image on the right shows the back of the box with the
DSUB-25 connector to which the LCD is attached, the MIDI-OUT 5-pin DIN connector in the center, and the
USB connector needed to power the system and for programming.

ziemann.web.cern.ch/ziemann/gadget/midi_keyboard/ 5/7
16/05/2021 Dual PS2 Keyboard MIDI controller

On the image below on the left we see the inside of the box with the FTDI USB converter on the lower right and the
MIDI and DSUB-25 connectors attached to the back side of the box. Slightly above and to the left of the FTDI is
the socketed ATMega328. At the top left the shiny block is the back-side of the PS2-connectors for the keyboards.
Finally the three potentiometers for keyboard velocities for the two keyboards and the speed for the arpeggiator are
seen on the top right.

While testing the box I hooked it up via an opto-coupler to a SoundBlaster card that sits in an old Windows PC
where I run the the VSTHost software with a number of VST plugins from VST4free. An image of that screen is
visible on the right above. I also tested a regular MIDI-to-USB converter and that worked as well.

History

I started this project in the summer of 2012 and added the learn mode in 2014. Since then the project lay dormant
due to lack of time, but maybe someone finds it useful and wants to build her own copy and add the missing
functionality.

There are a few ideas of how to extend the functionality. Foremost is built-in USB-midi support, probably by
programming a ATtiny to serve as a MIDI-TTL-to-MIDI-USB converter and placing it inside the MIDI box, which
has ample of space left. Another feature that is needed is an arpeggiator that handles notes of different lengths, such
that more complex rhythms are possible. But for the time being all that remains on the to-do list...

Download

Arduino sketch for midi_double2.ino


Hand sketch of the circuitry (My attempt with Fritzing looks even worse).

Disclaimer

ziemann.web.cern.ch/ziemann/gadget/midi_keyboard/ 6/7
16/05/2021 Dual PS2 Keyboard MIDI controller

I built the device during my spare time and with good intentions and care, but there may be bugs. If you fry your cat
using my contraption, it is your problem, not mine.

(C) Volker Ziemann, July 3, 2017

ziemann.web.cern.ch/ziemann/gadget/midi_keyboard/ 7/7

You might also like