pcm/Convert: migrate from class Error to C++ exceptions

This commit is contained in:
Max Kellermann 2016-09-05 12:19:20 +02:00
parent 860064c812
commit ae1eb9ccde
27 changed files with 197 additions and 305 deletions

View File

@ -520,10 +520,7 @@ try {
archive_plugin_init_all(); archive_plugin_init_all();
#endif #endif
if (!pcm_convert_global_init(error)) { pcm_convert_global_init();
LogError(error);
return EXIT_FAILURE;
}
decoder_plugin_init_all(); decoder_plugin_init_all();

View File

@ -73,11 +73,12 @@ decoder_initialized(Decoder &decoder,
decoder.convert = new PcmConvert(); decoder.convert = new PcmConvert();
Error error; try {
if (!decoder.convert->Open(dc.in_audio_format, decoder.convert->Open(dc.in_audio_format,
dc.out_audio_format, dc.out_audio_format);
error)) } catch (...) {
decoder.error = std::make_exception_ptr(std::move(error)); decoder.error = std::current_exception();
}
} }
const ScopeLock protect(dc.mutex); const ScopeLock protect(dc.mutex);
@ -484,19 +485,17 @@ decoder_data(Decoder &decoder,
if (decoder.convert != nullptr) { if (decoder.convert != nullptr) {
assert(dc.in_audio_format != dc.out_audio_format); assert(dc.in_audio_format != dc.out_audio_format);
Error error; try {
auto result = decoder.convert->Convert({data, length}, auto result = decoder.convert->Convert({data, length});
error); data = result.data;
if (data == nullptr) { length = result.size;
} catch (const std::runtime_error &e) {
/* the PCM conversion has failed - stop /* the PCM conversion has failed - stop
playback, since we have no better way to playback, since we have no better way to
bail out */ bail out */
LogError(error); LogError(e);
return DecoderCommand::STOP; return DecoderCommand::STOP;
} }
data = result.data;
length = result.size;
} else { } else {
assert(dc.in_audio_format == dc.out_audio_format); assert(dc.in_audio_format == dc.out_audio_format);
} }

View File

@ -25,7 +25,6 @@
#include "pcm/PcmConvert.hxx" #include "pcm/PcmConvert.hxx"
#include "util/Manual.hxx" #include "util/Manual.hxx"
#include "util/ConstBuffer.hxx" #include "util/ConstBuffer.hxx"
#include "util/Error.hxx"
#include "AudioFormat.hxx" #include "AudioFormat.hxx"
#include "poison.h" #include "poison.h"
@ -88,9 +87,7 @@ ConvertFilter::Set(const AudioFormat &_out_audio_format)
/* optimized special case: no-op */ /* optimized special case: no-op */
return; return;
Error error; state.Open(in_audio_format, _out_audio_format);
if (!state.Open(in_audio_format, _out_audio_format, error))
throw std::runtime_error(error.GetMessage());
out_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 */ /* optimized special case: no-op */
return src; return src;
Error error; return state.Convert(src);
auto result = state.Convert(src, error);
if (result.IsNull())
throw std::runtime_error(error.GetMessage());
return result;
} }
const struct filter_plugin convert_filter_plugin = { const struct filter_plugin convert_filter_plugin = {

View File

@ -76,9 +76,7 @@ public:
mixer(_mixer), base(_base), mode(REPLAY_GAIN_OFF) { mixer(_mixer), base(_base), mode(REPLAY_GAIN_OFF) {
info.Clear(); info.Clear();
Error error; pv.Open(out_audio_format.format);
if (!pv.Open(out_audio_format.format, error))
throw std::runtime_error(error.GetMessage());
} }
void SetInfo(const ReplayGainInfo *_info) { void SetInfo(const ReplayGainInfo *_info) {

View File

@ -25,7 +25,6 @@
#include "pcm/Volume.hxx" #include "pcm/Volume.hxx"
#include "AudioFormat.hxx" #include "AudioFormat.hxx"
#include "util/ConstBuffer.hxx" #include "util/ConstBuffer.hxx"
#include "util/Error.hxx"
#include <stdexcept> #include <stdexcept>
@ -35,9 +34,7 @@ class VolumeFilter final : public Filter {
public: public:
explicit VolumeFilter(const AudioFormat &audio_format) explicit VolumeFilter(const AudioFormat &audio_format)
:Filter(audio_format) { :Filter(audio_format) {
Error error; pv.Open(out_audio_format.format);
if (!pv.Open(out_audio_format.format, error))
throw std::runtime_error(error.GetMessage());
} }
unsigned GetVolume() const { unsigned GetVolume() const {

View File

@ -22,14 +22,13 @@
#include "PcmChannels.hxx" #include "PcmChannels.hxx"
#include "Domain.hxx" #include "Domain.hxx"
#include "util/ConstBuffer.hxx" #include "util/ConstBuffer.hxx"
#include "util/Error.hxx" #include "util/RuntimeError.hxx"
#include <assert.h> #include <assert.h>
bool void
PcmChannelsConverter::Open(SampleFormat _format, PcmChannelsConverter::Open(SampleFormat _format,
unsigned _src_channels, unsigned _dest_channels, unsigned _src_channels, unsigned _dest_channels)
gcc_unused Error &error)
{ {
assert(_format != SampleFormat::UNDEFINED); assert(_format != SampleFormat::UNDEFINED);
@ -41,16 +40,13 @@ PcmChannelsConverter::Open(SampleFormat _format,
break; break;
default: default:
error.Format(pcm_domain, throw FormatRuntimeError("PCM channel conversion for %s is not implemented",
"PCM channel conversion for %s is not implemented", sample_format_to_string(_format));
sample_format_to_string(_format));
return false;
} }
format = _format; format = _format;
src_channels = _src_channels; src_channels = _src_channels;
dest_channels = _dest_channels; dest_channels = _dest_channels;
return true;
} }
void void
@ -62,7 +58,7 @@ PcmChannelsConverter::Close()
} }
ConstBuffer<void> ConstBuffer<void>
PcmChannelsConverter::Convert(ConstBuffer<void> src, gcc_unused Error &error) PcmChannelsConverter::Convert(ConstBuffer<void> src)
{ {
switch (format) { switch (format) {
case SampleFormat::UNDEFINED: case SampleFormat::UNDEFINED:

View File

@ -28,7 +28,6 @@
#include <assert.h> #include <assert.h>
#endif #endif
class Error;
template<typename T> struct ConstBuffer; template<typename T> struct ConstBuffer;
/** /**
@ -53,15 +52,14 @@ public:
/** /**
* Opens the object, prepare for Convert(). * Opens the object, prepare for Convert().
* *
* Throws std::runtime_error on error.
*
* @param format the sample format * @param format the sample format
* @param src_channels the number of source channels * @param src_channels the number of source channels
* @param dest_channels the number of destination channels * @param dest_channels the number of destination channels
* @param error location to store the error
* @return true on success
*/ */
bool Open(SampleFormat format, void Open(SampleFormat format,
unsigned src_channels, unsigned dest_channels, unsigned src_channels, unsigned dest_channels);
Error &error);
/** /**
* Closes the object. After that, you may call Open() again. * Closes the object. After that, you may call Open() again.
@ -71,13 +69,13 @@ public:
/** /**
* Convert a block of PCM data. * Convert a block of PCM data.
* *
* Throws std::runtime_error on error.
*
* @param src the input buffer * @param src the input buffer
* @param error location to store the error * @return the destination buffer
* @return the destination buffer on success,
* ConstBuffer::Null() on error
*/ */
gcc_pure gcc_pure
ConstBuffer<void> Convert(ConstBuffer<void> src, Error &error); ConstBuffer<void> Convert(ConstBuffer<void> src);
}; };
#endif #endif

View File

@ -25,7 +25,7 @@
#include "config/ConfigError.hxx" #include "config/ConfigError.hxx"
#include "config/Block.hxx" #include "config/Block.hxx"
#include "config/Param.hxx" #include "config/Param.hxx"
#include "util/Error.hxx" #include "util/RuntimeError.hxx"
#ifdef ENABLE_LIBSAMPLERATE #ifdef ENABLE_LIBSAMPLERATE
#include "LibsamplerateResampler.hxx" #include "LibsamplerateResampler.hxx"
@ -112,7 +112,7 @@ MigrateResamplerConfig(const config_param *param, ConfigBlock &buffer)
} }
static const ConfigBlock * static const ConfigBlock *
GetResamplerConfig(ConfigBlock &buffer, Error &error) GetResamplerConfig(ConfigBlock &buffer)
{ {
const auto *old_param = const auto *old_param =
config_get_param(ConfigOption::SAMPLERATE_CONVERTER); config_get_param(ConfigOption::SAMPLERATE_CONVERTER);
@ -120,49 +120,39 @@ GetResamplerConfig(ConfigBlock &buffer, Error &error)
if (block == nullptr) if (block == nullptr)
return MigrateResamplerConfig(old_param, buffer); return MigrateResamplerConfig(old_param, buffer);
if (old_param != nullptr) { if (old_param != nullptr)
error.Format(config_domain, throw FormatRuntimeError("Cannot use both 'resampler' (line %d) and 'samplerate_converter' (line %d)",
"Cannot use both 'resampler' (line %d) and 'samplerate_converter' (line %d)", block->line, old_param->line);
block->line, old_param->line);
return nullptr;
}
return block; return block;
} }
bool void
pcm_resampler_global_init(Error &error) pcm_resampler_global_init()
{ {
ConfigBlock buffer; ConfigBlock buffer;
const auto *block = GetResamplerConfig(buffer, error); const auto *block = GetResamplerConfig(buffer);
if (block == nullptr)
return false;
const char *plugin_name = block->GetBlockValue("plugin"); const char *plugin_name = block->GetBlockValue("plugin");
if (plugin_name == nullptr) { if (plugin_name == nullptr)
error.Format(config_domain, throw FormatRuntimeError("'plugin' missing in line %d",
"'plugin' missing in line %d", block->line); block->line);
return false;
}
if (strcmp(plugin_name, "internal") == 0) { if (strcmp(plugin_name, "internal") == 0) {
selected_resampler = SelectedResampler::FALLBACK; selected_resampler = SelectedResampler::FALLBACK;
return true;
#ifdef ENABLE_SOXR #ifdef ENABLE_SOXR
} else if (strcmp(plugin_name, "soxr") == 0) { } else if (strcmp(plugin_name, "soxr") == 0) {
selected_resampler = SelectedResampler::SOXR; selected_resampler = SelectedResampler::SOXR;
return pcm_resample_soxr_global_init(*block, error); pcm_resample_soxr_global_init(*block);
#endif #endif
#ifdef ENABLE_LIBSAMPLERATE #ifdef ENABLE_LIBSAMPLERATE
} else if (strcmp(plugin_name, "libsamplerate") == 0) { } else if (strcmp(plugin_name, "libsamplerate") == 0) {
selected_resampler = SelectedResampler::LIBSAMPLERATE; selected_resampler = SelectedResampler::LIBSAMPLERATE;
return pcm_resample_lsr_global_init(*block, error); pcm_resample_lsr_global_init(*block);
#endif #endif
} else { } else {
error.Format(config_domain, throw FormatRuntimeError("No such resampler plugin: %s",
"No such resampler plugin: %s", plugin_name);
plugin_name);
return false;
} }
} }

View File

@ -22,11 +22,10 @@
#include "check.h" #include "check.h"
class Error;
class PcmResampler; class PcmResampler;
bool void
pcm_resampler_global_init(Error &error); pcm_resampler_global_init();
/** /**
* Create a #PcmResampler instance from the implementation class * Create a #PcmResampler instance from the implementation class

View File

@ -23,8 +23,7 @@
#include <assert.h> #include <assert.h>
AudioFormat AudioFormat
FallbackPcmResampler::Open(AudioFormat &af, unsigned new_sample_rate, FallbackPcmResampler::Open(AudioFormat &af, unsigned new_sample_rate)
gcc_unused Error &error)
{ {
assert(af.IsValid()); assert(af.IsValid());
assert(audio_valid_sample_rate(new_sample_rate)); assert(audio_valid_sample_rate(new_sample_rate));
@ -116,7 +115,7 @@ pcm_resample_fallback_void(PcmBuffer &buffer,
} }
ConstBuffer<void> ConstBuffer<void>
FallbackPcmResampler::Resample(ConstBuffer<void> src, gcc_unused Error &error) FallbackPcmResampler::Resample(ConstBuffer<void> src)
{ {
switch (format.format) { switch (format.format) {
case SampleFormat::UNDEFINED: case SampleFormat::UNDEFINED:

View File

@ -36,11 +36,9 @@ class FallbackPcmResampler final : public PcmResampler {
PcmBuffer buffer; PcmBuffer buffer;
public: public:
virtual AudioFormat Open(AudioFormat &af, unsigned new_sample_rate, AudioFormat Open(AudioFormat &af, unsigned new_sample_rate) override;
Error &error) override; void Close() override;
virtual void Close() override; ConstBuffer<void> Resample(ConstBuffer<void> src) override;
virtual ConstBuffer<void> Resample(ConstBuffer<void> src,
Error &error) override;
}; };
#endif #endif

View File

@ -20,15 +20,13 @@
#include "config.h" #include "config.h"
#include "FormatConverter.hxx" #include "FormatConverter.hxx"
#include "PcmFormat.hxx" #include "PcmFormat.hxx"
#include "Domain.hxx"
#include "util/ConstBuffer.hxx" #include "util/ConstBuffer.hxx"
#include "util/Error.hxx" #include "util/RuntimeError.hxx"
#include <assert.h> #include <assert.h>
bool void
PcmFormatConverter::Open(SampleFormat _src_format, SampleFormat _dest_format, PcmFormatConverter::Open(SampleFormat _src_format, SampleFormat _dest_format)
Error &error)
{ {
assert(_src_format != SampleFormat::UNDEFINED); assert(_src_format != SampleFormat::UNDEFINED);
assert(_dest_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::S8:
case SampleFormat::DSD: case SampleFormat::DSD:
error.Format(pcm_domain, throw FormatRuntimeError("PCM conversion from %s to %s is not implemented",
"PCM conversion from %s to %s is not implemented", sample_format_to_string(_src_format),
sample_format_to_string(_src_format), sample_format_to_string(_dest_format));
sample_format_to_string(_dest_format));
return false;
case SampleFormat::S16: case SampleFormat::S16:
case SampleFormat::S24_P32: case SampleFormat::S24_P32:
@ -55,7 +51,6 @@ PcmFormatConverter::Open(SampleFormat _src_format, SampleFormat _dest_format,
src_format = _src_format; src_format = _src_format;
dest_format = _dest_format; dest_format = _dest_format;
return true;
} }
void void
@ -68,7 +63,7 @@ PcmFormatConverter::Close()
} }
ConstBuffer<void> ConstBuffer<void>
PcmFormatConverter::Convert(ConstBuffer<void> src, gcc_unused Error &error) PcmFormatConverter::Convert(ConstBuffer<void> src)
{ {
switch (dest_format) { switch (dest_format) {
case SampleFormat::UNDEFINED: case SampleFormat::UNDEFINED:

View File

@ -29,7 +29,6 @@
#include <assert.h> #include <assert.h>
#endif #endif
class Error;
template<typename T> struct ConstBuffer; template<typename T> struct ConstBuffer;
/** /**
@ -56,13 +55,12 @@ public:
/** /**
* Opens the object, prepare for Convert(). * Opens the object, prepare for Convert().
* *
* Throws std::runtime_error on error.
*
* @param src_format the sample format of incoming data * @param src_format the sample format of incoming data
* @param dest_format the sample format of outgoing 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, void Open(SampleFormat src_format, SampleFormat dest_format);
Error &error);
/** /**
* Closes the object. After that, you may call Open() again. * Closes the object. After that, you may call Open() again.
@ -72,13 +70,13 @@ public:
/** /**
* Convert a block of PCM data. * Convert a block of PCM data.
* *
* Throws std::runtime_error on error.
*
* @param src the input buffer * @param src the input buffer
* @param error location to store the error * @return the destination buffer
* @return the destination buffer on success,
* ConstBuffer::Null() on error
*/ */
gcc_pure gcc_pure
ConstBuffer<void> Convert(ConstBuffer<void> src, Error &error); ConstBuffer<void> Convert(ConstBuffer<void> src);
}; };
#endif #endif

View File

@ -32,33 +32,28 @@ GluePcmResampler::~GluePcmResampler()
delete resampler; delete resampler;
} }
bool void
GluePcmResampler::Open(AudioFormat src_format, unsigned new_sample_rate, GluePcmResampler::Open(AudioFormat src_format, unsigned new_sample_rate)
Error &error)
{ {
assert(src_format.IsValid()); assert(src_format.IsValid());
assert(audio_valid_sample_rate(new_sample_rate)); assert(audio_valid_sample_rate(new_sample_rate));
AudioFormat requested_format = src_format; AudioFormat requested_format = src_format;
AudioFormat dest_format = resampler->Open(requested_format, AudioFormat dest_format = resampler->Open(requested_format,
new_sample_rate, new_sample_rate);
error); assert(dest_format.IsValid());
if (!dest_format.IsValid())
return false;
assert(requested_format.channels == src_format.channels); assert(requested_format.channels == src_format.channels);
assert(dest_format.channels == src_format.channels); assert(dest_format.channels == src_format.channels);
assert(dest_format.sample_rate == new_sample_rate); assert(dest_format.sample_rate == new_sample_rate);
if (requested_format.format != src_format.format && if (requested_format.format != src_format.format)
!format_converter.Open(src_format.format, requested_format.format, format_converter.Open(src_format.format,
error)) requested_format.format);
return false;
src_sample_format = src_format.format; src_sample_format = src_format.format;
requested_sample_format = requested_format.format; requested_sample_format = requested_format.format;
output_sample_format = dest_format.format; output_sample_format = dest_format.format;
return true;
} }
void void
@ -71,15 +66,12 @@ GluePcmResampler::Close()
} }
ConstBuffer<void> ConstBuffer<void>
GluePcmResampler::Resample(ConstBuffer<void> src, Error &error) GluePcmResampler::Resample(ConstBuffer<void> src)
{ {
assert(!src.IsNull()); assert(!src.IsNull());
if (requested_sample_format != src_sample_format) { if (requested_sample_format != src_sample_format)
src = format_converter.Convert(src, error); src = format_converter.Convert(src);
if (src.IsNull())
return nullptr;
}
return resampler->Resample(src, error); return resampler->Resample(src);
} }

