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

📄 nes_apu.c

📁 一个最快NFC的模拟器
💻 C
📖 第 1 页 / 共 4 页
字号:
               {
                  uint8 dummy = N106SoundRead(d->address & 0xFFFF);
               }
               else
               {
                  N106SoundWrite(d->address, d->value);
               }
            }
            else if(apu->ex_chip & 32)
            {
               PSGSoundWrite(d->address, d->value);
            }
         }

         elapsed_cycles += APU_FROM_FIXED(apu->cycle_rate);

         accum = 0;
         if (apu->mix_enable[0]) accum += apu_rectangle(&apu->apus.rectangle[0]);
         if (apu->mix_enable[1]) accum += apu_rectangle(&apu->apus.rectangle[1]);
         if (apu->mix_enable[2]) accum += apu_triangle(&apu->apus.triangle);
         if (apu->mix_enable[3]) accum += apu_noise(&apu->apus.noise);
         if (apu->mix_enable[4]) accum += apu_dmc(&apu->apus.dmc);

         //if (apu->ext && apu->mix_enable[5]) accum += apu->ext->process();

         if (apu->mix_enable[5])
         {
            if (apu->ex_chip & 1)
            {
               accum += VRC6SoundRender() >> 8;
            }
            else if (apu->ex_chip & 2)
            {
               accum += OPLLSoundRender() >> 8;
            }
            else if (apu->ex_chip & 4)
            {
               accum += FDSSoundRender() >> 8;
            }
            else if (apu->ex_chip & 8)
            {
               accum += MMC5SoundRender() >> 8;
            }
            else if (apu->ex_chip & 16)
            {
               accum += N106SoundRender() >> 8;
            }
            else if (apu->ex_chip & 32)
            {
               accum += PSGSoundRender() >> 7;
            }
         }

         /* do any filtering */
         if (APU_FILTER_NONE != apu->filter_type)
         {
            next_sample = accum;

            if (APU_FILTER_LOWPASS == apu->filter_type)
            {
               accum += prev_sample;
               accum >>= 1;
            }
            else
               next_sample =
               accum = (accum + accum + accum + prev_sample) >> 2;

            prev_sample = next_sample;
         }

         /* little extra kick for the kids */
         //accum <<= 1;
/* Overflow fixed by T.Yano Dec.12.2000 */
#ifdef APU_YANO
         // Cancel DC offset Dec.17.2000
         {
            static double ave, max, min;
            double delta;
            delta = (max-min)/32768.0;
            max -= delta;
            min += delta;
            if (accum>max) max=accum;
            if (accum<min) min=accum;
            ave -= ave/1024.0;
            ave += (max+min)/2048.0;
            accum -= (int32)ave;
         }
#ifdef APU_HPF_ENABLE
         { // Just test (1/167 for 44.1k)
            static double lpf_out;
            lpf_out *=166.0/167.0;
            lpf_out += accum/167.0;
            accum -= (int32) lpf_out;
         }
#endif /* APU_HPF_ENABLE */
#endif /* APU_YANO */

         /* prevent clipping */
         if (accum > 0x7FFF)
            accum = 0x7FFF;
         else if (accum < -0x8000)
            accum = -0x8000;

         /* signed 16-bit output, unsigned 8-bit */
         if (16 == apu->sample_bits)
            *((int16 *) buffer)++ = (int16) accum;
         else
            *((uint8 *) buffer)++ = (accum >> 8) ^ 0x80;
      }
   }

   /* resync cycle counter */
   apu->elapsed_cycles = nes6502_getcycles(FALSE);
}

/* set the filter type */
void apu_setfilter(int filter_type)
{
   ASSERT(apu);
   apu->filter_type = filter_type;
}

// apu_reset_apus() added by T.Yano
void apu_reset_apus(APUSOUND *apus)
{
   int i;
#ifdef APU_YANO
   boolean mode;
#endif

   // Reset rectangles
   for ( i=0; i<2; i++) {
      memset(&apus->rectangle[i], 0, sizeof(apus->rectangle[i]));
   }
   apus->rectangle[0].sweep_complement = TRUE;
   apus->rectangle[1].sweep_complement = FALSE;
#ifdef APU_YANO
   mode = apus->triangle.ideal_triangle;
#endif
   memset(&apus->triangle, 0, sizeof(apus->triangle));
#ifdef APU_YANO
   apus->triangle.ideal_triangle = mode;
#endif
   memset(&apus->noise, 0, sizeof(apus->noise));
   memset(&apus->dmc, 0, sizeof(apus->dmc));
}

