RACE/neopopsound.c
2020-10-03 23:57:24 +02:00

548 lines
15 KiB
C

/* Flavor modified sound.c and sound.h from NEOPOP
* which was originally based on sn76496.c from MAME
* some ideas also taken from NeoPop-SDL code
*---------------------------------------------------------------------------
* Originally from
* NEOPOP : Emulator as in Dreamland
*
* Copyright (c) 2001-2002 by neopop_uk
*---------------------------------------------------------------------------
*---------------------------------------------------------------------------
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version. See also the license.txt file for
* additional informations.
*---------------------------------------------------------------------------
*/
/************************************************************************
* *
* Portions, but not all of this source file are based on MAME v0.60 *
* File "sn76496.c". All copyright goes to the original author. *
* The remaining parts, including DAC processing, by neopop_uk *
* *
************************************************************************/
#include "types.h"
#include <string.h>
#include "neopopsound.h"
/* ============================================================================= */
SoundChip toneChip;
SoundChip noiseChip;
/* ==== DAC */
#define DAC_BUFFERSIZE (256 * 1024) /* at (256 * 1024) the PC version will crash on MS2 intro */
int dacLBufferRead, dacLBufferWrite, dacLBufferCount;
_u16 dacBufferL[DAC_BUFFERSIZE];
int fixsoundmahjong;
/* ============================================================================= */
#define SOUNDCHIPCLOCK (3072000) /* Unverified / sounds correct */
#define MAX_OUTPUT 0x7fff
#define STEP 0x10000 /* Fixed point adjuster */
#define MAX_OUTPUT_STEP 0x7fff0000
#define STEP_SHIFT 16
static _u32 VolTable[16];
static _u32 UpdateStep = 0; /* Number of steps during one sample. */
/* Formulas for noise generator */
/* bit0 = output */
/* noise feedback for white noise mode (verified on real SN76489 by John Kortink) */
#define FB_WNOISE 0x14002 /* (16bits) bit16 = bit0(out) ^ bit2 ^ bit15 */
/* noise feedback for periodic noise mode */
#define FB_PNOISE 0x08000 /* 15bit rotate */
/* noise generator start preset (for periodic noise) */
#define NG_PRESET 0x0f35
#define max(a,b) (a>b?a:b)
#define min(a,b) (a<b?a:b)
/* ============================================================================= */
static _u16 sample_chip_tone(void)
{
int i;
int vol[3];
unsigned int out;
int left;
/* vol[] keeps track of how long each square wave stays */
/* in the 1 position during the sample period. */
vol[0] = vol[1] = vol[2] = /*vol[3] = */ 0;
for (i = 0; i < 3; i++)
{
if (toneChip.Output[i]) vol[i] += toneChip.Count[i];
toneChip.Count[i] -= STEP;
/* Period[i] is the half period of the square wave. Here, in each */
/* loop I add Period[i] twice, so that at the end of the loop the */
/* square wave is in the same status (0 or 1) it was at the start. */
/* vol[i] is also incremented by Period[i], since the wave has been 1 */
/* exactly half of the time, regardless of the initial position. */
/* If we exit the loop in the middle, Output[i] has to be inverted */
/* and vol[i] incremented only if the exit status of the square */
/* wave is 1. */
while (toneChip.Count[i] <= 0)
{
toneChip.Count[i] += toneChip.Period[i];
if (toneChip.Count[i] > 0)
{
toneChip.Output[i] ^= 1;
if (toneChip.Output[i]) vol[i] += toneChip.Period[i];
break;
}
toneChip.Count[i] += toneChip.Period[i];
vol[i] += toneChip.Period[i];
}
if (toneChip.Output[i]) vol[i] -= toneChip.Count[i];
}
/*
left = STEP;
do
{
int nextevent;
if (toneChip.Count[3] < left) nextevent = toneChip.Count[3];
else nextevent = left;
if (toneChip.Output[3]) vol[3] += toneChip.Count[3];
toneChip.Count[3] -= nextevent;
if (toneChip.Count[3] <= 0)
{
if (toneChip.RNG & 1) toneChip.RNG ^= toneChip.NoiseFB;
toneChip.RNG >>= 1;
toneChip.Output[3] = toneChip.RNG & 1;
toneChip.Count[3] += toneChip.Period[3];
if (toneChip.Output[3]) vol[3] += toneChip.Period[3];
}
if (toneChip.Output[3]) vol[3] -= toneChip.Count[3];
left -= nextevent;
} while (left > 0);
*/
out = vol[0] * toneChip.Volume[0] + vol[1] * toneChip.Volume[1] +
vol[2] * toneChip.Volume[2];
if (out > MAX_OUTPUT_STEP)
out = MAX_OUTPUT_STEP;
return out>>STEP_SHIFT;
}
/* ============================================================================= */
static _u16 sample_chip_noise(void)
{
int i;
int vol3 = 0;
unsigned int out;
int left;
/* vol[] keeps track of how long each square wave stays */
/* in the 1 position during the sample period. */
/*
vol[0] = vol[1] = vol[2] = vol[3] = 0;
for (i = 0; i < 3; i++)
{
if (noiseChip.Output[i]) vol[i] += noiseChip.Count[i];
noiseChip.Count[i] -= STEP;
// Period[i] is the half period of the square wave. Here, in each
// loop I add Period[i] twice, so that at the end of the loop the
// square wave is in the same status (0 or 1) it was at the start.
// vol[i] is also incremented by Period[i], since the wave has been 1
// exactly half of the time, regardless of the initial position.
// If we exit the loop in the middle, Output[i] has to be inverted
// and vol[i] incremented only if the exit status of the square
// wave is 1.
while (noiseChip.Count[i] <= 0)
{
noiseChip.Count[i] += noiseChip.Period[i];
if (noiseChip.Count[i] > 0)
{
noiseChip.Output[i] ^= 1;
if (noiseChip.Output[i]) vol[i] += noiseChip.Period[i];
break;
}
noiseChip.Count[i] += noiseChip.Period[i];
vol[i] += noiseChip.Period[i];
}
if (noiseChip.Output[i]) vol[i] -= noiseChip.Count[i];
}
*/
if (noiseChip.Volume[3])
{
left = STEP;
do
{
int nextevent = min(noiseChip.Count[3],left);
#if 0
if (noiseChip.Count[3] < left)
nextevent = noiseChip.Count[3];
else
nextevent = left;
#endif
if (noiseChip.Output[3]) vol3 += noiseChip.Count[3];
noiseChip.Count[3] -= nextevent;
if (noiseChip.Count[3] <= 0)
{
if (noiseChip.RNG & 1) noiseChip.RNG ^= noiseChip.NoiseFB;
noiseChip.RNG >>= 1;
noiseChip.Output[3] = noiseChip.RNG & 1;
noiseChip.Count[3] += noiseChip.Period[3];
if (noiseChip.Output[3]) vol3 += noiseChip.Period[3];
}
if (noiseChip.Output[3]) vol3 -= noiseChip.Count[3];
left -= nextevent;
} while (left > 0);
}
out = vol3 * noiseChip.Volume[3];
if (out > MAX_OUTPUT_STEP)
out = MAX_OUTPUT_STEP;
return out>>STEP_SHIFT;
}
/* ============================================================================= */
void sound_update(_u16* chip_buffer, int length_bytes)
{
length_bytes >>= 1; /* turn it into words */
while (length_bytes)
{
/* Mix a mono track out of: (Tone + Noise) >> 1
* Write it to the sound buffer */
*(chip_buffer++) = (sample_chip_tone() + sample_chip_noise()) >> 1;
length_bytes--;
}
}
/* ============================================================================= */
void WriteSoundChip(SoundChip* chip, _u8 data)
{
/* Command */
if (data & 0x80)
{
int r = (data & 0x70) >> 4;
int c = r>>1;
chip->LastRegister = r;
chip->Register[r] = (chip->Register[r] & 0x3f0) | (data & 0x0f);
switch(r)
{
case 0: /* tone 0 : frequency */
case 2: /* tone 1 : frequency */
case 4: /* tone 2 : frequency */
chip->Period[c] = UpdateStep * chip->Register[r];
if (chip->Period[c] == 0) chip->Period[c] = UpdateStep;
if (r == 4)
{
/* update noise shift frequency */
if ((chip->Register[6] & 0x03) == 0x03)
chip->Period[3] = chip->Period[2]<<1;
}
break;
case 1: /* tone 0 : volume */
case 3: /* tone 1 : volume */
case 5: /* tone 2 : volume */
case 7: /* noise : volume */
#ifdef NEOPOP_DEBUG
if (filter_sound)
{
if (chip == &toneChip)
system_debug_message("sound (T): Set Tone %d Volume to %d (0 = min, 15 = max)", c, 15 - (data & 0xF));
else
system_debug_message("sound (N): Set Tone %d Volume to %d (0 = min, 15 = max)", c, 15 - (data & 0xF));
}
#endif
chip->Volume[c] = VolTable[data & 0xF];
break;
case 6: /* noise : frequency, mode */
{
int n = chip->Register[6];
#ifdef NEOPOP_DEBUG
if (filter_sound)
{
char *pm, *nm = "White";
if ((n & 4)) nm = "Periodic";
switch(n & 3)
{
case 0: pm = "N/512"; break;
case 1: pm = "N/1024"; break;
case 2: pm = "N/2048"; break;
case 3: pm = "Tone#2"; break;
}
if (chip == &toneChip)
system_debug_message("sound (T): Set Noise Mode to %s, Period = %s", nm, pm);
else
system_debug_message("sound (N): Set Noise Mode to %s, Period = %s", nm, pm);
}
#endif
chip->NoiseFB = (n & 4) ? FB_WNOISE : FB_PNOISE;
n &= 3;
/* N/512,N/1024,N/2048,Tone #2 output */
chip->Period[3] = (n == 3) ? 2 * chip->Period[2] : (UpdateStep << (5+n));
/* reset noise shifter */
chip->RNG = NG_PRESET;
chip->Output[3] = chip->RNG & 1;
}
break;
}
}
else
{
int r = chip->LastRegister;
int c = r/2;
switch (r)
{
case 0: /* tone 0 : frequency */
case 2: /* tone 1 : frequency */
case 4: /* tone 2 : frequency */
chip->Register[r] = (chip->Register[r] & 0x0f) | ((data & 0x3f) << 4);
chip->Period[c] = UpdateStep * chip->Register[r];
if (chip->Period[c] == 0) chip->Period[c] = UpdateStep;
if (r == 4)
{
/* update noise shift frequency */
if ((chip->Register[6] & 0x03) == 0x03)
chip->Period[3] = chip->Period[2]<<1;
}
#ifdef NEOPOP_DEBUG
if (filter_sound)
{
if (chip == &toneChip)
system_debug_message("sound (T): Set Tone %d Frequency to %d", c, chip->Register[r]);
else
system_debug_message("sound (N): Set Tone %d Frequency to %d", c, chip->Register[r]);
}
#endif
break;
}
}
}
/* ============================================================================= */
void dac_writeL(unsigned char data)
{
unsigned i;
static int conv=5;
/* pretend that conv=5.5 (44100/8000) conversion factor */
if(conv==5)
conv=6;
else
{
conv=5;
/* Arregla el sonido del Super Real Mahjong */
if (fixsoundmahjong>500)
conv=3;
}
for(i=0;i<conv;i++)
{
/* Write to buffer */
dacBufferL[dacLBufferWrite++] = (data-0x80)<<8;
#if 0
dacLBufferWrite++;
#endif
if (dacLBufferWrite == DAC_BUFFERSIZE)
dacLBufferWrite = 0;
/* Overflow? */
dacLBufferCount++;
if (dacLBufferCount == DAC_BUFFERSIZE)
{
#if 0
dbg_printf("dac_write: DAC buffer overflow\nPlease report this to the author.");
#endif
dacLBufferCount = 0;
}
}
}
#if 0
void dac_writeR(unsigned char data)
{
/* Write to buffer */
dacBufferR[dacRBufferWrite] = data;
dacRBufferWrite++;
if (dacRBufferWrite == DAC_BUFFERSIZE)
dacRBufferWrite = 0;
/* Overflow? */
dacRBufferCount++;
if (dacRBufferCount == DAC_BUFFERSIZE)
{
dbg_printf("dac_write: DAC buffer overflow\nPlease report this to the author.");
dacRBufferCount = 0;
}
}
#endif
void dac_mixer(_u16* stream, int length_bytes)
{
}
void dac_update(_u16* dac_buffer, int length_bytes)
{
while (length_bytes > 1)
{
/* Copy then clear DAC data */
*(dac_buffer++) |= dacBufferL[dacLBufferRead];
dacBufferL[dacLBufferRead] = 0; /* silence? */
length_bytes -= 2; /* 1 byte = 8 bits */
if (dacLBufferCount > 0)
{
dacLBufferCount--;
/* Advance the DAC read */
#if 0
dacLBufferRead++;
#endif
if (++dacLBufferRead == DAC_BUFFERSIZE)
dacLBufferRead = 0;
}
}
}
/*============================================================================= */
/*Resets the sound chips, also used whenever sound options are changed */
void sound_init(int SampleRate)
{
int i;
double out;
/* the base clock for the tone generators is the chip clock divided by 16; */
/* for the noise generator, it is clock / 256. */
/* Here we calculate the number of steps which happen during one sample */
/* at the given sample rate. No. of events = sample rate / (clock/16). */
/* STEP is a multiplier used to turn the fraction into a fixed point */
/* number. */
UpdateStep = (_u32)(((double)STEP * SampleRate * 16) / SOUNDCHIPCLOCK);
/* Initialise Left Chip */
memset(&toneChip, 0, sizeof(SoundChip));
/* Initialise Right Chip */
memset(&noiseChip, 0, sizeof(SoundChip));
/* Default register settings */
for (i = 0;i < 8;i+=2)
{
toneChip.Register[i] = 0;
toneChip.Register[i + 1] = 0x0f; /* volume = 0 */
noiseChip.Register[i] = 0;
noiseChip.Register[i + 1] = 0x0f; /* volume = 0 */
}
for (i = 0;i < 4;i++)
{
toneChip.Output[i] = 0;
toneChip.Period[i] = toneChip.Count[i] = UpdateStep;
noiseChip.Output[i] = 0;
noiseChip.Period[i] = noiseChip.Count[i] = UpdateStep;
}
/* Build the volume table */
out = MAX_OUTPUT / 3;
/* build volume table (2dB per step) */
for (i = 0;i < 15;i++)
{
VolTable[i] = (_u32)out;
out /= 1.258925412; /* = 10 ^ (2/20) = 2dB */
}
VolTable[15] = 0;
/* Clear the DAC buffer */
for (i = 0; i < DAC_BUFFERSIZE; i++)
dacBufferL[i] = 0;
dacLBufferCount = 0;
dacLBufferRead = 0;
dacLBufferWrite = 0;
}
/* ============================================================================= */
#define NGPC_CHIP_FREQUENCY 44100
int chip_freq=NGPC_CHIP_FREQUENCY; /* what we'd prefer */
#define CHIPBUFFERLENGTH 35280
#define UNDEFINED 0xFFFFFF
/* ====== Chip sound ========= */
static int lastChipWrite = 0, chipWrite = UNDEFINED; /* Write Cursor */
/* ====== DAC sound ========= */
static int lastDacWrite = 0, dacWrite = UNDEFINED; /* Write Cursor */
_u8 blockSound[CHIPBUFFERLENGTH], blockDAC[CHIPBUFFERLENGTH]; /* Gets filled with sound data. */
unsigned int blockSoundWritePtr = 0;
unsigned int blockSoundReadPtr = 0;
void system_sound_chipreset(void)
{
/* Initialises sound chips, matching frequencies */
sound_init(chip_freq);
}
int sound_system_init(void)
{
system_sound_chipreset(); /* Resets chips */
return 1;
}
/* call this every so often to update the sound output */
void system_sound_update(int nframes)
{
}