View File

@ -24,7 +24,6 @@
#include "AudioFormat.hxx" #include "AudioFormat.hxx"
#include "FormatConverter.hxx" #include "FormatConverter.hxx"
class Error;
class PcmResampler; class PcmResampler;
template<typename T> struct ConstBuffer; template<typename T> struct ConstBuffer;
@ -49,15 +48,14 @@ public:
GluePcmResampler(); GluePcmResampler();
~GluePcmResampler(); ~GluePcmResampler();
bool Open(AudioFormat src_format, unsigned new_sample_rate, void Open(AudioFormat src_format, unsigned new_sample_rate);
Error &error);
void Close(); void Close();
SampleFormat GetOutputSampleFormat() const { SampleFormat GetOutputSampleFormat() const {
return output_sample_format; return output_sample_format;
} }
ConstBuffer<void> Resample(ConstBuffer<void> src, Error &error); ConstBuffer<void> Resample(ConstBuffer<void> src);
}; };
#endif #endif

View File

@ -21,7 +21,7 @@
#include "LibsamplerateResampler.hxx" #include "LibsamplerateResampler.hxx"
#include "config/Block.hxx" #include "config/Block.hxx"
#include "util/ASCII.hxx" #include "util/ASCII.hxx"
#include "util/Error.hxx" #include "util/RuntimeError.hxx"
#include "util/Domain.hxx" #include "util/Domain.hxx"
#include "Log.hxx" #include "Log.hxx"
@ -63,26 +63,21 @@ lsr_parse_converter(const char *s)
return false; return false;
} }
bool void
pcm_resample_lsr_global_init(const ConfigBlock &block, Error &error) pcm_resample_lsr_global_init(const ConfigBlock &block)
{ {
const char *converter = block.GetBlockValue("type", "2"); const char *converter = block.GetBlockValue("type", "2");
if (!lsr_parse_converter(converter)) { if (!lsr_parse_converter(converter))
error.Format(libsamplerate_domain, throw FormatRuntimeError("unknown samplerate converter '%s'",
"unknown samplerate converter '%s'", converter); converter);
return false;
}
FormatDebug(libsamplerate_domain, FormatDebug(libsamplerate_domain,
"libsamplerate converter '%s'", "libsamplerate converter '%s'",
src_get_name(lsr_converter)); src_get_name(lsr_converter));
return true;
} }
AudioFormat AudioFormat
LibsampleratePcmResampler::Open(AudioFormat &af, unsigned new_sample_rate, LibsampleratePcmResampler::Open(AudioFormat &af, unsigned new_sample_rate)
Error &error)
{ {
assert(af.IsValid()); assert(af.IsValid());
assert(audio_valid_sample_rate(new_sample_rate)); assert(audio_valid_sample_rate(new_sample_rate));
@ -96,12 +91,9 @@ LibsampleratePcmResampler::Open(AudioFormat &af, unsigned new_sample_rate,
int src_error; int src_error;
state = src_new(lsr_converter, channels, &src_error); state = src_new(lsr_converter, channels, &src_error);
if (!state) { if (!state)
error.Format(libsamplerate_domain, src_error, throw FormatRuntimeError("libsamplerate initialization has failed: %s",
"libsamplerate initialization has failed: %s", src_strerror(src_error));
src_strerror(src_error));
return AudioFormat::Undefined();
}
memset(&data, 0, sizeof(data)); memset(&data, 0, sizeof(data));
@ -122,22 +114,8 @@ LibsampleratePcmResampler::Close()
state = src_delete(state); 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> inline ConstBuffer<float>
LibsampleratePcmResampler::Resample2(ConstBuffer<float> src, Error &error) LibsampleratePcmResampler::Resample2(ConstBuffer<float> src)
{ {
assert(src.size % channels == 0); assert(src.size % channels == 0);
@ -151,15 +129,17 @@ LibsampleratePcmResampler::Resample2(ConstBuffer<float> src, Error &error)
data.input_frames = src_frames; data.input_frames = src_frames;
data.output_frames = dest_frames; data.output_frames = dest_frames;
if (!src_process(state, &data, error)) int result = src_process(state, &data);
return nullptr; if (result != 0)
throw FormatRuntimeError("libsamplerate has failed: %s",
src_strerror(result));
return ConstBuffer<float>(data.data_out, return ConstBuffer<float>(data.data_out,
data.output_frames_gen * channels); data.output_frames_gen * channels);
} }
ConstBuffer<void> 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();
} }

View File

@ -42,17 +42,15 @@ class LibsampleratePcmResampler final : public PcmResampler {
PcmBuffer buffer; PcmBuffer buffer;
public: public:
virtual AudioFormat Open(AudioFormat &af, unsigned new_sample_rate, AudioFormat Open(AudioFormat &af, unsigned new_sample_rate) override;
Error &error) override; void Close() override;
virtual void Close() override; ConstBuffer<void> Resample(ConstBuffer<void> src) override;
virtual ConstBuffer<void> Resample(ConstBuffer<void> src,
Error &error) override;
private: private:
ConstBuffer<float> Resample2(ConstBuffer<float> src, Error &error); ConstBuffer<float> Resample2(ConstBuffer<float> src);
}; };
bool void
pcm_resample_lsr_global_init(const ConfigBlock &block, Error &error); pcm_resample_lsr_global_init(const ConfigBlock &block);
#endif #endif

