-sound: Removed DirectSound sound module.

-sound/xaudio2_sound.cpp: Use more fine-grained locking in voice
 callbacks, simplified further simplified buffer queueing.
This commit is contained in:
Vas Crabb 2025-05-29 16:20:33 +10:00
parent 25dd68334f
commit a0412a6387
7 changed files with 82 additions and 639 deletions

View File

@ -2970,7 +2970,7 @@ Core Sound Options
.. _mame-commandline-sound:
**-sound** *<wasapi | xaudio2 | dsound | coreaudio | pipewire | pulse | sdl | portaudio | none>*
**-sound** *<wasapi | xaudio2 | coreaudio | pipewire | pulse | sdl | portaudio | none>*
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.

View File

@ -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",

View File

@ -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);

View File

@ -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 <SDL2/SDL_syswm.h>
#else
#include "winmain.h"
#include "window.h"
#endif
#include <utility>
// standard windows headers
#include <windows.h>
#include <mmreg.h>
#include <mmsystem.h>
#include <dsound.h>
#include <wrl/client.h>
//============================================================
// 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<IDirectSoundBuffer> 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<IDirectSound> 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<sdl_window_info &>(*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<win_window_info &>(*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)

View File

@ -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) {

View File

@ -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<int16_t> 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;

View File

@ -23,10 +23,14 @@
#include "strconv.h"
#include "windows/winutil.h"
// MAME utility library
#include "util/corealloc.h"
// stdlib includes
#include <algorithm>
#include <atomic>
#include <cassert>
#include <cstring>
#include <condition_variable>
#include <iterator>
#include <memory>
@ -129,9 +133,10 @@ using x_audio_2_ptr = Microsoft::WRL::ComPtr<IXAudio2>;
class bufferpool
{
private:
std::vector<std::unique_ptr<BYTE []> > m_pool;
CRITICAL_SECTION m_critical_section;
int m_initial;
int m_buffersize;
std::queue<std::unique_ptr<BYTE []>> 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<BYTE []>(m_buffersize);
memset(newBuffer.get(), 0, m_buffersize);
m_queue.push(std::move(newBuffer));
}
m_pool.emplace_back(util::make_unique_clear<BYTE []>(m_buffersize));
}
~bufferpool()
{
DeleteCriticalSection(&m_critical_section);
}
// get next buffer element from the pool
std::unique_ptr<BYTE []> next()
{
std::unique_ptr<BYTE []> 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<BYTE []>(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<bool> 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<BYTE []> &&data, DWORD length) const
inline void sound_xaudio2::voice_info::submit_buffer(std::unique_ptr<BYTE []> &&data, DWORD length) const
{
assert(length);
@ -1097,20 +1111,21 @@ void sound_xaudio2::voice_info::submit_buffer(std::unique_ptr<BYTE []> &&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<BYTE *>(pBufferContext);
std::lock_guard voice_lock(m_host.m_voice_mutex);
if (completed_buffer)
m_buffer_pool->return_to_pool(std::unique_ptr<BYTE []>(completed_buffer));
m_need_update = true;
m_need_update.store(true, std::memory_order_relaxed);
SetEvent(m_host.m_need_update_event.get());
}