mirror of
https://github.com/ekeeke/Genesis-Plus-GX.git
synced 2024-12-26 11:11:48 +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
|
* 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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)
|
||||||
|
@ -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);
|
||||||
|
@ -94,15 +94,10 @@ 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++)
|
||||||
{
|
{
|
||||||
SN76489.PreAmp[i][0] = 100;
|
SN76489.PreAmp[i][0] = 100;
|
||||||
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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);
|
||||||
|
@ -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)
|
||||||
{
|
{
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
388
gx/gx_video.c
388
gx/gx_video.c
@ -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 */
|
||||||
|
@ -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
|
||||||
|
77
gx/main.c
77
gx/main.c
@ -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();
|
||||||
|
7
gx/osd.h
7
gx/osd.h
@ -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);
|
||||||
|
Loading…
Reference in New Issue
Block a user