https://os.mbed.com/handbook/MIDI-Controller-for-Guitar-Players

MIDI Controller for Guitar Players

Team Members: John Jones, John Mikrut /media/uploads/jcm777/img_5392.jpg

Introduction

Most guitar players want to add sound effects to their guitar's signal chain, however new amplifiers or effects pedals can be expensive, especially for us humble bedroom players. Guitar players can use software like the Guitar Rig 5 to emulate countless guitar pedals and amplifiers. And although the Guitar Rig 5 demo is great for experimenting with effects and virtual amplifiers, it is difficult to manipulate mid-song while simultaneously playing the instrument. MIDI controllers can solve this problem. Guitar Rig 5 along with most other musician-oriented software applications are designed to respond to MIDI messages to interact with a MIDI controller. We created a MIDI controller that guitar players can use with their feet. As far as we know at the time of writing this documentation, this is the first DIY midi controller that utilizes two microprocessors (one is effectively an analog input mux).

Parts Used

  • 1 x Mbed

250

  • 1 x Arduino Mini Pro

250

  • 1 x Mini usb breakout

250

  • 4 x Stomp Switches

250

  • 4 x Knob potentiometers

250

  • 3 x Sliding potentiometers(Faders)

250

  • 4 x LEDs

250

Description

A MIDI foot controller designed for guitar players to use with amp and effects modeling software like Guitar Rig 5. The board will utilize the mbed and MIDIUSB library to interface with a computer running Guitar Rig 5. The user inputs will be expanded using an Arduino mini pro connected to the mbed through an SPI bus.

1000

What is MIDI:

MIDI (Musical Instrument Digital Interface) is a serial communications standard designed for music applications. DAWs (Digital Audio Workstations) used to mix and master music tracks or amp modeling software for guitar players are designed to receive MIDI messages from controllers and interact with the application accordingly. An online MIDI keyboard is available at http://www.caseyrule.com/projects/piano/ and the HelloWorld for MIDIUSB is available at https://os.mbed.com/cookbook/USBMIDI. The hello world steps through notes 48 to 84 which is standardized to the notes in the table below. The standard for pre-programmed notes values and switches. Guitar Rig does not use the midi messages not to signify notes, but pairs these note commands to virtual knobs and switches in the application. More information on MIDI is available at at https://learn.sparkfun.com/tutorials/midi-tutorial/all.

/media/uploads/jcm777/midi_notes_table.jpg

The standard message packet consist of a status bye and at at least one data byte. The three message types in this project are:

/media/uploads/jcm777/midi_message_table.png

Setting Up the Project

Arduino Mini Pro

Most of the following info is for people who have little to no experience working with arduino. There are also details on setting up SPI on the arduino side.

You can download the arduino IDE from the following link https://www.arduino.cc/en/Main/OldSoftwareReleases.

The verify button on the Arduino IDE compiles your code and can be identified by the checkmark in the top right corner of the GUI. The upload button automatically puts your program onto the pro mini and can also be identified by an arrow pointing to the right. Just remember to reset the pro mini after uploading a new program.

600

Arduino syntax is pseudo-C, so writing the code should be intuitive. If not, the Arduino APIs can be found online. For this project, the SPI master example code and API were very useful. Finding documentation for various Arduino APIs is similar to the mbed handbook. The void setup() function only runs once and is used to initialize variables and setup devices. The void loop() function is similar to while(1) on the mbed and is where your main loop runs.

You can test your mini pro by selecting a basic blinky program from drop down box shown in the image:

This https://www.arduino.cc/en/Tutorial/DigitalPotControl is a good example of how to setup the Arduino as a master. Another good resource: http://tronixstuff.com/2011/05/13/tutorial-arduino-and-the-spi-bus/.

Arduino Pro Mini Pin Layout

850

Guitar Rig 5

Once you have the Arduino IDE set up, you can move on to Guitar Rig 5.

Download mbed code below:

The download is available at https://www.native-instruments.com/en/products/komplete/guitar/guitar-rig-5-pro/downloads/ step through the installer.

Check your devices in the preference. You most likely will need to install ASIO4ALL from http://www.asio4all.org/.

Once installed open Guitar Rig and go to file->Audio and MIDI Settings

1021

Click on the “ASIO Config” button and make sure only your input from instrument and output to the speaker are selected.

1000

You'll then need to go to the MIDI tab and make sure that your new MIDI controller is turned on in Guitar Rig 5.

1000

You should be able to rock on from here:

Implementation

Values read from the potentiometers are transmitted to the mbed through an SPI Bus. A 3-bit bus is being used to determine which potentiometer is being changed. SPISlave class was used to configure the mbed as a slave. Luckily for us, there was a MIDI controller library for mbed already written. We used the library to send commands to the Guitar Rig 5 software from the mbed. The mini pro was used to take in the analog data from the potentiometers and push them to the mbed. The mbed does not have enough analog inputs for the number of potentiometers required, so the mini pro was a good option due to its extra analog input pins.

