[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
* 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
* provided that the following conditions are met:
@ -150,9 +150,6 @@ static const char extensions[SUPPORTED_EXT][16] =
" - %d.wav"
};
static blip_t* blip[2];
#ifdef USE_LIBTREMOR
#ifdef DISABLE_MANY_OGG_OPEN_FILES
static void ogg_free(int i)
@ -172,14 +169,12 @@ static void ogg_free(int i)
#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 */
/* Audio stream is resampled to desired rate using Blip Buffer */
blip[0] = left;
blip[1] = right;
blip_set_rates(left, 44100, snd.sample_rate);
blip_set_rates(right, 44100, snd.sample_rate);
blip_set_rates(snd.blips[2][0], 44100, samplerate);
blip_set_rates(snd.blips[2][1], 44100, samplerate);
}
void cdd_reset(void)
@ -953,7 +948,7 @@ void cdd_read_audio(unsigned int samples)
int16 r = cdd.audio[1];
/* 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 ? */
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;
ptr++;
l += delta;
blip_add_delta_fast(blip[0], i, delta);
blip_add_delta_fast(snd.blips[2][0], i, delta);
/* right channel */
delta = ((ptr[0] * mul) / 1024) - r;
ptr++;
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) */
if (curVol < endVol)
@ -1048,7 +1043,7 @@ void cdd_read_audio(unsigned int samples)
ptr += 2;
#endif
l += delta;
blip_add_delta_fast(blip[0], i, delta);
blip_add_delta_fast(snd.blips[2][0], i, delta);
/* right channel */
#ifdef LSB_FIRST
@ -1059,7 +1054,7 @@ void cdd_read_audio(unsigned int samples)
ptr += 2;
#endif
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) */
if (curVol < endVol)
@ -1090,8 +1085,8 @@ void cdd_read_audio(unsigned int samples)
else
{
/* no audio output */
if (l) blip_add_delta_fast(blip[0], 0, -l);
if (r) blip_add_delta_fast(blip[1], 0, -r);
if (l) blip_add_delta_fast(snd.blips[2][0], 0, -l);
if (r) blip_add_delta_fast(snd.blips[2][1], 0, -r);
/* save audio output for next frame */
cdd.audio[0] = 0;
@ -1099,8 +1094,8 @@ void cdd_read_audio(unsigned int samples)
}
/* end of Blip Buffer timeframe */
blip_end_frame(blip[0], samples);
blip_end_frame(blip[1], samples);
blip_end_frame(snd.blips[2][0], samples);
blip_end_frame(snd.blips[2][1], samples);
}

View File

@ -2,7 +2,7 @@
* Genesis Plus
* 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
* provided that the following conditions are met:
@ -98,7 +98,7 @@ typedef struct
} cdd_t;
/* Function prototypes */
extern void cdd_init(blip_t* left, blip_t* right);
extern void cdd_init(int samplerate);
extern void cdd_reset(void);
extern int cdd_context_save(uint8 *state);
extern int cdd_context_load(uint8 *state);

View File

@ -2,7 +2,7 @@
* Genesis Plus
* 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
* provided that the following conditions are met:
@ -41,19 +41,12 @@
#define pcm scd.pcm_hw
static blip_t* blip[2];
void pcm_init(blip_t* left, blip_t* right)
void pcm_init(double clock, int samplerate)
{
/* number of SCD master clocks run per second */
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 */
/* PCM chip is running at original rate and is synchronized with SUB-CPU */
/* Chip output is resampled to desired rate using Blip Buffer. */
blip[0] = left;
blip[1] = right;
blip_set_rates(left, mclk / PCM_SCYCLES_RATIO, snd.sample_rate);
blip_set_rates(right, mclk / PCM_SCYCLES_RATIO, snd.sample_rate);
blip_set_rates(snd.blips[1][0], clock / PCM_SCYCLES_RATIO, samplerate);
blip_set_rates(snd.blips[1][1], clock / PCM_SCYCLES_RATIO, samplerate);
}
void pcm_reset(void)
@ -78,8 +71,8 @@ void pcm_reset(void)
pcm.cycles = 0;
/* clear blip buffers */
blip_clear(blip[0]);
blip_clear(blip[1]);
blip_clear(snd.blips[1][0]);
blip_clear(snd.blips[1][1]);
}
int pcm_context_save(uint8 *state)
@ -190,14 +183,14 @@ void pcm_run(unsigned int length)
/* check if PCM left output changed */
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;
}
/* check if PCM right output changed */
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;
}
}
@ -207,21 +200,21 @@ void pcm_run(unsigned int length)
/* check if PCM left output changed */
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;
}
/* check if PCM right output changed */
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;
}
}
/* end of blip buffer frame */
blip_end_frame(blip[0], length);
blip_end_frame(blip[1], length);
blip_end_frame(snd.blips[1][0], length);
blip_end_frame(snd.blips[1][1], length);
/* update PCM master clock counter */
pcm.cycles += length * PCM_SCYCLES_RATIO;
@ -230,7 +223,7 @@ void pcm_run(unsigned int length)
void pcm_update(unsigned int samples)
{
/* 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 */
if (clocks > 0)

View File

@ -2,7 +2,7 @@
* Genesis Plus
* 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
* provided that the following conditions are met:
@ -65,7 +65,7 @@ typedef struct
} pcm_t;
/* 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 int pcm_context_save(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 blip_t* blip[2];
void SN76489_Init(blip_t* left, blip_t* right, int type)
void SN76489_Init(int type)
{
int i;
blip[0] = left;
blip[1] = right;
for (i=0; i<4; i++)
{
@ -177,7 +172,7 @@ INLINE void UpdateToneAmplitude(int i, int time)
if (delta != 0)
{
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 */
@ -185,7 +180,7 @@ INLINE void UpdateToneAmplitude(int i, int time)
if (delta != 0)
{
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)
{
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 */
@ -207,7 +202,7 @@ INLINE void UpdateNoiseAmplitude(int time)
if (delta != 0)
{
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
/* 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_Config(unsigned int clocks, int preAmp, int boostNoise, int stereo);
extern void SN76489_Write(unsigned int clocks, unsigned int data);

View File

@ -102,6 +102,7 @@ void sound_init( void )
}
/* Initialize PSG chip */
SN76489_Init((system_hw == SYSTEM_SG) ? SN_DISCRETE : SN_INTEGRATED);
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]);
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);
}
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]);
}

