From 5346ef0c652890b3efcb23b4ab322fbf662cd313 Mon Sep 17 00:00:00 2001 From: arbee Date: Sat, 2 Nov 2024 11:59:49 -0400 Subject: sound/cdda.cpp: Updates and additions [R. Belmont] * More flexible API; you can set the start and length separately without forcing unpause/play start/etc. * Data tracks are force muted, headphone users are safe * Added forward and reverse fast scan capability nscsi/cd.cpp: AppleCD improvements [R. Belmont, thanks to Joshua Bell] * Fixed bug causing APPLE_AUDIO_STATUS to always return PLAYING * APPLE_READ_SUB_CHANNEL now returns the current location information even when playback is stopped * APPLE_AUDIO_PLAY semantics have been overhauled to support the start playback/set end flag * APPLE_AUDIO_STOP now sets the stop position to the end of the specified track, as per comments in Apple II DeskTop's CD Remote and the behavior of Apple CD-ROM Explorer. * APPLE_AUDIO_SCAN is now implemented and works * Many other minor fixes and behavior updates --- src/devices/bus/nscsi/cd.cpp | 191 +++++++++++++++++++++++++++++++------------ src/devices/sound/cdda.cpp | 121 +++++++++++++++++++++------ src/devices/sound/cdda.h | 7 +- 3 files changed, 241 insertions(+), 78 deletions(-) diff --git a/src/devices/bus/nscsi/cd.cpp b/src/devices/bus/nscsi/cd.cpp index 0e629186b46..2d2aaffcde3 100644 --- a/src/devices/bus/nscsi/cd.cpp +++ b/src/devices/bus/nscsi/cd.cpp @@ -6,7 +6,7 @@ #include "coreutil.h" #include "multibyte.h" -#define VERBOSE 0 +#define VERBOSE (0) #include "logmacro.h" DEFINE_DEVICE_TYPE(NSCSI_CDROM, nscsi_cdrom_device, "scsi_cdrom", "SCSI CD-ROM") @@ -902,6 +902,7 @@ void nscsi_cdrom_apple_device::return_no_cd() void nscsi_cdrom_apple_device::scsi_command() { + #if 1 if (scsi_cmdbuf[0] != 8 && scsi_cmdbuf[0] != 0x28 && scsi_cmdbuf[0] != 0 && scsi_cmdbuf[0] != 0x03) { LOG("CD command: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", @@ -910,10 +911,10 @@ void nscsi_cdrom_apple_device::scsi_command() scsi_cmdbuf[6], scsi_cmdbuf[7], scsi_cmdbuf[8], scsi_cmdbuf[9]); } - + #endif switch (scsi_cmdbuf[0]) { case SC_TEST_UNIT_READY: - LOG("command TEST UNIT READY (AppleCD)\n"); + //LOG("command TEST UNIT READY (AppleCD)\n"); if(image->exists()) { scsi_status_complete(SS_GOOD); @@ -1085,7 +1086,7 @@ void nscsi_cdrom_apple_device::scsi_command() break; } - // Audio status codes, adapted from CDRemote.equ in GS/OS 6.0.1 (matches CDU-541 as well) + // Audio status codes, adapted from CDRemote.equ in GS/OS 6.0.1 (matches Sony CDU-541 as well) enum cd_status_t: u8 { PLAYING = 0, // Audio is playing @@ -1106,9 +1107,6 @@ void nscsi_cdrom_apple_device::scsi_command() scsi_cmdbuf[4] = dec_2_bcd(BIT(frame, 8, 8)); // second scsi_cmdbuf[5] = dec_2_bcd(BIT(frame, 0, 8)); // frame - // TODO: When you pause, both 7.1's CD Remote and 7.6+'s more modern player still see the - // status as "playing" even though it's definitely returning "paused". I've tried returning - // every code and nothing works. if (type == 0) { scsi_cmdbuf[1] = 0; @@ -1150,28 +1148,25 @@ void nscsi_cdrom_apple_device::scsi_command() // Other 4 bytes unknown in this mode, going with the same as type 0 for now } } - //printf("status => %02x %02x %02x %02x %02x %02x\n", scsi_cmdbuf[0], scsi_cmdbuf[1], scsi_cmdbuf[2], scsi_cmdbuf[3], scsi_cmdbuf[4], scsi_cmdbuf[5]); - scsi_cmdbuf[0] = 0; scsi_data_in(SBUF_MAIN, 6); scsi_status_complete(SS_GOOD); break; case APPLE_READ_SUB_CHANNEL: - LOG("command READ SUB CHANNEL\n"); - - scsi_cmdbuf[0] = 0; // Control nibble - scsi_cmdbuf[1] = 0; // Track - scsi_cmdbuf[2] = 0; // Index - scsi_cmdbuf[3] = 0; // Relative Minute - scsi_cmdbuf[4] = 0; // Relative Second - scsi_cmdbuf[5] = 0; // Relative Frame - scsi_cmdbuf[6] = 0; // AMinute - scsi_cmdbuf[7] = 0; // ASecond - scsi_cmdbuf[8] = 0; // AFrame - - if (cdda->audio_active()) { - int track = image->get_track(cdda->get_audio_lba()); + LOG("command READ SUB CHANNEL\n"); + + scsi_cmdbuf[0] = 0; // Control nibble + scsi_cmdbuf[1] = 0; // Track + scsi_cmdbuf[2] = 0; // Index + scsi_cmdbuf[3] = 0; // Relative Minute + scsi_cmdbuf[4] = 0; // Relative Second + scsi_cmdbuf[5] = 0; // Relative Frame + scsi_cmdbuf[6] = 0; // AMinute + scsi_cmdbuf[7] = 0; // ASecond + scsi_cmdbuf[8] = 0; // AFrame + + const int track = image->get_track(cdda->get_audio_lba()); scsi_cmdbuf[1] = dec_2_bcd(track+1); scsi_cmdbuf[2] = 1; // Index @@ -1192,48 +1187,88 @@ void nscsi_cdrom_apple_device::scsi_command() scsi_cmdbuf[6] = dec_2_bcd(BIT(frame, 16, 8)); // minute scsi_cmdbuf[7] = dec_2_bcd(BIT(frame, 8, 8)); // second scsi_cmdbuf[8] = dec_2_bcd(BIT(frame, 0, 8)); // frame - } - scsi_data_in(SBUF_MAIN, 9); - scsi_status_complete(SS_GOOD); + scsi_data_in(SBUF_MAIN, 9); + scsi_status_complete(SS_GOOD); + } break; + // This command does not change the play or pause status. case APPLE_AUDIO_PLAY: - case APPLE_AUDIO_PLAY_TRACK: { const uint8_t val = scsi_cmdbuf[5]; const uint8_t start_track = (((val & 0xf0) >> 4) * 10) + (val & 0x0f); + cdda->cancel_scan(); + if (cdda->audio_paused()) + { + cdda->pause_audio(0); + } + uint32_t start_lba = 0; - if (scsi_cmdbuf[0] == APPLE_AUDIO_PLAY) + if (scsi_cmdbuf[9] == 0x40) + { + LOG("command APPLE AUDIO PLAY MSF = %02x:%02x:%02x\n", scsi_cmdbuf[5], scsi_cmdbuf[6], scsi_cmdbuf[7]); + const uint32_t msf = (bcd_2_dec(scsi_cmdbuf[5]) << 16) | (bcd_2_dec(scsi_cmdbuf[6]) << 8) | bcd_2_dec(scsi_cmdbuf[7]); + start_lba = cdrom_file::msf_to_lba(msf); + } + else if (scsi_cmdbuf[9] == 0x80) { - if (scsi_cmdbuf[9] == 0x40) + LOG("command APPLE AUDIO PLAY track = %d\n", start_track); + if (start_track == 0) { - LOG("command APPLE AUDIO PLAY MSF = %02x:%02x:%02x\n", scsi_cmdbuf[5], scsi_cmdbuf[6], scsi_cmdbuf[7]); - const uint32_t msf = (bcd_2_dec(scsi_cmdbuf[5]) << 16) | (bcd_2_dec(scsi_cmdbuf[6]) << 8) | bcd_2_dec(scsi_cmdbuf[7]); - start_lba = cdrom_file::msf_to_lba(msf); + cdda->stop_audio(); + scsi_status_complete(SS_GOOD); + return; } - else if (scsi_cmdbuf[9] == 0x80) + else { - LOG("command APPLE AUDIO PLAY track = %d\n", start_track); - if (start_track == 0) + start_lba = image->get_track_start(start_track - 1); + m_stop_position = image->get_track_start(start_track); + + // If this is set, then the stop position is calculated similar to + // the AUDIO_PLAY_TRACK command. Otherwise the previously set length + // is used. (Also playback is started? Unclear) + if ((scsi_cmdbuf[1] & 0x10) == 0x10) { - cdda->stop_audio(); - scsi_status_complete(SS_GOOD); - return; + if (m_stopped) + { + cdda->start_audio(start_lba, m_stop_position - start_lba); + m_stopped = false; + } + else + { + cdda->set_audio_lba(start_lba); + cdda->set_audio_length(m_stop_position - start_lba); + } } - else + else // don't set end, and don't start playback { - start_lba = image->get_track_start(start_track - 1); + cdda->set_audio_lba(start_lba); } } - else - { - logerror("nscsi_cdrom_apple_device: Unknown APPLE AUDIO PLAY address mode %02x\n", scsi_cmdbuf[9]); - } } - else // APPLE_AUDIO_PLAY_TRACK + else + { + logerror("nscsi_cdrom_apple_device: Unknown APPLE AUDIO PLAY address mode %02x\n", scsi_cmdbuf[9]); + } + scsi_status_complete(SS_GOOD); + } + break; + + case APPLE_AUDIO_PLAY_TRACK: + { + const uint8_t val = scsi_cmdbuf[5]; + const uint8_t start_track = (((val & 0xf0) >> 4) * 10) + (val & 0x0f); + + cdda->cancel_scan(); + if (cdda->audio_paused()) + { + cdda->pause_audio(0); + } + + if (scsi_cmdbuf[9] == 0x80) { LOG("command APPLE AUDIO PLAY TRACK track = %d\n", start_track); @@ -1247,23 +1282,60 @@ void nscsi_cdrom_apple_device::scsi_command() } else { - start_lba = image->get_track_start(start_track - 1); - m_stop_position = image->get_track_start(start_track); + const uint32_t start_lba = image->get_track_start(start_track - 1); + m_stop_position = image->get_track_start(start_track + 1); + cdda->start_audio(start_lba, m_stop_position - start_lba); + m_stopped = false; } } + else if (scsi_cmdbuf[9] == 0x40) + { + LOG("command APPLE AUDIO PLAY TRACK MSF = %02x:%02x:%02x\n", scsi_cmdbuf[3], scsi_cmdbuf[4], scsi_cmdbuf[5]); + + const uint32_t msf = (bcd_2_dec(scsi_cmdbuf[3]) << 16) | (bcd_2_dec(scsi_cmdbuf[4]) << 8) | bcd_2_dec(scsi_cmdbuf[5]); + const uint32_t start_lba = cdrom_file::msf_to_lba(msf); + cdda->start_audio(start_lba, m_stop_position - start_lba); + m_stopped = false; + } + else + { + fatalerror("nscsi_cdrom_apple_device: unknown mode for APPLE AUDIO PLAY TRACK %02x\n", scsi_cmdbuf[9]); + } - m_stopped = false; - cdda->start_audio(start_lba, m_stop_position - start_lba); scsi_status_complete(SS_GOOD); } break; case APPLE_AUDIO_SCAN: + /* + 3/4/5 = MSF of starting position + 1 = 00 for forward scan, 0x10 for reverse + Scan ends with a PAUSE OFF command. + */ LOG("command APPLE_AUDIO_SCAN: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", scsi_cmdbuf[0], scsi_cmdbuf[1], scsi_cmdbuf[2], scsi_cmdbuf[3], scsi_cmdbuf[4], scsi_cmdbuf[5], scsi_cmdbuf[6], scsi_cmdbuf[7], scsi_cmdbuf[8], scsi_cmdbuf[9]); + if (!m_stopped) + { + // begin seeking from the specified M/S/F + if (scsi_cmdbuf[9] == 0x40) + { + const uint32_t msf = (bcd_2_dec(scsi_cmdbuf[3]) << 16) | (bcd_2_dec(scsi_cmdbuf[4]) << 8) | bcd_2_dec(scsi_cmdbuf[5]); + const uint32_t start_lba = cdrom_file::msf_to_lba(msf); + cdda->set_audio_lba(start_lba); + + if (scsi_cmdbuf[1] == 0) + { + cdda->scan_forward(); + } + else + { + cdda->scan_reverse(); + } + } + } scsi_status_complete(SS_GOOD); break; @@ -1279,7 +1351,8 @@ void nscsi_cdrom_apple_device::scsi_command() } else { - // release pause + // release pause and stop scanning + cdda->cancel_scan(); if (cdda->audio_paused()) { cdda->pause_audio(0); @@ -1289,10 +1362,10 @@ void nscsi_cdrom_apple_device::scsi_command() break; case APPLE_AUDIO_STOP: - LOG("command APPLE AUDIO STOP\n"); switch (scsi_cmdbuf[9]) { case 0x00: // stop immediately + LOG("command APPLE AUDIO STOP immediately\n"); m_stop_position = 0; m_stopped = true; cdda->stop_audio(); @@ -1302,14 +1375,23 @@ void nscsi_cdrom_apple_device::scsi_command() { const uint32_t msf = (bcd_2_dec(scsi_cmdbuf[5]) << 16) | (bcd_2_dec(scsi_cmdbuf[6]) << 8) | bcd_2_dec(scsi_cmdbuf[7]); m_stop_position = cdrom_file::msf_to_lba(msf); + LOG("command APPLE AUDIO STOP at MSF %02x:%02x:%02x\n", scsi_cmdbuf[5], scsi_cmdbuf[6], scsi_cmdbuf[7]); } break; case 0x80: // stop at track number { - const uint8_t val = scsi_cmdbuf[5]; - const uint8_t start_track = (((val & 0xf0) >> 4) * 10) + (val & 0x0f); - m_stop_position = image->get_track_start(start_track); + const uint8_t start_track = bcd_2_dec(scsi_cmdbuf[5]); + if (start_track >= image->get_last_track()) + { + m_stop_position = image->get_track_start(0xaa); + } + else + { + m_stop_position = image->get_track_start(start_track + 1); + } + m_stop_position--; + LOG("command APPLE AUDIO STOP at end of track %d (LBA %d)\n", start_track, m_stop_position); } break; @@ -1356,6 +1438,7 @@ void nscsi_cdrom_apple_device::scsi_command() case SC_READ_10: case SC_READ_12: cdda->stop_audio(); + m_stopped = true; [[fallthrough]]; default: diff --git a/src/devices/sound/cdda.cpp b/src/devices/sound/cdda.cpp index cc222534f98..5c39bbfe84e 100644 --- a/src/devices/sound/cdda.cpp +++ b/src/devices/sound/cdda.cpp @@ -8,8 +8,8 @@ #include "emu.h" #include "cdda.h" -#define MAX_SECTORS ( 4 ) - +static constexpr int MAX_SECTORS = 4; +static constexpr int MAX_SCAN_SECTORS = 2; //------------------------------------------------- // sound_stream_update - handle a stream update @@ -39,22 +39,25 @@ void cdda_device::device_start() m_audio_samples = 0; m_audio_bptr = 0; m_sequence_counter = 0; - - save_item( NAME(m_audio_playing) ); - save_item( NAME(m_audio_pause) ); - save_item( NAME(m_audio_ended_normally) ); - save_item( NAME(m_audio_lba) ); - save_item( NAME(m_audio_length) ); - save_pointer( NAME(m_audio_cache), cdrom_file::MAX_SECTOR_DATA * MAX_SECTORS ); - save_item( NAME(m_audio_samples) ); - save_item( NAME(m_audio_bptr) ); - save_item( NAME(m_sequence_counter) ); - + m_audio_scan = 0; + m_audio_scan_direction = 0; + + save_item(NAME(m_audio_playing)); + save_item(NAME(m_audio_pause)); + save_item(NAME(m_audio_ended_normally)); + save_item(NAME(m_audio_lba)); + save_item(NAME(m_audio_length)); + save_pointer(NAME(m_audio_cache), cdrom_file::MAX_SECTOR_DATA * MAX_SECTORS); + save_item(NAME(m_audio_samples)); + save_item(NAME(m_audio_bptr)); + save_item(NAME(m_sequence_counter)); + save_item(NAME(m_audio_scan)); + save_item(NAME(m_audio_scan_direction)); } /*------------------------------------------------- - cdda_start_audio - begin playback of a Red + start_audio - begin playback of a Red Book audio track -------------------------------------------------*/ @@ -71,7 +74,7 @@ void cdda_device::start_audio(uint32_t startlba, uint32_t numblocks) /*------------------------------------------------- - cdda_stop_audio - stop playback of a Red Book + stop_audio - stop playback of a Red Book audio track -------------------------------------------------*/ @@ -84,7 +87,7 @@ void cdda_device::stop_audio() /*------------------------------------------------- - cdda_pause_audio - pause/unpause playback of + pause_audio - pause/unpause playback of a Red Book audio track -------------------------------------------------*/ @@ -94,9 +97,48 @@ void cdda_device::pause_audio(int pause) m_audio_pause = pause; } +/*------------------------------------------------- + scan_forward - begins an audible fast scan + in the forward direction +-------------------------------------------------*/ +void cdda_device::scan_forward() +{ + m_audio_scan = true; + m_audio_scan_direction = true; +} + +/*------------------------------------------------- + scan_reverse - begins an audible fast scan + in the backwards direction +-------------------------------------------------*/ +void cdda_device::scan_reverse() +{ + m_audio_scan = true; + m_audio_scan_direction = false; +} + +/*------------------------------------------------- + cancel_scan - stops scan mode and resumes + normal operation +-------------------------------------------------*/ +void cdda_device::cancel_scan() +{ + m_audio_scan = false; +} + +/*------------------------------------------------- + set_audio_lba - sets the current audio LBA + play position without changing any modes. +-------------------------------------------------*/ + +void cdda_device::set_audio_lba(uint32_t lba) +{ + m_stream->update(); + m_audio_lba = lba; +} /*------------------------------------------------- - cdda_get_audio_lba - returns the current LBA + get_audio_lba - returns the current LBA (physical sector) during Red Book playback -------------------------------------------------*/ @@ -106,9 +148,18 @@ uint32_t cdda_device::get_audio_lba() return m_audio_lba - ((m_audio_samples + (cdrom_file::MAX_SECTOR_DATA / 4) - 1) / (cdrom_file::MAX_SECTOR_DATA / 4)); } +/*------------------------------------------------- + set_audio_length - sets the current audio LBA + play position without changing any modes. +-------------------------------------------------*/ + +void cdda_device::set_audio_length(uint32_t sectors) +{ + m_audio_length = sectors; +} /*------------------------------------------------- - cdda_audio_active - returns Red Book audio + audio_active - returns Red Book audio playback status -------------------------------------------------*/ @@ -120,7 +171,7 @@ int cdda_device::audio_active() /*------------------------------------------------- - cdda_audio_paused - returns if Red Book + audio_paused - returns if Red Book playback is paused -------------------------------------------------*/ @@ -131,7 +182,7 @@ int cdda_device::audio_paused() /*------------------------------------------------- - cdda_audio_ended - returns if a Red Book + audio_ended - returns if a Red Book track reached it's natural end -------------------------------------------------*/ @@ -203,9 +254,33 @@ void cdda_device::get_audio_data(write_stream_view &bufL, write_stream_view &buf for (i = 0; i < sectors; i++) { - m_disc->read_data(m_audio_lba, &m_audio_cache[cdrom_file::MAX_SECTOR_DATA*i], cdrom_file::CD_TRACK_AUDIO); - - m_audio_lba++; + const auto adr_control = m_disc->get_adr_control(m_disc->get_track(m_audio_lba)); + + // Don't attempt to play data tracks + if (BIT(adr_control, 2)) + { + std::fill_n(&m_audio_cache[cdrom_file::MAX_SECTOR_DATA * i], cdrom_file::MAX_SECTOR_DATA, 0); + } + else + { + m_disc->read_data(m_audio_lba, &m_audio_cache[cdrom_file::MAX_SECTOR_DATA * i], cdrom_file::CD_TRACK_AUDIO); + } + + if (!m_audio_scan) + { + m_audio_lba++; + } + else + { + if (m_audio_scan_direction) + { + m_audio_lba += 2; + } + else + { + m_audio_lba -= 2; + } + } } m_audio_samples = (cdrom_file::MAX_SECTOR_DATA*sectors)/4; diff --git a/src/devices/sound/cdda.h b/src/devices/sound/cdda.h index 23c30a487b2..dcdea3cc5bf 100644 --- a/src/devices/sound/cdda.h +++ b/src/devices/sound/cdda.h @@ -18,9 +18,14 @@ public: void start_audio(uint32_t startlba, uint32_t numblocks); void stop_audio(); void pause_audio(int pause); + void scan_forward(); + void scan_reverse(); + void cancel_scan(); int16_t get_channel_sample(int channel); + void set_audio_lba(uint32_t lba); uint32_t get_audio_lba(); + void set_audio_length(uint32_t sectors); int audio_active(); int audio_paused(); int audio_ended(); @@ -42,7 +47,7 @@ private: // internal state sound_stream * m_stream; - int8_t m_audio_playing, m_audio_pause, m_audio_ended_normally; + int8_t m_audio_playing, m_audio_pause, m_audio_ended_normally, m_audio_scan, m_audio_scan_direction; uint32_t m_audio_lba, m_audio_length; std::unique_ptr m_audio_cache; -- cgit v1.2.3