mirror of
https://github.com/ekeeke/Genesis-Plus-GX.git
synced 2024-12-26 03:01:50 +01:00
[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:
parent
37dd44df6c
commit
bf184b9440
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 |
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -94,15 +94,10 @@ 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++)
|
||||
{
|
||||
SN76489.PreAmp[i][0] = 100;
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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]);
|
||||
}
|
||||
|
||||
|
100
core/system.c
100
core/system.c
@ -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;
|
||||
|
||||
/* PCM core */
|
||||
pcm_init(mclk, samplerate);
|
||||
|
||||
/* CDD core */
|
||||
cdd_init(samplerate);
|
||||
}
|
||||
|
||||
/* Initialize PCM core */
|
||||
pcm_init(snd.blips[1][0], snd.blips[1][1]);
|
||||
|
||||
/* Initialize CDD core */
|
||||
cdd_init(snd.blips[2][0], snd.blips[2][1]);
|
||||
}
|
||||
|
||||
/* 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)
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
/* Current available soundbuffer */
|
||||
s16 *sb = (s16 *)(soundbuffer[bufferIndex]);
|
||||
|
||||
/* Make sure current audio frame has not already been updated */
|
||||
if (status & AUDIO_UPDATE)
|
||||
{
|
||||
return SYNC_WAIT;
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* Current available soundbuffer */
|
||||
s16 *sb = (s16 *)(soundbuffer[mixbuffer]);
|
||||
/* 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);
|
||||
|
||||
/* 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;
|
||||
/* Next soundbuffer */
|
||||
bufferIndex = (bufferIndex + 1) % SOUND_BUFFER_NUM;
|
||||
|
||||
/* Set audio wait flag */
|
||||
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 */
|
||||
AUDIO_StopDMA();
|
||||
AUDIO_StartDMA();
|
||||
audioStarted = 1;
|
||||
/* Current audio frame is ready for upcoming DMA */
|
||||
status &= ~AUDIO_WAIT;
|
||||
}
|
||||
|
||||
return SYNC_AUDIO;
|
||||
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;
|
||||
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)
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
124
gx/gx_video.c
124
gx/gx_video.c
@ -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 */
|
||||
#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,10 +1701,12 @@ 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;
|
||||
|
||||
/* make sure current video frame has not already been updated */
|
||||
if (status & VIDEO_UPDATE)
|
||||
{
|
||||
/* set video wait flag if VSYNC is enabled */
|
||||
videoWait = videoSync;
|
||||
|
||||
/* check if display has changed during frame */
|
||||
@ -1797,6 +1833,7 @@ int gx_video_Update(u32 done)
|
||||
GX_SetBlendMode(GX_BM_NONE,GX_BL_SRCALPHA,GX_BL_INVSRCALPHA,GX_LO_CLEAR);
|
||||
GX_Flush();
|
||||
|
||||
/* display on-screen message */
|
||||
gxDrawOnScreenText(msg);
|
||||
}
|
||||
|
||||
@ -1814,30 +1851,41 @@ int gx_video_Update(u32 done)
|
||||
gxResetRendering(0);
|
||||
}
|
||||
|
||||
/* GX draw interrupt will be triggered when EFB rendering is finished */
|
||||
/* 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 update flags */
|
||||
/* clear viewport update flags */
|
||||
bitmap.viewport.changed &= ~1;
|
||||
|
||||
/* field synchronization */
|
||||
VIDEO_WaitVSync();
|
||||
if (rmode->viTVMode & VI_NON_INTERLACE)
|
||||
{
|
||||
VIDEO_WaitVSync();
|
||||
do VIDEO_WaitVSync();
|
||||
while (VIDEO_GetNextField() != odd_frame);
|
||||
|
||||
/* resynchronize audio playback with video */
|
||||
AUDIO_StopDMA();
|
||||
AUDIO_StartDMA();
|
||||
}
|
||||
else while (VIDEO_GetNextField() != odd_frame)
|
||||
{
|
||||
VIDEO_WaitVSync();
|
||||
}
|
||||
|
||||
/* Audio DMA need to be resynchronized with VSYNC */
|
||||
audioStarted = 0;
|
||||
}
|
||||
|
||||
return SYNC_VIDEO;
|
||||
/* wait until current video frame starts before emulating next frame */
|
||||
return ((status & ~(VIDEO_WAIT|VIDEO_UPDATE)) | videoWait);
|
||||
}
|
||||
|
||||
/* Initialize VIDEO subsystem */
|
||||
|
@ -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
|
||||
|
77
gx/main.c
77
gx/main.c
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user