📄 nes_apu.c
字号:
{
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 + -