1500

SPI

We used a serial peripheral interface bus to transfer the potentiometer data from the Arduino pro mini to the mbed. The pro mini was acting as the master and the mbed as the slave. We chose to do our implementation in this way because the mbed does not have enough analog inputs to support all of the analog devices we were trying to use. When attempting to implement a master-slave configuration like this, be sure to add a large enough delay after the data is transferred on the Arduino side. Not having enough delay will cause the data to be cut off too early resulting in incorrect readings from the potentiometer. To allow for the mbed to be configured as a slave, we used the SPI slave class. This is included in the mbed.h library.

Mbed's Hello World example (contains a link to the SPI Wikipedia page which is helpful):

https://os.mbed.com/handbook/SPI

Mbed Spi slave example:

https://docs.mbed.com/docs/mbed-os-api-reference/en/5.1/APIs/interfaces/digital/SPISlave/

Mbed Code

main.cpp

#include "mbed.h"
#include "USBMIDI.h"
#include "PinDetect.h"

#define SPI_CC_START_NUM 102
#define MBED_CC_START_NUM 20
#define MBED_SWITCH_START 70

USBMIDI midi;

//Serial pc(USBTX,USBRX);

SPISlave device(p5, p6, p7, p8);

BusIn pot_id_bus(p10, p11, p12);
int pot_id;

DigitalOut led1(p21);
DigitalOut led2(p22);
DigitalOut led3(p23);
DigitalOut led4(p24);

PinDetect  pinD1(p25);
PinDetect  pinD2(p26);
PinDetect  pinD3(p27);
PinDetect  pinD4(p28);

AnalogIn analog1(p20);
AnalogIn analog2(p19);
AnalogIn analog3(p18);

// foot switches
bool sw1 = 0;
bool sw2 = 0;
bool sw3 = 0;
bool sw4 = 0;
 

// continuous controlls
int cc1 = 0;
int cc2 = 0;
int cc3 = 0;
int last_cc1 = 0;
int last_cc2 = 0;
int last_cc3 = 0;

uint8_t v; // spi value read

uint32_t count = 0; // spi receive count for debug purposes

void key1Pressed( void )
{
    sw1 = !sw1;
    if(sw1)
        midi.write(MIDIMessage::NoteOn(MBED_SWITCH_START));
    else
        midi.write(MIDIMessage::NoteOff(MBED_SWITCH_START));
}
//
void key2Pressed( void )
{
    sw2 = !sw2;
    if(sw2)
        midi.write(MIDIMessage::NoteOn(MBED_SWITCH_START+1));
    else
        midi.write(MIDIMessage::NoteOff(MBED_SWITCH_START+1));
}

void key3Pressed( void )
{
    sw3 = !sw3;
    if(sw3)
        midi.write(MIDIMessage::NoteOn(MBED_SWITCH_START+2));
    else
        midi.write(MIDIMessage::NoteOff(MBED_SWITCH_START+2));
}

void key4Pressed( void )
{
    sw4 = !sw4;
    if(sw4)
        midi.write(MIDIMessage::NoteOn(MBED_SWITCH_START+3));
    else
        midi.write(MIDIMessage::NoteOff(MBED_SWITCH_START+3));
}

int main()
{
//    pc.printf("Hello World\n\r");
    
    // declare digital pins detects
    pinD1.mode(PullDown);
    pinD1.attach_asserted(&key1Pressed);
    pinD2.mode(PullDown);
    pinD2.attach_asserted(&key2Pressed);
    pinD3.mode(PullDown);
    pinD3.attach_asserted(&key3Pressed);
    pinD4.mode(PullDown);
    pinD4.attach_asserted(&key4Pressed);
    // digital pin detects frequencies
    pinD1.setSampleFrequency(); // Defaults to 20ms.
    pinD2.setSampleFrequency(); // Defaults to 20ms.
    pinD3.setSampleFrequency(); // Defaults to 20ms.
    pinD4.setSampleFrequency(); // Defaults to 20ms.

    device.format(8, 0); // 16 bits per message, SPI_MODE 0
    device.frequency(16e6);
    device.reply(0x01);         // Prime SPI with first reply
    device.reply(0x01);         // Prime SPI with first reply again

    while (1) {
        // set pedal led's
        led1 = sw1;
        led2 = sw2;
        led3 = sw3;
        led4 = sw4;

        // mbed cc's
        cc1 = (int)(127*analog1.read());
        cc2 = (int)(127*analog2.read());
        cc3 = (int)(127*analog3.read());

        if(cc1 != last_cc1) {
            last_cc1 = cc1;
//            pc.printf("cc1: %d\n\r",cc1);
            midi.write(MIDIMessage::ControlChange(MBED_CC_START_NUM+0,cc1));
        }
        if(cc2 != last_cc2) {
            last_cc2 = cc2;
//            pc.printf("cc2: %d\n\r",cc2);
            midi.write(MIDIMessage::ControlChange(MBED_CC_START_NUM+1,cc2));
        }
        if(cc3 != last_cc3) {
            last_cc3 = cc3;
//            pc.printf("cc3: %d\n\r",cc3);
            midi.write(MIDIMessage::ControlChange(MBED_CC_START_NUM+2,cc3));
        }
//
//
        while(device.receive()) {
            v = device.read();   // Read byte from master
            switch(pot_id_bus) {
                case 0x0:
                    pot_id = 0;
                    break;
                case 0x1:
                    pot_id = 1;
                    break;
                case 0x2:
                    pot_id = 2;
                    break;
                case 0x3:
                    pot_id = 3;
                    break;
                case 0x4:
                    pot_id = 4;
                    break;
            }
            //pc.printf("received value: %d\n\r", v);
//            pc.printf("id: %d\n\r",pot_id);
//            ++count;
//            pc.printf("count: %d\n\r", count);
            midi.write(MIDIMessage::ControlChange(SPI_CC_START_NUM+pot_id,v));
            device.reply((v + 1) % 0x100); // Make this the next reply
        }

        // wait to allow PinDetect to have some cpu time
        wait(.1);
    }
}
//}

