aboutsummaryrefslogtreecommitdiffstats
path: root/src/decoder
diff options
context:
space:
mode:
Diffstat (limited to 'src/decoder')
-rw-r--r--src/decoder/AdPlugDecoderPlugin.cxx142
-rw-r--r--src/decoder/AdPlugDecoderPlugin.h25
-rw-r--r--src/decoder/AudiofileDecoderPlugin.cxx (renamed from src/decoder/audiofile_decoder_plugin.c)127
-rw-r--r--src/decoder/AudiofileDecoderPlugin.hxx25
-rw-r--r--src/decoder/DsdLib.cxx (renamed from src/decoder/dsdlib.c)91
-rw-r--r--src/decoder/DsdLib.hxx (renamed from src/decoder/dsdlib.h)15
-rw-r--r--src/decoder/DsdiffDecoderPlugin.cxx (renamed from src/decoder/dsdiff_decoder_plugin.c)310
-rw-r--r--src/decoder/DsdiffDecoderPlugin.hxx (renamed from src/decoder/dsdiff_decoder_plugin.h)2
-rw-r--r--src/decoder/DsfDecoderPlugin.cxx (renamed from src/decoder/dsf_decoder_plugin.c)130
-rw-r--r--src/decoder/DsfDecoderPlugin.hxx (renamed from src/decoder/dsf_decoder_plugin.h)2
-rw-r--r--src/decoder/FaadDecoderPlugin.cxx (renamed from src/decoder/faad_decoder_plugin.c)248
-rw-r--r--src/decoder/FaadDecoderPlugin.hxx25
-rw-r--r--src/decoder/FfmpegDecoderPlugin.cxx (renamed from src/decoder/ffmpeg_decoder_plugin.c)461
-rw-r--r--src/decoder/FfmpegDecoderPlugin.hxx25
-rw-r--r--src/decoder/FfmpegMetaData.cxx (renamed from src/decoder/ffmpeg_metadata.c)25
-rw-r--r--src/decoder/FfmpegMetaData.hxx (renamed from src/decoder/ffmpeg_metadata.h)17
-rw-r--r--src/decoder/FlacCommon.cxx (renamed from src/decoder/_flac_common.c)117
-rw-r--r--src/decoder/FlacCommon.hxx (renamed from src/decoder/_flac_common.h)37
-rw-r--r--src/decoder/FlacDecoderPlugin.cxx (renamed from src/decoder/flac_decoder_plugin.c)331
-rw-r--r--src/decoder/FlacDecoderPlugin.h26
-rw-r--r--src/decoder/FlacDomain.cxx24
-rw-r--r--src/decoder/FlacDomain.hxx27
-rw-r--r--src/decoder/FlacIOHandle.cxx114
-rw-r--r--src/decoder/FlacIOHandle.hxx45
-rw-r--r--src/decoder/FlacInput.cxx154
-rw-r--r--src/decoder/FlacInput.hxx72
-rw-r--r--src/decoder/FlacMetadata.cxx (renamed from src/decoder/flac_metadata.c)143
-rw-r--r--src/decoder/FlacMetadata.hxx140
-rw-r--r--src/decoder/FlacPcm.cxx (renamed from src/decoder/flac_pcm.c)22
-rw-r--r--src/decoder/FlacPcm.hxx (renamed from src/decoder/flac_pcm.h)10
-rw-r--r--src/decoder/FluidsynthDecoderPlugin.cxx (renamed from src/decoder/fluidsynth_decoder_plugin.c)96
-rw-r--r--src/decoder/FluidsynthDecoderPlugin.hxx25
-rw-r--r--src/decoder/GmeDecoderPlugin.cxx290
-rw-r--r--src/decoder/GmeDecoderPlugin.hxx25
-rw-r--r--src/decoder/MadDecoderPlugin.cxx (renamed from src/decoder/mad_decoder_plugin.c)719
-rw-r--r--src/decoder/MadDecoderPlugin.hxx25
-rw-r--r--src/decoder/MikmodDecoderPlugin.cxx (renamed from src/decoder/mikmod_decoder_plugin.c)84
-rw-r--r--src/decoder/MikmodDecoderPlugin.hxx25
-rw-r--r--src/decoder/ModplugDecoderPlugin.cxx (renamed from src/decoder/modplug_decoder_plugin.c)110
-rw-r--r--src/decoder/ModplugDecoderPlugin.hxx25
-rw-r--r--src/decoder/MpcdecDecoderPlugin.cxx (renamed from src/decoder/mpcdec_decoder_plugin.c)214
-rw-r--r--src/decoder/MpcdecDecoderPlugin.hxx25
-rw-r--r--src/decoder/Mpg123DecoderPlugin.cxx (renamed from src/decoder/mpg123_decoder_plugin.c)103
-rw-r--r--src/decoder/Mpg123DecoderPlugin.hxx25
-rw-r--r--src/decoder/OggCodec.cxx (renamed from src/decoder/_ogg_common.c)22
-rw-r--r--src/decoder/OggCodec.hxx (renamed from src/decoder/_ogg_common.h)18
-rw-r--r--src/decoder/OggFind.cxx37
-rw-r--r--src/decoder/OggFind.hxx38
-rw-r--r--src/decoder/OggSyncState.hxx78
-rw-r--r--src/decoder/OggUtil.cxx118
-rw-r--r--src/decoder/OggUtil.hxx87
-rw-r--r--src/decoder/OpusDecoderPlugin.cxx403
-rw-r--r--src/decoder/OpusDecoderPlugin.h25
-rw-r--r--src/decoder/OpusDomain.cxx24
-rw-r--r--src/decoder/OpusDomain.hxx27
-rw-r--r--src/decoder/OpusHead.cxx44
-rw-r--r--src/decoder/OpusHead.hxx30
-rw-r--r--src/decoder/OpusReader.hxx100
-rw-r--r--src/decoder/OpusTags.cxx77
-rw-r--r--src/decoder/OpusTags.hxx31
-rw-r--r--src/decoder/PcmDecoderPlugin.cxx (renamed from src/decoder/pcm_decoder_plugin.c)74
-rw-r--r--src/decoder/PcmDecoderPlugin.hxx (renamed from src/decoder/pcm_decoder_plugin.h)6
-rw-r--r--src/decoder/SndfileDecoderPlugin.cxx (renamed from src/decoder/sndfile_decoder_plugin.c)125
-rw-r--r--src/decoder/SndfileDecoderPlugin.hxx25
-rw-r--r--src/decoder/VorbisComments.cxx (renamed from src/decoder/vorbis_comments.c)42
-rw-r--r--src/decoder/VorbisComments.hxx (renamed from src/decoder/vorbis_comments.h)11
-rw-r--r--src/decoder/VorbisDecoderPlugin.cxx (renamed from src/decoder/vorbis_decoder_plugin.c)208
-rw-r--r--src/decoder/VorbisDecoderPlugin.h25
-rw-r--r--src/decoder/VorbisDomain.cxx24
-rw-r--r--src/decoder/VorbisDomain.hxx27
-rw-r--r--src/decoder/WavpackDecoderPlugin.cxx (renamed from src/decoder/wavpack_decoder_plugin.c)146
-rw-r--r--src/decoder/WavpackDecoderPlugin.hxx25
-rw-r--r--src/decoder/WildmidiDecoderPlugin.cxx (renamed from src/decoder/wildmidi_decoder_plugin.c)99
-rw-r--r--src/decoder/WildmidiDecoderPlugin.hxx25
-rw-r--r--src/decoder/XiphTags.cxx28
-rw-r--r--src/decoder/XiphTags.hxx28
-rw-r--r--src/decoder/flac_compat.h114
-rw-r--r--src/decoder/flac_metadata.h64
-rw-r--r--src/decoder/gme_decoder_plugin.c257
-rw-r--r--src/decoder/mp4ff_decoder_plugin.c448
-rw-r--r--src/decoder/sidplay_decoder_plugin.cxx78
81 files changed, 4702 insertions, 3059 deletions
diff --git a/src/decoder/AdPlugDecoderPlugin.cxx b/src/decoder/AdPlugDecoderPlugin.cxx
new file mode 100644
index 000000000..3ec48cb23
--- /dev/null
+++ b/src/decoder/AdPlugDecoderPlugin.cxx
@@ -0,0 +1,142 @@
+/*
+ * 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/TagHandler.hxx"
+#include "DecoderAPI.hxx"
+#include "CheckAudioFormat.hxx"
+#include "util/Error.hxx"
+#include "Log.hxx"
+
+#include <adplug/adplug.h>
+#include <adplug/emuopl.h>
+
+#include <glib.h>
+
+#include <assert.h>
+
+static unsigned sample_rate;
+
+static bool
+adplug_init(const config_param &param)
+{
+ Error error;
+
+ sample_rate = param.GetBlockValue("sample_rate", 48000u);
+ if (!audio_check_sample_rate(sample_rate, error)) {
+ LogError(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;
+
+ const AudioFormat audio_format(sample_rate, SampleFormat::S16, 2);
+ assert(audio_format.IsValid());
+
+ decoder_initialized(decoder, audio_format, false,
+ player->songlength() / 1000.);
+
+ int16_t buffer[2048];
+ const unsigned frames_per_buffer = G_N_ELEMENTS(buffer) / 2;
+ DecoderCommand cmd;
+
+ do {
+ if (!player->update())
+ break;
+
+ opl.update(buffer, frames_per_buffer);
+ cmd = decoder_data(decoder, NULL,
+ buffer, sizeof(buffer),
+ 0);
+ } while (cmd == DecoderCommand::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/audiofile_decoder_plugin.c b/src/decoder/AudiofileDecoderPlugin.cxx
index b344795e7..c7b1b1016 100644
--- a/src/decoder/audiofile_decoder_plugin.c
+++ b/src/decoder/AudiofileDecoderPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,26 +18,29 @@
*/
#include "config.h"
-#include "decoder_api.h"
-#include "audio_check.h"
-#include "tag_handler.h"
+#include "AudiofileDecoderPlugin.hxx"
+#include "DecoderAPI.hxx"
+#include "InputStream.hxx"
+#include "CheckAudioFormat.hxx"
+#include "tag/TagHandler.hxx"
+#include "util/Error.hxx"
+#include "util/Domain.hxx"
+#include "Log.hxx"
#include <audiofile.h>
#include <af_vfs.h>
-#include <assert.h>
-#include <glib.h>
-#include <stdio.h>
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "audiofile"
+#include <assert.h>
/* pick 1020 since its devisible for 8,16,24, and 32-bit audio */
#define CHUNK_SIZE 1020
+static constexpr Domain audiofile_domain("audiofile");
+
static int audiofile_get_duration(const char *file)
{
int total_time;
- AFfilehandle af_fp = afOpenFile(file, "r", NULL);
+ AFfilehandle af_fp = afOpenFile(file, "r", nullptr);
if (af_fp == AF_NULL_FILEHANDLE) {
return -1;
}
@@ -52,13 +55,11 @@ static ssize_t
audiofile_file_read(AFvirtualfile *vfile, void *data, size_t length)
{
struct input_stream *is = (struct input_stream *) vfile->closure;
- GError *error = NULL;
- size_t nbytes;
- nbytes = input_stream_lock_read(is, data, length, &error);
- if (nbytes == 0 && error != NULL) {
- g_warning("%s", error->message);
- g_error_free(error);
+ Error error;
+ size_t nbytes = is->LockRead(data, length, error);
+ if (nbytes == 0 && error.IsDefined()) {
+ LogError(error);
return -1;
}
@@ -69,22 +70,22 @@ static AFfileoffset
audiofile_file_length(AFvirtualfile *vfile)
{
struct input_stream *is = (struct input_stream *) vfile->closure;
- return is->size;
+ return is->GetSize();
}
static AFfileoffset
audiofile_file_tell(AFvirtualfile *vfile)
{
struct input_stream *is = (struct input_stream *) vfile->closure;
- return is->offset;
+ return is->GetOffset();
}
static void
audiofile_file_destroy(AFvirtualfile *vfile)
{
- assert(vfile->closure != NULL);
+ assert(vfile->closure != nullptr);
- vfile->closure = NULL;
+ vfile->closure = nullptr;
}
static AFfileoffset
@@ -92,8 +93,10 @@ audiofile_file_seek(AFvirtualfile *vfile, AFfileoffset offset, int is_relative)
{
struct input_stream *is = (struct input_stream *) vfile->closure;
int whence = (is_relative ? SEEK_CUR : SEEK_SET);
- if (input_stream_lock_seek(is, offset, whence, NULL)) {
- return is->offset;
+
+ Error error;
+ if (is->LockSeek(offset, whence, error)) {
+ return is->GetOffset();
} else {
return -1;
}
@@ -102,9 +105,9 @@ audiofile_file_seek(AFvirtualfile *vfile, AFfileoffset offset, int is_relative)
static AFvirtualfile *
setup_virtual_fops(struct input_stream *stream)
{
- AFvirtualfile *vf = g_malloc(sizeof(AFvirtualfile));
+ AFvirtualfile *vf = new AFvirtualfile();
vf->closure = stream;
- vf->write = NULL;
+ vf->write = nullptr;
vf->read = audiofile_file_read;
vf->length = audiofile_file_length;
vf->destroy = audiofile_file_destroy;
@@ -113,35 +116,36 @@ setup_virtual_fops(struct input_stream *stream)
return vf;
}
-static enum sample_format
+static SampleFormat
audiofile_bits_to_sample_format(int bits)
{
switch (bits) {
case 8:
- return SAMPLE_FORMAT_S8;
+ return SampleFormat::S8;
case 16:
- return SAMPLE_FORMAT_S16;
+ return SampleFormat::S16;
case 24:
- return SAMPLE_FORMAT_S24_P32;
+ return SampleFormat::S24_P32;
case 32:
- return SAMPLE_FORMAT_S32;
+ return SampleFormat::S32;
}
- return SAMPLE_FORMAT_UNDEFINED;
+ return SampleFormat::UNDEFINED;
}
-static enum sample_format
+static SampleFormat
audiofile_setup_sample_format(AFfilehandle af_fp)
{
int fs, bits;
afGetSampleFormat(af_fp, AF_DEFAULT_TRACK, &fs, &bits);
if (!audio_valid_sample_format(audiofile_bits_to_sample_format(bits))) {
- g_debug("input file has %d bit samples, converting to 16",
- bits);
+ FormatDebug(audiofile_domain,
+ "input file has %d bit samples, converting to 16",
+ bits);
bits = 16;
}
@@ -155,37 +159,35 @@ audiofile_setup_sample_format(AFfilehandle af_fp)
static void
audiofile_stream_decode(struct decoder *decoder, struct input_stream *is)
{
- GError *error = NULL;
AFvirtualfile *vf;
int fs, frame_count;
AFfilehandle af_fp;
- struct audio_format audio_format;
+ AudioFormat audio_format;
float total_time;
uint16_t bit_rate;
int ret;
char chunk[CHUNK_SIZE];
- enum decoder_command cmd;
- if (!is->seekable) {
- g_warning("not seekable");
+ if (!is->IsSeekable()) {
+ LogWarning(audiofile_domain, "not seekable");
return;
}
vf = setup_virtual_fops(is);
- af_fp = afOpenVirtualFile(vf, "r", NULL);
+ af_fp = afOpenVirtualFile(vf, "r", nullptr);
if (af_fp == AF_NULL_FILEHANDLE) {
- g_warning("failed to input stream\n");
+ LogWarning(audiofile_domain, "failed to input stream");
return;
}
- if (!audio_format_init_checked(&audio_format,
+ Error error;
+ if (!audio_format_init_checked(audio_format,
afGetRate(af_fp, AF_DEFAULT_TRACK),
audiofile_setup_sample_format(af_fp),
afGetVirtualChannels(af_fp, AF_DEFAULT_TRACK),
- &error)) {
- g_warning("%s", error->message);
- g_error_free(error);
+ error)) {
+ LogError(error);
afCloseFile(af_fp);
return;
}
@@ -194,31 +196,32 @@ audiofile_stream_decode(struct decoder *decoder, struct input_stream *is)
total_time = ((float)frame_count / (float)audio_format.sample_rate);
- bit_rate = (uint16_t)(is->size * 8.0 / total_time / 1000.0 + 0.5);
+ bit_rate = (uint16_t)(is->GetSize() * 8.0 / total_time / 1000.0 + 0.5);
fs = (int)afGetVirtualFrameSize(af_fp, AF_DEFAULT_TRACK, 1);
- decoder_initialized(decoder, &audio_format, true, total_time);
+ decoder_initialized(decoder, audio_format, true, total_time);
+ DecoderCommand cmd;
do {
ret = afReadFrames(af_fp, AF_DEFAULT_TRACK, chunk,
CHUNK_SIZE / fs);
if (ret <= 0)
break;
- cmd = decoder_data(decoder, NULL,
+ cmd = decoder_data(decoder, nullptr,
chunk, ret * fs,
bit_rate);
- if (cmd == DECODE_COMMAND_SEEK) {
+ if (cmd == DecoderCommand::SEEK) {
AFframecount frame = decoder_seek_where(decoder) *
audio_format.sample_rate;
afSeekFrame(af_fp, AF_DEFAULT_TRACK, frame);
decoder_command_finished(decoder);
- cmd = DECODE_COMMAND_NONE;
+ cmd = DecoderCommand::NONE;
}
- } while (cmd == DECODE_COMMAND_NONE);
+ } while (cmd == DecoderCommand::NONE);
afCloseFile(af_fp);
}
@@ -230,8 +233,9 @@ audiofile_scan_file(const char *file,
int total_time = audiofile_get_duration(file);
if (total_time < 0) {
- g_debug("Failed to get total song time from: %s\n",
- file);
+ FormatWarning(audiofile_domain,
+ "Failed to get total song time from: %s",
+ file);
return false;
}
@@ -240,19 +244,24 @@ audiofile_scan_file(const char *file,
}
static const char *const audiofile_suffixes[] = {
- "wav", "au", "aiff", "aif", NULL
+ "wav", "au", "aiff", "aif", nullptr
};
static const char *const audiofile_mime_types[] = {
"audio/x-wav",
"audio/x-aiff",
- NULL
+ nullptr
};
const struct decoder_plugin audiofile_decoder_plugin = {
- .name = "audiofile",
- .stream_decode = audiofile_stream_decode,
- .scan_file = audiofile_scan_file,
- .suffixes = audiofile_suffixes,
- .mime_types = audiofile_mime_types,
+ "audiofile",
+ nullptr,
+ nullptr,
+ audiofile_stream_decode,
+ nullptr,
+ audiofile_scan_file,
+ nullptr,
+ nullptr,
+ audiofile_suffixes,
+ audiofile_mime_types,
};
diff --git a/src/decoder/AudiofileDecoderPlugin.hxx b/src/decoder/AudiofileDecoderPlugin.hxx
new file mode 100644
index 000000000..59c09c006
--- /dev/null
+++ b/src/decoder/AudiofileDecoderPlugin.hxx
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2003-2013 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_AUDIOFILE_HXX
+#define MPD_DECODER_AUDIOFILE_HXX
+
+extern const struct decoder_plugin audiofile_decoder_plugin;
+
+#endif
diff --git a/src/decoder/dsdlib.c b/src/decoder/DsdLib.cxx
index 3df9497c4..7135c9903 100644
--- a/src/decoder/dsdlib.c
+++ b/src/decoder/DsdLib.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -24,20 +24,27 @@
*/
#include "config.h"
-#include "dsf_decoder_plugin.h"
-#include "decoder_api.h"
+#include "DsdLib.hxx"
+#include "DecoderAPI.hxx"
+#include "InputStream.hxx"
#include "util/bit_reverse.h"
-#include "dsdlib.h"
-#include "dsdiff_decoder_plugin.h"
+#include "tag/TagHandler.hxx"
+#include "tag/TagId3.hxx"
+#include "util/Error.hxx"
#include <unistd.h>
+#include <string.h>
#include <stdio.h> /* for SEEK_SET, SEEK_CUR */
+#ifdef HAVE_ID3TAG
+#include <id3tag.h>
+#endif
+
bool
dsdlib_id_equals(const struct dsdlib_id *id, const char *s)
{
- assert(id != NULL);
- assert(s != NULL);
+ assert(id != nullptr);
+ assert(s != nullptr);
assert(strlen(s) == sizeof(id->value));
return memcmp(id->value, s, sizeof(id->value)) == 0;
@@ -58,24 +65,24 @@ bool
dsdlib_skip_to(struct decoder *decoder, struct input_stream *is,
goffset offset)
{
- if (is->seekable)
- return input_stream_seek(is, offset, SEEK_SET, NULL);
+ if (is->IsSeekable())
+ return is->Seek(offset, SEEK_SET, IgnoreError());
- if (is->offset > offset)
+ if (is->GetOffset() > offset)
return false;
char buffer[8192];
- while (is->offset < offset) {
+ while (is->GetOffset() < offset) {
size_t length = sizeof(buffer);
- if (offset - is->offset < (goffset)length)
- length = offset - is->offset;
+ if (offset - is->GetOffset() < (goffset)length)
+ length = offset - is->GetOffset();
size_t nbytes = decoder_read(decoder, is, buffer, length);
if (nbytes == 0)
return false;
}
- assert(is->offset == offset);
+ assert(is->GetOffset() == offset);
return true;
}
@@ -91,8 +98,8 @@ dsdlib_skip(struct decoder *decoder, struct input_stream *is,
if (delta == 0)
return true;
- if (is->seekable)
- return input_stream_seek(is, delta, SEEK_CUR, NULL);
+ if (is->IsSeekable())
+ return is->Seek(delta, SEEK_CUR, IgnoreError());
char buffer[8192];
while (delta > 0) {
@@ -110,3 +117,55 @@ dsdlib_skip(struct decoder *decoder, struct input_stream *is,
return true;
}
+/**
+ * Add tags from ID3 tag. All tags commonly found in the ID3 tags of
+ * DSF and DSDIFF files are imported
+ */
+
+#ifdef HAVE_ID3TAG
+void
+dsdlib_tag_id3(struct input_stream *is,
+ const struct tag_handler *handler,
+ void *handler_ctx, goffset tagoffset)
+{
+ assert(tagoffset >= 0);
+
+ if (tagoffset == 0)
+ return;
+
+ if (!dsdlib_skip_to(nullptr, is, tagoffset))
+ return;
+
+ struct id3_tag *id3_tag = nullptr;
+ id3_length_t count;
+
+ /* Prevent broken files causing problems */
+ const goffset size = is->GetSize();
+ const goffset offset = is->GetOffset();
+ if (offset >= size)
+ return;
+
+ count = size - offset;
+
+ /* Check and limit id3 tag size to prevent a stack overflow */
+ if (count == 0 || count > 4096)
+ return;
+
+ id3_byte_t dsdid3[count];
+ id3_byte_t *dsdid3data;
+ dsdid3data = dsdid3;
+
+ if (!dsdlib_read(nullptr, is, dsdid3data, count))
+ return;
+
+ id3_tag = id3_tag_parse(dsdid3data, count);
+ if (id3_tag == nullptr)
+ return;
+
+ scan_id3_tag(id3_tag, handler, handler_ctx);
+
+ id3_tag_delete(id3_tag);
+
+ return;
+}
+#endif
diff --git a/src/decoder/dsdlib.h b/src/decoder/DsdLib.hxx
index d9675f5fe..2a8e15190 100644
--- a/src/decoder/dsdlib.h
+++ b/src/decoder/DsdLib.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,12 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_DECODER_DSDLIB_H
-#define MPD_DECODER_DSDLIB_H
+#ifndef MPD_DECODER_DSDLIB_HXX
+#define MPD_DECODER_DSDLIB_HXX
+
+#include <stdlib.h>
+
+#include <glib.h>
struct dsdlib_id {
char value[4];
@@ -39,4 +43,9 @@ bool
dsdlib_skip(struct decoder *decoder, struct input_stream *is,
goffset delta);
+void
+dsdlib_tag_id3(struct input_stream *is,
+ const struct tag_handler *handler,
+ void *handler_ctx, goffset tagoffset);
+
#endif
diff --git a/src/decoder/dsdiff_decoder_plugin.c b/src/decoder/DsdiffDecoderPlugin.cxx
index 84471fb3a..43002768a 100644
--- a/src/decoder/dsdiff_decoder_plugin.c
+++ b/src/decoder/DsdiffDecoderPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -27,58 +27,68 @@
*/
#include "config.h"
-#include "dsdiff_decoder_plugin.h"
-#include "decoder_api.h"
-#include "audio_check.h"
+#include "DsdiffDecoderPlugin.hxx"
+#include "DecoderAPI.hxx"
+#include "InputStream.hxx"
+#include "CheckAudioFormat.hxx"
#include "util/bit_reverse.h"
-#include "tag_handler.h"
-#include "dsdlib.h"
-#include "tag_handler.h"
+#include "util/Error.hxx"
+#include "tag/TagHandler.hxx"
+#include "DsdLib.hxx"
+#include "Log.hxx"
#include <unistd.h>
#include <stdio.h> /* for SEEK_SET, SEEK_CUR */
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "dsdiff"
-
-struct dsdiff_header {
+struct DsdiffHeader {
struct dsdlib_id id;
uint32_t size_high, size_low;
struct dsdlib_id format;
};
-struct dsdiff_chunk_header {
+struct DsdiffChunkHeader {
struct dsdlib_id id;
uint32_t size_high, size_low;
+
+ /**
+ * Read the "size" attribute from the specified header, converting it
+ * to the host byte order if needed.
+ */
+ gcc_const
+ uint64_t GetSize() const {
+ return (((uint64_t)GUINT32_FROM_BE(size_high)) << 32) |
+ ((uint64_t)GUINT32_FROM_BE(size_low));
+ }
};
-struct dsdiff_metadata {
+/** struct for DSDIFF native Artist and Title tags */
+struct dsdiff_native_tag {
+ uint32_t size;
+};
+
+struct DsdiffMetaData {
unsigned sample_rate, channels;
bool bitreverse;
uint64_t chunk_size;
+#ifdef HAVE_ID3TAG
+ goffset id3_offset;
+ uint64_t id3_size;
+#endif
+ /** offset for artist tag */
+ goffset diar_offset;
+ /** offset for title tag */
+ goffset diti_offset;
};
static bool lsbitfirst;
static bool
-dsdiff_init(const struct config_param *param)
+dsdiff_init(const config_param &param)
{
- lsbitfirst = config_get_block_bool(param, "lsbitfirst", false);
+ lsbitfirst = param.GetBlockValue("lsbitfirst", false);
return true;
}
-/**
- * Read the "size" attribute from the specified header, converting it
- * to the host byte order if needed.
- */
-G_GNUC_CONST
-static uint64_t
-dsdiff_chunk_size(const struct dsdiff_chunk_header *header)
-{
- return (((uint64_t)GUINT32_FROM_BE(header->size_high)) << 32) |
- ((uint64_t)GUINT32_FROM_BE(header->size_low));
-}
-
static bool
dsdiff_read_id(struct decoder *decoder, struct input_stream *is,
struct dsdlib_id *id)
@@ -88,17 +98,17 @@ dsdiff_read_id(struct decoder *decoder, struct input_stream *is,
static bool
dsdiff_read_chunk_header(struct decoder *decoder, struct input_stream *is,
- struct dsdiff_chunk_header *header)
+ DsdiffChunkHeader *header)
{
return dsdlib_read(decoder, is, header, sizeof(*header));
}
static bool
dsdiff_read_payload(struct decoder *decoder, struct input_stream *is,
- const struct dsdiff_chunk_header *header,
+ const DsdiffChunkHeader *header,
void *data, size_t length)
{
- uint64_t size = dsdiff_chunk_size(header);
+ uint64_t size = header->GetSize();
if (size != (uint64_t)length)
return false;
@@ -111,16 +121,16 @@ dsdiff_read_payload(struct decoder *decoder, struct input_stream *is,
*/
static bool
dsdiff_read_prop_snd(struct decoder *decoder, struct input_stream *is,
- struct dsdiff_metadata *metadata,
+ DsdiffMetaData *metadata,
goffset end_offset)
{
- struct dsdiff_chunk_header header;
- while ((goffset)(is->offset + sizeof(header)) <= end_offset) {
+ DsdiffChunkHeader header;
+ while ((goffset)(is->GetOffset() + sizeof(header)) <= end_offset) {
if (!dsdiff_read_chunk_header(decoder, is, &header))
return false;
- goffset chunk_end_offset =
- is->offset + dsdiff_chunk_size(&header);
+ goffset chunk_end_offset = is->GetOffset()
+ + header.GetSize();
if (chunk_end_offset > end_offset)
return false;
@@ -134,7 +144,7 @@ dsdiff_read_prop_snd(struct decoder *decoder, struct input_stream *is,
metadata->sample_rate = GUINT32_FROM_BE(sample_rate);
} else if (dsdlib_id_equals(&header.id, "CHNL")) {
uint16_t channels;
- if (dsdiff_chunk_size(&header) < sizeof(channels) ||
+ if (header.GetSize() < sizeof(channels) ||
!dsdlib_read(decoder, is,
&channels, sizeof(channels)) ||
!dsdlib_skip_to(decoder, is, chunk_end_offset))
@@ -143,7 +153,7 @@ dsdiff_read_prop_snd(struct decoder *decoder, struct input_stream *is,
metadata->channels = GUINT16_FROM_BE(channels);
} else if (dsdlib_id_equals(&header.id, "CMPR")) {
struct dsdlib_id type;
- if (dsdiff_chunk_size(&header) < sizeof(type) ||
+ if (header.GetSize() < sizeof(type) ||
!dsdlib_read(decoder, is,
&type, sizeof(type)) ||
!dsdlib_skip_to(decoder, is, chunk_end_offset))
@@ -161,7 +171,7 @@ dsdiff_read_prop_snd(struct decoder *decoder, struct input_stream *is,
}
}
- return is->offset == end_offset;
+ return is->GetOffset() == end_offset;
}
/**
@@ -169,11 +179,11 @@ dsdiff_read_prop_snd(struct decoder *decoder, struct input_stream *is,
*/
static bool
dsdiff_read_prop(struct decoder *decoder, struct input_stream *is,
- struct dsdiff_metadata *metadata,
- const struct dsdiff_chunk_header *prop_header)
+ DsdiffMetaData *metadata,
+ const DsdiffChunkHeader *prop_header)
{
- uint64_t prop_size = dsdiff_chunk_size(prop_header);
- goffset end_offset = is->offset + prop_size;
+ uint64_t prop_size = prop_header->GetSize();
+ goffset end_offset = is->GetOffset() + prop_size;
struct dsdlib_id prop_id;
if (prop_size < sizeof(prop_id) ||
@@ -187,6 +197,127 @@ dsdiff_read_prop(struct decoder *decoder, struct input_stream *is,
return dsdlib_skip_to(decoder, is, end_offset);
}
+static void
+dsdiff_handle_native_tag(struct input_stream *is,
+ const struct tag_handler *handler,
+ void *handler_ctx, goffset tagoffset,
+ enum tag_type type)
+{
+ if (!dsdlib_skip_to(nullptr, is, tagoffset))
+ return;
+
+ struct dsdiff_native_tag metatag;
+
+ if (!dsdlib_read(nullptr, is, &metatag, sizeof(metatag)))
+ return;
+
+ uint32_t length = GUINT32_FROM_BE(metatag.size);
+
+ /* Check and limit size of the tag to prevent a stack overflow */
+ if (length == 0 || length > 60)
+ return;
+
+ char string[length];
+ char *label;
+ label = string;
+
+ if (!dsdlib_read(nullptr, is, label, (size_t)length))
+ return;
+
+ string[length] = '\0';
+ tag_handler_invoke_tag(handler, handler_ctx, type, label);
+ return;
+}
+
+/**
+ * Read and parse additional metadata chunks for tagging purposes. By default
+ * dsdiff files only support equivalents for artist and title but some of the
+ * extract tools add an id3 tag to provide more tags. If such id3 is found
+ * this will be used for tagging otherwise the native tags (if any) will be
+ * used
+ */
+
+static bool
+dsdiff_read_metadata_extra(struct decoder *decoder, struct input_stream *is,
+ DsdiffMetaData *metadata,
+ DsdiffChunkHeader *chunk_header,
+ const struct tag_handler *handler,
+ void *handler_ctx)
+{
+
+ /* skip from DSD data to next chunk header */
+ if (!dsdlib_skip(decoder, is, metadata->chunk_size))
+ return false;
+ if (!dsdiff_read_chunk_header(decoder, is, chunk_header))
+ return false;
+
+#ifdef HAVE_ID3TAG
+ metadata->id3_size = 0;
+#endif
+
+ /* Now process all the remaining chunk headers in the stream
+ and record their position and size */
+
+ const goffset size = is->GetSize();
+ while (is->GetOffset() < size) {
+ uint64_t chunk_size = chunk_header->GetSize();
+
+ /* DIIN chunk, is directly followed by other chunks */
+ if (dsdlib_id_equals(&chunk_header->id, "DIIN"))
+ chunk_size = 0;
+
+ /* DIAR chunk - DSDIFF native tag for Artist */
+ if (dsdlib_id_equals(&chunk_header->id, "DIAR")) {
+ chunk_size = chunk_header->GetSize();
+ metadata->diar_offset = is->GetOffset();
+ }
+
+ /* DITI chunk - DSDIFF native tag for Title */
+ if (dsdlib_id_equals(&chunk_header->id, "DITI")) {
+ chunk_size = chunk_header->GetSize();
+ metadata->diti_offset = is->GetOffset();
+ }
+#ifdef HAVE_ID3TAG
+ /* 'ID3 ' chunk, offspec. Used by sacdextract */
+ if (dsdlib_id_equals(&chunk_header->id, "ID3 ")) {
+ chunk_size = chunk_header->GetSize();
+ metadata->id3_offset = is->GetOffset();
+ metadata->id3_size = chunk_size;
+ }
+#endif
+ if (chunk_size != 0) {
+ if (!dsdlib_skip(decoder, is, chunk_size))
+ break;
+ }
+
+ if (is->GetOffset() < size) {
+ if (!dsdiff_read_chunk_header(decoder, is, chunk_header))
+ return false;
+ }
+ chunk_size = 0;
+ }
+ /* done processing chunk headers, process tags if any */
+
+#ifdef HAVE_ID3TAG
+ if (metadata->id3_offset != 0)
+ {
+ /* a ID3 tag has preference over the other tags, do not process
+ other tags if we have one */
+ dsdlib_tag_id3(is, handler, handler_ctx, metadata->id3_offset);
+ return true;
+ }
+#endif
+
+ if (metadata->diar_offset != 0)
+ dsdiff_handle_native_tag(is, handler, handler_ctx,
+ metadata->diar_offset, TAG_ARTIST);
+
+ if (metadata->diti_offset != 0)
+ dsdiff_handle_native_tag(is, handler, handler_ctx,
+ metadata->diti_offset, TAG_TITLE);
+ return true;
+}
+
/**
* Read and parse all metadata chunks at the beginning. Stop when the
* first "DSD" chunk is seen, and return its header in the
@@ -194,10 +325,10 @@ dsdiff_read_prop(struct decoder *decoder, struct input_stream *is,
*/
static bool
dsdiff_read_metadata(struct decoder *decoder, struct input_stream *is,
- struct dsdiff_metadata *metadata,
- struct dsdiff_chunk_header *chunk_header)
+ DsdiffMetaData *metadata,
+ DsdiffChunkHeader *chunk_header)
{
- struct dsdiff_header header;
+ DsdiffHeader header;
if (!dsdlib_read(decoder, is, &header, sizeof(header)) ||
!dsdlib_id_equals(&header.id, "FRM8") ||
!dsdlib_id_equals(&header.format, "DSD "))
@@ -213,15 +344,14 @@ dsdiff_read_metadata(struct decoder *decoder, struct input_stream *is,
chunk_header))
return false;
} else if (dsdlib_id_equals(&chunk_header->id, "DSD ")) {
- uint64_t chunk_size;
- chunk_size = dsdiff_chunk_size(chunk_header);
+ const uint64_t chunk_size = chunk_header->GetSize();
metadata->chunk_size = chunk_size;
return true;
} else {
/* ignore unknown chunk */
- uint64_t chunk_size;
- chunk_size = dsdiff_chunk_size(chunk_header);
- goffset chunk_end_offset = is->offset + chunk_size;
+ const uint64_t chunk_size = chunk_header->GetSize();
+ goffset chunk_end_offset = is->GetOffset()
+ + chunk_size;
if (!dsdlib_skip_to(decoder, is, chunk_end_offset))
return false;
@@ -271,17 +401,16 @@ dsdiff_decode_chunk(struct decoder *decoder, struct input_stream *is,
if (lsbitfirst)
bit_reverse_buffer(buffer, buffer + nbytes);
- enum decoder_command cmd =
- decoder_data(decoder, is, buffer, nbytes, 0);
+ const auto cmd = decoder_data(decoder, is, buffer, nbytes, 0);
switch (cmd) {
- case DECODE_COMMAND_NONE:
+ case DecoderCommand::NONE:
break;
- case DECODE_COMMAND_START:
- case DECODE_COMMAND_STOP:
+ case DecoderCommand::START:
+ case DecoderCommand::STOP:
return false;
- case DECODE_COMMAND_SEEK:
+ case DecoderCommand::SEEK:
/* Not implemented yet */
decoder_seek_error(decoder);
@@ -294,23 +423,19 @@ dsdiff_decode_chunk(struct decoder *decoder, struct input_stream *is,
static void
dsdiff_stream_decode(struct decoder *decoder, struct input_stream *is)
{
- struct dsdiff_metadata metadata = {
- .sample_rate = 0,
- .channels = 0,
- };
+ DsdiffMetaData metadata;
- struct dsdiff_chunk_header chunk_header;
+ DsdiffChunkHeader chunk_header;
/* check if it is is a proper DFF file */
if (!dsdiff_read_metadata(decoder, is, &metadata, &chunk_header))
return;
- GError *error = NULL;
- struct audio_format audio_format;
- if (!audio_format_init_checked(&audio_format, metadata.sample_rate / 8,
- SAMPLE_FORMAT_DSD,
- metadata.channels, &error)) {
- g_warning("%s", error->message);
- g_error_free(error);
+ Error error;
+ AudioFormat audio_format;
+ if (!audio_format_init_checked(audio_format, metadata.sample_rate / 8,
+ SampleFormat::DSD,
+ metadata.channels, error)) {
+ LogError(error);
return;
}
@@ -320,13 +445,13 @@ dsdiff_stream_decode(struct decoder *decoder, struct input_stream *is)
(float) metadata.sample_rate;
/* success: file was recognized */
- decoder_initialized(decoder, &audio_format, false, songtime);
+ decoder_initialized(decoder, audio_format, false, songtime);
/* every iteration of the following loop decodes one "DSD"
chunk from a DFF file */
while (true) {
- chunk_size = dsdiff_chunk_size(&chunk_header);
+ chunk_size = chunk_header.GetSize();
if (dsdlib_id_equals(&chunk_header.id, "DSD ")) {
if (!dsdiff_decode_chunk(decoder, is,
@@ -349,23 +474,20 @@ dsdiff_stream_decode(struct decoder *decoder, struct input_stream *is)
static bool
dsdiff_scan_stream(struct input_stream *is,
- G_GNUC_UNUSED const struct tag_handler *handler,
- G_GNUC_UNUSED void *handler_ctx)
+ gcc_unused const struct tag_handler *handler,
+ gcc_unused void *handler_ctx)
{
- struct dsdiff_metadata metadata = {
- .sample_rate = 0,
- .channels = 0,
- };
+ DsdiffMetaData metadata;
+ DsdiffChunkHeader chunk_header;
- struct dsdiff_chunk_header chunk_header;
/* First check for DFF metadata */
- if (!dsdiff_read_metadata(NULL, is, &metadata, &chunk_header))
+ if (!dsdiff_read_metadata(nullptr, is, &metadata, &chunk_header))
return false;
- struct audio_format audio_format;
- if (!audio_format_init_checked(&audio_format, metadata.sample_rate / 8,
- SAMPLE_FORMAT_DSD,
- metadata.channels, NULL))
+ AudioFormat audio_format;
+ if (!audio_format_init_checked(audio_format, metadata.sample_rate / 8,
+ SampleFormat::DSD,
+ metadata.channels, IgnoreError()))
/* refuse to parse files which we cannot play anyway */
return false;
@@ -374,24 +496,32 @@ dsdiff_scan_stream(struct input_stream *is,
metadata.sample_rate;
tag_handler_invoke_duration(handler, handler_ctx, songtime);
+ /* Read additional metadata and created tags if available */
+ dsdiff_read_metadata_extra(nullptr, is, &metadata, &chunk_header,
+ handler, handler_ctx);
+
return true;
}
static const char *const dsdiff_suffixes[] = {
"dff",
- NULL
+ nullptr
};
static const char *const dsdiff_mime_types[] = {
"application/x-dff",
- NULL
+ nullptr
};
const struct decoder_plugin dsdiff_decoder_plugin = {
- .name = "dsdiff",
- .init = dsdiff_init,
- .stream_decode = dsdiff_stream_decode,
- .scan_stream = dsdiff_scan_stream,
- .suffixes = dsdiff_suffixes,
- .mime_types = dsdiff_mime_types,
+ "dsdiff",
+ dsdiff_init,
+ nullptr,
+ dsdiff_stream_decode,
+ nullptr,
+ nullptr,
+ dsdiff_scan_stream,
+ nullptr,
+ dsdiff_suffixes,
+ dsdiff_mime_types,
};
diff --git a/src/decoder/dsdiff_decoder_plugin.h b/src/decoder/DsdiffDecoderPlugin.hxx
index 452f9050b..c50605457 100644
--- a/src/decoder/dsdiff_decoder_plugin.h
+++ b/src/decoder/DsdiffDecoderPlugin.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/decoder/dsf_decoder_plugin.c b/src/decoder/DsfDecoderPlugin.cxx
index c0107eb30..4c4a66aaa 100644
--- a/src/decoder/dsf_decoder_plugin.c
+++ b/src/decoder/DsfDecoderPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -28,26 +28,30 @@
*/
#include "config.h"
-#include "dsf_decoder_plugin.h"
-#include "decoder_api.h"
-#include "audio_check.h"
+#include "DsfDecoderPlugin.hxx"
+#include "DecoderAPI.hxx"
+#include "InputStream.hxx"
+#include "CheckAudioFormat.hxx"
#include "util/bit_reverse.h"
-#include "dsdlib.h"
-#include "tag_handler.h"
+#include "util/Error.hxx"
+#include "DsdLib.hxx"
+#include "tag/TagHandler.hxx"
+#include "Log.hxx"
#include <unistd.h>
#include <stdio.h> /* for SEEK_SET, SEEK_CUR */
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "dsf"
-
-struct dsf_metadata {
+struct DsfMetaData {
unsigned sample_rate, channels;
bool bitreverse;
uint64_t chunk_size;
+#ifdef HAVE_ID3TAG
+ goffset id3_offset;
+ uint64_t id3_size;
+#endif
};
-struct dsf_header {
+struct DsfHeader {
/** DSF header id: "DSD " */
struct dsdlib_id id;
/** DSD chunk size, including id = 28 */
@@ -57,9 +61,9 @@ struct dsf_header {
/** pointer to id3v2 metadata, should be at the end of the file */
uint32_t pmeta_low, pmeta_high;
};
-/** DSF file fmt chunk */
-struct dsf_fmt_chunk {
+/** DSF file fmt chunk */
+struct DsfFmtChunk {
/** id: "fmt " */
struct dsdlib_id id;
/** fmt chunk size, including id, normally 52 */
@@ -84,7 +88,7 @@ struct dsf_fmt_chunk {
uint32_t reserved;
};
-struct dsf_data_chunk {
+struct DsfDataChunk {
struct dsdlib_id id;
/** "data" chunk size, includes header (id+size) */
uint32_t size_low, size_high;
@@ -95,10 +99,10 @@ struct dsf_data_chunk {
*/
static bool
dsf_read_metadata(struct decoder *decoder, struct input_stream *is,
- struct dsf_metadata *metadata)
+ DsfMetaData *metadata)
{
uint64_t chunk_size;
- struct dsf_header dsf_header;
+ DsfHeader dsf_header;
if (!dsdlib_read(decoder, is, &dsf_header, sizeof(dsf_header)) ||
!dsdlib_id_equals(&dsf_header.id, "DSD "))
return false;
@@ -109,8 +113,14 @@ dsf_read_metadata(struct decoder *decoder, struct input_stream *is,
if (sizeof(dsf_header) != chunk_size)
return false;
+#ifdef HAVE_ID3TAG
+ uint64_t metadata_offset;
+ metadata_offset = (((uint64_t)GUINT32_FROM_LE(dsf_header.pmeta_high)) << 32) |
+ ((uint64_t)GUINT32_FROM_LE(dsf_header.pmeta_low));
+#endif
+
/* read the 'fmt ' chunk of the DSF file */
- struct dsf_fmt_chunk dsf_fmt_chunk;
+ DsfFmtChunk dsf_fmt_chunk;
if (!dsdlib_read(decoder, is, &dsf_fmt_chunk, sizeof(dsf_fmt_chunk)) ||
!dsdlib_id_equals(&dsf_fmt_chunk.id, "fmt "))
return false;
@@ -139,7 +149,7 @@ dsf_read_metadata(struct decoder *decoder, struct input_stream *is,
return false;
/* read the 'data' chunk of the DSF file */
- struct dsf_data_chunk data_chunk;
+ DsfDataChunk data_chunk;
if (!dsdlib_read(decoder, is, &data_chunk, sizeof(data_chunk)) ||
!dsdlib_id_equals(&data_chunk.id, "data"))
return false;
@@ -153,9 +163,20 @@ dsf_read_metadata(struct decoder *decoder, struct input_stream *is,
data_size -= sizeof(data_chunk);
metadata->chunk_size = data_size;
+ /* data_size cannot be bigger or equal to total file size */
+ const uint64_t size = (uint64_t)is->GetSize();
+ if (data_size >= size)
+ return false;
+
metadata->channels = (unsigned) dsf_fmt_chunk.channelnum;
metadata->sample_rate = samplefreq;
-
+#ifdef HAVE_ID3TAG
+ /* metada_offset cannot be bigger then or equal to total file size */
+ if (metadata_offset >= size)
+ metadata->id3_offset = 0;
+ else
+ metadata->id3_offset = (goffset) metadata_offset;
+#endif
/* check bits per sample format, determine if bitreverse is needed */
metadata->bitreverse = dsf_fmt_chunk.bitssample == 1;
return true;
@@ -235,17 +256,16 @@ dsf_decode_chunk(struct decoder *decoder, struct input_stream *is,
dsf_to_pcm_order(buffer, dsf_scratch_buffer, nbytes);
- enum decoder_command cmd =
- decoder_data(decoder, is, buffer, nbytes, 0);
+ const auto cmd = decoder_data(decoder, is, buffer, nbytes, 0);
switch (cmd) {
- case DECODE_COMMAND_NONE:
+ case DecoderCommand::NONE:
break;
- case DECODE_COMMAND_START:
- case DECODE_COMMAND_STOP:
+ case DecoderCommand::START:
+ case DecoderCommand::STOP:
return false;
- case DECODE_COMMAND_SEEK:
+ case DecoderCommand::SEEK:
/* not implemented yet */
decoder_seek_error(decoder);
@@ -258,22 +278,17 @@ dsf_decode_chunk(struct decoder *decoder, struct input_stream *is,
static void
dsf_stream_decode(struct decoder *decoder, struct input_stream *is)
{
- struct dsf_metadata metadata = {
- .sample_rate = 0,
- .channels = 0,
- };
-
/* check if it is a proper DSF file */
+ DsfMetaData metadata;
if (!dsf_read_metadata(decoder, is, &metadata))
return;
- GError *error = NULL;
- struct audio_format audio_format;
- if (!audio_format_init_checked(&audio_format, metadata.sample_rate / 8,
- SAMPLE_FORMAT_DSD,
- metadata.channels, &error)) {
- g_warning("%s", error->message);
- g_error_free(error);
+ Error error;
+ AudioFormat audio_format;
+ if (!audio_format_init_checked(audio_format, metadata.sample_rate / 8,
+ SampleFormat::DSD,
+ metadata.channels, error)) {
+ LogError(error);
return;
}
/* Calculate song time from DSD chunk size and sample frequency */
@@ -282,32 +297,28 @@ dsf_stream_decode(struct decoder *decoder, struct input_stream *is)
(float) metadata.sample_rate;
/* success: file was recognized */
- decoder_initialized(decoder, &audio_format, false, songtime);
+ decoder_initialized(decoder, audio_format, false, songtime);
if (!dsf_decode_chunk(decoder, is, metadata.channels,
- metadata.chunk_size,
+ chunk_size,
metadata.bitreverse))
return;
}
static bool
dsf_scan_stream(struct input_stream *is,
- G_GNUC_UNUSED const struct tag_handler *handler,
- G_GNUC_UNUSED void *handler_ctx)
+ gcc_unused const struct tag_handler *handler,
+ gcc_unused void *handler_ctx)
{
- struct dsf_metadata metadata = {
- .sample_rate = 0,
- .channels = 0,
- };
-
/* check DSF metadata */
+ DsfMetaData metadata;
if (!dsf_read_metadata(NULL, is, &metadata))
return false;
- struct audio_format audio_format;
- if (!audio_format_init_checked(&audio_format, metadata.sample_rate / 8,
- SAMPLE_FORMAT_DSD,
- metadata.channels, NULL))
+ AudioFormat audio_format;
+ if (!audio_format_init_checked(audio_format, metadata.sample_rate / 8,
+ SampleFormat::DSD,
+ metadata.channels, IgnoreError()))
/* refuse to parse files which we cannot play anyway */
return false;
@@ -316,6 +327,10 @@ dsf_scan_stream(struct input_stream *is,
metadata.sample_rate;
tag_handler_invoke_duration(handler, handler_ctx, songtime);
+#ifdef HAVE_ID3TAG
+ /* Add available tags from the ID3 tag */
+ dsdlib_tag_id3(is, handler, handler_ctx, metadata.id3_offset);
+#endif
return true;
}
@@ -330,9 +345,14 @@ static const char *const dsf_mime_types[] = {
};
const struct decoder_plugin dsf_decoder_plugin = {
- .name = "dsf",
- .stream_decode = dsf_stream_decode,
- .scan_stream = dsf_scan_stream,
- .suffixes = dsf_suffixes,
- .mime_types = dsf_mime_types,
+ "dsf",
+ nullptr,
+ nullptr,
+ dsf_stream_decode,
+ nullptr,
+ nullptr,
+ dsf_scan_stream,
+ nullptr,
+ dsf_suffixes,
+ dsf_mime_types,
};
diff --git a/src/decoder/dsf_decoder_plugin.h b/src/decoder/DsfDecoderPlugin.hxx
index 401d3fed7..749032d1f 100644
--- a/src/decoder/dsf_decoder_plugin.h
+++ b/src/decoder/DsfDecoderPlugin.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/decoder/faad_decoder_plugin.c b/src/decoder/FaadDecoderPlugin.cxx
index 911f033b8..de846c61a 100644
--- a/src/decoder/faad_decoder_plugin.c
+++ b/src/decoder/FaadDecoderPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,34 +18,30 @@
*/
#include "config.h"
-#include "decoder_api.h"
-#include "decoder_buffer.h"
-#include "audio_check.h"
-#include "tag_handler.h"
-
-#define AAC_MAX_CHANNELS 6
+#include "FaadDecoderPlugin.hxx"
+#include "DecoderAPI.hxx"
+#include "DecoderBuffer.hxx"
+#include "InputStream.hxx"
+#include "CheckAudioFormat.hxx"
+#include "tag/TagHandler.hxx"
+#include "util/Error.hxx"
+#include "util/Domain.hxx"
+#include "Log.hxx"
+
+#include <neaacdec.h>
#include <assert.h>
+#include <string.h>
#include <unistd.h>
-#include <faad.h>
-#include <glib.h>
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "faad"
+#define AAC_MAX_CHANNELS 6
static const unsigned adts_sample_rates[] =
{ 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
16000, 12000, 11025, 8000, 7350, 0, 0, 0
};
-/**
- * The GLib quark used for errors reported by this plugin.
- */
-static inline GQuark
-faad_decoder_quark(void)
-{
- return g_quark_from_static_string("faad");
-}
+static constexpr Domain faad_decoder_domain("faad_decoder");
/**
* Check whether the buffer head is an AAC frame, and return the frame
@@ -68,15 +64,15 @@ adts_check_frame(const unsigned char *data)
* found or if not enough data is available.
*/
static size_t
-adts_find_frame(struct decoder_buffer *buffer)
+adts_find_frame(DecoderBuffer *buffer)
{
- const unsigned char *data, *p;
size_t length, frame_length;
bool ret;
while (true) {
- data = decoder_buffer_read(buffer, &length);
- if (data == NULL || length < 8) {
+ const uint8_t *data = (const uint8_t *)
+ decoder_buffer_read(buffer, &length);
+ if (data == nullptr || length < 8) {
/* not enough data yet */
ret = decoder_buffer_fill(buffer);
if (!ret)
@@ -87,8 +83,8 @@ adts_find_frame(struct decoder_buffer *buffer)
}
/* find the 0xff marker */
- p = memchr(data, 0xff, length);
- if (p == NULL) {
+ const uint8_t *p = (const uint8_t *)memchr(data, 0xff, length);
+ if (p == nullptr) {
/* no marker - discard the buffer */
decoder_buffer_consume(buffer, length);
continue;
@@ -118,8 +114,9 @@ adts_find_frame(struct decoder_buffer *buffer)
/* not enough data; discard this frame
to prevent a possible buffer
overflow */
- data = decoder_buffer_read(buffer, &length);
- if (data != NULL)
+ data = (const uint8_t *)
+ decoder_buffer_read(buffer, &length);
+ if (data != nullptr)
decoder_buffer_consume(buffer, length);
}
@@ -132,7 +129,7 @@ adts_find_frame(struct decoder_buffer *buffer)
}
static float
-adts_song_duration(struct decoder_buffer *buffer)
+adts_song_duration(DecoderBuffer *buffer)
{
unsigned int frames, frame_length;
unsigned sample_rate = 0;
@@ -146,11 +143,10 @@ adts_song_duration(struct decoder_buffer *buffer)
if (frames == 0) {
- const unsigned char *data;
size_t buffer_length;
-
- data = decoder_buffer_read(buffer, &buffer_length);
- assert(data != NULL);
+ const uint8_t *data = (const uint8_t *)
+ decoder_buffer_read(buffer, &buffer_length);
+ assert(data != nullptr);
assert(frame_length <= buffer_length);
sample_rate = adts_sample_rates[(data[2] & 0x3c) >> 2];
@@ -167,19 +163,20 @@ adts_song_duration(struct decoder_buffer *buffer)
}
static float
-faad_song_duration(struct decoder_buffer *buffer, struct input_stream *is)
+faad_song_duration(DecoderBuffer *buffer, struct input_stream *is)
{
size_t fileread;
size_t tagsize;
- const unsigned char *data;
size_t length;
bool success;
- fileread = is->size >= 0 ? is->size : 0;
+ const goffset size = is->GetSize();
+ fileread = size >= 0 ? size : 0;
decoder_buffer_fill(buffer);
- data = decoder_buffer_read(buffer, &length);
- if (data == NULL)
+ const uint8_t *data = (const uint8_t *)
+ decoder_buffer_read(buffer, &length);
+ if (data == nullptr)
return -1;
tagsize = 0;
@@ -196,20 +193,20 @@ faad_song_duration(struct decoder_buffer *buffer, struct input_stream *is)
if (!success)
return -1;
- data = decoder_buffer_read(buffer, &length);
- if (data == NULL)
+ data = (const uint8_t *)decoder_buffer_read(buffer, &length);
+ if (data == nullptr)
return -1;
}
- if (is->seekable && length >= 2 &&
+ if (is->IsSeekable() && length >= 2 &&
data[0] == 0xFF && ((data[1] & 0xF6) == 0xF0)) {
/* obtain the duration from the ADTS header */
float song_length = adts_song_duration(buffer);
- input_stream_lock_seek(is, tagsize, SEEK_SET, NULL);
+ is->LockSeek(tagsize, SEEK_SET, IgnoreError());
- data = decoder_buffer_read(buffer, &length);
- if (data != NULL)
+ data = (const uint8_t *)decoder_buffer_read(buffer, &length);
+ if (data != nullptr)
decoder_buffer_consume(buffer, length);
decoder_buffer_fill(buffer);
@@ -238,19 +235,13 @@ faad_song_duration(struct decoder_buffer *buffer, struct input_stream *is)
}
/**
- * Wrapper for faacDecInit() which works around some API
+ * Wrapper for NeAACDecInit() which works around some API
* inconsistencies in libfaad.
*/
static bool
-faad_decoder_init(faacDecHandle decoder, struct decoder_buffer *buffer,
- struct audio_format *audio_format, GError **error_r)
+faad_decoder_init(NeAACDecHandle decoder, DecoderBuffer *buffer,
+ AudioFormat &audio_format, Error &error)
{
- union {
- /* deconst hack for libfaad */
- const void *in;
- void *out;
- } u;
- size_t length;
int32_t nbytes;
uint32_t sample_rate;
uint8_t channels;
@@ -263,58 +254,48 @@ faad_decoder_init(faacDecHandle decoder, struct decoder_buffer *buffer,
uint32_t *sample_rate_p = &sample_rate;
#endif
- u.in = decoder_buffer_read(buffer, &length);
- if (u.in == NULL) {
- g_set_error(error_r, faad_decoder_quark(), 0,
- "Empty file");
+ size_t length;
+ const unsigned char *data = (const unsigned char *)
+ decoder_buffer_read(buffer, &length);
+ if (data == nullptr) {
+ error.Set(faad_decoder_domain, "Empty file");
return false;
}
- nbytes = faacDecInit(decoder, u.out,
-#ifdef HAVE_FAAD_BUFLEN_FUNCS
+ nbytes = NeAACDecInit(decoder,
+ /* deconst hack, libfaad requires this */
+ const_cast<unsigned char *>(data),
length,
-#endif
sample_rate_p, &channels);
if (nbytes < 0) {
- g_set_error(error_r, faad_decoder_quark(), 0,
- "Not an AAC stream");
+ error.Set(faad_decoder_domain, "Not an AAC stream");
return false;
}
decoder_buffer_consume(buffer, nbytes);
return audio_format_init_checked(audio_format, sample_rate,
- SAMPLE_FORMAT_S16, channels, error_r);
+ SampleFormat::S16, channels, error);
}
/**
- * Wrapper for faacDecDecode() which works around some API
+ * Wrapper for NeAACDecDecode() which works around some API
* inconsistencies in libfaad.
*/
static const void *
-faad_decoder_decode(faacDecHandle decoder, struct decoder_buffer *buffer,
- faacDecFrameInfo *frame_info)
+faad_decoder_decode(NeAACDecHandle decoder, DecoderBuffer *buffer,
+ NeAACDecFrameInfo *frame_info)
{
- union {
- /* deconst hack for libfaad */
- const void *in;
- void *out;
- } u;
size_t length;
- void *result;
-
- u.in = decoder_buffer_read(buffer, &length);
- if (u.in == NULL)
- return NULL;
-
- result = faacDecDecode(decoder, frame_info,
- u.out
-#ifdef HAVE_FAAD_BUFLEN_FUNCS
- , length
-#endif
- );
-
- return result;
+ const unsigned char *data = (const unsigned char *)
+ decoder_buffer_read(buffer, &length);
+ if (data == nullptr)
+ return nullptr;
+
+ return NeAACDecDecode(decoder, frame_info,
+ /* deconst hack, libfaad requires this */
+ const_cast<unsigned char *>(data),
+ length);
}
/**
@@ -325,32 +306,32 @@ faad_decoder_decode(faacDecHandle decoder, struct decoder_buffer *buffer,
static float
faad_get_file_time_float(struct input_stream *is)
{
- struct decoder_buffer *buffer;
+ DecoderBuffer *buffer;
float length;
- faacDecHandle decoder;
- faacDecConfigurationPtr config;
- buffer = decoder_buffer_new(NULL, is,
+ buffer = decoder_buffer_new(nullptr, is,
FAAD_MIN_STREAMSIZE * AAC_MAX_CHANNELS);
length = faad_song_duration(buffer, is);
if (length < 0) {
bool ret;
- struct audio_format audio_format;
+ AudioFormat audio_format;
- decoder = faacDecOpen();
+ NeAACDecHandle decoder = NeAACDecOpen();
- config = faacDecGetCurrentConfiguration(decoder);
+ NeAACDecConfigurationPtr config =
+ NeAACDecGetCurrentConfiguration(decoder);
config->outputFormat = FAAD_FMT_16BIT;
- faacDecSetConfiguration(decoder, config);
+ NeAACDecSetConfiguration(decoder, config);
decoder_buffer_fill(buffer);
- ret = faad_decoder_init(decoder, buffer, &audio_format, NULL);
+ ret = faad_decoder_init(decoder, buffer, audio_format,
+ IgnoreError());
if (ret)
length = 0;
- faacDecClose(decoder);
+ NeAACDecClose(decoder);
}
decoder_buffer_free(buffer);
@@ -378,15 +359,11 @@ faad_get_file_time(struct input_stream *is)
static void
faad_stream_decode(struct decoder *mpd_decoder, struct input_stream *is)
{
- GError *error = NULL;
float total_time = 0;
- faacDecHandle decoder;
- struct audio_format audio_format;
- faacDecConfigurationPtr config;
+ AudioFormat audio_format;
bool ret;
uint16_t bit_rate = 0;
- struct decoder_buffer *buffer;
- enum decoder_command cmd;
+ DecoderBuffer *buffer;
buffer = decoder_buffer_new(mpd_decoder, is,
FAAD_MIN_STREAMSIZE * AAC_MAX_CHANNELS);
@@ -394,45 +371,42 @@ faad_stream_decode(struct decoder *mpd_decoder, struct input_stream *is)
/* create the libfaad decoder */
- decoder = faacDecOpen();
+ NeAACDecHandle decoder = NeAACDecOpen();
- config = faacDecGetCurrentConfiguration(decoder);
+ NeAACDecConfigurationPtr config =
+ NeAACDecGetCurrentConfiguration(decoder);
config->outputFormat = FAAD_FMT_16BIT;
-#ifdef HAVE_FAACDECCONFIGURATION_DOWNMATRIX
config->downMatrix = 1;
-#endif
-#ifdef HAVE_FAACDECCONFIGURATION_DONTUPSAMPLEIMPLICITSBR
config->dontUpSampleImplicitSBR = 0;
-#endif
- faacDecSetConfiguration(decoder, config);
+ NeAACDecSetConfiguration(decoder, config);
- while (!decoder_buffer_is_full(buffer) &&
- !input_stream_lock_eof(is) &&
- decoder_get_command(mpd_decoder) == DECODE_COMMAND_NONE) {
+ while (!decoder_buffer_is_full(buffer) && !is->LockIsEOF() &&
+ decoder_get_command(mpd_decoder) == DecoderCommand::NONE) {
adts_find_frame(buffer);
decoder_buffer_fill(buffer);
}
/* initialize it */
- ret = faad_decoder_init(decoder, buffer, &audio_format, &error);
+ Error error;
+ ret = faad_decoder_init(decoder, buffer, audio_format, error);
if (!ret) {
- g_warning("%s", error->message);
- g_error_free(error);
- faacDecClose(decoder);
+ LogError(error);
+ NeAACDecClose(decoder);
return;
}
/* initialize the MPD core */
- decoder_initialized(mpd_decoder, &audio_format, false, total_time);
+ decoder_initialized(mpd_decoder, audio_format, false, total_time);
/* the decoder loop */
+ DecoderCommand cmd;
do {
size_t frame_size;
const void *decoded;
- faacDecFrameInfo frame_info;
+ NeAACDecFrameInfo frame_info;
/* find the next frame */
@@ -446,25 +420,26 @@ faad_stream_decode(struct decoder *mpd_decoder, struct input_stream *is)
decoded = faad_decoder_decode(decoder, buffer, &frame_info);
if (frame_info.error > 0) {
- g_warning("error decoding AAC stream: %s\n",
- faacDecGetErrorMessage(frame_info.error));
+ FormatWarning(faad_decoder_domain,
+ "error decoding AAC stream: %s",
+ NeAACDecGetErrorMessage(frame_info.error));
break;
}
if (frame_info.channels != audio_format.channels) {
- g_warning("channel count changed from %u to %u",
- audio_format.channels, frame_info.channels);
+ FormatInfo(faad_decoder_domain,
+ "channel count changed from %u to %u",
+ audio_format.channels, frame_info.channels);
break;
}
-#ifdef HAVE_FAACDECFRAMEINFO_SAMPLERATE
if (frame_info.samplerate != audio_format.sample_rate) {
- g_warning("sample rate changed from %u to %lu",
- audio_format.sample_rate,
- (unsigned long)frame_info.samplerate);
+ FormatInfo(faad_decoder_domain,
+ "sample rate changed from %u to %lu",
+ audio_format.sample_rate,
+ (unsigned long)frame_info.samplerate);
break;
}
-#endif
decoder_buffer_consume(buffer, frame_info.bytesconsumed);
@@ -481,11 +456,11 @@ faad_stream_decode(struct decoder *mpd_decoder, struct input_stream *is)
cmd = decoder_data(mpd_decoder, is, decoded,
(size_t)frame_info.samples * 2,
bit_rate);
- } while (cmd != DECODE_COMMAND_STOP);
+ } while (cmd != DecoderCommand::STOP);
/* cleanup */
- faacDecClose(decoder);
+ NeAACDecClose(decoder);
}
static bool
@@ -501,15 +476,20 @@ faad_scan_stream(struct input_stream *is,
return true;
}
-static const char *const faad_suffixes[] = { "aac", NULL };
+static const char *const faad_suffixes[] = { "aac", nullptr };
static const char *const faad_mime_types[] = {
- "audio/aac", "audio/aacp", NULL
+ "audio/aac", "audio/aacp", nullptr
};
const struct decoder_plugin faad_decoder_plugin = {
- .name = "faad",
- .stream_decode = faad_stream_decode,
- .scan_stream = faad_scan_stream,
- .suffixes = faad_suffixes,
- .mime_types = faad_mime_types,
+ "faad",
+ nullptr,
+ nullptr,
+ faad_stream_decode,
+ nullptr,
+ nullptr,
+ faad_scan_stream,
+ nullptr,
+ faad_suffixes,
+ faad_mime_types,
};
diff --git a/src/decoder/FaadDecoderPlugin.hxx b/src/decoder/FaadDecoderPlugin.hxx
new file mode 100644
index 000000000..162c155ad
--- /dev/null
+++ b/src/decoder/FaadDecoderPlugin.hxx
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2003-2013 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_FAAD_DECODER_PLUGIN_HXX
+#define MPD_FAAD_DECODER_PLUGIN_HXX
+
+extern const struct decoder_plugin faad_decoder_plugin;
+
+#endif
diff --git a/src/decoder/ffmpeg_decoder_plugin.c b/src/decoder/FfmpegDecoderPlugin.cxx
index 58bd2f54a..646b8f974 100644
--- a/src/decoder/ffmpeg_decoder_plugin.c
+++ b/src/decoder/FfmpegDecoderPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,11 +17,19 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
+/* necessary because libavutil/common.h uses UINT64_C */
+#define __STDC_CONSTANT_MACROS
+
#include "config.h"
-#include "decoder_api.h"
-#include "audio_check.h"
-#include "ffmpeg_metadata.h"
-#include "tag_handler.h"
+#include "FfmpegDecoderPlugin.hxx"
+#include "DecoderAPI.hxx"
+#include "FfmpegMetaData.hxx"
+#include "tag/TagHandler.hxx"
+#include "InputStream.hxx"
+#include "CheckAudioFormat.hxx"
+#include "util/Error.hxx"
+#include "util/Domain.hxx"
+#include "LogV.hxx"
#include <glib.h>
@@ -34,36 +42,40 @@
#include <sys/stat.h>
#include <unistd.h>
+extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavformat/avio.h>
#include <libavutil/avutil.h>
#include <libavutil/log.h>
#include <libavutil/mathematics.h>
-#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51,5,0)
#include <libavutil/dict.h>
-#endif
+}
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "ffmpeg"
+static constexpr Domain ffmpeg_domain("ffmpeg");
-static GLogLevelFlags
-level_ffmpeg_to_glib(int level)
+/* suppress the ffmpeg compatibility macro */
+#ifdef SampleFormat
+#undef SampleFormat
+#endif
+
+static LogLevel
+import_ffmpeg_level(int level)
{
if (level <= AV_LOG_FATAL)
- return G_LOG_LEVEL_CRITICAL;
+ return LogLevel::ERROR;
- if (level <= AV_LOG_ERROR)
- return G_LOG_LEVEL_WARNING;
+ if (level <= AV_LOG_WARNING)
+ return LogLevel::WARNING;
if (level <= AV_LOG_INFO)
- return G_LOG_LEVEL_MESSAGE;
+ return LogLevel::INFO;
- return G_LOG_LEVEL_DEBUG;
+ return LogLevel::DEBUG;
}
static void
-mpd_ffmpeg_log_callback(G_GNUC_UNUSED void *ptr, int level,
+mpd_ffmpeg_log_callback(gcc_unused void *ptr, int level,
const char *fmt, va_list vl)
{
const AVClass * cls = NULL;
@@ -72,28 +84,36 @@ mpd_ffmpeg_log_callback(G_GNUC_UNUSED void *ptr, int level,
cls = *(const AVClass *const*)ptr;
if (cls != NULL) {
- char *domain = g_strconcat(G_LOG_DOMAIN, "/", cls->item_name(ptr), NULL);
- g_logv(domain, level_ffmpeg_to_glib(level), fmt, vl);
+ char *domain = g_strconcat(ffmpeg_domain.GetName(), "/", cls->item_name(ptr), NULL);
+ const Domain d(domain);
+ LogFormatV(d, import_ffmpeg_level(level), fmt, vl);
g_free(domain);
}
}
-struct mpd_ffmpeg_stream {
+struct AvioStream {
struct decoder *decoder;
struct input_stream *input;
-#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(52,101,0)
AVIOContext *io;
-#else
- ByteIOContext *io;
-#endif
+
unsigned char buffer[8192];
+
+ AvioStream(struct decoder *_decoder, input_stream *_input)
+ :decoder(_decoder), input(_input), io(nullptr) {}
+
+ ~AvioStream() {
+ if (io != nullptr)
+ av_free(io);
+ }
+
+ bool Open();
};
static int
mpd_ffmpeg_stream_read(void *opaque, uint8_t *buf, int size)
{
- struct mpd_ffmpeg_stream *stream = opaque;
+ AvioStream *stream = (AvioStream *)opaque;
return decoder_read(stream->decoder, stream->input,
(void *)buf, size);
@@ -102,42 +122,27 @@ mpd_ffmpeg_stream_read(void *opaque, uint8_t *buf, int size)
static int64_t
mpd_ffmpeg_stream_seek(void *opaque, int64_t pos, int whence)
{
- struct mpd_ffmpeg_stream *stream = opaque;
+ AvioStream *stream = (AvioStream *)opaque;
if (whence == AVSEEK_SIZE)
return stream->input->size;
- if (!input_stream_lock_seek(stream->input, pos, whence, NULL))
+ Error error;
+ if (!stream->input->LockSeek(pos, whence, error))
return -1;
return stream->input->offset;
}
-static struct mpd_ffmpeg_stream *
-mpd_ffmpeg_stream_open(struct decoder *decoder, struct input_stream *input)
+bool
+AvioStream::Open()
{
- struct mpd_ffmpeg_stream *stream = g_new(struct mpd_ffmpeg_stream, 1);
- stream->decoder = decoder;
- stream->input = input;
-#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(52,101,0)
- stream->io = avio_alloc_context(stream->buffer, sizeof(stream->buffer),
- false, stream,
- mpd_ffmpeg_stream_read, NULL,
- input->seekable
- ? mpd_ffmpeg_stream_seek : NULL);
-#else
- stream->io = av_alloc_put_byte(stream->buffer, sizeof(stream->buffer),
- false, stream,
- mpd_ffmpeg_stream_read, NULL,
- input->seekable
- ? mpd_ffmpeg_stream_seek : NULL);
-#endif
- if (stream->io == NULL) {
- g_free(stream);
- return NULL;
- }
-
- return stream;
+ io = avio_alloc_context(buffer, sizeof(buffer),
+ false, this,
+ mpd_ffmpeg_stream_read, nullptr,
+ input->seekable
+ ? mpd_ffmpeg_stream_seek : nullptr);
+ return io != nullptr;
}
/**
@@ -146,15 +151,10 @@ mpd_ffmpeg_stream_open(struct decoder *decoder, struct input_stream *input)
*/
static int
mpd_ffmpeg_open_input(AVFormatContext **ic_ptr,
-#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(52,101,0)
AVIOContext *pb,
-#else
- ByteIOContext *pb,
-#endif
const char *filename,
AVInputFormat *fmt)
{
-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53,1,3)
AVFormatContext *context = avformat_alloc_context();
if (context == NULL)
return AVERROR(ENOMEM);
@@ -162,20 +162,10 @@ mpd_ffmpeg_open_input(AVFormatContext **ic_ptr,
context->pb = pb;
*ic_ptr = context;
return avformat_open_input(ic_ptr, filename, fmt, NULL);
-#else
- return av_open_input_stream(ic_ptr, pb, filename, fmt, NULL);
-#endif
-}
-
-static void
-mpd_ffmpeg_stream_close(struct mpd_ffmpeg_stream *stream)
-{
- av_free(stream->io);
- g_free(stream);
}
static bool
-ffmpeg_init(G_GNUC_UNUSED const struct config_param *param)
+ffmpeg_init(gcc_unused const config_param &param)
{
av_log_set_callback(mpd_ffmpeg_log_callback);
@@ -188,34 +178,13 @@ ffmpeg_find_audio_stream(const AVFormatContext *format_context)
{
for (unsigned i = 0; i < format_context->nb_streams; ++i)
if (format_context->streams[i]->codec->codec_type ==
-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52, 64, 0)
AVMEDIA_TYPE_AUDIO)
-#else
- CODEC_TYPE_AUDIO)
-#endif
return i;
return -1;
}
-#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(53,25,0)
-/**
- * On some platforms, libavcodec wants the output buffer aligned to 16
- * bytes (because it uses SSE/Altivec internally). This function
- * returns the aligned version of the specified buffer, and corrects
- * the buffer size.
- */
-static void *
-align16(void *p, size_t *length_p)
-{
- unsigned add = 16 - (size_t)p % 16;
-
- *length_p -= add;
- return (char *)p + add;
-}
-#endif
-
-G_GNUC_CONST
+gcc_const
static double
time_from_ffmpeg(int64_t t, const AVRational time_base)
{
@@ -225,7 +194,7 @@ time_from_ffmpeg(int64_t t, const AVRational time_base)
/ (double)1024;
}
-G_GNUC_CONST
+gcc_const
static int64_t
time_to_ffmpeg(double t, const AVRational time_base)
{
@@ -233,8 +202,6 @@ time_to_ffmpeg(double t, const AVRational time_base)
time_base);
}
-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53,25,0)
-
static void
copy_interleave_frame2(uint8_t *dest, uint8_t **src,
unsigned nframes, unsigned nchannels,
@@ -255,7 +222,8 @@ copy_interleave_frame2(uint8_t *dest, uint8_t **src,
static int
copy_interleave_frame(const AVCodecContext *codec_context,
const AVFrame *frame,
- uint8_t *buffer, size_t buffer_size)
+ uint8_t **output_buffer,
+ uint8_t **global_buffer, int *global_buffer_size)
{
int plane_size;
const int data_size =
@@ -263,68 +231,49 @@ copy_interleave_frame(const AVCodecContext *codec_context,
codec_context->channels,
frame->nb_samples,
codec_context->sample_fmt, 1);
- if (buffer_size < (size_t)data_size)
- /* buffer is too small - shouldn't happen */
- return AVERROR(EINVAL);
-
if (av_sample_fmt_is_planar(codec_context->sample_fmt) &&
codec_context->channels > 1) {
- copy_interleave_frame2(buffer, frame->extended_data,
+ if(*global_buffer_size < data_size) {
+ av_freep(global_buffer);
+
+ *global_buffer = (uint8_t*)av_malloc(data_size);
+
+ if (!*global_buffer)
+ /* Not enough memory - shouldn't happen */
+ return AVERROR(ENOMEM);
+ *global_buffer_size = data_size;
+ }
+ *output_buffer = *global_buffer;
+ copy_interleave_frame2(*output_buffer, frame->extended_data,
frame->nb_samples,
codec_context->channels,
av_get_bytes_per_sample(codec_context->sample_fmt));
} else {
- memcpy(buffer, frame->extended_data[0], data_size);
+ *output_buffer = frame->extended_data[0];
}
return data_size;
}
-#endif
-static enum decoder_command
+static DecoderCommand
ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is,
const AVPacket *packet,
AVCodecContext *codec_context,
- const AVRational *time_base)
+ const AVRational *time_base,
+ AVFrame *frame,
+ uint8_t **buffer, int *buffer_size)
{
if (packet->pts >= 0 && packet->pts != (int64_t)AV_NOPTS_VALUE)
decoder_timestamp(decoder,
time_from_ffmpeg(packet->pts, *time_base));
-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52,25,0)
AVPacket packet2 = *packet;
-#else
- const uint8_t *packet_data = packet->data;
- int packet_size = packet->size;
-#endif
-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53,25,0)
- uint8_t aligned_buffer[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2 + 16];
- const size_t buffer_size = sizeof(aligned_buffer);
-#else
- /* libavcodec < 0.8 needs an aligned buffer */
- uint8_t audio_buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2 + 16];
- size_t buffer_size = sizeof(audio_buf);
- int16_t *aligned_buffer = align16(audio_buf, &buffer_size);
-#endif
-
- enum decoder_command cmd = DECODE_COMMAND_NONE;
- while (
-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52,25,0)
- packet2.size > 0 &&
-#else
- packet_size > 0 &&
-#endif
- cmd == DECODE_COMMAND_NONE) {
- int audio_size = buffer_size;
-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53,25,0)
-
- AVFrame *frame = avcodec_alloc_frame();
- if (frame == NULL) {
- g_warning("Could not allocate frame");
- break;
- }
+ uint8_t *output_buffer;
+ DecoderCommand cmd = DecoderCommand::NONE;
+ while (packet2.size > 0 && cmd == DecoderCommand::NONE) {
+ int audio_size = 0;
int got_frame = 0;
int len = avcodec_decode_audio4(codec_context,
frame, &got_frame,
@@ -332,103 +281,64 @@ ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is,
if (len >= 0 && got_frame) {
audio_size = copy_interleave_frame(codec_context,
frame,
- aligned_buffer,
- buffer_size);
+ &output_buffer,
+ buffer, buffer_size);
if (audio_size < 0)
len = audio_size;
- } else if (len >= 0)
- len = -1;
-
-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(54, 28, 0)
- avcodec_free_frame(&frame);
-#else
- av_freep(&frame);
-#endif
-
-#elif LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52,25,0)
- int len = avcodec_decode_audio3(codec_context,
- aligned_buffer, &audio_size,
- &packet2);
-#else
- int len = avcodec_decode_audio2(codec_context,
- aligned_buffer, &audio_size,
- packet_data, packet_size);
-#endif
+ }
if (len < 0) {
/* if error, we skip the frame */
- g_message("decoding failed, frame skipped\n");
+ LogInfo(ffmpeg_domain,
+ "decoding failed, frame skipped");
break;
}
-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52,25,0)
packet2.data += len;
packet2.size -= len;
-#else
- packet_data += len;
- packet_size -= len;
-#endif
if (audio_size <= 0)
continue;
cmd = decoder_data(decoder, is,
- aligned_buffer, audio_size,
+ output_buffer, audio_size,
codec_context->bit_rate / 1000);
}
return cmd;
}
-#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(52, 94, 1)
-#define AVSampleFormat SampleFormat
-#endif
-
-G_GNUC_CONST
-static enum sample_format
+gcc_const
+static SampleFormat
ffmpeg_sample_format(enum AVSampleFormat sample_fmt)
{
switch (sample_fmt) {
-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52, 94, 1)
case AV_SAMPLE_FMT_S16:
-#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51,17,0)
case AV_SAMPLE_FMT_S16P:
-#endif
-#else
- case SAMPLE_FMT_S16:
-#endif
- return SAMPLE_FORMAT_S16;
+ return SampleFormat::S16;
-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52, 94, 1)
case AV_SAMPLE_FMT_S32:
-#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51,17,0)
case AV_SAMPLE_FMT_S32P:
-#endif
-#else
- case SAMPLE_FMT_S32:
-#endif
- return SAMPLE_FORMAT_S32;
+ return SampleFormat::S32;
-#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51,17,0)
case AV_SAMPLE_FMT_FLTP:
- return SAMPLE_FORMAT_FLOAT;
-#endif
+ return SampleFormat::FLOAT;
default:
break;
}
-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52, 94, 1)
char buffer[64];
const char *name = av_get_sample_fmt_string(buffer, sizeof(buffer),
sample_fmt);
if (name != NULL)
- g_warning("Unsupported libavcodec SampleFormat value: %s (%d)",
- name, sample_fmt);
+ FormatError(ffmpeg_domain,
+ "Unsupported libavcodec SampleFormat value: %s (%d)",
+ name, sample_fmt);
else
-#endif
- g_warning("Unsupported libavcodec SampleFormat value: %d",
- sample_fmt);
- return SAMPLE_FORMAT_UNDEFINED;
+ FormatError(ffmpeg_domain,
+ "Unsupported libavcodec SampleFormat value: %d",
+ sample_fmt);
+ return SampleFormat::UNDEFINED;
}
static AVInputFormat *
@@ -439,10 +349,11 @@ ffmpeg_probe(struct decoder *decoder, struct input_stream *is)
PADDING = 16,
};
- unsigned char *buffer = g_malloc(BUFFER_SIZE);
+ Error error;
+
+ unsigned char *buffer = (unsigned char *)g_malloc(BUFFER_SIZE);
size_t nbytes = decoder_read(decoder, is, buffer, BUFFER_SIZE);
- if (nbytes <= PADDING ||
- !input_stream_lock_seek(is, 0, SEEK_SET, NULL)) {
+ if (nbytes <= PADDING || !is->LockSeek(0, SEEK_SET, error)) {
g_free(buffer);
return NULL;
}
@@ -453,11 +364,10 @@ ffmpeg_probe(struct decoder *decoder, struct input_stream *is)
size */
nbytes -= PADDING;
- AVProbeData avpd = {
- .buf = buffer,
- .buf_size = nbytes,
- .filename = is->uri,
- };
+ AVProbeData avpd;
+ avpd.buf = buffer;
+ avpd.buf_size = nbytes;
+ avpd.filename = is->uri.c_str();
AVInputFormat *format = av_probe_input_format(&avpd, true);
g_free(buffer);
@@ -472,51 +382,36 @@ ffmpeg_decode(struct decoder *decoder, struct input_stream *input)
if (input_format == NULL)
return;
- g_debug("detected input format '%s' (%s)",
- input_format->name, input_format->long_name);
+ FormatDebug(ffmpeg_domain, "detected input format '%s' (%s)",
+ input_format->name, input_format->long_name);
- struct mpd_ffmpeg_stream *stream =
- mpd_ffmpeg_stream_open(decoder, input);
- if (stream == NULL) {
- g_warning("Failed to open stream");
+ AvioStream stream(decoder, input);
+ if (!stream.Open()) {
+ LogError(ffmpeg_domain, "Failed to open stream");
return;
}
//ffmpeg works with ours "fileops" helper
AVFormatContext *format_context = NULL;
- if (mpd_ffmpeg_open_input(&format_context, stream->io, input->uri,
+ if (mpd_ffmpeg_open_input(&format_context, stream.io,
+ input->uri.c_str(),
input_format) != 0) {
- g_warning("Open failed\n");
- mpd_ffmpeg_stream_close(stream);
+ LogError(ffmpeg_domain, "Open failed");
return;
}
-#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,6,0)
const int find_result =
avformat_find_stream_info(format_context, NULL);
-#else
- const int find_result = av_find_stream_info(format_context);
-#endif
if (find_result < 0) {
- g_warning("Couldn't find stream info\n");
-#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,17,0)
+ LogError(ffmpeg_domain, "Couldn't find stream info");
avformat_close_input(&format_context);
-#else
- av_close_input_stream(format_context);
-#endif
- mpd_ffmpeg_stream_close(stream);
return;
}
int audio_stream = ffmpeg_find_audio_stream(format_context);
if (audio_stream == -1) {
- g_warning("No audio stream inside\n");
-#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,17,0)
+ LogError(ffmpeg_domain, "No audio stream inside");
avformat_close_input(&format_context);
-#else
- av_close_input_stream(format_context);
-#endif
- mpd_ffmpeg_stream_close(stream);
return;
}
@@ -524,40 +419,30 @@ ffmpeg_decode(struct decoder *decoder, struct input_stream *input)
AVCodecContext *codec_context = av_stream->codec;
if (codec_context->codec_name[0] != 0)
- g_debug("codec '%s'", codec_context->codec_name);
+ FormatDebug(ffmpeg_domain, "codec '%s'",
+ codec_context->codec_name);
AVCodec *codec = avcodec_find_decoder(codec_context->codec_id);
if (!codec) {
- g_warning("Unsupported audio codec\n");
-#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,17,0)
+ LogError(ffmpeg_domain, "Unsupported audio codec");
avformat_close_input(&format_context);
-#else
- av_close_input_stream(format_context);
-#endif
- mpd_ffmpeg_stream_close(stream);
return;
}
- const enum sample_format sample_format =
+ const SampleFormat sample_format =
ffmpeg_sample_format(codec_context->sample_fmt);
- if (sample_format == SAMPLE_FORMAT_UNDEFINED)
+ if (sample_format == SampleFormat::UNDEFINED)
return;
- GError *error = NULL;
- struct audio_format audio_format;
- if (!audio_format_init_checked(&audio_format,
+ Error error;
+ AudioFormat audio_format;
+ if (!audio_format_init_checked(audio_format,
codec_context->sample_rate,
sample_format,
- codec_context->channels, &error)) {
- g_warning("%s", error->message);
- g_error_free(error);
-#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,17,0)
+ codec_context->channels, error)) {
+ LogError(error);
avformat_close_input(&format_context);
-#else
- av_close_input_stream(format_context);
-#endif
- mpd_ffmpeg_stream_close(stream);
return;
}
@@ -566,19 +451,10 @@ ffmpeg_decode(struct decoder *decoder, struct input_stream *input)
values into AVCodecContext.channels - a change that will be
reverted later by avcodec_decode_audio3() */
-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53,6,0)
const int open_result = avcodec_open2(codec_context, codec, NULL);
-#else
- const int open_result = avcodec_open(codec_context, codec);
-#endif
if (open_result < 0) {
- g_warning("Could not open codec\n");
-#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,17,0)
+ LogError(ffmpeg_domain, "Could not open codec");
avformat_close_input(&format_context);
-#else
- av_close_input_stream(format_context);
-#endif
- mpd_ffmpeg_stream_close(stream);
return;
}
@@ -586,10 +462,20 @@ ffmpeg_decode(struct decoder *decoder, struct input_stream *input)
? format_context->duration / AV_TIME_BASE
: 0;
- decoder_initialized(decoder, &audio_format,
+ decoder_initialized(decoder, audio_format,
input->seekable, total_time);
- enum decoder_command cmd;
+ AVFrame *frame = avcodec_alloc_frame();
+ if (!frame) {
+ LogError(ffmpeg_domain, "Could not allocate frame");
+ avformat_close_input(&format_context);
+ return;
+ }
+
+ uint8_t *interleaved_buffer = NULL;
+ int interleaved_buffer_size = 0;
+
+ DecoderCommand cmd;
do {
AVPacket packet;
if (av_read_frame(format_context, &packet) < 0)
@@ -599,13 +485,15 @@ ffmpeg_decode(struct decoder *decoder, struct input_stream *input)
if (packet.stream_index == audio_stream)
cmd = ffmpeg_send_packet(decoder, input,
&packet, codec_context,
- &av_stream->time_base);
+ &av_stream->time_base,
+ frame,
+ &interleaved_buffer, &interleaved_buffer_size);
else
cmd = decoder_get_command(decoder);
av_free_packet(&packet);
- if (cmd == DECODE_COMMAND_SEEK) {
+ if (cmd == DecoderCommand::SEEK) {
int64_t where =
time_to_ffmpeg(decoder_seek_where(decoder),
av_stream->time_base);
@@ -618,15 +506,17 @@ ffmpeg_decode(struct decoder *decoder, struct input_stream *input)
decoder_command_finished(decoder);
}
}
- } while (cmd != DECODE_COMMAND_STOP);
+ } while (cmd != DecoderCommand::STOP);
- avcodec_close(codec_context);
-#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,17,0)
- avformat_close_input(&format_context);
+#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(54, 28, 0)
+ avcodec_free_frame(&frame);
#else
- av_close_input_stream(format_context);
+ av_freep(&frame);
#endif
- mpd_ffmpeg_stream_close(stream);
+ av_freep(&interleaved_buffer);
+
+ avcodec_close(codec_context);
+ avformat_close_input(&format_context);
}
//no tag reading in ffmpeg, check if playable
@@ -638,30 +528,19 @@ ffmpeg_scan_stream(struct input_stream *is,
if (input_format == NULL)
return false;
- struct mpd_ffmpeg_stream *stream = mpd_ffmpeg_stream_open(NULL, is);
- if (stream == NULL)
+ AvioStream stream(nullptr, is);
+ if (!stream.Open())
return false;
AVFormatContext *f = NULL;
- if (mpd_ffmpeg_open_input(&f, stream->io, is->uri,
- input_format) != 0) {
- mpd_ffmpeg_stream_close(stream);
+ if (mpd_ffmpeg_open_input(&f, stream.io, is->uri.c_str(),
+ input_format) != 0)
return false;
- }
-#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,6,0)
const int find_result =
avformat_find_stream_info(f, NULL);
-#else
- const int find_result = av_find_stream_info(f);
-#endif
if (find_result < 0) {
-#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,17,0)
avformat_close_input(&f);
-#else
- av_close_input_stream(f);
-#endif
- mpd_ffmpeg_stream_close(stream);
return false;
}
@@ -669,23 +548,13 @@ ffmpeg_scan_stream(struct input_stream *is,
tag_handler_invoke_duration(handler, handler_ctx,
f->duration / AV_TIME_BASE);
-#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(52,101,0)
- av_metadata_conv(f, NULL, f->iformat->metadata_conv);
-#endif
-
ffmpeg_scan_dictionary(f->metadata, handler, handler_ctx);
int idx = ffmpeg_find_audio_stream(f);
if (idx >= 0)
ffmpeg_scan_dictionary(f->streams[idx]->metadata,
handler, handler_ctx);
-#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,17,0)
avformat_close_input(&f);
-#else
- av_close_input_stream(f);
-#endif
- mpd_ffmpeg_stream_close(stream);
-
return true;
}
@@ -805,10 +674,14 @@ static const char *const ffmpeg_mime_types[] = {
};
const struct decoder_plugin ffmpeg_decoder_plugin = {
- .name = "ffmpeg",
- .init = ffmpeg_init,
- .stream_decode = ffmpeg_decode,
- .scan_stream = ffmpeg_scan_stream,
- .suffixes = ffmpeg_suffixes,
- .mime_types = ffmpeg_mime_types
+ "ffmpeg",
+ ffmpeg_init,
+ nullptr,
+ ffmpeg_decode,
+ nullptr,
+ nullptr,
+ ffmpeg_scan_stream,
+ nullptr,
+ ffmpeg_suffixes,
+ ffmpeg_mime_types
};
diff --git a/src/decoder/FfmpegDecoderPlugin.hxx b/src/decoder/FfmpegDecoderPlugin.hxx
new file mode 100644
index 000000000..9a637fff0
--- /dev/null
+++ b/src/decoder/FfmpegDecoderPlugin.hxx
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2003-2013 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_FFMPEG_HXX
+#define MPD_DECODER_FFMPEG_HXX
+
+extern const struct decoder_plugin ffmpeg_decoder_plugin;
+
+#endif
diff --git a/src/decoder/ffmpeg_metadata.c b/src/decoder/FfmpegMetaData.cxx
index 3ef774f63..9965e4d28 100644
--- a/src/decoder/ffmpeg_metadata.c
+++ b/src/decoder/FfmpegMetaData.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,18 +17,15 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#include "config.h"
-#include "ffmpeg_metadata.h"
-#include "tag_table.h"
-#include "tag_handler.h"
+/* necessary because libavutil/common.h uses UINT64_C */
+#define __STDC_CONSTANT_MACROS
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "ffmpeg"
+#include "config.h"
+#include "FfmpegMetaData.hxx"
+#include "tag/TagTable.hxx"
+#include "tag/TagHandler.hxx"
static const struct tag_table ffmpeg_tags[] = {
-#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(52,50,0)
- { "author", TAG_ARTIST },
-#endif
{ "year", TAG_DATE },
{ "author-sort", TAG_ARTIST_SORT },
{ "album_artist", TAG_ALBUM_ARTIST },
@@ -50,8 +47,6 @@ ffmpeg_copy_metadata(enum tag_type type,
type, mt->value);
}
-#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51,5,0)
-
static void
ffmpeg_scan_pairs(AVDictionary *dict,
const struct tag_handler *handler, void *handler_ctx)
@@ -63,14 +58,12 @@ ffmpeg_scan_pairs(AVDictionary *dict,
i->key, i->value);
}
-#endif
-
void
ffmpeg_scan_dictionary(AVDictionary *dict,
const struct tag_handler *handler, void *handler_ctx)
{
for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i)
- ffmpeg_copy_metadata(i, dict, tag_item_names[i],
+ ffmpeg_copy_metadata(tag_type(i), dict, tag_item_names[i],
handler, handler_ctx);
for (const struct tag_table *i = ffmpeg_tags;
@@ -78,8 +71,6 @@ ffmpeg_scan_dictionary(AVDictionary *dict,
ffmpeg_copy_metadata(i->type, dict, i->name,
handler, handler_ctx);
-#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51,5,0)
if (handler->pair != NULL)
ffmpeg_scan_pairs(dict, handler, handler_ctx);
-#endif
}
diff --git a/src/decoder/ffmpeg_metadata.h b/src/decoder/FfmpegMetaData.hxx
index 60658f479..0fd73df04 100644
--- a/src/decoder/ffmpeg_metadata.h
+++ b/src/decoder/FfmpegMetaData.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,19 +17,18 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_FFMPEG_METADATA_H
-#define MPD_FFMPEG_METADATA_H
+#ifndef MPD_FFMPEG_METADATA_HXX
+#define MPD_FFMPEG_METADATA_HXX
+extern "C" {
#include <libavformat/avformat.h>
#include <libavutil/avutil.h>
-#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51,5,0)
#include <libavutil/dict.h>
-#endif
+}
-#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53,1,0)
-#define AVDictionary AVMetadata
-#define AVDictionaryEntry AVMetadataTag
-#define av_dict_get av_metadata_get
+/* suppress the ffmpeg compatibility macro */
+#ifdef SampleFormat
+#undef SampleFormat
#endif
struct tag_handler;
diff --git a/src/decoder/_flac_common.c b/src/decoder/FlacCommon.cxx
index d7f0c4a8a..6bafeb9c2 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,60 +22,43 @@
*/
#include "config.h"
-#include "_flac_common.h"
-#include "flac_metadata.h"
-#include "flac_pcm.h"
-#include "audio_check.h"
-
-#include <glib.h>
+#include "FlacCommon.hxx"
+#include "FlacMetadata.hxx"
+#include "FlacPcm.hxx"
+#include "CheckAudioFormat.hxx"
+#include "util/Error.hxx"
+#include "util/Domain.hxx"
+#include "Log.hxx"
#include <assert.h>
-void
-flac_data_init(struct flac_data *data, struct decoder * decoder,
- struct input_stream *input_stream)
+flac_data::flac_data(struct decoder *_decoder,
+ struct input_stream *_input_stream)
+ :FlacInput(_input_stream, _decoder),
+ initialized(false), unsupported(false),
+ total_frames(0), first_frame(0), next_frame(0), position(0),
+ decoder(_decoder), input_stream(_input_stream)
{
- pcm_buffer_init(&data->buffer);
-
- data->unsupported = false;
- data->initialized = false;
- data->total_frames = 0;
- data->first_frame = 0;
- data->next_frame = 0;
-
- data->position = 0;
- data->decoder = decoder;
- data->input_stream = input_stream;
- data->tag = NULL;
-}
-
-void
-flac_data_deinit(struct flac_data *data)
-{
- pcm_buffer_deinit(&data->buffer);
-
- if (data->tag != NULL)
- tag_free(data->tag);
}
-static enum sample_format
+static SampleFormat
flac_sample_format(unsigned bits_per_sample)
{
switch (bits_per_sample) {
case 8:
- return SAMPLE_FORMAT_S8;
+ return SampleFormat::S8;
case 16:
- return SAMPLE_FORMAT_S16;
+ return SampleFormat::S16;
case 24:
- return SAMPLE_FORMAT_S24_P32;
+ return SampleFormat::S24_P32;
case 32:
- return SAMPLE_FORMAT_S32;
+ return SampleFormat::S32;
default:
- return SAMPLE_FORMAT_UNDEFINED;
+ return SampleFormat::UNDEFINED;
}
}
@@ -86,18 +69,17 @@ flac_got_stream_info(struct flac_data *data,
if (data->initialized || data->unsupported)
return;
- GError *error = NULL;
- if (!audio_format_init_checked(&data->audio_format,
+ Error error;
+ if (!audio_format_init_checked(data->audio_format,
stream_info->sample_rate,
flac_sample_format(stream_info->bits_per_sample),
- stream_info->channels, &error)) {
- g_warning("%s", error->message);
- g_error_free(error);
+ stream_info->channels, error)) {
+ LogError(error);
data->unsupported = true;
return;
}
- data->frame_size = audio_format_frame_size(&data->audio_format);
+ data->frame_size = data->audio_format.GetFrameSize();
if (data->total_frames == 0)
data->total_frames = stream_info->total_samples;
@@ -114,7 +96,6 @@ void flac_metadata_common_cb(const FLAC__StreamMetadata * block,
struct replay_gain_info rgi;
char *mixramp_start;
char *mixramp_end;
- float replay_gain_db = 0;
switch (block->type) {
case FLAC__METADATA_TYPE_STREAMINFO:
@@ -123,30 +104,20 @@ void flac_metadata_common_cb(const FLAC__StreamMetadata * block,
case FLAC__METADATA_TYPE_VORBIS_COMMENT:
if (flac_parse_replay_gain(&rgi, block))
- replay_gain_db = decoder_replay_gain(data->decoder, &rgi);
+ decoder_replay_gain(data->decoder, &rgi);
if (flac_parse_mixramp(&mixramp_start, &mixramp_end, block))
- decoder_mixramp(data->decoder, replay_gain_db,
+ decoder_mixramp(data->decoder,
mixramp_start, mixramp_end);
- if (data->tag != NULL)
- flac_vorbis_comments_to_tag(data->tag, NULL,
- &block->data.vorbis_comment);
+ flac_vorbis_comments_to_tag(data->tag,
+ &block->data.vorbis_comment);
default:
break;
}
}
-void flac_error_common_cb(const FLAC__StreamDecoderErrorStatus status,
- struct flac_data *data)
-{
- if (decoder_get_command(data->decoder) == DECODE_COMMAND_STOP)
- return;
-
- g_warning("%s", FLAC__StreamDecoderErrorStatusString[status]);
-}
-
/**
* This function attempts to call decoder_initialized() in case there
* was no STREAMINFO block. This is allowed for nonseekable streams,
@@ -160,20 +131,19 @@ flac_got_first_frame(struct flac_data *data, const FLAC__FrameHeader *header)
if (data->unsupported)
return false;
- GError *error = NULL;
- if (!audio_format_init_checked(&data->audio_format,
+ Error error;
+ if (!audio_format_init_checked(data->audio_format,
header->sample_rate,
flac_sample_format(header->bits_per_sample),
- header->channels, &error)) {
- g_warning("%s", error->message);
- g_error_free(error);
+ header->channels, error)) {
+ LogError(error);
data->unsupported = true;
return false;
}
- data->frame_size = audio_format_frame_size(&data->audio_format);
+ data->frame_size = data->audio_format.GetFrameSize();
- decoder_initialized(data->decoder, &data->audio_format,
+ decoder_initialized(data->decoder, data->audio_format,
data->input_stream->seekable,
(float)data->total_frames /
(float)data->audio_format.sample_rate);
@@ -188,7 +158,6 @@ flac_common_write(struct flac_data *data, const FLAC__Frame * frame,
const FLAC__int32 *const buf[],
FLAC__uint64 nbytes)
{
- enum decoder_command cmd;
void *buffer;
unsigned bit_rate;
@@ -196,7 +165,7 @@ flac_common_write(struct flac_data *data, const FLAC__Frame * frame,
return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
size_t buffer_size = frame->header.blocksize * data->frame_size;
- buffer = pcm_buffer_get(&data->buffer, buffer_size);
+ buffer = data->buffer.Get(buffer_size);
flac_convert(buffer, frame->header.channels,
data->audio_format.format, buf,
@@ -208,19 +177,19 @@ flac_common_write(struct flac_data *data, const FLAC__Frame * frame,
else
bit_rate = 0;
- cmd = decoder_data(data->decoder, data->input_stream,
- buffer, buffer_size,
- bit_rate);
+ auto cmd = decoder_data(data->decoder, data->input_stream,
+ buffer, buffer_size,
+ bit_rate);
data->next_frame += frame->header.blocksize;
switch (cmd) {
- case DECODE_COMMAND_NONE:
- case DECODE_COMMAND_START:
+ case DecoderCommand::NONE:
+ case DecoderCommand::START:
break;
- case DECODE_COMMAND_STOP:
+ case DecoderCommand::STOP:
return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
- case DECODE_COMMAND_SEEK:
+ case DecoderCommand::SEEK:
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}
diff --git a/src/decoder/_flac_common.h b/src/decoder/FlacCommon.hxx
index 0d90ba656..726e9de92 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,22 +21,18 @@
* 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
-#include "decoder_api.h"
-#include "pcm_buffer.h"
-
-#include <glib.h>
+#include "FlacInput.hxx"
+#include "DecoderAPI.hxx"
+#include "pcm/PcmBuffer.hxx"
#include <FLAC/stream_decoder.h>
#include <FLAC/metadata.h>
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "flac"
-
-struct flac_data {
- struct pcm_buffer buffer;
+struct flac_data : public FlacInput {
+ PcmBuffer buffer;
/**
* The size of one frame in the output buffer.
@@ -57,7 +53,7 @@ struct flac_data {
* The validated audio format of the FLAC file. This
* attribute is defined if "initialized" is true.
*/
- struct audio_format audio_format;
+ AudioFormat audio_format;
/**
* The total number of frames in this song. The decoder
@@ -78,25 +74,18 @@ struct flac_data {
FLAC__uint64 next_frame;
FLAC__uint64 position;
+
struct decoder *decoder;
struct input_stream *input_stream;
- struct tag *tag;
-};
-/* initializes a given FlacData struct */
-void
-flac_data_init(struct flac_data *data, struct decoder * decoder,
- struct input_stream *input_stream);
+ Tag tag;
-void
-flac_data_deinit(struct flac_data *data);
+ flac_data(struct decoder *decoder, struct input_stream *input_stream);
+};
void flac_metadata_common_cb(const FLAC__StreamMetadata * block,
struct flac_data *data);
-void flac_error_common_cb(FLAC__StreamDecoderErrorStatus status,
- struct flac_data *data);
-
FLAC__StreamDecoderWriteStatus
flac_common_write(struct flac_data *data, const FLAC__Frame * frame,
const FLAC__int32 *const buf[],
diff --git a/src/decoder/flac_decoder_plugin.c b/src/decoder/FlacDecoderPlugin.cxx
index fb0b3502d..7ce44febd 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"
-
-#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
-#include "_ogg_common.h"
-#endif
+#include "FlacDecoderPlugin.h"
+#include "FlacDomain.hxx"
+#include "FlacCommon.hxx"
+#include "FlacMetadata.hxx"
+#include "OggCodec.hxx"
+#include "util/Error.hxx"
+#include "Log.hxx"
#include <glib.h>
@@ -34,114 +34,10 @@
#include <sys/stat.h>
#include <sys/types.h>
-/* 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,
- void *fdata)
-{
- struct flac_data *data = fdata;
- size_t r;
-
- r = decoder_read(data->decoder, data->input_stream,
- (void *)buf, *bytes);
- *bytes = r;
-
- if (r == 0) {
- if (decoder_get_command(data->decoder) != DECODE_COMMAND_NONE ||
- input_stream_lock_eof(data->input_stream))
- return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
- else
- return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
- }
-
- return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
-}
-
-static FLAC__StreamDecoderSeekStatus
-flac_seek_cb(G_GNUC_UNUSED const FLAC__StreamDecoder *fd,
- FLAC__uint64 offset, void *fdata)
-{
- struct flac_data *data = (struct flac_data *) fdata;
-
- if (!data->input_stream->seekable)
- return FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED;
-
- if (!input_stream_lock_seek(data->input_stream, offset, SEEK_SET,
- NULL))
- return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
-
- return FLAC__STREAM_DECODER_SEEK_STATUS_OK;
-}
-
-static FLAC__StreamDecoderTellStatus
-flac_tell_cb(G_GNUC_UNUSED const FLAC__StreamDecoder *fd,
- FLAC__uint64 * offset, void *fdata)
-{
- struct flac_data *data = (struct flac_data *) fdata;
-
- if (!data->input_stream->seekable)
- return FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED;
-
- *offset = (long)(data->input_stream->offset);
-
- return FLAC__STREAM_DECODER_TELL_STATUS_OK;
-}
-
-static FLAC__StreamDecoderLengthStatus
-flac_length_cb(G_GNUC_UNUSED const FLAC__StreamDecoder *fd,
- FLAC__uint64 * length, void *fdata)
-{
- struct flac_data *data = (struct flac_data *) fdata;
-
- if (data->input_stream->size < 0)
- return FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED;
-
- *length = (size_t) (data->input_stream->size);
-
- return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
-}
-
-static FLAC__bool
-flac_eof_cb(G_GNUC_UNUSED const FLAC__StreamDecoder *fd, void *fdata)
-{
- struct flac_data *data = (struct flac_data *) fdata;
-
- return (decoder_get_command(data->decoder) != DECODE_COMMAND_NONE &&
- decoder_get_command(data->decoder) != DECODE_COMMAND_SEEK) ||
- input_stream_lock_eof(data->input_stream);
-}
-
-static void
-flac_error_cb(G_GNUC_UNUSED const FLAC__StreamDecoder *fd,
- FLAC__StreamDecoderErrorStatus status, void *fdata)
-{
- 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;
- }
+#error libFLAC is too old
+#endif
- g_warning("%s\n", FLAC__SeekableStreamDecoderStateString[state]);
-}
-#else /* FLAC_API_VERSION_CURRENT >= 7 */
static void flacPrintErroredState(FLAC__StreamDecoderState state)
{
switch (state) {
@@ -160,11 +56,10 @@ static void flacPrintErroredState(FLAC__StreamDecoderState state)
break;
}
- g_warning("%s\n", FLAC__StreamDecoderStateString[state]);
+ LogError(flac_domain, FLAC__StreamDecoderStateString[state]);
}
-#endif /* FLAC_API_VERSION_CURRENT >= 7 */
-static void flacMetadata(G_GNUC_UNUSED const FLAC__StreamDecoder * dec,
+static void flacMetadata(gcc_unused const FLAC__StreamDecoder * dec,
const FLAC__StreamMetadata * block, void *vdata)
{
flac_metadata_common_cb(block, (struct flac_data *) vdata);
@@ -195,7 +90,32 @@ 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);
+ FlacMetadataChain chain;
+ if (!chain.Read(file)) {
+ FormatDebug(flac_domain,
+ "Failed to read FLAC tags: %s",
+ chain.GetStatusString());
+ return false;
+ }
+
+ chain.Scan(handler, handler_ctx);
+ return true;
+}
+
+static bool
+flac_scan_stream(struct input_stream *is,
+ const struct tag_handler *handler, void *handler_ctx)
+{
+ FlacMetadataChain chain;
+ if (!chain.Read(is)) {
+ FormatDebug(flac_domain,
+ "Failed to read FLAC tags: %s",
+ chain.GetStatusString());
+ return false;
+ }
+
+ chain.Scan(handler, handler_ctx);
+ return true;
}
/**
@@ -205,15 +125,15 @@ static FLAC__StreamDecoder *
flac_decoder_new(void)
{
FLAC__StreamDecoder *sd = FLAC__stream_decoder_new();
- if (sd == NULL) {
- g_warning("FLAC__stream_decoder_new() failed");
- return NULL;
+ if (sd == nullptr) {
+ LogError(flac_domain,
+ "FLAC__stream_decoder_new() failed");
+ 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
+ LogDebug(flac_domain,
+ "FLAC__stream_decoder_set_metadata_respond() has failed");
return sd;
}
@@ -225,13 +145,13 @@ flac_decoder_initialize(struct flac_data *data, FLAC__StreamDecoder *sd,
data->total_frames = duration;
if (!FLAC__stream_decoder_process_until_end_of_metadata(sd)) {
- g_warning("problem reading metadata");
+ LogWarning(flac_domain, "problem reading metadata");
return false;
}
if (data->initialized) {
/* done */
- decoder_initialized(data->decoder, &data->audio_format,
+ decoder_initialized(data->decoder, data->audio_format,
data->input_stream->seekable,
(float)data->total_frames /
(float)data->audio_format.sample_rate);
@@ -254,20 +174,19 @@ flac_decoder_loop(struct flac_data *data, FLAC__StreamDecoder *flac_dec,
FLAC__uint64 t_start, FLAC__uint64 t_end)
{
struct decoder *decoder = data->decoder;
- enum decoder_command cmd;
data->first_frame = t_start;
while (true) {
- if (data->tag != NULL && !tag_is_empty(data->tag)) {
+ DecoderCommand cmd;
+ if (!data->tag.IsEmpty()) {
cmd = decoder_tag(data->decoder, data->input_stream,
- data->tag);
- tag_free(data->tag);
- data->tag = tag_new();
+ std::move(data->tag));
+ data->tag.Clear();
} else
cmd = decoder_get_command(decoder);
- if (cmd == DECODE_COMMAND_SEEK) {
+ if (cmd == DecoderCommand::SEEK) {
FLAC__uint64 seek_sample = t_start +
decoder_seek_where(decoder) *
data->audio_format.sample_rate;
@@ -279,7 +198,7 @@ flac_decoder_loop(struct flac_data *data, FLAC__StreamDecoder *flac_dec,
decoder_command_finished(decoder);
} else
decoder_seek_error(decoder);
- } else if (cmd == DECODE_COMMAND_STOP ||
+ } else if (cmd == DecoderCommand::STOP ||
FLAC__stream_decoder_get_state(flac_dec) == FLAC__STREAM_DECODER_END_OF_STREAM)
break;
@@ -288,7 +207,7 @@ flac_decoder_loop(struct flac_data *data, FLAC__StreamDecoder *flac_dec,
break;
if (!FLAC__stream_decoder_process_single(flac_dec) &&
- decoder_get_command(decoder) == DECODE_COMMAND_NONE) {
+ decoder_get_command(decoder) == DecoderCommand::NONE) {
/* a failure that was not triggered by a
decoder command */
flacPrintErroredState(FLAC__stream_decoder_get_state(flac_dec));
@@ -300,34 +219,30 @@ 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,
- flac_tell_cb,
- flac_length_cb,
- flac_eof_cb,
+ FlacInput::Read,
+ FlacInput::Seek,
+ FlacInput::Tell,
+ FlacInput::Length,
+ FlacInput::Eof,
flac_write_cb,
flacMetadata,
- flac_error_cb,
+ FlacInput::Error,
data);
-#else
- (void)flac_dec;
- (void)data;
-
- return FLAC__STREAM_DECODER_INIT_STATUS_ERROR;
-#endif
}
static FLAC__StreamDecoderInitStatus
stream_init_flac(FLAC__StreamDecoder *flac_dec, struct flac_data *data)
{
return FLAC__stream_decoder_init_stream(flac_dec,
- flac_read_cb, flac_seek_cb,
- flac_tell_cb, flac_length_cb,
- flac_eof_cb, flac_write_cb,
+ FlacInput::Read,
+ FlacInput::Seek,
+ FlacInput::Tell,
+ FlacInput::Length,
+ FlacInput::Eof,
+ flac_write_cb,
flacMetadata,
- flac_error_cb,
+ FlacInput::Error,
data);
}
@@ -345,28 +260,23 @@ flac_decode_internal(struct decoder * decoder,
bool is_ogg)
{
FLAC__StreamDecoder *flac_dec;
- 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);
- data.tag = tag_new();
+ struct flac_data data(decoder, input_stream);
FLAC__StreamDecoderInitStatus status =
stream_init(flac_dec, &data, is_ogg);
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
+ LogWarning(flac_domain,
+ FLAC__StreamDecoderInitStatusString[status]);
return;
}
if (!flac_decoder_initialize(&data, flac_dec, 0)) {
- flac_data_deinit(&data);
FLAC__stream_decoder_finish(flac_dec);
FLAC__stream_decoder_delete(flac_dec);
return;
@@ -374,8 +284,6 @@ flac_decode_internal(struct decoder * decoder,
flac_decoder_loop(&data, flac_dec, 0, 0);
- flac_data_deinit(&data);
-
FLAC__stream_decoder_finish(flac_dec);
FLAC__stream_decoder_delete(flac_dec);
}
@@ -386,101 +294,98 @@ 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)
+oggflac_init(gcc_unused const 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)
{
- FLAC__Metadata_Iterator *it;
- FLAC__StreamMetadata *block;
- FLAC__Metadata_Chain *chain = FLAC__metadata_chain_new();
-
- if (!(FLAC__metadata_chain_read_ogg(chain, file))) {
- FLAC__metadata_chain_delete(chain);
+ FlacMetadataChain chain;
+ if (!chain.ReadOgg(file)) {
+ FormatDebug(flac_domain,
+ "Failed to read OggFLAC tags: %s",
+ chain.GetStatusString());
return false;
}
- it = FLAC__metadata_iterator_new();
- FLAC__metadata_iterator_init(it, chain);
-
- do {
- if (!(block = FLAC__metadata_iterator_get_block(it)))
- break;
+ chain.Scan(handler, handler_ctx);
+ return true;
+}
- flac_scan_metadata(NULL, block,
- handler, handler_ctx);
- } while (FLAC__metadata_iterator_next(it));
- FLAC__metadata_iterator_delete(it);
+static bool
+oggflac_scan_stream(struct input_stream *is,
+ const struct tag_handler *handler, void *handler_ctx)
+{
+ FlacMetadataChain chain;
+ if (!chain.ReadOgg(is)) {
+ FormatDebug(flac_domain,
+ "Failed to read OggFLAC tags: %s",
+ chain.GetStatusString());
+ return false;
+ }
- FLAC__metadata_chain_delete(chain);
+ chain.Scan(handler, handler_ctx);
return true;
}
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->LockSeek(0, SEEK_SET, IgnoreError());
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,
+ oggflac_scan_stream,
+ 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,
+ flac_scan_stream,
+ 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/FlacDomain.cxx b/src/decoder/FlacDomain.cxx
new file mode 100644
index 000000000..2bc91079e
--- /dev/null
+++ b/src/decoder/FlacDomain.cxx
@@ -0,0 +1,24 @@
+/*
+ * 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 "FlacDomain.hxx"
+#include "util/Domain.hxx"
+
+const Domain flac_domain("flac");
diff --git a/src/decoder/FlacDomain.hxx b/src/decoder/FlacDomain.hxx
new file mode 100644
index 000000000..8d5b825ed
--- /dev/null
+++ b/src/decoder/FlacDomain.hxx
@@ -0,0 +1,27 @@
+/*
+ * 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_FLAC_DOMAIN_HXX
+#define MPD_FLAC_DOMAIN_HXX
+
+#include "check.h"
+
+extern const class Domain flac_domain;
+
+#endif
diff --git a/src/decoder/FlacIOHandle.cxx b/src/decoder/FlacIOHandle.cxx
new file mode 100644
index 000000000..77da864e5
--- /dev/null
+++ b/src/decoder/FlacIOHandle.cxx
@@ -0,0 +1,114 @@
+/*
+ * 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 "FlacIOHandle.hxx"
+#include "util/Error.hxx"
+#include "gcc.h"
+
+#include <errno.h>
+
+static size_t
+FlacIORead(void *ptr, size_t size, size_t nmemb, FLAC__IOHandle handle)
+{
+ input_stream *is = (input_stream *)handle;
+
+ uint8_t *const p0 = (uint8_t *)ptr, *p = p0,
+ *const end = p0 + size * nmemb;
+
+ /* libFLAC is very picky about short reads, and expects the IO
+ callback to fill the whole buffer (undocumented!) */
+
+ Error error;
+ while (p < end) {
+ size_t nbytes = is->LockRead(p, end - p, error);
+ if (nbytes == 0) {
+ if (!error.IsDefined())
+ /* end of file */
+ break;
+
+ if (error.IsDomain(errno_domain))
+ errno = error.GetCode();
+ else
+ /* just some random non-zero
+ errno value */
+ errno = EINVAL;
+ return 0;
+ }
+
+ p += nbytes;
+ }
+
+ /* libFLAC expects a clean errno after returning from the IO
+ callbacks (undocumented!) */
+ errno = 0;
+ return (p - p0) / size;
+}
+
+static int
+FlacIOSeek(FLAC__IOHandle handle, FLAC__int64 offset, int whence)
+{
+ input_stream *is = (input_stream *)handle;
+
+ Error error;
+ return is->LockSeek(offset, whence, error) ? 0 : -1;
+}
+
+static FLAC__int64
+FlacIOTell(FLAC__IOHandle handle)
+{
+ input_stream *is = (input_stream *)handle;
+
+ return is->offset;
+}
+
+static int
+FlacIOEof(FLAC__IOHandle handle)
+{
+ input_stream *is = (input_stream *)handle;
+
+ return is->LockIsEOF();
+}
+
+static int
+FlacIOClose(gcc_unused FLAC__IOHandle handle)
+{
+ /* no-op because the libFLAC caller is repsonsible for closing
+ the #input_stream */
+
+ return 0;
+}
+
+const FLAC__IOCallbacks flac_io_callbacks = {
+ FlacIORead,
+ nullptr,
+ nullptr,
+ nullptr,
+ FlacIOEof,
+ FlacIOClose,
+};
+
+const FLAC__IOCallbacks flac_io_callbacks_seekable = {
+ FlacIORead,
+ nullptr,
+ FlacIOSeek,
+ FlacIOTell,
+ FlacIOEof,
+ FlacIOClose,
+};
diff --git a/src/decoder/FlacIOHandle.hxx b/src/decoder/FlacIOHandle.hxx
new file mode 100644
index 000000000..3216dafa4
--- /dev/null
+++ b/src/decoder/FlacIOHandle.hxx
@@ -0,0 +1,45 @@
+/*
+ * 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_FLAC_IO_HANDLE_HXX
+#define MPD_FLAC_IO_HANDLE_HXX
+
+#include "gcc.h"
+#include "InputStream.hxx"
+
+#include <FLAC/callback.h>
+
+extern const FLAC__IOCallbacks flac_io_callbacks;
+extern const FLAC__IOCallbacks flac_io_callbacks_seekable;
+
+static inline FLAC__IOHandle
+ToFlacIOHandle(input_stream *is)
+{
+ return (FLAC__IOHandle)is;
+}
+
+static inline const FLAC__IOCallbacks &
+GetFlacIOCallbacks(const input_stream *is)
+{
+ return is->seekable
+ ? flac_io_callbacks_seekable
+ : flac_io_callbacks;
+}
+
+#endif
diff --git a/src/decoder/FlacInput.cxx b/src/decoder/FlacInput.cxx
new file mode 100644
index 000000000..88b942971
--- /dev/null
+++ b/src/decoder/FlacInput.cxx
@@ -0,0 +1,154 @@
+/*
+ * 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 "FlacInput.hxx"
+#include "FlacDomain.hxx"
+#include "DecoderAPI.hxx"
+#include "InputStream.hxx"
+#include "util/Error.hxx"
+#include "Log.hxx"
+#include "gcc.h"
+
+FLAC__StreamDecoderReadStatus
+FlacInput::Read(FLAC__byte buffer[], size_t *bytes)
+{
+ size_t r = decoder_read(decoder, input_stream, (void *)buffer, *bytes);
+ *bytes = r;
+
+ if (r == 0) {
+ if (input_stream->LockIsEOF() ||
+ (decoder != nullptr &&
+ decoder_get_command(decoder) != DecoderCommand::NONE))
+ return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
+ else
+ return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
+ }
+
+ return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
+}
+
+FLAC__StreamDecoderSeekStatus
+FlacInput::Seek(FLAC__uint64 absolute_byte_offset)
+{
+ if (!input_stream->seekable)
+ return FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED;
+
+ ::Error error;
+ if (!input_stream->LockSeek(absolute_byte_offset, SEEK_SET, error)) {
+ LogError(error);
+ return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
+ }
+
+ return FLAC__STREAM_DECODER_SEEK_STATUS_OK;
+}
+
+FLAC__StreamDecoderTellStatus
+FlacInput::Tell(FLAC__uint64 *absolute_byte_offset)
+{
+ if (!input_stream->seekable)
+ return FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED;
+
+ *absolute_byte_offset = (FLAC__uint64)input_stream->offset;
+ return FLAC__STREAM_DECODER_TELL_STATUS_OK;
+}
+
+FLAC__StreamDecoderLengthStatus
+FlacInput::Length(FLAC__uint64 *stream_length)
+{
+ if (input_stream->size < 0)
+ return FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED;
+
+ *stream_length = (FLAC__uint64)input_stream->size;
+ return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
+}
+
+FLAC__bool
+FlacInput::Eof()
+{
+ return (decoder != nullptr &&
+ decoder_get_command(decoder) != DecoderCommand::NONE &&
+ decoder_get_command(decoder) != DecoderCommand::SEEK) ||
+ input_stream->LockIsEOF();
+}
+
+void
+FlacInput::Error(FLAC__StreamDecoderErrorStatus status)
+{
+ if (decoder == nullptr ||
+ decoder_get_command(decoder) != DecoderCommand::STOP)
+ LogWarning(flac_domain,
+ FLAC__StreamDecoderErrorStatusString[status]);
+}
+
+FLAC__StreamDecoderReadStatus
+FlacInput::Read(gcc_unused const FLAC__StreamDecoder *flac_decoder,
+ FLAC__byte buffer[], size_t *bytes,
+ void *client_data)
+{
+ FlacInput *i = (FlacInput *)client_data;
+
+ return i->Read(buffer, bytes);
+}
+
+FLAC__StreamDecoderSeekStatus
+FlacInput::Seek(gcc_unused const FLAC__StreamDecoder *flac_decoder,
+ FLAC__uint64 absolute_byte_offset, void *client_data)
+{
+ FlacInput *i = (FlacInput *)client_data;
+
+ return i->Seek(absolute_byte_offset);
+}
+
+FLAC__StreamDecoderTellStatus
+FlacInput::Tell(gcc_unused const FLAC__StreamDecoder *flac_decoder,
+ FLAC__uint64 *absolute_byte_offset, void *client_data)
+{
+ FlacInput *i = (FlacInput *)client_data;
+
+ return i->Tell(absolute_byte_offset);
+}
+
+FLAC__StreamDecoderLengthStatus
+FlacInput::Length(gcc_unused const FLAC__StreamDecoder *flac_decoder,
+ FLAC__uint64 *stream_length, void *client_data)
+{
+ FlacInput *i = (FlacInput *)client_data;
+
+ return i->Length(stream_length);
+}
+
+FLAC__bool
+FlacInput::Eof(gcc_unused const FLAC__StreamDecoder *flac_decoder,
+ void *client_data)
+{
+ FlacInput *i = (FlacInput *)client_data;
+
+ return i->Eof();
+}
+
+void
+FlacInput::Error(gcc_unused const FLAC__StreamDecoder *decoder,
+ FLAC__StreamDecoderErrorStatus status, void *client_data)
+{
+ FlacInput *i = (FlacInput *)client_data;
+
+ i->Error(status);
+}
+
diff --git a/src/decoder/FlacInput.hxx b/src/decoder/FlacInput.hxx
new file mode 100644
index 000000000..8fc69f960
--- /dev/null
+++ b/src/decoder/FlacInput.hxx
@@ -0,0 +1,72 @@
+/*
+ * 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_FLAC_INPUT_HXX
+#define MPD_FLAC_INPUT_HXX
+
+#include <FLAC/stream_decoder.h>
+
+/**
+ * This class wraps an #input_stream in libFLAC stream decoder
+ * callbacks.
+ */
+class FlacInput {
+ struct decoder *decoder;
+
+ struct input_stream *input_stream;
+
+public:
+ FlacInput(struct input_stream *_input_stream,
+ struct decoder *_decoder=nullptr)
+ :decoder(_decoder), input_stream(_input_stream) {}
+
+protected:
+ FLAC__StreamDecoderReadStatus Read(FLAC__byte buffer[], size_t *bytes);
+ FLAC__StreamDecoderSeekStatus Seek(FLAC__uint64 absolute_byte_offset);
+ FLAC__StreamDecoderTellStatus Tell(FLAC__uint64 *absolute_byte_offset);
+ FLAC__StreamDecoderLengthStatus Length(FLAC__uint64 *stream_length);
+ FLAC__bool Eof();
+ void Error(FLAC__StreamDecoderErrorStatus status);
+
+public:
+ static FLAC__StreamDecoderReadStatus
+ Read(const FLAC__StreamDecoder *flac_decoder,
+ FLAC__byte buffer[], size_t *bytes, void *client_data);
+
+ static FLAC__StreamDecoderSeekStatus
+ Seek(const FLAC__StreamDecoder *flac_decoder,
+ FLAC__uint64 absolute_byte_offset, void *client_data);
+
+ static FLAC__StreamDecoderTellStatus
+ Tell(const FLAC__StreamDecoder *flac_decoder,
+ FLAC__uint64 *absolute_byte_offset, void *client_data);
+
+ static FLAC__StreamDecoderLengthStatus
+ Length(const FLAC__StreamDecoder *flac_decoder,
+ FLAC__uint64 *stream_length, void *client_data);
+
+ static FLAC__bool
+ Eof(const FLAC__StreamDecoder *flac_decoder, void *client_data);
+
+ static void
+ Error(const FLAC__StreamDecoder *decoder,
+ FLAC__StreamDecoderErrorStatus status, void *client_data);
+};
+
+#endif
diff --git a/src/decoder/flac_metadata.c b/src/decoder/FlacMetadata.cxx
index bd1eaf323..078d0c081 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-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,17 +18,18 @@
*/
#include "config.h"
-#include "flac_metadata.h"
-#include "replay_gain_info.h"
-#include "tag.h"
-#include "tag_handler.h"
-#include "tag_table.h"
+#include "FlacMetadata.hxx"
+#include "XiphTags.hxx"
+#include "tag/Tag.hxx"
+#include "tag/TagHandler.hxx"
+#include "tag/TagTable.hxx"
+#include "tag/TagBuilder.hxx"
+#include "ReplayGainInfo.hxx"
#include <glib.h>
#include <assert.h>
-#include <stdbool.h>
-#include <stdlib.h>
+#include <string.h>
static bool
flac_find_float_comment(const FLAC__StreamMetadata *block,
@@ -91,7 +92,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)
@@ -128,36 +129,21 @@ flac_parse_mixramp(char **mixramp_start, char **mixramp_end,
*/
static const char *
flac_comment_value(const FLAC__StreamMetadata_VorbisComment_Entry *entry,
- const char *name, const char *char_tnum, size_t *length_r)
+ const char *name, size_t *length_r)
{
size_t name_length = strlen(name);
- size_t char_tnum_length = 0;
const char *comment = (const char*)entry->entry;
if (entry->length <= name_length ||
g_ascii_strncasecmp(comment, name, name_length) != 0)
- return NULL;
-
- if (char_tnum != NULL) {
- char_tnum_length = strlen(char_tnum);
- if (entry->length > name_length + char_tnum_length + 2 &&
- comment[name_length] == '[' &&
- g_ascii_strncasecmp(comment + name_length + 1,
- char_tnum, char_tnum_length) == 0 &&
- comment[name_length + char_tnum_length + 1] == ']')
- name_length = name_length + char_tnum_length + 2;
- else if (entry->length > name_length + char_tnum_length &&
- g_ascii_strncasecmp(comment + name_length,
- char_tnum, char_tnum_length) == 0)
- name_length = name_length + char_tnum_length;
- }
+ return nullptr;
if (comment[name_length] == '=') {
*length_r = entry->length - name_length - 1;
return comment + name_length + 1;
}
- return NULL;
+ return nullptr;
}
/**
@@ -167,14 +153,13 @@ flac_comment_value(const FLAC__StreamMetadata_VorbisComment_Entry *entry,
static bool
flac_copy_comment(const FLAC__StreamMetadata_VorbisComment_Entry *entry,
const char *name, enum tag_type tag_type,
- const char *char_tnum,
const struct tag_handler *handler, void *handler_ctx)
{
const char *value;
size_t value_length;
- value = flac_comment_value(entry, name, char_tnum, &value_length);
- if (value != NULL) {
+ value = flac_comment_value(entry, name, &value_length);
+ 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 +169,15 @@ 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,
+flac_scan_comment(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,36 +186,34 @@ flac_scan_comment(const char *char_tnum,
g_free(name);
}
- for (const struct tag_table *i = flac_tags; i->name != NULL; ++i)
- if (flac_copy_comment(entry, i->name, i->type, char_tnum,
+ for (const struct tag_table *i = xiph_tags; i->name != nullptr; ++i)
+ if (flac_copy_comment(entry, i->name, i->type,
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,
handler, handler_ctx))
return;
}
static void
-flac_scan_comments(const char *char_tnum,
- const FLAC__StreamMetadata_VorbisComment *comment,
+flac_scan_comments(const FLAC__StreamMetadata_VorbisComment *comment,
const struct tag_handler *handler, void *handler_ctx)
{
for (unsigned i = 0; i < comment->num_comments; ++i)
- flac_scan_comment(char_tnum, &comment->comments[i],
+ flac_scan_comment(&comment->comments[i],
handler, handler_ctx);
}
void
-flac_scan_metadata(const char *track,
- const FLAC__StreamMetadata *block,
+flac_scan_metadata(const FLAC__StreamMetadata *block,
const struct tag_handler *handler, void *handler_ctx)
{
switch (block->type) {
case FLAC__METADATA_TYPE_VORBIS_COMMENT:
- flac_scan_comments(track, &block->data.vorbis_comment,
+ flac_scan_comments(&block->data.vorbis_comment,
handler, handler_ctx);
break;
@@ -254,70 +229,24 @@ flac_scan_metadata(const char *track,
}
void
-flac_vorbis_comments_to_tag(struct tag *tag, const char *char_tnum,
+flac_vorbis_comments_to_tag(Tag &tag,
const FLAC__StreamMetadata_VorbisComment *comment)
{
- flac_scan_comments(char_tnum, comment,
- &add_tag_handler, tag);
+ TagBuilder tag_builder;
+ flac_scan_comments(comment, &add_tag_handler, &tag_builder);
+ tag_builder.Commit(tag);
}
-bool
-flac_scan_file2(const char *file, const char *char_tnum,
- const struct tag_handler *handler, void *handler_ctx)
+void
+FlacMetadataChain::Scan(const struct tag_handler *handler, void *handler_ctx)
{
- FLAC__Metadata_SimpleIterator *it;
- FLAC__StreamMetadata *block = NULL;
-
- it = FLAC__metadata_simple_iterator_new();
- if (!FLAC__metadata_simple_iterator_init(it, file, 1, 0)) {
- const char *err;
- FLAC_API FLAC__Metadata_SimpleIteratorStatus s;
-
- s = FLAC__metadata_simple_iterator_status(it);
-
- switch (s) { /* slightly more human-friendly messages: */
- case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT:
- err = "illegal input";
- break;
- case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ERROR_OPENING_FILE:
- err = "error opening file";
- break;
- case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_A_FLAC_FILE:
- err = "not a FLAC file";
- break;
- default:
- err = FLAC__Metadata_SimpleIteratorStatusString[s];
- }
- g_debug("Reading '%s' metadata gave the following error: %s\n",
- file, err);
- FLAC__metadata_simple_iterator_delete(it);
- return false;
- }
+ FLACMetadataIterator iterator(*this);
do {
- block = FLAC__metadata_simple_iterator_get_block(it);
- if (!block)
+ FLAC__StreamMetadata *block = iterator.GetBlock();
+ if (block == nullptr)
break;
- flac_scan_metadata(char_tnum, block, handler, handler_ctx);
- FLAC__metadata_object_delete(block);
- } while (FLAC__metadata_simple_iterator_next(it));
-
- FLAC__metadata_simple_iterator_delete(it);
-
- 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;
+ flac_scan_metadata(block, handler, handler_ctx);
+ } while (iterator.Next());
}
diff --git a/src/decoder/FlacMetadata.hxx b/src/decoder/FlacMetadata.hxx
new file mode 100644
index 000000000..57769672f
--- /dev/null
+++ b/src/decoder/FlacMetadata.hxx
@@ -0,0 +1,140 @@
+/*
+ * 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_FLAC_METADATA_H
+#define MPD_FLAC_METADATA_H
+
+#include "gcc.h"
+#include "FlacIOHandle.hxx"
+
+#include <FLAC/metadata.h>
+
+#include <assert.h>
+
+class FlacMetadataChain {
+ FLAC__Metadata_Chain *chain;
+
+public:
+ FlacMetadataChain():chain(::FLAC__metadata_chain_new()) {}
+
+ ~FlacMetadataChain() {
+ ::FLAC__metadata_chain_delete(chain);
+ }
+
+ explicit operator FLAC__Metadata_Chain *() {
+ return chain;
+ }
+
+ bool Read(const char *path) {
+ return ::FLAC__metadata_chain_read(chain, path);
+ }
+
+ bool Read(FLAC__IOHandle handle, FLAC__IOCallbacks callbacks) {
+ return ::FLAC__metadata_chain_read_with_callbacks(chain,
+ handle,
+ callbacks);
+ }
+
+ bool Read(input_stream *is) {
+ return Read(::ToFlacIOHandle(is), ::GetFlacIOCallbacks(is));
+ }
+
+ bool ReadOgg(const char *path) {
+ return ::FLAC__metadata_chain_read_ogg(chain, path);
+ }
+
+ bool ReadOgg(FLAC__IOHandle handle, FLAC__IOCallbacks callbacks) {
+ return ::FLAC__metadata_chain_read_ogg_with_callbacks(chain,
+ handle,
+ callbacks);
+ }
+
+ bool ReadOgg(input_stream *is) {
+ return ReadOgg(::ToFlacIOHandle(is), ::GetFlacIOCallbacks(is));
+ }
+
+ gcc_pure
+ FLAC__Metadata_ChainStatus GetStatus() const {
+ return ::FLAC__metadata_chain_status(chain);
+ }
+
+ gcc_pure
+ const char *GetStatusString() const {
+ return FLAC__Metadata_ChainStatusString[GetStatus()];
+ }
+
+ void Scan(const struct tag_handler *handler, void *handler_ctx);
+};
+
+class FLACMetadataIterator {
+ FLAC__Metadata_Iterator *iterator;
+
+public:
+ FLACMetadataIterator():iterator(::FLAC__metadata_iterator_new()) {}
+
+ FLACMetadataIterator(FlacMetadataChain &chain)
+ :iterator(::FLAC__metadata_iterator_new()) {
+ ::FLAC__metadata_iterator_init(iterator,
+ (FLAC__Metadata_Chain *)chain);
+ }
+
+ ~FLACMetadataIterator() {
+ ::FLAC__metadata_iterator_delete(iterator);
+ }
+
+ bool Next() {
+ return ::FLAC__metadata_iterator_next(iterator);
+ }
+
+ gcc_pure
+ FLAC__StreamMetadata *GetBlock() {
+ return ::FLAC__metadata_iterator_get_block(iterator);
+ }
+};
+
+struct tag_handler;
+struct Tag;
+struct replay_gain_info;
+
+static inline unsigned
+flac_duration(const FLAC__StreamMetadata_StreamInfo *stream_info)
+{
+ assert(stream_info->sample_rate > 0);
+
+ return (stream_info->total_samples + stream_info->sample_rate - 1) /
+ stream_info->sample_rate;
+}
+
+bool
+flac_parse_replay_gain(struct replay_gain_info *rgi,
+ const FLAC__StreamMetadata *block);
+
+bool
+flac_parse_mixramp(char **mixramp_start, char **mixramp_end,
+ const FLAC__StreamMetadata *block);
+
+void
+flac_vorbis_comments_to_tag(Tag &tag,
+ const FLAC__StreamMetadata_VorbisComment *comment);
+
+void
+flac_scan_metadata(const FLAC__StreamMetadata *block,
+ const struct tag_handler *handler, void *handler_ctx);
+
+#endif
diff --git a/src/decoder/flac_pcm.c b/src/decoder/FlacPcm.cxx
index 6964d8ac6..ff855fa70 100644
--- a/src/decoder/flac_pcm.c
+++ b/src/decoder/FlacPcm.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 "FlacPcm.hxx"
#include <assert.h>
@@ -76,12 +76,12 @@ flac_convert_8(int8_t *dest,
void
flac_convert(void *dest,
- unsigned int num_channels, enum sample_format sample_format,
+ unsigned int num_channels, SampleFormat sample_format,
const FLAC__int32 *const buf[],
unsigned int position, unsigned int end)
{
switch (sample_format) {
- case SAMPLE_FORMAT_S16:
+ case SampleFormat::S16:
if (num_channels == 2)
flac_convert_stereo16((int16_t*)dest, buf,
position, end);
@@ -90,21 +90,21 @@ flac_convert(void *dest,
position, end);
break;
- case SAMPLE_FORMAT_S24_P32:
- case SAMPLE_FORMAT_S32:
+ case SampleFormat::S24_P32:
+ case SampleFormat::S32:
flac_convert_32((int32_t*)dest, num_channels, buf,
position, end);
break;
- case SAMPLE_FORMAT_S8:
+ case SampleFormat::S8:
flac_convert_8((int8_t*)dest, num_channels, buf,
position, end);
break;
- case SAMPLE_FORMAT_FLOAT:
- case SAMPLE_FORMAT_DSD:
- case SAMPLE_FORMAT_UNDEFINED:
- /* unreachable */
+ case SampleFormat::FLOAT:
+ case SampleFormat::DSD:
+ case SampleFormat::UNDEFINED:
assert(false);
+ gcc_unreachable();
}
}
diff --git a/src/decoder/flac_pcm.h b/src/decoder/FlacPcm.hxx
index a931998c1..fa85f65dd 100644
--- a/src/decoder/flac_pcm.h
+++ b/src/decoder/FlacPcm.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,16 +17,16 @@
* 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"
+#include "AudioFormat.hxx"
#include <FLAC/ordinals.h>
void
flac_convert(void *dest,
- unsigned int num_channels, enum sample_format sample_format,
+ unsigned int num_channels, SampleFormat sample_format,
const FLAC__int32 *const buf[],
unsigned int position, unsigned int end);
diff --git a/src/decoder/fluidsynth_decoder_plugin.c b/src/decoder/FluidsynthDecoderPlugin.cxx
index 894b2d353..99f1598c8 100644
--- a/src/decoder/fluidsynth_decoder_plugin.c
+++ b/src/decoder/FluidsynthDecoderPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,16 +18,18 @@
*/
#include "config.h"
-#include "decoder_api.h"
-#include "audio_check.h"
-#include "conf.h"
+#include "FluidsynthDecoderPlugin.hxx"
+#include "DecoderAPI.hxx"
+#include "CheckAudioFormat.hxx"
+#include "util/Error.hxx"
+#include "util/Domain.hxx"
+#include "Log.hxx"
#include <glib.h>
#include <fluidsynth.h>
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "fluidsynth"
+static constexpr Domain fluidsynth_domain("fluidsynth");
static unsigned sample_rate;
static const char *soundfont_path;
@@ -35,27 +37,27 @@ static const char *soundfont_path;
/**
* Convert a fluidsynth log level to a GLib log level.
*/
-static GLogLevelFlags
-fluidsynth_level_to_glib(enum fluid_log_level level)
+static LogLevel
+fluidsynth_level_to_mpd(enum fluid_log_level level)
{
switch (level) {
case FLUID_PANIC:
case FLUID_ERR:
- return G_LOG_LEVEL_CRITICAL;
+ return LogLevel::ERROR;
case FLUID_WARN:
- return G_LOG_LEVEL_WARNING;
+ return LogLevel::WARNING;
case FLUID_INFO:
- return G_LOG_LEVEL_INFO;
+ return LogLevel::INFO;
case FLUID_DBG:
case LAST_LOG_LEVEL:
- return G_LOG_LEVEL_DEBUG;
+ return LogLevel::DEBUG;
}
/* invalid fluidsynth log level */
- return G_LOG_LEVEL_MESSAGE;
+ return LogLevel::INFO;
}
/**
@@ -63,29 +65,29 @@ fluidsynth_level_to_glib(enum fluid_log_level level)
* logging library.
*/
static void
-fluidsynth_mpd_log_function(int level, char *message, G_GNUC_UNUSED void *data)
+fluidsynth_mpd_log_function(int level, char *message, gcc_unused void *data)
{
- g_log(G_LOG_DOMAIN, fluidsynth_level_to_glib(level), "%s", message);
+ Log(fluidsynth_domain,
+ fluidsynth_level_to_mpd(fluid_log_level(level)),
+ message);
}
static bool
-fluidsynth_init(const struct config_param *param)
+fluidsynth_init(const config_param &param)
{
- GError *error = NULL;
+ Error error;
- 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);
+ sample_rate = param.GetBlockValue("sample_rate", 48000u);
+ if (!audio_check_sample_rate(sample_rate, error)) {
+ LogError(error);
return false;
}
- soundfont_path =
- config_get_block_string(param, "soundfont",
- "/usr/share/sounds/sf2/FluidR3_GM.sf2");
+ soundfont_path = param.GetBlockValue("soundfont",
+ "/usr/share/sounds/sf2/FluidR3_GM.sf2");
fluid_set_log_function(LAST_LOG_LEVEL,
- fluidsynth_mpd_log_function, NULL);
+ fluidsynth_mpd_log_function, nullptr);
return true;
}
@@ -102,12 +104,11 @@ fluidsynth_file_decode(struct decoder *decoder, const char *path_fs)
fluid_synth_t *synth;
fluid_player_t *player;
int ret;
- enum decoder_command cmd;
/* set up fluid settings */
settings = new_fluid_settings();
- if (settings == NULL)
+ if (settings == nullptr)
return;
fluid_settings_setnum(settings, setting_sample_rate, sample_rate);
@@ -119,14 +120,14 @@ fluidsynth_file_decode(struct decoder *decoder, const char *path_fs)
/* create the fluid synth */
synth = new_fluid_synth(settings);
- if (synth == NULL) {
+ if (synth == nullptr) {
delete_fluid_settings(settings);
return;
}
ret = fluid_synth_sfload(synth, soundfont_path, true);
if (ret < 0) {
- g_warning("fluid_synth_sfload() failed");
+ LogWarning(fluidsynth_domain, "fluid_synth_sfload() failed");
delete_fluid_synth(synth);
delete_fluid_settings(settings);
return;
@@ -135,7 +136,7 @@ fluidsynth_file_decode(struct decoder *decoder, const char *path_fs)
/* create the fluid player */
player = new_fluid_player(synth);
- if (player == NULL) {
+ if (player == nullptr) {
delete_fluid_synth(synth);
delete_fluid_settings(settings);
return;
@@ -143,7 +144,7 @@ fluidsynth_file_decode(struct decoder *decoder, const char *path_fs)
ret = fluid_player_add(player, path_fs);
if (ret != 0) {
- g_warning("fluid_player_add() failed");
+ LogWarning(fluidsynth_domain, "fluid_player_add() failed");
delete_fluid_player(player);
delete_fluid_synth(synth);
delete_fluid_settings(settings);
@@ -154,7 +155,7 @@ fluidsynth_file_decode(struct decoder *decoder, const char *path_fs)
ret = fluid_player_play(player);
if (ret != 0) {
- g_warning("fluid_player_play() failed");
+ LogWarning(fluidsynth_domain, "fluid_player_play() failed");
delete_fluid_player(player);
delete_fluid_synth(synth);
delete_fluid_settings(settings);
@@ -164,10 +165,10 @@ fluidsynth_file_decode(struct decoder *decoder, const char *path_fs)
/* initialization complete - announce the audio format to the
MPD core */
- struct audio_format audio_format;
- audio_format_init(&audio_format, sample_rate, SAMPLE_FORMAT_S16, 2);
- decoder_initialized(decoder, &audio_format, false, -1);
+ const AudioFormat audio_format(sample_rate, SampleFormat::S16, 2);
+ decoder_initialized(decoder, audio_format, false, -1);
+ DecoderCommand cmd;
while (fluid_player_get_status(player) == FLUID_PLAYER_PLAYING) {
int16_t buffer[2048];
const unsigned max_frames = G_N_ELEMENTS(buffer) / 2;
@@ -181,9 +182,9 @@ fluidsynth_file_decode(struct decoder *decoder, const char *path_fs)
if (ret != 0)
break;
- cmd = decoder_data(decoder, NULL, buffer, sizeof(buffer),
+ cmd = decoder_data(decoder, nullptr, buffer, sizeof(buffer),
0);
- if (cmd != DECODE_COMMAND_NONE)
+ if (cmd != DecoderCommand::NONE)
break;
}
@@ -199,21 +200,26 @@ fluidsynth_file_decode(struct decoder *decoder, const char *path_fs)
static bool
fluidsynth_scan_file(const char *file,
- G_GNUC_UNUSED const struct tag_handler *handler,
- G_GNUC_UNUSED void *handler_ctx)
+ gcc_unused const struct tag_handler *handler,
+ gcc_unused void *handler_ctx)
{
return fluid_is_midifile(file);
}
static const char *const fluidsynth_suffixes[] = {
"mid",
- NULL
+ nullptr
};
const struct decoder_plugin fluidsynth_decoder_plugin = {
- .name = "fluidsynth",
- .init = fluidsynth_init,
- .file_decode = fluidsynth_file_decode,
- .scan_file = fluidsynth_scan_file,
- .suffixes = fluidsynth_suffixes,
+ "fluidsynth",
+ fluidsynth_init,
+ nullptr,
+ nullptr,
+ fluidsynth_file_decode,
+ fluidsynth_scan_file,
+ nullptr,
+ nullptr,
+ fluidsynth_suffixes,
+ nullptr,
};
diff --git a/src/decoder/FluidsynthDecoderPlugin.hxx b/src/decoder/FluidsynthDecoderPlugin.hxx
new file mode 100644
index 000000000..40ed7e4d8
--- /dev/null
+++ b/src/decoder/FluidsynthDecoderPlugin.hxx
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2003-2013 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_FLUIDSYNTH_HXX
+#define MPD_DECODER_FLUIDSYNTH_HXX
+
+extern const struct decoder_plugin fluidsynth_decoder_plugin;
+
+#endif
diff --git a/src/decoder/GmeDecoderPlugin.cxx b/src/decoder/GmeDecoderPlugin.cxx
new file mode 100644
index 000000000..bbcc9618a
--- /dev/null
+++ b/src/decoder/GmeDecoderPlugin.cxx
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2003-2013 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 "GmeDecoderPlugin.hxx"
+#include "DecoderAPI.hxx"
+#include "CheckAudioFormat.hxx"
+#include "tag/TagHandler.hxx"
+#include "util/UriUtil.hxx"
+#include "util/Error.hxx"
+#include "util/Domain.hxx"
+#include "Log.hxx"
+
+#include <glib.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <gme/gme.h>
+
+#define SUBTUNE_PREFIX "tune_"
+
+static constexpr Domain gme_domain("gme");
+
+static constexpr unsigned GME_SAMPLE_RATE = 44100;
+static constexpr unsigned GME_CHANNELS = 2;
+static constexpr unsigned GME_BUFFER_FRAMES = 2048;
+static constexpr unsigned GME_BUFFER_SAMPLES =
+ GME_BUFFER_FRAMES * GME_CHANNELS;
+
+/**
+ * returns the file path stripped of any /tune_xxx.* subtune
+ * suffix
+ */
+static char *
+get_container_name(const char *path_fs)
+{
+ const char *subtune_suffix = uri_get_suffix(path_fs);
+ char *path_container = g_strdup(path_fs);
+ char *pat = g_strconcat("*/" SUBTUNE_PREFIX "???.",
+ subtune_suffix, nullptr);
+ GPatternSpec *path_with_subtune = g_pattern_spec_new(pat);
+ g_free(pat);
+ if (!g_pattern_match(path_with_subtune,
+ strlen(path_container), path_container, nullptr)) {
+ g_pattern_spec_free(path_with_subtune);
+ return path_container;
+ }
+
+ char *ptr = g_strrstr(path_container, "/" SUBTUNE_PREFIX);
+ if (ptr != nullptr)
+ *ptr='\0';
+
+ g_pattern_spec_free(path_with_subtune);
+ return path_container;
+}
+
+/**
+ * returns tune number from file.nsf/tune_xxx.* style path or 0 if no subtune
+ * is appended.
+ */
+static int
+get_song_num(const char *path_fs)
+{
+ const char *subtune_suffix = uri_get_suffix(path_fs);
+ char *pat = g_strconcat("*/" SUBTUNE_PREFIX "???.",
+ subtune_suffix, nullptr);
+ GPatternSpec *path_with_subtune = g_pattern_spec_new(pat);
+ g_free(pat);
+
+ if (g_pattern_match(path_with_subtune,
+ strlen(path_fs), path_fs, nullptr)) {
+ char *sub = g_strrstr(path_fs, "/" SUBTUNE_PREFIX);
+ g_pattern_spec_free(path_with_subtune);
+ if (!sub)
+ return 0;
+
+ sub += strlen("/" SUBTUNE_PREFIX);
+ int song_num = strtol(sub, nullptr, 10);
+
+ return song_num - 1;
+ } else {
+ g_pattern_spec_free(path_with_subtune);
+ return 0;
+ }
+}
+
+static char *
+gme_container_scan(const char *path_fs, const unsigned int tnum)
+{
+ Music_Emu *emu;
+ const char *gme_err = gme_open_file(path_fs, &emu, GME_SAMPLE_RATE);
+ if (gme_err != nullptr) {
+ LogWarning(gme_domain, gme_err);
+ return nullptr;
+ }
+
+ const unsigned num_songs = gme_track_count(emu);
+ /* if it only contains a single tune, don't treat as container */
+ if (num_songs < 2)
+ return nullptr;
+
+ const char *subtune_suffix = uri_get_suffix(path_fs);
+ if (tnum <= num_songs){
+ char *subtune = g_strdup_printf(
+ SUBTUNE_PREFIX "%03u.%s", tnum, subtune_suffix);
+ return subtune;
+ } else
+ return nullptr;
+}
+
+static void
+gme_file_decode(struct decoder *decoder, const char *path_fs)
+{
+ char *path_container = get_container_name(path_fs);
+
+ Music_Emu *emu;
+ const char *gme_err =
+ gme_open_file(path_container, &emu, GME_SAMPLE_RATE);
+ g_free(path_container);
+ if (gme_err != nullptr) {
+ LogWarning(gme_domain, gme_err);
+ return;
+ }
+
+ gme_info_t *ti;
+ const int song_num = get_song_num(path_fs);
+ gme_err = gme_track_info(emu, &ti, song_num);
+ if (gme_err != nullptr) {
+ LogWarning(gme_domain, gme_err);
+ gme_delete(emu);
+ return;
+ }
+
+ const float song_len = ti->length > 0
+ ? ti->length / 1000.0
+ : -1.0;
+
+ /* initialize the MPD decoder */
+
+ Error error;
+ AudioFormat audio_format;
+ if (!audio_format_init_checked(audio_format, GME_SAMPLE_RATE,
+ SampleFormat::S16, GME_CHANNELS,
+ error)) {
+ LogError(error);
+ gme_free_info(ti);
+ gme_delete(emu);
+ return;
+ }
+
+ decoder_initialized(decoder, audio_format, true, song_len);
+
+ gme_err = gme_start_track(emu, song_num);
+ if (gme_err != nullptr)
+ LogWarning(gme_domain, gme_err);
+
+ if (ti->length > 0)
+ gme_set_fade(emu, ti->length);
+
+ /* play */
+ DecoderCommand cmd;
+ do {
+ short buf[GME_BUFFER_SAMPLES];
+ gme_err = gme_play(emu, GME_BUFFER_SAMPLES, buf);
+ if (gme_err != nullptr) {
+ LogWarning(gme_domain, gme_err);
+ return;
+ }
+
+ cmd = decoder_data(decoder, nullptr, buf, sizeof(buf), 0);
+ if (cmd == DecoderCommand::SEEK) {
+ float where = decoder_seek_where(decoder);
+ gme_err = gme_seek(emu, int(where * 1000));
+ if (gme_err != nullptr)
+ LogWarning(gme_domain, gme_err);
+ decoder_command_finished(decoder);
+ }
+
+ if (gme_track_ended(emu))
+ break;
+ } while (cmd != DecoderCommand::STOP);
+
+ gme_free_info(ti);
+ gme_delete(emu);
+}
+
+static bool
+gme_scan_file(const char *path_fs,
+ const struct tag_handler *handler, void *handler_ctx)
+{
+ char *path_container = get_container_name(path_fs);
+
+ Music_Emu *emu;
+ const char *gme_err =
+ gme_open_file(path_container, &emu, GME_SAMPLE_RATE);
+ g_free(path_container);
+ if (gme_err != nullptr) {
+ LogWarning(gme_domain, gme_err);
+ return false;
+ }
+
+ const int song_num = get_song_num(path_fs);
+
+ gme_info_t *ti;
+ gme_err = gme_track_info(emu, &ti, song_num);
+ if (gme_err != nullptr) {
+ LogWarning(gme_domain, gme_err);
+ gme_delete(emu);
+ return false;
+ }
+
+ assert(ti != nullptr);
+
+ if (ti->length > 0)
+ tag_handler_invoke_duration(handler, handler_ctx,
+ ti->length / 100);
+
+ if (ti->song != nullptr) {
+ if (gme_track_count(emu) > 1) {
+ /* start numbering subtunes from 1 */
+ char *tag_title =
+ g_strdup_printf("%s (%d/%d)",
+ ti->song, song_num + 1,
+ gme_track_count(emu));
+ tag_handler_invoke_tag(handler, handler_ctx,
+ TAG_TITLE, tag_title);
+ g_free(tag_title);
+ } else
+ tag_handler_invoke_tag(handler, handler_ctx,
+ TAG_TITLE, ti->song);
+ }
+
+ if (ti->author != nullptr)
+ tag_handler_invoke_tag(handler, handler_ctx,
+ TAG_ARTIST, ti->author);
+
+ if (ti->game != nullptr)
+ tag_handler_invoke_tag(handler, handler_ctx,
+ TAG_ALBUM, ti->game);
+
+ if (ti->comment != nullptr)
+ tag_handler_invoke_tag(handler, handler_ctx,
+ TAG_COMMENT, ti->comment);
+
+ if (ti->copyright != nullptr)
+ tag_handler_invoke_tag(handler, handler_ctx,
+ TAG_DATE, ti->copyright);
+
+ gme_free_info(ti);
+ gme_delete(emu);
+
+ return true;
+}
+
+static const char *const gme_suffixes[] = {
+ "ay", "gbs", "gym", "hes", "kss", "nsf",
+ "nsfe", "sap", "spc", "vgm", "vgz",
+ nullptr
+};
+
+extern const struct decoder_plugin gme_decoder_plugin;
+const struct decoder_plugin gme_decoder_plugin = {
+ "gme",
+ nullptr,
+ nullptr,
+ nullptr,
+ gme_file_decode,
+ gme_scan_file,
+ nullptr,
+ gme_container_scan,
+ gme_suffixes,
+ nullptr,
+};
diff --git a/src/decoder/GmeDecoderPlugin.hxx b/src/decoder/GmeDecoderPlugin.hxx
new file mode 100644
index 000000000..fba735d92
--- /dev/null
+++ b/src/decoder/GmeDecoderPlugin.hxx
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2003-2013 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_GME_HXX
+#define MPD_DECODER_GME_HXX
+
+extern const struct decoder_plugin gme_decoder_plugin;
+
+#endif
diff --git a/src/decoder/mad_decoder_plugin.c b/src/decoder/MadDecoderPlugin.cxx
index 62c371642..1cce24f31 100644
--- a/src/decoder/mad_decoder_plugin.c
+++ b/src/decoder/MadDecoderPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,17 +18,23 @@
*/
#include "config.h"
-#include "decoder_api.h"
-#include "conf.h"
-#include "tag_id3.h"
-#include "tag_rva2.h"
-#include "tag_handler.h"
-#include "audio_check.h"
+#include "MadDecoderPlugin.hxx"
+#include "DecoderAPI.hxx"
+#include "InputStream.hxx"
+#include "ConfigGlobal.hxx"
+#include "tag/TagId3.hxx"
+#include "tag/TagRva2.hxx"
+#include "tag/TagHandler.hxx"
+#include "CheckAudioFormat.hxx"
+#include "util/Error.hxx"
+#include "util/Domain.hxx"
+#include "Log.hxx"
#include <assert.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
+#include <string.h>
#include <glib.h>
#include <mad.h>
@@ -36,9 +42,6 @@
#include <id3tag.h>
#endif
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "mad"
-
#define FRAMES_CUSHION 2000
#define READ_BUFFER_SIZE 40960
@@ -61,6 +64,8 @@ enum muteframe {
#define DEFAULT_GAPLESS_MP3_PLAYBACK true
+static constexpr Domain mad_domain("mad");
+
static bool gapless_playback;
static inline int32_t
@@ -76,9 +81,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 */
@@ -99,7 +104,7 @@ mad_fixed_to_24_buffer(int32_t *dest, const struct mad_synth *synth,
}
static bool
-mp3_plugin_init(G_GNUC_UNUSED const struct config_param *param)
+mp3_plugin_init(gcc_unused const config_param &param)
{
gapless_playback = config_get_bool(CONF_GAPLESS_MP3_PLAYBACK,
DEFAULT_GAPLESS_MP3_PLAYBACK);
@@ -108,7 +113,7 @@ mp3_plugin_init(G_GNUC_UNUSED const struct config_param *param)
#define MP3_DATA_OUTPUT_BUFFER_SIZE 2048
-struct mp3_data {
+struct MadDecoder {
struct mad_stream stream;
struct mad_frame frame;
struct mad_synth synth;
@@ -136,64 +141,96 @@ struct mp3_data {
struct decoder *decoder;
struct input_stream *input_stream;
enum mad_layer layer;
+
+ MadDecoder(struct decoder *decoder, struct input_stream *input_stream);
+ ~MadDecoder();
+
+ bool Seek(long offset);
+ bool FillBuffer();
+ void ParseId3(size_t tagsize, Tag **mpd_tag);
+ enum mp3_action DecodeNextFrameHeader(Tag **tag);
+ enum mp3_action DecodeNextFrame();
+
+ gcc_pure
+ goffset ThisFrameOffset() const;
+
+ gcc_pure
+ goffset RestIncludingThisFrame() const;
+
+ /**
+ * Attempt to calulcate the length of the song from filesize
+ */
+ void FileSizeToSongLength();
+
+ bool DecodeFirstFrame(Tag **tag);
+
+ gcc_pure
+ long TimeToFrame(double t) const;
+
+ void UpdateTimerNextFrame();
+
+ /**
+ * Sends the synthesized current frame via decoder_data().
+ */
+ DecoderCommand SendPCM(unsigned i, unsigned pcm_length);
+
+ /**
+ * Synthesize the current frame and send it via
+ * decoder_data().
+ */
+ DecoderCommand SyncAndSend();
+
+ bool Read();
};
-static void
-mp3_data_init(struct mp3_data *data, struct decoder *decoder,
- struct input_stream *input_stream)
+MadDecoder::MadDecoder(struct decoder *_decoder,
+ struct input_stream *_input_stream)
+ :mute_frame(MUTEFRAME_NONE),
+ frame_offsets(nullptr),
+ times(nullptr),
+ highest_frame(0), max_frames(0), current_frame(0),
+ drop_start_frames(0), drop_end_frames(0),
+ drop_start_samples(0), drop_end_samples(0),
+ found_replay_gain(false), found_xing(false),
+ found_first_frame(false), decoded_first_frame(false),
+ decoder(_decoder), input_stream(_input_stream),
+ layer(mad_layer(0))
{
- data->mute_frame = MUTEFRAME_NONE;
- data->highest_frame = 0;
- data->max_frames = 0;
- data->frame_offsets = NULL;
- data->times = NULL;
- data->current_frame = 0;
- data->drop_start_frames = 0;
- data->drop_end_frames = 0;
- data->drop_start_samples = 0;
- data->drop_end_samples = 0;
- data->found_replay_gain = false;
- data->found_xing = false;
- data->found_first_frame = false;
- data->decoded_first_frame = false;
- data->decoder = decoder;
- data->input_stream = input_stream;
- data->layer = 0;
-
- mad_stream_init(&data->stream);
- mad_stream_options(&data->stream, MAD_OPTION_IGNORECRC);
- mad_frame_init(&data->frame);
- mad_synth_init(&data->synth);
- mad_timer_reset(&data->timer);
+ mad_stream_init(&stream);
+ mad_stream_options(&stream, MAD_OPTION_IGNORECRC);
+ mad_frame_init(&frame);
+ mad_synth_init(&synth);
+ mad_timer_reset(&timer);
}
-static bool mp3_seek(struct mp3_data *data, long offset)
+inline bool
+MadDecoder::Seek(long offset)
{
- if (!input_stream_lock_seek(data->input_stream, offset, SEEK_SET, NULL))
+ Error error;
+ if (!input_stream->LockSeek(offset, SEEK_SET, error))
return false;
- mad_stream_buffer(&data->stream, data->input_buffer, 0);
- (data->stream).error = 0;
+ mad_stream_buffer(&stream, input_buffer, 0);
+ stream.error = MAD_ERROR_NONE;
return true;
}
-static bool
-mp3_fill_buffer(struct mp3_data *data)
+inline bool
+MadDecoder::FillBuffer()
{
size_t remaining, length;
unsigned char *dest;
- if (data->stream.next_frame != NULL) {
- remaining = data->stream.bufend - data->stream.next_frame;
- memmove(data->input_buffer, data->stream.next_frame,
- remaining);
- dest = (data->input_buffer) + remaining;
+ if (stream.next_frame != nullptr) {
+ remaining = stream.bufend - stream.next_frame;
+ memmove(input_buffer, stream.next_frame, remaining);
+ dest = input_buffer + remaining;
length = READ_BUFFER_SIZE - remaining;
} else {
remaining = 0;
length = READ_BUFFER_SIZE;
- dest = data->input_buffer;
+ dest = input_buffer;
}
/* we've exhausted the read buffer, so give up!, these potential
@@ -201,13 +238,12 @@ mp3_fill_buffer(struct mp3_data *data)
if (length == 0)
return false;
- length = decoder_read(data->decoder, data->input_stream, dest, length);
+ length = decoder_read(decoder, input_stream, dest, length);
if (length == 0)
return false;
- mad_stream_buffer(&data->stream, data->input_buffer,
- length + remaining);
- (data->stream).error = 0;
+ mad_stream_buffer(&stream, input_buffer, length + remaining);
+ stream.error = MAD_ERROR_NONE;
return true;
}
@@ -271,8 +307,8 @@ parse_id3_mixramp(char **mixramp_start, char **mixramp_end,
struct id3_frame *frame;
bool found = false;
- *mixramp_start = NULL;
- *mixramp_end = NULL;
+ *mixramp_start = nullptr;
+ *mixramp_end = nullptr;
for (i = 0; (frame = id3_tag_findframe(tag, "TXXX", i)); i++) {
if (frame->nfields < 3)
@@ -301,29 +337,29 @@ parse_id3_mixramp(char **mixramp_start, char **mixramp_end,
}
#endif
-static void mp3_parse_id3(struct mp3_data *data, size_t tagsize,
- struct tag **mpd_tag)
+inline void
+MadDecoder::ParseId3(size_t tagsize, Tag **mpd_tag)
{
#ifdef HAVE_ID3TAG
- struct id3_tag *id3_tag = NULL;
+ struct id3_tag *id3_tag = nullptr;
id3_length_t count;
id3_byte_t const *id3_data;
- id3_byte_t *allocated = NULL;
+ id3_byte_t *allocated = nullptr;
- count = data->stream.bufend - data->stream.this_frame;
+ count = stream.bufend - stream.this_frame;
if (tagsize <= count) {
- id3_data = data->stream.this_frame;
- mad_stream_skip(&(data->stream), tagsize);
+ id3_data = stream.this_frame;
+ mad_stream_skip(&(stream), tagsize);
} else {
- allocated = g_malloc(tagsize);
- memcpy(allocated, data->stream.this_frame, count);
- mad_stream_skip(&(data->stream), count);
+ allocated = (id3_byte_t *)g_malloc(tagsize);
+ memcpy(allocated, stream.this_frame, count);
+ mad_stream_skip(&(stream), count);
while (count < tagsize) {
size_t len;
- len = decoder_read(data->decoder, data->input_stream,
+ len = decoder_read(decoder, input_stream,
allocated + count, tagsize - count);
if (len == 0)
break;
@@ -332,7 +368,7 @@ static void mp3_parse_id3(struct mp3_data *data, size_t tagsize,
}
if (count != tagsize) {
- g_debug("error parsing ID3 tag");
+ LogDebug(mad_domain, "error parsing ID3 tag");
g_free(allocated);
return;
}
@@ -341,34 +377,31 @@ static void mp3_parse_id3(struct mp3_data *data, size_t tagsize,
}
id3_tag = id3_tag_parse(id3_data, tagsize);
- if (id3_tag == NULL) {
+ if (id3_tag == nullptr) {
g_free(allocated);
return;
}
if (mpd_tag) {
- struct tag *tmp_tag = tag_id3_import(id3_tag);
- if (tmp_tag != NULL) {
- if (*mpd_tag != NULL)
- tag_free(*mpd_tag);
+ Tag *tmp_tag = tag_id3_import(id3_tag);
+ if (tmp_tag != nullptr) {
+ delete *mpd_tag;
*mpd_tag = tmp_tag;
}
}
- if (data->decoder != NULL) {
+ if (decoder != nullptr) {
struct replay_gain_info rgi;
char *mixramp_start;
char *mixramp_end;
- float replay_gain_db = 0;
if (parse_id3_replay_gain_info(&rgi, id3_tag)) {
- replay_gain_db = decoder_replay_gain(data->decoder, &rgi);
- data->found_replay_gain = true;
+ decoder_replay_gain(decoder, &rgi);
+ found_replay_gain = true;
}
if (parse_id3_mixramp(&mixramp_start, &mixramp_end, id3_tag))
- decoder_mixramp(data->decoder, replay_gain_db,
- mixramp_start, mixramp_end);
+ decoder_mixramp(decoder, mixramp_start, mixramp_end);
}
id3_tag_delete(id3_tag);
@@ -380,12 +413,12 @@ static void mp3_parse_id3(struct mp3_data *data, size_t tagsize,
/* This code is enabled when libid3tag is disabled. Instead
of parsing the ID3 frame, it just skips it. */
- size_t count = data->stream.bufend - data->stream.this_frame;
+ size_t count = stream.bufend - stream.this_frame;
if (tagsize <= count) {
- mad_stream_skip(&data->stream, tagsize);
+ mad_stream_skip(&stream, tagsize);
} else {
- mad_stream_skip(&data->stream, count);
+ mad_stream_skip(&stream, count);
while (count < tagsize) {
size_t len = tagsize - count;
@@ -393,7 +426,7 @@ static void mp3_parse_id3(struct mp3_data *data, size_t tagsize,
if (len > sizeof(ignored))
len = sizeof(ignored);
- len = decoder_read(data->decoder, data->input_stream,
+ len = decoder_read(decoder, input_stream,
ignored, len);
if (len == 0)
break;
@@ -414,7 +447,7 @@ static void mp3_parse_id3(struct mp3_data *data, size_t tagsize,
static signed long
id3_tag_query(const void *p0, size_t length)
{
- const char *p = p0;
+ const char *p = (const char *)p0;
return length >= 10 && memcmp(p, "ID3", 3) == 0
? (p[8] << 7) + p[9] + 10
@@ -422,59 +455,51 @@ id3_tag_query(const void *p0, size_t length)
}
#endif /* !HAVE_ID3TAG */
-static enum mp3_action
-decode_next_frame_header(struct mp3_data *data, G_GNUC_UNUSED struct tag **tag)
+enum mp3_action
+MadDecoder::DecodeNextFrameHeader(Tag **tag)
{
- enum mad_layer layer;
+ if ((stream.buffer == nullptr || stream.error == MAD_ERROR_BUFLEN) &&
+ !FillBuffer())
+ return DECODE_BREAK;
- if ((data->stream).buffer == NULL
- || (data->stream).error == MAD_ERROR_BUFLEN) {
- if (!mp3_fill_buffer(data))
- return DECODE_BREAK;
- }
- if (mad_header_decode(&data->frame.header, &data->stream)) {
- if ((data->stream).error == MAD_ERROR_LOSTSYNC &&
- (data->stream).this_frame) {
- signed long tagsize = id3_tag_query((data->stream).
- this_frame,
- (data->stream).
- bufend -
- (data->stream).
- this_frame);
+ if (mad_header_decode(&frame.header, &stream)) {
+ if (stream.error == MAD_ERROR_LOSTSYNC && stream.this_frame) {
+ signed long tagsize = id3_tag_query(stream.this_frame,
+ stream.bufend -
+ stream.this_frame);
if (tagsize > 0) {
if (tag && !(*tag)) {
- mp3_parse_id3(data, (size_t)tagsize,
- tag);
+ ParseId3((size_t)tagsize, tag);
} else {
- mad_stream_skip(&(data->stream),
- tagsize);
+ mad_stream_skip(&stream, tagsize);
}
return DECODE_CONT;
}
}
- if (MAD_RECOVERABLE((data->stream).error)) {
+ if (MAD_RECOVERABLE(stream.error)) {
return DECODE_SKIP;
} else {
- if ((data->stream).error == MAD_ERROR_BUFLEN)
+ if (stream.error == MAD_ERROR_BUFLEN)
return DECODE_CONT;
else {
- g_warning("unrecoverable frame level error "
- "(%s).\n",
- mad_stream_errorstr(&data->stream));
+ FormatWarning(mad_domain,
+ "unrecoverable frame level error: %s",
+ mad_stream_errorstr(&stream));
return DECODE_BREAK;
}
}
}
- layer = data->frame.header.layer;
- if (!data->layer) {
- if (layer != MAD_LAYER_II && layer != MAD_LAYER_III) {
+ enum mad_layer new_layer = frame.header.layer;
+ if (layer == (mad_layer)0) {
+ if (new_layer != MAD_LAYER_II && new_layer != MAD_LAYER_III) {
/* Only layer 2 and 3 have been tested to work */
return DECODE_SKIP;
}
- data->layer = layer;
- } else if (layer != data->layer) {
+
+ layer = new_layer;
+ } else if (new_layer != layer) {
/* Don't decode frames with a different layer than the first */
return DECODE_SKIP;
}
@@ -482,36 +507,32 @@ decode_next_frame_header(struct mp3_data *data, G_GNUC_UNUSED struct tag **tag)
return DECODE_OK;
}
-static enum mp3_action
-decodeNextFrame(struct mp3_data *data)
+enum mp3_action
+MadDecoder::DecodeNextFrame()
{
- if ((data->stream).buffer == NULL
- || (data->stream).error == MAD_ERROR_BUFLEN) {
- if (!mp3_fill_buffer(data))
- return DECODE_BREAK;
- }
- if (mad_frame_decode(&data->frame, &data->stream)) {
- if ((data->stream).error == MAD_ERROR_LOSTSYNC) {
- signed long tagsize = id3_tag_query((data->stream).
- this_frame,
- (data->stream).
- bufend -
- (data->stream).
- this_frame);
+ if ((stream.buffer == nullptr || stream.error == MAD_ERROR_BUFLEN) &&
+ !FillBuffer())
+ return DECODE_BREAK;
+
+ if (mad_frame_decode(&frame, &stream)) {
+ if (stream.error == MAD_ERROR_LOSTSYNC) {
+ signed long tagsize = id3_tag_query(stream.this_frame,
+ stream.bufend -
+ stream.this_frame);
if (tagsize > 0) {
- mad_stream_skip(&(data->stream), tagsize);
+ mad_stream_skip(&stream, tagsize);
return DECODE_CONT;
}
}
- if (MAD_RECOVERABLE((data->stream).error)) {
+ if (MAD_RECOVERABLE(stream.error)) {
return DECODE_SKIP;
} else {
- if ((data->stream).error == MAD_ERROR_BUFLEN)
+ if (stream.error == MAD_ERROR_BUFLEN)
return DECODE_CONT;
else {
- g_warning("unrecoverable frame level error "
- "(%s).\n",
- mad_stream_errorstr(&data->stream));
+ FormatWarning(mad_domain,
+ "unrecoverable frame level error: %s",
+ mad_stream_errorstr(&stream));
return DECODE_BREAK;
}
}
@@ -682,8 +703,8 @@ parse_lame(struct lame *lame, struct mad_bitptr *ptr, int *bitlen)
&lame->version.major, &lame->version.minor) != 2)
return false;
- g_debug("detected LAME version %i.%i (\"%s\")\n",
- lame->version.major, lame->version.minor, lame->encoder);
+ FormatDebug(mad_domain, "detected LAME version %i.%i (\"%s\")",
+ lame->version.major, lame->version.minor, lame->encoder);
/* The reference volume was changed from the 83dB used in the
* ReplayGain spec to 89dB in lame 3.95.1. Bump the gain for older
@@ -699,7 +720,7 @@ parse_lame(struct lame *lame, struct mad_bitptr *ptr, int *bitlen)
mad_bit_read(ptr, 16);
lame->peak = mad_f_todouble(mad_bit_read(ptr, 32) << 5); /* peak */
- g_debug("LAME peak found: %f\n", lame->peak);
+ FormatDebug(mad_domain, "LAME peak found: %f", lame->peak);
lame->track_gain = 0;
name = mad_bit_read(ptr, 3); /* gain name */
@@ -708,7 +729,8 @@ parse_lame(struct lame *lame, struct mad_bitptr *ptr, int *bitlen)
gain = mad_bit_read(ptr, 9); /* gain*10 */
if (gain && name == 1 && orig != 0) {
lame->track_gain = ((sign ? -gain : gain) / 10.0) + adj;
- g_debug("LAME track gain found: %f\n", lame->track_gain);
+ FormatDebug(mad_domain, "LAME track gain found: %f",
+ lame->track_gain);
}
/* tmz reports that this isn't currently written by any version of lame
@@ -723,7 +745,8 @@ parse_lame(struct lame *lame, struct mad_bitptr *ptr, int *bitlen)
gain = mad_bit_read(ptr, 9); /* gain*10 */
if (gain && name == 2 && orig != 0) {
lame->album_gain = ((sign ? -gain : gain) / 10.0) + adj;
- g_debug("LAME album gain found: %f\n", lame->track_gain);
+ FormatDebug(mad_domain, "LAME album gain found: %f",
+ lame->track_gain);
}
#else
mad_bit_read(ptr, 16);
@@ -734,8 +757,8 @@ parse_lame(struct lame *lame, struct mad_bitptr *ptr, int *bitlen)
lame->encoder_delay = mad_bit_read(ptr, 12);
lame->encoder_padding = mad_bit_read(ptr, 12);
- g_debug("encoder delay is %i, encoder padding is %i\n",
- lame->encoder_delay, lame->encoder_padding);
+ FormatDebug(mad_domain, "encoder delay is %i, encoder padding is %i",
+ lame->encoder_delay, lame->encoder_padding);
mad_bit_read(ptr, 80);
@@ -753,47 +776,43 @@ mp3_frame_duration(const struct mad_frame *frame)
MAD_UNITS_MILLISECONDS) / 1000.0;
}
-static goffset
-mp3_this_frame_offset(const struct mp3_data *data)
+inline goffset
+MadDecoder::ThisFrameOffset() const
{
- goffset offset = data->input_stream->offset;
+ goffset offset = input_stream->GetOffset();
- if (data->stream.this_frame != NULL)
- offset -= data->stream.bufend - data->stream.this_frame;
+ if (stream.this_frame != nullptr)
+ offset -= stream.bufend - stream.this_frame;
else
- offset -= data->stream.bufend - data->stream.buffer;
+ offset -= stream.bufend - stream.buffer;
return offset;
}
-static goffset
-mp3_rest_including_this_frame(const struct mp3_data *data)
+inline goffset
+MadDecoder::RestIncludingThisFrame() const
{
- return data->input_stream->size - mp3_this_frame_offset(data);
+ return input_stream->GetSize() - ThisFrameOffset();
}
-/**
- * Attempt to calulcate the length of the song from filesize
- */
-static void
-mp3_filesize_to_song_length(struct mp3_data *data)
+inline void
+MadDecoder::FileSizeToSongLength()
{
- goffset rest = mp3_rest_including_this_frame(data);
+ goffset rest = RestIncludingThisFrame();
if (rest > 0) {
- float frame_duration = mp3_frame_duration(&data->frame);
+ float frame_duration = mp3_frame_duration(&frame);
- data->total_time = (rest * 8.0) / (data->frame).header.bitrate;
- data->max_frames = data->total_time / frame_duration +
- FRAMES_CUSHION;
+ total_time = (rest * 8.0) / frame.header.bitrate;
+ max_frames = total_time / frame_duration + FRAMES_CUSHION;
} else {
- data->max_frames = FRAMES_CUSHION;
- data->total_time = 0;
+ max_frames = FRAMES_CUSHION;
+ total_time = 0;
}
}
-static bool
-mp3_decode_first_frame(struct mp3_data *data, struct tag **tag)
+inline bool
+MadDecoder::DecodeFirstFrame(Tag **tag)
{
struct xing xing;
struct lame lame;
@@ -807,127 +826,103 @@ mp3_decode_first_frame(struct mp3_data *data, struct tag **tag)
while (true) {
do {
- ret = decode_next_frame_header(data, tag);
+ ret = DecodeNextFrameHeader(tag);
} while (ret == DECODE_CONT);
if (ret == DECODE_BREAK)
return false;
if (ret == DECODE_SKIP) continue;
do {
- ret = decodeNextFrame(data);
+ ret = DecodeNextFrame();
} while (ret == DECODE_CONT);
if (ret == DECODE_BREAK)
return false;
if (ret == DECODE_OK) break;
}
- ptr = data->stream.anc_ptr;
- bitlen = data->stream.anc_bitlen;
+ ptr = stream.anc_ptr;
+ bitlen = stream.anc_bitlen;
- mp3_filesize_to_song_length(data);
+ FileSizeToSongLength();
/*
* if an xing tag exists, use that!
*/
if (parse_xing(&xing, &ptr, &bitlen)) {
- data->found_xing = true;
- data->mute_frame = MUTEFRAME_SKIP;
+ found_xing = true;
+ mute_frame = MUTEFRAME_SKIP;
if ((xing.flags & XING_FRAMES) && xing.frames) {
- mad_timer_t duration = data->frame.header.duration;
+ mad_timer_t duration = frame.header.duration;
mad_timer_multiply(&duration, xing.frames);
- data->total_time = ((float)mad_timer_count(duration, MAD_UNITS_MILLISECONDS)) / 1000;
- data->max_frames = xing.frames;
+ total_time = ((float)mad_timer_count(duration, MAD_UNITS_MILLISECONDS)) / 1000;
+ max_frames = xing.frames;
}
if (parse_lame(&lame, &ptr, &bitlen)) {
- if (gapless_playback &&
- data->input_stream->seekable) {
- data->drop_start_samples = lame.encoder_delay +
+ if (gapless_playback && input_stream->IsSeekable()) {
+ drop_start_samples = lame.encoder_delay +
DECODERDELAY;
- data->drop_end_samples = lame.encoder_padding;
+ drop_end_samples = lame.encoder_padding;
}
/* Album gain isn't currently used. See comment in
* parse_lame() for details. -- jat */
- if (data->decoder != NULL &&
- !data->found_replay_gain &&
+ if (decoder != nullptr && !found_replay_gain &&
lame.track_gain) {
struct replay_gain_info rgi;
replay_gain_info_init(&rgi);
rgi.tuples[REPLAY_GAIN_TRACK].gain = lame.track_gain;
rgi.tuples[REPLAY_GAIN_TRACK].peak = lame.peak;
- decoder_replay_gain(data->decoder, &rgi);
+ decoder_replay_gain(decoder, &rgi);
}
}
}
- if (!data->max_frames)
+ if (!max_frames)
return false;
- if (data->max_frames > 8 * 1024 * 1024) {
- g_warning("mp3 file header indicates too many frames: %lu\n",
- data->max_frames);
+ if (max_frames > 8 * 1024 * 1024) {
+ FormatWarning(mad_domain,
+ "mp3 file header indicates too many frames: %lu",
+ max_frames);
return false;
}
- data->frame_offsets = g_malloc(sizeof(long) * data->max_frames);
- data->times = g_malloc(sizeof(mad_timer_t) * data->max_frames);
+ frame_offsets = new long[max_frames];
+ times = new mad_timer_t[max_frames];
return true;
}
-static void mp3_data_finish(struct mp3_data *data)
+MadDecoder::~MadDecoder()
{
- mad_synth_finish(&data->synth);
- mad_frame_finish(&data->frame);
- mad_stream_finish(&data->stream);
+ mad_synth_finish(&synth);
+ mad_frame_finish(&frame);
+ mad_stream_finish(&stream);
- g_free(data->frame_offsets);
- g_free(data->times);
+ delete[] frame_offsets;
+ delete[] times;
}
/* this is primarily used for getting total time for tags */
static int
mad_decoder_total_file_time(struct input_stream *is)
{
- struct mp3_data data;
- int ret;
-
- mp3_data_init(&data, NULL, is);
- if (!mp3_decode_first_frame(&data, NULL))
- ret = -1;
- else
- ret = data.total_time + 0.5;
- mp3_data_finish(&data);
-
- return ret;
+ MadDecoder data(nullptr, is);
+ return data.DecodeFirstFrame(nullptr)
+ ? data.total_time + 0.5
+ : -1;
}
-static bool
-mp3_open(struct input_stream *is, struct mp3_data *data,
- struct decoder *decoder, struct tag **tag)
-{
- mp3_data_init(data, decoder, is);
- *tag = NULL;
- if (!mp3_decode_first_frame(data, tag)) {
- mp3_data_finish(data);
- if (tag && *tag)
- tag_free(*tag);
- return false;
- }
-
- return true;
-}
-
-static long
-mp3_time_to_frame(const struct mp3_data *data, double t)
+long
+MadDecoder::TimeToFrame(double t) const
{
unsigned long i;
- for (i = 0; i < data->highest_frame; ++i) {
+ for (i = 0; i < highest_frame; ++i) {
double frame_time =
- mad_timer_count(data->times[i],
+ mad_timer_count(times[i],
MAD_UNITS_MILLISECONDS) / 1000.;
if (frame_time >= t)
break;
@@ -936,168 +931,152 @@ mp3_time_to_frame(const struct mp3_data *data, double t)
return i;
}
-static void
-mp3_update_timer_next_frame(struct mp3_data *data)
+void
+MadDecoder::UpdateTimerNextFrame()
{
- if (data->current_frame >= data->highest_frame) {
- /* record this frame's properties in
- data->frame_offsets (for seeking) and
- data->times */
- data->bit_rate = (data->frame).header.bitrate;
-
- if (data->current_frame >= data->max_frames)
- /* cap data->current_frame */
- data->current_frame = data->max_frames - 1;
+ if (current_frame >= highest_frame) {
+ /* record this frame's properties in frame_offsets
+ (for seeking) and times */
+ bit_rate = frame.header.bitrate;
+
+ if (current_frame >= max_frames)
+ /* cap current_frame */
+ current_frame = max_frames - 1;
else
- data->highest_frame++;
+ highest_frame++;
- data->frame_offsets[data->current_frame] =
- mp3_this_frame_offset(data);
+ frame_offsets[current_frame] = ThisFrameOffset();
- mad_timer_add(&data->timer, (data->frame).header.duration);
- data->times[data->current_frame] = data->timer;
+ mad_timer_add(&timer, frame.header.duration);
+ times[current_frame] = timer;
} else
- /* get the new timer value from data->times */
- data->timer = data->times[data->current_frame];
+ /* get the new timer value from "times" */
+ timer = times[current_frame];
- data->current_frame++;
- data->elapsed_time =
- mad_timer_count(data->timer, MAD_UNITS_MILLISECONDS) / 1000.0;
+ current_frame++;
+ elapsed_time = mad_timer_count(timer, MAD_UNITS_MILLISECONDS) / 1000.0;
}
-/**
- * Sends the synthesized current frame via decoder_data().
- */
-static enum decoder_command
-mp3_send_pcm(struct mp3_data *data, unsigned i, unsigned pcm_length)
+DecoderCommand
+MadDecoder::SendPCM(unsigned i, unsigned pcm_length)
{
unsigned max_samples;
- max_samples = sizeof(data->output_buffer) /
- sizeof(data->output_buffer[0]) /
- MAD_NCHANNELS(&(data->frame).header);
+ max_samples = sizeof(output_buffer) /
+ sizeof(output_buffer[0]) /
+ MAD_NCHANNELS(&frame.header);
while (i < pcm_length) {
- enum decoder_command cmd;
unsigned int num_samples = pcm_length - i;
if (num_samples > max_samples)
num_samples = max_samples;
i += num_samples;
- mad_fixed_to_24_buffer(data->output_buffer,
- &data->synth,
+ mad_fixed_to_24_buffer(output_buffer, &synth,
i - num_samples, i,
- MAD_NCHANNELS(&(data->frame).header));
- num_samples *= MAD_NCHANNELS(&(data->frame).header);
-
- cmd = decoder_data(data->decoder, data->input_stream,
- data->output_buffer,
- sizeof(data->output_buffer[0]) * num_samples,
- data->bit_rate / 1000);
- if (cmd != DECODE_COMMAND_NONE)
+ MAD_NCHANNELS(&frame.header));
+ num_samples *= MAD_NCHANNELS(&frame.header);
+
+ auto cmd = decoder_data(decoder, input_stream, output_buffer,
+ sizeof(output_buffer[0]) * num_samples,
+ bit_rate / 1000);
+ if (cmd != DecoderCommand::NONE)
return cmd;
}
- return DECODE_COMMAND_NONE;
+ return DecoderCommand::NONE;
}
-/**
- * Synthesize the current frame and send it via decoder_data().
- */
-static enum decoder_command
-mp3_synth_and_send(struct mp3_data *data)
+inline DecoderCommand
+MadDecoder::SyncAndSend()
{
- unsigned i, pcm_length;
- enum decoder_command cmd;
-
- mad_synth_frame(&data->synth, &data->frame);
-
- if (!data->found_first_frame) {
- unsigned int samples_per_frame = data->synth.pcm.length;
- data->drop_start_frames = data->drop_start_samples / samples_per_frame;
- data->drop_end_frames = data->drop_end_samples / samples_per_frame;
- data->drop_start_samples = data->drop_start_samples % samples_per_frame;
- data->drop_end_samples = data->drop_end_samples % samples_per_frame;
- data->found_first_frame = true;
+ mad_synth_frame(&synth, &frame);
+
+ if (!found_first_frame) {
+ unsigned int samples_per_frame = synth.pcm.length;
+ drop_start_frames = drop_start_samples / samples_per_frame;
+ drop_end_frames = drop_end_samples / samples_per_frame;
+ drop_start_samples = drop_start_samples % samples_per_frame;
+ drop_end_samples = drop_end_samples % samples_per_frame;
+ found_first_frame = true;
}
- if (data->drop_start_frames > 0) {
- data->drop_start_frames--;
- return DECODE_COMMAND_NONE;
- } else if ((data->drop_end_frames > 0) &&
- (data->current_frame == (data->max_frames + 1 - data->drop_end_frames))) {
+ if (drop_start_frames > 0) {
+ drop_start_frames--;
+ return DecoderCommand::NONE;
+ } else if ((drop_end_frames > 0) &&
+ (current_frame == (max_frames + 1 - drop_end_frames))) {
/* stop decoding, effectively dropping all remaining
frames */
- return DECODE_COMMAND_STOP;
+ return DecoderCommand::STOP;
}
- if (!data->decoded_first_frame) {
- i = data->drop_start_samples;
- data->decoded_first_frame = true;
- } else
- i = 0;
+ unsigned i = 0;
+ if (!decoded_first_frame) {
+ i = drop_start_samples;
+ decoded_first_frame = true;
+ }
- pcm_length = data->synth.pcm.length;
- if (data->drop_end_samples &&
- (data->current_frame == data->max_frames - data->drop_end_frames)) {
- if (data->drop_end_samples >= pcm_length)
+ unsigned pcm_length = synth.pcm.length;
+ if (drop_end_samples &&
+ (current_frame == max_frames - drop_end_frames)) {
+ if (drop_end_samples >= pcm_length)
pcm_length = 0;
else
- pcm_length -= data->drop_end_samples;
+ pcm_length -= drop_end_samples;
}
- cmd = mp3_send_pcm(data, i, pcm_length);
- if (cmd != DECODE_COMMAND_NONE)
+ auto cmd = SendPCM(i, pcm_length);
+ if (cmd != DecoderCommand::NONE)
return cmd;
- if (data->drop_end_samples &&
- (data->current_frame == data->max_frames - data->drop_end_frames))
+ if (drop_end_samples &&
+ (current_frame == max_frames - drop_end_frames))
/* stop decoding, effectively dropping
* all remaining samples */
- return DECODE_COMMAND_STOP;
+ return DecoderCommand::STOP;
- return DECODE_COMMAND_NONE;
+ return DecoderCommand::NONE;
}
-static bool
-mp3_read(struct mp3_data *data)
+inline bool
+MadDecoder::Read()
{
- struct decoder *decoder = data->decoder;
enum mp3_action ret;
- enum decoder_command cmd;
- mp3_update_timer_next_frame(data);
+ UpdateTimerNextFrame();
+
+ switch (mute_frame) {
+ DecoderCommand cmd;
- switch (data->mute_frame) {
case MUTEFRAME_SKIP:
- data->mute_frame = MUTEFRAME_NONE;
+ mute_frame = MUTEFRAME_NONE;
break;
case MUTEFRAME_SEEK:
- if (data->elapsed_time >= data->seek_where)
- data->mute_frame = MUTEFRAME_NONE;
+ if (elapsed_time >= seek_where)
+ mute_frame = MUTEFRAME_NONE;
break;
case MUTEFRAME_NONE:
- cmd = mp3_synth_and_send(data);
- if (cmd == DECODE_COMMAND_SEEK) {
+ cmd = SyncAndSend();
+ if (cmd == DecoderCommand::SEEK) {
unsigned long j;
- assert(data->input_stream->seekable);
+ assert(input_stream->IsSeekable());
- j = mp3_time_to_frame(data,
- decoder_seek_where(decoder));
- if (j < data->highest_frame) {
- if (mp3_seek(data, data->frame_offsets[j])) {
- data->current_frame = j;
+ j = TimeToFrame(decoder_seek_where(decoder));
+ if (j < highest_frame) {
+ if (Seek(frame_offsets[j])) {
+ current_frame = j;
decoder_command_finished(decoder);
} else
decoder_seek_error(decoder);
} else {
- data->seek_where = decoder_seek_where(decoder);
- data->mute_frame = MUTEFRAME_SEEK;
+ seek_where = decoder_seek_where(decoder);
+ mute_frame = MUTEFRAME_SEEK;
decoder_command_finished(decoder);
}
- } else if (cmd != DECODE_COMMAND_NONE)
+ } else if (cmd != DecoderCommand::NONE)
return false;
}
@@ -1105,13 +1084,14 @@ mp3_read(struct mp3_data *data)
bool skip = false;
do {
- struct tag *tag = NULL;
+ Tag *tag = nullptr;
- ret = decode_next_frame_header(data, &tag);
+ ret = DecodeNextFrameHeader(&tag);
- if (tag != NULL) {
- decoder_tag(decoder, data->input_stream, tag);
- tag_free(tag);
+ if (tag != nullptr) {
+ decoder_tag(decoder, input_stream,
+ std::move(*tag));
+ delete tag;
}
} while (ret == DECODE_CONT);
if (ret == DECODE_BREAK)
@@ -1119,9 +1099,9 @@ mp3_read(struct mp3_data *data)
else if (ret == DECODE_SKIP)
skip = true;
- if (data->mute_frame == MUTEFRAME_NONE) {
+ if (mute_frame == MUTEFRAME_NONE) {
do {
- ret = decodeNextFrame(data);
+ ret = DecodeNextFrame();
} while (ret == DECODE_CONT);
if (ret == DECODE_BREAK)
return false;
@@ -1137,43 +1117,40 @@ mp3_read(struct mp3_data *data)
static void
mp3_decode(struct decoder *decoder, struct input_stream *input_stream)
{
- struct mp3_data data;
- GError *error = NULL;
- struct tag *tag = NULL;
- struct audio_format audio_format;
-
- if (!mp3_open(input_stream, &data, decoder, &tag)) {
- if (decoder_get_command(decoder) == DECODE_COMMAND_NONE)
- g_warning
- ("Input does not appear to be a mp3 bit stream.\n");
+ MadDecoder data(decoder, input_stream);
+
+ Tag *tag = nullptr;
+ if (!data.DecodeFirstFrame(&tag)) {
+ delete tag;
+
+ if (decoder_get_command(decoder) == DecoderCommand::NONE)
+ LogError(mad_domain,
+ "Input does not appear to be a mp3 bit stream");
return;
}
- if (!audio_format_init_checked(&audio_format,
+ Error error;
+ AudioFormat audio_format;
+ if (!audio_format_init_checked(audio_format,
data.frame.header.samplerate,
- SAMPLE_FORMAT_S24_P32,
+ SampleFormat::S24_P32,
MAD_NCHANNELS(&data.frame.header),
- &error)) {
- g_warning("%s", error->message);
- g_error_free(error);
-
- if (tag != NULL)
- tag_free(tag);
- mp3_data_finish(&data);
+ error)) {
+ LogError(error);
+ delete tag;
return;
}
- decoder_initialized(decoder, &audio_format,
- data.input_stream->seekable, data.total_time);
+ decoder_initialized(decoder, audio_format,
+ input_stream->IsSeekable(),
+ data.total_time);
- if (tag != NULL) {
- decoder_tag(decoder, input_stream, tag);
- tag_free(tag);
+ if (tag != nullptr) {
+ decoder_tag(decoder, input_stream, std::move(*tag));
+ delete tag;
}
- while (mp3_read(&data)) ;
-
- mp3_data_finish(&data);
+ while (data.Read()) {}
}
static bool
@@ -1190,14 +1167,18 @@ mad_decoder_scan_stream(struct input_stream *is,
return true;
}
-static const char *const mp3_suffixes[] = { "mp3", "mp2", NULL };
-static const char *const mp3_mime_types[] = { "audio/mpeg", NULL };
+static const char *const mp3_suffixes[] = { "mp3", "mp2", nullptr };
+static const char *const mp3_mime_types[] = { "audio/mpeg", nullptr };
const struct decoder_plugin mad_decoder_plugin = {
- .name = "mad",
- .init = mp3_plugin_init,
- .stream_decode = mp3_decode,
- .scan_stream = mad_decoder_scan_stream,
- .suffixes = mp3_suffixes,
- .mime_types = mp3_mime_types
+ "mad",
+ mp3_plugin_init,
+ nullptr,
+ mp3_decode,
+ nullptr,
+ nullptr,
+ mad_decoder_scan_stream,
+ nullptr,
+ mp3_suffixes,
+ mp3_mime_types,
};
diff --git a/src/decoder/MadDecoderPlugin.hxx b/src/decoder/MadDecoderPlugin.hxx
new file mode 100644
index 000000000..c7a77304c
--- /dev/null
+++ b/src/decoder/MadDecoderPlugin.hxx
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2003-2013 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_MAD_HXX
+#define MPD_DECODER_MAD_HXX
+
+extern const struct decoder_plugin mad_decoder_plugin;
+
+#endif
diff --git a/src/decoder/mikmod_decoder_plugin.c b/src/decoder/MikmodDecoderPlugin.cxx
index a8fe818de..fb82eb732 100644
--- a/src/decoder/mikmod_decoder_plugin.c
+++ b/src/decoder/MikmodDecoderPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,20 +18,22 @@
*/
#include "config.h"
-#include "decoder_api.h"
-#include "mpd_error.h"
-#include "tag_handler.h"
+#include "MikmodDecoderPlugin.hxx"
+#include "DecoderAPI.hxx"
+#include "tag/TagHandler.hxx"
+#include "system/FatalError.hxx"
+#include "util/Domain.hxx"
+#include "Log.hxx"
#include <glib.h>
#include <mikmod.h>
#include <assert.h>
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "mikmod"
+static constexpr Domain mikmod_domain("mikmod");
/* this is largely copied from alsaplayer */
-#define MIKMOD_FRAME_SIZE 4096
+static constexpr size_t MIKMOD_FRAME_SIZE = 4096;
static BOOL
mikmod_mpd_init(void)
@@ -64,7 +66,7 @@ static char drv_alias[] = PACKAGE;
#endif
static MDRIVER drv_mpd = {
- NULL,
+ nullptr,
drv_name,
drv_version,
0,
@@ -72,9 +74,9 @@ static MDRIVER drv_mpd = {
#if (LIBMIKMOD_VERSION > 0x030106)
drv_alias,
#if (LIBMIKMOD_VERSION >= 0x030200)
- NULL, /* CmdLineHelp */
+ nullptr, /* CmdLineHelp */
#endif
- NULL, /* CommandLine */
+ nullptr, /* CommandLine */
#endif
mikmod_mpd_is_present,
VC_SampleLoad,
@@ -83,12 +85,12 @@ static MDRIVER drv_mpd = {
VC_SampleLength,
mikmod_mpd_init,
mikmod_mpd_exit,
- NULL,
+ nullptr,
VC_SetNumVoices,
VC_PlayStart,
VC_PlayStop,
mikmod_mpd_update,
- NULL,
+ nullptr,
VC_VoiceSetVolume,
VC_VoiceGetVolume,
VC_VoiceSetFrequency,
@@ -105,15 +107,14 @@ static MDRIVER drv_mpd = {
static unsigned mikmod_sample_rate;
static bool
-mikmod_decoder_init(const struct config_param *param)
+mikmod_decoder_init(const config_param &param)
{
static char params[] = "";
- mikmod_sample_rate = config_get_block_unsigned(param, "sample_rate",
- 44100);
+ mikmod_sample_rate = param.GetBlockValue("sample_rate", 44100u);
if (!audio_valid_sample_rate(mikmod_sample_rate))
- MPD_ERROR("Invalid sample rate in line %d: %u",
- param->line, mikmod_sample_rate);
+ FormatFatalError("Invalid sample rate in line %d: %u",
+ param.line, mikmod_sample_rate);
md_device = 0;
md_reverb = 0;
@@ -127,8 +128,9 @@ mikmod_decoder_init(const struct config_param *param)
DMODE_16BITS);
if (MikMod_Init(params)) {
- g_warning("Could not init MikMod: %s\n",
- MikMod_strerror(MikMod_errno));
+ FormatError(mikmod_domain,
+ "Could not init MikMod: %s",
+ MikMod_strerror(MikMod_errno));
return false;
}
@@ -146,32 +148,33 @@ mikmod_decoder_file_decode(struct decoder *decoder, const char *path_fs)
{
char *path2;
MODULE *handle;
- struct audio_format audio_format;
int ret;
SBYTE buffer[MIKMOD_FRAME_SIZE];
- enum decoder_command cmd = DECODE_COMMAND_NONE;
path2 = g_strdup(path_fs);
handle = Player_Load(path2, 128, 0);
g_free(path2);
- if (handle == NULL) {
- g_warning("failed to open mod: %s", path_fs);
+ if (handle == nullptr) {
+ FormatError(mikmod_domain,
+ "failed to open mod: %s", path_fs);
return;
}
/* Prevent module from looping forever */
handle->loop = 0;
- audio_format_init(&audio_format, mikmod_sample_rate, SAMPLE_FORMAT_S16, 2);
- assert(audio_format_valid(&audio_format));
+ const AudioFormat audio_format(mikmod_sample_rate, SampleFormat::S16, 2);
+ assert(audio_format.IsValid());
- decoder_initialized(decoder, &audio_format, false, 0);
+ decoder_initialized(decoder, audio_format, false, 0);
Player_Start(handle);
- while (cmd == DECODE_COMMAND_NONE && Player_Active()) {
+
+ DecoderCommand cmd = DecoderCommand::NONE;
+ while (cmd == DecoderCommand::NONE && Player_Active()) {
ret = VC_WriteBytes(buffer, sizeof(buffer));
- cmd = decoder_data(decoder, NULL, buffer, ret, 0);
+ cmd = decoder_data(decoder, nullptr, buffer, ret, 0);
}
Player_Stop();
@@ -185,9 +188,10 @@ mikmod_decoder_scan_file(const char *path_fs,
char *path2 = g_strdup(path_fs);
MODULE *handle = Player_Load(path2, 128, 0);
- if (handle == NULL) {
+ if (handle == nullptr) {
g_free(path2);
- g_debug("Failed to open file: %s", path_fs);
+ FormatDebug(mikmod_domain,
+ "Failed to open file: %s", path_fs);
return false;
}
@@ -197,7 +201,7 @@ mikmod_decoder_scan_file(const char *path_fs,
char *title = Player_LoadTitle(path2);
g_free(path2);
- if (title != NULL) {
+ if (title != nullptr) {
tag_handler_invoke_tag(handler, handler_ctx,
TAG_TITLE, title);
#if (LIBMIKMOD_VERSION >= 0x030200)
@@ -226,14 +230,18 @@ static const char *const mikmod_decoder_suffixes[] = {
"ult",
"uni",
"xm",
- NULL
+ nullptr
};
const struct decoder_plugin mikmod_decoder_plugin = {
- .name = "mikmod",
- .init = mikmod_decoder_init,
- .finish = mikmod_decoder_finish,
- .file_decode = mikmod_decoder_file_decode,
- .scan_file = mikmod_decoder_scan_file,
- .suffixes = mikmod_decoder_suffixes,
+ "mikmod",
+ mikmod_decoder_init,
+ mikmod_decoder_finish,
+ nullptr,
+ mikmod_decoder_file_decode,
+ mikmod_decoder_scan_file,
+ nullptr,
+ nullptr,
+ mikmod_decoder_suffixes,
+ nullptr,
};
diff --git a/src/decoder/MikmodDecoderPlugin.hxx b/src/decoder/MikmodDecoderPlugin.hxx
new file mode 100644
index 000000000..dd3b1389e
--- /dev/null
+++ b/src/decoder/MikmodDecoderPlugin.hxx
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2003-2013 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_MIKMOD_HXX
+#define MPD_DECODER_MIKMOD_HXX
+
+extern const struct decoder_plugin mikmod_decoder_plugin;
+
+#endif
diff --git a/src/decoder/modplug_decoder_plugin.c b/src/decoder/ModplugDecoderPlugin.cxx
index 5ae4b1a04..39c366492 100644
--- a/src/decoder/modplug_decoder_plugin.c
+++ b/src/decoder/ModplugDecoderPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,66 +18,70 @@
*/
#include "config.h"
-#include "decoder_api.h"
-#include "tag_handler.h"
+#include "ModplugDecoderPlugin.hxx"
+#include "DecoderAPI.hxx"
+#include "InputStream.hxx"
+#include "tag/TagHandler.hxx"
+#include "util/Domain.hxx"
+#include "Log.hxx"
-#include <glib.h>
#include <libmodplug/modplug.h>
+
+#include <glib.h>
+
#include <assert.h>
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "modplug"
+static constexpr Domain modplug_domain("modplug");
-enum {
- MODPLUG_FRAME_SIZE = 4096,
- MODPLUG_PREALLOC_BLOCK = 256 * 1024,
- MODPLUG_READ_BLOCK = 128 * 1024,
- MODPLUG_FILE_LIMIT = 100 * 1024 * 1024,
-};
+static constexpr size_t MODPLUG_FRAME_SIZE = 4096;
+static constexpr size_t MODPLUG_PREALLOC_BLOCK = 256 * 1024;
+static constexpr size_t MODPLUG_READ_BLOCK = 128 * 1024;
+static constexpr goffset MODPLUG_FILE_LIMIT = 100 * 1024 * 1024;
-static GByteArray *mod_loadfile(struct decoder *decoder, struct input_stream *is)
+static GByteArray *
+mod_loadfile(struct decoder *decoder, struct input_stream *is)
{
- unsigned char *data;
- GByteArray *bdatas;
- size_t ret;
+ const goffset size = is->GetSize();
- if (is->size == 0) {
- g_warning("file is empty");
- return NULL;
+ if (size == 0) {
+ LogWarning(modplug_domain, "file is empty");
+ return nullptr;
}
- if (is->size > MODPLUG_FILE_LIMIT) {
- g_warning("file too large");
- return NULL;
+ if (size > MODPLUG_FILE_LIMIT) {
+ LogWarning(modplug_domain, "file too large");
+ return nullptr;
}
//known/unknown size, preallocate array, lets read in chunks
- if (is->size > 0) {
- bdatas = g_byte_array_sized_new(is->size);
+ GByteArray *bdatas;
+ if (size > 0) {
+ bdatas = g_byte_array_sized_new(size);
} else {
bdatas = g_byte_array_sized_new(MODPLUG_PREALLOC_BLOCK);
}
- data = g_malloc(MODPLUG_READ_BLOCK);
+ unsigned char *data = (unsigned char *)g_malloc(MODPLUG_READ_BLOCK);
while (true) {
- ret = decoder_read(decoder, is, data, MODPLUG_READ_BLOCK);
+ size_t ret = decoder_read(decoder, is, data,
+ MODPLUG_READ_BLOCK);
if (ret == 0) {
- if (input_stream_lock_eof(is))
+ if (is->LockIsEOF())
/* end of file */
break;
/* I/O error - skip this song */
g_free(data);
g_byte_array_free(bdatas, true);
- return NULL;
+ return nullptr;
}
- if (bdatas->len + ret > MODPLUG_FILE_LIMIT) {
- g_warning("stream too large\n");
+ if (goffset(bdatas->len + ret) > MODPLUG_FILE_LIMIT) {
+ LogWarning(modplug_domain, "stream too large");
g_free(data);
g_byte_array_free(bdatas, TRUE);
- return NULL;
+ return nullptr;
}
g_byte_array_append(bdatas, data, ret);
@@ -94,15 +98,13 @@ mod_decode(struct decoder *decoder, struct input_stream *is)
ModPlugFile *f;
ModPlug_Settings settings;
GByteArray *bdatas;
- struct audio_format audio_format;
int ret;
char audio_buffer[MODPLUG_FRAME_SIZE];
- enum decoder_command cmd = DECODE_COMMAND_NONE;
bdatas = mod_loadfile(decoder, is);
if (!bdatas) {
- g_warning("could not load stream\n");
+ LogWarning(modplug_domain, "could not load stream");
return;
}
@@ -118,26 +120,28 @@ mod_decode(struct decoder *decoder, struct input_stream *is)
f = ModPlug_Load(bdatas->data, bdatas->len);
g_byte_array_free(bdatas, TRUE);
if (!f) {
- g_warning("could not decode stream\n");
+ LogWarning(modplug_domain, "could not decode stream");
return;
}
- audio_format_init(&audio_format, 44100, SAMPLE_FORMAT_S16, 2);
- assert(audio_format_valid(&audio_format));
+ static constexpr AudioFormat audio_format(44100, SampleFormat::S16, 2);
+ assert(audio_format.IsValid());
- decoder_initialized(decoder, &audio_format,
- is->seekable, ModPlug_GetLength(f) / 1000.0);
+ decoder_initialized(decoder, audio_format,
+ is->IsSeekable(),
+ ModPlug_GetLength(f) / 1000.0);
+ DecoderCommand cmd;
do {
ret = ModPlug_Read(f, audio_buffer, MODPLUG_FRAME_SIZE);
if (ret <= 0)
break;
- cmd = decoder_data(decoder, NULL,
+ cmd = decoder_data(decoder, nullptr,
audio_buffer, ret,
0);
- if (cmd == DECODE_COMMAND_SEEK) {
+ if (cmd == DecoderCommand::SEEK) {
float where = decoder_seek_where(decoder);
ModPlug_Seek(f, (int)(where * 1000.0));
@@ -145,7 +149,7 @@ mod_decode(struct decoder *decoder, struct input_stream *is)
decoder_command_finished(decoder);
}
- } while (cmd != DECODE_COMMAND_STOP);
+ } while (cmd != DecoderCommand::STOP);
ModPlug_Unload(f);
}
@@ -157,20 +161,20 @@ modplug_scan_stream(struct input_stream *is,
ModPlugFile *f;
GByteArray *bdatas;
- bdatas = mod_loadfile(NULL, is);
+ bdatas = mod_loadfile(nullptr, is);
if (!bdatas)
return false;
f = ModPlug_Load(bdatas->data, bdatas->len);
g_byte_array_free(bdatas, TRUE);
- if (f == NULL)
+ if (f == nullptr)
return false;
tag_handler_invoke_duration(handler, handler_ctx,
ModPlug_GetLength(f) / 1000);
const char *title = ModPlug_GetName(f);
- if (title != NULL)
+ if (title != nullptr)
tag_handler_invoke_tag(handler, handler_ctx,
TAG_TITLE, title);
@@ -183,12 +187,18 @@ static const char *const mod_suffixes[] = {
"669", "amf", "ams", "dbm", "dfm", "dsm", "far", "it",
"med", "mdl", "mod", "mtm", "mt2", "okt", "s3m", "stm",
"ult", "umx", "xm",
- NULL
+ nullptr
};
const struct decoder_plugin modplug_decoder_plugin = {
- .name = "modplug",
- .stream_decode = mod_decode,
- .scan_stream = modplug_scan_stream,
- .suffixes = mod_suffixes,
+ "modplug",
+ nullptr,
+ nullptr,
+ mod_decode,
+ nullptr,
+ nullptr,
+ modplug_scan_stream,
+ nullptr,
+ mod_suffixes,
+ nullptr,
};
diff --git a/src/decoder/ModplugDecoderPlugin.hxx b/src/decoder/ModplugDecoderPlugin.hxx
new file mode 100644
index 000000000..fefb02b05
--- /dev/null
+++ b/src/decoder/ModplugDecoderPlugin.hxx
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2003-2013 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_MODPLUG_HXX
+#define MPD_DECODER_MODPLUG_HXX
+
+extern const struct decoder_plugin modplug_decoder_plugin;
+
+#endif
diff --git a/src/decoder/mpcdec_decoder_plugin.c b/src/decoder/MpcdecDecoderPlugin.cxx
index d4768b35b..c0785accc 100644
--- a/src/decoder/mpcdec_decoder_plugin.c
+++ b/src/decoder/MpcdecDecoderPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,75 +18,72 @@
*/
#include "config.h"
-#include "decoder_api.h"
-#include "audio_check.h"
-#include "tag_handler.h"
+#include "MpcdecDecoderPlugin.hxx"
+#include "DecoderAPI.hxx"
+#include "InputStream.hxx"
+#include "CheckAudioFormat.hxx"
+#include "tag/TagHandler.hxx"
+#include "util/Error.hxx"
+#include "util/Domain.hxx"
+#include "Log.hxx"
-#ifdef MPC_IS_OLD_API
-#include <mpcdec/mpcdec.h>
-#else
#include <mpc/mpcdec.h>
-#include <math.h>
-#endif
#include <glib.h>
#include <assert.h>
#include <unistd.h>
-
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "mpcdec"
+#include <math.h>
struct mpc_decoder_data {
struct input_stream *is;
struct decoder *decoder;
};
-#ifdef MPC_IS_OLD_API
-#define cb_first_arg void *vdata
-#define cb_data vdata
-#else
-#define cb_first_arg mpc_reader *reader
-#define cb_data reader->data
-#endif
+static constexpr Domain mpcdec_domain("mpcdec");
static mpc_int32_t
-mpc_read_cb(cb_first_arg, void *ptr, mpc_int32_t size)
+mpc_read_cb(mpc_reader *reader, void *ptr, mpc_int32_t size)
{
- struct mpc_decoder_data *data = (struct mpc_decoder_data *) cb_data;
+ struct mpc_decoder_data *data =
+ (struct mpc_decoder_data *)reader->data;
return decoder_read(data->decoder, data->is, ptr, size);
}
static mpc_bool_t
-mpc_seek_cb(cb_first_arg, mpc_int32_t offset)
+mpc_seek_cb(mpc_reader *reader, mpc_int32_t offset)
{
- struct mpc_decoder_data *data = (struct mpc_decoder_data *) cb_data;
+ struct mpc_decoder_data *data =
+ (struct mpc_decoder_data *)reader->data;
- return input_stream_lock_seek(data->is, offset, SEEK_SET, NULL);
+ return data->is->LockSeek(offset, SEEK_SET, IgnoreError());
}
static mpc_int32_t
-mpc_tell_cb(cb_first_arg)
+mpc_tell_cb(mpc_reader *reader)
{
- struct mpc_decoder_data *data = (struct mpc_decoder_data *) cb_data;
+ struct mpc_decoder_data *data =
+ (struct mpc_decoder_data *)reader->data;
- return (long)(data->is->offset);
+ return (long)data->is->GetOffset();
}
static mpc_bool_t
-mpc_canseek_cb(cb_first_arg)
+mpc_canseek_cb(mpc_reader *reader)
{
- struct mpc_decoder_data *data = (struct mpc_decoder_data *) cb_data;
+ struct mpc_decoder_data *data =
+ (struct mpc_decoder_data *)reader->data;
- return data->is->seekable;
+ return data->is->IsSeekable();
}
static mpc_int32_t
-mpc_getsize_cb(cb_first_arg)
+mpc_getsize_cb(mpc_reader *reader)
{
- struct mpc_decoder_data *data = (struct mpc_decoder_data *) cb_data;
+ struct mpc_decoder_data *data =
+ (struct mpc_decoder_data *)reader->data;
- return data->is->size;
+ return data->is->GetSize();
}
/* this _looks_ performance-critical, don't de-inline -- eric */
@@ -135,31 +132,13 @@ mpc_to_mpd_buffer(int32_t *dest, const MPC_SAMPLE_FORMAT *src,
static void
mpcdec_decode(struct decoder *mpd_decoder, struct input_stream *is)
{
-#ifdef MPC_IS_OLD_API
- mpc_decoder decoder;
-#else
- mpc_demux *demux;
- mpc_frame_info frame;
- mpc_status status;
-#endif
- mpc_reader reader;
- mpc_streaminfo info;
- GError *error = NULL;
- struct audio_format audio_format;
-
- struct mpc_decoder_data data;
-
MPC_SAMPLE_FORMAT sample_buffer[MPC_DECODER_BUFFER_LENGTH];
- mpc_uint32_t ret;
- int32_t chunk[G_N_ELEMENTS(sample_buffer)];
- long bit_rate = 0;
- mpc_uint32_t vbr_update_bits;
- enum decoder_command cmd = DECODE_COMMAND_NONE;
-
+ struct mpc_decoder_data data;
data.is = is;
data.decoder = mpd_decoder;
+ mpc_reader reader;
reader.read = mpc_read_cb;
reader.seek = mpc_seek_cb;
reader.tell = mpc_tell_cb;
@@ -167,137 +146,94 @@ mpcdec_decode(struct decoder *mpd_decoder, struct input_stream *is)
reader.canseek = mpc_canseek_cb;
reader.data = &data;
-#ifdef MPC_IS_OLD_API
- mpc_streaminfo_init(&info);
-
- if ((ret = mpc_streaminfo_read(&info, &reader)) != ERROR_CODE_OK) {
- if (decoder_get_command(mpd_decoder) != DECODE_COMMAND_STOP)
- g_warning("Not a valid musepack stream\n");
- return;
- }
-
- mpc_decoder_setup(&decoder, &reader);
-
- if (!mpc_decoder_initialize(&decoder, &info)) {
- if (decoder_get_command(mpd_decoder) != DECODE_COMMAND_STOP)
- g_warning("Not a valid musepack stream\n");
- return;
- }
-#else
- demux = mpc_demux_init(&reader);
- if (demux == NULL) {
- if (decoder_get_command(mpd_decoder) != DECODE_COMMAND_STOP)
- g_warning("Not a valid musepack stream");
+ mpc_demux *demux = mpc_demux_init(&reader);
+ if (demux == nullptr) {
+ if (decoder_get_command(mpd_decoder) != DecoderCommand::STOP)
+ LogWarning(mpcdec_domain,
+ "Not a valid musepack stream");
return;
}
+ mpc_streaminfo info;
mpc_demux_get_info(demux, &info);
-#endif
- if (!audio_format_init_checked(&audio_format, info.sample_freq,
- SAMPLE_FORMAT_S24_P32,
- info.channels, &error)) {
- g_warning("%s", error->message);
- g_error_free(error);
-#ifndef MPC_IS_OLD_API
+ Error error;
+ AudioFormat audio_format;
+ if (!audio_format_init_checked(audio_format, info.sample_freq,
+ SampleFormat::S24_P32,
+ info.channels, error)) {
+ LogError(error);
mpc_demux_exit(demux);
-#endif
return;
}
struct replay_gain_info replay_gain_info;
replay_gain_info_init(&replay_gain_info);
-#ifdef MPC_IS_OLD_API
- replay_gain_info.tuples[REPLAY_GAIN_ALBUM].gain = info.gain_album * 0.01;
- replay_gain_info.tuples[REPLAY_GAIN_ALBUM].peak = info.peak_album / 32767.0;
- replay_gain_info.tuples[REPLAY_GAIN_TRACK].gain = info.gain_title * 0.01;
- replay_gain_info.tuples[REPLAY_GAIN_TRACK].peak = info.peak_title / 32767.0;
-#else
replay_gain_info.tuples[REPLAY_GAIN_ALBUM].gain = MPC_OLD_GAIN_REF - (info.gain_album / 256.);
replay_gain_info.tuples[REPLAY_GAIN_ALBUM].peak = pow(10, info.peak_album / 256. / 20) / 32767;
replay_gain_info.tuples[REPLAY_GAIN_TRACK].gain = MPC_OLD_GAIN_REF - (info.gain_title / 256.);
replay_gain_info.tuples[REPLAY_GAIN_TRACK].peak = pow(10, info.peak_title / 256. / 20) / 32767;
-#endif
decoder_replay_gain(mpd_decoder, &replay_gain_info);
- decoder_initialized(mpd_decoder, &audio_format,
- is->seekable,
+ decoder_initialized(mpd_decoder, audio_format,
+ is->IsSeekable(),
mpc_streaminfo_get_length(&info));
+ DecoderCommand cmd = DecoderCommand::NONE;
do {
- if (cmd == DECODE_COMMAND_SEEK) {
+ if (cmd == DecoderCommand::SEEK) {
mpc_int64_t where = decoder_seek_where(mpd_decoder) *
audio_format.sample_rate;
bool success;
-#ifdef MPC_IS_OLD_API
- success = mpc_decoder_seek_sample(&decoder, where);
-#else
success = mpc_demux_seek_sample(demux, where)
== MPC_STATUS_OK;
-#endif
if (success)
decoder_command_finished(mpd_decoder);
else
decoder_seek_error(mpd_decoder);
}
- vbr_update_bits = 0;
+ mpc_uint32_t vbr_update_bits = 0;
-#ifdef MPC_IS_OLD_API
- mpc_uint32_t vbr_update_acc = 0;
-
- ret = mpc_decoder_decode(&decoder, sample_buffer,
- &vbr_update_acc, &vbr_update_bits);
- if (ret == 0 || ret == (mpc_uint32_t)-1)
- break;
-#else
+ mpc_frame_info frame;
frame.buffer = (MPC_SAMPLE_FORMAT *)sample_buffer;
- status = mpc_demux_decode(demux, &frame);
+ mpc_status status = mpc_demux_decode(demux, &frame);
if (status != MPC_STATUS_OK) {
- g_warning("Failed to decode sample");
+ LogWarning(mpcdec_domain,
+ "Failed to decode sample");
break;
}
if (frame.bits == -1)
break;
- ret = frame.samples;
-#endif
-
+ mpc_uint32_t ret = frame.samples;
ret *= info.channels;
+ int32_t chunk[G_N_ELEMENTS(sample_buffer)];
mpc_to_mpd_buffer(chunk, sample_buffer, ret);
- bit_rate = vbr_update_bits * audio_format.sample_rate
+ long bit_rate = vbr_update_bits * audio_format.sample_rate
/ 1152 / 1000;
cmd = decoder_data(mpd_decoder, is,
chunk, ret * sizeof(chunk[0]),
bit_rate);
- } while (cmd != DECODE_COMMAND_STOP);
+ } while (cmd != DecoderCommand::STOP);
-#ifndef MPC_IS_OLD_API
mpc_demux_exit(demux);
-#endif
}
static float
mpcdec_get_file_duration(struct input_stream *is)
{
- float total_time = -1;
-
- mpc_reader reader;
-#ifndef MPC_IS_OLD_API
- mpc_demux *demux;
-#endif
- mpc_streaminfo info;
struct mpc_decoder_data data;
-
data.is = is;
- data.decoder = NULL;
+ data.decoder = nullptr;
+ mpc_reader reader;
reader.read = mpc_read_cb;
reader.seek = mpc_seek_cb;
reader.tell = mpc_tell_cb;
@@ -305,23 +241,15 @@ mpcdec_get_file_duration(struct input_stream *is)
reader.canseek = mpc_canseek_cb;
reader.data = &data;
-#ifdef MPC_IS_OLD_API
- mpc_streaminfo_init(&info);
-
- if (mpc_streaminfo_read(&info, &reader) != ERROR_CODE_OK)
- return -1;
-#else
- demux = mpc_demux_init(&reader);
- if (demux == NULL)
+ mpc_demux *demux = mpc_demux_init(&reader);
+ if (demux == nullptr)
return -1;
+ mpc_streaminfo info;
mpc_demux_get_info(demux, &info);
mpc_demux_exit(demux);
-#endif
-
- total_time = mpc_streaminfo_get_length(&info);
- return total_time;
+ return mpc_streaminfo_get_length(&info);
}
static bool
@@ -337,11 +265,17 @@ mpcdec_scan_stream(struct input_stream *is,
return true;
}
-static const char *const mpcdec_suffixes[] = { "mpc", NULL };
+static const char *const mpcdec_suffixes[] = { "mpc", nullptr };
const struct decoder_plugin mpcdec_decoder_plugin = {
- .name = "mpcdec",
- .stream_decode = mpcdec_decode,
- .scan_stream = mpcdec_scan_stream,
- .suffixes = mpcdec_suffixes,
+ "mpcdec",
+ nullptr,
+ nullptr,
+ mpcdec_decode,
+ nullptr,
+ nullptr,
+ mpcdec_scan_stream,
+ nullptr,
+ mpcdec_suffixes,
+ nullptr,
};
diff --git a/src/decoder/MpcdecDecoderPlugin.hxx b/src/decoder/MpcdecDecoderPlugin.hxx
new file mode 100644
index 000000000..7e9b51cdb
--- /dev/null
+++ b/src/decoder/MpcdecDecoderPlugin.hxx
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2003-2013 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_MPCDEC_HXX
+#define MPD_DECODER_MPCDEC_HXX
+
+extern const struct decoder_plugin mpcdec_decoder_plugin;
+
+#endif
diff --git a/src/decoder/mpg123_decoder_plugin.c b/src/decoder/Mpg123DecoderPlugin.cxx
index 657a9c889..928af39e6 100644
--- a/src/decoder/mpg123_decoder_plugin.c
+++ b/src/decoder/Mpg123DecoderPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,20 +18,23 @@
*/
#include "config.h" /* must be first for large file support */
-#include "decoder_api.h"
-#include "audio_check.h"
-#include "tag_handler.h"
+#include "Mpg123DecoderPlugin.hxx"
+#include "DecoderAPI.hxx"
+#include "CheckAudioFormat.hxx"
+#include "tag/TagHandler.hxx"
+#include "util/Error.hxx"
+#include "util/Domain.hxx"
+#include "Log.hxx"
#include <glib.h>
#include <mpg123.h>
#include <stdio.h>
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "mpg123"
+static constexpr Domain mpg123_domain("mpg123");
static bool
-mpd_mpg123_init(G_GNUC_UNUSED const struct config_param *param)
+mpd_mpg123_init(gcc_unused const config_param &param)
{
mpg123_init();
@@ -55,9 +58,8 @@ mpd_mpg123_finish(void)
*/
static bool
mpd_mpg123_open(mpg123_handle *handle, const char *path_fs,
- struct audio_format *audio_format)
+ AudioFormat &audio_format)
{
- GError *gerror = NULL;
char *path_dup;
int error;
int channels, encoding;
@@ -69,8 +71,9 @@ mpd_mpg123_open(mpg123_handle *handle, const char *path_fs,
error = mpg123_open(handle, path_dup);
g_free(path_dup);
if (error != MPG123_OK) {
- g_warning("libmpg123 failed to open %s: %s",
- path_fs, mpg123_plain_strerror(error));
+ FormatWarning(mpg123_domain,
+ "libmpg123 failed to open %s: %s",
+ path_fs, mpg123_plain_strerror(error));
return false;
}
@@ -78,21 +81,24 @@ mpd_mpg123_open(mpg123_handle *handle, const char *path_fs,
error = mpg123_getformat(handle, &rate, &channels, &encoding);
if (error != MPG123_OK) {
- g_warning("mpg123_getformat() failed: %s",
- mpg123_plain_strerror(error));
+ FormatWarning(mpg123_domain,
+ "mpg123_getformat() failed: %s",
+ mpg123_plain_strerror(error));
return false;
}
if (encoding != MPG123_ENC_SIGNED_16) {
/* other formats not yet implemented */
- g_warning("expected MPG123_ENC_SIGNED_16, got %d", encoding);
+ FormatWarning(mpg123_domain,
+ "expected MPG123_ENC_SIGNED_16, got %d",
+ encoding);
return false;
}
- if (!audio_format_init_checked(audio_format, rate, SAMPLE_FORMAT_S16,
- channels, &gerror)) {
- g_warning("%s", gerror->message);
- g_error_free(gerror);
+ Error error2;
+ if (!audio_format_init_checked(audio_format, rate, SampleFormat::S16,
+ channels, error2)) {
+ LogError(error2);
return false;
}
@@ -102,23 +108,23 @@ mpd_mpg123_open(mpg123_handle *handle, const char *path_fs,
static void
mpd_mpg123_file_decode(struct decoder *decoder, const char *path_fs)
{
- struct audio_format audio_format;
mpg123_handle *handle;
int error;
off_t num_samples;
- enum decoder_command cmd;
struct mpg123_frameinfo info;
/* open the file */
- handle = mpg123_new(NULL, &error);
- if (handle == NULL) {
- g_warning("mpg123_new() failed: %s",
- mpg123_plain_strerror(error));
+ handle = mpg123_new(nullptr, &error);
+ if (handle == nullptr) {
+ FormatError(mpg123_domain,
+ "mpg123_new() failed: %s",
+ mpg123_plain_strerror(error));
return;
}
- if (!mpd_mpg123_open(handle, path_fs, &audio_format)) {
+ AudioFormat audio_format;
+ if (!mpd_mpg123_open(handle, path_fs, audio_format)) {
mpg123_delete(handle);
return;
}
@@ -127,7 +133,7 @@ mpd_mpg123_file_decode(struct decoder *decoder, const char *path_fs)
/* tell MPD core we're ready */
- decoder_initialized(decoder, &audio_format, true,
+ decoder_initialized(decoder, audio_format, true,
(float)num_samples /
(float)audio_format.sample_rate);
@@ -148,6 +154,7 @@ mpd_mpg123_file_decode(struct decoder *decoder, const char *path_fs)
/* the decoder main loop */
+ DecoderCommand cmd;
do {
unsigned char buffer[8192];
size_t nbytes;
@@ -157,8 +164,9 @@ mpd_mpg123_file_decode(struct decoder *decoder, const char *path_fs)
error = mpg123_read(handle, buffer, sizeof(buffer), &nbytes);
if (error != MPG123_OK) {
if (error != MPG123_DONE)
- g_warning("mpg123_read() failed: %s",
- mpg123_plain_strerror(error));
+ FormatWarning(mpg123_domain,
+ "mpg123_read() failed: %s",
+ mpg123_plain_strerror(error));
break;
}
@@ -172,9 +180,9 @@ mpd_mpg123_file_decode(struct decoder *decoder, const char *path_fs)
/* send to MPD */
- cmd = decoder_data(decoder, NULL, buffer, nbytes, info.bitrate);
+ cmd = decoder_data(decoder, nullptr, buffer, nbytes, info.bitrate);
- if (cmd == DECODE_COMMAND_SEEK) {
+ if (cmd == DecoderCommand::SEEK) {
off_t c = decoder_seek_where(decoder)*audio_format.sample_rate;
c = mpg123_seek(handle, c, SEEK_SET);
if (c < 0)
@@ -184,9 +192,9 @@ mpd_mpg123_file_decode(struct decoder *decoder, const char *path_fs)
decoder_timestamp(decoder, c/(double)audio_format.sample_rate);
}
- cmd = DECODE_COMMAND_NONE;
+ cmd = DecoderCommand::NONE;
}
- } while (cmd == DECODE_COMMAND_NONE);
+ } while (cmd == DecoderCommand::NONE);
/* cleanup */
@@ -197,19 +205,20 @@ static bool
mpd_mpg123_scan_file(const char *path_fs,
const struct tag_handler *handler, void *handler_ctx)
{
- struct audio_format audio_format;
mpg123_handle *handle;
int error;
off_t num_samples;
- handle = mpg123_new(NULL, &error);
- if (handle == NULL) {
- g_warning("mpg123_new() failed: %s",
- mpg123_plain_strerror(error));
+ handle = mpg123_new(nullptr, &error);
+ if (handle == nullptr) {
+ FormatError(mpg123_domain,
+ "mpg123_new() failed: %s",
+ mpg123_plain_strerror(error));
return false;
}
- if (!mpd_mpg123_open(handle, path_fs, &audio_format)) {
+ AudioFormat audio_format;
+ if (!mpd_mpg123_open(handle, path_fs, audio_format)) {
mpg123_delete(handle);
return false;
}
@@ -231,15 +240,19 @@ mpd_mpg123_scan_file(const char *path_fs,
static const char *const mpg123_suffixes[] = {
"mp3",
- NULL
+ nullptr
};
const struct decoder_plugin mpg123_decoder_plugin = {
- .name = "mpg123",
- .init = mpd_mpg123_init,
- .finish = mpd_mpg123_finish,
- .file_decode = mpd_mpg123_file_decode,
+ "mpg123",
+ mpd_mpg123_init,
+ mpd_mpg123_finish,
/* streaming not yet implemented */
- .scan_file = mpd_mpg123_scan_file,
- .suffixes = mpg123_suffixes,
+ nullptr,
+ mpd_mpg123_file_decode,
+ mpd_mpg123_scan_file,
+ nullptr,
+ nullptr,
+ mpg123_suffixes,
+ nullptr,
};
diff --git a/src/decoder/Mpg123DecoderPlugin.hxx b/src/decoder/Mpg123DecoderPlugin.hxx
new file mode 100644
index 000000000..273b03eaf
--- /dev/null
+++ b/src/decoder/Mpg123DecoderPlugin.hxx
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2003-2013 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_MPG123_HXX
+#define MPD_DECODER_MPG123_HXX
+
+extern const struct decoder_plugin mpg123_decoder_plugin;
+
+#endif
diff --git a/src/decoder/_ogg_common.c b/src/decoder/OggCodec.cxx
index 09d2712da..d7e5b7642 100644
--- a/src/decoder/_ogg_common.c
+++ b/src/decoder/OggCodec.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -22,25 +22,29 @@
*/
#include "config.h"
-#include "_ogg_common.h"
+#include "OggCodec.hxx"
-ogg_stream_type ogg_stream_type_detect(struct input_stream *inStream)
+#include <string.h>
+
+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/OggCodec.hxx
index 85e4ebba6..eb709286b 100644
--- a/src/decoder/_ogg_common.h
+++ b/src/decoder/OggCodec.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -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_HXX
+#define MPD_OGG_CODEC_HXX
-#include "decoder_api.h"
+#include "DecoderAPI.hxx"
-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/OggFind.cxx b/src/decoder/OggFind.cxx
new file mode 100644
index 000000000..9df4c6455
--- /dev/null
+++ b/src/decoder/OggFind.cxx
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2003-2013 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 "OggFind.hxx"
+#include "OggSyncState.hxx"
+
+bool
+OggFindEOS(OggSyncState &oy, ogg_stream_state &os, ogg_packet &packet)
+{
+ while (true) {
+ int r = ogg_stream_packetout(&os, &packet);
+ if (r == 0) {
+ if (!oy.ExpectPageIn(os))
+ return false;
+
+ continue;
+ } else if (r > 0 && packet.e_o_s)
+ return true;
+ }
+}
diff --git a/src/decoder/OggFind.hxx b/src/decoder/OggFind.hxx
new file mode 100644
index 000000000..7d18d2067
--- /dev/null
+++ b/src/decoder/OggFind.hxx
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2003-2013 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_FIND_HXX
+#define MPD_OGG_FIND_HXX
+
+#include "check.h"
+
+#include <ogg/ogg.h>
+
+class OggSyncState;
+
+/**
+ * Skip all pages/packets until an end-of-stream (EOS) packet for the
+ * specified stream is found.
+ *
+ * @return true if the EOS packet was found
+ */
+bool
+OggFindEOS(OggSyncState &oy, ogg_stream_state &os, ogg_packet &packet);
+
+#endif
diff --git a/src/decoder/OggSyncState.hxx b/src/decoder/OggSyncState.hxx
new file mode 100644
index 000000000..eaeb9bd8c
--- /dev/null
+++ b/src/decoder/OggSyncState.hxx
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2003-2013 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_SYNC_STATE_HXX
+#define MPD_OGG_SYNC_STATE_HXX
+
+#include "check.h"
+#include "OggUtil.hxx"
+
+#include <ogg/ogg.h>
+
+#include <stddef.h>
+
+/**
+ * Wrapper for an ogg_sync_state.
+ */
+class OggSyncState {
+ ogg_sync_state oy;
+
+ input_stream &is;
+ struct decoder *const decoder;
+
+public:
+ OggSyncState(input_stream &_is, struct decoder *const _decoder=nullptr)
+ :is(_is), decoder(_decoder) {
+ ogg_sync_init(&oy);
+ }
+
+ ~OggSyncState() {
+ ogg_sync_clear(&oy);
+ }
+
+ void Reset() {
+ ogg_sync_reset(&oy);
+ }
+
+ bool Feed(size_t size) {
+ return OggFeed(oy, decoder, &is, size);
+ }
+
+ bool ExpectPage(ogg_page &page) {
+ return OggExpectPage(oy, page, decoder, &is);
+ }
+
+ bool ExpectFirstPage(ogg_stream_state &os) {
+ return OggExpectFirstPage(oy, os, decoder, &is);
+ }
+
+ bool ExpectPageIn(ogg_stream_state &os) {
+ return OggExpectPageIn(oy, os, decoder, &is);
+ }
+
+ bool ExpectPageSeek(ogg_page &page) {
+ return OggExpectPageSeek(oy, page, decoder, &is);
+ }
+
+ bool ExpectPageSeekIn(ogg_stream_state &os) {
+ return OggExpectPageSeekIn(oy, os, decoder, &is);
+ }
+};
+
+#endif
diff --git a/src/decoder/OggUtil.cxx b/src/decoder/OggUtil.cxx
new file mode 100644
index 000000000..0e2f48f51
--- /dev/null
+++ b/src/decoder/OggUtil.cxx
@@ -0,0 +1,118 @@
+/*
+ * 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"
+#include "DecoderAPI.hxx"
+
+bool
+OggFeed(ogg_sync_state &oy, struct decoder *decoder,
+ 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,
+ decoder *decoder, 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;
+ }
+}
+
+bool
+OggExpectFirstPage(ogg_sync_state &oy, ogg_stream_state &os,
+ decoder *decoder, input_stream *is)
+{
+ ogg_page page;
+ if (!OggExpectPage(oy, page, decoder, is))
+ return false;
+
+ ogg_stream_init(&os, ogg_page_serialno(&page));
+ ogg_stream_pagein(&os, &page);
+ return true;
+}
+
+bool
+OggExpectPageIn(ogg_sync_state &oy, ogg_stream_state &os,
+ decoder *decoder, input_stream *is)
+{
+ ogg_page page;
+ if (!OggExpectPage(oy, page, decoder, is))
+ return false;
+
+ ogg_stream_pagein(&os, &page);
+ return true;
+}
+
+bool
+OggExpectPageSeek(ogg_sync_state &oy, ogg_page &page,
+ decoder *decoder, input_stream *input_stream)
+{
+ size_t remaining_skipped = 16384;
+
+ while (true) {
+ int r = ogg_sync_pageseek(&oy, &page);
+ if (r > 0)
+ return true;
+
+ if (r < 0) {
+ /* skipped -r bytes */
+ size_t nbytes = -r;
+ if (nbytes > remaining_skipped)
+ /* still no ogg page - we lost our
+ patience, abort */
+ return false;
+
+ remaining_skipped -= nbytes;
+ continue;
+ }
+
+ if (!OggFeed(oy, decoder, input_stream, 1024))
+ return false;
+ }
+}
+
+bool
+OggExpectPageSeekIn(ogg_sync_state &oy, ogg_stream_state &os,
+ decoder *decoder, input_stream *is)
+{
+ ogg_page page;
+ if (!OggExpectPageSeek(oy, page, decoder, is))
+ return false;
+
+ ogg_stream_pagein(&os, &page);
+ return true;
+}
diff --git a/src/decoder/OggUtil.hxx b/src/decoder/OggUtil.hxx
new file mode 100644
index 000000000..324797815
--- /dev/null
+++ b/src/decoder/OggUtil.hxx
@@ -0,0 +1,87 @@
+/*
+ * 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>
+
+struct input_stream;
+struct decoder;
+
+/**
+ * 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, input_stream *is,
+ 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,
+ decoder *decoder, input_stream *input_stream);
+
+/**
+ * Combines OggExpectPage(), ogg_stream_init() and
+ * ogg_stream_pagein().
+ *
+ * @return true if the stream was initialized and the first page was
+ * delivered to it
+ */
+bool
+OggExpectFirstPage(ogg_sync_state &oy, ogg_stream_state &os,
+ decoder *decoder, input_stream *is);
+
+/**
+ * Combines OggExpectPage() and ogg_stream_pagein().
+ *
+ * @return true if a page was delivered to the stream
+ */
+bool
+OggExpectPageIn(ogg_sync_state &oy, ogg_stream_state &os,
+ decoder *decoder, input_stream *is);
+
+/**
+ * Like OggExpectPage(), but allow skipping garbage (after seeking).
+ */
+bool
+OggExpectPageSeek(ogg_sync_state &oy, ogg_page &page,
+ decoder *decoder, input_stream *input_stream);
+
+/**
+ * Combines OggExpectPageSeek() and ogg_stream_pagein().
+ *
+ * @return true if a page was delivered to the stream
+ */
+bool
+OggExpectPageSeekIn(ogg_sync_state &oy, ogg_stream_state &os,
+ decoder *decoder, input_stream *is);
+
+#endif
diff --git a/src/decoder/OpusDecoderPlugin.cxx b/src/decoder/OpusDecoderPlugin.cxx
new file mode 100644
index 000000000..96c52a083
--- /dev/null
+++ b/src/decoder/OpusDecoderPlugin.cxx
@@ -0,0 +1,403 @@
+/*
+ * 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 "OpusDomain.hxx"
+#include "OpusHead.hxx"
+#include "OpusTags.hxx"
+#include "OggUtil.hxx"
+#include "OggFind.hxx"
+#include "OggSyncState.hxx"
+#include "DecoderAPI.hxx"
+#include "OggCodec.hxx"
+#include "CheckAudioFormat.hxx"
+#include "tag/TagHandler.hxx"
+#include "tag/TagBuilder.hxx"
+#include "InputStream.hxx"
+#include "util/Error.hxx"
+#include "Log.hxx"
+
+#include <opus.h>
+#include <ogg/ogg.h>
+
+#include <glib.h>
+
+#include <stdio.h>
+#include <string.h>
+
+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(gcc_unused const config_param &param)
+{
+ LogDebug(opus_domain, opus_get_version_string());
+
+ return true;
+}
+
+class MPDOpusDecoder {
+ struct decoder *decoder;
+ struct input_stream *input_stream;
+
+ ogg_stream_state os;
+
+ OpusDecoder *opus_decoder;
+ opus_int16 *output_buffer;
+ unsigned output_size;
+
+ bool os_initialized;
+ bool found_opus;
+
+ int opus_serialno;
+
+ size_t frame_size;
+
+public:
+ MPDOpusDecoder(struct decoder *_decoder,
+ struct input_stream *_input_stream)
+ :decoder(_decoder), input_stream(_input_stream),
+ opus_decoder(nullptr),
+ output_buffer(nullptr), output_size(0),
+ os_initialized(false), found_opus(false) {}
+ ~MPDOpusDecoder();
+
+ bool ReadFirstPage(OggSyncState &oy);
+ bool ReadNextPage(OggSyncState &oy);
+
+ DecoderCommand HandlePackets();
+ DecoderCommand HandlePacket(const ogg_packet &packet);
+ DecoderCommand HandleBOS(const ogg_packet &packet);
+ DecoderCommand HandleTags(const ogg_packet &packet);
+ DecoderCommand 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);
+}
+
+inline bool
+MPDOpusDecoder::ReadFirstPage(OggSyncState &oy)
+{
+ assert(!os_initialized);
+
+ if (!oy.ExpectFirstPage(os))
+ return false;
+
+ os_initialized = true;
+ return true;
+}
+
+inline bool
+MPDOpusDecoder::ReadNextPage(OggSyncState &oy)
+{
+ assert(os_initialized);
+
+ ogg_page page;
+ if (!oy.ExpectPage(page))
+ return false;
+
+ const auto page_serialno = ogg_page_serialno(&page);
+ if (page_serialno != os.serialno)
+ ogg_stream_reset_serialno(&os, page_serialno);
+
+ ogg_stream_pagein(&os, &page);
+ return true;
+}
+
+inline DecoderCommand
+MPDOpusDecoder::HandlePackets()
+{
+ ogg_packet packet;
+ while (ogg_stream_packetout(&os, &packet) == 1) {
+ auto cmd = HandlePacket(packet);
+ if (cmd != DecoderCommand::NONE)
+ return cmd;
+ }
+
+ return DecoderCommand::NONE;
+}
+
+inline DecoderCommand
+MPDOpusDecoder::HandlePacket(const ogg_packet &packet)
+{
+ if (packet.e_o_s)
+ return DecoderCommand::STOP;
+
+ if (packet.b_o_s)
+ return HandleBOS(packet);
+ else if (!found_opus)
+ return DecoderCommand::STOP;
+
+ if (IsOpusTags(packet))
+ return HandleTags(packet);
+
+ return HandleAudio(packet);
+}
+
+inline DecoderCommand
+MPDOpusDecoder::HandleBOS(const ogg_packet &packet)
+{
+ assert(packet.b_o_s);
+
+ if (found_opus || !IsOpusHead(packet))
+ return DecoderCommand::STOP;
+
+ unsigned channels;
+ if (!ScanOpusHeader(packet.packet, packet.bytes, channels) ||
+ !audio_valid_channel_count(channels))
+ return DecoderCommand::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) {
+ FormatError(opus_domain, "libopus error: %s",
+ opus_strerror(opus_error));
+ return DecoderCommand::STOP;
+ }
+
+ const AudioFormat audio_format(opus_sample_rate,
+ SampleFormat::S16, channels);
+ decoder_initialized(decoder, audio_format, false, -1);
+ frame_size = audio_format.GetFrameSize();
+
+ /* 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);
+}
+
+inline DecoderCommand
+MPDOpusDecoder::HandleTags(const ogg_packet &packet)
+{
+ TagBuilder tag_builder;
+
+ DecoderCommand cmd;
+ if (ScanOpusTags(packet.packet, packet.bytes,
+ &add_tag_handler, &tag_builder) &&
+ !tag_builder.IsEmpty()) {
+ Tag tag;
+ tag_builder.Commit(tag);
+ cmd = decoder_tag(decoder, input_stream, std::move(tag));
+ } else
+ cmd = decoder_get_command(decoder);
+
+ return cmd;
+}
+
+inline DecoderCommand
+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) {
+ LogError(opus_domain, opus_strerror(nframes));
+ return DecoderCommand::STOP;
+ }
+
+ if (nframes > 0) {
+ const size_t nbytes = nframes * frame_size;
+ auto cmd = decoder_data(decoder, input_stream,
+ output_buffer, nbytes,
+ 0);
+ if (cmd != DecoderCommand::NONE)
+ return cmd;
+ }
+
+ return DecoderCommand::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->LockSeek(0, SEEK_SET, IgnoreError());
+
+ MPDOpusDecoder d(decoder, input_stream);
+ OggSyncState oy(*input_stream, decoder);
+
+ if (!d.ReadFirstPage(oy))
+ return;
+
+ while (true) {
+ auto cmd = d.HandlePackets();
+ if (cmd != DecoderCommand::NONE)
+ break;
+
+ if (!d.ReadNextPage(oy))
+ break;
+
+ }
+}
+
+static bool
+SeekFindEOS(OggSyncState &oy, ogg_stream_state &os, ogg_packet &packet,
+ input_stream *is)
+{
+ if (is->size > 0 && is->size - is->offset < 65536)
+ return OggFindEOS(oy, os, packet);
+
+ if (!is->CheapSeeking())
+ return false;
+
+ oy.Reset();
+
+ Error error;
+ return is->LockSeek(-65536, SEEK_END, error) &&
+ oy.ExpectPageSeekIn(os) &&
+ OggFindEOS(oy, os, packet);
+}
+
+static bool
+mpd_opus_scan_stream(struct input_stream *is,
+ const struct tag_handler *handler, void *handler_ctx)
+{
+ OggSyncState oy(*is);
+
+ ogg_stream_state os;
+ if (!oy.ExpectFirstPage(os))
+ return false;
+
+ /* read at most two more pages */
+ unsigned remaining_pages = 2;
+
+ bool result = false;
+
+ 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 (!oy.ExpectPageIn(os)) {
+ result = false;
+ break;
+ }
+
+ 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;
+ }
+ }
+
+ if (packet.e_o_s || SeekFindEOS(oy, os, packet, is))
+ tag_handler_invoke_duration(handler, handler_ctx,
+ packet.granulepos / opus_sample_rate);
+
+ ogg_stream_clear(&os);
+
+ 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/OpusDomain.cxx b/src/decoder/OpusDomain.cxx
new file mode 100644
index 000000000..2b8bb1bba
--- /dev/null
+++ b/src/decoder/OpusDomain.cxx
@@ -0,0 +1,24 @@
+/*
+ * 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 "OpusDomain.hxx"
+#include "util/Domain.hxx"
+
+const Domain opus_domain("opus");
diff --git a/src/decoder/OpusDomain.hxx b/src/decoder/OpusDomain.hxx
new file mode 100644
index 000000000..488eca27d
--- /dev/null
+++ b/src/decoder/OpusDomain.hxx
@@ -0,0 +1,27 @@
+/*
+ * 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_DOMAIN_HXX
+#define MPD_OPUS_DOMAIN_HXX
+
+#include "check.h"
+
+extern const class Domain opus_domain;
+
+#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..7e161fd0f
--- /dev/null
+++ b/src/decoder/OpusReader.hxx
@@ -0,0 +1,100 @@
+/*
+ * 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;
+
+ char *dest = new char[length + 1];
+ memcpy(dest, src, length);
+ dest[length] = 0;
+ return dest;
+ }
+};
+
+#endif
diff --git a/src/decoder/OpusTags.cxx b/src/decoder/OpusTags.cxx
new file mode 100644
index 000000000..f09d79c3b
--- /dev/null
+++ b/src/decoder/OpusTags.cxx
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2003-2013 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.hxx"
+#include "tag/TagHandler.hxx"
+
+#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/pcm_decoder_plugin.c b/src/decoder/PcmDecoderPlugin.cxx
index fc7dffc05..6996b583a 100644
--- a/src/decoder/pcm_decoder_plugin.c
+++ b/src/decoder/PcmDecoderPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,47 +18,52 @@
*/
#include "config.h"
-#include "decoder/pcm_decoder_plugin.h"
-#include "decoder_api.h"
+#include "decoder/PcmDecoderPlugin.hxx"
+#include "DecoderAPI.hxx"
+#include "InputStream.hxx"
+#include "util/Error.hxx"
+#include "Log.hxx"
+
+extern "C" {
#include "util/byte_reverse.h"
+}
#include <glib.h>
#include <unistd.h>
+#include <string.h>
#include <stdio.h> /* for SEEK_SET */
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "pcm"
-
static void
pcm_stream_decode(struct decoder *decoder, struct input_stream *is)
{
- static const struct audio_format audio_format = {
- .sample_rate = 44100,
- .format = SAMPLE_FORMAT_S16,
- .channels = 2,
+ static constexpr AudioFormat audio_format = {
+ 44100,
+ SampleFormat::S16,
+ 2,
};
- const bool reverse_endian = is->mime != NULL &&
- strcmp(is->mime, "audio/x-mpd-cdda-pcm-reverse") == 0;
+ const char *const mime = is->GetMimeType();
+ const bool reverse_endian = mime != nullptr &&
+ strcmp(mime, "audio/x-mpd-cdda-pcm-reverse") == 0;
- GError *error = NULL;
- enum decoder_command cmd;
-
- double time_to_size = audio_format_time_to_size(&audio_format);
+ const double time_to_size = audio_format.GetTimeToSize();
float total_time = -1;
- if (is->size >= 0)
- total_time = is->size / time_to_size;
+ const goffset size = is->GetSize();
+ if (size >= 0)
+ total_time = size / time_to_size;
- decoder_initialized(decoder, &audio_format, is->seekable, total_time);
+ decoder_initialized(decoder, audio_format,
+ is->IsSeekable(), total_time);
+ DecoderCommand cmd;
do {
char buffer[4096];
size_t nbytes = decoder_read(decoder, is,
buffer, sizeof(buffer));
- if (nbytes == 0 && input_stream_lock_eof(is))
+ if (nbytes == 0 && is->LockIsEOF())
break;
if (reverse_endian)
@@ -71,21 +76,21 @@ pcm_stream_decode(struct decoder *decoder, struct input_stream *is)
? decoder_data(decoder, is,
buffer, nbytes, 0)
: decoder_get_command(decoder);
- if (cmd == DECODE_COMMAND_SEEK) {
+ if (cmd == DecoderCommand::SEEK) {
goffset offset = (goffset)(time_to_size *
decoder_seek_where(decoder));
- if (input_stream_lock_seek(is, offset, SEEK_SET,
- &error)) {
+
+ Error error;
+ if (is->LockSeek(offset, SEEK_SET, error)) {
decoder_command_finished(decoder);
} else {
- g_warning("seeking failed: %s", error->message);
- g_error_free(error);
+ LogError(error);
decoder_seek_error(decoder);
}
- cmd = DECODE_COMMAND_NONE;
+ cmd = DecoderCommand::NONE;
}
- } while (cmd == DECODE_COMMAND_NONE);
+ } while (cmd == DecoderCommand::NONE);
}
static const char *const pcm_mime_types[] = {
@@ -95,11 +100,18 @@ static const char *const pcm_mime_types[] = {
/* same as above, but with reverse byte order */
"audio/x-mpd-cdda-pcm-reverse",
- NULL
+ nullptr
};
const struct decoder_plugin pcm_decoder_plugin = {
- .name = "pcm",
- .stream_decode = pcm_stream_decode,
- .mime_types = pcm_mime_types,
+ "pcm",
+ nullptr,
+ nullptr,
+ pcm_stream_decode,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ pcm_mime_types,
};
diff --git a/src/decoder/pcm_decoder_plugin.h b/src/decoder/PcmDecoderPlugin.hxx
index 11df80155..2883e866e 100644
--- a/src/decoder/pcm_decoder_plugin.h
+++ b/src/decoder/PcmDecoderPlugin.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -25,8 +25,8 @@
* which does not need a decoder.
*/
-#ifndef MPD_DECODER_PCM_H
-#define MPD_DECODER_PCM_H
+#ifndef MPD_DECODER_PCM_HXX
+#define MPD_DECODER_PCM_HXX
extern const struct decoder_plugin pcm_decoder_plugin;
diff --git a/src/decoder/sndfile_decoder_plugin.c b/src/decoder/SndfileDecoderPlugin.cxx
index 8dd98236f..5c7efe230 100644
--- a/src/decoder/sndfile_decoder_plugin.c
+++ b/src/decoder/SndfileDecoderPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,47 +18,47 @@
*/
#include "config.h"
-#include "decoder_api.h"
-#include "audio_check.h"
-#include "tag_handler.h"
+#include "SndfileDecoderPlugin.hxx"
+#include "DecoderAPI.hxx"
+#include "InputStream.hxx"
+#include "CheckAudioFormat.hxx"
+#include "tag/TagHandler.hxx"
+#include "util/Error.hxx"
+#include "util/Domain.hxx"
+#include "Log.hxx"
#include <sndfile.h>
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "sndfile"
+static constexpr Domain sndfile_domain("sndfile");
static sf_count_t
sndfile_vio_get_filelen(void *user_data)
{
- const struct input_stream *is = user_data;
+ const struct input_stream *is = (const struct input_stream *)user_data;
- return is->size;
+ return is->GetSize();
}
static sf_count_t
sndfile_vio_seek(sf_count_t offset, int whence, void *user_data)
{
- struct input_stream *is = user_data;
- bool success;
+ struct input_stream *is = (struct input_stream *)user_data;
- success = input_stream_lock_seek(is, offset, whence, NULL);
- if (!success)
+ if (!is->LockSeek(offset, whence, IgnoreError()))
return -1;
- return is->offset;
+ return is->GetOffset();
}
static sf_count_t
sndfile_vio_read(void *ptr, sf_count_t count, void *user_data)
{
- struct input_stream *is = user_data;
- GError *error = NULL;
- size_t nbytes;
-
- nbytes = input_stream_lock_read(is, ptr, count, &error);
- if (nbytes == 0 && error != NULL) {
- g_warning("%s", error->message);
- g_error_free(error);
+ struct input_stream *is = (struct input_stream *)user_data;
+
+ Error error;
+ size_t nbytes = is->LockRead(ptr, count, error);
+ if (nbytes == 0 && error.IsDefined()) {
+ LogError(error);
return -1;
}
@@ -66,9 +66,9 @@ sndfile_vio_read(void *ptr, sf_count_t count, void *user_data)
}
static sf_count_t
-sndfile_vio_write(G_GNUC_UNUSED const void *ptr,
- G_GNUC_UNUSED sf_count_t count,
- G_GNUC_UNUSED void *user_data)
+sndfile_vio_write(gcc_unused const void *ptr,
+ gcc_unused sf_count_t count,
+ gcc_unused void *user_data)
{
/* no writing! */
return -1;
@@ -77,9 +77,9 @@ sndfile_vio_write(G_GNUC_UNUSED const void *ptr,
static sf_count_t
sndfile_vio_tell(void *user_data)
{
- const struct input_stream *is = user_data;
+ const struct input_stream *is = (const struct input_stream *)user_data;
- return is->offset;
+ return is->GetOffset();
}
/**
@@ -87,18 +87,18 @@ sndfile_vio_tell(void *user_data)
* libsndfile stream.
*/
static SF_VIRTUAL_IO vio = {
- .get_filelen = sndfile_vio_get_filelen,
- .seek = sndfile_vio_seek,
- .read = sndfile_vio_read,
- .write = sndfile_vio_write,
- .tell = sndfile_vio_tell,
+ sndfile_vio_get_filelen,
+ sndfile_vio_seek,
+ sndfile_vio_read,
+ sndfile_vio_write,
+ sndfile_vio_tell,
};
/**
* Converts a frame number to a timestamp (in seconds).
*/
static float
-frame_to_time(sf_count_t frame, const struct audio_format *audio_format)
+frame_to_time(sf_count_t frame, const AudioFormat *audio_format)
{
return (float)frame / (float)audio_format->sample_rate;
}
@@ -107,7 +107,7 @@ frame_to_time(sf_count_t frame, const struct audio_format *audio_format)
* Converts a timestamp (in seconds) to a frame number.
*/
static sf_count_t
-time_to_frame(float t, const struct audio_format *audio_format)
+time_to_frame(float t, const AudioFormat *audio_format)
{
return (sf_count_t)(t * audio_format->sample_rate);
}
@@ -115,40 +115,39 @@ time_to_frame(float t, const struct audio_format *audio_format)
static void
sndfile_stream_decode(struct decoder *decoder, struct input_stream *is)
{
- GError *error = NULL;
SNDFILE *sf;
SF_INFO info;
- struct audio_format audio_format;
size_t frame_size;
sf_count_t read_frames, num_frames;
int buffer[4096];
- enum decoder_command cmd;
info.format = 0;
sf = sf_open_virtual(&vio, SFM_READ, &info, is);
- if (sf == NULL) {
- g_warning("sf_open_virtual() failed");
+ if (sf == nullptr) {
+ LogWarning(sndfile_domain, "sf_open_virtual() failed");
return;
}
/* for now, always read 32 bit samples. Later, we could lower
MPD's CPU usage by reading 16 bit samples with
sf_readf_short() on low-quality source files. */
- if (!audio_format_init_checked(&audio_format, info.samplerate,
- SAMPLE_FORMAT_S32,
- info.channels, &error)) {
- g_warning("%s", error->message);
- g_error_free(error);
+ Error error;
+ AudioFormat audio_format;
+ if (!audio_format_init_checked(audio_format, info.samplerate,
+ SampleFormat::S32,
+ info.channels, error)) {
+ LogError(error);
return;
}
- decoder_initialized(decoder, &audio_format, info.seekable,
+ decoder_initialized(decoder, audio_format, info.seekable,
frame_to_time(info.frames, &audio_format));
- frame_size = audio_format_frame_size(&audio_format);
+ frame_size = audio_format.GetFrameSize();
read_frames = sizeof(buffer) / frame_size;
+ DecoderCommand cmd;
do {
num_frames = sf_readf_int(sf, buffer, read_frames);
if (num_frames <= 0)
@@ -157,7 +156,7 @@ sndfile_stream_decode(struct decoder *decoder, struct input_stream *is)
cmd = decoder_data(decoder, is,
buffer, num_frames * frame_size,
0);
- if (cmd == DECODE_COMMAND_SEEK) {
+ if (cmd == DecoderCommand::SEEK) {
sf_count_t c =
time_to_frame(decoder_seek_where(decoder),
&audio_format);
@@ -166,9 +165,9 @@ sndfile_stream_decode(struct decoder *decoder, struct input_stream *is)
decoder_seek_error(decoder);
else
decoder_command_finished(decoder);
- cmd = DECODE_COMMAND_NONE;
+ cmd = DecoderCommand::NONE;
}
- } while (cmd == DECODE_COMMAND_NONE);
+ } while (cmd == DecoderCommand::NONE);
sf_close(sf);
}
@@ -184,12 +183,13 @@ sndfile_scan_file(const char *path_fs,
info.format = 0;
sf = sf_open(path_fs, SFM_READ, &info);
- if (sf == NULL)
+ if (sf == nullptr)
return false;
if (!audio_valid_sample_rate(info.samplerate)) {
sf_close(sf);
- g_warning("Invalid sample rate in %s\n", path_fs);
+ FormatWarning(sndfile_domain,
+ "Invalid sample rate in %s", path_fs);
return false;
}
@@ -197,17 +197,17 @@ sndfile_scan_file(const char *path_fs,
info.frames / info.samplerate);
p = sf_get_string(sf, SF_STR_TITLE);
- if (p != NULL)
+ if (p != nullptr)
tag_handler_invoke_tag(handler, handler_ctx,
TAG_TITLE, p);
p = sf_get_string(sf, SF_STR_ARTIST);
- if (p != NULL)
+ if (p != nullptr)
tag_handler_invoke_tag(handler, handler_ctx,
TAG_ARTIST, p);
p = sf_get_string(sf, SF_STR_DATE);
- if (p != NULL)
+ if (p != nullptr)
tag_handler_invoke_tag(handler, handler_ctx,
TAG_DATE, p);
@@ -234,7 +234,7 @@ static const char *const sndfile_suffixes[] = {
linking with libFLAC and libvorbis - we can do better, we
have native plugins for these libraries */
- NULL
+ nullptr
};
static const char *const sndfile_mime_types[] = {
@@ -243,13 +243,18 @@ static const char *const sndfile_mime_types[] = {
/* what are the MIME types of the other supported formats? */
- NULL
+ nullptr
};
const struct decoder_plugin sndfile_decoder_plugin = {
- .name = "sndfile",
- .stream_decode = sndfile_stream_decode,
- .scan_file = sndfile_scan_file,
- .suffixes = sndfile_suffixes,
- .mime_types = sndfile_mime_types,
+ "sndfile",
+ nullptr,
+ nullptr,
+ sndfile_stream_decode,
+ nullptr,
+ sndfile_scan_file,
+ nullptr,
+ nullptr,
+ sndfile_suffixes,
+ sndfile_mime_types,
};
diff --git a/src/decoder/SndfileDecoderPlugin.hxx b/src/decoder/SndfileDecoderPlugin.hxx
new file mode 100644
index 000000000..ba60fafd0
--- /dev/null
+++ b/src/decoder/SndfileDecoderPlugin.hxx
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2003-2013 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_SNDFILE_HXX
+#define MPD_DECODER_SNDFILE_HXX
+
+extern const struct decoder_plugin sndfile_decoder_plugin;
+
+#endif
diff --git a/src/decoder/vorbis_comments.c b/src/decoder/VorbisComments.cxx
index 6c2d57b72..402ee7c2b 100644
--- a/src/decoder/vorbis_comments.c
+++ b/src/decoder/VorbisComments.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,13 +18,16 @@
*/
#include "config.h"
-#include "vorbis_comments.h"
-#include "tag.h"
-#include "tag_table.h"
-#include "tag_handler.h"
-#include "replay_gain_info.h"
+#include "VorbisComments.hxx"
+#include "XiphTags.hxx"
+#include "tag/Tag.hxx"
+#include "tag/TagTable.hxx"
+#include "tag/TagHandler.hxx"
+#include "tag/TagBuilder.hxx"
+#include "ReplayGainInfo.hxx"
#include <glib.h>
+
#include <assert.h>
#include <stddef.h>
#include <string.h>
@@ -95,13 +98,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,14 +115,14 @@ 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;
for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i)
if (vorbis_copy_comment(comment,
- tag_item_names[i], i,
+ tag_item_names[i], tag_type(i),
handler, handler_ctx))
return;
}
@@ -141,16 +137,12 @@ vorbis_comments_scan(char **comments,
}
-struct tag *
+Tag *
vorbis_comments_to_tag(char **comments)
{
- struct tag *tag = tag_new();
- vorbis_comments_scan(comments, &add_tag_handler, tag);
-
- if (tag_is_empty(tag)) {
- tag_free(tag);
- tag = NULL;
- }
-
- return tag;
+ TagBuilder tag_builder;
+ vorbis_comments_scan(comments, &add_tag_handler, &tag_builder);
+ return tag_builder.IsEmpty()
+ ? nullptr
+ : tag_builder.Commit();
}
diff --git a/src/decoder/vorbis_comments.h b/src/decoder/VorbisComments.hxx
index c15096930..7a8374785 100644
--- a/src/decoder/vorbis_comments.h
+++ b/src/decoder/VorbisComments.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,15 +17,14 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_VORBIS_COMMENTS_H
-#define MPD_VORBIS_COMMENTS_H
+#ifndef MPD_VORBIS_COMMENTS_HXX
+#define MPD_VORBIS_COMMENTS_HXX
#include "check.h"
-#include <stdbool.h>
-
struct replay_gain_info;
struct tag_handler;
+struct Tag;
bool
vorbis_comments_to_replay_gain(struct replay_gain_info *rgi, char **comments);
@@ -34,7 +33,7 @@ void
vorbis_comments_scan(char **comments,
const struct tag_handler *handler, void *handler_ctx);
-struct tag *
+Tag *
vorbis_comments_to_tag(char **comments);
#endif
diff --git a/src/decoder/vorbis_decoder_plugin.c b/src/decoder/VorbisDecoderPlugin.cxx
index 15cdc0ca9..55ce943e8 100644
--- a/src/decoder/vorbis_decoder_plugin.c
+++ b/src/decoder/VorbisDecoderPlugin.cxx
@@ -18,11 +18,17 @@
*/
#include "config.h"
-#include "vorbis_comments.h"
-#include "_ogg_common.h"
-#include "audio_check.h"
-#include "uri.h"
-#include "tag_handler.h"
+#include "VorbisDecoderPlugin.h"
+#include "VorbisComments.hxx"
+#include "VorbisDomain.hxx"
+#include "DecoderAPI.hxx"
+#include "InputStream.hxx"
+#include "OggCodec.hxx"
+#include "util/Error.hxx"
+#include "util/UriUtil.hxx"
+#include "CheckAudioFormat.hxx"
+#include "tag/TagHandler.hxx"
+#include "Log.hxx"
#ifndef HAVE_TREMOR
#define OV_EXCLUDE_STATIC_CALLBACKS
@@ -46,14 +52,10 @@
#include <errno.h>
#include <unistd.h>
-#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 {
@@ -65,10 +67,9 @@ 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);
+ struct vorbis_input_stream *vis = (struct vorbis_input_stream *)data;
+ size_t ret = decoder_read(vis->decoder, vis->input_stream,
+ ptr, size * nmemb);
errno = 0;
@@ -77,32 +78,33 @@ static size_t ogg_read_cb(void *ptr, size_t size, size_t nmemb, void *data)
static int ogg_seek_cb(void *data, ogg_int64_t offset, int whence)
{
- struct vorbis_input_stream *vis = data;
+ struct vorbis_input_stream *vis = (struct vorbis_input_stream *)data;
+ Error error;
return vis->seekable &&
- (!vis->decoder || decoder_get_command(vis->decoder) != DECODE_COMMAND_STOP) &&
- input_stream_lock_seek(vis->input_stream, offset, whence, NULL)
+ (!vis->decoder || decoder_get_command(vis->decoder) != DecoderCommand::STOP) &&
+ vis->input_stream->LockSeek(offset, whence, error)
? 0 : -1;
}
/* TODO: check Ogg libraries API and see if we can just not have this func */
-static int ogg_close_cb(G_GNUC_UNUSED void *data)
+static int ogg_close_cb(gcc_unused void *data)
{
return 0;
}
static long ogg_tell_cb(void *data)
{
- const struct vorbis_input_stream *vis = data;
+ struct vorbis_input_stream *vis = (struct vorbis_input_stream *)data;
return (long)vis->input_stream->offset;
}
static const ov_callbacks vorbis_is_callbacks = {
- .read_func = ogg_read_cb,
- .seek_func = ogg_seek_cb,
- .close_func = ogg_close_cb,
- .tell_func = ogg_tell_cb,
+ ogg_read_cb,
+ ogg_seek_cb,
+ ogg_close_cb,
+ ogg_tell_cb,
};
static const char *
@@ -135,16 +137,15 @@ vorbis_is_open(struct vorbis_input_stream *vis, OggVorbis_File *vf,
{
vis->decoder = decoder;
vis->input_stream = input_stream;
- vis->seekable = input_stream->seekable &&
- (input_stream->uri == NULL ||
- !uri_has_scheme(input_stream->uri));
+ vis->seekable = input_stream->CheapSeeking();
int ret = ov_open_callbacks(vis, vf, NULL, 0, vorbis_is_callbacks);
if (ret < 0) {
if (decoder == NULL ||
- decoder_get_command(decoder) == DECODE_COMMAND_NONE)
- g_warning("Failed to open Ogg Vorbis stream: %s",
- vorbis_strerror(ret));
+ decoder_get_command(decoder) == DecoderCommand::NONE)
+ FormatWarning(vorbis_domain,
+ "Failed to open Ogg Vorbis stream: %s",
+ vorbis_strerror(ret));
return false;
}
@@ -155,67 +156,86 @@ static void
vorbis_send_comments(struct decoder *decoder, struct input_stream *is,
char **comments)
{
- struct tag *tag;
-
- tag = vorbis_comments_to_tag(comments);
+ Tag *tag = vorbis_comments_to_tag(comments);
if (!tag)
return;
- decoder_tag(decoder, is, tag);
- tag_free(tag);
+ decoder_tag(decoder, is, std::move(*tag));
+ delete 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);
+ input_stream->LockSeek(0, SEEK_SET, IgnoreError());
+ 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");
+ LogWarning(vorbis_domain, "ov_info() has failed");
return;
}
- if (!audio_format_init_checked(&audio_format, vi->rate,
- SAMPLE_FORMAT_S16,
- vi->channels, &error)) {
- g_warning("%s", error->message);
- g_error_free(error);
+ Error error;
+ AudioFormat audio_format;
+ if (!audio_format_init_checked(audio_format, vi->rate,
+#ifdef HAVE_TREMOR
+ SampleFormat::S16,
+#else
+ SampleFormat::FLOAT,
+#endif
+ vi->channels, error)) {
+ LogError(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);
+ decoder_initialized(decoder, audio_format, vis.seekable, total_time);
+
+#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;
+ DecoderCommand cmd = decoder_get_command(decoder);
do {
- if (cmd == DECODE_COMMAND_SEEK) {
+ if (cmd == DecoderCommand::SEEK) {
double seek_where = decoder_seek_where(decoder);
if (0 == ov_time_seek_page(&vf, seek_where)) {
decoder_command_finished(decoder);
@@ -223,20 +243,37 @@ 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");
+ LogWarning(vorbis_domain,
+ "ov_info() has failed");
break;
}
@@ -244,11 +281,12 @@ vorbis_stream_decode(struct decoder *decoder,
vi->channels != (int)audio_format.channels) {
/* we don't support audio format
change yet */
- g_warning("audio format change, stopping here");
+ LogWarning(vorbis_domain,
+ "audio format change, stopping here");
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,13 +296,14 @@ 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);
- } while (cmd != DECODE_COMMAND_STOP);
+ buffer, nbytes,
+ kbit_rate);
+ } while (cmd != DecoderCommand::STOP);
ov_clear(&vf);
}
@@ -306,9 +345,14 @@ static const char *const vorbis_mime_types[] = {
};
const struct decoder_plugin vorbis_decoder_plugin = {
- .name = "vorbis",
- .stream_decode = vorbis_stream_decode,
- .scan_stream = vorbis_scan_stream,
- .suffixes = vorbis_suffixes,
- .mime_types = vorbis_mime_types
+ "vorbis",
+ nullptr,
+ nullptr,
+ vorbis_stream_decode,
+ nullptr,
+ nullptr,
+ vorbis_scan_stream,
+ nullptr,
+ vorbis_suffixes,
+ vorbis_mime_types
};
diff --git a/src/decoder/VorbisDecoderPlugin.h b/src/decoder/VorbisDecoderPlugin.h
new file mode 100644
index 000000000..618c9ffde
--- /dev/null
+++ b/src/decoder/VorbisDecoderPlugin.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_VORBIS_H
+#define MPD_DECODER_VORBIS_H
+
+extern const struct decoder_plugin vorbis_decoder_plugin;
+
+#endif
diff --git a/src/decoder/VorbisDomain.cxx b/src/decoder/VorbisDomain.cxx
new file mode 100644
index 000000000..d7c70a641
--- /dev/null
+++ b/src/decoder/VorbisDomain.cxx
@@ -0,0 +1,24 @@
+/*
+ * 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 "VorbisDomain.hxx"
+#include "util/Domain.hxx"
+
+const Domain vorbis_domain("vorbis");
diff --git a/src/decoder/VorbisDomain.hxx b/src/decoder/VorbisDomain.hxx
new file mode 100644
index 000000000..69e2e11cb
--- /dev/null
+++ b/src/decoder/VorbisDomain.hxx
@@ -0,0 +1,27 @@
+/*
+ * 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_VORBIS_DOMAIN_HXX
+#define MPD_VORBIS_DOMAIN_HXX
+
+#include "check.h"
+
+extern const class Domain vorbis_domain;
+
+#endif
diff --git a/src/decoder/wavpack_decoder_plugin.c b/src/decoder/WavpackDecoderPlugin.cxx
index 9ebd0fccc..8ee898e30 100644
--- a/src/decoder/wavpack_decoder_plugin.c
+++ b/src/decoder/WavpackDecoderPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,27 +18,27 @@
*/
#include "config.h"
-#include "decoder_api.h"
-#include "audio_check.h"
-#include "path.h"
-#include "utils.h"
-#include "tag_table.h"
-#include "tag_handler.h"
-#include "tag_ape.h"
+#include "WavpackDecoderPlugin.hxx"
+#include "DecoderAPI.hxx"
+#include "InputStream.hxx"
+#include "CheckAudioFormat.hxx"
+#include "tag/TagHandler.hxx"
+#include "tag/ApeTag.hxx"
+#include "util/Error.hxx"
+#include "util/Domain.hxx"
+#include "Log.hxx"
#include <wavpack/wavpack.h>
#include <glib.h>
#include <assert.h>
-#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "wavpack"
-
#define ERRORLEN 80
+static constexpr Domain wavpack_domain("wavpack");
+
/** A pointer type for format converter function. */
typedef void (*format_samples_t)(
int bytes_per_sample,
@@ -53,18 +53,18 @@ typedef void (*format_samples_t)(
static void
format_samples_int(int bytes_per_sample, void *buffer, uint32_t count)
{
- int32_t *src = buffer;
+ int32_t *src = (int32_t *)buffer;
switch (bytes_per_sample) {
case 1: {
- int8_t *dst = buffer;
+ int8_t *dst = (int8_t *)buffer;
/*
* The asserts like the following one are because we do the
* formatting of samples within a single buffer. The size
* of the output samples never can be greater than the size
* of the input ones. Otherwise we would have an overflow.
*/
- assert_static(sizeof(*dst) <= sizeof(*src));
+ static_assert(sizeof(*dst) <= sizeof(*src), "Wrong size");
/* pass through and align 8-bit samples */
while (count--) {
@@ -73,8 +73,8 @@ format_samples_int(int bytes_per_sample, void *buffer, uint32_t count)
break;
}
case 2: {
- uint16_t *dst = buffer;
- assert_static(sizeof(*dst) <= sizeof(*src));
+ uint16_t *dst = (uint16_t *)buffer;
+ static_assert(sizeof(*dst) <= sizeof(*src), "Wrong size");
/* pass through and align 16-bit samples */
while (count--) {
@@ -94,10 +94,10 @@ format_samples_int(int bytes_per_sample, void *buffer, uint32_t count)
* This function converts floating point sample data to 24-bit integer.
*/
static void
-format_samples_float(G_GNUC_UNUSED int bytes_per_sample, void *buffer,
+format_samples_float(gcc_unused int bytes_per_sample, void *buffer,
uint32_t count)
{
- float *p = buffer;
+ float *p = (float *)buffer;
while (count--) {
*p /= (1 << 23);
@@ -108,27 +108,27 @@ format_samples_float(G_GNUC_UNUSED int bytes_per_sample, void *buffer,
/**
* Choose a MPD sample format from libwavpacks' number of bits.
*/
-static enum sample_format
+static SampleFormat
wavpack_bits_to_sample_format(bool is_float, int bytes_per_sample)
{
if (is_float)
- return SAMPLE_FORMAT_FLOAT;
+ return SampleFormat::FLOAT;
switch (bytes_per_sample) {
case 1:
- return SAMPLE_FORMAT_S8;
+ return SampleFormat::S8;
case 2:
- return SAMPLE_FORMAT_S16;
+ return SampleFormat::S16;
case 3:
- return SAMPLE_FORMAT_S24_P32;
+ return SampleFormat::S24_P32;
case 4:
- return SAMPLE_FORMAT_S32;
+ return SampleFormat::S32;
default:
- return SAMPLE_FORMAT_UNDEFINED;
+ return SampleFormat::UNDEFINED;
}
}
@@ -139,10 +139,9 @@ wavpack_bits_to_sample_format(bool is_float, int bytes_per_sample)
static void
wavpack_decode(struct decoder *decoder, WavpackContext *wpc, bool can_seek)
{
- GError *error = NULL;
bool is_float;
- enum sample_format sample_format;
- struct audio_format audio_format;
+ SampleFormat sample_format;
+ AudioFormat audio_format;
format_samples_t format_samples;
float total_time;
int bytes_per_sample, output_sample_size;
@@ -152,12 +151,12 @@ wavpack_decode(struct decoder *decoder, WavpackContext *wpc, bool can_seek)
wavpack_bits_to_sample_format(is_float,
WavpackGetBytesPerSample(wpc));
- if (!audio_format_init_checked(&audio_format,
+ Error error;
+ if (!audio_format_init_checked(audio_format,
WavpackGetSampleRate(wpc),
sample_format,
- WavpackGetNumChannels(wpc), &error)) {
- g_warning("%s", error->message);
- g_error_free(error);
+ WavpackGetNumChannels(wpc), error)) {
+ LogError(error);
return;
}
@@ -170,18 +169,18 @@ wavpack_decode(struct decoder *decoder, WavpackContext *wpc, bool can_seek)
total_time = WavpackGetNumSamples(wpc);
total_time /= audio_format.sample_rate;
bytes_per_sample = WavpackGetBytesPerSample(wpc);
- output_sample_size = audio_format_frame_size(&audio_format);
+ output_sample_size = audio_format.GetFrameSize();
/* wavpack gives us all kind of samples in a 32-bit space */
int32_t chunk[1024];
const uint32_t samples_requested = G_N_ELEMENTS(chunk) /
audio_format.channels;
- decoder_initialized(decoder, &audio_format, can_seek, total_time);
+ decoder_initialized(decoder, audio_format, can_seek, total_time);
- enum decoder_command cmd = decoder_get_command(decoder);
- while (cmd != DECODE_COMMAND_STOP) {
- if (cmd == DECODE_COMMAND_SEEK) {
+ DecoderCommand cmd = decoder_get_command(decoder);
+ while (cmd != DecoderCommand::STOP) {
+ if (cmd == DecoderCommand::SEEK) {
if (can_seek) {
unsigned where = decoder_seek_where(decoder) *
audio_format.sample_rate;
@@ -296,10 +295,9 @@ wavpack_scan_file(const char *fname,
wpc = WavpackOpenFileInput(fname, error, OPEN_TAGS, 0);
if (wpc == NULL) {
- g_warning(
- "failed to open WavPack file \"%s\": %s\n",
- fname, error
- );
+ FormatError(wavpack_domain,
+ "failed to open WavPack file \"%s\": %s",
+ fname, error);
return false;
}
@@ -359,7 +357,7 @@ static struct wavpack_input *
wpin(void *id)
{
assert(id);
- return id;
+ return (struct wavpack_input *)id;
}
static int32_t
@@ -403,15 +401,13 @@ wavpack_input_get_pos(void *id)
static int
wavpack_input_set_pos_abs(void *id, uint32_t pos)
{
- return input_stream_lock_seek(wpin(id)->is, pos, SEEK_SET, NULL)
- ? 0 : -1;
+ return wpin(id)->is->LockSeek(pos, SEEK_SET, IgnoreError()) ? 0 : -1;
}
static int
wavpack_input_set_pos_rel(void *id, int32_t delta, int mode)
{
- return input_stream_lock_seek(wpin(id)->is, delta, mode, NULL)
- ? 0 : -1;
+ return wpin(id)->is->LockSeek(delta, mode, IgnoreError()) ? 0 : -1;
}
static int
@@ -441,14 +437,14 @@ wavpack_input_can_seek(void *id)
}
static WavpackStreamReader mpd_is_reader = {
- .read_bytes = wavpack_input_read_bytes,
- .get_pos = wavpack_input_get_pos,
- .set_pos_abs = wavpack_input_set_pos_abs,
- .set_pos_rel = wavpack_input_set_pos_rel,
- .push_back_byte = wavpack_input_push_back_byte,
- .get_length = wavpack_input_get_length,
- .can_seek = wavpack_input_can_seek,
- .write_bytes = NULL /* no need to write edited tags */
+ wavpack_input_read_bytes,
+ wavpack_input_get_pos,
+ wavpack_input_set_pos_abs,
+ wavpack_input_set_pos_rel,
+ wavpack_input_push_back_byte,
+ wavpack_input_get_length,
+ wavpack_input_can_seek,
+ nullptr /* no need to write edited tags */
};
static void
@@ -462,7 +458,7 @@ wavpack_input_init(struct wavpack_input *isp, struct decoder *decoder,
static struct input_stream *
wavpack_open_wvc(struct decoder *decoder, const char *uri,
- GMutex *mutex, GCond *cond,
+ Mutex &mutex, Cond &cond,
struct wavpack_input *wpi)
{
struct input_stream *is_wvc;
@@ -475,10 +471,11 @@ wavpack_open_wvc(struct decoder *decoder, const char *uri,
* single files. utf8url is not absolute file path :/
*/
if (uri == NULL)
- return false;
+ return nullptr;
wvc_url = g_strconcat(uri, "c", NULL);
- is_wvc = input_stream_open(wvc_url, mutex, cond, NULL);
+
+ is_wvc = input_stream::Open(wvc_url, mutex, cond, IgnoreError());
g_free(wvc_url);
if (is_wvc == NULL)
@@ -492,7 +489,7 @@ wavpack_open_wvc(struct decoder *decoder, const char *uri,
decoder, is_wvc, &first_byte, sizeof(first_byte)
);
if (nbytes == 0) {
- input_stream_close(is_wvc);
+ is_wvc->Close();
return NULL;
}
@@ -515,7 +512,8 @@ wavpack_streamdecode(struct decoder * decoder, struct input_stream *is)
struct wavpack_input isp, isp_wvc;
bool can_seek = is->seekable;
- is_wvc = wavpack_open_wvc(decoder, is->uri, is->mutex, is->cond,
+ is_wvc = wavpack_open_wvc(decoder, is->uri.c_str(),
+ is->mutex, is->cond,
&isp_wvc);
if (is_wvc != NULL) {
open_flags |= OPEN_WVC;
@@ -534,7 +532,8 @@ wavpack_streamdecode(struct decoder * decoder, struct input_stream *is)
);
if (wpc == NULL) {
- g_warning("failed to open WavPack stream: %s\n", error);
+ FormatError(wavpack_domain,
+ "failed to open WavPack stream: %s", error);
return;
}
@@ -542,7 +541,7 @@ wavpack_streamdecode(struct decoder * decoder, struct input_stream *is)
WavpackCloseFile(wpc);
if (open_flags & OPEN_WVC) {
- input_stream_close(is_wvc);
+ is_wvc->Close();
}
}
@@ -560,10 +559,9 @@ wavpack_filedecode(struct decoder *decoder, const char *fname)
OPEN_TAGS | OPEN_WVC | OPEN_NORMALIZE, 23
);
if (wpc == NULL) {
- g_warning(
- "failed to open WavPack file \"%s\": %s\n",
- fname, error
- );
+ FormatWarning(wavpack_domain,
+ "failed to open WavPack file \"%s\": %s",
+ fname, error);
return;
}
@@ -587,10 +585,14 @@ static char const *const wavpack_mime_types[] = {
};
const struct decoder_plugin wavpack_decoder_plugin = {
- .name = "wavpack",
- .stream_decode = wavpack_streamdecode,
- .file_decode = wavpack_filedecode,
- .scan_file = wavpack_scan_file,
- .suffixes = wavpack_suffixes,
- .mime_types = wavpack_mime_types
+ "wavpack",
+ nullptr,
+ nullptr,
+ wavpack_streamdecode,
+ wavpack_filedecode,
+ wavpack_scan_file,
+ nullptr,
+ nullptr,
+ wavpack_suffixes,
+ wavpack_mime_types
};
diff --git a/src/decoder/WavpackDecoderPlugin.hxx b/src/decoder/WavpackDecoderPlugin.hxx
new file mode 100644
index 000000000..9ebe6354f
--- /dev/null
+++ b/src/decoder/WavpackDecoderPlugin.hxx
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2003-2013 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_WAVPACK_HXX
+#define MPD_DECODER_WAVPACK_HXX
+
+extern const struct decoder_plugin wavpack_decoder_plugin;
+
+#endif
diff --git a/src/decoder/wildmidi_decoder_plugin.c b/src/decoder/WildmidiDecoderPlugin.cxx
index 2cdb30a9c..1a390706f 100644
--- a/src/decoder/wildmidi_decoder_plugin.c
+++ b/src/decoder/WildmidiDecoderPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,36 +18,43 @@
*/
#include "config.h"
-#include "decoder_api.h"
-#include "tag_handler.h"
-#include "glib_compat.h"
-
-#include <glib.h>
-
+#include "WildmidiDecoderPlugin.hxx"
+#include "DecoderAPI.hxx"
+#include "tag/TagHandler.hxx"
+#include "util/Error.hxx"
+#include "util/Domain.hxx"
+#include "fs/Path.hxx"
+#include "fs/FileSystem.hxx"
+#include "system/FatalError.hxx"
+#include "Log.hxx"
+
+extern "C" {
#include <wildmidi_lib.h>
+}
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "wildmidi"
+static constexpr Domain wildmidi_domain("wildmidi");
-enum {
- WILDMIDI_SAMPLE_RATE = 48000,
-};
+static constexpr unsigned WILDMIDI_SAMPLE_RATE = 48000;
static bool
-wildmidi_init(const struct config_param *param)
+wildmidi_init(const config_param &param)
{
- const char *config_file;
- int ret;
-
- config_file = config_get_block_string(param, "config_file",
- "/etc/timidity/timidity.cfg");
- if (!g_file_test(config_file, G_FILE_TEST_IS_REGULAR)) {
- g_debug("configuration file does not exist: %s", config_file);
+ Error error;
+ const Path path = param.GetBlockPath("config_file",
+ "/etc/timidity/timidity.cfg",
+ error);
+ if (path.IsNull())
+ FatalError(error);
+
+ if (!FileExists(path)) {
+ const auto utf8 = path.ToUTF8();
+ FormatDebug(wildmidi_domain,
+ "configuration file does not exist: %s",
+ utf8.c_str());
return false;
}
- ret = WildMidi_Init(config_file, WILDMIDI_SAMPLE_RATE, 0);
- return ret == 0;
+ return WildMidi_Init(path.c_str(), WILDMIDI_SAMPLE_RATE, 0) == 0;
}
static void
@@ -59,43 +66,43 @@ wildmidi_finish(void)
static void
wildmidi_file_decode(struct decoder *decoder, const char *path_fs)
{
- static const struct audio_format audio_format = {
- .sample_rate = WILDMIDI_SAMPLE_RATE,
- .format = SAMPLE_FORMAT_S16,
- .channels = 2,
+ static constexpr AudioFormat audio_format = {
+ WILDMIDI_SAMPLE_RATE,
+ SampleFormat::S16,
+ 2,
};
midi *wm;
const struct _WM_Info *info;
- enum decoder_command cmd;
wm = WildMidi_Open(path_fs);
- if (wm == NULL)
+ if (wm == nullptr)
return;
info = WildMidi_GetInfo(wm);
- if (info == NULL) {
+ if (info == nullptr) {
WildMidi_Close(wm);
return;
}
- decoder_initialized(decoder, &audio_format, true,
+ decoder_initialized(decoder, audio_format, true,
info->approx_total_samples / WILDMIDI_SAMPLE_RATE);
+ DecoderCommand cmd;
do {
char buffer[4096];
int len;
info = WildMidi_GetInfo(wm);
- if (info == NULL)
+ if (info == nullptr)
break;
len = WildMidi_GetOutput(wm, buffer, sizeof(buffer));
if (len <= 0)
break;
- cmd = decoder_data(decoder, NULL, buffer, len, 0);
+ cmd = decoder_data(decoder, nullptr, buffer, len, 0);
- if (cmd == DECODE_COMMAND_SEEK) {
+ if (cmd == DecoderCommand::SEEK) {
unsigned long seek_where = WILDMIDI_SAMPLE_RATE *
decoder_seek_where(decoder);
@@ -105,10 +112,10 @@ wildmidi_file_decode(struct decoder *decoder, const char *path_fs)
WildMidi_FastSeek(wm, &seek_where);
#endif
decoder_command_finished(decoder);
- cmd = DECODE_COMMAND_NONE;
+ cmd = DecoderCommand::NONE;
}
- } while (cmd == DECODE_COMMAND_NONE);
+ } while (cmd == DecoderCommand::NONE);
WildMidi_Close(wm);
}
@@ -118,11 +125,11 @@ wildmidi_scan_file(const char *path_fs,
const struct tag_handler *handler, void *handler_ctx)
{
midi *wm = WildMidi_Open(path_fs);
- if (wm == NULL)
+ if (wm == nullptr)
return false;
const struct _WM_Info *info = WildMidi_GetInfo(wm);
- if (info == NULL) {
+ if (info == nullptr) {
WildMidi_Close(wm);
return false;
}
@@ -137,14 +144,18 @@ wildmidi_scan_file(const char *path_fs,
static const char *const wildmidi_suffixes[] = {
"mid",
- NULL
+ nullptr
};
const struct decoder_plugin wildmidi_decoder_plugin = {
- .name = "wildmidi",
- .init = wildmidi_init,
- .finish = wildmidi_finish,
- .file_decode = wildmidi_file_decode,
- .scan_file = wildmidi_scan_file,
- .suffixes = wildmidi_suffixes,
+ "wildmidi",
+ wildmidi_init,
+ wildmidi_finish,
+ nullptr,
+ wildmidi_file_decode,
+ wildmidi_scan_file,
+ nullptr,
+ nullptr,
+ wildmidi_suffixes,
+ nullptr,
};
diff --git a/src/decoder/WildmidiDecoderPlugin.hxx b/src/decoder/WildmidiDecoderPlugin.hxx
new file mode 100644
index 000000000..956b72299
--- /dev/null
+++ b/src/decoder/WildmidiDecoderPlugin.hxx
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2003-2013 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_WILDMIDI_HXX
+#define MPD_DECODER_WILDMIDI_HXX
+
+extern const struct decoder_plugin wildmidi_decoder_plugin;
+
+#endif
diff --git a/src/decoder/XiphTags.cxx b/src/decoder/XiphTags.cxx
new file mode 100644
index 000000000..b9958a19a
--- /dev/null
+++ b/src/decoder/XiphTags.cxx
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2003-2013 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.hxx"
+
+const struct tag_table xiph_tags[] = {
+ { "tracknumber", TAG_TRACK },
+ { "discnumber", TAG_DISC },
+ { "album artist", TAG_ALBUM_ARTIST },
+ { nullptr, TAG_NUM_OF_ITEM_TYPES }
+};
diff --git a/src/decoder/XiphTags.hxx b/src/decoder/XiphTags.hxx
new file mode 100644
index 000000000..606dfef10
--- /dev/null
+++ b/src/decoder/XiphTags.hxx
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2003-2013 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_HXX
+#define MPD_XIPH_TAGS_HXX
+
+#include "check.h"
+#include "tag/TagTable.hxx"
+
+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/flac_metadata.h b/src/decoder/flac_metadata.h
deleted file mode 100644
index 3c463d5d6..000000000
--- a/src/decoder/flac_metadata.h
+++ /dev/null
@@ -1,64 +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.
- */
-
-#ifndef MPD_FLAC_METADATA_H
-#define MPD_FLAC_METADATA_H
-
-#include <assert.h>
-#include <stdbool.h>
-#include <FLAC/metadata.h>
-
-struct tag_handler;
-struct tag;
-struct replay_gain_info;
-
-static inline unsigned
-flac_duration(const FLAC__StreamMetadata_StreamInfo *stream_info)
-{
- assert(stream_info->sample_rate > 0);
-
- return (stream_info->total_samples + stream_info->sample_rate - 1) /
- stream_info->sample_rate;
-}
-
-bool
-flac_parse_replay_gain(struct replay_gain_info *rgi,
- const FLAC__StreamMetadata *block);
-
-bool
-flac_parse_mixramp(char **mixramp_start, char **mixramp_end,
- const FLAC__StreamMetadata *block);
-
-void
-flac_vorbis_comments_to_tag(struct tag *tag, const char *char_tnum,
- const FLAC__StreamMetadata_VorbisComment *comment);
-
-void
-flac_scan_metadata(const char *track,
- const FLAC__StreamMetadata *block,
- const struct tag_handler *handler, void *handler_ctx);
-
-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/gme_decoder_plugin.c b/src/decoder/gme_decoder_plugin.c
deleted file mode 100644
index 237a1deb1..000000000
--- a/src/decoder/gme_decoder_plugin.c
+++ /dev/null
@@ -1,257 +0,0 @@
-#include "config.h"
-#include "../decoder_api.h"
-#include "audio_check.h"
-#include "uri.h"
-#include "tag_handler.h"
-
-#include <glib.h>
-#include <assert.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <gme/gme.h>
-
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "gme"
-
-#define SUBTUNE_PREFIX "tune_"
-
-enum {
- GME_SAMPLE_RATE = 44100,
- GME_CHANNELS = 2,
- GME_BUFFER_FRAMES = 2048,
- GME_BUFFER_SAMPLES = GME_BUFFER_FRAMES * GME_CHANNELS,
-};
-
-/**
- * returns the file path stripped of any /tune_xxx.* subtune
- * suffix
- */
-static char *
-get_container_name(const char *path_fs)
-{
- const char *subtune_suffix = uri_get_suffix(path_fs);
- char *path_container = g_strdup(path_fs);
- char *pat = g_strconcat("*/" SUBTUNE_PREFIX "???.", subtune_suffix, NULL);
- GPatternSpec *path_with_subtune = g_pattern_spec_new(pat);
- g_free(pat);
- if (!g_pattern_match(path_with_subtune,
- strlen(path_container), path_container, NULL)) {
- g_pattern_spec_free(path_with_subtune);
- return path_container;
- }
-
- char *ptr = g_strrstr(path_container, "/" SUBTUNE_PREFIX);
- if (ptr != NULL)
- *ptr='\0';
-
- g_pattern_spec_free(path_with_subtune);
- return path_container;
-}
-
-/**
- * returns tune number from file.nsf/tune_xxx.* style path or 0 if no subtune
- * is appended.
- */
-static int
-get_song_num(const char *path_fs)
-{
- const char *subtune_suffix = uri_get_suffix(path_fs);
- char *pat = g_strconcat("*/" SUBTUNE_PREFIX "???.", subtune_suffix, NULL);
- GPatternSpec *path_with_subtune = g_pattern_spec_new(pat);
- g_free(pat);
-
- if (g_pattern_match(path_with_subtune,
- strlen(path_fs), path_fs, NULL)) {
- char *sub = g_strrstr(path_fs, "/" SUBTUNE_PREFIX);
- g_pattern_spec_free(path_with_subtune);
- if(!sub)
- return 0;
-
- sub += strlen("/" SUBTUNE_PREFIX);
- int song_num = strtol(sub, NULL, 10);
-
- return song_num - 1;
- } else {
- g_pattern_spec_free(path_with_subtune);
- return 0;
- }
-}
-
-static char *
-gme_container_scan(const char *path_fs, const unsigned int tnum)
-{
- Music_Emu *emu;
- const char* gme_err;
- unsigned int num_songs;
-
- gme_err = gme_open_file(path_fs, &emu, GME_SAMPLE_RATE);
- if (gme_err != NULL) {
- g_warning("%s", gme_err);
- return NULL;
- }
-
- num_songs = gme_track_count(emu);
- /* if it only contains a single tune, don't treat as container */
- if (num_songs < 2)
- return NULL;
-
- const char *subtune_suffix = uri_get_suffix(path_fs);
- if (tnum <= num_songs){
- char *subtune = g_strdup_printf(
- SUBTUNE_PREFIX "%03u.%s", tnum, subtune_suffix);
- return subtune;
- } else
- return NULL;
-}
-
-static void
-gme_file_decode(struct decoder *decoder, const char *path_fs)
-{
- float song_len;
- Music_Emu *emu;
- gme_info_t *ti;
- struct audio_format audio_format;
- enum decoder_command cmd;
- short buf[GME_BUFFER_SAMPLES];
- const char* gme_err;
- char *path_container = get_container_name(path_fs);
- int song_num = get_song_num(path_fs);
-
- gme_err = gme_open_file(path_container, &emu, GME_SAMPLE_RATE);
- g_free(path_container);
- if (gme_err != NULL) {
- g_warning("%s", gme_err);
- return;
- }
-
- if((gme_err = gme_track_info(emu, &ti, song_num)) != NULL){
- g_warning("%s", gme_err);
- gme_delete(emu);
- return;
- }
-
- if(ti->length > 0)
- song_len = ti->length / 1000.0;
- else song_len = -1;
-
- /* initialize the MPD decoder */
-
- GError *error = NULL;
- if (!audio_format_init_checked(&audio_format, GME_SAMPLE_RATE,
- SAMPLE_FORMAT_S16, GME_CHANNELS,
- &error)) {
- g_warning("%s", error->message);
- g_error_free(error);
- gme_free_info(ti);
- gme_delete(emu);
- return;
- }
-
- decoder_initialized(decoder, &audio_format, true, song_len);
-
- if((gme_err = gme_start_track(emu, song_num)) != NULL)
- g_warning("%s", gme_err);
-
- if(ti->length > 0)
- gme_set_fade(emu, ti->length);
-
- /* play */
- do {
- gme_err = gme_play(emu, GME_BUFFER_SAMPLES, buf);
- if (gme_err != NULL) {
- g_warning("%s", gme_err);
- return;
- }
- cmd = decoder_data(decoder, NULL, buf, sizeof(buf), 0);
-
- if(cmd == DECODE_COMMAND_SEEK) {
- float where = decoder_seek_where(decoder);
- if((gme_err = gme_seek(emu, (int)where*1000)) != NULL)
- g_warning("%s", gme_err);
- decoder_command_finished(decoder);
- }
-
- if(gme_track_ended(emu))
- break;
- } while(cmd != DECODE_COMMAND_STOP);
-
- gme_free_info(ti);
- gme_delete(emu);
-}
-
-static bool
-gme_scan_file(const char *path_fs,
- const struct tag_handler *handler, void *handler_ctx)
-{
- Music_Emu *emu;
- gme_info_t *ti;
- const char* gme_err;
- char *path_container=get_container_name(path_fs);
- int song_num;
- song_num=get_song_num(path_fs);
-
- gme_err = gme_open_file(path_container, &emu, GME_SAMPLE_RATE);
- g_free(path_container);
- if (gme_err != NULL) {
- g_warning("%s", gme_err);
- return false;
- }
- if((gme_err = gme_track_info(emu, &ti, song_num)) != NULL){
- g_warning("%s", gme_err);
- gme_delete(emu);
- return false;
- }
-
- assert(ti != NULL);
-
- if(ti->length > 0)
- tag_handler_invoke_duration(handler, handler_ctx,
- ti->length / 100);
-
- if(ti->song != NULL){
- if(gme_track_count(emu) > 1){
- /* start numbering subtunes from 1 */
- char *tag_title=g_strdup_printf("%s (%d/%d)",
- ti->song, song_num+1, gme_track_count(emu));
- tag_handler_invoke_tag(handler, handler_ctx,
- TAG_TITLE, tag_title);
- g_free(tag_title);
- }else
- tag_handler_invoke_tag(handler, handler_ctx,
- TAG_TITLE, ti->song);
- }
- if(ti->author != NULL)
- tag_handler_invoke_tag(handler, handler_ctx,
- TAG_ARTIST, ti->author);
- if(ti->game != NULL)
- tag_handler_invoke_tag(handler, handler_ctx,
- TAG_ALBUM, ti->game);
- if(ti->comment != NULL)
- tag_handler_invoke_tag(handler, handler_ctx,
- TAG_COMMENT, ti->comment);
- if(ti->copyright != NULL)
- tag_handler_invoke_tag(handler, handler_ctx,
- TAG_DATE, ti->copyright);
-
- gme_free_info(ti);
- gme_delete(emu);
-
- return true;
-}
-
-static const char *const gme_suffixes[] = {
- "ay", "gbs", "gym", "hes", "kss", "nsf",
- "nsfe", "sap", "spc", "vgm", "vgz",
- NULL
-};
-
-extern const struct decoder_plugin gme_decoder_plugin;
-const struct decoder_plugin gme_decoder_plugin = {
- .name = "gme",
- .file_decode = gme_file_decode,
- .scan_file = gme_scan_file,
- .suffixes = gme_suffixes,
- .container_scan = gme_container_scan,
-};
diff --git a/src/decoder/mp4ff_decoder_plugin.c b/src/decoder/mp4ff_decoder_plugin.c
deleted file mode 100644
index ca78a22d0..000000000
--- a/src/decoder/mp4ff_decoder_plugin.c
+++ /dev/null
@@ -1,448 +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.
- */
-
-#include "config.h"
-#include "decoder_api.h"
-#include "audio_check.h"
-#include "tag_table.h"
-#include "tag_handler.h"
-
-#include <glib.h>
-
-#include <mp4ff.h>
-#include <faad.h>
-
-#include <assert.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "mp4ff"
-
-/* all code here is either based on or copied from FAAD2's frontend code */
-
-struct mp4ff_input_stream {
- mp4ff_callback_t callback;
-
- struct decoder *decoder;
- struct input_stream *input_stream;
-};
-
-static int
-mp4_get_aac_track(mp4ff_t * infile, faacDecHandle decoder,
- uint32_t *sample_rate, unsigned char *channels_r)
-{
-#ifdef HAVE_FAAD_LONG
- /* neaacdec.h declares all arguments as "unsigned long", but
- internally expects uint32_t pointers. To avoid gcc
- warnings, use this workaround. */
- unsigned long *sample_rate_r = (unsigned long*)sample_rate;
-#else
- uint32_t *sample_rate_r = sample_rate;
-#endif
- int i, rc;
- int num_tracks = mp4ff_total_tracks(infile);
-
- for (i = 0; i < num_tracks; i++) {
- unsigned char *buff = NULL;
- unsigned int buff_size = 0;
-
- if (mp4ff_get_track_type(infile, i) != 1)
- /* not an audio track */
- continue;
-
- if (decoder == NULL)
- /* have don't have a decoder to initialize -
- we're done now, because we found an audio
- track */
- return i;
-
- mp4ff_get_decoder_config(infile, i, &buff, &buff_size);
- if (buff == NULL)
- continue;
-
- rc = faacDecInit2(decoder, buff, buff_size,
- sample_rate_r, channels_r);
- free(buff);
-
- if (rc >= 0)
- /* found a valid AAC track */
- return i;
- }
-
- /* can't decode this */
- return -1;
-}
-
-static uint32_t
-mp4_read(void *user_data, void *buffer, uint32_t length)
-{
- struct mp4ff_input_stream *mis = user_data;
-
- if (length == 0)
- /* libmp4ff is known to attempt to read 0 bytes - make
- this a special case, because the input_stream API
- would not allow this */
- return 0;
-
- return decoder_read(mis->decoder, mis->input_stream, buffer, length);
-}
-
-static uint32_t
-mp4_seek(void *user_data, uint64_t position)
-{
- struct mp4ff_input_stream *mis = user_data;
-
- return input_stream_lock_seek(mis->input_stream, position, SEEK_SET,
- NULL)
- ? 0 : -1;
-}
-
-static const mp4ff_callback_t mpd_mp4ff_callback = {
- .read = mp4_read,
- .seek = mp4_seek,
-};
-
-static mp4ff_t *
-mp4ff_input_stream_open(struct mp4ff_input_stream *mis,
- struct decoder *decoder,
- struct input_stream *input_stream)
-{
- mis->callback = mpd_mp4ff_callback;
- mis->callback.user_data = mis;
- mis->decoder = decoder;
- mis->input_stream = input_stream;
-
- return mp4ff_open_read(&mis->callback);
-}
-
-static faacDecHandle
-mp4_faad_new(mp4ff_t *mp4fh, int *track_r, struct audio_format *audio_format)
-{
- faacDecHandle decoder;
- faacDecConfigurationPtr config;
- int track;
- uint32_t sample_rate;
- unsigned char channels;
- GError *error = NULL;
-
- decoder = faacDecOpen();
-
- config = faacDecGetCurrentConfiguration(decoder);
- config->outputFormat = FAAD_FMT_16BIT;
-#ifdef HAVE_FAACDECCONFIGURATION_DOWNMATRIX
- config->downMatrix = 1;
-#endif
-#ifdef HAVE_FAACDECCONFIGURATION_DONTUPSAMPLEIMPLICITSBR
- config->dontUpSampleImplicitSBR = 0;
-#endif
- faacDecSetConfiguration(decoder, config);
-
- track = mp4_get_aac_track(mp4fh, decoder, &sample_rate, &channels);
- if (track < 0) {
- g_warning("No AAC track found");
- faacDecClose(decoder);
- return NULL;
- }
-
- if (!audio_format_init_checked(audio_format, sample_rate,
- SAMPLE_FORMAT_S16, channels,
- &error)) {
- g_warning("%s", error->message);
- g_error_free(error);
- faacDecClose(decoder);
- return NULL;
- }
-
- *track_r = track;
-
- return decoder;
-}
-
-static void
-mp4_decode(struct decoder *mpd_decoder, struct input_stream *input_stream)
-{
- struct mp4ff_input_stream mis;
- mp4ff_t *mp4fh;
- int32_t track;
- float file_time, total_time;
- int32_t scale;
- faacDecHandle decoder;
- struct audio_format audio_format;
- faacDecFrameInfo frame_info;
- unsigned char *mp4_buffer;
- unsigned int mp4_buffer_size;
- long sample_id;
- long num_samples;
- long dur;
- unsigned int sample_count;
- char *sample_buffer;
- size_t sample_buffer_length;
- unsigned int initial = 1;
- float *seek_table;
- long seek_table_end = -1;
- bool seek_position_found = false;
- long offset;
- uint16_t bit_rate = 0;
- bool seeking = false;
- double seek_where = 0;
- enum decoder_command cmd = DECODE_COMMAND_NONE;
-
- mp4fh = mp4ff_input_stream_open(&mis, mpd_decoder, input_stream);
- if (!mp4fh) {
- g_warning("Input does not appear to be a mp4 stream.\n");
- return;
- }
-
- decoder = mp4_faad_new(mp4fh, &track, &audio_format);
- if (decoder == NULL) {
- mp4ff_close(mp4fh);
- return;
- }
-
- file_time = mp4ff_get_track_duration_use_offsets(mp4fh, track);
- scale = mp4ff_time_scale(mp4fh, track);
-
- if (scale < 0) {
- g_warning("Error getting audio format of mp4 AAC track.\n");
- faacDecClose(decoder);
- mp4ff_close(mp4fh);
- return;
- }
- total_time = ((float)file_time) / scale;
-
- num_samples = mp4ff_num_samples(mp4fh, track);
- if (num_samples > (long)(G_MAXINT / sizeof(float))) {
- g_warning("Integer overflow.\n");
- faacDecClose(decoder);
- mp4ff_close(mp4fh);
- return;
- }
-
- file_time = 0.0;
-
- seek_table = input_stream->seekable
- ? g_malloc(sizeof(float) * num_samples)
- : NULL;
-
- decoder_initialized(mpd_decoder, &audio_format,
- input_stream->seekable,
- total_time);
-
- for (sample_id = 0;
- sample_id < num_samples && cmd != DECODE_COMMAND_STOP;
- sample_id++) {
- if (cmd == DECODE_COMMAND_SEEK) {
- assert(seek_table != NULL);
-
- seeking = true;
- seek_where = decoder_seek_where(mpd_decoder);
- }
-
- if (seeking && seek_table_end > 1 &&
- seek_table[seek_table_end] >= seek_where) {
- int i = 2;
-
- assert(seek_table != NULL);
-
- while (seek_table[i] < seek_where)
- i++;
- sample_id = i - 1;
- file_time = seek_table[sample_id];
- }
-
- dur = mp4ff_get_sample_duration(mp4fh, track, sample_id);
- offset = mp4ff_get_sample_offset(mp4fh, track, sample_id);
-
- if (seek_table != NULL && sample_id > seek_table_end) {
- seek_table[sample_id] = file_time;
- seek_table_end = sample_id;
- }
-
- if (sample_id == 0)
- dur = 0;
- if (offset > dur)
- dur = 0;
- else
- dur -= offset;
- file_time += ((float)dur) / scale;
-
- if (seeking && file_time >= seek_where)
- seek_position_found = true;
-
- if (seeking && seek_position_found) {
- seek_position_found = false;
- seeking = 0;
- decoder_command_finished(mpd_decoder);
- }
-
- if (seeking)
- continue;
-
- if (mp4ff_read_sample(mp4fh, track, sample_id, &mp4_buffer,
- &mp4_buffer_size) == 0)
- break;
-
-#ifdef HAVE_FAAD_BUFLEN_FUNCS
- sample_buffer = faacDecDecode(decoder, &frame_info, mp4_buffer,
- mp4_buffer_size);
-#else
- sample_buffer = faacDecDecode(decoder, &frame_info, mp4_buffer);
-#endif
-
- free(mp4_buffer);
-
- if (frame_info.error > 0) {
- g_warning("faad2 error: %s\n",
- faacDecGetErrorMessage(frame_info.error));
- break;
- }
-
- if (frame_info.channels != audio_format.channels) {
- g_warning("channel count changed from %u to %u",
- audio_format.channels, frame_info.channels);
- break;
- }
-
-#ifdef HAVE_FAACDECFRAMEINFO_SAMPLERATE
- if (frame_info.samplerate != audio_format.sample_rate) {
- g_warning("sample rate changed from %u to %lu",
- audio_format.sample_rate,
- (unsigned long)frame_info.samplerate);
- break;
- }
-#endif
-
- if (audio_format.channels * (unsigned long)(dur + offset) > frame_info.samples) {
- dur = frame_info.samples / audio_format.channels;
- offset = 0;
- }
-
- sample_count = (unsigned long)(dur * audio_format.channels);
-
- if (sample_count > 0) {
- initial = 0;
- bit_rate = frame_info.bytesconsumed * 8.0 *
- frame_info.channels * scale /
- frame_info.samples / 1000 + 0.5;
- }
-
- sample_buffer_length = sample_count * 2;
-
- sample_buffer += offset * audio_format.channels * 2;
-
- cmd = decoder_data(mpd_decoder, input_stream,
- sample_buffer, sample_buffer_length,
- bit_rate);
- }
-
- g_free(seek_table);
- faacDecClose(decoder);
- mp4ff_close(mp4fh);
-}
-
-static const struct tag_table mp4ff_tags[] = {
- { "album artist", TAG_ALBUM_ARTIST },
- { "writer", TAG_COMPOSER },
- { "band", TAG_PERFORMER },
- { NULL, TAG_NUM_OF_ITEM_TYPES }
-};
-
-static enum tag_type
-mp4ff_tag_name_parse(const char *name)
-{
- enum tag_type type = tag_table_lookup_i(mp4ff_tags, name);
- if (type == TAG_NUM_OF_ITEM_TYPES)
- type = tag_name_parse_i(name);
-
- if (g_ascii_strcasecmp(name, "albumartist") == 0 ||
- g_ascii_strcasecmp(name, "album_artist") == 0)
- return TAG_ALBUM_ARTIST;
-
- return type;
-}
-
-static bool
-mp4ff_scan_stream(struct input_stream *is,
- const struct tag_handler *handler, void *handler_ctx)
-{
- struct mp4ff_input_stream mis;
- int32_t track;
- int32_t file_time;
- int32_t scale;
- int i;
-
- mp4ff_t *mp4fh = mp4ff_input_stream_open(&mis, NULL, is);
- if (mp4fh == NULL)
- return false;
-
- track = mp4_get_aac_track(mp4fh, NULL, NULL, NULL);
- if (track < 0) {
- mp4ff_close(mp4fh);
- return false;
- }
-
- file_time = mp4ff_get_track_duration_use_offsets(mp4fh, track);
- scale = mp4ff_time_scale(mp4fh, track);
- if (scale < 0) {
- mp4ff_close(mp4fh);
- return false;
- }
-
- tag_handler_invoke_duration(handler, handler_ctx,
- ((float)file_time) / scale + 0.5);
-
- for (i = 0; i < mp4ff_meta_get_num_items(mp4fh); i++) {
- char *item;
- char *value;
-
- mp4ff_meta_get_by_index(mp4fh, i, &item, &value);
-
- tag_handler_invoke_pair(handler, handler_ctx, item, value);
-
- enum tag_type type = mp4ff_tag_name_parse(item);
- if (type != TAG_NUM_OF_ITEM_TYPES)
- tag_handler_invoke_tag(handler, handler_ctx,
- type, value);
-
- free(item);
- free(value);
- }
-
- mp4ff_close(mp4fh);
-
- return true;
-}
-
-static const char *const mp4_suffixes[] = {
- "m4a",
- "m4b",
- "mp4",
- NULL
-};
-
-static const char *const mp4_mime_types[] = { "audio/mp4", "audio/m4a", NULL };
-
-const struct decoder_plugin mp4ff_decoder_plugin = {
- .name = "mp4ff",
- .stream_decode = mp4_decode,
- .scan_stream = mp4ff_scan_stream,
- .suffixes = mp4_suffixes,
- .mime_types = mp4_mime_types,
-};
diff --git a/src/decoder/sidplay_decoder_plugin.cxx b/src/decoder/sidplay_decoder_plugin.cxx
index 5d162f179..486dd816f 100644
--- a/src/decoder/sidplay_decoder_plugin.cxx
+++ b/src/decoder/sidplay_decoder_plugin.cxx
@@ -18,25 +18,24 @@
*/
#include "config.h"
-
-extern "C" {
-#include "../decoder_api.h"
-#include "tag_handler.h"
-}
+#include "../DecoderAPI.hxx"
+#include "tag/TagHandler.hxx"
+#include "util/Domain.hxx"
+#include "Log.hxx"
#include <errno.h>
#include <stdlib.h>
+#include <string.h>
#include <glib.h>
#include <sidplay/sidplay2.h>
#include <sidplay/builders/resid.h>
#include <sidplay/utils/SidTuneMod.h>
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "sidplay"
-
#define SUBTUNE_PREFIX "tune_"
+static constexpr Domain sidplay_domain("sidplay");
+
static GPatternSpec *path_with_subtune;
static const char *songlength_file;
static GKeyFile *songlength_database;
@@ -54,8 +53,9 @@ sidplay_load_songlength_db(const char *path)
gsize size;
if (!g_file_get_contents(path, &data, &size, &error)) {
- g_warning("unable to read songlengths file %s: %s",
- path, error->message);
+ FormatError(sidplay_domain,
+ "unable to read songlengths file %s: %s",
+ path, error->message);
g_error_free(error);
return NULL;
}
@@ -70,8 +70,9 @@ sidplay_load_songlength_db(const char *path)
G_KEY_FILE_NONE, &error);
g_free(data);
if (!success) {
- g_warning("unable to parse songlengths file %s: %s",
- path, error->message);
+ FormatError(sidplay_domain,
+ "unable to parse songlengths file %s: %s",
+ path, error->message);
g_error_free(error);
g_key_file_free(db);
return NULL;
@@ -82,29 +83,27 @@ sidplay_load_songlength_db(const char *path)
}
static bool
-sidplay_init(const struct config_param *param)
+sidplay_init(const config_param &param)
{
/* read the songlengths database file */
- songlength_file=config_get_block_string(param,
- "songlength_database", NULL);
+ songlength_file = param.GetBlockValue("songlength_database");
if (songlength_file != NULL)
songlength_database = sidplay_load_songlength_db(songlength_file);
- default_songlength=config_get_block_unsigned(param,
- "default_songlength", 0);
+ default_songlength = param.GetBlockValue("default_songlength", 0u);
- all_files_are_containers=config_get_block_bool(param,
- "all_files_are_containers", true);
+ all_files_are_containers =
+ param.GetBlockValue("all_files_are_containers", true);
path_with_subtune=g_pattern_spec_new(
"*/" SUBTUNE_PREFIX "???.sid");
- filter_setting=config_get_block_bool(param, "filter", true);
+ filter_setting = param.GetBlockValue("filter", true);
return true;
}
-void
+static void
sidplay_finish()
{
g_pattern_spec_free(path_with_subtune);
@@ -136,7 +135,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,
@@ -166,13 +165,14 @@ get_song_length(const char *path_fs)
SidTuneMod tune(sid_file);
g_free(sid_file);
if(!tune) {
- g_warning("failed to load file for calculating md5 sum");
+ LogWarning(sidplay_domain,
+ "failed to load file for calculating md5 sum");
return -1;
}
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,
@@ -209,7 +209,7 @@ sidplay_file_decode(struct decoder *decoder, const char *path_fs)
SidTune tune(path_container, NULL, true);
g_free(path_container);
if (!tune) {
- g_warning("failed to load file");
+ LogWarning(sidplay_domain, "failed to load file");
return;
}
@@ -224,7 +224,8 @@ sidplay_file_decode(struct decoder *decoder, const char *path_fs)
sidplay2 player;
int iret = player.load(&tune);
if (iret != 0) {
- g_warning("sidplay2.load() failed: %s", player.error());
+ FormatWarning(sidplay_domain,
+ "sidplay2.load() failed: %s", player.error());
return;
}
@@ -232,19 +233,20 @@ sidplay_file_decode(struct decoder *decoder, const char *path_fs)
ReSIDBuilder builder("ReSID");
if (!builder) {
- g_warning("failed to initialize ReSIDBuilder");
+ LogWarning(sidplay_domain,
+ "failed to initialize ReSIDBuilder");
return;
}
builder.create(player.info().maxsids);
if (!builder) {
- g_warning("ReSIDBuilder.create() failed");
+ LogWarning(sidplay_domain, "ReSIDBuilder.create() failed");
return;
}
builder.filter(filter_setting);
if (!builder) {
- g_warning("ReSIDBuilder.filter() failed");
+ LogWarning(sidplay_domain, "ReSIDBuilder.filter() failed");
return;
}
@@ -278,24 +280,24 @@ sidplay_file_decode(struct decoder *decoder, const char *path_fs)
iret = player.config(config);
if (iret != 0) {
- g_warning("sidplay2.config() failed: %s", player.error());
+ FormatWarning(sidplay_domain,
+ "sidplay2.config() failed: %s", player.error());
return;
}
/* initialize the MPD decoder */
- struct audio_format audio_format;
- audio_format_init(&audio_format, 48000, SAMPLE_FORMAT_S16, channels);
- assert(audio_format_valid(&audio_format));
+ const AudioFormat audio_format(48000, SampleFormat::S16, channels);
+ assert(audio_format.IsValid());
- decoder_initialized(decoder, &audio_format, true, (float)song_len);
+ decoder_initialized(decoder, audio_format, true, (float)song_len);
/* .. and play */
const unsigned timebase = player.timebase();
song_len *= timebase;
- enum decoder_command cmd;
+ DecoderCommand cmd;
do {
char buffer[4096];
size_t nbytes;
@@ -308,7 +310,7 @@ sidplay_file_decode(struct decoder *decoder, const char *path_fs)
cmd = decoder_data(decoder, NULL, buffer, nbytes, 0);
- if(cmd==DECODE_COMMAND_SEEK) {
+ if (cmd == DecoderCommand::SEEK) {
unsigned data_time = player.time();
unsigned target_time = (unsigned)
(decoder_seek_where(decoder) * timebase);
@@ -330,10 +332,10 @@ 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);
+ } while (cmd != DecoderCommand::STOP);
}
static bool