PicoDrive: CD Audio support. GPGX and PicoDrive both share the same MCD emulation core, which has significant accuracy difficulties with CD audio...

This commit is contained in:
nattthebear 2017-07-03 08:16:34 -04:00
parent 24cd317a1c
commit bc55597e90
7 changed files with 123 additions and 194 deletions

View File

@ -147,7 +147,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.PicoDrive
if (audio)
{
byte[] data = new byte[2352];
if (lba < _cd.Session1.LeadoutLBA)
if (lba < _cd.Session1.LeadoutLBA && lba >= _cd.Session1.Tracks[2].LBA)
{
_cdReader.ReadLBA_2352(lba, data, 0);
}

View File

@ -122,10 +122,6 @@ void emu_32x_startup(void)
{
}
int mp3_get_bitrate(void *f, int size) { return 0; }
void mp3_start_play(void *f, int pos) {}
void mp3_update(int *buffer, int length, int stereo) {}
static const uint8_t *TryLoadBios(const char *name)
{
FILE *f = fopen(name, "rb");

View File

@ -125,6 +125,12 @@ void cdd_reset(void)
/* reset logical block address */
cdd.lba = 0;
// reset audio subblock position
cdd.sampleOffset = 0;
// reset audio read position
cdd.sampleLba = 0;
/* reset status */
cdd.status = NO_DISC;
@ -198,164 +204,108 @@ void cdd_read_data(uint8 *dst)
}
}
#if 0
void cdd_read_audio(unsigned int samples)
void cdd_read_audio(short *buffer, unsigned int samples)
{
/* previous audio outputs */
int16 l = cdd.audio[0];
int16 r = cdd.audio[1];
printf("cdd_read_audio %u\n", samples);
short *outptr = buffer;
/* audio track playing ? */
if (!Pico_mcd->s68k_regs[0x36 + 0])
{
int i, mul;
/* get number of internal clocks (samples) needed */
samples = blip_clocks_needed(blip[0], samples);
/* current CD-DA fader volume */
int curVol = cdd.volume;
/* audio track playing ? */
if (!Pico_mcd->s68k_regs[0x36+0] && cdd.toc.tracks[cdd.index].fd)
{
int i, mul, delta;
/* CD-DA fader volume setup (0-1024) */
int endVol = Pico_mcd->s68k_regs[0x34] << 4 | Pico_mcd->s68k_regs[0x35] >> 4;
/* current CD-DA fader volume */
int curVol = cdd.volume;
/* CD-DA fader volume setup (0-1024) */
int endVol = Pico_mcd->regs[0x34>>1].w >> 4;
/* read samples from current block */
#ifdef USE_LIBTREMOR
if (cdd.toc.tracks[cdd.index].vf.datasource)
{
int len, done = 0;
int16 *ptr = (int16 *) (cdc.ram);
samples = samples * 4;
while (done < samples)
{
len = ov_read(&cdd.toc.tracks[cdd.index].vf, (char *)(cdc.ram + done), samples - done, 0);
if (len <= 0)
{
done = samples;
break;
}
done += len;
}
samples = done / 4;
/* process 16-bit (host-endian) stereo samples */
for (i=0; i<samples; i++)
{
/* CD-DA fader multiplier (cf. LC7883 datasheet) */
/* (MIN) 0,1,2,3,4,8,12,16,20...,1020,1024 (MAX) */
mul = (curVol & 0x7fc) ? (curVol & 0x7fc) : (curVol & 0x03);
/* left channel */
delta = ((ptr[0] * mul) / 1024) - l;
ptr++;
l += delta;
blip_add_delta_fast(blip[0], i, delta);
/* right channel */
delta = ((ptr[0] * mul) / 1024) - r;
ptr++;
r += delta;
blip_add_delta_fast(blip[1], i, delta);
/* update CD-DA fader volume (one step/sample) */
if (curVol < endVol)
{
/* fade-in */
curVol++;
}
else if (curVol > endVol)
{
/* fade-out */
curVol--;
}
else if (!curVol)
{
/* audio will remain muted until next setup */
break;
}
}
}
else
#endif
{
/* read samples from current block */
{
uint8_t audio_scratch[4096];
#ifdef LSB_FIRST
int16 *ptr = (int16 *) (cdc.ram);
int16 *ptr = (int16 *)audio_scratch;
#else
uint8 *ptr = cdc.ram;
uint8 *ptr = audio_scratch;
#endif
fread(cdc.ram, 1, samples * 4, cdd.toc.tracks[cdd.index].fd);
{
char scratch[2352];
int nsampreq = samples;
unsigned char *dest = audio_scratch;
while (nsampreq > 0)
{
int tocopy = 588 - cdd.sampleOffset;
if (tocopy > nsampreq)
tocopy = nsampreq;
CDReadSector(cdd.sampleLba, scratch, 1);
memcpy(dest, scratch + cdd.sampleOffset * 4, tocopy * 4);
nsampreq -= tocopy;
dest += tocopy * 4;
cdd.sampleOffset += tocopy;
if (cdd.sampleOffset == 588)
{
cdd.sampleOffset = 0;
cdd.sampleLba++;
}
}
/* process 16-bit (little-endian) stereo samples */
for (i=0; i<samples; i++)
{
/* CD-DA fader multiplier (cf. LC7883 datasheet) */
/* (MIN) 0,1,2,3,4,8,12,16,20...,1020,1024 (MAX) */
mul = (curVol & 0x7fc) ? (curVol & 0x7fc) : (curVol & 0x03);
//printf("samples: %i\n", samples);
//memset(cdc.ram, 0, samples * 4);
//fread(cdc.ram, 1, samples * 4, cdd.toc.tracks[cdd.index].fd);
}
/* left channel */
/* process 16-bit (little-endian) stereo samples */
for (i = 0; i < samples; i++)
{
/* CD-DA fader multiplier (cf. LC7883 datasheet) */
/* (MIN) 0,1,2,3,4,8,12,16,20...,1020,1024 (MAX) */
mul = (curVol & 0x7fc) ? (curVol & 0x7fc) : (curVol & 0x03);
/* left channel */
#ifdef LSB_FIRST
delta = ((ptr[0] * mul) / 1024) - l;
ptr++;
*outptr++ = ((ptr[0] * mul) / 1024);
ptr++;
#else
delta = (((int16)((ptr[0] + ptr[1]*256)) * mul) / 1024) - l;
ptr += 2;
*outptr++ = (((int16)((ptr[0] + ptr[1] * 256)) * mul) / 1024);
ptr += 2;
#endif
l += delta;
blip_add_delta_fast(blip[0], i, delta);
/* right channel */
/* right channel */
#ifdef LSB_FIRST
delta = ((ptr[0] * mul) / 1024) - r;
ptr++;
*outptr++ = ((ptr[0] * mul) / 1024);
ptr++;
#else
delta = (((int16)((ptr[0] + ptr[1]*256)) * mul) / 1024) - r;
ptr += 2;
*outptr++ = (((int16)((ptr[0] + ptr[1] * 256)) * mul) / 1024);
ptr += 2;
#endif
r += delta;
blip_add_delta_fast(blip[1], i, delta);
/* update CD-DA fader volume (one step/sample) */
if (curVol < endVol)
{
/* fade-in */
curVol++;
}
else if (curVol > endVol)
{
/* fade-out */
curVol--;
}
else if (!curVol)
{
/* audio will remain muted until next setup */
break;
}
}
}
/* update CD-DA fader volume (one step/sample) */
if (curVol < endVol)
{
/* fade-in */
curVol++;
}
else if (curVol > endVol)
{
/* fade-out */
curVol--;
}
else if (!curVol)
{
/* audio will remain muted until next setup */
break;
}
}
}
/* save current CD-DA fader volume */
cdd.volume = curVol;
/* save last audio output for next frame */
cdd.audio[0] = l;
cdd.audio[1] = r;
}
else
{
/* no audio output */
if (l) blip_add_delta_fast(blip[0], 0, -l);
if (r) blip_add_delta_fast(blip[1], 0, -r);
/* save audio output for next frame */
cdd.audio[0] = 0;
cdd.audio[1] = 0;
}
/* end of Blip Buffer timeframe */
blip_end_frame(blip[0], samples);
blip_end_frame(blip[1], samples);
/* save current CD-DA fader volume */
cdd.volume = curVol;
}
else
{
/* no audio output */
memset(buffer, 0, samples * 4);
}
}
#endif
void cdd_update(void)
{
@ -411,6 +361,12 @@ void cdd_update(void)
if (cdd.lba >= cdd.toc.tracks[cdd.index].start)
{
/* audio track playing */
// if it wasn't before, set the audio start position
if (Pico_mcd->s68k_regs[0x36 + 0])
{
cdd.sampleLba = cdd.lba + 1;
cdd.sampleOffset = 0;
}
Pico_mcd->s68k_regs[0x36 + 0] = 0x00;
}
@ -455,6 +411,7 @@ void cdd_update(void)
{
/* fast-forward or fast-rewind */
cdd.lba += cdd.scanOffset;
cdd.sampleLba += cdd.scanOffset;
/* check current track limits */
if (cdd.lba >= cdd.toc.tracks[cdd.index].end)
@ -469,6 +426,9 @@ void cdd_update(void)
if (cdd.status == CD_PLAY)
{
Pico_mcd->s68k_regs[0x36 + 0] = 0x00;
// set audio start point
cdd.sampleLba = cdd.lba;
cdd.sampleOffset = 0;
}
}
else if (cdd.lba < cdd.toc.tracks[cdd.index].start)

View File

@ -80,6 +80,8 @@ typedef struct
int lba;
int scanOffset;
int volume;
int sampleOffset;
int sampleLba;
uint8_t status;
toc_t toc;
int16_t audio[2];

View File

@ -19,11 +19,6 @@ extern "C" {
// message log
extern void lprintf(const char *fmt, ...);
// external funcs for Sega/Mega CD
extern int mp3_get_bitrate(void *f, int size);
extern void mp3_start_play(void *f, int pos);
extern void mp3_update(int *buffer, int length, int stereo);
// this one should handle display mode changes
extern void emu_video_mode_change(int start_line, int line_count, int is_32cols);

View File

@ -438,8 +438,6 @@ typedef struct
unsigned char bram[0x2000]; // 110200: 8K
struct mcd_misc m; // 112200: misc
struct mcd_pcm pcm; // 112240:
void *cdda_stream;
int cdda_type;
int pcm_mixbuf[PCM_MIXBUF_LEN * 2];
int pcm_mixpos;
char pcm_mixbuf_dirty;
@ -605,7 +603,7 @@ unsigned short cdc_host_r(void);
// cd/cdd.c
void cdd_reset(void);
void cdd_read_data(unsigned char *dst);
void cdd_read_audio(unsigned int samples);
void cdd_read_audio(short *buffer, unsigned int samples);
void cdd_update(void);
void cdd_process(void);
@ -709,7 +707,6 @@ PICO_INTERNAL int SekInterruptS68k(int irq);
void SekInterruptClearS68k(int irq);
// sound/sound.c
extern short cdda_out_buffer[2*1152];
extern int PsndLen_exc_cnt;
extern int PsndLen_exc_add;
extern int timer_a_next_oflow, timer_a_step; // in z80 cycles

View File

@ -24,7 +24,7 @@ static int PsndBuffer[2*(44100+100)/50];
static unsigned short dac_info[312+4]; // pppppppp ppppllll, p - pos in buff, l - length to write for this sample
// cdda output buffer
short cdda_out_buffer[2*1152];
static short cdda_out_buffer[2*1152];
// for Pico
int PsndRate=0;
@ -190,50 +190,30 @@ PICO_INTERNAL void PsndDoDAC(int line_to)
// cdda
static void cdda_raw_update(int *buffer, int length)
{
/*int ret, cdda_bytes, mult = 1;
cdd_read_audio(cdda_out_buffer, length);
cdda_bytes = length*4;
if (PsndRate <= 22050 + 100) mult = 2;
if (PsndRate < 22050 - 100) mult = 4;
cdda_bytes *= mult;
int mult = 1;
ret = pm_read(cdda_out_buffer, cdda_bytes, Pico_mcd->cdda_stream);
if (ret < cdda_bytes) {
memset((char *)cdda_out_buffer + ret, 0, cdda_bytes - ret);
Pico_mcd->cdda_stream = NULL;
return;
}
if (PsndRate <= 22050 + 100)
mult = 2;
if (PsndRate < 22050 - 100)
mult = 4;
// now mix
switch (mult) {
case 1: mix_16h_to_32(buffer, cdda_out_buffer, length*2); break;
case 2: mix_16h_to_32_s1(buffer, cdda_out_buffer, length*2); break;
case 4: mix_16h_to_32_s2(buffer, cdda_out_buffer, length*2); break;
}*/
// now mix
switch (mult)
{
case 1:
mix_16h_to_32(buffer, cdda_out_buffer, length * 2);
break;
case 2:
mix_16h_to_32_s1(buffer, cdda_out_buffer, length * 2);
break;
case 4:
mix_16h_to_32_s2(buffer, cdda_out_buffer, length * 2);
break;
}
}
void cdda_start_play(int lba_base, int lba_offset, int lb_len)
{
/*if (Pico_mcd->cdda_type == CT_MP3)
{
int pos1024 = 0;
if (lba_offset)
pos1024 = lba_offset * 1024 / lb_len;
mp3_start_play(Pico_mcd->cdda_stream, pos1024);
return;
}
pm_seek(Pico_mcd->cdda_stream, (lba_base + lba_offset) * 2352, SEEK_SET);
if (Pico_mcd->cdda_type == CT_WAV)
{
// skip headers, assume it's 44kHz stereo uncompressed
pm_seek(Pico_mcd->cdda_stream, 44, SEEK_CUR);
}*/
}
PICO_INTERNAL void PsndClear(void)
{
int len = PsndLen;
@ -297,7 +277,6 @@ static int PsndRender(int offset, int length)
// CD: CDDA audio
// CD mode, cdda enabled, not data track, CDC is reading
if ((PicoAHW & PAHW_MCD) && (PicoOpt & POPT_EN_MCD_CDDA)
&& Pico_mcd->cdda_stream != NULL
&& !(Pico_mcd->s68k_regs[0x36] & 1))
{
// note: only 44, 22 and 11 kHz supported, with forced stereo