Buongiorno ho realizzato il seguente sintetizzatore con ARDUINOUNO e YUN.
Si tratta di un sintetizzatore bifonico con 5 armoniche.
Ci sono quindi 5 oscillatori che generano 5 segnali sinusoidali diversi e fanno capo ad un mixer audio che li somma per fare la sintesi additiva.
Sono riuscito a riprodurre suoni interessanti combinado frequenze e ampiezze audio, ma mi sono piantato nella riproduzione dell'inviluppo.
Riporto di seguito la foto del progetto:
E sotto il sorgente con ARDUINO:
#include "Arduino.h"
#include "MCP4251.h"
#include "TimerOne.h"
#define MAX_BUFFER 2
//void read_USB();
int elemento_buffer=0;
char buffer[MAX_BUFFER];
#define cs1 2
#define cs2 3
#define cs3 4
#define cs4 5
#define cs5 6
#define cs6 7
#define cs7 8
#define cs8 9
#define cs9 10
#define cs14 14
#define pot0ResistanceRmax 100000 // These resistance values may vary
#define pot0ResistanceRmin 0
#define pot1ResistanceRmax 100000
#define pot1ResistanceRmin 0
extern TimerOne Timer0;
extern TimerOne Timer2;
extern TimerOne Timer3;
float fr0=879;
int a1=3300;
float fr1=1783;
int a2=9900;
float fr2=2722;
int a3=17000;
float fr3=3565;
int a4=17800;
float fr4=6528;
int a5=19000;
int mast_vol=20000;
int vol=20000;
unsigned long tatt = 20000;
unsigned long microsecatt = (tatt / 1000);
unsigned long tdec = 2000000000;
unsigned long microsecdec = (tdec / 1000);
MCP4251 f0(cs1, pot0ResistanceRmax, pot0ResistanceRmin, pot1ResistanceRmax, pot1ResistanceRmin);
MCP4251 f1(cs2, pot0ResistanceRmax, pot0ResistanceRmin, pot1ResistanceRmax, pot1ResistanceRmin);
MCP4251 f2(cs3, pot0ResistanceRmax, pot0ResistanceRmin, pot1ResistanceRmax, pot1ResistanceRmin);
MCP4251 f3(cs4, pot0ResistanceRmax, pot0ResistanceRmin, pot1ResistanceRmax, pot1ResistanceRmin);
MCP4251 f4(cs5, pot0ResistanceRmax, pot0ResistanceRmin, pot1ResistanceRmax, pot1ResistanceRmin);
MCP4251 f5(cs6, pot0ResistanceRmax, pot0ResistanceRmin, pot1ResistanceRmax, pot1ResistanceRmin);
MCP4251 f6(cs7, pot0ResistanceRmax, pot0ResistanceRmin, pot1ResistanceRmax, pot1ResistanceRmin);
MCP4251 f7(cs8, pot0ResistanceRmax, pot0ResistanceRmin, pot1ResistanceRmax, pot1ResistanceRmin);
MCP4251 f8(cs9, pot0ResistanceRmax, pot0ResistanceRmin, pot1ResistanceRmax, pot1ResistanceRmin);
MCP4251 f9(cs14, pot0ResistanceRmax, pot0ResistanceRmin, pot1ResistanceRmax, pot1ResistanceRmin);
void setup() {
pinMode(15, OUTPUT);
Serial.begin(9600);
f0.begin();
f0.DigitalPotTerminalBConnect(0);
f0.DigitalPotTerminalAConnect(0);
f0.DigitalPotWiperConnect(0);
f1.begin();
f1.DigitalPotTerminalBConnect(0);
f1.DigitalPotTerminalAConnect(0);
f1.DigitalPotWiperConnect(0);
f2.begin();
f2.DigitalPotTerminalBConnect(0);
f2.DigitalPotTerminalAConnect(0);
f2.DigitalPotWiperConnect(0);
f3.begin();
f3.DigitalPotTerminalBConnect(0);
f3.DigitalPotTerminalAConnect(0);
f3.DigitalPotWiperConnect(0);
f4.begin();
f4.DigitalPotTerminalBConnect(0);
f4.DigitalPotTerminalAConnect(0);
f4.DigitalPotWiperConnect(0);
f5.begin();
f5.DigitalPotTerminalBConnect(0);
f5.DigitalPotTerminalAConnect(0);
f5.DigitalPotWiperConnect(0);
f6.begin();
f6.DigitalPotTerminalBConnect(0);
f6.DigitalPotTerminalAConnect(0);
f6.DigitalPotWiperConnect(0);
f7.begin();
f7.DigitalPotTerminalBConnect(0);
f7.DigitalPotTerminalAConnect(0);
f7.DigitalPotWiperConnect(0);
f8.begin();
f8.DigitalPotTerminalBConnect(0);
f8.DigitalPotTerminalAConnect(0);
f8.DigitalPotWiperConnect(0);
f9.begin();
f9.DigitalPotTerminalBConnect(0);
f9.DigitalPotTerminalAConnect(0);
f9.DigitalPotWiperConnect(0);
//Timer0.initialize(1000000000);
//Timer0.attachInterrupt(read_USB);
//Timer1.initialize(microsecdec);
//Timer1.attachInterrupt(decay);
//interrupts();
Serial.begin(115200);
while (!Serial)
delay(10); // will pause Zero, Leonardo, etc until serial console opens
Serial.println("OK!");
//Timer1.initialize(1000000); //testa se ci sono comandi sulla USB ogni 1msec
//Timer1.attachInterrupt(read_USB); // read USB to run every 0.15 seconds
}
uint16_t wiper0;
uint16_t wiper1;
float conv_freq4_5(float freq)
{
float restot;
restot = 1 / (freq * 0.082);
restot = restot * 1000000;
restot = restot - 2200;
restot = restot / 2;
return restot;
}
float conv_freq1_2_3(float freq)
{
float restot;
restot = 1 / (freq * 6.28 * 0.022);
restot = restot * 1000000;
return restot;
}
void freq0(float freq)
{
float resistenza = conv_freq1_2_3(freq);
//primo pot. (10K)
wiper0 = f0.DigitalPotResistanceToPosition(0, resistenza * 2);
f0.DigitalPotSetWiperPosition(0, wiper0);
//secondo pot. (10K)
wiper1 = f0.DigitalPotResistanceToPosition(1, resistenza * 2);
f0.DigitalPotSetWiperPosition(1, wiper1);
//terzo pot. dig. (5K)
wiper0 = f1.DigitalPotResistanceToPosition(0, resistenza);
f1.DigitalPotSetWiperPosition(0, wiper0);
}
void freq1(float freq)
{
float resistenza = conv_freq1_2_3(freq);
//primo pot. (10K)
wiper1 = f2.DigitalPotResistanceToPosition(1, resistenza * 2);
f2.DigitalPotSetWiperPosition(1, wiper1);
//secondo pot. (10K)
wiper0 = f2.DigitalPotResistanceToPosition(0, resistenza * 2);
f2.DigitalPotSetWiperPosition(0, wiper0);
//terzo pot. dig. (5K)
wiper1 = f1.DigitalPotResistanceToPosition(1, resistenza);
f1.DigitalPotSetWiperPosition(1, wiper1);
}
void freq2(float freq)
{
float resistenza = conv_freq1_2_3(freq);
//primo pot. (10K)
wiper0 = f3.DigitalPotResistanceToPosition(0, resistenza * 2);
f3.DigitalPotSetWiperPosition(0, wiper0);
//secondo pot. (10K)
wiper1 = f3.DigitalPotResistanceToPosition(1, resistenza * 2);
f3.DigitalPotSetWiperPosition(1, wiper1);
//terzo pot. dig. (5K)
wiper1 = f4.DigitalPotResistanceToPosition(1, resistenza);
f4.DigitalPotSetWiperPosition(1, wiper1);
}
void freq3(float freq)
{
float resistenza = conv_freq4_5(freq);
//pot. 1
wiper0 = f4.DigitalPotResistanceToPosition(0, resistenza);
f4.DigitalPotSetWiperPosition(0, wiper0);
//pot. 2
wiper1 = f5.DigitalPotResistanceToPosition(1, resistenza);
f5.DigitalPotSetWiperPosition(1, wiper1);
}
void freq4(float freq)
{
float resistenza = conv_freq4_5(freq);
//pot. 1
wiper0 = f5.DigitalPotResistanceToPosition(0, resistenza);
f5.DigitalPotSetWiperPosition(0, wiper0);
//pot. 2
wiper1 = f6.DigitalPotResistanceToPosition(1, resistenza);
f6.DigitalPotSetWiperPosition(1, wiper1);
}
void amp1(int amp)
{
//wiper0 f6
wiper0 = f6.DigitalPotResistanceToPosition(0, amp);
f6.DigitalPotSetWiperPosition(0, wiper0);
}
void amp3(int amp)
{
//wiper1 f7
wiper1 = f7.DigitalPotResistanceToPosition(1, amp);
f7.DigitalPotSetWiperPosition(1, wiper1);
}
void amp2(float amp)
{
//wiper0 f7
wiper0 = f7.DigitalPotResistanceToPosition(0, amp);
f7.DigitalPotSetWiperPosition(0, wiper0);
}
void amp4(int amp)
{
//wiper0 f8
wiper0 = f8.DigitalPotResistanceToPosition(0, amp);
f8.DigitalPotSetWiperPosition(0, wiper0);
}
void amp5(int amp)
{
//wiper1 f8
wiper1 = f8.DigitalPotResistanceToPosition(1, amp);
f8.DigitalPotSetWiperPosition(1, wiper1);
}
void master_vol(int vol)
{
//wiper0 f9
wiper0= f9.DigitalPotResistanceToPosition(0, vol);
f9.DigitalPotSetWiperPosition(0, wiper0);
//wiper1 f9
wiper1= f9.DigitalPotResistanceToPosition(1, vol);
f9.DigitalPotSetWiperPosition(1, wiper1);
}
void cut_off(float ft)
{
}
void inviluppo(void)
{
//attacco a gradino
if(microsecatt == 0)
{
freq0(fr0);
freq1(fr1);
freq2(fr2);
freq3(fr3);
freq4(fr4);
amp1(a1);
amp2(a2);
amp3(a3);
amp4(a4);
amp5(a5);
master_vol(vol);
}
//attacco a rampa variabile
else
{
freq0(fr0);
freq1(fr1);
freq2(fr2);
freq3(fr3);
freq4(fr4);
amp1(a1);
amp2(a2);
amp3(a3);
amp4(a4);
amp5(a5);
if(vol!=0)
{
master_vol(vol--);
//delay(microsecatt);
}
}
//decay
/*if(vol>=0 && vol==mast_vol-5000)
{
master_vol(vol++);
//delay(1);
}
//sustan
//relese
if(vol<=20000)
{
master_vol(vol++);
delay(1);
}
if(vol>=20000) vol=20000;*/
}
void loop() {
if((Serial.available())>0)
{
// se c'è qualche byte da leggere, allora...
char carattere=Serial.read(); // ...leggi byte
//if(carattere=='\r') continue;
if(carattere=='\n')
{
buffer[elemento_buffer]=0; // inserisco un carettere NULL per indicare la fine dell'array
elemento_buffer=0; // resetto l'indice dell'array
}
if(carattere == 'a')
{
digitalWrite(15, HIGH);
inviluppo();
}
Serial.println(buffer);
buffer[elemento_buffer]=carattere; // creo l'array di caratteri
elemento_buffer++; // incremento l'indice del buffer
if(elemento_buffer==MAX_BUFFER) // se si raggiunge il limite di byte del buffer allora...
{
elemento_buffer=MAX_BUFFER-1; // ...sovrascrivi quello precedente
}
buffer[0]='0'; //pulisci buffer
digitalWrite(15, LOW);
}
else
{
vol=20000;
master_vol(vol);
}
}
Non riesco in particolare a far si che ad ogni pressione del tasto 'a' venga riprodotto un ATTACK graduale fino la volume massimo, ma si sente un ATTACK a volume a tratti poco gradevole da sentire.
Ho provato a generare le 4 fasi dell'inviluppo con l'interruprt, ma si
sente un effetto sgradevolissimo nn voluto, dovuto alla continua commutazione dal programma principale alla routine di interrupt.
Ho optato quindi per la soluzione senza interrupt da timer; con la chiamata inviluppo() da loop{} ad ogni presisone della lettera 'a'.
L'effetto però non è il massimo dato che non riesco a variare gradualmente il tempo di inviluppo dell'ATTACK agendo nell'incremento o decremento della variabile vol.
Non riesco ad ottenere una variazione graduale e non a tratti del volume del progredire dell'ATTACK.
Nessuno mi puo' aiutare con qualche idea?
Riporto pure il codice python che invia la lettera 'a' all'ARDUINO:
import time
import serial
import keyboard
#import pynput
def invia():
ser.write(i)
try:
ser = serial.Serial("/dev/ttyACM0", 115200)
if ser.is_open:
print ("\n Il collegamento è stato effettuato con successo \n")
#ser.close()
except:
print("""
Collegamento fallito,
controllare se il dispositivo è stato connesso correttamente,
o di avere inserito il nome della porta corretto \n""")
i=b'a\n'
while True: # making a loop
if keyboard.is_pressed('a'): # if key 'a' is pressed
print('You Pressed "a" Key!')
ser.write(i)
else:
print("\nTasto non premuto")# if user pressed a key other than the given key the loop will break
Chiedo quindi aiuto e consigli per ottenere un invìiluppo graduale e più piacevole da sentire: non a tratti.