void apu_reset(void)
{
   uint32 address;

   ASSERT(apu);

   apu->elapsed_cycles = 0;
   //memset(&apu->queue, 0, APUQUEUE_SIZE * sizeof(apudata_t));
   //apu->q_head = apu->q_tail = 0;
   memset(&apu->queue, 0, sizeof(apuqueue_t));

   //memset(&apu->ex_queue, 0, APUQUEUE_SIZE * sizeof(apudata_t));
   //apu->ex_q_head = apu->ex_q_tail = 0;
   memset(&apu->ex_queue, 0, sizeof(apuqueue_t));
   apu->ex_chip = 0;

   // added by T.Yano
   apu_reset_apus( &apu->apus);
   apu->enable_reg = 0;
   apu->enable_reg_cur = 0;

   /* use to avoid bugs =) */
   for (address = 0x4000; address <= 0x4013; address++)
   {
      apu_regwrite(address, 0);
      apu_write_cur(address, 0);
   }

#ifdef NSF_PLAYER
   apu_regwrite(0x400C, 0x10); /* silence noise channel on NSF start */
   apu_regwrite(0x4015, 0x0F);
   apu_write_cur(0x400c, 0x10);
   apu_write_cur(0x4015, 0x0F);
#else /* !NSF_PLAYER */
   apu_regwrite(0x4015, 0x00);
   apu_write_cur(0x4015, 0x00);
#endif /* !NSF_PLAYER */

   if (apu->ext)
      apu->ext->reset();

   // for ExSound
   LogTableInitialize ();
   FDSSoundReset();
   FDSSoundVolume(1);
   PSGSoundReset();
   PSGSoundVolume(1);
   N106SoundReset();
   N106SoundVolume(1);
   VRC6SoundReset();
   VRC6SoundVolume(1);
   OPLLSoundReset();
   OPLLSoundVolume(1);
   MMC5SoundReset();
   MMC5SoundVolume(1);

   // for $4017:bit7 by T.Yano
   apu_cnt_rate = 5;
}

void apu_build_luts(int num_samples)
{
   int i;

   // decay_lut[], vbl_lut[], trilength_lut[] modified (x5) for $4017:bit7 by T.Yano
   /* lut used for enveloping and frequency sweeps */
   for (i = 0; i < 16; i++)
      decay_lut[i] = num_samples * (i + 1) * 5;

   /* used for note length, based on vblanks and size of audio buffer */
   for (i = 0; i < 32; i++)
      vbl_lut[i] = vbl_length[i] * num_samples * 5;

   /* triangle wave channel's linear length table */
   for (i = 0; i < 128; i++)
      trilength_lut[i] = num_samples * i * 5;

#ifndef REALTIME_NOISE
   /* generate noise samples */
   shift_register15(noise_long_lut, APU_NOISE_32K);
   shift_register15(noise_short_lut, APU_NOISE_93);
#endif /* !REALTIME_NOISE */
}

static void apu_setactive(apu_t *active)
{
   ASSERT(active);
   apu = active;
}

void apu_setparams(int sample_rate, int refresh_rate, int frag_size, int sample_bits)
{
   apu->sample_rate = sample_rate;
   apu->refresh_rate = refresh_rate;
   apu->sample_bits = sample_bits;

   apu->num_samples = sample_rate / refresh_rate;
   //apu->num_samples = frag_size;
   frag_size = frag_size; /* quell warnings */

   /* turn into fixed point! */
   apu->cycle_rate = (int32) (APU_BASEFREQ * 65536.0 / (float) sample_rate);

   /* build various lookup tables for apu */
   apu_build_luts(apu->num_samples);

//DCR   apu_reset();
}

#ifdef APU_YANO
void apu_setmode(int item, int mode)
{
   switch (item) {
   case APUMODE_IDEAL_TRIANGLE:
      if ( apu != NULL) apu->apus.triangle.ideal_triangle = mode;
      break;
   case APUMODE_SMOOTH_ENVELOPE:
   case APUMODE_SMOOTH_SWEEP:
   default:
      break;
   }
}
#endif /* APU_YANO */

/* Initializes emulated sound hardware, creates waveforms/voices */
apu_t *apu_create(int sample_rate, int refresh_rate, int frag_size, int sample_bits)
{
   apu_t *temp_apu;
   int channel;

   temp_apu = malloc(sizeof(apu_t));
   memset(temp_apu, 0, sizeof(apu_t));	// Rick
   if (NULL == temp_apu)
      return NULL;

   /* set the stupid flag to tell difference between two rectangles */
   temp_apu->apus.rectangle[0].sweep_complement = TRUE;
   temp_apu->apus.rectangle[1].sweep_complement = FALSE;

   /* set the update routine */
   temp_apu->process = apu_process;
   temp_apu->ext = NULL;

   apu_setactive(temp_apu);

   apu_setparams(sample_rate, refresh_rate, frag_size, sample_bits);
   apu_reset(); //DCR

   for (channel = 0; channel < 6; channel++)
      apu_setchan(channel, TRUE);

   apu_setfilter(APU_FILTER_LOWPASS);
#ifdef APU_YANO
   apu_setmode(APUMODE_IDEAL_TRIANGLE, FALSE);
#endif /* APU_YANO */

   return temp_apu;
}

