midiwasp, kind of

Had a question from another Wasp owner about MIDI-ing up a Wasp so I chucked this thing together:

which seems like it works alright, but now I’m not so sure, due to my Wasp’s keyboard playing up fairly badly after being left alone for a few months. It seems like there’s a clocking problem somewhere in the keyboard note detection circuitry which causes it to semi-randomly flit between the note you pressed and a much lower one at a high speed… in some ways it’s quite an interesting effect, but not all the time.

To add to that, the timing from the x0xb0x via the Arduino seemed to get messed up as well, so I’m not convinced that this code is worth much but here goes anyway. Feel free to change it, smash it up, call it names.

/*

  __  __ ___ ___ _____      ___   ___ ___
 |  \/  |_ _|   \_ _\ \    / /_\ / __| _ \
 | |\/| || || |) | | \ \/\/ / _ \\__ \  _/
 |_|  |_|___|___/___| \_/\_/_/ \_\___/_|  
                                         

  - possibly cranky 5 pin DIN MIDI to Wasp LINK converter
  - uses PORTB (digital pins 8 to 13) for the note output
    and pin 7 for the gate trigger
  - tested on an Arduino Duemilanove,
  - should work on other 5V Arduinos - though the PORT
    number may change

  http://ua726.co.uk/2016/02/28/midiwasp-kind-of

  Libraries
  ===================
 
  You will need the...
 
  - FlexiTimer2 - http://playground.arduino.cc/Main/FlexiTimer2
  - MIDI library - http://arduinomidilib.fortyseveneffects.com/index.html

*/


// **************************************
// Set these up how you want them....

// MIDI base note is the lowest MIDI note that the Wasp will respond to
// 36 = C2
#define MIDI_BASE_NOTE 37

// MIDI input channel is the channel you want the Wasp to respond on
#define MIDI_INPUT_CHANNEL 1

//***************************************

#include <MIDI.h>
#include <FlexiTimer2.h>

#define TRIG_OUT 7
#define WASP_MAX_NOTE 35
#define NOTE_MEMORY_ARRAY_LENGTH 8
#define NO_NOTE -1

#define NP_LAST_NOTE 1
#define NP_HIGHEST_NOTE 2

const int8_t note_priority_mode = NP_LAST_NOTE;
volatile int8_t noteIsOnCount = 0;
volatile boolean triggerOutState = false;
int8_t noteMemory[NOTE_MEMORY_ARRAY_LENGTH] = {NO_NOTE, NO_NOTE, NO_NOTE, NO_NOTE, NO_NOTE, NO_NOTE, NO_NOTE, NO_NOTE};
const int8_t codeLookup [] = { 43, 42, 41, 40, 39, 38, 37, 36, 35, 34,
                      33, 32, 27, 26, 25, 24, 23, 22, 21, 20,
                      19, 18, 17, 16, 11, 10, 9,  8,  7,  6,
                      5, 4, 3, 2, 1, 0 };

MIDI_CREATE_DEFAULT_INSTANCE();

// -----------------------------------------------------------------------------

void handleNoteOn(byte channel, byte pitch, byte velocity)
{
  if(isMidiNoteInBounds(pitch)) {
    int8_t noteOn = pitch - MIDI_BASE_NOTE;
    noteMemory[noteIsOnCount] = noteOn;
    int8_t noteOnToPlay = getNoteToPlay();
    noteIsOnCount++;
    doNoteOn(noteOnToPlay);
  }
}

void handleNoteOff(byte channel, byte pitch, byte velocity)
{    
  if(isMidiNoteInBounds(pitch)) {
    int8_t noteOffToPlay = NO_NOTE;
    int8_t noteOff = pitch - MIDI_BASE_NOTE;
    removeNote(noteMemory, noteOff);
    noteIsOnCount--;
    noteOffToPlay = getNoteToPlay();
    if(noteIsOnCount > 0 && noteOffToPlay != NO_NOTE  ) {
        doNoteOn(noteOffToPlay);
    }
  }
}

