This is the documentation for the dsp-Gplug Analog Modeling MIDI Synth.
It is an exclusive license for Hackster.io so the images and code may not be published on any other web page. You may use the schematics and code for personal projects but the design and code is copyright DSP Synthesizers, Sweden All rights reserved.
Preprogrammed chips and part kits can be bought at http://www.dspsynth.eu
The ready assembled dsp-Gplug synth can also be found there.
The synth is contained inside a MIDI DIN plug and reads TTL level serial MIDI data and outputs sound through a 44.1KHz Delta-Sigma DAC. The synth is structured as a 5-voice paraphonic with 3 oscillators per voice, a 24db lowpass filter with resonance.It also has AMP ENV, filter ENV and LFO with triangle or sample&hold waveform.
The parameters are controlled through 19 MIDI-CC parameters:
CUTOFF = CC#74
RESONANCE = CC#71
FILTER A = CC#82
FILTER D = CC#83
FILTER S = CC#28
FILTER R = CC#29
FILTER ENV M = CC#81
FILTER LFO M = CC#01
WAVEFORM = CC#76
WRAP = CC#04
RANGE = CC#21
DETUNE = CC#93
OSCENV A = CC#73
OSCENV D = CC#75
OSCENV S = CC#31
OSCENV R = CC#72
MASTERVOL = CC#07
LFORATE = CC#16
LFOWAVE = CC#20
The heart of the synth is the 60 DMIPS NXP chip programmed in C. A 5-pin DIN, A 3.5mm jack, some capacitors and a 3.3v LDO regulator makes up for the rest.
//**************************************************************************/
// Analog Modeling Synth main code
// Copyright DSP Synthesizers (c) 2014
// Code may not be redistributed and is for personal use only
// The code may only be listed here on Hackster.io
//
//**************************************************************************/
#include "wiring.h"
#include "LPC8xx.h"
#include "sct_fsm.h"
//#include "TFT.h"
/* Control register bit definition. */
#define MRT_INT_ENA (0x1<<0)
#define MRT_REPEATED_MODE (0x00<<1)
#define MRT_ONE_SHOT_INT (0x01<<1)
#define MRT_ONE_SHOT_STALL (0x02<<1)
/* Status register bit definition */
#define MRT_STAT_IRQ_FLAG (0x1<<0)
#define MRT_STAT_RUN (0x1<<1)
#define SPI_CFG_ENABLE (0x1)
#define SPI_CFG_MASTER (0x4)
#define SPI_STAT_RXRDY (0x1)
#define SPI_STAT_TXRDY (0x2)
#define SPI_STAT_SSD (0x20)
#define SPI_STAT_MSTIDLE (0x100)
#define SPI_TXDATCTL_SSEL_N(s) ((s) << 16)
#define SPI_TXDATCTL_EOT (1 << 20)
#define SPI_TXDATCTL_EOF (1 << 21)
#define SPI_TXDATCTL_RXIGNORE (1 << 22)
#define SPI_TXDATCTL_FLEN(l) ((l) << 24)
//-------- Synth parameters --------------
uint32_t FREQ[15]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; //OSC pitches
volatile int32_t CUTOFF=65535; //freq 0-65535
volatile int32_t CFREQ=65535; //freq 0-65535
volatile int32_t RESONANCE=0; //reso=0-65535
volatile uint8_t TRIG=1; //MIDItrig 1=note ON
volatile uint32_t ATTACK0=0xFFFF; //Attackrate in 30mS steps
volatile uint32_t DECAY0=98; //Decayrate in 30mS steps
volatile int32_t SUSTAIN0=0xFFFF; //Sustainlevel 0-65535
volatile uint32_t RELEASE0=0x0; //Releaserate in 30mS steps
volatile uint32_t ATTACK1=98; //Attackrate in 30mS steps
volatile uint32_t DECAY1=98; //Decayrate in 30mS steps
volatile int32_t SUSTAIN1=0xFFFF; //Sustainlevel 0-65535
volatile uint32_t RELEASE1=0; //Releaserate in 30mS steps
volatile uint32_t DETUNE=1; //Osc spread or detune
volatile uint32_t RANGE=64; //Osc range
volatile uint8_t ENVMOD=0; //Filter ENVMOD
volatile uint8_t LFOMOD=0; //Filter LFOMOD
volatile uint8_t WAVEFORM=255; //OSC Waveform
volatile uint8_t WRAP=0; //OSC Wrap
volatile uint8_t LFORATE=0; //LFO frequency
volatile uint8_t LFOWAVE=0; //LFO waveform
volatile int32_t MASTERVOL=65535; //Master volume
volatile uint8_t CHORUSRATE=0; //Chorus rate
volatile uint8_t CHORUSMOD=0; //Chorus modlevel
volatile int32_t CHORUSMIX; //Chorus mix level
volatile int32_t CHORUSFB; //Chorus feedback level
//-----------------------------------------
volatile uint16_t DACL=1;
volatile uint16_t DACR=1;
uint32_t DCOPH[15];
volatile uint8_t state0=0; //needs to be reset on a new trig
volatile uint8_t state1=0; //needs to be reset on a new trig
volatile int32_t volume0=0;
volatile int32_t volume1=0;
volatile uint16_t envstat=1; //needs to be reset on a new trig
int32_t t[9];
int32_t vi;
int32_t vo;
int16_t DCO;
int16_t DCF;
int32_t ENV;
uint8_t lfocounter=0;
uint32_t next = 1;
volatile uint8_t shrandom;
uint8_t chrcounter=0;
uint8_t pwmcounter=0;
int8_t waveform[256];
const uint32_t NOTES[12]={208065>>2,220472>>2,233516>>2,247514>>2,262149>>2,277738>>2,294281>>2,311779>>2,330390>>2,349956>>2,370794>>2,392746>>2};
volatile uint8_t MIDISTATE=0;
volatile uint8_t MIDIRUNNINGSTATUS=0;
volatile uint8_t MIDINOTE;
volatile uint8_t MIDIVEL;
uint8_t OSCNOTES[5];
const uint16_t ENVRATES[32] = {65535,61540,57544,53548,49552,45556,41560,37564,33568,29572,25576,21580,17584,13588,9592,5596,1600,1503,1407,1311,1215,1119,1022,926,830,734,638,541,445,349,253,157 };
volatile uint16_t writeptr = 0;
volatile uint16_t delayL = 0;
volatile uint16_t delayR = 1023;
volatile int16_t leftCH;
volatile int16_t rightCH;
volatile int32_t chmixL;
volatile int32_t chmixR;
//---------------- Get the base frequency for the MIDI note ---------------
uint32_t MIDI2FREQ(uint8_t note) {
uint8_t key=note%12;
if (note<36) return (NOTES[key]>>(1+(35-note)/12));
if (note>47) return (NOTES[key]<<((note-36)/12));
return NOTES[key];
}
//-------------------------------------------------------------------------
void MRT_IRQHandler(void) {
if ( LPC_MRT->Channel[0].STAT & MRT_STAT_IRQ_FLAG ) {
LPC_MRT->Channel[0].STAT = MRT_STAT_IRQ_FLAG; /* clear interrupt flag */
uint8_t i;
//-------------------- 15 DCO block ------------------------------------------
DCO=0;
for (i=0;i<15;i++) {
DCOPH[i] += FREQ[i]; //Add freq to phaseacc's
DCO += waveform[(DCOPH[i]>>15)&255]; //Add DCO's to output
}
DCO = DCO<<4;
//---------------------------------------------------------------------------
//---------------- DCF block ---------------------------------------
//C implementation for a 4pole lowpass DCF with resonance:
vi=DCO;
for (i=0;i<8;i++) {
t[i] = ((t[i] * (65536 - CFREQ)) + (t[i+1] * CFREQ))>>16; //+3dB per iteration
}
t[8] = vi-((t[0]*RESONANCE)>>16); //resonance feedback
DCF=t[0];
//-----------------------------------------------------------------
//--------------------- ENV block ---------------------------------
if (!envstat--) {
envstat=1024;
if (TRIG) {
if (state0==1) {
volume0 -= DECAY0;
if (SUSTAIN0>volume0) {
volume0=SUSTAIN0;
state0++;
}
}
if (state0==0) {
volume0 += ATTACK0;
if (volume0>0xFFFF) {
volume0=0xFFFF;
state0++;
}
}
if (state1==1) {
volume1 -= DECAY1;
if (SUSTAIN1>volume1) {
volume1=SUSTAIN1;
state1++;
}
}
if (state1==0) {
volume1 += ATTACK1;
if (volume1>0xFFFF) {
volume1=0xFFFF;
state1++;
}
}
}
if (!TRIG) {
volume0 -= RELEASE0;
if (volume0<0) {
volume0=0;
}
volume1 -= RELEASE1;
if (volume1<0) {
volume1=0;
}
}
//-------------------- LFO block ----------------------------------
lfocounter+=LFORATE;
int8_t TRILFO=(lfocounter&127);
if (lfocounter&128) TRILFO=127-(lfocounter&127);
TRILFO=127-(TRILFO<<1);
if (LFOWAVE<64) {
CFREQ=(int32_t)((TRILFO*LFOMOD)+((volume1>>8)*ENVMOD));
CFREQ=CFREQ+CUTOFF;
if (CFREQ>65535) CFREQ=65535;
if (CFREQ<0) CFREQ=0;
}
else {
if (lfocounter&128) {
CFREQ=(int32_t)((shrandom*LFOMOD)+((volume1>>8)*ENVMOD));
CFREQ=CFREQ+CUTOFF;
if (CFREQ>65535) CFREQ=65535;
if (CFREQ<0) CFREQ=0;
lfocounter=0;
}
}
}
//-----------------------------------------------------------------
ENV=(volume0*DCF)>>16;
//-----------------------------------------------------------------
ENV=(MASTERVOL*ENV)>>16;
DACH=(ENV>>8);
DACL=(ENV&0xFF);
LPC_SCT->MATCHREL[1].L = DACL;
LPC_SCT->MATCHREL[1].H = DACH;
}
return;
}
void handleMIDICC(uint8_t CC,uint8_t value) {
uint8_t i;
/*
MIDI CCs:
CUTOFF = CC#74
RESONANCE = CC#71
FILTER A = CC#82
FILTER D = CC#83
FILTER S = CC#28
FILTER R = CC#29
FILTER ENV M = CC#81
FILTER LFO M = CC#01
WAVEFORM = CC#76
WRAP = CC#04
RANGE = CC#21
DETUNE = CC#93
OSCENV A = CC#73
OSCENV D = CC#75
OSCENV S = CC#31
OSCENV R = CC#72
MASTERVOL = CC#07
LFORATE = CC#16
LFOWAVE = CC#20
*/
switch(CC) {
case 74:
CUTOFF=value<<9;
break;
case 71:
RESONANCE=value<<9;
break;
case 82:
ATTACK1=ENVRATES[value>>2];
break;
case 83:
DECAY1=ENVRATES[value>>2];
break;
case 28:
SUSTAIN1=ENVRATES[value>>2];
break;
case 29:
RELEASE1=ENVRATES[value>>2];
break;
case 73:
ATTACK0=ENVRATES[value>>2];
break;
case 75:
DECAY0=ENVRATES[value>>2];
break;
case 31:
SUSTAIN0=ENVRATES[value>>2];
break;
case 72:
RELEASE0=ENVRATES[value>>2];
break;
case 93:
DETUNE=value;
for (i=0;i<5;i++) {
if (FREQ[i*3]) {
FREQ[i*3+1]=FREQ[i*3]+((FREQ[i*3]/50)*DETUNE/127)+((FREQ[i*3]/2)*RANGE/32);
FREQ[i*3+2]=FREQ[i*3]-((FREQ[i*3]/50)*DETUNE/127)+((FREQ[i*3]/2)*RANGE/32);
}
}
break;
case 21:
RANGE=value;
for (i=0;i<5;i++) {
if (FREQ[i*3]) {
FREQ[i*3+1]=FREQ[i*3]+((FREQ[i*3]/50)*DETUNE/127)+((FREQ[i*3]/2)*RANGE/32);
FREQ[i*3+2]=FREQ[i*3]-((FREQ[i*3]/50)*DETUNE/127)+((FREQ[i*3]/2)*RANGE/32);
}
}
break;
case 81:
ENVMOD=value<<1;
break;
case 01:
LFOMOD=value<<1;
break;
case 16:
LFORATE=(value>>1)+1;
break;
case 76:
WAVEFORM=value<<1;
break;
case 04:
WRAP=value<<1;
break;
case 20:
LFOWAVE=value;
break;
case 07:
MASTERVOL=value<<9;
break;
}
}
void handleMIDINOTE(uint8_t status,uint8_t note,uint8_t vel) {
uint8_t i;
//uint8_t trigflag=0;
uint32_t freq;
if ((!vel)&&(status==0x90)) status=0x80;
if (status==0x80) {
for (i=0;i<5;i++) {
if (OSCNOTES[i]==note) {
if (!RELEASE0) {
FREQ[i*3]=0;
FREQ[i*3+1]=0;
FREQ[i*3+2]=0;
}
OSCNOTES[i]=0;
}
//trigflag+=OSCNOTES[i];
}
if (!(OSCNOTES[0]|OSCNOTES[1]|OSCNOTES[2]|OSCNOTES[3]|OSCNOTES[4])) TRIG=0;
return;
}
if (status==0x90) {
if ((!TRIG)&&(RELEASE0)) {
for (i=0;i<14;i++) {
FREQ[i]=0;
}
}
i=0;
while (i<5) {
if (!OSCNOTES[i]) {
freq=MIDI2FREQ(note);
FREQ[i*3]=freq;
FREQ[i*3+1]=FREQ[i*3]+((FREQ[i*3]/50)*DETUNE/127)+((FREQ[i*3]/2)*RANGE/32);
FREQ[i*3+2]=FREQ[i*3]-((FREQ[i*3]/50)*DETUNE/127)+((FREQ[i*3]/2)*RANGE/32);
OSCNOTES[i]=note;
if (!TRIG) {
TRIG=1;
state0=0;
state1=0;
}
return;
}
i++;
}
}
}
void UART0_IRQHandler(void) {
uint8_t MIDIRX;
while (!(LPC_USART0->STAT & UART_STATUS_TXRDY));
MIDIRX = LPC_USART0->RXDATA;
/*
Handling "Running status"
1.Buffer is cleared (ie, set to 0) at power up.
2.Buffer stores the status when a Voice Category Status (ie, 0x80 to 0xEF) is received.
3.Buffer is cleared when a System Common Category Status (ie, 0xF0 to 0xF7) is received.
4.Nothing is done to the buffer when a RealTime Category message is received.
5.Any data bytes are ignored when the buffer is 0.
*/
if ((MIDIRX>0xBF)&&(MIDIRX<0xF8)) {
MIDIRUNNINGSTATUS=0;
MIDISTATE=0;
return;
}
if (MIDIRX>0xF7) return;
if (MIDIRX & 0x80) {
MIDIRUNNINGSTATUS=MIDIRX;
MIDISTATE=1;
return;
}
if (MIDIRX < 0x80) {
if (!MIDIRUNNINGSTATUS) return;
if (MIDISTATE==1) {
MIDINOTE=MIDIRX;
MIDISTATE++;
return;
}
if (MIDISTATE==2) {
MIDIVEL=MIDIRX;
MIDISTATE=1;
if ((MIDIRUNNINGSTATUS==0x80)||(MIDIRUNNINGSTATUS==0x90)) handleMIDINOTE(MIDIRUNNINGSTATUS,MIDINOTE,MIDIVEL);
if (MIDIRUNNINGSTATUS==0xB0) handleMIDICC(MIDINOTE,MIDIVEL);
return;
}
}
return;
}
void mrtInit(uint32_t delay)
{
/* Enable clock to MRT and reset the MRT peripheral */
LPC_SYSCON->SYSAHBCLKCTRL |= (0x1<<10);
LPC_SYSCON->PRESETCTRL &= ~(0x1<<7);
LPC_SYSCON->PRESETCTRL |= (0x1<<7);
LPC_MRT->Channel[0].INTVAL = delay;
LPC_MRT->Channel[0].INTVAL |= 0x1UL<<31;
LPC_MRT->Channel[0].CTRL = MRT_REPEATED_MODE|MRT_INT_ENA;
/* Enable the MRT Interrupt */
#if NMI_ENABLED
NVIC_DisableIRQ( MRT_IRQn );
NMI_Init( MRT_IRQn );
#else
NVIC_EnableIRQ(MRT_IRQn);
#endif
return;
}
int main(void)
{
/* Initialise the GPIO block */
/* Enable AHB clock to the GPIO domain. */
LPC_SYSCON->SYSAHBCLKCTRL |= (1 << 6);
LPC_SYSCON->SYSAHBCLKCTRL |= (1 << 11); //Enable SPI0 clk
LPC_SYSCON->PRESETCTRL &= ~(1 << 10);
LPC_SYSCON->PRESETCTRL |= (1 << 10);
LPC_SYSCON->PRESETCTRL &= ~(1); //Reset SPI0
LPC_SYSCON->PRESETCTRL |= (1); //Reset SPI0
LPC_SWM->PINENABLE0 = 0xffffffffUL; //All 6 GPIO enabled
LPC_SYSCON->SYSAHBCLKCTRL |= (1 << 8); // enable the SCT clock
LPC_SCT->CTRL_L |= ((0 << 5) | (1<<3)); // set pre-scalar, SCT clock, clear counter
LPC_SCT->CTRL_H |= ((0 << 5) | (1<<3)); // set pre-scalar, SCT clock, clear counter
sct_fsm_init();
LPC_SCT->CTRL_L &= ~(1<<2); // unhalt the SCT by clearing bit 2 in CTRL
LPC_SCT->CTRL_H &= ~(1<<2); // unhalt the SCT by clearing bit 2 in CTRL
LPC_SWM->PINASSIGN6 = 0x03ffffffUL; /* CTOUT_0 = P0_3*/
LPC_SWM->PINASSIGN7 = 0x0fffff02UL; /* CTOUT_1 = P0_2*/
/* Pin Assign 8 bit Configuration */
/* U0_TXD */
/* U0_RXD */
LPC_SWM->PINASSIGN0 = 0xffff0004UL;
/* Initialise the UART0 block for printf output */
uart0Init(31250);
/* Configure the multi-rate timer for 44.1K ticks */
//mrtInit(__SYSTEM_CLOCK/42188);
mrtInit(48000000/44100);
uint16_t i;
for (i=0;i<256;i++) {
waveform[i]=(i-127);
}
for (i=0;i<5;i++) {
FREQ[i] = 0;
}
i=0;
int8_t TRI;
int8_t SQR;
int8_t SAW;
int16_t SUM;
while(1) {
for (i=0;i<256;i++) {
SUM=0;
...
This file has been truncated, please download it to see its full contents.
Comments