Removed unused TIASound emulation library.

git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@766 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
This commit is contained in:
bwmott 2005-09-05 00:01:43 +00:00
parent df183c6e35
commit 58dcda81b5
3 changed files with 0 additions and 1077 deletions

View File

@ -1,713 +0,0 @@
/*****************************************************************************/
/* */
/* Module: TIA Chip Sound Simulator */
/* Purpose: To emulate the sound generation hardware of the Atari TIA chip. */
/* Author: Ron Fries */
/* */
/* Revision History: */
/* 10-Sep-96 - V1.0 - Initial Release */
/* 14-Jan-97 - V1.1 - Cleaned up sound output by eliminating counter */
/* reset. */
/* */
/*****************************************************************************/
/* */
/* License Information and Copyright Notice */
/* ======================================== */
/* */
/* TiaSound is Copyright(c) 1996 by Ron Fries */
/* */
/* This library is free software; you can redistribute it and/or modify it */
/* under the terms of version 2 of the GNU Library General Public License */
/* as published by the Free Software Foundation. */
/* */
/* 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 Library */
/* General Public License for more details. */
/* To obtain a copy of the GNU Library General Public License, write to the */
/* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
/* */
/* Any permitted reproduction of these routines, in whole or in part, must */
/* bear this legend. */
/* */
/*****************************************************************************/
#ifdef SOUND_SUPPORT
// For some braindead reason, Visual C++ won't let bspf.hxx be included
// here, so I have to redefine the following types (I hate Windows ...)
typedef signed char Int8;
typedef unsigned char uInt8;
typedef signed short Int16;
typedef unsigned short uInt16;
typedef signed int Int32;
typedef unsigned int uInt32;
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
/* CONSTANT DEFINITIONS */
/* definitions for AUDCx (15, 16) */
#define SET_TO_1 0x00 /* 0000 */
#define POLY4 0x01 /* 0001 */
#define DIV31_POLY4 0x02 /* 0010 */
#define POLY5_POLY4 0x03 /* 0011 */
#define PURE 0x04 /* 0100 */
#define PURE2 0x05 /* 0101 */
#define DIV31_PURE 0x06 /* 0110 */
#define POLY5_2 0x07 /* 0111 */
#define POLY9 0x08 /* 1000 */
#define POLY5 0x09 /* 1001 */
#define DIV31_POLY5 0x0a /* 1010 */
#define POLY5_POLY5 0x0b /* 1011 */
#define DIV3_PURE 0x0c /* 1100 */
#define DIV3_PURE2 0x0d /* 1101 */
#define DIV93_PURE 0x0e /* 1110 */
#define DIV3_POLY5 0x0f /* 1111 */
#define DIV3_MASK 0x0c
#define AUDC0 0x15
#define AUDC1 0x16
#define AUDF0 0x17
#define AUDF1 0x18
#define AUDV0 0x19
#define AUDV1 0x1a
/* the size (in entries) of the 4 polynomial tables */
#define POLY4_SIZE 0x000f
#define POLY5_SIZE 0x001f
#define POLY9_SIZE 0x01ff
/* channel definitions */
#define CHAN1 0
#define CHAN2 1
#define FALSE 0
#define TRUE 1
/* LOCAL GLOBAL VARIABLE DEFINITIONS */
/* structures to hold the 6 tia sound control bytes */
static uInt8 AUDC[2]; /* AUDCx (15, 16) */
static uInt8 AUDF[2]; /* AUDFx (17, 18) */
static uInt8 AUDV[2]; /* AUDVx (19, 1A) */
static uInt8 Outvol[2]; /* last output volume for each channel */
static uInt32 volume; /* output sample volume percentage */
/* Initialze the bit patterns for the polynomials. */
/* The 4bit and 5bit patterns are the identical ones used in the tia chip. */
/* Though the patterns could be packed with 8 bits per byte, using only a */
/* single bit per byte keeps the math simple, which is important for */
/* efficient processing. */
static uInt8 Bit4[POLY4_SIZE] =
{ 1,1,0,1,1,1,0,0,0,0,1,0,1,0,0 };
static uInt8 Bit5[POLY5_SIZE] =
{ 0,0,1,0,1,1,0,0,1,1,1,1,1,0,0,0,1,1,0,1,1,1,0,1,0,1,0,0,0,0,1 };
/* I've treated the 'Div by 31' counter as another polynomial because of */
/* the way it operates. It does not have a 50% duty cycle, but instead */
/* has a 13:18 ratio (of course, 13+18 = 31). This could also be */
/* implemented by using counters. */
static uInt8 Div31[POLY5_SIZE] =
{ 0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0 };
/* Rather than have a table with 511 entries, I use a random number */
/* generator. */
static uInt8 Bit9[POLY9_SIZE];
static uInt8 P4[2]; /* Position pointer for the 4-bit POLY array */
static uInt8 P5[2]; /* Position pointer for the 5-bit POLY array */
static uInt16 P9[2]; /* Position pointer for the 9-bit POLY array */
static uInt8 Div_n_cnt[2]; /* Divide by n counter. one for each channel */
static uInt8 Div_n_max[2]; /* Divide by n maximum, one for each channel */
/* In my routines, I treat the sample output as another divide by N counter. */
/* For better accuracy, the Samp_n_cnt has a fixed binary decimal point */
/* which has 8 binary digits to the right of the decimal point. */
static uInt16 Samp_n_max; /* Sample max, multiplied by 256 */
static uInt16 Samp_n_cnt; /* Sample cnt. */
/*****************************************************************************/
/* Module: Tia_sound_init() */
/* Purpose: to handle the power-up initialization functions */
/* these functions should only be executed on a cold-restart */
/* */
/* Author: Ron Fries */
/* Date: September 10, 1996 */
/* */
/* Inputs: sample_freq - the value for the '30 Khz' Tia audio clock */
/* playback_freq - the playback frequency in samples per second */
/* */
/* Outputs: Adjusts local globals - no return value */
/* */
/*****************************************************************************/
void Tia_sound_init (uInt16 sample_freq, uInt16 playback_freq)
{
uInt8 chan;
Int16 n;
/* fill the 9bit polynomial with random bits */
for (n=0; n<POLY9_SIZE; n++)
{
Bit9[n] = rand() & 0x01; /* fill poly9 with random bits */
}
/* calculate the sample 'divide by N' value based on the playback freq. */
Samp_n_max = (uInt16)(((uInt32)sample_freq<<8)/playback_freq);
Samp_n_cnt = 0; /* initialize all bits of the sample counter */
/* initialize the local globals */
for (chan = CHAN1; chan <= CHAN2; chan++)
{
Outvol[chan] = 0;
Div_n_cnt[chan] = 0;
Div_n_max[chan] = 0;
AUDC[chan] = 0;
AUDF[chan] = 0;
AUDV[chan] = 0;
P4[chan] = 0;
P5[chan] = 0;
P9[chan] = 0;
}
volume = 100;
}
/*****************************************************************************/
/* Module: Update_tia_sound() */
/* Purpose: To process the latest control values stored in the AUDF, AUDC, */
/* and AUDV registers. It pre-calculates as much information as */
/* possible for better performance. This routine has not been */
/* optimized. */
/* */
/* Author: Ron Fries */
/* Date: January 14, 1997 */
/* */
/* Inputs: addr - the address of the parameter to be changed */
/* val - the new value to be placed in the specified address */
/* */
/* Outputs: Adjusts local globals - no return value */
/* */
/*****************************************************************************/
void Update_tia_sound (uInt16 addr, uInt8 val)
{
uInt16 new_val = 0;
uInt8 chan;
/* determine which address was changed */
switch (addr)
{
case AUDC0:
AUDC[0] = val & 0x0f;
chan = 0;
break;
case AUDC1:
AUDC[1] = val & 0x0f;
chan = 1;
break;
case AUDF0:
AUDF[0] = val & 0x1f;
chan = 0;
break;
case AUDF1:
AUDF[1] = val & 0x1f;
chan = 1;
break;
case AUDV0:
AUDV[0] = (val & 0x0f) << 3;
chan = 0;
break;
case AUDV1:
AUDV[1] = (val & 0x0f) << 3;
chan = 1;
break;
default:
chan = 255;
break;
}
/* if the output value changed */
if (chan != 255)
{
/* an AUDC value of 0 is a special case */
if (AUDC[chan] == SET_TO_1)
{
/* indicate the clock is zero so no processing will occur */
new_val = 0;
/* and set the output to the selected volume */
Outvol[chan] = AUDV[chan];
}
else
{
/* otherwise calculate the 'divide by N' value */
new_val = AUDF[chan] + 1;
/* if bits 2 & 3 are set, then multiply the 'div by n' count by 3 */
if ((AUDC[chan] & DIV3_MASK) == DIV3_MASK)
{
new_val *= 3;
}
}
/* only reset those channels that have changed */
if (new_val != Div_n_max[chan])
{
/* reset the divide by n counters */
Div_n_max[chan] = new_val;
/* if the channel is now volume only or was volume only */
if ((Div_n_cnt[chan] == 0) || (new_val == 0))
{
/* reset the counter (otherwise let it complete the previous) */
Div_n_cnt[chan] = new_val;
}
}
}
}
/*****************************************************************************/
/* Module: Tia_process() */
/* Purpose: To fill the output buffer with the sound output based on the */
/* tia chip parameters. This routine has been optimized. */
/* */
/* Author: Ron Fries */
/* Date: September 10, 1996 */
/* */
/* Inputs: *buffer - pointer to the buffer where the audio output will */
/* be placed */
/* n - size of the playback buffer */
/* */
/* Outputs: the buffer will be filled with n bytes of audio - no return val */
/* */
/*****************************************************************************/
void Tia_process (register unsigned char *buffer, register uInt16 n)
{
register uInt8 audc0,audv0,audc1,audv1;
register uInt8 div_n_cnt0,div_n_cnt1;
register uInt8 p5_0, p5_1,outvol_0,outvol_1;
audc0 = AUDC[0];
audv0 = AUDV[0];
audc1 = AUDC[1];
audv1 = AUDV[1];
/* make temporary local copy */
p5_0 = P5[0];
p5_1 = P5[1];
outvol_0 = Outvol[0];
outvol_1 = Outvol[1];
div_n_cnt0 = Div_n_cnt[0];
div_n_cnt1 = Div_n_cnt[1];
/* loop until the buffer is filled */
while (n)
{
/* Process channel 0 */
if (div_n_cnt0 > 1)
{
div_n_cnt0--;
}
else if (div_n_cnt0 == 1)
{
div_n_cnt0 = Div_n_max[0];
/* the P5 counter has multiple uses, so we inc it here */
p5_0++;
if (p5_0 == POLY5_SIZE)
p5_0 = 0;
/* check clock modifier for clock tick */
if (((audc0 & 0x02) == 0) ||
(((audc0 & 0x01) == 0) && Div31[p5_0]) ||
(((audc0 & 0x01) == 1) && Bit5[p5_0]))
{
if (audc0 & 0x04) /* pure modified clock selected */
{
if (outvol_0) /* if the output was set */
outvol_0 = 0; /* turn it off */
else
outvol_0 = audv0; /* else turn it on */
}
else if (audc0 & 0x08) /* check for p5/p9 */
{
if (audc0 == POLY9) /* check for poly9 */
{
/* inc the poly9 counter */
P9[0]++;
if (P9[0] == POLY9_SIZE)
P9[0] = 0;
if (Bit9[P9[0]])
outvol_0 = audv0;
else
outvol_0 = 0;
}
else /* must be poly5 */
{
if (Bit5[p5_0])
outvol_0 = audv0;
else
outvol_0 = 0;
}
}
else /* poly4 is the only remaining option */
{
/* inc the poly4 counter */
P4[0]++;
if (P4[0] == POLY4_SIZE)
P4[0] = 0;
if (Bit4[P4[0]])
outvol_0 = audv0;
else
outvol_0 = 0;
}
}
}
/* Process channel 1 */
if (div_n_cnt1 > 1)
{
div_n_cnt1--;
}
else if (div_n_cnt1 == 1)
{
div_n_cnt1 = Div_n_max[1];
/* the P5 counter has multiple uses, so we inc it here */
p5_1++;
if (p5_1 == POLY5_SIZE)
p5_1 = 0;
/* check clock modifier for clock tick */
if (((audc1 & 0x02) == 0) ||
(((audc1 & 0x01) == 0) && Div31[p5_1]) ||
(((audc1 & 0x01) == 1) && Bit5[p5_1]))
{
if (audc1 & 0x04) /* pure modified clock selected */
{
if (outvol_1) /* if the output was set */
outvol_1 = 0; /* turn it off */
else
outvol_1 = audv1; /* else turn it on */
}
else if (audc1 & 0x08) /* check for p5/p9 */
{
if (audc1 == POLY9) /* check for poly9 */
{
/* inc the poly9 counter */
P9[1]++;
if (P9[1] == POLY9_SIZE)
P9[1] = 0;
if (Bit9[P9[1]])
outvol_1 = audv1;
else
outvol_1 = 0;
}
else /* must be poly5 */
{
if (Bit5[p5_1])
outvol_1 = audv1;
else
outvol_1 = 0;
}
}
else /* poly4 is the only remaining option */
{
/* inc the poly4 counter */
P4[1]++;
if (P4[1] == POLY4_SIZE)
P4[1] = 0;
if (Bit4[P4[1]])
outvol_1 = audv1;
else
outvol_1 = 0;
}
}
}
/* decrement the sample counter - value is 256 since the lower
byte contains the fractional part */
Samp_n_cnt -= 256;
/* if the count down has reached zero */
if (Samp_n_cnt < 256)
{
/* adjust the sample counter */
Samp_n_cnt += Samp_n_max;
/* calculate the latest output value and place in buffer
scale the volume by 128, since this is the default silence value
when using unsigned 8-bit samples in SDL */
*(buffer++) = ((uInt8) ((((uInt32)outvol_0 + (uInt32)outvol_1) * volume) / 100))/2 + 128;
/* *(buffer++) = ((((uInt32)outvol_0 + (uInt32)outvol_1) * volume) / 100); */
/* and indicate one less byte to process */
n--;
}
}
/* save for next round */
P5[0] = p5_0;
P5[1] = p5_1;
Outvol[0] = outvol_0;
Outvol[1] = outvol_1;
Div_n_cnt[0] = div_n_cnt0;
Div_n_cnt[1] = div_n_cnt1;
}
/*****************************************************************************/
/* Module: Tia_get_registers() */
/* Purpose: Returns the 6 TIA sound registers for use in state */
/* loading and saving. */
/* */
/* Author: Stephen Anthony */
/* Date: October 31, 2002 */
/* */
/* Inputs: reg .. reg6 - pointers to the variables where the registers */
/* will be placed */
/* */
/*****************************************************************************/
void Tia_get_registers (unsigned char *reg1, unsigned char *reg2, unsigned char *reg3,
unsigned char *reg4, unsigned char *reg5, unsigned char *reg6)
{
*reg1 = AUDC[0];
*reg2 = AUDC[1];
*reg3 = AUDF[0];
*reg4 = AUDF[1];
*reg5 = AUDV[0];
*reg6 = AUDV[1];
}
/*****************************************************************************/
/* Module: Tia_set_registers() */
/* Purpose: Sets the 6 TIA sound registers for use in state */
/* loading and saving. */
/* */
/* Author: Stephen Anthony */
/* Date: October 31, 2002 */
/* */
/* Inputs: reg .. reg6 - the registers to be set */
/* */
/*****************************************************************************/
void Tia_set_registers (unsigned char reg1, unsigned char reg2, unsigned char reg3,
unsigned char reg4, unsigned char reg5, unsigned char reg6)
{
AUDC[0] = reg1;
AUDC[1] = reg2;
AUDF[0] = reg3;
AUDF[1] = reg4;
AUDV[0] = reg5;
AUDV[1] = reg6;
}
/*****************************************************************************/
/* Module: Tia_clear_registers() */
/* Purpose: Clears all TIA sound registers; soft reboot */
/* */
/* Author: Stephen Anthony */
/* Date: May 12, 2005 */
/* */
/*****************************************************************************/
void Tia_clear_registers ()
{
uInt8 chan;
for (chan = CHAN1; chan <= CHAN2; chan++)
{
Outvol[chan] = 0;
Div_n_cnt[chan] = 0;
Div_n_max[chan] = 0;
AUDC[chan] = 0;
AUDF[chan] = 0;
AUDV[chan] = 0;
P4[chan] = 0;
P5[chan] = 0;
P9[chan] = 0;
}
}
/*****************************************************************************/
/* Module: Tia_volume() */
/* Purpose: Set volume to the specified percentage */
/* */
/* Author: Bradford W. Mott */
/* Date: June 12, 2004 */
/* */
/*****************************************************************************/
void Tia_volume (unsigned int percent)
{
if((percent >= 0) && (percent <= 100))
{
volume = percent;
}
}
#ifdef __cplusplus
}
#endif
#if 0
/*****************************************************************************/
/* Module: Tia_process_2() */
/* Purpose: To fill the output buffer with the sound output based on the */
/* tia chip parameters. This routine has not been optimized. */
/* Though it is not used by the program, I've left it for reference.*/
/* */
/* Author: Ron Fries */
/* Date: September 10, 1996 */
/* */
/* Inputs: *buffer - pointer to the buffer where the audio output will */
/* be placed */
/* n - size of the playback buffer */
/* */
/* Outputs: the buffer will be filled with n bytes of audio - no return val */
/* */
/*****************************************************************************/
void Tia_process_2 (register unsigned char *buffer, register uInt16 n)
{
register uInt8 chan;
/* loop until the buffer is filled */
while (n)
{
/* loop through the channels */
for (chan = CHAN1; chan <= CHAN2; chan++)
{
/* NOTE: this routine intentionally does not count down to zero */
/* since 0 is used as a special case - no clock */
/* if the divide by N counter can count down */
if (Div_n_cnt[chan] > 1)
{
/* decrement and loop */
Div_n_cnt[chan]--;
}
/* otherwise if we've reached the bottom */
else if (Div_n_cnt[chan] == 1)
{
/* reset the counter */
Div_n_cnt[chan] = Div_n_max[chan];
/* the P5 counter has multiple uses, so we inc it here */
P5[chan]++;
if (P5[chan] == POLY5_SIZE)
P5[chan] = 0;
/* check clock modifier for clock tick */
/* if we're using pure tones OR
we're using DIV31 and the DIV31 bit is set OR
we're using POLY5 and the POLY5 bit is set */
if (((AUDC[chan] & 0x02) == 0) ||
(((AUDC[chan] & 0x01) == 0) && Div31[P5[chan]]) ||
(((AUDC[chan] & 0x01) == 1) && Bit5[P5[chan]]))
{
if (AUDC[chan] & 0x04) /* pure modified clock selected */
{
if (Outvol[chan]) /* if the output was set */
Outvol[chan] = 0; /* turn it off */
else
Outvol[chan] = AUDV[chan]; /* else turn it on */
}
else if (AUDC[chan] & 0x08) /* check for p5/p9 */
{
if (AUDC[chan] == POLY9) /* check for poly9 */
{
/* inc the poly9 counter */
P9[chan]++;
if (P9[chan] == POLY9_SIZE)
P9[chan] = 0;
if (Bit9[P9[chan]]) /* if poly9 bit is set */
Outvol[chan] = AUDV[chan];
else
Outvol[chan] = 0;
}
else /* must be poly5 */
{
if (Bit5[P5[chan]])
Outvol[chan] = AUDV[chan];
else
Outvol[chan] = 0;
}
}
else /* poly4 is the only remaining option */
{
/* inc the poly4 counter */
P4[chan]++;
if (P4[chan] == POLY4_SIZE)
P4[chan] = 0;
if (Bit4[P4[chan]])
Outvol[chan] = AUDV[chan];
else
Outvol[chan] = 0;
}
}
}
}
/* decrement the sample counter - value is 256 since the lower
byte contains the fractional part */
Samp_n_cnt -= 256;
/* if the count down has reached zero */
if (Samp_n_cnt < 256)
{
/* adjust the sample counter */
Samp_n_cnt += Samp_n_max;
/* calculate the latest output value and place in buffer */
*(buffer++) = Outvol[0] + Outvol[1];
/* and indicate one less byte to process */
n--;
}
}
}
#endif
#endif // SOUND_SUPPORT

