Compare commits

...

43 Commits

Author SHA1 Message Date
Max Kellermann
5ccfcffcc1 release v0.21.22 2020-04-02 17:48:56 +02:00
Max Kellermann
afe2aaa5f6 fs/io/GzipOutputStream: increase buffer size to 16 kB
Reduce I/O overhead.
2020-04-02 17:17:58 +02:00
Max Kellermann
9b11caa0e6 fs/io/BufferedReader: larger default buffer (4 kB -> 16 kB)
Reduce I/O overhead.
2020-04-02 17:17:27 +02:00
Max Kellermann
a689b881d3 test/meson.build: work around linker failure due to statically linked CURL 2020-04-02 17:16:05 +02:00
Max Kellermann
e94c436264 src/event/meson.build: depend in libnet.a
The event library uses various libnet.a classes,
e.g. SocketDescriptor.
2020-04-02 17:16:05 +02:00
Max Kellermann
bad829509e test/ShutdownHandler: add inline to work around Windows linker problems 2020-04-02 17:16:05 +02:00
Max Kellermann
9c66b0414a test/*: fix Windows build using class FromNarrowPath 2020-04-02 17:16:05 +02:00
Max Kellermann
4d453a8313 fs/NarrowPath: add class FromNarrowPath
Move code from ParseCommandLine().
2020-04-02 17:15:34 +02:00
Max Kellermann
61d7b436a2 fs/NarrowPath: un-inline Windows constructor 2020-04-02 16:27:44 +02:00
Max Kellermann
cdddaf21b0 db/simple/Directory: optimize GetName() using the parent's path
This method gets called a lot during MPD startup, via FindChild() and
directory_load_subdir(), so this is worth optimizing at the expense of
code readability.

This speeds up MPD startup by 10%.
2020-04-02 16:12:08 +02:00
Max Kellermann
b267ba5f0a tag/Pool: enlarge hash table
This consumes more memory (plus 48 kB on 32 bit systems), but reduces
the number of hash collisions, speeding up MPD startup with large
databases.
2020-04-02 15:45:35 +02:00
Max Kellermann
8270043053 Revert "decoder/ffmpeg: copy the AVPacket in ffmpeg_send_packet()"
This reverts commit eb192137d6.

This is no longer necessary because we require FFmpeg 3.1 or newer
since MPD 0.21.2.

This fixes a deprecation warning because the implicit AVPacket copy
constructor copies the deprecated attribute `convergence_duration`.
2020-04-01 17:30:28 +02:00
Max Kellermann
c00ce42bca python/build/libs.py: update libmpdclient to 2.18 2020-04-01 17:17:30 +02:00
Max Kellermann
3852ddbbce .travis.yml: install more packages on OSX
Enable lots of plugins for better CI coverage.
2020-04-01 16:37:45 +02:00
Max Kellermann
672bc3ab67 time/Convert: fix GetTimeZoneOffset() on Windows
Was using the wrong parameter.
2020-04-01 16:21:29 +02:00
Max Kellermann
62229f14da test/time: add test for LocalTime(), GmTime() 2020-04-01 16:21:29 +02:00
Max Kellermann
a4c925c8d7 test/meson.build: move TestTime to time/ 2020-04-01 16:12:01 +02:00
Max Kellermann
60610e90b1 test/net/TestIPv[46]Address: fix Windows build errors 2020-04-01 16:09:24 +02:00
Max Kellermann
90184e0ce7 python/build/libs.py: update CURL to 7.69.1 2020-04-01 15:49:16 +02:00
Max Kellermann
9c3e1d450a fs/io/GunzipReader: increase buffer size to 64 kB
Reduces I/O overhead while reading a compressed database file.
2020-03-31 15:07:39 +02:00
Thomas Guillem
60f2116202 android/Settings: remove the EXPIRIMENTAL text
Using MPD from Android since quite some times now. I consider it very stable
now.
2020-03-26 17:31:31 +01:00
Thomas Guillem
4ff2532330 android: add TV support
TODO: Not sure the app could be accepted on the play store without a valid
banner.
2020-03-26 17:31:20 +01:00
Thomas Guillem
9c15760c4d android/Main: handle API26 NotificationChannel
This seems to be required on recent Android versions (tested with Android 10).
This is also required for android TV services (cf. next commit).

This is done using Java reflection so that the project doesn't depend on
android compat libs.
2020-03-26 17:30:55 +01:00
Max Kellermann
e1c43ec65f Merge branch 'ucl' of git://github.com/neheb/MPD into v0.21.x 2020-03-26 17:28:21 +01:00
Thomas Guillem
4dd10894ba lib/curl/Request: fix Exception "error" on Android
Apparently, it's not possible to change CURLOPT_NETRC on Android.
2020-03-26 17:26:14 +01:00
Rosen Penev
608d7ec1e7 [clang-tidy] change integer prefixes to uppercase
Found with readability-uppercase-literal-suffix

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2020-03-26 17:25:20 +01:00
Max Kellermann
8474599ed6 lib/curl/Easy: add method Unpause() 2020-03-26 17:22:10 +01:00
Max Kellermann
ab39f64fc0 lib/curl/Easy: add setter functions 2020-03-26 17:21:30 +01:00
Max Kellermann
185fbca282 lib/curl/Global: make ReadInfo() private 2020-03-26 17:20:10 +01:00
Max Kellermann
6e3b2fd844 lib/curl/Global: remove redundant API docs 2020-03-26 17:20:06 +01:00
Max Kellermann
dab39dc778 lib/curl: fix coding style 2020-03-26 17:19:48 +01:00
Max Kellermann
8cd5e79fbd event/*, ...: make GetEventLoop() const 2020-03-26 17:19:13 +01:00
Max Kellermann
1de3ac6c78 lib/curl/Init: add const overloads 2020-03-26 17:18:27 +01:00
Max Kellermann
abe06a5fa6 lib/curl/Init: add noexcept 2020-03-26 17:18:23 +01:00
Rosen Penev
85c27840a3 treewide: use boost::lround when std::round is unavailable
This is the case with uClibc-ng currently.

Signed-off-by: Rosen Penev <rosenp@gmail.com>
(cherry picked from commit 769cd0ee9f0cf8ceb026aa751b5d4a390bb5dbdc)
(changed define to match master)
2020-03-25 18:54:15 -07:00
Thomas Guillem
81c16273c5 output/sles: use the AndroidMixerPlugin 2020-03-25 20:08:53 +01:00
Thomas Guillem
801ae86b5d mixer: add AndroidMixerPlugin 2020-03-25 20:07:26 +01:00
Thomas Guillem
5619fd0bba android: Context: add GetAudioManager 2020-03-25 20:07:25 +01:00
Thomas Guillem
200258c7c3 android: add AudioManager 2020-03-25 20:07:24 +01:00
Max Kellermann
5418bb49fb android/Context: add noexcept 2020-03-25 20:07:18 +01:00
Max Kellermann
3449c14ff5 java/Object: rename class Object to GlobalObject 2020-03-25 20:07:15 +01:00
kowalcj0
36a89e8fe7 Support RSS feeds with application/xml MIME-type 2020-03-22 10:49:38 +01:00
Max Kellermann
8e6a21a9c2 increment version number to 0.21.22 2020-03-22 10:48:53 +01:00
99 changed files with 851 additions and 236 deletions
.travis.ymlNEWS
android
doc
meson.build
python/build
src
test

@@ -126,6 +126,22 @@ jobs:
packages:
- ccache
- meson
- icu4c
- ffmpeg
- libnfs
- yajl
- libupnp
- libid3tag
- chromaprint
- libsamplerate
- libsoxr
- libzzip
- flac
- opus
- libvorbis
- faad2
- wavpack
- libmpdclient
update: true
env:
- MATRIX_EVAL="export PATH=/usr/local/opt/ccache/libexec:$PATH HOMEBREW_NO_ANALYTICS=1"

15
NEWS

@@ -1,3 +1,18 @@
ver 0.21.22 (2020/04/02)
* database
- simple: optimize startup
* input
- curl: fix streaming errors on Android
* playlist
- rss: support MIME type application/xml
* mixer
- android: new mixer plugin for "sles" output
* Android
- TV support
* Windows
- fix time zone offset check
* fix build failures with uClibc-ng
ver 0.21.21 (2020/03/19)
* configuration
- fix bug in "metadata_to_use" setting

@@ -2,18 +2,25 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.musicpd"
android:installLocation="auto"
android:versionCode="44"
android:versionName="0.21.21">
android:versionCode="45"
android:versionName="0.21.22">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28"/>
<uses-feature android:name="android.software.leanback"
android:required="false" />
<uses-feature android:name="android.hardware.touchscreen"
android:required="false" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<application android:allowBackup="true"
android:icon="@drawable/icon"
android:banner="@drawable/icon"
android:label="@string/app_name">
<activity android:name=".Settings"
android:label="@string/app_name">
@@ -22,6 +29,14 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".Settings"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name=".Receiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />

@@ -21,6 +21,7 @@ package org.musicpd;
import android.annotation.TargetApi;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.ComponentName;
@@ -35,6 +36,9 @@ import android.os.RemoteException;
import android.util.Log;
import android.widget.RemoteViews;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class Main extends Service implements Runnable {
private static final String TAG = "Main";
private static final String REMOTE_ERROR = "MPD process was killed";
@@ -156,11 +160,36 @@ public class Main extends Service implements Runnable {
sendMessage(MSG_SEND_STATUS, mStatus, 0, mError);
}
private Notification.Builder createNotificationBuilderWithChannel() {
final NotificationManager notificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
if (notificationManager == null)
return null;
final String id = "org.musicpd";
final String name = "MPD service";
final int importance = 3; /* NotificationManager.IMPORTANCE_DEFAULT */
try {
Class<?> ncClass = Class.forName("android.app.NotificationChannel");
Constructor<?> ncCtor = ncClass.getConstructor(String.class, CharSequence.class, int.class);
Object nc = ncCtor.newInstance(id, name, importance);
Method nmCreateNotificationChannelMethod =
NotificationManager.class.getMethod("createNotificationChannel", ncClass);
nmCreateNotificationChannelMethod.invoke(notificationManager, nc);
Constructor nbCtor = Notification.Builder.class.getConstructor(Context.class, String.class);
return (Notification.Builder) nbCtor.newInstance(this, id);
} catch (Exception e)
{
Log.e(TAG, "error creating the NotificationChannel", e);
return null;
}
}
private void start() {
if (mThread != null)
return;
mThread = new Thread(this);
mThread.start();
final Intent mainIntent = new Intent(this, Settings.class);
mainIntent.setAction("android.intent.action.MAIN");
@@ -168,13 +197,25 @@ public class Main extends Service implements Runnable {
final PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
mainIntent, PendingIntent.FLAG_CANCEL_CURRENT);
Notification notification = new Notification.Builder(this)
.setContentTitle(getText(R.string.notification_title_mpd_running))
Notification.Builder nBuilder;
if (Build.VERSION.SDK_INT >= 26 /* Build.VERSION_CODES.O */)
{
nBuilder = createNotificationBuilderWithChannel();
if (nBuilder == null)
return;
}
else
nBuilder = new Notification.Builder(this);
Notification notification = nBuilder.setContentTitle(getText(R.string.notification_title_mpd_running))
.setContentText(getText(R.string.notification_text_mpd_running))
.setSmallIcon(R.drawable.notification_icon)
.setContentIntent(contentIntent)
.build();
mThread = new Thread(this);
mThread.start();
startForeground(R.string.notification_title_mpd_running, notification);
startService(new Intent(this, Main.class));
}