View File

@ -22,15 +22,14 @@
#include "Domain.hxx" #include "Domain.hxx"
#include "ConfiguredResampler.hxx" #include "ConfiguredResampler.hxx"
#include "AudioFormat.hxx" #include "AudioFormat.hxx"
#include "util/Error.hxx"
#include "util/ConstBuffer.hxx" #include "util/ConstBuffer.hxx"
#include <assert.h> #include <assert.h>
bool void
pcm_convert_global_init(Error &error) pcm_convert_global_init()
{ {
return pcm_resampler_global_init(error); pcm_resampler_global_init();
} }
PcmConvert::PcmConvert() PcmConvert::PcmConvert()
@ -47,9 +46,8 @@ PcmConvert::~PcmConvert()
assert(!dest_format.IsValid()); assert(!dest_format.IsValid());
} }
bool void
PcmConvert::Open(const AudioFormat _src_format, const AudioFormat _dest_format, PcmConvert::Open(const AudioFormat _src_format, const AudioFormat _dest_format)
Error &error)
{ {
assert(!src_format.IsValid()); assert(!src_format.IsValid());
assert(!dest_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; enable_resampler = format.sample_rate != _dest_format.sample_rate;
if (enable_resampler) { if (enable_resampler) {
if (!resampler.Open(format, _dest_format.sample_rate, error)) resampler.Open(format, _dest_format.sample_rate);
return false;
format.format = resampler.GetOutputSampleFormat(); format.format = resampler.GetOutputSampleFormat();
format.sample_rate = _dest_format.sample_rate; format.sample_rate = _dest_format.sample_rate;
} }
enable_format = format.format != _dest_format.format; enable_format = format.format != _dest_format.format;
if (enable_format && if (enable_format) {
!format_converter.Open(format.format, _dest_format.format, try {
error)) { format_converter.Open(format.format,
if (enable_resampler) _dest_format.format);
resampler.Close(); } catch (...) {
return false; if (enable_resampler)
resampler.Close();
throw;
}
} }
format.format = _dest_format.format; format.format = _dest_format.format;
enable_channels = format.channels != _dest_format.channels; enable_channels = format.channels != _dest_format.channels;
if (enable_channels && if (enable_channels) {
!channels_converter.Open(format.format, format.channels, try {
_dest_format.channels, error)) { channels_converter.Open(format.format, format.channels,
if (enable_format) _dest_format.channels);
format_converter.Close(); } catch (...) {
if (enable_resampler) if (enable_format)
resampler.Close(); format_converter.Close();
return false; if (enable_resampler)
resampler.Close();
throw;
}
} }
src_format = _src_format; src_format = _src_format;
dest_format = _dest_format; dest_format = _dest_format;
return true;
} }
void void
@ -118,39 +119,27 @@ PcmConvert::Close()
} }
ConstBuffer<void> ConstBuffer<void>
PcmConvert::Convert(ConstBuffer<void> buffer, Error &error) PcmConvert::Convert(ConstBuffer<void> buffer)
{ {
#ifdef ENABLE_DSD #ifdef ENABLE_DSD
if (src_format.format == SampleFormat::DSD) { if (src_format.format == SampleFormat::DSD) {
auto s = ConstBuffer<uint8_t>::FromVoid(buffer); auto s = ConstBuffer<uint8_t>::FromVoid(buffer);
auto d = dsd.ToFloat(src_format.channels, s); auto d = dsd.ToFloat(src_format.channels, s);
if (d.IsNull()) { if (d.IsNull())
error.Set(pcm_domain, throw std::runtime_error("DSD to PCM conversion failed");
"DSD to PCM conversion failed");
return nullptr;
}
buffer = d.ToVoid(); buffer = d.ToVoid();
} }
#endif #endif
if (enable_resampler) { if (enable_resampler)
buffer = resampler.Resample(buffer, error); buffer = resampler.Resample(buffer);
if (buffer.IsNull())
return nullptr;
}
if (enable_format) { if (enable_format)
buffer = format_converter.Convert(buffer, error); buffer = format_converter.Convert(buffer);
if (buffer.IsNull())
return nullptr;
}
if (enable_channels) { if (enable_channels)
buffer = channels_converter.Convert(buffer, error); buffer = channels_converter.Convert(buffer);
if (buffer.IsNull())
return nullptr;
}
return buffer; return buffer;
} }

View File

@ -31,7 +31,6 @@
#endif #endif
template<typename T> struct ConstBuffer; template<typename T> struct ConstBuffer;
class Error;
/** /**
* This object is statically allocated (within another struct), and * This object is statically allocated (within another struct), and
@ -57,9 +56,10 @@ public:
/** /**
* Prepare the object. Call Close() when done. * Prepare the object. Call Close() when done.
*
* Throws std::runtime_error on error.
*/ */
bool Open(AudioFormat _src_format, AudioFormat _dest_format, void Open(AudioFormat _src_format, AudioFormat _dest_format);
Error &error);
/** /**
* Close the object after it was prepared with Open(). After * Close the object after it was prepared with Open(). After
@ -70,14 +70,15 @@ public:
/** /**
* Converts PCM data between two audio formats. * Converts PCM data between two audio formats.
* *
* Throws std::runtime_error on error.
*
* @param src the source PCM buffer * @param src the source PCM buffer
* @param error location to store the error occurring * @return the destination buffer
* @return the destination buffer, or nullptr on error
*/ */
ConstBuffer<void> Convert(ConstBuffer<void> src, Error &error); ConstBuffer<void> Convert(ConstBuffer<void> src);
}; };
bool void
pcm_convert_global_init(Error &error); pcm_convert_global_init();
#endif #endif