int8_t getNoteToPlay()
{
  int8_t noteToPlay = NO_NOTE;
  if(note_priority_mode == NP_HIGHEST_NOTE)
  {
    noteToPlay = findHighestNote(noteMemory, NOTE_MEMORY_ARRAY_LENGTH);
  } else {
    noteToPlay = findLastNote(noteMemory, NOTE_MEMORY_ARRAY_LENGTH);
  }
 
  return noteToPlay;
}

boolean isMidiNoteInBounds(byte pitch)
{
  return pitch >= MIDI_BASE_NOTE && pitch <= MIDI_BASE_NOTE + WASP_MAX_NOTE;
}

int find(int8_t arr[], int8_t len, int8_t seek)
{
    for (int i = 0; i < len; ++i)
    {
        if (arr[i] == seek) return i;
    }
    return -1;
}
 
void removeElement(int8_t *array, int8_t index, int8_t array_length)
{
   int8_t i;
   for(i = index; i < array_length ; i++) {
     array[i] = array[i + 1];
   };
   array[array_length-1] = NO_NOTE;
}

void removeNote(int8_t *array, int8_t note)
{
  int8_t index = find(array, NOTE_MEMORY_ARRAY_LENGTH, note);
  if(index != -1) {
    removeElement(array,  index, NOTE_MEMORY_ARRAY_LENGTH);
  }
}

int8_t findHighestNote(int8_t *array, int8_t array_length)
{
  int8_t i = 0;
  int8_t highestNote = -1;
  while (i < array_length) {
    if(array[i] > highestNote ) {
      highestNote = array[i];
    }
    i++;
  }
  return highestNote;
}

int8_t findLastNote(int8_t *array, int8_t array_length)
{
  int8_t i = 0;
  int8_t lastNote = NO_NOTE;
  while (i < array_length) {
    if(array[i] != NO_NOTE ) {
      lastNote = array[i];
    }
    i++;
  }
  return lastNote;
}

// avoiding hanging notes with sequencers like the x0xb0x
void handleStop()
{
  doNoteOff();
  noteIsOnCount = 0;
}

void doNoteOn(uint8_t noteNumber)
{
  byte myNoteNumber = noteNumber;
  uint8_t note = codeLookup[noteNumber];
  PORTB = note;
  triggerOutState = HIGH;
  digitalWrite(TRIG_OUT, triggerOutState);
}

void doNoteOff()
{
  triggerOutState = LOW;
  digitalWrite(TRIG_OUT, LOW);
}

// 48hz - period of 20.833333333 ms
// need to use 10ms (as one-half of period)
void gateFlipTimerSetup()
{
  FlexiTimer2::set(10, 1.0/1000, gateFlip);
  FlexiTimer2::start();
}

void gateFlip()
{
  if(noteIsOnCount > 0) {
    triggerOutState = !triggerOutState;
    digitalWrite(TRIG_OUT, triggerOutState);
  }
}

void setup()
{
    // set PORT B to output mode
    DDRB = B11111111;
    PORTB = B00000000;
 
    // set up trigger out
    pinMode(TRIG_OUT, OUTPUT);
 
    gateFlipTimerSetup();
 
    MIDI.setHandleNoteOn(handleNoteOn);
    MIDI.setHandleNoteOff(handleNoteOff);
    MIDI.setHandleStop(handleStop);
    MIDI.begin(MIDI_INPUT_CHANNEL);
}

void loop()
{
    MIDI.read();  
}

I used a CNY13-4 as the optocoupler to connect MIDI to the Arduino this post just because that’s what I had to hand – for my final version I’d heed Olivier’s warning and use a 6N137. This is the relevant part from the MI Shruthi schematic:

Example MIDI to Arduino from MI Shruthi
The Wasp works off 5 volts so I’d recommend a 5v Arduino. It might work with a 3.3v Teensy 3.x, but you’d probably need a bunch of level shifting from 3.3v to 5v to get it work reliably. Also, out of laziness I’m using a PORTB command to write out to more than one pin at the same time, so you’d need to change this if you decided to use something other than an a Atmega 168/328-type Arduino.

