Hello, there is a very useful and interesting code for a synth in arduino. I would like to port it to the esp32 board but I do not have enough knowledge since it uses certain registers or access to ports that are very typical of the arduino board and I would not know what their equivalents are in the esp32 board, maybe someone in the community is interested in the subject and knows how to do it. Thank you all.
// Arduino polyphonic FM sound
// * 31250 Hz sampling rate
// * 9-bit resolution
// * 4-fold polyphony (4 different tones can play simulatenously)
// * FM-synthesis with time-varying modulation amplitude
// * ADSR envelopes
// * 12 preset instruments
// Through PWM with timer1, sound is generated on pin 9
// instrument-select button on A5
// 18 sound keys on the remaining i/o pins
// by Rolf Oldeman Feb 2019
// Licence CC BY-NC-SA 2.5 https://creativecommons.org/licenses/by-nc-sa/2.5/
//instrument definitions
#define ninstr 12 // piano xlphn guitar cmbll bell funky vibr metal violin bass trumpt harm
unsigned int ldness[ninstr] = { 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64}; // loudness
unsigned int pitch0[ninstr] = { 12, 12, 12, 12, 24, 24, 0, 12, 24, 12, 12, 24}; // pitch of key0
unsigned int ADSR_a[ninstr] = { 4096, 8192, 8192, 8192, 4096, 512, 512, 8192, 128, 128, 256, 256}; // attack parameter
unsigned int ADSR_d[ninstr] = { 8, 32, 16, 16, 8, 16, 16, 8, 16, 16, 64, 32}; // decay parameter
unsigned int ADSR_s[ninstr] = { 0, 0, 0, 0, 0, 0, 0, 0, 240, 240, 192, 192}; // sustain parameter
unsigned int ADSR_r[ninstr] = { 64, 128, 32, 32, 16, 32, 32, 32, 32, 32, 64, 64}; // release parameter
unsigned int FM_inc[ninstr] = { 256, 512, 768, 400, 200, 96, 528, 244, 256, 128, 64, 160}; // FM frequency wrt pitch
unsigned int FM_a1[ninstr] = { 128, 512, 512, 1024, 512, 0, 1024, 2048, 256, 256, 384, 256}; // FM amplitude start
unsigned int FM_a2[ninstr] = { 64, 0, 128, 128, 128, 512, 768, 512, 128, 128, 256, 128}; // FM amplitude end
unsigned int FM_dec[ninstr] = { 64, 128, 128, 128, 32, 128, 128, 128, 128, 128, 64, 64}; // FM decay
//define the pitch2key mapping
#define keyC4 0
#define keyC4s 1
#define keyD4 2
#define keyD4s 3
#define keyE4 4
#define keyF4 5
#define keyF4s 6
#define keyG4 7
#define keyG4s 8
#define keyA4 9
#define keyA4s 10
#define keyB4 11
#define keyC5 12
#define keyC5s 13
#define keyD5 14
#define keyD5s 15
#define keyE5 16
#define keyF5 17
#define nokey 255
#define instrkey 254
//define the pin to key mapping for 18-key keyboard
#define pinD0 keyC5 //Arduino pin D0
#define pinD1 keyB4 //Arduino pin D1
#define pinD2 keyA4s //Arduino pin D2
#define pinD3 keyA4 //Arduino pin D3
#define pinD4 keyG4s //Arduino pin D4
#define pinD5 keyG4 //Arduino pin D5
#define pinD6 keyF4s //Arduino pin D6
#define pinD7 keyF4 //Arduino pin D7
#define pinB0 keyE4 //Arduino pin D8
#define pinB1 nokey //Arduino pin D9 used for audio out
#define pinB2 keyD4s //Arduino pin D10
#define pinB3 keyD4 //Arduino pin D11
#define pinB4 keyC4s //Arduino pin D12
#define pinB5 keyC4 //Arduino pin D13
#define pinB6 nokey //Arduino pin D14 inexistent
#define pinB7 nokey //Arduino pin D15 inexistent
#define pinC0 keyC5s //Arduino pin A0
#define pinC1 keyD5 //Arduino pin A1
#define pinC2 keyD5s //Arduino pin A2
#define pinC3 keyE5 //Arduino pin A2
#define pinC4 keyF5 //Arduino pin A3
#define pinC5 instrkey //Arduino pin A4
#define pinC6 nokey //Arduino pin A5 inexistent
#define pinC7 nokey //Arduino pin A6 inexistent
//set up array with sine values in signed 8-bit numbers
const float pi = 3.14159265;
char sine[256];
void setsine() {
for (int i = 0; i < 256; ++i) {
sine[i] = (sin(2 * 3.14159265 * (i + 0.5) / 256)) * 128;
}
}
//setup frequencies/phase increments, starting at C3=0 to B6. (A4 is defined as 440Hz)
unsigned int tone_inc[48];
void settones() {
for (byte i=0; i<48; i++){
tone_inc[i]= 440.0 * pow(2.0, ( (i-21) / 12.0)) * 65536.0 / (16000000.0/512) + 0.5;
}
}
byte butstatD=0;
byte butstatB=0;
byte butstatC=0;
byte prevbutstatD=0;
byte prevbutstatB=0;
byte prevbutstatC=0;
byte instr=0;
void setup() {
//disable all inerrupts to avoid glitches
noInterrupts();
//setup the array with sine values
setsine();
//setup array with tone frequency phase increments
settones();
//Set a fast PWM signal on TIMER1A, 9-bit resolution, 31250Hz
pinMode(9, OUTPUT);
TCCR1A = 0B10000010; //9-bit fast PWM
TCCR1B = 0B00001001;
//setup pins for input with pull-up
if(pinD0 != nokey){DDRD &=~(1<<0);PORTD |=(1<<0);};
if(pinD1 != nokey){DDRD &=~(1<<1);PORTD |=(1<<1);};
if(pinD2 != nokey){DDRD &=~(1<<2);PORTD |=(1<<2);};
if(pinD3 != nokey){DDRD &=~(1<<3);PORTD |=(1<<3);};
if(pinD4 != nokey){DDRD &=~(1<<4);PORTD |=(1<<4);};
if(pinD5 != nokey){DDRD &=~(1<<5);PORTD |=(1<<5);};
if(pinD6 != nokey){DDRD &=~(1<<6);PORTD |=(1<<6);};
if(pinD7 != nokey){DDRD &=~(1<<7);PORTD |=(1<<7);};
if(pinB0 != nokey){DDRB &=~(1<<0);PORTB |=(1<<0);};
if(pinB1 != nokey){DDRB &=~(1<<1);PORTB |=(1<<1);};
if(pinB2 != nokey){DDRB &=~(1<<2);PORTB |=(1<<2);};
if(pinB3 != nokey){DDRB &=~(1<<3);PORTB |=(1<<3);};
if(pinB4 != nokey){DDRB &=~(1<<4);PORTB |=(1<<4);};
if(pinB5 != nokey){DDRB &=~(1<<5);PORTB |=(1<<5);};
if(pinB6 != nokey){DDRB &=~(1<<6);PORTB |=(1<<6);};
if(pinB7 != nokey){DDRB &=~(1<<7);PORTB |=(1<<7);};
if(pinC0 != nokey){DDRC &=~(1<<0);PORTC |=(1<<0);};
if(pinC1 != nokey){DDRC &=~(1<<1);PORTC |=(1<<1);};
if(pinC2 != nokey){DDRC &=~(1<<2);PORTC |=(1<<2);};
if(pinC3 != nokey){DDRC &=~(1<<3);PORTC |=(1<<3);};
if(pinC4 != nokey){DDRC &=~(1<<4);PORTC |=(1<<4);};
if(pinC5 != nokey){DDRC &=~(1<<5);PORTC |=(1<<5);};
if(pinC6 != nokey){DDRC &=~(1<<6);PORTC |=(1<<6);};
if(pinC7 != nokey){DDRC &=~(1<<7);PORTC |=(1<<7);};
//store button setting at startup
butstatD = PIND;
butstatB = PINB;
butstatC = PINC;
}
//initialize the main parameters of the pulse length setting
#define nch 4 //number of channels that can produce sound simultaneously
unsigned int phase[nch] = {0,0,0,0};
int inc[nch] = {0,0,0,0};
byte amp[nch] = {0,0,0,0};
unsigned int FMphase[nch]= {0,0,0,0};
unsigned int FMinc[nch] = {0,0,0,0};
unsigned int FMamp[nch] = {0,0,0,0};
// main function (forced inline) to update the pulse length
inline void setPWM() __attribute__((always_inline));
inline void setPWM() {
//wait for the timer to complete loop
while ((TIFR1 & 0B00000001) == 0);
//Clear(!) the overflow bit by writing a 1 to it
TIFR1 |= 0B00000001;
//increment the phases of the FM
FMphase[0] += FMinc[0];
FMphase[1] += FMinc[1];
FMphase[2] += FMinc[2];
FMphase[3] += FMinc[3];
//increment the phases of the note
phase[0] += inc[0];
phase[1] += inc[1];
phase[2] += inc[2];
phase[3] += inc[3];
//calculate the output value and set pulse width for timer2
int val = sine[(phase[0]+sine[FMphase[0]>>8]*FMamp[0]) >> 8] * amp[0];
val += sine[(phase[1]+sine[FMphase[1]>>8]*FMamp[1]) >> 8] * amp[1];
val += sine[(phase[2]+sine[FMphase[2]>>8]*FMamp[2]) >> 8] * amp[2];
val += sine[(phase[3]+sine[FMphase[3]>>8]*FMamp[3]) >> 8] * amp[3];
//set the pulse length
OCR1A = val/128 + 256;
}
//properties of each note played
byte iADSR[nch] = {0, 0, 0, 0};
unsigned int envADSR[nch] = {0, 0, 0, 0};
unsigned int ADSRa[nch] = {0, 0, 0, 0};
unsigned int ADSRd[nch] = {0, 0, 0, 0};
unsigned int ADSRs[nch] = {0, 0, 0, 0};
unsigned int ADSRr[nch] = {0, 0, 0, 0};
byte amp_base[nch] = {0, 0, 0, 0};
unsigned int inc_base[nch] = {0, 0, 0, 0};
unsigned int FMa0[nch] = {0, 0, 0, 0};
int FMda[nch] = {0, 0, 0, 0};
unsigned int FMinc_base[nch]= {0, 0, 0, 0};
unsigned int FMdec[nch] = {0, 0, 0, 0};
unsigned int FMexp[nch] = {0, 0, 0, 0};
unsigned int FMval[nch] = {0, 0, 0, 0};
byte keych[nch] = {0, 0, 0, 0};
unsigned int tch[nch] = {0, 0, 0, 0};
// main loop. Duration of loop is determined by number of setPWM calls
// Each setPWMcall corresponds to 512 cylcles=32mus
// Tloop= 32mus * #setPWM. #setPWM=15 gives Tloop=0.48ms
void loop() {
//read and interpret input buttons
prevbutstatD = butstatD;
prevbutstatB = butstatB;
prevbutstatC = butstatC;
butstatD = PIND;
butstatB = PINB;
butstatC = PINC;
byte keypressed = nokey;
byte keyreleased = nokey;
if(butstatD!=prevbutstatD){
if ( pinD0!=nokey and (butstatD & (1<<0)) == 0 and (prevbutstatD & (1<<0)) > 0 ) keypressed = pinD0;
if ( pinD0!=nokey and (butstatD & (1<<0)) > 0 and (prevbutstatD & (1<<0)) == 0 ) keyreleased = pinD0;
if ( pinD1!=nokey and (butstatD & (1<<1)) == 0 and (prevbutstatD & (1<<1)) > 0 ) keypressed = pinD1;
if ( pinD1!=nokey and (butstatD & (1<<1)) > 0 and (prevbutstatD & (1<<1)) == 0 ) keyreleased = pinD1;
if ( pinD2!=nokey and (butstatD & (1<<2)) == 0 and (prevbutstatD & (1<<2)) > 0 ) keypressed = pinD2;
if ( pinD2!=nokey and (butstatD & (1<<2)) > 0 and (prevbutstatD & (1<<2)) == 0 ) keyreleased = pinD2;
if ( pinD3!=nokey and (butstatD & (1<<3)) == 0 and (prevbutstatD & (1<<3)) > 0 ) keypressed = pinD3;
if ( pinD3!=nokey and (butstatD & (1<<3)) > 0 and (prevbutstatD & (1<<3)) == 0 ) keyreleased = pinD3;
if ( pinD4!=nokey and (butstatD & (1<<4)) == 0 and (prevbutstatD & (1<<4)) > 0 ) keypressed = pinD4;
if ( pinD4!=nokey and (butstatD & (1<<4)) > 0 and (prevbutstatD & (1<<4)) == 0 ) keyreleased = pinD4;
if ( pinD5!=nokey and (butstatD & (1<<5)) == 0 and (prevbutstatD & (1<<5)) > 0 ) keypressed = pinD5;
if ( pinD5!=nokey and (butstatD & (1<<5)) > 0 and (prevbutstatD & (1<<5)) == 0 ) keyreleased = pinD5;
if ( pinD6!=nokey and (butstatD & (1<<6)) == 0 and (prevbutstatD & (1<<6)) > 0 ) keypressed = pinD6;
if ( pinD6!=nokey and (butstatD & (1<<6)) > 0 and (prevbutstatD & (1<<6)) == 0 ) keyreleased = pinD6;
if ( pinD7!=nokey and (butstatD & (1<<7)) == 0 and (prevbutstatD & (1<<7)) > 0 ) keypressed = pinD7;
if ( pinD7!=nokey and (butstatD & (1<<7)) > 0 and (prevbutstatD & (1<<7)) == 0 ) keyreleased = pinD7;
}
if(butstatB!=prevbutstatB){
if ( pinB0!=nokey and (butstatB & (1<<0)) == 0 and (prevbutstatB & (1<<0)) > 0 ) keypressed = pinB0;
if ( pinB0!=nokey and (butstatB & (1<<0)) > 0 and (prevbutstatB & (1<<0)) == 0 ) keyreleased = pinB0;
if ( pinB1!=nokey and (butstatB & (1<<1)) == 0 and (prevbutstatB & (1<<1)) > 0 ) keypressed = pinB1;
if ( pinB1!=nokey and (butstatB & (1<<1)) > 0 and (prevbutstatB & (1<<1)) == 0 ) keyreleased = pinB1;
if ( pinB2!=nokey and (butstatB & (1<<2)) == 0 and (prevbutstatB & (1<<2)) > 0 ) keypressed = pinB2;
if ( pinB2!=nokey and (butstatB & (1<<2)) > 0 and (prevbutstatB & (1<<2)) == 0 ) keyreleased = pinB2;
if ( pinB3!=nokey and (butstatB & (1<<3)) == 0 and (prevbutstatB & (1<<3)) > 0 ) keypressed = pinB3;
if ( pinB3!=nokey and (butstatB & (1<<3)) > 0 and (prevbutstatB & (1<<3)) == 0 ) keyreleased = pinB3;
if ( pinB4!=nokey and (butstatB & (1<<4)) == 0 and (prevbutstatB & (1<<4)) > 0 ) keypressed = pinB4;
if ( pinB4!=nokey and (butstatB & (1<<4)) > 0 and (prevbutstatB & (1<<4)) == 0 ) keyreleased = pinB4;
if ( pinB5!=nokey and (butstatB & (1<<5)) == 0 and (prevbutstatB & (1<<5)) > 0 ) keypressed = pinB5;
if ( pinB5!=nokey and (butstatB & (1<<5)) > 0 and (prevbutstatB & (1<<5)) == 0 ) keyreleased = pinB5;
if ( pinB6!=nokey and (butstatB & (1<<6)) == 0 and (prevbutstatB & (1<<6)) > 0 ) keypressed = pinB6;
if ( pinB6!=nokey and (butstatB & (1<<6)) > 0 and (prevbutstatB & (1<<6)) == 0 ) keyreleased = pinB6;
if ( pinB7!=nokey and (butstatB & (1<<7)) == 0 and (prevbutstatB & (1<<7)) > 0 ) keypressed = pinB7;
if ( pinB7!=nokey and (butstatB & (1<<7)) > 0 and (prevbutstatB & (1<<7)) == 0 ) keyreleased = pinB7;
}
if(butstatC!=prevbutstatC){
if ( pinC0!=nokey and (butstatC & (1<<0)) == 0 and (prevbutstatC & (1<<0)) > 0 ) keypressed = pinC0;
if ( pinC0!=nokey and (butstatC & (1<<0)) > 0 and (prevbutstatC & (1<<0)) == 0 ) keyreleased = pinC0;
if ( pinC1!=nokey and (butstatC & (1<<1)) == 0 and (prevbutstatC & (1<<1)) > 0 ) keypressed = pinC1;
if ( pinC1!=nokey and (butstatC & (1<<1)) > 0 and (prevbutstatC & (1<<1)) == 0 ) keyreleased = pinC1;
if ( pinC2!=nokey and (butstatC & (1<<2)) == 0 and (prevbutstatC & (1<<2)) > 0 ) keypressed = pinC2;
if ( pinC2!=nokey and (butstatC & (1<<2)) > 0 and (prevbutstatC & (1<<2)) == 0 ) keyreleased = pinC2;
if ( pinC3!=nokey and (butstatC & (1<<3)) == 0 and (prevbutstatC & (1<<3)) > 0 ) keypressed = pinC3;
if ( pinC3!=nokey and (butstatC & (1<<3)) > 0 and (prevbutstatC & (1<<3)) == 0 ) keyreleased = pinC3;
if ( pinC4!=nokey and (butstatC & (1<<4)) == 0 and (prevbutstatC & (1<<4)) > 0 ) keypressed = pinC4;
if ( pinC4!=nokey and (butstatC & (1<<4)) > 0 and (prevbutstatC & (1<<4)) == 0 ) keyreleased = pinC4;
if ( pinC5!=nokey and (butstatC & (1<<5)) == 0 and (prevbutstatC & (1<<5)) > 0 ) keypressed = pinC5;
if ( pinC5!=nokey and (butstatC & (1<<5)) > 0 and (prevbutstatC & (1<<5)) == 0 ) keyreleased = pinC5;
if ( pinC6!=nokey and (butstatC & (1<<6)) == 0 and (prevbutstatC & (1<<6)) > 0 ) keypressed = pinC6;
if ( pinC6!=nokey and (butstatC & (1<<6)) > 0 and (prevbutstatC & (1<<6)) == 0 ) keyreleased = pinC6;
if ( pinC7!=nokey and (butstatC & (1<<7)) == 0 and (prevbutstatC & (1<<7)) > 0 ) keypressed = pinC7;
if ( pinC7!=nokey and (butstatC & (1<<7)) > 0 and (prevbutstatC & (1<<7)) == 0 ) keyreleased = pinC7;
}
setPWM(); //#1
//change instrument if instrument select button is pressed
if ( keypressed==instrkey) {
instr++;
if (instr>=ninstr) instr=0;
keypressed=keyA4;
}
if (keyreleased==instrkey) keyreleased=keyA4;
setPWM(); //#2
//find the best channel to start a new note
byte nextch = 255;
//first check if the key is still being played
if (iADSR[0] > 0 and keypressed == keych[0])nextch = 0;
if (iADSR[1] > 0 and keypressed == keych[1])nextch = 1;
if (iADSR[2] > 0 and keypressed == keych[2])nextch = 2;
if (iADSR[3] > 0 and keypressed == keych[3])nextch = 3;
//then check for an empty channel
if (nextch == 255) {
if (iADSR[0] == 0)nextch = 0;
if (iADSR[1] == 0)nextch = 1;
if (iADSR[2] == 0)nextch = 2;
if (iADSR[3] == 0)nextch = 3;
}
//otherwise use the channel with the longest playing note
if (nextch == 255) {
nextch = 0;
if (tch[0] > tch[nextch])nextch = 0;
if (tch[1] > tch[nextch])nextch = 1;
if (tch[2] > tch[nextch])nextch = 2;
if (tch[3] > tch[nextch])nextch = 3;
}
setPWM(); //#3
//initiate new note if needed
if (keypressed != nokey) {
phase[nextch]=0;
amp_base[nextch] = ldness[instr];
inc_base[nextch] = tone_inc[pitch0[instr]+keypressed];
ADSRa[nextch]=ADSR_a[instr];
ADSRd[nextch]=ADSR_d[instr];
ADSRs[nextch]=ADSR_s[instr]<<8;
ADSRr[nextch]=ADSR_r[instr];
iADSR[nextch] = 1;
FMphase[nextch]=0;
FMinc_base[nextch] = ((long)inc_base[nextch]*FM_inc[instr])/256;
FMa0[nextch] = FM_a2[instr];
FMda[nextch] = FM_a1[instr]-FM_a2[instr];
FMexp[nextch]=0xFFFF;
FMdec[nextch]=FM_dec[instr];
keych[nextch] = keypressed;
tch[nextch] = 0;
}
setPWM(); //#4
//stop a note if the button is released
if (keyreleased != nokey) {
if (keych[0] == keyreleased)iADSR[0] = 4;
if (keych[1] == keyreleased)iADSR[1] = 4;
if (keych[2] == keyreleased)iADSR[2] = 4;
if (keych[3] == keyreleased)iADSR[3] = 4;
}
setPWM(); //#5
//update FM decay exponential
FMexp[0]-=(long)FMexp[0]*FMdec[0]>>16;
FMexp[1]-=(long)FMexp[1]*FMdec[1]>>16;
FMexp[2]-=(long)FMexp[2]*FMdec[2]>>16;
FMexp[3]-=(long)FMexp[3]*FMdec[3]>>16;
setPWM(); //#6
//adjust the ADSR envelopes
for (byte ich = 0; ich < nch; ich++) {
if (iADSR[ich] == 4) {
if (envADSR[ich] <= ADSRr[ich]) {
envADSR[ich] = 0;
iADSR[ich] = 0;
}
else envADSR[ich] -= ADSRr[ich];
}
if (iADSR[ich] == 2) {
if (envADSR[ich] <= (ADSRs[ich] + ADSRd[ich])) {
envADSR[ich] = ADSRs[ich];
iADSR[ich] = 3;
}
else envADSR[ich] -= ADSRd[ich];
}
if (iADSR[ich] == 1) {
if ((0xFFFF - envADSR[ich]) <= ADSRa[ich]) {
envADSR[ich] = 0xFFFF;
iADSR[ich] = 2;
}
else envADSR[ich] += ADSRa[ich];
}
tch[ich]++;
setPWM(); //#7-10
}
//update the tone for channel 0
amp[0] = (amp_base[0] * (envADSR[0] >> 8)) >> 8;
inc[0] = inc_base[0];
FMamp[0] = FMa0[0] + ((long)FMda[0] * FMexp[0]>>16);
FMinc[0] = FMinc_base[0];
setPWM(); //#11
//update the tone for channel 1
amp[1] = (amp_base[1] * (envADSR[1] >> 8)) >> 8;
inc[1] = inc_base[1];
FMamp[1] = FMa0[1] + ((long)FMda[1] * FMexp[1]>>16);
FMinc[1] = FMinc_base[1];
setPWM(); //#12
//update the tone for channel 2
amp[2] = (amp_base[2] * (envADSR[2] >> 8)) >> 8;
inc[2] = inc_base[2];
FMamp[2] = FMa0[2] + ((long)FMda[2] * FMexp[2]>>16);
FMinc[2] = FMinc_base[2];
setPWM(); //#13
//update the tone for channel 3
amp[3] = (amp_base[3] * (envADSR[3] >> 8)) >> 8;
inc[3] = inc_base[3];
FMamp[3] = FMa0[3] + ((long)FMda[3] * FMexp[3]>>16);
FMinc[3] = FMinc_base[3];
setPWM(); //#14
//update counters
tch[0]++;
tch[1]++;
tch[2]++;
tch[3]++;
setPWM(); //#15
}