// license:BSD-3-Clause // copyright-holders:Aaron Giles,smf /* CD-DA "Red Book" audio sound hardware handler Relies on the actual CD logic and reading in cdrom.c. */ #include "emu.h" #include "cdda.h" #define MAX_SECTORS ( 4 ) //------------------------------------------------- // sound_stream_update - handle a stream update //------------------------------------------------- void cdda_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) { get_audio_data(&outputs[0][0], &outputs[1][0], samples); m_audio_volume[0] = int16_t(outputs[0][0]); m_audio_volume[1] = int16_t(outputs[1][0]); } //------------------------------------------------- // device_start - device-specific startup //------------------------------------------------- void cdda_device::device_start() { /* allocate an audio cache */ m_audio_cache = std::make_unique(CD_MAX_SECTOR_DATA * MAX_SECTORS ); m_stream = machine().sound().stream_alloc(*this, 0, 2, clock()); m_audio_playing = 0; m_audio_pause = 0; m_audio_ended_normally = 0; m_audio_lba = 0; m_audio_length = 0; m_audio_samples = 0; m_audio_bptr = 0; m_disc = nullptr; 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), CD_MAX_SECTOR_DATA * MAX_SECTORS ); save_item( NAME(m_audio_samples) ); save_item( NAME(m_audio_bptr) ); } /*------------------------------------------------- cdda_set_cdrom - set the CD-ROM file for the given CDDA stream -------------------------------------------------*/ void cdda_device::set_cdrom(void *file) { m_disc = (cdrom_file *)file; } /*------------------------------------------------- cdda_start_audio - begin playback of a Red Book audio track -------------------------------------------------*/ void cdda_device::start_audio(uint32_t startlba, uint32_t numblocks) { m_stream->update(); m_audio_playing = true; m_audio_pause = false; m_audio_ended_normally = false; m_audio_lba = startlba; m_audio_length = numblocks; m_audio_samples = 0; } /*------------------------------------------------- cdda_stop_audio - stop playback of a Red Book audio track -------------------------------------------------*/ void cdda_device::stop_audio() { m_stream->update(); m_audio_playing = false; m_audio_ended_normally = true; } /*------------------------------------------------- cdda_pause_audio - pause/unpause playback of a Red Book audio track -------------------------------------------------*/ void cdda_device::pause_audio(int pause) { m_stream->update(); m_audio_pause = pause; } /*------------------------------------------------- cdda_get_audio_lba - returns the current LBA (physical sector) during Red Book playback -------------------------------------------------*/ uint32_t cdda_device::get_audio_lba() { m_stream->update(); return m_audio_lba; } /*------------------------------------------------- cdda_audio_active - returns Red Book audio playback status -------------------------------------------------*/ int cdda_device::audio_active() { m_stream->update(); return m_audio_playing; } /*------------------------------------------------- cdda_audio_paused - returns if Red Book playback is paused -------------------------------------------------*/ int cdda_device::audio_paused() { return m_audio_pause; } /*------------------------------------------------- cdda_audio_ended - returns if a Red Book track reached it's natural end -------------------------------------------------*/ int cdda_device::audio_ended() { m_stream->update(); return m_audio_ended_normally; } /*------------------------------------------------- get_audio_data - reads Red Book data off the disc if playback is in progress and converts it to 2 16-bit 44.1 kHz streams -------------------------------------------------*/ void cdda_device::get_audio_data(stream_sample_t *bufL, stream_sample_t *bufR, uint32_t samples_wanted) { int i; int16_t *audio_cache = (int16_t *) m_audio_cache.get(); while (samples_wanted > 0) { /* if no file, audio not playing, audio paused, or out of disc data, just zero fill */ if (!m_disc || !m_audio_playing || m_audio_pause || (!m_audio_length && !m_audio_samples)) { if( m_disc && m_audio_playing && !m_audio_pause && !m_audio_length ) { m_audio_playing = false; m_audio_ended_normally = true; } memset(bufL, 0, sizeof(stream_sample_t)*samples_wanted); memset(bufR, 0, sizeof(stream_sample_t)*samples_wanted); return; } int samples = samples_wanted; if (samples > m_audio_samples) { samples = m_audio_samples; } for (i = 0; i < samples; i++) { /* CD-DA data on the disc is big-endian */ *bufL++ = (int16_t) big_endianize_int16( audio_cache[ m_audio_bptr ] ); m_audio_bptr++; *bufR++ = (int16_t) big_endianize_int16( audio_cache[ m_audio_bptr ] ); m_audio_bptr++; } samples_wanted -= samples; m_audio_samples -= samples; if (m_audio_samples == 0) { int sectors = m_audio_length; if (sectors > MAX_SECTORS) { sectors = MAX_SECTORS; } for (i = 0; i < sectors; i++) { cdrom_read_data(m_disc, m_audio_lba, &m_audio_cache[CD_MAX_SECTOR_DATA*i], CD_TRACK_AUDIO); m_audio_lba++; } m_audio_samples = (CD_MAX_SECTOR_DATA*sectors)/4; m_audio_length -= sectors; /* reset feedout ptr */ m_audio_bptr = 0; } } } /*------------------------------------------------- cdda_get_channel_volume - sets CD-DA volume level for either speaker, used for volume control display -------------------------------------------------*/ int16_t cdda_device::get_channel_volume(int channel) { return m_audio_volume[channel]; } DEFINE_DEVICE_TYPE(CDDA, cdda_device, "cdda", "CD/DA") cdda_device::cdda_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : device_t(mconfig, CDDA, tag, owner, clock) , device_sound_interface(mconfig, *this) , m_disc(nullptr) , m_stream(nullptr) { }