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