void apu_destroy(apu_t **src_apu)
{
   if (*src_apu)
   {
      if ((*src_apu)->ext)
         (*src_apu)->ext->shutdown();
      free(*src_apu);
   }
}

void apu_setext(apu_t *src_apu, apuext_t *ext)
{
   ASSERT(src_apu);

   src_apu->ext = ext;

   /* initialize it */
   if (src_apu->ext)
      src_apu->ext->init();
}

void sync_apu_register()
{
   if (!apu->apus.rectangle[0].holdnote_cur && apu->apus.rectangle[0].vbl_length_cur > 0)
   {
      apu->apus.rectangle[0].vbl_length_cur -= apu_cnt_rate;
   }
   if (!apu->apus.rectangle[1].holdnote_cur && apu->apus.rectangle[1].vbl_length_cur > 0)
   {
      apu->apus.rectangle[1].vbl_length_cur -= apu_cnt_rate;
   }
   if (apu->apus.triangle.counter_started_cur)
   {
      if (apu->apus.triangle.vbl_length_cur > 0 && !apu->apus.triangle.holdnote_cur)
      {
         apu->apus.triangle.vbl_length_cur -= apu_cnt_rate;
      }
   }
   if (!apu->apus.noise.holdnote_cur && apu->apus.noise.vbl_length_cur > 0)
   {
      apu->apus.noise.vbl_length_cur -= apu_cnt_rate;
   }
}

boolean sync_dmc_register(uint32 cpu_cycles)
{
	boolean irq_occurred = FALSE;

	// keep them local for speed. Rick.
	int phaseacc_cur = apu->apus.dmc.phaseacc_cur;
	
	phaseacc_cur -= (int)cpu_cycles;
	
	if (phaseacc_cur < 0) {
		int freq_cur = apu->apus.dmc.freq_cur * 8;
		int dma_length_cur = apu->apus.dmc.dma_length_cur;
		int cached_dmalength_cur = apu->apus.dmc.cached_dmalength_cur;
		int irq_occurred_cur = apu->apus.dmc.irq_occurred_cur;
		int enabled_cur = apu->apus.dmc.enabled_cur;
		int looping_cur = apu->apus.dmc.looping_cur;
		int irq_gen_cur = apu->apus.dmc.irq_gen_cur;
		
		while(phaseacc_cur < 0)
		{
			phaseacc_cur += freq_cur;
			if (dma_length_cur)
			{
				if (--dma_length_cur == 0)
				{
					if (looping_cur)
					{
						dma_length_cur = cached_dmalength_cur;
						irq_occurred_cur = FALSE;
					}
					else
					{
						dma_length_cur = 0;
						if (irq_gen_cur)
						{
							irq_occurred_cur = TRUE;
							irq_occurred = TRUE;
						}
						enabled_cur = FALSE;
					}
				}
			}
		}
		apu->apus.dmc.dma_length_cur = dma_length_cur;
		apu->apus.dmc.irq_occurred_cur = irq_occurred_cur;
		apu->apus.dmc.enabled_cur = enabled_cur;
	}
	apu->apus.dmc.phaseacc_cur = phaseacc_cur;

	return irq_occurred;
}

