dmusker
Published

Hacking Vox Valvetronix Amplifiers to accept Midi

The Vox AD60VT and AD120VT provide a huge range of amp models and effects. Emulating their foot controllers enables live Midi Control.

IntermediateWork in progress20 hours186
Hacking Vox Valvetronix Amplifiers to accept Midi

Things used in this project

Hardware components

etherCON Panel Mount RJ45 Receptacle with Feed Through Connection, 8 pin
etherCON Panel Mount RJ45 Receptacle with Feed Through Connection, 8 pin
×1
DIN Audio / Video Connector, 5 Contacts
DIN Audio / Video Connector, 5 Contacts
×2
Computer Cable, RJ45 Plug
Computer Cable, RJ45 Plug
×1
Arduino UNO
Arduino UNO
×1
4PDT STOMP FOOT / PEDAL SWITCH LATCHING
TaydaElectronics 4PDT STOMP FOOT / PEDAL SWITCH LATCHING
×6
LED (generic)
LED (generic)
×4

Software apps and online services

MIDI-OX
Cantabile
Arduino IDE
Arduino IDE

Story

Read more

Schematics

Fritzing Arduino-based Vox Valvetronix foot controller

Labelled Valvetronix Controller Schematic JPEG

Interface Box Schematic

VOXBUS to Midi Interface Box

Code

Valvetronix Midi Controller

Arduino
Arduino-based foot pedal to control Vox AD120VT, AD60VT and similar Vox amps, emulating the Vox VC12 and VC4 foot controllers
//This is a program intended to write control data to a Vox Valvetronix AD120VT - should work with other models too.
//This version is for Arduino Uno R3
//Copyright David Musker 2025.
//It can be freely used provided the author is acknowledged in the code.

#include <Bounce2.h> // needs the Bounce2 debouncing library

//CONSTANTS

//Constants for the port numbers

const uint8_t DelayPed_Pin = A1;
const uint8_t ParamPed_Pin = A4;
const uint8_t Volume_Pin = A2;
const uint8_t NumSwitches = 6;
const uint8_t LED_Pin[NumSwitches] = {8, 9, 10, 11, 12, 13}; //LEDs are on these pins
const uint8_t Switch_Pin[NumSwitches] = {2, 3, 4, 5, 6, 7}; //switches are on these pins
const uint8_t PC_Up_Pin = 6;
const uint8_t PC_Down_Pin = 7;


//Table of programs vs their logical variable effect states
//NOTE - would need to change if the amp patches are edited
const uint8_t FXState[32] = {B00000111, B00001111, B00001001, B00001100, B00001111, B00001101, B00000010, B00001101, B00000000, B00000001, B00001011, B00000001, B00001011, B00001001, B00001101, B00000101, B00001000, B00000101, B00000111, B00001011, B00001101, B00000001, B00001100, B00000101, B00001101, B00000101, B00001000, B00001101, B00001111, B00000011, B00001101, B00001001};

//VARIABLES
uint8_t Initial_PC = 0; //specify here which amp patch/program to start up on - initialised at zero here
uint8_t Current_PC; //counter variable to track the current program number
uint8_t Current_FX = 0; //counter variable to track the current effect state initialised all off
uint8_t Current_Vol = 127; //Initialise volume at full
uint8_t Prev_Vol = 127;
uint8_t Current_Param = 127; //Initialise FX parameter at full
uint8_t Prev_Param = 127; 
int Current_Del = 127; //Initialise delay length at max
int Prev_Del = 127;
unsigned long previousMillis = 0;
uint8_t tap_LSB = 0;
uint8_t tap_MSB = 0;
uint8_t tap_7bit = 0;
uint8_t ped_bit = 0;


//initialise pedal switch states at zero (off)
bool Pedal = 0; 
bool Modul = 0;
bool Delay = 0;
bool Reverb = 0;

//DECLARE FUNCTIONS
void PCChange();
void FXChange();
void LEDChange();
void VolChange();
void ParamChange();
void DelayChange();
//void DelChange();

//Instantiate Bounce objects
Bounce debouncer1 = Bounce(); 
Bounce debouncer2 = Bounce(); 
Bounce debouncer3 = Bounce(); 
Bounce debouncer4 = Bounce();
Bounce debouncer5 = Bounce();
Bounce debouncer6 = Bounce();
Bounce debouncer7 = Bounce();

void setup() {
  // setup the footswitch pins (and LED pins I guess)
  for (int i=0; i = (NumSwitches - 1); i++){   
    pinMode(Switch_Pin[i], INPUT_PULLUP);
    pinMode(LED_Pin[i], OUTPUT);
  }
  
  //setup the external pedal input pins
  pinMode(A3, OUTPUT); 
  digitalWrite(A3, HIGH); //output 5V - actually better to connect to the 5V line
  pinMode(A5, OUTPUT); 
  digitalWrite(A5, HIGH); //output 5V - actually better to connect to the 5V line
  pinMode(Volume_Pin, INPUT);
  pinMode(ParamPed_Pin, INPUT);
  pinMode(DelayPed_Pin, INPUT_PULLUP); 
      
  // After setting up the button, setup the Bounce instances
  debouncer1.attach(Switch_Pin[0]);
  debouncer1.interval(20); // interval in ms
  debouncer2.attach(Switch_Pin[1]);
  debouncer2.interval(20); 
  debouncer3.attach(Switch_Pin[2]);
  debouncer3.interval(20); 
  debouncer4.attach(Switch_Pin[3]);
  debouncer4.interval(20); 

  //Program Changes
  debouncer5.attach(Switch_Pin[4]);
  debouncer5.interval(20); 
  debouncer6.attach(Switch_Pin[5]);
  debouncer6.interval(20);
 
  //tap tempo
  debouncer7.attach(DelayPed_Pin);
  debouncer7.interval(20); 
 
  Serial.begin(31250); //Set MIDI baud rate
  //serial.begin(9600); just for test purposes
  
  //Initialise variables
  Current_PC = Initial_PC;
  Current_FX = FXState[(Current_PC)];
  Pedal = bitRead(Current_FX, 0);
  Modul = bitRead(Current_FX, 1);
  Delay = bitRead(Current_FX, 2);
  Reverb = bitRead(Current_FX, 3);
  delay(500); //wait for amp to warm up before sending anything
  PCChange();
  LEDChange();
}

void loop() {

//Check for changes and transmit
debouncer1.update();
if (debouncer1.rose() || debouncer6.fell()) {
  Pedal = !Pedal;
  ped_bit = 0;
  bitWrite(Current_FX, ped_bit, Pedal);
  FXChange();
  LEDChange();
  }
  
debouncer2.update();
if (debouncer2.rose() || debouncer6.fell()) {
  Modul = !Modul;
  ped_bit = 1;
  bitWrite(Current_FX, ped_bit, Modul);
  FXChange();
  LEDChange();
  }
  
debouncer3.update();
if (debouncer3.rose() || debouncer6.fell()) {
  Delay = !Delay;
  ped_bit = 2;
  bitWrite(Current_FX, ped_bit, Delay);
  FXChange();
  LEDChange();
  }
  
debouncer4.update();
if (debouncer4.rose() || debouncer6.fell()) {
  Reverb = !Reverb;
  ped_bit = 3;
  bitWrite(Current_FX, ped_bit, Reverb);
  FXChange();
  LEDChange();
  }

debouncer5.update(); //increment patch number
if (debouncer5.rose() || debouncer5.fell()) {
  Current_PC++;
  if (Current_PC == 32){
    Current_PC = 0;//rollover if you reach max
  }
  Current_FX = FXState[(Current_PC)];
  Pedal = bitRead(Current_FX, 0);
  Modul = bitRead(Current_FX, 1);
  Delay = bitRead(Current_FX, 2);
  Reverb = bitRead(Current_FX, 3);
  PCChange();
  LEDChange();
  // here transmit ped states to pc  FXChange();
  }

debouncer6.update(); //decrement patch number
if (debouncer6.rose() || debouncer6.fell()) {
   if (Current_PC == 0){
    Current_PC = 32;//rollover if you reach min
    }
  Current_PC--;
  Current_FX = FXState[(Current_PC)];
  Pedal = bitRead(Current_FX, 0);
  Modul = bitRead(Current_FX, 1);
  Delay = bitRead(Current_FX, 2);
  Reverb = bitRead(Current_FX, 3);
  PCChange();
  LEDChange();
  // here transmit ped states to pc  FXChange();
  }


//this is if nonlatching footswitch is used for delay
debouncer7.update(); //check for tap tempo
if (debouncer7.fell()){
  unsigned long tap_time = (millis() - previousMillis);//time since last switch down
  if (tap_time < 5000) { // this will be the second stomp within 5 secs so it starts the clock
  if (tap_time > 1521) {tap_time == 1521;}//cap at max delay
  uint16_t tap_short = (uint16_t)tap_time;
  tap_LSB = tap_short & 127;
  tap_MSB = (tap_short >>7) & 127;
  tap_7bit = (tap_short >> 4);
  DelayChange();
  }
  previousMillis = millis();//restart timer 
}
/*
//this is if CV pedal is used for delay
Prev_Del = Current_ Del;
Current_Del = analogRead(DelayPed_Pin);
if (Current_Del != Prev_Del){
  tap_7bit = (tap_short >> 3);
  //convert 10 bit read to tap_MSB and tap_LSB
  DelayChange();
}
*/

int b = analogRead(Volume_Pin) >> 3;
Current_Vol = (uint8_t)b;
if ((Current_Vol - Prev_Vol) > 3) {
  VolChange();
  Prev_Vol = Current_Vol;
}

int d = analogRead(ParamPed_Pin) >> 3;
Current_Param = (uint8_t)d;
if ((Current_Param - Prev_Param) > 3) {
  ParamChange();
  Prev_Param = Current_Param;
}

}
  
