From bc55597e90fa5d4a6b7ddbc32ab745be4af59c3e Mon Sep 17 00:00:00 2001 From: nattthebear Date: Mon, 3 Jul 2017 08:16:34 -0400 Subject: [PATCH] PicoDrive: CD Audio support. GPGX and PicoDrive both share the same MCD emulation core, which has significant accuracy difficulties with CD audio... --- .../Consoles/Sega/PicoDrive/PicoDrive.cs | 2 +- waterbox/picodrive/bizhawk.c | 4 - waterbox/picodrive/pico/cd/cdd.c | 238 ++++++++---------- waterbox/picodrive/pico/cd/cdd.h | 2 + waterbox/picodrive/pico/pico.h | 5 - waterbox/picodrive/pico/pico_int.h | 5 +- waterbox/picodrive/pico/sound/sound.c | 61 ++--- 7 files changed, 123 insertions(+), 194 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/PicoDrive/PicoDrive.cs b/BizHawk.Emulation.Cores/Consoles/Sega/PicoDrive/PicoDrive.cs index feaf8ad0dd..b91c903f22 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/PicoDrive/PicoDrive.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/PicoDrive/PicoDrive.cs @@ -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); } diff --git a/waterbox/picodrive/bizhawk.c b/waterbox/picodrive/bizhawk.c index a3b9e710f8..ea384c5863 100644 --- a/waterbox/picodrive/bizhawk.c +++ b/waterbox/picodrive/bizhawk.c @@ -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"); diff --git a/waterbox/picodrive/pico/cd/cdd.c b/waterbox/picodrive/pico/cd/cdd.c index c0c2c86960..3726506bbe 100644 --- a/waterbox/picodrive/pico/cd/cdd.c +++ b/waterbox/picodrive/pico/cd/cdd.c @@ -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 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 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) diff --git a/waterbox/picodrive/pico/cd/cdd.h b/waterbox/picodrive/pico/cd/cdd.h index a660d2bf26..9b7ca065d0 100644 --- a/waterbox/picodrive/pico/cd/cdd.h +++ b/waterbox/picodrive/pico/cd/cdd.h @@ -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]; diff --git a/waterbox/picodrive/pico/pico.h b/waterbox/picodrive/pico/pico.h index 818a6af33c..e64e85cef4 100644 --- a/waterbox/picodrive/pico/pico.h +++ b/waterbox/picodrive/pico/pico.h @@ -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); diff --git a/waterbox/picodrive/pico/pico_int.h b/waterbox/picodrive/pico/pico_int.h index 2d3d04b03b..baca076ad0 100644 --- a/waterbox/picodrive/pico/pico_int.h +++ b/waterbox/picodrive/pico/pico_int.h @@ -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 diff --git a/waterbox/picodrive/pico/sound/sound.c b/waterbox/picodrive/pico/sound/sound.c index 76c5c62df6..84061f4987 100644 --- a/waterbox/picodrive/pico/sound/sound.c +++ b/waterbox/picodrive/pico/sound/sound.c @@ -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