@@ -105,12 +105,13 @@ public class Settings extends Activity {
else
mRunButton.setChecked(false);
mFirstRun = true;
mTextStatus.setText("");
break;
case MSG_STARTED:
Log.d(TAG, "onStarted");
mRunButton.setChecked(true);
mFirstRun = true;
mTextStatus.setText("CAUTION: this version is EXPERIMENTAL!"); // XXX
mTextStatus.setText("MPD service started");
break;
case MSG_LOG:
if (mLogListArray.size() > MAX_LOGS)

@@ -38,7 +38,7 @@ author = 'Max Kellermann'
# built documents.
#
# The short X.Y version.
version = '0.21.21'
version = '0.21.22'
# The full version, including alpha/beta/rc tags.
release = version

@@ -1,7 +1,7 @@
project(
'mpd',
['c', 'cpp'],
version: '0.21.21',
version: '0.21.22',
meson_version: '>= 0.49.0',
default_options: [
'c_std=c99',
@@ -290,6 +290,7 @@ if not is_android
else
sources += [
'src/android/Context.cxx',
'src/android/AudioManager.cxx',
'src/android/Environment.cxx',
'src/android/LogListener.cxx',
]
@@ -311,6 +312,7 @@ subdir('src/util')
subdir('src/time')
subdir('src/system')
subdir('src/thread')
subdir('src/net')
subdir('src/event')
subdir('src/lib/dbus')
@@ -335,7 +337,6 @@ subdir('src/lib/yajl')
subdir('src/fs')
subdir('src/config')
subdir('src/net')
subdir('src/tag')
subdir('src/pcm')
subdir('src/neighbor')

@@ -9,8 +9,8 @@ from build.ffmpeg import FfmpegProject
from build.boost import BoostProject
libmpdclient = MesonProject(
'https://www.musicpd.org/download/libmpdclient/2/libmpdclient-2.17.tar.xz',
'ee9b8f1c7e95b65c8f18a354daf7b16bfcd455fc52a0f3b5abe402316bce3559',
'https://www.musicpd.org/download/libmpdclient/2/libmpdclient-2.18.tar.xz',
'4cb01e1f567e0169aca94875fb6e1200e7f5ce35b63a4df768ec1591fb1081fa',
'lib/libmpdclient.a',
)
@@ -341,8 +341,8 @@ ffmpeg = FfmpegProject(
)
curl = AutotoolsProject(
'http://curl.haxx.se/download/curl-7.68.0.tar.xz',
'b724240722276a27f6e770b952121a3afd097129d8c9fe18e6272dc34192035a',
'http://curl.haxx.se/download/curl-7.69.1.tar.xz',
'03c7d5e6697f7b7e40ada1b2256e565a555657398e6c1fcfa4cb251ccd819d4f',
'lib/libcurl.a',
[
'--disable-shared', '--enable-static',

@@ -33,11 +33,11 @@
#include "playlist/PlaylistRegistry.hxx"
#include "playlist/PlaylistPlugin.hxx"
#include "fs/AllocatedPath.hxx"
#include "fs/NarrowPath.hxx"
#include "fs/Traits.hxx"
#include "fs/FileSystem.hxx"
#include "fs/StandardDirectory.hxx"
#include "system/Error.hxx"
#include "util/Macros.hxx"
#include "util/RuntimeError.hxx"
#include "util/Domain.hxx"
#include "util/OptionDef.hxx"
@@ -380,17 +380,7 @@ ParseCommandLine(int argc, char **argv, struct options &options,
if (config_file != nullptr) {
/* use specified configuration file */
#ifdef _UNICODE
wchar_t buffer[MAX_PATH];
auto result = MultiByteToWideChar(CP_ACP, 0, config_file, -1,
buffer, ARRAY_SIZE(buffer));
if (result <= 0)
throw MakeLastError("MultiByteToWideChar() failed");
ReadConfigFile(config, Path::FromFS(buffer));
#else
ReadConfigFile(config, Path::FromFS(config_file));
#endif
ReadConfigFile(config, FromNarrowPath(config_file));
return;
}

@@ -29,9 +29,9 @@
#include "system/Clock.hxx"
#include "Log.hxx"
#include "time/ChronoUtil.hxx"
#include "util/Math.hxx"
#include <chrono>
#include <cmath>
#ifndef _WIN32
/**
@@ -121,7 +121,7 @@ stats_print(Response &r, const Partition &partition)
#else
(unsigned)std::chrono::duration_cast<std::chrono::seconds>(std::chrono::steady_clock::now() - start_time).count(),
#endif
std::lround(partition.pc.GetTotalPlayTime().count()));
lround(partition.pc.GetTotalPlayTime().count()));
#ifdef ENABLE_DATABASE
const Database *db = partition.instance.GetDatabase();

@@ -0,0 +1,56 @@
/*
* Copyright 2003-2020 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 "AudioManager.hxx"
#include "java/Class.hxx"
#include "java/Exception.hxx"
#include "java/File.hxx"
#define STREAM_MUSIC 3
AudioManager::AudioManager(JNIEnv *env, jobject obj) noexcept
: Java::GlobalObject(env, obj)
{
Java::Class cls(env, env->GetObjectClass(Get()));
jmethodID method = env->GetMethodID(cls, "getStreamMaxVolume", "(I)I");
assert(method);
maxVolume = env->CallIntMethod(Get(), method, STREAM_MUSIC);
getStreamVolumeMethod = env->GetMethodID(cls, "getStreamVolume", "(I)I");
assert(getStreamVolumeMethod);
setStreamVolumeMethod = env->GetMethodID(cls, "setStreamVolume", "(III)V");
assert(setStreamVolumeMethod);
}
int
AudioManager::GetVolume(JNIEnv *env)
{
if (maxVolume == 0)
return 0;
return env->CallIntMethod(Get(), getStreamVolumeMethod, STREAM_MUSIC);
}
void
AudioManager::SetVolume(JNIEnv *env, int volume)
{
if (maxVolume == 0)
return;
env->CallVoidMethod(Get(), setStreamVolumeMethod, STREAM_MUSIC, volume, 0);
}

@@ -0,0 +1,42 @@
/*
* Copyright 2003-2020 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_ANDROID_AUDIO_MANAGER_HXX
#define MPD_ANDROID_AUDIO_MANAGER_HXX
#include "java/Object.hxx"
class AudioManager : public Java::GlobalObject {
int maxVolume;
jmethodID getStreamVolumeMethod;
jmethodID setStreamVolumeMethod;
public:
AudioManager(JNIEnv *env, jobject obj) noexcept;
AudioManager(std::nullptr_t) noexcept { maxVolume = 0; }
~AudioManager() noexcept {}
int GetMaxVolume() { return maxVolume; }
int GetVolume(JNIEnv *env);
void SetVolume(JNIEnv *env, int);
};
#endif

@@ -1,5 +1,5 @@
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2019 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -20,10 +20,13 @@
#include "Context.hxx"
#include "java/Class.hxx"
#include "java/File.hxx"
#include "java/String.hxx"
#include "fs/AllocatedPath.hxx"
#include "AudioManager.hxx"
AllocatedPath
Context::GetCacheDir(JNIEnv *env) const
Context::GetCacheDir(JNIEnv *env) const noexcept
{
assert(env != nullptr);
@@ -40,3 +43,21 @@ Context::GetCacheDir(JNIEnv *env) const
return Java::File::ToAbsolutePath(env, file);
}
AudioManager *
Context::GetAudioManager(JNIEnv *env) noexcept
{
assert(env != nullptr);
Java::Class cls(env, env->GetObjectClass(Get()));
jmethodID method = env->GetMethodID(cls, "getSystemService",
"(Ljava/lang/String;)Ljava/lang/Object;");
assert(method);
Java::String name(env, "audio");
jobject am = env->CallObjectMethod(Get(), method, name.Get());
if (Java::DiscardException(env) || am == nullptr)
return nullptr;
return new AudioManager(env, am);
}

@@ -1,5 +1,5 @@
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2019 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -23,13 +23,18 @@
#include "java/Object.hxx"
class AllocatedPath;
class AudioManager;
class Context : public Java::Object {
class Context : public Java::GlobalObject {
public:
Context(JNIEnv *env, jobject obj):Java::Object(env, obj) {}
Context(JNIEnv *env, jobject obj) noexcept
:Java::GlobalObject(env, obj) {}
gcc_pure
AllocatedPath GetCacheDir(JNIEnv *env) const;
AllocatedPath GetCacheDir(JNIEnv *env) const noexcept;
gcc_pure
AudioManager *GetAudioManager(JNIEnv *env) noexcept;
};
#endif

@@ -22,9 +22,9 @@
#include "java/Object.hxx"
class LogListener : public Java::Object {
class LogListener : public Java::GlobalObject {
public:
LogListener(JNIEnv *env, jobject obj):Java::Object(env, obj) {}
LogListener(JNIEnv *env, jobject obj):Java::GlobalObject(env, obj) {}
void OnLog(JNIEnv *env, int priority, const char *fmt, ...) const;
};

@@ -34,13 +34,12 @@
#include "util/StringBuffer.hxx"
#include "util/ScopeExit.hxx"
#include "util/Exception.hxx"
#include "util/Math.hxx"
#ifdef ENABLE_DATABASE
#include "db/update/Service.hxx"
#endif
#include <cmath>
#define COMMAND_STATUS_STATE "state"
#define COMMAND_STATUS_REPEAT "repeat"
#define COMMAND_STATUS_SINGLE "single"
@@ -154,7 +153,7 @@ handle_status(Client &client, gcc_unused Request args, Response &r)
if (pc.GetCrossFade() > FloatDuration::zero())
r.Format(COMMAND_STATUS_CROSSFADE ": %lu\n",
std::lround(pc.GetCrossFade().count()));
lround(pc.GetCrossFade().count()));
if (pc.GetMixRampDelay() > FloatDuration::zero())
r.Format(COMMAND_STATUS_MIXRAMPDELAY ": %f\n",
@@ -173,7 +172,7 @@ handle_status(Client &client, gcc_unused Request args, Response &r)
COMMAND_STATUS_BITRATE ": %u\n",
player_status.elapsed_time.RoundS(),
player_status.total_time.IsNegative()
? 0u
? 0U
: unsigned(player_status.total_time.RoundS()),
player_status.elapsed_time.ToDoubleS(),
player_status.bit_rate);

@@ -448,7 +448,7 @@ ProxyDatabase::ProxyDatabase(EventLoop &_loop, DatabaseListener &_listener,
listener(_listener),
host(block.GetBlockValue("host", "")),
password(block.GetBlockValue("password", "")),
port(block.GetBlockValue("port", 0u)),
port(block.GetBlockValue("port", 0U)),
keepalive(block.GetBlockValue("keepalive", false))
{
}
@@ -517,7 +517,7 @@ ProxyDatabase::Connect()
(void)keepalive;
#endif
idle_received = ~0u;
idle_received = ~0U;
is_idle = false;
SocketMonitor::Open(SocketDescriptor(mpd_async_get_fd(mpd_connection_get_async(connection))));

@@ -32,6 +32,7 @@
#include "fs/Traits.hxx"
#include "util/Alloc.hxx"
#include "util/DeleteDisposer.hxx"
#include "util/StringCompare.hxx"
#include <assert.h>
#include <string.h>
@@ -69,7 +70,15 @@ Directory::GetName() const noexcept
{
assert(!IsRoot());
return PathTraitsUTF8::GetBase(path.c_str());
if (parent->IsRoot())
return path.c_str();
assert(StringAfterPrefix(path.c_str(), parent->path.c_str()) != nullptr);
assert(*StringAfterPrefix(path.c_str(), parent->path.c_str()) == PathTraitsUTF8::SEPARATOR);
/* strip the parent directory path and the slash separator
from this directory's path, and the base name remains */
return path.c_str() + parent->path.length() + 1;
}
Directory *

@@ -66,7 +66,7 @@ public:
~UpdateService();
EventLoop &GetEventLoop() noexcept {
auto &GetEventLoop() const noexcept {
return defer.GetEventLoop();
}

@@ -41,7 +41,7 @@ adplug_init(const ConfigBlock &block)
FormatDebug(adplug_domain, "adplug %s",
CAdPlug::get_version().c_str());
sample_rate = block.GetPositiveValue("sample_rate", 48000u);
sample_rate = block.GetPositiveValue("sample_rate", 48000U);
CheckSampleRate(sample_rate);
return true;

@@ -26,11 +26,11 @@
#include "util/ScopeExit.hxx"
#include "util/ConstBuffer.hxx"
#include "util/Domain.hxx"
#include "util/Math.hxx"
#include "Log.hxx"
#include <neaacdec.h>
#include <cmath>
#include <exception>
#include <assert.h>

@@ -297,7 +297,7 @@ FfmpegReceiveFrames(DecoderClient &client, InputStream &is,
*/
static DecoderCommand
ffmpeg_send_packet(DecoderClient &client, InputStream &is,
AVPacket &&packet,
const AVPacket &packet,
AVCodecContext &codec_context,
const AVStream &stream,
AVFrame &frame,
@@ -350,24 +350,6 @@ ffmpeg_send_packet(DecoderClient &client, InputStream &is,
return cmd;
}
static DecoderCommand
ffmpeg_send_packet(DecoderClient &client, InputStream &is,
const AVPacket &packet,
AVCodecContext &codec_context,
const AVStream &stream,
AVFrame &frame,
uint64_t min_frame, size_t pcm_frame_size,
FfmpegBuffer &buffer)
{
return ffmpeg_send_packet(client, is,
/* copy the AVPacket, because FFmpeg
< 3.0 requires this */
AVPacket(packet),
codec_context, stream,
frame, min_frame, pcm_frame_size,
buffer);
}
gcc_const
static SampleFormat
ffmpeg_sample_format(enum AVSampleFormat sample_fmt) noexcept

@@ -78,7 +78,7 @@ fluidsynth_mpd_log_function(int level,
static bool
fluidsynth_init(const ConfigBlock &block)
{
sample_rate = block.GetPositiveValue("sample_rate", 48000u);
sample_rate = block.GetPositiveValue("sample_rate", 48000U);
CheckSampleRate(sample_rate);
soundfont_path = block.GetBlockValue("soundfont",

@@ -186,7 +186,7 @@ HybridDsdDecode(DecoderClient &client, InputStream &input)
client.Ready(result.first, true, duration);
frame_size = result.first.GetFrameSize();
kbit_rate = frame_size * result.first.sample_rate /
(1024u / 8u);
(1024U / 8U);
total_frames = result.second / frame_size;
} catch (UnsupportedFile) {
/* not a Hybrid-DSD file; let the next decoder plugin
@@ -236,7 +236,7 @@ HybridDsdDecode(DecoderClient &client, InputStream &input)
/* fill the buffer */
auto w = buffer.Write();
if (!w.empty()) {
if (remaining_bytes < (1<<30ull) &&
if (remaining_bytes < (1<<30ULL) &&
w.size > size_t(remaining_bytes))
w.size = remaining_bytes;

@@ -107,7 +107,7 @@ mikmod_decoder_init(const ConfigBlock &block)
static char params[] = "";
mikmod_loop = block.GetBlockValue("loop", false);
mikmod_sample_rate = block.GetPositiveValue("sample_rate", 44100u);
mikmod_sample_rate = block.GetPositiveValue("sample_rate", 44100U);
if (!audio_valid_sample_rate(mikmod_sample_rate))
throw FormatRuntimeError("Invalid sample rate in line %d: %u",
block.line, mikmod_sample_rate);

@@ -115,7 +115,7 @@ sidplay_init(const ConfigBlock &block)
if (!database_path.IsNull())
songlength_database = sidplay_load_songlength_db(database_path);
default_songlength = block.GetPositiveValue("default_songlength", 0u);
default_songlength = block.GetPositiveValue("default_songlength", 0U);
all_files_are_containers =
block.GetBlockValue("all_files_are_containers", true);
@@ -387,7 +387,7 @@ sidplay_file_decode(DecoderClient &client, Path path_fs)
const unsigned timebase = player.timebase();
#endif
const unsigned end = duration.IsNegative()
? 0u
? 0U
: duration.ToScale<uint64_t>(timebase);
DecoderCommand cmd;

@@ -94,7 +94,7 @@ public:
};
PreparedFlacEncoder::PreparedFlacEncoder(const ConfigBlock &block)
:compression(block.GetBlockValue("compression", 5u))
:compression(block.GetBlockValue("compression", 5U))
{
}

@@ -107,7 +107,7 @@ PreparedOpusEncoder::PreparedOpusEncoder(const ConfigBlock &block)
throw std::runtime_error("Invalid bit rate");
}
complexity = block.GetBlockValue("complexity", 10u);
complexity = block.GetBlockValue("complexity", 10U);
if (complexity > 10)
throw std::runtime_error("Invalid complexity");

@@ -50,7 +50,7 @@ public:
Cancel();
}
EventLoop &GetEventLoop() noexcept {
EventLoop &GetEventLoop() const noexcept {
return loop;
}

@@ -44,7 +44,7 @@ public:
:defer(_loop, BIND_THIS_METHOD(RunDeferred)),
callback(_callback), pending_mask(0) {}
EventLoop &GetEventLoop() {
auto &GetEventLoop() const noexcept {
return defer.GetEventLoop();
}

@@ -68,7 +68,7 @@ public:
~SocketMonitor() noexcept;
EventLoop &GetEventLoop() noexcept {
auto &GetEventLoop() const noexcept {
return loop;
}

@@ -62,7 +62,7 @@ public:
Cancel();
}
EventLoop &GetEventLoop() noexcept {
auto &GetEventLoop() const noexcept {
return loop;
}

@@ -25,6 +25,7 @@ event_dep = declare_dependency(
link_with: event,
dependencies: [
thread_dep,
net_dep,
system_dep,
boost_dep,
],

55
src/fs/NarrowPath.cxx Normal file

@@ -0,0 +1,55 @@
/*
* Copyright 2003-2018 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 "NarrowPath.hxx"
#ifdef _UNICODE
#include "lib/icu/Win32.hxx"
#include "system/Error.hxx"
#include "util/Macros.hxx"
#include <windows.h>
NarrowPath::NarrowPath(Path _path) noexcept
:value(WideCharToMultiByte(CP_ACP, _path.c_str()))
{
if (value.IsNull())
/* fall back to empty string */
value = Value::Empty();
}
static AllocatedPath
AcpToAllocatedPath(const char *s)
{
wchar_t buffer[MAX_PATH];
auto result = MultiByteToWideChar(CP_ACP, 0, s, -1,
buffer, ARRAY_SIZE(buffer));
if (result <= 0)
throw MakeLastError("MultiByteToWideChar() failed");
return AllocatedPath::FromFS(buffer);
}
FromNarrowPath::FromNarrowPath(const char *s)
:value(AcpToAllocatedPath(s))
{
}
#endif /* _UNICODE */

@@ -21,12 +21,10 @@
#define MPD_FS_NARROW_PATH_HXX
#include "Path.hxx"
#include "util/Macros.hxx"
#ifdef _UNICODE
#include "lib/icu/Win32.hxx"
#include "AllocatedPath.hxx"
#include "util/AllocatedString.hxx"
#include <windows.h>
#else
#include "util/StringPointer.hxx"
#endif
@@ -48,12 +46,7 @@ class NarrowPath {
public:
#ifdef _UNICODE
explicit NarrowPath(Path _path)
:value(WideCharToMultiByte(CP_ACP, _path.c_str())) {
if (value.IsNull())
/* fall back to empty string */
value = Value::Empty();
}
explicit NarrowPath(Path _path) noexcept;
#else
explicit NarrowPath(Path _path):value(_path.c_str()) {}
#endif
@@ -67,4 +60,38 @@ public:
}
};
/**
* A path name converted from a "narrow" string. This is used to
* import an existing narrow string to a #Path.
*/
class FromNarrowPath {
#ifdef _UNICODE
using Value = AllocatedPath;
#else
using Value = Path;
#endif
Value value{nullptr};
public:
FromNarrowPath() = default;
#ifdef _UNICODE
/**
* Throws on error.
*/
FromNarrowPath(const char *s);
#else
constexpr FromNarrowPath(const char *s) noexcept
:value(Value::FromFS(s)) {}
#endif
#ifndef _UNICODE
constexpr
#endif
operator Path() const noexcept {
return value;
}
};
#endif

@@ -40,7 +40,7 @@ class BufferedReader {
public:
explicit BufferedReader(Reader &_reader) noexcept
:reader(_reader), buffer(4096) {}
:reader(_reader), buffer(16384) {}
/**
* Reset the internal state. Should be called after rewinding

@@ -36,7 +36,7 @@ class GunzipReader final : public Reader {
z_stream z;
StaticFifoBuffer<Bytef, 4096> buffer;
StaticFifoBuffer<Bytef, 65536> buffer;
public:
/**

@@ -62,7 +62,7 @@ GzipOutputStream::Flush()
z.avail_in = 0;
while (true) {
Bytef output[4096];
Bytef output[16384];
z.next_out = output;
z.avail_out = sizeof(output);
@@ -87,7 +87,7 @@ GzipOutputStream::Write(const void *_data, size_t size)
z.avail_in = size;
while (z.avail_in > 0) {
Bytef output[4096];
Bytef output[16384];
z.next_out = output;
z.avail_out = sizeof(output);

@@ -6,6 +6,7 @@ fs_sources = [
'Path.cxx',
'Path2.cxx',
'AllocatedPath.cxx',
'NarrowPath.cxx',
'FileSystem.cxx',
'List.cxx',
'StandardDirectory.cxx',

@@ -76,7 +76,7 @@ public:
virtual ~AsyncInputStream();
EventLoop &GetEventLoop() {
auto &GetEventLoop() const noexcept {
return deferred_resume.GetEventLoop();
}

@@ -113,7 +113,7 @@ input_cdio_init(EventLoop &, const ConfigBlock &block)
throw FormatRuntimeError("Unrecognized 'default_byte_order' setting: %s",
value);
}
speed = block.GetBlockValue("speed",0u);
speed = block.GetBlockValue("speed",0U);
}
struct CdioUri {

@@ -320,7 +320,7 @@ input_curl_init(EventLoop &event_loop, const ConfigBlock &block)
http_200_aliases = curl_slist_append(http_200_aliases, "ICY 200 OK");
proxy = block.GetBlockValue("proxy");
proxy_port = block.GetBlockValue("proxy_port", 0u);
proxy_port = block.GetBlockValue("proxy_port", 0U);
proxy_user = block.GetBlockValue("proxy_user");
proxy_password = block.GetBlockValue("proxy_password");
@@ -365,9 +365,9 @@ CurlInputStream::InitEasy()
request = new CurlRequest(**curl_init, GetURI(), *this);
request->SetOption(CURLOPT_HTTP200ALIASES, http_200_aliases);
request->SetOption(CURLOPT_FOLLOWLOCATION, 1l);
request->SetOption(CURLOPT_MAXREDIRS, 5l);
request->SetOption(CURLOPT_FAILONERROR, 1l);
request->SetOption(CURLOPT_FOLLOWLOCATION, 1L);
request->SetOption(CURLOPT_MAXREDIRS, 5L);
request->SetOption(CURLOPT_FAILONERROR, 1L);
if (proxy != nullptr)
request->SetOption(CURLOPT_PROXY, proxy);
@@ -380,8 +380,8 @@ CurlInputStream::InitEasy()
StringFormat<1024>("%s:%s", proxy_user,
proxy_password).c_str());
request->SetOption(CURLOPT_SSL_VERIFYPEER, verify_peer ? 1l : 0l);
request->SetOption(CURLOPT_SSL_VERIFYHOST, verify_host ? 2l : 0l);
request->SetOption(CURLOPT_SSL_VERIFYPEER, verify_peer ? 1L : 0L);
request->SetOption(CURLOPT_SSL_VERIFYHOST, verify_host ? 2L : 0L);
request->SetOption(CURLOPT_HTTPHEADER, request_headers.Get());
}

@@ -102,7 +102,7 @@ public:
~TidalSessionManager() noexcept;
EventLoop &GetEventLoop() noexcept {
auto &GetEventLoop() const noexcept {
return defer_invoke_handlers.GetEventLoop();
}

@@ -40,15 +40,15 @@ namespace Java {
*/
typedef LocalRef<jobject> LocalObject;
class Object : public GlobalRef<jobject> {
class GlobalObject : public GlobalRef<jobject> {
public:
/**
* Constructs an uninitialized object. The method
* set() must be called before it is destructed.
*/
Object() = default;
GlobalObject() = default;
Object(JNIEnv *env, jobject obj) noexcept
GlobalObject(JNIEnv *env, jobject obj) noexcept
:GlobalRef<jobject>(env, obj) {}
};
}

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2016-2018 Max Kellermann <max.kellermann@gmail.com>
* Copyright 2016-2018 Max Kellermann <max.kellermann@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -90,6 +90,82 @@ public:
throw std::runtime_error(curl_easy_strerror(code));
}
void SetPrivate(void *pointer) {
SetOption(CURLOPT_PRIVATE, pointer);
}
void SetErrorBuffer(char *buf) {
SetOption(CURLOPT_ERRORBUFFER, buf);
}
void SetURL(const char *value) {
SetOption(CURLOPT_URL, value);
}
void SetUserAgent(const char *value) {
SetOption(CURLOPT_USERAGENT, value);
}
void SetRequestHeaders(struct curl_slist *headers) {
SetOption(CURLOPT_HTTPHEADER, headers);
}
void SetBasicAuth(const char *userpwd) {
SetOption(CURLOPT_USERPWD, userpwd);
}
void SetNoProgress(bool value=true) {
SetOption(CURLOPT_NOPROGRESS, (long)value);
}
void SetNoSignal(bool value=true) {
SetOption(CURLOPT_NOSIGNAL, (long)value);
}
void SetFailOnError(bool value=true) {
SetOption(CURLOPT_FAILONERROR, (long)value);
}
void SetConnectTimeout(long timeout) {
SetOption(CURLOPT_CONNECTTIMEOUT, timeout);
}
void SetHeaderFunction(size_t (*function)(char *buffer, size_t size,
size_t nitems,
void *userdata),
void *userdata) {
SetOption(CURLOPT_HEADERFUNCTION, function);
SetOption(CURLOPT_HEADERDATA, userdata);
}
void SetWriteFunction(size_t (*function)(char *ptr, size_t size,
size_t nmemb, void *userdata),
void *userdata) {
SetOption(CURLOPT_WRITEFUNCTION, function);
SetOption(CURLOPT_WRITEDATA, userdata);
}
void SetNoBody(bool value=true) {
SetOption(CURLOPT_NOBODY, (long)value);
}
void SetPost(bool value=true) {
SetOption(CURLOPT_POST, (long)value);
}
void SetRequestBody(const void *data, size_t size) {
SetOption(CURLOPT_POSTFIELDS, data);
SetOption(CURLOPT_POSTFIELDSIZE, (long)size);
}
void SetHttpPost(const struct curl_httppost *post) {
SetOption(CURLOPT_HTTPPOST, post);
}
bool Unpause() noexcept {
return ::curl_easy_pause(handle, CURLPAUSE_CONT) == CURLE_OK;
}
CurlString Escape(const char *string, int length=0) const noexcept {
return CurlString(curl_easy_escape(handle, string, length));
}

@@ -48,7 +48,7 @@ public:
CurlSocket(CurlGlobal &_global, EventLoop &_loop, SocketDescriptor _fd)
:SocketMonitor(_fd, _loop), global(_global) {}
~CurlSocket() {
~CurlSocket() noexcept {
/* TODO: sometimes, CURL uses CURL_POLL_REMOVE after
closing the socket, and sometimes, it uses
CURL_POLL_REMOVE just to move the (still open)
@@ -109,7 +109,8 @@ CurlGlobal::CurlGlobal(EventLoop &_loop)
int
CurlSocket::SocketFunction(gcc_unused CURL *easy,
curl_socket_t s, int action,
void *userp, void *socketp) noexcept {
void *userp, void *socketp) noexcept
{
auto &global = *(CurlGlobal *)userp;
CurlSocket *cs = (CurlSocket *)socketp;
@@ -153,11 +154,6 @@ CurlSocket::OnSocketReady(unsigned flags) noexcept
return true;
}
/**
* Runs in the I/O thread. No lock needed.
*
* Throws std::runtime_error on error.
*/
void
CurlGlobal::Add(CURL *easy, CurlRequest &request)
{
@@ -194,11 +190,6 @@ ToRequest(CURL *easy) noexcept
return (CurlRequest *)p;
}
/**
* Check for finished HTTP responses.
*
* Runs in the I/O thread. The caller must not hold locks.
*/
inline void
CurlGlobal::ReadInfo() noexcept
{
@@ -217,6 +208,20 @@ CurlGlobal::ReadInfo() noexcept
}
}
void
CurlGlobal::SocketAction(curl_socket_t fd, int ev_bitmask) noexcept
{
int running_handles;
CURLMcode mcode = curl_multi_socket_action(multi.Get(), fd, ev_bitmask,
&running_handles);
if (mcode != CURLM_OK)
FormatError(curlm_domain,
"curl_multi_socket_action() failed: %s",
curl_multi_strerror(mcode));
defer_read_info.Schedule();
}
inline void
CurlGlobal::UpdateTimeout(long timeout_ms) noexcept
{
@@ -236,11 +241,11 @@ CurlGlobal::UpdateTimeout(long timeout_ms) noexcept
}
int
CurlGlobal::TimerFunction(gcc_unused CURLM *_global, long timeout_ms,
CurlGlobal::TimerFunction(gcc_unused CURLM *_multi, long timeout_ms,
void *userp) noexcept
{
auto &global = *(CurlGlobal *)userp;
assert(_global == global.multi.Get());
assert(_multi == global.multi.Get());
global.UpdateTimeout(timeout_ms);
return 0;
@@ -251,17 +256,3 @@ CurlGlobal::OnTimeout() noexcept
{
SocketAction(CURL_SOCKET_TIMEOUT, 0);
}
void
CurlGlobal::SocketAction(curl_socket_t fd, int ev_bitmask) noexcept
{
int running_handles;
CURLMcode mcode = curl_multi_socket_action(multi.Get(), fd, ev_bitmask,
&running_handles);
if (mcode != CURLM_OK)
FormatError(curlm_domain,
"curl_multi_socket_action() failed: %s",
curl_multi_strerror(mcode));
defer_read_info.Schedule();
}

@@ -50,33 +50,33 @@ class CurlGlobal final {
public:
explicit CurlGlobal(EventLoop &_loop);
EventLoop &GetEventLoop() noexcept {
auto &GetEventLoop() const noexcept {
return timeout_event.GetEventLoop();
}
void Add(CURL *easy, CurlRequest &request);
void Remove(CURL *easy) noexcept;
/**
* Check for finished HTTP responses.
*
* Runs in the I/O thread. The caller must not hold locks.
*/
void ReadInfo() noexcept;
void Assign(curl_socket_t fd, CurlSocket &cs) noexcept {
curl_multi_assign(multi.Get(), fd, &cs);
}
void SocketAction(curl_socket_t fd, int ev_bitmask) noexcept;
void InvalidateSockets() {
void InvalidateSockets() noexcept {
SocketAction(CURL_SOCKET_TIMEOUT, 0);
}
private:
/**
* Check for finished HTTP responses.
*
* Runs in the I/O thread. The caller must not hold locks.
*/
void ReadInfo() noexcept;
void UpdateTimeout(long timeout_ms) noexcept;
static int TimerFunction(CURLM *global, long timeout_ms,
static int TimerFunction(CURLM *multi, long timeout_ms,
void *userp) noexcept;
/* callback for #timeout_event */

@@ -50,11 +50,19 @@ public:
CurlInit(const CurlInit &) = delete;
CurlInit &operator=(const CurlInit &) = delete;
CurlGlobal &operator*() {
CurlGlobal &operator*() noexcept {
return *instance;
}
CurlGlobal *operator->() {
const CurlGlobal &operator*() const noexcept {
return *instance;
}
CurlGlobal *operator->() noexcept {
return instance;
}
const CurlGlobal *operator->() const noexcept {
return instance;
}
};

@@ -52,17 +52,17 @@ CurlRequest::CurlRequest(CurlGlobal &_global,
{
error_buffer[0] = 0;
easy.SetOption(CURLOPT_PRIVATE, (void *)this);
easy.SetOption(CURLOPT_USERAGENT, "Music Player Daemon " VERSION);
easy.SetOption(CURLOPT_HEADERFUNCTION, _HeaderFunction);
easy.SetOption(CURLOPT_WRITEHEADER, this);
easy.SetOption(CURLOPT_WRITEFUNCTION, WriteFunction);
easy.SetOption(CURLOPT_WRITEDATA, this);
easy.SetOption(CURLOPT_NETRC, 1l);
easy.SetOption(CURLOPT_ERRORBUFFER, error_buffer);
easy.SetOption(CURLOPT_NOPROGRESS, 1l);
easy.SetOption(CURLOPT_NOSIGNAL, 1l);
easy.SetOption(CURLOPT_CONNECTTIMEOUT, 10l);
easy.SetPrivate((void *)this);
easy.SetUserAgent("Music Player Daemon " VERSION);
easy.SetHeaderFunction(_HeaderFunction, this);
easy.SetWriteFunction(WriteFunction, this);
#ifndef ANDROID
easy.SetOption(CURLOPT_NETRC, 1L);
#endif
easy.SetErrorBuffer(error_buffer);
easy.SetNoProgress();
easy.SetNoSignal();
easy.SetConnectTimeout(10);
easy.SetOption(CURLOPT_HTTPAUTH, (long) CURLAUTH_ANY);
}
@@ -121,7 +121,7 @@ CurlRequest::Resume() noexcept
{
assert(registered);
curl_easy_pause(easy.Get(), CURLPAUSE_CONT);
easy.Unpause();
global.InvalidateSockets();
}
@@ -220,14 +220,14 @@ CurlRequest::HeaderFunction(StringView s) noexcept
}
size_t
CurlRequest::_HeaderFunction(void *ptr, size_t size, size_t nmemb,
CurlRequest::_HeaderFunction(char *ptr, size_t size, size_t nmemb,
void *stream) noexcept
{
CurlRequest &c = *(CurlRequest *)stream;
size *= nmemb;
c.HeaderFunction({(const char *)ptr, size});
c.HeaderFunction({ptr, size});
return size;
}
@@ -254,7 +254,7 @@ CurlRequest::DataReceived(const void *ptr, size_t received_size) noexcept
}
size_t
CurlRequest::WriteFunction(void *ptr, size_t size, size_t nmemb,
CurlRequest::WriteFunction(char *ptr, size_t size, size_t nmemb,
void *stream) noexcept
{
CurlRequest &c = *(CurlRequest *)stream;

@@ -127,7 +127,7 @@ public:
}
void SetUrl(const char *url) {
easy.SetOption(CURLOPT_URL, url);
easy.SetURL(url);
}
/**
@@ -160,11 +160,11 @@ private:
void OnPostponeError() noexcept;
/** called by curl when new data is available */
static size_t _HeaderFunction(void *ptr, size_t size, size_t nmemb,
static size_t _HeaderFunction(char *ptr, size_t size, size_t nmemb,
void *stream) noexcept;
/** called by curl when new data is available */
static size_t WriteFunction(void *ptr, size_t size, size_t nmemb,
static size_t WriteFunction(char *ptr, size_t size, size_t nmemb,
void *stream) noexcept;
};

@@ -42,7 +42,7 @@ class CurlSlist {
struct curl_slist *head = nullptr;
public:
CurlSlist() = default;
CurlSlist() noexcept = default;
CurlSlist(CurlSlist &&src) noexcept
:head(std::exchange(src.head, nullptr)) {}

@@ -42,7 +42,7 @@ public:
DisconnectIndirect();
}
EventLoop &GetEventLoop() noexcept {
auto &GetEventLoop() const noexcept {
return watch.GetEventLoop();
}

@@ -102,7 +102,7 @@ public:
void Shutdown() noexcept;
EventLoop &GetEventLoop() noexcept {
auto &GetEventLoop() const noexcept {
return defer_dispatch.GetEventLoop();
}

@@ -59,7 +59,7 @@ try {
if (u.IsNull())
return AllocatedString<>::Duplicate(src);
AllocatedArray<UChar> folded(u.size() * 2u);
AllocatedArray<UChar> folded(u.size() * 2U);
UErrorCode error_code = U_ZERO_ERROR;
size_t folded_length = u_strFoldCase(folded.begin(), folded.size(),

@@ -161,9 +161,7 @@ public:
return export_name.c_str();
}
EventLoop &GetEventLoop() noexcept {
return SocketMonitor::GetEventLoop();
}
using SocketMonitor::GetEventLoop;
/**
* Ensure that the connection is established. The connection

@@ -69,7 +69,7 @@ public:
NfsFileReader() noexcept;
~NfsFileReader() noexcept;
EventLoop &GetEventLoop() noexcept {
auto &GetEventLoop() const noexcept {
return defer_open.GetEventLoop();
}

@@ -272,7 +272,7 @@ UPnPDeviceDirectory::~UPnPDeviceDirectory() noexcept
}
inline EventLoop &
UPnPDeviceDirectory::GetEventLoop() noexcept
UPnPDeviceDirectory::GetEventLoop() const noexcept
{
return curl->GetEventLoop();
}

@@ -168,7 +168,7 @@ public:
UPnPDeviceDirectory(const UPnPDeviceDirectory &) = delete;
UPnPDeviceDirectory& operator=(const UPnPDeviceDirectory &) = delete;
EventLoop &GetEventLoop() noexcept;
EventLoop &GetEventLoop() const noexcept;
void Start();

@@ -29,6 +29,7 @@ struct MixerPlugin;
extern const MixerPlugin null_mixer_plugin;
extern const MixerPlugin software_mixer_plugin;
extern const MixerPlugin android_mixer_plugin;
extern const MixerPlugin alsa_mixer_plugin;
extern const MixerPlugin haiku_mixer_plugin;
extern const MixerPlugin oss_mixer_plugin;

@@ -0,0 +1,116 @@
/*
* Copyright 2003-2020 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 "mixer/MixerInternal.hxx"
#include "filter/plugins/VolumeFilterPlugin.hxx"
#include "pcm/Volume.hxx"
#include "android/Context.hxx"
#include "android/AudioManager.hxx"
#include "Main.hxx"
#include <cassert>
#include <cmath>
class AndroidMixer final : public Mixer {
AudioManager *audioManager;
int currentVolume;
int maxAndroidVolume;
int lastAndroidVolume;
public:
explicit AndroidMixer(MixerListener &_listener);
~AndroidMixer() override;
/* virtual methods from class Mixer */
void Open() override {
}
void Close() noexcept override {
}
int GetVolume() override;
void SetVolume(unsigned volume) override;
};
static Mixer *
android_mixer_init([[maybe_unused]] EventLoop &event_loop,
[[maybe_unused]] AudioOutput &ao,
MixerListener &listener,
[[maybe_unused]] const ConfigBlock &block)
{
return new AndroidMixer(listener);
}
AndroidMixer::AndroidMixer(MixerListener &_listener)
:Mixer(android_mixer_plugin, _listener)
{
JNIEnv *env = Java::GetEnv();
audioManager = context->GetAudioManager(env);
maxAndroidVolume = audioManager->GetMaxVolume();
if (maxAndroidVolume != 0)
{
lastAndroidVolume = audioManager->GetVolume(env);
currentVolume = 100 * lastAndroidVolume / maxAndroidVolume;
}
}
AndroidMixer::~AndroidMixer()
{
delete audioManager;
}
int
AndroidMixer::GetVolume()
{
JNIEnv *env = Java::GetEnv();
if (maxAndroidVolume == 0)
return -1;
// The android volume index (or scale) is very likely inferior to the
// MPD one (100). The last volume set by MPD is saved into
// currentVolume, this volume is returned instead of the Android one
// when the Android mixer was not touched by an other application. This
// allows to fake a 0..100 scale from MPD.
int volume = audioManager->GetVolume(env);
if (volume == lastAndroidVolume)
return currentVolume;
return 100 * volume / maxAndroidVolume;
}
void
AndroidMixer::SetVolume(unsigned newVolume)
{
JNIEnv *env = Java::GetEnv();
if (maxAndroidVolume == 0)
return;
currentVolume = newVolume;
lastAndroidVolume = currentVolume * maxAndroidVolume / 100;
audioManager->SetVolume(env, lastAndroidVolume);
}
const MixerPlugin android_mixer_plugin = {
android_mixer_init,
true,
};

@@ -20,13 +20,13 @@
#include "mixer/MixerInternal.hxx"
#include "output/OutputAPI.hxx"
#include "output/plugins/WinmmOutputPlugin.hxx"
#include "util/Math.hxx"
#include <mmsystem.h>
#include <stdexcept>
#include <assert.h>
#include <math.h>
#include <windows.h>
class WinmmMixer final : public Mixer {

@@ -34,6 +34,10 @@ if is_windows
mixer_plugins_sources += 'WinmmMixerPlugin.cxx'
endif
if is_android
mixer_plugins_sources += 'AndroidMixerPlugin.cxx'
endif
mixer_plugins = static_library(
'mixer_plugins',
mixer_plugins_sources,

@@ -70,7 +70,7 @@ public:
NeighborListener &_listener) noexcept
:NeighborExplorer(_listener), event_loop(_event_loop) {}
auto &GetEventLoop() noexcept {
auto &GetEventLoop() const noexcept {
return event_loop;
}

@@ -395,7 +395,7 @@ AlsaOutput::AlsaOutput(EventLoop &_loop, const ConfigBlock &block)
#endif
buffer_time(block.GetPositiveValue("buffer_time",
MPD_ALSA_BUFFER_TIME_US)),
period_time(block.GetPositiveValue("period_time", 0u))
period_time(block.GetPositiveValue("period_time", 0U))
{
#ifdef SND_PCM_NO_AUTO_RESAMPLE
if (!block.GetBlockValue("auto_resample", true))

@@ -102,7 +102,7 @@ MakeAoError()
AoOutput::AoOutput(const ConfigBlock &block)
:AudioOutput(0),
write_size(block.GetPositiveValue("write_size", 1024u))
write_size(block.GetPositiveValue("write_size", 1024U))
{
const char *value = block.GetBlockValue("driver", "default");
if (0 == strcmp(value, "default"))

@@ -22,6 +22,7 @@
#include "../OutputAPI.hxx"
#include "mixer/MixerList.hxx"
#include "util/Domain.hxx"
#include "util/Math.hxx"
#include "system/Error.hxx"
#include "Log.hxx"
@@ -37,8 +38,6 @@
#include <StringList.h>
#include <SoundPlayer.h>
#include <cmath>
#include <string.h>
#define UTF8_PLAY "\xE2\x96\xB6"

@@ -212,7 +212,7 @@ JackOutput::JackOutput(const ConfigBlock &block)
num_source_ports, num_destination_ports,
block.line);
ringbuffer_size = block.GetPositiveValue("ringbuffer_size", 32768u);
ringbuffer_size = block.GetPositiveValue("ringbuffer_size", 32768U);
}
inline jack_nframes_t

@@ -101,7 +101,7 @@ ShoutOutput::ShoutOutput(const ConfigBlock &block)
{
const char *host = require_block_string(block, "host");
const char *mount = require_block_string(block, "mount");
unsigned port = block.GetBlockValue("port", 0u);
unsigned port = block.GetBlockValue("port", 0U);
if (port == 0)
throw std::runtime_error("shout port must be configured");

@@ -54,11 +54,11 @@ HttpdOutput::HttpdOutput(EventLoop &_loop, const ConfigBlock &block)
genre = block.GetBlockValue("genre", "Set genre in config");
website = block.GetBlockValue("website", "Set website in config");
clients_max = block.GetBlockValue("max_clients", 0u);
clients_max = block.GetBlockValue("max_clients", 0U);
/* set up bind_to_address */
ServerSocketAddGeneric(*this, block.GetBlockValue("bind_to_address"), block.GetBlockValue("port", 8000u));
ServerSocketAddGeneric(*this, block.GetBlockValue("bind_to_address"), block.GetBlockValue("port", 8000U));
/* determine content type */
content_type = prepared_encoder->GetMimeType();

@@ -28,6 +28,7 @@
#include "util/Macros.hxx"
#include "util/Domain.hxx"
#include "system/ByteOrder.hxx"
#include "mixer/MixerList.hxx"
#include "Log.hxx"
#include <SLES/OpenSLES.h>
@@ -412,5 +413,5 @@ const struct AudioOutputPlugin sles_output_plugin = {
"sles",
sles_test_default_device,
SlesOutput::Create,
nullptr,
&android_mixer_plugin,
};

@@ -22,11 +22,10 @@
#include "Clamp.hxx"
#include "Traits.hxx"
#include "util/Clamp.hxx"
#include "util/Math.hxx"
#include "PcmDither.cxx" // including the .cxx file to get inlined templates
#include <cmath>
#include <assert.h>
template<SampleFormat F, class Traits=SampleTraits<F>>
@@ -225,7 +224,7 @@ pcm_mix(PcmDither &dither, void *buffer1, const void *buffer2, size_t size,
s = sin(M_PI_2 * portion1);
s *= s;
int vol1 = std::lround(s * PCM_VOLUME_1S);
int vol1 = lround(s * PCM_VOLUME_1S);
vol1 = Clamp<int>(vol1, 0, PCM_VOLUME_1S);
return pcm_add_vol(dither, buffer1, buffer2, size,

@@ -23,10 +23,9 @@
#include "AudioFormat.hxx"
#include "util/NumberParser.hxx"
#include "util/Domain.hxx"
#include "util/Math.hxx"
#include "Log.hxx"
#include <cmath>
#include <assert.h>
static constexpr Domain cross_fade_domain("cross_fade");
@@ -112,7 +111,7 @@ CrossFadeSettings::Calculate(SignedSongTime total_time,
if (mixramp_delay <= FloatDuration::zero() ||
!mixramp_start || !mixramp_prev_end) {
chunks = std::lround(duration / chunk_duration);
chunks = lround(duration / chunk_duration);
} else {
/* Calculate mixramp overlap. */
const auto mixramp_overlap_current =

@@ -162,6 +162,7 @@ static const char *const rss_suffixes[] = {
static const char *const rss_mime_types[] = {
"application/rss+xml",
"application/xml",
"text/xml",
nullptr
};

@@ -266,8 +266,8 @@ public:
CommonExpatParser(ExpatNamespaceSeparator{'|'})
{
request.SetOption(CURLOPT_CUSTOMREQUEST, "PROPFIND");
request.SetOption(CURLOPT_FOLLOWLOCATION, 1l);
request.SetOption(CURLOPT_MAXREDIRS, 1l);
request.SetOption(CURLOPT_FOLLOWLOCATION, 1L);
request.SetOption(CURLOPT_MAXREDIRS, 1L);
request_headers.Append(StringFormat<40>("depth: %u", depth));

@@ -129,7 +129,7 @@ public:
}
private:
EventLoop &GetEventLoop() noexcept {
EventLoop &GetEventLoop() const noexcept {
return defer_connect.GetEventLoop();
}

@@ -94,7 +94,7 @@ public:
}
}
EventLoop &GetEventLoop() noexcept {
EventLoop &GetEventLoop() const noexcept {
return defer_mount.GetEventLoop();
}

@@ -32,7 +32,7 @@
Mutex tag_pool_lock;
static constexpr size_t NUM_SLOTS = 4093;
static constexpr size_t NUM_SLOTS = 16127;
struct TagPoolSlot {
TagPoolSlot *next;

@@ -77,15 +77,15 @@ static time_t
GetTimeZoneOffset() noexcept
{
time_t t = 1234567890;
struct tm tm;
tm.tm_isdst = 0;
#ifdef _WIN32
struct tm *p = gmtime(&t);
#else
struct tm tm;
tm.tm_isdst = 0;
struct tm *p = &tm;
gmtime_r(&t, p);
#endif
return t - mktime(&tm);
return t - mktime(p);
}
#endif /* !__GLIBC__ */

41
src/util/Math.hxx Normal file

@@ -0,0 +1,41 @@
/*
* Copyright (C) 2018 Max Kellermann <max.kellermann@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef MATH_HXX
#define MATH_HXX
#if (defined(__GLIBCPP__) || defined(__GLIBCXX__)) && !defined(_GLIBCXX_USE_C99_MATH)
#include <boost/math/special_functions/round.hpp>
using boost::math::lround;
#else
#include <cmath>
using std::lround;
#endif
#endif

@@ -202,7 +202,7 @@ struct CheckSequenceUTF8 {
};
template<>
struct CheckSequenceUTF8<0u> {
struct CheckSequenceUTF8<0U> {
constexpr bool operator()(gcc_unused const char *p) const noexcept {
return true;
}
@@ -215,7 +215,7 @@ InnerSequenceLengthUTF8(const char *p) noexcept
{
return CheckSequenceUTF8<L>()(p)
? L + 1
: 0u;
: 0U;
}
size_t

@@ -23,6 +23,7 @@
#include "decoder/DecoderList.hxx"
#include "decoder/DecoderPlugin.hxx"
#include "fs/Path.hxx"
#include "fs/NarrowPath.hxx"
#include "fs/io/StdioOutputStream.hxx"
#include "fs/io/BufferedOutputStream.hxx"
#include "util/UriUtil.hxx"
@@ -63,7 +64,7 @@ try {
return EXIT_FAILURE;
}
const Path path = Path::FromFS(argv[1]);
const FromNarrowPath path = argv[1];
const ScopeDecoderPluginsInit decoder_plugins_init({});

@@ -29,6 +29,7 @@
#include "ConfigGlue.hxx"
#include "tag/Config.hxx"
#include "fs/Path.hxx"
#include "fs/NarrowPath.hxx"
#include "event/Thread.hxx"
#include "util/ScopeExit.hxx"
#include "util/PrintException.hxx"
@@ -107,7 +108,7 @@ try {
return 1;
}
const Path config_path = Path::FromFS(argv[1]);
const FromNarrowPath config_path = argv[1];
const char *const plugin_name = argv[2];
const DatabasePlugin *plugin = GetDatabasePluginByName(plugin_name);

@@ -21,6 +21,7 @@
#include "tag/ApeLoader.hxx"
#include "thread/Mutex.hxx"
#include "fs/Path.hxx"
#include "fs/NarrowPath.hxx"
#include "input/InputStream.hxx"
#include "input/LocalOpen.hxx"
#include "util/StringView.hxx"
@@ -58,7 +59,7 @@ try {
return EXIT_FAILURE;
}
const Path path = Path::FromFS(argv[1]);
const FromNarrowPath path = argv[1];
Mutex mutex;

@@ -29,8 +29,8 @@ public:
};
#ifdef _WIN32
ShutdownHandler::ShutdownHandler(EventLoop &loop) {}
ShutdownHandler::~ShutdownHandler() {}
inline ShutdownHandler::ShutdownHandler(EventLoop &) {}
inline ShutdownHandler::~ShutdownHandler() {}
#endif
#endif

@@ -18,6 +18,7 @@
*/
#include "fs/io/FileOutputStream.hxx"
#include "fs/NarrowPath.hxx"
#include "util/PrintException.hxx"
#include <unistd.h>
@@ -54,7 +55,7 @@ try {
return EXIT_FAILURE;
}
const Path path = Path::FromFS(argv[1]);
const FromNarrowPath path = argv[1];
FileOutputStream fos(path);

@@ -28,6 +28,7 @@
#include "playlist/PlaylistRegistry.hxx"
#include "playlist/PlaylistPlugin.hxx"
#include "fs/Path.hxx"
#include "fs/NarrowPath.hxx"
#include "fs/io/BufferedOutputStream.hxx"
#include "fs/io/StdioOutputStream.hxx"
#include "thread/Cond.hxx"
@@ -54,7 +55,7 @@ try {
return EXIT_FAILURE;
}
const Path config_path = Path::FromFS(argv[1]);
const FromNarrowPath config_path = argv[1];
uri = argv[2];
/* initialize MPD */

@@ -24,6 +24,7 @@ gtest_dep = declare_dependency(
)
subdir('net')
subdir('time')
executable(
'read_conf',
@@ -51,19 +52,6 @@ test('TestUtil', executable(
],
))
test(
'TestTime',
executable(
'TestTime',
'TestISO8601.cxx',
include_directories: inc,
dependencies: [
time_dep,
gtest_dep,
],
),
)
test('TestRewindInputStream', executable(
'TestRewindInputStream',
'TestRewindInputStream.cxx',
@@ -348,6 +336,11 @@ if curl_dep.found()
include_directories: inc,
dependencies: [
curl_dep,
# Explicitly linking with zlib here works around a linker
# failure on Windows, because our Windows CURL build is
# statically linked and thus declares no dependency on zlib
zlib_dep,
],
)

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 Max Kellermann <max.kellermann@gmail.com>
* Copyright 2012-2020 Max Kellermann <max.kellermann@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -34,13 +34,22 @@
#include <stdexcept>
#ifndef _WIN32
#include <arpa/inet.h>
#endif
static std::string
ToString(const struct in_addr &a)
{
#ifdef _WIN32
/* on mingw32, the parameter is non-const (PVOID) */
const auto p = const_cast<struct in_addr *>(&a);
#else
const auto p = &a;
#endif
char buffer[256];
const char *result = inet_ntop(AF_INET, &a, buffer, sizeof(buffer));
const char *result = inet_ntop(AF_INET, p, buffer, sizeof(buffer));
if (result == nullptr)
throw std::runtime_error("inet_ntop() failed");
return result;

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 Max Kellermann <max.kellermann@gmail.com>
* Copyright 2012-2020 Max Kellermann <max.kellermann@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -34,13 +34,22 @@
#include <stdexcept>
#ifndef _WIN32
#include <arpa/inet.h>
#endif
static std::string
ToString(const struct in6_addr &a)
{
#ifdef _WIN32
/* on mingw32, the parameter is non-const (PVOID) */
const auto p = const_cast<struct in6_addr *>(&a);
#else
const auto p = &a;
#endif
char buffer[256];
const char *result = inet_ntop(AF_INET6, &a, buffer, sizeof(buffer));
const char *result = inet_ntop(AF_INET6, p, buffer, sizeof(buffer));
if (result == nullptr)
throw std::runtime_error("inet_ntop() failed");
return result;

@@ -21,7 +21,7 @@
#include "config/Param.hxx"
#include "config/File.hxx"
#include "fs/Path.hxx"
#include "fs/Path.hxx"
#include "fs/NarrowPath.hxx"
#include "util/PrintException.hxx"
#include "util/RuntimeError.hxx"
@@ -36,7 +36,7 @@ try {
return EXIT_FAILURE;
}
const Path config_path = Path::FromFS(argv[1]);
const FromNarrowPath config_path = argv[1];
const char *name = argv[2];
const auto option = ParseConfigOptionName(name);

@@ -27,6 +27,7 @@
#include "tag/Handler.hxx"
#include "tag/Generic.hxx"
#include "fs/Path.hxx"
#include "fs/NarrowPath.hxx"
#include "AudioFormat.hxx"
#include "util/ScopeExit.hxx"
#include "util/StringBuffer.hxx"
@@ -88,7 +89,7 @@ try {
}
decoder_name = argv[1];
const Path path = Path::FromFS(argv[2]);
const char *path = argv[2];
EventThread io_thread;
io_thread.Start();
@@ -107,7 +108,7 @@ try {
DumpTagHandler h;
bool success;
try {
success = plugin->ScanFile(path, h);
success = plugin->ScanFile(FromNarrowPath(path), h);
} catch (...) {
PrintException(std::current_exception());
success = false;
@@ -117,7 +118,7 @@ try {
InputStreamPtr is;
if (!success && plugin->scan_stream != NULL) {
is = InputStream::OpenReady(path.c_str(), mutex);
is = InputStream::OpenReady(path, mutex);
success = plugin->ScanStream(*is, h);
}
@@ -130,7 +131,7 @@ try {
if (is)
ScanGenericTags(*is, h);
else
ScanGenericTags(path, h);
ScanGenericTags(FromNarrowPath(path), h);
}
return 0;

@@ -26,6 +26,7 @@
#include "input/Init.hxx"
#include "input/InputStream.hxx"
#include "fs/Path.hxx"
#include "fs/NarrowPath.hxx"
#include "AudioFormat.hxx"
#include "util/OptionDef.hxx"
#include "util/OptionParser.hxx"
@@ -44,7 +45,7 @@ struct CommandLine {
const char *decoder = nullptr;
const char *uri = nullptr;
Path config_path = nullptr;
FromNarrowPath config_path;
bool verbose = false;
};
@@ -68,7 +69,7 @@ ParseCommandLine(int argc, char **argv)
while (auto o = option_parser.Next()) {
switch (Option(o.index)) {
case OPTION_CONFIG:
c.config_path = Path::FromFS(o.value);
c.config_path = o.value;
break;
case OPTION_VERBOSE:
@@ -118,7 +119,7 @@ try {
DumpDecoderClient client;
if (plugin->file_decode != nullptr) {
try {
plugin->FileDecode(client, Path::FromFS(c.uri));
plugin->FileDecode(client, FromNarrowPath(c.uri));
} catch (StopDecoder) {
}
} else if (plugin->stream_decode != nullptr) {

@@ -19,6 +19,7 @@
#include "ConfigGlue.hxx"
#include "fs/Path.hxx"
#include "fs/NarrowPath.hxx"
#include "AudioParser.hxx"
#include "AudioFormat.hxx"
#include "filter/LoadOne.hxx"
@@ -68,7 +69,7 @@ try {
return EXIT_FAILURE;
}
const Path config_path = Path::FromFS(argv[1]);
const FromNarrowPath config_path = argv[1];
AudioFormat audio_format(44100, SampleFormat::S16, 2);

@@ -20,6 +20,7 @@
#include "fs/io/GunzipReader.hxx"
#include "fs/io/FileReader.hxx"
#include "fs/io/StdioOutputStream.hxx"
#include "fs/NarrowPath.hxx"
#include "util/PrintException.hxx"
#include <stdio.h>
@@ -62,7 +63,7 @@ try {
return EXIT_FAILURE;
}
Path path = Path::FromFS(argv[1]);
FromNarrowPath path = argv[1];
CopyGunzip(stdout, path);
return EXIT_SUCCESS;

@@ -32,6 +32,7 @@
#include "Log.hxx"
#include "LogBackend.hxx"
#include "fs/Path.hxx"
#include "fs/NarrowPath.hxx"
#include "fs/io/BufferedOutputStream.hxx"
#include "fs/io/StdioOutputStream.hxx"
#include "util/ConstBuffer.hxx"
@@ -51,7 +52,7 @@
struct CommandLine {
const char *uri = nullptr;
Path config_path = nullptr;
FromNarrowPath config_path;
bool verbose = false;
@@ -79,7 +80,7 @@ ParseCommandLine(int argc, char **argv)
while (auto o = option_parser.Next()) {
switch (Option(o.index)) {
case OPTION_CONFIG:
c.config_path = Path::FromFS(o.value);
c.config_path = o.value;
break;
case OPTION_VERBOSE:

@@ -23,6 +23,7 @@
#include "ConfigGlue.hxx"
#include "event/Thread.hxx"
#include "fs/Path.hxx"
#include "fs/NarrowPath.hxx"
#include "AudioParser.hxx"
#include "pcm/PcmConvert.hxx"
#include "util/StringBuffer.hxx"
@@ -111,7 +112,7 @@ try {
return EXIT_FAILURE;
}
const Path config_path = Path::FromFS(argv[1]);
const FromNarrowPath config_path = argv[1];
AudioFormat audio_format(44100, SampleFormat::S16, 2);

65
test/time/TestConvert.cxx Normal file

@@ -0,0 +1,65 @@
/*
* Copyright 2020 Max Kellermann <max.kellermann@gmail.com>
* All rights reserved.
*
* author: Max Kellermann <mk@cm4all.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "time/Convert.hxx"
#include <gtest/gtest.h>
static constexpr time_t times[] = {
1234567890,
1580566807,
1585750807,
1590934807,
};
TEST(Time, LocalTime)
{
/* convert back and forth using local time zone */
for (const auto t : times) {
auto tp = std::chrono::system_clock::from_time_t(t);
auto tm = LocalTime(tp);
EXPECT_EQ(MakeTime(tm), tp);
}
}
TEST(Time, GmTime)
{
/* convert back and forth using UTC */
for (const auto t : times) {
auto tp = std::chrono::system_clock::from_time_t(t);
auto tm = GmTime(tp);
EXPECT_EQ(std::chrono::system_clock::to_time_t(TimeGm(tm)),
t);
}
}

17
test/time/meson.build Normal file

@@ -0,0 +1,17 @@
test_time_sources = [
'TestConvert.cxx',
'TestISO8601.cxx',
]
test(
'TestTime',
executable(
'TestTime',
test_time_sources,
include_directories: inc,
dependencies: [
time_dep,
gtest_dep,
],
),
)