📄 sound.c
字号:
/*
SNEeSe, an Open Source Super NES emulator.
Copyright (c) 1998-2004 Charles Bilyue'.
Portions Copyright (c) 2003-2004 Daniel Horchner.
This is free software. See 'LICENSE' for details.
You must read and accept the license prior to use.
*/
#include <stdio.h>
#include <string.h>
#include "wrapaleg.h"
#include "helper.h"
#include "apu/sound.h"
#include "apu/sounddef.h"
#include "apu/spc.h"
//#define NO_PITCH_MODULATION /* do not remove - needs to be fixed */
//#define FAULT_ON_PITCH_MODULATION_USE
#define DSP_SPEED_HACK
//#define DUMP_SOUND
#define ZERO_ENVX_ON_VOICE_OFF
#define ZERO_OUTX_ON_VOICE_OFF
//#define LOG_SOUND_DSP_READ
//#define LOG_SOUND_DSP_WRITE
//#define LOG_SOUND_VOICE_OFF
#define MASK_PITCH_H
//#define DISALLOW_ENVX_OUTX_WRITE
#define ZERO_ENVX_ON_KEY_ON
typedef enum {
ATTACK, /* A of ADSR */
DECAY, /* D of ADSR */
SUSTAIN, /* S of ADSR */
RELEASE, /* R of ADSR */
DECREASE, /* GAIN linear decrease mode */
EXP, /* GAIN exponential decrease mode */
INCREASE, /* GAIN linear increase mode */
BENT, /* GAIN bent line increase mode */
DIRECT, /* Directly specify ENVX */
VOICE_OFF /* Voice is not playing */
} ENVSTATE;
extern unsigned char SPCRAM[65536];
unsigned char SPC_MASK;
unsigned SPC_DSP_DATA;
signed char sound_enabled;
int sound_bits;
signed char sound_echo_enabled = TRUE;
signed char sound_gauss_enabled = TRUE;
unsigned sound_cycle_latch;
unsigned sound_output_position;
unsigned sound_sample_latch;
static AUDIOSTREAM *stream_buffer = NULL;
static void *sound_buffer_preload = NULL;
static output_sample_16 *noise_buffer = NULL;
static signed char *outx_buffer = NULL; /* for pitch modulation */
static int *mix_buffer = NULL;
static signed char block_written;
#ifdef DUMP_SOUND
static signed char block_dumped;
#endif
signed char ENVX_ENABLED = -1;
unsigned char SPC_DSP[256];
// INTERNAL (static) STUFF
int main_lvol, main_rvol, main_jvol;
int echo_lvol, echo_rvol, echo_jvol;
unsigned short echo_base, echo_delay, echo_address;
int echo_feedback;
int FIR_taps[8][2];
/* FIR_coeff[0] = C7, FIR_coeff[7] = C0 */
int FIR_coeff[8];
int FIR_address;
static struct voice_state {
short buf[4];
short last1, last2;
unsigned short sample_start_address;
unsigned envx;
unsigned env_sample_latch, voice_sample_latch, env_update_count;
unsigned ar, dr, sl, sr;
unsigned gain_update_count;
int env_counter;
int lvol, rvol, jvol;
int outx;
int brr_samples_left;
unsigned step;
int pitch_counter;
int bufptr;
unsigned brrptr;
unsigned char brr_header, env_state, adsr_state, key_wait;
} SNDvoices[8];
unsigned char SNDkeys, keying_on;
static const short gauss[]={
0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000,
0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000,
0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001,
0x001, 0x001, 0x001, 0x002, 0x002, 0x002, 0x002, 0x002,
0x002, 0x002, 0x003, 0x003, 0x003, 0x003, 0x003, 0x004,
0x004, 0x004, 0x004, 0x004, 0x005, 0x005, 0x005, 0x005,
0x006, 0x006, 0x006, 0x006, 0x007, 0x007, 0x007, 0x008,
0x008, 0x008, 0x009, 0x009, 0x009, 0x00A, 0x00A, 0x00A,
0x00B, 0x00B, 0x00B, 0x00C, 0x00C, 0x00D, 0x00D, 0x00E,
0x00E, 0x00F, 0x00F, 0x00F, 0x010, 0x010, 0x011, 0x011,
0x012, 0x013, 0x013, 0x014, 0x014, 0x015, 0x015, 0x016,
0x017, 0x017, 0x018, 0x018, 0x019, 0x01A, 0x01B, 0x01B,
0x01C, 0x01D, 0x01D, 0x01E, 0x01F, 0x020, 0x020, 0x021,
0x022, 0x023, 0x024, 0x024, 0x025, 0x026, 0x027, 0x028,
0x029, 0x02A, 0x02B, 0x02C, 0x02D, 0x02E, 0x02F, 0x030,
0x031, 0x032, 0x033, 0x034, 0x035, 0x036, 0x037, 0x038,
0x03A, 0x03B, 0x03C, 0x03D, 0x03E, 0x040, 0x041, 0x042,
0x043, 0x045, 0x046, 0x047, 0x049, 0x04A, 0x04C, 0x04D,
0x04E, 0x050, 0x051, 0x053, 0x054, 0x056, 0x057, 0x059,
0x05A, 0x05C, 0x05E, 0x05F, 0x061, 0x063, 0x064, 0x066,
0x068, 0x06A, 0x06B, 0x06D, 0x06F, 0x071, 0x073, 0x075,
0x076, 0x078, 0x07A, 0x07C, 0x07E, 0x080, 0x082, 0x084,
0x086, 0x089, 0x08B, 0x08D, 0x08F, 0x091, 0x093, 0x096,
0x098, 0x09A, 0x09C, 0x09F, 0x0A1, 0x0A3, 0x0A6, 0x0A8,
0x0AB, 0x0AD, 0x0AF, 0x0B2, 0x0B4, 0x0B7, 0x0BA, 0x0BC,
0x0BF, 0x0C1, 0x0C4, 0x0C7, 0x0C9, 0x0CC, 0x0CF, 0x0D2,
0x0D4, 0x0D7, 0x0DA, 0x0DD, 0x0E0, 0x0E3, 0x0E6, 0x0E9,
0x0EC, 0x0EF, 0x0F2, 0x0F5, 0x0F8, 0x0FB, 0x0FE, 0x101,
0x104, 0x107, 0x10B, 0x10E, 0x111, 0x114, 0x118, 0x11B,
0x11E, 0x122, 0x125, 0x129, 0x12C, 0x130, 0x133, 0x137,
0x13A, 0x13E, 0x141, 0x145, 0x148, 0x14C, 0x150, 0x153,
0x157, 0x15B, 0x15F, 0x162, 0x166, 0x16A, 0x16E, 0x172,
0x176, 0x17A, 0x17D, 0x181, 0x185, 0x189, 0x18D, 0x191,
0x195, 0x19A, 0x19E, 0x1A2, 0x1A6, 0x1AA, 0x1AE, 0x1B2,
0x1B7, 0x1BB, 0x1BF, 0x1C3, 0x1C8, 0x1CC, 0x1D0, 0x1D5,
0x1D9, 0x1DD, 0x1E2, 0x1E6, 0x1EB, 0x1EF, 0x1F3, 0x1F8,
0x1FC, 0x201, 0x205, 0x20A, 0x20F, 0x213, 0x218, 0x21C,
0x221, 0x226, 0x22A, 0x22F, 0x233, 0x238, 0x23D, 0x241,
0x246, 0x24B, 0x250, 0x254, 0x259, 0x25E, 0x263, 0x267,
0x26C, 0x271, 0x276, 0x27B, 0x280, 0x284, 0x289, 0x28E,
0x293, 0x298, 0x29D, 0x2A2, 0x2A6, 0x2AB, 0x2B0, 0x2B5,
0x2BA, 0x2BF, 0x2C4, 0x2C9, 0x2CE, 0x2D3, 0x2D8, 0x2DC,
0x2E1, 0x2E6, 0x2EB, 0x2F0, 0x2F5, 0x2FA, 0x2FF, 0x304,
0x309, 0x30E, 0x313, 0x318, 0x31D, 0x322, 0x326, 0x32B,
0x330, 0x335, 0x33A, 0x33F, 0x344, 0x349, 0x34E, 0x353,
0x357, 0x35C, 0x361, 0x366, 0x36B, 0x370, 0x374, 0x379,
0x37E, 0x383, 0x388, 0x38C, 0x391, 0x396, 0x39B, 0x39F,
0x3A4, 0x3A9, 0x3AD, 0x3B2, 0x3B7, 0x3BB, 0x3C0, 0x3C5,
0x3C9, 0x3CE, 0x3D2, 0x3D7, 0x3DC, 0x3E0, 0x3E5, 0x3E9,
0x3ED, 0x3F2, 0x3F6, 0x3FB, 0x3FF, 0x403, 0x408, 0x40C,
0x410, 0x415, 0x419, 0x41D, 0x421, 0x425, 0x42A, 0x42E,
0x432, 0x436, 0x43A, 0x43E, 0x442, 0x446, 0x44A, 0x44E,
0x452, 0x455, 0x459, 0x45D, 0x461, 0x465, 0x468, 0x46C,
0x470, 0x473, 0x477, 0x47A, 0x47E, 0x481, 0x485, 0x488,
0x48C, 0x48F, 0x492, 0x496, 0x499, 0x49C, 0x49F, 0x4A2,
0x4A6, 0x4A9, 0x4AC, 0x4AF, 0x4B2, 0x4B5, 0x4B7, 0x4BA,
0x4BD, 0x4C0, 0x4C3, 0x4C5, 0x4C8, 0x4CB, 0x4CD, 0x4D0,
0x4D2, 0x4D5, 0x4D7, 0x4D9, 0x4DC, 0x4DE, 0x4E0, 0x4E3,
0x4E5, 0x4E7, 0x4E9, 0x4EB, 0x4ED, 0x4EF, 0x4F1, 0x4F3,
0x4F5, 0x4F6, 0x4F8, 0x4FA, 0x4FB, 0x4FD, 0x4FF, 0x500,
0x502, 0x503, 0x504, 0x506, 0x507, 0x508, 0x50A, 0x50B,
0x50C, 0x50D, 0x50E, 0x50F, 0x510, 0x511, 0x511, 0x512,
0x513, 0x514, 0x514, 0x515, 0x516, 0x516, 0x517, 0x517,
0x517, 0x518, 0x518, 0x518, 0x518, 0x518, 0x519, 0x519};
static const short *G1=&gauss[256],
*G2=&gauss[512],
*G3=&gauss[255],
*G4=&gauss[-1]; /* Ptrs to Gaussian table */
/* How many cycles till ADSR/GAIN adjustment */
/* Thanks to Brad Martin for providing this */
static const int apu_counter_reset_value = 0x7800;
static const int counter_update_table[32] =
{
0x0000, 0x000F, 0x0014, 0x0018, 0x001E, 0x0028, 0x0030, 0x003C,
0x0050, 0x0060, 0x0078, 0x00A0, 0x00C0, 0x00F0, 0x0140, 0x0180,
0x01E0, 0x0280, 0x0300, 0x03C0, 0x0500, 0x0600, 0x0780, 0x0A00,
0x0C00, 0x0F00, 0x1400, 0x1800, 0x1E00, 0x2800, 0x3C00, 0x7800
};
#define attack_time(x) (counter_update_table[(x) * 2 + 1])
#define decay_time(x) (counter_update_table[(x) * 2 + 16])
#define linear_time(x) (counter_update_table[(x)])
#define exp_time(x) (counter_update_table[(x)])
#define bent_time(x) (counter_update_table[(x)])
int noise_vol;
int noise_countdown;
unsigned noise_update_count;
// OLD STUFF
/* Timers 0/1 must be updated once every 10M, 2 every 1.28M SPC cycles */
void Update_SPC_Timer_0()
{
if (!(SPC_CTRL & 1)) return;
SPC_T0_position += (TotalCycles - SPC_T0_cycle_latch) / TIMER_0_CYCLES_PER_TICK;
SPC_T0_cycle_latch += (TotalCycles - SPC_T0_cycle_latch) & ~(TIMER_0_CYCLES_PER_TICK - 1);
if (SPC_T0_position < SPC_T0_target) return;
SPC_T0_counter = (SPC_T0_counter + (SPC_T0_position / SPC_T0_target)) & 0xF;
SPC_T0_position %= SPC_T0_target;
}
void Update_SPC_Timer_1()
{
if (!(SPC_CTRL & 2)) return;
SPC_T1_position += (TotalCycles - SPC_T1_cycle_latch) / TIMER_1_CYCLES_PER_TICK;
SPC_T1_cycle_latch += (TotalCycles - SPC_T1_cycle_latch) & ~(TIMER_1_CYCLES_PER_TICK - 1);
if (SPC_T1_position < SPC_T1_target) return;
SPC_T1_counter = (SPC_T1_counter + (SPC_T1_position / SPC_T1_target)) & 0xF;
SPC_T1_position %= SPC_T1_target;
}
void Update_SPC_Timer_2()
{
if (!(SPC_CTRL & 4)) return;
SPC_T2_position += (TotalCycles - SPC_T2_cycle_latch) / TIMER_2_CYCLES_PER_TICK;
SPC_T2_cycle_latch += (TotalCycles - SPC_T2_cycle_latch) & ~(TIMER_2_CYCLES_PER_TICK - 1);
if (SPC_T2_position < SPC_T2_target) return;
SPC_T2_counter = (SPC_T2_counter + (SPC_T2_position / SPC_T2_target)) & 0xF;
SPC_T2_position %= SPC_T2_target;
}
void Wrap_SPC_Cyclecounter()
{
TotalCycles -= 0xF0000000;
SPC_Cycles -= 0xF0000000;
SPC_T0_cycle_latch -= 0xF0000000;
SPC_T1_cycle_latch -= 0xF0000000;
SPC_T2_cycle_latch -= 0xF0000000;
sound_cycle_latch -= 0xF0000000;
}
void Wrap_SPC_Samplecounter()
{
if (sound_sample_latch >= 0xF8000000)
{
int c;
sound_sample_latch -= 0xF0000000;
for (c = 0; c < 8; c++)
{
SNDvoices[c].env_sample_latch -= 0xF0000000;
SNDvoices[c].voice_sample_latch -= 0xF0000000;
}
}
}
#ifndef INLINE
#ifdef __GNUC__
#define INLINE inline
#else
#define INLINE
#endif
#endif
static void SPC_VoiceOff(int voice, const char *reason)
{
if (SNDkeys & (1 << voice))
{
#ifdef LOG_SOUND_VOICE_OFF
printf("Voice off: %d (%s)\n", voice, reason);
#endif
SNDkeys &= ~(1 << voice);
SNDvoices[voice].env_state = VOICE_OFF;
SNDvoices[voice].env_update_count = 0;
#ifdef ZERO_OUTX_ON_VOICE_OFF
SPC_DSP[(voice << 4) + DSP_VOICE_OUTX] = SNDvoices[voice].outx = 0;
#else
SPC_DSP[(voice << 4) + DSP_VOICE_OUTX] = SNDvoices[voice].outx;
#endif
#ifdef ZERO_ENVX_ON_VOICE_OFF
SPC_DSP[(voice << 4) + DSP_VOICE_ENVX] = SNDvoices[voice].envx = 0;
#else
SPC_DSP[(voice << 4) + DSP_VOICE_ENVX] = SNDvoices[voice].envx >>
ENVX_DOWNSHIT_BITS;
#endif
}
}
static void SPC_VoicesOff(int voices, const char *reason)
{
int voice;
voices &= SNDkeys;
for (voice = 0; voice < 8; voice++)
{
if (voices & (1 << voice))
{
SPC_VoiceOff(voice, reason);
}
}
}
static INLINE int validate_brr_address(int voice)
{
if (SNDvoices[voice].brrptr < 0x10000) return 0;
SPC_VoiceOff(voice, "BRR address overflow");
return 1;
}
static int get_brr_block(int voice, struct voice_state *pvs)
{
int output, last1, last2;
unsigned char range, filter, input;
last1 = pvs->last1;
last2 = pvs->last2;
while (pvs->pitch_counter >= 0)
{
if (!pvs->brr_samples_left)
{
if (pvs->brr_header & BRR_PACKET_END)
{
if (pvs->brr_header & BRR_PACKET_LOOP)
{
unsigned short *samp_dir = (unsigned short *)
&SPCRAM[(int) SPC_DSP[DSP_DIR] << 8];
int cursamp = SPC_DSP[(voice << 4) + DSP_VOICE_SRCN];
pvs->brrptr = samp_dir[cursamp * 2 + 1];
}
else
{
SPC_VoiceOff(voice, "end block completed without loop");
}
}
if (validate_brr_address(voice)) return 1;
pvs->brr_header = SPCRAM[pvs->brrptr++];
if (pvs->brr_header & BRR_PACKET_END)
{
SPC_DSP[DSP_ENDX] |= 1 << voice;
}
pvs->brr_samples_left = 16;
}
range = pvs->brr_header >> 4;
filter = (pvs->brr_header >> 2) & 3;
if (!(pvs->brr_samples_left-- & 1))
{
if (validate_brr_address(voice)) return 1;
input = SPCRAM[pvs->brrptr] >> 4;
}
else
{
input = SPCRAM[pvs->brrptr++] & 0x0F;
}
output = (input ^ 8) - 8;
if (range <= 12) output = (output << range) >> 1;
else output &= ~0xFFF;
if (filter)
{
switch (filter)
{
case 1:
output += (last1 >> 1) + ((-last1) >> 5);
break;
case 2:
output += last1 + ((-(last1 + (last1 >> 1))) >> 5) +
(-last2 >> 1) + (last2 >> 5);
break;
case 3: default:
output += last1 + ((-(last1 + (last1 << 2) + (last1 << 3))) >> 7) +
(-last2 >> 1) + ((last2 + (last2 >> 1)) >> 4);
break;
}
// Clip underflow/overflow (saturation)
if (output > 0x7FFF)
output = 0x7FFF;
else if (output < -0x8000)
output = -0x8000;
}
last2 = last1;
pvs->bufptr = (pvs->bufptr + 1) & 3;
last1 = pvs->buf[pvs->bufptr] = (short) (output << 1);
pvs->pitch_counter -= 0x1000;
}
pvs->last1 = last1;
pvs->last2 = last2;
return 0;
}
#define SoundGetEnvelopeHeight(voice) (UpdateEnvelopeHeight(voice))
INLINE static unsigned UpdateEnvelopeHeight(int voice)
{
struct voice_state *pvs;
unsigned envx;
unsigned samples;
pvs = &SNDvoices[voice];
envx = pvs->envx;
while ((samples = pvs->voice_sample_latch - pvs->env_sample_latch) != 0)
{
unsigned env_update_count;
env_update_count = pvs->env_update_count;
/* Should we ever adjust envelope? */
if (!env_update_count)
{
pvs->env_sample_latch = pvs->voice_sample_latch;
break;
}
/* Is it time to adjust envelope? */
for (;samples && pvs->env_counter > 0;
samples--, pvs->env_sample_latch++, pvs->env_counter -= env_update_count);
if (pvs->env_counter <= 0)
{
pvs->env_counter = apu_counter_reset_value;
switch (pvs->env_state)
{
case ATTACK:
if (env_update_count == 1)
{
envx += ENVX_MAX_BASE / 2; //add 1/2nd
}
else
{
envx += ENVX_MAX_BASE / 64; //add 1/64th
}
if (envx >= ENVX_MAX)
{
envx = ENVX_MAX;
pvs->env_state = pvs->adsr_state = DECAY;
pvs->env_update_count = pvs->dr;
}
continue;
case DECAY:
envx -= (((int) envx - 1) >> 8) + 1; //mult by 1-1/256
if (envx <= pvs->sl)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -