Pocket Synthesizer

A mini piano synthesizer running on Arduino UNO. The main objective of this piano being his ability to function independently.

Pocket Synthesizer

Things used in this project

 

Hardware components

HARDWARE LIST
1 Arduino UNO
1 Music Shield SEEED Studio
1 LiPo rider Pro SEEED Studio
1 Accu 3,7v
1 DFRobot Rotary Switch Module V1
1 Adafruit Stereo 2.8W Class D Audio Amplifier - TS2012
2 Speakers 2W
27 Button
27 Resistor 10k ohm
3 Texas Instruments Shift Register- Parallel to Serial

Story

 

My project idea is a mini piano synthesizer running on Arduino UNO. The main objective of this piano being his ability to function independently, that means to be able to transmit sound without any other external device (eg PC, VST). The device must also operate without external power so that you can use it anywhere.

 

I have chosen this project because I am passionate about music and electronics. This project brings together these two elements, which allowed me to work with motivation and interest. Before starting the project I did some extensive research to find out how to generate sound from an Arduino.

 

How to trigger these sounds also was a question to be asked from the beginning. I have studied the capabilities of existing product in the market, to inspire me and develop a prototype. From this research I was able to develop a concept, create a list of equipment and draw first sketches for a suitable design. The project budget is about 200 euros, so I had to select components wisely to meet a quality / price compromise. The synthesizer was fully drawn with a 3D design software and a waterjet cutting was used to create the front panel. Thanks for reading :)

 

Custom parts and enclosures

 

CAD

 

 

Schematics

 

Schematic #1

 

 

 

Schematic#2

 

With all the components

 

 


 

Code

 

Source code on Arduino UNO

C/C++

Source code with comments! Multiplexing is in it too ;) Check this tutorial -> https://www.arduino.cc/en/Tutorial/ShiftOut

