aboutsummaryrefslogtreecommitdiffstats
path: root/src/decoder
diff options
context:
space:
mode:
Diffstat (limited to 'src/decoder')
-rw-r--r--src/decoder/AdPlugDecoderPlugin.cxx148
-rw-r--r--src/decoder/AdPlugDecoderPlugin.h25
-rw-r--r--src/decoder/FLACCommon.cxx (renamed from src/decoder/_flac_common.c)25
-rw-r--r--src/decoder/FLACCommon.hxx (renamed from src/decoder/_flac_common.h)10
-rw-r--r--src/decoder/FLACDecoderPlugin.cxx (renamed from src/decoder/flac_decoder_plugin.c)130
-rw-r--r--src/decoder/FLACDecoderPlugin.h26
-rw-r--r--src/decoder/FLACMetaData.cxx (renamed from src/decoder/flac_metadata.c)51
-rw-r--r--src/decoder/FLACMetaData.hxx (renamed from src/decoder/flac_metadata.h)5
-rw-r--r--src/decoder/FLAC_PCM.cxx (renamed from src/decoder/flac_pcm.c)4
-rw-r--r--src/decoder/FLAC_PCM.hxx (renamed from src/decoder/flac_pcm.h)6
-rw-r--r--src/decoder/OggUtil.cxx56
-rw-r--r--src/decoder/OggUtil.hxx48
-rw-r--r--src/decoder/OpusDecoderPlugin.cxx366
-rw-r--r--src/decoder/OpusDecoderPlugin.h25
-rw-r--r--src/decoder/OpusHead.cxx44
-rw-r--r--src/decoder/OpusHead.hxx30
-rw-r--r--src/decoder/OpusReader.hxx97
-rw-r--r--src/decoder/OpusTags.cxx77
-rw-r--r--src/decoder/OpusTags.hxx31
-rw-r--r--src/decoder/XiphTags.c28
-rw-r--r--src/decoder/XiphTags.h28
-rw-r--r--src/decoder/flac_compat.h114
-rw-r--r--src/decoder/mad_decoder_plugin.c4
-rw-r--r--src/decoder/ogg_codec.c (renamed from src/decoder/_ogg_common.c)18
-rw-r--r--src/decoder/ogg_codec.h (renamed from src/decoder/_ogg_common.h)14
-rw-r--r--src/decoder/sidplay_decoder_plugin.cxx8
-rw-r--r--src/decoder/vorbis_comments.c10
-rw-r--r--src/decoder/vorbis_decoder_plugin.c115
28 files changed, 1223 insertions, 320 deletions
diff --git a/src/decoder/AdPlugDecoderPlugin.cxx b/src/decoder/AdPlugDecoderPlugin.cxx
new file mode 100644
index 000000000..b3b7f1d73
--- /dev/null
+++ b/src/decoder/AdPlugDecoderPlugin.cxx
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2003-2012 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 "config.h"
+#include "AdPlugDecoderPlugin.h"
+#include "tag_handler.h"
+
+extern "C" {
+#include "decoder_api.h"
+#include "audio_check.h"
+}
+
+#include <adplug/adplug.h>
+#include <adplug/emuopl.h>
+
+#include <glib.h>
+
+#include <assert.h>
+
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "adplug"
+
+static unsigned sample_rate;
+
+static bool
+adplug_init(const struct config_param *param)
+{
+ GError *error = NULL;
+
+ sample_rate = config_get_block_unsigned(param, "sample_rate", 48000);
+ if (!audio_check_sample_rate(sample_rate, &error)) {
+ g_warning("%s\n", error->message);
+ g_error_free(error);
+ return false;
+ }
+
+ return true;
+}
+
+static void
+adplug_file_decode(struct decoder *decoder, const char *path_fs)
+{
+ CEmuopl opl(sample_rate, true, true);
+ opl.init();
+
+ CPlayer *player = CAdPlug::factory(path_fs, &opl);
+ if (player == nullptr)
+ return;
+
+ struct audio_format audio_format;
+ audio_format_init(&audio_format, sample_rate, SAMPLE_FORMAT_S16, 2);
+ assert(audio_format_valid(&audio_format));
+
+ decoder_initialized(decoder, &audio_format, false,
+ player->songlength() / 1000.);
+
+ int16_t buffer[2048];
+ const unsigned frames_per_buffer = G_N_ELEMENTS(buffer) / 2;
+ enum decoder_command cmd;
+
+ do {
+ if (!player->update())
+ break;
+
+ opl.update(buffer, frames_per_buffer);
+ cmd = decoder_data(decoder, NULL,
+ buffer, sizeof(buffer),
+ 0);
+ } while (cmd == DECODE_COMMAND_NONE);
+
+ delete player;
+}
+
+static void
+adplug_scan_tag(enum tag_type type, const std::string &value,
+ const struct tag_handler *handler, void *handler_ctx)
+{
+ if (!value.empty())
+ tag_handler_invoke_tag(handler, handler_ctx,
+ type, value.c_str());
+}
+
+static bool
+adplug_scan_file(const char *path_fs,
+ const struct tag_handler *handler, void *handler_ctx)
+{
+ CEmuopl opl(sample_rate, true, true);
+ opl.init();
+
+ CPlayer *player = CAdPlug::factory(path_fs, &opl);
+ if (player == nullptr)
+ return false;
+
+ tag_handler_invoke_duration(handler, handler_ctx,
+ player->songlength() / 1000);
+
+ if (handler->tag != nullptr) {
+ adplug_scan_tag(TAG_TITLE, player->gettitle(),
+ handler, handler_ctx);
+ adplug_scan_tag(TAG_ARTIST, player->getauthor(),
+ handler, handler_ctx);
+ adplug_scan_tag(TAG_COMMENT, player->getdesc(),
+ handler, handler_ctx);
+ }
+
+ delete player;
+ return true;
+}
+
+static const char *const adplug_suffixes[] = {
+ "amd",
+ "d00",
+ "hsc",
+ "laa",
+ "rad",
+ "raw",
+ "sa2",
+ nullptr
+};
+
+const struct decoder_plugin adplug_decoder_plugin = {
+ "adplug",
+ adplug_init,
+ nullptr,
+ nullptr,
+ adplug_file_decode,
+ adplug_scan_file,
+ nullptr,
+ nullptr,
+ adplug_suffixes,
+ nullptr,
+};
diff --git a/src/decoder/AdPlugDecoderPlugin.h b/src/decoder/AdPlugDecoderPlugin.h
new file mode 100644
index 000000000..9fdf438aa
--- /dev/null
+++ b/src/decoder/AdPlugDecoderPlugin.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2003-2012 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.
+ */
+
+#ifndef MPD_DECODER_ADPLUG_H
+#define MPD_DECODER_ADPLUG_H
+
+extern const struct decoder_plugin adplug_decoder_plugin;
+
+#endif
diff --git a/src/decoder/_flac_common.c b/src/decoder/FLACCommon.cxx
index d7f0c4a8a..8e86b6ae4 100644
--- a/src/decoder/_flac_common.c
+++ b/src/decoder/FLACCommon.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -22,10 +22,13 @@
*/
#include "config.h"
-#include "_flac_common.h"
-#include "flac_metadata.h"
-#include "flac_pcm.h"
+#include "FLACCommon.hxx"
+#include "FLACMetaData.hxx"
+#include "FLAC_PCM.hxx"
+
+extern "C" {
#include "audio_check.h"
+}
#include <glib.h>
@@ -46,7 +49,7 @@ flac_data_init(struct flac_data *data, struct decoder * decoder,
data->position = 0;
data->decoder = decoder;
data->input_stream = input_stream;
- data->tag = NULL;
+ data->tag = nullptr;
}
void
@@ -54,7 +57,7 @@ flac_data_deinit(struct flac_data *data)
{
pcm_buffer_deinit(&data->buffer);
- if (data->tag != NULL)
+ if (data->tag != nullptr)
tag_free(data->tag);
}
@@ -86,7 +89,7 @@ flac_got_stream_info(struct flac_data *data,
if (data->initialized || data->unsupported)
return;
- GError *error = NULL;
+ GError *error = nullptr;
if (!audio_format_init_checked(&data->audio_format,
stream_info->sample_rate,
flac_sample_format(stream_info->bits_per_sample),
@@ -129,8 +132,8 @@ void flac_metadata_common_cb(const FLAC__StreamMetadata * block,
decoder_mixramp(data->decoder, replay_gain_db,
mixramp_start, mixramp_end);
- if (data->tag != NULL)
- flac_vorbis_comments_to_tag(data->tag, NULL,
+ if (data->tag != nullptr)
+ flac_vorbis_comments_to_tag(data->tag, nullptr,
&block->data.vorbis_comment);
default:
@@ -160,7 +163,7 @@ flac_got_first_frame(struct flac_data *data, const FLAC__FrameHeader *header)
if (data->unsupported)
return false;
- GError *error = NULL;
+ GError *error = nullptr;
if (!audio_format_init_checked(&data->audio_format,
header->sample_rate,
flac_sample_format(header->bits_per_sample),
@@ -199,7 +202,7 @@ flac_common_write(struct flac_data *data, const FLAC__Frame * frame,
buffer = pcm_buffer_get(&data->buffer, buffer_size);
flac_convert(buffer, frame->header.channels,
- data->audio_format.format, buf,
+ (enum sample_format)data->audio_format.format, buf,
0, frame->header.blocksize);
if (nbytes > 0)
diff --git a/src/decoder/_flac_common.h b/src/decoder/FLACCommon.hxx
index 0d90ba656..3d280cc49 100644
--- a/src/decoder/_flac_common.h
+++ b/src/decoder/FLACCommon.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -21,13 +21,13 @@
* Common data structures and functions used by FLAC and OggFLAC
*/
-#ifndef MPD_FLAC_COMMON_H
-#define MPD_FLAC_COMMON_H
+#ifndef MPD_FLAC_COMMON_HXX
+#define MPD_FLAC_COMMON_HXX
+extern "C" {
#include "decoder_api.h"
#include "pcm_buffer.h"
-
-#include <glib.h>
+}
#include <FLAC/stream_decoder.h>
#include <FLAC/metadata.h>
diff --git a/src/decoder/flac_decoder_plugin.c b/src/decoder/FLACDecoderPlugin.cxx
index fb0b3502d..0b543ea65 100644
--- a/src/decoder/flac_decoder_plugin.c
+++ b/src/decoder/FLACDecoderPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,13 +18,13 @@
*/
#include "config.h" /* must be first for large file support */
-#include "_flac_common.h"
-#include "flac_compat.h"
-#include "flac_metadata.h"
+#include "FLACDecoderPlugin.h"
+#include "FLACCommon.hxx"
+#include "FLACMetaData.hxx"
-#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
-#include "_ogg_common.h"
-#endif
+extern "C" {
+#include "ogg_codec.h"
+}
#include <glib.h>
@@ -34,14 +34,18 @@
#include <sys/stat.h>
#include <sys/types.h>
+#if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT <= 7
+#error libFLAC is too old
+#endif
+
/* this code was based on flac123, from flac-tools */
static FLAC__StreamDecoderReadStatus
flac_read_cb(G_GNUC_UNUSED const FLAC__StreamDecoder *fd,
- FLAC__byte buf[], flac_read_status_size_t *bytes,
+ FLAC__byte buf[], size_t *bytes,
void *fdata)
{
- struct flac_data *data = fdata;
+ struct flac_data *data = (struct flac_data *)fdata;
size_t r;
r = decoder_read(data->decoder, data->input_stream,
@@ -69,7 +73,7 @@ flac_seek_cb(G_GNUC_UNUSED const FLAC__StreamDecoder *fd,
return FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED;
if (!input_stream_lock_seek(data->input_stream, offset, SEEK_SET,
- NULL))
+ nullptr))
return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
return FLAC__STREAM_DECODER_SEEK_STATUS_OK;
@@ -120,28 +124,6 @@ flac_error_cb(G_GNUC_UNUSED const FLAC__StreamDecoder *fd,
flac_error_common_cb(status, (struct flac_data *) fdata);
}
-#if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT <= 7
-static void flacPrintErroredState(FLAC__SeekableStreamDecoderState state)
-{
- switch (state) {
- case FLAC__SEEKABLE_STREAM_DECODER_OK:
- case FLAC__SEEKABLE_STREAM_DECODER_SEEKING:
- case FLAC__SEEKABLE_STREAM_DECODER_END_OF_STREAM:
- return;
-
- case FLAC__SEEKABLE_STREAM_DECODER_MEMORY_ALLOCATION_ERROR:
- case FLAC__SEEKABLE_STREAM_DECODER_READ_ERROR:
- case FLAC__SEEKABLE_STREAM_DECODER_SEEK_ERROR:
- case FLAC__SEEKABLE_STREAM_DECODER_STREAM_DECODER_ERROR:
- case FLAC__SEEKABLE_STREAM_DECODER_ALREADY_INITIALIZED:
- case FLAC__SEEKABLE_STREAM_DECODER_INVALID_CALLBACK:
- case FLAC__SEEKABLE_STREAM_DECODER_UNINITIALIZED:
- break;
- }
-
- g_warning("%s\n", FLAC__SeekableStreamDecoderStateString[state]);
-}
-#else /* FLAC_API_VERSION_CURRENT >= 7 */
static void flacPrintErroredState(FLAC__StreamDecoderState state)
{
switch (state) {
@@ -162,7 +144,6 @@ static void flacPrintErroredState(FLAC__StreamDecoderState state)
g_warning("%s\n", FLAC__StreamDecoderStateString[state]);
}
-#endif /* FLAC_API_VERSION_CURRENT >= 7 */
static void flacMetadata(G_GNUC_UNUSED const FLAC__StreamDecoder * dec,
const FLAC__StreamMetadata * block, void *vdata)
@@ -195,7 +176,7 @@ static bool
flac_scan_file(const char *file,
const struct tag_handler *handler, void *handler_ctx)
{
- return flac_scan_file2(file, NULL, handler, handler_ctx);
+ return flac_scan_file2(file, nullptr, handler, handler_ctx);
}
/**
@@ -205,15 +186,13 @@ static FLAC__StreamDecoder *
flac_decoder_new(void)
{
FLAC__StreamDecoder *sd = FLAC__stream_decoder_new();
- if (sd == NULL) {
+ if (sd == nullptr) {
g_warning("FLAC__stream_decoder_new() failed");
- return NULL;
+ return nullptr;
}
-#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
if(!FLAC__stream_decoder_set_metadata_respond(sd, FLAC__METADATA_TYPE_VORBIS_COMMENT))
g_debug("FLAC__stream_decoder_set_metadata_respond() has failed");
-#endif
return sd;
}
@@ -259,7 +238,7 @@ flac_decoder_loop(struct flac_data *data, FLAC__StreamDecoder *flac_dec,
data->first_frame = t_start;
while (true) {
- if (data->tag != NULL && !tag_is_empty(data->tag)) {
+ if (data->tag != nullptr && !tag_is_empty(data->tag)) {
cmd = decoder_tag(data->decoder, data->input_stream,
data->tag);
tag_free(data->tag);
@@ -300,7 +279,6 @@ flac_decoder_loop(struct flac_data *data, FLAC__StreamDecoder *flac_dec,
static FLAC__StreamDecoderInitStatus
stream_init_oggflac(FLAC__StreamDecoder *flac_dec, struct flac_data *data)
{
-#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
return FLAC__stream_decoder_init_ogg_stream(flac_dec,
flac_read_cb,
flac_seek_cb,
@@ -311,12 +289,6 @@ stream_init_oggflac(FLAC__StreamDecoder *flac_dec, struct flac_data *data)
flacMetadata,
flac_error_cb,
data);
-#else
- (void)flac_dec;
- (void)data;
-
- return FLAC__STREAM_DECODER_INIT_STATUS_ERROR;
-#endif
}
static FLAC__StreamDecoderInitStatus
@@ -348,7 +320,7 @@ flac_decode_internal(struct decoder * decoder,
struct flac_data data;
flac_dec = flac_decoder_new();
- if (flac_dec == NULL)
+ if (flac_dec == nullptr)
return;
flac_data_init(&data, decoder, input_stream);
@@ -359,9 +331,7 @@ flac_decode_internal(struct decoder * decoder,
if (status != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
flac_data_deinit(&data);
FLAC__stream_decoder_delete(flac_dec);
-#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
g_warning("%s", FLAC__StreamDecoderInitStatusString[status]);
-#endif
return;
}
@@ -386,21 +356,12 @@ flac_decode(struct decoder * decoder, struct input_stream *input_stream)
flac_decode_internal(decoder, input_stream, false);
}
-#ifndef HAVE_OGGFLAC
-
static bool
oggflac_init(G_GNUC_UNUSED const struct config_param *param)
{
-#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
return !!FLAC_API_SUPPORTS_OGG_FLAC;
-#else
- /* disable oggflac when libflac is too old */
- return false;
-#endif
}
-#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
-
static bool
oggflac_scan_file(const char *file,
const struct tag_handler *handler, void *handler_ctx)
@@ -421,7 +382,7 @@ oggflac_scan_file(const char *file,
if (!(block = FLAC__metadata_iterator_get_block(it)))
break;
- flac_scan_metadata(NULL, block,
+ flac_scan_metadata(nullptr, block,
handler, handler_ctx);
} while (FLAC__metadata_iterator_next(it));
FLAC__metadata_iterator_delete(it);
@@ -433,54 +394,57 @@ oggflac_scan_file(const char *file,
static void
oggflac_decode(struct decoder *decoder, struct input_stream *input_stream)
{
- if (ogg_stream_type_detect(input_stream) != FLAC)
+ if (ogg_codec_detect(decoder, input_stream) != OGG_CODEC_FLAC)
return;
- /* rewind the stream, because ogg_stream_type_detect() has
+ /* rewind the stream, because ogg_codec_detect() has
moved it */
- input_stream_lock_seek(input_stream, 0, SEEK_SET, NULL);
+ input_stream_lock_seek(input_stream, 0, SEEK_SET, nullptr);
flac_decode_internal(decoder, input_stream, true);
}
-static const char *const oggflac_suffixes[] = { "ogg", "oga", NULL };
+static const char *const oggflac_suffixes[] = { "ogg", "oga", nullptr };
static const char *const oggflac_mime_types[] = {
"application/ogg",
"application/x-ogg",
"audio/ogg",
"audio/x-flac+ogg",
"audio/x-ogg",
- NULL
+ nullptr
};
-#endif /* FLAC_API_VERSION_CURRENT >= 7 */
-
const struct decoder_plugin oggflac_decoder_plugin = {
- .name = "oggflac",
- .init = oggflac_init,
-#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
- .stream_decode = oggflac_decode,
- .scan_file = oggflac_scan_file,
- .suffixes = oggflac_suffixes,
- .mime_types = oggflac_mime_types
-#endif
+ "oggflac",
+ oggflac_init,
+ nullptr,
+ oggflac_decode,
+ nullptr,
+ oggflac_scan_file,
+ nullptr,
+ nullptr,
+ oggflac_suffixes,
+ oggflac_mime_types,
};
-#endif /* HAVE_OGGFLAC */
-
-static const char *const flac_suffixes[] = { "flac", NULL };
+static const char *const flac_suffixes[] = { "flac", nullptr };
static const char *const flac_mime_types[] = {
"application/flac",
"application/x-flac",
"audio/flac",
"audio/x-flac",
- NULL
+ nullptr
};
const struct decoder_plugin flac_decoder_plugin = {
- .name = "flac",
- .stream_decode = flac_decode,
- .scan_file = flac_scan_file,
- .suffixes = flac_suffixes,
- .mime_types = flac_mime_types,
+ "flac",
+ nullptr,
+ nullptr,
+ flac_decode,
+ nullptr,
+ flac_scan_file,
+ nullptr,
+ nullptr,
+ flac_suffixes,
+ flac_mime_types,
};
diff --git a/src/decoder/FLACDecoderPlugin.h b/src/decoder/FLACDecoderPlugin.h
new file mode 100644
index 000000000..c99deeef7
--- /dev/null
+++ b/src/decoder/FLACDecoderPlugin.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2003-2012 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.
+ */
+
+#ifndef MPD_DECODER_FLAC_H
+#define MPD_DECODER_FLAC_H
+
+extern const struct decoder_plugin flac_decoder_plugin;
+extern const struct decoder_plugin oggflac_decoder_plugin;
+
+#endif
diff --git a/src/decoder/flac_metadata.c b/src/decoder/FLACMetaData.cxx
index bd1eaf323..b1d82a93d 100644
--- a/src/decoder/flac_metadata.c
+++ b/src/decoder/FLACMetaData.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,9 +18,14 @@
*/
#include "config.h"
-#include "flac_metadata.h"
+#include "FLACMetaData.hxx"
+
+extern "C" {
+#include "XiphTags.h"
#include "replay_gain_info.h"
#include "tag.h"
+}
+
#include "tag_handler.h"
#include "tag_table.h"
@@ -91,7 +96,7 @@ flac_find_string_comment(const FLAC__StreamMetadata *block,
int len;
const unsigned char *p;
- *str = NULL;
+ *str = nullptr;
offset = FLAC__metadata_object_vorbiscomment_find_entry_from(block, 0,
cmnt);
if (offset < 0)
@@ -136,9 +141,9 @@ flac_comment_value(const FLAC__StreamMetadata_VorbisComment_Entry *entry,
if (entry->length <= name_length ||
g_ascii_strncasecmp(comment, name, name_length) != 0)
- return NULL;
+ return nullptr;
- if (char_tnum != NULL) {
+ if (char_tnum != nullptr) {
char_tnum_length = strlen(char_tnum);
if (entry->length > name_length + char_tnum_length + 2 &&
comment[name_length] == '[' &&
@@ -157,7 +162,7 @@ flac_comment_value(const FLAC__StreamMetadata_VorbisComment_Entry *entry,
return comment + name_length + 1;
}
- return NULL;
+ return nullptr;
}
/**
@@ -174,7 +179,7 @@ flac_copy_comment(const FLAC__StreamMetadata_VorbisComment_Entry *entry,
size_t value_length;
value = flac_comment_value(entry, name, char_tnum, &value_length);
- if (value != NULL) {
+ if (value != nullptr) {
char *p = g_strndup(value, value_length);
tag_handler_invoke_tag(handler, handler_ctx, tag_type, p);
g_free(p);
@@ -184,23 +189,16 @@ flac_copy_comment(const FLAC__StreamMetadata_VorbisComment_Entry *entry,
return false;
}
-static const struct tag_table flac_tags[] = {
- { "tracknumber", TAG_TRACK },
- { "discnumber", TAG_DISC },
- { "album artist", TAG_ALBUM_ARTIST },
- { NULL, TAG_NUM_OF_ITEM_TYPES }
-};
-
static void
flac_scan_comment(const char *char_tnum,
const FLAC__StreamMetadata_VorbisComment_Entry *entry,
const struct tag_handler *handler, void *handler_ctx)
{
- if (handler->pair != NULL) {
+ if (handler->pair != nullptr) {
char *name = g_strdup((const char*)entry->entry);
char *value = strchr(name, '=');
- if (value != NULL && value > name) {
+ if (value != nullptr && value > name) {
*value++ = 0;
tag_handler_invoke_pair(handler, handler_ctx,
name, value);
@@ -209,14 +207,15 @@ flac_scan_comment(const char *char_tnum,
g_free(name);
}
- for (const struct tag_table *i = flac_tags; i->name != NULL; ++i)
+ for (const struct tag_table *i = xiph_tags; i->name != nullptr; ++i)
if (flac_copy_comment(entry, i->name, i->type, char_tnum,
handler, handler_ctx))
return;
for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i)
if (flac_copy_comment(entry,
- tag_item_names[i], i, char_tnum,
+ tag_item_names[i], (enum tag_type)i,
+ char_tnum,
handler, handler_ctx))
return;
}
@@ -266,7 +265,7 @@ flac_scan_file2(const char *file, const char *char_tnum,
const struct tag_handler *handler, void *handler_ctx)
{
FLAC__Metadata_SimpleIterator *it;
- FLAC__StreamMetadata *block = NULL;
+ FLAC__StreamMetadata *block = nullptr;
it = FLAC__metadata_simple_iterator_new();
if (!FLAC__metadata_simple_iterator_init(it, file, 1, 0)) {
@@ -307,17 +306,3 @@ flac_scan_file2(const char *file, const char *char_tnum,
return true;
}
-
-struct tag *
-flac_tag_load(const char *file, const char *char_tnum)
-{
- struct tag *tag = tag_new();
-
- if (!flac_scan_file2(file, char_tnum, &add_tag_handler, tag) ||
- tag_is_empty(tag)) {
- tag_free(tag);
- tag = NULL;
- }
-
- return tag;
-}
diff --git a/src/decoder/flac_metadata.h b/src/decoder/FLACMetaData.hxx
index 3c463d5d6..1dbdc9a88 100644
--- a/src/decoder/flac_metadata.h
+++ b/src/decoder/FLACMetaData.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -58,7 +58,4 @@ bool
flac_scan_file2(const char *file, const char *char_tnum,
const struct tag_handler *handler, void *handler_ctx);
-struct tag *
-flac_tag_load(const char *file, const char *char_tnum);
-
#endif
diff --git a/src/decoder/flac_pcm.c b/src/decoder/FLAC_PCM.cxx
index 6964d8ac6..303530aa7 100644
--- a/src/decoder/flac_pcm.c
+++ b/src/decoder/FLAC_PCM.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,7 +18,7 @@
*/
#include "config.h"
-#include "flac_pcm.h"
+#include "FLAC_PCM.hxx"
#include <assert.h>
diff --git a/src/decoder/flac_pcm.h b/src/decoder/FLAC_PCM.hxx
index a931998c1..97d214c17 100644
--- a/src/decoder/flac_pcm.h
+++ b/src/decoder/FLAC_PCM.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_FLAC_PCM_H
-#define MPD_FLAC_PCM_H
+#ifndef MPD_FLAC_PCM_HXX
+#define MPD_FLAC_PCM_HXX
#include "audio_format.h"
diff --git a/src/decoder/OggUtil.cxx b/src/decoder/OggUtil.cxx
new file mode 100644
index 000000000..99f73d48e
--- /dev/null
+++ b/src/decoder/OggUtil.cxx
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2003-2012 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 "config.h"
+#include "OggUtil.hxx"
+
+extern "C" {
+#include "decoder_api.h"
+}
+
+bool
+OggFeed(ogg_sync_state &oy, struct decoder *decoder,
+ struct input_stream *input_stream, size_t size)
+{
+ char *buffer = ogg_sync_buffer(&oy, size);
+ if (buffer == nullptr)
+ return false;
+
+ size_t nbytes = decoder_read(decoder, input_stream,
+ buffer, size);
+ if (nbytes == 0)
+ return false;
+
+ ogg_sync_wrote(&oy, nbytes);
+ return true;
+}
+
+bool
+OggExpectPage(ogg_sync_state &oy, ogg_page &page,
+ struct decoder *decoder, struct input_stream *input_stream)
+{
+ while (true) {
+ int r = ogg_sync_pageout(&oy, &page);
+ if (r != 0)
+ return r > 0;
+
+ if (!OggFeed(oy, decoder, input_stream, 1024))
+ return false;
+ }
+}
diff --git a/src/decoder/OggUtil.hxx b/src/decoder/OggUtil.hxx
new file mode 100644
index 000000000..95bf6472f
--- /dev/null
+++ b/src/decoder/OggUtil.hxx
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2003-2012 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.
+ */
+
+#ifndef MPD_OGG_UTIL_HXX
+#define MPD_OGG_UTIL_HXX
+
+#include "check.h"
+
+#include <ogg/ogg.h>
+
+#include <stddef.h>
+
+/**
+ * Feed data from the #input_stream into the #ogg_sync_state.
+ *
+ * @return false on error or end-of-file
+ */
+bool
+OggFeed(ogg_sync_state &oy, struct decoder *decoder,
+ struct input_stream *input_stream, size_t size);
+
+/**
+ * Feed into the #ogg_sync_state until a page gets available. Garbage
+ * data at the beginning is considered a fatal error.
+ *
+ * @return true if a page is available
+ */
+bool
+OggExpectPage(ogg_sync_state &oy, ogg_page &page,
+ struct decoder *decoder, struct input_stream *input_stream);
+
+#endif
diff --git a/src/decoder/OpusDecoderPlugin.cxx b/src/decoder/OpusDecoderPlugin.cxx
new file mode 100644
index 000000000..35e368ca9
--- /dev/null
+++ b/src/decoder/OpusDecoderPlugin.cxx
@@ -0,0 +1,366 @@
+/*
+ * Copyright (C) 2003-2012 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 "config.h" /* must be first for large file support */
+#include "OpusDecoderPlugin.h"
+#include "OpusHead.hxx"
+#include "OpusTags.hxx"
+#include "OggUtil.hxx"
+
+extern "C" {
+#include "ogg_codec.h"
+#include "decoder_api.h"
+}
+
+#include "audio_check.h"
+#include "tag_handler.h"
+
+#include <opus.h>
+#include <ogg/ogg.h>
+
+#include <glib.h>
+
+#include <stdio.h>
+
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "opus"
+
+static const opus_int32 opus_sample_rate = 48000;
+
+gcc_pure
+static bool
+IsOpusHead(const ogg_packet &packet)
+{
+ return packet.bytes >= 8 && memcmp(packet.packet, "OpusHead", 8) == 0;
+}
+
+gcc_pure
+static bool
+IsOpusTags(const ogg_packet &packet)
+{
+ return packet.bytes >= 8 && memcmp(packet.packet, "OpusTags", 8) == 0;
+}
+
+static bool
+mpd_opus_init(G_GNUC_UNUSED const struct config_param *param)
+{
+ g_debug("%s", opus_get_version_string());
+
+ return true;
+}
+
+class MPDOpusDecoder {
+ struct decoder *decoder;
+ struct input_stream *input_stream;
+
+ ogg_stream_state os;
+
+ OpusDecoder *opus_decoder = nullptr;
+ opus_int16 *output_buffer = nullptr;
+ unsigned output_size = 0;
+
+ bool os_initialized = false;
+ bool found_opus = false;
+
+ int opus_serialno;
+
+ size_t frame_size;
+
+public:
+ MPDOpusDecoder(struct decoder *_decoder,
+ struct input_stream *_input_stream)
+ :decoder(_decoder), input_stream(_input_stream) {}
+ ~MPDOpusDecoder();
+
+ enum decoder_command HandlePage(ogg_page &page);
+ enum decoder_command HandlePacket(const ogg_packet &packet);
+ enum decoder_command HandleBOS(const ogg_packet &packet);
+ enum decoder_command HandleTags(const ogg_packet &packet);
+ enum decoder_command HandleAudio(const ogg_packet &packet);
+};
+
+MPDOpusDecoder::~MPDOpusDecoder()
+{
+ g_free(output_buffer);
+
+ if (opus_decoder != nullptr)
+ opus_decoder_destroy(opus_decoder);
+
+ if (os_initialized)
+ ogg_stream_clear(&os);
+}
+
+enum decoder_command
+MPDOpusDecoder::HandlePage(ogg_page &page)
+{
+ const auto page_serialno = ogg_page_serialno(&page);
+ if (!os_initialized) {
+ os_initialized = true;
+ ogg_stream_init(&os, page_serialno);
+ } else if (page_serialno != os.serialno)
+ ogg_stream_reset_serialno(&os, page_serialno);
+
+ ogg_stream_pagein(&os, &page);
+
+ ogg_packet packet;
+ while (ogg_stream_packetout(&os, &packet) == 1) {
+ enum decoder_command cmd = HandlePacket(packet);
+ if (cmd != DECODE_COMMAND_NONE)
+ return cmd;
+ }
+
+ return DECODE_COMMAND_NONE;
+}
+
+enum decoder_command
+MPDOpusDecoder::HandlePacket(const ogg_packet &packet)
+{
+ if (packet.e_o_s)
+ return DECODE_COMMAND_STOP;
+
+ if (packet.b_o_s)
+ return HandleBOS(packet);
+ else if (!found_opus)
+ return DECODE_COMMAND_STOP;
+
+ if (IsOpusTags(packet))
+ return HandleTags(packet);
+
+ return HandleAudio(packet);
+}
+
+enum decoder_command
+MPDOpusDecoder::HandleBOS(const ogg_packet &packet)
+{
+ assert(packet.b_o_s);
+
+ if (found_opus || !IsOpusHead(packet))
+ return DECODE_COMMAND_STOP;
+
+ unsigned channels;
+ if (!ScanOpusHeader(packet.packet, packet.bytes, channels) ||
+ !audio_valid_channel_count(channels))
+ return DECODE_COMMAND_STOP;
+
+ assert(opus_decoder == nullptr);
+ assert(output_buffer == nullptr);
+
+ opus_serialno = os.serialno;
+ found_opus = true;
+
+ /* TODO: parse attributes from the OpusHead (sample rate,
+ channels, ...) */
+
+ int opus_error;
+ opus_decoder = opus_decoder_create(opus_sample_rate, channels,
+ &opus_error);
+ if (opus_decoder == nullptr) {
+ g_warning("libopus error: %s",
+ opus_strerror(opus_error));
+ return DECODE_COMMAND_STOP;
+ }
+
+ struct audio_format audio_format;
+ audio_format_init(&audio_format, opus_sample_rate,
+ SAMPLE_FORMAT_S16, channels);
+ decoder_initialized(decoder, &audio_format, false, -1);
+ frame_size = audio_format_frame_size(&audio_format);
+
+ /* allocate an output buffer for 16 bit PCM samples big enough
+ to hold a quarter second, larger than 120ms required by
+ libopus */
+ output_size = audio_format.sample_rate / 4;
+ output_buffer = (opus_int16 *)
+ g_malloc(sizeof(*output_buffer) * output_size *
+ audio_format.channels);
+
+ return decoder_get_command(decoder);
+}
+
+enum decoder_command
+MPDOpusDecoder::HandleTags(const ogg_packet &packet)
+{
+ struct tag *tag = tag_new();
+
+ enum decoder_command cmd;
+ if (ScanOpusTags(packet.packet, packet.bytes, &add_tag_handler, tag) &&
+ !tag_is_empty(tag))
+ cmd = decoder_tag(decoder, input_stream, tag);
+ else
+ cmd = decoder_get_command(decoder);
+
+ tag_free(tag);
+ return cmd;
+}
+
+enum decoder_command
+MPDOpusDecoder::HandleAudio(const ogg_packet &packet)
+{
+ assert(opus_decoder != nullptr);
+
+ int nframes = opus_decode(opus_decoder,
+ (const unsigned char*)packet.packet,
+ packet.bytes,
+ output_buffer, output_size,
+ 0);
+ if (nframes < 0) {
+ g_warning("%s", opus_strerror(nframes));
+ return DECODE_COMMAND_STOP;
+ }
+
+ if (nframes > 0) {
+ const size_t nbytes = nframes * frame_size;
+ enum decoder_command cmd =
+ decoder_data(decoder, input_stream,
+ output_buffer, nbytes,
+ 0);
+ if (cmd != DECODE_COMMAND_NONE)
+ return cmd;
+ }
+
+ return DECODE_COMMAND_NONE;
+}
+
+static void
+mpd_opus_stream_decode(struct decoder *decoder,
+ struct input_stream *input_stream)
+{
+ if (ogg_codec_detect(decoder, input_stream) != OGG_CODEC_OPUS)
+ return;
+
+ /* rewind the stream, because ogg_codec_detect() has
+ moved it */
+ input_stream_lock_seek(input_stream, 0, SEEK_SET, nullptr);
+
+ MPDOpusDecoder d(decoder, input_stream);
+
+ ogg_sync_state oy;
+ ogg_sync_init(&oy);
+
+ while (true) {
+ if (!OggFeed(oy, decoder, input_stream, 1024))
+ break;
+
+ ogg_page page;
+ while (ogg_sync_pageout(&oy, &page) == 1) {
+ enum decoder_command cmd = d.HandlePage(page);
+ if (cmd != DECODE_COMMAND_NONE)
+ break;
+ }
+ }
+
+ ogg_sync_clear(&oy);
+}
+
+static bool
+mpd_opus_scan_stream(struct input_stream *is,
+ const struct tag_handler *handler, void *handler_ctx)
+{
+ ogg_sync_state oy;
+ ogg_sync_init(&oy);
+
+ ogg_page page;
+ if (!OggExpectPage(oy, page, nullptr, is)) {
+ ogg_sync_clear(&oy);
+ return false;
+ }
+
+ /* read at most two more pages */
+ unsigned remaining_pages = 2;
+
+ bool result = false;
+
+ ogg_stream_state os;
+ ogg_stream_init(&os, ogg_page_serialno(&page));
+ ogg_stream_pagein(&os, &page);
+
+ ogg_packet packet;
+ while (true) {
+ int r = ogg_stream_packetout(&os, &packet);
+ if (r < 0) {
+ result = false;
+ break;
+ }
+
+ if (r == 0) {
+ if (remaining_pages-- == 0)
+ break;
+
+ if (!OggExpectPage(oy, page, nullptr, is)) {
+ result = false;
+ break;
+ }
+
+ ogg_stream_pagein(&os, &page);
+ continue;
+ }
+
+ if (packet.b_o_s) {
+ if (!IsOpusHead(packet))
+ break;
+
+ unsigned channels;
+ if (!ScanOpusHeader(packet.packet, packet.bytes, channels) ||
+ !audio_valid_channel_count(channels)) {
+ result = false;
+ break;
+ }
+
+ result = true;
+ } else if (!result)
+ break;
+ else if (IsOpusTags(packet)) {
+ if (!ScanOpusTags(packet.packet, packet.bytes,
+ handler, handler_ctx))
+ result = false;
+
+ break;
+ }
+ }
+
+ ogg_stream_clear(&os);
+ ogg_sync_clear(&oy);
+
+ return result;
+}
+
+static const char *const opus_suffixes[] = {
+ "opus",
+ "ogg",
+ "oga",
+ nullptr
+};
+
+static const char *const opus_mime_types[] = {
+ "audio/opus",
+ nullptr
+};
+
+const struct decoder_plugin opus_decoder_plugin = {
+ "opus",
+ mpd_opus_init,
+ nullptr,
+ mpd_opus_stream_decode,
+ nullptr,
+ nullptr,
+ mpd_opus_scan_stream,
+ nullptr,
+ opus_suffixes,
+ opus_mime_types,
+};
diff --git a/src/decoder/OpusDecoderPlugin.h b/src/decoder/OpusDecoderPlugin.h
new file mode 100644
index 000000000..c95d6ded3
--- /dev/null
+++ b/src/decoder/OpusDecoderPlugin.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2003-2012 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.
+ */
+
+#ifndef MPD_DECODER_OPUS_H
+#define MPD_DECODER_OPUS_H
+
+extern const struct decoder_plugin opus_decoder_plugin;
+
+#endif
diff --git a/src/decoder/OpusHead.cxx b/src/decoder/OpusHead.cxx
new file mode 100644
index 000000000..c57e08e10
--- /dev/null
+++ b/src/decoder/OpusHead.cxx
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2003-2012 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 "config.h"
+#include "OpusHead.hxx"
+
+#include <stdint.h>
+#include <string.h>
+
+struct OpusHead {
+ char signature[8];
+ uint8_t version, channels;
+ uint16_t pre_skip;
+ uint32_t sample_rate;
+ uint16_t output_gain;
+ uint8_t channel_mapping;
+};
+
+bool
+ScanOpusHeader(const void *data, size_t size, unsigned &channels_r)
+{
+ const OpusHead *h = (const OpusHead *)data;
+ if (size < 19 || (h->version & 0xf0) != 0)
+ return false;
+
+ channels_r = h->channels;
+ return true;
+}
diff --git a/src/decoder/OpusHead.hxx b/src/decoder/OpusHead.hxx
new file mode 100644
index 000000000..9f75c4f70
--- /dev/null
+++ b/src/decoder/OpusHead.hxx
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2003-2012 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.
+ */
+
+#ifndef MPD_OPUS_HEAD_HXX
+#define MPD_OPUS_HEAD_HXX
+
+#include "check.h"
+
+#include <stddef.h>
+
+bool
+ScanOpusHeader(const void *data, size_t size, unsigned &channels_r);
+
+#endif
diff --git a/src/decoder/OpusReader.hxx b/src/decoder/OpusReader.hxx
new file mode 100644
index 000000000..2cfc14118
--- /dev/null
+++ b/src/decoder/OpusReader.hxx
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2003-2012 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.
+ */
+
+#ifndef MPD_OPUS_READER_HXX
+#define MPD_OPUS_READER_HXX
+
+#include "check.h"
+
+#include <stdint.h>
+#include <string.h>
+
+class OpusReader {
+ const uint8_t *p, *const end;
+
+public:
+ OpusReader(const void *_p, size_t size)
+ :p((const uint8_t *)_p), end(p + size) {}
+
+ bool Skip(size_t length) {
+ p += length;
+ return p <= end;
+ }
+
+ const void *Read(size_t length) {
+ const uint8_t *result = p;
+ return Skip(length)
+ ? result
+ : nullptr;
+ }
+
+ bool Expect(const void *value, size_t length) {
+ const void *data = Read(length);
+ return data != nullptr && memcmp(value, data, length) == 0;
+ }
+
+ bool ReadByte(uint8_t &value_r) {
+ if (p >= end)
+ return false;
+
+ value_r = *p++;
+ return true;
+ }
+
+ bool ReadShort(uint16_t &value_r) {
+ const uint8_t *value = (const uint8_t *)Read(sizeof(value_r));
+ if (value == nullptr)
+ return false;
+
+ value_r = value[0] | (value[1] << 8);
+ return true;
+ }
+
+ bool ReadWord(uint32_t &value_r) {
+ const uint8_t *value = (const uint8_t *)Read(sizeof(value_r));
+ if (value == nullptr)
+ return false;
+
+ value_r = value[0] | (value[1] << 8)
+ | (value[2] << 16) | (value[3] << 24);
+ return true;
+ }
+
+ bool SkipString() {
+ uint32_t length;
+ return ReadWord(length) && Skip(length);
+ }
+
+ char *ReadString() {
+ uint32_t length;
+ if (!ReadWord(length))
+ return nullptr;
+
+ const char *src = (const char *)Read(length);
+ if (src == nullptr)
+ return nullptr;
+
+ return strndup(src, length);
+ }
+};
+
+#endif
diff --git a/src/decoder/OpusTags.cxx b/src/decoder/OpusTags.cxx
new file mode 100644
index 000000000..cb35a6247
--- /dev/null
+++ b/src/decoder/OpusTags.cxx
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2003-2012 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 "config.h"
+#include "OpusTags.hxx"
+#include "OpusReader.hxx"
+#include "XiphTags.h"
+#include "tag_handler.h"
+
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+
+static void
+ScanOneOpusTag(const char *name, const char *value,
+ const struct tag_handler *handler, void *ctx)
+{
+ tag_handler_invoke_pair(handler, ctx, name, value);
+
+ if (handler->tag != nullptr) {
+ enum tag_type t = tag_table_lookup_i(xiph_tags, name);
+ if (t != TAG_NUM_OF_ITEM_TYPES)
+ tag_handler_invoke_tag(handler, ctx, t, value);
+ }
+}
+
+bool
+ScanOpusTags(const void *data, size_t size,
+ const struct tag_handler *handler, void *ctx)
+{
+ OpusReader r(data, size);
+ if (!r.Expect("OpusTags", 8))
+ return false;
+
+ if (handler->pair == nullptr && handler->tag == nullptr)
+ return true;
+
+ if (!r.SkipString())
+ return false;
+
+ uint32_t n;
+ if (!r.ReadWord(n))
+ return false;
+
+ while (n-- > 0) {
+ char *p = r.ReadString();
+ if (p == nullptr)
+ return false;
+
+ char *eq = strchr(p, '=');
+ if (eq != nullptr && eq > p) {
+ *eq = 0;
+
+ ScanOneOpusTag(p, eq + 1, handler, ctx);
+ }
+
+ free(p);
+ }
+
+ return true;
+}
diff --git a/src/decoder/OpusTags.hxx b/src/decoder/OpusTags.hxx
new file mode 100644
index 000000000..2f3eec844
--- /dev/null
+++ b/src/decoder/OpusTags.hxx
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2003-2012 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.
+ */
+
+#ifndef MPD_OPUS_TAGS_HXX
+#define MPD_OPUS_TAGS_HXX
+
+#include "check.h"
+
+#include <stddef.h>
+
+bool
+ScanOpusTags(const void *data, size_t size,
+ const struct tag_handler *handler, void *ctx);
+
+#endif
diff --git a/src/decoder/XiphTags.c b/src/decoder/XiphTags.c
new file mode 100644
index 000000000..d55787b94
--- /dev/null
+++ b/src/decoder/XiphTags.c
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2003-2012 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 "config.h"
+#include "XiphTags.h"
+
+const struct tag_table xiph_tags[] = {
+ { "tracknumber", TAG_TRACK },
+ { "discnumber", TAG_DISC },
+ { "album artist", TAG_ALBUM_ARTIST },
+ { NULL, TAG_NUM_OF_ITEM_TYPES }
+};
diff --git a/src/decoder/XiphTags.h b/src/decoder/XiphTags.h
new file mode 100644
index 000000000..22a4e2204
--- /dev/null
+++ b/src/decoder/XiphTags.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2003-2012 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.
+ */
+
+#ifndef MPD_XIPH_TAGS_H
+#define MPD_XIPH_TAGS_H
+
+#include "check.h"
+#include "tag_table.h"
+
+extern const struct tag_table xiph_tags[];
+
+#endif
diff --git a/src/decoder/flac_compat.h b/src/decoder/flac_compat.h
deleted file mode 100644
index 9a30acc26..000000000
--- a/src/decoder/flac_compat.h
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright (C) 2003-2011 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.
- */
-
-/*
- * Common data structures and functions used by FLAC and OggFLAC
- */
-
-#ifndef MPD_FLAC_COMPAT_H
-#define MPD_FLAC_COMPAT_H
-
-#include <FLAC/export.h>
-#if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT <= 7
-# include <FLAC/seekable_stream_decoder.h>
-
-/* starting with libFLAC 1.1.3, the SeekableStreamDecoder has been
- merged into the StreamDecoder. The following macros try to emulate
- the new API for libFLAC 1.1.2 by mapping MPD's StreamDecoder calls
- to the old SeekableStreamDecoder API. */
-
-#define FLAC__StreamDecoder FLAC__SeekableStreamDecoder
-#define FLAC__stream_decoder_new FLAC__seekable_stream_decoder_new
-#define FLAC__stream_decoder_get_decode_position FLAC__seekable_stream_decoder_get_decode_position
-#define FLAC__stream_decoder_get_state FLAC__seekable_stream_decoder_get_state
-#define FLAC__stream_decoder_process_single FLAC__seekable_stream_decoder_process_single
-#define FLAC__stream_decoder_process_until_end_of_metadata FLAC__seekable_stream_decoder_process_until_end_of_metadata
-#define FLAC__stream_decoder_seek_absolute FLAC__seekable_stream_decoder_seek_absolute
-#define FLAC__stream_decoder_finish FLAC__seekable_stream_decoder_finish
-#define FLAC__stream_decoder_delete FLAC__seekable_stream_decoder_delete
-
-#define FLAC__STREAM_DECODER_END_OF_STREAM FLAC__SEEKABLE_STREAM_DECODER_END_OF_STREAM
-
-typedef unsigned flac_read_status_size_t;
-
-#define FLAC__StreamDecoderReadStatus FLAC__SeekableStreamDecoderReadStatus
-#define FLAC__STREAM_DECODER_READ_STATUS_CONTINUE FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_OK
-#define FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_OK
-#define FLAC__STREAM_DECODER_READ_STATUS_ABORT FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_ERROR
-
-#define FLAC__StreamDecoderSeekStatus FLAC__SeekableStreamDecoderSeekStatus
-#define FLAC__STREAM_DECODER_SEEK_STATUS_OK FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_OK
-#define FLAC__STREAM_DECODER_SEEK_STATUS_ERROR FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_ERROR
-#define FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_ERROR
-
-#define FLAC__StreamDecoderTellStatus FLAC__SeekableStreamDecoderTellStatus
-#define FLAC__STREAM_DECODER_TELL_STATUS_OK FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_OK
-#define FLAC__STREAM_DECODER_TELL_STATUS_ERROR FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_ERROR
-#define FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_ERROR
-
-#define FLAC__StreamDecoderLengthStatus FLAC__SeekableStreamDecoderLengthStatus
-#define FLAC__STREAM_DECODER_LENGTH_STATUS_OK FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_OK
-#define FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_ERROR
-#define FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_ERROR
-
-typedef enum {
- FLAC__STREAM_DECODER_INIT_STATUS_OK,
- FLAC__STREAM_DECODER_INIT_STATUS_ERROR,
-} FLAC__StreamDecoderInitStatus;
-
-static inline FLAC__StreamDecoderInitStatus
-FLAC__stream_decoder_init_stream(FLAC__SeekableStreamDecoder *decoder,
- FLAC__SeekableStreamDecoderReadCallback read_cb,
- FLAC__SeekableStreamDecoderSeekCallback seek_cb,
- FLAC__SeekableStreamDecoderTellCallback tell_cb,
- FLAC__SeekableStreamDecoderLengthCallback length_cb,
- FLAC__SeekableStreamDecoderEofCallback eof_cb,
- FLAC__SeekableStreamDecoderWriteCallback write_cb,
- FLAC__SeekableStreamDecoderMetadataCallback metadata_cb,
- FLAC__SeekableStreamDecoderErrorCallback error_cb,
- void *data)
-{
- return FLAC__seekable_stream_decoder_set_read_callback(decoder, read_cb) &&
- FLAC__seekable_stream_decoder_set_seek_callback(decoder, seek_cb) &&
- FLAC__seekable_stream_decoder_set_tell_callback(decoder, tell_cb) &&
- FLAC__seekable_stream_decoder_set_length_callback(decoder, length_cb) &&
- FLAC__seekable_stream_decoder_set_eof_callback(decoder, eof_cb) &&
- FLAC__seekable_stream_decoder_set_write_callback(decoder, write_cb) &&
- FLAC__seekable_stream_decoder_set_metadata_callback(decoder, metadata_cb) &&
- FLAC__seekable_stream_decoder_set_metadata_respond(decoder, FLAC__METADATA_TYPE_VORBIS_COMMENT) &&
- FLAC__seekable_stream_decoder_set_error_callback(decoder, error_cb) &&
- FLAC__seekable_stream_decoder_set_client_data(decoder, data) &&
- FLAC__seekable_stream_decoder_init(decoder) == FLAC__SEEKABLE_STREAM_DECODER_OK
- ? FLAC__STREAM_DECODER_INIT_STATUS_OK
- : FLAC__STREAM_DECODER_INIT_STATUS_ERROR;
-}
-
-#else /* FLAC_API_VERSION_CURRENT > 7 */
-
-# include <FLAC/stream_decoder.h>
-
-# define flac_init(a,b,c,d,e,f,g,h,i,j) \
- (FLAC__stream_decoder_init_stream(a,b,c,d,e,f,g,h,i,j) \
- == FLAC__STREAM_DECODER_INIT_STATUS_OK)
-
-typedef size_t flac_read_status_size_t;
-
-#endif /* FLAC_API_VERSION_CURRENT >= 7 */
-
-#endif /* _FLAC_COMMON_H */
diff --git a/src/decoder/mad_decoder_plugin.c b/src/decoder/mad_decoder_plugin.c
index 62c371642..c1a0c4b0f 100644
--- a/src/decoder/mad_decoder_plugin.c
+++ b/src/decoder/mad_decoder_plugin.c
@@ -76,9 +76,9 @@ mad_fixed_to_24_sample(mad_fixed_t sample)
sample = sample + (1L << (MAD_F_FRACBITS - bits));
/* clip */
- if (sample > MAX)
+ if (gcc_unlikely(sample > MAX))
sample = MAX;
- else if (sample < MIN)
+ else if (gcc_unlikely(sample < MIN))
sample = MIN;
/* quantize */
diff --git a/src/decoder/_ogg_common.c b/src/decoder/ogg_codec.c
index 09d2712da..7416f27da 100644
--- a/src/decoder/_ogg_common.c
+++ b/src/decoder/ogg_codec.c
@@ -22,25 +22,27 @@
*/
#include "config.h"
-#include "_ogg_common.h"
+#include "ogg_codec.h"
-ogg_stream_type ogg_stream_type_detect(struct input_stream *inStream)
+enum ogg_codec
+ogg_codec_detect(struct decoder *decoder, struct input_stream *is)
{
/* oggflac detection based on code in ogg123 and this post
* http://lists.xiph.org/pipermail/flac/2004-December/000393.html
* ogg123 trunk still doesn't have this patch as of June 2005 */
unsigned char buf[41];
- size_t r;
-
- r = decoder_read(NULL, inStream, buf, sizeof(buf));
+ size_t r = decoder_read(decoder, is, buf, sizeof(buf));
if (r < sizeof(buf) || memcmp(buf, "OggS", 4) != 0)
- return VORBIS;
+ return OGG_CODEC_UNKNOWN;
if ((memcmp(buf + 29, "FLAC", 4) == 0 &&
memcmp(buf + 37, "fLaC", 4) == 0) ||
memcmp(buf + 28, "FLAC", 4) == 0 ||
memcmp(buf + 28, "fLaC", 4) == 0)
- return FLAC;
+ return OGG_CODEC_FLAC;
+
+ if (memcmp(buf + 28, "Opus", 4) == 0)
+ return OGG_CODEC_OPUS;
- return VORBIS;
+ return OGG_CODEC_VORBIS;
}
diff --git a/src/decoder/_ogg_common.h b/src/decoder/ogg_codec.h
index 85e4ebba6..fd1fecfbb 100644
--- a/src/decoder/_ogg_common.h
+++ b/src/decoder/ogg_codec.h
@@ -21,13 +21,19 @@
* Common functions used for Ogg data streams (Ogg-Vorbis and OggFLAC)
*/
-#ifndef MPD_OGG_COMMON_H
-#define MPD_OGG_COMMON_H
+#ifndef MPD_OGG_CODEC_H
+#define MPD_OGG_CODEC_H
#include "decoder_api.h"
-typedef enum _ogg_stream_type { VORBIS, FLAC } ogg_stream_type;
+enum ogg_codec {
+ OGG_CODEC_UNKNOWN,
+ OGG_CODEC_VORBIS,
+ OGG_CODEC_FLAC,
+ OGG_CODEC_OPUS,
+};
-ogg_stream_type ogg_stream_type_detect(struct input_stream *inStream);
+enum ogg_codec
+ogg_codec_detect(struct decoder *decoder, struct input_stream *is);
#endif /* _OGG_COMMON_H */
diff --git a/src/decoder/sidplay_decoder_plugin.cxx b/src/decoder/sidplay_decoder_plugin.cxx
index 5d162f179..de2e599e9 100644
--- a/src/decoder/sidplay_decoder_plugin.cxx
+++ b/src/decoder/sidplay_decoder_plugin.cxx
@@ -104,7 +104,7 @@ sidplay_init(const struct config_param *param)
return true;
}
-void
+static void
sidplay_finish()
{
g_pattern_spec_free(path_with_subtune);
@@ -136,7 +136,7 @@ get_container_name(const char *path_fs)
* returns tune number from file.sid/tune_xxx.sid style path or 1 if
* no subtune is appended
*/
-static int
+static unsigned
get_song_num(const char *path_fs)
{
if(g_pattern_match(path_with_subtune,
@@ -172,7 +172,7 @@ get_song_length(const char *path_fs)
char md5sum[SIDTUNE_MD5_LENGTH+1];
tune.createMD5(md5sum);
- int song_num=get_song_num(path_fs);
+ const unsigned song_num = get_song_num(path_fs);
gsize num_items;
gchar **values=g_key_file_get_string_list(songlength_database,
@@ -330,7 +330,7 @@ sidplay_file_decode(struct decoder *decoder, const char *path_fs)
decoder_command_finished(decoder);
}
- if (song_len > 0 && player.time() >= song_len)
+ if (song_len > 0 && player.time() >= (unsigned)song_len)
break;
} while (cmd != DECODE_COMMAND_STOP);
diff --git a/src/decoder/vorbis_comments.c b/src/decoder/vorbis_comments.c
index 6c2d57b72..84f7c5014 100644
--- a/src/decoder/vorbis_comments.c
+++ b/src/decoder/vorbis_comments.c
@@ -19,6 +19,7 @@
#include "config.h"
#include "vorbis_comments.h"
+#include "XiphTags.h"
#include "tag.h"
#include "tag_table.h"
#include "tag_handler.h"
@@ -95,13 +96,6 @@ vorbis_copy_comment(const char *comment,
return false;
}
-static const struct tag_table vorbis_tags[] = {
- { "tracknumber", TAG_TRACK },
- { "discnumber", TAG_DISC },
- { "album artist", TAG_ALBUM_ARTIST },
- { NULL, TAG_NUM_OF_ITEM_TYPES }
-};
-
static void
vorbis_scan_comment(const char *comment,
const struct tag_handler *handler, void *handler_ctx)
@@ -119,7 +113,7 @@ vorbis_scan_comment(const char *comment,
g_free(name);
}
- for (const struct tag_table *i = vorbis_tags; i->name != NULL; ++i)
+ for (const struct tag_table *i = xiph_tags; i->name != NULL; ++i)
if (vorbis_copy_comment(comment, i->name, i->type,
handler, handler_ctx))
return;
diff --git a/src/decoder/vorbis_decoder_plugin.c b/src/decoder/vorbis_decoder_plugin.c
index 15cdc0ca9..f180a6559 100644
--- a/src/decoder/vorbis_decoder_plugin.c
+++ b/src/decoder/vorbis_decoder_plugin.c
@@ -19,7 +19,7 @@
#include "config.h"
#include "vorbis_comments.h"
-#include "_ogg_common.h"
+#include "ogg_codec.h"
#include "audio_check.h"
#include "uri.h"
#include "tag_handler.h"
@@ -48,12 +48,11 @@
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "vorbis"
-#define OGG_CHUNK_SIZE 4096
#if G_BYTE_ORDER == G_BIG_ENDIAN
-#define OGG_DECODE_USE_BIGENDIAN 1
+#define VORBIS_BIG_ENDIAN true
#else
-#define OGG_DECODE_USE_BIGENDIAN 0
+#define VORBIS_BIG_ENDIAN false
#endif
struct vorbis_input_stream {
@@ -66,9 +65,8 @@ struct vorbis_input_stream {
static size_t ogg_read_cb(void *ptr, size_t size, size_t nmemb, void *data)
{
struct vorbis_input_stream *vis = data;
- size_t ret;
-
- ret = decoder_read(vis->decoder, vis->input_stream, ptr, size * nmemb);
+ size_t ret = decoder_read(vis->decoder, vis->input_stream,
+ ptr, size * nmemb);
errno = 0;
@@ -155,9 +153,7 @@ static void
vorbis_send_comments(struct decoder *decoder, struct input_stream *is,
char **comments)
{
- struct tag *tag;
-
- tag = vorbis_comments_to_tag(comments);
+ struct tag *tag = vorbis_comments_to_tag(comments);
if (!tag)
return;
@@ -165,55 +161,79 @@ vorbis_send_comments(struct decoder *decoder, struct input_stream *is,
tag_free(tag);
}
+#ifndef HAVE_TREMOR
+static void
+vorbis_interleave(float *dest, const float *const*src,
+ unsigned nframes, unsigned channels)
+{
+ for (const float *const*src_end = src + channels;
+ src != src_end; ++src, ++dest) {
+ float *d = dest;
+ for (const float *s = *src, *s_end = s + nframes;
+ s != s_end; ++s, d += channels)
+ *d = *s;
+ }
+}
+#endif
+
/* public */
static void
vorbis_stream_decode(struct decoder *decoder,
struct input_stream *input_stream)
{
GError *error = NULL;
- OggVorbis_File vf;
- struct vorbis_input_stream vis;
- struct audio_format audio_format;
- float total_time;
- int current_section;
- int prev_section = -1;
- long ret;
- char chunk[OGG_CHUNK_SIZE];
- long bitRate = 0;
- long test;
- const vorbis_info *vi;
- enum decoder_command cmd = DECODE_COMMAND_NONE;
-
- if (ogg_stream_type_detect(input_stream) != VORBIS)
+
+ if (ogg_codec_detect(decoder, input_stream) != OGG_CODEC_VORBIS)
return;
- /* rewind the stream, because ogg_stream_type_detect() has
+ /* rewind the stream, because ogg_codec_detect() has
moved it */
input_stream_lock_seek(input_stream, 0, SEEK_SET, NULL);
+ struct vorbis_input_stream vis;
+ OggVorbis_File vf;
if (!vorbis_is_open(&vis, &vf, decoder, input_stream))
return;
- vi = ov_info(&vf, -1);
+ const vorbis_info *vi = ov_info(&vf, -1);
if (vi == NULL) {
g_warning("ov_info() has failed");
return;
}
+ struct audio_format audio_format;
if (!audio_format_init_checked(&audio_format, vi->rate,
+#ifdef HAVE_TREMOR
SAMPLE_FORMAT_S16,
+#else
+ SAMPLE_FORMAT_FLOAT,
+#endif
vi->channels, &error)) {
g_warning("%s", error->message);
g_error_free(error);
return;
}
- total_time = ov_time_total(&vf, -1);
+ float total_time = ov_time_total(&vf, -1);
if (total_time < 0)
total_time = 0;
decoder_initialized(decoder, &audio_format, vis.seekable, total_time);
+ enum decoder_command cmd = decoder_get_command(decoder);
+
+#ifdef HAVE_TREMOR
+ char buffer[4096];
+#else
+ float buffer[2048];
+ const int frames_per_buffer =
+ G_N_ELEMENTS(buffer) / audio_format.channels;
+ const unsigned frame_size = sizeof(buffer[0]) * audio_format.channels;
+#endif
+
+ int prev_section = -1;
+ unsigned kbit_rate = 0;
+
do {
if (cmd == DECODE_COMMAND_SEEK) {
double seek_where = decoder_seek_where(decoder);
@@ -223,17 +243,33 @@ vorbis_stream_decode(struct decoder *decoder,
decoder_seek_error(decoder);
}
- ret = ov_read(&vf, chunk, sizeof(chunk),
- OGG_DECODE_USE_BIGENDIAN, 2, 1, &current_section);
- if (ret == OV_HOLE) /* bad packet */
- ret = 0;
- else if (ret <= 0)
+ int current_section;
+
+#ifdef HAVE_TREMOR
+ long nbytes = ov_read(&vf, buffer, sizeof(buffer),
+ VORBIS_BIG_ENDIAN, 2, 1,
+ &current_section);
+#else
+ float **per_channel;
+ long nframes = ov_read_float(&vf, &per_channel,
+ frames_per_buffer,
+ &current_section);
+ long nbytes = nframes;
+ if (nframes > 0) {
+ vorbis_interleave(buffer,
+ (const float*const*)per_channel,
+ nframes, audio_format.channels);
+ nbytes *= frame_size;
+ }
+#endif
+
+ if (nbytes == OV_HOLE) /* bad packet */
+ nbytes = 0;
+ else if (nbytes <= 0)
/* break on EOF or other error */
break;
if (current_section != prev_section) {
- char **comments;
-
vi = ov_info(&vf, -1);
if (vi == NULL) {
g_warning("ov_info() has failed");
@@ -248,7 +284,7 @@ vorbis_stream_decode(struct decoder *decoder,
break;
}
- comments = ov_comment(&vf, -1)->user_comments;
+ char **comments = ov_comment(&vf, -1)->user_comments;
vorbis_send_comments(decoder, input_stream, comments);
struct replay_gain_info rgi;
@@ -258,12 +294,13 @@ vorbis_stream_decode(struct decoder *decoder,
prev_section = current_section;
}
- if ((test = ov_bitrate_instant(&vf)) > 0)
- bitRate = test / 1000;
+ long test = ov_bitrate_instant(&vf);
+ if (test > 0)
+ kbit_rate = test / 1000;
cmd = decoder_data(decoder, input_stream,
- chunk, ret,
- bitRate);
+ buffer, nbytes,
+ kbit_rate);
} while (cmd != DECODE_COMMAND_STOP);
ov_clear(&vf);