Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
396 lines (332 sloc) 12.5 KB
#ifndef _SYNTH
#define _SYNTH
#define MAX(a,b) ((a)<(b)?(b):(a))
#define MIN(a,b) ((a)>(b)?(b):(a))
//*************************************************************************************
// ADAPTATION OF:
// Arduino synth V4.1
// Optimized audio driver, modulation engine, envelope engine.
//
// Dzl/Illutron 2014
//
// See https://github.com/dzlonline/the_synth for the original code.
//
// This modification turns a single PWM output into 3 outputs, each with a 120' offset,
// to simultaneously drive 3 speakers and a single brushless DC motor.
// This only makes sense if you read:
// http://cassettepunk.com/blog/2016/03/21/nielsen-speaker
// Adaptation by Alec Smecher.
//*************************************************************************************
#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include "tables.h"
#include <Arduino.h>
#define DIFF 1
#define CHA 2
#define CHB 3
#define SINE 0
#define TRIANGLE 1
#define SQUARE 2
#define SAW 3
#define RAMP 4
#define NOISE 5
#define ENVELOPE0 0
#define ENVELOPE1 1
#define ENVELOPE2 2
#define ENVELOPE3 3
#define FS 20000.0 //-Sample rate (NOTE: must match tables.h)
#define SET(x,y) (x |=(1<<y)) //-Bit set/clear macros
#define CLR(x,y) (x &= (~(1<<y))) // |
#define CHK(x,y) (x & (1<<y)) // |
#define TOG(x,y) (x^=(1<<y)) //-+
volatile unsigned int PCW[4] = {
0, 0, 0, 0}; //-Wave phase accumolators
volatile unsigned int FTW[4] = {
1000, 200, 300, 400}; //-Wave frequency tuning words
volatile unsigned char AMP[4] = {
255, 255, 255, 255}; //-Wave amplitudes [0-255]
volatile unsigned int PITCH[4] = {
500, 500, 500, 500}; //-Voice pitch
volatile int MOD[4] = {
20, 0, 64, 127}; //-Voice envelope modulation [0-1023 512=no mod. <512 pitch down >512 pitch up]
volatile unsigned int wavs[4]; //-Wave table selector [address of wave in memory]
volatile unsigned int envs[4]; //-Envelopte selector [address of envelope in memory]
volatile unsigned int EPCW[4] = {
0x8000, 0x8000, 0x8000, 0x8000}; //-Envelope phase accumolator
volatile unsigned int EFTW[4] = {
10, 10, 10, 10}; //-Envelope speed tuning word
volatile unsigned char divider = 4; //-Sample rate decimator for envelope
volatile unsigned int tim = 0;
volatile unsigned char tik = 0;
volatile unsigned char output_mode;
//*********************************************************************************************
// Audio driver interrupt
//*********************************************************************************************
SIGNAL(TIMER1_COMPA_vect)
{
//-------------------------------
// Time division
//-------------------------------
divider++;
if(!(divider&=0x03))
tik=1;
//-------------------------------
// Volume envelope generator
//-------------------------------
if (!(((unsigned char*)&EPCW[divider])[1]&0x80))
AMP[divider] = pgm_read_byte(envs[divider] + (((unsigned char*)&(EPCW[divider]+=EFTW[divider]))[1]));
else
AMP[divider] = 0;
//-------------------------------
// Synthesizer/audio mixer
//-------------------------------
PCW[0] += FTW[0]; PCW[1] += FTW[1]; PCW[2] += FTW[2]; PCW[3] += FTW[3];
volatile unsigned int a = ((signed char *)&(PCW[0]))[1];
volatile unsigned int b = ((signed char *)&(PCW[1]))[1];
// volatile unsigned int c = ((signed char *)&(PCW[2]))[1];
// volatile unsigned int d = ((signed char *)&(PCW[3]))[1];
volatile unsigned int ocr2a = 127 + ( // Pin 3
((
(((signed char)pgm_read_byte(wavs[0] + a) * AMP[0]) >> 8) +
(((signed char)pgm_read_byte(wavs[1] + b) * AMP[1]) >> 8)/* +
(((signed char)pgm_read_byte(wavs[2] + c) * AMP[2]) >> 8) +
(((signed char)pgm_read_byte(wavs[3] + d) * AMP[3]) >> 8)*/
) >> 2));
OCR2A = (MAX(127, ocr2a)-127)*8; // Chop & scale for the motor
volatile unsigned int ocr2b = 127 + ( // Pin 11
((
(((signed char)pgm_read_byte(wavs[0] + ((a+85)%256)) * AMP[0]) >> 8) +
(((signed char)pgm_read_byte(wavs[1] + ((b+85)%256)) * AMP[1]) >> 8)/* +
(((signed char)pgm_read_byte(wavs[2] + ((c+85)%256)) * AMP[2]) >> 8) +
(((signed char)pgm_read_byte(wavs[3] + ((d+85)%256)) * AMP[3]) >> 8)*/
) >> 2));
OCR2B = (MAX(127, ocr2b)-127)*8; // Chop & scale for the motor
volatile unsigned int ocr0b = 127 + ( // Pin 5
((
(((signed char)pgm_read_byte(wavs[0] + ((a+171)%256)) * AMP[0]) >> 8) +
(((signed char)pgm_read_byte(wavs[1] + ((b+171)%256)) * AMP[1]) >> 8)/* +
(((signed char)pgm_read_byte(wavs[2] + ((c+171)%256)) * AMP[2]) >> 8) +
(((signed char)pgm_read_byte(wavs[3] + ((d+171)%256)) * AMP[3]) >> 8)*/
) >> 2));
OCR0B = (MAX(127, ocr0b)-127)*8; // Chop & scale for the motor
//************************************************
// Modulation engine
//************************************************
// FTW[divider] = PITCH[divider] + (int) (((PITCH[divider]/64)*(EPCW[divider]/64)) /128)*MOD[divider];
FTW[divider] = PITCH[divider] + (int) (((PITCH[divider]>>6)*(EPCW[divider]>>6))/128)*MOD[divider];
tim++;
}
class synth
{
private:
public:
synth()
{
}
//*********************************************************************
// Startup default
//*********************************************************************
void begin()
{
output_mode=CHA;
TCCR1A = 0x00; //-Start audio interrupt
TCCR1B = 0x09;
TCCR1C = 0x00;
OCR1A=16000000.0 / FS; //-Auto sample rate
SET(TIMSK1, OCIE1A); //-Start audio interrupt
sei(); //-+
TCCR2A = _BV(COM2A1) | _BV(COM2B1) | _BV(WGM21) | _BV(WGM20);
TCCR2B = _BV(CS20);
OCR2A = OCR2B = 127;
pinMode(3, OUTPUT);
pinMode(11, OUTPUT);
TCCR0A = _BV(COM0A1) | _BV(COM0B1) | _BV(WGM01) | _BV(WGM00);
TCCR0B = _BV(CS20);
OCR0A = OCR0B = 127;
pinMode(5, OUTPUT);
}
//*********************************************************************
// Startup fancy selecting varoius output modes
//*********************************************************************
void begin(unsigned char d)
{
TCCR1A = 0x00; //-Start audio interrupt
TCCR1B = 0x09;
TCCR1C = 0x00;
OCR1A=16000000.0 / FS; //-Auto sample rate
SET(TIMSK1, OCIE1A); //-Start audio interrupt
sei(); //-+
output_mode=d;
switch(d)
{
case DIFF: //-Differntial signal on CHA and CHB pins (11,3)
TCCR2A = 0xB3; //-8 bit audio PWM
TCCR2B = 0x01; // |
OCR2A = OCR2B = 127; //-+
SET(DDRB, 3); //-PWM pin
SET(DDRD, 3); //-PWM pin
break;
case CHB: //-Single ended signal on CHB pin (3)
TCCR2A = 0x23; //-8 bit audio PWM
TCCR2B = 0x01; // |
OCR2A = OCR2B = 127; //-+
SET(DDRD, 3); //-PWM pin
break;
case CHA:
default:
output_mode=CHA; //-Single ended signal in CHA pin (11)
TCCR2A = 0x83; //-8 bit audio PWM
TCCR2B = 0x01; // |
OCR2A = OCR2B = 127; //-+
SET(DDRB, 3); //-PWM pin
break;
}
}
//*********************************************************************
// Timing/sequencing functions
//*********************************************************************
unsigned char synthTick(void)
{
if(tik)
{
tik=0;
return 1; //-True every 4 samples
}
return 0;
}
unsigned char voiceFree(unsigned char voice)
{
if (!(((unsigned char*)&EPCW[voice])[1]&0x80))
return 0;
return 1;
}
//*********************************************************************
// Setup all voice parameters in MIDI range
// voice[0-3],wave[0-6],pitch[0-127],envelope[0-4],length[0-127],mod[0-127:64=no mod]
//*********************************************************************
void setupVoice(unsigned char voice, unsigned char wave, unsigned char pitch, unsigned char env, unsigned char length, unsigned int mod)
{
setWave(voice,wave);
setPitch(voice,pitch);
setEnvelope(voice,env);
setLength(voice,length);
setMod(voice,mod);
}
//*********************************************************************
// Setup wave [0-6]
//*********************************************************************
void setWave(unsigned char voice, unsigned char wave)
{
switch (wave)
{
case TRIANGLE:
wavs[voice] = (unsigned int)TriangleTable;
break;
case SQUARE:
wavs[voice] = (unsigned int)SquareTable;
break;
case SAW:
wavs[voice] = (unsigned int)SawTable;
break;
case RAMP:
wavs[voice] = (unsigned int)RampTable;
break;
case NOISE:
wavs[voice] = (unsigned int)NoiseTable;
break;
default:
wavs[voice] = (unsigned int)SinTable;
break;
}
}
//*********************************************************************
// Setup Pitch [0-127]
//*********************************************************************
void setPitch(unsigned char voice,unsigned char MIDInote)
{
PITCH[voice]=pgm_read_word(&PITCHS[MIDInote]);
}
//*********************************************************************
// Setup Envelope [0-4]
//*********************************************************************
void setEnvelope(unsigned char voice, unsigned char env)
{
switch (env)
{
case 1:
envs[voice] = (unsigned int)Env0;
break;
case 2:
envs[voice] = (unsigned int)Env1;
break;
case 3:
envs[voice] = (unsigned int)Env2;
break;
case 4:
envs[voice] = (unsigned int)Env3;
break;
default:
envs[voice] = (unsigned int)Env0;
break;
}
}
//*********************************************************************
// Setup Length [0-128]
//*********************************************************************
void setLength(unsigned char voice,unsigned char length)
{
EFTW[voice]=pgm_read_word(&EFTWS[length]);
}
//*********************************************************************
// Setup mod
//*********************************************************************
void setMod(unsigned char voice,unsigned char mod)
{
// MOD[voice]=(unsigned int)mod*8;//0-1023 512=no mod
MOD[voice]=(int)mod-64;//0-1023 512=no mod
}
//*********************************************************************
// Midi trigger
//*********************************************************************
void mTrigger(unsigned char voice,unsigned char MIDInote)
{
PITCH[voice]=pgm_read_word(&PITCHS[MIDInote]);
EPCW[voice]=0;
FTW[divider] = PITCH[voice] + (int) (((PITCH[voice]>>6)*(EPCW[voice]>>6))/128)*MOD[voice];
}
//*********************************************************************
// Set frequency direct
//*********************************************************************
void setFrequency(unsigned char voice,float f)
{
PITCH[voice]=f/(FS/65535.0);
}
//*********************************************************************
// Set time
//*********************************************************************
void setTime(unsigned char voice,float t)
{
EFTW[voice]=(1.0/t)/(FS/(32767.5*10.0));//[s];
}
//*********************************************************************
// Simple trigger
//*********************************************************************
void trigger(unsigned char voice)
{
EPCW[voice]=0;
FTW[voice]=PITCH[voice];
// FTW[voice]=PITCH[voice]+(PITCH[voice]*(EPCW[voice]/(32767.5*128.0 ))*((int)MOD[voice]-512));
}
//*********************************************************************
// Suspend/resume synth
//*********************************************************************
void suspend()
{
CLR(TIMSK1, OCIE1A); //-Stop audio interrupt
}
void resume()
{
SET(TIMSK1, OCIE1A); //-Start audio interrupt
}
};
#endif