CODE
#include <SPI.h>
#include <Arduino.h> #include <MusicPlayer.h> //#include <SD.h>
#define dataPin 8 #define clockPin 9 #define latchPin 10
#define dataPin2 5 #define clockPin2 6 #define latchPin2 7
#define dnote 4
#define plus 3 #define moin 2
//Define variables to hold the data
//for each shift register.
//starting with non-zero numbers can help //troubleshoot
byte switchVar1 = 0; byte switchVar2 = 0; byte switchVar3 = 0;
51
//int note = 0; int volu = 120; int ton = 100; int toff = 0;
int instru = 79;
int sensorvalue = 500;
void son (int myNote, int myVolu, int myTon, int myToff) {
player.midiNoteOn(0, myNote, myVolu); delay(myTon);
player.midiNoteOff(0, myNote, myVolu); delay(myToff);
}
int but(int x, int y, int h, int l) {
if ((digitalRead (moin) == HIGH) && (x<=(h-y))) { x=x+y
;
}
else if ((digitalRead (plus) == HIGH) && (x>=(l+y))){
x=x-y; }
52
return x; }
void setup() {
//start serial
Serial.begin(9600);
player.beginInMidiFmt();
player.midiWriteData(0xB0, 0x07, 120); // set volume player.midiDemoPlayer();
//define pin modes pinMode(latchPin, OUTPUT); pinMode(clockPin, OUTPUT); pinMode(dataPin, INPUT);
pinMode(latchPin2, OUTPUT); pinMode(clockPin2, OUTPUT); pinMode(dataPin2, INPUT);
pinMode(dnote, INPUT);
pinMode(plus, INPUT);
pinMode(moin, INPUT); }
void loop() {
// delay(1000);
53
sensorvalue = analogRead(5); // Serial.println(sensorvalue);
// read value from different position
//
if (sensorvalue < 540){ Serial.println("1");
volu = but (volu, 5, 127, 0); Serial.println(volu);
delay (1000);
}
else if (sensorvalue < 620){
Serial.println("2");
ton = but (ton, 10, 10, 1000); Serial.println(ton);
}
else if (sensorvalue < 700){ Serial.println("3");
toff = but (toff, 10, 10, 1000); Serial.println(toff);
}
else if (sensorvalue < 780){
Serial.println("4"); player.midiWriteData(0xB0, 0, 0x78); player.midiWriteData(0xC0, 40, 0);
}
else if (sensorvalue < 815){ Serial.println("5"); player.midiWriteData(0xB0, 0, 0x79);
54
// set percussion 78 or synth 79
// set percussion 78 or synth 79
if (instru > 8)instru = 1;
instru = but(instru, 1, 8, 1); player.midiWriteData(0xC0, instru, 0); Serial.println(instru);
}
else if (sensorvalue < 845){ Serial.println("6"); player.midiWriteData(0xB0, 0, 0x79); if ((instru<9) || (instru>16)) instru = 9; instru = but(instru, 1, 16, 9); player.midiWriteData(0xC0, instru, 0); Serial.println(instru);
}
else if (sensorvalue < 870){
// set percussion 78 or synth 79
Serial.println("7"); player.midiWriteData(0xB0, 0, 0x79);
if ((instru<17) || (instru>24)) instru = 17; instru = but(instru, 1, 24, 17); player.midiWriteData(0xC0, instru, 0);
Serial.println(instru); }
else if (sensorvalue < 888){ Serial.println("8"); player.midiWriteData(0xB0, 0, 0x79);
if ((instru<25) || (instru>40)) instru = 25; instru = but(instru, 1, 40, 25); player.midiWriteData(0xC0, instru, 0);
55
// set percussion 78 or synth 79
// set percussion 78 or synth 79
Serial.println(instru); }
else if (sensorvalue < 910){
Serial.println("9");
player.midiWriteData(0xB0, 0, 0x79); // set percussion 78 or synth 79 if ((instru<41) || (instru>56)) instru = 41;
instru = but(instru, 1, 56, 41);
player.midiWriteData(0xC0, instru, 0);
Serial.println(instru); }
else if (sensorvalue < 928){ Serial.println("10"); player.midiWriteData(0xB0, 0, 0x79);
if ((instru<57) || (instru>80)) instru = 65; instru = but(instru, 1, 80, 57); player.midiWriteData(0xC0, instru, 0);
Serial.println(instru); }
else if (sensorvalue < 945){ Serial.println("11"); player.midiWriteData(0xB0, 0, 0x79);
if ((instru<81) || (instru>96)) instru = 91; instru = but(instru, 1, 96, 81); player.midiWriteData(0xC0, instru, 0);
Serial.println(instru); }
else if (sensorvalue < 1000){
56
// set percussion 78 or synth 79
// set percussion 78 or synth 79
Serial.println("12");
player.midiWriteData(0xB0, 0, 0x79); // set percussion 78 or synth 79 if ((instru<97) || (instru>124)) instru = 99;
instru = but(instru, 1, 120, 97);
player.midiWriteData(0xC0, instru, 0);
Serial.println(instru); }
else{ Serial.println("Error");
}
//Pulse the latch pin:
//set it to 1 to collect parallel data digitalWrite(latchPin,1);
//set it to 1 to collect parallel data, wait delayMicroseconds(20);
//set it to 0 to transmit data serially digitalWrite(latchPin,0);
//while the shift register is in serial mode //collect each shift register into a byte
//the register attached to the chip comes in first switchVar1 = shiftIn(dataPin, clockPin); switchVar2 = shiftIn(dataPin, clockPin);
57
//Pulse the latch pin:
//set it to 1 to collect parallel data digitalWrite(latchPin2,1);
//set it to 1 to collect parallel data, wait delayMicroseconds(20);
//set it to 0 to transmit data serially digitalWrite(latchPin2,0);
switchVar3 = shiftIn(dataPin2, clockPin2);
//Print out the results.
//leading 0's at the top of the byte //(7, 6, 5, etc) will be dropped before //the first pin that has a high input //reading
Serial.println(switchVar1, BIN); Serial.println(switchVar2, BIN); Serial.println(switchVar3, BIN);
//This is a away to examine the whole //byte at once and create combinations //of settings.
//By passing the switchVar1 variable to
//a "switch" statement and comparing it against //a set nemerical value (written in binary)
//you can create special cases
58
switch (switchVar1) { case B1:
son (55, volu, ton, toff);
break; case B10:
son (53, volu, ton, toff);
break; case B100:
son (52, volu, ton, toff);
break; case B1000:
son (50, volu, ton, toff);
break;
case B10000:
son (54, volu, ton, toff);
break;
case B100000:
son (51, volu, ton, toff);
break;
case B1000000:
son (49, volu, ton, toff);
break;
case B10000000:
son (48, volu, ton, toff);
break; }
59
//--- SHIFT REGISTER 2
// This is a more complicated behavior
switch (switchVar2) { case B1:
son (59, volu, ton, toff);
break; case B10:
son (58, volu, ton, toff);
break; case B100:
son (60, volu, ton, toff);
break; case B1000:
son (57, volu, ton, toff);
break;
case B10000:
son (63, volu, ton, toff);
break;
case B100000:
son (62, volu, ton, toff);
break;
case B1000000:
son (61, volu, ton, toff); break;
60
case B10000000:
son (56, volu, ton, toff); break;
}
//If the switch attached to pin 7 is High
//--- SHIFT REGISTER 3
switch (switchVar3) { case B1:
son (71, volu, ton, toff);
break; case B10:
son (65, volu, ton, toff);
break; case B100:
son (69, volu, ton, toff);
break; case B1000:
son (67, volu, ton, toff);
break;
case B10000:
son (70, volu, ton, toff);
break;
case B100000:
son (66, volu, ton, toff);
61
break;
case B1000000:
son (68, volu, ton, toff);
break;
case B10000000:
son (64, volu, ton, toff);
break; }
if (digitalRead (dnote) == HIGH) son (72, volu, ton, toff);
//white space
Serial.println("-------------------");
//delay so all these print satements can keep up. //delay(50);
}
//------------------------------------------------end main loop
////// ----------------------------------------shiftIn function
///// just needs the location of the data pin and the clock pin ///// it returns a byte with each bit in the byte corresponding ///// to a pin on the shift register. leftBit 7 = Pin 7 / Bit 0= Pin 0 byte shiftIn(int myDataPin, int myClockPin) {
int i;
62
int temp = 0;
int pinState;
byte myDataIn = 0;
pinMode(myClockPin, OUTPUT);
pinMode(myDataPin, INPUT);
//we will be holding the clock pin high 8 times (0,..,7) at the //end of each time through the for loop
//at the begining of each loop when we set the clock low, it will //be doing the necessary low to high drop to cause the shift //register's DataPin to change state based on the value
//of the next bit in its serial information flow.
//The register transmits the information about the pins from pin 7 to pin 0 //so that is why our function counts down
for (i=7; i>=0; i--) {
digitalWrite(myClockPin, 0); delayMicroseconds(2);
temp = digitalRead(myDataPin); if (temp) {
pinState = 1;
//set the bit to 0 no matter what myDataIn = myDataIn | (1 << i);
}
else {
//turn it off -- only necessary for debuging
63
//print statement since myDataIn starts as 0 pinState = 0;
}
//Debuging print statementss //Serial.print(pinState); //Serial.print(" "); //Serial.println (dataIn, BIN);
digitalWrite(myClockPin, 1);
}
//debuging print statements whitespace //Serial.println(); //Serial.println(myDataIn, BIN);
return myDataIn;
}