View File

@ -60,6 +60,50 @@ static int16 llp,rrp;
/******************************************************************************************/
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. */
/* 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;
/* 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 */
/* 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 */
@ -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][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 */
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;
}
/* number of SCD master clocks run per second */
mclk = framerate ? (SCYCLES_PER_LINE * (vdp_pal ? 313 : 262) * framerate) : SCD_CLOCK;
/* Initialize PCM core */
pcm_init(snd.blips[1][0], snd.blips[1][1]);
/* PCM core */
pcm_init(mclk, samplerate);
/* Initialize CDD core */
cdd_init(snd.blips[2][0], snd.blips[2][1]);
/* CDD core */
cdd_init(samplerate);
}
/* Set audio enable flag */
snd.enabled = 1;
/* Reset audio */
audio_reset();
return (0);
/* Reinitialize internal rates */
snd.sample_rate = samplerate;
snd.frame_rate = framerate;
}
void audio_reset(void)

View File

@ -106,6 +106,7 @@ extern uint32 system_clock;
/* Function prototypes */
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_shutdown(void);
extern int audio_update(int16 *buffer);

View File

@ -45,14 +45,12 @@
/* Number of sound buffers */
#define SOUND_BUFFER_NUM 3
/* audio DMA status */
u32 audioStarted;
/* DMA soundbuffers (required to be 32-bytes aligned) */
static u8 soundbuffer[SOUND_BUFFER_NUM][SOUND_BUFFER_LEN] ATTRIBUTE_ALIGN(32);
/* Current work soundbuffer */
static u8 mixbuffer;
static int bufferIndex;
static int bufferSize;
/* Background music */
static u8 *Bg_music_ogg = NULL;
@ -96,9 +94,6 @@ void gx_audio_Init(void)
}
fclose(f);
}
/* emulation is synchronized with audio hardware by default */
audioSync = 1;
}
/* 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
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 */
s16 *sb = (s16 *)(soundbuffer[mixbuffer]);
s16 *sb = (s16 *)(soundbuffer[bufferIndex]);
/* Retrieve audio samples (size must be multiple of 32 bytes) */
int size = audio_update(sb) * 4;
/* 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)
/* Make sure current audio frame has not already been updated */
if (status & AUDIO_UPDATE)
{
/* restart audio DMA */
AUDIO_StopDMA();
AUDIO_StartDMA();
audioStarted = 1;
/* Retrieve audio samples (size must be multiple of 32 bytes) */
bufferSize = audio_update(sb) * 4;
DCFlushRange((void *)sb, bufferSize);
/* 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
This function restart the audio engine
This function restarts the audio engine
This is called when coming back from Main Menu
***/
void gx_audio_Start(void)
@ -181,11 +190,14 @@ void gx_audio_Start(void)
/* DMA Interrupt callback */
AUDIO_RegisterDMACallback(ai_callback);
/* emulation is synchronized with audio hardware by default */
audioSync = AUDIO_WAIT;
/* reset emulation audio processing */
memset(soundbuffer, 0, sizeof(soundbuffer));
audioStarted = 0;
mixbuffer = 0;
audioWait = 0;
audioWait = 0;
bufferSize = 0;
bufferIndex = 0;
}
/***
@ -193,7 +205,6 @@ void gx_audio_Start(void)
This function stops current Audio DMA process
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)
{

View File

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

View File

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

View File

@ -357,9 +357,42 @@ static u8 d_list[32] ATTRIBUTE_ALIGN(32) =
/* VSYNC callback */
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 */
videoWait = 0;
}
/* XFB update */
static void xfb_update(u32 cnt)
{
/* check if EFB rendering is finished */
if (drawDone)
{
@ -493,8 +526,8 @@ static void gxSetAspectRatio(int *xscale, int *yscale)
/* Horizontal Scaling */
/* Wii/Gamecube pixel clock = 13.5 Mhz */
/* "H32" pixel clock = Master Clock / 10 = 5.3693175 Mhz (NTSC) or 5.3203424 (PAL) */
/* "H40" pixel clock = Master Clock / 8 = 6,711646875 Mhz (NTSC) or 6,650428 Mhz (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) */
if (config.overscan & 2)
{
/* Horizontal borders are emulated */
@ -505,7 +538,7 @@ static void gxSetAspectRatio(int *xscale, int *yscale)
}
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;
}
}
@ -1424,15 +1457,15 @@ void gxTextureClose(gx_texture **p_texture)
/* Emulation mode -> Menu mode */
void gx_video_Stop(void)
{
/* disable VSYNC callback */
/* disable VSYNC callbacks */
VIDEO_SetPostRetraceCallback(NULL);
VIDEO_SetPreRetraceCallback(NULL);
/* wait for next even field */
/* this prevents screen artefacts when switching between interlaced & non-interlaced modes */
do VIDEO_WaitVSync();
while (!VIDEO_GetNextField());
/* adjust TV width */
vmode->viWidth = config.screen_w;
vmode->viXOrigin = (VI_MAX_WIDTH_NTSC - vmode->viWidth)/2;
@ -1478,11 +1511,6 @@ void gx_video_Stop(void)
/* Menu mode -> Emulation mode */
void gx_video_Start(void)
{
#ifdef HW_RVL
VIDEO_SetTrapFilter(config.trap);
VIDEO_SetGamma((int)(config.gamma * 10.0));
#endif
/* TV mode */
if ((config.tv_mode == 1) || ((config.tv_mode == 2) && vdp_pal))
{
@ -1495,16 +1523,6 @@ void gx_video_Start(void)
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 */
if (config.render == 2)
{
@ -1657,8 +1675,24 @@ void gx_video_Start(void)
do VIDEO_WaitVSync();
while (!VIDEO_GetNextField());
/* VSYNC callback */
VIDEO_SetPostRetraceCallback(vi_callback);
#ifdef HW_RVL
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 */
videoWait = 0;
@ -1667,177 +1701,191 @@ void gx_video_Start(void)
}
/* GX render update */
int gx_video_Update(u32 done)
int gx_video_Update(int status)
{
if (videoWait || done) return SYNC_WAIT;
videoWait = videoSync;
/* check if display has changed during frame */
if (bitmap.viewport.changed & 1)
/* make sure current video frame has not already been updated */
if (status & VIDEO_UPDATE)
{
/* update texture size */
vwidth = bitmap.viewport.w + (2 * bitmap.viewport.x);
vheight = bitmap.viewport.h + (2 * bitmap.viewport.y);
/* set video wait flag if VSYNC is enabled */
videoWait = videoSync;
/* interlaced mode */
if (config.render && interlaced)
/* check if display has changed during frame */
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 */
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
{
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)
/* interlaced mode */
if (config.render && interlaced)
{
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
{
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();
rmode = tvmodes[gc_pal*3 + interlaced];
}
/* disable EFB alpha blending for text background */
GX_SetBlendMode(GX_BM_NONE,GX_BL_SRCALPHA,GX_BL_INVSRCALPHA,GX_LO_CLEAR);
GX_Flush();
/* update aspect ratio */
gxResetScaler(vwidth);
gxDrawOnScreenText(msg);
/* update GX rendering mode */
gxResetMode(rmode, config.vfilter);
/* update VI mode */
VIDEO_Configure(rmode);
VIDEO_Flush();
}
/* 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);
/* texture is now directly mapped by the line renderer */
/* force texture cache update */
DCFlushRange(bitmap.data, vwidth*vheight*2);
GX_InvalidateTexAll();
/* restore GX rendering */
gxResetRendering(0);
}
/* disable EFB copy until rendering is done */
drawDone = 0;
/* GX draw interrupt will be triggered when EFB rendering is finished */
GX_SetDrawDone();
/* render textured quad */
GX_CallDispList(d_list, 32);
if (bitmap.viewport.changed & 1)
{
/* clear update flags */
bitmap.viewport.changed &= ~1;
/* field synchronization */
VIDEO_WaitVSync();
if (rmode->viTVMode & VI_NON_INTERLACE)
/* on-screen display */
if (osd)
{
VIDEO_WaitVSync();
}
else while (VIDEO_GetNextField() != odd_frame)
{
VIDEO_WaitVSync();
/* 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]);
}
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 */
audioStarted = 0;
/* Do not wait for EFB rendering, GX draw interrupt will be triggered when it is finished */
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 */

View File

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

View File

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

View File

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