View File

@ -24,7 +24,6 @@
#include "Compiler.h" #include "Compiler.h"
struct AudioFormat; struct AudioFormat;
class Error;
/** /**
* This is an interface for plugins that convert PCM data to a * This is an interface for plugins that convert PCM data to a
@ -37,16 +36,17 @@ public:
/** /**
* Opens the resampler, preparing it for Resample(). * Opens the resampler, preparing it for Resample().
* *
* Throws std::runtime_error on error.
*
* @param af the audio format of incoming data; the plugin may * @param af the audio format of incoming data; the plugin may
* modify the object to enforce another input format (however, * modify the object to enforce another input format (however,
* it may not request a different input sample rate) * it may not request a different input sample rate)
* @param new_sample_rate the requested output sample rate * @param new_sample_rate the requested output sample rate
* @param error location to store the error * @param error location to store the error
* @return the format of outgoing data or * @return the format of outgoing data
* AudioFormat::Undefined() on error
*/ */
virtual AudioFormat Open(AudioFormat &af, unsigned new_sample_rate, virtual AudioFormat Open(AudioFormat &af,
Error &error) = 0; unsigned new_sample_rate) = 0;
/** /**
* Closes the resampler. After that, you may call Open() * Closes the resampler. After that, you may call Open()
@ -58,14 +58,11 @@ public:
* Resamples a block of PCM data. * Resamples a block of PCM data.
* *
* @param src the input buffer * @param src the input buffer
* @param error location to store the error occurring * @return the destination buffer (will be invalidated by
* @return the destination buffer on success (will be * filter_close() or filter_filter())
* invalidated by filter_close() or filter_filter()), nullptr on
* error
*/ */
gcc_pure gcc_pure
virtual ConstBuffer<void> Resample(ConstBuffer<void> src, virtual ConstBuffer<void> Resample(ConstBuffer<void> src) = 0;
Error &error) = 0;
}; };
#endif #endif

