Introduction: MIDI Controlled Analog FM Synthesizer

About: Music Technology at NYU Steinhardt is a leading and internationally recognized program in music, sound and audio technology. Students prepare for successful careers in sound engineering, computer music, audio-…

This circuit and Arduino sketch comprise an advanced project for face melting sounds from an analog source controlled via MIDI.

The required parts include:
•  Solderless breadboard
•  Lots of hookup wire
•  Arduino
•  AD5206
•  2x 7555 Timers
•  2x 1µF capacitors
•  100µF capacitor
•  1kΩ resistor
•  220Ω resistor
•  LED
•  Audio output connections

• additional capacitors and resistors for filters if needed/desired.

You will need means of generating and transmitting MIDI Continuous control messages.
If doing this with a hardware MIDI interface, you will need the a MIDI (5pin din) jack and an additional 220Ω resistor.
See http://arduino.cc/en/Tutorial/Midi for more on MIDI connections with the Arduino

Step 1: Getting Started With SPI and Digital Potentiometer


If you are feeling ambitious or are fairly well experienced with electronics, you may be able to skip this step.

Check out the Arduino Digital pot tutorial and use the example code provided with the program.
http://arduino.cc/en/Tutorial/SPIDigitalPot

When comfortable with LEDs.  
Add a 7555 circuit to the mix and use the digital potentiometer to control the volume of the output of the 7555.
If 7555 astable oscillators are giving you trouble, please check out some helpful tutorials.
https://www.instructables.com/id/A-fairly-simple-555-tester/
http://www.youtube.com/watch?v=GnieHmmEChE

See the schematic here:
https://www.circuitlab.com/circuit/9c989j/digipot-basics/

The astable oscillator is omitted from the schematic to try to keep things simple on the page.

Step 2: Testing the Basic Circuit

If you are well experience with Arduino and basic electronics you may want to skip ahead.

With the circuit built, run the SPI digital potentiometer in your Arduino IDE.

When wired correctly the LED and volume of the audio signal should jump to they maximum and then fade out repeatedly.


Step 3: Connecting MIDI

For this step, you will need one of several senarious working for you.

1. You will have a hardware MIDI device that outputs spec. MIDI.
2. You will be comfortable programming MIDI firmware onto the Arduino (HIDUINO) so that the Arduino can receive MIDI messages from your DAW.
3. You will have a MIDI program that can receive MIDI and pass the messages as serial to Arduino (PD/Max).
     (this will require changing the baud in the sketch to match that of the software)



For any of the above, you will need to be able to send continuous control messages on MIDI channel 1, via controllers number 1 through 6.

Step 4: Play Time

With a MIDI controller connected to your Arduino and Audio connected to the output of the Digital Potentiometer, run the code (pasted/linked below) and you are ready to rock.

Here is a demonstation video of what my build sounded like when using a battery powered audio amplifier:  http://youtu.be/JzKzRq4mcdk



//____________________________________________________
/*

  write_pot(0, 0);  // 7555car Threshold,  
  write_pot(1, 0);  // 7555car Discharge,
  write_pot(2, 0);  // 7555car Volume,
  write_pot(3, 0);  // 7555mod vol, effectively modulation index
  write_pot(4, 0);  // 7555mod Threshold
  write_pot(5, 0);  // 7555mod Discharge 

when threshold and discharge are moved together on one chip (carrier or modulator)
they control frequency,  when moved independently, they control pitch but with changing
puslewidth or duty cicle.  This changes the timbre of the output.

*/

//Tuning presets.
// scale 0-127 by this much for each pot, volume is scaled differently than other parameters.
int data_scale[]= {2, 2, 1.25, 1.25, 2, 2};

// you might need to play with this number to get your controller numbers lined up with the 6 parameters.
int controllerOffSet = 0; // this number is specfic to the novation midi controller



int DATAOUT = 11;
int DATAIN = 12;      //MISO - not used, but part of builtin SPI
int SPICLOCK = 13;    //sck
int SLAVESELECT = 10;  //ss
int incomingByte = 0;
int note = 0;
int vel = 0;
int controller = 0;
int value = 0;


byte resistance=0;
int index;
int outdex;



void setup()
{
  //  Set MIDI baud rate:
  Serial.begin(31250);
      // debugger, or serial control baud
  // Serial.begin(9600);

  byte i = 0;                                                                                                    
  byte clr = 0;

  //setup pins to work with SPI
  pinMode(DATAOUT, OUTPUT);
  pinMode(DATAIN, INPUT);
  pinMode(SPICLOCK,OUTPUT);
  pinMode(SLAVESELECT,OUTPUT);
  digitalWrite(SLAVESELECT,HIGH); //disable device
  // SPCR = 01010000
  //interrupt disabled,spi enabled,msb 1st,master,clk low when idle,
  //sample on leading edge of clk,system clock/4 (fastest)
  SPCR = (1 << SPE) | (1 << MSTR);                                                                                                                                                                                                                                                           
  clr=SPSR;
  clr=SPDR;
  delay(10);

  // flash an LED and flash each digital pot address (for debugging) and to silence the circuit

  //Set all pots to minimum/maximum resistance and back again
  for (i=0; i < 6; i++)
  {
    digitalWrite(2, HIGH);
     delay(100);   
    write_pot(i,0);
      digitalWrite(2, LOW);
     delay(100); 
  }
  delay(1000);  //wait a second


   for (i=0; i < 6; i++)
  {
     digitalWrite(2, HIGH);
     delay(100); 
    write_pot(i, 255);
      digitalWrite(2, LOW);
     delay(100); 
    // 255 = full resistance...
  }

}





// main program
void loop()
{


  if (Serial.available() > 0) {    
    incomingByte = Serial.read();       

    // see if there is a specific message format, if a specific MIDI status byte is detected, then
    //treat the next two incoming bytes as data bytes
   //  
      if (incomingByte == 176){   // status message for midi cc channel 1
        delay(1);
        // record the next byte as the controller number
        controller = Serial.read() - controllerOffSet; 
        delay(1);   
        // record the third byte as the controller data or value
        value =  Serial.read();



      }
      // if the controller number is within the right range,
      // use the controller number to set the address(1 of the 6 resistors) and
      // use the value of that controller to set the resistance of said address

      if (controller < 6 && controller >= 0){

       // some of the parameters may need scaling,
      write_pot( controller, ( (value * data_scale[controller])));

      // turn on an LED when data is sent (for user feedback/debugging)
      digitalWrite(2, HIGH);
      delay(1);
          }// end if controller
    }// end if serial





    delay(1);
    digitalWrite(2, LOW);
}// end main loop

//-----------------------------------------------------------custom function calls--------------------



// function to send data to a particular address
byte write_pot(int address, int value)
{
  digitalWrite(SLAVESELECT,LOW);
  //2 byte opcode
  spi_transfer(address);
  spi_transfer(value);
  digitalWrite(SLAVESELECT,HIGH); //release chip, signal end transfer
}




//function for transfering data over SPI
char spi_transfer(volatile char data)
{
  SPDR = data;                    // Start the transmission
  while (! (SPSR & (1 << SPIF)))     // Wait for the end of the transmission
  {
  };
  return SPDR;                    // return the received byte
}