MusicPlayer.h :

C/C++

Modified version of the MusicPlayer.h file on the Music Shield

CODE
#ifndef MUSICPLAYER_H #define MUSICPLAYER_H
#include "vs10xx.h"
class MusicPlayer {
public:
CODE
void beginInMidiFmt(void);
void midiDemoPlayer(void);
void midiWriteData(byte cmd, byte high, byte low); void midiNoteOn(byte channel, byte note, byte rate); void midiNoteOff(byte channel, byte note, byte rate); void midiSendByte(byte data);
};
extern MusicPlayer player; #endif

MusicPlayer.cpp :

C/C++

Code that gives the midi player function to the music shield

CODE
#include <MusicPlayer.h>
const int chipSelect = 10;
MusicPlayer player;
/**********************************Midi Player Part***************************/
void MusicPlayer::beginInMidiFmt(void) {
// initIOForLED();
//Init VS105B in Midi Format
Serial.print("Init vs10xx in MIDI format..."); Serial.print("Fuck this shit !!!! "); vs1053.initForMidiFmt(); Serial.print("done\r\n");
pinMode(chipSelect, OUTPUT); }
void MusicPlayer::midiWriteData(byte cmd, byte high, byte low) {
while(!digitalRead(VS_DREQ)); digitalWrite(VS_XDCS, LOW);
midiSendByte(cmd);
if((cmd & 0xF0) <= 0xB0 || (cmd & 0xF0) >= 0xE0) {
midiSendByte(high);
midiSendByte(low); }
else
{
midiSendByte(high);
}
digitalWrite(VS_XDCS, HIGH); }
void MusicPlayer::midiNoteOn(byte channel, byte note, byte rate) {
midiWriteData((0x90 | channel), note, rate); }
void MusicPlayer::midiNoteOff(byte channel, byte note, byte rate) {
midiWriteData((0x80 | channel), note, rate); }
49
void MusicPlayer::midiSendByte(byte data) {
SPI.transfer(0x00);
SPI.transfer(data); }
void MusicPlayer::midiDemoPlayer(void) {
Serial.print("Step 1\n"); delay(1000); midiWriteData(0xB0, 0x07, 120); Serial.print("Step 2\n");
//GM2 Mode
Serial.print("Fancy Midi Sounds!\r\n"); midiWriteData(0xB0, 0, 0x78); Serial.print("Step 3\n");
//
number. 0xC0 is a 1 data byte command
//Set instrument
midiWriteData(0xC0, instrument, 0);
for (int i=0; i<4; i++) {
Serial.print(i+1);
//Note on channel 1 (0x90), some note value (note), middle velocity (0x45):
midiNoteOn(0, 42, 127); delay(100); midiNoteOff(0, 42, 127); delay(200);
}
Serial.print("\ndone\n"); // }
}

pins_config.h :

C/C++

Modified code in the pins_config.h file. This helps to remove the default functionality of some ports so that you can use them for the project!

CODE
#ifndef PINS_CONFIG_H #define PINS_CONFIG_H
#include <avr/io.h>
#include <arduino.h>
///////////////////////for vs10xx/////////////////////////////////// #define VS_XRESET A0
#define VS_DREQ A1
#define VS_XDCS A2
#define VS_XCS A3
#endif

The article was first published in hackster, April 18, 2016

cr: https://www.hackster.io/etiennedesportes/pocket-synthesizer-785b50

author: Etienne Desportes

License
All Rights
Reserved
licensBg
0