output/winmm: migrate from class Error to C++ exceptions
This commit is contained in:
		| @@ -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 | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Max Kellermann
					Max Kellermann