/*
** $Log: nes_apu.c,v $
** Revision 1.5  2003/06/22 08:30:13  Rick
** minor speed-ups
**
** Revision 1.4  2003/03/24 14:40:29  Rick
** replaced APU code with uonester's one
**
** Revision 2.16  2001/02/18 21:31:00  YANO, takashi
** added $4017:bit7 control
** fixed set chan->enabled
** fixed bug of DPCM last sample in APU_YANO
** trim channel balance again
** fixed APU_BASEFREQ
**
** Revision 2.15  2001/01/04 22:09:10  YANO, takashi
** fixed dmc bug of delta_bit
**
** Revision 2.14  2001/01/03 17:46:00  YANO, takashi
** fixed code for DC balance
** trim noise and dmc volume
**
** Revision 2.13  2000/12/23 02:00:40  YANO, takashi
** added apu_setmode()
**
** Revision 2.12  2000/12/22 23:23:40  YANO, takashi
** trim output volume balance
** re-disabled APU_IDEAL_TRIANGLE
** modify weighted filter
**
** Revision 2.11  2000/12/17 00:08:10  YANO, takashi
** cancel DC offset
** enable APU_IDEAL_TRIANGLE
**
** Revision 2.10  2000/12/12 02:07:46  YANO, takashi
** improve sound purity
**
** Revision 2.09  2000/12/09 11:41:00  TAKEDA, toshiya
** sync DPCM registers
** support DMCP IRQ
**
** Revision 2.08  2000/12/07 00:10:00  TAKEDA, toshiya
** sync DPCM registers
**
** Revision 2.07  2000/11/15 16:32:00  TAKEDA, toshiya
** fixed memory reak of ExtraSound
**
** Revision 2.06  2000/11/02 21:40:00  TAKEDA, toshiya
** fixed read $4015 (triangle.write_latency)
**
** Revision 2.05  2000/11/01 21:44:00  TAKEDA, toshiya
** fixed read $4015
**
** Revision 2.04  2000/10/26 00:05:00  TAKEDA, toshiya
** changed VRC6 volume
** changed chip number
**
** Revision 2.03  2000/10/23 16:06:00  TAKEDA, toshiya
** added ExtraSound Support of MMC5
** sync All ExtraSound
**
** Revision 2.02  2000/10/23 00:07:00  TAKEDA, toshiya
** fixed VRC6 write reg
**
** Revision 2.01  2000/10/22 21:12:00  TAKEDA, toshiya
** added ExtraSound Support of FME7
**
** Revision 2.00  2000/10/22 00:12:15  TAKEDA, toshiya
** added ExtraSound Support of N106, FDS, VRC6, VRC7
**
** ---------------------------------------------------
**
** Revision 1.33  2000/08/15 12:38:04  matt
** removed debug output
**
** Revision 1.32  2000/08/15 12:36:51  matt
** calling apu_process with buffer=NULL causes silent emulation of APU
**
** Revision 1.31  2000/08/11 02:27:21  matt
** general cleanups, plus apu_setparams routine
**
** Revision 1.30  2000/07/31 04:32:52  matt
** fragsize problem fixed, perhaps
**
** Revision 1.29  2000/07/30 04:32:59  matt
** no more apu_getcyclerate hack
**
** Revision 1.28  2000/07/28 03:15:46  matt
** accuracy changes for rectangle frequency sweeps
**
** Revision 1.27  2000/07/27 02:49:50  matt
** eccentricity in sweeping hardware now emulated correctly
**
** Revision 1.26  2000/07/25 02:25:14  matt
** safer apu_destroy
**
** Revision 1.25  2000/07/23 15:10:54  matt
** hacks for win32
**
** Revision 1.24  2000/07/17 01:52:31  matt
** made sure last line of all source files is a newline
**
** Revision 1.23  2000/07/10 19:24:55  matt
** irqs are not supported in NSF playing mode
**
** Revision 1.22  2000/07/10 13:54:32  matt
** using generic nes_irq() now
**
** Revision 1.21  2000/07/10 05:29:34  matt
** moved joypad/oam dma from apu to ppu
**
** Revision 1.20  2000/07/09 03:49:31  matt
** apu irqs now draw an irq line (bleh)
**
** Revision 1.19  2000/07/04 04:53:26  matt
** minor changes, sound amplification
**
** Revision 1.18  2000/07/03 02:18:53  matt
** much better external module exporting
**
** Revision 1.17  2000/06/26 11:01:55  matt
** made triangle a tad quieter
**
** Revision 1.16  2000/06/26 05:10:33  matt
** fixed cycle rate generation accuracy
**
** Revision 1.15  2000/06/26 05:00:37  matt
** cleanups
**
** Revision 1.14  2000/06/23 11:06:24  matt
** more faithful mixing of channels
**
** Revision 1.13  2000/06/23 03:29:27  matt
** cleaned up external sound inteface
**
** Revision 1.12  2000/06/20 00:08:39  matt
** bugfix to rectangle wave
**
** Revision 1.11  2000/06/13 13:48:58  matt
** fixed triangle write latency for fixed point apu cycle rate
**
** Revision 1.10  2000/06/12 01:14:36  matt
** minor change to clipping extents
**
** Revision 1.9  2000/06/09 20:00:56  matt
** fixed noise hiccup in NSF player mode
**
** Revision 1.8  2000/06/09 16:49:02  matt
** removed all floating point from sound generation
**
** Revision 1.7  2000/06/09 15:12:28  matt
** initial revision
**
*/

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -