Commit d2679f59 authored by Max Kellermann's avatar Max Kellermann

PcmConvert: add methods Open(), Close()

Replaces Reset() and eliminates the AudioFormat parameters from the Convert() method.
parent 4ee147ea
......@@ -71,6 +71,12 @@ decoder_initialized(Decoder &decoder,
&af_string));
decoder.convert = new PcmConvert();
Error error;
if (!decoder.convert->Open(dc.in_audio_format,
dc.out_audio_format,
error))
decoder.error = std::move(error);
}
dc.Lock();
......@@ -401,9 +407,7 @@ decoder_data(Decoder &decoder,
assert(dc.in_audio_format != dc.out_audio_format);
Error error;
data = decoder.convert->Convert(dc.in_audio_format,
data, length,
dc.out_audio_format,
data = decoder.convert->Convert(data, length,
&length,
error);
if (data == nullptr) {
......
......@@ -33,7 +33,11 @@ Decoder::~Decoder()
/* caller must flush the chunk */
assert(chunk == nullptr);
delete convert;
if (convert != nullptr) {
convert->Close();
delete convert;
}
delete song_tag;
delete stream_tag;
delete decoder_tag;
......
......@@ -184,7 +184,15 @@ ao_open(struct audio_output *ao)
return;
}
convert_filter_set(ao->convert_filter, ao->out_audio_format);
if (!convert_filter_set(ao->convert_filter, ao->out_audio_format,
error)) {
FormatError(error, "Failed to convert for \"%s\" [%s]",
ao->name, ao->plugin->name);
ao_filter_close(ao);
ao->fail_timer = g_timer_new();
return;
}
ao->open = true;
......@@ -233,7 +241,9 @@ ao_reopen_filter(struct audio_output *ao)
ao_filter_close(ao);
const AudioFormat filter_audio_format =
ao_filter_open(ao, ao->in_audio_format, error);
if (!filter_audio_format.IsDefined()) {
if (!filter_audio_format.IsDefined() ||
!convert_filter_set(ao->convert_filter, ao->out_audio_format,
error)) {
FormatError(error,
"Failed to open filter for \"%s\" [%s]",
ao->name, ao->plugin->name);
......@@ -254,8 +264,6 @@ ao_reopen_filter(struct audio_output *ao)
return;
}
convert_filter_set(ao->convert_filter, ao->out_audio_format);
}
static void
......
......@@ -88,7 +88,11 @@ AutoConvertFilter::Open(AudioFormat &in_audio_format, Error &error)
assert(audio_format2 == in_audio_format);
convert_filter_set(convert, child_audio_format);
if (!convert_filter_set(convert, child_audio_format, error)) {
delete convert;
filter->Close();
return AudioFormat::Undefined();
}
} else
/* no */
convert = nullptr;
......
......@@ -39,21 +39,18 @@ class ConvertFilter final : public Filter {
/**
* The output audio format; the consumer of this plugin
* expects PCM data in this format. This defaults to
* #in_audio_format, and can be set with convert_filter_set().
* expects PCM data in this format.
*
* If this is AudioFormat::Undefined(), then the #PcmConvert
* attribute is not open. This can mean that Set() has failed
* or that no conversion is necessary.
*/
AudioFormat out_audio_format;
Manual<PcmConvert> state;
public:
void Set(const AudioFormat &_out_audio_format) {
assert(in_audio_format.IsValid());
assert(out_audio_format.IsValid());
assert(_out_audio_format.IsValid());
out_audio_format = _out_audio_format;
}
bool Set(const AudioFormat &_out_audio_format, Error &error);
virtual AudioFormat Open(AudioFormat &af, Error &error) override;
virtual void Close() override;
......@@ -69,12 +66,40 @@ convert_filter_init(gcc_unused const config_param &param,
return new ConvertFilter();
}
bool
ConvertFilter::Set(const AudioFormat &_out_audio_format, Error &error)
{
assert(in_audio_format.IsValid());
assert(_out_audio_format.IsValid());
if (_out_audio_format == out_audio_format)
/* no change */
return true;
if (out_audio_format.IsValid()) {
out_audio_format.Clear();
state->Close();
}
if (_out_audio_format == in_audio_format)
/* optimized special case: no-op */
return true;
if (!state->Open(in_audio_format, _out_audio_format, error))
return false;
out_audio_format = _out_audio_format;
return true;
}
AudioFormat
ConvertFilter::Open(AudioFormat &audio_format, gcc_unused Error &error)
{
assert(audio_format.IsValid());
in_audio_format = out_audio_format = audio_format;
in_audio_format = audio_format;
out_audio_format.Clear();
state.Construct();
return in_audio_format;
......@@ -83,6 +108,11 @@ ConvertFilter::Open(AudioFormat &audio_format, gcc_unused Error &error)
void
ConvertFilter::Close()
{
assert(in_audio_format.IsValid());
if (out_audio_format.IsValid())
state->Close();
state.Destruct();
poison_undefined(&in_audio_format, sizeof(in_audio_format));
......@@ -93,15 +123,15 @@ const void *
ConvertFilter::FilterPCM(const void *src, size_t src_size,
size_t *dest_size_r, Error &error)
{
if (in_audio_format == out_audio_format) {
assert(in_audio_format.IsValid());
if (!out_audio_format.IsValid()) {
/* optimized special case: no-op */
*dest_size_r = src_size;
return src;
}
return state->Convert(in_audio_format,
src, src_size,
out_audio_format, dest_size_r,
return state->Convert(src, src_size, dest_size_r,
error);
}
......@@ -110,10 +140,11 @@ const struct filter_plugin convert_filter_plugin = {
convert_filter_init,
};
void
convert_filter_set(Filter *_filter, const AudioFormat out_audio_format)
bool
convert_filter_set(Filter *_filter, AudioFormat out_audio_format,
Error &error)
{
ConvertFilter *filter = (ConvertFilter *)_filter;
filter->Set(out_audio_format);
return filter->Set(out_audio_format, error);
}
......@@ -21,6 +21,7 @@
#define MPD_CONVERT_FILTER_PLUGIN_HXX
class Filter;
class Error;
struct AudioFormat;
/**
......@@ -29,7 +30,8 @@ struct AudioFormat;
* format switch is a violation of the filter API, this filter must be
* the last in a chain.
*/
void
convert_filter_set(Filter *filter, AudioFormat out_audio_format);
bool
convert_filter_set(Filter *filter, AudioFormat out_audio_format,
Error &error);
#endif
......@@ -32,23 +32,48 @@ const Domain pcm_convert_domain("pcm_convert");
PcmConvert::PcmConvert()
{
#ifndef NDEBUG
src_format.Clear();
dest_format.Clear();
#endif
}
PcmConvert::~PcmConvert()
{
assert(!src_format.IsValid());
assert(!dest_format.IsValid());
}
bool
PcmConvert::Open(AudioFormat _src_format, AudioFormat _dest_format,
gcc_unused Error &error)
{
assert(!src_format.IsValid());
assert(!dest_format.IsValid());
assert(_src_format.IsValid());
assert(_dest_format.IsValid());
src_format = _src_format;
dest_format = _dest_format;
return true;
}
void
PcmConvert::Reset()
PcmConvert::Close()
{
dsd.Reset();
resampler.Reset();
#ifndef NDEBUG
src_format.Clear();
dest_format.Clear();
#endif
}
inline const int16_t *
PcmConvert::Convert16(const AudioFormat src_format,
const void *src_buffer, size_t src_size,
const AudioFormat dest_format, size_t *dest_size_r,
PcmConvert::Convert16(const void *src_buffer, size_t src_size,
size_t *dest_size_r,
Error &error)
{
const int16_t *buf;
......@@ -96,9 +121,8 @@ PcmConvert::Convert16(const AudioFormat src_format,
}
inline const int32_t *
PcmConvert::Convert24(const AudioFormat src_format,
const void *src_buffer, size_t src_size,
const AudioFormat dest_format, size_t *dest_size_r,
PcmConvert::Convert24(const void *src_buffer, size_t src_size,
size_t *dest_size_r,
Error &error)
{
const int32_t *buf;
......@@ -145,9 +169,8 @@ PcmConvert::Convert24(const AudioFormat src_format,
}
inline const int32_t *
PcmConvert::Convert32(const AudioFormat src_format,
const void *src_buffer, size_t src_size,
const AudioFormat dest_format, size_t *dest_size_r,
PcmConvert::Convert32(const void *src_buffer, size_t src_size,
size_t *dest_size_r,
Error &error)
{
const int32_t *buf;
......@@ -194,9 +217,8 @@ PcmConvert::Convert32(const AudioFormat src_format,
}
inline const float *
PcmConvert::ConvertFloat(const AudioFormat src_format,
const void *src_buffer, size_t src_size,
const AudioFormat dest_format, size_t *dest_size_r,
PcmConvert::ConvertFloat(const void *src_buffer, size_t src_size,
size_t *dest_size_r,
Error &error)
{
const float *buffer = (const float *)src_buffer;
......@@ -251,9 +273,7 @@ PcmConvert::ConvertFloat(const AudioFormat src_format,
}
const void *
PcmConvert::Convert(AudioFormat src_format,
const void *src, size_t src_size,
const AudioFormat dest_format,
PcmConvert::Convert(const void *src, size_t src_size,
size_t *dest_size_r,
Error &error)
{
......@@ -279,23 +299,23 @@ PcmConvert::Convert(AudioFormat src_format,
switch (dest_format.format) {
case SampleFormat::S16:
return Convert16(src_format, src, src_size,
dest_format, dest_size_r,
return Convert16(src, src_size,
dest_size_r,
error);
case SampleFormat::S24_P32:
return Convert24(src_format, src, src_size,
dest_format, dest_size_r,
return Convert24(src, src_size,
dest_size_r,
error);
case SampleFormat::S32:
return Convert32(src_format, src, src_size,
dest_format, dest_size_r,
return Convert32(src, src_size,
dest_size_r,
error);
case SampleFormat::FLOAT:
return ConvertFloat(src_format, src, src_size,
dest_format, dest_size_r,
return ConvertFloat(src, src_size,
dest_size_r,
error);
default:
......
......@@ -24,10 +24,10 @@
#include "PcmDsd.hxx"
#include "PcmResample.hxx"
#include "PcmBuffer.hxx"
#include "AudioFormat.hxx"
#include <stddef.h>
struct AudioFormat;
class Error;
/**
......@@ -48,17 +48,23 @@ class PcmConvert {
/** the buffer for converting the channel count */
PcmBuffer channels_buffer;
AudioFormat src_format, dest_format;
public:
PcmConvert();
~PcmConvert();
/**
* Prepare the object. Call Close() when done.
*/
bool Open(AudioFormat _src_format, AudioFormat _dest_format,
Error &error);
/**
* Reset the pcm_convert_state object. Use this at the
* boundary between two distinct songs and each time the
* format changes.
* Close the object after it was prepared with Open(). After
* that, it may be reused by calling Open() again.
*/
void Reset();
void Close();
/**
* Converts PCM data between two audio formats.
......@@ -72,34 +78,24 @@ public:
* ignore errors
* @return the destination buffer, or NULL on error
*/
const void *Convert(AudioFormat src_format,
const void *src, size_t src_size,
AudioFormat dest_format,
const void *Convert(const void *src, size_t src_size,
size_t *dest_size_r,
Error &error);
private:
const int16_t *Convert16(AudioFormat src_format,
const void *src_buffer, size_t src_size,
AudioFormat dest_format,
const int16_t *Convert16(const void *src_buffer, size_t src_size,
size_t *dest_size_r,
Error &error);
const int32_t *Convert24(AudioFormat src_format,
const void *src_buffer, size_t src_size,
AudioFormat dest_format,
const int32_t *Convert24(const void *src_buffer, size_t src_size,
size_t *dest_size_r,
Error &error);
const int32_t *Convert32(AudioFormat src_format,
const void *src_buffer, size_t src_size,
AudioFormat dest_format,
const int32_t *Convert32(const void *src_buffer, size_t src_size,
size_t *dest_size_r,
Error &error);
const float *ConvertFloat(AudioFormat src_format,
const void *src_buffer, size_t src_size,
AudioFormat dest_format,
const float *ConvertFloat(const void *src_buffer, size_t src_size,
size_t *dest_size_r,
Error &error);
};
......
......@@ -90,6 +90,11 @@ int main(int argc, char **argv)
const size_t in_frame_size = in_audio_format.GetFrameSize();
PcmConvert state;
if (!state.Open(in_audio_format, out_audio_format_mask, error)) {
g_printerr("Failed to open PcmConvert: %s\n",
error.GetMessage());
return EXIT_FAILURE;
}
FifoBuffer<uint8_t, 4096> buffer;
......@@ -115,9 +120,10 @@ int main(int argc, char **argv)
buffer.Consume(src.size);
size_t length;
output = state.Convert(in_audio_format, src.data, src.size,
out_audio_format, &length, error);
output = state.Convert(src.data, src.size,
&length, error);
if (output == NULL) {
state.Close();
g_printerr("Failed to convert: %s\n", error.GetMessage());
return 2;
}
......@@ -125,5 +131,7 @@ int main(int argc, char **argv)
gcc_unused ssize_t ignored = write(1, output, length);
}
state.Close();
return EXIT_SUCCESS;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment