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