[Gamecube/Wii] improved A/V Sync: when VSYNC is enabled, audio resampler output rate is now adjusted (+/-0,1 %) to always keep audio & video synchronized and input lag is reduced by one frame.

This commit is contained in:
EkeEke 2014-10-10 18:34:33 +02:00
parent 37dd44df6c
commit bf184b9440
20 changed files with 391 additions and 384 deletions

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 MiB

After

Width:  |  Height:  |  Size: 3.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 MiB

After

Width:  |  Height:  |  Size: 3.8 MiB

View File

@ -2,7 +2,7 @@
* Genesis Plus * Genesis Plus
* CD drive processor & CD-DA fader * CD drive processor & CD-DA fader
* *
* Copyright (C) 2012-2013 Eke-Eke (Genesis Plus GX) * Copyright (C) 2012-2014 Eke-Eke (Genesis Plus GX)
* *
* Redistribution and use of this code or any derivative works are permitted * Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met: * provided that the following conditions are met:
@ -150,9 +150,6 @@ static const char extensions[SUPPORTED_EXT][16] =
" - %d.wav" " - %d.wav"
}; };
static blip_t* blip[2];
#ifdef USE_LIBTREMOR #ifdef USE_LIBTREMOR
#ifdef DISABLE_MANY_OGG_OPEN_FILES #ifdef DISABLE_MANY_OGG_OPEN_FILES
static void ogg_free(int i) static void ogg_free(int i)
@ -172,14 +169,12 @@ static void ogg_free(int i)
#endif #endif
#endif #endif
void cdd_init(blip_t* left, blip_t* right) void cdd_init(int samplerate)
{ {
/* CD-DA is running by default at 44100 Hz */ /* CD-DA is running by default at 44100 Hz */
/* Audio stream is resampled to desired rate using Blip Buffer */ /* Audio stream is resampled to desired rate using Blip Buffer */
blip[0] = left; blip_set_rates(snd.blips[2][0], 44100, samplerate);
blip[1] = right; blip_set_rates(snd.blips[2][1], 44100, samplerate);
blip_set_rates(left, 44100, snd.sample_rate);
blip_set_rates(right, 44100, snd.sample_rate);
} }
void cdd_reset(void) void cdd_reset(void)
@ -953,7 +948,7 @@ void cdd_read_audio(unsigned int samples)
int16 r = cdd.audio[1]; int16 r = cdd.audio[1];
/* get number of internal clocks (samples) needed */ /* get number of internal clocks (samples) needed */
samples = blip_clocks_needed(blip[0], samples); samples = blip_clocks_needed(snd.blips[2][0], samples);
/* audio track playing ? */ /* audio track playing ? */
if (!scd.regs[0x36>>1].byte.h && cdd.toc.tracks[cdd.index].fd) if (!scd.regs[0x36>>1].byte.h && cdd.toc.tracks[cdd.index].fd)
@ -996,13 +991,13 @@ void cdd_read_audio(unsigned int samples)
delta = ((ptr[0] * mul) / 1024) - l; delta = ((ptr[0] * mul) / 1024) - l;
ptr++; ptr++;
l += delta; l += delta;
blip_add_delta_fast(blip[0], i, delta); blip_add_delta_fast(snd.blips[2][0], i, delta);
/* right channel */ /* right channel */
delta = ((ptr[0] * mul) / 1024) - r; delta = ((ptr[0] * mul) / 1024) - r;
ptr++; ptr++;
r += delta; r += delta;
blip_add_delta_fast(blip[1], i, delta); blip_add_delta_fast(snd.blips[2][1], i, delta);
/* update CD-DA fader volume (one step/sample) */ /* update CD-DA fader volume (one step/sample) */
if (curVol < endVol) if (curVol < endVol)
@ -1048,7 +1043,7 @@ void cdd_read_audio(unsigned int samples)
ptr += 2; ptr += 2;
#endif #endif
l += delta; l += delta;
blip_add_delta_fast(blip[0], i, delta); blip_add_delta_fast(snd.blips[2][0], i, delta);
/* right channel */ /* right channel */
#ifdef LSB_FIRST #ifdef LSB_FIRST
@ -1059,7 +1054,7 @@ void cdd_read_audio(unsigned int samples)
ptr += 2; ptr += 2;
#endif #endif
r += delta; r += delta;
blip_add_delta_fast(blip[1], i, delta); blip_add_delta_fast(snd.blips[2][1], i, delta);
/* update CD-DA fader volume (one step/sample) */ /* update CD-DA fader volume (one step/sample) */
if (curVol < endVol) if (curVol < endVol)
@ -1090,8 +1085,8 @@ void cdd_read_audio(unsigned int samples)
else else
{ {
/* no audio output */ /* no audio output */
if (l) blip_add_delta_fast(blip[0], 0, -l); if (l) blip_add_delta_fast(snd.blips[2][0], 0, -l);
if (r) blip_add_delta_fast(blip[1], 0, -r); if (r) blip_add_delta_fast(snd.blips[2][1], 0, -r);
/* save audio output for next frame */ /* save audio output for next frame */
cdd.audio[0] = 0; cdd.audio[0] = 0;
@ -1099,8 +1094,8 @@ void cdd_read_audio(unsigned int samples)
} }
/* end of Blip Buffer timeframe */ /* end of Blip Buffer timeframe */
blip_end_frame(blip[0], samples); blip_end_frame(snd.blips[2][0], samples);
blip_end_frame(blip[1], samples); blip_end_frame(snd.blips[2][1], samples);
} }

View File

@ -2,7 +2,7 @@
* Genesis Plus * Genesis Plus
* CD drive processor & CD-DA fader * CD drive processor & CD-DA fader
* *
* Copyright (C) 2012-2013 Eke-Eke (Genesis Plus GX) * Copyright (C) 2012-2014 Eke-Eke (Genesis Plus GX)
* *
* Redistribution and use of this code or any derivative works are permitted * Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met: * provided that the following conditions are met:
@ -98,7 +98,7 @@ typedef struct
} cdd_t; } cdd_t;
/* Function prototypes */ /* Function prototypes */
extern void cdd_init(blip_t* left, blip_t* right); extern void cdd_init(int samplerate);
extern void cdd_reset(void); extern void cdd_reset(void);
extern int cdd_context_save(uint8 *state); extern int cdd_context_save(uint8 *state);
extern int cdd_context_load(uint8 *state); extern int cdd_context_load(uint8 *state);

View File

@ -2,7 +2,7 @@
* Genesis Plus * Genesis Plus
* PCM sound chip (315-5476A) (RF5C164 compatible) * PCM sound chip (315-5476A) (RF5C164 compatible)
* *
* Copyright (C) 2012 Eke-Eke (Genesis Plus GX) * Copyright (C) 2012-2014 Eke-Eke (Genesis Plus GX)
* *
* Redistribution and use of this code or any derivative works are permitted * Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met: * provided that the following conditions are met:
@ -41,19 +41,12 @@
#define pcm scd.pcm_hw #define pcm scd.pcm_hw
static blip_t* blip[2]; void pcm_init(double clock, int samplerate)
void pcm_init(blip_t* left, blip_t* right)
{ {
/* number of SCD master clocks run per second */ /* PCM chip is running at original rate and is synchronized with SUB-CPU */
double mclk = snd.frame_rate ? (SCYCLES_PER_LINE * (vdp_pal ? 313 : 262) * snd.frame_rate) : SCD_CLOCK;
/* PCM chips is running at original rate and is synchronized with SUB-CPU */
/* Chip output is resampled to desired rate using Blip Buffer. */ /* Chip output is resampled to desired rate using Blip Buffer. */
blip[0] = left; blip_set_rates(snd.blips[1][0], clock / PCM_SCYCLES_RATIO, samplerate);
blip[1] = right; blip_set_rates(snd.blips[1][1], clock / PCM_SCYCLES_RATIO, samplerate);
blip_set_rates(left, mclk / PCM_SCYCLES_RATIO, snd.sample_rate);
blip_set_rates(right, mclk / PCM_SCYCLES_RATIO, snd.sample_rate);
} }
void pcm_reset(void) void pcm_reset(void)
@ -78,8 +71,8 @@ void pcm_reset(void)
pcm.cycles = 0; pcm.cycles = 0;
/* clear blip buffers */ /* clear blip buffers */
blip_clear(blip[0]); blip_clear(snd.blips[1][0]);
blip_clear(blip[1]); blip_clear(snd.blips[1][1]);
} }
int pcm_context_save(uint8 *state) int pcm_context_save(uint8 *state)
@ -190,14 +183,14 @@ void pcm_run(unsigned int length)
/* check if PCM left output changed */ /* check if PCM left output changed */
if (pcm.out[0] != l) if (pcm.out[0] != l)
{ {
blip_add_delta_fast(blip[0], i, l-pcm.out[0]); blip_add_delta_fast(snd.blips[1][0], i, l-pcm.out[0]);
pcm.out[0] = l; pcm.out[0] = l;
} }
/* check if PCM right output changed */ /* check if PCM right output changed */
if (pcm.out[1] != r) if (pcm.out[1] != r)
{ {
blip_add_delta_fast(blip[1], i, r-pcm.out[1]); blip_add_delta_fast(snd.blips[1][1], i, r-pcm.out[1]);
pcm.out[1] = r; pcm.out[1] = r;
} }
} }
@ -207,21 +200,21 @@ void pcm_run(unsigned int length)
/* check if PCM left output changed */ /* check if PCM left output changed */
if (pcm.out[0]) if (pcm.out[0])
{ {
blip_add_delta_fast(blip[0], 0, -pcm.out[0]); blip_add_delta_fast(snd.blips[1][0], 0, -pcm.out[0]);
pcm.out[0] = 0; pcm.out[0] = 0;
} }
/* check if PCM right output changed */ /* check if PCM right output changed */
if (pcm.out[1]) if (pcm.out[1])
{ {
blip_add_delta_fast(blip[1], 0, -pcm.out[1]); blip_add_delta_fast(snd.blips[1][1], 0, -pcm.out[1]);
pcm.out[1] = 0; pcm.out[1] = 0;
} }
} }
/* end of blip buffer frame */ /* end of blip buffer frame */
blip_end_frame(blip[0], length); blip_end_frame(snd.blips[1][0], length);
blip_end_frame(blip[1], length); blip_end_frame(snd.blips[1][1], length);
/* update PCM master clock counter */ /* update PCM master clock counter */
pcm.cycles += length * PCM_SCYCLES_RATIO; pcm.cycles += length * PCM_SCYCLES_RATIO;
@ -230,7 +223,7 @@ void pcm_run(unsigned int length)
void pcm_update(unsigned int samples) void pcm_update(unsigned int samples)
{ {
/* get number of internal clocks (samples) needed */ /* get number of internal clocks (samples) needed */
unsigned int clocks = blip_clocks_needed(blip[0], samples); unsigned int clocks = blip_clocks_needed(snd.blips[1][0], samples);
/* run PCM chip */ /* run PCM chip */
if (clocks > 0) if (clocks > 0)

View File

@ -2,7 +2,7 @@
* Genesis Plus * Genesis Plus
* PCM sound chip (315-5476A) (RF5C164 compatible) * PCM sound chip (315-5476A) (RF5C164 compatible)
* *
* Copyright (C) 2012 Eke-Eke (Genesis Plus GX) * Copyright (C) 2012-2014 Eke-Eke (Genesis Plus GX)
* *
* Redistribution and use of this code or any derivative works are permitted * Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met: * provided that the following conditions are met:
@ -65,7 +65,7 @@ typedef struct
} pcm_t; } pcm_t;
/* Function prototypes */ /* Function prototypes */
extern void pcm_init(blip_t* left, blip_t* right); extern void pcm_init(double clock, int rate);
extern void pcm_reset(void); extern void pcm_reset(void);
extern int pcm_context_save(uint8 *state); extern int pcm_context_save(uint8 *state);
extern int pcm_context_load(uint8 *state); extern int pcm_context_load(uint8 *state);

View File

