..

Week of the 04/07/2025 - #15

Contents

tech

  • MIDI Resources

MIDI Resources

I’m interested to see if I can hook up my Teenage Engineering EP-133 KOII to my old Apple 2c via the RS-232 interface. The idea is to build an RS-232 to MIDI bridge to hook up the EP-133 to my old Apple 2c. With this I can create demos and use the EP-133 as my sound card to get decent sound off the Apple //c.

To test what MIDI commands the EP-133 can handle I use amidi command in Linux. Here’s what I do:

To list MIDI devices:

amidi -l

which displays:

Dir Device    Name
IO  hw:0,0,0  EP-133 MIDI 1
IO  hw:1,0,0  U-24 ZOOM U-24 MIDI I/O Port
IO  hw:1,0,1  U-24 ZOOM U-24 Reserved Port

To send a MIDI start to the KOII you can do:

amidi -p "hw:0,0,0" -S fa

And to make it stop:

amidi -p "hw:0,0,0" -S fc

With the latest 2.0 update to the KOII you can now trigger any pad from any group via MIDI commands. In this link you can see what MIDI note triggers which pad: midi note map. If you want to be able to play a sample chromatically then what you need to do is go to the pad that has that sample, assign a MIDI channel to it and then send notes to it. For example, to send a 0x40 (b0100000) note to channel 2 you would do:

amidi -p "hw:0,0,0" -S 91 40 7f

In this way you can program drums on certain pads and assign MIDI channels to the ones you want to play as an instument. I still need to do more tests but seems to me this is what I need to be able to ‘play’ the KOII from the Apple //c.

The next step is to create an intereface between the RS-232 port of the Apple 2c and an output MIDI port. For this there are several projects in the internet that can be used. In the following section I have several links to the more intereting ones.

This is the sample code to map from one serial port (computer) to the other (MIDI)

#include <MIDI.h>
#include <AltSoftSerial.h>

AltSoftSerial SWSerial;

// A variable to know how long the LED has been turned on
elapsedMillis ledOnMillis;


// Create the Serial MIDI ports
MIDI_CREATE_INSTANCE(AltSoftSerial, SWSerial, MIDIsoft);

//NEW2 to run RS232 at 38400
struct CustomBaudRateSettings : public MIDI_NAMESPACE::DefaultSerialSettings {
  static const long BaudRate = 38400;
};

MIDI_NAMESPACE::SerialMIDI<HardwareSerial, CustomBaudRateSettings> serialMIDI(Serial1);
MIDI_NAMESPACE::MidiInterface<MIDI_NAMESPACE::SerialMIDI<HardwareSerial, CustomBaudRateSettings>> MIDIserial((MIDI_NAMESPACE::SerialMIDI<HardwareSerial, CustomBaudRateSettings>&)serialMIDI);


void setup() {
  pinMode(11, OUTPUT); // LED pin
  pinMode(10, INPUT); // rx pin for MIDI
  pinMode(9, OUTPUT);// tx pin for MIDI
  
  Serial1.begin(38400);

  MIDIserial.begin(MIDI_CHANNEL_OMNI);
  MIDIserial.turnThruOff();

  MIDIsoft.begin(MIDI_CHANNEL_OMNI);
  MIDIsoft.turnThruOff();

  digitalWriteFast(11, HIGH); // LED on
  delay(50);
  digitalWriteFast(11, LOW);
  delay(50);
  digitalWriteFast(11, HIGH); // LED on
  delay(50);
  digitalWriteFast(11, LOW);
}

void loop() {
  bool activity = false;

  if (MIDIserial.read()) {
    // get a MIDI IN1 (Serial) message
    byte type = MIDIserial.getType();
    byte channel = MIDIserial.getChannel();
    byte data1 = MIDIserial.getData1();
    byte data2 = MIDIserial.getData2();

    // forward the message to USB MIDI virtual cable #0
    if (type != midi::SystemExclusive) {
      // Normal messages, simply give the data to the usbMIDI.send()
      usbMIDI.send(type, data1, data2, channel, 0);
      MIDIsoft.send(type, data1, data2, channel);
   
    } else {
      // SysEx messages are special.  The message length is given in data1 & data2
      unsigned int SysExLength = data1 + data2 * 256;
      usbMIDI.sendSysEx(SysExLength, MIDIserial.getSysExArray(), true, 0);
      MIDIsoft.sendSysEx(SysExLength, MIDIserial.getSysExArray(), true);

    }
    activity = true;
  }

  if (MIDIsoft.read()) {
    // get a MIDI IN2 (Serial) message
    byte type = MIDIsoft.getType();
    byte channel = MIDIsoft.getChannel();
    byte data1 = MIDIsoft.getData1();
    byte data2 = MIDIsoft.getData2();

    // forward the message to USB MIDI virtual cable #1
    if (type != midi::SystemExclusive) {
      // Normal messages, simply give the data to the usbMIDI.send()
      usbMIDI.send(type, data1, data2, channel, 0);
      MIDIserial.send(type, data1, data2, channel);
      
    } else {
      // SysEx messages are special.  The message length is given in data1 & data2
      unsigned int SysExLength = data1 + data2 * 256;
      usbMIDI.sendSysEx(SysExLength, MIDIsoft.getSysExArray(), true, 0);
      MIDIserial.sendSysEx(SysExLength, MIDIsoft.getSysExArray(), true);

    }
    activity = true;
  }

  if (usbMIDI.read()) {
    // get a MIDI IN1 (Serial) message
    byte type = usbMIDI.getType();
    byte channel = usbMIDI.getChannel();
    byte data1 = usbMIDI.getData1();
    byte data2 = usbMIDI.getData2();

    // forward the message to USB MIDI virtual cable #0
    if (type != midi::SystemExclusive) {
      // Normal messages, simply give the data to the usbMIDI.send()
      MIDIserial.send(type, data1, data2, channel);
      MIDIsoft.send(type, data1, data2, channel);
      
    } else {
      // SysEx messages are special.  The message length is given in data1 & data2
      unsigned int SysExLength = data1 + data2 * 256;
      MIDIserial.sendSysEx(SysExLength, usbMIDI.getSysExArray(), true);
      MIDIsoft.sendSysEx(SysExLength, usbMIDI.getSysExArray(), true);

    }
    activity = true;
  }


  // blink the LED when any activity has happened
  if (activity) {
    digitalWriteFast(11, HIGH); // LED on
    ledOnMillis = 0;
  }
  if (ledOnMillis > 15) {
    digitalWriteFast(11, LOW);  // LED off
  }

}
  • AltSoftSerial Library - Library homepage
  • AltSoftSerial Library @Github - Improved software emulated serial, using hardware timers for precise signal timing and availability of CPU time for other libraries to respond to interrupts during data AltSoftSerial data transmission and reception.
  • Arduino example - Learn how to communicate with a computer using a MAX3323 single channel RS-232 driver/receiver and a software serial connection on the Arduino.
  • Control Surface: “Control Surface is an Arduino library for building MIDI controllers and control surfaces. At its core, the library features a flexible MIDI abstraction layer with support for serial 5-pin DIN MIDI, MIDI over USB, MIDI over BLE, etc. These MIDI interfaces are compatible with a wide range of Arduino boards (a full table can be found here) and are useful in any Arduino MIDI project.”
  • Control Surface GitHub - GitHub repository for project