Merge branch 'v0.21.x'
This commit is contained in:
commit
9bcd02d178
6
NEWS
6
NEWS
@ -22,6 +22,12 @@ ver 0.22 (not yet released)
|
|||||||
* switch to C++17
|
* switch to C++17
|
||||||
- GCC 7 or clang 4 (or newer) recommended
|
- GCC 7 or clang 4 (or newer) recommended
|
||||||
|
|
||||||
|
ver 0.21.14 (not yet released)
|
||||||
|
* decoder
|
||||||
|
- sidplay: show track durations in database
|
||||||
|
- sidplay: convert tag values from Windows-1252 charset
|
||||||
|
- sidplay: strip text from "Date" tag
|
||||||
|
|
||||||
ver 0.21.13 (2019/08/06)
|
ver 0.21.13 (2019/08/06)
|
||||||
* input
|
* input
|
||||||
- cdio_paranoia: require libcdio-paranoia 10.2+0.93+1
|
- cdio_paranoia: require libcdio-paranoia 10.2+0.93+1
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="org.musicpd"
|
package="org.musicpd"
|
||||||
android:installLocation="auto"
|
android:installLocation="auto"
|
||||||
android:versionCode="36"
|
android:versionCode="37"
|
||||||
android:versionName="0.21.13">
|
android:versionName="0.21.14">
|
||||||
|
|
||||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="26"/>
|
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="26"/>
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
#include "song/DetachedSong.hxx"
|
#include "song/DetachedSong.hxx"
|
||||||
#include "fs/Path.hxx"
|
#include "fs/Path.hxx"
|
||||||
#include "fs/AllocatedPath.hxx"
|
#include "fs/AllocatedPath.hxx"
|
||||||
|
#include "lib/icu/Converter.hxx"
|
||||||
#ifdef HAVE_SIDPLAYFP
|
#ifdef HAVE_SIDPLAYFP
|
||||||
#include "fs/io/FileReader.hxx"
|
#include "fs/io/FileReader.hxx"
|
||||||
#include "util/RuntimeError.hxx"
|
#include "util/RuntimeError.hxx"
|
||||||
@ -32,6 +33,8 @@
|
|||||||
#include "util/StringFormat.hxx"
|
#include "util/StringFormat.hxx"
|
||||||
#include "util/StringView.hxx"
|
#include "util/StringView.hxx"
|
||||||
#include "util/Domain.hxx"
|
#include "util/Domain.hxx"
|
||||||
|
#include "util/AllocatedString.hxx"
|
||||||
|
#include "util/CharUtil.hxx"
|
||||||
#include "util/ByteOrder.hxx"
|
#include "util/ByteOrder.hxx"
|
||||||
#include "Log.hxx"
|
#include "Log.hxx"
|
||||||
|
|
||||||
@ -437,19 +440,70 @@ sidplay_file_decode(DecoderClient &client, Path path_fs)
|
|||||||
} while (cmd != DecoderCommand::STOP);
|
} while (cmd != DecoderCommand::STOP);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static AllocatedString<char>
|
||||||
|
Windows1252ToUTF8(const char *s) noexcept
|
||||||
|
{
|
||||||
|
#ifdef HAVE_ICU_CONVERTER
|
||||||
|
try {
|
||||||
|
std::unique_ptr<IcuConverter>
|
||||||
|
converter(IcuConverter::Create("windows-1252"));
|
||||||
|
|
||||||
|
return converter->ToUTF8(s);
|
||||||
|
} catch (...) { }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fallback to not transcoding windows-1252 to utf-8, that may result
|
||||||
|
* in invalid utf-8 unless nonprintable characters are replaced.
|
||||||
|
*/
|
||||||
|
auto t = AllocatedString<char>::Duplicate(s);
|
||||||
|
|
||||||
|
for (size_t i = 0; t[i] != AllocatedString<char>::SENTINEL; i++)
|
||||||
|
if (!IsPrintableASCII(t[i]))
|
||||||
|
t[i] = '?';
|
||||||
|
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
gcc_pure
|
gcc_pure
|
||||||
static const char *
|
static AllocatedString<char>
|
||||||
GetInfoString(const SidTuneInfo &info, unsigned i) noexcept
|
GetInfoString(const SidTuneInfo &info, unsigned i) noexcept
|
||||||
{
|
{
|
||||||
#ifdef HAVE_SIDPLAYFP
|
#ifdef HAVE_SIDPLAYFP
|
||||||
return info.numberOfInfoStrings() > i
|
const char *s = info.numberOfInfoStrings() > i
|
||||||
? info.infoString(i)
|
? info.infoString(i)
|
||||||
: nullptr;
|
: "";
|
||||||
#else
|
#else
|
||||||
return info.numberOfInfoStrings > i
|
const char *s = info.numberOfInfoStrings > i
|
||||||
? info.infoString[i]
|
? info.infoString[i]
|
||||||
: nullptr;
|
: "";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
return Windows1252ToUTF8(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
gcc_pure
|
||||||
|
static AllocatedString<char>
|
||||||
|
GetDateString(const SidTuneInfo &info) noexcept
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Field 2 is called <released>, previously used as <copyright>.
|
||||||
|
* It is formatted <year><space><company or author or group>,
|
||||||
|
* where <year> may be <YYYY>, <YYY?>, <YY??> or <YYYY-YY>, for
|
||||||
|
* example "1987", "199?", "19??" or "1985-87". The <company or
|
||||||
|
* author or group> may be for example Rob Hubbard. A full field
|
||||||
|
* may be for example "1987 Rob Hubbard".
|
||||||
|
*/
|
||||||
|
AllocatedString<char> release = GetInfoString(info, 2);
|
||||||
|
|
||||||
|
/* Keep the <year> part only for the date. */
|
||||||
|
for (size_t i = 0; release[i] != AllocatedString<char>::SENTINEL; i++)
|
||||||
|
if (std::isspace(release[i])) {
|
||||||
|
release[i] = AllocatedString<char>::SENTINEL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return release;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -457,31 +511,29 @@ ScanSidTuneInfo(const SidTuneInfo &info, unsigned track, unsigned n_tracks,
|
|||||||
TagHandler &handler) noexcept
|
TagHandler &handler) noexcept
|
||||||
{
|
{
|
||||||
/* title */
|
/* title */
|
||||||
const char *title = GetInfoString(info, 0);
|
const auto title = GetInfoString(info, 0);
|
||||||
if (title == nullptr)
|
|
||||||
title = "";
|
|
||||||
|
|
||||||
if (n_tracks > 1) {
|
if (n_tracks > 1) {
|
||||||
const auto tag_title =
|
const auto tag_title =
|
||||||
StringFormat<1024>("%s (%u/%u)",
|
StringFormat<1024>("%s (%u/%u)",
|
||||||
title, track, n_tracks);
|
title.c_str(), track, n_tracks);
|
||||||
handler.OnTag(TAG_TITLE, tag_title.c_str());
|
handler.OnTag(TAG_TITLE, tag_title.c_str());
|
||||||
} else
|
} else
|
||||||
handler.OnTag(TAG_TITLE, title);
|
handler.OnTag(TAG_TITLE, title.c_str());
|
||||||
|
|
||||||
/* artist */
|
/* artist */
|
||||||
const char *artist = GetInfoString(info, 1);
|
const auto artist = GetInfoString(info, 1);
|
||||||
if (artist != nullptr)
|
if (!artist.empty())
|
||||||
handler.OnTag(TAG_ARTIST, artist);
|
handler.OnTag(TAG_ARTIST, artist.c_str());
|
||||||
|
|
||||||
/* genre */
|
/* genre */
|
||||||
if (!default_genre.empty())
|
if (!default_genre.empty())
|
||||||
handler.OnTag(TAG_GENRE, default_genre.c_str());
|
handler.OnTag(TAG_GENRE, default_genre.c_str());
|
||||||
|
|
||||||
/* date */
|
/* date */
|
||||||
const char *date = GetInfoString(info, 2);
|
const auto date = GetDateString(info);
|
||||||
if (date != nullptr)
|
if (!date.empty())
|
||||||
handler.OnTag(TAG_DATE, date);
|
handler.OnTag(TAG_DATE, date.c_str());
|
||||||
|
|
||||||
/* track */
|
/* track */
|
||||||
handler.OnTag(TAG_TRACK, StringFormat<16>("%u", track).c_str());
|
handler.OnTag(TAG_TRACK, StringFormat<16>("%u", track).c_str());
|
||||||
@ -556,6 +608,10 @@ sidplay_container_scan(Path path_fs)
|
|||||||
AddTagHandler h(tag_builder);
|
AddTagHandler h(tag_builder);
|
||||||
ScanSidTuneInfo(info, i, n_tracks, h);
|
ScanSidTuneInfo(info, i, n_tracks, h);
|
||||||
|
|
||||||
|
const SignedSongTime duration = get_song_length(tune);
|
||||||
|
if (!duration.IsNegative())
|
||||||
|
h.OnDuration(SongTime(duration));
|
||||||
|
|
||||||
char track_name[32];
|
char track_name[32];
|
||||||
/* Construct container/tune path names, eg.
|
/* Construct container/tune path names, eg.
|
||||||
Delta.sid/tune_001.sid */
|
Delta.sid/tune_001.sid */
|
||||||
|
Loading…
Reference in New Issue
Block a user