diff options
Diffstat (limited to 'src/decoder')
-rw-r--r-- | src/decoder/AdPlugDecoderPlugin.cxx | 142 | ||||
-rw-r--r-- | src/decoder/AdPlugDecoderPlugin.h | 25 | ||||
-rw-r--r-- | src/decoder/AudiofileDecoderPlugin.cxx (renamed from src/decoder/audiofile_decoder_plugin.c) | 127 | ||||
-rw-r--r-- | src/decoder/AudiofileDecoderPlugin.hxx | 25 | ||||
-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.hxx | 25 | ||||
-rw-r--r-- | src/decoder/FfmpegDecoderPlugin.cxx (renamed from src/decoder/ffmpeg_decoder_plugin.c) | 461 | ||||
-rw-r--r-- | src/decoder/FfmpegDecoderPlugin.hxx | 25 | ||||
-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.h | 26 | ||||
-rw-r--r-- | src/decoder/FlacDomain.cxx | 24 | ||||
-rw-r--r-- | src/decoder/FlacDomain.hxx | 27 | ||||
-rw-r--r-- | src/decoder/FlacIOHandle.cxx | 114 | ||||
-rw-r--r-- | src/decoder/FlacIOHandle.hxx | 45 | ||||
-rw-r--r-- | src/decoder/FlacInput.cxx | 154 | ||||
-rw-r--r-- | src/decoder/FlacInput.hxx | 72 | ||||
-rw-r--r-- | src/decoder/FlacMetadata.cxx (renamed from src/decoder/flac_metadata.c) | 143 | ||||
-rw-r--r-- | src/decoder/FlacMetadata.hxx | 140 | ||||
-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.hxx | 25 | ||||
-rw-r--r-- | src/decoder/GmeDecoderPlugin.cxx | 290 | ||||
-rw-r--r-- | src/decoder/GmeDecoderPlugin.hxx | 25 | ||||
-rw-r--r-- | src/decoder/MadDecoderPlugin.cxx (renamed from src/decoder/mad_decoder_plugin.c) | 719 | ||||
-rw-r--r-- | src/decoder/MadDecoderPlugin.hxx | 25 | ||||
-rw-r--r-- | src/decoder/MikmodDecoderPlugin.cxx (renamed from src/decoder/mikmod_decoder_plugin.c) | 84 | ||||
-rw-r--r-- | src/decoder/MikmodDecoderPlugin.hxx | 25 | ||||
-rw-r--r-- | src/decoder/ModplugDecoderPlugin.cxx (renamed from src/decoder/modplug_decoder_plugin.c) | 110 | ||||
-rw-r--r-- | src/decoder/ModplugDecoderPlugin.hxx | 25 | ||||
-rw-r--r-- | src/decoder/MpcdecDecoderPlugin.cxx (renamed from src/decoder/mpcdec_decoder_plugin.c) | 214 | ||||
-rw-r--r-- | src/decoder/MpcdecDecoderPlugin.hxx | 25 | ||||
-rw-r--r-- | src/decoder/Mpg123DecoderPlugin.cxx (renamed from src/decoder/mpg123_decoder_plugin.c) | 103 | ||||
-rw-r--r-- | src/decoder/Mpg123DecoderPlugin.hxx | 25 | ||||
-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.cxx | 37 | ||||
-rw-r--r-- | src/decoder/OggFind.hxx | 38 | ||||
-rw-r--r-- | src/decoder/OggSyncState.hxx | 78 | ||||
-rw-r--r-- | src/decoder/OggUtil.cxx | 118 | ||||
-rw-r--r-- | src/decoder/OggUtil.hxx | 87 | ||||
-rw-r--r-- | src/decoder/OpusDecoderPlugin.cxx | 403 | ||||
-rw-r--r-- | src/decoder/OpusDecoderPlugin.h | 25 | ||||
-rw-r--r-- | src/decoder/OpusDomain.cxx | 24 | ||||
-rw-r--r-- | src/decoder/OpusDomain.hxx | 27 | ||||
-rw-r--r-- | src/decoder/OpusHead.cxx | 44 | ||||
-rw-r--r-- | src/decoder/OpusHead.hxx | 30 | ||||
-rw-r--r-- | src/decoder/OpusReader.hxx | 100 | ||||
-rw-r--r-- | src/decoder/OpusTags.cxx | 77 | ||||
-rw-r--r-- | src/decoder/OpusTags.hxx | 31 | ||||
-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.hxx | 25 | ||||
-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.h | 25 | ||||
-rw-r--r-- | src/decoder/VorbisDomain.cxx | 24 | ||||
-rw-r--r-- | src/decoder/VorbisDomain.hxx | 27 | ||||
-rw-r--r-- | src/decoder/WavpackDecoderPlugin.cxx (renamed from src/decoder/wavpack_decoder_plugin.c) | 146 | ||||
-rw-r--r-- | src/decoder/WavpackDecoderPlugin.hxx | 25 | ||||
-rw-r--r-- | src/decoder/WildmidiDecoderPlugin.cxx (renamed from src/decoder/wildmidi_decoder_plugin.c) | 99 | ||||
-rw-r--r-- | src/decoder/WildmidiDecoderPlugin.hxx | 25 | ||||
-rw-r--r-- | src/decoder/XiphTags.cxx | 28 | ||||
-rw-r--r-- | src/decoder/XiphTags.hxx | 28 | ||||
-rw-r--r-- | src/decoder/flac_compat.h | 114 | ||||
-rw-r--r-- | src/decoder/flac_metadata.h | 64 | ||||
-rw-r--r-- | src/decoder/gme_decoder_plugin.c | 257 | ||||
-rw-r--r-- | src/decoder/mp4ff_decoder_plugin.c | 448 | ||||
-rw-r--r-- | src/decoder/sidplay_decoder_plugin.cxx | 78 |
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 ¶m) +{ + 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 ¶m) { - 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 ¶m) { 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 ¶m) { -#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 ¶m) { - 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 ¶m) { 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 ¶m) { 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 ¶m) { 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 ¶m) +{ + 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, ¤t_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, + ¤t_section); +#else + float **per_channel; + long nframes = ov_read_float(&vf, &per_channel, + frames_per_buffer, + ¤t_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 ¶m) { - 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 ¶m) { /* 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 |