pcm/Convert: migrate from class Error to C++ exceptions
This commit is contained in:
		| @@ -520,10 +520,7 @@ try { | ||||
| 	archive_plugin_init_all(); | ||||
| #endif | ||||
|  | ||||
| 	if (!pcm_convert_global_init(error)) { | ||||
| 		LogError(error); | ||||
| 		return EXIT_FAILURE; | ||||
| 	} | ||||
| 	pcm_convert_global_init(); | ||||
|  | ||||
| 	decoder_plugin_init_all(); | ||||
|  | ||||
|   | ||||
| @@ -73,11 +73,12 @@ decoder_initialized(Decoder &decoder, | ||||
|  | ||||
| 		decoder.convert = new PcmConvert(); | ||||
|  | ||||
| 		Error error; | ||||
| 		if (!decoder.convert->Open(dc.in_audio_format, | ||||
| 					   dc.out_audio_format, | ||||
| 					   error)) | ||||
| 			decoder.error = std::make_exception_ptr(std::move(error)); | ||||
| 		try { | ||||
| 			decoder.convert->Open(dc.in_audio_format, | ||||
| 					      dc.out_audio_format); | ||||
| 		} catch (...) { | ||||
| 			decoder.error = std::current_exception(); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	const ScopeLock protect(dc.mutex); | ||||
| @@ -484,19 +485,17 @@ decoder_data(Decoder &decoder, | ||||
| 	if (decoder.convert != nullptr) { | ||||
| 		assert(dc.in_audio_format != dc.out_audio_format); | ||||
|  | ||||
| 		Error error; | ||||
| 		auto result = decoder.convert->Convert({data, length}, | ||||
| 						       error); | ||||
| 		if (data == nullptr) { | ||||
| 		try { | ||||
| 			auto result = decoder.convert->Convert({data, length}); | ||||
| 			data = result.data; | ||||
| 			length = result.size; | ||||
| 		} catch (const std::runtime_error &e) { | ||||
| 			/* the PCM conversion has failed - stop | ||||
| 			   playback, since we have no better way to | ||||
| 			   bail out */ | ||||
| 			LogError(error); | ||||
| 			LogError(e); | ||||
| 			return DecoderCommand::STOP; | ||||
| 		} | ||||
|  | ||||
| 		data = result.data; | ||||
| 		length = result.size; | ||||
| 	} else { | ||||
| 		assert(dc.in_audio_format == dc.out_audio_format); | ||||
| 	} | ||||
|   | ||||
| @@ -25,7 +25,6 @@ | ||||
| #include "pcm/PcmConvert.hxx" | ||||
| #include "util/Manual.hxx" | ||||
| #include "util/ConstBuffer.hxx" | ||||
| #include "util/Error.hxx" | ||||
| #include "AudioFormat.hxx" | ||||
| #include "poison.h" | ||||
|  | ||||
| @@ -88,9 +87,7 @@ ConvertFilter::Set(const AudioFormat &_out_audio_format) | ||||
| 		/* optimized special case: no-op */ | ||||
| 		return; | ||||
|  | ||||
| 	Error error; | ||||
| 	if (!state.Open(in_audio_format, _out_audio_format, error)) | ||||
| 		throw std::runtime_error(error.GetMessage()); | ||||
| 	state.Open(in_audio_format, _out_audio_format); | ||||
|  | ||||
| 	out_audio_format = _out_audio_format; | ||||
| } | ||||
| @@ -125,12 +122,7 @@ ConvertFilter::FilterPCM(ConstBuffer<void> src) | ||||
| 		/* optimized special case: no-op */ | ||||
| 		return src; | ||||
|  | ||||
| 	Error error; | ||||
| 	auto result = state.Convert(src, error); | ||||
| 	if (result.IsNull()) | ||||
| 		throw std::runtime_error(error.GetMessage()); | ||||
|  | ||||
| 	return result; | ||||
| 	return state.Convert(src); | ||||
| } | ||||
|  | ||||
| const struct filter_plugin convert_filter_plugin = { | ||||
|   | ||||
| @@ -76,9 +76,7 @@ public: | ||||
| 		 mixer(_mixer), base(_base), mode(REPLAY_GAIN_OFF) { | ||||
| 		info.Clear(); | ||||
|  | ||||
| 		Error error; | ||||
| 		if (!pv.Open(out_audio_format.format, error)) | ||||
| 			throw std::runtime_error(error.GetMessage()); | ||||
| 		pv.Open(out_audio_format.format); | ||||
| 	} | ||||
|  | ||||
| 	void SetInfo(const ReplayGainInfo *_info) { | ||||
|   | ||||
| @@ -25,7 +25,6 @@ | ||||
| #include "pcm/Volume.hxx" | ||||
| #include "AudioFormat.hxx" | ||||
| #include "util/ConstBuffer.hxx" | ||||
| #include "util/Error.hxx" | ||||
|  | ||||
| #include <stdexcept> | ||||
|  | ||||
| @@ -35,9 +34,7 @@ class VolumeFilter final : public Filter { | ||||
| public: | ||||
| 	explicit VolumeFilter(const AudioFormat &audio_format) | ||||
| 		:Filter(audio_format) { | ||||
| 		Error error; | ||||
| 		if (!pv.Open(out_audio_format.format, error)) | ||||
| 			throw std::runtime_error(error.GetMessage()); | ||||
| 		pv.Open(out_audio_format.format); | ||||
| 	} | ||||
|  | ||||
| 	unsigned GetVolume() const { | ||||
|   | ||||
| @@ -22,14 +22,13 @@ | ||||
| #include "PcmChannels.hxx" | ||||
| #include "Domain.hxx" | ||||
| #include "util/ConstBuffer.hxx" | ||||
| #include "util/Error.hxx" | ||||
| #include "util/RuntimeError.hxx" | ||||
|  | ||||
| #include <assert.h> | ||||
|  | ||||
| bool | ||||
| void | ||||
| PcmChannelsConverter::Open(SampleFormat _format, | ||||
| 			   unsigned _src_channels, unsigned _dest_channels, | ||||
| 			   gcc_unused Error &error) | ||||
| 			   unsigned _src_channels, unsigned _dest_channels) | ||||
| { | ||||
| 	assert(_format != SampleFormat::UNDEFINED); | ||||
|  | ||||
| @@ -41,16 +40,13 @@ PcmChannelsConverter::Open(SampleFormat _format, | ||||
| 		break; | ||||
|  | ||||
| 	default: | ||||
| 		error.Format(pcm_domain, | ||||
| 			     "PCM channel conversion for %s is not implemented", | ||||
| 			     sample_format_to_string(_format)); | ||||
| 		return false; | ||||
| 		throw FormatRuntimeError("PCM channel conversion for %s is not implemented", | ||||
| 					 sample_format_to_string(_format)); | ||||
| 	} | ||||
|  | ||||
| 	format = _format; | ||||
| 	src_channels = _src_channels; | ||||
| 	dest_channels = _dest_channels; | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| void | ||||
| @@ -62,7 +58,7 @@ PcmChannelsConverter::Close() | ||||
| } | ||||
|  | ||||
| ConstBuffer<void> | ||||
| PcmChannelsConverter::Convert(ConstBuffer<void> src, gcc_unused Error &error) | ||||
| PcmChannelsConverter::Convert(ConstBuffer<void> src) | ||||
| { | ||||
| 	switch (format) { | ||||
| 	case SampleFormat::UNDEFINED: | ||||
|   | ||||
| @@ -28,7 +28,6 @@ | ||||
| #include <assert.h> | ||||
| #endif | ||||
|  | ||||
| class Error; | ||||
| template<typename T> struct ConstBuffer; | ||||
|  | ||||
| /** | ||||
| @@ -53,15 +52,14 @@ public: | ||||
| 	/** | ||||
| 	 * Opens the object, prepare for Convert(). | ||||
| 	 * | ||||
| 	 * Throws std::runtime_error on error. | ||||
| 	 * | ||||
| 	 * @param format the sample format | ||||
| 	 * @param src_channels the number of source channels | ||||
| 	 * @param dest_channels the number of destination channels | ||||
| 	 * @param error location to store the error | ||||
| 	 * @return true on success | ||||
| 	 */ | ||||
| 	bool Open(SampleFormat format, | ||||
| 		  unsigned src_channels, unsigned dest_channels, | ||||
| 		  Error &error); | ||||
| 	void Open(SampleFormat format, | ||||
| 		  unsigned src_channels, unsigned dest_channels); | ||||
|  | ||||
| 	/** | ||||
| 	 * Closes the object.  After that, you may call Open() again. | ||||
| @@ -71,13 +69,13 @@ public: | ||||
| 	/** | ||||
| 	 * Convert a block of PCM data. | ||||
| 	 * | ||||
| 	 * Throws std::runtime_error on error. | ||||
| 	 * | ||||
| 	 * @param src the input buffer | ||||
| 	 * @param error location to store the error | ||||
| 	 * @return the destination buffer on success, | ||||
| 	 * ConstBuffer::Null() on error | ||||
| 	 * @return the destination buffer | ||||
| 	 */ | ||||
| 	gcc_pure | ||||
| 	ConstBuffer<void> Convert(ConstBuffer<void> src, Error &error); | ||||
| 	ConstBuffer<void> Convert(ConstBuffer<void> src); | ||||
| }; | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -25,7 +25,7 @@ | ||||
| #include "config/ConfigError.hxx" | ||||
| #include "config/Block.hxx" | ||||
| #include "config/Param.hxx" | ||||
| #include "util/Error.hxx" | ||||
| #include "util/RuntimeError.hxx" | ||||
|  | ||||
| #ifdef ENABLE_LIBSAMPLERATE | ||||
| #include "LibsamplerateResampler.hxx" | ||||
| @@ -112,7 +112,7 @@ MigrateResamplerConfig(const config_param *param, ConfigBlock &buffer) | ||||
| } | ||||
|  | ||||
| static const ConfigBlock * | ||||
| GetResamplerConfig(ConfigBlock &buffer, Error &error) | ||||
| GetResamplerConfig(ConfigBlock &buffer) | ||||
| { | ||||
| 	const auto *old_param = | ||||
| 		config_get_param(ConfigOption::SAMPLERATE_CONVERTER); | ||||
| @@ -120,49 +120,39 @@ GetResamplerConfig(ConfigBlock &buffer, Error &error) | ||||
| 	if (block == nullptr) | ||||
| 		return MigrateResamplerConfig(old_param, buffer); | ||||
|  | ||||
| 	if (old_param != nullptr) { | ||||
| 		error.Format(config_domain, | ||||
| 			     "Cannot use both 'resampler' (line %d) and 'samplerate_converter' (line %d)", | ||||
| 			     block->line, old_param->line); | ||||
| 		return nullptr; | ||||
| 	} | ||||
| 	if (old_param != nullptr) | ||||
| 		throw FormatRuntimeError("Cannot use both 'resampler' (line %d) and 'samplerate_converter' (line %d)", | ||||
| 					 block->line, old_param->line); | ||||
|  | ||||
| 	return block; | ||||
| } | ||||
|  | ||||
| bool | ||||
| pcm_resampler_global_init(Error &error) | ||||
| void | ||||
| pcm_resampler_global_init() | ||||
| { | ||||
| 	ConfigBlock buffer; | ||||
| 	const auto *block = GetResamplerConfig(buffer, error); | ||||
| 	if (block == nullptr) | ||||
| 		return false; | ||||
| 	const auto *block = GetResamplerConfig(buffer); | ||||
|  | ||||
| 	const char *plugin_name = block->GetBlockValue("plugin"); | ||||
| 	if (plugin_name == nullptr) { | ||||
| 		error.Format(config_domain, | ||||
| 			     "'plugin' missing in line %d", block->line); | ||||
| 		return false; | ||||
| 	} | ||||
| 	if (plugin_name == nullptr) | ||||
| 		throw FormatRuntimeError("'plugin' missing in line %d", | ||||
| 					 block->line); | ||||
|  | ||||
| 	if (strcmp(plugin_name, "internal") == 0) { | ||||
| 		selected_resampler = SelectedResampler::FALLBACK; | ||||
| 		return true; | ||||
| #ifdef ENABLE_SOXR | ||||
| 	} else if (strcmp(plugin_name, "soxr") == 0) { | ||||
| 		selected_resampler = SelectedResampler::SOXR; | ||||
| 		return pcm_resample_soxr_global_init(*block, error); | ||||
| 		pcm_resample_soxr_global_init(*block); | ||||
| #endif | ||||
| #ifdef ENABLE_LIBSAMPLERATE | ||||
| 	} else if (strcmp(plugin_name, "libsamplerate") == 0) { | ||||
| 		selected_resampler = SelectedResampler::LIBSAMPLERATE; | ||||
| 		return pcm_resample_lsr_global_init(*block, error); | ||||
| 		pcm_resample_lsr_global_init(*block); | ||||
| #endif | ||||
| 	} else { | ||||
| 		error.Format(config_domain, | ||||
| 			     "No such resampler plugin: %s", | ||||
| 			     plugin_name); | ||||
| 		return false; | ||||
| 		throw FormatRuntimeError("No such resampler plugin: %s", | ||||
| 					 plugin_name); | ||||
| 	} | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -22,11 +22,10 @@ | ||||
|  | ||||
| #include "check.h" | ||||
|  | ||||
| class Error; | ||||
| class PcmResampler; | ||||
|  | ||||
| bool | ||||
| pcm_resampler_global_init(Error &error); | ||||
| void | ||||
| pcm_resampler_global_init(); | ||||
|  | ||||
| /** | ||||
|  * Create a #PcmResampler instance from the implementation class | ||||
|   | ||||
| @@ -23,8 +23,7 @@ | ||||
| #include <assert.h> | ||||
|  | ||||
| AudioFormat | ||||
| FallbackPcmResampler::Open(AudioFormat &af, unsigned new_sample_rate, | ||||
| 			   gcc_unused Error &error) | ||||
| FallbackPcmResampler::Open(AudioFormat &af, unsigned new_sample_rate) | ||||
| { | ||||
| 	assert(af.IsValid()); | ||||
| 	assert(audio_valid_sample_rate(new_sample_rate)); | ||||
| @@ -116,7 +115,7 @@ pcm_resample_fallback_void(PcmBuffer &buffer, | ||||
| } | ||||
|  | ||||
| ConstBuffer<void> | ||||
| FallbackPcmResampler::Resample(ConstBuffer<void> src, gcc_unused Error &error) | ||||
| FallbackPcmResampler::Resample(ConstBuffer<void> src) | ||||
| { | ||||
| 	switch (format.format) { | ||||
| 	case SampleFormat::UNDEFINED: | ||||
|   | ||||
| @@ -36,11 +36,9 @@ class FallbackPcmResampler final : public PcmResampler { | ||||
| 	PcmBuffer buffer; | ||||
|  | ||||
| public: | ||||
| 	virtual AudioFormat Open(AudioFormat &af, unsigned new_sample_rate, | ||||
| 				 Error &error) override; | ||||
| 	virtual void Close() override; | ||||
| 	virtual ConstBuffer<void> Resample(ConstBuffer<void> src, | ||||
| 					   Error &error) override; | ||||
| 	AudioFormat Open(AudioFormat &af, unsigned new_sample_rate) override; | ||||
| 	void Close() override; | ||||
| 	ConstBuffer<void> Resample(ConstBuffer<void> src) override; | ||||
| }; | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -20,15 +20,13 @@ | ||||
| #include "config.h" | ||||
| #include "FormatConverter.hxx" | ||||
| #include "PcmFormat.hxx" | ||||
| #include "Domain.hxx" | ||||
| #include "util/ConstBuffer.hxx" | ||||
| #include "util/Error.hxx" | ||||
| #include "util/RuntimeError.hxx" | ||||
|  | ||||
| #include <assert.h> | ||||
|  | ||||
| bool | ||||
| PcmFormatConverter::Open(SampleFormat _src_format, SampleFormat _dest_format, | ||||
| 			 Error &error) | ||||
| void | ||||
| PcmFormatConverter::Open(SampleFormat _src_format, SampleFormat _dest_format) | ||||
| { | ||||
| 	assert(_src_format != SampleFormat::UNDEFINED); | ||||
| 	assert(_dest_format != SampleFormat::UNDEFINED); | ||||
| @@ -40,11 +38,9 @@ PcmFormatConverter::Open(SampleFormat _src_format, SampleFormat _dest_format, | ||||
|  | ||||
| 	case SampleFormat::S8: | ||||
| 	case SampleFormat::DSD: | ||||
| 		error.Format(pcm_domain, | ||||
| 			     "PCM conversion from %s to %s is not implemented", | ||||
| 			     sample_format_to_string(_src_format), | ||||
| 			     sample_format_to_string(_dest_format)); | ||||
| 		return false; | ||||
| 		throw FormatRuntimeError("PCM conversion from %s to %s is not implemented", | ||||
| 					 sample_format_to_string(_src_format), | ||||
| 					 sample_format_to_string(_dest_format)); | ||||
|  | ||||
| 	case SampleFormat::S16: | ||||
| 	case SampleFormat::S24_P32: | ||||
| @@ -55,7 +51,6 @@ PcmFormatConverter::Open(SampleFormat _src_format, SampleFormat _dest_format, | ||||
|  | ||||
| 	src_format = _src_format; | ||||
| 	dest_format = _dest_format; | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| void | ||||
| @@ -68,7 +63,7 @@ PcmFormatConverter::Close() | ||||
| } | ||||
|  | ||||
| ConstBuffer<void> | ||||
| PcmFormatConverter::Convert(ConstBuffer<void> src, gcc_unused Error &error) | ||||
| PcmFormatConverter::Convert(ConstBuffer<void> src) | ||||
| { | ||||
| 	switch (dest_format) { | ||||
| 	case SampleFormat::UNDEFINED: | ||||
|   | ||||
| @@ -29,7 +29,6 @@ | ||||
| #include <assert.h> | ||||
| #endif | ||||
|  | ||||
| class Error; | ||||
| template<typename T> struct ConstBuffer; | ||||
|  | ||||
| /** | ||||
| @@ -56,13 +55,12 @@ public: | ||||
| 	/** | ||||
| 	 * Opens the object, prepare for Convert(). | ||||
| 	 * | ||||
| 	 * Throws std::runtime_error on error. | ||||
| 	 * | ||||
| 	 * @param src_format the sample format of incoming data | ||||
| 	 * @param dest_format the sample format of outgoing data | ||||
| 	 * @param error location to store the error | ||||
| 	 * @return true on success | ||||
| 	 */ | ||||
| 	bool Open(SampleFormat src_format, SampleFormat dest_format, | ||||
| 		  Error &error); | ||||
| 	void Open(SampleFormat src_format, SampleFormat dest_format); | ||||
|  | ||||
| 	/** | ||||
| 	 * Closes the object.  After that, you may call Open() again. | ||||
| @@ -72,13 +70,13 @@ public: | ||||
| 	/** | ||||
| 	 * Convert a block of PCM data. | ||||
| 	 * | ||||
| 	 * Throws std::runtime_error on error. | ||||
| 	 * | ||||
| 	 * @param src the input buffer | ||||
| 	 * @param error location to store the error | ||||
| 	 * @return the destination buffer on success, | ||||
| 	 * ConstBuffer::Null() on error | ||||
| 	 * @return the destination buffer | ||||
| 	 */ | ||||
| 	gcc_pure | ||||
| 	ConstBuffer<void> Convert(ConstBuffer<void> src, Error &error); | ||||
| 	ConstBuffer<void> Convert(ConstBuffer<void> src); | ||||
| }; | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -32,33 +32,28 @@ GluePcmResampler::~GluePcmResampler() | ||||
| 	delete resampler; | ||||
| } | ||||
|  | ||||
| bool | ||||
| GluePcmResampler::Open(AudioFormat src_format, unsigned new_sample_rate, | ||||
| 		       Error &error) | ||||
| void | ||||
| GluePcmResampler::Open(AudioFormat src_format, unsigned new_sample_rate) | ||||
| { | ||||
| 	assert(src_format.IsValid()); | ||||
| 	assert(audio_valid_sample_rate(new_sample_rate)); | ||||
|  | ||||
| 	AudioFormat requested_format = src_format; | ||||
| 	AudioFormat dest_format = resampler->Open(requested_format, | ||||
| 						  new_sample_rate, | ||||
| 						  error); | ||||
| 	if (!dest_format.IsValid()) | ||||
| 		return false; | ||||
| 						  new_sample_rate); | ||||
| 	assert(dest_format.IsValid()); | ||||
|  | ||||
| 	assert(requested_format.channels == src_format.channels); | ||||
| 	assert(dest_format.channels == src_format.channels); | ||||
| 	assert(dest_format.sample_rate == new_sample_rate); | ||||
|  | ||||
| 	if (requested_format.format != src_format.format && | ||||
| 	    !format_converter.Open(src_format.format, requested_format.format, | ||||
| 				   error)) | ||||
| 		return false; | ||||
| 	if (requested_format.format != src_format.format) | ||||
| 		format_converter.Open(src_format.format, | ||||
| 				      requested_format.format); | ||||
|  | ||||
| 	src_sample_format = src_format.format; | ||||
| 	requested_sample_format = requested_format.format; | ||||
| 	output_sample_format = dest_format.format; | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| void | ||||
| @@ -71,15 +66,12 @@ GluePcmResampler::Close() | ||||
| } | ||||
|  | ||||
| ConstBuffer<void> | ||||
| GluePcmResampler::Resample(ConstBuffer<void> src, Error &error) | ||||
| GluePcmResampler::Resample(ConstBuffer<void> src) | ||||
| { | ||||
| 	assert(!src.IsNull()); | ||||
|  | ||||
| 	if (requested_sample_format != src_sample_format) { | ||||
| 		src = format_converter.Convert(src, error); | ||||
| 		if (src.IsNull()) | ||||
| 			return nullptr; | ||||
| 	} | ||||
| 	if (requested_sample_format != src_sample_format) | ||||
| 		src = format_converter.Convert(src); | ||||
|  | ||||
| 	return resampler->Resample(src, error); | ||||
| 	return resampler->Resample(src); | ||||
| } | ||||
|   | ||||
| @@ -24,7 +24,6 @@ | ||||
| #include "AudioFormat.hxx" | ||||
| #include "FormatConverter.hxx" | ||||
|  | ||||
| class Error; | ||||
| class PcmResampler; | ||||
| template<typename T> struct ConstBuffer; | ||||
|  | ||||
| @@ -49,15 +48,14 @@ public: | ||||
| 	GluePcmResampler(); | ||||
| 	~GluePcmResampler(); | ||||
|  | ||||
| 	bool Open(AudioFormat src_format, unsigned new_sample_rate, | ||||
| 		  Error &error); | ||||
| 	void Open(AudioFormat src_format, unsigned new_sample_rate); | ||||
| 	void Close(); | ||||
|  | ||||
| 	SampleFormat GetOutputSampleFormat() const { | ||||
| 		return output_sample_format; | ||||
| 	} | ||||
|  | ||||
| 	ConstBuffer<void> Resample(ConstBuffer<void> src, Error &error); | ||||
| 	ConstBuffer<void> Resample(ConstBuffer<void> src); | ||||
| }; | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -21,7 +21,7 @@ | ||||
| #include "LibsamplerateResampler.hxx" | ||||
| #include "config/Block.hxx" | ||||
| #include "util/ASCII.hxx" | ||||
| #include "util/Error.hxx" | ||||
| #include "util/RuntimeError.hxx" | ||||
| #include "util/Domain.hxx" | ||||
| #include "Log.hxx" | ||||
|  | ||||
| @@ -63,26 +63,21 @@ lsr_parse_converter(const char *s) | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| bool | ||||
| pcm_resample_lsr_global_init(const ConfigBlock &block, Error &error) | ||||
| void | ||||
| pcm_resample_lsr_global_init(const ConfigBlock &block) | ||||
| { | ||||
| 	const char *converter = block.GetBlockValue("type", "2"); | ||||
| 	if (!lsr_parse_converter(converter)) { | ||||
| 		error.Format(libsamplerate_domain, | ||||
| 			     "unknown samplerate converter '%s'", converter); | ||||
| 		return false; | ||||
| 	} | ||||
| 	if (!lsr_parse_converter(converter)) | ||||
| 		throw FormatRuntimeError("unknown samplerate converter '%s'", | ||||
| 					 converter); | ||||
|  | ||||
| 	FormatDebug(libsamplerate_domain, | ||||
| 		    "libsamplerate converter '%s'", | ||||
| 		    src_get_name(lsr_converter)); | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| AudioFormat | ||||
| LibsampleratePcmResampler::Open(AudioFormat &af, unsigned new_sample_rate, | ||||
| 				Error &error) | ||||
| LibsampleratePcmResampler::Open(AudioFormat &af, unsigned new_sample_rate) | ||||
| { | ||||
| 	assert(af.IsValid()); | ||||
| 	assert(audio_valid_sample_rate(new_sample_rate)); | ||||
| @@ -96,12 +91,9 @@ LibsampleratePcmResampler::Open(AudioFormat &af, unsigned new_sample_rate, | ||||
|  | ||||
| 	int src_error; | ||||
| 	state = src_new(lsr_converter, channels, &src_error); | ||||
| 	if (!state) { | ||||
| 		error.Format(libsamplerate_domain, src_error, | ||||
| 			     "libsamplerate initialization has failed: %s", | ||||
| 			     src_strerror(src_error)); | ||||
| 		return AudioFormat::Undefined(); | ||||
| 	} | ||||
| 	if (!state) | ||||
| 		throw FormatRuntimeError("libsamplerate initialization has failed: %s", | ||||
| 					 src_strerror(src_error)); | ||||
|  | ||||
| 	memset(&data, 0, sizeof(data)); | ||||
|  | ||||
| @@ -122,22 +114,8 @@ LibsampleratePcmResampler::Close() | ||||
| 	state = src_delete(state); | ||||
| } | ||||
|  | ||||
| static bool | ||||
| src_process(SRC_STATE *state, SRC_DATA *data, Error &error) | ||||
| { | ||||
| 	int result = src_process(state, data); | ||||
| 	if (result != 0) { | ||||
| 		error.Format(libsamplerate_domain, result, | ||||
| 			     "libsamplerate has failed: %s", | ||||
| 			     src_strerror(result)); | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| inline ConstBuffer<float> | ||||
| LibsampleratePcmResampler::Resample2(ConstBuffer<float> src, Error &error) | ||||
| LibsampleratePcmResampler::Resample2(ConstBuffer<float> src) | ||||
| { | ||||
| 	assert(src.size % channels == 0); | ||||
|  | ||||
| @@ -151,15 +129,17 @@ LibsampleratePcmResampler::Resample2(ConstBuffer<float> src, Error &error) | ||||
| 	data.input_frames = src_frames; | ||||
| 	data.output_frames = dest_frames; | ||||
|  | ||||
| 	if (!src_process(state, &data, error)) | ||||
| 		return nullptr; | ||||
| 	int result = src_process(state, &data); | ||||
| 	if (result != 0) | ||||
| 		throw FormatRuntimeError("libsamplerate has failed: %s", | ||||
| 					 src_strerror(result)); | ||||
|  | ||||
| 	return ConstBuffer<float>(data.data_out, | ||||
| 				  data.output_frames_gen * channels); | ||||
| } | ||||
|  | ||||
| ConstBuffer<void> | ||||
| LibsampleratePcmResampler::Resample(ConstBuffer<void> src, Error &error) | ||||
| LibsampleratePcmResampler::Resample(ConstBuffer<void> src) | ||||
| { | ||||
| 	return Resample2(ConstBuffer<float>::FromVoid(src), error).ToVoid(); | ||||
| 	return Resample2(ConstBuffer<float>::FromVoid(src)).ToVoid(); | ||||
| } | ||||
|   | ||||
| @@ -42,17 +42,15 @@ class LibsampleratePcmResampler final : public PcmResampler { | ||||
| 	PcmBuffer buffer; | ||||
|  | ||||
| public: | ||||
| 	virtual AudioFormat Open(AudioFormat &af, unsigned new_sample_rate, | ||||
| 				 Error &error) override; | ||||
| 	virtual void Close() override; | ||||
| 	virtual ConstBuffer<void> Resample(ConstBuffer<void> src, | ||||
| 					   Error &error) override; | ||||
| 	AudioFormat Open(AudioFormat &af, unsigned new_sample_rate) override; | ||||
| 	void Close() override; | ||||
| 	ConstBuffer<void> Resample(ConstBuffer<void> src) override; | ||||
|  | ||||
| private: | ||||
| 	ConstBuffer<float> Resample2(ConstBuffer<float> src, Error &error); | ||||
| 	ConstBuffer<float> Resample2(ConstBuffer<float> src); | ||||
| }; | ||||
|  | ||||
| bool | ||||
| pcm_resample_lsr_global_init(const ConfigBlock &block, Error &error); | ||||
| void | ||||
| pcm_resample_lsr_global_init(const ConfigBlock &block); | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -22,15 +22,14 @@ | ||||
| #include "Domain.hxx" | ||||
| #include "ConfiguredResampler.hxx" | ||||
| #include "AudioFormat.hxx" | ||||
| #include "util/Error.hxx" | ||||
| #include "util/ConstBuffer.hxx" | ||||
|  | ||||
| #include <assert.h> | ||||
|  | ||||
| bool | ||||
| pcm_convert_global_init(Error &error) | ||||
| void | ||||
| pcm_convert_global_init() | ||||
| { | ||||
| 	return pcm_resampler_global_init(error); | ||||
| 	pcm_resampler_global_init(); | ||||
| } | ||||
|  | ||||
| PcmConvert::PcmConvert() | ||||
| @@ -47,9 +46,8 @@ PcmConvert::~PcmConvert() | ||||
| 	assert(!dest_format.IsValid()); | ||||
| } | ||||
|  | ||||
| bool | ||||
| PcmConvert::Open(const AudioFormat _src_format, const AudioFormat _dest_format, | ||||
| 		 Error &error) | ||||
| void | ||||
| PcmConvert::Open(const AudioFormat _src_format, const AudioFormat _dest_format) | ||||
| { | ||||
| 	assert(!src_format.IsValid()); | ||||
| 	assert(!dest_format.IsValid()); | ||||
| @@ -62,39 +60,42 @@ PcmConvert::Open(const AudioFormat _src_format, const AudioFormat _dest_format, | ||||
|  | ||||
| 	enable_resampler = format.sample_rate != _dest_format.sample_rate; | ||||
| 	if (enable_resampler) { | ||||
| 		if (!resampler.Open(format, _dest_format.sample_rate, error)) | ||||
| 			return false; | ||||
| 		resampler.Open(format, _dest_format.sample_rate); | ||||
|  | ||||
| 		format.format = resampler.GetOutputSampleFormat(); | ||||
| 		format.sample_rate = _dest_format.sample_rate; | ||||
| 	} | ||||
|  | ||||
| 	enable_format = format.format != _dest_format.format; | ||||
| 	if (enable_format && | ||||
| 	    !format_converter.Open(format.format, _dest_format.format, | ||||
| 				   error)) { | ||||
| 		if (enable_resampler) | ||||
| 			resampler.Close(); | ||||
| 		return false; | ||||
| 	if (enable_format) { | ||||
| 		try { | ||||
| 			format_converter.Open(format.format, | ||||
| 					      _dest_format.format); | ||||
| 		} catch (...) { | ||||
| 			if (enable_resampler) | ||||
| 				resampler.Close(); | ||||
| 			throw; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	format.format = _dest_format.format; | ||||
|  | ||||
| 	enable_channels = format.channels != _dest_format.channels; | ||||
| 	if (enable_channels && | ||||
| 	    !channels_converter.Open(format.format, format.channels, | ||||
| 				     _dest_format.channels, error)) { | ||||
| 		if (enable_format) | ||||
| 			format_converter.Close(); | ||||
| 		if (enable_resampler) | ||||
| 			resampler.Close(); | ||||
| 		return false; | ||||
| 	if (enable_channels) { | ||||
| 		try { | ||||
| 			channels_converter.Open(format.format, format.channels, | ||||
| 						_dest_format.channels); | ||||
| 		} catch (...) { | ||||
| 			if (enable_format) | ||||
| 				format_converter.Close(); | ||||
| 			if (enable_resampler) | ||||
| 				resampler.Close(); | ||||
| 			throw; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	src_format = _src_format; | ||||
| 	dest_format = _dest_format; | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| void | ||||
| @@ -118,39 +119,27 @@ PcmConvert::Close() | ||||
| } | ||||
|  | ||||
| ConstBuffer<void> | ||||
| PcmConvert::Convert(ConstBuffer<void> buffer, Error &error) | ||||
| PcmConvert::Convert(ConstBuffer<void> buffer) | ||||
| { | ||||
| #ifdef ENABLE_DSD | ||||
| 	if (src_format.format == SampleFormat::DSD) { | ||||
| 		auto s = ConstBuffer<uint8_t>::FromVoid(buffer); | ||||
| 		auto d = dsd.ToFloat(src_format.channels, s); | ||||
| 		if (d.IsNull()) { | ||||
| 			error.Set(pcm_domain, | ||||
| 				  "DSD to PCM conversion failed"); | ||||
| 			return nullptr; | ||||
| 		} | ||||
| 		if (d.IsNull()) | ||||
| 			throw std::runtime_error("DSD to PCM conversion failed"); | ||||
|  | ||||
| 		buffer = d.ToVoid(); | ||||
| 	} | ||||
| #endif | ||||
|  | ||||
| 	if (enable_resampler) { | ||||
| 		buffer = resampler.Resample(buffer, error); | ||||
| 		if (buffer.IsNull()) | ||||
| 			return nullptr; | ||||
| 	} | ||||
| 	if (enable_resampler) | ||||
| 		buffer = resampler.Resample(buffer); | ||||
|  | ||||
| 	if (enable_format) { | ||||
| 		buffer = format_converter.Convert(buffer, error); | ||||
| 		if (buffer.IsNull()) | ||||
| 			return nullptr; | ||||
| 	} | ||||
| 	if (enable_format) | ||||
| 		buffer = format_converter.Convert(buffer); | ||||
|  | ||||
| 	if (enable_channels) { | ||||
| 		buffer = channels_converter.Convert(buffer, error); | ||||
| 		if (buffer.IsNull()) | ||||
| 			return nullptr; | ||||
| 	} | ||||
| 	if (enable_channels) | ||||
| 		buffer = channels_converter.Convert(buffer); | ||||
|  | ||||
| 	return buffer; | ||||
| } | ||||
|   | ||||
| @@ -31,7 +31,6 @@ | ||||
| #endif | ||||
|  | ||||
| template<typename T> struct ConstBuffer; | ||||
| class Error; | ||||
|  | ||||
| /** | ||||
|  * This object is statically allocated (within another struct), and | ||||
| @@ -57,9 +56,10 @@ public: | ||||
|  | ||||
| 	/** | ||||
| 	 * Prepare the object.  Call Close() when done. | ||||
| 	 * | ||||
| 	 * Throws std::runtime_error on error. | ||||
| 	 */ | ||||
| 	bool Open(AudioFormat _src_format, AudioFormat _dest_format, | ||||
| 		  Error &error); | ||||
| 	void Open(AudioFormat _src_format, AudioFormat _dest_format); | ||||
|  | ||||
| 	/** | ||||
| 	 * Close the object after it was prepared with Open().  After | ||||
| @@ -70,14 +70,15 @@ public: | ||||
| 	/** | ||||
| 	 * Converts PCM data between two audio formats. | ||||
| 	 * | ||||
| 	 * Throws std::runtime_error on error. | ||||
| 	 * | ||||
| 	 * @param src the source PCM buffer | ||||
| 	 * @param error location to store the error occurring | ||||
| 	 * @return the destination buffer, or nullptr on error | ||||
| 	 * @return the destination buffer | ||||
| 	 */ | ||||
| 	ConstBuffer<void> Convert(ConstBuffer<void> src, Error &error); | ||||
| 	ConstBuffer<void> Convert(ConstBuffer<void> src); | ||||
| }; | ||||
|  | ||||
| bool | ||||
| pcm_convert_global_init(Error &error); | ||||
| void | ||||
| pcm_convert_global_init(); | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -24,7 +24,6 @@ | ||||
| #include "Compiler.h" | ||||
|  | ||||
| struct AudioFormat; | ||||
| class Error; | ||||
|  | ||||
| /** | ||||
|  * This is an interface for plugins that convert PCM data to a | ||||
| @@ -37,16 +36,17 @@ public: | ||||
| 	/** | ||||
| 	 * Opens the resampler, preparing it for Resample(). | ||||
| 	 * | ||||
| 	 * Throws std::runtime_error on error. | ||||
| 	 * | ||||
| 	 * @param af the audio format of incoming data; the plugin may | ||||
| 	 * modify the object to enforce another input format (however, | ||||
| 	 * it may not request a different input sample rate) | ||||
| 	 * @param new_sample_rate the requested output sample rate | ||||
| 	 * @param error location to store the error | ||||
| 	 * @return the format of outgoing data or | ||||
| 	 * AudioFormat::Undefined() on error | ||||
| 	 * @return the format of outgoing data | ||||
| 	 */ | ||||
| 	virtual AudioFormat Open(AudioFormat &af, unsigned new_sample_rate, | ||||
| 				 Error &error) = 0; | ||||
| 	virtual AudioFormat Open(AudioFormat &af, | ||||
| 				 unsigned new_sample_rate) = 0; | ||||
|  | ||||
| 	/** | ||||
| 	 * Closes the resampler.  After that, you may call Open() | ||||
| @@ -58,14 +58,11 @@ public: | ||||
| 	 * Resamples a block of PCM data. | ||||
| 	 * | ||||
| 	 * @param src the input buffer | ||||
| 	 * @param error location to store the error occurring | ||||
| 	 * @return the destination buffer on success (will be | ||||
| 	 * invalidated by filter_close() or filter_filter()), nullptr on | ||||
| 	 * error | ||||
| 	 * @return the destination buffer (will be invalidated by | ||||
| 	 * filter_close() or filter_filter()) | ||||
| 	 */ | ||||
| 	gcc_pure | ||||
| 	virtual ConstBuffer<void> Resample(ConstBuffer<void> src, | ||||
| 					   Error &error) = 0; | ||||
| 	virtual ConstBuffer<void> Resample(ConstBuffer<void> src) = 0; | ||||
| }; | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -21,7 +21,7 @@ | ||||
| #include "SoxrResampler.hxx" | ||||
| #include "AudioFormat.hxx" | ||||
| #include "config/Block.hxx" | ||||
| #include "util/Error.hxx" | ||||
| #include "util/RuntimeError.hxx" | ||||
| #include "util/Domain.hxx" | ||||
| #include "Log.hxx" | ||||
|  | ||||
| @@ -80,18 +80,16 @@ soxr_parse_quality(const char *quality) | ||||
| 	return SOXR_INVALID_RECIPE; | ||||
| } | ||||
|  | ||||
| bool | ||||
| pcm_resample_soxr_global_init(const ConfigBlock &block, Error &error) | ||||
| void | ||||
| pcm_resample_soxr_global_init(const ConfigBlock &block) | ||||
| { | ||||
| 	const char *quality_string = block.GetBlockValue("quality"); | ||||
| 	unsigned long recipe = soxr_parse_quality(quality_string); | ||||
| 	if (recipe == SOXR_INVALID_RECIPE) { | ||||
| 		assert(quality_string != nullptr); | ||||
|  | ||||
| 		error.Format(soxr_domain, | ||||
| 			     "unknown quality setting '%s' in line %d", | ||||
| 			     quality_string, block.line); | ||||
| 		return false; | ||||
| 		throw FormatRuntimeError("unknown quality setting '%s' in line %d", | ||||
| 					 quality_string, block.line); | ||||
| 	} | ||||
|  | ||||
| 	soxr_quality = soxr_quality_spec(recipe, 0); | ||||
| @@ -102,13 +100,10 @@ pcm_resample_soxr_global_init(const ConfigBlock &block, Error &error) | ||||
|  | ||||
| 	const unsigned n_threads = block.GetBlockValue("threads", 1); | ||||
| 	soxr_runtime = soxr_runtime_spec(n_threads); | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| AudioFormat | ||||
| SoxrPcmResampler::Open(AudioFormat &af, unsigned new_sample_rate, | ||||
| 		       Error &error) | ||||
| SoxrPcmResampler::Open(AudioFormat &af, unsigned new_sample_rate) | ||||
| { | ||||
| 	assert(af.IsValid()); | ||||
| 	assert(audio_valid_sample_rate(new_sample_rate)); | ||||
| @@ -117,11 +112,9 @@ SoxrPcmResampler::Open(AudioFormat &af, unsigned new_sample_rate, | ||||
| 	soxr = soxr_create(af.sample_rate, new_sample_rate, | ||||
| 			   af.channels, &e, | ||||
| 			   nullptr, &soxr_quality, &soxr_runtime); | ||||
| 	if (soxr == nullptr) { | ||||
| 		error.Format(soxr_domain, | ||||
| 			     "soxr initialization has failed: %s", e); | ||||
| 		return AudioFormat::Undefined(); | ||||
| 	} | ||||
| 	if (soxr == nullptr) | ||||
| 		throw FormatRuntimeError("soxr initialization has failed: %s", | ||||
| 					 e); | ||||
|  | ||||
| 	FormatDebug(soxr_domain, "soxr engine '%s'", soxr_engine(soxr)); | ||||
|  | ||||
| @@ -147,7 +140,7 @@ SoxrPcmResampler::Close() | ||||
| } | ||||
|  | ||||
| ConstBuffer<void> | ||||
| SoxrPcmResampler::Resample(ConstBuffer<void> src, Error &error) | ||||
| SoxrPcmResampler::Resample(ConstBuffer<void> src) | ||||
| { | ||||
| 	const size_t frame_size = channels * sizeof(float); | ||||
| 	assert(src.size % frame_size == 0); | ||||
| @@ -162,10 +155,8 @@ SoxrPcmResampler::Resample(ConstBuffer<void> src, Error &error) | ||||
| 	size_t i_done, o_done; | ||||
| 	soxr_error_t e = soxr_process(soxr, src.data, n_frames, &i_done, | ||||
| 				      output_buffer, o_frames, &o_done); | ||||
| 	if (e != nullptr) { | ||||
| 		error.Format(soxr_domain, "soxr error: %s", e); | ||||
| 		return nullptr; | ||||
| 	} | ||||
| 	if (e != nullptr) | ||||
| 		throw FormatRuntimeError("soxr error: %s", e); | ||||
|  | ||||
| 	return { output_buffer, o_done * frame_size }; | ||||
| } | ||||
|   | ||||
| @@ -39,14 +39,12 @@ class SoxrPcmResampler final : public PcmResampler { | ||||
| 	PcmBuffer buffer; | ||||
|  | ||||
| public: | ||||
| 	virtual AudioFormat Open(AudioFormat &af, unsigned new_sample_rate, | ||||
| 				 Error &error) override; | ||||
| 	virtual void Close() override; | ||||
| 	virtual ConstBuffer<void> Resample(ConstBuffer<void> src, | ||||
| 					   Error &error) override; | ||||
| 	AudioFormat Open(AudioFormat &af, unsigned new_sample_rate) override; | ||||
| 	void Close() override; | ||||
| 	ConstBuffer<void> Resample(ConstBuffer<void> src) override; | ||||
| }; | ||||
|  | ||||
| bool | ||||
| pcm_resample_soxr_global_init(const ConfigBlock &block, Error &error); | ||||
| void | ||||
| pcm_resample_soxr_global_init(const ConfigBlock &block); | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -24,7 +24,7 @@ | ||||
| #include "Traits.hxx" | ||||
| #include "util/ConstBuffer.hxx" | ||||
| #include "util/WritableBuffer.hxx" | ||||
| #include "util/Error.hxx" | ||||
| #include "util/RuntimeError.hxx" | ||||
|  | ||||
| #include "PcmDither.cxx" // including the .cxx file to get inlined templates | ||||
|  | ||||
| @@ -97,17 +97,15 @@ pcm_volume_change_float(float *dest, const float *src, size_t n, | ||||
| 		dest[i] = src[i] * volume; | ||||
| } | ||||
|  | ||||
| bool | ||||
| PcmVolume::Open(SampleFormat _format, Error &error) | ||||
| void | ||||
| PcmVolume::Open(SampleFormat _format) | ||||
| { | ||||
| 	assert(format == SampleFormat::UNDEFINED); | ||||
|  | ||||
| 	switch (_format) { | ||||
| 	case SampleFormat::UNDEFINED: | ||||
| 		error.Format(pcm_domain, | ||||
| 			     "Software volume for %s is not implemented", | ||||
| 			     sample_format_to_string(_format)); | ||||
| 		return false; | ||||
| 		throw FormatRuntimeError("Software volume for %s is not implemented", | ||||
| 					 sample_format_to_string(_format)); | ||||
|  | ||||
| 	case SampleFormat::S8: | ||||
| 	case SampleFormat::S16: | ||||
| @@ -122,7 +120,6 @@ PcmVolume::Open(SampleFormat _format, Error &error) | ||||
| 	} | ||||
|  | ||||
| 	format = _format; | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| ConstBuffer<void> | ||||
|   | ||||
| @@ -28,7 +28,6 @@ | ||||
| #include <assert.h> | ||||
| #endif | ||||
|  | ||||
| class Error; | ||||
| template<typename T> struct ConstBuffer; | ||||
|  | ||||
| /** | ||||
| @@ -93,11 +92,11 @@ public: | ||||
| 	/** | ||||
| 	 * Opens the object, prepare for Apply(). | ||||
| 	 * | ||||
| 	 * Throws std::runtime_error on error. | ||||
| 	 * | ||||
| 	 * @param format the sample format | ||||
| 	 * @param error location to store the error | ||||
| 	 * @return true on success | ||||
| 	 */ | ||||
| 	bool Open(SampleFormat format, Error &error); | ||||
| 	void Open(SampleFormat format); | ||||
|  | ||||
| 	/** | ||||
| 	 * Closes the object.  After that, you may call Open() again. | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Max Kellermann
					Max Kellermann