From 72b6c09a738d53ce2860e8c4ae86e7c17b1dac8f Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Fri, 6 Jul 2018 13:22:17 +0200 Subject: [PATCH] db/simple: add an AudioFormat to each Song --- Makefile.am | 3 +++ NEWS | 2 ++ doc/protocol.xml | 11 +++++++++++ src/SongPrint.cxx | 3 +++ src/SongSave.cxx | 18 +++++++++++++++++- src/SongSave.hxx | 4 +++- src/SongUpdate.cxx | 8 ++++++-- src/db/LightSong.hxx | 7 +++++++ src/db/plugins/simple/DirectorySave.cxx | 11 ++++++++--- src/db/plugins/simple/Song.cxx | 1 + src/db/plugins/simple/Song.hxx | 7 +++++++ 11 files changed, 68 insertions(+), 7 deletions(-) diff --git a/Makefile.am b/Makefile.am index 11e9d4476..64e4b1d4e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1932,6 +1932,9 @@ test_DumpDatabase_SOURCES = test/DumpDatabase.cxx \ src/db/Selection.cxx \ src/db/PlaylistVector.cxx \ src/db/DatabaseLock.cxx \ + src/AudioFormat.cxx \ + src/AudioParser.cxx \ + src/pcm/SampleFormat.cxx \ src/SongSave.cxx \ src/DetachedSong.cxx \ src/TagSave.cxx \ diff --git a/NEWS b/NEWS index 105f3375c..817824d2e 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,8 @@ ver 0.21 (not yet released) - "outputs" prints the plugin name - "outputset" sets runtime attributes - close connection when client sends HTTP request +* database + - simple: scan audio formats * player - "one-shot" single mode * input diff --git a/doc/protocol.xml b/doc/protocol.xml index e0532e0e4..c2a74af1e 100644 --- a/doc/protocol.xml +++ b/doc/protocol.xml @@ -256,6 +256,17 @@ + + + Format: the audio format of the song + (or an approximation to a format supported by MPD and the + decoder plugin being used). When playing this file, the + audio value in the status + response should be the same. + + + Last-Modified: the time stamp of the diff --git a/src/SongPrint.cxx b/src/SongPrint.cxx index 9db113f80..b42287c02 100644 --- a/src/SongPrint.cxx +++ b/src/SongPrint.cxx @@ -92,6 +92,9 @@ song_print_info(Response &r, const LightSong &song, bool base) noexcept if (!IsNegative(song.mtime)) time_print(r, "Last-Modified", song.mtime); + if (song.audio_format.IsDefined()) + r.Format("Format: %s\n", ToString(song.audio_format).c_str()); + tag_print(r, song.tag); } diff --git a/src/SongSave.cxx b/src/SongSave.cxx index c3593d823..e42c58f49 100644 --- a/src/SongSave.cxx +++ b/src/SongSave.cxx @@ -19,6 +19,7 @@ #include "config.h" #include "SongSave.hxx" +#include "AudioParser.hxx" #include "db/plugins/simple/Song.hxx" #include "DetachedSong.hxx" #include "TagSave.hxx" @@ -28,6 +29,8 @@ #include "tag/Tag.hxx" #include "tag/Builder.hxx" #include "util/ChronoUtil.hxx" +#include "util/StringAPI.hxx" +#include "util/StringBuffer.hxx" #include "util/StringStrip.hxx" #include "util/RuntimeError.hxx" #include "util/NumberParser.hxx" @@ -56,6 +59,9 @@ song_save(BufferedOutputStream &os, const Song &song) tag_save(os, song.tag); + if (song.audio_format.IsDefined()) + os.Format("Format: %s\n", ToString(song.audio_format).c_str()); + if (!IsNegative(song.mtime)) os.Format(SONG_MTIME ": %li\n", (long)std::chrono::system_clock::to_time_t(song.mtime)); @@ -78,7 +84,8 @@ song_save(BufferedOutputStream &os, const DetachedSong &song) } std::unique_ptr -song_load(TextFile &file, const char *uri) +song_load(TextFile &file, const char *uri, + AudioFormat *audio_format_r) { auto song = std::make_unique(uri); @@ -100,6 +107,15 @@ song_load(TextFile &file, const char *uri) tag.AddItem(type, value); } else if (strcmp(line, "Time") == 0) { tag.SetDuration(SignedSongTime::FromS(ParseDouble(value))); + } else if (StringIsEqual(line, "Format")) { + if (audio_format_r != nullptr) { + try { + *audio_format_r = + ParseAudioFormat(value, false); + } catch (...) { + /* ignore parser errors */ + } + } } else if (strcmp(line, "Playlist") == 0) { tag.SetHasPlaylist(strcmp(value, "yes") == 0); } else if (strcmp(line, SONG_MTIME) == 0) { diff --git a/src/SongSave.hxx b/src/SongSave.hxx index 9e6ae151d..81505b91c 100644 --- a/src/SongSave.hxx +++ b/src/SongSave.hxx @@ -25,6 +25,7 @@ #define SONG_BEGIN "song_begin: " struct Song; +struct AudioFormat; class DetachedSong; class BufferedOutputStream; class TextFile; @@ -42,6 +43,7 @@ song_save(BufferedOutputStream &os, const DetachedSong &song); * Throws #std::runtime_error on error. */ std::unique_ptr -song_load(TextFile &file, const char *uri); +song_load(TextFile &file, const char *uri, + AudioFormat *audio_format_r=nullptr); #endif diff --git a/src/SongUpdate.cxx b/src/SongUpdate.cxx index e43c45961..5738955b3 100644 --- a/src/SongUpdate.cxx +++ b/src/SongUpdate.cxx @@ -77,19 +77,23 @@ Song::UpdateFile(Storage &storage) noexcept return false; TagBuilder tag_builder; + auto new_audio_format = AudioFormat::Undefined(); const auto path_fs = storage.MapFS(relative_uri.c_str()); if (path_fs.IsNull()) { const auto absolute_uri = storage.MapUTF8(relative_uri.c_str()); - if (!tag_stream_scan(absolute_uri.c_str(), tag_builder)) + if (!tag_stream_scan(absolute_uri.c_str(), tag_builder, + &new_audio_format)) return false; } else { - if (!ScanFileTagsWithGeneric(path_fs, tag_builder)) + if (!ScanFileTagsWithGeneric(path_fs, tag_builder, + &new_audio_format)) return false; } mtime = info.mtime; + audio_format = new_audio_format; tag_builder.Commit(tag); return true; } diff --git a/src/db/LightSong.hxx b/src/db/LightSong.hxx index 7b5b51043..7bc21e030 100644 --- a/src/db/LightSong.hxx +++ b/src/db/LightSong.hxx @@ -21,6 +21,7 @@ #define MPD_LIGHT_SONG_HXX #include "Chrono.hxx" +#include "AudioFormat.hxx" #include "Compiler.h" #include @@ -78,6 +79,12 @@ struct LightSong { */ SongTime end_time = SongTime::zero(); + /** + * The audio format of the song, if given by the decoder + * plugin. May be undefined if unknown. + */ + AudioFormat audio_format = AudioFormat::Undefined(); + LightSong(const char *_uri, const Tag &_tag) noexcept :uri(_uri), tag(_tag) {} diff --git a/src/db/plugins/simple/DirectorySave.cxx b/src/db/plugins/simple/DirectorySave.cxx index 3cba77295..35406cadd 100644 --- a/src/db/plugins/simple/DirectorySave.cxx +++ b/src/db/plugins/simple/DirectorySave.cxx @@ -161,10 +161,15 @@ directory_load(TextFile &file, Directory &directory) if (directory.FindSong(name) != nullptr) throw FormatRuntimeError("Duplicate song '%s'", name); - auto song = song_load(file, name); + auto audio_format = AudioFormat::Undefined(); + auto detached_song = song_load(file, name, + &audio_format); - directory.AddSong(Song::NewFrom(std::move(*song), - directory)); + auto song = Song::NewFrom(std::move(*detached_song), + directory); + song->audio_format = audio_format; + + directory.AddSong(song); } else if ((p = StringAfterPrefix(line, PLAYLIST_META_BEGIN))) { const char *name = p; playlist_metadata_load(file, directory.playlists, name); diff --git a/src/db/plugins/simple/Song.cxx b/src/db/plugins/simple/Song.cxx index 61ed929df..4933e0e38 100644 --- a/src/db/plugins/simple/Song.cxx +++ b/src/db/plugins/simple/Song.cxx @@ -104,5 +104,6 @@ Song::Export() const noexcept dest.mtime = mtime; dest.start_time = start_time; dest.end_time = end_time; + dest.audio_format = audio_format; return dest; } diff --git a/src/db/plugins/simple/Song.hxx b/src/db/plugins/simple/Song.hxx index 6802c7059..1cd344f9f 100644 --- a/src/db/plugins/simple/Song.hxx +++ b/src/db/plugins/simple/Song.hxx @@ -23,6 +23,7 @@ #include "check.h" #include "Chrono.hxx" #include "tag/Tag.hxx" +#include "AudioFormat.hxx" #include "Compiler.h" #include @@ -88,6 +89,12 @@ struct Song { */ SongTime end_time = SongTime::zero(); + /** + * The audio format of the song, if given by the decoder + * plugin. May be undefined if unknown. + */ + AudioFormat audio_format = AudioFormat::Undefined(); + /** * The file name. */