Tuesday 28 January 2014

Arduino MIDI Synth No.3

And moving rapidly on with following the Gordophone's work, Marmonizer v2 which adds chords.

This is really neat, taking the MIDI in note, a chord is formed from adding notes below the pitch to form a chord. In this case the drop is -4 and -9 from the original note (quoting Gordophone this this is a major triad in second inversion - a 6/4 chord).

Here's my code version:

///////////////////////////////////////////////////////////////////////
//
// HondrouMidifier
//
// Initial testing using Gordon Good's Marmonizer as a base
// http://gordophone.blogspot.co.uk/2010/01/marmonizer-v2.html
// Gordon Good (velo27 <at> yahoo <dot> com)
//
// This uses the MIDI library from FortySevenEffects
// http://fortyseveneffects.github.io/arduino_midi_library/
//
// 
// implements a simple MIDI harmonizer. The harmonizarion ia simple; the
// input note is the top voice of a triad in second inversion (e.g. if the
// input note is E4, then the voices below are G3 and C4. For discussion of
// what those note names mean, see http://en.wikipedia.org/wiki/C_%28musical_note%29
// The transposition pot is retained.
//
// Limitations: the algorithm always "maps down" so we may roll notes off the
// deep end of the MIDI spec.

#include <SoftwareSerial.h>
#include <Midi.h>

SoftwareSerial MusicSerial(2, 3); //Soft TX on 3, we don't use RX in this code

// defines for MIDI Shield components only
#define KNOB1  0
#define KNOB2  1

#define STAT1  7
#define STAT2  6

// Music shield pins - both digital
#define MUSIC_PIN_MIDI_IN  3 // soft serial TX -> VS1053 RX
#define MUSIC_PIN_RESET  4   // VS1053 RESET

#define HARM_NOTE_1_OFFSET -4
#define HARM_NOTE_2_OFFSET -9


int trPotPin = KNOB1; // Analog pin for reading the transposition potentiometer
int ledPin = STAT1;  // LED pin to blink for debugging

int transposition = 0; // number of semitones to transpose (negative = transpose down)

void noteOnCallback(byte channel, byte pitch, byte velocity) 
{  
  digitalWrite(ledPin, HIGH);

  noteOn(channel, pitch + transposition, velocity);
  noteOn(channel, pitch + transposition + HARM_NOTE_1_OFFSET, velocity);
  noteOn(channel, pitch + transposition + HARM_NOTE_2_OFFSET, velocity);
}

void noteOffCallback(byte channel, byte pitch, byte velocity) 
{
  digitalWrite(ledPin, LOW);
  
  noteOff(channel, pitch + transposition, velocity);  
  noteOff(channel, pitch + transposition + HARM_NOTE_1_OFFSET, velocity);  
  noteOff(channel, pitch + transposition + HARM_NOTE_2_OFFSET, velocity);  
}

/////////////////////////////////////////////////////////////////////////////
//  Setup routine             
/////////////////////////////////////////////////////////////////////////////
void setup() 
{
  pinMode(STAT1,OUTPUT);   
  pinMode(STAT2,OUTPUT);  
  
  // Initiate MIDI communications, listen to all channels
  MIDI.begin(MIDI_CHANNEL_OMNI);    
  
  // Connect the HandleNoteOn function to the library, so it is called upon reception of 
  // a NoteOn.
  MIDI.setHandleNoteOn(noteOnCallback);    
  MIDI.setHandleNoteOff(noteOffCallback);  

  pinMode(trPotPin, INPUT);
  digitalWrite(trPotPin, HIGH);
  
  // VS1053
  //start serial with midi baudrate 31250
  MusicSerial.begin(31250);      
  
  // Reset the VS1053
  pinMode(MUSIC_PIN_RESET, OUTPUT);
  digitalWrite(MUSIC_PIN_RESET, LOW);
  delay(100);
  digitalWrite(MUSIC_PIN_RESET, HIGH);
  delay(100);
  talkMIDI(0xB0, 0x07, 120); //0xB0 is channel message, set channel volume to near max (127)
  
  digitalWrite(STAT1,LOW);
  digitalWrite(STAT2,LOW);
}

/////////////////////////////////////////////////////////////////////////////
//  Main loop             
/////////////////////////////////////////////////////////////////////////////

void loop() 
{
    // Read the transposition pot, and map the value to a + or - one octave transposition
    transposition = map(analogRead(trPotPin), 0, 1023, -12, 12);
    
    // Call MIDI.read the fastest you can for real-time performance.
    MIDI.read();
  
    // There is no need to check if there are messages incoming if they are bound to a Callback 
    // function.
}

/////////////////////////////////////////////////////////////////////////////
//  Functions             
/////////////////////////////////////////////////////////////////////////////

//Send a MIDI note-on message.  Like pressing a piano key
//channel ranges from 0-15
void noteOn(byte channel, byte note, byte attack_velocity) 
{
  talkMIDI( (0x90 | channel), note, attack_velocity);
}

//Send a MIDI note-off message.  Like releasing a piano key
void noteOff(byte channel, byte note, byte release_velocity) 
{
  talkMIDI( (0x80 | channel), note, release_velocity);
}

//Plays a MIDI note. Doesn't check to see that cmd is greater than 127, or that data values are less than 127
void talkMIDI(byte cmd, byte data1, byte data2) 
{
  digitalWrite(STAT1,HIGH);

  MusicSerial.write(cmd);
  MusicSerial.write(data1);

  //Some commands only have one data byte. All cmds less than 0xBn have 2 data bytes 
  //(sort of: http://253.ccarh.org/handout/midiprotocol/)
  if( (cmd & 0xF0) <= 0xB0)
  {
    MusicSerial.write(data2);
  }
  digitalWrite(STAT1,LOW);
}

No comments:

Post a Comment