output/winmm: migrate from class Error to C++ exceptions

This commit is contained in:
Max Kellermann 2016-11-07 07:42:18 +01:00
parent b45ea66175
commit 30bb3f1fcb

View File

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