⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sound.c

📁 NES game Emulator in Linux.c and asm codes.
💻 C
📖 第 1 页 / 共 4 页
字号:
/*

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 + -