Compare commits

...

46 Commits

Author SHA1 Message Date
Max Kellermann
a7fdfa08e1 release v0.20.11 2017-10-18 10:14:46 +02:00
Max Kellermann
9703a401c5 Playlist{File,Save}: always use UTF-8 in playlists on Windows
Turns out that using CP_ACP is a lousy idea, because only very few
Unicode characters can be represented by it.  Instead, switch to UTF-8
(which every sane person on other operating system already uses).

Closes 
2017-10-18 10:05:26 +02:00
Max Kellermann
753a2aa462 PlaylistSave: move code to playlist_print_path() 2017-10-18 09:51:04 +02:00
Max Kellermann
10990a0684 queue/Playlist: call MoveOrderToCurrent() in SeekSongOrder() on song change
Applies the improvements from the previous commit to the "seek"
commands, which are also capable of switching songs.

Closes 
2017-10-18 09:14:27 +02:00
Max Kellermann
91254e9211 queue/PlaylistControl: keep order list consistency in MoveOrderToCurrent()
Our previous use of Queue::SwapOrders() could cause surprising
results:

- sometimes, the old "current" song would be played again (if the
  newly selected song had not been played already)

- sometimes, the old "current" song would not be played again (if the
  newly selected song had already been played)

This is inconsistent, because it should not depend on whether the
newly selected song had already been played.

So instead of Queue::SwapOrders() we now use Queue::MoveOrderAfter()
and Queue::MoveOrderBefore(), which is more expensive, but also more
consistent.  It attempts to retain as much from the previous order
list as possible, and only moves the newly selected song around.
2017-10-18 09:05:47 +02:00
Max Kellermann
0f79287b04 queue/Playlist: move code to MoveOrderToCurrent() 2017-10-18 09:05:24 +02:00
Max Kellermann
f2fac77d8c queue/Queue: add methods MoveOrderBefore() and MoveOrderAfter() 2017-10-18 08:50:01 +02:00
Max Kellermann
81b7373637 queue/Queue: MoveOrder() returns to_order 2017-10-18 08:46:31 +02:00
Max Kellermann
fa67c2548a decoder/Thread: clear the command after catching an exception
If an early exception gets caught (e.g. from
AllocatedPath::FromUTF8Throw()) before
DecoderControl::CommandFinishedLocked() is called, the decoder thread
would go in an endless loop, because DecoderCommand::START is still
set.

Closes 
2017-09-27 17:08:16 +02:00
John Regan
ea80587ddb GME Plugin: fix track numbering
GME starts all track indexes at zero, but subtune prefixes
start at one. This fixes an off-by-one error during track
enumeration.
2017-09-27 11:18:03 +02:00
Max Kellermann
828f5f8384 lib/icu/CaseFold: disable broken strxfrm() callback 2017-09-20 23:55:14 +02:00
Max Kellermann
1295a1272a lib/icu/Compare: add fallback using strcasecmp() and strcasestr()
Our IcuCaseFold() fallback using strxfrm() is not actually case
insensitive.  This commit fixes the problem by switching to
strcasecmp().  That function is not guaranteed to support UTF-8, but
it's the best we can do in this sparse situation.

Closes 
2017-09-20 23:43:27 +02:00
Max Kellermann
66646d9276 SongFilter: use class IcuCompare 2017-09-20 23:43:26 +02:00
Max Kellermann
d0497dba92 lib/icu/Compare: OO wrapper for IcuCaseFold() 2017-09-20 23:32:55 +02:00
Max Kellermann
42914e8227 lib/icu/CaseFold: add "noexcept" 2017-09-20 23:32:54 +02:00
Max Kellermann
59b49b7881 db/Selection: add missing config.h 2017-09-20 23:32:54 +02:00
Max Kellermann
5620f16330 lib/icu/Collate: move IcuCaseFold() to CaseFold.cxx 2017-09-20 23:11:58 +02:00
Max Kellermann
be024d4ad7 lib/icu/Collate: remove unnecessary assert() 2017-09-20 23:05:31 +02:00
Max Kellermann
75c740fe2b output/sndio: fix indent 2017-09-19 18:50:35 +02:00
Max Kellermann
6c8d86bb90 output/sndio: rename the "sio_hdl" variable to avoid clash with struct name 2017-09-19 18:49:33 +02:00
Charlie Waters
b253a6b71e ffmpeg plugin: when decoded stream duration is unavailable, attempt fallback to container duration (fix ) 2017-09-18 10:39:27 +02:00
Max Kellermann
ca7b4df812 doc/user: document the Opus encoder 2017-09-07 14:21:40 +02:00
Max Kellermann
bc8dd57236 doc/protocol.xml: document status/volume=-1
Closes 
2017-09-04 08:15:41 +02:00
Max Kellermann
f4f461b8bb storage/curl: support Content-Type application/xml 2017-09-01 11:32:40 +02:00
Max Kellermann
cbb9b6957f storage/curl: use StringStartsWith() 2017-09-01 11:31:10 +02:00
Max Kellermann
f6b56c9317 storage/curl: move code to IsXmlContentType() 2017-09-01 11:30:30 +02:00
Max Kellermann
3717fb6c8d win32/build.py: add -march=pentium3 to fix 32 bit LAME build
Workaround for the following LAME build failure:

 error: inlining failed in call to always_inline '_mm_sqrt_ps': target
 specific option mismatch

This is because the LAME build scripts do not check whether SSE is
available; they only check for the presence of the "xmmintrin.h"
header.

Requiring a Pentium 3 CPU is reasonable enough, and it's the first CPU
to feature SSE support.
2017-08-31 19:48:59 +02:00
Max Kellermann
f6abbc01bd increment version number to 0.20.11 2017-08-31 19:48:59 +02:00
Max Kellermann
57a71c157d release v0.20.10 2017-08-24 09:15:43 +02:00
Max Kellermann
cc76aeb7bb python/build/libs: upgrade CURL to 7.55.1 2017-08-24 09:06:15 +02:00
Max Kellermann
811cabf8a9 python/libs: upgrade Opus to 1.2.1 2017-08-24 09:06:15 +02:00
Max Kellermann
bf8d2f93d2 python/build/libs: upgrade FFmpeg to 3.3.3 2017-08-24 09:06:15 +02:00
Max Kellermann
07d8259ad6 python/libs: upgrade Boost to 1.65 2017-08-23 17:46:25 +02:00
Max Kellermann
a00d412008 player/Thread: initialize play_audio_format, fixes assertion
This fixes an assertion failure caused by resuming playback before the
decoder has finished startup.
2017-08-23 17:43:49 +02:00
Matthew Leon
5fb39658f1 OSX mixer 2017-08-21 20:05:50 +01:00
Max Kellermann
b0703b92c3 util/FormatString: pass the allocated buffer to AllocatedString::Donate()
.. and not the stack buffer.  This made the AllocatedString destructor
crash.

Closes 
2017-08-03 00:25:30 +02:00
Max Kellermann
dd9fd3d8a7 tag/Aiff: the FORM chunk size is big-endian
Was broken by commit 8a86460b8f

Closes 
2017-07-31 13:46:09 +02:00
Max Kellermann
cf0c59864f doc/protocol.xml: clarify that idle events do not get lost 2017-07-21 09:51:43 +02:00
Matthew Leon
4c0404c70d Check for MusicBrainz id3v2 tags in ffmpeg.
Addresses .

Previously, the ffmpeg decoder only checked for the "generic"
MusicBrainz metadata keys used in other metadata container formats.
2017-07-20 08:28:14 +02:00
Matthew Leon
573a413ee1 move MusicBrainz id3v2 tags to separate file
We will reuse these tags elsewhere.
2017-07-20 08:26:29 +02:00
Max Kellermann
f633e6ca49 python/build/libs: add LAME
Enable it in the Windows build script, closes .

LAME currently doesn't support Android:

 checking host system type... Invalid configuration `arm-linux-androideabi': system `androideabi' not recognized
2017-07-19 20:53:52 +02:00
Max Kellermann
07b06d76be {android,win32}/build.py: concatenate variables from the command line 2017-07-19 20:53:52 +02:00
Max Kellermann
856fe2da15 python/build/libs: upgrade FFmpeg to 3.3.2, CURL to 7.54.1 2017-06-15 21:53:22 +02:00
Max Kellermann
f82aae65cd doc/user: add more Debian build dependencies 2017-06-15 21:37:07 +02:00
Max Kellermann
3fbd11a104 doc/user: update build dependencies for Debian Jessie 2017-06-15 21:37:05 +02:00
Max Kellermann
58a99f1907 increment version number to 0.20.10 2017-06-15 21:35:23 +02:00
42 changed files with 783 additions and 165 deletions

@@ -30,3 +30,4 @@ The following people have contributed code to MPD:
Jurgen Kramer <gtmkramer@xs4all.nl>
Jean-Francois Dockes <jf@dockes.org>
Yue Wang <yuleopen@gmail.com>
Matthew Leon Grinshpun <ml@matthewleon.com>

@@ -518,6 +518,8 @@ libevent_a_SOURCES = \
# UTF-8 library
libicu_a_SOURCES = \
src/lib/icu/CaseFold.cxx src/lib/icu/CaseFold.hxx \
src/lib/icu/Compare.cxx src/lib/icu/Compare.hxx \
src/lib/icu/Collate.cxx src/lib/icu/Collate.hxx \
src/lib/icu/Converter.cxx src/lib/icu/Converter.hxx
@@ -923,6 +925,7 @@ libtag_a_SOURCES =\
src/tag/ReplayGain.cxx src/tag/ReplayGain.hxx \
src/tag/MixRamp.cxx src/tag/MixRamp.hxx \
src/tag/Generic.cxx src/tag/Generic.hxx \
src/tag/Id3MusicBrainz.cxx src/tag/Id3MusicBrainz.hxx \
src/tag/ApeLoader.cxx src/tag/ApeLoader.hxx \
src/tag/ApeReplayGain.cxx src/tag/ApeReplayGain.hxx \
src/tag/ApeTag.cxx src/tag/ApeTag.hxx
@@ -1489,6 +1492,8 @@ liboutput_plugins_a_SOURCES += \
src/output/plugins/OSXOutputPlugin.cxx \
src/output/plugins/OSXOutputPlugin.hxx
endif
libmixer_plugins_a_SOURCES += \
src/mixer/plugins/OSXMixerPlugin.cxx
if ENABLE_PULSE
liboutput_plugins_a_SOURCES += \

21
NEWS

@@ -1,3 +1,24 @@
ver 0.20.11 (2017/10/18)
* storage
- curl: support Content-Type application/xml
* decoder
- ffmpeg: more reliable song duration
- gme: fix track numbering
* improve random song order when switching songs manually
* fix case insensitive search without libicu
* fix Unicode file names in playlists on Windows
* fix endless loop when accessing malformed file names in ZIP files
ver 0.20.10 (2017/08/24)
* decoder
- ffmpeg: support MusicBrainz ID3v2 tags
* tags
- aiff: fix FORM chunk size endianess (is big-endian)
* mixer
- osx: add a mixer for OSX.
* fix crash when resuming playback before decoder is ready
* fix crash on Windows
ver 0.20.9 (2017/06/04)
* decoder
- ffmpeg: support *.adx

@@ -154,5 +154,9 @@ configure = [
] + configure_args
from build.cmdline import concatenate_cmdline_variables
configure = concatenate_cmdline_variables(configure,
set(('CFLAGS', 'CXXFLAGS', 'CPPFLAGS', 'LDFLAGS', 'LIBS')))
subprocess.check_call(configure, env=toolchain.env)
subprocess.check_call(['/usr/bin/make', '--quiet', '-j12'], env=toolchain.env)

@@ -1,10 +1,10 @@
AC_PREREQ(2.60)
AC_INIT(mpd, 0.20.9, musicpd-dev-team@lists.sourceforge.net)
AC_INIT(mpd, 0.20.11, musicpd-dev-team@lists.sourceforge.net)
VERSION_MAJOR=0
VERSION_MINOR=20
VERSION_REVISION=9
VERSION_REVISION=11
VERSION_EXTRA=0
AC_CONFIG_SRCDIR([src/Main.cxx])
@@ -241,6 +241,7 @@ AC_CHECK_FUNCS(getpwnam_r getpwuid_r)
AC_CHECK_FUNCS(initgroups)
AC_CHECK_FUNCS(fnmatch)
AC_CHECK_FUNCS(strndup)
AC_CHECK_FUNCS(strcasestr)
if test x$host_is_linux = xyes; then
MPD_OPTIONAL_FUNC(eventfd, eventfd, USE_EVENTFD)

@@ -403,6 +403,15 @@
</para>
</listitem>
</itemizedlist>
<para>
Change events accumulate, even while the connection is
not in "idle" mode; no events gets lost while the client
is doing something else with the connection. If an
event had already occurred since the last call, the new
<command>idle</command> command will return immediately.
</para>
<para>
While a client is waiting for <command>idle</command>
results, the server disables timeouts, allowing a client
@@ -436,7 +445,9 @@
<listitem>
<para>
<varname>volume</varname>:
<returnvalue>0-100</returnvalue>
<returnvalue>0-100</returnvalue> or
<returnvalue>-1</returnvalue> if the volume cannot
be determined
</para>
</listitem>
<listitem>

@@ -113,7 +113,7 @@ cd mpd-version</programlisting>
<para>
For example, the following installs a fairly complete list of
build dependencies on Debian Wheezy:
build dependencies on Debian Jessie:
</para>
<programlisting>
@@ -125,19 +125,20 @@ apt-get install g++ \
libmpcdec-dev libwavpack-dev libwildmidi-dev \
libsidplay2-dev libsidutils-dev libresid-builder-dev \
libavcodec-dev libavformat-dev \
libmp3lame-dev \
libmp3lame-dev libtwolame-dev libshine-dev \
libsamplerate0-dev libsoxr-dev \
libbz2-dev libcdio-paranoia-dev libiso9660-dev libmms-dev \
libzzip-dev \
libcurl4-gnutls-dev libyajl-dev libexpat-dev \
libasound2-dev libao-dev libjack-jackd2-dev libopenal-dev \
libpulse-dev libroar-dev libshout3-dev \
libsndio-dev \
libmpdclient-dev \
libnfs-dev libsmbclient-dev \
libupnp-dev \
libavahi-client-dev \
libsqlite3-dev \
libsystemd-daemon-dev libwrap0-dev \
libsystemd-dev libwrap0-dev \
libcppunit-dev xmlto \
libboost-dev \
libicu-dev
@@ -2990,6 +2991,60 @@ run</programlisting>
</informaltable>
</section>
<section id="opus_encoder">
<title><varname>opus</varname></title>
<para>
Encodes into <ulink
url="http://www.opus-codec.org/">Ogg Opus</ulink>.
</para>
<informaltable>
<tgroup cols="2">
<thead>
<row>
<entry>Setting</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>
<varname>bitrate</varname>
</entry>
<entry>
Sets the data rate in bit per second. The special
value "auto" lets <application>libopus</application>
choose a rate (which is the default), and "max" uses
the maximum possible data rate.
</entry>
</row>
<row>
<entry>
<varname>complexity</varname>
</entry>
<entry>
Sets the <ulink
url="https://wiki.xiph.org/OpusFAQ#What_is_the_complexity_of_Opus.3F">Opus
complexity</ulink>.
</entry>
</row>
<row>
<entry>
<varname>signal</varname>
</entry>
<entry>
Sets the Opus signal type. Valid values are "auto"
(the default), "voice" and "music".
</entry>
</row>
</tbody>
</tgroup>
</informaltable>
</section>
<section id="vorbis_encoder">
<title><varname>vorbis</varname></title>

29
python/build/cmdline.py Normal file

@@ -0,0 +1,29 @@
def concatenate_cmdline_variables(src, names):
"""Find duplicate variable declarations on the given source list, and
concatenate the values of those in the 'names' list."""
# the result list being constructed
dest = []
# a map of variable name to destination list index
positions = {}
for item in src:
i = item.find('=')
if i > 0:
# it's a variable
name = item[:i]
if name in names:
# it's a known variable
if name in positions:
# already specified: concatenate instead of
# appending it
dest[positions[name]] += ' ' + item[i + 1:]
continue
else:
# not yet seen: append it and remember the list
# index
positions[name] = len(dest)
dest.append(item)
return dest

@@ -19,8 +19,8 @@ libvorbis = AutotoolsProject(
)
opus = AutotoolsProject(
'http://downloads.xiph.org/releases/opus/opus-1.1.4.tar.gz',
'9122b6b380081dd2665189f97bfd777f04f92dc3ab6698eea1dbb27ad59d8692',
'https://archive.mozilla.org/pub/opus/opus-1.2.1.tar.gz',
'cfafd339ccd9c5ef8d6ab15d7e1a412c054bf4cb4ecbbbcc78c12ef2def70732',
'lib/libopus.a',
['--disable-shared', '--enable-static'],
)
@@ -57,9 +57,20 @@ libmad = AutotoolsProject(
autogen=True,
)
liblame = AutotoolsProject(
'http://downloads.sourceforge.net/project/lame/lame/3.99/lame-3.99.5.tar.gz',
'24346b4158e4af3bd9f2e194bb23eb473c75fb7377011523353196b19b9a23ff',
'lib/libmp3lame.a',
[
'--disable-shared', '--enable-static',
'--disable-gtktest', '--disable-analyzer-hooks',
'--disable-decoder', '--disable-frontend',
],
)
ffmpeg = FfmpegProject(
'http://ffmpeg.org/releases/ffmpeg-3.3.1.tar.xz',
'b702a7fc656ac23e276b8c823a2f646e4e6f6309bb2788435a708e69bea98f2f',
'http://ffmpeg.org/releases/ffmpeg-3.3.3.tar.xz',
'd2a9002cdc6b533b59728827186c044ad02ba64841f1b7cd6c21779875453a1e',
'lib/libavcodec.a',
[
'--disable-shared', '--enable-static',
@@ -82,8 +93,8 @@ ffmpeg = FfmpegProject(
)
curl = AutotoolsProject(
'http://curl.haxx.se/download/curl-7.54.0.tar.lzma',
'cd6aa6039f13e0b06e0a93e1b93754f6dc07f444812bb6c32be75a8f28c4070a',
'http://curl.haxx.se/download/curl-7.55.1.tar.xz',
'3eafca6e84ecb4af5f35795dee84e643d5428287e88c041122bb8dac18676bb7',
'lib/libcurl.a',
[
'--disable-shared', '--enable-static',
@@ -103,7 +114,7 @@ curl = AutotoolsProject(
)
boost = BoostProject(
'http://downloads.sourceforge.net/project/boost/boost/1.64.0/boost_1_64_0.tar.bz2',
'7bcc5caace97baa948931d712ea5f37038dbb1c5d89b43ad4def4ed7cb683332',
'http://downloads.sourceforge.net/project/boost/boost/1.65.0/boost_1_65_0.tar.bz2',
'ea26712742e2fb079c2a566a31f3266973b76e38222b9f88b387e3c8b2f9902c',
'include/boost/version.hpp',
)

@@ -207,13 +207,12 @@ try {
continue;
#ifdef _UNICODE
wchar_t buffer[MAX_PATH];
auto result = MultiByteToWideChar(CP_ACP, 0, s, -1,
buffer, ARRAY_SIZE(buffer));
if (result <= 0)
/* on Windows, playlists always contain UTF-8, because
its "narrow" charset (i.e. CP_ACP) is incapable of
storing all Unicode paths */
const auto path = AllocatedPath::FromUTF8(s);
if (path.IsNull())
continue;
const Path path = Path::FromFS(buffer);
#else
const Path path = Path::FromFS(s);
#endif

@@ -28,13 +28,25 @@
#include "fs/AllocatedPath.hxx"
#include "fs/Traits.hxx"
#include "fs/FileSystem.hxx"
#include "fs/NarrowPath.hxx"
#include "fs/io/FileOutputStream.hxx"
#include "fs/io/BufferedOutputStream.hxx"
#include "util/UriUtil.hxx"
#include <stdexcept>
static void
playlist_print_path(BufferedOutputStream &os, const Path path)
{
#ifdef _UNICODE
/* on Windows, playlists always contain UTF-8, because its
"narrow" charset (i.e. CP_ACP) is incapable of storing all
Unicode paths */
os.Format("%s\n", path.ToUTF8().c_str());
#else
os.Format("%s\n", path.c_str());
#endif
}
void
playlist_print_song(BufferedOutputStream &os, const DetachedSong &song)
{
@@ -44,7 +56,7 @@ playlist_print_song(BufferedOutputStream &os, const DetachedSong &song)
try {
const auto uri_fs = AllocatedPath::FromUTF8Throw(uri_utf8);
os.Format("%s\n", NarrowPath(uri_fs).c_str());
playlist_print_path(os, uri_fs);
} catch (const std::runtime_error &) {
}
}
@@ -63,7 +75,7 @@ playlist_print_uri(BufferedOutputStream &os, const char *uri)
AllocatedPath::FromUTF8Throw(uri);
if (!path.IsNull())
os.Format("%s\n", NarrowPath(path).c_str());
playlist_print_path(os, path);
} catch (const std::runtime_error &) {
}
}

@@ -27,7 +27,7 @@
#include "util/ASCII.hxx"
#include "util/TimeParser.hxx"
#include "util/UriUtil.hxx"
#include "lib/icu/Collate.hxx"
#include "lib/icu/CaseFold.hxx"
#include <stdexcept>
@@ -57,17 +57,10 @@ locate_parse_type(const char *str) noexcept
return tag_name_parse_i(str);
}
static AllocatedString<>
ImportString(const char *p, bool fold_case)
{
return fold_case
? IcuCaseFold(p)
: AllocatedString<>::Duplicate(p);
}
SongFilter::Item::Item(unsigned _tag, const char *_value, bool _fold_case)
:tag(_tag), fold_case(_fold_case),
value(ImportString(_value, _fold_case))
:tag(_tag),
value(AllocatedString<>::Duplicate(_value)),
fold_case(_fold_case ? IcuCompare(value.c_str()) : IcuCompare())
{
}
@@ -87,9 +80,7 @@ SongFilter::Item::StringMatch(const char *s) const noexcept
assert(tag != LOCATE_TAG_MODIFIED_SINCE);
if (fold_case) {
const auto folded = IcuCaseFold(s);
assert(!folded.IsNull());
return StringFind(folded.c_str(), value.c_str()) != nullptr;
return fold_case.IsIn(s);
} else {
return StringIsEqual(s, value.c_str());
}

@@ -20,6 +20,7 @@
#ifndef MPD_SONG_FILTER_HXX
#define MPD_SONG_FILTER_HXX
#include "lib/icu/Compare.hxx"
#include "util/AllocatedString.hxx"
#include "Compiler.h"
@@ -48,10 +49,13 @@ public:
class Item {
uint8_t tag;
bool fold_case;
AllocatedString<> value;
/**
* This value is only set if case folding is enabled.
*/
IcuCompare fold_case;
/**
* For #LOCATE_TAG_MODIFIED_SINCE
*/

@@ -17,6 +17,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "Selection.hxx"
#include "SongFilter.hxx"

@@ -508,6 +508,7 @@ try {
decoder_run_song(dc, song, uri_utf8, path_fs);
} catch (...) {
dc.state = DecoderState::ERROR;
dc.command = DecoderCommand::NONE;
dc.error = std::current_exception();
dc.client_cond.signal();
}

@@ -712,7 +712,9 @@ FfmpegDecode(DecoderClient &client, InputStream &input,
#endif
const SignedSongTime total_time =
FromFfmpegTimeChecked(av_stream.duration, av_stream.time_base);
av_stream.duration != (int64_t)AV_NOPTS_VALUE
? FromFfmpegTimeChecked(av_stream.duration, av_stream.time_base)
: FromFfmpegTimeChecked(format_context.duration, AV_TIME_BASE_Q);
client.Ready(audio_format, input.IsSeekable(), total_time);
@@ -842,6 +844,10 @@ FfmpegScanStream(AVFormatContext &format_context,
tag_handler_invoke_duration(handler, handler_ctx,
FromFfmpegTime(stream.duration,
stream.time_base));
else if (format_context.duration != (int64_t)AV_NOPTS_VALUE)
tag_handler_invoke_duration(handler, handler_ctx,
FromFfmpegTime(format_context.duration,
AV_TIME_BASE_Q));
FfmpegScanMetadata(format_context, audio_stream, handler, handler_ctx);

@@ -24,6 +24,7 @@
#include "FfmpegMetaData.hxx"
#include "tag/TagTable.hxx"
#include "tag/TagHandler.hxx"
#include "tag/Id3MusicBrainz.hxx"
extern "C" {
#include <libavutil/dict.h>
@@ -75,6 +76,11 @@ FfmpegScanDictionary(AVDictionary *dict,
i->name != nullptr; ++i)
FfmpegScanTag(i->type, dict, i->name,
handler, handler_ctx);
for (const struct tag_table *i = musicbrainz_txxx_tags;
i->name != nullptr; ++i)
FfmpegScanTag(i->type, dict, i->name,
handler, handler_ctx);
}
if (handler.pair != nullptr)

@@ -293,13 +293,13 @@ gme_container_scan(Path path_fs)
TagBuilder tag_builder;
auto tail = list.before_begin();
for (unsigned i = 1; i <= num_songs; ++i) {
for (unsigned i = 0; i < num_songs; ++i) {
ScanMusicEmu(emu, i,
add_tag_handler, &tag_builder);
char track_name[64];
snprintf(track_name, sizeof(track_name),
SUBTUNE_PREFIX "%03u.%s", i, subtune_suffix);
SUBTUNE_PREFIX "%03u.%s", i+1, subtune_suffix);
tail = list.emplace_after(tail, track_name,
tag_builder.Commit());
}

102
src/lib/icu/CaseFold.cxx Normal file

@@ -0,0 +1,102 @@
/*
* Copyright 2003-2017 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 "CaseFold.hxx"
#ifdef HAVE_ICU_CASE_FOLD
#include "util/AllocatedString.hxx"
#ifdef HAVE_ICU
#include "Util.hxx"
#include "util/AllocatedArray.hxx"
#include "util/ConstBuffer.hxx"
#include <unicode/ucol.h>
#include <unicode/ustring.h>
#else
#include <algorithm>
#include <ctype.h>
#endif
#ifdef WIN32
#include "Win32.hxx"
#include <windows.h>
#endif
#include <memory>
#include <stdexcept>
#include <assert.h>
#include <string.h>
AllocatedString<>
IcuCaseFold(const char *src) noexcept
try {
#ifdef HAVE_ICU
#if !CLANG_CHECK_VERSION(3,6)
/* disabled on clang due to -Wtautological-pointer-compare */
assert(src != nullptr);
#endif
const auto u = UCharFromUTF8(src);
if (u.IsNull())
return AllocatedString<>::Duplicate(src);
AllocatedArray<UChar> folded(u.size() * 2u);
UErrorCode error_code = U_ZERO_ERROR;
size_t folded_length = u_strFoldCase(folded.begin(), folded.size(),
u.begin(), u.size(),
U_FOLD_CASE_DEFAULT,
&error_code);
if (folded_length == 0 || error_code != U_ZERO_ERROR)
return AllocatedString<>::Duplicate(src);
folded.SetSize(folded_length);
return UCharToUTF8({folded.begin(), folded.size()});
#elif defined(WIN32)
const auto u = MultiByteToWideChar(CP_UTF8, src);
const int size = LCMapStringEx(LOCALE_NAME_INVARIANT,
LCMAP_SORTKEY|LINGUISTIC_IGNORECASE,
u.c_str(), -1, nullptr, 0,
nullptr, nullptr, 0);
if (size <= 0)
return AllocatedString<>::Duplicate(src);
std::unique_ptr<wchar_t[]> buffer(new wchar_t[size]);
if (LCMapStringEx(LOCALE_NAME_INVARIANT,
LCMAP_SORTKEY|LINGUISTIC_IGNORECASE,
u.c_str(), -1, buffer.get(), size,
nullptr, nullptr, 0) <= 0)
return AllocatedString<>::Duplicate(src);
return WideCharToMultiByte(CP_UTF8, buffer.get());
#else
#error not implemented
#endif
} catch (const std::runtime_error &) {
return AllocatedString<>::Duplicate(src);
}
#endif /* HAVE_ICU_CASE_FOLD */

38
src/lib/icu/CaseFold.hxx Normal file

@@ -0,0 +1,38 @@
/*
* Copyright 2003-2017 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_ICU_CASE_FOLD_HXX
#define MPD_ICU_CASE_FOLD_HXX
#include "check.h"
#if defined(HAVE_ICU) || defined(_WIN32)
#define HAVE_ICU_CASE_FOLD
#include "Compiler.h"
template<typename T> class AllocatedString;
gcc_nonnull_all
AllocatedString<char>
IcuCaseFold(const char *src) noexcept;
#endif
#endif

@@ -23,8 +23,6 @@
#ifdef HAVE_ICU
#include "Util.hxx"
#include "util/AllocatedArray.hxx"
#include "util/ConstBuffer.hxx"
#include "util/RuntimeError.hxx"
#include <unicode/ucol.h>
@@ -141,70 +139,3 @@ IcuCollate(const char *a, const char *b) noexcept
return strcoll(a, b);
#endif
}
AllocatedString<>
IcuCaseFold(const char *src)
try {
#ifdef HAVE_ICU
assert(collator != nullptr);
#if !CLANG_CHECK_VERSION(3,6)
/* disabled on clang due to -Wtautological-pointer-compare */
assert(src != nullptr);
#endif
const auto u = UCharFromUTF8(src);
if (u.IsNull())
return AllocatedString<>::Duplicate(src);
AllocatedArray<UChar> folded(u.size() * 2u);
UErrorCode error_code = U_ZERO_ERROR;
size_t folded_length = u_strFoldCase(folded.begin(), folded.size(),
u.begin(), u.size(),
U_FOLD_CASE_DEFAULT,
&error_code);
if (folded_length == 0 || error_code != U_ZERO_ERROR)
return AllocatedString<>::Duplicate(src);
folded.SetSize(folded_length);
return UCharToUTF8({folded.begin(), folded.size()});
#elif defined(WIN32)
const auto u = MultiByteToWideChar(CP_UTF8, src);
const int size = LCMapStringEx(LOCALE_NAME_INVARIANT,
LCMAP_SORTKEY|LINGUISTIC_IGNORECASE,
u.c_str(), -1, nullptr, 0,
nullptr, nullptr, 0);
if (size <= 0)
return AllocatedString<>::Duplicate(src);
std::unique_ptr<wchar_t[]> buffer(new wchar_t[size]);
if (LCMapStringEx(LOCALE_NAME_INVARIANT,
LCMAP_SORTKEY|LINGUISTIC_IGNORECASE,
u.c_str(), -1, buffer.get(), size,
nullptr, nullptr, 0) <= 0)
return AllocatedString<>::Duplicate(src);
return WideCharToMultiByte(CP_UTF8, buffer.get());
#else
size_t size = strlen(src) + 1;
std::unique_ptr<char[]> buffer(new char[size]);
size_t nbytes = strxfrm(buffer.get(), src, size);
if (nbytes >= size) {
/* buffer too small - reallocate and try again */
buffer.reset();
size = nbytes + 1;
buffer.reset(new char[size]);
nbytes = strxfrm(buffer.get(), src, size);
}
assert(nbytes < size);
assert(buffer[nbytes] == 0);
return AllocatedString<>::Donate(buffer.release());
#endif
} catch (const std::runtime_error &) {
return AllocatedString<>::Duplicate(src);
}

@@ -23,8 +23,6 @@
#include "check.h"
#include "Compiler.h"
template<typename T> class AllocatedString;
/**
* Throws #std::runtime_error on error.
*/
@@ -38,8 +36,4 @@ gcc_pure gcc_nonnull_all
int
IcuCollate(const char *a, const char *b) noexcept;
gcc_nonnull_all
AllocatedString<char>
IcuCaseFold(const char *src);
#endif

66
src/lib/icu/Compare.cxx Normal file

@@ -0,0 +1,66 @@
/*
* Copyright 2003-2017 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 "Compare.hxx"
#include "CaseFold.hxx"
#include "util/StringAPI.hxx"
#include <string.h>
#ifdef HAVE_ICU_CASE_FOLD
IcuCompare::IcuCompare(const char *_needle) noexcept
:needle(IcuCaseFold(_needle)) {}
#else
IcuCompare::IcuCompare(const char *_needle) noexcept
:needle(AllocatedString<>::Duplicate(_needle)) {}
#endif
bool
IcuCompare::operator==(const char *haystack) const noexcept
{
#ifdef HAVE_ICU_CASE_FOLD
return StringIsEqual(IcuCaseFold(haystack).c_str(), needle.c_str());
#else
return strcasecmp(haystack, needle.c_str());
#endif
}
bool
IcuCompare::IsIn(const char *haystack) const noexcept
{
#ifdef HAVE_ICU_CASE_FOLD
return StringFind(IcuCaseFold(haystack).c_str(),
needle.c_str()) != nullptr;
#elif defined(HAVE_STRCASESTR)
return strcasestr(haystack, needle.c_str()) != nullptr;
#else
/* poor man's strcasestr() */
for (const size_t length = strlen(needle.c_str());
*haystack != 0; ++haystack)
if (strncasecmp(haystack, needle.c_str(), length) == 0)
return true;
return false;
#endif
}

55
src/lib/icu/Compare.hxx Normal file

@@ -0,0 +1,55 @@
/*
* Copyright 2003-2017 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_ICU_COMPARE_HXX
#define MPD_ICU_COMPARE_HXX
#include "check.h"
#include "Compiler.h"
#include "util/AllocatedString.hxx"
/**
* This class can compare one string ("needle") with lots of other
* strings ("haystacks") efficiently, ignoring case. With some
* configurations, it can prepare a case-folded version of the needle.
*/
class IcuCompare {
AllocatedString<> needle;
public:
IcuCompare():needle(nullptr) {}
explicit IcuCompare(const char *needle) noexcept;
IcuCompare(IcuCompare &&) = default;
IcuCompare &operator=(IcuCompare &&) = default;
gcc_pure
operator bool() const noexcept {
return !needle.IsNull();
}
gcc_pure
bool operator==(const char *haystack) const noexcept;
gcc_pure
bool IsIn(const char *haystack) const noexcept;
};
#endif

@@ -32,6 +32,7 @@ extern const MixerPlugin software_mixer_plugin;
extern const MixerPlugin alsa_mixer_plugin;
extern const MixerPlugin haiku_mixer_plugin;
extern const MixerPlugin oss_mixer_plugin;
extern const MixerPlugin osx_mixer_plugin;
extern const MixerPlugin roar_mixer_plugin;
extern const MixerPlugin pulse_mixer_plugin;
extern const MixerPlugin winmm_mixer_plugin;

@@ -0,0 +1,69 @@
/*
* Copyright 2003-2017 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 "mixer/MixerInternal.hxx"
#include "output/plugins/OSXOutputPlugin.hxx"
class OSXMixer final : public Mixer {
OSXOutput &output;
public:
OSXMixer(OSXOutput &_output, MixerListener &_listener)
:Mixer(osx_mixer_plugin, _listener),
output(_output)
{
}
/* virtual methods from class Mixer */
void Open() override {
}
void Close() override {
}
int GetVolume() override;
void SetVolume(unsigned volume) override;
};
int
OSXMixer::GetVolume()
{
return osx_output_get_volume(output);
}
void
OSXMixer::SetVolume(unsigned new_volume)
{
osx_output_set_volume(output, new_volume);
}
static Mixer *
osx_mixer_init(gcc_unused EventLoop &event_loop, AudioOutput &ao,
MixerListener &listener,
gcc_unused const ConfigBlock &block)
{
OSXOutput &osxo = (OSXOutput &)ao;
return new OSXMixer(osxo, listener);
}
const MixerPlugin osx_mixer_plugin = {
osx_mixer_init,
true,
};

@@ -20,6 +20,7 @@
#include "config.h"
#include "OSXOutputPlugin.hxx"
#include "../OutputAPI.hxx"
#include "mixer/MixerList.hxx"
#include "util/ScopeExit.hxx"
#include "util/RuntimeError.hxx"
#include "util/Domain.hxx"
@@ -53,6 +54,9 @@ struct OSXOutput {
boost::lockfree::spsc_queue<uint8_t> *ring_buffer;
OSXOutput(const ConfigBlock &block);
int GetVolume();
void SetVolume(unsigned new_volume);
};
static constexpr Domain osx_output_domain("osx_output");
@@ -103,6 +107,44 @@ OSXOutput::OSXOutput(const ConfigBlock &block)
sync_sample_rate = block.GetBlockValue("sync_sample_rate", false);
}
int
OSXOutput::GetVolume()
{
AudioUnitParameterValue dvolume;
char errormsg[1024];
OSStatus status = AudioUnitGetParameter(au, kHALOutputParam_Volume,
kAudioUnitScope_Global, 0, &dvolume);
if (status != noErr) {
osx_os_status_to_cstring(status, errormsg, sizeof(errormsg));
throw FormatRuntimeError("unable to get volume: %s", errormsg);
}
/* see the explanation in SetVolume, below */
return static_cast<int>(dvolume * dvolume * 100.0);
}
void
OSXOutput::SetVolume(unsigned new_volume) {
char errormsg[1024];
/* The scaling below makes shifts in volume greater at the lower end
* of the scale. This mimics the "feel" of physical volume levers. This is
* generally what users of audio software expect.
*/
AudioUnitParameterValue scaled_volume =
sqrt(static_cast<AudioUnitParameterValue>(new_volume) / 100.0);
OSStatus status = AudioUnitSetParameter(au, kHALOutputParam_Volume,
kAudioUnitScope_Global, 0, scaled_volume, 0);
if (status != noErr) {
osx_os_status_to_cstring(status, errormsg, sizeof(errormsg));
throw FormatRuntimeError( "unable to set new volume %u: %s",
new_volume, errormsg);
}
}
static AudioOutput *
osx_output_init(const ConfigBlock &block)
{
@@ -678,6 +720,18 @@ osx_output_delay(AudioOutput *ao) noexcept
: std::chrono::milliseconds(25);
}
int
osx_output_get_volume(OSXOutput &output)
{
return output.GetVolume();
}
void
osx_output_set_volume(OSXOutput &output, unsigned new_volume)
{
return output.SetVolume(new_volume);
}
const struct AudioOutputPlugin osx_output_plugin = {
"osx",
osx_output_test_default_device,
@@ -693,5 +747,6 @@ const struct AudioOutputPlugin osx_output_plugin = {
nullptr,
nullptr,
nullptr,
nullptr,
&osx_mixer_plugin,
};

@@ -20,6 +20,14 @@
#ifndef MPD_OSX_OUTPUT_PLUGIN_HXX
#define MPD_OSX_OUTPUT_PLUGIN_HXX
struct OSXOutput;
extern const struct AudioOutputPlugin osx_output_plugin;
int
osx_output_get_volume(OSXOutput &output);
void
osx_output_set_volume(OSXOutput &output, unsigned new_volume);
#endif

@@ -50,7 +50,7 @@ class SndioOutput {
AudioOutput base;
const char *const device;
const unsigned buffer_time; /* in ms */
struct sio_hdl *sio_hdl;
struct sio_hdl *hdl;
public:
SndioOutput(const ConfigBlock &block);
@@ -80,16 +80,14 @@ SndioOutput::Create(const ConfigBlock &block)
static bool
sndio_test_default_device()
{
struct sio_hdl *sio_hdl;
sio_hdl = sio_open(SIO_DEVANY, SIO_PLAY, 0);
if (!sio_hdl) {
auto *hdl = sio_open(SIO_DEVANY, SIO_PLAY, 0);
if (!hdl) {
FormatError(sndio_output_domain,
"Error opening default sndio device");
"Error opening default sndio device");
return false;
}
sio_close(sio_hdl);
sio_close(hdl);
return true;
}
@@ -99,8 +97,8 @@ SndioOutput::Open(AudioFormat &audio_format)
struct sio_par par;
unsigned bits, rate, chans;
sio_hdl = sio_open(device, SIO_PLAY, 0);
if (!sio_hdl)
hdl = sio_open(device, SIO_PLAY, 0);
if (!hdl)
throw std::runtime_error("Failed to open default sndio device");
switch (audio_format.format) {
@@ -130,9 +128,9 @@ SndioOutput::Open(AudioFormat &audio_format)
par.le = SIO_LE_NATIVE;
par.appbufsz = rate * buffer_time / 1000;
if (!sio_setpar(sio_hdl, &par) ||
!sio_getpar(sio_hdl, &par)) {
sio_close(sio_hdl);
if (!sio_setpar(hdl, &par) ||
!sio_getpar(hdl, &par)) {
sio_close(hdl);
throw std::runtime_error("Failed to set/get audio params");
}
@@ -142,12 +140,12 @@ SndioOutput::Open(AudioFormat &audio_format)
par.pchan != chans ||
par.sig != 1 ||
par.le != SIO_LE_NATIVE) {
sio_close(sio_hdl);
sio_close(hdl);
throw std::runtime_error("Requested audio params cannot be satisfied");
}
if (!sio_start(sio_hdl)) {
sio_close(sio_hdl);
if (!sio_start(hdl)) {
sio_close(hdl);
throw std::runtime_error("Failed to start audio device");
}
}
@@ -155,7 +153,7 @@ SndioOutput::Open(AudioFormat &audio_format)
void
SndioOutput::Close()
{
sio_close(sio_hdl);
sio_close(hdl);
}
size_t
@@ -163,8 +161,8 @@ SndioOutput::Play(const void *chunk, size_t size)
{
size_t n;
n = sio_write(sio_hdl, chunk, size);
if (n == 0 && sio_eof(sio_hdl) != 0)
n = sio_write(hdl, chunk, size);
if (n == 0 && sio_eof(hdl) != 0)
throw std::runtime_error("sndio write failed");
return n;
}

@@ -135,7 +135,7 @@ class Player {
/**
* The current audio format for the audio outputs.
*/
AudioFormat play_audio_format;
AudioFormat play_audio_format = AudioFormat::Undefined();
/**
* The time stamp of the chunk most recently sent to the

@@ -310,8 +310,7 @@ playlist::SetRandom(PlayerControl &pc, bool status)
playlist is played after that */
unsigned current_order =
queue.PositionToOrder(current_position);
queue.MoveOrder(current_order, 0);
current = 0;
current = queue.MoveOrder(current_order, 0);
} else
current = -1;
} else

@@ -356,6 +356,18 @@ public:
}
void SetConsume(bool new_value);
private:
/**
* Prepare a manual song change: move the given song to the
* current playback order. This is done to avoid skipping
* upcoming songs in the order list. The newly selected song
* shall be inserted in the order list, and the rest shall be
* played after that as previously planned.
*
* @return the new order number of the given song
*/
unsigned MoveOrderToCurrent(unsigned old_order);
};
#endif

@@ -56,6 +56,30 @@ playlist::Stop(PlayerControl &pc)
}
}
unsigned
playlist::MoveOrderToCurrent(unsigned old_order)
{
if (!queue.random)
/* no-op because there is no order list */
return old_order;
if (playing) {
/* already playing: move the specified song after the
current one (because the current one has already
been playing and shall not be played again) */
return queue.MoveOrderAfter(old_order, current);
} else if (current >= 0) {
/* not playing: move the specified song before the
current one, so it will be played eventually */
return queue.MoveOrderBefore(old_order, current);
} else {
/* not playing anything: move the specified song to
the front */
queue.SwapOrders(old_order, 0);
return 0;
}
}
void
playlist::PlayPosition(PlayerControl &pc, int song)
{
@@ -90,13 +114,7 @@ playlist::PlayPosition(PlayerControl &pc, int song)
number, because random mode is enabled */
i = queue.PositionToOrder(song);
if (!playing)
current = 0;
/* swap the new song with the previous "current" one,
so playback continues as planned */
queue.SwapOrders(i, current);
i = current;
i = MoveOrderToCurrent(i);
}
stop_on_error = false;
@@ -205,6 +223,8 @@ playlist::SeekSongOrder(PlayerControl &pc, unsigned i, SongTime seek_time)
/* seeking is not within the current song - prepare
song change */
i = MoveOrderToCurrent(i);
playing = true;
current = i;

@@ -195,7 +195,7 @@ Queue::MoveRange(unsigned start, unsigned end, unsigned to) noexcept
}
}
void
unsigned
Queue::MoveOrder(unsigned from_order, unsigned to_order) noexcept
{
assert(from_order < length);
@@ -212,6 +212,25 @@ Queue::MoveOrder(unsigned from_order, unsigned to_order) noexcept
}
order[to_order] = from_position;
return to_order;
}
unsigned
Queue::MoveOrderBefore(unsigned from_order, unsigned to_order) noexcept
{
/* if "from_order" comes before "to_order", then the new
position is "to_order-1"; otherwise the "to_order" song is
moved one ahead */
return MoveOrder(from_order, to_order - (from_order < to_order));
}
unsigned
Queue::MoveOrderAfter(unsigned from_order, unsigned to_order) noexcept
{
/* if "from_order" comes after "to_order", then the new
position is "to_order+1"; otherwise the "to_order" song is
moved one back */
return MoveOrder(from_order, to_order + (from_order > to_order));
}
void

@@ -284,8 +284,28 @@ struct Queue {
/**
* Moves a song to a new position in the "order" list.
*
* @return to_order
*/
void MoveOrder(unsigned from_order, unsigned to_order) noexcept;
unsigned MoveOrder(unsigned from_order, unsigned to_order) noexcept;
/**
* Moves a song to a new position in the "order" list before
* the given one.
*
* @return the new order number of the given "from" song
*/
unsigned MoveOrderBefore(unsigned from_order,
unsigned to_order) noexcept;
/**
* Moves a song to a new position in the "order" list after
* the given one.
*
* @return the new order number of the given "from" song
*/
unsigned MoveOrderAfter(unsigned from_order,
unsigned to_order) noexcept;
/**
* Moves a song to a new position.

@@ -247,6 +247,22 @@ ParseU64(const char *s, size_t length)
return ParseU64(std::string(s, length).c_str());
}
gcc_pure
static bool
IsXmlContentType(const char *content_type) noexcept
{
return StringStartsWith(content_type, "text/xml") ||
StringStartsWith(content_type, "application/xml");
}
gcc_pure
static bool
IsXmlContentType(const std::multimap<std::string, std::string> &headers) noexcept
{
auto i = headers.find("content-type");
return i != headers.end() && IsXmlContentType(i->second.c_str());
}
/**
* A WebDAV PROPFIND request. Each "response" element will be passed
* to OnDavResponse() (to be implemented by a derived class).
@@ -308,9 +324,7 @@ private:
throw FormatRuntimeError("Status %d from WebDAV server; expected \"207 Multi-Status\"",
status);
auto i = headers.find("content-type");
if (i == headers.end() ||
strncmp(i->second.c_str(), "text/xml", 8) != 0)
if (!IsXmlContentType(headers))
throw std::runtime_error("Unexpected Content-Type from WebDAV server");
}

@@ -49,7 +49,7 @@ aiff_seek_id3(InputStream &is)
aiff_header header;
is.ReadFull(&header, sizeof(header));
if (memcmp(header.id, "FORM", 4) != 0 ||
(is.KnownSize() && FromLE32(header.size) > is.GetSize()) ||
(is.KnownSize() && FromBE32(header.size) > is.GetSize()) ||
(memcmp(header.format, "AIFF", 4) != 0 &&
memcmp(header.format, "AIFC", 4) != 0))
throw std::runtime_error("Not an AIFF file");

@@ -0,0 +1,34 @@
/*
* Copyright 2003-2017 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 "Id3MusicBrainz.hxx"
#include "TagTable.hxx"
#include "TagType.h"
const struct tag_table musicbrainz_txxx_tags[] = {
{ "ALBUMARTISTSORT", TAG_ALBUM_ARTIST_SORT },
{ "MusicBrainz Artist Id", TAG_MUSICBRAINZ_ARTISTID },
{ "MusicBrainz Album Id", TAG_MUSICBRAINZ_ALBUMID },
{ "MusicBrainz Album Artist Id",
TAG_MUSICBRAINZ_ALBUMARTISTID },
{ "MusicBrainz Track Id", TAG_MUSICBRAINZ_TRACKID },
{ "MusicBrainz Release Track Id",
TAG_MUSICBRAINZ_RELEASETRACKID },
{ nullptr, TAG_NUM_OF_ITEM_TYPES }
};

@@ -0,0 +1,25 @@
/*
* Copyright 2003-2017 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_TAG_ID3MUSICBRAINZ_HXX
#define MPD_TAG_ID3MUSICBRAINZ_HXX
extern const struct tag_table musicbrainz_txxx_tags[];
#endif

@@ -20,6 +20,7 @@
#include "config.h"
#include "TagId3.hxx"
#include "Id3Load.hxx"
#include "Id3MusicBrainz.hxx"
#include "TagHandler.hxx"
#include "TagTable.hxx"
#include "TagBuilder.hxx"
@@ -205,19 +206,8 @@ gcc_pure
static TagType
tag_id3_parse_txxx_name(const char *name) noexcept
{
static constexpr struct tag_table txxx_tags[] = {
{ "ALBUMARTISTSORT", TAG_ALBUM_ARTIST_SORT },
{ "MusicBrainz Artist Id", TAG_MUSICBRAINZ_ARTISTID },
{ "MusicBrainz Album Id", TAG_MUSICBRAINZ_ALBUMID },
{ "MusicBrainz Album Artist Id",
TAG_MUSICBRAINZ_ALBUMARTISTID },
{ "MusicBrainz Track Id", TAG_MUSICBRAINZ_TRACKID },
{ "MusicBrainz Release Track Id",
TAG_MUSICBRAINZ_RELEASETRACKID },
{ nullptr, TAG_NUM_OF_ITEM_TYPES }
};
return tag_table_lookup(txxx_tags, name);
return tag_table_lookup(musicbrainz_txxx_tags, name);
}
/**

@@ -57,7 +57,7 @@ FormatStringV(const char *fmt, va_list args)
const size_t length = strlen(buffer);
char *p = new char[length + 1];
memcpy(p, buffer, length + 1);
return AllocatedString<>::Donate(buffer);
return AllocatedString<>::Donate(p);
#endif
}

@@ -51,6 +51,11 @@ class CrossGccToolchain:
self.strip = os.path.join(toolchain_bin, arch + '-strip')
common_flags = ''
if not x64:
# enable SSE support which is required for LAME
common_flags += ' -march=pentium3'
self.cflags = '-O2 -g ' + common_flags
self.cxxflags = '-O2 -g ' + common_flags
self.cppflags = '-isystem ' + os.path.join(install_prefix, 'include')
@@ -76,6 +81,7 @@ thirdparty_libs = [
flac,
zlib,
libid3tag,
liblame,
ffmpeg,
curl,
boost,
@@ -112,5 +118,9 @@ configure = [
] + configure_args
from build.cmdline import concatenate_cmdline_variables
configure = concatenate_cmdline_variables(configure,
set(('CFLAGS', 'CXXFLAGS', 'CPPFLAGS', 'LDFLAGS', 'LIBS')))
subprocess.check_call(configure, env=toolchain.env)
subprocess.check_call(['/usr/bin/make', '--quiet', '-j12'], env=toolchain.env)