mirror of
https://github.com/holub/mame
synced 2025-07-03 17:08:39 +03:00
-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:
parent
25dd68334f
commit
a0412a6387
@ -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.
|
||||
|
||||
|
@ -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",
|
||||
|
@ -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);
|
||||
|
@ -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)
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user