2
0
Jack Humbert 8 лет назад
Родитель
Сommit
56c47f2aed
2 измененных файлов с 539 добавлено и 0 удалено
  1. 419 0
      quantum/audio/SID.cpp
  2. 120 0
      quantum/audio/SID.h

+ 419 - 0
quantum/audio/SID.cpp

@@ -0,0 +1,419 @@
+/*
+ SID.cpp - Atmega8 MOS6581 SID Emulator
+ Copyright (c) 2007 Christoph Haberer, christoph(at)roboterclub-freiburg.de
+ Arduino Library Conversion by Mario Patino, cybernesto(at)gmail.com
+  
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+ 
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ Lesser General Public License for more details.
+ 
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+ 
+/************************************************************************
+
+	Atmega8 MOS6581 SID Emulator
+
+	SID = Sound Interface Device
+
+	This program tries to emulate the sound chip SID of the famous 
+	historical C64 Commodore computer.
+	The SID emulator includes all registers of the original SID, but 
+	some functions may not be implemented yet.
+	If you want	to program the SID registers to generate your own sound,
+	please refer to the MOS6581 datasheet. The emulator tries to be as
+	compatible as possible.
+
+	In the main program there is an interrupt routine which sets the 
+	ouptut values for the PWM-Output at 62.5kHz. Therefore the high 
+	frequency noise	of the PWM should not be audible to normal people.
+	The output is calculated with a 16kHz sample frequency to save 
+	processing cycles.
+	
+	The envelope generators are updated every 1ms. 
+
+	The amplitude value is output as an 8Bit PWM value. 
+	The PWM-Output may be directly connected to an audio amplifier.
+	
+************************************************************************
+
+	Hardware
+	
+	processor:	ATMEGA8, ATMEGA168
+	clock:		16MHz Crystal
+
+	PIN15	PB1/OC1A		8Bit PWM sound output
+	PIN19	PB0				test LED
+
+***************************************************************************/
+
+#include <avr/interrupt.h>
+
+#include "SID.h"
+
+// attack, decay, release envelope timings
+const static uint16_t AttackRate[16]={2,4,16,24,38,58,68,80,100,250,500,800,1000,3000,5000,8000};
+const static uint16_t DecayReleaseRate[16]={6,24,48,72,114,168,204,240,300,750,1500,2400,3000,9000,15000,24000};
+	
+static uint8_t output;
+static Sid_t Sid;
+static Oscillator_t osc[OSCILLATORS];
+
+void initialize()
+{
+	// TIMER1 used to generate sound output
+	// TIMER1: Fast PWM 8-bit
+	TCCR1A = (1 << WGM10) | (1 << COM1A1) ; 
+	// TIMER1: no prescaling
+	TCCR1B = (1 << WGM12) | (1 << CS10);	
+		
+#if defined(__AVR_ATmega8__)|| defined(__AVR_ATmega128__)
+	// TIMER2 used to generate sample and ms interrupts
+	// TIMER2: Normal Mode
+	TCCR2 = 0;
+	// TIMER2: clock/8 prescaling
+	TCCR2 |= (1 << CS21);
+	// TIMER2: set compare value to generate a 16kHz sample rate
+	OCR2 = SAMPLERATECOUNT;	
+	// interrupt mask register: enable timer2 OCR2A interrupt	
+	TIMSK = (1 << OCIE2);
+	
+	// interrupt mask register: enable timer1 overflow
+	TIMSK |= (1 << TOIE1);	
+#else
+	// TIMER2 used to generate sample and ms interrupts
+	// TIMER2: Normal Mode
+	TCCR2A = 0 ;
+	// TIMER2: clock/8 prescaling 
+	TCCR2B = (1 << CS21);
+	// TIMER2: set compare value to generate a 16kHz sample rate
+	OCR2A = SAMPLERATECOUNT;
+	// interrupt mask register: enable timer2 OCR2A interrupt
+	TIMSK2 = (1 << OCIE2A);
+	
+	// interrupt mask register: enable timer1 overflow	
+	TIMSK1 = (1 << TOIE1);
+#endif
+}
+
+static int8_t wave(Voice_t *voice, uint16_t phase)
+{
+	int8_t out;
+	uint8_t n = phase >> 8;
+	uint8_t wavetype = voice->ControlReg;
+	
+	if(wavetype & SAWTOOTH)  
+	{
+		out = n - 128;
+	}
+
+	if(wavetype & TRIANGLE) 
+	{
+		if(n&0x80) 
+			out = ((n^0xFF)<<1)-128;
+		else 
+			out = (n<<1)-128;
+	}
+
+	if(wavetype & RECTANGLE) 
+	{
+		if(n > (voice->PW >> 4)) // SID has 12Bit pwm, here we use only 8Bit
+			out = 127;
+		else 
+			out = -127;
+	}
+		
+	return out;
+}
+
+static void waveforms()
+{
+	static uint16_t phase[3], sig[3];
+	static int16_t temp,temp1;
+	static uint8_t i,j,k;
+	static uint16_t noise = 0xACE1;
+	static uint8_t noise8;
+	static uint16_t tempphase;
+
+	// noise generator based on Galois LFSR
+	noise = (noise >> 1) ^ (-(noise & 1) & 0xB400u);	
+	noise8 = noise>>8;
+	
+	for(i = 0; i< 3; i++)
+	{
+		j = (i == 0 ? 2 : i - 1);
+		tempphase=phase[i]+osc[i].freq_coefficient; //0.88us
+		if(Sid.block.voice[i].ControlReg&NOISE)
+		{				
+			if((tempphase^phase[i])&0x4000) sig[i]=noise8*osc[i].envelope;			
+		}
+		else
+		{
+			if(Sid.block.voice[i].ControlReg&RINGMOD)
+			{				
+				if(phase[j]&0x8000) 
+					sig[i]=osc[i].envelope*-wave(&Sid.block.voice[i],phase[i]);
+				else 
+					sig[i]=osc[i].envelope*wave(&Sid.block.voice[i],phase[i]);
+			}
+			else 
+			{
+				if(Sid.block.voice[i].ControlReg&SYNC)
+				{
+					if(tempphase < phase[j]) 
+						phase[i] = 0;
+				}
+				else 
+					sig[i]=osc[i].envelope*wave(&Sid.block.voice[i],phase[i]); //2.07us
+			}
+		}
+		phase[i]=tempphase;
+	}
+	
+	// voice filter selection
+	temp=0; // direct output variable
+	temp1=0; // filter output variable
+	if(Sid.block.RES_Filt&FILT1) temp1+=sig[0];
+	else temp+=sig[0];
+	if(Sid.block.RES_Filt&FILT2) temp1+=sig[1];
+	else temp+=sig[1];
+	if(Sid.block.RES_Filt&FILT3) temp1+=sig[2];
+	else if(!(Sid.block.Mode_Vol&VOICE3OFF))temp+=sig[2]; // voice 3 with special turn off bit
+
+	//filterOutput = IIR2((struct IIR_filter*)&filter04_06, filterInput);
+	//IIR2(filter04_06, temp1);
+	k=(temp>>8)+128;
+	k+=temp1>>10; // no real filter implemeted yet
+	
+	output = k; // Output to PWM
+}
+
+
+static void envelopes()
+{
+	uint8_t n;
+	uint8_t controll_regadr[3]={4,11,18};
+	// if gate is ONE then the attack,decay,sustain cycle begins
+	// if gate switches to zero the sound decays
+	for(n=0;n<OSCILLATORS;n++)
+	{
+		if(Sid.sidregister[controll_regadr[n]]&GATE) // if gate set then attack,decay,sustain
+		{
+			if(osc[n].attackdecay_flag) 
+			{	// if attack cycle
+				osc[n].amp+=osc[n].m_attack;
+				if(osc[n].amp>MAXLEVEL)
+				{
+					osc[n].amp=MAXLEVEL;
+					osc[n].attackdecay_flag=false; // if level reached, then switch to decay
+				}
+			}
+			else // decay cycle
+			{
+				if(osc[n].amp>osc[n].level_sustain)
+				{
+					osc[n].amp-=osc[n].m_decay;
+					if(osc[n].amp<osc[n].level_sustain) osc[n].amp=osc[n].level_sustain;
+				}
+
+			} 
+		}
+		else // if gate flag is not set then release
+		{
+			osc[n].attackdecay_flag=true; // at next attack/decay cycle start wiht attack
+			if(osc[n].amp>0)
+			{
+				osc[n].amp-=osc[n].m_release;
+				if(osc[n].amp<0) osc[n].amp=0;
+			}			
+		}
+		osc[n].envelope=osc[n].amp>>8;
+	}
+}
+
+/************************************************************************
+
+	interrupt routine timer 1 overflow
+	- set PWM output
+	
+************************************************************************/
+ISR(TIMER1_OVF_vect)
+{
+	OCR1A = output; // Output to PWM
+}
+
+
+/************************************************************************
+
+	interrupt routine timer 2 16kHz
+	- calculate waverform phases
+	- calculate waveforms
+	- calculate attack decay release (1kHz)
+	
+************************************************************************/
+#if defined(__AVR_ATmega8__)|| defined(__AVR_ATmega128__)
+ISR(TIMER2_COMP_vect)
+{
+	static uint8_t mscounter = 0;
+	OCR2 += SAMPLERATECOUNT; // Output to PWM
+	waveforms(); //~22us
+	
+	if(mscounter++ >= MSCOUNT)
+	{
+		envelopes(); //~16us
+		mscounter = 0;
+	}
+}
+#else
+
+ISR(TIMER2_COMPA_vect)
+{
+	static uint8_t mscounter = 0;
+	OCR2A += SAMPLERATECOUNT; // Output to PWM
+	waveforms(); //~36us
+	
+	if(mscounter++ >= MSCOUNT)
+	{
+		envelopes(); //~16us
+		mscounter = 0;
+	}
+}
+#endif	
+
+// Constructor /////////////////////////////////////////////////////////////////
+// Function that handles the creation and setup of instances
+
+void SID::begin()
+{
+	pinMode(9, OUTPUT);
+	initialize();
+		
+	//initialize SID-registers	
+	Sid.sidregister[6]=0xF0;
+	Sid.sidregister[13]=0xF0;
+	Sid.sidregister[20]=0xF0;
+
+	
+	// set all amplitudes to zero
+	for(int n=0;n<OSCILLATORS;n++) {
+		osc[n].attackdecay_flag=true;
+		setenvelope(&Sid.block.voice[n]);
+		osc[n].amp=0;
+	}
+}
+
+
+// Public Methods //////////////////////////////////////////////////////////////
+// Functions available in Wiring sketches, this library, and other libraries
+/************************************************************************
+	
+	uint8_t set_sidregister(uint8_t regnum, uint8_t value)
+
+	The registers of the virtual SID are set by this routine.
+	For some registers it is necessary to transform the SID-register
+	values to some internal settings of the emulator. 
+	To select this registers and to start the calculation, the switch/
+	case statement is used.
+	For instance: If setting the SID envelope register, new attach, decay
+	sustain times are calculated.
+	If an invalid register is requested the returned value will be 0.
+
+	4.2007 ch
+
+************************************************************************/
+uint8_t SID::set_register(uint8_t regnum, uint8_t value)
+{
+	if(regnum>NUMREGISTERS-1) 
+		return 0;
+		
+	Sid.sidregister[regnum]=value;
+
+	switch(regnum)
+	{
+		//voice1
+		case 1:
+			osc[0].freq_coefficient=((uint16_t)Sid.sidregister[0]+((uint16_t)Sid.sidregister[1]<<8))>>2;
+			break;
+		case 5: setenvelope(&Sid.block.voice[0]);break;
+		case 6: setenvelope(&Sid.block.voice[0]);break;
+		
+		//voice2
+		case 8:
+			osc[1].freq_coefficient=((uint16_t)Sid.sidregister[7]+((uint16_t)Sid.sidregister[8]<<8))>>2;
+			break;
+		case 12: setenvelope(&Sid.block.voice[1]);break;
+		case 13: setenvelope(&Sid.block.voice[1]);break;		
+		
+		//voice3
+		case 15:
+			osc[2].freq_coefficient=((uint16_t)Sid.sidregister[14]+((uint16_t)Sid.sidregister[15]<<8))>>2;
+			break;
+		case 19: setenvelope(&Sid.block.voice[2]);break;
+		case 20: setenvelope(&Sid.block.voice[2]);break;			
+	}	
+	return 1;
+}
+
+/************************************************************************
+	
+	uint8_t get_sidregister(uint8_t regnum)
+
+	The registers of the virtual SID are read by this routine.
+	If an invalid register is requested it returns zero.
+
+************************************************************************/
+uint8_t SID::get_register(uint8_t regnum)
+{
+	if(regnum>NUMREGISTERS-1)
+		return 0;
+    return Sid.sidregister[regnum];
+}
+
+// Private Methods /////////////////////////////////////////////////////////////
+// Functions only available to other functions in this library
+
+uint8_t SID::get_wavenum(Voice_t *voice)
+{
+	uint8_t n;
+
+	if(voice==&Sid.block.voice[0]) n=0;
+	if(voice==&Sid.block.voice[1]) n=1;
+	if(voice==&Sid.block.voice[2]) n=2;
+	
+	return n;
+}
+
+void SID::setfreq(Voice_t *voice,uint16_t freq)
+{
+	uint32_t templong;
+	uint8_t n;
+
+	n=get_wavenum(voice);
+	
+	templong=freq;
+	osc[n].freq_coefficient=templong*4000/SAMPLEFREQ;
+}
+
+
+void SID::setenvelope(Voice_t *voice)
+{
+	uint8_t n;
+	
+	n=get_wavenum(voice);
+	osc[n].attackdecay_flag=true;
+
+	osc[n].level_sustain=(voice->SustainRelease>>4)*SUSTAINFACTOR;
+	osc[n].m_attack=MAXLEVEL/AttackRate[voice->AttackDecay>>4];
+	osc[n].m_decay=(MAXLEVEL-osc[n].level_sustain*SUSTAINFACTOR)/DecayReleaseRate[voice->AttackDecay&0x0F];
+	osc[n].m_release=(osc[n].level_sustain)/DecayReleaseRate[voice->SustainRelease&0x0F];
+}
+
+

