aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMax Kellermann <max@duempel.org>2009-07-14 23:07:41 +0200
committerMax Kellermann <max@duempel.org>2009-07-14 23:07:41 +0200
commit6233de05464c11b714cbfcb2692ff22691b1475a (patch)
tree074d6e00f9b85e0c7d840044ea8198b0aead2666
parentb1afa40fc12088818a01c810d8cf6617a3afe627 (diff)
downloadmpd-6233de05464c11b714cbfcb2692ff22691b1475a.tar.gz
mpd-6233de05464c11b714cbfcb2692ff22691b1475a.tar.xz
mpd-6233de05464c11b714cbfcb2692ff22691b1475a.zip
encoder/twolame: new encoder plugin based on libtwolame
This encoder plugin is a replacement for the LAME encoder plugin for those who prefer a "free" (non-patent encumbered) encoder library. Most of the plugin source code is copied from the LAME encoder plugin, since the LAME and TwoLAME APIs are nearly the same.
-rw-r--r--Makefile.am6
-rw-r--r--NEWS2
-rw-r--r--configure.ac25
-rw-r--r--src/encoder/twolame_encoder.c299
-rw-r--r--src/encoder_list.c4
5 files changed, 335 insertions, 1 deletions
diff --git a/Makefile.am b/Makefile.am
index 7b16b1116..5c53ca5c1 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -432,10 +432,12 @@ endif
ENCODER_CFLAGS = \
$(LAME_CFLAGS) \
+ $(TWOLAME_CFLAGS) \
$(VORBISENC_CFLAGS)
ENCODER_LIBS = \
$(LAME_LIBS) \
+ $(TWOLAME_LIBS) \
$(VORBISENC_LIBS)
ENCODER_SRC =
@@ -450,6 +452,10 @@ endif
if ENABLE_LAME_ENCODER
ENCODER_SRC += src/encoder/lame_encoder.c
endif
+
+if ENABLE_TWOLAME_ENCODER
+ENCODER_SRC += src/encoder/twolame_encoder.c
+endif
endif
diff --git a/NEWS b/NEWS
index dd52d8008..6a6696b45 100644
--- a/NEWS
+++ b/NEWS
@@ -10,6 +10,8 @@ ver 0.16 (20??/??/??)
- ffmpeg: support multiple tags
- sndfile: new decoder plugin based on libsndfile
- flac: load external cue sheet when no internal one
+* encoders:
+ - twolame: new encoder plugin based on libtwolame
* mixers:
- removed support for legacy mixer configuration
- reimplemented software volume as mixer+filter plugin
diff --git a/configure.ac b/configure.ac
index 27251e427..c3a780dc2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -618,6 +618,11 @@ AC_ARG_ENABLE(lame-encoder,
[enable the LAME mp3 encoder]),,
enable_lame_encoder=auto)
+AC_ARG_ENABLE(twolame-encoder,
+ AS_HELP_STRING([--enable-twolame-encoder],
+ [enable the TwoLAME mp2 encoder]),,
+ enable_twolame_encoder=auto)
+
dnl
dnl audio output plugins
@@ -1002,6 +1007,7 @@ else
# don't bother to check for encoder plugins
enable_vorbis_encoder=no
enable_lame_encoder=no
+ enable_twolame_encoder=no
fi
MPD_AUTO_PKG(vorbis_encoder, VORBISENC, [vorbisenc],
@@ -1016,8 +1022,12 @@ fi
AC_SUBST(LAME_CFLAGS)
AC_SUBST(LAME_LIBS)
+MPD_AUTO_PKG(twolame_encoder, TWOLAME, [twolame],
+ [TwoLAME encoder], [libtwolame not found])
+
if test x$enable_vorbis_encoder != xno ||
- test x$enable_lame_encoder != xno; then
+ test x$enable_lame_encoder != xno ||
+ test x$enable_twolame_encoder != xno; then
# at least one encoder plugin is enabled
enable_encoder=yes
else
@@ -1076,6 +1086,12 @@ if test x$enable_lame_encoder = xyes; then
[Define to enable the lame encoder plugin])
fi
+AM_CONDITIONAL(ENABLE_TWOLAME_ENCODER, test x$enable_twolame_encoder = xyes)
+if test x$enable_twolame_encoder = xyes; then
+ AC_DEFINE(ENABLE_TWOLAME_ENCODER, 1,
+ [Define to enable the TwoLAME encoder plugin])
+fi
+
dnl
dnl Documentation
@@ -1305,6 +1321,13 @@ if
else
echo " Ogg Vorbis encoder ............disabled"
fi
+
+ if test x$enable_twolame_encoder = xyes; then
+ echo " TwoLAME mp3 encoder ...........enabled"
+ else
+ echo " TwoLAME mp3 encoder ...........disabled"
+ fi
+
echo ""
fi
diff --git a/src/encoder/twolame_encoder.c b/src/encoder/twolame_encoder.c
new file mode 100644
index 000000000..5a8a82d81
--- /dev/null
+++ b/src/encoder/twolame_encoder.c
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2003-2009 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "encoder_api.h"
+#include "encoder_plugin.h"
+#include "audio_format.h"
+
+#include <twolame.h>
+#include <assert.h>
+#include <string.h>
+
+struct twolame_encoder {
+ struct encoder encoder;
+
+ struct audio_format audio_format;
+ float quality;
+ int bitrate;
+
+ twolame_options *options;
+
+ unsigned char buffer[32768];
+ size_t buffer_length;
+
+ /**
+ * Call libtwolame's flush function when the buffer is empty?
+ */
+ bool flush;
+};
+
+extern const struct encoder_plugin twolame_encoder_plugin;
+
+static inline GQuark
+twolame_encoder_quark(void)
+{
+ return g_quark_from_static_string("twolame_encoder");
+}
+
+static bool
+twolame_encoder_configure(struct twolame_encoder *encoder,
+ const struct config_param *param, GError **error)
+{
+ const char *value;
+ char *endptr;
+
+ value = config_get_block_string(param, "quality", NULL);
+ if (value != NULL) {
+ /* a quality was configured (VBR) */
+
+ encoder->quality = g_ascii_strtod(value, &endptr);
+
+ if (*endptr != '\0' || encoder->quality < -1.0 ||
+ encoder->quality > 10.0) {
+ g_set_error(error, twolame_encoder_quark(), 0,
+ "quality \"%s\" is not a number in the "
+ "range -1 to 10, line %i",
+ value, param->line);
+ return false;
+ }
+
+ if (config_get_block_string(param, "bitrate", NULL) != NULL) {
+ g_set_error(error, twolame_encoder_quark(), 0,
+ "quality and bitrate are "
+ "both defined (line %i)",
+ param->line);
+ return false;
+ }
+ } else {
+ /* a bit rate was configured */
+
+ value = config_get_block_string(param, "bitrate", NULL);
+ if (value == NULL) {
+ g_set_error(error, twolame_encoder_quark(), 0,
+ "neither bitrate nor quality defined "
+ "at line %i",
+ param->line);
+ return false;
+ }
+
+ encoder->quality = -2.0;
+ encoder->bitrate = g_ascii_strtoll(value, &endptr, 10);
+
+ if (*endptr != '\0' || encoder->bitrate <= 0) {
+ g_set_error(error, twolame_encoder_quark(), 0,
+ "bitrate at line %i should be a positive integer",
+ param->line);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static struct encoder *
+twolame_encoder_init(const struct config_param *param, GError **error)
+{
+ struct twolame_encoder *encoder;
+
+ g_debug("libtwolame version %s", get_twolame_version());
+
+ encoder = g_new(struct twolame_encoder, 1);
+ encoder_struct_init(&encoder->encoder, &twolame_encoder_plugin);
+
+ /* load configuration from "param" */
+ if (!twolame_encoder_configure(encoder, param, error)) {
+ /* configuration has failed, roll back and return error */
+ g_free(encoder);
+ return NULL;
+ }
+
+ return &encoder->encoder;
+}
+
+static void
+twolame_encoder_finish(struct encoder *_encoder)
+{
+ struct twolame_encoder *encoder = (struct twolame_encoder *)_encoder;
+
+ /* the real libtwolame cleanup was already performed by
+ twolame_encoder_close(), so no real work here */
+ g_free(encoder);
+}
+
+static bool
+twolame_encoder_setup(struct twolame_encoder *encoder, GError **error)
+{
+ if (encoder->quality >= -1.0) {
+ /* a quality was configured (VBR) */
+
+ if (0 != twolame_set_VBR(encoder->options, true)) {
+ g_set_error(error, twolame_encoder_quark(), 0,
+ "error setting twolame VBR mode");
+ return false;
+ }
+ if (0 != twolame_set_VBR_q(encoder->options, encoder->quality)) {
+ g_set_error(error, twolame_encoder_quark(), 0,
+ "error setting twolame VBR quality");
+ return false;
+ }
+ } else {
+ /* a bit rate was configured */
+
+ if (0 != twolame_set_brate(encoder->options, encoder->bitrate)) {
+ g_set_error(error, twolame_encoder_quark(), 0,
+ "error setting twolame bitrate");
+ return false;
+ }
+ }
+
+ if (0 != twolame_set_num_channels(encoder->options,
+ encoder->audio_format.channels)) {
+ g_set_error(error, twolame_encoder_quark(), 0,
+ "error setting twolame num channels");
+ return false;
+ }
+
+ if (0 != twolame_set_in_samplerate(encoder->options,
+ encoder->audio_format.sample_rate)) {
+ g_set_error(error, twolame_encoder_quark(), 0,
+ "error setting twolame sample rate");
+ return false;
+ }
+
+ if (0 > twolame_init_params(encoder->options)) {
+ g_set_error(error, twolame_encoder_quark(), 0,
+ "error initializing twolame params");
+ return false;
+ }
+
+ return true;
+}
+
+static bool
+twolame_encoder_open(struct encoder *_encoder, struct audio_format *audio_format,
+ GError **error)
+{
+ struct twolame_encoder *encoder = (struct twolame_encoder *)_encoder;
+
+ audio_format->bits = 16;
+ audio_format->channels = 2;
+
+ encoder->audio_format = *audio_format;
+
+ encoder->options = twolame_init();
+ if (encoder->options == NULL) {
+ g_set_error(error, twolame_encoder_quark(), 0,
+ "twolame_init() failed");
+ return false;
+ }
+
+ if (!twolame_encoder_setup(encoder, error)) {
+ twolame_close(&encoder->options);
+ return false;
+ }
+
+ encoder->buffer_length = 0;
+ encoder->flush = false;
+
+ return true;
+}
+
+static void
+twolame_encoder_close(struct encoder *_encoder)
+{
+ struct twolame_encoder *encoder = (struct twolame_encoder *)_encoder;
+
+ twolame_close(&encoder->options);
+}
+
+static bool
+twolame_encoder_flush(struct encoder *_encoder, G_GNUC_UNUSED GError **error)
+{
+ struct twolame_encoder *encoder = (struct twolame_encoder *)_encoder;
+
+ encoder->flush = true;
+ return true;
+}
+
+static bool
+twolame_encoder_write(struct encoder *_encoder,
+ const void *data, size_t length,
+ G_GNUC_UNUSED GError **error)
+{
+ struct twolame_encoder *encoder = (struct twolame_encoder *)_encoder;
+ unsigned num_frames;
+ const int16_t *src = (const int16_t*)data;
+ int bytes_out;
+
+ assert(encoder->buffer_length == 0);
+
+ num_frames =
+ length / audio_format_frame_size(&encoder->audio_format);
+
+ bytes_out = twolame_encode_buffer_interleaved(encoder->options,
+ src, num_frames,
+ encoder->buffer,
+ sizeof(encoder->buffer));
+ if (bytes_out < 0) {
+ g_set_error(error, twolame_encoder_quark(), 0,
+ "twolame encoder failed");
+ return false;
+ }
+
+ encoder->buffer_length = (size_t)bytes_out;
+ return true;
+}
+
+static size_t
+twolame_encoder_read(struct encoder *_encoder, void *dest, size_t length)
+{
+ struct twolame_encoder *encoder = (struct twolame_encoder *)_encoder;
+
+ if (encoder->buffer_length == 0 && encoder->flush) {
+ int ret = twolame_encode_flush(encoder->options,
+ encoder->buffer,
+ sizeof(encoder->buffer));
+ if (ret > 0)
+ encoder->buffer_length = (size_t)ret;
+
+ encoder->flush = false;
+ }
+
+ if (length > encoder->buffer_length)
+ length = encoder->buffer_length;
+
+ memcpy(dest, encoder->buffer, length);
+
+ encoder->buffer_length -= length;
+ memmove(encoder->buffer, encoder->buffer + length,
+ encoder->buffer_length);
+
+ return length;
+}
+
+const struct encoder_plugin twolame_encoder_plugin = {
+ .name = "twolame",
+ .init = twolame_encoder_init,
+ .finish = twolame_encoder_finish,
+ .open = twolame_encoder_open,
+ .close = twolame_encoder_close,
+ .flush = twolame_encoder_flush,
+ .write = twolame_encoder_write,
+ .read = twolame_encoder_read,
+};
diff --git a/src/encoder_list.c b/src/encoder_list.c
index d563b6bc8..2016d4cba 100644
--- a/src/encoder_list.c
+++ b/src/encoder_list.c
@@ -25,6 +25,7 @@
extern const struct encoder_plugin vorbis_encoder_plugin;
extern const struct encoder_plugin lame_encoder_plugin;
+extern const struct encoder_plugin twolame_encoder_plugin;
static const struct encoder_plugin *encoder_plugins[] = {
#ifdef ENABLE_VORBIS_ENCODER
@@ -33,6 +34,9 @@ static const struct encoder_plugin *encoder_plugins[] = {
#ifdef ENABLE_LAME_ENCODER
&lame_encoder_plugin,
#endif
+#ifdef ENABLE_TWOLAME_ENCODER
+ &twolame_encoder_plugin,
+#endif
NULL
};