2024-07-30 04:15:50 -04:00
# include "sid3.h"
2024-07-30 11:15:08 -04:00
# include <math.h>
2024-07-30 04:15:50 -04:00
# define SAFETY_HEADER if(sid3 == NULL) return;
2024-07-30 11:15:08 -04:00
enum State { ATTACK , DECAY_SUSTAIN , RELEASE } ; //for envelope
# ifndef M_PI
# define M_PI 3.14159265358979323846
# endif
//these 4 ones are util only
double square ( double x ) {
return fmod ( x , ( 2 * M_PI ) ) > = M_PI ? - 1 : 1 ;
}
double triangle ( double x ) {
return asin ( sin ( x ) ) / ( M_PI / 2 ) ;
}
double saw ( double x ) {
return atan ( tan ( x / 2 ) ) / ( M_PI / 2 ) ;
}
double rectSquare ( double x ) {
return square ( x ) > 0 ? square ( x ) : 0 ;
}
//===========================
double sinus ( double x ) { //taken from waveEdit.cpp of Furnace tracker
return sin ( x ) ;
}
double rectSin ( double x ) {
return sin ( x ) > 0 ? sin ( x ) : 0 ;
}
double absSin ( double x ) {
return fabs ( sin ( x ) ) ;
}
double quartSin ( double x ) {
return absSin ( x ) * rectSquare ( 2 * x ) ;
}
double squiSin ( double x ) {
return sin ( x ) > = 0 ? sin ( 2 * x ) : 0 ;
}
double squiAbsSin ( double x ) {
return fabs ( squiSin ( x ) ) ;
}
double rectSaw ( double x ) {
return saw ( x ) > 0 ? saw ( x ) : 0 ;
}
double absSaw ( double x ) {
return saw ( x ) < 0 ? saw ( x ) + 1 : saw ( x ) ;
}
double cubSaw ( double x ) {
return pow ( saw ( x ) , 3 ) ;
}
double rectCubSaw ( double x ) {
return pow ( rectSaw ( x ) , 3 ) ;
}
double absCubSaw ( double x ) {
return pow ( absSaw ( x ) , 3 ) ;
}
double cubSine ( double x ) {
return pow ( sin ( x ) , 3 ) ;
}
double rectCubSin ( double x ) {
return pow ( rectSin ( x ) , 3 ) ;
}
double absCubSin ( double x ) {
return pow ( absSin ( x ) , 3 ) ;
}
double quartCubSin ( double x ) {
return pow ( quartSin ( x ) , 3 ) ;
}
double squishCubSin ( double x ) {
return pow ( squiSin ( x ) , 3 ) ;
}
double squishAbsCubSin ( double x ) {
return pow ( squiAbsSin ( x ) , 3 ) ;
}
double rectTri ( double x ) {
return triangle ( x ) > 0 ? triangle ( x ) : 0 ;
}
double absTri ( double x ) {
return fabs ( triangle ( x ) ) ;
}
double quartTri ( double x ) {
return absTri ( x ) * rectSquare ( 2 * x ) ;
}
double squiTri ( double x ) {
return sin ( x ) > = 0 ? triangle ( 2 * x ) : 0 ;
}
double absSquiTri ( double x ) {
return fabs ( squiTri ( x ) ) ;
}
double cubTriangle ( double x ) {
return pow ( triangle ( x ) , 3 ) ;
}
double cubRectTri ( double x ) {
return pow ( rectTri ( x ) , 3 ) ;
}
double cubAbsTri ( double x ) {
return pow ( absTri ( x ) , 3 ) ;
}
double cubQuartTri ( double x ) {
return pow ( quartTri ( x ) , 3 ) ;
}
double cubSquiTri ( double x ) {
return pow ( squiTri ( x ) , 3 ) ;
}
double absCubSquiTri ( double x ) {
return fabs ( cubSquiTri ( x ) ) ;
}
typedef double ( * WaveFunc ) ( double a ) ;
WaveFunc waveFuncs [ ] = {
sinus ,
rectSin ,
absSin ,
quartSin ,
squiSin ,
squiAbsSin ,
rectSaw ,
absSaw ,
cubSaw ,
rectCubSaw ,
absCubSaw ,
cubSine ,
rectCubSin ,
absCubSin ,
quartCubSin ,
squishCubSin ,
squishAbsCubSin ,
rectTri ,
absTri ,
quartTri ,
squiTri ,
absSquiTri ,
cubTriangle ,
cubRectTri ,
cubAbsTri ,
cubQuartTri ,
cubSquiTri ,
absCubSquiTri
} ;
2024-07-30 04:15:50 -04:00
SID3 * sid3_create ( )
{
SID3 * sid3 = ( SID3 * ) malloc ( sizeof ( SID3 ) ) ;
2024-07-30 11:15:08 -04:00
2024-07-30 14:01:17 -04:00
memset ( sid3 , 0 , sizeof ( SID3 ) ) ;
2024-07-30 11:15:08 -04:00
for ( int i = 0 ; i < SID3_NUM_SPECIAL_WAVES ; i + + )
{
for ( int j = 0 ; j < SID3_SPECIAL_WAVE_LENGTH ; j + + )
{
sid3 - > special_waves [ i ] [ j ] = waveFuncs [ i ] ( ( double ) j / ( double ) SID3_SPECIAL_WAVE_LENGTH ) ;
}
}
2024-07-30 04:15:50 -04:00
return sid3 ;
}
void sid3_reset ( SID3 * sid3 )
{
SAFETY_HEADER
2024-07-30 11:15:08 -04:00
for ( int i = 0 ; i < SID3_NUM_CHANNELS - 1 ; i + + )
{
memset ( & sid3 - > chan [ i ] , 0 , sizeof ( sid3_channel ) ) ;
sid3 - > chan [ i ] . accumulator = 0 ;
sid3 - > chan [ i ] . adsr . a = 0x8 ;
sid3 - > chan [ i ] . adsr . d = 0x8 ;
sid3 - > chan [ i ] . adsr . s = 0x80 ;
sid3 - > chan [ i ] . adsr . r = 0x07 ;
2024-07-30 14:01:17 -04:00
sid3 - > chan [ i ] . adsr . vol = 0xf0 ;
sid3 - > chan [ i ] . adsr . hold_zero = true ;
2024-07-30 11:15:08 -04:00
//....
}
//TODO: wavetable chan
}
void sid3_gate_bit ( uint8_t gate_next , uint8_t gate , sid3_channel_adsr * adsr )
{
// The rate counter is never reset, thus there will be a delay before the
// envelope counter starts counting up (attack) or down (release).
// Gate bit on: Start attack, decay, sustain.
if ( ! gate & & gate_next )
{
adsr - > state = ATTACK ;
adsr - > rate_period = adsr - > a < < 8 ; //todo: make it properly
// Switching to attack state unlocks the zero freeze.
adsr - > hold_zero = false ;
adsr - > rate_counter = 0 ;
adsr - > exponential_counter = 0 ;
//envelope_counter = 0;
if ( adsr - > envelope_counter = = 0xff )
{
adsr - > envelope_counter - - ; //idk why it happens, but when envelope has max sustain and I retrigger with new note it just becomes silent so this is the only solution I found so far
}
}
// Gate bit off: Start release.
else if ( gate & & ! gate_next )
{
adsr - > state = RELEASE ;
adsr - > rate_period = adsr - > r < < 8 ; //todo: make it properly
adsr - > rate_counter = 0 ;
adsr - > exponential_counter = 0 ;
}
//gate = gate_next;
}
void sid3_adsr_clock ( sid3_channel_adsr * adsr )
{
if ( adsr - > rate_counter < adsr - > rate_period )
{
adsr - > rate_counter + + ;
}
if ( adsr - > rate_counter > adsr - > rate_period )
{
adsr - > rate_counter = adsr - > rate_period ; //so you can do alternating writes (e.g. writing attack 10-11-10-11-... results in the somewhat average envelope speed)
}
if ( adsr - > rate_counter ! = adsr - > rate_period ) {
return ;
}
adsr - > rate_counter = 0 ;
// The first envelope step in the attack state also resets the exponential
// counter. This has been verified by sampling ENV3.
//
if ( adsr - > state = = ATTACK | | + + adsr - > exponential_counter = = adsr - > exponential_counter_period )
{
adsr - > exponential_counter = 0 ;
// Check whether the envelope counter is frozen at zero.
if ( adsr - > hold_zero )
{
return ;
}
switch ( adsr - > state )
{
case ATTACK :
// The envelope counter can flip from 0xff to 0x00 by changing state to
// release, then to attack. The envelope counter is then frozen at
// zero; to unlock this situation the state must be changed to release,
// then to attack. This has been verified by sampling ENV3.
//
adsr - > envelope_counter + + ;
adsr - > envelope_counter & = 0xff ;
if ( adsr - > envelope_counter = = 0xff )
{
adsr - > state = DECAY_SUSTAIN ;
adsr - > rate_period = ( adsr - > d < < 8 ) ; //todo: do it properly
}
break ;
case DECAY_SUSTAIN :
if ( adsr - > envelope_counter ! = ( adsr - > s ) )
{
- - adsr - > envelope_counter ;
}
break ;
case RELEASE :
// The envelope counter can flip from 0x00 to 0xff by changing state to
// attack, then to release. The envelope counter will then continue
// counting down in the release state.
// This has been verified by sampling ENV3.
// NB! The operation below requires two's complement integer.
//
//--envelope_counter &= 0xff;
adsr - > envelope_counter - - ;
break ;
}
// Check for change of exponential counter period.
switch ( adsr - > envelope_counter )
{
case 0xff :
adsr - > exponential_counter_period = 1 ;
break ;
case 0x5d :
adsr - > exponential_counter_period = 2 ;
break ;
case 0x36 :
adsr - > exponential_counter_period = 4 ;
break ;
case 0x1a :
adsr - > exponential_counter_period = 8 ;
break ;
case 0x0e :
adsr - > exponential_counter_period = 16 ;
break ;
case 0x06 :
adsr - > exponential_counter_period = 30 ;
break ;
case 0x00 :
adsr - > exponential_counter_period = 1 ;
// When the envelope counter is changed to zero, it is frozen at zero.
// This has been verified by sampling ENV3.
adsr - > hold_zero = true ;
break ;
}
}
}
int sid3_adsr_output ( sid3_channel_adsr * adsr , int input )
{
2024-07-30 14:01:17 -04:00
return ( int ) ( ( int64_t ) input * ( int64_t ) adsr - > envelope_counter * ( int64_t ) adsr - > vol / ( int64_t ) SID3_MAX_VOL ) ;
2024-07-30 04:15:50 -04:00
}
void sid3_write ( SID3 * sid3 , uint8_t address , uint8_t data )
{
SAFETY_HEADER
2024-07-30 11:15:08 -04:00
uint8_t channel = address / SID3_REGISTERS_PER_CHANNEL ;
if ( channel > = SID3_NUM_CHANNELS ) return ;
switch ( address % SID3_REGISTERS_PER_CHANNEL ) //NB: only works if registers for each channel are the same and their addresses
//are x + y*n, where x is address of channel 0 register, y is number of current channel and n is how many registers you have
//for each channel
{
case 0 :
{
if ( channel ! = SID3_NUM_CHANNELS - 1 )
{
uint8_t prev_flags = sid3 - > chan [ channel ] . flags & SID3_CHAN_ENABLE_GATE ;
sid3 - > chan [ channel ] . flags = data ;
sid3_gate_bit ( sid3 - > chan [ channel ] . flags & SID3_CHAN_ENABLE_GATE , prev_flags , & sid3 - > chan [ channel ] . adsr ) ;
}
else
{
uint8_t prev_flags = sid3 - > chan [ channel ] . flags & SID3_CHAN_ENABLE_GATE ;
sid3 - > wave_chan . flags = data ;
sid3_gate_bit ( sid3 - > wave_chan . flags & SID3_CHAN_ENABLE_GATE , prev_flags , & sid3 - > wave_chan . adsr ) ;
}
break ;
}
case 1 :
{
if ( channel ! = SID3_NUM_CHANNELS - 1 )
{
sid3 - > chan [ channel ] . adsr . a = data ;
}
else
{
sid3 - > wave_chan . adsr . a = data ;
}
break ;
}
case 2 :
{
if ( channel ! = SID3_NUM_CHANNELS - 1 )
{
sid3 - > chan [ channel ] . adsr . d = data ;
}
else
{
sid3 - > wave_chan . adsr . d = data ;
}
break ;
}
case 3 :
{
if ( channel ! = SID3_NUM_CHANNELS - 1 )
{
sid3 - > chan [ channel ] . adsr . s = data ;
}
else
{
sid3 - > wave_chan . adsr . s = data ;
}
break ;
}
case 4 :
{
if ( channel ! = SID3_NUM_CHANNELS - 1 )
{
sid3 - > chan [ channel ] . adsr . sr = data ;
}
else
{
sid3 - > wave_chan . adsr . sr = data ;
}
break ;
}
case 5 :
{
if ( channel ! = SID3_NUM_CHANNELS - 1 )
{
sid3 - > chan [ channel ] . adsr . r = data ;
}
else
{
sid3 - > wave_chan . adsr . r = data ;
}
break ;
}
case 6 :
{
if ( channel ! = SID3_NUM_CHANNELS - 1 )
{
sid3 - > chan [ channel ] . waveform = data ;
}
else
{
sid3 - > wave_chan . mode = data ;
}
break ;
}
case 7 :
{
if ( channel ! = SID3_NUM_CHANNELS - 1 )
{
sid3 - > chan [ channel ] . pw & = 0x00ff ;
sid3 - > chan [ channel ] . pw | = data < < 8 ;
}
else
{
sid3 - > wave_chan . wave_address = data ;
}
break ;
}
case 8 :
{
if ( channel ! = SID3_NUM_CHANNELS - 1 )
{
sid3 - > chan [ channel ] . pw & = 0xff00 ;
sid3 - > chan [ channel ] . pw | = data ;
}
else
{
sid3 - > wave_chan . wavetable [ sid3 - > wave_chan . wave_address ] = data ;
}
break ;
}
case 9 :
{
if ( channel ! = SID3_NUM_CHANNELS - 1 )
{
sid3 - > chan [ channel ] . special_wave = data ;
}
break ;
}
2024-07-30 14:01:17 -04:00
case 10 :
{
if ( channel ! = SID3_NUM_CHANNELS - 1 )
{
sid3 - > chan [ channel ] . frequency & = 0x00ffff ;
sid3 - > chan [ channel ] . frequency | = data < < 16 ;
}
else
{
sid3 - > wave_chan . frequency & = 0x00ffff ;
sid3 - > wave_chan . frequency | = data < < 16 ;
}
break ;
}
case 11 :
{
if ( channel ! = SID3_NUM_CHANNELS - 1 )
{
sid3 - > chan [ channel ] . frequency & = 0xff00ff ;
sid3 - > chan [ channel ] . frequency | = data < < 8 ;
}
else
{
sid3 - > wave_chan . frequency & = 0xff00ff ;
sid3 - > wave_chan . frequency | = data < < 8 ;
}
break ;
}
case 12 :
{
if ( channel ! = SID3_NUM_CHANNELS - 1 )
{
sid3 - > chan [ channel ] . frequency & = 0xffff00 ;
sid3 - > chan [ channel ] . frequency | = data ;
}
else
{
sid3 - > wave_chan . frequency & = 0xffff00 ;
sid3 - > wave_chan . frequency | = data ;
}
break ;
}
default : break ;
}
}
uint16_t sid3_pulse ( uint32_t acc , uint16_t pw ) // 0-FFFF pulse width range
{
return ( ( ( acc > > ( ( SID3_ACC_BITS - 16 ) ) ) > = ( ( pw = = 0xffff ? pw + 1 : pw ) ) ? ( 0xffff ) : 0 ) ) ;
}
uint16_t sid3_saw ( uint32_t acc )
{
return ( acc > > ( SID3_ACC_BITS - 16 ) ) & ( 0xffff ) ;
}
uint16_t sid3_triangle ( uint32_t acc )
{
return ( ( ( acc < ( 1 < < ( SID3_ACC_BITS - 1 ) ) ) ? ~ acc : acc ) > > ( SID3_ACC_BITS - 17 ) ) ;
}
uint16_t sid3_get_waveform ( sid3_channel * ch )
{
switch ( ch - > mix_mode )
{
case SID3_MIX_8580 :
{
switch ( ch - > waveform )
{
case SID3_WAVE_TRIANGLE :
{
return sid3_triangle ( ch - > accumulator ) ;
break ;
}
case SID3_WAVE_SAW :
{
return sid3_saw ( ch - > accumulator ) ;
break ;
}
case SID3_WAVE_PULSE :
{
return sid3_pulse ( ch - > accumulator , ch - > pw ) ;
break ;
}
}
break ;
}
2024-07-30 11:15:08 -04:00
default : break ;
}
2024-07-30 04:15:50 -04:00
}
void sid3_clock ( SID3 * sid3 )
{
SAFETY_HEADER
2024-07-30 11:15:08 -04:00
2024-07-30 14:01:17 -04:00
sid3 - > output_l = sid3 - > output_r = 0 ;
2024-07-30 11:15:08 -04:00
for ( int i = 0 ; i < SID3_NUM_CHANNELS - 1 ; i + + )
{
2024-07-30 14:01:17 -04:00
sid3_channel * ch = & sid3 - > chan [ i ] ;
uint32_t prev_acc = ch - > accumulator ;
ch - > accumulator + = ch - > frequency ;
if ( ch - > accumulator & ( 1 < < SID3_ACC_BITS ) )
{
ch - > sync_bit = 1 ;
}
else
{
ch - > sync_bit = 0 ;
}
ch - > accumulator & = SID3_ACC_MASK ;
//todo: phase mod
int waveform = sid3_get_waveform ( ch ) ;
waveform - = 0x7fff ;
sid3_adsr_clock ( & ch - > adsr ) ;
sid3 - > output_l + = sid3_adsr_output ( & ch - > adsr , waveform / 1024 ) ;
sid3 - > output_r + = sid3_adsr_output ( & ch - > adsr , waveform / 1024 ) ;
2024-07-30 11:15:08 -04:00
2024-07-30 14:01:17 -04:00
sid3 - > channel_output [ i ] = sid3_adsr_output ( & ch - > adsr , waveform / 1024 ) ;
2024-07-30 11:15:08 -04:00
}
2024-07-30 04:15:50 -04:00
}
void sid3_set_is_muted ( SID3 * sid3 , uint8_t ch , bool mute )
{
SAFETY_HEADER
}
void sid3_free ( SID3 * sid3 )
{
SAFETY_HEADER
free ( sid3 ) ;
}