encoder/Interface: move instance methods to abstract class

Rename struct Encoder to PreparedEncoder, and add a new (abstract)
class Encoder which represents one encoder instance.
This commit is contained in:
Max Kellermann 2016-05-04 09:31:21 +02:00
parent 69bf835059
commit e7edc02647
18 changed files with 777 additions and 996 deletions

View File

@ -21,30 +21,108 @@
#define MPD_ENCODER_INTERFACE_HXX #define MPD_ENCODER_INTERFACE_HXX
#include "EncoderPlugin.hxx" #include "EncoderPlugin.hxx"
#include "Compiler.h"
#include <assert.h> #include <assert.h>
#include <stddef.h>
struct Encoder { struct Tag;
class Encoder {
const bool implements_tag;
public:
explicit Encoder(bool _implements_tag)
:implements_tag(_implements_tag) {}
virtual ~Encoder() {}
bool ImplementsTag() const {
return implements_tag;
}
/**
* Ends the stream: flushes the encoder object, generate an
* end-of-stream marker (if applicable), make everything which
* might currently be buffered available by encoder_read().
*
* After this function has been called, the encoder may not be
* usable for more data, and only Read() and Close() can be
* called.
*
* @return true on success
*/
virtual bool End(gcc_unused Error &error) {
return true;
}
/**
* Flushes an encoder object, make everything which might
* currently be buffered available by Read().
*
* @return true on success
*/
virtual bool Flush(gcc_unused Error &error) {
return true;
}
/**
* Prepare for sending a tag to the encoder. This is used by
* some encoders to flush the previous sub-stream, in
* preparation to begin a new one.
*
* @return true on success
*/
virtual bool PreTag(gcc_unused Error &error) {
return true;
}
/**
* Sends a tag to the encoder.
*
* Instructions: call PreTag(); then obtain flushed data with
* Read(); finally call Tag().
*
* @param tag the tag object
* @return true on success
*/
virtual bool SendTag(gcc_unused const Tag &tag,
gcc_unused Error &error) {
return true;
}
/**
* Writes raw PCM data to the encoder.
*
* @param data the buffer containing PCM samples
* @param length the length of the buffer in bytes
* @return true on success
*/
virtual bool Write(const void *data, size_t length,
Error &error) = 0;
/**
* Reads encoded data from the encoder.
*
* Call this repeatedly until no more data is returned.
*
* @param dest the destination buffer to copy to
* @param length the maximum length of the destination buffer
* @return the number of bytes written to #dest
*/
virtual size_t Read(void *dest, size_t length) = 0;
};
struct PreparedEncoder {
const EncoderPlugin &plugin; const EncoderPlugin &plugin;
#ifndef NDEBUG explicit PreparedEncoder(const EncoderPlugin &_plugin)
bool open, pre_tag, tag, end; :plugin(_plugin) {}
#endif
explicit Encoder(const EncoderPlugin &_plugin)
:plugin(_plugin)
#ifndef NDEBUG
, open(false)
#endif
{}
/** /**
* Frees an #Encoder object. * Frees an #Encoder object.
*/ */
void Dispose() { void Dispose() {
assert(!open);
plugin.finish(this); plugin.finish(this);
} }
@ -61,191 +139,19 @@ struct Encoder {
* may modify the struct to adapt it to its abilities * may modify the struct to adapt it to its abilities
* @return true on success * @return true on success
*/ */
bool Open(AudioFormat &audio_format, Error &error) { Encoder *Open(AudioFormat &audio_format, Error &error) {
assert(!open); return plugin.open(this, audio_format, error);
bool success = plugin.open(this, audio_format, error);
#ifndef NDEBUG
open = success;
pre_tag = tag = end = false;
#endif
return success;
} }
/**
* Closes the object. This disables the encoder, and readies
* it for reusal by calling Open() again.
*/
void Close() {
assert(open);
if (plugin.close != nullptr)
plugin.close(this);
#ifndef NDEBUG
open = false;
#endif
}
}; };
/**
* Ends the stream: flushes the encoder object, generate an
* end-of-stream marker (if applicable), make everything which might
* currently be buffered available by encoder_read().
*
* After this function has been called, the encoder may not be usable
* for more data, and only encoder_read() and Encoder::Close() can be
* called.
*
* @param encoder the encoder
* @return true on success
*/
static inline bool
encoder_end(Encoder *encoder, Error &error)
{
assert(encoder->open);
assert(!encoder->end);
#ifndef NDEBUG
encoder->end = true;
#endif
/* this method is optional */
return encoder->plugin.end != nullptr
? encoder->plugin.end(encoder, error)
: true;
}
/**
* Flushes an encoder object, make everything which might currently be
* buffered available by encoder_read().
*
* @param encoder the encoder
* @return true on success
*/
static inline bool
encoder_flush(Encoder *encoder, Error &error)
{
assert(encoder->open);
assert(!encoder->pre_tag);
assert(!encoder->tag);
assert(!encoder->end);
/* this method is optional */
return encoder->plugin.flush != nullptr
? encoder->plugin.flush(encoder, error)
: true;
}
/**
* Prepare for sending a tag to the encoder. This is used by some
* encoders to flush the previous sub-stream, in preparation to begin
* a new one.
*
* @param encoder the encoder
* @return true on success
*/
static inline bool
encoder_pre_tag(Encoder *encoder, Error &error)
{
assert(encoder->open);
assert(!encoder->pre_tag);
assert(!encoder->tag);
assert(!encoder->end);
/* this method is optional */
bool success = encoder->plugin.pre_tag != nullptr
? encoder->plugin.pre_tag(encoder, error)
: true;
#ifndef NDEBUG
encoder->pre_tag = success;
#endif
return success;
}
/**
* Sends a tag to the encoder.
*
* Instructions: call encoder_pre_tag(); then obtain flushed data with
* encoder_read(); finally call encoder_tag().
*
* @param encoder the encoder
* @param tag the tag object
* @return true on success
*/
static inline bool
encoder_tag(Encoder *encoder, const Tag &tag, Error &error)
{
assert(encoder->open);
assert(!encoder->pre_tag);
assert(encoder->tag);
assert(!encoder->end);
#ifndef NDEBUG
encoder->tag = false;
#endif
/* this method is optional */
return encoder->plugin.tag != nullptr
? encoder->plugin.tag(encoder, tag, error)
: true;
}
/**
* Writes raw PCM data to the encoder.
*
* @param encoder the encoder
* @param data the buffer containing PCM samples
* @param length the length of the buffer in bytes
* @return true on success
*/
static inline bool
encoder_write(Encoder *encoder, const void *data, size_t length,
Error &error)
{
assert(encoder->open);
assert(!encoder->pre_tag);
assert(!encoder->tag);
assert(!encoder->end);
return encoder->plugin.write(encoder, data, length, error);
}
/**
* Reads encoded data from the encoder.
*
* Call this repeatedly until no more data is returned.
*
* @param encoder the encoder
* @param dest the destination buffer to copy to
* @param length the maximum length of the destination buffer
* @return the number of bytes written to #dest
*/
static inline size_t
encoder_read(Encoder *encoder, void *dest, size_t length)
{
assert(encoder->open);
assert(!encoder->pre_tag || !encoder->tag);
#ifndef NDEBUG
if (encoder->pre_tag) {
encoder->pre_tag = false;
encoder->tag = true;
}
#endif
return encoder->plugin.read(encoder, dest, length);
}
/** /**
* Get mime type of encoded content. * Get mime type of encoded content.
* *
* @return an constant string, nullptr on failure * @return an constant string, nullptr on failure
*/ */
static inline const char * static inline const char *
encoder_get_mime_type(Encoder *encoder) encoder_get_mime_type(PreparedEncoder *encoder)
{ {
/* this method is optional */ /* this method is optional */
return encoder->plugin.get_mime_type != nullptr return encoder->plugin.get_mime_type != nullptr

View File

@ -20,44 +20,25 @@
#ifndef MPD_ENCODER_PLUGIN_HXX #ifndef MPD_ENCODER_PLUGIN_HXX
#define MPD_ENCODER_PLUGIN_HXX #define MPD_ENCODER_PLUGIN_HXX
#include <stddef.h> struct PreparedEncoder;
class Encoder;
struct Encoder;
struct AudioFormat; struct AudioFormat;
struct ConfigBlock; struct ConfigBlock;
struct Tag;
class Error; class Error;
struct EncoderPlugin { struct EncoderPlugin {
const char *name; const char *name;
Encoder *(*init)(const ConfigBlock &block, PreparedEncoder *(*init)(const ConfigBlock &block,
Error &error);
void (*finish)(PreparedEncoder *encoder);
Encoder *(*open)(PreparedEncoder *encoder,
AudioFormat &audio_format,
Error &error); Error &error);
void (*finish)(Encoder *encoder); const char *(*get_mime_type)(PreparedEncoder *encoder);
bool (*open)(Encoder *encoder,
AudioFormat &audio_format,
Error &error);
void (*close)(Encoder *encoder);
bool (*end)(Encoder *encoder, Error &error);
bool (*flush)(Encoder *encoder, Error &error);
bool (*pre_tag)(Encoder *encoder, Error &error);
bool (*tag)(Encoder *encoder, const Tag &tag,
Error &error);
bool (*write)(Encoder *encoder,
const void *data, size_t length,
Error &error);
size_t (*read)(Encoder *encoder, void *dest, size_t length);
const char *(*get_mime_type)(Encoder *encoder);
}; };
/** /**
@ -67,7 +48,7 @@ struct EncoderPlugin {
* @param error location to store the error occurring, or nullptr to ignore errors. * @param error location to store the error occurring, or nullptr to ignore errors.
* @return an encoder object on success, nullptr on failure * @return an encoder object on success, nullptr on failure
*/ */
static inline Encoder * static inline PreparedEncoder *
encoder_init(const EncoderPlugin &plugin, const ConfigBlock &block, encoder_init(const EncoderPlugin &plugin, const ConfigBlock &block,
Error &error) Error &error)
{ {

View File

@ -29,7 +29,7 @@ EncoderToOutputStream(OutputStream &os, Encoder &encoder)
/* read from the encoder */ /* read from the encoder */
char buffer[32768]; char buffer[32768];
size_t nbytes = encoder_read(&encoder, buffer, sizeof(buffer)); size_t nbytes = encoder.Read(buffer, sizeof(buffer));
if (nbytes == 0) if (nbytes == 0)
return; return;

View File

@ -22,8 +22,8 @@
#include "check.h" #include "check.h"
struct Encoder;
class OutputStream; class OutputStream;
class Encoder;
void void
EncoderToOutputStream(OutputStream &os, Encoder &encoder); EncoderToOutputStream(OutputStream &os, Encoder &encoder);

View File

@ -23,7 +23,6 @@
#include "AudioFormat.hxx" #include "AudioFormat.hxx"
#include "pcm/PcmBuffer.hxx" #include "pcm/PcmBuffer.hxx"
#include "config/ConfigError.hxx" #include "config/ConfigError.hxx"
#include "util/Manual.hxx"
#include "util/DynamicFifoBuffer.hxx" #include "util/DynamicFifoBuffer.hxx"
#include "util/Error.hxx" #include "util/Error.hxx"
#include "util/Domain.hxx" #include "util/Domain.hxx"
@ -34,13 +33,10 @@
#error libFLAC is too old #error libFLAC is too old
#endif #endif
struct flac_encoder { class FlacEncoder final : public Encoder {
Encoder encoder; const AudioFormat audio_format;
AudioFormat audio_format; FLAC__StreamEncoder *const fse;
unsigned compression;
FLAC__StreamEncoder *fse;
PcmBuffer expand_buffer; PcmBuffer expand_buffer;
@ -48,29 +44,76 @@ struct flac_encoder {
* This buffer will hold encoded data from libFLAC until it is * This buffer will hold encoded data from libFLAC until it is
* picked up with flac_encoder_read(). * picked up with flac_encoder_read().
*/ */
Manual<DynamicFifoBuffer<uint8_t>> output_buffer; DynamicFifoBuffer<uint8_t> output_buffer;
flac_encoder():encoder(flac_encoder_plugin) {} public:
FlacEncoder(AudioFormat _audio_format, FLAC__StreamEncoder *_fse)
:Encoder(false),
audio_format(_audio_format), fse(_fse),
output_buffer(8192) {}
~FlacEncoder() override {
FLAC__stream_encoder_delete(fse);
}
bool Init(Error &error);
/* virtual methods from class Encoder */
bool End(Error &) override {
(void) FLAC__stream_encoder_finish(fse);
return true;
}
bool Flush(Error &) override {
(void) FLAC__stream_encoder_finish(fse);
return true;
}
bool Write(const void *data, size_t length, Error &) override;
size_t Read(void *dest, size_t length) override {
return output_buffer.Read((uint8_t *)dest, length);
}
private:
static FLAC__StreamEncoderWriteStatus WriteCallback(const FLAC__StreamEncoder *,
const FLAC__byte data[],
size_t bytes,
gcc_unused unsigned samples,
gcc_unused unsigned current_frame,
void *client_data) {
auto &encoder = *(FlacEncoder *)client_data;
encoder.output_buffer.Append((const uint8_t *)data, bytes);
return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
}
};
struct PreparedFlacEncoder {
PreparedEncoder encoder;
unsigned compression;
PreparedFlacEncoder():encoder(flac_encoder_plugin) {}
bool Configure(const ConfigBlock &block, Error &error);
}; };
static constexpr Domain flac_encoder_domain("vorbis_encoder"); static constexpr Domain flac_encoder_domain("vorbis_encoder");
static bool bool
flac_encoder_configure(struct flac_encoder *encoder, const ConfigBlock &block, PreparedFlacEncoder::Configure(const ConfigBlock &block, Error &)
gcc_unused Error &error)
{ {
encoder->compression = block.GetBlockValue("compression", 5u); compression = block.GetBlockValue("compression", 5u);
return true; return true;
} }
static Encoder * static PreparedEncoder *
flac_encoder_init(const ConfigBlock &block, Error &error) flac_encoder_init(const ConfigBlock &block, Error &error)
{ {
flac_encoder *encoder = new flac_encoder(); auto *encoder = new PreparedFlacEncoder();
/* load configuration from "block" */ /* load configuration from "block" */
if (!flac_encoder_configure(encoder, block, error)) { if (!encoder->Configure(block, error)) {
/* configuration has failed, roll back and return error */ /* configuration has failed, roll back and return error */
delete encoder; delete encoder;
return nullptr; return nullptr;
@ -80,9 +123,9 @@ flac_encoder_init(const ConfigBlock &block, Error &error)
} }
static void static void
flac_encoder_finish(Encoder *_encoder) flac_encoder_finish(PreparedEncoder *_encoder)
{ {
struct flac_encoder *encoder = (struct flac_encoder *)_encoder; auto *encoder = (PreparedFlacEncoder *)_encoder;
/* the real libFLAC cleanup was already performed by /* the real libFLAC cleanup was already performed by
flac_encoder_close(), so no real work here */ flac_encoder_close(), so no real work here */
@ -90,71 +133,67 @@ flac_encoder_finish(Encoder *_encoder)
} }
static bool static bool
flac_encoder_setup(struct flac_encoder *encoder, unsigned bits_per_sample, flac_encoder_setup(FLAC__StreamEncoder *fse, unsigned compression,
const AudioFormat &audio_format, unsigned bits_per_sample,
Error &error) Error &error)
{ {
if ( !FLAC__stream_encoder_set_compression_level(encoder->fse, if (!FLAC__stream_encoder_set_compression_level(fse, compression)) {
encoder->compression)) {
error.Format(config_domain, error.Format(config_domain,
"error setting flac compression to %d", "error setting flac compression to %d",
encoder->compression); compression);
return false; return false;
} }
if ( !FLAC__stream_encoder_set_channels(encoder->fse, if (!FLAC__stream_encoder_set_channels(fse, audio_format.channels)) {
encoder->audio_format.channels)) {
error.Format(config_domain, error.Format(config_domain,
"error setting flac channels num to %d", "error setting flac channels num to %d",
encoder->audio_format.channels); audio_format.channels);
return false; return false;
} }
if ( !FLAC__stream_encoder_set_bits_per_sample(encoder->fse,
bits_per_sample)) { if (!FLAC__stream_encoder_set_bits_per_sample(fse, bits_per_sample)) {
error.Format(config_domain, error.Format(config_domain,
"error setting flac bit format to %d", "error setting flac bit format to %d",
bits_per_sample); bits_per_sample);
return false; return false;
} }
if ( !FLAC__stream_encoder_set_sample_rate(encoder->fse,
encoder->audio_format.sample_rate)) { if (!FLAC__stream_encoder_set_sample_rate(fse,
audio_format.sample_rate)) {
error.Format(config_domain, error.Format(config_domain,
"error setting flac sample rate to %d", "error setting flac sample rate to %d",
encoder->audio_format.sample_rate); audio_format.sample_rate);
return false; return false;
} }
return true; return true;
} }
static FLAC__StreamEncoderWriteStatus bool
flac_write_callback(gcc_unused const FLAC__StreamEncoder *fse, FlacEncoder::Init(Error &error)
const FLAC__byte data[],
size_t bytes,
gcc_unused unsigned samples,
gcc_unused unsigned current_frame, void *client_data)
{ {
struct flac_encoder *encoder = (struct flac_encoder *) client_data; /* this immediately outputs data through callback */
//transfer data to buffer auto init_status =
encoder->output_buffer->Append((const uint8_t *)data, bytes); FLAC__stream_encoder_init_stream(fse,
WriteCallback,
nullptr, nullptr, nullptr,
this);
return FLAC__STREAM_ENCODER_WRITE_STATUS_OK; if (init_status != FLAC__STREAM_ENCODER_INIT_STATUS_OK) {
error.Format(flac_encoder_domain,
"failed to initialize encoder: %s\n",
FLAC__StreamEncoderInitStatusString[init_status]);
return false;
}
return true;
} }
static void static Encoder *
flac_encoder_close(Encoder *_encoder) flac_encoder_open(PreparedEncoder *_encoder, AudioFormat &audio_format, Error &error)
{ {
struct flac_encoder *encoder = (struct flac_encoder *)_encoder; auto *encoder = (PreparedFlacEncoder *)_encoder;
FLAC__stream_encoder_delete(encoder->fse);
encoder->expand_buffer.Clear();
encoder->output_buffer.Destruct();
}
static bool
flac_encoder_open(Encoder *_encoder, AudioFormat &audio_format, Error &error)
{
struct flac_encoder *encoder = (struct flac_encoder *)_encoder;
unsigned bits_per_sample; unsigned bits_per_sample;
/* FIXME: flac should support 32bit as well */ /* FIXME: flac should support 32bit as well */
@ -176,51 +215,26 @@ flac_encoder_open(Encoder *_encoder, AudioFormat &audio_format, Error &error)
audio_format.format = SampleFormat::S24_P32; audio_format.format = SampleFormat::S24_P32;
} }
encoder->audio_format = audio_format;
/* allocate the encoder */ /* allocate the encoder */
encoder->fse = FLAC__stream_encoder_new(); auto fse = FLAC__stream_encoder_new();
if (encoder->fse == nullptr) { if (fse == nullptr) {
error.Set(flac_encoder_domain, "flac_new() failed"); error.Set(flac_encoder_domain, "FLAC__stream_encoder_new() failed");
return false; return nullptr;
} }
if (!flac_encoder_setup(encoder, bits_per_sample, error)) { if (!flac_encoder_setup(fse, encoder->compression,
FLAC__stream_encoder_delete(encoder->fse); audio_format, bits_per_sample, error)) {
return false; FLAC__stream_encoder_delete(fse);
return nullptr;
} }
encoder->output_buffer.Construct(8192); auto *e = new FlacEncoder(audio_format, fse);
if (!e->Init(error)) {
/* this immediately outputs data through callback */ delete e;
return nullptr;
{
FLAC__StreamEncoderInitStatus init_status;
init_status = FLAC__stream_encoder_init_stream(encoder->fse,
flac_write_callback,
nullptr, nullptr, nullptr, encoder);
if(init_status != FLAC__STREAM_ENCODER_INIT_STATUS_OK) {
error.Format(flac_encoder_domain,
"failed to initialize encoder: %s\n",
FLAC__StreamEncoderInitStatusString[init_status]);
flac_encoder_close(_encoder);
return false;
}
} }
return true; return e;
}
static bool
flac_encoder_flush(Encoder *_encoder, gcc_unused Error &error)
{
struct flac_encoder *encoder = (struct flac_encoder *)_encoder;
(void) FLAC__stream_encoder_finish(encoder->fse);
return true;
} }
static inline void static inline void
@ -241,31 +255,27 @@ pcm16_to_flac(int32_t *out, const int16_t *in, unsigned num_samples)
} }
} }
static bool bool
flac_encoder_write(Encoder *_encoder, FlacEncoder::Write(const void *data, size_t length, Error &error)
const void *data, size_t length,
gcc_unused Error &error)
{ {
struct flac_encoder *encoder = (struct flac_encoder *)_encoder;
unsigned num_frames, num_samples;
void *exbuffer; void *exbuffer;
const void *buffer = nullptr; const void *buffer = nullptr;
/* format conversion */ /* format conversion */
num_frames = length / encoder->audio_format.GetFrameSize(); const unsigned num_frames = length / audio_format.GetFrameSize();
num_samples = num_frames * encoder->audio_format.channels; const unsigned num_samples = num_frames * audio_format.channels;
switch (encoder->audio_format.format) { switch (audio_format.format) {
case SampleFormat::S8: case SampleFormat::S8:
exbuffer = encoder->expand_buffer.Get(length * 4); exbuffer = expand_buffer.Get(length * 4);
pcm8_to_flac((int32_t *)exbuffer, (const int8_t *)data, pcm8_to_flac((int32_t *)exbuffer, (const int8_t *)data,
num_samples); num_samples);
buffer = exbuffer; buffer = exbuffer;
break; break;
case SampleFormat::S16: case SampleFormat::S16:
exbuffer = encoder->expand_buffer.Get(length * 2); exbuffer = expand_buffer.Get(length * 2);
pcm16_to_flac((int32_t *)exbuffer, (const int16_t *)data, pcm16_to_flac((int32_t *)exbuffer, (const int16_t *)data,
num_samples); num_samples);
buffer = exbuffer; buffer = exbuffer;
@ -284,7 +294,7 @@ flac_encoder_write(Encoder *_encoder,
/* feed samples to encoder */ /* feed samples to encoder */
if (!FLAC__stream_encoder_process_interleaved(encoder->fse, if (!FLAC__stream_encoder_process_interleaved(fse,
(const FLAC__int32 *)buffer, (const FLAC__int32 *)buffer,
num_frames)) { num_frames)) {
error.Set(flac_encoder_domain, "flac encoder process failed"); error.Set(flac_encoder_domain, "flac encoder process failed");
@ -294,16 +304,8 @@ flac_encoder_write(Encoder *_encoder,
return true; return true;
} }
static size_t
flac_encoder_read(Encoder *_encoder, void *dest, size_t length)
{
struct flac_encoder *encoder = (struct flac_encoder *)_encoder;
return encoder->output_buffer->Read((uint8_t *)dest, length);
}
static const char * static const char *
flac_encoder_get_mime_type(gcc_unused Encoder *_encoder) flac_encoder_get_mime_type(gcc_unused PreparedEncoder *_encoder)
{ {
return "audio/flac"; return "audio/flac";
} }
@ -313,13 +315,6 @@ const EncoderPlugin flac_encoder_plugin = {
flac_encoder_init, flac_encoder_init,
flac_encoder_finish, flac_encoder_finish,
flac_encoder_open, flac_encoder_open,
flac_encoder_close,
flac_encoder_flush,
flac_encoder_flush,
nullptr,
nullptr,
flac_encoder_write,
flac_encoder_read,
flac_encoder_get_mime_type, flac_encoder_get_mime_type,
}; };

View File

@ -24,7 +24,6 @@
#include "config/ConfigError.hxx" #include "config/ConfigError.hxx"
#include "util/NumberParser.hxx" #include "util/NumberParser.hxx"
#include "util/ReusableArray.hxx" #include "util/ReusableArray.hxx"
#include "util/Manual.hxx"
#include "util/Error.hxx" #include "util/Error.hxx"
#include "util/Domain.hxx" #include "util/Domain.hxx"
@ -33,19 +32,34 @@
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>
struct LameEncoder final { class LameEncoder final : public Encoder {
Encoder encoder; const AudioFormat audio_format;
lame_global_flags *const gfp;
ReusableArray<unsigned char, 32768> output_buffer;
unsigned char *output_begin = nullptr, *output_end = nullptr;
public:
LameEncoder(const AudioFormat _audio_format,
lame_global_flags *_gfp)
:Encoder(false),
audio_format(_audio_format), gfp(_gfp) {}
~LameEncoder() override;
/* virtual methods from class Encoder */
bool Write(const void *data, size_t length, Error &) override;
size_t Read(void *dest, size_t length) override;
};
struct PreparedLameEncoder final {
PreparedEncoder encoder;
AudioFormat audio_format;
float quality; float quality;
int bitrate; int bitrate;
lame_global_flags *gfp; PreparedLameEncoder():encoder(lame_encoder_plugin) {}
Manual<ReusableArray<unsigned char, 32768>> output_buffer;
unsigned char *output_begin, *output_end;
LameEncoder():encoder(lame_encoder_plugin) {}
bool Configure(const ConfigBlock &block, Error &error); bool Configure(const ConfigBlock &block, Error &error);
}; };
@ -53,7 +67,7 @@ struct LameEncoder final {
static constexpr Domain lame_encoder_domain("lame_encoder"); static constexpr Domain lame_encoder_domain("lame_encoder");
bool bool
LameEncoder::Configure(const ConfigBlock &block, Error &error) PreparedLameEncoder::Configure(const ConfigBlock &block, Error &error)
{ {
const char *value; const char *value;
char *endptr; char *endptr;
@ -100,10 +114,10 @@ LameEncoder::Configure(const ConfigBlock &block, Error &error)
return true; return true;
} }
static Encoder * static PreparedEncoder *
lame_encoder_init(const ConfigBlock &block, Error &error) lame_encoder_init(const ConfigBlock &block, Error &error)
{ {
LameEncoder *encoder = new LameEncoder(); auto *encoder = new PreparedLameEncoder();
/* load configuration from "block" */ /* load configuration from "block" */
if (!encoder->Configure(block, error)) { if (!encoder->Configure(block, error)) {
@ -116,9 +130,9 @@ lame_encoder_init(const ConfigBlock &block, Error &error)
} }
static void static void
lame_encoder_finish(Encoder *_encoder) lame_encoder_finish(PreparedEncoder *_encoder)
{ {
LameEncoder *encoder = (LameEncoder *)_encoder; auto *encoder = (PreparedLameEncoder *)_encoder;
/* the real liblame cleanup was already performed by /* the real liblame cleanup was already performed by
lame_encoder_close(), so no real work here */ lame_encoder_close(), so no real work here */
@ -126,17 +140,18 @@ lame_encoder_finish(Encoder *_encoder)
} }
static bool static bool
lame_encoder_setup(LameEncoder *encoder, Error &error) lame_encoder_setup(lame_global_flags *gfp, float quality, int bitrate,
const AudioFormat &audio_format, Error &error)
{ {
if (encoder->quality >= -1.0) { if (quality >= -1.0) {
/* a quality was configured (VBR) */ /* a quality was configured (VBR) */
if (0 != lame_set_VBR(encoder->gfp, vbr_rh)) { if (0 != lame_set_VBR(gfp, vbr_rh)) {
error.Set(lame_encoder_domain, error.Set(lame_encoder_domain,
"error setting lame VBR mode"); "error setting lame VBR mode");
return false; return false;
} }
if (0 != lame_set_VBR_q(encoder->gfp, encoder->quality)) { if (0 != lame_set_VBR_q(gfp, quality)) {
error.Set(lame_encoder_domain, error.Set(lame_encoder_domain,
"error setting lame VBR quality"); "error setting lame VBR quality");
return false; return false;
@ -144,35 +159,32 @@ lame_encoder_setup(LameEncoder *encoder, Error &error)
} else { } else {
/* a bit rate was configured */ /* a bit rate was configured */
if (0 != lame_set_brate(encoder->gfp, encoder->bitrate)) { if (0 != lame_set_brate(gfp, bitrate)) {
error.Set(lame_encoder_domain, error.Set(lame_encoder_domain,
"error setting lame bitrate"); "error setting lame bitrate");
return false; return false;
} }
} }
if (0 != lame_set_num_channels(encoder->gfp, if (0 != lame_set_num_channels(gfp, audio_format.channels)) {
encoder->audio_format.channels)) {
error.Set(lame_encoder_domain, error.Set(lame_encoder_domain,
"error setting lame num channels"); "error setting lame num channels");
return false; return false;
} }
if (0 != lame_set_in_samplerate(encoder->gfp, if (0 != lame_set_in_samplerate(gfp, audio_format.sample_rate)) {
encoder->audio_format.sample_rate)) {
error.Set(lame_encoder_domain, error.Set(lame_encoder_domain,
"error setting lame sample rate"); "error setting lame sample rate");
return false; return false;
} }
if (0 != lame_set_out_samplerate(encoder->gfp, if (0 != lame_set_out_samplerate(gfp, audio_format.sample_rate)) {
encoder->audio_format.sample_rate)) {
error.Set(lame_encoder_domain, error.Set(lame_encoder_domain,
"error setting lame out sample rate"); "error setting lame out sample rate");
return false; return false;
} }
if (0 > lame_init_params(encoder->gfp)) { if (0 > lame_init_params(gfp)) {
error.Set(lame_encoder_domain, error.Set(lame_encoder_domain,
"error initializing lame params"); "error initializing lame params");
return false; return false;
@ -181,98 +193,84 @@ lame_encoder_setup(LameEncoder *encoder, Error &error)
return true; return true;
} }
static bool static Encoder *
lame_encoder_open(Encoder *_encoder, AudioFormat &audio_format, Error &error) lame_encoder_open(PreparedEncoder *_encoder, AudioFormat &audio_format,
Error &error)
{ {
LameEncoder *encoder = (LameEncoder *)_encoder; auto *encoder = (PreparedLameEncoder *)_encoder;
audio_format.format = SampleFormat::S16; audio_format.format = SampleFormat::S16;
audio_format.channels = 2; audio_format.channels = 2;
encoder->audio_format = audio_format; auto gfp = lame_init();
if (gfp == nullptr) {
encoder->gfp = lame_init();
if (encoder->gfp == nullptr) {
error.Set(lame_encoder_domain, "lame_init() failed"); error.Set(lame_encoder_domain, "lame_init() failed");
return false; return nullptr;
} }
if (!lame_encoder_setup(encoder, error)) { if (!lame_encoder_setup(gfp, encoder->quality, encoder->bitrate,
lame_close(encoder->gfp); audio_format, error)) {
return false; lame_close(gfp);
return nullptr;
} }
encoder->output_buffer.Construct(); return new LameEncoder(audio_format, gfp);
encoder->output_begin = encoder->output_end = nullptr;
return true;
} }
static void LameEncoder::~LameEncoder()
lame_encoder_close(Encoder *_encoder)
{ {
LameEncoder *encoder = (LameEncoder *)_encoder; lame_close(gfp);
lame_close(encoder->gfp);
encoder->output_buffer.Destruct();
} }
static bool bool
lame_encoder_write(Encoder *_encoder, LameEncoder::Write(const void *data, size_t length,
const void *data, size_t length,
gcc_unused Error &error) gcc_unused Error &error)
{ {
LameEncoder *encoder = (LameEncoder *)_encoder;
const int16_t *src = (const int16_t*)data; const int16_t *src = (const int16_t*)data;
assert(encoder->output_begin == encoder->output_end); assert(output_begin == output_end);
const unsigned num_frames = const unsigned num_frames = length / audio_format.GetFrameSize();
length / encoder->audio_format.GetFrameSize(); const unsigned num_samples = length / audio_format.GetSampleSize();
const unsigned num_samples =
length / encoder->audio_format.GetSampleSize();
/* worst-case formula according to LAME documentation */ /* worst-case formula according to LAME documentation */
const size_t output_buffer_size = 5 * num_samples / 4 + 7200; const size_t output_buffer_size = 5 * num_samples / 4 + 7200;
const auto output_buffer = encoder->output_buffer->Get(output_buffer_size); const auto dest = output_buffer.Get(output_buffer_size);
/* this is for only 16-bit audio */ /* this is for only 16-bit audio */
int bytes_out = lame_encode_buffer_interleaved(encoder->gfp, int bytes_out = lame_encode_buffer_interleaved(gfp,
const_cast<short *>(src), const_cast<short *>(src),
num_frames, num_frames,
output_buffer, dest, output_buffer_size);
output_buffer_size);
if (bytes_out < 0) { if (bytes_out < 0) {
error.Set(lame_encoder_domain, "lame encoder failed"); error.Set(lame_encoder_domain, "lame encoder failed");
return false; return false;
} }
encoder->output_begin = output_buffer; output_begin = dest;
encoder->output_end = output_buffer + bytes_out; output_end = dest + bytes_out;
return true; return true;
} }
static size_t size_t
lame_encoder_read(Encoder *_encoder, void *dest, size_t length) LameEncoder::Read(void *dest, size_t length)
{ {
LameEncoder *encoder = (LameEncoder *)_encoder; const auto begin = output_begin;
assert(begin <= output_end);
const auto begin = encoder->output_begin; const size_t remainning = output_end - begin;
assert(begin <= encoder->output_end);
const size_t remainning = encoder->output_end - begin;
if (length > remainning) if (length > remainning)
length = remainning; length = remainning;
memcpy(dest, begin, length); memcpy(dest, begin, length);
encoder->output_begin = begin + length; output_begin = begin + length;
return length; return length;
} }
static const char * static const char *
lame_encoder_get_mime_type(gcc_unused Encoder *_encoder) lame_encoder_get_mime_type(gcc_unused PreparedEncoder *_encoder)
{ {
return "audio/mpeg"; return "audio/mpeg";
} }
@ -282,12 +280,5 @@ const EncoderPlugin lame_encoder_plugin = {
lame_encoder_init, lame_encoder_init,
lame_encoder_finish, lame_encoder_finish,
lame_encoder_open, lame_encoder_open,
lame_encoder_close,
nullptr,
nullptr,
nullptr,
nullptr,
lame_encoder_write,
lame_encoder_read,
lame_encoder_get_mime_type, lame_encoder_get_mime_type,
}; };

View File

@ -20,73 +20,58 @@
#include "config.h" #include "config.h"
#include "NullEncoderPlugin.hxx" #include "NullEncoderPlugin.hxx"
#include "../EncoderAPI.hxx" #include "../EncoderAPI.hxx"
#include "util/Manual.hxx"
#include "util/DynamicFifoBuffer.hxx" #include "util/DynamicFifoBuffer.hxx"
#include "Compiler.h" #include "Compiler.h"
#include <assert.h> #include <assert.h>
struct NullEncoder final { class NullEncoder final : public Encoder {
Encoder encoder; DynamicFifoBuffer<uint8_t> buffer;
Manual<DynamicFifoBuffer<uint8_t>> buffer;
public:
NullEncoder() NullEncoder()
:Encoder(false),
buffer(8192) {}
/* virtual methods from class Encoder */
bool Write(const void *data, size_t length, Error &) override {
buffer.Append((const uint8_t *)data, length);
return true;
}
size_t Read(void *dest, size_t length) override {
return buffer.Read((uint8_t *)dest, length);
}
};
struct PreparedNullEncoder final {
PreparedEncoder encoder;
PreparedNullEncoder()
:encoder(null_encoder_plugin) {} :encoder(null_encoder_plugin) {}
}; };
static Encoder * static PreparedEncoder *
null_encoder_init(gcc_unused const ConfigBlock &block, null_encoder_init(gcc_unused const ConfigBlock &block,
gcc_unused Error &error) gcc_unused Error &error)
{ {
NullEncoder *encoder = new NullEncoder(); auto *encoder = new PreparedNullEncoder();
return &encoder->encoder; return &encoder->encoder;
} }
static void static void
null_encoder_finish(Encoder *_encoder) null_encoder_finish(PreparedEncoder *_encoder)
{ {
NullEncoder *encoder = (NullEncoder *)_encoder; auto *encoder = (PreparedNullEncoder *)_encoder;
delete encoder; delete encoder;
} }
static void static Encoder *
null_encoder_close(Encoder *_encoder) null_encoder_open(gcc_unused PreparedEncoder *encoder,
{
NullEncoder *encoder = (NullEncoder *)_encoder;
encoder->buffer.Destruct();
}
static bool
null_encoder_open(Encoder *_encoder,
gcc_unused AudioFormat &audio_format, gcc_unused AudioFormat &audio_format,
gcc_unused Error &error) gcc_unused Error &error)
{ {
NullEncoder *encoder = (NullEncoder *)_encoder; return new NullEncoder();
encoder->buffer.Construct(8192);
return true;
}
static bool
null_encoder_write(Encoder *_encoder,
const void *data, size_t length,
gcc_unused Error &error)
{
NullEncoder *encoder = (NullEncoder *)_encoder;
encoder->buffer->Append((const uint8_t *)data, length);
return length;
}
static size_t
null_encoder_read(Encoder *_encoder, void *dest, size_t length)
{
NullEncoder *encoder = (NullEncoder *)_encoder;
return encoder->buffer->Read((uint8_t *)dest, length);
} }
const EncoderPlugin null_encoder_plugin = { const EncoderPlugin null_encoder_plugin = {
@ -94,12 +79,5 @@ const EncoderPlugin null_encoder_plugin = {
null_encoder_init, null_encoder_init,
null_encoder_finish, null_encoder_finish,
null_encoder_open, null_encoder_open,
null_encoder_close,
nullptr,
nullptr,
nullptr,
nullptr,
null_encoder_write,
null_encoder_read,
nullptr, nullptr,
}; };

View File

@ -35,26 +35,18 @@
#include <assert.h> #include <assert.h>
#include <stdlib.h> #include <stdlib.h>
struct OpusEncoder { namespace {
/** the base class */
Encoder encoder;
/* configuration */ class OpusEncoder final : public Encoder {
const AudioFormat audio_format;
opus_int32 bitrate; const size_t frame_size;
int complexity;
int signal;
/* runtime information */ const size_t buffer_frames, buffer_size;
size_t buffer_position = 0;
uint8_t *const buffer;
AudioFormat audio_format; ::OpusEncoder *const enc;
size_t frame_size;
size_t buffer_frames, buffer_size, buffer_position;
uint8_t *buffer;
OpusEncoder *enc;
unsigned char buffer2[1275 * 3 + 7]; unsigned char buffer2[1275 * 3 + 7];
@ -62,35 +54,49 @@ struct OpusEncoder {
int lookahead; int lookahead;
ogg_int64_t packetno; ogg_int64_t packetno = 0;
ogg_int64_t granulepos; ogg_int64_t granulepos;
OpusEncoder():encoder(opus_encoder_plugin), granulepos(0) {} public:
OpusEncoder(AudioFormat &_audio_format, ::OpusEncoder *_enc);
~OpusEncoder() override;
bool Configure(const ConfigBlock &block, Error &error); /* virtual methods from class Encoder */
bool Open(AudioFormat &audio_format, Error &error); bool End(Error &) override;
void Close(); bool Flush(Error &) override;
bool Write(const void *data, size_t length, Error &) override;
size_t Read(void *dest, size_t length) override;
private:
bool DoEncode(bool eos, Error &error); bool DoEncode(bool eos, Error &error);
bool End(Error &error);
bool Flush(Error &error);
bool WriteSilence(unsigned fill_frames, Error &error); bool WriteSilence(unsigned fill_frames, Error &error);
bool Write(const void *_data, size_t length, Error &error);
void GenerateHead(); void GenerateHead();
void GenerateTags(); void GenerateTags();
};
size_t Read(void *dest, size_t length); struct PreparedOpusEncoder {
/** the base class */
PreparedEncoder encoder;
/* configuration */
opus_int32 bitrate;
int complexity;
int signal;
PreparedOpusEncoder():encoder(opus_encoder_plugin) {}
bool Configure(const ConfigBlock &block, Error &error);
Encoder *Open(AudioFormat &audio_format, Error &error);
}; };
static constexpr Domain opus_encoder_domain("opus_encoder"); static constexpr Domain opus_encoder_domain("opus_encoder");
bool bool
OpusEncoder::Configure(const ConfigBlock &block, Error &error) PreparedOpusEncoder::Configure(const ConfigBlock &block, Error &error)
{ {
const char *value = block.GetBlockValue("bitrate", "auto"); const char *value = block.GetBlockValue("bitrate", "auto");
if (strcmp(value, "auto") == 0) if (strcmp(value, "auto") == 0)
@ -128,10 +134,10 @@ OpusEncoder::Configure(const ConfigBlock &block, Error &error)
return true; return true;
} }
static Encoder * static PreparedEncoder *
opus_encoder_init(const ConfigBlock &block, Error &error) opus_encoder_init(const ConfigBlock &block, Error &error)
{ {
auto *encoder = new OpusEncoder(); auto *encoder = new PreparedOpusEncoder();
/* load configuration from "block" */ /* load configuration from "block" */
if (!encoder->Configure(block, error)) { if (!encoder->Configure(block, error)) {
@ -144,93 +150,86 @@ opus_encoder_init(const ConfigBlock &block, Error &error)
} }
static void static void
opus_encoder_finish(Encoder *_encoder) opus_encoder_finish(PreparedEncoder *_encoder)
{ {
auto *encoder = (OpusEncoder *)_encoder; auto *encoder = (PreparedOpusEncoder *)_encoder;
/* the real libopus cleanup was already performed by /* the real libopus cleanup was already performed by
opus_encoder_close(), so no real work here */ opus_encoder_close(), so no real work here */
delete encoder; delete encoder;
} }
bool OpusEncoder::OpusEncoder(AudioFormat &_audio_format, ::OpusEncoder *_enc)
OpusEncoder::Open(AudioFormat &_audio_format, Error &error) :Encoder(false),
audio_format(_audio_format),
frame_size(_audio_format.GetFrameSize()),
buffer_frames(_audio_format.sample_rate / 50),
buffer_size(frame_size * buffer_frames),
buffer((unsigned char *)xalloc(buffer_size)),
enc(_enc)
{
opus_encoder_ctl(enc, OPUS_GET_LOOKAHEAD(&lookahead));
stream.Initialize(GenerateOggSerial());
}
Encoder *
PreparedOpusEncoder::Open(AudioFormat &audio_format, Error &error)
{ {
/* libopus supports only 48 kHz */ /* libopus supports only 48 kHz */
_audio_format.sample_rate = 48000; audio_format.sample_rate = 48000;
if (_audio_format.channels > 2) if (audio_format.channels > 2)
_audio_format.channels = 1; audio_format.channels = 1;
switch (_audio_format.format) { switch (audio_format.format) {
case SampleFormat::S16: case SampleFormat::S16:
case SampleFormat::FLOAT: case SampleFormat::FLOAT:
break; break;
case SampleFormat::S8: case SampleFormat::S8:
_audio_format.format = SampleFormat::S16; audio_format.format = SampleFormat::S16;
break; break;
default: default:
_audio_format.format = SampleFormat::FLOAT; audio_format.format = SampleFormat::FLOAT;
break; break;
} }
audio_format = _audio_format;
frame_size = _audio_format.GetFrameSize();
int error_code; int error_code;
enc = opus_encoder_create(_audio_format.sample_rate, auto *enc = opus_encoder_create(audio_format.sample_rate,
_audio_format.channels, audio_format.channels,
OPUS_APPLICATION_AUDIO, OPUS_APPLICATION_AUDIO,
&error_code); &error_code);
if (enc == nullptr) { if (enc == nullptr) {
error.Set(opus_encoder_domain, error_code, error.Set(opus_encoder_domain, error_code,
opus_strerror(error_code)); opus_strerror(error_code));
return false; return nullptr;
} }
opus_encoder_ctl(enc, OPUS_SET_BITRATE(bitrate)); opus_encoder_ctl(enc, OPUS_SET_BITRATE(bitrate));
opus_encoder_ctl(enc, OPUS_SET_COMPLEXITY(complexity)); opus_encoder_ctl(enc, OPUS_SET_COMPLEXITY(complexity));
opus_encoder_ctl(enc, OPUS_SET_SIGNAL(signal)); opus_encoder_ctl(enc, OPUS_SET_SIGNAL(signal));
opus_encoder_ctl(enc, OPUS_GET_LOOKAHEAD(&lookahead)); return new OpusEncoder(audio_format, enc);
buffer_frames = _audio_format.sample_rate / 50;
buffer_size = frame_size * buffer_frames;
buffer_position = 0;
buffer = (unsigned char *)xalloc(buffer_size);
stream.Initialize(GenerateOggSerial());
packetno = 0;
return true;
} }
static bool static Encoder *
opus_encoder_open(Encoder *_encoder, opus_encoder_open(PreparedEncoder *_encoder,
AudioFormat &audio_format, AudioFormat &audio_format,
Error &error) Error &error)
{ {
auto &encoder = *(OpusEncoder *)_encoder; auto &encoder = *(PreparedOpusEncoder *)_encoder;
return encoder.Open(audio_format, error); return encoder.Open(audio_format, error);
} }
void OpusEncoder::~OpusEncoder()
OpusEncoder::Close()
{ {
stream.Deinitialize(); stream.Deinitialize();
free(buffer); free(buffer);
opus_encoder_destroy(enc); opus_encoder_destroy(enc);
} }
static void
opus_encoder_close(Encoder *_encoder)
{
auto &encoder = *(OpusEncoder *)_encoder;
encoder.Close();
}
bool bool
OpusEncoder::DoEncode(bool eos, Error &error) OpusEncoder::DoEncode(bool eos, Error &error)
{ {
@ -281,13 +280,6 @@ OpusEncoder::End(Error &error)
return DoEncode(true, error); return DoEncode(true, error);
} }
static bool
opus_encoder_end(Encoder *_encoder, Error &error)
{
auto &encoder = *(OpusEncoder *)_encoder;
return encoder.End(error);
}
bool bool
OpusEncoder::Flush(gcc_unused Error &error) OpusEncoder::Flush(gcc_unused Error &error)
{ {
@ -295,14 +287,6 @@ OpusEncoder::Flush(gcc_unused Error &error)
return true; return true;
} }
static bool
opus_encoder_flush(Encoder *_encoder, Error &error)
{
auto &encoder = *(OpusEncoder *)_encoder;
return encoder.Flush(error);
}
bool bool
OpusEncoder::WriteSilence(unsigned fill_frames, Error &error) OpusEncoder::WriteSilence(unsigned fill_frames, Error &error)
{ {
@ -360,15 +344,6 @@ OpusEncoder::Write(const void *_data, size_t length, Error &error)
return true; return true;
} }
static bool
opus_encoder_write(Encoder *_encoder,
const void *data, size_t length,
Error &error)
{
auto &encoder = *(OpusEncoder *)_encoder;
return encoder.Write(data, length, error);
}
void void
OpusEncoder::GenerateHead() OpusEncoder::GenerateHead()
{ {
@ -430,30 +405,18 @@ OpusEncoder::Read(void *dest, size_t length)
return stream.PageOut(dest, length); return stream.PageOut(dest, length);
} }
static size_t
opus_encoder_read(Encoder *_encoder, void *dest, size_t length)
{
auto &encoder = *(OpusEncoder *)_encoder;
return encoder.Read(dest, length);
}
static const char * static const char *
opus_encoder_get_mime_type(gcc_unused Encoder *_encoder) opus_encoder_get_mime_type(gcc_unused PreparedEncoder *_encoder)
{ {
return "audio/ogg"; return "audio/ogg";
} }
}
const EncoderPlugin opus_encoder_plugin = { const EncoderPlugin opus_encoder_plugin = {
"opus", "opus",
opus_encoder_init, opus_encoder_init,
opus_encoder_finish, opus_encoder_finish,
opus_encoder_open, opus_encoder_open,
opus_encoder_close,
opus_encoder_end,
opus_encoder_flush,
nullptr,
nullptr,
opus_encoder_write,
opus_encoder_read,
opus_encoder_get_mime_type, opus_encoder_get_mime_type,
}; };

View File

@ -22,7 +22,6 @@
#include "../EncoderAPI.hxx" #include "../EncoderAPI.hxx"
#include "AudioFormat.hxx" #include "AudioFormat.hxx"
#include "config/ConfigError.hxx" #include "config/ConfigError.hxx"
#include "util/Manual.hxx"
#include "util/DynamicFifoBuffer.hxx" #include "util/DynamicFifoBuffer.hxx"
#include "util/Error.hxx" #include "util/Error.hxx"
@ -34,32 +33,71 @@ extern "C"
static constexpr size_t BUFFER_INIT_SIZE = 8192; static constexpr size_t BUFFER_INIT_SIZE = 8192;
static constexpr unsigned CHANNELS = 2; static constexpr unsigned CHANNELS = 2;
struct ShineEncoder { class ShineEncoder final : public Encoder {
Encoder encoder; const AudioFormat audio_format;
AudioFormat audio_format; const shine_t shine;
shine_t shine; const size_t frame_size;
/* workaround for bug:
https://github.com/savonet/shine/issues/11 */
size_t input_pos = SHINE_MAX_SAMPLES + 1;
int16_t *stereo[CHANNELS];
DynamicFifoBuffer<uint8_t> output_buffer;
public:
ShineEncoder(AudioFormat _audio_format, shine_t _shine)
:Encoder(false),
audio_format(_audio_format), shine(_shine),
frame_size(shine_samples_per_pass(shine)),
stereo{new int16_t[frame_size], new int16_t[frame_size]},
output_buffer(BUFFER_INIT_SIZE) {}
~ShineEncoder() override {
if (input_pos > SHINE_MAX_SAMPLES) {
/* write zero chunk */
input_pos = 0;
WriteChunk(true);
}
shine_close(shine);
delete[] stereo[0];
delete[] stereo[1];
}
bool WriteChunk(bool flush);
/* virtual methods from class Encoder */
bool End(Error &error) override {
return Flush(error);
}
bool Flush(Error &) override;
bool Write(const void *data, size_t length, Error &) override;
size_t Read(void *dest, size_t length) override {
return output_buffer.Read((uint8_t *)dest, length);
}
};
struct PreparedShineEncoder {
PreparedEncoder encoder;
shine_config_t config; shine_config_t config;
size_t frame_size; PreparedShineEncoder():encoder(shine_encoder_plugin) {}
size_t input_pos;
int16_t *stereo[CHANNELS];
Manual<DynamicFifoBuffer<uint8_t>> output_buffer;
ShineEncoder():encoder(shine_encoder_plugin){}
bool Configure(const ConfigBlock &block, Error &error); bool Configure(const ConfigBlock &block, Error &error);
bool Setup(Error &error); bool Setup(Error &error);
bool WriteChunk(bool flush);
}; };
inline bool inline bool
ShineEncoder::Configure(const ConfigBlock &block, gcc_unused Error &error) PreparedShineEncoder::Configure(const ConfigBlock &block, Error &)
{ {
shine_set_config_mpeg_defaults(&config.mpeg); shine_set_config_mpeg_defaults(&config.mpeg);
config.mpeg.bitr = block.GetBlockValue("bitrate", 128); config.mpeg.bitr = block.GetBlockValue("bitrate", 128);
@ -67,10 +105,10 @@ ShineEncoder::Configure(const ConfigBlock &block, gcc_unused Error &error)
return true; return true;
} }
static Encoder * static PreparedEncoder *
shine_encoder_init(const ConfigBlock &block, Error &error) shine_encoder_init(const ConfigBlock &block, Error &error)
{ {
ShineEncoder *encoder = new ShineEncoder(); auto *encoder = new PreparedShineEncoder();
/* load configuration from "block" */ /* load configuration from "block" */
if (!encoder->Configure(block, error)) { if (!encoder->Configure(block, error)) {
@ -83,16 +121,20 @@ shine_encoder_init(const ConfigBlock &block, Error &error)
} }
static void static void
shine_encoder_finish(Encoder *_encoder) shine_encoder_finish(PreparedEncoder *_encoder)
{ {
ShineEncoder *encoder = (ShineEncoder *)_encoder; auto *encoder = (PreparedShineEncoder *)_encoder;
delete encoder; delete encoder;
} }
inline bool static shine_t
ShineEncoder::Setup(Error &error) SetupShine(shine_config_t config, AudioFormat &audio_format,
Error &error)
{ {
audio_format.format = SampleFormat::S16;
audio_format.channels = CHANNELS;
config.mpeg.mode = audio_format.channels == 2 ? STEREO : MONO; config.mpeg.mode = audio_format.channels == 2 ? STEREO : MONO;
config.wave.samplerate = audio_format.sample_rate; config.wave.samplerate = audio_format.sample_rate;
config.wave.channels = config.wave.channels =
@ -106,61 +148,28 @@ ShineEncoder::Setup(Error &error)
config.wave.samplerate, config.wave.samplerate,
config.mpeg.bitr); config.mpeg.bitr);
return false; return nullptr;
} }
shine = shine_initialise(&config); auto shine = shine_initialise(&config);
if (!shine)
if (!shine) {
error.Format(config_domain, error.Format(config_domain,
"error initializing shine."); "error initializing shine.");
return false; return shine;
}
frame_size = shine_samples_per_pass(shine);
return true;
} }
static bool static Encoder *
shine_encoder_open(Encoder *_encoder, AudioFormat &audio_format, Error &error) shine_encoder_open(PreparedEncoder *_encoder, AudioFormat &audio_format,
Error &error)
{ {
ShineEncoder *encoder = (ShineEncoder *)_encoder; auto *encoder = (PreparedShineEncoder *)_encoder;
audio_format.format = SampleFormat::S16; auto shine = SetupShine(encoder->config, audio_format, error);
audio_format.channels = CHANNELS; if (!shine)
encoder->audio_format = audio_format; return nullptr;
if (!encoder->Setup(error)) return new ShineEncoder(audio_format, shine);
return false;
encoder->stereo[0] = new int16_t[encoder->frame_size];
encoder->stereo[1] = new int16_t[encoder->frame_size];
/* workaround for bug:
https://github.com/savonet/shine/issues/11 */
encoder->input_pos = SHINE_MAX_SAMPLES + 1;
encoder->output_buffer.Construct(BUFFER_INIT_SIZE);
return true;
}
static void
shine_encoder_close(Encoder *_encoder)
{
ShineEncoder *encoder = (ShineEncoder *)_encoder;
if (encoder->input_pos > SHINE_MAX_SAMPLES) {
/* write zero chunk */
encoder->input_pos = 0;
encoder->WriteChunk(true);
}
shine_close(encoder->shine);
delete[] encoder->stereo[0];
delete[] encoder->stereo[1];
encoder->output_buffer.Destruct();
} }
bool bool
@ -179,7 +188,7 @@ ShineEncoder::WriteChunk(bool flush)
shine_encode_buffer(shine, stereo, &written); shine_encode_buffer(shine, stereo, &written);
if (written > 0) if (written > 0)
output_buffer->Append(out, written); output_buffer.Append(out, written);
input_pos = 0; input_pos = 0;
} }
@ -187,65 +196,50 @@ ShineEncoder::WriteChunk(bool flush)
return true; return true;
} }
static bool bool
shine_encoder_write(Encoder *_encoder, ShineEncoder::Write(const void *_data, size_t length, gcc_unused Error &error)
const void *_data, size_t length,
gcc_unused Error &error)
{ {
ShineEncoder *encoder = (ShineEncoder *)_encoder;
const int16_t *data = (const int16_t*)_data; const int16_t *data = (const int16_t*)_data;
length /= sizeof(*data) * encoder->audio_format.channels; length /= sizeof(*data) * audio_format.channels;
size_t written = 0; size_t written = 0;
if (encoder->input_pos > SHINE_MAX_SAMPLES) { if (input_pos > SHINE_MAX_SAMPLES)
encoder->input_pos = 0; input_pos = 0;
}
/* write all data to de-interleaved buffers */ /* write all data to de-interleaved buffers */
while (written < length) { while (written < length) {
for (; for (;
written < length written < length && input_pos < frame_size;
&& encoder->input_pos < encoder->frame_size; written++, input_pos++) {
written++, encoder->input_pos++) {
const size_t base = const size_t base =
written * encoder->audio_format.channels; written * audio_format.channels;
encoder->stereo[0][encoder->input_pos] = data[base]; stereo[0][input_pos] = data[base];
encoder->stereo[1][encoder->input_pos] = data[base + 1]; stereo[1][input_pos] = data[base + 1];
} }
/* write if chunk is filled */ /* write if chunk is filled */
encoder->WriteChunk(false); WriteChunk(false);
} }
return true; return true;
} }
static bool bool
shine_encoder_flush(Encoder *_encoder, gcc_unused Error &error) ShineEncoder::Flush(gcc_unused Error &error)
{ {
ShineEncoder *encoder = (ShineEncoder *)_encoder;
/* flush buffers and flush shine */ /* flush buffers and flush shine */
encoder->WriteChunk(true); WriteChunk(true);
int written; int written;
const uint8_t *data = shine_flush(encoder->shine, &written); const uint8_t *data = shine_flush(shine, &written);
if (written > 0) if (written > 0)
encoder->output_buffer->Append(data, written); output_buffer.Append(data, written);
return true; return true;
} }
static size_t
shine_encoder_read(Encoder *_encoder, void *dest, size_t length)
{
ShineEncoder *encoder = (ShineEncoder *)_encoder;
return encoder->output_buffer->Read((uint8_t *)dest, length);
}
static const char * static const char *
shine_encoder_get_mime_type(gcc_unused Encoder *_encoder) shine_encoder_get_mime_type(gcc_unused PreparedEncoder *_encoder)
{ {
return "audio/mpeg"; return "audio/mpeg";
} }
@ -255,12 +249,5 @@ const EncoderPlugin shine_encoder_plugin = {
shine_encoder_init, shine_encoder_init,
shine_encoder_finish, shine_encoder_finish,
shine_encoder_open, shine_encoder_open,
shine_encoder_close,
shine_encoder_flush,
shine_encoder_flush,
nullptr,
nullptr,
shine_encoder_write,
shine_encoder_read,
shine_encoder_get_mime_type, shine_encoder_get_mime_type,
}; };

View File

@ -32,26 +32,53 @@
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>
struct TwolameEncoder final { class TwolameEncoder final : public Encoder {
Encoder encoder; const AudioFormat audio_format;
AudioFormat audio_format;
float quality;
int bitrate;
twolame_options *options; twolame_options *options;
unsigned char output_buffer[32768]; unsigned char output_buffer[32768];
size_t output_buffer_length; size_t output_buffer_length = 0;
size_t output_buffer_position; size_t output_buffer_position = 0;
/** /**
* Call libtwolame's flush function when the output_buffer is * Call libtwolame's flush function when the output_buffer is
* empty? * empty?
*/ */
bool flush; bool flush = false;
TwolameEncoder():encoder(twolame_encoder_plugin) {} public:
TwolameEncoder(const AudioFormat _audio_format,
twolame_options *_options)
:Encoder(false),
audio_format(_audio_format), options(_options) {}
~TwolameEncoder() override;
bool Configure(const ConfigBlock &block, Error &error);
/* virtual methods from class Encoder */
bool End(Error &) override {
flush = true;
return true;
}
bool Flush(Error &) override {
flush = true;
return true;
}
bool Write(const void *data, size_t length, Error &) override;
size_t Read(void *dest, size_t length) override;
};
struct PreparedTwolameEncoder final {
PreparedEncoder encoder;
float quality;
int bitrate;
PreparedTwolameEncoder():encoder(twolame_encoder_plugin) {}
bool Configure(const ConfigBlock &block, Error &error); bool Configure(const ConfigBlock &block, Error &error);
}; };
@ -59,7 +86,7 @@ struct TwolameEncoder final {
static constexpr Domain twolame_encoder_domain("twolame_encoder"); static constexpr Domain twolame_encoder_domain("twolame_encoder");
bool bool
TwolameEncoder::Configure(const ConfigBlock &block, Error &error) PreparedTwolameEncoder::Configure(const ConfigBlock &block, Error &error)
{ {
const char *value; const char *value;
char *endptr; char *endptr;
@ -106,13 +133,13 @@ TwolameEncoder::Configure(const ConfigBlock &block, Error &error)
return true; return true;
} }
static Encoder * static PreparedEncoder *
twolame_encoder_init(const ConfigBlock &block, Error &error_r) twolame_encoder_init(const ConfigBlock &block, Error &error_r)
{ {
FormatDebug(twolame_encoder_domain, FormatDebug(twolame_encoder_domain,
"libtwolame version %s", get_twolame_version()); "libtwolame version %s", get_twolame_version());
TwolameEncoder *encoder = new TwolameEncoder(); auto *encoder = new PreparedTwolameEncoder();
/* load configuration from "block" */ /* load configuration from "block" */
if (!encoder->Configure(block, error_r)) { if (!encoder->Configure(block, error_r)) {
@ -125,9 +152,9 @@ twolame_encoder_init(const ConfigBlock &block, Error &error_r)
} }
static void static void
twolame_encoder_finish(Encoder *_encoder) twolame_encoder_finish(PreparedEncoder *_encoder)
{ {
TwolameEncoder *encoder = (TwolameEncoder *)_encoder; auto *encoder = (PreparedTwolameEncoder *)_encoder;
/* the real libtwolame cleanup was already performed by /* the real libtwolame cleanup was already performed by
twolame_encoder_close(), so no real work here */ twolame_encoder_close(), so no real work here */
@ -135,17 +162,18 @@ twolame_encoder_finish(Encoder *_encoder)
} }
static bool static bool
twolame_encoder_setup(TwolameEncoder *encoder, Error &error) twolame_encoder_setup(twolame_options *options, float quality, int bitrate,
const AudioFormat &audio_format, Error &error)
{ {
if (encoder->quality >= -1.0) { if (quality >= -1.0) {
/* a quality was configured (VBR) */ /* a quality was configured (VBR) */
if (0 != twolame_set_VBR(encoder->options, true)) { if (0 != twolame_set_VBR(options, true)) {
error.Set(twolame_encoder_domain, error.Set(twolame_encoder_domain,
"error setting twolame VBR mode"); "error setting twolame VBR mode");
return false; return false;
} }
if (0 != twolame_set_VBR_q(encoder->options, encoder->quality)) { if (0 != twolame_set_VBR_q(options, quality)) {
error.Set(twolame_encoder_domain, error.Set(twolame_encoder_domain,
"error setting twolame VBR quality"); "error setting twolame VBR quality");
return false; return false;
@ -153,28 +181,27 @@ twolame_encoder_setup(TwolameEncoder *encoder, Error &error)
} else { } else {
/* a bit rate was configured */ /* a bit rate was configured */
if (0 != twolame_set_brate(encoder->options, encoder->bitrate)) { if (0 != twolame_set_brate(options, bitrate)) {
error.Set(twolame_encoder_domain, error.Set(twolame_encoder_domain,
"error setting twolame bitrate"); "error setting twolame bitrate");
return false; return false;
} }
} }
if (0 != twolame_set_num_channels(encoder->options, if (0 != twolame_set_num_channels(options, audio_format.channels)) {
encoder->audio_format.channels)) {
error.Set(twolame_encoder_domain, error.Set(twolame_encoder_domain,
"error setting twolame num channels"); "error setting twolame num channels");
return false; return false;
} }
if (0 != twolame_set_in_samplerate(encoder->options, if (0 != twolame_set_in_samplerate(options,
encoder->audio_format.sample_rate)) { audio_format.sample_rate)) {
error.Set(twolame_encoder_domain, error.Set(twolame_encoder_domain,
"error setting twolame sample rate"); "error setting twolame sample rate");
return false; return false;
} }
if (0 > twolame_init_params(encoder->options)) { if (0 > twolame_init_params(options)) {
error.Set(twolame_encoder_domain, error.Set(twolame_encoder_domain,
"error initializing twolame params"); "error initializing twolame params");
return false; return false;
@ -183,117 +210,89 @@ twolame_encoder_setup(TwolameEncoder *encoder, Error &error)
return true; return true;
} }
static bool static Encoder *
twolame_encoder_open(Encoder *_encoder, AudioFormat &audio_format, twolame_encoder_open(PreparedEncoder *_encoder, AudioFormat &audio_format,
Error &error) Error &error)
{ {
TwolameEncoder *encoder = (TwolameEncoder *)_encoder; auto *encoder = (PreparedTwolameEncoder *)_encoder;
audio_format.format = SampleFormat::S16; audio_format.format = SampleFormat::S16;
audio_format.channels = 2; audio_format.channels = 2;
encoder->audio_format = audio_format; auto options = twolame_init();
if (options == nullptr) {
encoder->options = twolame_init();
if (encoder->options == nullptr) {
error.Set(twolame_encoder_domain, "twolame_init() failed"); error.Set(twolame_encoder_domain, "twolame_init() failed");
return false; return nullptr;
} }
if (!twolame_encoder_setup(encoder, error)) { if (!twolame_encoder_setup(options, encoder->quality, encoder->bitrate,
twolame_close(&encoder->options); audio_format, error)) {
return false; twolame_close(&options);
return nullptr;
} }
encoder->output_buffer_length = 0; return new TwolameEncoder(audio_format, options);
encoder->output_buffer_position = 0;
encoder->flush = false;
return true;
} }
static void TwolameEncoder::~TwolameEncoder()
twolame_encoder_close(Encoder *_encoder)
{ {
TwolameEncoder *encoder = (TwolameEncoder *)_encoder; twolame_close(&options);
twolame_close(&encoder->options);
} }
static bool bool
twolame_encoder_flush(Encoder *_encoder, gcc_unused Error &error) TwolameEncoder::Write(const void *data, size_t length,
{
TwolameEncoder *encoder = (TwolameEncoder *)_encoder;
encoder->flush = true;
return true;
}
static bool
twolame_encoder_write(Encoder *_encoder,
const void *data, size_t length,
gcc_unused Error &error) gcc_unused Error &error)
{ {
TwolameEncoder *encoder = (TwolameEncoder *)_encoder;
const int16_t *src = (const int16_t*)data; const int16_t *src = (const int16_t*)data;
assert(encoder->output_buffer_position == assert(output_buffer_position == output_buffer_length);
encoder->output_buffer_length);
const unsigned num_frames = const unsigned num_frames = length / audio_format.GetFrameSize();
length / encoder->audio_format.GetFrameSize();
int bytes_out = twolame_encode_buffer_interleaved(encoder->options, int bytes_out = twolame_encode_buffer_interleaved(options,
src, num_frames, src, num_frames,
encoder->output_buffer, output_buffer,
sizeof(encoder->output_buffer)); sizeof(output_buffer));
if (bytes_out < 0) { if (bytes_out < 0) {
error.Set(twolame_encoder_domain, "twolame encoder failed"); error.Set(twolame_encoder_domain, "twolame encoder failed");
return false; return false;
} }
encoder->output_buffer_length = (size_t)bytes_out; output_buffer_length = (size_t)bytes_out;
encoder->output_buffer_position = 0; output_buffer_position = 0;
return true; return true;
} }
static size_t size_t
twolame_encoder_read(Encoder *_encoder, void *dest, size_t length) TwolameEncoder::Read(void *dest, size_t length)
{ {
TwolameEncoder *encoder = (TwolameEncoder *)_encoder; assert(output_buffer_position <= output_buffer_length);
assert(encoder->output_buffer_position <= if (output_buffer_position == output_buffer_length && flush) {
encoder->output_buffer_length); int ret = twolame_encode_flush(options, output_buffer,
sizeof(output_buffer));
if (encoder->output_buffer_position == encoder->output_buffer_length &&
encoder->flush) {
int ret = twolame_encode_flush(encoder->options,
encoder->output_buffer,
sizeof(encoder->output_buffer));
if (ret > 0) { if (ret > 0) {
encoder->output_buffer_length = (size_t)ret; output_buffer_length = (size_t)ret;
encoder->output_buffer_position = 0; output_buffer_position = 0;
} }
encoder->flush = false; flush = false;
} }
const size_t remainning = encoder->output_buffer_length const size_t remainning = output_buffer_length - output_buffer_position;
- encoder->output_buffer_position;
if (length > remainning) if (length > remainning)
length = remainning; length = remainning;
memcpy(dest, encoder->output_buffer + encoder->output_buffer_position, memcpy(dest, output_buffer + output_buffer_position, length);
length);
encoder->output_buffer_position += length; output_buffer_position += length;
return length; return length;
} }
static const char * static const char *
twolame_encoder_get_mime_type(gcc_unused Encoder *_encoder) twolame_encoder_get_mime_type(gcc_unused PreparedEncoder *_encoder)
{ {
return "audio/mpeg"; return "audio/mpeg";
} }
@ -303,12 +302,5 @@ const EncoderPlugin twolame_encoder_plugin = {
twolame_encoder_init, twolame_encoder_init,
twolame_encoder_finish, twolame_encoder_finish,
twolame_encoder_open, twolame_encoder_open,
twolame_encoder_close,
twolame_encoder_flush,
twolame_encoder_flush,
nullptr,
nullptr,
twolame_encoder_write,
twolame_encoder_read,
twolame_encoder_get_mime_type, twolame_encoder_get_mime_type,
}; };

View File

@ -31,17 +31,7 @@
#include <vorbis/vorbisenc.h> #include <vorbis/vorbisenc.h>
struct VorbisEncoder { class VorbisEncoder final : public Encoder {
/** the base class */
Encoder encoder;
/* configuration */
float quality;
int bitrate;
/* runtime information */
AudioFormat audio_format; AudioFormat audio_format;
vorbis_dsp_state vd; vorbis_dsp_state vd;
@ -50,22 +40,57 @@ struct VorbisEncoder {
OggStream stream; OggStream stream;
VorbisEncoder():encoder(vorbis_encoder_plugin) {} public:
VorbisEncoder()
:Encoder(true) {}
bool Configure(const ConfigBlock &block, Error &error); virtual ~VorbisEncoder() {
Clear();
}
bool Reinit(Error &error); bool Open(float quality, int bitrate, AudioFormat &audio_format,
Error &error);
/* virtual methods from class Encoder */
bool End(Error &error) override {
return PreTag(error);
}
bool Flush(Error &error) override;
bool PreTag(Error &error) override;
bool SendTag(const Tag &tag, Error &error) override;
bool Write(const void *data, size_t length, Error &) override;
size_t Read(void *dest, size_t length) override {
return stream.PageOut(dest, length);
}
private:
void HeaderOut(vorbis_comment &vc); void HeaderOut(vorbis_comment &vc);
void SendHeader(); void SendHeader();
void BlockOut(); void BlockOut();
void Clear(); void Clear();
}; };
struct PreparedVorbisEncoder {
/** the base class */
PreparedEncoder encoder;
/* configuration */
float quality;
int bitrate;
PreparedVorbisEncoder():encoder(vorbis_encoder_plugin) {}
bool Configure(const ConfigBlock &block, Error &error);
};
static constexpr Domain vorbis_encoder_domain("vorbis_encoder"); static constexpr Domain vorbis_encoder_domain("vorbis_encoder");
bool bool
VorbisEncoder::Configure(const ConfigBlock &block, Error &error) PreparedVorbisEncoder::Configure(const ConfigBlock &block, Error &error)
{ {
const char *value = block.GetBlockValue("quality"); const char *value = block.GetBlockValue("quality");
if (value != nullptr) { if (value != nullptr) {
@ -111,10 +136,10 @@ VorbisEncoder::Configure(const ConfigBlock &block, Error &error)
return true; return true;
} }
static Encoder * static PreparedEncoder *
vorbis_encoder_init(const ConfigBlock &block, Error &error) vorbis_encoder_init(const ConfigBlock &block, Error &error)
{ {
auto *encoder = new VorbisEncoder(); auto *encoder = new PreparedVorbisEncoder();
/* load configuration from "block" */ /* load configuration from "block" */
if (!encoder->Configure(block, error)) { if (!encoder->Configure(block, error)) {
@ -127,9 +152,9 @@ vorbis_encoder_init(const ConfigBlock &block, Error &error)
} }
static void static void
vorbis_encoder_finish(Encoder *_encoder) vorbis_encoder_finish(PreparedEncoder *_encoder)
{ {
VorbisEncoder *encoder = (VorbisEncoder *)_encoder; auto *encoder = (PreparedVorbisEncoder *)_encoder;
/* the real libvorbis/libogg cleanup was already performed by /* the real libvorbis/libogg cleanup was already performed by
vorbis_encoder_close(), so no real work here */ vorbis_encoder_close(), so no real work here */
@ -137,8 +162,12 @@ vorbis_encoder_finish(Encoder *_encoder)
} }
bool bool
VorbisEncoder::Reinit(Error &error) VorbisEncoder::Open(float quality, int bitrate, AudioFormat &_audio_format,
Error &error)
{ {
_audio_format.format = SampleFormat::FLOAT;
audio_format = _audio_format;
vorbis_info_init(&vi); vorbis_info_init(&vi);
if (quality >= -1.0) { if (quality >= -1.0) {
@ -171,6 +200,8 @@ VorbisEncoder::Reinit(Error &error)
vorbis_block_init(&vd, &vb); vorbis_block_init(&vd, &vb);
stream.Initialize(GenerateOggSerial()); stream.Initialize(GenerateOggSerial());
SendHeader();
return true; return true;
} }
@ -197,23 +228,20 @@ VorbisEncoder::SendHeader()
vorbis_comment_clear(&vc); vorbis_comment_clear(&vc);
} }
static bool static Encoder *
vorbis_encoder_open(Encoder *_encoder, vorbis_encoder_open(PreparedEncoder *_encoder,
AudioFormat &audio_format, AudioFormat &audio_format,
Error &error) Error &error)
{ {
auto &encoder = *(VorbisEncoder *)_encoder; auto &encoder = *(PreparedVorbisEncoder *)_encoder;
audio_format.format = SampleFormat::FLOAT; auto *e = new VorbisEncoder();
if (!e->Open(encoder.quality, encoder.bitrate, audio_format, error)) {
delete e;
return nullptr;
}
encoder.audio_format = audio_format; return e;
if (!encoder.Reinit(error))
return false;
encoder.SendHeader();
return true;
} }
void void
@ -225,14 +253,6 @@ VorbisEncoder::Clear()
vorbis_info_clear(&vi); vorbis_info_clear(&vi);
} }
static void
vorbis_encoder_close(Encoder *_encoder)
{
auto &encoder = *(VorbisEncoder *)_encoder;
encoder.Clear();
}
void void
VorbisEncoder::BlockOut() VorbisEncoder::BlockOut()
{ {
@ -246,31 +266,27 @@ VorbisEncoder::BlockOut()
} }
} }
static bool bool
vorbis_encoder_flush(Encoder *_encoder, gcc_unused Error &error) VorbisEncoder::Flush(gcc_unused Error &error)
{ {
auto &encoder = *(VorbisEncoder *)_encoder; stream.Flush();
encoder.stream.Flush();
return true; return true;
} }
static bool bool
vorbis_encoder_pre_tag(Encoder *_encoder, gcc_unused Error &error) VorbisEncoder::PreTag(gcc_unused Error &error)
{ {
auto &encoder = *(VorbisEncoder *)_encoder; vorbis_analysis_wrote(&vd, 0);
BlockOut();
vorbis_analysis_wrote(&encoder.vd, 0);
encoder.BlockOut();
/* reinitialize vorbis_dsp_state and vorbis_block to reset the /* reinitialize vorbis_dsp_state and vorbis_block to reset the
end-of-stream marker */ end-of-stream marker */
vorbis_block_clear(&encoder.vb); vorbis_block_clear(&vb);
vorbis_dsp_clear(&encoder.vd); vorbis_dsp_clear(&vd);
vorbis_analysis_init(&encoder.vd, &encoder.vi); vorbis_analysis_init(&vd, &vi);
vorbis_block_init(&encoder.vd, &encoder.vb); vorbis_block_init(&vd, &vb);
encoder.stream.Flush(); stream.Flush();
return true; return true;
} }
@ -284,11 +300,9 @@ copy_tag_to_vorbis_comment(vorbis_comment *vc, const Tag &tag)
} }
} }
static bool bool
vorbis_encoder_tag(Encoder *_encoder, const Tag &tag, VorbisEncoder::SendTag(const Tag &tag, gcc_unused Error &error)
gcc_unused Error &error)
{ {
auto &encoder = *(VorbisEncoder *)_encoder;
vorbis_comment comment; vorbis_comment comment;
/* write the vorbis_comment object */ /* write the vorbis_comment object */
@ -298,11 +312,11 @@ vorbis_encoder_tag(Encoder *_encoder, const Tag &tag,
/* reset ogg_stream_state and begin a new stream */ /* reset ogg_stream_state and begin a new stream */
encoder.stream.Reinitialize(GenerateOggSerial()); stream.Reinitialize(GenerateOggSerial());
/* send that vorbis_comment to the ogg_stream_state */ /* send that vorbis_comment to the ogg_stream_state */
encoder.HeaderOut(comment); HeaderOut(comment);
vorbis_comment_clear(&comment); vorbis_comment_clear(&comment);
return true; return true;
@ -317,38 +331,25 @@ interleaved_to_vorbis_buffer(float **dest, const float *src,
dest[j][i] = *src++; dest[j][i] = *src++;
} }
static bool bool
vorbis_encoder_write(Encoder *_encoder, VorbisEncoder::Write(const void *data, size_t length, gcc_unused Error &error)
const void *data, size_t length,
gcc_unused Error &error)
{ {
auto &encoder = *(VorbisEncoder *)_encoder; unsigned num_frames = length / audio_format.GetFrameSize();
unsigned num_frames = length / encoder.audio_format.GetFrameSize();
/* this is for only 16-bit audio */ /* this is for only 16-bit audio */
interleaved_to_vorbis_buffer(vorbis_analysis_buffer(&encoder.vd, interleaved_to_vorbis_buffer(vorbis_analysis_buffer(&vd, num_frames),
num_frames),
(const float *)data, (const float *)data,
num_frames, num_frames,
encoder.audio_format.channels); audio_format.channels);
vorbis_analysis_wrote(&encoder.vd, num_frames); vorbis_analysis_wrote(&vd, num_frames);
encoder.BlockOut(); BlockOut();
return true; return true;
} }
static size_t
vorbis_encoder_read(Encoder *_encoder, void *dest, size_t length)
{
auto &encoder = *(VorbisEncoder *)_encoder;
return encoder.stream.PageOut(dest, length);
}
static const char * static const char *
vorbis_encoder_get_mime_type(gcc_unused Encoder *_encoder) vorbis_encoder_get_mime_type(gcc_unused PreparedEncoder *_encoder)
{ {
return "audio/ogg"; return "audio/ogg";
} }
@ -358,12 +359,5 @@ const EncoderPlugin vorbis_encoder_plugin = {
vorbis_encoder_init, vorbis_encoder_init,
vorbis_encoder_finish, vorbis_encoder_finish,
vorbis_encoder_open, vorbis_encoder_open,
vorbis_encoder_close,
vorbis_encoder_pre_tag,
vorbis_encoder_flush,
vorbis_encoder_pre_tag,
vorbis_encoder_tag,
vorbis_encoder_write,
vorbis_encoder_read,
vorbis_encoder_get_mime_type, vorbis_encoder_get_mime_type,
}; };

View File

@ -21,7 +21,6 @@
#include "WaveEncoderPlugin.hxx" #include "WaveEncoderPlugin.hxx"
#include "../EncoderAPI.hxx" #include "../EncoderAPI.hxx"
#include "system/ByteOrder.hxx" #include "system/ByteOrder.hxx"
#include "util/Manual.hxx"
#include "util/DynamicFifoBuffer.hxx" #include "util/DynamicFifoBuffer.hxx"
#include <assert.h> #include <assert.h>
@ -29,13 +28,26 @@
static constexpr uint16_t WAVE_FORMAT_PCM = 1; static constexpr uint16_t WAVE_FORMAT_PCM = 1;
struct WaveEncoder { class WaveEncoder final : public Encoder {
Encoder encoder;
unsigned bits; unsigned bits;
Manual<DynamicFifoBuffer<uint8_t>> buffer; DynamicFifoBuffer<uint8_t> buffer;
WaveEncoder():encoder(wave_encoder_plugin) {} public:
WaveEncoder(AudioFormat &audio_format);
/* virtual methods from class Encoder */
bool Write(const void *data, size_t length, Error &) override;
size_t Read(void *dest, size_t length) override {
return buffer.Read((uint8_t *)dest, length);
}
};
struct PreparedWaveEncoder {
PreparedEncoder encoder;
PreparedWaveEncoder():encoder(wave_encoder_plugin) {}
}; };
struct WaveHeader { struct WaveHeader {
@ -80,78 +92,71 @@ fill_wave_header(WaveHeader *header, int channels, int bits,
header->riff_size = ToLE32(4 + (8 + 16) + (8 + data_size)); header->riff_size = ToLE32(4 + (8 + 16) + (8 + data_size));
} }
static Encoder * static PreparedEncoder *
wave_encoder_init(gcc_unused const ConfigBlock &block, wave_encoder_init(gcc_unused const ConfigBlock &block,
gcc_unused Error &error) gcc_unused Error &error)
{ {
WaveEncoder *encoder = new WaveEncoder(); auto *encoder = new PreparedWaveEncoder();
return &encoder->encoder; return &encoder->encoder;
} }
static void static void
wave_encoder_finish(Encoder *_encoder) wave_encoder_finish(PreparedEncoder *_encoder)
{ {
WaveEncoder *encoder = (WaveEncoder *)_encoder; auto *encoder = (PreparedWaveEncoder *)_encoder;
delete encoder; delete encoder;
} }
static bool WaveEncoder::WaveEncoder(AudioFormat &audio_format)
wave_encoder_open(Encoder *_encoder, :Encoder(false),
AudioFormat &audio_format, buffer(8192)
gcc_unused Error &error)
{ {
WaveEncoder *encoder = (WaveEncoder *)_encoder;
assert(audio_format.IsValid()); assert(audio_format.IsValid());
switch (audio_format.format) { switch (audio_format.format) {
case SampleFormat::S8: case SampleFormat::S8:
encoder->bits = 8; bits = 8;
break; break;
case SampleFormat::S16: case SampleFormat::S16:
encoder->bits = 16; bits = 16;
break; break;
case SampleFormat::S24_P32: case SampleFormat::S24_P32:
encoder->bits = 24; bits = 24;
break; break;
case SampleFormat::S32: case SampleFormat::S32:
encoder->bits = 32; bits = 32;
break; break;
default: default:
audio_format.format = SampleFormat::S16; audio_format.format = SampleFormat::S16;
encoder->bits = 16; bits = 16;
break; break;
} }
encoder->buffer.Construct(8192); auto range = buffer.Write();
auto range = encoder->buffer->Write();
assert(range.size >= sizeof(WaveHeader)); assert(range.size >= sizeof(WaveHeader));
auto *header = (WaveHeader *)range.data; auto *header = (WaveHeader *)range.data;
/* create PCM wave header in initial buffer */ /* create PCM wave header in initial buffer */
fill_wave_header(header, fill_wave_header(header,
audio_format.channels, audio_format.channels,
encoder->bits, bits,
audio_format.sample_rate, audio_format.sample_rate,
(encoder->bits / 8) * audio_format.channels); (bits / 8) * audio_format.channels);
encoder->buffer->Append(sizeof(*header)); buffer.Append(sizeof(*header));
return true;
} }
static void static Encoder *
wave_encoder_close(Encoder *_encoder) wave_encoder_open(gcc_unused PreparedEncoder *_encoder,
AudioFormat &audio_format,
gcc_unused Error &error)
{ {
WaveEncoder *encoder = (WaveEncoder *)_encoder; return new WaveEncoder(audio_format);
encoder->buffer.Destruct();
} }
static size_t static size_t
@ -194,17 +199,14 @@ pcm24_to_wave(uint8_t *dst8, const uint32_t *src32, size_t length)
return (dst8 - dst_old); return (dst8 - dst_old);
} }
static bool bool
wave_encoder_write(Encoder *_encoder, WaveEncoder::Write(const void *src, size_t length,
const void *src, size_t length,
gcc_unused Error &error) gcc_unused Error &error)
{ {
WaveEncoder *encoder = (WaveEncoder *)_encoder; uint8_t *dst = buffer.Write(length);
uint8_t *dst = encoder->buffer->Write(length);
if (IsLittleEndian()) { if (IsLittleEndian()) {
switch (encoder->bits) { switch (bits) {
case 8: case 8:
case 16: case 16:
case 32:// optimized cases case 32:// optimized cases
@ -215,7 +217,7 @@ wave_encoder_write(Encoder *_encoder,
break; break;
} }
} else { } else {
switch (encoder->bits) { switch (bits) {
case 8: case 8:
memcpy(dst, src, length); memcpy(dst, src, length);
break; break;
@ -233,20 +235,12 @@ wave_encoder_write(Encoder *_encoder,
} }
} }
encoder->buffer->Append(length); buffer.Append(length);
return true; return true;
} }
static size_t
wave_encoder_read(Encoder *_encoder, void *dest, size_t length)
{
WaveEncoder *encoder = (WaveEncoder *)_encoder;
return encoder->buffer->Read((uint8_t *)dest, length);
}
static const char * static const char *
wave_encoder_get_mime_type(gcc_unused Encoder *_encoder) wave_encoder_get_mime_type(gcc_unused PreparedEncoder *_encoder)
{ {
return "audio/wav"; return "audio/wav";
} }
@ -256,12 +250,5 @@ const EncoderPlugin wave_encoder_plugin = {
wave_encoder_init, wave_encoder_init,
wave_encoder_finish, wave_encoder_finish,
wave_encoder_open, wave_encoder_open,
wave_encoder_close,
nullptr,
nullptr,
nullptr,
nullptr,
wave_encoder_write,
wave_encoder_read,
wave_encoder_get_mime_type, wave_encoder_get_mime_type,
}; };

View File

@ -47,7 +47,8 @@ class RecorderOutput {
/** /**
* The configured encoder plugin. * The configured encoder plugin.
*/ */
Encoder *encoder = nullptr; PreparedEncoder *prepared_encoder = nullptr;
Encoder *encoder;
/** /**
* The destination file name. * The destination file name.
@ -75,8 +76,8 @@ class RecorderOutput {
:base(recorder_output_plugin) {} :base(recorder_output_plugin) {}
~RecorderOutput() { ~RecorderOutput() {
if (encoder != nullptr) if (prepared_encoder != nullptr)
encoder->Dispose(); prepared_encoder->Dispose();
} }
bool Initialize(const ConfigBlock &block, Error &error_r) { bool Initialize(const ConfigBlock &block, Error &error_r) {
@ -148,8 +149,8 @@ RecorderOutput::Configure(const ConfigBlock &block, Error &error)
/* initialize encoder */ /* initialize encoder */
encoder = encoder_init(*encoder_plugin, block, error); prepared_encoder = encoder_init(*encoder_plugin, block, error);
if (encoder == nullptr) if (prepared_encoder == nullptr)
return false; return false;
return true; return true;
@ -205,7 +206,8 @@ RecorderOutput::Open(AudioFormat &audio_format, Error &error)
/* open the encoder */ /* open the encoder */
if (!encoder->Open(audio_format, error)) { encoder = prepared_encoder->Open(audio_format, error);
if (encoder == nullptr) {
delete file; delete file;
return false; return false;
} }
@ -214,7 +216,7 @@ RecorderOutput::Open(AudioFormat &audio_format, Error &error)
try { try {
EncoderToFile(); EncoderToFile();
} catch (const std::exception &e) { } catch (const std::exception &e) {
encoder->Close(); delete encoder;
error.Set(recorder_domain, e.what()); error.Set(recorder_domain, e.what());
return false; return false;
} }
@ -224,7 +226,7 @@ RecorderOutput::Open(AudioFormat &audio_format, Error &error)
/* close the encoder for now; it will be opened as /* close the encoder for now; it will be opened as
soon as we have received a tag */ soon as we have received a tag */
encoder->Close(); delete encoder;
} }
return true; return true;
@ -237,19 +239,19 @@ RecorderOutput::Commit(Error &error)
/* flush the encoder and write the rest to the file */ /* flush the encoder and write the rest to the file */
bool success = encoder_end(encoder, error); bool success = encoder->End(error);
if (success) { if (success) {
try { try {
EncoderToFile(); EncoderToFile();
} catch (...) { } catch (...) {
encoder->Close(); delete encoder;
throw; throw;
} }
} }
/* now really close everything */ /* now really close everything */
encoder->Close(); delete encoder;
if (success) { if (success) {
try { try {
@ -326,7 +328,8 @@ RecorderOutput::ReopenFormat(AllocatedPath &&new_path, Error &error)
} }
AudioFormat new_audio_format = effective_audio_format; AudioFormat new_audio_format = effective_audio_format;
if (!encoder->Open(new_audio_format, error)) { encoder = prepared_encoder->Open(new_audio_format, error);
if (encoder == nullptr) {
delete new_file; delete new_file;
return false; return false;
} }
@ -338,7 +341,7 @@ RecorderOutput::ReopenFormat(AllocatedPath &&new_path, Error &error)
try { try {
EncoderToOutputStream(*new_file, *encoder); EncoderToOutputStream(*new_file, *encoder);
} catch (const std::exception &e) { } catch (const std::exception &e) {
encoder->Close(); delete encoder;
delete new_file; delete new_file;
error.Set(recorder_domain, e.what()); error.Set(recorder_domain, e.what());
return false; return false;
@ -386,7 +389,7 @@ RecorderOutput::SendTag(const Tag &tag)
} }
Error error; Error error;
if (!encoder_pre_tag(encoder, error)) { if (!encoder->PreTag(error)) {
LogError(error); LogError(error);
return; return;
} }
@ -398,7 +401,7 @@ RecorderOutput::SendTag(const Tag &tag)
return; return;
} }
if (!encoder_tag(encoder, tag, error)) if (!encoder->SendTag(tag, error))
LogError(error); LogError(error);
} }
@ -413,7 +416,7 @@ RecorderOutput::Play(const void *chunk, size_t size, Error &error)
return size; return size;
} }
if (!encoder_write(encoder, chunk, size, error)) if (!encoder->Write(chunk, size, error))
return 0; return 0;
try { try {

View File

@ -45,7 +45,8 @@ struct ShoutOutput final {
shout_t *shout_conn; shout_t *shout_conn;
shout_metadata_t *shout_meta; shout_metadata_t *shout_meta;
Encoder *encoder = nullptr; PreparedEncoder *prepared_encoder = nullptr;
Encoder *encoder;
float quality = -2.0; float quality = -2.0;
int bitrate = -1; int bitrate = -1;
@ -93,8 +94,7 @@ ShoutOutput::~ShoutOutput()
if (shout_init_count == 0) if (shout_init_count == 0)
shout_shutdown(); shout_shutdown();
if (encoder != nullptr) delete prepared_encoder;
encoder->Dispose();
} }
static const EncoderPlugin * static const EncoderPlugin *
@ -192,8 +192,8 @@ ShoutOutput::Configure(const ConfigBlock &block, Error &error)
return false; return false;
} }
encoder = encoder_init(*encoder_plugin, block, error); prepared_encoder = encoder_init(*encoder_plugin, block, error);
if (encoder == nullptr) if (prepared_encoder == nullptr)
return false; return false;
unsigned shout_format; unsigned shout_format;
@ -345,8 +345,8 @@ write_page(ShoutOutput *sd, Error &error)
assert(sd->encoder != nullptr); assert(sd->encoder != nullptr);
while (true) { while (true) {
size_t nbytes = encoder_read(sd->encoder, size_t nbytes = sd->encoder->Read(sd->buffer,
sd->buffer, sizeof(sd->buffer)); sizeof(sd->buffer));
if (nbytes == 0) if (nbytes == 0)
return true; return true;
@ -362,10 +362,10 @@ void
ShoutOutput::Close() ShoutOutput::Close()
{ {
if (encoder != nullptr) { if (encoder != nullptr) {
if (encoder_end(encoder, IgnoreError())) if (encoder->End(IgnoreError()))
write_page(this, IgnoreError()); write_page(this, IgnoreError());
encoder->Close(); delete encoder;
} }
if (shout_get_connected(shout_conn) != SHOUTERR_UNCONNECTED && if (shout_get_connected(shout_conn) != SHOUTERR_UNCONNECTED &&
@ -406,13 +406,14 @@ ShoutOutput::Open(AudioFormat &audio_format, Error &error)
if (!shout_connect(this, error)) if (!shout_connect(this, error))
return false; return false;
if (!encoder->Open(audio_format, error)) { encoder = prepared_encoder->Open(audio_format, error);
if (encoder == nullptr) {
shout_close(shout_conn); shout_close(shout_conn);
return false; return false;
} }
if (!write_page(this, error)) { if (!write_page(this, error)) {
encoder->Close(); delete encoder;
shout_close(shout_conn); shout_close(shout_conn);
return false; return false;
} }
@ -433,7 +434,7 @@ ShoutOutput::Delay() const
size_t size_t
ShoutOutput::Play(const void *chunk, size_t size, Error &error) ShoutOutput::Play(const void *chunk, size_t size, Error &error)
{ {
return encoder_write(encoder, chunk, size, error) && return encoder->Write(chunk, size, error) &&
write_page(this, error) write_page(this, error)
? size ? size
: 0; : 0;
@ -476,13 +477,13 @@ shout_tag_to_metadata(const Tag &tag, char *dest, size_t size)
void void
ShoutOutput::SendTag(const Tag &tag) ShoutOutput::SendTag(const Tag &tag)
{ {
if (encoder->plugin.tag != nullptr) { if (encoder->ImplementsTag()) {
/* encoder plugin supports stream tags */ /* encoder plugin supports stream tags */
Error error; Error error;
if (!encoder_pre_tag(encoder, error) || if (!encoder->PreTag(error) ||
!write_page(this, error) || !write_page(this, error) ||
!encoder_tag(encoder, tag, error)) { !encoder->SendTag(tag, error)) {
LogError(error); LogError(error);
return; return;
} }

View File

@ -45,7 +45,8 @@ class EventLoop;
class ServerSocket; class ServerSocket;
class HttpdClient; class HttpdClient;
class Page; class Page;
struct Encoder; struct PreparedEncoder;
class Encoder;
struct Tag; struct Tag;
class HttpdOutput final : ServerSocket, DeferredMonitor { class HttpdOutput final : ServerSocket, DeferredMonitor {
@ -60,6 +61,7 @@ class HttpdOutput final : ServerSocket, DeferredMonitor {
/** /**
* The configured encoder plugin. * The configured encoder plugin.
*/ */
PreparedEncoder *prepared_encoder = nullptr;
Encoder *encoder; Encoder *encoder;
/** /**

View File

@ -63,8 +63,8 @@ HttpdOutput::~HttpdOutput()
if (metadata != nullptr) if (metadata != nullptr)
metadata->Unref(); metadata->Unref();
if (encoder != nullptr) if (prepared_encoder != nullptr)
encoder->Dispose(); prepared_encoder->Dispose();
} }
@ -123,12 +123,12 @@ HttpdOutput::Configure(const ConfigBlock &block, Error &error)
/* initialize encoder */ /* initialize encoder */
encoder = encoder_init(*encoder_plugin, block, error); prepared_encoder = encoder_init(*encoder_plugin, block, error);
if (encoder == nullptr) if (prepared_encoder == nullptr)
return false; return false;
/* determine content type */ /* determine content type */
content_type = encoder_get_mime_type(encoder); content_type = encoder_get_mime_type(prepared_encoder);
if (content_type == nullptr) if (content_type == nullptr)
content_type = "application/octet-stream"; content_type = "application/octet-stream";
@ -169,7 +169,7 @@ inline void
HttpdOutput::AddClient(int fd) HttpdOutput::AddClient(int fd)
{ {
auto *client = new HttpdClient(*this, fd, GetEventLoop(), auto *client = new HttpdClient(*this, fd, GetEventLoop(),
encoder->plugin.tag == nullptr); !encoder->ImplementsTag());
clients.push_front(*client); clients.push_front(*client);
/* pass metadata to client */ /* pass metadata to client */
@ -250,15 +250,14 @@ HttpdOutput::ReadPage()
/* we have fed a lot of input into the encoder, but it /* we have fed a lot of input into the encoder, but it
didn't give anything back yet - flush now to avoid didn't give anything back yet - flush now to avoid
buffer underruns */ buffer underruns */
encoder_flush(encoder, IgnoreError()); encoder->Flush(IgnoreError());
unflushed_input = 0; unflushed_input = 0;
} }
size_t size = 0; size_t size = 0;
do { do {
size_t nbytes = encoder_read(encoder, size_t nbytes = encoder->Read(buffer + size,
buffer + size, sizeof(buffer) - size);
sizeof(buffer) - size);
if (nbytes == 0) if (nbytes == 0)
break; break;
@ -292,7 +291,8 @@ httpd_output_disable(AudioOutput *ao)
inline bool inline bool
HttpdOutput::OpenEncoder(AudioFormat &audio_format, Error &error) HttpdOutput::OpenEncoder(AudioFormat &audio_format, Error &error)
{ {
if (!encoder->Open(audio_format, error)) encoder = prepared_encoder->Open(audio_format, error);
if (encoder == nullptr)
return false; return false;
/* we have to remember the encoder header, i.e. the first /* we have to remember the encoder header, i.e. the first
@ -351,7 +351,7 @@ HttpdOutput::Close()
if (header != nullptr) if (header != nullptr)
header->Unref(); header->Unref();
encoder->Close(); delete encoder;
} }
static void static void
@ -441,7 +441,7 @@ HttpdOutput::BroadcastFromEncoder()
inline bool inline bool
HttpdOutput::EncodeAndPlay(const void *chunk, size_t size, Error &error) HttpdOutput::EncodeAndPlay(const void *chunk, size_t size, Error &error)
{ {
if (!encoder_write(encoder, chunk, size, error)) if (!encoder->Write(chunk, size, error))
return false; return false;
unflushed_input += size; unflushed_input += size;
@ -491,18 +491,18 @@ httpd_output_pause(AudioOutput *ao)
inline void inline void
HttpdOutput::SendTag(const Tag &tag) HttpdOutput::SendTag(const Tag &tag)
{ {
if (encoder->plugin.tag != nullptr) { if (encoder->ImplementsTag()) {
/* embed encoder tags */ /* embed encoder tags */
/* flush the current stream, and end it */ /* flush the current stream, and end it */
encoder_pre_tag(encoder, IgnoreError()); encoder->PreTag(IgnoreError());
BroadcastFromEncoder(); BroadcastFromEncoder();
/* send the tag to the encoder - which starts a new /* send the tag to the encoder - which starts a new
stream now */ stream now */
encoder_tag(encoder, tag, IgnoreError()); encoder->SendTag(tag, IgnoreError());
/* the first page generated by the encoder will now be /* the first page generated by the encoder will now be
used as the new "header" page, which is sent to all used as the new "header" page, which is sent to all

View File

@ -65,8 +65,8 @@ int main(int argc, char **argv)
try { try {
Error error; Error error;
const auto encoder = encoder_init(*plugin, block, error); const auto p_encoder = encoder_init(*plugin, block, error);
if (encoder == NULL) { if (p_encoder == nullptr) {
LogError(error, "Failed to initialize encoder"); LogError(error, "Failed to initialize encoder");
return EXIT_FAILURE; return EXIT_FAILURE;
} }
@ -81,7 +81,8 @@ int main(int argc, char **argv)
} }
} }
if (!encoder->Open(audio_format, error)) { auto *encoder = p_encoder->Open(audio_format, error);
if (encoder == nullptr) {
LogError(error, "Failed to open encoder"); LogError(error, "Failed to open encoder");
return EXIT_FAILURE; return EXIT_FAILURE;
} }
@ -94,7 +95,7 @@ int main(int argc, char **argv)
ssize_t nbytes; ssize_t nbytes;
while ((nbytes = read(0, buffer, sizeof(buffer))) > 0) { while ((nbytes = read(0, buffer, sizeof(buffer))) > 0) {
if (!encoder_write(encoder, buffer, nbytes, error)) { if (!encoder->Write(buffer, nbytes, error)) {
LogError(error, "encoder_write() failed"); LogError(error, "encoder_write() failed");
return EXIT_FAILURE; return EXIT_FAILURE;
} }
@ -102,15 +103,15 @@ int main(int argc, char **argv)
EncoderToOutputStream(os, *encoder); EncoderToOutputStream(os, *encoder);
} }
if (!encoder_end(encoder, error)) { if (!encoder->End(error)) {
LogError(error, "encoder_flush() failed"); LogError(error, "encoder_flush() failed");
return EXIT_FAILURE; return EXIT_FAILURE;
} }
EncoderToOutputStream(os, *encoder); EncoderToOutputStream(os, *encoder);
encoder->Close(); delete encoder;
encoder->Dispose(); p_encoder->Dispose();
return EXIT_SUCCESS; return EXIT_SUCCESS;
} catch (const std::exception &e) { } catch (const std::exception &e) {

View File

@ -48,15 +48,15 @@ main(gcc_unused int argc, gcc_unused char **argv)
ConfigBlock block; ConfigBlock block;
block.AddBlockParam("quality", "5.0", -1); block.AddBlockParam("quality", "5.0", -1);
const auto encoder = encoder_init(*plugin, block, IgnoreError()); const auto p_encoder = encoder_init(*plugin, block, IgnoreError());
assert(encoder != NULL); assert(p_encoder != nullptr);
try { try {
/* open the encoder */ /* open the encoder */
AudioFormat audio_format(44100, SampleFormat::S16, 2); AudioFormat audio_format(44100, SampleFormat::S16, 2);
success = encoder->Open(audio_format, IgnoreError()); auto encoder = p_encoder->Open(audio_format, IgnoreError());
assert(success); assert(encoder != nullptr);
StdioOutputStream os(stdout); StdioOutputStream os(stdout);
@ -64,14 +64,14 @@ main(gcc_unused int argc, gcc_unused char **argv)
/* write a block of data */ /* write a block of data */
success = encoder_write(encoder, zero, sizeof(zero), IgnoreError()); success = encoder->Write(zero, sizeof(zero), IgnoreError());
assert(success); assert(success);
EncoderToOutputStream(os, *encoder); EncoderToOutputStream(os, *encoder);
/* write a tag */ /* write a tag */
success = encoder_pre_tag(encoder, IgnoreError()); success = encoder->PreTag(IgnoreError());
assert(success); assert(success);
EncoderToOutputStream(os, *encoder); EncoderToOutputStream(os, *encoder);
@ -85,25 +85,25 @@ main(gcc_unused int argc, gcc_unused char **argv)
tag_builder.Commit(tag); tag_builder.Commit(tag);
} }
success = encoder_tag(encoder, tag, IgnoreError()); success = encoder->SendTag(tag, IgnoreError());
assert(success); assert(success);
EncoderToOutputStream(os, *encoder); EncoderToOutputStream(os, *encoder);
/* write another block of data */ /* write another block of data */
success = encoder_write(encoder, zero, sizeof(zero), IgnoreError()); success = encoder->Write(zero, sizeof(zero), IgnoreError());
assert(success); assert(success);
/* finish */ /* finish */
success = encoder_end(encoder, IgnoreError()); success = encoder->End(IgnoreError());
assert(success); assert(success);
EncoderToOutputStream(os, *encoder); EncoderToOutputStream(os, *encoder);
encoder->Close(); delete encoder;
encoder->Dispose(); p_encoder->Dispose();
return EXIT_SUCCESS; return EXIT_SUCCESS;
} catch (const std::exception &e) { } catch (const std::exception &e) {