View File

@ -21,7 +21,7 @@
#include "SoxrResampler.hxx" #include "SoxrResampler.hxx"
#include "AudioFormat.hxx" #include "AudioFormat.hxx"
#include "config/Block.hxx" #include "config/Block.hxx"
#include "util/Error.hxx" #include "util/RuntimeError.hxx"
#include "util/Domain.hxx" #include "util/Domain.hxx"
#include "Log.hxx" #include "Log.hxx"
@ -80,18 +80,16 @@ soxr_parse_quality(const char *quality)
return SOXR_INVALID_RECIPE; return SOXR_INVALID_RECIPE;
} }
bool void
pcm_resample_soxr_global_init(const ConfigBlock &block, Error &error) pcm_resample_soxr_global_init(const ConfigBlock &block)
{ {
const char *quality_string = block.GetBlockValue("quality"); const char *quality_string = block.GetBlockValue("quality");
unsigned long recipe = soxr_parse_quality(quality_string); unsigned long recipe = soxr_parse_quality(quality_string);
if (recipe == SOXR_INVALID_RECIPE) { if (recipe == SOXR_INVALID_RECIPE) {
assert(quality_string != nullptr); assert(quality_string != nullptr);
error.Format(soxr_domain, throw FormatRuntimeError("unknown quality setting '%s' in line %d",
"unknown quality setting '%s' in line %d", quality_string, block.line);
quality_string, block.line);
return false;
} }
soxr_quality = soxr_quality_spec(recipe, 0); 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); const unsigned n_threads = block.GetBlockValue("threads", 1);
soxr_runtime = soxr_runtime_spec(n_threads); soxr_runtime = soxr_runtime_spec(n_threads);
return true;
} }
AudioFormat AudioFormat
SoxrPcmResampler::Open(AudioFormat &af, unsigned new_sample_rate, SoxrPcmResampler::Open(AudioFormat &af, unsigned new_sample_rate)
Error &error)
{ {
assert(af.IsValid()); assert(af.IsValid());
assert(audio_valid_sample_rate(new_sample_rate)); 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, soxr = soxr_create(af.sample_rate, new_sample_rate,
af.channels, &e, af.channels, &e,
nullptr, &soxr_quality, &soxr_runtime); nullptr, &soxr_quality, &soxr_runtime);
if (soxr == nullptr) { if (soxr == nullptr)
error.Format(soxr_domain, throw FormatRuntimeError("soxr initialization has failed: %s",
"soxr initialization has failed: %s", e); e);
return AudioFormat::Undefined();
}
FormatDebug(soxr_domain, "soxr engine '%s'", soxr_engine(soxr)); FormatDebug(soxr_domain, "soxr engine '%s'", soxr_engine(soxr));
@ -147,7 +140,7 @@ SoxrPcmResampler::Close()
} }
ConstBuffer<void> ConstBuffer<void>
SoxrPcmResampler::Resample(ConstBuffer<void> src, Error &error) SoxrPcmResampler::Resample(ConstBuffer<void> src)
{ {
const size_t frame_size = channels * sizeof(float); const size_t frame_size = channels * sizeof(float);
assert(src.size % frame_size == 0); assert(src.size % frame_size == 0);
@ -162,10 +155,8 @@ SoxrPcmResampler::Resample(ConstBuffer<void> src, Error &error)
size_t i_done, o_done; size_t i_done, o_done;
soxr_error_t e = soxr_process(soxr, src.data, n_frames, &i_done, soxr_error_t e = soxr_process(soxr, src.data, n_frames, &i_done,
output_buffer, o_frames, &o_done); output_buffer, o_frames, &o_done);
if (e != nullptr) { if (e != nullptr)
error.Format(soxr_domain, "soxr error: %s", e); throw FormatRuntimeError("soxr error: %s", e);
return nullptr;
}
return { output_buffer, o_done * frame_size }; return { output_buffer, o_done * frame_size };
} }

View File

@ -39,14 +39,12 @@ class SoxrPcmResampler final : public PcmResampler {
PcmBuffer buffer; PcmBuffer buffer;
public: public:
virtual AudioFormat Open(AudioFormat &af, unsigned new_sample_rate, AudioFormat Open(AudioFormat &af, unsigned new_sample_rate) override;
Error &error) override; void Close() override;
virtual void Close() override; ConstBuffer<void> Resample(ConstBuffer<void> src) override;
virtual ConstBuffer<void> Resample(ConstBuffer<void> src,
Error &error) override;
}; };
bool void
pcm_resample_soxr_global_init(const ConfigBlock &block, Error &error); pcm_resample_soxr_global_init(const ConfigBlock &block);
#endif #endif

View File

@ -24,7 +24,7 @@
#include "Traits.hxx" #include "Traits.hxx"
#include "util/ConstBuffer.hxx" #include "util/ConstBuffer.hxx"
#include "util/WritableBuffer.hxx" #include "util/WritableBuffer.hxx"
#include "util/Error.hxx" #include "util/RuntimeError.hxx"
#include "PcmDither.cxx" // including the .cxx file to get inlined templates #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; dest[i] = src[i] * volume;
} }
bool void
PcmVolume::Open(SampleFormat _format, Error &error) PcmVolume::Open(SampleFormat _format)
{ {
assert(format == SampleFormat::UNDEFINED); assert(format == SampleFormat::UNDEFINED);
switch (_format) { switch (_format) {
case SampleFormat::UNDEFINED: case SampleFormat::UNDEFINED:
error.Format(pcm_domain, throw FormatRuntimeError("Software volume for %s is not implemented",
"Software volume for %s is not implemented", sample_format_to_string(_format));
sample_format_to_string(_format));
return false;
case SampleFormat::S8: case SampleFormat::S8:
case SampleFormat::S16: case SampleFormat::S16:
@ -122,7 +120,6 @@ PcmVolume::Open(SampleFormat _format, Error &error)
} }
format = _format; format = _format;
return true;
} }
ConstBuffer<void> ConstBuffer<void>

