diff --git a/docs/source/commandline/commandline-all.rst b/docs/source/commandline/commandline-all.rst index fef34895f0b..498742f96cd 100644 --- a/docs/source/commandline/commandline-all.rst +++ b/docs/source/commandline/commandline-all.rst @@ -2970,7 +2970,7 @@ Core Sound Options .. _mame-commandline-sound: -**-sound** ** +**-sound** ** Specifies which sound module to use. Selecting ``none`` disables sound output and input altogether (sound hardware is still emulated). @@ -2999,7 +2999,6 @@ Core Sound Options * - Module - Supported OS - - Output - Input - Output monitoring - Multi-channel @@ -3007,27 +3006,17 @@ Core Sound Options * - ``wasapi`` - Windows - Yes - - Yes - - Yes (Windows 10 1703 or later) + - Yes [#SoundWASAPIMonitoring]_ - Yes - Yes * - ``xaudio2`` - - Windows 8 or later - - Yes + - Windows [#SoundXAudio2OS]_ - No - No - Yes - Yes - * - ``dsound`` - - Windows - - Yes - - No - - No - - No - - No * - ``coreaudio`` - macOS - - Yes - No - No - No @@ -3035,20 +3024,17 @@ Core Sound Options * - ``pipewire`` - Linux - Yes - - Yes - ? - Yes - Yes * - ``pulse`` - Linux - - Yes - No - No - Yes - Yes * - ``sdl`` - All [#SoundWinSDL]_ - - Yes - No - No - Yes @@ -3056,7 +3042,6 @@ Core Sound Options * - ``portaudio`` - All - Yes - - Yes - Yes [#SoundPortAudioMonitoring]_ - Yes - No @@ -3064,6 +3049,11 @@ Core Sound Options .. rubric:: Footnotes +.. [#SoundWASAPIMonitoring] MAME requires Windows 10 1703 or later to use + output moitoring with WASAPI. + +.. [#SoundXAudio2OS] MAME requires Windows 8 or later to use XAudio2. + .. [#SoundWinSDL] While SDL is not a supported option on official MAME builds for Windows, you can compile MAME with SDL support on Windows. diff --git a/scripts/src/osd/modules.lua b/scripts/src/osd/modules.lua index 737727d0a02..773859116dc 100644 --- a/scripts/src/osd/modules.lua +++ b/scripts/src/osd/modules.lua @@ -130,7 +130,6 @@ function osdmodulesbuild() MAME_DIR .. "src/osd/modules/render/drawsdl.cpp", MAME_DIR .. "src/osd/modules/render/render_module.h", MAME_DIR .. "src/osd/modules/sound/coreaudio_sound.cpp", - MAME_DIR .. "src/osd/modules/sound/direct_sound.cpp", MAME_DIR .. "src/osd/modules/sound/js_sound.cpp", MAME_DIR .. "src/osd/modules/sound/mmdevice_helpers.cpp", MAME_DIR .. "src/osd/modules/sound/mmdevice_helpers.h", diff --git a/src/osd/modules/lib/osdobj_common.cpp b/src/osd/modules/lib/osdobj_common.cpp index 9d567a0cae8..cacead90f76 100644 --- a/src/osd/modules/lib/osdobj_common.cpp +++ b/src/osd/modules/lib/osdobj_common.cpp @@ -255,7 +255,6 @@ void osd_common_t::register_options() REGISTER_MODULE(m_mod_man, SOUND_WASAPI); REGISTER_MODULE(m_mod_man, SOUND_XAUDIO2); - REGISTER_MODULE(m_mod_man, SOUND_DSOUND); REGISTER_MODULE(m_mod_man, SOUND_COREAUDIO); REGISTER_MODULE(m_mod_man, SOUND_JS); REGISTER_MODULE(m_mod_man, SOUND_SDL); diff --git a/src/osd/modules/sound/direct_sound.cpp b/src/osd/modules/sound/direct_sound.cpp deleted file mode 100644 index 8fbeed796ba..00000000000 --- a/src/osd/modules/sound/direct_sound.cpp +++ /dev/null @@ -1,557 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:Aaron Giles -//============================================================ -// -// sound.c - Win32 implementation of MAME sound routines -// -//============================================================ - -#include "sound_module.h" -#include "modules/osdmodule.h" - -#if defined(OSD_WINDOWS) || defined(SDLMAME_WIN32) - -// MAME headers -#include "emuopts.h" - -// osd headers -#include "modules/lib/osdobj_common.h" -#include "osdepend.h" -#include "osdcore.h" - -#ifdef SDLMAME_WIN32 -#include "sdl/window.h" -#include -#else -#include "winmain.h" -#include "window.h" -#endif - -#include - -// standard windows headers - -#include - -#include -#include - -#include - -#include - - -//============================================================ -// DEBUGGING -//============================================================ - -#define LOG_SOUND 0 - -#define LOG(...) do { if (LOG_SOUND) osd_printf_verbose(__VA_ARGS__); } while(0) - - -namespace osd { - -namespace { - -class buffer_base -{ -public: - explicit operator bool() const { return bool(m_buffer); } - - unsigned long release() { return m_buffer.Reset(); } - -protected: - Microsoft::WRL::ComPtr m_buffer; -}; - - -class primary_buffer : public buffer_base -{ -public: - HRESULT create(LPDIRECTSOUND dsound) - { - assert(!m_buffer); - DSBUFFERDESC desc; - memset(&desc, 0, sizeof(desc)); - desc.dwSize = sizeof(desc); - desc.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_GETCURRENTPOSITION2; - desc.lpwfxFormat = nullptr; - return dsound->CreateSoundBuffer(&desc, &m_buffer, nullptr); - } - - HRESULT get_format(WAVEFORMATEX &format) const - { - assert(m_buffer); - return m_buffer->GetFormat(&format, sizeof(format), nullptr); - } - - HRESULT set_format(WAVEFORMATEX const &format) const - { - assert(m_buffer); - return m_buffer->SetFormat(&format); - } -}; - - -class stream_buffer : public buffer_base -{ -public: - HRESULT create(LPDIRECTSOUND dsound, DWORD size, WAVEFORMATEX &format) - { - assert(!m_buffer); - DSBUFFERDESC desc; - memset(&desc, 0, sizeof(desc)); - desc.dwSize = sizeof(desc); - desc.dwFlags = DSBCAPS_CTRLVOLUME | DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2; - desc.dwBufferBytes = size; - desc.lpwfxFormat = &format; - m_size = size; - return dsound->CreateSoundBuffer(&desc, &m_buffer, nullptr); - } - - HRESULT play_looping() const - { - assert(m_buffer); - return m_buffer->Play(0, 0, DSBPLAY_LOOPING); - } - HRESULT stop() const - { - assert(m_buffer); - return m_buffer->Stop(); - } - HRESULT get_current_positions(DWORD &play_pos, DWORD &write_pos) const - { - assert(m_buffer); - return m_buffer->GetCurrentPosition(&play_pos, &write_pos); - } - HRESULT copy_data(DWORD cursor, DWORD bytes, void const *data) - { - HRESULT result = lock(cursor, bytes); - if (DS_OK != result) - return result; - - assert(m_bytes1); - assert((m_locked1 + m_locked2) >= bytes); - memcpy(m_bytes1, data, std::min(m_locked1, bytes)); - if (m_locked1 < bytes) - { - assert(m_bytes2); - memcpy(m_bytes2, (uint8_t const *)data + m_locked1, bytes - m_locked1); - } - - unlock(); - return DS_OK; - } - HRESULT clear() - { - HRESULT result = lock_all(); - if (DS_OK != result) - return result; - - assert(m_bytes1); - assert(!m_bytes2); - assert(m_size == m_locked1); - assert(0U == m_locked2); - memset(m_bytes1, 0, m_locked1); - - unlock(); - return DS_OK; - } - - DWORD size() const { return m_size; } - -private: - HRESULT lock(DWORD cursor, DWORD bytes) - { - assert(cursor < m_size); - assert(bytes <= m_size); - assert(m_buffer); - assert(!m_bytes1); - return m_buffer->Lock( - cursor, bytes, - &m_bytes1, - &m_locked1, - &m_bytes2, - &m_locked2, - 0); - } - HRESULT lock_all() { return lock(0, m_size); } - HRESULT unlock() - { - assert(m_buffer); - assert(m_bytes1); - HRESULT const result = m_buffer->Unlock( - m_bytes1, - m_locked1, - m_bytes2, - m_locked2); - m_bytes1 = m_bytes2 = nullptr; - m_locked1 = m_locked2 = 0; - return result; - } - - DWORD m_size = 0; - void *m_bytes1 = nullptr, *m_bytes2 = nullptr; - DWORD m_locked1 = 0, m_locked2 = 0; -}; - - -class sound_direct_sound : public osd_module, public sound_module -{ -public: - sound_direct_sound() : - osd_module(OSD_SOUND_PROVIDER, "dsound"), - sound_module(), - m_sample_rate(0), - m_audio_latency(0.0f), - m_bytes_per_sample(0), - m_primary_buffer(), - m_stream_buffer(), - m_stream_buffer_in(0), - m_buffer_underflows(0), - m_buffer_overflows(0) - { - } - - virtual int init(osd_interface &osd, osd_options const &options) override; - virtual void exit() override; - - // sound_module - virtual void stream_sink_update(uint32_t, int16_t const *buffer, int samples_this_frame) override; - -private: - HRESULT dsound_init(); - void dsound_kill(); - HRESULT create_buffers(DWORD size, WAVEFORMATEX &format); - void destroy_buffers(); - - // DirectSound objects - Microsoft::WRL::ComPtr m_dsound; - - // configuration - int m_sample_rate; - float m_audio_latency; - - // descriptors and formats - uint32_t m_bytes_per_sample; - - // sound buffers - primary_buffer m_primary_buffer; - stream_buffer m_stream_buffer; - uint32_t m_stream_buffer_in; - - // buffer over/underflow counts - unsigned m_buffer_underflows; - unsigned m_buffer_overflows; -}; - - -//============================================================ -// init -//============================================================ - -int sound_direct_sound::init(osd_interface &osd, osd_options const &options) -{ - m_buffer_underflows = m_buffer_overflows = 0; - m_sample_rate = options.sample_rate(); - m_audio_latency = options.audio_latency(); - if (m_audio_latency == 0.0f) - m_audio_latency = 0.1f; - - // attempt to initialize DirectSound - if (dsound_init() != DS_OK) - return -1; - - return 0; -} - - -//============================================================ -// exit -//============================================================ - -void sound_direct_sound::exit() -{ - // kill the buffers and dsound - destroy_buffers(); - dsound_kill(); - - // print out over/underflow stats - if (m_buffer_overflows || m_buffer_underflows) - { - osd_printf_verbose( - "Sound: buffer overflows=%u underflows=%u\n", - m_buffer_overflows, - m_buffer_underflows); - } - - LOG("Sound buffer: overflows=%u underflows=%u\n", m_buffer_overflows, m_buffer_underflows); -} - - -//============================================================ -// stream_sink_update -//============================================================ - -void sound_direct_sound::stream_sink_update( - uint32_t, - int16_t const *buffer, - int samples_this_frame) -{ - int const bytes_this_frame = samples_this_frame * m_bytes_per_sample; - HRESULT result; - - // if no sound, there is no buffer - if (!m_stream_buffer) - return; - - // determine the current play position - DWORD play_position, write_position; - result = m_stream_buffer.get_current_positions(play_position, write_position); - if (DS_OK != result) - return; - - //DWORD orig_write = write_position; - // normalize the write position so it is always after the play position - if (write_position < play_position) - write_position += m_stream_buffer.size(); - - // normalize the stream in position so it is always after the write position - DWORD stream_in = m_stream_buffer_in; - if (stream_in < write_position) - stream_in += m_stream_buffer.size(); - - // now we should have, in order: - // <------pp---wp---si---------------> - - // if we're between play and write positions, then bump forward, but only in full chunks - while (stream_in < write_position) - { - //printf("Underflow: PP=%d WP=%d(%d) SI=%d(%d) BTF=%d\n", (int)play_position, (int)write_position, (int)orig_write, (int)stream_in, (int)m_stream_buffer_in, (int)bytes_this_frame); - m_buffer_underflows++; - stream_in += bytes_this_frame; - } - - // if we're going to overlap the play position, just skip this chunk - if ((stream_in + bytes_this_frame) > (play_position + m_stream_buffer.size())) - { - //printf("Overflow: PP=%d WP=%d(%d) SI=%d(%d) BTF=%d\n", (int)play_position, (int)write_position, (int)orig_write, (int)stream_in, (int)m_stream_buffer_in, (int)bytes_this_frame); - m_buffer_overflows++; - return; - } - - // now we know where to copy; let's do it - m_stream_buffer_in = stream_in % m_stream_buffer.size(); - result = m_stream_buffer.copy_data(m_stream_buffer_in, bytes_this_frame, buffer); - - // if we failed, assume it was an underflow (i.e., - if (result != DS_OK) - { - m_buffer_underflows++; - return; - } - - // adjust the input pointer - m_stream_buffer_in = (m_stream_buffer_in + bytes_this_frame) % m_stream_buffer.size(); -} - - -//============================================================ -// dsound_init -//============================================================ - -HRESULT sound_direct_sound::dsound_init() -{ - assert(!m_dsound); - HRESULT result; - - // create the DirectSound object - result = DirectSoundCreate(nullptr, &m_dsound, nullptr); - if (result != DS_OK) - { - osd_printf_error("Error creating DirectSound: %08x\n", result); - goto error; - } - - // get the capabilities - DSCAPS dsound_caps; - dsound_caps.dwSize = sizeof(dsound_caps); - result = m_dsound->GetCaps(&dsound_caps); - if (result != DS_OK) - { - osd_printf_error("Error getting DirectSound capabilities: %08x\n", result); - goto error; - } - - // set the cooperative level - { -#ifdef SDLMAME_WIN32 - SDL_SysWMinfo wminfo; - SDL_VERSION(&wminfo.version); - if (!SDL_GetWindowWMInfo(dynamic_cast(*osd_common_t::window_list().front()).platform_window(), &wminfo)) - { - result = DSERR_UNSUPPORTED; // just so it has something to return - goto error; - } - HWND const window = wminfo.info.win.window; -#else // SDLMAME_WIN32 - HWND const window = dynamic_cast(*osd_common_t::window_list().front()).platform_window(); -#endif // SDLMAME_WIN32 - result = m_dsound->SetCooperativeLevel(window, DSSCL_PRIORITY); - } - if (result != DS_OK) - { - osd_printf_error("Error setting DirectSound cooperative level: %08x\n", result); - goto error; - } - - { - // make a format description for what we want - WAVEFORMATEX stream_format; - stream_format.wFormatTag = WAVE_FORMAT_PCM; - stream_format.nChannels = 2; - stream_format.nSamplesPerSec = m_sample_rate; - stream_format.wBitsPerSample = 16; - stream_format.nBlockAlign = stream_format.wBitsPerSample * stream_format.nChannels / 8; - stream_format.nAvgBytesPerSec = stream_format.nSamplesPerSec * stream_format.nBlockAlign; - stream_format.cbSize = 0; - - // compute the buffer size based on the output sample rate - DWORD stream_buffer_size = stream_format.nSamplesPerSec * stream_format.nBlockAlign * m_audio_latency; - stream_buffer_size = std::max(DWORD(1024), (stream_buffer_size / 1024) * 1024); - - LOG("stream_buffer_size = %u\n", stream_buffer_size); - - // create the buffers - m_bytes_per_sample = stream_format.nBlockAlign; - m_stream_buffer_in = 0; - result = create_buffers(stream_buffer_size, stream_format); - if (result != DS_OK) - goto error; - } - - // start playing - result = m_stream_buffer.play_looping(); - if (result != DS_OK) - { - osd_printf_error("Error playing: %08x\n", result); - goto error; - } - return DS_OK; - - // error handling -error: - destroy_buffers(); - dsound_kill(); - return result; -} - - -//============================================================ -// dsound_kill -//============================================================ - -void sound_direct_sound::dsound_kill() -{ - // release the object - m_dsound.Reset(); -} - - -//============================================================ -// create_buffers -//============================================================ - -HRESULT sound_direct_sound::create_buffers(DWORD size, WAVEFORMATEX &format) -{ - assert(m_dsound); - assert(!m_primary_buffer); - assert(!m_stream_buffer); - HRESULT result; - - // create the primary buffer - result = m_primary_buffer.create(m_dsound.Get()); - if (result != DS_OK) - { - osd_printf_error("Error creating primary DirectSound buffer: %08x\n", result); - goto error; - } - - // attempt to set the primary format - result = m_primary_buffer.set_format(format); - if (result != DS_OK) - { - osd_printf_error("Error setting primary DirectSound buffer format: %08x\n", result); - goto error; - } - - // log the primary format - WAVEFORMATEX primary_format; - result = m_primary_buffer.get_format(primary_format); - if (result != DS_OK) - { - osd_printf_error("Error getting primary DirectSound buffer format: %08x\n", result); - goto error; - } - osd_printf_verbose( - "DirectSound: Primary buffer: %d Hz, %d bits, %d channels\n", - primary_format.nSamplesPerSec, - primary_format.wBitsPerSample, - primary_format.nChannels); - - // create the stream buffer - result = m_stream_buffer.create(m_dsound.Get(), size, format); - if (result != DS_OK) - { - osd_printf_error("Error creating DirectSound stream buffer: %08x\n", result); - goto error; - } - - // clear the buffer - result = m_stream_buffer.clear(); - if (result != DS_OK) - { - osd_printf_error("Error locking DirectSound stream buffer: %08x\n", result); - goto error; - } - - return DS_OK; - - // error handling -error: - destroy_buffers(); - return result; -} - - -//============================================================ -// destroy_buffers -//============================================================ - -void sound_direct_sound::destroy_buffers() -{ - // stop any playback - if (m_stream_buffer) - m_stream_buffer.stop(); - - // release the stream buffer - m_stream_buffer.release(); - - // release the primary buffer - m_primary_buffer.release(); -} - -} // anonymous namespace - -} // namespace osd - - -#else // defined(OSD_WINDOWS) || defined(SDLMAME_WIN32) - -namespace osd { namespace { MODULE_NOT_SUPPORTED(sound_direct_sound, OSD_SOUND_PROVIDER, "dsound") } } - -#endif // defined(OSD_WINDOWS) || defined(SDLMAME_WIN32) - - -MODULE_DEFINITION(SOUND_DSOUND, osd::sound_direct_sound) diff --git a/src/osd/modules/sound/sound_module.cpp b/src/osd/modules/sound/sound_module.cpp index 7188f6a6741..af6d893e441 100644 --- a/src/osd/modules/sound/sound_module.cpp +++ b/src/osd/modules/sound/sound_module.cpp @@ -38,11 +38,11 @@ osd::audio_info sound_module::get_information() return result; } -sound_module::abuffer::abuffer(uint32_t channels) : m_channels(channels), m_used_buffers(0), m_last_sample(channels, 0) +sound_module::abuffer::abuffer(uint32_t channels) noexcept : m_channels(channels), m_used_buffers(0), m_last_sample(channels, 0) { } -void sound_module::abuffer::get(int16_t *data, uint32_t samples) +void sound_module::abuffer::get(int16_t *data, uint32_t samples) noexcept { uint32_t pos = 0; while(pos != samples) { @@ -99,7 +99,7 @@ void sound_module::abuffer::push(const int16_t *data, uint32_t samples) } } -uint32_t sound_module::abuffer::available() const +uint32_t sound_module::abuffer::available() const noexcept { uint32_t result = 0; for(uint32_t i = 0; m_used_buffers > i; ++i) @@ -107,7 +107,7 @@ uint32_t sound_module::abuffer::available() const return result; } -inline void sound_module::abuffer::pop_buffer() +inline void sound_module::abuffer::pop_buffer() noexcept { assert(m_used_buffers); if(--m_used_buffers) { diff --git a/src/osd/modules/sound/sound_module.h b/src/osd/modules/sound/sound_module.h index 7f8b7efe2b7..d40eade3f0b 100644 --- a/src/osd/modules/sound/sound_module.h +++ b/src/osd/modules/sound/sound_module.h @@ -38,25 +38,25 @@ public: protected: class abuffer { public: - abuffer(uint32_t channels); - void get(int16_t *data, uint32_t samples); + abuffer(uint32_t channels) noexcept; + void get(int16_t *data, uint32_t samples) noexcept; void push(const int16_t *data, uint32_t samples); - uint32_t channels() const { return m_channels; } - uint32_t available() const; + uint32_t channels() const noexcept { return m_channels; } + uint32_t available() const noexcept; private: struct buffer { uint32_t m_cpos; std::vector m_data; - buffer() = default; + buffer() noexcept = default; buffer(const buffer &) = default; - buffer(buffer &&) = default; + buffer(buffer &&) noexcept = default; buffer &operator=(const buffer &) = default; - buffer &operator=(buffer &&) = default; + buffer &operator=(buffer &&) noexcept = default; }; - void pop_buffer(); + void pop_buffer() noexcept; buffer &push_buffer(); uint32_t m_channels; diff --git a/src/osd/modules/sound/xaudio2_sound.cpp b/src/osd/modules/sound/xaudio2_sound.cpp index 2a5462a47f4..59ec934e309 100644 --- a/src/osd/modules/sound/xaudio2_sound.cpp +++ b/src/osd/modules/sound/xaudio2_sound.cpp @@ -23,10 +23,14 @@ #include "strconv.h" #include "windows/winutil.h" +// MAME utility library +#include "util/corealloc.h" + // stdlib includes #include #include #include +#include #include #include #include @@ -129,9 +133,10 @@ using x_audio_2_ptr = Microsoft::WRL::ComPtr; class bufferpool { private: + std::vector > m_pool; + CRITICAL_SECTION m_critical_section; int m_initial; int m_buffersize; - std::queue> m_queue; public: // constructor @@ -139,28 +144,33 @@ public: m_initial(capacity), m_buffersize(bufferSize) { + InitializeCriticalSectionAndSpinCount(&m_critical_section, 4096); + + m_pool.reserve(m_initial * 2); for (int i = 0; i < m_initial; i++) - { - auto newBuffer = std::make_unique(m_buffersize); - memset(newBuffer.get(), 0, m_buffersize); - m_queue.push(std::move(newBuffer)); - } + m_pool.emplace_back(util::make_unique_clear(m_buffersize)); + } + + ~bufferpool() + { + DeleteCriticalSection(&m_critical_section); } // get next buffer element from the pool std::unique_ptr next() { std::unique_ptr next_buffer; - if (!m_queue.empty()) + + EnterCriticalSection(&m_critical_section); + if (!m_pool.empty()) { - next_buffer = std::move(m_queue.front()); - m_queue.pop(); - } - else - { - next_buffer.reset(new BYTE[m_buffersize]); - memset(next_buffer.get(), 0, m_buffersize); + next_buffer = std::move(m_pool.back()); + m_pool.pop_back(); } + LeaveCriticalSection(&m_critical_section); + + if (!next_buffer) + next_buffer = util::make_unique_clear(m_buffersize); return next_buffer; } @@ -170,7 +180,17 @@ public: { assert(buffer); memset(buffer.get(), 0, m_buffersize); - m_queue.push(std::move(buffer)); + EnterCriticalSection(&m_critical_section); + try + { + m_pool.emplace_back(std::move(buffer)); + LeaveCriticalSection(&m_critical_section); + } + catch (...) + { + LeaveCriticalSection(&m_critical_section); + throw; + } } }; @@ -275,7 +295,7 @@ private: unsigned const m_sample_bytes; unsigned m_buffer_count; uint32_t m_write_position; - bool m_need_update; + std::atomic m_need_update; bool m_underflowing; }; @@ -972,20 +992,19 @@ void sound_xaudio2::voice_info::update(int16_t const *buffer, int samples_this_f uint32_t bytes_left = bytes_this_frame; while (bytes_left) { - uint32_t const chunk = std::min(uint32_t(m_buffer_size), bytes_left); - - // roll buffer if the chunk won't fit - if ((m_write_position + chunk) >= m_buffer_size) - roll_buffer(); - - // copy sample data - memcpy(&m_current_buffer[m_write_position], buffer, chunk); + // copy as much data as we can into the current buffer + uint32_t const chunk = std::min(uint32_t(m_buffer_size - m_write_position), bytes_left); + std::memcpy(&m_current_buffer[m_write_position], buffer, chunk); m_write_position += chunk; buffer += chunk / 2; bytes_left -= chunk; + + // roll buffer if it's full + if (m_write_position >= m_buffer_size) + roll_buffer(); } - m_need_update = true; + m_need_update.store(true, std::memory_order_relaxed); SetEvent(m_host.m_need_update_event.get()); } @@ -995,7 +1014,7 @@ void sound_xaudio2::voice_info::update(int16_t const *buffer, int samples_this_f void sound_xaudio2::voice_info::submit_if_needed() { - if (!m_need_update) + if (!m_need_update.load(std::memory_order_relaxed)) return; m_need_update = false; @@ -1008,15 +1027,11 @@ void sound_xaudio2::voice_info::submit_if_needed() if ((1 <= state.BuffersQueued) && m_buffer_queue.empty()) return; - // We do however want to achieve some kind of minimal latency, so if the queued buffers - // are greater than 2, flush them to re-sync the audio - if (2 < state.BuffersQueued) - { - voice->FlushSourceBuffers(); - m_host.m_overflows.fetch_add(1, std::memory_order_relaxed); - } + // don't keep more than two buffers queued in the source voice + if (2 <= state.BuffersQueued) + return; - if (m_buffer_queue.empty()) + if (m_buffer_queue.empty() && m_write_position) roll_buffer(); if (!m_buffer_queue.empty()) @@ -1043,7 +1058,7 @@ void sound_xaudio2::voice_info::stop() while (!m_buffer_queue.empty()) m_buffer_queue.pop(); m_write_position = 0; - m_need_update = false; + m_need_update.store(false, std::memory_order_relaxed); if (voice) { voice->Stop(0, XAUDIO2_COMMIT_NOW); @@ -1058,8 +1073,7 @@ void sound_xaudio2::voice_info::stop() void sound_xaudio2::voice_info::roll_buffer() { // don't queue an empty buffer - if (!m_write_position) - return; + assert(m_write_position); // queue the current buffer xaudio2_buffer buf; @@ -1088,7 +1102,7 @@ void sound_xaudio2::voice_info::roll_buffer() // submit_buffer //============================================================ -void sound_xaudio2::voice_info::submit_buffer(std::unique_ptr &&data, DWORD length) const +inline void sound_xaudio2::voice_info::submit_buffer(std::unique_ptr &&data, DWORD length) const { assert(length); @@ -1097,20 +1111,21 @@ void sound_xaudio2::voice_info::submit_buffer(std::unique_ptr &&data, D buf.pAudioData = data.get(); buf.PlayBegin = 0; buf.PlayLength = length / m_sample_bytes; - buf.Flags = XAUDIO2_END_OF_STREAM; + buf.Flags = XAUDIO2_END_OF_STREAM; // this flag just suppresses underrun warnings buf.pContext = data.get(); - HRESULT result; - if (FAILED(result = voice->SubmitSourceBuffer(&buf))) + HRESULT const result = voice->SubmitSourceBuffer(&buf); + if (SUCCEEDED(result)) + { + // If we succeeded, relinquish the buffer allocation to the XAudio2 runtime + // The buffer will be freed on the OnBufferCompleted callback + data.release(); + } + else { osd_printf_verbose("Sound: XAudio2 failed to submit source buffer (non-fatal). Error: 0x%X\n", result); m_buffer_pool->return_to_pool(std::move(data)); - return; } - - // If we succeeded, relinquish the buffer allocation to the XAudio2 runtime - // The buffer will be freed on the OnBufferCompleted callback - data.release(); } //============================================================ @@ -1136,14 +1151,11 @@ void sound_xaudio2::voice_info::OnVoiceProcessingPassStart(UINT32 BytesRequired) void sound_xaudio2::voice_info::OnBufferEnd(void *pBufferContext) noexcept { - // FIXME: this is an XAudio2 callback, so it should really use a more fine-grained lock BYTE *const completed_buffer = static_cast(pBufferContext); - std::lock_guard voice_lock(m_host.m_voice_mutex); - if (completed_buffer) m_buffer_pool->return_to_pool(std::unique_ptr(completed_buffer)); - m_need_update = true; + m_need_update.store(true, std::memory_order_relaxed); SetEvent(m_host.m_need_update_event.get()); }