View File

@ -1,67 +0,0 @@
/*****************************************************************************/
/* */
/* Module: TIA Chip Sound Simulator Includes, V1.1 */
/* Purpose: Define global function prototypes and structures for the TIA */
/* Chip Sound Simulator. */
/* Author: Ron Fries */
/* */
/* Revision History: */
/* 10-Sep-96 - V1.0 - Initial Release */
/* 14-Jan-97 - V1.1 - Added compiler directives to facilitate compilation */
/* on a C++ compiler. */
/* */
/*****************************************************************************/
/* */
/* License Information and Copyright Notice */
/* ======================================== */
/* */
/* TiaSound is Copyright(c) 1997 by Ron Fries */
/* */
/* This library is free software; you can redistribute it and/or modify it */
/* under the terms of version 2 of the GNU Library General Public License */
/* as published by the Free Software Foundation. */
/* */
/* 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 Library */
/* General Public License for more details. */
/* To obtain a copy of the GNU Library General Public License, write to the */
/* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
/* */
/* Any permitted reproduction of these routines, in whole or in part, must */
/* bear this legend. */
/* */
/*****************************************************************************/
#ifndef _TIASOUND_H
#define _TIASOUND_H
#ifdef SOUND_SUPPORT
#ifdef __cplusplus
extern "C" {
#endif
void Tia_sound_init (unsigned int sample_freq, unsigned int playback_freq);
void Update_tia_sound (unsigned int addr, unsigned char val);
void Tia_process_2 (register unsigned char *buffer,
register unsigned int n);
void Tia_process (register unsigned char *buffer,
register unsigned int n);
void Tia_get_registers (unsigned char *reg1, unsigned char *reg2, unsigned char *reg3,
unsigned char *reg4, unsigned char *reg5, unsigned char *reg6);
void Tia_set_registers (unsigned char reg1, unsigned char reg2, unsigned char reg3,
unsigned char reg4, unsigned char reg5, unsigned char reg6);
void Tia_clear_registers (void);
void Tia_volume (unsigned int percent);
#ifdef __cplusplus
}
#endif
#endif // SOUND_SUPPORT
#endif

View File

@ -1,297 +0,0 @@
TIASOUND.C - TIA SOUND EMULATION V1.0
=====================================
As I'm sure you've already discovered, the Stella manual isn't always
correct. My observations show what I believe to be several discrepancies
in the description of the distortions. Of course, I could be wrong on a few
of these, so if some of the games don't sound right, please let me know
which ones. If possible, it would be best if you could send me a wave file
of what it is supposed to sound like, preferably at 44 KHz. Only a few
seconds should be necessary.
TIA AUDIO CIRCUITRY
===================
THE HARDWARE
------------
In general, the sound hardware can be described as follows:
Selecting the Clock Modifier - Bits D0 and D1
---------------------------------------------
Bits D0 and D1 select the output clock modifier:
D1 D0
-------
0 0 - direct clock (pure)
0 1 - direct clock (pure)
1 0 - divide by 31
1 1 - 5-bit polynomial
The 'divide by 31' isn't simply the input clock divided by 31. It is, in
essence, a 5-bit polynomial with only two bits set. The resulting square
wave actually has a 13:18 ratio. This may be implemented in the hardware
as a pair of traps on a 5-bit counter.
Selecting the Source Pattern - Bits D2 and D3
---------------------------------------------
Bits D2 and D3 select the source to be clocked:
D3 D2
-------
0 0 - 4-bit polynomial
0 1 - pure (Q')
1 0 - 5-bit polynomial
1 1 - pure (Q')
The 'pure' tones are generated by toggling the output. Whenever a clock
tick is received, the output will change states. I've used the notation Q'
to indicate the 'logical NOT of the last output'. Note that since the output
toggles, this can be thought of as a divide by 2 since the output frequency
will be half of the input frequency. This is only true for the pure tones.
Selecting the Source Clock - Bits D2 and D3
-------------------------------------------
When bits D2 and D3 are both set, it affects the source clock. I believe
the '30KHz' clock is actually the 3.58MHz clock divided by 114. When bits
D2 and D3 are set, the input source is switched to the 1.19MHz clock, so the
'30KHz' source clock is reduced to approximately 10KHz.
Exceptions - Selecting No Output or the 9-bit Polynomial
--------------------------------------------------------
There are two exceptions that occur when bits D0-D2 are all 0. If AUDC is
zero (0000), then I believe the output is set equal to the volume. If AUDC is
equal to 8 (1000), the 9-bit polynomial is selected as the source to be
clocked.
Updated Detailed Functions for AUDC
-----------------------------------
From my observations, I would describe the distortion selections as follows:
HEX D3 D2 D1 D0 Clock Source Clock Modifier Source Pattern
--- ------------- -------------- ---------------- ----------------
0 0 0 0 0 3.58 MHz/114 -> none (pure) -> none
1 0 0 0 1 3.58 MHz/114 -> none (pure) -> 4-bit poly
2 0 0 1 0 3.58 MHz/114 -> divide by 31 -> 4-bit poly
3 0 0 1 1 3.58 MHz/114 -> 5-bit poly -> 4-bit poly
4 0 1 0 0 3.58 MHz/114 -> none (pure) -> pure (~Q)
5 0 1 0 1 3.58 MHz/114 -> none (pure) -> pure (~Q)
6 0 1 1 0 3.58 MHz/114 -> divide by 31 -> pure (~Q)
7 0 1 1 1 3.58 MHz/114 -> 5-bit poly -> pure (~Q)
8 1 0 0 0 3.58 MHz/114 -> none (pure) -> 9-bit poly
9 1 0 0 1 3.58 MHz/114 -> none (pure) -> 5-bit poly
A 1 0 1 0 3.58 MHz/114 -> divide by 31 -> 5-bit poly
B 1 0 1 1 3.58 MHz/114 -> 5-bit poly -> 5-bit poly
C 1 1 0 0 1.19 MHz/114 -> none (pure) -> pure (~Q)
D 1 1 0 1 1.19 MHz/114 -> none (pure) -> pure (~Q)
E 1 1 1 0 1.19 MHz/114 -> divide by 31 -> pure (~Q)
F 1 1 1 1 1.19 MHz/114 -> 5-bit poly -> pure (~Q)
For the most part, this follows the Stella manual, but there are a few
differences. Probably the most notable are hex entries 'A' and 'B', which
are listed in the Stella manual as 'div 31: pure tone' and 'set last 4 bits
to 1'.
On entries 'A' and 'B', both the data source and the clock have the same
number of entries (31). Because of this, they will always align in the
same way. Entry 'A' will then reduce to a pure 'div by 31' output which
is identical to entry '6'. On 'B', both the source and the clock align
in such a way that the output will always be 1.
THE POLYNOMIALS
---------------
The 4-bit, 5-bit, and 9-bit polynomials are essentially tables containing
a random series of bits (they are implemented in hardware as shift
registers). Because the tables are fixed in length, the 'random' pattern
will repeat periodically.
The size of the table is described by its name. The actual size of the
table is 2^x - 1, where x is either the 4, 5 or 9. The 4-bit polynomial
has 15 entries, the 5-bit polynomial has 31 entries, and the 9-bit
polynomial has 511 entries.
I've performed some analysis on the output of the actual Atari 2600, and
believe I have the actual 4-bit and 5-bit polynomials used by the Atari.
These have been encoded as byte arrays in my routines. For the 9-bit
polynomial, I use a random number generator which should produce
approximately the same results.
THE CLOCK
---------
I believe the input clock for the audio is a division of the main system
clock. With this assumption, I determined that the input clock for the
audio is equal to the 3.58MHz system clock divided by 114. Note that this
produces an actual audio clock of 31.4 KHz. This value closely matches the
frequencies I recorded from my unit. The Stella manual describes the Audio
Clock as approximately 30KHz, which I suppose is correct if you consider 5%
to be approximate.
If both bits D2 and D3 of the AUDC register are set, I believe the TIA chip
uses the 1.19MHz clock for the base audio clock instead of the 3.58MHz.
This, of course, produces the 'divide by 3' functionality described in the
Stella manual.
TIASOUND.C
==========
The TIASOUND.C file is the heart of the TIA Sound Emulation program.
Although the routines in the file must work together, no other files are
modules are required for operation. A header file, 'TIASOUND.H', has
been included for use in other modules, and provides the necessary
function prototypes. I've attempted to make the routines as portable as
possible, so the file should compile on almost any compiler with little
or no modification.
I have made some attempts at optimizing the routines, though I am sure
more optimization can be done. They are currently only available in 'C'.
I'll be happy to convert them to assembly language if desired. Please feel
free to send me e-mail (see below).
The routines are easy to use. Detailed descriptions on the function calls
are listed below.
GENERAL OVERVIEW
----------------
On start-up of the system, a single call should be made to Tia_sound_init.
This routine will prepare the structures for sound output. This routine
can be called again if necessary during warm-start or other reset.
Once in the main loop, there are two other functions that will be used.
Whenever the system needs to write to either the AUDC, AUDV or AUDF values,
a call should be made to the Update_tia_sound routine. This routine will
take care of updating the internal registers. It will pre-calculate several
values to help with optimization.
The only other routine that is called is the Tia_process function. This
function will fill a audio buffer with a specified number of bytes. This
function should be called whenever a new audio buffer is required.
For best results, I recommend using at least two output buffers. Using this
scheme, the sound card can be playing one buffer while the system is filling
the other.
DETAILED FUNCTION DESCRIPTIONS
------------------------------
Tia_sound_init(uint16 sample_freq, uint16 playback_freq)
--------------------------------------------------------
This function initializes the structures used by the TiaSound.C routines.
This function takes two parameters: the sample frequency and the playback
frequency. The sample frequency is the frequency of the 30KHz source clock.
For my calculations, this clock is about 31.4 KHz, though any 16-bit
unsigned integer value can be used (1-65535).
The playback frequency is the frequency of the sound playback (the frequency
used by the sound card). For best results, both the playback frequency and
the sample frequency should be identical. In the case where the sound card
cannot support 31.4 KHz (i.e. the original SB and many 'SB compatible' cards),
there are three options:
1) Set the sample frequency to 31400 and the playback frequency to the
maximum possible (e.g. 22050). Though the system will reproduce all
output values, a significant amount of aliasing is introduced.
2) Set the sample frequency and the playback frequency to the maximum possible
(e.g. 22050). In this case, all output values are reproduced with no
distortions; however, the pitch is about 2/3 of an octave low.
3) Set the sample frequency to 31400 and the playback frequency to exactly
1/2 of the sample frequency (15700). In this case, the output frequency
is correct and the aliasing is minimized. The aliasing is only
noticeable on the higher frequencies. Most notably, the highest pure tone
(AUDF = 0) is inaudible.
Feel free to experiment to find other alternatives as well.
This function has no return value (void).
Update_tia_sound (uint16 addr, uint8 val)
-----------------------------------------
This function should be called each time an AUDC, AUDF or AUDV value
changes. This function takes two parameters: the address to change and
the new value. The address should be one of the following values:
Addr Description
---- -----------
0x15 AUDC0
0x16 AUDC1
0x17 AUDF0
0x18 AUDF1
0x19 AUDV0
0x1A AUDV1
Any values outside of this range will be ignored.
The routine pre-calculates several values that are needed by the
processing function. This is done to optimize performance.
This function has no return value (void).
Tia_process (unsigned char *buffer, uint16 n)
---------------------------------------------
This function calculates and fills a buffer with unsigned 8-bit mono audio.
This function takes two parameters: a pointer to the buffer to fill and
the size of the buffer (limited to 65535). This function fills the
buffer based on the requested size and returns. It automatically
updates the pointers for the next call, so subsequent calls to this function
will provide a continuous stream of data.
The size of the buffer that is needed depends on the playback frequency.
It is best to keep the buffer as small as possible to maximize response time
to changes in the sound. Of course, the minimum size is dependent on
system and emulator performance.
Selecting the correct buffer size is a careful balance. Selecting a buffer
size that is too small will produce noticeable clicks in the output, though
selecting a size that is too large will cause a poor response time and
possible delays in the system when the new buffer is filled.
This function has no return value (void).
License Information and Copyright Notice
========================================
TiaSound is Copyright(c) 1996 by Ron Fries
This library is free software; you can redistribute it and/or modify it under
the terms of version 2 of the GNU Library General Public License as published
by the Free Software Foundation.
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 Library General Public License for more
details.
To obtain a copy of the GNU Library General Public License, write to the Free
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
Any permitted reproduction of these routines, in whole or in part, must bear
this legend.