View File

@ -28,7 +28,6 @@
#include <assert.h> #include <assert.h>
#endif #endif
class Error;
template<typename T> struct ConstBuffer; template<typename T> struct ConstBuffer;
/** /**
@ -93,11 +92,11 @@ public:
/** /**
* Opens the object, prepare for Apply(). * Opens the object, prepare for Apply().
* *
* Throws std::runtime_error on error.
*
* @param format the sample format * @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. * Closes the object. After that, you may call Open() again.

View File

@ -38,8 +38,9 @@
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
int main(int argc, char **argv) int
{ main(int argc, char **argv)
try {
AudioFormat in_audio_format, out_audio_format; AudioFormat in_audio_format, out_audio_format;
if (argc != 3) { if (argc != 3) {
@ -68,10 +69,7 @@ int main(int argc, char **argv)
const size_t in_frame_size = in_audio_format.GetFrameSize(); const size_t in_frame_size = in_audio_format.GetFrameSize();
PcmConvert state; PcmConvert state;
if (!state.Open(in_audio_format, out_audio_format_mask, error)) { state.Open(in_audio_format, out_audio_format_mask);
LogError(error, "Failed to open PcmConvert");
return EXIT_FAILURE;
}
StaticFifoBuffer<uint8_t, 4096> buffer; StaticFifoBuffer<uint8_t, 4096> buffer;
@ -96,12 +94,7 @@ int main(int argc, char **argv)
buffer.Consume(src.size); buffer.Consume(src.size);
auto output = state.Convert({src.data, src.size}, error); auto output = state.Convert({src.data, src.size});
if (output.IsNull()) {
state.Close();
LogError(error, "Failed to convert");
return EXIT_FAILURE;
}
gcc_unused ssize_t ignored = write(1, output.data, gcc_unused ssize_t ignored = write(1, output.data,
output.size); output.size);
@ -110,4 +103,7 @@ int main(int argc, char **argv)
state.Close(); state.Close();
return EXIT_SUCCESS; return EXIT_SUCCESS;
} catch (const std::exception &e) {
LogError(e);
return EXIT_FAILURE;
} }

View File

@ -36,8 +36,9 @@
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
int main(int argc, char **argv) int
{ main(int argc, char **argv)
try {
static char buffer[4096]; static char buffer[4096];
ssize_t nbytes; ssize_t nbytes;
@ -56,10 +57,7 @@ int main(int argc, char **argv)
} }
PcmVolume pv; PcmVolume pv;
if (!pv.Open(audio_format.format, error)) { pv.Open(audio_format.format);
fprintf(stderr, "%s\n", error.GetMessage());
return EXIT_FAILURE;
}
while ((nbytes = read(0, buffer, sizeof(buffer))) > 0) { while ((nbytes = read(0, buffer, sizeof(buffer))) > 0) {
auto dest = pv.Apply({buffer, size_t(nbytes)}); auto dest = pv.Apply({buffer, size_t(nbytes)});
@ -67,4 +65,7 @@ int main(int argc, char **argv)
} }
pv.Close(); pv.Close();
} catch (const std::exception &e) {
LogError(e);
return EXIT_FAILURE;
} }

View File

@ -22,7 +22,6 @@
#include "pcm/Volume.hxx" #include "pcm/Volume.hxx"
#include "pcm/Traits.hxx" #include "pcm/Traits.hxx"
#include "util/ConstBuffer.hxx" #include "util/ConstBuffer.hxx"
#include "util/Error.hxx"
#include "test_pcm_util.hxx" #include "test_pcm_util.hxx"
#include <algorithm> #include <algorithm>
@ -37,7 +36,7 @@ TestVolume(G g=G())
typedef typename Traits::value_type value_type; typedef typename Traits::value_type value_type;
PcmVolume pv; PcmVolume pv;
CPPUNIT_ASSERT(pv.Open(F, IgnoreError())); pv.Open(F);
constexpr size_t N = 509; constexpr size_t N = 509;
static value_type zero[N]; static value_type zero[N];
@ -96,7 +95,7 @@ void
PcmVolumeTest::TestVolumeFloat() PcmVolumeTest::TestVolumeFloat()
{ {
PcmVolume pv; PcmVolume pv;
CPPUNIT_ASSERT(pv.Open(SampleFormat::FLOAT, IgnoreError())); pv.Open(SampleFormat::FLOAT);
constexpr size_t N = 509; constexpr size_t N = 509;
static float zero[N]; static float zero[N];