void PCChange() { //send Patch Change message
  Serial.write(196); //send program change command on channel 5
  Serial.write(Current_PC); //send new program number
  //insert send to PC via USB here
  return;
}

void FXChange() { //send FX Pedal State change message
  Serial.write(176); //send CC indicator channel 0, for the amp
  Serial.write(95); //send control change number
  Serial.write(Current_FX); //send FX pedal config as CC data
  Serial.write(180); //send CC indicator channel 5, for the PC
  Serial.write(80 + ped_bit); //send CC dependent on pedal pressed
  Serial.write(127);
  //insert send to PC via USB here;
  return;
}

void LEDChange() { //update LEDs
   for (int i=0; i <= 3; i++){   
    digitalWrite(LED_Pin[i], bitRead(Current_FX, i)); //set Pedal LED
   }
  return;
}

void VolChange() {
  //Controller hex B0 11 is volume (0-127 for the data values)
  Serial.write(176); //send CC indicator on channel 0, for the amp
  Serial.write(11); //send control change number
  Serial.write(Current_Vol); //send control change number
  Serial.write(180); //send CC on Channel 5, for the PC
  Serial.write(7); 
  Serial.write(Current_Vol);
  //insert send to PC via USB here
  return;
}

void ParamChange() {
  //Controller 1 is param (0-127 for the data values)
  Serial.write(176); //send CC indicator on channel 0 for the amp
  Serial.write(1); //send control change number
  Serial.write(Current_Param); //send control change number
  Serial.write(180); //send CC on Channel 5 for the PC
  Serial.write(4);
  Serial.write(Current_Param);
  //insert send to PC via USB here
  return;
}


void DelayChange() {
//y = map(Current_Del, 0, 1023, 0, 1521);
//Controller hex B0 11 is volume (0-127 for the data values)
  Serial.write(224); //pitch wheel, used by Vox for delay length
  Serial.write(tap_LSB); //send first data byte
  Serial.write(tap_MSB); //send second data byte
  Serial.write(180); //send CC on Channel 5
  Serial.write(79);
  Serial.write((uint8_t)tap_7bit);
  //insert send to PC via USB here
  return;
}

Credits

dmusker
4 projects • 3 followers
Contact
Thanks to Kai Zimmermann and other Vox users and Rich 'Voxman' Birch.

Comments

Please log in or sign up to comment.