output/winmm: migrate from class Error to C++ exceptions
This commit is contained in:
parent
b45ea66175
commit
30bb3f1fcb
@ -23,8 +23,7 @@
|
|||||||
#include "pcm/PcmBuffer.hxx"
|
#include "pcm/PcmBuffer.hxx"
|
||||||
#include "mixer/MixerList.hxx"
|
#include "mixer/MixerList.hxx"
|
||||||
#include "fs/AllocatedPath.hxx"
|
#include "fs/AllocatedPath.hxx"
|
||||||
#include "util/Error.hxx"
|
#include "util/RuntimeError.hxx"
|
||||||
#include "util/Domain.hxx"
|
|
||||||
#include "util/Macros.hxx"
|
#include "util/Macros.hxx"
|
||||||
#include "util/StringCompare.hxx"
|
#include "util/StringCompare.hxx"
|
||||||
|
|
||||||
@ -40,7 +39,7 @@ struct WinmmBuffer {
|
|||||||
struct WinmmOutput {
|
struct WinmmOutput {
|
||||||
AudioOutput base;
|
AudioOutput base;
|
||||||
|
|
||||||
UINT device_id;
|
const UINT device_id;
|
||||||
HWAVEOUT handle;
|
HWAVEOUT handle;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -52,22 +51,18 @@ struct WinmmOutput {
|
|||||||
WinmmBuffer buffers[8];
|
WinmmBuffer buffers[8];
|
||||||
unsigned next_buffer;
|
unsigned next_buffer;
|
||||||
|
|
||||||
WinmmOutput()
|
WinmmOutput(const ConfigBlock &block);
|
||||||
:base(winmm_output_plugin) {}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr Domain winmm_output_domain("winmm_output");
|
static std::runtime_error
|
||||||
|
MakeWaveOutError(MMRESULT result, const char *prefix)
|
||||||
static void
|
|
||||||
SetWaveOutError(Error &error, MMRESULT result, const char *prefix)
|
|
||||||
{
|
{
|
||||||
char buffer[256];
|
char buffer[256];
|
||||||
if (waveOutGetErrorTextA(result, buffer,
|
if (waveOutGetErrorTextA(result, buffer,
|
||||||
ARRAY_SIZE(buffer)) == MMSYSERR_NOERROR)
|
ARRAY_SIZE(buffer)) == MMSYSERR_NOERROR)
|
||||||
error.Format(winmm_output_domain, int(result),
|
return FormatRuntimeError("%s: %s", prefix, buffer);
|
||||||
"%s: %s", prefix, buffer);
|
|
||||||
else
|
else
|
||||||
error.Set(winmm_output_domain, int(result), prefix);
|
return std::runtime_error(prefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
HWAVEOUT
|
HWAVEOUT
|
||||||
@ -82,14 +77,12 @@ winmm_output_test_default_device(void)
|
|||||||
return waveOutGetNumDevs() > 0;
|
return waveOutGetNumDevs() > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static UINT
|
||||||
get_device_id(const char *device_name, UINT *device_id, Error &error)
|
get_device_id(const char *device_name)
|
||||||
{
|
{
|
||||||
/* if device is not specified use wave mapper */
|
/* if device is not specified use wave mapper */
|
||||||
if (device_name == nullptr) {
|
if (device_name == nullptr)
|
||||||
*device_id = WAVE_MAPPER;
|
return WAVE_MAPPER;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
UINT numdevs = waveOutGetNumDevs();
|
UINT numdevs = waveOutGetNumDevs();
|
||||||
|
|
||||||
@ -97,22 +90,16 @@ get_device_id(const char *device_name, UINT *device_id, Error &error)
|
|||||||
char *endptr;
|
char *endptr;
|
||||||
UINT id = strtoul(device_name, &endptr, 0);
|
UINT id = strtoul(device_name, &endptr, 0);
|
||||||
if (endptr > device_name && *endptr == 0) {
|
if (endptr > device_name && *endptr == 0) {
|
||||||
if (id >= numdevs) {
|
if (id >= numdevs)
|
||||||
error.Format(winmm_output_domain,
|
throw FormatRuntimeError("device \"%s\" is not found",
|
||||||
"device \"%s\" is not found",
|
device_name);
|
||||||
device_name);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
*device_id = id;
|
return id;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* check for device name */
|
/* check for device name */
|
||||||
const AllocatedPath device_name_fs =
|
const AllocatedPath device_name_fs =
|
||||||
AllocatedPath::FromUTF8(device_name, error);
|
AllocatedPath::FromUTF8Throw(device_name);
|
||||||
if (device_name_fs.IsNull())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
for (UINT i = 0; i < numdevs; i++) {
|
for (UINT i = 0; i < numdevs; i++) {
|
||||||
WAVEOUTCAPS caps;
|
WAVEOUTCAPS caps;
|
||||||
@ -121,33 +108,23 @@ get_device_id(const char *device_name, UINT *device_id, Error &error)
|
|||||||
continue;
|
continue;
|
||||||
/* szPname is only 32 chars long, so it is often truncated.
|
/* szPname is only 32 chars long, so it is often truncated.
|
||||||
Use partial match to work around this. */
|
Use partial match to work around this. */
|
||||||
if (StringStartsWith(device_name_fs.c_str(), caps.szPname)) {
|
if (StringStartsWith(device_name_fs.c_str(), caps.szPname))
|
||||||
*device_id = i;
|
return i;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
error.Format(winmm_output_domain,
|
throw FormatRuntimeError("device \"%s\" is not found", device_name);
|
||||||
"device \"%s\" is not found", device_name);
|
}
|
||||||
return false;
|
|
||||||
|
WinmmOutput::WinmmOutput(const ConfigBlock &block)
|
||||||
|
:base(winmm_output_plugin, block),
|
||||||
|
device_id(get_device_id(block.GetBlockValue("device")))
|
||||||
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
static AudioOutput *
|
static AudioOutput *
|
||||||
winmm_output_init(const ConfigBlock &block, Error &error)
|
winmm_output_init(const ConfigBlock &block, Error &)
|
||||||
{
|
{
|
||||||
WinmmOutput *wo = new WinmmOutput();
|
return &(new WinmmOutput(block))->base;
|
||||||
if (!wo->base.Configure(block, error)) {
|
|
||||||
delete wo;
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *device = block.GetBlockValue("device");
|
|
||||||
if (!get_device_id(device, &wo->device_id, error)) {
|
|
||||||
delete wo;
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return &wo->base;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -159,16 +136,13 @@ winmm_output_finish(AudioOutput *ao)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
winmm_output_open(AudioOutput *ao, AudioFormat &audio_format,
|
winmm_output_open(AudioOutput *ao, AudioFormat &audio_format, Error &)
|
||||||
Error &error)
|
|
||||||
{
|
{
|
||||||
WinmmOutput *wo = (WinmmOutput *)ao;
|
WinmmOutput *wo = (WinmmOutput *)ao;
|
||||||
|
|
||||||
wo->event = CreateEvent(nullptr, false, false, nullptr);
|
wo->event = CreateEvent(nullptr, false, false, nullptr);
|
||||||
if (wo->event == nullptr) {
|
if (wo->event == nullptr)
|
||||||
error.Set(winmm_output_domain, "CreateEvent() failed");
|
throw std::runtime_error("CreateEvent() failed");
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (audio_format.format) {
|
switch (audio_format.format) {
|
||||||
case SampleFormat::S8:
|
case SampleFormat::S8:
|
||||||
@ -202,8 +176,7 @@ winmm_output_open(AudioOutput *ao, AudioFormat &audio_format,
|
|||||||
(DWORD_PTR)wo->event, 0, CALLBACK_EVENT);
|
(DWORD_PTR)wo->event, 0, CALLBACK_EVENT);
|
||||||
if (result != MMSYSERR_NOERROR) {
|
if (result != MMSYSERR_NOERROR) {
|
||||||
CloseHandle(wo->event);
|
CloseHandle(wo->event);
|
||||||
SetWaveOutError(error, result, "waveOutOpen() failed");
|
throw MakeWaveOutError(result, "waveOutOpen() failed");
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (unsigned i = 0; i < ARRAY_SIZE(wo->buffers); ++i) {
|
for (unsigned i = 0; i < ARRAY_SIZE(wo->buffers); ++i) {
|
||||||
@ -231,10 +204,9 @@ winmm_output_close(AudioOutput *ao)
|
|||||||
/**
|
/**
|
||||||
* Copy data into a buffer, and prepare the wave header.
|
* Copy data into a buffer, and prepare the wave header.
|
||||||
*/
|
*/
|
||||||
static bool
|
static void
|
||||||
winmm_set_buffer(WinmmOutput *wo, WinmmBuffer *buffer,
|
winmm_set_buffer(WinmmOutput *wo, WinmmBuffer *buffer,
|
||||||
const void *data, size_t size,
|
const void *data, size_t size)
|
||||||
Error &error)
|
|
||||||
{
|
{
|
||||||
void *dest = buffer->buffer.Get(size);
|
void *dest = buffer->buffer.Get(size);
|
||||||
assert(dest != nullptr);
|
assert(dest != nullptr);
|
||||||
@ -247,37 +219,30 @@ winmm_set_buffer(WinmmOutput *wo, WinmmBuffer *buffer,
|
|||||||
|
|
||||||
MMRESULT result = waveOutPrepareHeader(wo->handle, &buffer->hdr,
|
MMRESULT result = waveOutPrepareHeader(wo->handle, &buffer->hdr,
|
||||||
sizeof(buffer->hdr));
|
sizeof(buffer->hdr));
|
||||||
if (result != MMSYSERR_NOERROR) {
|
if (result != MMSYSERR_NOERROR)
|
||||||
SetWaveOutError(error, result,
|
throw MakeWaveOutError(result,
|
||||||
"waveOutPrepareHeader() failed");
|
"waveOutPrepareHeader() failed");
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wait until the buffer is finished.
|
* Wait until the buffer is finished.
|
||||||
*/
|
*/
|
||||||
static bool
|
static void
|
||||||
winmm_drain_buffer(WinmmOutput *wo, WinmmBuffer *buffer,
|
winmm_drain_buffer(WinmmOutput *wo, WinmmBuffer *buffer)
|
||||||
Error &error)
|
|
||||||
{
|
{
|
||||||
if ((buffer->hdr.dwFlags & WHDR_DONE) == WHDR_DONE)
|
if ((buffer->hdr.dwFlags & WHDR_DONE) == WHDR_DONE)
|
||||||
/* already finished */
|
/* already finished */
|
||||||
return true;
|
return;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
MMRESULT result = waveOutUnprepareHeader(wo->handle,
|
MMRESULT result = waveOutUnprepareHeader(wo->handle,
|
||||||
&buffer->hdr,
|
&buffer->hdr,
|
||||||
sizeof(buffer->hdr));
|
sizeof(buffer->hdr));
|
||||||
if (result == MMSYSERR_NOERROR)
|
if (result == MMSYSERR_NOERROR)
|
||||||
return true;
|
return;
|
||||||
else if (result != WAVERR_STILLPLAYING) {
|
else if (result != WAVERR_STILLPLAYING)
|
||||||
SetWaveOutError(error, result,
|
throw MakeWaveOutError(result,
|
||||||
"waveOutUnprepareHeader() failed");
|
"waveOutUnprepareHeader() failed");
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* wait some more */
|
/* wait some more */
|
||||||
WaitForSingleObject(wo->event, INFINITE);
|
WaitForSingleObject(wo->event, INFINITE);
|
||||||
@ -285,15 +250,14 @@ winmm_drain_buffer(WinmmOutput *wo, WinmmBuffer *buffer,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static size_t
|
static size_t
|
||||||
winmm_output_play(AudioOutput *ao, const void *chunk, size_t size, Error &error)
|
winmm_output_play(AudioOutput *ao, const void *chunk, size_t size, Error &)
|
||||||
{
|
{
|
||||||
WinmmOutput *wo = (WinmmOutput *)ao;
|
WinmmOutput *wo = (WinmmOutput *)ao;
|
||||||
|
|
||||||
/* get the next buffer from the ring and prepare it */
|
/* get the next buffer from the ring and prepare it */
|
||||||
WinmmBuffer *buffer = &wo->buffers[wo->next_buffer];
|
WinmmBuffer *buffer = &wo->buffers[wo->next_buffer];
|
||||||
if (!winmm_drain_buffer(wo, buffer, error) ||
|
winmm_drain_buffer(wo, buffer);
|
||||||
!winmm_set_buffer(wo, buffer, chunk, size, error))
|
winmm_set_buffer(wo, buffer, chunk, size);
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* enqueue the buffer */
|
/* enqueue the buffer */
|
||||||
MMRESULT result = waveOutWrite(wo->handle, &buffer->hdr,
|
MMRESULT result = waveOutWrite(wo->handle, &buffer->hdr,
|
||||||
@ -301,8 +265,7 @@ winmm_output_play(AudioOutput *ao, const void *chunk, size_t size, Error &error)
|
|||||||
if (result != MMSYSERR_NOERROR) {
|
if (result != MMSYSERR_NOERROR) {
|
||||||
waveOutUnprepareHeader(wo->handle, &buffer->hdr,
|
waveOutUnprepareHeader(wo->handle, &buffer->hdr,
|
||||||
sizeof(buffer->hdr));
|
sizeof(buffer->hdr));
|
||||||
SetWaveOutError(error, result, "waveOutWrite() failed");
|
throw MakeWaveOutError(result, "waveOutWrite() failed");
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* mark our buffer as "used" */
|
/* mark our buffer as "used" */
|
||||||
@ -312,18 +275,14 @@ winmm_output_play(AudioOutput *ao, const void *chunk, size_t size, Error &error)
|
|||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static void
|
||||||
winmm_drain_all_buffers(WinmmOutput *wo, Error &error)
|
winmm_drain_all_buffers(WinmmOutput *wo)
|
||||||
{
|
{
|
||||||
for (unsigned i = wo->next_buffer; i < ARRAY_SIZE(wo->buffers); ++i)
|
for (unsigned i = wo->next_buffer; i < ARRAY_SIZE(wo->buffers); ++i)
|
||||||
if (!winmm_drain_buffer(wo, &wo->buffers[i], error))
|
winmm_drain_buffer(wo, &wo->buffers[i]);
|
||||||
return false;
|
|
||||||
|
|
||||||
for (unsigned i = 0; i < wo->next_buffer; ++i)
|
for (unsigned i = 0; i < wo->next_buffer; ++i)
|
||||||
if (!winmm_drain_buffer(wo, &wo->buffers[i], error))
|
winmm_drain_buffer(wo, &wo->buffers[i]);
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -343,8 +302,12 @@ winmm_output_drain(AudioOutput *ao)
|
|||||||
{
|
{
|
||||||
WinmmOutput *wo = (WinmmOutput *)ao;
|
WinmmOutput *wo = (WinmmOutput *)ao;
|
||||||
|
|
||||||
if (!winmm_drain_all_buffers(wo, IgnoreError()))
|
try {
|
||||||
|
winmm_drain_all_buffers(wo);
|
||||||
|
} catch (...) {
|
||||||
winmm_stop(wo);
|
winmm_stop(wo);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
Loading…
Reference in New Issue
Block a user