@ -94,14 +94,9 @@ static const uint16 PSGVolumeValues[16] =
static SN76489_Context SN76489; static SN76489_Context SN76489;
static blip_t* blip[2]; void SN76489_Init(int type)
void SN76489_Init(blip_t* left, blip_t* right, int type)
{ {
int i; int i;
blip[0] = left;
blip[1] = right;
for (i=0; i<4; i++) for (i=0; i<4; i++)
{ {
@ -177,7 +172,7 @@ INLINE void UpdateToneAmplitude(int i, int time)
if (delta != 0) if (delta != 0)
{ {
SN76489.ChanOut[i][0] += delta; SN76489.ChanOut[i][0] += delta;
blip_add_delta_fast(blip[0], time, delta); blip_add_delta_fast(snd.blips[0][0], time, delta);
} }
/* right output */ /* right output */
@ -185,7 +180,7 @@ INLINE void UpdateToneAmplitude(int i, int time)
if (delta != 0) if (delta != 0)
{ {
SN76489.ChanOut[i][1] += delta; SN76489.ChanOut[i][1] += delta;
blip_add_delta_fast(blip[1], time, delta); blip_add_delta_fast(snd.blips[0][1], time, delta);
} }
} }
@ -199,7 +194,7 @@ INLINE void UpdateNoiseAmplitude(int time)
if (delta != 0) if (delta != 0)
{ {
SN76489.ChanOut[3][0] += delta; SN76489.ChanOut[3][0] += delta;
blip_add_delta_fast(blip[0], time, delta); blip_add_delta_fast(snd.blips[0][0], time, delta);
} }
/* right output */ /* right output */
@ -207,7 +202,7 @@ INLINE void UpdateNoiseAmplitude(int time)
if (delta != 0) if (delta != 0)
{ {
SN76489.ChanOut[3][1] += delta; SN76489.ChanOut[3][1] += delta;
blip_add_delta_fast(blip[1], time, delta); blip_add_delta_fast(snd.blips[0][1], time, delta);
} }
} }

View File

@ -12,7 +12,7 @@
#define SN_INTEGRATED 1 #define SN_INTEGRATED 1
/* Function prototypes */ /* Function prototypes */
extern void SN76489_Init(blip_t* left, blip_t* right, int type); extern void SN76489_Init(int type);
extern void SN76489_Reset(void); extern void SN76489_Reset(void);
extern void SN76489_Config(unsigned int clocks, int preAmp, int boostNoise, int stereo); extern void SN76489_Config(unsigned int clocks, int preAmp, int boostNoise, int stereo);
extern void SN76489_Write(unsigned int clocks, unsigned int data); extern void SN76489_Write(unsigned int clocks, unsigned int data);

View File

@ -102,6 +102,7 @@ void sound_init( void )
} }
/* Initialize PSG chip */ /* Initialize PSG chip */
SN76489_Init((system_hw == SYSTEM_SG) ? SN_DISCRETE : SN_INTEGRATED);
SN76489_Config(0, config.psg_preamp, config.psgBoostNoise, 0xff); SN76489_Config(0, config.psg_preamp, config.psgBoostNoise, 0xff);
} }

View File

@ -116,12 +116,10 @@ int state_load(unsigned char *state)
bufferptr += sound_context_load(&state[bufferptr]); bufferptr += sound_context_load(&state[bufferptr]);
if ((system_hw & SYSTEM_PBC) == SYSTEM_MD) if ((system_hw & SYSTEM_PBC) == SYSTEM_MD)
{ {
SN76489_Init(snd.blips[0][0], snd.blips[0][1], SN_INTEGRATED);
SN76489_Config(0, config.psg_preamp, config.psgBoostNoise, 0xff); SN76489_Config(0, config.psg_preamp, config.psgBoostNoise, 0xff);
} }
else else
{ {
SN76489_Init(snd.blips[0][0], snd.blips[0][1], (system_hw == SYSTEM_SG) ? SN_DISCRETE : SN_INTEGRATED);
SN76489_Config(0, config.psg_preamp, config.psgBoostNoise, io_reg[6]); SN76489_Config(0, config.psg_preamp, config.psgBoostNoise, io_reg[6]);
} }

View File

@ -60,6 +60,50 @@ static int16 llp,rrp;
/******************************************************************************************/ /******************************************************************************************/
int audio_init(int samplerate, double framerate) int audio_init(int samplerate, double framerate)
{
/* Shutdown first */
audio_shutdown();
/* Clear the sound data context */
memset(&snd, 0, sizeof (snd));
/* Initialize Blip Buffers */
snd.blips[0][0] = blip_new(samplerate / 10);
snd.blips[0][1] = blip_new(samplerate / 10);
if (!snd.blips[0][0] || !snd.blips[0][1])
{
audio_shutdown();
return -1;
}
/* Mega CD sound hardware */
if (system_hw == SYSTEM_MCD)
{
/* allocate blip buffers */
snd.blips[1][0] = blip_new(samplerate / 10);
snd.blips[1][1] = blip_new(samplerate / 10);
snd.blips[2][0] = blip_new(samplerate / 10);
snd.blips[2][1] = blip_new(samplerate / 10);
if (!snd.blips[1][0] || !snd.blips[1][1] || !snd.blips[2][0] || !snd.blips[2][1])
{
audio_shutdown();
return -1;
}
}
/* Initialize resampler internal rates */
audio_set_rate(samplerate, framerate);
/* Set audio enable flag */
snd.enabled = 1;
/* Reset audio */
audio_reset();
return (0);
}
void audio_set_rate(int samplerate, double framerate)
{ {
/* Number of M-cycles executed per second. */ /* Number of M-cycles executed per second. */
/* All emulated chips are kept in sync by using a common oscillator (MCLOCK) */ /* All emulated chips are kept in sync by using a common oscillator (MCLOCK) */
@ -84,25 +128,6 @@ int audio_init(int samplerate, double framerate)
/* */ /* */
double mclk = framerate ? (MCYCLES_PER_LINE * (vdp_pal ? 313 : 262) * framerate) : system_clock; double mclk = framerate ? (MCYCLES_PER_LINE * (vdp_pal ? 313 : 262) * framerate) : system_clock;
/* Shutdown first */
audio_shutdown();
/* Clear the sound data context */
memset(&snd, 0, sizeof (snd));
/* Initialize audio rates */
snd.sample_rate = samplerate;
snd.frame_rate = framerate;
/* Initialize Blip Buffers */
snd.blips[0][0] = blip_new(samplerate / 10);
snd.blips[0][1] = blip_new(samplerate / 10);
if (!snd.blips[0][0] || !snd.blips[0][1])
{
audio_shutdown();
return -1;
}
/* For maximal accuracy, sound chips are running at their original rate using common */ /* For maximal accuracy, sound chips are running at their original rate using common */
/* master clock timebase so they remain perfectly synchronized together, while still */ /* master clock timebase so they remain perfectly synchronized together, while still */
/* being synchronized with 68K and Z80 CPUs as well. Mixed sound chip output is then */ /* being synchronized with 68K and Z80 CPUs as well. Mixed sound chip output is then */
@ -110,37 +135,22 @@ int audio_init(int samplerate, double framerate)
blip_set_rates(snd.blips[0][0], mclk, samplerate); blip_set_rates(snd.blips[0][0], mclk, samplerate);
blip_set_rates(snd.blips[0][1], mclk, samplerate); blip_set_rates(snd.blips[0][1], mclk, samplerate);
/* Initialize PSG core */
SN76489_Init(snd.blips[0][0], snd.blips[0][1], (system_hw == SYSTEM_SG) ? SN_DISCRETE : SN_INTEGRATED);
/* Mega CD sound hardware */ /* Mega CD sound hardware */
if (system_hw == SYSTEM_MCD) if (system_hw == SYSTEM_MCD)
{ {
/* allocate blip buffers */ /* number of SCD master clocks run per second */
snd.blips[1][0] = blip_new(samplerate / 10); mclk = framerate ? (SCYCLES_PER_LINE * (vdp_pal ? 313 : 262) * framerate) : SCD_CLOCK;
snd.blips[1][1] = blip_new(samplerate / 10);
snd.blips[2][0] = blip_new(samplerate / 10);
snd.blips[2][1] = blip_new(samplerate / 10);
if (!snd.blips[1][0] || !snd.blips[1][1] || !snd.blips[2][0] || !snd.blips[2][1])
{
audio_shutdown();
return -1;
}
/* Initialize PCM core */ /* PCM core */
pcm_init(snd.blips[1][0], snd.blips[1][1]); pcm_init(mclk, samplerate);
/* Initialize CDD core */ /* CDD core */
cdd_init(snd.blips[2][0], snd.blips[2][1]); cdd_init(samplerate);
} }
/* Set audio enable flag */ /* Reinitialize internal rates */
snd.enabled = 1; snd.sample_rate = samplerate;
snd.frame_rate = framerate;
/* Reset audio */
audio_reset();
return (0);
} }
void audio_reset(void) void audio_reset(void)

