[queued] Axis 49 (C-Thru Music) remap and aftertouch mod with a teensy LC

lokki

Well-known member
hi there,

so i played on an axis-49 midi controller for the last few years, mostly with my laptop. this now discontinued product is very compact and has 98 keys!! https://www.c-thru-music.com/cgi/?page=prod_axis-49
i rearranged the keycaps and remapped the keys in software to play it with a wicki-hayden layout. so far so good.

lately i wanted to have more expression and control over the sound, and also wanted to use hardware synths exclusively. so i attached some FSR strips to the bottom of the axis case, and searched for a solution to integrate those in the MIDI stream and remap the axis before it hits my synths.
IMG_20210618_183804.jpg

my first try involved a teensy 4 acting as a usb hub and sending the midi-stream down the usb-client port (to hardware synths usb-host port). unfortunately the teensy 4 client midi port is somehow not usb 1 compatible, so my hardware synths don't recognise it as midi input.

upon looking at the PCB of the axis-49 i realised that it has only two microcontrollers on board, the DSP and a PIC to handle USB-MIDI.
IMG_20210618_183549.jpg

since i knew the axis 49 has a bigger brother with din-midi output (the axis64) and the source code of the axis-49 firmware is luckily available, i quickly found out the DSP-->PIC communication was happening over a serial line, and was essentially MIDI already. i hooked up my oscilloscope to Pin18 of the PIC (the Serial RX Pin) and sure enough i got some nice activity when hitting keys on the axis. next i soldered a cable to PIN 18 (see jpeg above) and connected that to the teensy-LC RX pin and wrote a little serial-to usb MIDI converter Sketch. i powered it on, and nothing happened...

PIC3.jpg

upon further investigation i found out, that while they use MIDI, they don't use the MIDI Baudrate, instead they run the communication at 57600 baud. changed that in the teensy sketch, and boom, it worked!! attached the fsr strips to an analog input and also added a softpot for pitchbend, and it all worked really well.

for more convenience, i cut the two traces from the PIC to d+ and d- on the usb connector of the axis and connected the teensy lc 5v GND d+ and d- to the axis port. that way it is all contained in the original case... i also changed the MIDI name of the Teensy to Axis-49 WH, cool!

PIC4.jpg

here is the code, not a final version, but working properly:

main project file:
Code:
/**-------------------hardware mods-----------------
 1. connect teensy-lc rx pin (0) to PIC pin 18 (rx)
 2. connect teensy-lc ground to axis49 ground
 3. remove the two SMD inductors from pin 1 and 3 of the USB connector on axis49
 4. connect 5v D+ and D- from AXIS 49 USB port to the corresponding teensy-lc PINS
 5. hook up other sensors you wish to inlcude, enjoy :-)
**/

#include <MIDI.h>
//table to remap selfless axis 49 mode to wicki hayden
uint8_t reMap[99] {  
  18, 20, 22, 24, 26, 28, 30, 25, 27, 29, 31, 33, 35, 37, 
  30, 32, 34, 36, 38, 40, 42, 37, 39, 41, 43, 45, 47, 49, 
  42, 44, 46, 48, 50, 52, 54, 49, 51, 53, 55, 57, 59, 61, 
  54, 56, 58, 60, 62, 64, 66, 61, 63, 65, 67, 69, 71, 73, 
  68, 70, 72, 74, 76, 78, 80, 73, 75, 77, 79, 81, 83, 85, 
  80, 82, 84, 86, 88, 90, 92, 85, 87, 89, 91, 93, 95, 97, 
  92, 94, 96, 98, 100, 102, 104, 97, 99, 101, 103, 105, 107, 109,  
  };
//do not output aftertouch on noteOn
bool noteEvent = 0;

uint8_t afterTouch = 0;
uint8_t oldTouch = 0;

int softPot = 0;
int oldPot = 0;


MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI1);

void setup() {
  MIDI1.begin(MIDI_CHANNEL_OMNI);
  //turns out "midi" on axis49 uses 57600 baud from the DSP to the PIC
  Serial1.begin(57600);
  //setup FSR input pin
  pinMode(A0, INPUT);
  pinMode(A1, INPUT);
}

void loop() {
  //read serial midi from axis and send it out remapped via usb
    if (MIDI1.read()) {
    // get a MIDI IN1 (Serial) message
    byte type = MIDI1.getType();
    byte channel = MIDI1.getChannel();
    byte data1 = reMap[MIDI1.getData1() -1];
    byte data2 = MIDI1.getData2();
    // Normal messages, simply forward the data to the usbMIDI.send()
    usbMIDI.send(type, data1, data2, channel, 0);   
    noteEvent = 1;
    }
    
    if (!noteEvent) {
   int fsrRead = analogRead(A0);
   fsrRead = map(fsrRead,1000,600,0,127);
   afterTouch = constrain(fsrRead,0,127);
   if (afterTouch != oldTouch) {
    oldTouch = afterTouch;
    usbMIDI.sendAfterTouch(afterTouch,1);
       }
//softpot is connected to A1, and there is also a voltage divider (10k from gnd and 3.3v) connected to A1 
//to get a center reading when the softpot is not touched

    int softPotRead = analogRead(A1);
   int temp = softPotRead;
   softPotRead = map(softPotRead,55,958,-8192,8191);
 

   softPot = constrain(softPotRead,-8192,8191);
   //if sensor is not touched, output pitchbend of 0
   if ((temp > 500) && (temp < 520)) softPot = 0;
   if (softPot != oldPot) {
    usbMIDI.sendPitchBend(softPot,1);
    oldPot = softPot; 
       }  
       
    }
    
  if (noteEvent) noteEvent = 0;

}

name.c to rename the midi serial nr. and displayed name:
Code:
// To give your project a unique name, this code must be
// placed into a .c file (its own tab).  It can not be in
// a .cpp file or your main sketch (the .ino file).

#include "usb_names.h"

// Edit these lines to create your own name.  The length must
// match the number of characters in your custom name.

#define MANUFACTURER_NAME   {'C','-','T','H','R','U'}
#define MANUFACTURER_NAME_LEN  6
#define MIDI_NAME   {'A','X','I','S','4','9','W','H',}
#define MIDI_NAME_LEN  8
#define SERIAL_NUM {'1','2','3','4','5','6','7','8','9','a','b','c'}
#define SERIAL_NUM_LEN 12

// Do not change this part.  This exact format is required by USB.

struct usb_string_descriptor_struct usb_string_manufacturer_name = {
        2 + MANUFACTURER_NAME_LEN * 2,
        3,
        MANUFACTURER_NAME
};

struct usb_string_descriptor_struct usb_string_product_name = {
        2 + MIDI_NAME_LEN * 2,
        3,
        MIDI_NAME
};


struct usb_string_descriptor_struct usb_string_serial_number = {
        2 + SERIAL_NUM_LEN * 2,
        3,
        SERIAL_NUM
};
 
Back
Top