For the connection from the Arduino to the Wasp you’ll need a 8 pin DIN plug. The connector that you’ll need to make, looking from the back from left to right, goes like this:

1 – trigger
2 – a (least significant bit)
3 – b
4 – c
5 – d
6 – e
7 – f (most significant bit)

…and don’t forget the ground at the bottom, that needs to connect to ground on your Arduino. The trigger pin will connect to pin 7 on the Arduino, with pins 2 – 7 being connected to Arduino digital inputs 8 – 13 respectively.

Looking at the Spider schematic, the note outputs should all be connected through 10K resistors, with the trigger connected through 47K. In my quick test I’ve gone direct, mostly because the keyboard is playing up and affecting the input.

The code started out being the most simple thing that would work, before I realised that I wanted to to do highest note priority, as with the original keyboard. Then I wanted it to do last note priority, so it got more complicated.

You will need to stop MIDI input when uploading the program to the Arduino because it uses the TX and RX pins as part of that process.

You could make it fancier, or try variations on the theme. It’d be good to do a USB MIDI > Wasp version for example – that should be easier with an Arduino Uno or Teensy. Or if you’ve modded your Wasp (or Jasper…) for filter CV input you could add a simple DAC to control the VCF from velocity or other MIDI controller.

I guess you could do a CV/gate > Wasp version as well if you were really keen to sequence it from your MC-4 or whatever. It’s just a matter of sampling the CV voltage when interrupted by a gate, and then quantising that to a note number.

It remembers the notes you’ve held down (up to a maximum of 8) so you could step through them on a clock (MIDI or analogue clock input) to make a simple arpeggiator.

Also also… if that Disklavier in the corner of the room just seems a bit unsatisfying, why not alter the code to make it into a MIDI output and control that grand piano from the Wasp’s capacitive touch keyboard? Endless fun, and so on.

9 Comments

DIY Spider sequencer


While I’m still working on other non-synth things… after I got the Wasp running last year I started to fiddle with making a Spider-ish sequencer out of a Teensy 2++ board, and this is as far as I got at the time.

Compared with the original track the oscillators should be an octave apart, but I never got the chance to re-record it before we moved house and the DIY Spider fell to the bottom of a box, somewhere in our loft. You get the idea, anyway.

The 7-pin DIN sockets on the top of the Wasp aren’t MIDI, but more like a weird digital CV/gate. .

The pitch is set in binary, with four pins determining the note within the octave, and the fifth and six pins defining the octave. There’s a table in the service manual which helps to work out the encoding. A 2 bit number has three states so you can get an extra octave from playing it externally.

The seventh pin is the “trig”, but it’s not just a simple on = note on, off = note off, rather it’s a square wave running at about 48Hz.

Everything works at 5v, which made it simpler to use the Teensy 2++ rather than the spare 3.3v Teensy 3.0 I had kicking around, and the extra I/O came in handy.

Compared to the original I added an LED display for showing the current step, and I made the transpose buttons latching. I almost got the real-time record working (with a silly piezo sounder for the metronome), but that always seems a bit pointless on these sorts of sequencers. Or maybe I’m just shit at playing in time.

I hadn’t got round to adding a clock input – from reading the Spider user manual, it’s not immediately clear how this might work in real-time playback mode – probably not very well. Or just very slowly.

The pin out of the Wasp DIN socket goes something like this

1 – f (MSB) – orange
2 – e – yellow
3 – d – green
4 – c – blue
5 – b – purple
6 – a (LSB) – grey
7 – trigger – white

… as if you are looking at the back of the socket (with the soldering) with the ground at the bottom, from left to right. Here’s a photo:

Wasp DIN socket

The sequencer is alright but to be honest it’s more fun to play from the keyboard… never thought I’d ever say that about any synth. I must be ill or something.

No Comments