From df3e7a9248c6e896329385a6c5677fe9a5f69c6f Mon Sep 17 00:00:00 2001 From: Denis Krjuchkov Date: Sun, 5 May 2013 13:28:18 +0600 Subject: .gitignore: always ignore any .exe files --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index abb37b0db..a20d3c54b 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ *.la *.lo *.o +*.exe .deps .dirstamp Makefile @@ -31,7 +32,6 @@ ltmain.sh missing mkinstalldirs mpd -mpd.exe mpd.service stamp-h1 tags -- cgit v1.2.3 From 896015bf533c6bbca3026f6d47400d7241a3935a Mon Sep 17 00:00:00 2001 From: Denis Krjuchkov Date: Sun, 5 May 2013 13:34:12 +0600 Subject: DirectoryReader: rename Failed() to HasFailed() for consistency with TextFile --- src/Mapper.cxx | 2 +- src/fs/DirectoryReader.hxx | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Mapper.cxx b/src/Mapper.cxx index 559df0d23..40e762e4b 100644 --- a/src/Mapper.cxx +++ b/src/Mapper.cxx @@ -100,7 +100,7 @@ check_directory(const char *path_utf8, const Path &path_fs) #endif const DirectoryReader reader(path_fs); - if (reader.Failed() && errno == EACCES) + if (reader.HasFailed() && errno == EACCES) g_warning("No permission to read directory: %s", path_utf8); } diff --git a/src/fs/DirectoryReader.hxx b/src/fs/DirectoryReader.hxx index caa1e90ec..4e4a56345 100644 --- a/src/fs/DirectoryReader.hxx +++ b/src/fs/DirectoryReader.hxx @@ -47,14 +47,14 @@ public: * Destroys this instance. */ ~DirectoryReader() { - if (!Failed()) + if (!HasFailed()) closedir(dirp); } /** * Checks if directory failed to open. */ - bool Failed() const { + bool HasFailed() const { return dirp == nullptr; } @@ -62,7 +62,7 @@ public: * Checks if directory entry is available. */ bool HasEntry() const { - assert(!Failed()); + assert(!HasFailed()); return ent != nullptr; } @@ -70,7 +70,7 @@ public: * Reads next directory entry. */ bool ReadEntry() { - assert(!Failed()); + assert(!HasFailed()); ent = readdir(dirp); return HasEntry(); } -- cgit v1.2.3 From 96019f4a025bfd918673f7c628f92423aef72276 Mon Sep 17 00:00:00 2001 From: Denis Krjuchkov Date: Sun, 5 May 2013 14:58:07 +0600 Subject: UpdateWalk, ExcludeList: use Path, file system API, DirectoryReader, log in UTF8 --- src/ExcludeList.cxx | 6 +++--- src/ExcludeList.hxx | 2 +- src/UpdateWalk.cxx | 50 +++++++++++++++++++++++--------------------------- 3 files changed, 27 insertions(+), 31 deletions(-) diff --git a/src/ExcludeList.cxx b/src/ExcludeList.cxx index 0daa432dd..ed04b8d38 100644 --- a/src/ExcludeList.cxx +++ b/src/ExcludeList.cxx @@ -63,14 +63,14 @@ ExcludeList::LoadFile(const Path &path_fs) } bool -ExcludeList::Check(const char *name_fs) const +ExcludeList::Check(const Path &name_fs) const { - assert(name_fs != NULL); + assert(!name_fs.IsNull()); /* XXX include full path name in check */ for (const auto &i : patterns) - if (i.Check(name_fs)) + if (i.Check(name_fs.c_str())) return true; return false; diff --git a/src/ExcludeList.hxx b/src/ExcludeList.hxx index f3dc1f057..7111465a3 100644 --- a/src/ExcludeList.hxx +++ b/src/ExcludeList.hxx @@ -73,7 +73,7 @@ public: * Checks whether one of the patterns in the .mpdignore file matches * the specified file name. */ - bool Check(const char *name_fs) const; + bool Check(const Path &name_fs) const; }; diff --git a/src/UpdateWalk.cxx b/src/UpdateWalk.cxx index 1d26e0ebd..4b415895e 100644 --- a/src/UpdateWalk.cxx +++ b/src/UpdateWalk.cxx @@ -34,6 +34,7 @@ #include "conf.h" #include "fs/Path.hxx" #include "fs/FileSystem.hxx" +#include "fs/DirectoryReader.hxx" #include "util/UriUtil.hxx" #include @@ -102,7 +103,7 @@ remove_excluded_from_directory(Directory *directory, directory_for_each_child_safe(child, n, directory) { const Path name_fs = Path::FromUTF8(child->GetName()); - if (name_fs.IsNull() || exclude_list.Check(name_fs.c_str())) { + if (name_fs.IsNull() || exclude_list.Check(name_fs)) { delete_directory(child); modified = true; } @@ -113,7 +114,7 @@ remove_excluded_from_directory(Directory *directory, assert(song->parent == directory); const Path name_fs = Path::FromUTF8(song->uri); - if (name_fs.IsNull() || exclude_list.Check(name_fs.c_str())) { + if (name_fs.IsNull() || exclude_list.Check(name_fs)) { delete_song(directory, song); modified = true; } @@ -261,8 +262,9 @@ update_directory_child(Directory *directory, /* we don't look at "." / ".." nor files with newlines in their name */ G_GNUC_PURE -static bool skip_path(const char *path) +static bool skip_path(const Path &path_fs) { + const char *path = path_fs.c_str(); return (path[0] == '.' && path[1] == 0) || (path[0] == '.' && path[1] == '.' && path[2] == 0) || strchr(path, '\n') != NULL; @@ -277,20 +279,11 @@ skip_symlink(const Directory *directory, const char *utf8_name) if (path_fs.IsNull()) return true; - char buffer[MPD_PATH_MAX]; - ssize_t length = readlink(path_fs.c_str(), buffer, sizeof(buffer)); - if (length < 0) + const Path target = ReadLink(path_fs); + if (target.IsNull()) /* don't skip if this is not a symlink */ return errno != EINVAL; - if ((size_t)length >= sizeof(buffer)) - /* skip symlinks when the buffer is too small for the - link target */ - return true; - - /* null-terminate the buffer, because readlink() will not */ - buffer[length] = 0; - if (!follow_inside_symlinks && !follow_outside_symlinks) { /* ignore all symlinks */ return true; @@ -299,16 +292,18 @@ skip_symlink(const Directory *directory, const char *utf8_name) return false; } - if (g_path_is_absolute(buffer)) { + const char *target_str = target.c_str(); + + if (g_path_is_absolute(target_str)) { /* if the symlink points to an absolute path, see if that path is inside the music directory */ - const char *relative = map_to_relative_path(buffer); - return relative > buffer + const char *relative = map_to_relative_path(target_str); + return relative > target_str ? !follow_inside_symlinks : !follow_outside_symlinks; } - const char *p = buffer; + const char *p = target_str; while (*p == '.') { if (p[1] == '.' && G_IS_DIR_SEPARATOR(p[2])) { /* "../" moves to parent directory */ @@ -352,10 +347,12 @@ update_directory(Directory *directory, const struct stat *st) if (path_fs.IsNull()) return false; - DIR *dir = opendir(path_fs.c_str()); - if (!dir) { + DirectoryReader reader(path_fs); + if (reader.HasFailed()) { + int error = errno; + const auto path_utf8 = path_fs.ToUTF8(); g_warning("Failed to open directory %s: %s", - path_fs.c_str(), g_strerror(errno)); + path_utf8.c_str(), g_strerror(error)); return false; } @@ -367,15 +364,16 @@ update_directory(Directory *directory, const struct stat *st) purge_deleted_from_directory(directory); - struct dirent *ent; - while ((ent = readdir(dir))) { + while (reader.ReadEntry()) { std::string utf8; struct stat st2; - if (skip_path(ent->d_name) || exclude_list.Check(ent->d_name)) + const Path entry = reader.GetEntry(); + + if (skip_path(entry) || exclude_list.Check(entry)) continue; - utf8 = Path::ToUTF8(ent->d_name); + utf8 = entry.ToUTF8(); if (utf8.empty()) continue; @@ -390,8 +388,6 @@ update_directory(Directory *directory, const struct stat *st) modified |= delete_name_in(directory, utf8.c_str()); } - closedir(dir); - directory->mtime = st->st_mtime; return true; -- cgit v1.2.3 From 459d824c50b3ef444f0b94cc60bcbec83addf801 Mon Sep 17 00:00:00 2001 From: Denis Krjuchkov Date: Sun, 5 May 2013 15:38:59 +0600 Subject: ClientFile.cxx: include config.h --- src/ClientFile.cxx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ClientFile.cxx b/src/ClientFile.cxx index ca5acb229..dccf0a327 100644 --- a/src/ClientFile.cxx +++ b/src/ClientFile.cxx @@ -17,6 +17,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "config.h" #include "ClientFile.hxx" #include "Client.hxx" #include "ack.h" -- cgit v1.2.3 From a688745bdc6db32e2cb7765c5d113958dcd49411 Mon Sep 17 00:00:00 2001 From: Denis Krjuchkov Date: Sun, 5 May 2013 15:42:29 +0600 Subject: ClientFile: use Path and file system API, update usages accordingly This commit also fixes incorrect passing of UTF-8 strings to client_allow_file --- src/ClientFile.cxx | 6 ++++-- src/ClientFile.hxx | 3 ++- src/OtherCommands.cxx | 10 +++++++++- src/QueueCommands.cxx | 27 +++++++++++++++++++++------ 4 files changed, 36 insertions(+), 10 deletions(-) diff --git a/src/ClientFile.cxx b/src/ClientFile.cxx index dccf0a327..07fa767b0 100644 --- a/src/ClientFile.cxx +++ b/src/ClientFile.cxx @@ -22,6 +22,8 @@ #include "Client.hxx" #include "ack.h" #include "io_error.h" +#include "fs/Path.hxx" +#include "fs/FileSystem.hxx" #include #include @@ -29,7 +31,7 @@ #include bool -client_allow_file(const Client *client, const char *path_fs, +client_allow_file(const Client *client, const Path &path_fs, GError **error_r) { #ifdef WIN32 @@ -54,7 +56,7 @@ client_allow_file(const Client *client, const char *path_fs, } struct stat st; - if (stat(path_fs, &st) < 0) { + if (!StatFile(path_fs, st)) { set_error_errno(error_r); return false; } diff --git a/src/ClientFile.hxx b/src/ClientFile.hxx index 48e00c44f..c2ea25f51 100644 --- a/src/ClientFile.hxx +++ b/src/ClientFile.hxx @@ -25,6 +25,7 @@ #include class Client; +class Path; /** * Is this client allowed to use the specified local file? @@ -37,7 +38,7 @@ class Client; * @return true if access is allowed */ bool -client_allow_file(const Client *client, const char *path_fs, +client_allow_file(const Client *client, const Path &path_fs, GError **error_r); #endif diff --git a/src/OtherCommands.cxx b/src/OtherCommands.cxx index 07fbc8442..7f592ee9f 100644 --- a/src/OtherCommands.cxx +++ b/src/OtherCommands.cxx @@ -34,6 +34,7 @@ #include "ls.hxx" #include "Volume.hxx" #include "util/UriUtil.hxx" +#include "fs/Path.hxx" extern "C" { #include "stats.h" @@ -117,9 +118,16 @@ handle_lsinfo(Client *client, int argc, char *argv[]) if (strncmp(uri, "file:///", 8) == 0) { /* print information about an arbitrary local file */ const char *path_utf8 = uri + 7; + const Path path_fs = Path::FromUTF8(path_utf8); + + if (path_fs.IsNull()) { + command_error(client, ACK_ERROR_NO_EXIST, + "unsupported file name"); + return COMMAND_RETURN_ERROR; + } GError *error = NULL; - if (!client_allow_file(client, path_utf8, &error)) + if (!client_allow_file(client, path_fs, &error)) return print_error(client, error); struct song *song = song_file_load(path_utf8, NULL); diff --git a/src/QueueCommands.cxx b/src/QueueCommands.cxx index aac6d5a51..210b1501a 100644 --- a/src/QueueCommands.cxx +++ b/src/QueueCommands.cxx @@ -32,6 +32,7 @@ #include "protocol/Result.hxx" #include "ls.hxx" #include "util/UriUtil.hxx" +#include "fs/Path.hxx" #include @@ -42,13 +43,20 @@ handle_add(Client *client, G_GNUC_UNUSED int argc, char *argv[]) enum playlist_result result; if (strncmp(uri, "file:///", 8) == 0) { - const char *path = uri + 7; + const char *path_utf8 = uri + 7; + const Path path_fs = Path::FromUTF8(path_utf8); + + if (path_fs.IsNull()) { + command_error(client, ACK_ERROR_NO_EXIST, + "unsupported file name"); + return COMMAND_RETURN_ERROR; + } GError *error = NULL; - if (!client_allow_file(client, path, &error)) + if (!client_allow_file(client, path_fs, &error)) return print_error(client, error); - result = client->partition.AppendFile(path); + result = client->partition.AppendFile(path_utf8); return print_playlist_result(client, result); } @@ -78,13 +86,20 @@ handle_addid(Client *client, int argc, char *argv[]) enum playlist_result result; if (strncmp(uri, "file:///", 8) == 0) { - const char *path = uri + 7; + const char *path_utf8 = uri + 7; + const Path path_fs = Path::FromUTF8(path_utf8); + + if (path_fs.IsNull()) { + command_error(client, ACK_ERROR_NO_EXIST, + "unsupported file name"); + return COMMAND_RETURN_ERROR; + } GError *error = NULL; - if (!client_allow_file(client, path, &error)) + if (!client_allow_file(client, path_fs, &error)) return print_error(client, error); - result = client->partition.AppendFile(path, &added_id); + result = client->partition.AppendFile(path_utf8, &added_id); } else { if (uri_has_scheme(uri) && !uri_supported_scheme(uri)) { command_error(client, ACK_ERROR_NO_EXIST, -- cgit v1.2.3 From 1d9b84a5afe44f2ea25bab836ed245e8de3ed901 Mon Sep 17 00:00:00 2001 From: Denis Krjuchkov Date: Sun, 5 May 2013 16:12:35 +0600 Subject: PlaylistFile: use DirectoryReader and file system API --- src/PlaylistFile.cxx | 51 ++++++++++++++++++++++++--------------------------- 1 file changed, 24 insertions(+), 27 deletions(-) diff --git a/src/PlaylistFile.cxx b/src/PlaylistFile.cxx index 662c4de96..8cc009e0c 100644 --- a/src/PlaylistFile.cxx +++ b/src/PlaylistFile.cxx @@ -32,6 +32,7 @@ #include "Idle.hxx" #include "fs/Path.hxx" #include "fs/FileSystem.hxx" +#include "fs/DirectoryReader.hxx" #include "util/UriUtil.hxx" #include @@ -77,7 +78,7 @@ spl_valid_name(const char *name_utf8) strchr(name_utf8, '\r') == NULL; } -static const char * +static const Path & spl_map(GError **error_r) { const Path &path_fs = map_spl_path(); @@ -85,8 +86,7 @@ spl_map(GError **error_r) g_set_error_literal(error_r, playlist_quark(), PLAYLIST_RESULT_DISABLED, "Stored playlists are disabled"); - - return path_fs.c_str(); + return path_fs; } static bool @@ -105,8 +105,7 @@ spl_check_name(const char *name_utf8, GError **error_r) static Path spl_map_to_fs(const char *name_utf8, GError **error_r) { - if (spl_map(error_r) == NULL || - !spl_check_name(name_utf8, error_r)) + if (spl_map(error_r).IsNull() || !spl_check_name(name_utf8, error_r)) return Path::Null(); Path path_fs = map_spl_utf8_to_fs(name_utf8); @@ -139,25 +138,24 @@ playlist_errno(GError **error_r) static bool LoadPlaylistFileInfo(PlaylistInfo &info, - const char *parent_path_fs, const char *name_fs) + const Path &parent_path_fs, const Path &name_fs) { - size_t name_length = strlen(name_fs); + const char *name_fs_str = name_fs.c_str(); + size_t name_length = strlen(name_fs_str); if (name_length < sizeof(PLAYLIST_FILE_SUFFIX) || - memchr(name_fs, '\n', name_length) != NULL) + memchr(name_fs_str, '\n', name_length) != NULL) return false; - if (!g_str_has_suffix(name_fs, PLAYLIST_FILE_SUFFIX)) + if (!g_str_has_suffix(name_fs_str, PLAYLIST_FILE_SUFFIX)) return false; - char *path_fs = g_build_filename(parent_path_fs, name_fs, NULL); + Path path_fs = Path::Build(parent_path_fs, name_fs); struct stat st; - int ret = stat(path_fs, &st); - g_free(path_fs); - if (ret < 0 || !S_ISREG(st.st_mode)) + if (!StatFile(path_fs, st) || !S_ISREG(st.st_mode)) return false; - char *name = g_strndup(name_fs, + char *name = g_strndup(name_fs_str, name_length + 1 - sizeof(PLAYLIST_FILE_SUFFIX)); std::string name_utf8 = Path::ToUTF8(name); g_free(name); @@ -174,24 +172,23 @@ ListPlaylistFiles(GError **error_r) { PlaylistVector list; - const char *parent_path_fs = spl_map(error_r); - if (parent_path_fs == NULL) + const Path &parent_path_fs = spl_map(error_r); + if (parent_path_fs.IsNull()) return list; - DIR *dir = opendir(parent_path_fs); - if (dir == NULL) { + DirectoryReader reader(parent_path_fs); + if (reader.HasFailed()) { set_error_errno(error_r); return list; } PlaylistInfo info; - struct dirent *ent; - while ((ent = readdir(dir)) != NULL) { - if (LoadPlaylistFileInfo(info, parent_path_fs, ent->d_name)) + while (reader.ReadEntry()) { + const Path entry = reader.GetEntry(); + if (LoadPlaylistFileInfo(info, parent_path_fs, entry)) list.push_back(std::move(info)); } - closedir(dir); return list; } @@ -201,7 +198,7 @@ SavePlaylistFile(const PlaylistFileContents &contents, const char *utf8path, { assert(utf8path != NULL); - if (spl_map(error_r) == NULL) + if (spl_map(error_r).IsNull()) return false; const Path path_fs = spl_map_to_fs(utf8path, error_r); @@ -226,7 +223,7 @@ LoadPlaylistFile(const char *utf8path, GError **error_r) { PlaylistFileContents contents; - if (spl_map(error_r) == NULL) + if (spl_map(error_r).IsNull()) return contents; const Path path_fs = spl_map_to_fs(utf8path, error_r); @@ -302,7 +299,7 @@ spl_move_index(const char *utf8path, unsigned src, unsigned dest, bool spl_clear(const char *utf8path, GError **error_r) { - if (spl_map(error_r) == NULL) + if (spl_map(error_r).IsNull()) return false; const Path path_fs = spl_map_to_fs(utf8path, error_r); @@ -365,7 +362,7 @@ spl_remove_index(const char *utf8path, unsigned pos, GError **error_r) bool spl_append_song(const char *utf8path, struct song *song, GError **error_r) { - if (spl_map(error_r) == NULL) + if (spl_map(error_r).IsNull()) return false; const Path path_fs = spl_map_to_fs(utf8path, error_r); @@ -454,7 +451,7 @@ spl_rename_internal(const Path &from_path_fs, const Path &to_path_fs, bool spl_rename(const char *utf8from, const char *utf8to, GError **error_r) { - if (spl_map(error_r) == NULL) + if (spl_map(error_r).IsNull()) return false; Path from_path_fs = spl_map_to_fs(utf8from, error_r); -- cgit v1.2.3 From 21dac6c05ddff1ce130843a82127618f9ed766a7 Mon Sep 17 00:00:00 2001 From: Denis Krjuchkov Date: Sun, 5 May 2013 18:19:04 +0600 Subject: decoder/FLAC*: rename files and symbols to Flac* --- Makefile.am | 16 +- src/DecoderList.cxx | 2 +- src/decoder/FLACCommon.cxx | 213 --------------------- src/decoder/FLACCommon.hxx | 101 ---------- src/decoder/FLACDecoderPlugin.cxx | 383 -------------------------------------- src/decoder/FLACDecoderPlugin.h | 26 --- src/decoder/FLACIOHandle.cxx | 114 ------------ src/decoder/FLACIOHandle.hxx | 45 ----- src/decoder/FLACInput.cxx | 149 --------------- src/decoder/FLACInput.hxx | 72 ------- src/decoder/FLACMetaData.cxx | 253 ------------------------- src/decoder/FLACMetaData.hxx | 140 -------------- src/decoder/FLAC_PCM.cxx | 110 ----------- src/decoder/FLAC_PCM.hxx | 33 ---- src/decoder/FlacCommon.cxx | 213 +++++++++++++++++++++ src/decoder/FlacCommon.hxx | 101 ++++++++++ src/decoder/FlacDecoderPlugin.cxx | 383 ++++++++++++++++++++++++++++++++++++++ src/decoder/FlacDecoderPlugin.h | 26 +++ src/decoder/FlacIOHandle.cxx | 114 ++++++++++++ src/decoder/FlacIOHandle.hxx | 45 +++++ src/decoder/FlacInput.cxx | 149 +++++++++++++++ src/decoder/FlacInput.hxx | 72 +++++++ src/decoder/FlacMetadata.cxx | 253 +++++++++++++++++++++++++ src/decoder/FlacMetadata.hxx | 140 ++++++++++++++ src/decoder/FlacPcm.cxx | 110 +++++++++++ src/decoder/FlacPcm.hxx | 33 ++++ 26 files changed, 1648 insertions(+), 1648 deletions(-) delete mode 100644 src/decoder/FLACCommon.cxx delete mode 100644 src/decoder/FLACCommon.hxx delete mode 100644 src/decoder/FLACDecoderPlugin.cxx delete mode 100644 src/decoder/FLACDecoderPlugin.h delete mode 100644 src/decoder/FLACIOHandle.cxx delete mode 100644 src/decoder/FLACIOHandle.hxx delete mode 100644 src/decoder/FLACInput.cxx delete mode 100644 src/decoder/FLACInput.hxx delete mode 100644 src/decoder/FLACMetaData.cxx delete mode 100644 src/decoder/FLACMetaData.hxx delete mode 100644 src/decoder/FLAC_PCM.cxx delete mode 100644 src/decoder/FLAC_PCM.hxx create mode 100644 src/decoder/FlacCommon.cxx create mode 100644 src/decoder/FlacCommon.hxx create mode 100644 src/decoder/FlacDecoderPlugin.cxx create mode 100644 src/decoder/FlacDecoderPlugin.h create mode 100644 src/decoder/FlacIOHandle.cxx create mode 100644 src/decoder/FlacIOHandle.hxx create mode 100644 src/decoder/FlacInput.cxx create mode 100644 src/decoder/FlacInput.hxx create mode 100644 src/decoder/FlacMetadata.cxx create mode 100644 src/decoder/FlacMetadata.hxx create mode 100644 src/decoder/FlacPcm.cxx create mode 100644 src/decoder/FlacPcm.hxx diff --git a/Makefile.am b/Makefile.am index 5fd0a74f7..695b43a5e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -555,13 +555,13 @@ endif if HAVE_FLAC libdecoder_plugins_a_SOURCES += \ - src/decoder/FLACInput.cxx src/decoder/FLACInput.hxx \ - src/decoder/FLACIOHandle.cxx src/decoder/FLACIOHandle.hxx \ - src/decoder/FLACMetaData.cxx src/decoder/FLACMetaData.hxx \ - src/decoder/FLAC_PCM.cxx src/decoder/FLAC_PCM.hxx \ - src/decoder/FLACCommon.cxx src/decoder/FLACCommon.hxx \ - src/decoder/FLACDecoderPlugin.cxx \ - src/decoder/FLACDecoderPlugin.h + src/decoder/FlacInput.cxx src/decoder/FlacInput.hxx \ + src/decoder/FlacIOHandle.cxx src/decoder/FlacIOHandle.hxx \ + src/decoder/FlacMetadata.cxx src/decoder/FlacMetadata.hxx \ + src/decoder/FlacPcm.cxx src/decoder/FlacPcm.hxx \ + src/decoder/FlacCommon.cxx src/decoder/FlacCommon.hxx \ + src/decoder/FlacDecoderPlugin.cxx \ + src/decoder/FlacDecoderPlugin.h endif if HAVE_AUDIOFILE @@ -1137,7 +1137,7 @@ test_dump_playlist_SOURCES = test/dump_playlist.cxx \ if HAVE_FLAC test_dump_playlist_SOURCES += \ src/ReplayGainInfo.cxx \ - src/decoder/FLACMetaData.cxx + src/decoder/FlacMetadata.cxx endif test_run_decoder_LDADD = \ diff --git a/src/DecoderList.cxx b/src/DecoderList.cxx index aa37c105e..1d33d5deb 100644 --- a/src/DecoderList.cxx +++ b/src/DecoderList.cxx @@ -25,7 +25,7 @@ #include "decoder/pcm_decoder_plugin.h" #include "decoder/dsdiff_decoder_plugin.h" #include "decoder/dsf_decoder_plugin.h" -#include "decoder/FLACDecoderPlugin.h" +#include "decoder/FlacDecoderPlugin.h" #include "decoder/OpusDecoderPlugin.h" #include "decoder/VorbisDecoderPlugin.h" #include "decoder/AdPlugDecoderPlugin.h" diff --git a/src/decoder/FLACCommon.cxx b/src/decoder/FLACCommon.cxx deleted file mode 100644 index 25fd1f964..000000000 --- a/src/decoder/FLACCommon.cxx +++ /dev/null @@ -1,213 +0,0 @@ -/* - * 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. - */ - -/* - * Common data structures and functions used by FLAC and OggFLAC - */ - -#include "config.h" -#include "FLACCommon.hxx" -#include "FLACMetaData.hxx" -#include "FLAC_PCM.hxx" - -extern "C" { -#include "audio_check.h" -} - -#include - -#include - -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), - tag(nullptr) -{ - pcm_buffer_init(&buffer); -} - -flac_data::~flac_data() -{ - pcm_buffer_deinit(&buffer); - - if (tag != nullptr) - tag_free(tag); -} - -static enum sample_format -flac_sample_format(unsigned bits_per_sample) -{ - switch (bits_per_sample) { - case 8: - return SAMPLE_FORMAT_S8; - - case 16: - return SAMPLE_FORMAT_S16; - - case 24: - return SAMPLE_FORMAT_S24_P32; - - case 32: - return SAMPLE_FORMAT_S32; - - default: - return SAMPLE_FORMAT_UNDEFINED; - } -} - -static void -flac_got_stream_info(struct flac_data *data, - const FLAC__StreamMetadata_StreamInfo *stream_info) -{ - if (data->initialized || data->unsupported) - return; - - GError *error = nullptr; - 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); - data->unsupported = true; - return; - } - - data->frame_size = audio_format_frame_size(&data->audio_format); - - if (data->total_frames == 0) - data->total_frames = stream_info->total_samples; - - data->initialized = true; -} - -void flac_metadata_common_cb(const FLAC__StreamMetadata * block, - struct flac_data *data) -{ - if (data->unsupported) - return; - - struct replay_gain_info rgi; - char *mixramp_start; - char *mixramp_end; - - switch (block->type) { - case FLAC__METADATA_TYPE_STREAMINFO: - flac_got_stream_info(data, &block->data.stream_info); - break; - - case FLAC__METADATA_TYPE_VORBIS_COMMENT: - if (flac_parse_replay_gain(&rgi, block)) - decoder_replay_gain(data->decoder, &rgi); - - if (flac_parse_mixramp(&mixramp_start, &mixramp_end, block)) - decoder_mixramp(data->decoder, - mixramp_start, mixramp_end); - - if (data->tag != nullptr) - flac_vorbis_comments_to_tag(data->tag, - &block->data.vorbis_comment); - - default: - break; - } -} - -/** - * This function attempts to call decoder_initialized() in case there - * was no STREAMINFO block. This is allowed for nonseekable streams, - * where the server sends us only a part of the file, without - * providing the STREAMINFO block from the beginning of the file - * (e.g. when seeking with SqueezeBox Server). - */ -static bool -flac_got_first_frame(struct flac_data *data, const FLAC__FrameHeader *header) -{ - if (data->unsupported) - return false; - - GError *error = nullptr; - 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); - data->unsupported = true; - return false; - } - - data->frame_size = audio_format_frame_size(&data->audio_format); - - decoder_initialized(data->decoder, &data->audio_format, - data->input_stream->seekable, - (float)data->total_frames / - (float)data->audio_format.sample_rate); - - data->initialized = true; - - return true; -} - -FLAC__StreamDecoderWriteStatus -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; - - if (!data->initialized && !flac_got_first_frame(data, &frame->header)) - 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); - - flac_convert(buffer, frame->header.channels, - (enum sample_format)data->audio_format.format, buf, - 0, frame->header.blocksize); - - if (nbytes > 0) - bit_rate = nbytes * 8 * frame->header.sample_rate / - (1000 * frame->header.blocksize); - else - bit_rate = 0; - - 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: - break; - - case DECODE_COMMAND_STOP: - return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; - - case DECODE_COMMAND_SEEK: - return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; - } - - return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; -} diff --git a/src/decoder/FLACCommon.hxx b/src/decoder/FLACCommon.hxx deleted file mode 100644 index 20f93c8c7..000000000 --- a/src/decoder/FLACCommon.hxx +++ /dev/null @@ -1,101 +0,0 @@ -/* - * 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. - */ - -/* - * Common data structures and functions used by FLAC and OggFLAC - */ - -#ifndef MPD_FLAC_COMMON_HXX -#define MPD_FLAC_COMMON_HXX - -#include "FLACInput.hxx" -#include "decoder_api.h" - -extern "C" { -#include "pcm/pcm_buffer.h" -} - -#include -#include - -#undef G_LOG_DOMAIN -#define G_LOG_DOMAIN "flac" - -struct flac_data : public FLACInput { - struct pcm_buffer buffer; - - /** - * The size of one frame in the output buffer. - */ - unsigned frame_size; - - /** - * Has decoder_initialized() been called yet? - */ - bool initialized; - - /** - * Does the FLAC file contain an unsupported audio format? - */ - bool unsupported; - - /** - * The validated audio format of the FLAC file. This - * attribute is defined if "initialized" is true. - */ - struct audio_format audio_format; - - /** - * The total number of frames in this song. The decoder - * plugin may initialize this attribute to override the value - * provided by libFLAC (e.g. for sub songs from a CUE sheet). - */ - FLAC__uint64 total_frames; - - /** - * The number of the first frame in this song. This is only - * non-zero if playing sub songs from a CUE sheet. - */ - FLAC__uint64 first_frame; - - /** - * The number of the next frame which is going to be decoded. - */ - FLAC__uint64 next_frame; - - FLAC__uint64 position; - - struct decoder *decoder; - struct input_stream *input_stream; - - struct tag *tag; - - flac_data(struct decoder *decoder, struct input_stream *input_stream); - ~flac_data(); -}; - -void flac_metadata_common_cb(const FLAC__StreamMetadata * block, - struct flac_data *data); - -FLAC__StreamDecoderWriteStatus -flac_common_write(struct flac_data *data, const FLAC__Frame * frame, - const FLAC__int32 *const buf[], - FLAC__uint64 nbytes); - -#endif /* _FLAC_COMMON_H */ diff --git a/src/decoder/FLACDecoderPlugin.cxx b/src/decoder/FLACDecoderPlugin.cxx deleted file mode 100644 index 43b604097..000000000 --- a/src/decoder/FLACDecoderPlugin.cxx +++ /dev/null @@ -1,383 +0,0 @@ -/* - * 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 "FLACDecoderPlugin.h" -#include "FLACCommon.hxx" -#include "FLACMetaData.hxx" -#include "OggCodec.hxx" - -#include - -#include -#include - -#include -#include - -#if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT <= 7 -#error libFLAC is too old -#endif - -static void flacPrintErroredState(FLAC__StreamDecoderState state) -{ - switch (state) { - case FLAC__STREAM_DECODER_SEARCH_FOR_METADATA: - case FLAC__STREAM_DECODER_READ_METADATA: - case FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC: - case FLAC__STREAM_DECODER_READ_FRAME: - case FLAC__STREAM_DECODER_END_OF_STREAM: - return; - - case FLAC__STREAM_DECODER_OGG_ERROR: - case FLAC__STREAM_DECODER_SEEK_ERROR: - case FLAC__STREAM_DECODER_ABORTED: - case FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR: - case FLAC__STREAM_DECODER_UNINITIALIZED: - break; - } - - g_warning("%s\n", FLAC__StreamDecoderStateString[state]); -} - -static void flacMetadata(G_GNUC_UNUSED const FLAC__StreamDecoder * dec, - const FLAC__StreamMetadata * block, void *vdata) -{ - flac_metadata_common_cb(block, (struct flac_data *) vdata); -} - -static FLAC__StreamDecoderWriteStatus -flac_write_cb(const FLAC__StreamDecoder *dec, const FLAC__Frame *frame, - const FLAC__int32 *const buf[], void *vdata) -{ - struct flac_data *data = (struct flac_data *) vdata; - FLAC__uint64 nbytes = 0; - - if (FLAC__stream_decoder_get_decode_position(dec, &nbytes)) { - if (data->position > 0 && nbytes > data->position) { - nbytes -= data->position; - data->position += nbytes; - } else { - data->position = nbytes; - nbytes = 0; - } - } else - nbytes = 0; - - return flac_common_write(data, frame, buf, nbytes); -} - -static bool -flac_scan_file(const char *file, - const struct tag_handler *handler, void *handler_ctx) -{ - FLACMetadataChain chain; - if (!chain.Read(file)) { - g_debug("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)) { - g_debug("Failed to read FLAC tags: %s", - chain.GetStatusString()); - return false; - } - - chain.Scan(handler, handler_ctx); - return true; -} - -/** - * Some glue code around FLAC__stream_decoder_new(). - */ -static FLAC__StreamDecoder * -flac_decoder_new(void) -{ - FLAC__StreamDecoder *sd = FLAC__stream_decoder_new(); - if (sd == nullptr) { - g_warning("FLAC__stream_decoder_new() failed"); - return nullptr; - } - - if(!FLAC__stream_decoder_set_metadata_respond(sd, FLAC__METADATA_TYPE_VORBIS_COMMENT)) - g_debug("FLAC__stream_decoder_set_metadata_respond() has failed"); - - return sd; -} - -static bool -flac_decoder_initialize(struct flac_data *data, FLAC__StreamDecoder *sd, - FLAC__uint64 duration) -{ - data->total_frames = duration; - - if (!FLAC__stream_decoder_process_until_end_of_metadata(sd)) { - g_warning("problem reading metadata"); - return false; - } - - if (data->initialized) { - /* done */ - decoder_initialized(data->decoder, &data->audio_format, - data->input_stream->seekable, - (float)data->total_frames / - (float)data->audio_format.sample_rate); - return true; - } - - if (data->input_stream->seekable) - /* allow the workaround below only for nonseekable - streams*/ - return false; - - /* no stream_info packet found; try to initialize the decoder - from the first frame header */ - FLAC__stream_decoder_process_single(sd); - return data->initialized; -} - -static void -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 != nullptr && !tag_is_empty(data->tag)) { - cmd = decoder_tag(data->decoder, data->input_stream, - data->tag); - tag_free(data->tag); - data->tag = tag_new(); - } else - cmd = decoder_get_command(decoder); - - if (cmd == DECODE_COMMAND_SEEK) { - FLAC__uint64 seek_sample = t_start + - decoder_seek_where(decoder) * - data->audio_format.sample_rate; - if (seek_sample >= t_start && - (t_end == 0 || seek_sample <= t_end) && - FLAC__stream_decoder_seek_absolute(flac_dec, seek_sample)) { - data->next_frame = seek_sample; - data->position = 0; - decoder_command_finished(decoder); - } else - decoder_seek_error(decoder); - } else if (cmd == DECODE_COMMAND_STOP || - FLAC__stream_decoder_get_state(flac_dec) == FLAC__STREAM_DECODER_END_OF_STREAM) - break; - - if (t_end != 0 && data->next_frame >= t_end) - /* end of this sub track */ - break; - - if (!FLAC__stream_decoder_process_single(flac_dec) && - decoder_get_command(decoder) == DECODE_COMMAND_NONE) { - /* a failure that was not triggered by a - decoder command */ - flacPrintErroredState(FLAC__stream_decoder_get_state(flac_dec)); - break; - } - } -} - -static FLAC__StreamDecoderInitStatus -stream_init_oggflac(FLAC__StreamDecoder *flac_dec, struct flac_data *data) -{ - return FLAC__stream_decoder_init_ogg_stream(flac_dec, - FLACInput::Read, - FLACInput::Seek, - FLACInput::Tell, - FLACInput::Length, - FLACInput::Eof, - flac_write_cb, - flacMetadata, - FLACInput::Error, - data); -} - -static FLAC__StreamDecoderInitStatus -stream_init_flac(FLAC__StreamDecoder *flac_dec, struct flac_data *data) -{ - return FLAC__stream_decoder_init_stream(flac_dec, - FLACInput::Read, - FLACInput::Seek, - FLACInput::Tell, - FLACInput::Length, - FLACInput::Eof, - flac_write_cb, - flacMetadata, - FLACInput::Error, - data); -} - -static FLAC__StreamDecoderInitStatus -stream_init(FLAC__StreamDecoder *flac_dec, struct flac_data *data, bool is_ogg) -{ - return is_ogg - ? stream_init_oggflac(flac_dec, data) - : stream_init_flac(flac_dec, data); -} - -static void -flac_decode_internal(struct decoder * decoder, - struct input_stream *input_stream, - bool is_ogg) -{ - FLAC__StreamDecoder *flac_dec; - - flac_dec = flac_decoder_new(); - if (flac_dec == nullptr) - return; - - struct flac_data data(decoder, input_stream); - data.tag = tag_new(); - - FLAC__StreamDecoderInitStatus status = - stream_init(flac_dec, &data, is_ogg); - if (status != FLAC__STREAM_DECODER_INIT_STATUS_OK) { - FLAC__stream_decoder_delete(flac_dec); - g_warning("%s", FLAC__StreamDecoderInitStatusString[status]); - return; - } - - if (!flac_decoder_initialize(&data, flac_dec, 0)) { - FLAC__stream_decoder_finish(flac_dec); - FLAC__stream_decoder_delete(flac_dec); - return; - } - - flac_decoder_loop(&data, flac_dec, 0, 0); - - FLAC__stream_decoder_finish(flac_dec); - FLAC__stream_decoder_delete(flac_dec); -} - -static void -flac_decode(struct decoder * decoder, struct input_stream *input_stream) -{ - flac_decode_internal(decoder, input_stream, false); -} - -static bool -oggflac_init(G_GNUC_UNUSED const struct config_param *param) -{ - return !!FLAC_API_SUPPORTS_OGG_FLAC; -} - -static bool -oggflac_scan_file(const char *file, - const struct tag_handler *handler, void *handler_ctx) -{ - FLACMetadataChain chain; - if (!chain.ReadOgg(file)) { - g_debug("Failed to read OggFLAC tags: %s", - chain.GetStatusString()); - return false; - } - - chain.Scan(handler, handler_ctx); - return true; -} - -static bool -oggflac_scan_stream(struct input_stream *is, - const struct tag_handler *handler, void *handler_ctx) -{ - FLACMetadataChain chain; - if (!chain.ReadOgg(is)) { - g_debug("Failed to read OggFLAC tags: %s", - chain.GetStatusString()); - return false; - } - - chain.Scan(handler, handler_ctx); - return true; -} - -static void -oggflac_decode(struct decoder *decoder, struct input_stream *input_stream) -{ - if (ogg_codec_detect(decoder, input_stream) != OGG_CODEC_FLAC) - return; - - /* rewind the stream, because ogg_codec_detect() has - moved it */ - input_stream_lock_seek(input_stream, 0, SEEK_SET, nullptr); - - flac_decode_internal(decoder, input_stream, true); -} - -static const char *const oggflac_suffixes[] = { "ogg", "oga", nullptr }; -static const char *const oggflac_mime_types[] = { - "application/ogg", - "application/x-ogg", - "audio/ogg", - "audio/x-flac+ogg", - "audio/x-ogg", - nullptr -}; - -const struct decoder_plugin oggflac_decoder_plugin = { - "oggflac", - oggflac_init, - nullptr, - oggflac_decode, - nullptr, - oggflac_scan_file, - oggflac_scan_stream, - nullptr, - oggflac_suffixes, - oggflac_mime_types, -}; - -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", - nullptr -}; - -const struct decoder_plugin flac_decoder_plugin = { - "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 deleted file mode 100644 index c99deeef7..000000000 --- a/src/decoder/FLACDecoderPlugin.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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/FLACIOHandle.cxx b/src/decoder/FLACIOHandle.cxx deleted file mode 100644 index 08ec36e48..000000000 --- a/src/decoder/FLACIOHandle.cxx +++ /dev/null @@ -1,114 +0,0 @@ -/* - * 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 "io_error.h" -#include "gcc.h" - -#include - -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!) */ - - GError *error = nullptr; - while (p < end) { - size_t nbytes = input_stream_lock_read(is, p, end - p, &error); - if (nbytes == 0) { - if (error == nullptr) - /* end of file */ - break; - - if (error->domain == errno_quark()) - errno = error->code; - else - /* just some random non-zero - errno value */ - errno = EINVAL; - g_error_free(error); - 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; - - return input_stream_lock_seek(is, offset, whence, nullptr) ? 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 input_stream_lock_eof(is); -} - -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 deleted file mode 100644 index 505d2db1a..000000000 --- a/src/decoder/FLACIOHandle.hxx +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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 - -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 deleted file mode 100644 index ba0a86ce8..000000000 --- a/src/decoder/FLACInput.cxx +++ /dev/null @@ -1,149 +0,0 @@ -/* - * 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 "decoder_api.h" -#include "gcc.h" -#include "InputStream.hxx" - -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_lock_eof(input_stream) || - (decoder != nullptr && - decoder_get_command(decoder) != DECODE_COMMAND_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; - - if (!input_stream_lock_seek(input_stream, - absolute_byte_offset, SEEK_SET, - nullptr)) - 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) != DECODE_COMMAND_NONE && - decoder_get_command(decoder) != DECODE_COMMAND_SEEK) || - input_stream_lock_eof(input_stream); -} - -void -FLACInput::Error(FLAC__StreamDecoderErrorStatus status) -{ - if (decoder == nullptr || - decoder_get_command(decoder) != DECODE_COMMAND_STOP) - g_warning("%s", 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 deleted file mode 100644 index 7661567d1..000000000 --- a/src/decoder/FLACInput.hxx +++ /dev/null @@ -1,72 +0,0 @@ -/* - * 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 - -/** - * 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/FLACMetaData.cxx b/src/decoder/FLACMetaData.cxx deleted file mode 100644 index 8273a230b..000000000 --- a/src/decoder/FLACMetaData.cxx +++ /dev/null @@ -1,253 +0,0 @@ -/* - * 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 "FLACMetaData.hxx" - -extern "C" { -#include "XiphTags.h" -} - -#include "tag.h" -#include "tag_handler.h" -#include "tag_table.h" -#include "replay_gain_info.h" - -#include - -#include -#include - -static bool -flac_find_float_comment(const FLAC__StreamMetadata *block, - const char *cmnt, float *fl) -{ - int offset; - size_t pos; - int len; - unsigned char tmp, *p; - - offset = FLAC__metadata_object_vorbiscomment_find_entry_from(block, 0, - cmnt); - if (offset < 0) - return false; - - pos = strlen(cmnt) + 1; /* 1 is for '=' */ - len = block->data.vorbis_comment.comments[offset].length - pos; - if (len <= 0) - return false; - - p = &block->data.vorbis_comment.comments[offset].entry[pos]; - tmp = p[len]; - p[len] = '\0'; - *fl = (float)atof((char *)p); - p[len] = tmp; - - return true; -} - -bool -flac_parse_replay_gain(struct replay_gain_info *rgi, - const FLAC__StreamMetadata *block) -{ - bool found = false; - - replay_gain_info_init(rgi); - - if (flac_find_float_comment(block, "replaygain_album_gain", - &rgi->tuples[REPLAY_GAIN_ALBUM].gain)) - found = true; - if (flac_find_float_comment(block, "replaygain_album_peak", - &rgi->tuples[REPLAY_GAIN_ALBUM].peak)) - found = true; - if (flac_find_float_comment(block, "replaygain_track_gain", - &rgi->tuples[REPLAY_GAIN_TRACK].gain)) - found = true; - if (flac_find_float_comment(block, "replaygain_track_peak", - &rgi->tuples[REPLAY_GAIN_TRACK].peak)) - found = true; - - return found; -} - -static bool -flac_find_string_comment(const FLAC__StreamMetadata *block, - const char *cmnt, char **str) -{ - int offset; - size_t pos; - int len; - const unsigned char *p; - - *str = nullptr; - offset = FLAC__metadata_object_vorbiscomment_find_entry_from(block, 0, - cmnt); - if (offset < 0) - return false; - - pos = strlen(cmnt) + 1; /* 1 is for '=' */ - len = block->data.vorbis_comment.comments[offset].length - pos; - if (len <= 0) - return false; - - p = &block->data.vorbis_comment.comments[offset].entry[pos]; - *str = g_strndup((const char *)p, len); - - return true; -} - -bool -flac_parse_mixramp(char **mixramp_start, char **mixramp_end, - const FLAC__StreamMetadata *block) -{ - bool found = false; - - if (flac_find_string_comment(block, "mixramp_start", mixramp_start)) - found = true; - if (flac_find_string_comment(block, "mixramp_end", mixramp_end)) - found = true; - - return found; -} - -/** - * Checks if the specified name matches the entry's name, and if yes, - * returns the comment value (not null-temrinated). - */ -static const char * -flac_comment_value(const FLAC__StreamMetadata_VorbisComment_Entry *entry, - const char *name, size_t *length_r) -{ - size_t name_length = strlen(name); - const char *comment = (const char*)entry->entry; - - if (entry->length <= name_length || - g_ascii_strncasecmp(comment, name, name_length) != 0) - return nullptr; - - if (comment[name_length] == '=') { - *length_r = entry->length - name_length - 1; - return comment + name_length + 1; - } - - return nullptr; -} - -/** - * Check if the comment's name equals the passed name, and if so, copy - * the comment value into the tag. - */ -static bool -flac_copy_comment(const FLAC__StreamMetadata_VorbisComment_Entry *entry, - const char *name, enum tag_type tag_type, - const struct tag_handler *handler, void *handler_ctx) -{ - const char *value; - size_t value_length; - - 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); - return true; - } - - return false; -} - -static void -flac_scan_comment(const FLAC__StreamMetadata_VorbisComment_Entry *entry, - const struct tag_handler *handler, void *handler_ctx) -{ - if (handler->pair != nullptr) { - char *name = g_strdup((const char*)entry->entry); - char *value = strchr(name, '='); - - if (value != nullptr && value > name) { - *value++ = 0; - tag_handler_invoke_pair(handler, handler_ctx, - name, value); - } - - g_free(name); - } - - 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], (enum tag_type)i, - handler, handler_ctx)) - return; -} - -static void -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(&comment->comments[i], - handler, handler_ctx); -} - -void -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(&block->data.vorbis_comment, - handler, handler_ctx); - break; - - case FLAC__METADATA_TYPE_STREAMINFO: - if (block->data.stream_info.sample_rate > 0) - tag_handler_invoke_duration(handler, handler_ctx, - flac_duration(&block->data.stream_info)); - break; - - default: - break; - } -} - -void -flac_vorbis_comments_to_tag(struct tag *tag, - const FLAC__StreamMetadata_VorbisComment *comment) -{ - flac_scan_comments(comment, &add_tag_handler, tag); -} - -void -FLACMetadataChain::Scan(const struct tag_handler *handler, void *handler_ctx) -{ - FLACMetadataIterator iterator(*this); - - do { - FLAC__StreamMetadata *block = iterator.GetBlock(); - if (block == nullptr) - break; - - flac_scan_metadata(block, handler, handler_ctx); - } while (iterator.Next()); -} diff --git a/src/decoder/FLACMetaData.hxx b/src/decoder/FLACMetaData.hxx deleted file mode 100644 index 0eceec23c..000000000 --- a/src/decoder/FLACMetaData.hxx +++ /dev/null @@ -1,140 +0,0 @@ -/* - * 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 - -#include - -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(struct 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.cxx b/src/decoder/FLAC_PCM.cxx deleted file mode 100644 index 303530aa7..000000000 --- a/src/decoder/FLAC_PCM.cxx +++ /dev/null @@ -1,110 +0,0 @@ -/* - * 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 "FLAC_PCM.hxx" - -#include - -static void flac_convert_stereo16(int16_t *dest, - const FLAC__int32 * const buf[], - unsigned int position, unsigned int end) -{ - for (; position < end; ++position) { - *dest++ = buf[0][position]; - *dest++ = buf[1][position]; - } -} - -static void -flac_convert_16(int16_t *dest, - unsigned int num_channels, - const FLAC__int32 * const buf[], - unsigned int position, unsigned int end) -{ - unsigned int c_chan; - - for (; position < end; ++position) - for (c_chan = 0; c_chan < num_channels; c_chan++) - *dest++ = buf[c_chan][position]; -} - -/** - * Note: this function also handles 24 bit files! - */ -static void -flac_convert_32(int32_t *dest, - unsigned int num_channels, - const FLAC__int32 * const buf[], - unsigned int position, unsigned int end) -{ - unsigned int c_chan; - - for (; position < end; ++position) - for (c_chan = 0; c_chan < num_channels; c_chan++) - *dest++ = buf[c_chan][position]; -} - -static void -flac_convert_8(int8_t *dest, - unsigned int num_channels, - const FLAC__int32 * const buf[], - unsigned int position, unsigned int end) -{ - unsigned int c_chan; - - for (; position < end; ++position) - for (c_chan = 0; c_chan < num_channels; c_chan++) - *dest++ = buf[c_chan][position]; -} - -void -flac_convert(void *dest, - unsigned int num_channels, enum sample_format sample_format, - const FLAC__int32 *const buf[], - unsigned int position, unsigned int end) -{ - switch (sample_format) { - case SAMPLE_FORMAT_S16: - if (num_channels == 2) - flac_convert_stereo16((int16_t*)dest, buf, - position, end); - else - flac_convert_16((int16_t*)dest, num_channels, buf, - position, end); - break; - - case SAMPLE_FORMAT_S24_P32: - case SAMPLE_FORMAT_S32: - flac_convert_32((int32_t*)dest, num_channels, buf, - position, end); - break; - - case SAMPLE_FORMAT_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 */ - assert(false); - } -} diff --git a/src/decoder/FLAC_PCM.hxx b/src/decoder/FLAC_PCM.hxx deleted file mode 100644 index 97d214c17..000000000 --- a/src/decoder/FLAC_PCM.hxx +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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_PCM_HXX -#define MPD_FLAC_PCM_HXX - -#include "audio_format.h" - -#include - -void -flac_convert(void *dest, - unsigned int num_channels, enum sample_format sample_format, - const FLAC__int32 *const buf[], - unsigned int position, unsigned int end); - -#endif diff --git a/src/decoder/FlacCommon.cxx b/src/decoder/FlacCommon.cxx new file mode 100644 index 000000000..425ab55df --- /dev/null +++ b/src/decoder/FlacCommon.cxx @@ -0,0 +1,213 @@ +/* + * 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. + */ + +/* + * Common data structures and functions used by FLAC and OggFLAC + */ + +#include "config.h" +#include "FlacCommon.hxx" +#include "FlacMetadata.hxx" +#include "FlacPcm.hxx" + +extern "C" { +#include "audio_check.h" +} + +#include + +#include + +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), + tag(nullptr) +{ + pcm_buffer_init(&buffer); +} + +flac_data::~flac_data() +{ + pcm_buffer_deinit(&buffer); + + if (tag != nullptr) + tag_free(tag); +} + +static enum sample_format +flac_sample_format(unsigned bits_per_sample) +{ + switch (bits_per_sample) { + case 8: + return SAMPLE_FORMAT_S8; + + case 16: + return SAMPLE_FORMAT_S16; + + case 24: + return SAMPLE_FORMAT_S24_P32; + + case 32: + return SAMPLE_FORMAT_S32; + + default: + return SAMPLE_FORMAT_UNDEFINED; + } +} + +static void +flac_got_stream_info(struct flac_data *data, + const FLAC__StreamMetadata_StreamInfo *stream_info) +{ + if (data->initialized || data->unsupported) + return; + + GError *error = nullptr; + 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); + data->unsupported = true; + return; + } + + data->frame_size = audio_format_frame_size(&data->audio_format); + + if (data->total_frames == 0) + data->total_frames = stream_info->total_samples; + + data->initialized = true; +} + +void flac_metadata_common_cb(const FLAC__StreamMetadata * block, + struct flac_data *data) +{ + if (data->unsupported) + return; + + struct replay_gain_info rgi; + char *mixramp_start; + char *mixramp_end; + + switch (block->type) { + case FLAC__METADATA_TYPE_STREAMINFO: + flac_got_stream_info(data, &block->data.stream_info); + break; + + case FLAC__METADATA_TYPE_VORBIS_COMMENT: + if (flac_parse_replay_gain(&rgi, block)) + decoder_replay_gain(data->decoder, &rgi); + + if (flac_parse_mixramp(&mixramp_start, &mixramp_end, block)) + decoder_mixramp(data->decoder, + mixramp_start, mixramp_end); + + if (data->tag != nullptr) + flac_vorbis_comments_to_tag(data->tag, + &block->data.vorbis_comment); + + default: + break; + } +} + +/** + * This function attempts to call decoder_initialized() in case there + * was no STREAMINFO block. This is allowed for nonseekable streams, + * where the server sends us only a part of the file, without + * providing the STREAMINFO block from the beginning of the file + * (e.g. when seeking with SqueezeBox Server). + */ +static bool +flac_got_first_frame(struct flac_data *data, const FLAC__FrameHeader *header) +{ + if (data->unsupported) + return false; + + GError *error = nullptr; + 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); + data->unsupported = true; + return false; + } + + data->frame_size = audio_format_frame_size(&data->audio_format); + + decoder_initialized(data->decoder, &data->audio_format, + data->input_stream->seekable, + (float)data->total_frames / + (float)data->audio_format.sample_rate); + + data->initialized = true; + + return true; +} + +FLAC__StreamDecoderWriteStatus +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; + + if (!data->initialized && !flac_got_first_frame(data, &frame->header)) + 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); + + flac_convert(buffer, frame->header.channels, + (enum sample_format)data->audio_format.format, buf, + 0, frame->header.blocksize); + + if (nbytes > 0) + bit_rate = nbytes * 8 * frame->header.sample_rate / + (1000 * frame->header.blocksize); + else + bit_rate = 0; + + 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: + break; + + case DECODE_COMMAND_STOP: + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + + case DECODE_COMMAND_SEEK: + return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; + } + + return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; +} diff --git a/src/decoder/FlacCommon.hxx b/src/decoder/FlacCommon.hxx new file mode 100644 index 000000000..0cd295e0d --- /dev/null +++ b/src/decoder/FlacCommon.hxx @@ -0,0 +1,101 @@ +/* + * 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. + */ + +/* + * Common data structures and functions used by FLAC and OggFLAC + */ + +#ifndef MPD_FLAC_COMMON_HXX +#define MPD_FLAC_COMMON_HXX + +#include "FlacInput.hxx" +#include "decoder_api.h" + +extern "C" { +#include "pcm/pcm_buffer.h" +} + +#include +#include + +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "flac" + +struct flac_data : public FlacInput { + struct pcm_buffer buffer; + + /** + * The size of one frame in the output buffer. + */ + unsigned frame_size; + + /** + * Has decoder_initialized() been called yet? + */ + bool initialized; + + /** + * Does the FLAC file contain an unsupported audio format? + */ + bool unsupported; + + /** + * The validated audio format of the FLAC file. This + * attribute is defined if "initialized" is true. + */ + struct audio_format audio_format; + + /** + * The total number of frames in this song. The decoder + * plugin may initialize this attribute to override the value + * provided by libFLAC (e.g. for sub songs from a CUE sheet). + */ + FLAC__uint64 total_frames; + + /** + * The number of the first frame in this song. This is only + * non-zero if playing sub songs from a CUE sheet. + */ + FLAC__uint64 first_frame; + + /** + * The number of the next frame which is going to be decoded. + */ + FLAC__uint64 next_frame; + + FLAC__uint64 position; + + struct decoder *decoder; + struct input_stream *input_stream; + + struct tag *tag; + + flac_data(struct decoder *decoder, struct input_stream *input_stream); + ~flac_data(); +}; + +void flac_metadata_common_cb(const FLAC__StreamMetadata * block, + struct flac_data *data); + +FLAC__StreamDecoderWriteStatus +flac_common_write(struct flac_data *data, const FLAC__Frame * frame, + const FLAC__int32 *const buf[], + FLAC__uint64 nbytes); + +#endif /* _FLAC_COMMON_H */ diff --git a/src/decoder/FlacDecoderPlugin.cxx b/src/decoder/FlacDecoderPlugin.cxx new file mode 100644 index 000000000..cd15b4b29 --- /dev/null +++ b/src/decoder/FlacDecoderPlugin.cxx @@ -0,0 +1,383 @@ +/* + * 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 "FlacDecoderPlugin.h" +#include "FlacCommon.hxx" +#include "FlacMetadata.hxx" +#include "OggCodec.hxx" + +#include + +#include +#include + +#include +#include + +#if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT <= 7 +#error libFLAC is too old +#endif + +static void flacPrintErroredState(FLAC__StreamDecoderState state) +{ + switch (state) { + case FLAC__STREAM_DECODER_SEARCH_FOR_METADATA: + case FLAC__STREAM_DECODER_READ_METADATA: + case FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC: + case FLAC__STREAM_DECODER_READ_FRAME: + case FLAC__STREAM_DECODER_END_OF_STREAM: + return; + + case FLAC__STREAM_DECODER_OGG_ERROR: + case FLAC__STREAM_DECODER_SEEK_ERROR: + case FLAC__STREAM_DECODER_ABORTED: + case FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR: + case FLAC__STREAM_DECODER_UNINITIALIZED: + break; + } + + g_warning("%s\n", FLAC__StreamDecoderStateString[state]); +} + +static void flacMetadata(G_GNUC_UNUSED const FLAC__StreamDecoder * dec, + const FLAC__StreamMetadata * block, void *vdata) +{ + flac_metadata_common_cb(block, (struct flac_data *) vdata); +} + +static FLAC__StreamDecoderWriteStatus +flac_write_cb(const FLAC__StreamDecoder *dec, const FLAC__Frame *frame, + const FLAC__int32 *const buf[], void *vdata) +{ + struct flac_data *data = (struct flac_data *) vdata; + FLAC__uint64 nbytes = 0; + + if (FLAC__stream_decoder_get_decode_position(dec, &nbytes)) { + if (data->position > 0 && nbytes > data->position) { + nbytes -= data->position; + data->position += nbytes; + } else { + data->position = nbytes; + nbytes = 0; + } + } else + nbytes = 0; + + return flac_common_write(data, frame, buf, nbytes); +} + +static bool +flac_scan_file(const char *file, + const struct tag_handler *handler, void *handler_ctx) +{ + FlacMetadataChain chain; + if (!chain.Read(file)) { + g_debug("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)) { + g_debug("Failed to read FLAC tags: %s", + chain.GetStatusString()); + return false; + } + + chain.Scan(handler, handler_ctx); + return true; +} + +/** + * Some glue code around FLAC__stream_decoder_new(). + */ +static FLAC__StreamDecoder * +flac_decoder_new(void) +{ + FLAC__StreamDecoder *sd = FLAC__stream_decoder_new(); + if (sd == nullptr) { + g_warning("FLAC__stream_decoder_new() failed"); + return nullptr; + } + + if(!FLAC__stream_decoder_set_metadata_respond(sd, FLAC__METADATA_TYPE_VORBIS_COMMENT)) + g_debug("FLAC__stream_decoder_set_metadata_respond() has failed"); + + return sd; +} + +static bool +flac_decoder_initialize(struct flac_data *data, FLAC__StreamDecoder *sd, + FLAC__uint64 duration) +{ + data->total_frames = duration; + + if (!FLAC__stream_decoder_process_until_end_of_metadata(sd)) { + g_warning("problem reading metadata"); + return false; + } + + if (data->initialized) { + /* done */ + decoder_initialized(data->decoder, &data->audio_format, + data->input_stream->seekable, + (float)data->total_frames / + (float)data->audio_format.sample_rate); + return true; + } + + if (data->input_stream->seekable) + /* allow the workaround below only for nonseekable + streams*/ + return false; + + /* no stream_info packet found; try to initialize the decoder + from the first frame header */ + FLAC__stream_decoder_process_single(sd); + return data->initialized; +} + +static void +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 != nullptr && !tag_is_empty(data->tag)) { + cmd = decoder_tag(data->decoder, data->input_stream, + data->tag); + tag_free(data->tag); + data->tag = tag_new(); + } else + cmd = decoder_get_command(decoder); + + if (cmd == DECODE_COMMAND_SEEK) { + FLAC__uint64 seek_sample = t_start + + decoder_seek_where(decoder) * + data->audio_format.sample_rate; + if (seek_sample >= t_start && + (t_end == 0 || seek_sample <= t_end) && + FLAC__stream_decoder_seek_absolute(flac_dec, seek_sample)) { + data->next_frame = seek_sample; + data->position = 0; + decoder_command_finished(decoder); + } else + decoder_seek_error(decoder); + } else if (cmd == DECODE_COMMAND_STOP || + FLAC__stream_decoder_get_state(flac_dec) == FLAC__STREAM_DECODER_END_OF_STREAM) + break; + + if (t_end != 0 && data->next_frame >= t_end) + /* end of this sub track */ + break; + + if (!FLAC__stream_decoder_process_single(flac_dec) && + decoder_get_command(decoder) == DECODE_COMMAND_NONE) { + /* a failure that was not triggered by a + decoder command */ + flacPrintErroredState(FLAC__stream_decoder_get_state(flac_dec)); + break; + } + } +} + +static FLAC__StreamDecoderInitStatus +stream_init_oggflac(FLAC__StreamDecoder *flac_dec, struct flac_data *data) +{ + return FLAC__stream_decoder_init_ogg_stream(flac_dec, + FlacInput::Read, + FlacInput::Seek, + FlacInput::Tell, + FlacInput::Length, + FlacInput::Eof, + flac_write_cb, + flacMetadata, + FlacInput::Error, + data); +} + +static FLAC__StreamDecoderInitStatus +stream_init_flac(FLAC__StreamDecoder *flac_dec, struct flac_data *data) +{ + return FLAC__stream_decoder_init_stream(flac_dec, + FlacInput::Read, + FlacInput::Seek, + FlacInput::Tell, + FlacInput::Length, + FlacInput::Eof, + flac_write_cb, + flacMetadata, + FlacInput::Error, + data); +} + +static FLAC__StreamDecoderInitStatus +stream_init(FLAC__StreamDecoder *flac_dec, struct flac_data *data, bool is_ogg) +{ + return is_ogg + ? stream_init_oggflac(flac_dec, data) + : stream_init_flac(flac_dec, data); +} + +static void +flac_decode_internal(struct decoder * decoder, + struct input_stream *input_stream, + bool is_ogg) +{ + FLAC__StreamDecoder *flac_dec; + + flac_dec = flac_decoder_new(); + if (flac_dec == nullptr) + return; + + struct flac_data data(decoder, input_stream); + data.tag = tag_new(); + + FLAC__StreamDecoderInitStatus status = + stream_init(flac_dec, &data, is_ogg); + if (status != FLAC__STREAM_DECODER_INIT_STATUS_OK) { + FLAC__stream_decoder_delete(flac_dec); + g_warning("%s", FLAC__StreamDecoderInitStatusString[status]); + return; + } + + if (!flac_decoder_initialize(&data, flac_dec, 0)) { + FLAC__stream_decoder_finish(flac_dec); + FLAC__stream_decoder_delete(flac_dec); + return; + } + + flac_decoder_loop(&data, flac_dec, 0, 0); + + FLAC__stream_decoder_finish(flac_dec); + FLAC__stream_decoder_delete(flac_dec); +} + +static void +flac_decode(struct decoder * decoder, struct input_stream *input_stream) +{ + flac_decode_internal(decoder, input_stream, false); +} + +static bool +oggflac_init(G_GNUC_UNUSED const struct config_param *param) +{ + return !!FLAC_API_SUPPORTS_OGG_FLAC; +} + +static bool +oggflac_scan_file(const char *file, + const struct tag_handler *handler, void *handler_ctx) +{ + FlacMetadataChain chain; + if (!chain.ReadOgg(file)) { + g_debug("Failed to read OggFLAC tags: %s", + chain.GetStatusString()); + return false; + } + + chain.Scan(handler, handler_ctx); + return true; +} + +static bool +oggflac_scan_stream(struct input_stream *is, + const struct tag_handler *handler, void *handler_ctx) +{ + FlacMetadataChain chain; + if (!chain.ReadOgg(is)) { + g_debug("Failed to read OggFLAC tags: %s", + chain.GetStatusString()); + return false; + } + + chain.Scan(handler, handler_ctx); + return true; +} + +static void +oggflac_decode(struct decoder *decoder, struct input_stream *input_stream) +{ + if (ogg_codec_detect(decoder, input_stream) != OGG_CODEC_FLAC) + return; + + /* rewind the stream, because ogg_codec_detect() has + moved it */ + input_stream_lock_seek(input_stream, 0, SEEK_SET, nullptr); + + flac_decode_internal(decoder, input_stream, true); +} + +static const char *const oggflac_suffixes[] = { "ogg", "oga", nullptr }; +static const char *const oggflac_mime_types[] = { + "application/ogg", + "application/x-ogg", + "audio/ogg", + "audio/x-flac+ogg", + "audio/x-ogg", + nullptr +}; + +const struct decoder_plugin oggflac_decoder_plugin = { + "oggflac", + oggflac_init, + nullptr, + oggflac_decode, + nullptr, + oggflac_scan_file, + oggflac_scan_stream, + nullptr, + oggflac_suffixes, + oggflac_mime_types, +}; + +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", + nullptr +}; + +const struct decoder_plugin flac_decoder_plugin = { + "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/FlacIOHandle.cxx b/src/decoder/FlacIOHandle.cxx new file mode 100644 index 000000000..16a07a9d1 --- /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 "io_error.h" +#include "gcc.h" + +#include + +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!) */ + + GError *error = nullptr; + while (p < end) { + size_t nbytes = input_stream_lock_read(is, p, end - p, &error); + if (nbytes == 0) { + if (error == nullptr) + /* end of file */ + break; + + if (error->domain == errno_quark()) + errno = error->code; + else + /* just some random non-zero + errno value */ + errno = EINVAL; + g_error_free(error); + 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; + + return input_stream_lock_seek(is, offset, whence, nullptr) ? 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 input_stream_lock_eof(is); +} + +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 + +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..f5dedd97b --- /dev/null +++ b/src/decoder/FlacInput.cxx @@ -0,0 +1,149 @@ +/* + * 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 "decoder_api.h" +#include "gcc.h" +#include "InputStream.hxx" + +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_lock_eof(input_stream) || + (decoder != nullptr && + decoder_get_command(decoder) != DECODE_COMMAND_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; + + if (!input_stream_lock_seek(input_stream, + absolute_byte_offset, SEEK_SET, + nullptr)) + 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) != DECODE_COMMAND_NONE && + decoder_get_command(decoder) != DECODE_COMMAND_SEEK) || + input_stream_lock_eof(input_stream); +} + +void +FlacInput::Error(FLAC__StreamDecoderErrorStatus status) +{ + if (decoder == nullptr || + decoder_get_command(decoder) != DECODE_COMMAND_STOP) + g_warning("%s", 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 + +/** + * 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/FlacMetadata.cxx b/src/decoder/FlacMetadata.cxx new file mode 100644 index 000000000..694c88c8f --- /dev/null +++ b/src/decoder/FlacMetadata.cxx @@ -0,0 +1,253 @@ +/* + * 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 "FlacMetadata.hxx" + +extern "C" { +#include "XiphTags.h" +} + +#include "tag.h" +#include "tag_handler.h" +#include "tag_table.h" +#include "replay_gain_info.h" + +#include + +#include +#include + +static bool +flac_find_float_comment(const FLAC__StreamMetadata *block, + const char *cmnt, float *fl) +{ + int offset; + size_t pos; + int len; + unsigned char tmp, *p; + + offset = FLAC__metadata_object_vorbiscomment_find_entry_from(block, 0, + cmnt); + if (offset < 0) + return false; + + pos = strlen(cmnt) + 1; /* 1 is for '=' */ + len = block->data.vorbis_comment.comments[offset].length - pos; + if (len <= 0) + return false; + + p = &block->data.vorbis_comment.comments[offset].entry[pos]; + tmp = p[len]; + p[len] = '\0'; + *fl = (float)atof((char *)p); + p[len] = tmp; + + return true; +} + +bool +flac_parse_replay_gain(struct replay_gain_info *rgi, + const FLAC__StreamMetadata *block) +{ + bool found = false; + + replay_gain_info_init(rgi); + + if (flac_find_float_comment(block, "replaygain_album_gain", + &rgi->tuples[REPLAY_GAIN_ALBUM].gain)) + found = true; + if (flac_find_float_comment(block, "replaygain_album_peak", + &rgi->tuples[REPLAY_GAIN_ALBUM].peak)) + found = true; + if (flac_find_float_comment(block, "replaygain_track_gain", + &rgi->tuples[REPLAY_GAIN_TRACK].gain)) + found = true; + if (flac_find_float_comment(block, "replaygain_track_peak", + &rgi->tuples[REPLAY_GAIN_TRACK].peak)) + found = true; + + return found; +} + +static bool +flac_find_string_comment(const FLAC__StreamMetadata *block, + const char *cmnt, char **str) +{ + int offset; + size_t pos; + int len; + const unsigned char *p; + + *str = nullptr; + offset = FLAC__metadata_object_vorbiscomment_find_entry_from(block, 0, + cmnt); + if (offset < 0) + return false; + + pos = strlen(cmnt) + 1; /* 1 is for '=' */ + len = block->data.vorbis_comment.comments[offset].length - pos; + if (len <= 0) + return false; + + p = &block->data.vorbis_comment.comments[offset].entry[pos]; + *str = g_strndup((const char *)p, len); + + return true; +} + +bool +flac_parse_mixramp(char **mixramp_start, char **mixramp_end, + const FLAC__StreamMetadata *block) +{ + bool found = false; + + if (flac_find_string_comment(block, "mixramp_start", mixramp_start)) + found = true; + if (flac_find_string_comment(block, "mixramp_end", mixramp_end)) + found = true; + + return found; +} + +/** + * Checks if the specified name matches the entry's name, and if yes, + * returns the comment value (not null-temrinated). + */ +static const char * +flac_comment_value(const FLAC__StreamMetadata_VorbisComment_Entry *entry, + const char *name, size_t *length_r) +{ + size_t name_length = strlen(name); + const char *comment = (const char*)entry->entry; + + if (entry->length <= name_length || + g_ascii_strncasecmp(comment, name, name_length) != 0) + return nullptr; + + if (comment[name_length] == '=') { + *length_r = entry->length - name_length - 1; + return comment + name_length + 1; + } + + return nullptr; +} + +/** + * Check if the comment's name equals the passed name, and if so, copy + * the comment value into the tag. + */ +static bool +flac_copy_comment(const FLAC__StreamMetadata_VorbisComment_Entry *entry, + const char *name, enum tag_type tag_type, + const struct tag_handler *handler, void *handler_ctx) +{ + const char *value; + size_t value_length; + + 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); + return true; + } + + return false; +} + +static void +flac_scan_comment(const FLAC__StreamMetadata_VorbisComment_Entry *entry, + const struct tag_handler *handler, void *handler_ctx) +{ + if (handler->pair != nullptr) { + char *name = g_strdup((const char*)entry->entry); + char *value = strchr(name, '='); + + if (value != nullptr && value > name) { + *value++ = 0; + tag_handler_invoke_pair(handler, handler_ctx, + name, value); + } + + g_free(name); + } + + 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], (enum tag_type)i, + handler, handler_ctx)) + return; +} + +static void +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(&comment->comments[i], + handler, handler_ctx); +} + +void +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(&block->data.vorbis_comment, + handler, handler_ctx); + break; + + case FLAC__METADATA_TYPE_STREAMINFO: + if (block->data.stream_info.sample_rate > 0) + tag_handler_invoke_duration(handler, handler_ctx, + flac_duration(&block->data.stream_info)); + break; + + default: + break; + } +} + +void +flac_vorbis_comments_to_tag(struct tag *tag, + const FLAC__StreamMetadata_VorbisComment *comment) +{ + flac_scan_comments(comment, &add_tag_handler, tag); +} + +void +FlacMetadataChain::Scan(const struct tag_handler *handler, void *handler_ctx) +{ + FLACMetadataIterator iterator(*this); + + do { + FLAC__StreamMetadata *block = iterator.GetBlock(); + if (block == nullptr) + break; + + 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..cce34c3a7 --- /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 + +#include + +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(struct 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/FlacPcm.cxx b/src/decoder/FlacPcm.cxx new file mode 100644 index 000000000..82896bd24 --- /dev/null +++ b/src/decoder/FlacPcm.cxx @@ -0,0 +1,110 @@ +/* + * 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 "FlacPcm.hxx" + +#include + +static void flac_convert_stereo16(int16_t *dest, + const FLAC__int32 * const buf[], + unsigned int position, unsigned int end) +{ + for (; position < end; ++position) { + *dest++ = buf[0][position]; + *dest++ = buf[1][position]; + } +} + +static void +flac_convert_16(int16_t *dest, + unsigned int num_channels, + const FLAC__int32 * const buf[], + unsigned int position, unsigned int end) +{ + unsigned int c_chan; + + for (; position < end; ++position) + for (c_chan = 0; c_chan < num_channels; c_chan++) + *dest++ = buf[c_chan][position]; +} + +/** + * Note: this function also handles 24 bit files! + */ +static void +flac_convert_32(int32_t *dest, + unsigned int num_channels, + const FLAC__int32 * const buf[], + unsigned int position, unsigned int end) +{ + unsigned int c_chan; + + for (; position < end; ++position) + for (c_chan = 0; c_chan < num_channels; c_chan++) + *dest++ = buf[c_chan][position]; +} + +static void +flac_convert_8(int8_t *dest, + unsigned int num_channels, + const FLAC__int32 * const buf[], + unsigned int position, unsigned int end) +{ + unsigned int c_chan; + + for (; position < end; ++position) + for (c_chan = 0; c_chan < num_channels; c_chan++) + *dest++ = buf[c_chan][position]; +} + +void +flac_convert(void *dest, + unsigned int num_channels, enum sample_format sample_format, + const FLAC__int32 *const buf[], + unsigned int position, unsigned int end) +{ + switch (sample_format) { + case SAMPLE_FORMAT_S16: + if (num_channels == 2) + flac_convert_stereo16((int16_t*)dest, buf, + position, end); + else + flac_convert_16((int16_t*)dest, num_channels, buf, + position, end); + break; + + case SAMPLE_FORMAT_S24_P32: + case SAMPLE_FORMAT_S32: + flac_convert_32((int32_t*)dest, num_channels, buf, + position, end); + break; + + case SAMPLE_FORMAT_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 */ + assert(false); + } +} diff --git a/src/decoder/FlacPcm.hxx b/src/decoder/FlacPcm.hxx new file mode 100644 index 000000000..97d214c17 --- /dev/null +++ b/src/decoder/FlacPcm.hxx @@ -0,0 +1,33 @@ +/* + * 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_PCM_HXX +#define MPD_FLAC_PCM_HXX + +#include "audio_format.h" + +#include + +void +flac_convert(void *dest, + unsigned int num_channels, enum sample_format sample_format, + const FLAC__int32 *const buf[], + unsigned int position, unsigned int end); + +#endif -- cgit v1.2.3 From 49a3845135142ada6d581d5a6f4a5192aaed49be Mon Sep 17 00:00:00 2001 From: Denis Krjuchkov Date: Sun, 12 May 2013 19:03:42 +0600 Subject: timer: convert to class --- Makefile.am | 6 +-- src/Timer.cxx | 80 ++++++++++++++++++++++++++++++++++++ src/Timer.hxx | 49 ++++++++++++++++++++++ src/clock.h | 8 ++++ src/output/FifoOutputPlugin.cxx | 20 ++++----- src/output/HttpdInternal.hxx | 6 +-- src/output/HttpdOutputPlugin.cxx | 16 ++++---- src/output/NullOutputPlugin.cxx | 22 +++++----- src/timer.c | 89 ---------------------------------------- src/timer.h | 59 -------------------------- 10 files changed, 172 insertions(+), 183 deletions(-) create mode 100644 src/Timer.cxx create mode 100644 src/Timer.hxx delete mode 100644 src/timer.c delete mode 100644 src/timer.h diff --git a/Makefile.am b/Makefile.am index 695b43a5e..a8d077bdd 100644 --- a/Makefile.am +++ b/Makefile.am @@ -94,7 +94,7 @@ mpd_headers = \ src/tag_ape.h \ src/tag_id3.h \ src/tag_rva2.h \ - src/timer.h \ + src/Timer.hxx \ src/mpd_error.h src_mpd_SOURCES = \ @@ -243,7 +243,7 @@ src_mpd_SOURCES = \ src/SongFilter.cxx src/SongFilter.hxx \ src/SongPointer.hxx \ src/PlaylistFile.cxx src/PlaylistFile.hxx \ - src/timer.c + src/Timer.cxx # # Windows resource file @@ -1299,7 +1299,7 @@ test_run_output_SOURCES = test/run_output.cxx \ src/audio_check.c \ src/audio_format.c \ src/AudioParser.cxx \ - src/timer.c src/clock.c \ + src/Timer.cxx src/clock.c \ src/Tag.cxx src/TagNames.c src/TagPool.cxx \ src/Page.cxx \ src/SocketUtil.cxx \ diff --git a/src/Timer.cxx b/src/Timer.cxx new file mode 100644 index 000000000..7ddbda3da --- /dev/null +++ b/src/Timer.cxx @@ -0,0 +1,80 @@ +/* + * 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 "Timer.hxx" +#include "audio_format.h" +#include "clock.h" + +#include + +#include +#include +#include + +Timer::Timer(const struct audio_format &af) + : time(0), + started(false), + rate(af.sample_rate * audio_format_frame_size(&af)) +{ +} + +void Timer::Start() +{ + time = monotonic_clock_us(); + started = true; +} + +void Timer::Reset() +{ + time = 0; + started = false; +} + +void Timer::Add(int size) +{ + assert(started); + + // (size samples) / (rate samples per second) = duration seconds + // duration seconds * 1000000 = duration us + time += ((uint64_t)size * 1000000) / rate; +} + +unsigned Timer::GetDelay() const +{ + int64_t delay = (int64_t)(time - monotonic_clock_us()) / 1000; + if (delay < 0) + return 0; + + if (delay > G_MAXINT) + delay = G_MAXINT; + + return delay; +} + +void Timer::Synchronize() const +{ + int64_t sleep_duration; + + assert(started); + + sleep_duration = time - monotonic_clock_us(); + if (sleep_duration > 0) + g_usleep(sleep_duration); +} diff --git a/src/Timer.hxx b/src/Timer.hxx new file mode 100644 index 000000000..96446a988 --- /dev/null +++ b/src/Timer.hxx @@ -0,0 +1,49 @@ +/* + * 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_TIMER_HXX +#define MPD_TIMER_HXX + +#include + +struct audio_format; + +class Timer { + uint64_t time; + bool started; + const int rate; +public: + explicit Timer(const struct audio_format& af); + + bool IsStarted() const { return started; } + + void Start(); + void Reset(); + + void Add(int size); + + /** + * Returns the number of milliseconds to sleep to get back to sync. + */ + unsigned GetDelay() const; + + void Synchronize() const; +}; + +#endif diff --git a/src/clock.h b/src/clock.h index f1338938f..c98b9a652 100644 --- a/src/clock.h +++ b/src/clock.h @@ -24,6 +24,10 @@ #include +#ifdef __cplusplus +extern "C" { +#endif + /** * Returns the value of a monotonic clock in milliseconds. */ @@ -38,4 +42,8 @@ gcc_pure uint64_t monotonic_clock_us(void); +#ifdef __cplusplus +} +#endif + #endif diff --git a/src/output/FifoOutputPlugin.cxx b/src/output/FifoOutputPlugin.cxx index ebebc254f..15b107c45 100644 --- a/src/output/FifoOutputPlugin.cxx +++ b/src/output/FifoOutputPlugin.cxx @@ -20,7 +20,7 @@ #include "config.h" #include "FifoOutputPlugin.hxx" #include "output_api.h" -#include "timer.h" +#include "Timer.hxx" #include "fd_util.h" #include "open.h" @@ -44,7 +44,7 @@ struct FifoOutput { int input; int output; bool created; - struct timer *timer; + Timer *timer; FifoOutput() :path(nullptr), input(-1), output(-1), created(false) {} @@ -232,7 +232,7 @@ fifo_output_open(struct audio_output *ao, struct audio_format *audio_format, { FifoOutput *fd = (FifoOutput *)ao; - fd->timer = timer_new(audio_format); + fd->timer = new Timer(*audio_format); return true; } @@ -242,7 +242,7 @@ fifo_output_close(struct audio_output *ao) { FifoOutput *fd = (FifoOutput *)ao; - timer_free(fd->timer); + delete fd->timer; } static void @@ -252,7 +252,7 @@ fifo_output_cancel(struct audio_output *ao) char buf[FIFO_BUFFER_SIZE]; int bytes = 1; - timer_reset(fd->timer); + fd->timer->Reset(); while (bytes > 0 && errno != EINTR) bytes = read(fd->input, buf, FIFO_BUFFER_SIZE); @@ -268,8 +268,8 @@ fifo_output_delay(struct audio_output *ao) { FifoOutput *fd = (FifoOutput *)ao; - return fd->timer->started - ? timer_delay(fd->timer) + return fd->timer->IsStarted() + ? fd->timer->GetDelay() : 0; } @@ -280,9 +280,9 @@ fifo_output_play(struct audio_output *ao, const void *chunk, size_t size, FifoOutput *fd = (FifoOutput *)ao; ssize_t bytes; - if (!fd->timer->started) - timer_start(fd->timer); - timer_add(fd->timer, size); + if (!fd->timer->IsStarted()) + fd->timer->Start(); + fd->timer->Add(size); while (true) { bytes = write(fd->output, chunk, size); diff --git a/src/output/HttpdInternal.hxx b/src/output/HttpdInternal.hxx index e79b9a224..b79a22905 100644 --- a/src/output/HttpdInternal.hxx +++ b/src/output/HttpdInternal.hxx @@ -26,7 +26,7 @@ #define MPD_OUTPUT_HTTPD_INTERNAL_H #include "OutputInternal.hxx" -#include "timer.h" +#include "Timer.hxx" #include "thread/Mutex.hxx" #include "event/ServerSocket.hxx" @@ -72,10 +72,10 @@ struct HttpdOutput final : private ServerSocket { mutable Mutex mutex; /** - * A #timer object to synchronize this output with the + * A #Timer object to synchronize this output with the * wallclock. */ - struct timer *timer; + Timer *timer; /** * The header page, which is sent to every client on connect. diff --git a/src/output/HttpdOutputPlugin.cxx b/src/output/HttpdOutputPlugin.cxx index bb644c318..2c4884827 100644 --- a/src/output/HttpdOutputPlugin.cxx +++ b/src/output/HttpdOutputPlugin.cxx @@ -320,7 +320,7 @@ HttpdOutput::Open(struct audio_format *audio_format, GError **error_r) /* initialize other attributes */ clients_cnt = 0; - timer = timer_new(audio_format); + timer = new Timer(*audio_format); open = true; @@ -346,7 +346,7 @@ HttpdOutput::Close() open = false; - timer_free(timer); + delete timer; clients.clear(); @@ -398,7 +398,7 @@ httpd_output_delay(struct audio_output *ao) then httpd_output_pause() will not do anything, it will not fill the buffer and it will not update the timer; therefore, we reset the timer here */ - timer_reset(httpd->timer); + httpd->timer->Reset(); /* some arbitrary delay that is long enough to avoid consuming too much CPU, and short enough to notice @@ -406,8 +406,8 @@ httpd_output_delay(struct audio_output *ao) return 1000; } - return httpd->timer->started - ? timer_delay(httpd->timer) + return httpd->timer->IsStarted() + ? httpd->timer->GetDelay() : 0; } @@ -463,9 +463,9 @@ httpd_output_play(struct audio_output *ao, const void *chunk, size_t size, return 0; } - if (!httpd->timer->started) - timer_start(httpd->timer); - timer_add(httpd->timer, size); + if (!httpd->timer->IsStarted()) + httpd->timer->Start(); + httpd->timer->Add(size); return size; } diff --git a/src/output/NullOutputPlugin.cxx b/src/output/NullOutputPlugin.cxx index bbcf26c00..b167032b6 100644 --- a/src/output/NullOutputPlugin.cxx +++ b/src/output/NullOutputPlugin.cxx @@ -20,7 +20,7 @@ #include "config.h" #include "NullOutputPlugin.hxx" #include "output_api.h" -#include "timer.h" +#include "Timer.hxx" #include @@ -29,7 +29,7 @@ struct NullOutput { bool sync; - struct timer *timer; + Timer *timer; bool Initialize(const config_param *param, GError **error_r) { return ao_base_init(&base, &null_output_plugin, param, @@ -72,7 +72,7 @@ null_open(struct audio_output *ao, struct audio_format *audio_format, NullOutput *nd = (NullOutput *)ao; if (nd->sync) - nd->timer = timer_new(audio_format); + nd->timer = new Timer(*audio_format); return true; } @@ -83,7 +83,7 @@ null_close(struct audio_output *ao) NullOutput *nd = (NullOutput *)ao; if (nd->sync) - timer_free(nd->timer); + delete nd->timer; } static unsigned @@ -91,8 +91,8 @@ null_delay(struct audio_output *ao) { NullOutput *nd = (NullOutput *)ao; - return nd->sync && nd->timer->started - ? timer_delay(nd->timer) + return nd->sync && nd->timer->IsStarted() + ? nd->timer->GetDelay() : 0; } @@ -101,14 +101,14 @@ null_play(struct audio_output *ao, gcc_unused const void *chunk, size_t size, gcc_unused GError **error) { NullOutput *nd = (NullOutput *)ao; - struct timer *timer = nd->timer; + Timer *timer = nd->timer; if (!nd->sync) return size; - if (!timer->started) - timer_start(timer); - timer_add(timer, size); + if (!timer->IsStarted()) + timer->Start(); + timer->Add(size); return size; } @@ -121,7 +121,7 @@ null_cancel(struct audio_output *ao) if (!nd->sync) return; - timer_reset(nd->timer); + nd->timer->Reset(); } const struct audio_output_plugin null_output_plugin = { diff --git a/src/timer.c b/src/timer.c deleted file mode 100644 index 9a3228465..000000000 --- a/src/timer.c +++ /dev/null @@ -1,89 +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 "timer.h" -#include "audio_format.h" -#include "clock.h" - -#include - -#include -#include -#include - -struct timer *timer_new(const struct audio_format *af) -{ - struct timer *timer = g_new(struct timer, 1); - timer->time = 0; // us - timer->started = 0; // false - timer->rate = af->sample_rate * audio_format_frame_size(af); // samples per second - - return timer; -} - -void timer_free(struct timer *timer) -{ - g_free(timer); -} - -void timer_start(struct timer *timer) -{ - timer->time = monotonic_clock_us(); - timer->started = 1; -} - -void timer_reset(struct timer *timer) -{ - timer->time = 0; - timer->started = 0; -} - -void timer_add(struct timer *timer, int size) -{ - assert(timer->started); - - // (size samples) / (rate samples per second) = duration seconds - // duration seconds * 1000000 = duration us - timer->time += ((uint64_t)size * 1000000) / timer->rate; -} - -unsigned -timer_delay(const struct timer *timer) -{ - int64_t delay = (int64_t)(timer->time - monotonic_clock_us()) / 1000; - if (delay < 0) - return 0; - - if (delay > G_MAXINT) - delay = G_MAXINT; - - return delay; -} - -void timer_sync(struct timer *timer) -{ - int64_t sleep_duration; - - assert(timer->started); - - sleep_duration = timer->time - monotonic_clock_us(); - if (sleep_duration > 0) - g_usleep(sleep_duration); -} diff --git a/src/timer.h b/src/timer.h deleted file mode 100644 index 1506c9173..000000000 --- a/src/timer.h +++ /dev/null @@ -1,59 +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_TIMER_H -#define MPD_TIMER_H - -#include - -struct audio_format; - -struct timer { - uint64_t time; - int started; - int rate; -}; - -#ifdef __cplusplus -extern "C" { -#endif - -struct timer *timer_new(const struct audio_format *af); - -void timer_free(struct timer *timer); - -void timer_start(struct timer *timer); - -void timer_reset(struct timer *timer); - -void timer_add(struct timer *timer, int size); - -/** - * Returns the number of milliseconds to sleep to get back to sync. - */ -unsigned -timer_delay(const struct timer *timer); - -void timer_sync(struct timer *timer); - -#ifdef __cplusplus -} -#endif - -#endif -- cgit v1.2.3 From e9e55b08127dc45b4c6045e1f42e34115086a521 Mon Sep 17 00:00:00 2001 From: Denis Krjuchkov Date: Sun, 12 May 2013 20:02:27 +0600 Subject: text_input_stream: convert to class --- Makefile.am | 8 +-- src/TextInputStream.cxx | 92 ++++++++++++++++++++++++++++ src/TextInputStream.hxx | 59 ++++++++++++++++++ src/playlist/CuePlaylistPlugin.cxx | 16 ++--- src/playlist/ExtM3uPlaylistPlugin.cxx | 38 ++++++------ src/playlist/M3uPlaylistPlugin.cxx | 27 ++++----- src/text_input_stream.c | 111 ---------------------------------- src/text_input_stream.h | 52 ---------------- test/dump_text_file.cxx | 22 +++---- 9 files changed, 202 insertions(+), 223 deletions(-) create mode 100644 src/TextInputStream.cxx create mode 100644 src/TextInputStream.hxx delete mode 100644 src/text_input_stream.c delete mode 100644 src/text_input_stream.h diff --git a/Makefile.am b/Makefile.am index a8d077bdd..19db8b644 100644 --- a/Makefile.am +++ b/Makefile.am @@ -70,7 +70,7 @@ mpd_headers = \ src/gcc.h \ src/decoder/pcm_decoder_plugin.h \ src/input_stream.h \ - src/text_input_stream.h \ + src/TextInputStream.hxx \ src/ls.h \ src/mixer_plugin.h \ src/daemon.h \ @@ -238,7 +238,7 @@ src_mpd_SOURCES = \ src/tag_handler.c src/tag_handler.h \ src/TagFile.cxx src/TagFile.hxx \ src/TextFile.cxx src/TextFile.hxx \ - src/text_input_stream.c \ + src/TextInputStream.cxx \ src/Volume.cxx src/Volume.hxx \ src/SongFilter.cxx src/SongFilter.hxx \ src/SongPointer.hxx \ @@ -1108,7 +1108,7 @@ test_dump_text_file_SOURCES = test/dump_text_file.cxx \ test/stdbin.h \ src/IOThread.cxx \ src/Tag.cxx src/TagNames.c src/TagPool.cxx \ - src/text_input_stream.c \ + src/TextInputStream.cxx \ src/fd_util.c test_dump_playlist_LDADD = \ @@ -1130,7 +1130,7 @@ test_dump_playlist_SOURCES = test/dump_playlist.cxx \ src/Song.cxx src/Tag.cxx src/TagNames.c src/TagPool.cxx src/TagSave.cxx \ src/tag_handler.c src/TagFile.cxx \ src/audio_check.c \ - src/text_input_stream.c \ + src/TextInputStream.cxx \ src/cue/CueParser.cxx src/cue/CueParser.hxx \ src/fd_util.c diff --git a/src/TextInputStream.cxx b/src/TextInputStream.cxx new file mode 100644 index 000000000..5e963ae01 --- /dev/null +++ b/src/TextInputStream.cxx @@ -0,0 +1,92 @@ +/* + * 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 "TextInputStream.hxx" +#include "input_stream.h" +#include "util/fifo_buffer.h" + +#include + +#include +#include + +TextInputStream::TextInputStream(struct input_stream *_is) + : is(_is), + buffer(fifo_buffer_new(4096)) +{ +} + +TextInputStream::~TextInputStream() +{ + fifo_buffer_free(buffer); +} + +bool TextInputStream::ReadLine(std::string &line) +{ + GError *error = nullptr; + void *dest; + const char *src, *p; + size_t length, nbytes; + + do { + dest = fifo_buffer_write(buffer, &length); + if (dest != nullptr && length >= 2) { + /* reserve one byte for the null terminator if + the last line is not terminated by a + newline character */ + --length; + + nbytes = input_stream_lock_read(is, dest, length, + &error); + if (nbytes > 0) + fifo_buffer_append(buffer, nbytes); + else if (error != nullptr) { + g_warning("%s", error->message); + g_error_free(error); + return false; + } + } else + nbytes = 0; + + auto src_p = fifo_buffer_read(buffer, &length); + src = reinterpret_cast(src_p); + + if (src == nullptr) + return false; + + p = reinterpret_cast(memchr(src, '\n', length)); + if (p == nullptr && nbytes == 0) { + /* end of file (or line too long): terminate + the current line */ + dest = fifo_buffer_write(buffer, &nbytes); + assert(dest != nullptr); + *(char *)dest = '\n'; + fifo_buffer_append(buffer, 1); + } + } while (p == nullptr); + + length = p - src + 1; + while (p > src && g_ascii_isspace(p[-1])) + --p; + + line = std::string(src, p - src); + fifo_buffer_consume(buffer, length); + return true; +} diff --git a/src/TextInputStream.hxx b/src/TextInputStream.hxx new file mode 100644 index 000000000..2608184e2 --- /dev/null +++ b/src/TextInputStream.hxx @@ -0,0 +1,59 @@ +/* + * 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_TEXT_INPUT_STREAM_HXX +#define MPD_TEXT_INPUT_STREAM_HXX + +#include + +struct input_stream; +struct fifo_buffer; + +class TextInputStream { + struct input_stream *is; + struct fifo_buffer *buffer; +public: + /** + * Wraps an existing #input_stream object into a #TextInputStream, + * to read its contents as text lines. + * + * @param _is an open #input_stream object + */ + explicit TextInputStream(struct input_stream *_is); + + /** + * Frees the #TextInputStream object. Does not close or free the + * underlying #input_stream. + */ + ~TextInputStream(); + + TextInputStream(const TextInputStream &) = delete; + TextInputStream& operator=(const TextInputStream &) = delete; + + /** + * Reads the next line from the stream with newline character stripped. + * + * @param line a string to put result to + * @return true if line is read successfully, false on end of file + * or error + */ + bool ReadLine(std::string &line); +}; + +#endif diff --git a/src/playlist/CuePlaylistPlugin.cxx b/src/playlist/CuePlaylistPlugin.cxx index 730ef783b..3d8098672 100644 --- a/src/playlist/CuePlaylistPlugin.cxx +++ b/src/playlist/CuePlaylistPlugin.cxx @@ -24,10 +24,7 @@ #include "song.h" #include "input_stream.h" #include "cue/CueParser.hxx" - -extern "C" { -#include "text_input_stream.h" -} +#include "TextInputStream.hxx" #include #include @@ -40,16 +37,15 @@ struct CuePlaylist { struct playlist_provider base; struct input_stream *is; - struct text_input_stream *tis; + TextInputStream tis; CueParser parser; CuePlaylist(struct input_stream *_is) - :is(_is), tis(text_input_stream_new(is)) { + :is(_is), tis(is) { playlist_provider_init(&base, &cue_playlist_plugin); } ~CuePlaylist() { - text_input_stream_free(tis); } }; @@ -76,9 +72,9 @@ cue_playlist_read(struct playlist_provider *_playlist) if (song != NULL) return song; - const char *line; - while ((line = text_input_stream_read(playlist->tis)) != NULL) { - playlist->parser.Feed(line); + std::string line; + while (playlist->tis.ReadLine(line)) { + playlist->parser.Feed(line.c_str()); song = playlist->parser.Get(); if (song != NULL) return song; diff --git a/src/playlist/ExtM3uPlaylistPlugin.cxx b/src/playlist/ExtM3uPlaylistPlugin.cxx index 2043bea52..923536ea9 100644 --- a/src/playlist/ExtM3uPlaylistPlugin.cxx +++ b/src/playlist/ExtM3uPlaylistPlugin.cxx @@ -23,10 +23,7 @@ #include "song.h" #include "tag.h" #include "util/StringUtil.hxx" - -extern "C" { -#include "text_input_stream.h" -} +#include "TextInputStream.hxx" #include @@ -36,20 +33,21 @@ extern "C" { struct ExtM3uPlaylist { struct playlist_provider base; - struct text_input_stream *tis; + TextInputStream *tis; }; static struct playlist_provider * extm3u_open_stream(struct input_stream *is) { ExtM3uPlaylist *playlist = g_new(ExtM3uPlaylist, 1); - playlist->tis = text_input_stream_new(is); + playlist->tis = new TextInputStream(is); - const char *line = text_input_stream_read(playlist->tis); - if (line == NULL || strcmp(line, "#EXTM3U") != 0) { + std::string line; + if (!playlist->tis->ReadLine(line) + || strcmp(line.c_str(), "#EXTM3U") != 0) { /* no EXTM3U header: fall back to the plain m3u plugin */ - text_input_stream_free(playlist->tis); + delete playlist->tis; g_free(playlist); return NULL; } @@ -63,7 +61,7 @@ extm3u_close(struct playlist_provider *_playlist) { ExtM3uPlaylist *playlist = (ExtM3uPlaylist *)_playlist; - text_input_stream_free(playlist->tis); + delete playlist->tis; g_free(playlist); } @@ -112,29 +110,31 @@ extm3u_read(struct playlist_provider *_playlist) { ExtM3uPlaylist *playlist = (ExtM3uPlaylist *)_playlist; struct tag *tag = NULL; - const char *line; + std::string line; + const char *line_s; struct song *song; do { - line = text_input_stream_read(playlist->tis); - if (line == NULL) { + if (!playlist->tis->ReadLine(line)) { if (tag != NULL) tag_free(tag); return NULL; } + + line_s = line.c_str(); - if (g_str_has_prefix(line, "#EXTINF:")) { + if (g_str_has_prefix(line_s, "#EXTINF:")) { if (tag != NULL) tag_free(tag); - tag = extm3u_parse_tag(line + 8); + tag = extm3u_parse_tag(line_s + 8); continue; } - while (*line != 0 && g_ascii_isspace(*line)) - ++line; - } while (line[0] == '#' || *line == 0); + while (*line_s != 0 && g_ascii_isspace(*line_s)) + ++line_s; + } while (line_s[0] == '#' || *line_s == 0); - song = song_remote_new(line); + song = song_remote_new(line_s); song->tag = tag; return song; } diff --git a/src/playlist/M3uPlaylistPlugin.cxx b/src/playlist/M3uPlaylistPlugin.cxx index eeecd2779..e1e0a803b 100644 --- a/src/playlist/M3uPlaylistPlugin.cxx +++ b/src/playlist/M3uPlaylistPlugin.cxx @@ -21,17 +21,14 @@ #include "M3uPlaylistPlugin.hxx" #include "PlaylistPlugin.hxx" #include "song.h" - -extern "C" { -#include "text_input_stream.h" -} +#include "TextInputStream.hxx" #include struct M3uPlaylist { struct playlist_provider base; - struct text_input_stream *tis; + TextInputStream *tis; }; static struct playlist_provider * @@ -40,7 +37,7 @@ m3u_open_stream(struct input_stream *is) M3uPlaylist *playlist = g_new(M3uPlaylist, 1); playlist_provider_init(&playlist->base, &m3u_playlist_plugin); - playlist->tis = text_input_stream_new(is); + playlist->tis = new TextInputStream(is); return &playlist->base; } @@ -50,7 +47,7 @@ m3u_close(struct playlist_provider *_playlist) { M3uPlaylist *playlist = (M3uPlaylist *)_playlist; - text_input_stream_free(playlist->tis); + delete playlist->tis; g_free(playlist); } @@ -58,18 +55,20 @@ static struct song * m3u_read(struct playlist_provider *_playlist) { M3uPlaylist *playlist = (M3uPlaylist *)_playlist; - const char *line; + std::string line; + const char *line_s; do { - line = text_input_stream_read(playlist->tis); - if (line == NULL) + if (!playlist->tis->ReadLine(line)) return NULL; - while (*line != 0 && g_ascii_isspace(*line)) - ++line; - } while (line[0] == '#' || *line == 0); + line_s = line.c_str(); + + while (*line_s != 0 && g_ascii_isspace(*line_s)) + ++line_s; + } while (line_s[0] == '#' || *line_s == 0); - return song_remote_new(line); + return song_remote_new(line_s); } static const char *const m3u_suffixes[] = { diff --git a/src/text_input_stream.c b/src/text_input_stream.c deleted file mode 100644 index 4a2eeb817..000000000 --- a/src/text_input_stream.c +++ /dev/null @@ -1,111 +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 "text_input_stream.h" -#include "input_stream.h" -#include "util/fifo_buffer.h" - -#include - -#include -#include - -struct text_input_stream { - struct input_stream *is; - - struct fifo_buffer *buffer; - - char *line; -}; - -struct text_input_stream * -text_input_stream_new(struct input_stream *is) -{ - struct text_input_stream *tis = g_new(struct text_input_stream, 1); - - tis->is = is; - tis->buffer = fifo_buffer_new(4096); - tis->line = NULL; - - return tis; -} - -void -text_input_stream_free(struct text_input_stream *tis) -{ - fifo_buffer_free(tis->buffer); - g_free(tis->line); - g_free(tis); -} - -const char * -text_input_stream_read(struct text_input_stream *tis) -{ - GError *error = NULL; - void *dest; - const char *src, *p; - size_t length, nbytes; - - g_free(tis->line); - tis->line = NULL; - - do { - dest = fifo_buffer_write(tis->buffer, &length); - if (dest != NULL && length >= 2) { - /* reserve one byte for the null terminator if - the last line is not terminated by a - newline character */ - --length; - - nbytes = input_stream_lock_read(tis->is, dest, length, - &error); - if (nbytes > 0) - fifo_buffer_append(tis->buffer, nbytes); - else if (error != NULL) { - g_warning("%s", error->message); - g_error_free(error); - return NULL; - } - } else - nbytes = 0; - - src = fifo_buffer_read(tis->buffer, &length); - if (src == NULL) - return NULL; - - p = memchr(src, '\n', length); - if (p == NULL && nbytes == 0) { - /* end of file (or line too long): terminate - the current line */ - dest = fifo_buffer_write(tis->buffer, &nbytes); - assert(dest != NULL); - *(char *)dest = '\n'; - fifo_buffer_append(tis->buffer, 1); - } - } while (p == NULL); - - length = p - src + 1; - while (p > src && g_ascii_isspace(p[-1])) - --p; - - tis->line = g_strndup(src, p - src); - fifo_buffer_consume(tis->buffer, length); - return tis->line; -} diff --git a/src/text_input_stream.h b/src/text_input_stream.h deleted file mode 100644 index 9b3245689..000000000 --- a/src/text_input_stream.h +++ /dev/null @@ -1,52 +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_TEXT_INPUT_STREAM_H -#define MPD_TEXT_INPUT_STREAM_H - -struct input_stream; -struct text_input_stream; - -/** - * Wraps an existing #input_stream object into a #text_input_stream, - * to read its contents as text lines. - * - * @param is an open #input_stream object - * @return the new #text_input_stream object - */ -struct text_input_stream * -text_input_stream_new(struct input_stream *is); - -/** - * Frees the #text_input_stream object. Does not close or free the - * underlying #input_stream. - */ -void -text_input_stream_free(struct text_input_stream *tis); - -/** - * Reads the next line from the stream. - * - * @return a line (newline character stripped), or NULL on end of file - * or error - */ -const char * -text_input_stream_read(struct text_input_stream *tis); - -#endif diff --git a/test/dump_text_file.cxx b/test/dump_text_file.cxx index 19502c7fd..40b3a878f 100644 --- a/test/dump_text_file.cxx +++ b/test/dump_text_file.cxx @@ -23,10 +23,7 @@ #include "InputStream.hxx" #include "conf.h" #include "stdbin.h" - -extern "C" { -#include "text_input_stream.h" -} +#include "TextInputStream.hxx" #ifdef ENABLE_ARCHIVE #include "ArchiveList.hxx" @@ -49,11 +46,11 @@ my_log_func(const gchar *log_domain, G_GNUC_UNUSED GLogLevelFlags log_level, } static void -dump_text_file(struct text_input_stream *is) +dump_text_file(TextInputStream &is) { - const char *line; - while ((line = text_input_stream_read(is)) != NULL) - printf("'%s'\n", line); + std::string line; + while (is.ReadLine(line)) + printf("'%s'\n", line.c_str()); } static int @@ -77,11 +74,10 @@ dump_input_stream(struct input_stream *is) /* read data and tags from the stream */ input_stream_unlock(is); - - struct text_input_stream *tis = text_input_stream_new(is); - dump_text_file(tis); - text_input_stream_free(tis); - + { + TextInputStream tis(is); + dump_text_file(tis); + } input_stream_lock(is); if (!input_stream_check(is, &error)) { -- cgit v1.2.3