+ 120 - 0
quantum/audio/SID.h

@@ -0,0 +1,120 @@
+/*
+ SID.h - Atmega8 MOS6581 SID Emulator
+ Copyright (c) 2007 Christoph Haberer, christoph(at)roboterclub-freiburg.de
+ Arduino Library Conversion by Mario Patino, cybernesto(at)gmail.com
+ 
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+ 
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ Lesser General Public License for more details.
+ 
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+// ensure this library description is only included once
+#ifndef SID_h
+#define SID_h
+
+#include <inttypes.h>
+
+#define NUMREGISTERS 29
+#define OSCILLATORS 3
+#define MAXLEVEL ( 0xFFFF / OSCILLATORS )
+#define SUSTAINFACTOR ( MAXLEVEL / 15 )
+
+#define SAMPLEFREQ 16000L
+#define SAMPLERATECOUNT (F_CPU/(8*SAMPLEFREQ)-1)
+
+#define ENVELOPE_FREQ 1000L  
+#define MSCOUNT (SAMPLEFREQ/ENVELOPE_FREQ-1)
+
+
+// SID Registers
+#define VOICE1	0
+#define VOICE2	7
+#define VOICE3	14
+#define CONTROLREG 4
+#define ATTACKDECAY 5
+#define SUSTAINRELEASE 6
+
+// SID voice control register bits
+#define GATE (1<<0)
+#define SYNC (1<<1)
+#define RINGMOD (1<<2)
+#define TEST (1<<3)		// not implemented
+#define TRIANGLE (1<<4)
+#define SAWTOOTH (1<<5)
+#define RECTANGLE (1<<6)
+#define NOISE (1<<7)
+
+// SID RES/FILT ( reg.23 )
+#define FILT1 (1<<0)
+#define FILT2 (1<<1)
+#define FILT3 (1<<2)
+// SID MODE/VOL ( reg.24 )  
+#define VOICE3OFF (1<<7)
+
+typedef struct
+{
+	uint16_t	Freq;			// Frequency: FreqLo/FreqHi
+	uint16_t	PW;				// PulseWidth: PW LO/HI only 12 bits used in SID
+	uint8_t		ControlReg;		// NOISE,RECTANGLE,SAWTOOTH,TRIANGLE,TEST,RINGMOD,SYNC,GATE
+	uint8_t		AttackDecay;	// bit0-3 decay, bit4-7 attack
+	uint8_t		SustainRelease;	// bit0-3 release, bit4-7 sustain
+} Voice_t;
+
+typedef struct
+{
+	Voice_t voice[3];
+	uint16_t FC;		// not implemented
+	uint8_t RES_Filt;	// partly implemented
+	uint8_t Mode_Vol;	// partly implemented
+	uint8_t POTX;		// not implemented
+	uint8_t POTY;		// not implemented
+	uint8_t OSC3_Random;// not implemented
+	uint8_t ENV3;		// not implemented
+} Blocks_t;
+
+typedef union
+{
+	Blocks_t block;
+	uint8_t sidregister[NUMREGISTERS];
+} Sid_t;
+
+typedef struct
+{
+	uint16_t freq_coefficient;
+	uint8_t envelope;
+	uint16_t m_attack;
+	uint16_t m_decay;
+	uint16_t m_release;
+	uint8_t	attackdecay_flag;
+	int16_t	level_sustain;	
+	int16_t amp;
+} Oscillator_t;
+
+// library interface description
+class SID
+{
+  // user-accessible "public" interface
+  public:
+	void begin();
+    uint8_t set_register(uint8_t regnum, uint8_t value);
+    uint8_t get_register(uint8_t regnum);
+  // library-accessible "private" interface
+  private:
+    uint8_t get_wavenum(Voice_t *voice);
+	void setfreq(Voice_t *voice,uint16_t freq);
+	void init_waveform(Voice_t *voice);
+	void setenvelope(Voice_t *voice);
+};
+
+#endif
+