Compare commits
64 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
5ccfcffcc1 | ||
![]() |
afe2aaa5f6 | ||
![]() |
9b11caa0e6 | ||
![]() |
a689b881d3 | ||
![]() |
e94c436264 | ||
![]() |
bad829509e | ||
![]() |
9c66b0414a | ||
![]() |
4d453a8313 | ||
![]() |
61d7b436a2 | ||
![]() |
cdddaf21b0 | ||
![]() |
b267ba5f0a | ||
![]() |
8270043053 | ||
![]() |
c00ce42bca | ||
![]() |
3852ddbbce | ||
![]() |
672bc3ab67 | ||
![]() |
62229f14da | ||
![]() |
a4c925c8d7 | ||
![]() |
60610e90b1 | ||
![]() |
90184e0ce7 | ||
![]() |
9c3e1d450a | ||
![]() |
60f2116202 | ||
![]() |
4ff2532330 | ||
![]() |
9c15760c4d | ||
![]() |
e1c43ec65f | ||
![]() |
4dd10894ba | ||
![]() |
608d7ec1e7 | ||
![]() |
8474599ed6 | ||
![]() |
ab39f64fc0 | ||
![]() |
185fbca282 | ||
![]() |
6e3b2fd844 | ||
![]() |
dab39dc778 | ||
![]() |
8cd5e79fbd | ||
![]() |
1de3ac6c78 | ||
![]() |
abe06a5fa6 | ||
![]() |
85c27840a3 | ||
![]() |
81c16273c5 | ||
![]() |
801ae86b5d | ||
![]() |
5619fd0bba | ||
![]() |
200258c7c3 | ||
![]() |
5418bb49fb | ||
![]() |
3449c14ff5 | ||
![]() |
36a89e8fe7 | ||
![]() |
8e6a21a9c2 | ||
![]() |
c560ec8ea6 | ||
![]() |
56c234b410 | ||
![]() |
82743dfd02 | ||
![]() |
33694642bd | ||
![]() |
c71242d743 | ||
![]() |
c45f113856 | ||
![]() |
e0a8fd398c | ||
![]() |
3e97058151 | ||
![]() |
51b1dd8672 | ||
![]() |
98a7d8da6c | ||
![]() |
acb29f792f | ||
![]() |
cd364023ae | ||
![]() |
8d34a1cfc6 | ||
![]() |
73a1f078a6 | ||
![]() |
b7ce452308 | ||
![]() |
5faf76051d | ||
![]() |
5fe70a3417 | ||
![]() |
7a68b1e71f | ||
![]() |
d5468dfe89 | ||
![]() |
976372ff63 | ||
![]() |
9abb686eeb |
.travis.ymlNEWS
android
doc
meson.buildpython/build
src
CommandLine.cxxMain.cxxStats.cxx
android
archive
plugins
command
db
decoder
plugins
encoder
event
fs
input
java
lib
curl
dbus
icu
nfs
upnp
mixer
neighbor
plugins
output
plugins
pcm
player
playlist
storage
tag
time
util
test
19
.travis.yml
19
.travis.yml
@@ -1,6 +1,6 @@
|
||||
language: cpp
|
||||
|
||||
matrix:
|
||||
jobs:
|
||||
include:
|
||||
# Ubuntu Bionic (18.04) with GCC 7
|
||||
- os: linux
|
||||
@@ -126,6 +126,23 @@ matrix:
|
||||
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"
|
||||
|
||||
|
28
NEWS
28
NEWS
@@ -1,3 +1,31 @@
|
||||
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
|
||||
* playlist
|
||||
- asx, xspf: fix corrupt tags in the presence of XML entities
|
||||
* archive
|
||||
- iso9660: skip empty file names to work around libcdio bug
|
||||
* decoder
|
||||
- gme: ignore empty tags
|
||||
* output
|
||||
- solaris: port to NetBSD
|
||||
* raise default "max_connections" value to 100
|
||||
|
||||
ver 0.21.20 (2020/02/16)
|
||||
* decoder
|
||||
- audiofile, ffmpeg, sndfile: handle MIME type "audio/wav"
|
||||
|
@@ -2,18 +2,25 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.musicpd"
|
||||
android:installLocation="auto"
|
||||
android:versionCode="43"
|
||||
android:versionName="0.21.20">
|
||||
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.20'
|
||||
version = '0.21.22'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = version
|
||||
|
||||
|
@@ -695,7 +695,7 @@ These settings are various limitations to prevent :program:`MPD` from using too
|
||||
* - **connection_timeout SECONDS**
|
||||
- If a client does not send any new data in this time period, the connection is closed. Clients waiting in "idle" mode are excluded from this. Default is 60.
|
||||
* - **max_connections NUMBER**
|
||||
- This specifies the maximum number of clients that can be connected to :program:`MPD` at the same time. Default is 5.
|
||||
- This specifies the maximum number of clients that can be connected to :program:`MPD` at the same time. Default is 100.
|
||||
* - **max_playlist_length NUMBER**
|
||||
- The maximum number of songs that can be in the playlist. Default is 16384.
|
||||
* - **max_command_list_size KBYTES**
|
||||
|
@@ -1,7 +1,7 @@
|
||||
project(
|
||||
'mpd',
|
||||
['c', 'cpp'],
|
||||
version: '0.21.20',
|
||||
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;
|
||||
}
|
||||
|
||||
|
@@ -460,7 +460,7 @@ MainOrThrow(int argc, char *argv[])
|
||||
#endif
|
||||
|
||||
const unsigned max_clients =
|
||||
raw_config.GetPositive(ConfigOption::MAX_CONN, 10);
|
||||
raw_config.GetPositive(ConfigOption::MAX_CONN, 100);
|
||||
instance->client_list = new ClientList(max_clients);
|
||||
|
||||
initialize_decoder_and_player(raw_config, config.replay_gain);
|
||||
|
@@ -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();
|
||||
|
56
src/android/AudioManager.cxx
Normal file
56
src/android/AudioManager.cxx
Normal file
@@ -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);
|
||||
}
|
42
src/android/AudioManager.hxx
Normal file
42
src/android/AudioManager.hxx
Normal file
@@ -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;
|
||||
};
|
||||
|
@@ -28,6 +28,7 @@
|
||||
#include "input/InputStream.hxx"
|
||||
#include "fs/Path.hxx"
|
||||
#include "util/RuntimeError.hxx"
|
||||
#include "util/StringCompare.hxx"
|
||||
|
||||
#include <cdio/iso9660.h>
|
||||
|
||||
@@ -93,7 +94,10 @@ Iso9660ArchiveFile::Visit(char *path, size_t length, size_t capacity,
|
||||
auto *statbuf = (iso9660_stat_t *)
|
||||
_cdio_list_node_data(entnode);
|
||||
const char *filename = statbuf->filename;
|
||||
if (strcmp(filename, ".") == 0 || strcmp(filename, "..") == 0)
|
||||
if (StringIsEmpty(filename) ||
|
||||
PathTraitsUTF8::IsSpecialFilename(filename))
|
||||
/* skip empty names (libcdio bug?) */
|
||||
/* skip special names like "." and ".." */
|
||||
continue;
|
||||
|
||||
size_t filename_length = strlen(filename);
|
||||
|
@@ -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 *
|
||||
|
@@ -24,6 +24,7 @@
|
||||
#include "storage/StorageInterface.hxx"
|
||||
#include "fs/AllocatedPath.hxx"
|
||||
#include "fs/FileInfo.hxx"
|
||||
#include "fs/Traits.hxx"
|
||||
#include "Log.hxx"
|
||||
|
||||
#include <string>
|
||||
@@ -146,8 +147,7 @@ WatchDirectory::GetUriFS() const noexcept
|
||||
/* we don't look at "." / ".." nor files with newlines in their name */
|
||||
static bool skip_path(const char *path)
|
||||
{
|
||||
return (path[0] == '.' && path[1] == 0) ||
|
||||
(path[0] == '.' && path[1] == '.' && path[2] == 0) ||
|
||||
return PathTraitsFS::IsSpecialFilename(path) ||
|
||||
strchr(path, '\n') != nullptr;
|
||||
}
|
||||
|
||||
|
@@ -66,7 +66,7 @@ public:
|
||||
|
||||
~UpdateService();
|
||||
|
||||
EventLoop &GetEventLoop() noexcept {
|
||||
auto &GetEventLoop() const noexcept {
|
||||
return defer.GetEventLoop();
|
||||
}
|
||||
|
||||
|
@@ -237,7 +237,7 @@ try {
|
||||
LogError(std::current_exception());
|
||||
}
|
||||
|
||||
/* we don't look at "." / ".." nor files with newlines in their name */
|
||||
/* we don't look at files with newlines in their name */
|
||||
gcc_pure
|
||||
static bool
|
||||
skip_path(const char *name_utf8) noexcept
|
||||
|
@@ -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",
|
||||
|
@@ -28,6 +28,7 @@
|
||||
#include "fs/AllocatedPath.hxx"
|
||||
#include "fs/FileSystem.hxx"
|
||||
#include "util/ScopeExit.hxx"
|
||||
#include "util/StringCompare.hxx"
|
||||
#include "util/StringFormat.hxx"
|
||||
#include "util/UriUtil.hxx"
|
||||
#include "util/Domain.hxx"
|
||||
@@ -222,7 +223,7 @@ ScanGmeInfo(const gme_info_t &info, unsigned song_num, int track_count,
|
||||
if (track_count > 1)
|
||||
handler.OnTag(TAG_TRACK, StringFormat<16>("%u", song_num + 1));
|
||||
|
||||
if (info.song != nullptr) {
|
||||
if (!StringIsEmpty(info.song)) {
|
||||
if (track_count > 1) {
|
||||
/* start numbering subtunes from 1 */
|
||||
const auto tag_title =
|
||||
@@ -234,16 +235,16 @@ ScanGmeInfo(const gme_info_t &info, unsigned song_num, int track_count,
|
||||
handler.OnTag(TAG_TITLE, info.song);
|
||||
}
|
||||
|
||||
if (info.author != nullptr)
|
||||
if (!StringIsEmpty(info.author))
|
||||
handler.OnTag(TAG_ARTIST, info.author);
|
||||
|
||||
if (info.game != nullptr)
|
||||
if (!StringIsEmpty(info.game))
|
||||
handler.OnTag(TAG_ALBUM, info.game);
|
||||
|
||||
if (info.comment != nullptr)
|
||||
if (!StringIsEmpty(info.comment))
|
||||
handler.OnTag(TAG_COMMENT, info.comment);
|
||||
|
||||
if (info.copyright != nullptr)
|
||||
if (!StringIsEmpty(info.copyright))
|
||||
handler.OnTag(TAG_DATE, info.copyright);
|
||||
}
|
||||
|
||||
|
@@ -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
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
|
||||
|
@@ -108,6 +108,12 @@ struct PathTraitsFS {
|
||||
return IsSeparator(*p);
|
||||
}
|
||||
|
||||
gcc_pure gcc_nonnull_all
|
||||
static bool IsSpecialFilename(const_pointer_type name) noexcept {
|
||||
return (name[0] == '.' && name[1] == 0) ||
|
||||
(name[0] == '.' && name[1] == '.' && name[2] == 0);
|
||||
}
|
||||
|
||||
gcc_pure gcc_nonnull_all
|
||||
static size_t GetLength(const_pointer_type p) noexcept {
|
||||
return StringLength(p);
|
||||
@@ -216,6 +222,12 @@ struct PathTraitsUTF8 {
|
||||
return IsSeparator(*p);
|
||||
}
|
||||
|
||||
gcc_pure gcc_nonnull_all
|
||||
static bool IsSpecialFilename(const_pointer_type name) noexcept {
|
||||
return (name[0] == '.' && name[1] == 0) ||
|
||||
(name[0] == '.' && name[1] == '.' && name[2] == 0);
|
||||
}
|
||||
|
||||
gcc_pure gcc_nonnull_all
|
||||
static size_t GetLength(const_pointer_type p) noexcept {
|
||||
return StringLength(p);
|
||||
|
@@ -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;
|
||||
|
116
src/mixer/plugins/AndroidMixerPlugin.cxx
Normal file
116
src/mixer/plugins/AndroidMixerPlugin.cxx
Normal file
@@ -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");
|
||||
|
||||
|
@@ -22,22 +22,23 @@
|
||||
#include "system/FileDescriptor.hxx"
|
||||
#include "system/Error.hxx"
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#ifdef __sun
|
||||
#if defined(__sun)
|
||||
#include <sys/audio.h>
|
||||
#include <sys/stropts.h>
|
||||
#elif defined(__NetBSD__)
|
||||
#include <sys/audioio.h>
|
||||
#else
|
||||
|
||||
/* some fake declarations that allow build this plugin on systems
|
||||
other than Solaris, just to see if it compiles */
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#ifndef I_FLUSH
|
||||
#define I_FLUSH 0
|
||||
#endif
|
||||
@@ -147,7 +148,11 @@ SolarisOutput::Play(const void *chunk, size_t size)
|
||||
void
|
||||
SolarisOutput::Cancel() noexcept
|
||||
{
|
||||
#if defined(AUDIO_FLUSH)
|
||||
ioctl(fd.Get(), AUDIO_FLUSH);
|
||||
#elif defined(I_FLUSH)
|
||||
ioctl(fd.Get(), I_FLUSH);
|
||||
#endif
|
||||
}
|
||||
|
||||
const struct AudioOutputPlugin solaris_output_plugin = {
|
||||
|
@@ -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 =
|
||||
|
@@ -21,6 +21,7 @@
|
||||
#include "../PlaylistPlugin.hxx"
|
||||
#include "../MemorySongEnumerator.hxx"
|
||||
#include "tag/Builder.hxx"
|
||||
#include "tag/Table.hxx"
|
||||
#include "util/ASCII.hxx"
|
||||
#include "util/StringView.hxx"
|
||||
#include "lib/expat/ExpatParser.hxx"
|
||||
@@ -41,6 +42,7 @@ struct AsxParser {
|
||||
*/
|
||||
enum {
|
||||
ROOT, ENTRY,
|
||||
TAG,
|
||||
} state;
|
||||
|
||||
/**
|
||||
@@ -57,23 +59,33 @@ struct AsxParser {
|
||||
|
||||
TagBuilder tag_builder;
|
||||
|
||||
std::string value;
|
||||
|
||||
AsxParser()
|
||||
:state(ROOT) {}
|
||||
|
||||
};
|
||||
|
||||
static constexpr struct tag_table asx_tag_elements[] = {
|
||||
/* is that correct? or should it be COMPOSER or PERFORMER? */
|
||||
{ "author", TAG_ARTIST },
|
||||
|
||||
{ "title", TAG_TITLE },
|
||||
{ nullptr, TAG_NUM_OF_ITEM_TYPES }
|
||||
};
|
||||
|
||||
static void XMLCALL
|
||||
asx_start_element(void *user_data, const XML_Char *element_name,
|
||||
const XML_Char **atts)
|
||||
{
|
||||
AsxParser *parser = (AsxParser *)user_data;
|
||||
parser->value.clear();
|
||||
|
||||
switch (parser->state) {
|
||||
case AsxParser::ROOT:
|
||||
if (StringEqualsCaseASCII(element_name, "entry")) {
|
||||
parser->state = AsxParser::ENTRY;
|
||||
parser->location.clear();
|
||||
parser->tag_type = TAG_NUM_OF_ITEM_TYPES;
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -84,14 +96,17 @@ asx_start_element(void *user_data, const XML_Char *element_name,
|
||||
ExpatParser::GetAttributeCase(atts, "href");
|
||||
if (href != nullptr)
|
||||
parser->location = href;
|
||||
} else if (StringEqualsCaseASCII(element_name, "author"))
|
||||
/* is that correct? or should it be COMPOSER
|
||||
or PERFORMER? */
|
||||
parser->tag_type = TAG_ARTIST;
|
||||
else if (StringEqualsCaseASCII(element_name, "title"))
|
||||
parser->tag_type = TAG_TITLE;
|
||||
} else {
|
||||
parser->tag_type = tag_table_lookup_i(asx_tag_elements,
|
||||
element_name);
|
||||
if (parser->tag_type != TAG_NUM_OF_ITEM_TYPES)
|
||||
parser->state = AsxParser::TAG;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case AsxParser::TAG:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,11 +126,20 @@ asx_end_element(void *user_data, const XML_Char *element_name)
|
||||
parser->tag_builder.Commit());
|
||||
|
||||
parser->state = AsxParser::ROOT;
|
||||
} else
|
||||
parser->tag_type = TAG_NUM_OF_ITEM_TYPES;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case AsxParser::TAG:
|
||||
if (!parser->value.empty())
|
||||
parser->tag_builder.AddItem(parser->tag_type,
|
||||
StringView(parser->value.data(),
|
||||
parser->value.length()));
|
||||
parser->state = AsxParser::ENTRY;
|
||||
break;
|
||||
}
|
||||
|
||||
parser->value.clear();
|
||||
}
|
||||
|
||||
static void XMLCALL
|
||||
@@ -125,13 +149,11 @@ asx_char_data(void *user_data, const XML_Char *s, int len)
|
||||
|
||||
switch (parser->state) {
|
||||
case AsxParser::ROOT:
|
||||
case AsxParser::ENTRY:
|
||||
break;
|
||||
|
||||
case AsxParser::ENTRY:
|
||||
if (parser->tag_type != TAG_NUM_OF_ITEM_TYPES)
|
||||
parser->tag_builder.AddItem(parser->tag_type,
|
||||
StringView(s, len));
|
||||
|
||||
case AsxParser::TAG:
|
||||
parser->value.append(s, len);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@@ -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
|
||||
};
|
||||
|
@@ -23,6 +23,7 @@
|
||||
#include "song/DetachedSong.hxx"
|
||||
#include "input/InputStream.hxx"
|
||||
#include "tag/Builder.hxx"
|
||||
#include "tag/Table.hxx"
|
||||
#include "util/StringView.hxx"
|
||||
#include "lib/expat/ExpatParser.hxx"
|
||||
#include "Log.hxx"
|
||||
@@ -44,8 +45,8 @@ struct XspfParser {
|
||||
*/
|
||||
enum {
|
||||
ROOT, PLAYLIST, TRACKLIST, TRACK,
|
||||
LOCATION,
|
||||
} state;
|
||||
TAG, LOCATION,
|
||||
} state = ROOT;
|
||||
|
||||
/**
|
||||
* The current tag within the "track" element. This is only
|
||||
@@ -61,8 +62,20 @@ struct XspfParser {
|
||||
|
||||
TagBuilder tag_builder;
|
||||
|
||||
XspfParser()
|
||||
:state(ROOT) {}
|
||||
std::string value;
|
||||
};
|
||||
|
||||
static constexpr struct tag_table xspf_tag_elements[] = {
|
||||
{ "title", TAG_TITLE },
|
||||
|
||||
/* TAG_COMPOSER would be more correct according to the XSPF
|
||||
spec */
|
||||
{ "creator", TAG_ARTIST },
|
||||
|
||||
{ "annotation", TAG_COMMENT },
|
||||
{ "album", TAG_ALBUM },
|
||||
{ "trackNum", TAG_TRACK },
|
||||
{ nullptr, TAG_NUM_OF_ITEM_TYPES }
|
||||
};
|
||||
|
||||
static void XMLCALL
|
||||
@@ -70,6 +83,7 @@ xspf_start_element(void *user_data, const XML_Char *element_name,
|
||||
gcc_unused const XML_Char **atts)
|
||||
{
|
||||
XspfParser *parser = (XspfParser *)user_data;
|
||||
parser->value.clear();
|
||||
|
||||
switch (parser->state) {
|
||||
case XspfParser::ROOT:
|
||||
@@ -88,7 +102,6 @@ xspf_start_element(void *user_data, const XML_Char *element_name,
|
||||
if (strcmp(element_name, "track") == 0) {
|
||||
parser->state = XspfParser::TRACK;
|
||||
parser->location.clear();
|
||||
parser->tag_type = TAG_NUM_OF_ITEM_TYPES;
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -96,21 +109,16 @@ xspf_start_element(void *user_data, const XML_Char *element_name,
|
||||
case XspfParser::TRACK:
|
||||
if (strcmp(element_name, "location") == 0)
|
||||
parser->state = XspfParser::LOCATION;
|
||||
else if (strcmp(element_name, "title") == 0)
|
||||
parser->tag_type = TAG_TITLE;
|
||||
else if (strcmp(element_name, "creator") == 0)
|
||||
/* TAG_COMPOSER would be more correct
|
||||
according to the XSPF spec */
|
||||
parser->tag_type = TAG_ARTIST;
|
||||
else if (strcmp(element_name, "annotation") == 0)
|
||||
parser->tag_type = TAG_COMMENT;
|
||||
else if (strcmp(element_name, "album") == 0)
|
||||
parser->tag_type = TAG_ALBUM;
|
||||
else if (strcmp(element_name, "trackNum") == 0)
|
||||
parser->tag_type = TAG_TRACK;
|
||||
else if (!parser->location.empty()) {
|
||||
parser->tag_type = tag_table_lookup(xspf_tag_elements,
|
||||
element_name);
|
||||
if (parser->tag_type != TAG_NUM_OF_ITEM_TYPES)
|
||||
parser->state = XspfParser::TAG;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case XspfParser::TAG:
|
||||
case XspfParser::LOCATION:
|
||||
break;
|
||||
}
|
||||
@@ -144,15 +152,26 @@ xspf_end_element(void *user_data, const XML_Char *element_name)
|
||||
parser->tag_builder.Commit());
|
||||
|
||||
parser->state = XspfParser::TRACKLIST;
|
||||
} else
|
||||
parser->tag_type = TAG_NUM_OF_ITEM_TYPES;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case XspfParser::TAG:
|
||||
if (!parser->value.empty())
|
||||
parser->tag_builder.AddItem(parser->tag_type,
|
||||
StringView(parser->value.data(),
|
||||
parser->value.length()));
|
||||
|
||||
parser->state = XspfParser::TRACK;
|
||||
break;
|
||||
|
||||
case XspfParser::LOCATION:
|
||||
parser->location = std::move(parser->value);
|
||||
parser->state = XspfParser::TRACK;
|
||||
break;
|
||||
}
|
||||
|
||||
parser->value.clear();
|
||||
}
|
||||
|
||||
static void XMLCALL
|
||||
@@ -164,19 +183,12 @@ xspf_char_data(void *user_data, const XML_Char *s, int len)
|
||||
case XspfParser::ROOT:
|
||||
case XspfParser::PLAYLIST:
|
||||
case XspfParser::TRACKLIST:
|
||||
break;
|
||||
|
||||
case XspfParser::TRACK:
|
||||
if (!parser->location.empty() &&
|
||||
parser->tag_type != TAG_NUM_OF_ITEM_TYPES)
|
||||
parser->tag_builder.AddItem(parser->tag_type,
|
||||
StringView(s, len));
|
||||
|
||||
break;
|
||||
|
||||
case XspfParser::TAG:
|
||||
case XspfParser::LOCATION:
|
||||
parser->location.assign(s, len);
|
||||
|
||||
parser->value.append(s, len);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@@ -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));
|
||||
|
||||
|
@@ -144,21 +144,12 @@ LocalStorage::OpenDirectory(const char *uri_utf8)
|
||||
return std::make_unique<LocalDirectoryReader>(MapFSOrThrow(uri_utf8));
|
||||
}
|
||||
|
||||
gcc_pure
|
||||
static bool
|
||||
SkipNameFS(PathTraitsFS::const_pointer_type name_fs) noexcept
|
||||
{
|
||||
return name_fs[0] == '.' &&
|
||||
(name_fs[1] == 0 ||
|
||||
(name_fs[1] == '.' && name_fs[2] == 0));
|
||||
}
|
||||
|
||||
const char *
|
||||
LocalDirectoryReader::Read() noexcept
|
||||
{
|
||||
while (reader.ReadEntry()) {
|
||||
const Path name_fs = reader.GetEntry();
|
||||
if (SkipNameFS(name_fs.c_str()))
|
||||
if (PathTraitsFS::IsSpecialFilename(name_fs.c_str()))
|
||||
continue;
|
||||
|
||||
try {
|
||||
|
@@ -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();
|
||||
}
|
||||
|
||||
|
@@ -89,7 +89,7 @@ public:
|
||||
}
|
||||
|
||||
void Unset(TagType tag) {
|
||||
*this |= ~TagMask(tag);
|
||||
*this &= ~TagMask(tag);
|
||||
}
|
||||
};
|
||||
|
||||
|
@@ -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__ */
|
||||
|
@@ -37,6 +37,7 @@
|
||||
#include <stdexcept>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
StringBuffer<64>
|
||||
FormatISO8601(const struct tm &tm) noexcept
|
||||
|
41
src/util/Math.hxx
Normal file
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,
|
||||
],
|
||||
)
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user