Arduino Code

Arduino

#include <SPI.h>

#define NUM_POTS 4 // number of potentiometers being read from the arduino pro mini
#define MAP_SCALAR 1020 // max reading from pot, ideally this is 1024 on arduino but varies with pot quallity
#define DEL 180

SPISettings mySPISettings(16e6, MSBFIRST, SPI_MODE0); // (clock freq, MSBF/LSBF, SPI MODE)

const int ss = 10; // using digital pin 10 for SPI slave select

int potValue[NUM_POTS]; //Holds current analog control values
int last_potValue[NUM_POTS]; //Holds previous analog control values

unsigned int count;

byte pots[8]; //Stores pin names of analog channels

void setup()
{
//  Serial.begin(9600);      // open the serial port at 9600 bps:

  pinMode(ss, OUTPUT); // we use this for SS pin
  SPI.begin(); // wake up the SPI bus.

  pinMode(7, OUTPUT);
  pinMode(8, OUTPUT);
  pinMode(9, OUTPUT);

  pots[0] = A0;
  pots[1] = A1;
  pots[2] = A2;
  pots[3] = A3;
  pots[4] = A4;
  pots[5] = A5;
  pots[6] = A6;
  pots[7] = A7;
}

void loop()
{
  // for each pot
  for (int i = 0; i < NUM_POTS; ++i) {

    // read analog in and scale to 00-127]
    potValue[i] = analog2control(analogRead(pots[i]));

    // push data to mbed thru SPI
      if (potValue[i] != last_potValue[i]){
      spi_push_value(i, (unsigned int)potValue[i]);
      last_potValue[i] = potValue[i];
    }
  }
}


void spi_push_value(int id, unsigned int value)
{
  switch (id) {
    case 0:
      digitalWrite(7, LOW);
      digitalWrite(8, LOW);
      digitalWrite(9, LOW);
      break;
    case 1:
      digitalWrite(7, HIGH);
      digitalWrite(8, LOW);
      digitalWrite(9, LOW);
      break;
    case 2:
      digitalWrite(7, LOW);
      digitalWrite(8, HIGH);
      digitalWrite(9, LOW);
      break;
    case 3:
      digitalWrite(7, HIGH);
      digitalWrite(8, HIGH);
      digitalWrite(9, LOW);
      break;
      break;
    case 4:
      digitalWrite(7, LOW);
      digitalWrite(8, LOW);
      digitalWrite(9, HIGH);
      break;
  }
  SPI.beginTransaction(mySPISettings);
  digitalWrite(ss, LOW);
  SPI.transfer(value);
  digitalWrite(ss, HIGH);
  SPI.endTransaction();
  delay(DEL);
}

int analog2control(int in_value)
{
  if (in_value > MAP_SCALAR)
    return 127;
  else
    return (int) (in_value * (127.0 / MAP_SCALAR));
}

Wiring

Arduino Mini ProMbed
VCCVU
gndgnd
p11p5
p12p6
p13p7
p10p8
p7p10
p8p11
p9p12
MbedMini Usb Breakout
VUVCC
gndgnd
D-D-
D+D+

Schematic and Block Diagram

850

Future Improvements

/media/uploads/jcm777/future_improvement.png

/media/uploads/jcm777/future_improvement2.png

Some improvements that could be made to the project are converting the entire system into a single windows IoT device, adding an onboard LCD screen that would display information about what is going on in whatever software is being used, and adding a home studio sound processing card. Converting the MIDI controller to an IoT device would make the whole system more compact by eliminating the need for a laptop and a speaker. The LCD would be a part of the interface for the IoT device. It would display information about what software function each analog device controls. The sound processing card would be implemented to improve overall sound quality of the MIDI controller.


Please log in to post comments.