View File

@ -106,6 +106,7 @@ extern uint32 system_clock;
/* Function prototypes */ /* Function prototypes */
extern int audio_init(int samplerate, double framerate); extern int audio_init(int samplerate, double framerate);
extern void audio_set_rate(int samplerate, double framerate);
extern void audio_reset(void); extern void audio_reset(void);
extern void audio_shutdown(void); extern void audio_shutdown(void);
extern int audio_update(int16 *buffer); extern int audio_update(int16 *buffer);

View File

@ -45,14 +45,12 @@
/* Number of sound buffers */ /* Number of sound buffers */
#define SOUND_BUFFER_NUM 3 #define SOUND_BUFFER_NUM 3
/* audio DMA status */
u32 audioStarted;
/* DMA soundbuffers (required to be 32-bytes aligned) */ /* DMA soundbuffers (required to be 32-bytes aligned) */
static u8 soundbuffer[SOUND_BUFFER_NUM][SOUND_BUFFER_LEN] ATTRIBUTE_ALIGN(32); static u8 soundbuffer[SOUND_BUFFER_NUM][SOUND_BUFFER_LEN] ATTRIBUTE_ALIGN(32);
/* Current work soundbuffer */ /* Current work soundbuffer */
static u8 mixbuffer; static int bufferIndex;
static int bufferSize;
/* Background music */ /* Background music */
static u8 *Bg_music_ogg = NULL; static u8 *Bg_music_ogg = NULL;
@ -96,9 +94,6 @@ void gx_audio_Init(void)
} }
fclose(f); fclose(f);
} }
/* emulation is synchronized with audio hardware by default */
audioSync = 1;
} }
/* AUDIO engine shutdown */ /* AUDIO engine shutdown */
@ -119,50 +114,64 @@ void gx_audio_Shutdown(void)
This function retrieves samples for the frame then set the next DMA parameters This function retrieves samples for the frame then set the next DMA parameters
Parameters will be taken in account only when current DMA operation is over Parameters will be taken in account only when current DMA operation is over
To keep audio & video synchronized, DMA from external memory to audio interface is
started once by video update function when first frame is ready to be displayed,
then anytime video mode is changed and emulation resynchronized to video hardware.
Once started, audio DMA restarts automatically when all samples have been played.
At that time:
- if DMA settings have not been updated, previous sound buffer will be played again
- if DMA settings are updated too fast, one sound buffer frame might be skipped
Therefore, in order to maintain perfect audio playback without any sound skipping
or lagging, we need to make sure frame emulation is completed and this function is
called before previous DMA transfer is finished and after it has been started.
This is done by synchronizing frame emulation with audio DMA interrupt (which happens
anytime audio DMA restarts). When video sync is enabled, to keep emulation in sync
with both video AND audio, an appropriate number of samples is rendered per frame by
adjusting emulator output samplerate.
***/ ***/
int gx_audio_Update(void) int gx_audio_Update(int status)
{ {
if (audioWait && audioStarted)
{
return SYNC_WAIT;
}
/* Current available soundbuffer */ /* Current available soundbuffer */
s16 *sb = (s16 *)(soundbuffer[mixbuffer]); s16 *sb = (s16 *)(soundbuffer[bufferIndex]);
/* Retrieve audio samples (size must be multiple of 32 bytes) */ /* Make sure current audio frame has not already been updated */
int size = audio_update(sb) * 4; if (status & AUDIO_UPDATE)
/* Update DMA settings */
DCFlushRange((void *)sb, size);
AUDIO_InitDMA((u32) sb, size);
mixbuffer = (mixbuffer + 1) % SOUND_BUFFER_NUM;
audioWait = audioSync;
/* Start Audio DMA */
/* this is called once to kick-off DMA from external memory to audio interface */
/* DMA operation is automatically restarted when all samples have been sent. */
/* If DMA settings are not updated at that time, previous sound buffer will be used. */
/* Therefore we need to make sure frame emulation is completed before current DMA is */
/* completed, by synchronizing frame emulation with DMA start and also by syncing it */
/* with Video Interrupt and outputing a suitable number of samples per frame. */
if (!audioStarted)
{ {
/* restart audio DMA */ /* Retrieve audio samples (size must be multiple of 32 bytes) */
AUDIO_StopDMA(); bufferSize = audio_update(sb) * 4;
AUDIO_StartDMA(); DCFlushRange((void *)sb, bufferSize);
audioStarted = 1;
/* Mark current audio frame as being updated */
status &= ~AUDIO_UPDATE;
} }
return SYNC_AUDIO; /* Wait until previous audio frame is started before pushing current audio frame into DMA */
if ((status & AUDIO_WAIT) && !audioWait)
{
/* Update audio DMA settings for current frame */
AUDIO_InitDMA((u32)sb, bufferSize);
/* Next soundbuffer */
bufferIndex = (bufferIndex + 1) % SOUND_BUFFER_NUM;
/* Set audio wait flag */
audioWait = audioSync;
/* Current audio frame is ready for upcoming DMA */
status &= ~AUDIO_WAIT;
}
return status;
} }
/*** /***
gx_audio_Start gx_audio_Start
This function restart the audio engine This function restarts the audio engine
This is called when coming back from Main Menu This is called when coming back from Main Menu
***/ ***/
void gx_audio_Start(void) void gx_audio_Start(void)
@ -181,11 +190,14 @@ void gx_audio_Start(void)
/* DMA Interrupt callback */ /* DMA Interrupt callback */
AUDIO_RegisterDMACallback(ai_callback); AUDIO_RegisterDMACallback(ai_callback);
/* emulation is synchronized with audio hardware by default */
audioSync = AUDIO_WAIT;
/* reset emulation audio processing */ /* reset emulation audio processing */
memset(soundbuffer, 0, sizeof(soundbuffer)); memset(soundbuffer, 0, sizeof(soundbuffer));
audioStarted = 0; audioWait = 0;
mixbuffer = 0; bufferSize = 0;
audioWait = 0; bufferIndex = 0;
} }
/*** /***
@ -193,7 +205,6 @@ void gx_audio_Start(void)
This function stops current Audio DMA process This function stops current Audio DMA process
This is called when going back to Main Menu This is called when going back to Main Menu
DMA need to be restarted when going back to the game (see above)
***/ ***/
void gx_audio_Stop(void) void gx_audio_Stop(void)
{ {

View File

@ -40,13 +40,12 @@
#ifndef _GC_AUDIO_H_ #ifndef _GC_AUDIO_H_
#define _GC_AUDIO_H_ #define _GC_AUDIO_H_
extern u32 audioStarted;
extern u32 audioSync; extern u32 audioSync;
extern void gx_audio_Init(void); extern void gx_audio_Init(void);
extern void gx_audio_Shutdown(void); extern void gx_audio_Shutdown(void);
extern void gx_audio_Start(void); extern void gx_audio_Start(void);
extern void gx_audio_Stop(void); extern void gx_audio_Stop(void);
extern int gx_audio_Update(void); extern int gx_audio_Update(int status);
#endif #endif

View File

@ -221,8 +221,8 @@ static void pad_update(s8 chan, u8 i)
/* Default fast-forward key combo */ /* Default fast-forward key combo */
if ((p & PAD_TRIGGER_R) && (PAD_ButtonsDown(0) & PAD_BUTTON_START)) if ((p & PAD_TRIGGER_R) && (PAD_ButtonsDown(0) & PAD_BUTTON_START))
{ {
audioSync ^= 1; audioSync ^= AUDIO_WAIT;
videoSync = audioSync & config.vsync & !(gc_pal ^ vdp_pal); videoSync = (audioSync && config.vsync && (gc_pal != vdp_pal)) ? VIDEO_WAIT : 0;
return; return;
} }
@ -1534,8 +1534,8 @@ void gx_input_UpdateEmu(void)
/* Default fast-forward key combo */ /* Default fast-forward key combo */
if (WPAD_ButtonsHeld(0) & (WPAD_BUTTON_MINUS | WPAD_CLASSIC_BUTTON_MINUS)) if (WPAD_ButtonsHeld(0) & (WPAD_BUTTON_MINUS | WPAD_CLASSIC_BUTTON_MINUS))
{ {
audioSync ^= 1; audioSync ^= AUDIO_WAIT;
videoSync = audioSync & config.vsync & !(gc_pal ^ vdp_pal); videoSync = (audioSync && config.vsync && (gc_pal != vdp_pal)) ? VIDEO_WAIT : 0;
return; return;
} }

View File

@ -357,9 +357,42 @@ static u8 d_list[32] ATTRIBUTE_ALIGN(32) =
/* VSYNC callback */ /* VSYNC callback */
static void vi_callback(u32 cnt) static void vi_callback(u32 cnt)
{ {
/* get audio DMA remaining length */
vu16* const _dspReg = (u16*)0xCC005000;
u16 remain = _dspReg[29];
/* adjust desired output samplerate if audio playback is not perfectly in sync with video */
if (remain > 0)
{
int samplerate;
/* check current audio DMA position (from testing, delta keeps limited to +/- one 32-bit block i.e 8 samples) */
if (remain < 5)
{
/* previous frame DMA is not yet finished (real output rate is slightly slower than expected value) */
samplerate = 47950;
}
else
{
/* current frame DMA is already started (real output rate is slightly faster than expected value) */
samplerate = 48050;
}
/* update resampler ratios if output samplerate has changed */
if (samplerate != snd.sample_rate)
{
/* this will adjust the number of samples returned on next frame(s) */
audio_set_rate(samplerate, snd.frame_rate);
}
}
/* clear VSYNC flag */ /* clear VSYNC flag */
videoWait = 0; videoWait = 0;
}
/* XFB update */
static void xfb_update(u32 cnt)
{
/* check if EFB rendering is finished */ /* check if EFB rendering is finished */
if (drawDone) if (drawDone)
{ {
@ -493,8 +526,8 @@ static void gxSetAspectRatio(int *xscale, int *yscale)
/* Horizontal Scaling */ /* Horizontal Scaling */
/* Wii/Gamecube pixel clock = 13.5 Mhz */ /* Wii/Gamecube pixel clock = 13.5 Mhz */
/* "H32" pixel clock = Master Clock / 10 = 5.3693175 Mhz (NTSC) or 5.3203424 (PAL) */ /* "H32" pixel clock = Master Clock / 10 = 5.3693175 Mhz (NTSC) or 5.3203424 Mhz (PAL) */
/* "H40" pixel clock = Master Clock / 8 = 6,711646875 Mhz (NTSC) or 6,650428 Mhz (PAL) */ /* "H40" pixel clock = Master Clock / 8 = 6.711646875 Mhz (NTSC) or 6.650428 Mhz (PAL) */
if (config.overscan & 2) if (config.overscan & 2)
{ {
/* Horizontal borders are emulated */ /* Horizontal borders are emulated */
@ -505,7 +538,7 @@ static void gxSetAspectRatio(int *xscale, int *yscale)
} }
else else
{ {
/* 284 "H32" pixels = 284 * Wii/GC pixel clock / "H40" pixel clock = approx. 714 (NTSC) or 721 (PAL) Wii/GC pixels */ /* 284 "H32" pixels = 284 * Wii/GC pixel clock / "H32" pixel clock = approx. 714 (NTSC) or 721 (PAL) Wii/GC pixels */
*xscale = (system_clock == MCLOCK_NTSC) ? 357 : 361; *xscale = (system_clock == MCLOCK_NTSC) ? 357 : 361;
} }
} }
@ -1424,15 +1457,15 @@ void gxTextureClose(gx_texture **p_texture)
/* Emulation mode -> Menu mode */ /* Emulation mode -> Menu mode */
void gx_video_Stop(void) void gx_video_Stop(void)
{ {
/* disable VSYNC callback */ /* disable VSYNC callbacks */
VIDEO_SetPostRetraceCallback(NULL); VIDEO_SetPostRetraceCallback(NULL);
VIDEO_SetPreRetraceCallback(NULL);
/* wait for next even field */ /* wait for next even field */
/* this prevents screen artefacts when switching between interlaced & non-interlaced modes */ /* this prevents screen artefacts when switching between interlaced & non-interlaced modes */
do VIDEO_WaitVSync(); do VIDEO_WaitVSync();
while (!VIDEO_GetNextField()); while (!VIDEO_GetNextField());
/* adjust TV width */ /* adjust TV width */
vmode->viWidth = config.screen_w; vmode->viWidth = config.screen_w;
vmode->viXOrigin = (VI_MAX_WIDTH_NTSC - vmode->viWidth)/2; vmode->viXOrigin = (VI_MAX_WIDTH_NTSC - vmode->viWidth)/2;
@ -1478,11 +1511,6 @@ void gx_video_Stop(void)
/* Menu mode -> Emulation mode */ /* Menu mode -> Emulation mode */
void gx_video_Start(void) void gx_video_Start(void)
{ {
#ifdef HW_RVL
VIDEO_SetTrapFilter(config.trap);
VIDEO_SetGamma((int)(config.gamma * 10.0));
#endif
/* TV mode */ /* TV mode */
if ((config.tv_mode == 1) || ((config.tv_mode == 2) && vdp_pal)) if ((config.tv_mode == 1) || ((config.tv_mode == 2) && vdp_pal))
{ {
@ -1495,16 +1523,6 @@ void gx_video_Start(void)
gc_pal = 0; gc_pal = 0;
} }
/* When VSYNC is set to AUTO & console TV mode matches emulated video mode, emulation is synchronized with video hardware as well */
if (config.vsync && (gc_pal == vdp_pal))
{
videoSync = audioSync;
}
else
{
videoSync = 0;
}
/* Enable progressive or interlaced video mode */ /* Enable progressive or interlaced video mode */
if (config.render == 2) if (config.render == 2)
{ {
@ -1657,8 +1675,24 @@ void gx_video_Start(void)
do VIDEO_WaitVSync(); do VIDEO_WaitVSync();
while (!VIDEO_GetNextField()); while (!VIDEO_GetNextField());
/* VSYNC callback */ #ifdef HW_RVL
VIDEO_SetPostRetraceCallback(vi_callback); VIDEO_SetTrapFilter(config.trap);
VIDEO_SetGamma((int)(config.gamma * 10.0));
#endif
/* XFB update is done during VBLANK */
VIDEO_SetPreRetraceCallback(xfb_update);
/* Emulation is synchronized with video hardware if VSYNC is set to AUTO & TV mode matches emulated video mode */
if (config.vsync && (gc_pal == vdp_pal))
{
VIDEO_SetPostRetraceCallback(vi_callback);
videoSync = VIDEO_WAIT;
}
else
{
videoSync = 0;
}
/* restart frame sync */ /* restart frame sync */
videoWait = 0; videoWait = 0;
@ -1667,177 +1701,191 @@ void gx_video_Start(void)
} }
/* GX render update */ /* GX render update */
int gx_video_Update(u32 done) int gx_video_Update(int status)
{ {
if (videoWait || done) return SYNC_WAIT; /* make sure current video frame has not already been updated */
if (status & VIDEO_UPDATE)
videoWait = videoSync;
/* check if display has changed during frame */
if (bitmap.viewport.changed & 1)
{ {
/* update texture size */ /* set video wait flag if VSYNC is enabled */
vwidth = bitmap.viewport.w + (2 * bitmap.viewport.x); videoWait = videoSync;
vheight = bitmap.viewport.h + (2 * bitmap.viewport.y);
/* interlaced mode */ /* check if display has changed during frame */
if (config.render && interlaced) if (bitmap.viewport.changed & 1)
{ {
vheight = vheight << 1; /* update texture size */
} vwidth = bitmap.viewport.w + (2 * bitmap.viewport.x);
vheight = bitmap.viewport.h + (2 * bitmap.viewport.y);
/* ntsc filter */ /* interlaced mode */
if (config.ntsc) if (config.render && interlaced)
{
vwidth = (reg[12] & 1) ? MD_NTSC_OUT_WIDTH(vwidth) : SMS_NTSC_OUT_WIDTH(vwidth);
/* texel width must remain multiple of 4 */
vwidth = (vwidth >> 2) << 2;
}
/* initialize texture object */
GXTexObj texobj;
GX_InitTexObj(&texobj, bitmap.data, vwidth, vheight, GX_TF_RGB565, GX_CLAMP, GX_CLAMP, GX_FALSE);
/* configure texture filtering */
if (!config.bilinear)
{
GX_InitTexObjLOD(&texobj,GX_NEAR,GX_NEAR_MIP_NEAR,0.0,10.0,0.0,GX_FALSE,GX_FALSE,GX_ANISO_1);
}
/* load texture object */
GX_LoadTexObj(&texobj, GX_TEXMAP0);
/* update rendering mode */
if (config.render)
{
rmode = tvmodes[gc_pal*3 + 2];
}
else
{
rmode = tvmodes[gc_pal*3 + interlaced];
}
/* update aspect ratio */
gxResetScaler(vwidth);
/* update GX rendering mode */
gxResetMode(rmode, config.vfilter);
/* update VI mode */
VIDEO_Configure(rmode);
VIDEO_Flush();
}
/* texture is now directly mapped by the line renderer */
/* force texture cache update */
DCFlushRange(bitmap.data, vwidth*vheight*2);
GX_InvalidateTexAll();
/* disable EFB copy until rendering is done */
drawDone = 0;
/* render textured quad */
GX_CallDispList(d_list, 32);
/* on-screen display */
if (osd)
{
/* reset GX rendering */
gxResetRendering(1);
/* lightgun # 1 screen mark */
if (crosshair[0])
{
if (input.system[0] == SYSTEM_LIGHTPHASER)
{ {
gxDrawCrosshair(crosshair[0], input.analog[0][0],input.analog[0][1]); vheight = vheight << 1;
}
/* ntsc filter */
if (config.ntsc)
{
vwidth = (reg[12] & 1) ? MD_NTSC_OUT_WIDTH(vwidth) : SMS_NTSC_OUT_WIDTH(vwidth);
/* texel width must remain multiple of 4 */
vwidth = (vwidth >> 2) << 2;
}
/* initialize texture object */
GXTexObj texobj;
GX_InitTexObj(&texobj, bitmap.data, vwidth, vheight, GX_TF_RGB565, GX_CLAMP, GX_CLAMP, GX_FALSE);
/* configure texture filtering */
if (!config.bilinear)
{
GX_InitTexObjLOD(&texobj,GX_NEAR,GX_NEAR_MIP_NEAR,0.0,10.0,0.0,GX_FALSE,GX_FALSE,GX_ANISO_1);
}
/* load texture object */
GX_LoadTexObj(&texobj, GX_TEXMAP0);
/* update rendering mode */
if (config.render)
{
rmode = tvmodes[gc_pal*3 + 2];
} }
else else
{ {
gxDrawCrosshair(crosshair[0], input.analog[4][0],input.analog[4][1]); rmode = tvmodes[gc_pal*3 + interlaced];
}
}
/* lightgun #2 screen mark */
if (crosshair[1])
{
if (input.system[1] == SYSTEM_LIGHTPHASER)
{
gxDrawCrosshair(crosshair[1], input.analog[4][0],input.analog[4][1]);
}
else
{
gxDrawCrosshair(crosshair[1], input.analog[5][0],input.analog[5][1]);
}
}
/* CD LEDS */
if (cd_leds[1][1])
{
/* CD LEDS status */
u8 mode = scd.regs[0x06 >> 1].byte.h;
gxDrawCdLeds(cd_leds[1][(mode >> 1) & 1], cd_leds[0][mode & 1]);
}
/* FPS counter */
if (config.fps)
{
u32 delta = diff_usec(starttime, gettime());
frameCount++;
if (delta > 1000000)
{
sprintf(msg,"%3.2f FPS", (float)frameCount * 1000000.0 / (float)delta);
frameCount = 0;
starttime = gettime();
} }
/* disable EFB alpha blending for text background */ /* update aspect ratio */
GX_SetBlendMode(GX_BM_NONE,GX_BL_SRCALPHA,GX_BL_INVSRCALPHA,GX_LO_CLEAR); gxResetScaler(vwidth);
GX_Flush();
gxDrawOnScreenText(msg); /* update GX rendering mode */
gxResetMode(rmode, config.vfilter);
/* update VI mode */
VIDEO_Configure(rmode);
VIDEO_Flush();
} }
/* restore texture object */ /* texture is now directly mapped by the line renderer */
GXTexObj texobj;
GX_InitTexObj(&texobj, bitmap.data, vwidth, vheight, GX_TF_RGB565, GX_CLAMP, GX_CLAMP, GX_FALSE); /* force texture cache update */
if (!config.bilinear) DCFlushRange(bitmap.data, vwidth*vheight*2);
{
GX_InitTexObjLOD(&texobj,GX_NEAR,GX_NEAR_MIP_NEAR,0.0,10.0,0.0,GX_FALSE,GX_FALSE,GX_ANISO_1);
}
GX_LoadTexObj(&texobj, GX_TEXMAP0);
GX_InvalidateTexAll(); GX_InvalidateTexAll();
/* restore GX rendering */ /* disable EFB copy until rendering is done */
gxResetRendering(0); drawDone = 0;
}
/* GX draw interrupt will be triggered when EFB rendering is finished */ /* render textured quad */
GX_SetDrawDone(); GX_CallDispList(d_list, 32);
if (bitmap.viewport.changed & 1) /* on-screen display */
{ if (osd)
/* clear update flags */
bitmap.viewport.changed &= ~1;
/* field synchronization */
VIDEO_WaitVSync();
if (rmode->viTVMode & VI_NON_INTERLACE)
{ {
VIDEO_WaitVSync(); /* reset GX rendering */
} gxResetRendering(1);
else while (VIDEO_GetNextField() != odd_frame)
{ /* lightgun # 1 screen mark */
VIDEO_WaitVSync(); if (crosshair[0])
{
if (input.system[0] == SYSTEM_LIGHTPHASER)
{
gxDrawCrosshair(crosshair[0], input.analog[0][0],input.analog[0][1]);
}
else
{
gxDrawCrosshair(crosshair[0], input.analog[4][0],input.analog[4][1]);
}
}
/* lightgun #2 screen mark */
if (crosshair[1])
{
if (input.system[1] == SYSTEM_LIGHTPHASER)
{
gxDrawCrosshair(crosshair[1], input.analog[4][0],input.analog[4][1]);
}
else
{
gxDrawCrosshair(crosshair[1], input.analog[5][0],input.analog[5][1]);
}
}
/* CD LEDS */
if (cd_leds[1][1])
{
/* CD LEDS status */
u8 mode = scd.regs[0x06 >> 1].byte.h;
gxDrawCdLeds(cd_leds[1][(mode >> 1) & 1], cd_leds[0][mode & 1]);
}
/* FPS counter */
if (config.fps)
{
u32 delta = diff_usec(starttime, gettime());
frameCount++;
if (delta > 1000000)
{
sprintf(msg,"%3.2f FPS", (float)frameCount * 1000000.0 / (float)delta);
frameCount = 0;
starttime = gettime();
}
/* disable EFB alpha blending for text background */
GX_SetBlendMode(GX_BM_NONE,GX_BL_SRCALPHA,GX_BL_INVSRCALPHA,GX_LO_CLEAR);
GX_Flush();
/* display on-screen message */
gxDrawOnScreenText(msg);
}
/* restore texture object */
GXTexObj texobj;
GX_InitTexObj(&texobj, bitmap.data, vwidth, vheight, GX_TF_RGB565, GX_CLAMP, GX_CLAMP, GX_FALSE);
if (!config.bilinear)
{
GX_InitTexObjLOD(&texobj,GX_NEAR,GX_NEAR_MIP_NEAR,0.0,10.0,0.0,GX_FALSE,GX_FALSE,GX_ANISO_1);
}
GX_LoadTexObj(&texobj, GX_TEXMAP0);
GX_InvalidateTexAll();
/* restore GX rendering */
gxResetRendering(0);
} }
/* Audio DMA need to be resynchronized with VSYNC */ /* Do not wait for EFB rendering, GX draw interrupt will be triggered when it is finished */
audioStarted = 0; GX_SetDrawDone();
/* check interlaced mode change */
if (bitmap.viewport.changed & 4)
{
/* "original" mode */
if (!config.render && config.vsync && (gc_pal == vdp_pal))
{
/* framerate has changed, reinitialize audio timings */
audio_init(snd.sample_rate, get_framerate());
}
/* clear flag */
bitmap.viewport.changed &= ~4;
}
/* Check if video mode has changed */
if (bitmap.viewport.changed & 1)
{
/* clear viewport update flags */
bitmap.viewport.changed &= ~1;
/* field synchronization */
do VIDEO_WaitVSync();
while (VIDEO_GetNextField() != odd_frame);
/* resynchronize audio playback with video */
AUDIO_StopDMA();
AUDIO_StartDMA();
}
} }
return SYNC_VIDEO; /* wait until current video frame starts before emulating next frame */
return ((status & ~(VIDEO_WAIT|VIDEO_UPDATE)) | videoWait);
} }
/* Initialize VIDEO subsystem */ /* Initialize VIDEO subsystem */

View File

@ -99,6 +99,6 @@ extern void gx_video_Init(void);
extern void gx_video_Shutdown(void); extern void gx_video_Shutdown(void);
extern void gx_video_Start(void); extern void gx_video_Start(void);
extern void gx_video_Stop(void); extern void gx_video_Stop(void);
extern int gx_video_Update(u32 done); extern int gx_video_Update(int status);
#endif #endif

View File

@ -57,9 +57,6 @@ extern bool sdio_Deinitialize();
extern void USBStorage_Deinitialize(); extern void USBStorage_Deinitialize();
#endif #endif
/* output samplerate, adjusted to take resampler precision in account */
#define SAMPLERATE_48KHZ 47992
u32 Shutdown = 0; u32 Shutdown = 0;
u32 ConfigRequested = 1; u32 ConfigRequested = 1;
@ -153,28 +150,14 @@ static void run_emulation(void)
system_frame_scd(0); system_frame_scd(0);
/* audio/video sync */ /* audio/video sync */
sync = SYNC_WAIT; sync = VIDEO_WAIT | VIDEO_UPDATE | AUDIO_WAIT | AUDIO_UPDATE;
while (sync != (SYNC_VIDEO | SYNC_AUDIO)) while (sync)
{ {
/* update video */
sync |= gx_video_Update(sync & SYNC_VIDEO);
/* update audio */ /* update audio */
sync |= gx_audio_Update(); sync &= gx_audio_Update(sync);
}
/* check interlaced mode change */ /* update video */
if (bitmap.viewport.changed & 4) sync &= gx_video_Update(sync);
{
/* VSYNC "original" mode */
if (!config.render && config.vsync && (gc_pal == vdp_pal))
{
/* framerate has changed, reinitialize audio timings */
audio_init(SAMPLERATE_48KHZ, get_framerate());
}
/* clear flag */
bitmap.viewport.changed &= ~4;
} }
} }
} }
@ -187,28 +170,14 @@ static void run_emulation(void)
system_frame_gen(0); system_frame_gen(0);
/* audio/video sync */ /* audio/video sync */
sync = SYNC_WAIT; sync = VIDEO_WAIT | VIDEO_UPDATE | AUDIO_WAIT | AUDIO_UPDATE;
while (sync != (SYNC_VIDEO | SYNC_AUDIO)) while (sync)
{ {
/* update video */
sync |= gx_video_Update(sync & SYNC_VIDEO);
/* update audio */ /* update audio */
sync |= gx_audio_Update(); sync &= gx_audio_Update(sync);
}
/* check interlaced mode change */ /* update video */
if (bitmap.viewport.changed & 4) sync &= gx_video_Update(sync);
{
/* VSYNC "original" mode */
if (!config.render && config.vsync && (gc_pal == vdp_pal))
{
/* framerate has changed, reinitialize audio timings */
audio_init(SAMPLERATE_48KHZ, get_framerate());
}
/* clear flag */
bitmap.viewport.changed &= ~4;
} }
} }
} }
@ -221,28 +190,14 @@ static void run_emulation(void)
system_frame_sms(0); system_frame_sms(0);
/* audio/video sync */ /* audio/video sync */
sync = SYNC_WAIT; sync = VIDEO_WAIT | VIDEO_UPDATE | AUDIO_WAIT | AUDIO_UPDATE;
while (sync != (SYNC_VIDEO | SYNC_AUDIO)) while (sync)
{ {
/* update video */
sync |= gx_video_Update(sync & SYNC_VIDEO);
/* update audio */ /* update audio */
sync |= gx_audio_Update(); sync &= gx_audio_Update(sync);
}
/* check interlaced mode change (PBC mode only) */ /* update video */
if (bitmap.viewport.changed & 4) sync &= gx_video_Update(sync);
{
/* "original" mode */
if (!config.render && config.vsync && (gc_pal == vdp_pal))
{
/* framerate has changed, reinitialize audio timings */
audio_init(SAMPLERATE_48KHZ, get_framerate());
}
/* clear flag */
bitmap.viewport.changed &= ~4;
} }
} }
} }
@ -316,7 +271,7 @@ void reloadrom(void)
{ {
/* Initialize audio emulation */ /* Initialize audio emulation */
interlaced = 0; interlaced = 0;
audio_init(SAMPLERATE_48KHZ, get_framerate()); audio_init(48000, get_framerate());
/* System Power-On */ /* System Power-On */
system_init(); system_init();

View File

@ -71,9 +71,10 @@
#define VERSION "Genesis Plus GX 1.7.5" #define VERSION "Genesis Plus GX 1.7.5"
#define SYNC_WAIT 0 #define VIDEO_WAIT 0x01
#define SYNC_VIDEO 1 #define AUDIO_WAIT 0x02
#define SYNC_AUDIO 2 #define VIDEO_UPDATE 0x04
#define AUDIO_UPDATE 0x08
/* globals */ /* globals */
extern void legal(void); extern void legal(void);