Compare commits
68 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6c240f667c | ||
|
|
3040ddb5ec | ||
|
|
fdb28eb0c4 | ||
|
|
7ded244a61 | ||
|
|
8ed533acf3 | ||
|
|
a27580d0cc | ||
|
|
905db05cf9 | ||
|
|
4242aee21e | ||
|
|
e71bd2a08b | ||
|
|
e53a4d0a9e | ||
|
|
159389164a | ||
|
|
0a92fbc18e | ||
|
|
138c29320b | ||
|
|
8f00dbea45 | ||
|
|
f3fd2eb618 | ||
|
|
fc92db83cf | ||
|
|
3b0f8d5516 | ||
|
|
a5273d6992 | ||
|
|
b18074f899 | ||
|
|
3d8067a041 | ||
|
|
f6fe001fa9 | ||
|
|
32a5bf043b | ||
|
|
8d2079482f | ||
|
|
c331c75fde | ||
|
|
6080c3b4ba | ||
|
|
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 |
16
.travis.yml
16
.travis.yml
@@ -126,6 +126,22 @@ jobs:
|
|||||||
packages:
|
packages:
|
||||||
- ccache
|
- ccache
|
||||||
- meson
|
- meson
|
||||||
|
- icu4c
|
||||||
|
- ffmpeg
|
||||||
|
- libnfs
|
||||||
|
- yajl
|
||||||
|
- libupnp
|
||||||
|
- libid3tag
|
||||||
|
- chromaprint
|
||||||
|
- libsamplerate
|
||||||
|
- libsoxr
|
||||||
|
- libzzip
|
||||||
|
- flac
|
||||||
|
- opus
|
||||||
|
- libvorbis
|
||||||
|
- faad2
|
||||||
|
- wavpack
|
||||||
|
- libmpdclient
|
||||||
update: true
|
update: true
|
||||||
env:
|
env:
|
||||||
- MATRIX_EVAL="export PATH=/usr/local/opt/ccache/libexec:$PATH HOMEBREW_NO_ANALYTICS=1"
|
- MATRIX_EVAL="export PATH=/usr/local/opt/ccache/libexec:$PATH HOMEBREW_NO_ANALYTICS=1"
|
||||||
|
|||||||
33
NEWS
33
NEWS
@@ -1,3 +1,36 @@
|
|||||||
|
ver 0.21.23 (2020/04/23)
|
||||||
|
* protocol
|
||||||
|
- add tag fallback for AlbumSort
|
||||||
|
* storage
|
||||||
|
- curl: fix corrupt "href" values in the presence of XML entities
|
||||||
|
- curl: unescape "href" values
|
||||||
|
* input
|
||||||
|
- nfs: fix crash bug
|
||||||
|
- nfs: fix freeze bug on reconnect
|
||||||
|
* decoder
|
||||||
|
- gme: adapt to API change in the upcoming version 0.7.0
|
||||||
|
* output
|
||||||
|
- alsa: implement channel mapping for 5.0 and 7.0
|
||||||
|
* player
|
||||||
|
- drain outputs at end of song in "single" mode
|
||||||
|
* Windows
|
||||||
|
- fix case insensitive search
|
||||||
|
|
||||||
|
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)
|
ver 0.21.21 (2020/03/19)
|
||||||
* configuration
|
* configuration
|
||||||
- fix bug in "metadata_to_use" setting
|
- fix bug in "metadata_to_use" setting
|
||||||
|
|||||||
@@ -2,18 +2,25 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="org.musicpd"
|
package="org.musicpd"
|
||||||
android:installLocation="auto"
|
android:installLocation="auto"
|
||||||
android:versionCode="44"
|
android:versionCode="46"
|
||||||
android:versionName="0.21.21">
|
android:versionName="0.21.23">
|
||||||
|
|
||||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28"/>
|
<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.READ_EXTERNAL_STORAGE"/>
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
|
|
||||||
<application android:allowBackup="true"
|
<application android:allowBackup="true"
|
||||||
android:icon="@drawable/icon"
|
android:icon="@drawable/icon"
|
||||||
|
android:banner="@drawable/icon"
|
||||||
android:label="@string/app_name">
|
android:label="@string/app_name">
|
||||||
<activity android:name=".Settings"
|
<activity android:name=".Settings"
|
||||||
android:label="@string/app_name">
|
android:label="@string/app_name">
|
||||||
@@ -22,6 +29,14 @@
|
|||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</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">
|
<receiver android:name=".Receiver">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ package org.musicpd;
|
|||||||
|
|
||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
|
import android.app.NotificationManager;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.app.Service;
|
import android.app.Service;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
@@ -35,6 +36,9 @@ import android.os.RemoteException;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.widget.RemoteViews;
|
import android.widget.RemoteViews;
|
||||||
|
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
public class Main extends Service implements Runnable {
|
public class Main extends Service implements Runnable {
|
||||||
private static final String TAG = "Main";
|
private static final String TAG = "Main";
|
||||||
private static final String REMOTE_ERROR = "MPD process was killed";
|
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);
|
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() {
|
private void start() {
|
||||||
if (mThread != null)
|
if (mThread != null)
|
||||||
return;
|
return;
|
||||||
mThread = new Thread(this);
|
|
||||||
mThread.start();
|
|
||||||
|
|
||||||
final Intent mainIntent = new Intent(this, Settings.class);
|
final Intent mainIntent = new Intent(this, Settings.class);
|
||||||
mainIntent.setAction("android.intent.action.MAIN");
|
mainIntent.setAction("android.intent.action.MAIN");
|
||||||
@@ -168,13 +197,25 @@ public class Main extends Service implements Runnable {
|
|||||||
final PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
|
final PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
|
||||||
mainIntent, PendingIntent.FLAG_CANCEL_CURRENT);
|
mainIntent, PendingIntent.FLAG_CANCEL_CURRENT);
|
||||||
|
|
||||||
Notification notification = new Notification.Builder(this)
|
Notification.Builder nBuilder;
|
||||||
.setContentTitle(getText(R.string.notification_title_mpd_running))
|
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))
|
.setContentText(getText(R.string.notification_text_mpd_running))
|
||||||
.setSmallIcon(R.drawable.notification_icon)
|
.setSmallIcon(R.drawable.notification_icon)
|
||||||
.setContentIntent(contentIntent)
|
.setContentIntent(contentIntent)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
mThread = new Thread(this);
|
||||||
|
mThread.start();
|
||||||
|
|
||||||
startForeground(R.string.notification_title_mpd_running, notification);
|
startForeground(R.string.notification_title_mpd_running, notification);
|
||||||
startService(new Intent(this, Main.class));
|
startService(new Intent(this, Main.class));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,12 +105,13 @@ public class Settings extends Activity {
|
|||||||
else
|
else
|
||||||
mRunButton.setChecked(false);
|
mRunButton.setChecked(false);
|
||||||
mFirstRun = true;
|
mFirstRun = true;
|
||||||
|
mTextStatus.setText("");
|
||||||
break;
|
break;
|
||||||
case MSG_STARTED:
|
case MSG_STARTED:
|
||||||
Log.d(TAG, "onStarted");
|
Log.d(TAG, "onStarted");
|
||||||
mRunButton.setChecked(true);
|
mRunButton.setChecked(true);
|
||||||
mFirstRun = true;
|
mFirstRun = true;
|
||||||
mTextStatus.setText("CAUTION: this version is EXPERIMENTAL!"); // XXX
|
mTextStatus.setText("MPD service started");
|
||||||
break;
|
break;
|
||||||
case MSG_LOG:
|
case MSG_LOG:
|
||||||
if (mLogListArray.size() > MAX_LOGS)
|
if (mLogListArray.size() > MAX_LOGS)
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ author = 'Max Kellermann'
|
|||||||
# built documents.
|
# built documents.
|
||||||
#
|
#
|
||||||
# The short X.Y version.
|
# The short X.Y version.
|
||||||
version = '0.21.21'
|
version = '0.21.23'
|
||||||
# The full version, including alpha/beta/rc tags.
|
# The full version, including alpha/beta/rc tags.
|
||||||
release = version
|
release = version
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
project(
|
project(
|
||||||
'mpd',
|
'mpd',
|
||||||
['c', 'cpp'],
|
['c', 'cpp'],
|
||||||
version: '0.21.21',
|
version: '0.21.23',
|
||||||
meson_version: '>= 0.49.0',
|
meson_version: '>= 0.49.0',
|
||||||
default_options: [
|
default_options: [
|
||||||
'c_std=c99',
|
'c_std=c99',
|
||||||
@@ -290,6 +290,7 @@ if not is_android
|
|||||||
else
|
else
|
||||||
sources += [
|
sources += [
|
||||||
'src/android/Context.cxx',
|
'src/android/Context.cxx',
|
||||||
|
'src/android/AudioManager.cxx',
|
||||||
'src/android/Environment.cxx',
|
'src/android/Environment.cxx',
|
||||||
'src/android/LogListener.cxx',
|
'src/android/LogListener.cxx',
|
||||||
]
|
]
|
||||||
@@ -311,6 +312,7 @@ subdir('src/util')
|
|||||||
subdir('src/time')
|
subdir('src/time')
|
||||||
subdir('src/system')
|
subdir('src/system')
|
||||||
subdir('src/thread')
|
subdir('src/thread')
|
||||||
|
subdir('src/net')
|
||||||
subdir('src/event')
|
subdir('src/event')
|
||||||
|
|
||||||
subdir('src/lib/dbus')
|
subdir('src/lib/dbus')
|
||||||
@@ -335,7 +337,6 @@ subdir('src/lib/yajl')
|
|||||||
|
|
||||||
subdir('src/fs')
|
subdir('src/fs')
|
||||||
subdir('src/config')
|
subdir('src/config')
|
||||||
subdir('src/net')
|
|
||||||
subdir('src/tag')
|
subdir('src/tag')
|
||||||
subdir('src/pcm')
|
subdir('src/pcm')
|
||||||
subdir('src/neighbor')
|
subdir('src/neighbor')
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ from build.ffmpeg import FfmpegProject
|
|||||||
from build.boost import BoostProject
|
from build.boost import BoostProject
|
||||||
|
|
||||||
libmpdclient = MesonProject(
|
libmpdclient = MesonProject(
|
||||||
'https://www.musicpd.org/download/libmpdclient/2/libmpdclient-2.17.tar.xz',
|
'https://www.musicpd.org/download/libmpdclient/2/libmpdclient-2.18.tar.xz',
|
||||||
'ee9b8f1c7e95b65c8f18a354daf7b16bfcd455fc52a0f3b5abe402316bce3559',
|
'4cb01e1f567e0169aca94875fb6e1200e7f5ce35b63a4df768ec1591fb1081fa',
|
||||||
'lib/libmpdclient.a',
|
'lib/libmpdclient.a',
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -341,8 +341,8 @@ ffmpeg = FfmpegProject(
|
|||||||
)
|
)
|
||||||
|
|
||||||
curl = AutotoolsProject(
|
curl = AutotoolsProject(
|
||||||
'http://curl.haxx.se/download/curl-7.68.0.tar.xz',
|
'http://curl.haxx.se/download/curl-7.69.1.tar.xz',
|
||||||
'b724240722276a27f6e770b952121a3afd097129d8c9fe18e6272dc34192035a',
|
'03c7d5e6697f7b7e40ada1b2256e565a555657398e6c1fcfa4cb251ccd819d4f',
|
||||||
'lib/libcurl.a',
|
'lib/libcurl.a',
|
||||||
[
|
[
|
||||||
'--disable-shared', '--enable-static',
|
'--disable-shared', '--enable-static',
|
||||||
|
|||||||
@@ -33,11 +33,11 @@
|
|||||||
#include "playlist/PlaylistRegistry.hxx"
|
#include "playlist/PlaylistRegistry.hxx"
|
||||||
#include "playlist/PlaylistPlugin.hxx"
|
#include "playlist/PlaylistPlugin.hxx"
|
||||||
#include "fs/AllocatedPath.hxx"
|
#include "fs/AllocatedPath.hxx"
|
||||||
|
#include "fs/NarrowPath.hxx"
|
||||||
#include "fs/Traits.hxx"
|
#include "fs/Traits.hxx"
|
||||||
#include "fs/FileSystem.hxx"
|
#include "fs/FileSystem.hxx"
|
||||||
#include "fs/StandardDirectory.hxx"
|
#include "fs/StandardDirectory.hxx"
|
||||||
#include "system/Error.hxx"
|
#include "system/Error.hxx"
|
||||||
#include "util/Macros.hxx"
|
|
||||||
#include "util/RuntimeError.hxx"
|
#include "util/RuntimeError.hxx"
|
||||||
#include "util/Domain.hxx"
|
#include "util/Domain.hxx"
|
||||||
#include "util/OptionDef.hxx"
|
#include "util/OptionDef.hxx"
|
||||||
@@ -380,17 +380,7 @@ ParseCommandLine(int argc, char **argv, struct options &options,
|
|||||||
|
|
||||||
if (config_file != nullptr) {
|
if (config_file != nullptr) {
|
||||||
/* use specified configuration file */
|
/* use specified configuration file */
|
||||||
#ifdef _UNICODE
|
ReadConfigFile(config, FromNarrowPath(config_file));
|
||||||
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
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,9 +29,9 @@
|
|||||||
#include "system/Clock.hxx"
|
#include "system/Clock.hxx"
|
||||||
#include "Log.hxx"
|
#include "Log.hxx"
|
||||||
#include "time/ChronoUtil.hxx"
|
#include "time/ChronoUtil.hxx"
|
||||||
|
#include "util/Math.hxx"
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <cmath>
|
|
||||||
|
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
/**
|
/**
|
||||||
@@ -121,7 +121,7 @@ stats_print(Response &r, const Partition &partition)
|
|||||||
#else
|
#else
|
||||||
(unsigned)std::chrono::duration_cast<std::chrono::seconds>(std::chrono::steady_clock::now() - start_time).count(),
|
(unsigned)std::chrono::duration_cast<std::chrono::seconds>(std::chrono::steady_clock::now() - start_time).count(),
|
||||||
#endif
|
#endif
|
||||||
std::lround(partition.pc.GetTotalPlayTime().count()));
|
lround(partition.pc.GetTotalPlayTime().count()));
|
||||||
|
|
||||||
#ifdef ENABLE_DATABASE
|
#ifdef ENABLE_DATABASE
|
||||||
const Database *db = partition.instance.GetDatabase();
|
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
|
* http://www.musicpd.org
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
@@ -20,10 +20,13 @@
|
|||||||
#include "Context.hxx"
|
#include "Context.hxx"
|
||||||
#include "java/Class.hxx"
|
#include "java/Class.hxx"
|
||||||
#include "java/File.hxx"
|
#include "java/File.hxx"
|
||||||
|
#include "java/String.hxx"
|
||||||
#include "fs/AllocatedPath.hxx"
|
#include "fs/AllocatedPath.hxx"
|
||||||
|
|
||||||
|
#include "AudioManager.hxx"
|
||||||
|
|
||||||
AllocatedPath
|
AllocatedPath
|
||||||
Context::GetCacheDir(JNIEnv *env) const
|
Context::GetCacheDir(JNIEnv *env) const noexcept
|
||||||
{
|
{
|
||||||
assert(env != nullptr);
|
assert(env != nullptr);
|
||||||
|
|
||||||
@@ -40,3 +43,21 @@ Context::GetCacheDir(JNIEnv *env) const
|
|||||||
|
|
||||||
return Java::File::ToAbsolutePath(env, file);
|
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
|
* http://www.musicpd.org
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
@@ -23,13 +23,18 @@
|
|||||||
#include "java/Object.hxx"
|
#include "java/Object.hxx"
|
||||||
|
|
||||||
class AllocatedPath;
|
class AllocatedPath;
|
||||||
|
class AudioManager;
|
||||||
|
|
||||||
class Context : public Java::Object {
|
class Context : public Java::GlobalObject {
|
||||||
public:
|
public:
|
||||||
Context(JNIEnv *env, jobject obj):Java::Object(env, obj) {}
|
Context(JNIEnv *env, jobject obj) noexcept
|
||||||
|
:Java::GlobalObject(env, obj) {}
|
||||||
|
|
||||||
gcc_pure
|
gcc_pure
|
||||||
AllocatedPath GetCacheDir(JNIEnv *env) const;
|
AllocatedPath GetCacheDir(JNIEnv *env) const noexcept;
|
||||||
|
|
||||||
|
gcc_pure
|
||||||
|
AudioManager *GetAudioManager(JNIEnv *env) noexcept;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -22,9 +22,9 @@
|
|||||||
|
|
||||||
#include "java/Object.hxx"
|
#include "java/Object.hxx"
|
||||||
|
|
||||||
class LogListener : public Java::Object {
|
class LogListener : public Java::GlobalObject {
|
||||||
public:
|
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;
|
void OnLog(JNIEnv *env, int priority, const char *fmt, ...) const;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -34,13 +34,12 @@
|
|||||||
#include "util/StringBuffer.hxx"
|
#include "util/StringBuffer.hxx"
|
||||||
#include "util/ScopeExit.hxx"
|
#include "util/ScopeExit.hxx"
|
||||||
#include "util/Exception.hxx"
|
#include "util/Exception.hxx"
|
||||||
|
#include "util/Math.hxx"
|
||||||
|
|
||||||
#ifdef ENABLE_DATABASE
|
#ifdef ENABLE_DATABASE
|
||||||
#include "db/update/Service.hxx"
|
#include "db/update/Service.hxx"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <cmath>
|
|
||||||
|
|
||||||
#define COMMAND_STATUS_STATE "state"
|
#define COMMAND_STATUS_STATE "state"
|
||||||
#define COMMAND_STATUS_REPEAT "repeat"
|
#define COMMAND_STATUS_REPEAT "repeat"
|
||||||
#define COMMAND_STATUS_SINGLE "single"
|
#define COMMAND_STATUS_SINGLE "single"
|
||||||
@@ -154,7 +153,7 @@ handle_status(Client &client, gcc_unused Request args, Response &r)
|
|||||||
|
|
||||||
if (pc.GetCrossFade() > FloatDuration::zero())
|
if (pc.GetCrossFade() > FloatDuration::zero())
|
||||||
r.Format(COMMAND_STATUS_CROSSFADE ": %lu\n",
|
r.Format(COMMAND_STATUS_CROSSFADE ": %lu\n",
|
||||||
std::lround(pc.GetCrossFade().count()));
|
lround(pc.GetCrossFade().count()));
|
||||||
|
|
||||||
if (pc.GetMixRampDelay() > FloatDuration::zero())
|
if (pc.GetMixRampDelay() > FloatDuration::zero())
|
||||||
r.Format(COMMAND_STATUS_MIXRAMPDELAY ": %f\n",
|
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",
|
COMMAND_STATUS_BITRATE ": %u\n",
|
||||||
player_status.elapsed_time.RoundS(),
|
player_status.elapsed_time.RoundS(),
|
||||||
player_status.total_time.IsNegative()
|
player_status.total_time.IsNegative()
|
||||||
? 0u
|
? 0U
|
||||||
: unsigned(player_status.total_time.RoundS()),
|
: unsigned(player_status.total_time.RoundS()),
|
||||||
player_status.elapsed_time.ToDoubleS(),
|
player_status.elapsed_time.ToDoubleS(),
|
||||||
player_status.bit_rate);
|
player_status.bit_rate);
|
||||||
|
|||||||
@@ -448,7 +448,7 @@ ProxyDatabase::ProxyDatabase(EventLoop &_loop, DatabaseListener &_listener,
|
|||||||
listener(_listener),
|
listener(_listener),
|
||||||
host(block.GetBlockValue("host", "")),
|
host(block.GetBlockValue("host", "")),
|
||||||
password(block.GetBlockValue("password", "")),
|
password(block.GetBlockValue("password", "")),
|
||||||
port(block.GetBlockValue("port", 0u)),
|
port(block.GetBlockValue("port", 0U)),
|
||||||
keepalive(block.GetBlockValue("keepalive", false))
|
keepalive(block.GetBlockValue("keepalive", false))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -517,7 +517,7 @@ ProxyDatabase::Connect()
|
|||||||
(void)keepalive;
|
(void)keepalive;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
idle_received = ~0u;
|
idle_received = ~0U;
|
||||||
is_idle = false;
|
is_idle = false;
|
||||||
|
|
||||||
SocketMonitor::Open(SocketDescriptor(mpd_async_get_fd(mpd_connection_get_async(connection))));
|
SocketMonitor::Open(SocketDescriptor(mpd_async_get_fd(mpd_connection_get_async(connection))));
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
#include "fs/Traits.hxx"
|
#include "fs/Traits.hxx"
|
||||||
#include "util/Alloc.hxx"
|
#include "util/Alloc.hxx"
|
||||||
#include "util/DeleteDisposer.hxx"
|
#include "util/DeleteDisposer.hxx"
|
||||||
|
#include "util/StringCompare.hxx"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -69,7 +70,15 @@ Directory::GetName() const noexcept
|
|||||||
{
|
{
|
||||||
assert(!IsRoot());
|
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 *
|
Directory *
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ public:
|
|||||||
|
|
||||||
~UpdateService();
|
~UpdateService();
|
||||||
|
|
||||||
EventLoop &GetEventLoop() noexcept {
|
auto &GetEventLoop() const noexcept {
|
||||||
return defer.GetEventLoop();
|
return defer.GetEventLoop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ adplug_init(const ConfigBlock &block)
|
|||||||
FormatDebug(adplug_domain, "adplug %s",
|
FormatDebug(adplug_domain, "adplug %s",
|
||||||
CAdPlug::get_version().c_str());
|
CAdPlug::get_version().c_str());
|
||||||
|
|
||||||
sample_rate = block.GetPositiveValue("sample_rate", 48000u);
|
sample_rate = block.GetPositiveValue("sample_rate", 48000U);
|
||||||
CheckSampleRate(sample_rate);
|
CheckSampleRate(sample_rate);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -26,11 +26,11 @@
|
|||||||
#include "util/ScopeExit.hxx"
|
#include "util/ScopeExit.hxx"
|
||||||
#include "util/ConstBuffer.hxx"
|
#include "util/ConstBuffer.hxx"
|
||||||
#include "util/Domain.hxx"
|
#include "util/Domain.hxx"
|
||||||
|
#include "util/Math.hxx"
|
||||||
#include "Log.hxx"
|
#include "Log.hxx"
|
||||||
|
|
||||||
#include <neaacdec.h>
|
#include <neaacdec.h>
|
||||||
|
|
||||||
#include <cmath>
|
|
||||||
#include <exception>
|
#include <exception>
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|||||||
@@ -297,7 +297,7 @@ FfmpegReceiveFrames(DecoderClient &client, InputStream &is,
|
|||||||
*/
|
*/
|
||||||
static DecoderCommand
|
static DecoderCommand
|
||||||
ffmpeg_send_packet(DecoderClient &client, InputStream &is,
|
ffmpeg_send_packet(DecoderClient &client, InputStream &is,
|
||||||
AVPacket &&packet,
|
const AVPacket &packet,
|
||||||
AVCodecContext &codec_context,
|
AVCodecContext &codec_context,
|
||||||
const AVStream &stream,
|
const AVStream &stream,
|
||||||
AVFrame &frame,
|
AVFrame &frame,
|
||||||
@@ -350,24 +350,6 @@ ffmpeg_send_packet(DecoderClient &client, InputStream &is,
|
|||||||
return cmd;
|
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
|
gcc_const
|
||||||
static SampleFormat
|
static SampleFormat
|
||||||
ffmpeg_sample_format(enum AVSampleFormat sample_fmt) noexcept
|
ffmpeg_sample_format(enum AVSampleFormat sample_fmt) noexcept
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ fluidsynth_mpd_log_function(int level,
|
|||||||
static bool
|
static bool
|
||||||
fluidsynth_init(const ConfigBlock &block)
|
fluidsynth_init(const ConfigBlock &block)
|
||||||
{
|
{
|
||||||
sample_rate = block.GetPositiveValue("sample_rate", 48000u);
|
sample_rate = block.GetPositiveValue("sample_rate", 48000U);
|
||||||
CheckSampleRate(sample_rate);
|
CheckSampleRate(sample_rate);
|
||||||
|
|
||||||
soundfont_path = block.GetBlockValue("soundfont",
|
soundfont_path = block.GetBlockValue("soundfont",
|
||||||
|
|||||||
@@ -185,7 +185,11 @@ gme_file_decode(DecoderClient &client, Path path_fs)
|
|||||||
LogWarning(gme_domain, gme_err);
|
LogWarning(gme_domain, gme_err);
|
||||||
|
|
||||||
if (length > 0)
|
if (length > 0)
|
||||||
gme_set_fade(emu, length);
|
gme_set_fade(emu, length
|
||||||
|
#if GME_VERSION >= 0x000700
|
||||||
|
, 8000
|
||||||
|
#endif
|
||||||
|
);
|
||||||
|
|
||||||
/* play */
|
/* play */
|
||||||
DecoderCommand cmd;
|
DecoderCommand cmd;
|
||||||
|
|||||||
@@ -186,7 +186,7 @@ HybridDsdDecode(DecoderClient &client, InputStream &input)
|
|||||||
client.Ready(result.first, true, duration);
|
client.Ready(result.first, true, duration);
|
||||||
frame_size = result.first.GetFrameSize();
|
frame_size = result.first.GetFrameSize();
|
||||||
kbit_rate = frame_size * result.first.sample_rate /
|
kbit_rate = frame_size * result.first.sample_rate /
|
||||||
(1024u / 8u);
|
(1024U / 8U);
|
||||||
total_frames = result.second / frame_size;
|
total_frames = result.second / frame_size;
|
||||||
} catch (UnsupportedFile) {
|
} catch (UnsupportedFile) {
|
||||||
/* not a Hybrid-DSD file; let the next decoder plugin
|
/* not a Hybrid-DSD file; let the next decoder plugin
|
||||||
@@ -236,7 +236,7 @@ HybridDsdDecode(DecoderClient &client, InputStream &input)
|
|||||||
/* fill the buffer */
|
/* fill the buffer */
|
||||||
auto w = buffer.Write();
|
auto w = buffer.Write();
|
||||||
if (!w.empty()) {
|
if (!w.empty()) {
|
||||||
if (remaining_bytes < (1<<30ull) &&
|
if (remaining_bytes < (1<<30ULL) &&
|
||||||
w.size > size_t(remaining_bytes))
|
w.size > size_t(remaining_bytes))
|
||||||
w.size = remaining_bytes;
|
w.size = remaining_bytes;
|
||||||
|
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ mikmod_decoder_init(const ConfigBlock &block)
|
|||||||
static char params[] = "";
|
static char params[] = "";
|
||||||
|
|
||||||
mikmod_loop = block.GetBlockValue("loop", false);
|
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))
|
if (!audio_valid_sample_rate(mikmod_sample_rate))
|
||||||
throw FormatRuntimeError("Invalid sample rate in line %d: %u",
|
throw FormatRuntimeError("Invalid sample rate in line %d: %u",
|
||||||
block.line, mikmod_sample_rate);
|
block.line, mikmod_sample_rate);
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ sidplay_init(const ConfigBlock &block)
|
|||||||
if (!database_path.IsNull())
|
if (!database_path.IsNull())
|
||||||
songlength_database = sidplay_load_songlength_db(database_path);
|
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 =
|
all_files_are_containers =
|
||||||
block.GetBlockValue("all_files_are_containers", true);
|
block.GetBlockValue("all_files_are_containers", true);
|
||||||
@@ -387,7 +387,7 @@ sidplay_file_decode(DecoderClient &client, Path path_fs)
|
|||||||
const unsigned timebase = player.timebase();
|
const unsigned timebase = player.timebase();
|
||||||
#endif
|
#endif
|
||||||
const unsigned end = duration.IsNegative()
|
const unsigned end = duration.IsNegative()
|
||||||
? 0u
|
? 0U
|
||||||
: duration.ToScale<uint64_t>(timebase);
|
: duration.ToScale<uint64_t>(timebase);
|
||||||
|
|
||||||
DecoderCommand cmd;
|
DecoderCommand cmd;
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
PreparedFlacEncoder::PreparedFlacEncoder(const ConfigBlock &block)
|
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");
|
throw std::runtime_error("Invalid bit rate");
|
||||||
}
|
}
|
||||||
|
|
||||||
complexity = block.GetBlockValue("complexity", 10u);
|
complexity = block.GetBlockValue("complexity", 10U);
|
||||||
if (complexity > 10)
|
if (complexity > 10)
|
||||||
throw std::runtime_error("Invalid complexity");
|
throw std::runtime_error("Invalid complexity");
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ public:
|
|||||||
Cancel();
|
Cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
EventLoop &GetEventLoop() noexcept {
|
EventLoop &GetEventLoop() const noexcept {
|
||||||
return loop;
|
return loop;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ public:
|
|||||||
:defer(_loop, BIND_THIS_METHOD(RunDeferred)),
|
:defer(_loop, BIND_THIS_METHOD(RunDeferred)),
|
||||||
callback(_callback), pending_mask(0) {}
|
callback(_callback), pending_mask(0) {}
|
||||||
|
|
||||||
EventLoop &GetEventLoop() {
|
auto &GetEventLoop() const noexcept {
|
||||||
return defer.GetEventLoop();
|
return defer.GetEventLoop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,8 +23,8 @@
|
|||||||
|
|
||||||
#include "PollGroupWinSelect.hxx"
|
#include "PollGroupWinSelect.hxx"
|
||||||
|
|
||||||
constexpr int EVENT_READ = 0;
|
static constexpr int EVENT_READ = 0;
|
||||||
constexpr int EVENT_WRITE = 1;
|
static constexpr int EVENT_WRITE = 1;
|
||||||
|
|
||||||
static constexpr
|
static constexpr
|
||||||
bool HasEvent(unsigned events, int event_id) noexcept
|
bool HasEvent(unsigned events, int event_id) noexcept
|
||||||
|
|||||||
@@ -20,6 +20,10 @@
|
|||||||
#include "SocketMonitor.hxx"
|
#include "SocketMonitor.hxx"
|
||||||
#include "Loop.hxx"
|
#include "Loop.hxx"
|
||||||
|
|
||||||
|
#ifdef USE_EPOLL
|
||||||
|
#include <cerrno>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
@@ -86,6 +90,21 @@ SocketMonitor::Schedule(unsigned flags) noexcept
|
|||||||
|
|
||||||
if (success)
|
if (success)
|
||||||
scheduled_flags = flags;
|
scheduled_flags = flags;
|
||||||
|
#ifdef USE_EPOLL
|
||||||
|
else if (errno == EBADF || errno == ENOENT)
|
||||||
|
/* the socket was probably closed by somebody else
|
||||||
|
(EBADF) or a new file descriptor with the same
|
||||||
|
number was created but not registered already
|
||||||
|
(ENOENT) - we can assume that there are no
|
||||||
|
scheduled events */
|
||||||
|
/* note that when this happens, we're actually lucky
|
||||||
|
that it has failed - imagine another thread may
|
||||||
|
meanwhile have created something on the same file
|
||||||
|
descriptor number, and has registered it; the
|
||||||
|
epoll_ctl() call above would then have succeeded,
|
||||||
|
but broke the other thread's epoll registration */
|
||||||
|
scheduled_flags = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ public:
|
|||||||
|
|
||||||
~SocketMonitor() noexcept;
|
~SocketMonitor() noexcept;
|
||||||
|
|
||||||
EventLoop &GetEventLoop() noexcept {
|
auto &GetEventLoop() const noexcept {
|
||||||
return loop;
|
return loop;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,7 +109,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool ScheduleRead() noexcept {
|
bool ScheduleRead() noexcept {
|
||||||
return Schedule(GetScheduledFlags() | READ | HANGUP | ERROR);
|
return Schedule(GetScheduledFlags() | READ);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ScheduleWrite() noexcept {
|
bool ScheduleWrite() noexcept {
|
||||||
@@ -117,7 +117,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CancelRead() noexcept {
|
void CancelRead() noexcept {
|
||||||
Schedule(GetScheduledFlags() & ~(READ|HANGUP|ERROR));
|
Schedule(GetScheduledFlags() & ~READ);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CancelWrite() noexcept {
|
void CancelWrite() noexcept {
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ public:
|
|||||||
Cancel();
|
Cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
EventLoop &GetEventLoop() noexcept {
|
auto &GetEventLoop() const noexcept {
|
||||||
return loop;
|
return loop;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ event_dep = declare_dependency(
|
|||||||
link_with: event,
|
link_with: event,
|
||||||
dependencies: [
|
dependencies: [
|
||||||
thread_dep,
|
thread_dep,
|
||||||
|
net_dep,
|
||||||
system_dep,
|
system_dep,
|
||||||
boost_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
|
#define MPD_FS_NARROW_PATH_HXX
|
||||||
|
|
||||||
#include "Path.hxx"
|
#include "Path.hxx"
|
||||||
#include "util/Macros.hxx"
|
|
||||||
|
|
||||||
#ifdef _UNICODE
|
#ifdef _UNICODE
|
||||||
#include "lib/icu/Win32.hxx"
|
#include "AllocatedPath.hxx"
|
||||||
#include "util/AllocatedString.hxx"
|
#include "util/AllocatedString.hxx"
|
||||||
#include <windows.h>
|
|
||||||
#else
|
#else
|
||||||
#include "util/StringPointer.hxx"
|
#include "util/StringPointer.hxx"
|
||||||
#endif
|
#endif
|
||||||
@@ -48,12 +46,7 @@ class NarrowPath {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
#ifdef _UNICODE
|
#ifdef _UNICODE
|
||||||
explicit NarrowPath(Path _path)
|
explicit NarrowPath(Path _path) noexcept;
|
||||||
:value(WideCharToMultiByte(CP_ACP, _path.c_str())) {
|
|
||||||
if (value.IsNull())
|
|
||||||
/* fall back to empty string */
|
|
||||||
value = Value::Empty();
|
|
||||||
}
|
|
||||||
#else
|
#else
|
||||||
explicit NarrowPath(Path _path):value(_path.c_str()) {}
|
explicit NarrowPath(Path _path):value(_path.c_str()) {}
|
||||||
#endif
|
#endif
|
||||||
@@ -67,4 +60,43 @@ 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 {
|
||||||
|
#ifdef _UNICODE
|
||||||
|
if (value.IsNull())
|
||||||
|
return nullptr;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ class BufferedReader {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
explicit BufferedReader(Reader &_reader) noexcept
|
explicit BufferedReader(Reader &_reader) noexcept
|
||||||
:reader(_reader), buffer(4096) {}
|
:reader(_reader), buffer(16384) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reset the internal state. Should be called after rewinding
|
* Reset the internal state. Should be called after rewinding
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ class GunzipReader final : public Reader {
|
|||||||
|
|
||||||
z_stream z;
|
z_stream z;
|
||||||
|
|
||||||
StaticFifoBuffer<Bytef, 4096> buffer;
|
StaticFifoBuffer<Bytef, 65536> buffer;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ GzipOutputStream::Flush()
|
|||||||
z.avail_in = 0;
|
z.avail_in = 0;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
Bytef output[4096];
|
Bytef output[16384];
|
||||||
z.next_out = output;
|
z.next_out = output;
|
||||||
z.avail_out = sizeof(output);
|
z.avail_out = sizeof(output);
|
||||||
|
|
||||||
@@ -87,7 +87,7 @@ GzipOutputStream::Write(const void *_data, size_t size)
|
|||||||
z.avail_in = size;
|
z.avail_in = size;
|
||||||
|
|
||||||
while (z.avail_in > 0) {
|
while (z.avail_in > 0) {
|
||||||
Bytef output[4096];
|
Bytef output[16384];
|
||||||
z.next_out = output;
|
z.next_out = output;
|
||||||
z.avail_out = sizeof(output);
|
z.avail_out = sizeof(output);
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ fs_sources = [
|
|||||||
'Path.cxx',
|
'Path.cxx',
|
||||||
'Path2.cxx',
|
'Path2.cxx',
|
||||||
'AllocatedPath.cxx',
|
'AllocatedPath.cxx',
|
||||||
|
'NarrowPath.cxx',
|
||||||
'FileSystem.cxx',
|
'FileSystem.cxx',
|
||||||
'List.cxx',
|
'List.cxx',
|
||||||
'StandardDirectory.cxx',
|
'StandardDirectory.cxx',
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ public:
|
|||||||
|
|
||||||
virtual ~AsyncInputStream();
|
virtual ~AsyncInputStream();
|
||||||
|
|
||||||
EventLoop &GetEventLoop() {
|
auto &GetEventLoop() const noexcept {
|
||||||
return deferred_resume.GetEventLoop();
|
return deferred_resume.GetEventLoop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ input_cdio_init(EventLoop &, const ConfigBlock &block)
|
|||||||
throw FormatRuntimeError("Unrecognized 'default_byte_order' setting: %s",
|
throw FormatRuntimeError("Unrecognized 'default_byte_order' setting: %s",
|
||||||
value);
|
value);
|
||||||
}
|
}
|
||||||
speed = block.GetBlockValue("speed",0u);
|
speed = block.GetBlockValue("speed",0U);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CdioUri {
|
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");
|
http_200_aliases = curl_slist_append(http_200_aliases, "ICY 200 OK");
|
||||||
|
|
||||||
proxy = block.GetBlockValue("proxy");
|
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_user = block.GetBlockValue("proxy_user");
|
||||||
proxy_password = block.GetBlockValue("proxy_password");
|
proxy_password = block.GetBlockValue("proxy_password");
|
||||||
|
|
||||||
@@ -365,9 +365,9 @@ CurlInputStream::InitEasy()
|
|||||||
request = new CurlRequest(**curl_init, GetURI(), *this);
|
request = new CurlRequest(**curl_init, GetURI(), *this);
|
||||||
|
|
||||||
request->SetOption(CURLOPT_HTTP200ALIASES, http_200_aliases);
|
request->SetOption(CURLOPT_HTTP200ALIASES, http_200_aliases);
|
||||||
request->SetOption(CURLOPT_FOLLOWLOCATION, 1l);
|
request->SetOption(CURLOPT_FOLLOWLOCATION, 1L);
|
||||||
request->SetOption(CURLOPT_MAXREDIRS, 5l);
|
request->SetOption(CURLOPT_MAXREDIRS, 5L);
|
||||||
request->SetOption(CURLOPT_FAILONERROR, 1l);
|
request->SetOption(CURLOPT_FAILONERROR, 1L);
|
||||||
|
|
||||||
if (proxy != nullptr)
|
if (proxy != nullptr)
|
||||||
request->SetOption(CURLOPT_PROXY, proxy);
|
request->SetOption(CURLOPT_PROXY, proxy);
|
||||||
@@ -380,8 +380,8 @@ CurlInputStream::InitEasy()
|
|||||||
StringFormat<1024>("%s:%s", proxy_user,
|
StringFormat<1024>("%s:%s", proxy_user,
|
||||||
proxy_password).c_str());
|
proxy_password).c_str());
|
||||||
|
|
||||||
request->SetOption(CURLOPT_SSL_VERIFYPEER, verify_peer ? 1l : 0l);
|
request->SetOption(CURLOPT_SSL_VERIFYPEER, verify_peer ? 1L : 0L);
|
||||||
request->SetOption(CURLOPT_SSL_VERIFYHOST, verify_host ? 2l : 0l);
|
request->SetOption(CURLOPT_SSL_VERIFYHOST, verify_host ? 2L : 0L);
|
||||||
request->SetOption(CURLOPT_HTTPHEADER, request_headers.Get());
|
request->SetOption(CURLOPT_HTTPHEADER, request_headers.Get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ public:
|
|||||||
|
|
||||||
~TidalSessionManager() noexcept;
|
~TidalSessionManager() noexcept;
|
||||||
|
|
||||||
EventLoop &GetEventLoop() noexcept {
|
auto &GetEventLoop() const noexcept {
|
||||||
return defer_invoke_handlers.GetEventLoop();
|
return defer_invoke_handlers.GetEventLoop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,15 +40,15 @@ namespace Java {
|
|||||||
*/
|
*/
|
||||||
typedef LocalRef<jobject> LocalObject;
|
typedef LocalRef<jobject> LocalObject;
|
||||||
|
|
||||||
class Object : public GlobalRef<jobject> {
|
class GlobalObject : public GlobalRef<jobject> {
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* Constructs an uninitialized object. The method
|
* Constructs an uninitialized object. The method
|
||||||
* set() must be called before it is destructed.
|
* 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) {}
|
: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
|
* Redistribution and use in source and binary forms, with or without
|
||||||
* modification, are permitted provided that the following conditions
|
* modification, are permitted provided that the following conditions
|
||||||
@@ -90,6 +90,82 @@ public:
|
|||||||
throw std::runtime_error(curl_easy_strerror(code));
|
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 {
|
CurlString Escape(const char *string, int length=0) const noexcept {
|
||||||
return CurlString(curl_easy_escape(handle, string, length));
|
return CurlString(curl_easy_escape(handle, string, length));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ public:
|
|||||||
CurlSocket(CurlGlobal &_global, EventLoop &_loop, SocketDescriptor _fd)
|
CurlSocket(CurlGlobal &_global, EventLoop &_loop, SocketDescriptor _fd)
|
||||||
:SocketMonitor(_fd, _loop), global(_global) {}
|
:SocketMonitor(_fd, _loop), global(_global) {}
|
||||||
|
|
||||||
~CurlSocket() {
|
~CurlSocket() noexcept {
|
||||||
/* TODO: sometimes, CURL uses CURL_POLL_REMOVE after
|
/* TODO: sometimes, CURL uses CURL_POLL_REMOVE after
|
||||||
closing the socket, and sometimes, it uses
|
closing the socket, and sometimes, it uses
|
||||||
CURL_POLL_REMOVE just to move the (still open)
|
CURL_POLL_REMOVE just to move the (still open)
|
||||||
@@ -109,7 +109,8 @@ CurlGlobal::CurlGlobal(EventLoop &_loop)
|
|||||||
int
|
int
|
||||||
CurlSocket::SocketFunction(gcc_unused CURL *easy,
|
CurlSocket::SocketFunction(gcc_unused CURL *easy,
|
||||||
curl_socket_t s, int action,
|
curl_socket_t s, int action,
|
||||||
void *userp, void *socketp) noexcept {
|
void *userp, void *socketp) noexcept
|
||||||
|
{
|
||||||
auto &global = *(CurlGlobal *)userp;
|
auto &global = *(CurlGlobal *)userp;
|
||||||
CurlSocket *cs = (CurlSocket *)socketp;
|
CurlSocket *cs = (CurlSocket *)socketp;
|
||||||
|
|
||||||
@@ -153,11 +154,6 @@ CurlSocket::OnSocketReady(unsigned flags) noexcept
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Runs in the I/O thread. No lock needed.
|
|
||||||
*
|
|
||||||
* Throws std::runtime_error on error.
|
|
||||||
*/
|
|
||||||
void
|
void
|
||||||
CurlGlobal::Add(CURL *easy, CurlRequest &request)
|
CurlGlobal::Add(CURL *easy, CurlRequest &request)
|
||||||
{
|
{
|
||||||
@@ -194,11 +190,6 @@ ToRequest(CURL *easy) noexcept
|
|||||||
return (CurlRequest *)p;
|
return (CurlRequest *)p;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check for finished HTTP responses.
|
|
||||||
*
|
|
||||||
* Runs in the I/O thread. The caller must not hold locks.
|
|
||||||
*/
|
|
||||||
inline void
|
inline void
|
||||||
CurlGlobal::ReadInfo() noexcept
|
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
|
inline void
|
||||||
CurlGlobal::UpdateTimeout(long timeout_ms) noexcept
|
CurlGlobal::UpdateTimeout(long timeout_ms) noexcept
|
||||||
{
|
{
|
||||||
@@ -236,11 +241,11 @@ CurlGlobal::UpdateTimeout(long timeout_ms) noexcept
|
|||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
CurlGlobal::TimerFunction(gcc_unused CURLM *_global, long timeout_ms,
|
CurlGlobal::TimerFunction(gcc_unused CURLM *_multi, long timeout_ms,
|
||||||
void *userp) noexcept
|
void *userp) noexcept
|
||||||
{
|
{
|
||||||
auto &global = *(CurlGlobal *)userp;
|
auto &global = *(CurlGlobal *)userp;
|
||||||
assert(_global == global.multi.Get());
|
assert(_multi == global.multi.Get());
|
||||||
|
|
||||||
global.UpdateTimeout(timeout_ms);
|
global.UpdateTimeout(timeout_ms);
|
||||||
return 0;
|
return 0;
|
||||||
@@ -251,17 +256,3 @@ CurlGlobal::OnTimeout() noexcept
|
|||||||
{
|
{
|
||||||
SocketAction(CURL_SOCKET_TIMEOUT, 0);
|
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:
|
public:
|
||||||
explicit CurlGlobal(EventLoop &_loop);
|
explicit CurlGlobal(EventLoop &_loop);
|
||||||
|
|
||||||
EventLoop &GetEventLoop() noexcept {
|
auto &GetEventLoop() const noexcept {
|
||||||
return timeout_event.GetEventLoop();
|
return timeout_event.GetEventLoop();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Add(CURL *easy, CurlRequest &request);
|
void Add(CURL *easy, CurlRequest &request);
|
||||||
void Remove(CURL *easy) noexcept;
|
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 {
|
void Assign(curl_socket_t fd, CurlSocket &cs) noexcept {
|
||||||
curl_multi_assign(multi.Get(), fd, &cs);
|
curl_multi_assign(multi.Get(), fd, &cs);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SocketAction(curl_socket_t fd, int ev_bitmask) noexcept;
|
void SocketAction(curl_socket_t fd, int ev_bitmask) noexcept;
|
||||||
|
|
||||||
void InvalidateSockets() {
|
void InvalidateSockets() noexcept {
|
||||||
SocketAction(CURL_SOCKET_TIMEOUT, 0);
|
SocketAction(CURL_SOCKET_TIMEOUT, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
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;
|
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;
|
void *userp) noexcept;
|
||||||
|
|
||||||
/* callback for #timeout_event */
|
/* callback for #timeout_event */
|
||||||
|
|||||||
@@ -50,11 +50,19 @@ public:
|
|||||||
CurlInit(const CurlInit &) = delete;
|
CurlInit(const CurlInit &) = delete;
|
||||||
CurlInit &operator=(const CurlInit &) = delete;
|
CurlInit &operator=(const CurlInit &) = delete;
|
||||||
|
|
||||||
CurlGlobal &operator*() {
|
CurlGlobal &operator*() noexcept {
|
||||||
return *instance;
|
return *instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
CurlGlobal *operator->() {
|
const CurlGlobal &operator*() const noexcept {
|
||||||
|
return *instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
CurlGlobal *operator->() noexcept {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CurlGlobal *operator->() const noexcept {
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -52,17 +52,17 @@ CurlRequest::CurlRequest(CurlGlobal &_global,
|
|||||||
{
|
{
|
||||||
error_buffer[0] = 0;
|
error_buffer[0] = 0;
|
||||||
|
|
||||||
easy.SetOption(CURLOPT_PRIVATE, (void *)this);
|
easy.SetPrivate((void *)this);
|
||||||
easy.SetOption(CURLOPT_USERAGENT, "Music Player Daemon " VERSION);
|
easy.SetUserAgent("Music Player Daemon " VERSION);
|
||||||
easy.SetOption(CURLOPT_HEADERFUNCTION, _HeaderFunction);
|
easy.SetHeaderFunction(_HeaderFunction, this);
|
||||||
easy.SetOption(CURLOPT_WRITEHEADER, this);
|
easy.SetWriteFunction(WriteFunction, this);
|
||||||
easy.SetOption(CURLOPT_WRITEFUNCTION, WriteFunction);
|
#ifndef ANDROID
|
||||||
easy.SetOption(CURLOPT_WRITEDATA, this);
|
easy.SetOption(CURLOPT_NETRC, 1L);
|
||||||
easy.SetOption(CURLOPT_NETRC, 1l);
|
#endif
|
||||||
easy.SetOption(CURLOPT_ERRORBUFFER, error_buffer);
|
easy.SetErrorBuffer(error_buffer);
|
||||||
easy.SetOption(CURLOPT_NOPROGRESS, 1l);
|
easy.SetNoProgress();
|
||||||
easy.SetOption(CURLOPT_NOSIGNAL, 1l);
|
easy.SetNoSignal();
|
||||||
easy.SetOption(CURLOPT_CONNECTTIMEOUT, 10l);
|
easy.SetConnectTimeout(10);
|
||||||
easy.SetOption(CURLOPT_HTTPAUTH, (long) CURLAUTH_ANY);
|
easy.SetOption(CURLOPT_HTTPAUTH, (long) CURLAUTH_ANY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,7 +121,7 @@ CurlRequest::Resume() noexcept
|
|||||||
{
|
{
|
||||||
assert(registered);
|
assert(registered);
|
||||||
|
|
||||||
curl_easy_pause(easy.Get(), CURLPAUSE_CONT);
|
easy.Unpause();
|
||||||
|
|
||||||
global.InvalidateSockets();
|
global.InvalidateSockets();
|
||||||
}
|
}
|
||||||
@@ -220,14 +220,14 @@ CurlRequest::HeaderFunction(StringView s) noexcept
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t
|
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
|
void *stream) noexcept
|
||||||
{
|
{
|
||||||
CurlRequest &c = *(CurlRequest *)stream;
|
CurlRequest &c = *(CurlRequest *)stream;
|
||||||
|
|
||||||
size *= nmemb;
|
size *= nmemb;
|
||||||
|
|
||||||
c.HeaderFunction({(const char *)ptr, size});
|
c.HeaderFunction({ptr, size});
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,7 +254,7 @@ CurlRequest::DataReceived(const void *ptr, size_t received_size) noexcept
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t
|
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
|
void *stream) noexcept
|
||||||
{
|
{
|
||||||
CurlRequest &c = *(CurlRequest *)stream;
|
CurlRequest &c = *(CurlRequest *)stream;
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SetUrl(const char *url) {
|
void SetUrl(const char *url) {
|
||||||
easy.SetOption(CURLOPT_URL, url);
|
easy.SetURL(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -160,11 +160,11 @@ private:
|
|||||||
void OnPostponeError() noexcept;
|
void OnPostponeError() noexcept;
|
||||||
|
|
||||||
/** called by curl when new data is available */
|
/** 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;
|
void *stream) noexcept;
|
||||||
|
|
||||||
/** called by curl when new data is available */
|
/** 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;
|
void *stream) noexcept;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ class CurlSlist {
|
|||||||
struct curl_slist *head = nullptr;
|
struct curl_slist *head = nullptr;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CurlSlist() = default;
|
CurlSlist() noexcept = default;
|
||||||
|
|
||||||
CurlSlist(CurlSlist &&src) noexcept
|
CurlSlist(CurlSlist &&src) noexcept
|
||||||
:head(std::exchange(src.head, nullptr)) {}
|
:head(std::exchange(src.head, nullptr)) {}
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ public:
|
|||||||
DisconnectIndirect();
|
DisconnectIndirect();
|
||||||
}
|
}
|
||||||
|
|
||||||
EventLoop &GetEventLoop() noexcept {
|
auto &GetEventLoop() const noexcept {
|
||||||
return watch.GetEventLoop();
|
return watch.GetEventLoop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ public:
|
|||||||
|
|
||||||
void Shutdown() noexcept;
|
void Shutdown() noexcept;
|
||||||
|
|
||||||
EventLoop &GetEventLoop() noexcept {
|
auto &GetEventLoop() const noexcept {
|
||||||
return defer_dispatch.GetEventLoop();
|
return defer_dispatch.GetEventLoop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,11 +36,6 @@
|
|||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#include "Win32.hxx"
|
|
||||||
#include <windows.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
@@ -59,7 +54,7 @@ try {
|
|||||||
if (u.IsNull())
|
if (u.IsNull())
|
||||||
return AllocatedString<>::Duplicate(src);
|
return AllocatedString<>::Duplicate(src);
|
||||||
|
|
||||||
AllocatedArray<UChar> folded(u.size() * 2u);
|
AllocatedArray<UChar> folded(u.size() * 2U);
|
||||||
|
|
||||||
UErrorCode error_code = U_ZERO_ERROR;
|
UErrorCode error_code = U_ZERO_ERROR;
|
||||||
size_t folded_length = u_strFoldCase(folded.begin(), folded.size(),
|
size_t folded_length = u_strFoldCase(folded.begin(), folded.size(),
|
||||||
@@ -72,25 +67,6 @@ try {
|
|||||||
folded.SetSize(folded_length);
|
folded.SetSize(folded_length);
|
||||||
return UCharToUTF8({folded.begin(), folded.size()});
|
return UCharToUTF8({folded.begin(), folded.size()});
|
||||||
|
|
||||||
#elif defined(_WIN32)
|
|
||||||
const auto u = MultiByteToWideChar(CP_UTF8, src);
|
|
||||||
|
|
||||||
const int size = LCMapStringEx(LOCALE_NAME_INVARIANT,
|
|
||||||
LCMAP_SORTKEY|LINGUISTIC_IGNORECASE,
|
|
||||||
u.c_str(), -1, nullptr, 0,
|
|
||||||
nullptr, nullptr, 0);
|
|
||||||
if (size <= 0)
|
|
||||||
return AllocatedString<>::Duplicate(src);
|
|
||||||
|
|
||||||
std::unique_ptr<wchar_t[]> buffer(new wchar_t[size]);
|
|
||||||
if (LCMapStringEx(LOCALE_NAME_INVARIANT,
|
|
||||||
LCMAP_SORTKEY|LINGUISTIC_IGNORECASE,
|
|
||||||
u.c_str(), -1, buffer.get(), size,
|
|
||||||
nullptr, nullptr, 0) <= 0)
|
|
||||||
return AllocatedString<>::Duplicate(src);
|
|
||||||
|
|
||||||
return WideCharToMultiByte(CP_UTF8, buffer.get());
|
|
||||||
|
|
||||||
#else
|
#else
|
||||||
#error not implemented
|
#error not implemented
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#if defined(HAVE_ICU) || defined(_WIN32)
|
#ifdef HAVE_ICU
|
||||||
#define HAVE_ICU_CASE_FOLD
|
#define HAVE_ICU_CASE_FOLD
|
||||||
|
|
||||||
#include "util/Compiler.h"
|
#include "util/Compiler.h"
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ IcuCollate(const char *a, const char *b) noexcept
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto result = CompareStringEx(LOCALE_NAME_INVARIANT,
|
auto result = CompareStringEx(LOCALE_NAME_INVARIANT,
|
||||||
LINGUISTIC_IGNORECASE,
|
NORM_IGNORECASE,
|
||||||
wa.c_str(), -1,
|
wa.c_str(), -1,
|
||||||
wb.c_str(), -1,
|
wb.c_str(), -1,
|
||||||
nullptr, nullptr, 0);
|
nullptr, nullptr, 0);
|
||||||
|
|||||||
@@ -22,6 +22,11 @@
|
|||||||
#include "util/StringAPI.hxx"
|
#include "util/StringAPI.hxx"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include "Win32.hxx"
|
||||||
|
#include <windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#ifdef HAVE_ICU_CASE_FOLD
|
#ifdef HAVE_ICU_CASE_FOLD
|
||||||
@@ -29,6 +34,17 @@
|
|||||||
IcuCompare::IcuCompare(const char *_needle) noexcept
|
IcuCompare::IcuCompare(const char *_needle) noexcept
|
||||||
:needle(IcuCaseFold(_needle)) {}
|
:needle(IcuCaseFold(_needle)) {}
|
||||||
|
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
|
||||||
|
IcuCompare::IcuCompare(const char *_needle) noexcept
|
||||||
|
:needle(nullptr)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
needle = MultiByteToWideChar(CP_UTF8, _needle);
|
||||||
|
} catch (...) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
IcuCompare::IcuCompare(const char *_needle) noexcept
|
IcuCompare::IcuCompare(const char *_needle) noexcept
|
||||||
@@ -41,6 +57,22 @@ IcuCompare::operator==(const char *haystack) const noexcept
|
|||||||
{
|
{
|
||||||
#ifdef HAVE_ICU_CASE_FOLD
|
#ifdef HAVE_ICU_CASE_FOLD
|
||||||
return StringIsEqual(IcuCaseFold(haystack).c_str(), needle.c_str());
|
return StringIsEqual(IcuCaseFold(haystack).c_str(), needle.c_str());
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
if (needle.IsNull())
|
||||||
|
/* the MultiByteToWideChar() call in the constructor
|
||||||
|
has failed, so let's always fail the comparison */
|
||||||
|
return false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
auto w_haystack = MultiByteToWideChar(CP_UTF8, haystack);
|
||||||
|
return CompareStringEx(LOCALE_NAME_INVARIANT,
|
||||||
|
NORM_IGNORECASE,
|
||||||
|
w_haystack.c_str(), -1,
|
||||||
|
needle.c_str(), -1,
|
||||||
|
nullptr, nullptr, 0) == CSTR_EQUAL;
|
||||||
|
} catch (...) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
return strcasecmp(haystack, needle.c_str());
|
return strcasecmp(haystack, needle.c_str());
|
||||||
#endif
|
#endif
|
||||||
@@ -52,6 +84,24 @@ IcuCompare::IsIn(const char *haystack) const noexcept
|
|||||||
#ifdef HAVE_ICU_CASE_FOLD
|
#ifdef HAVE_ICU_CASE_FOLD
|
||||||
return StringFind(IcuCaseFold(haystack).c_str(),
|
return StringFind(IcuCaseFold(haystack).c_str(),
|
||||||
needle.c_str()) != nullptr;
|
needle.c_str()) != nullptr;
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
if (needle.IsNull())
|
||||||
|
/* the MultiByteToWideChar() call in the constructor
|
||||||
|
has failed, so let's always fail the comparison */
|
||||||
|
return false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
auto w_haystack = MultiByteToWideChar(CP_UTF8, haystack);
|
||||||
|
return FindNLSStringEx(LOCALE_NAME_INVARIANT,
|
||||||
|
FIND_FROMSTART|NORM_IGNORECASE,
|
||||||
|
w_haystack.c_str(), -1,
|
||||||
|
needle.c_str(), -1,
|
||||||
|
nullptr,
|
||||||
|
nullptr, nullptr, 0) >= 0;
|
||||||
|
} catch (...) {
|
||||||
|
/* MultiByteToWideChar() has failed */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
#elif defined(HAVE_STRCASESTR)
|
#elif defined(HAVE_STRCASESTR)
|
||||||
return strcasestr(haystack, needle.c_str()) != nullptr;
|
return strcasestr(haystack, needle.c_str()) != nullptr;
|
||||||
#else
|
#else
|
||||||
|
|||||||
@@ -23,13 +23,23 @@
|
|||||||
#include "util/Compiler.h"
|
#include "util/Compiler.h"
|
||||||
#include "util/AllocatedString.hxx"
|
#include "util/AllocatedString.hxx"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <wchar.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class can compare one string ("needle") with lots of other
|
* This class can compare one string ("needle") with lots of other
|
||||||
* strings ("haystacks") efficiently, ignoring case. With some
|
* strings ("haystacks") efficiently, ignoring case. With some
|
||||||
* configurations, it can prepare a case-folded version of the needle.
|
* configurations, it can prepare a case-folded version of the needle.
|
||||||
*/
|
*/
|
||||||
class IcuCompare {
|
class IcuCompare {
|
||||||
|
#ifdef _WIN32
|
||||||
|
/* Windows API functions work with wchar_t strings, so let's
|
||||||
|
cache the MultiByteToWideChar() result for performance */
|
||||||
|
AllocatedString<wchar_t> needle;
|
||||||
|
#else
|
||||||
AllocatedString<> needle;
|
AllocatedString<> needle;
|
||||||
|
#endif
|
||||||
|
|
||||||
public:
|
public:
|
||||||
IcuCompare():needle(nullptr) {}
|
IcuCompare():needle(nullptr) {}
|
||||||
@@ -38,12 +48,12 @@ public:
|
|||||||
|
|
||||||
IcuCompare(const IcuCompare &src) noexcept
|
IcuCompare(const IcuCompare &src) noexcept
|
||||||
:needle(src
|
:needle(src
|
||||||
? AllocatedString<>::Duplicate(src.needle.c_str())
|
? src.needle.Clone()
|
||||||
: nullptr) {}
|
: nullptr) {}
|
||||||
|
|
||||||
IcuCompare &operator=(const IcuCompare &src) noexcept {
|
IcuCompare &operator=(const IcuCompare &src) noexcept {
|
||||||
needle = src
|
needle = src
|
||||||
? AllocatedString<>::Duplicate(src.needle.c_str())
|
? src.needle.Clone()
|
||||||
: nullptr;
|
: nullptr;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -191,7 +191,9 @@ static constexpr int
|
|||||||
events_to_libnfs(unsigned i) noexcept
|
events_to_libnfs(unsigned i) noexcept
|
||||||
{
|
{
|
||||||
return ((i & SocketMonitor::READ) ? POLLIN : 0) |
|
return ((i & SocketMonitor::READ) ? POLLIN : 0) |
|
||||||
((i & SocketMonitor::WRITE) ? POLLOUT : 0);
|
((i & SocketMonitor::WRITE) ? POLLOUT : 0) |
|
||||||
|
((i & SocketMonitor::HANGUP) ? POLLHUP : 0) |
|
||||||
|
((i & SocketMonitor::ERROR) ? POLLERR : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
NfsConnection::~NfsConnection() noexcept
|
NfsConnection::~NfsConnection() noexcept
|
||||||
@@ -450,8 +452,7 @@ NfsConnection::ScheduleSocket() noexcept
|
|||||||
SocketMonitor::Open(_fd);
|
SocketMonitor::Open(_fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
SocketMonitor::Schedule(libnfs_to_events(which_events)
|
SocketMonitor::Schedule(libnfs_to_events(which_events));
|
||||||
| SocketMonitor::HANGUP);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int
|
inline int
|
||||||
|
|||||||
@@ -161,9 +161,7 @@ public:
|
|||||||
return export_name.c_str();
|
return export_name.c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
EventLoop &GetEventLoop() noexcept {
|
using SocketMonitor::GetEventLoop;
|
||||||
return SocketMonitor::GetEventLoop();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ensure that the connection is established. The connection
|
* Ensure that the connection is established. The connection
|
||||||
|
|||||||
@@ -180,7 +180,6 @@ NfsFileReader::OnNfsConnectionDisconnected(std::exception_ptr e) noexcept
|
|||||||
inline void
|
inline void
|
||||||
NfsFileReader::OpenCallback(nfsfh *_fh) noexcept
|
NfsFileReader::OpenCallback(nfsfh *_fh) noexcept
|
||||||
{
|
{
|
||||||
assert(state == State::OPEN);
|
|
||||||
assert(connection != nullptr);
|
assert(connection != nullptr);
|
||||||
assert(_fh != nullptr);
|
assert(_fh != nullptr);
|
||||||
|
|
||||||
@@ -197,27 +196,33 @@ NfsFileReader::OpenCallback(nfsfh *_fh) noexcept
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline void
|
inline void
|
||||||
NfsFileReader::StatCallback(const struct stat *st) noexcept
|
NfsFileReader::StatCallback(const struct stat *_st) noexcept
|
||||||
{
|
{
|
||||||
assert(state == State::STAT);
|
|
||||||
assert(connection != nullptr);
|
assert(connection != nullptr);
|
||||||
assert(fh != nullptr);
|
assert(fh != nullptr);
|
||||||
assert(st != nullptr);
|
assert(_st != nullptr);
|
||||||
|
|
||||||
|
#if defined(_WIN32) && !defined(_WIN64)
|
||||||
|
/* on 32-bit Windows, libnfs enables -D_FILE_OFFSET_BITS=64,
|
||||||
|
but MPD (Meson) doesn't - to work around this mismatch, we
|
||||||
|
cast explicitly to "struct stat64" */
|
||||||
|
const auto *st = (const struct stat64 *)_st;
|
||||||
|
#else
|
||||||
|
const auto *st = _st;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (!S_ISREG(st->st_mode)) {
|
if (!S_ISREG(st->st_mode)) {
|
||||||
OnNfsFileError(std::make_exception_ptr(std::runtime_error("Not a regular file")));
|
OnNfsFileError(std::make_exception_ptr(std::runtime_error("Not a regular file")));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
state = State::IDLE;
|
|
||||||
|
|
||||||
OnNfsFileOpen(st->st_size);
|
OnNfsFileOpen(st->st_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
NfsFileReader::OnNfsCallback(unsigned status, void *data) noexcept
|
NfsFileReader::OnNfsCallback(unsigned status, void *data) noexcept
|
||||||
{
|
{
|
||||||
switch (state) {
|
switch (std::exchange(state, State::IDLE)) {
|
||||||
case State::INITIAL:
|
case State::INITIAL:
|
||||||
case State::DEFER:
|
case State::DEFER:
|
||||||
case State::MOUNT:
|
case State::MOUNT:
|
||||||
@@ -234,7 +239,6 @@ NfsFileReader::OnNfsCallback(unsigned status, void *data) noexcept
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case State::READ:
|
case State::READ:
|
||||||
state = State::IDLE;
|
|
||||||
OnNfsFileRead(data, status);
|
OnNfsFileRead(data, status);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ public:
|
|||||||
NfsFileReader() noexcept;
|
NfsFileReader() noexcept;
|
||||||
~NfsFileReader() noexcept;
|
~NfsFileReader() noexcept;
|
||||||
|
|
||||||
EventLoop &GetEventLoop() noexcept {
|
auto &GetEventLoop() const noexcept {
|
||||||
return defer_open.GetEventLoop();
|
return defer_open.GetEventLoop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -272,7 +272,7 @@ UPnPDeviceDirectory::~UPnPDeviceDirectory() noexcept
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline EventLoop &
|
inline EventLoop &
|
||||||
UPnPDeviceDirectory::GetEventLoop() noexcept
|
UPnPDeviceDirectory::GetEventLoop() const noexcept
|
||||||
{
|
{
|
||||||
return curl->GetEventLoop();
|
return curl->GetEventLoop();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -168,7 +168,7 @@ public:
|
|||||||
UPnPDeviceDirectory(const UPnPDeviceDirectory &) = delete;
|
UPnPDeviceDirectory(const UPnPDeviceDirectory &) = delete;
|
||||||
UPnPDeviceDirectory& operator=(const UPnPDeviceDirectory &) = delete;
|
UPnPDeviceDirectory& operator=(const UPnPDeviceDirectory &) = delete;
|
||||||
|
|
||||||
EventLoop &GetEventLoop() noexcept;
|
EventLoop &GetEventLoop() const noexcept;
|
||||||
|
|
||||||
void Start();
|
void Start();
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ struct MixerPlugin;
|
|||||||
|
|
||||||
extern const MixerPlugin null_mixer_plugin;
|
extern const MixerPlugin null_mixer_plugin;
|
||||||
extern const MixerPlugin software_mixer_plugin;
|
extern const MixerPlugin software_mixer_plugin;
|
||||||
|
extern const MixerPlugin android_mixer_plugin;
|
||||||
extern const MixerPlugin alsa_mixer_plugin;
|
extern const MixerPlugin alsa_mixer_plugin;
|
||||||
extern const MixerPlugin haiku_mixer_plugin;
|
extern const MixerPlugin haiku_mixer_plugin;
|
||||||
extern const MixerPlugin oss_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 "mixer/MixerInternal.hxx"
|
||||||
#include "output/OutputAPI.hxx"
|
#include "output/OutputAPI.hxx"
|
||||||
#include "output/plugins/WinmmOutputPlugin.hxx"
|
#include "output/plugins/WinmmOutputPlugin.hxx"
|
||||||
|
#include "util/Math.hxx"
|
||||||
|
|
||||||
#include <mmsystem.h>
|
#include <mmsystem.h>
|
||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <math.h>
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
class WinmmMixer final : public Mixer {
|
class WinmmMixer final : public Mixer {
|
||||||
|
|||||||
@@ -34,6 +34,10 @@ if is_windows
|
|||||||
mixer_plugins_sources += 'WinmmMixerPlugin.cxx'
|
mixer_plugins_sources += 'WinmmMixerPlugin.cxx'
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
if is_android
|
||||||
|
mixer_plugins_sources += 'AndroidMixerPlugin.cxx'
|
||||||
|
endif
|
||||||
|
|
||||||
mixer_plugins = static_library(
|
mixer_plugins = static_library(
|
||||||
'mixer_plugins',
|
'mixer_plugins',
|
||||||
mixer_plugins_sources,
|
mixer_plugins_sources,
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ public:
|
|||||||
NeighborListener &_listener) noexcept
|
NeighborListener &_listener) noexcept
|
||||||
:NeighborExplorer(_listener), event_loop(_event_loop) {}
|
:NeighborExplorer(_listener), event_loop(_event_loop) {}
|
||||||
|
|
||||||
auto &GetEventLoop() noexcept {
|
auto &GetEventLoop() const noexcept {
|
||||||
return event_loop;
|
return event_loop;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -395,7 +395,7 @@ AlsaOutput::AlsaOutput(EventLoop &_loop, const ConfigBlock &block)
|
|||||||
#endif
|
#endif
|
||||||
buffer_time(block.GetPositiveValue("buffer_time",
|
buffer_time(block.GetPositiveValue("buffer_time",
|
||||||
MPD_ALSA_BUFFER_TIME_US)),
|
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
|
#ifdef SND_PCM_NO_AUTO_RESAMPLE
|
||||||
if (!block.GetBlockValue("auto_resample", true))
|
if (!block.GetBlockValue("auto_resample", true))
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ MakeAoError()
|
|||||||
|
|
||||||
AoOutput::AoOutput(const ConfigBlock &block)
|
AoOutput::AoOutput(const ConfigBlock &block)
|
||||||
:AudioOutput(0),
|
:AudioOutput(0),
|
||||||
write_size(block.GetPositiveValue("write_size", 1024u))
|
write_size(block.GetPositiveValue("write_size", 1024U))
|
||||||
{
|
{
|
||||||
const char *value = block.GetBlockValue("driver", "default");
|
const char *value = block.GetBlockValue("driver", "default");
|
||||||
if (0 == strcmp(value, "default"))
|
if (0 == strcmp(value, "default"))
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
#include "../OutputAPI.hxx"
|
#include "../OutputAPI.hxx"
|
||||||
#include "mixer/MixerList.hxx"
|
#include "mixer/MixerList.hxx"
|
||||||
#include "util/Domain.hxx"
|
#include "util/Domain.hxx"
|
||||||
|
#include "util/Math.hxx"
|
||||||
#include "system/Error.hxx"
|
#include "system/Error.hxx"
|
||||||
#include "Log.hxx"
|
#include "Log.hxx"
|
||||||
|
|
||||||
@@ -37,8 +38,6 @@
|
|||||||
#include <StringList.h>
|
#include <StringList.h>
|
||||||
#include <SoundPlayer.h>
|
#include <SoundPlayer.h>
|
||||||
|
|
||||||
#include <cmath>
|
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#define UTF8_PLAY "\xE2\x96\xB6"
|
#define UTF8_PLAY "\xE2\x96\xB6"
|
||||||
|
|||||||
@@ -212,7 +212,7 @@ JackOutput::JackOutput(const ConfigBlock &block)
|
|||||||
num_source_ports, num_destination_ports,
|
num_source_ports, num_destination_ports,
|
||||||
block.line);
|
block.line);
|
||||||
|
|
||||||
ringbuffer_size = block.GetPositiveValue("ringbuffer_size", 32768u);
|
ringbuffer_size = block.GetPositiveValue("ringbuffer_size", 32768U);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline jack_nframes_t
|
inline jack_nframes_t
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ ShoutOutput::ShoutOutput(const ConfigBlock &block)
|
|||||||
{
|
{
|
||||||
const char *host = require_block_string(block, "host");
|
const char *host = require_block_string(block, "host");
|
||||||
const char *mount = require_block_string(block, "mount");
|
const char *mount = require_block_string(block, "mount");
|
||||||
unsigned port = block.GetBlockValue("port", 0u);
|
unsigned port = block.GetBlockValue("port", 0U);
|
||||||
if (port == 0)
|
if (port == 0)
|
||||||
throw std::runtime_error("shout port must be configured");
|
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");
|
genre = block.GetBlockValue("genre", "Set genre in config");
|
||||||
website = block.GetBlockValue("website", "Set website 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 */
|
/* 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 */
|
/* determine content type */
|
||||||
content_type = prepared_encoder->GetMimeType();
|
content_type = prepared_encoder->GetMimeType();
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
#include "util/Macros.hxx"
|
#include "util/Macros.hxx"
|
||||||
#include "util/Domain.hxx"
|
#include "util/Domain.hxx"
|
||||||
#include "system/ByteOrder.hxx"
|
#include "system/ByteOrder.hxx"
|
||||||
|
#include "mixer/MixerList.hxx"
|
||||||
#include "Log.hxx"
|
#include "Log.hxx"
|
||||||
|
|
||||||
#include <SLES/OpenSLES.h>
|
#include <SLES/OpenSLES.h>
|
||||||
@@ -412,5 +413,5 @@ const struct AudioOutputPlugin sles_output_plugin = {
|
|||||||
"sles",
|
"sles",
|
||||||
sles_test_default_device,
|
sles_test_default_device,
|
||||||
SlesOutput::Create,
|
SlesOutput::Create,
|
||||||
nullptr,
|
&android_mixer_plugin,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -21,6 +21,28 @@
|
|||||||
#include "PcmBuffer.hxx"
|
#include "PcmBuffer.hxx"
|
||||||
#include "util/ConstBuffer.hxx"
|
#include "util/ConstBuffer.hxx"
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* According to:
|
||||||
|
* - https://xiph.org/flac/format.html#frame_header
|
||||||
|
* - https://github.com/nu774/qaac/wiki/Multichannel--handling
|
||||||
|
* the source channel order (after decoding, e.g., flac, alac) is for
|
||||||
|
* - 1ch: mono
|
||||||
|
* - 2ch: left, right
|
||||||
|
* - 3ch: left, right, center
|
||||||
|
* - 4ch: front left, front right, back left, back right
|
||||||
|
* - 5ch: front left, front right, front center, back/surround left, back/surround right
|
||||||
|
* - 6ch (aka 5.1): front left, front right, front center, LFE, back/surround left, back/surround right
|
||||||
|
* - 7ch: front left, front right, front center, LFE, back center, side left, side right
|
||||||
|
* - 8ch: (aka 7.1): front left, front right, front center, LFE, back left, back right, side left, side right
|
||||||
|
*
|
||||||
|
* The ALSA default channel map is (see /usr/share/alsa/pcm/surround71.conf):
|
||||||
|
* - front left, front right, back left, back right, front center, LFE, side left, side right
|
||||||
|
*
|
||||||
|
* Hence, in case of the following source channel orders 3ch, 5ch, 6ch (aka
|
||||||
|
* 5.1), 7ch and 8ch the channel order has to be adapted
|
||||||
|
*/
|
||||||
|
|
||||||
template<typename V>
|
template<typename V>
|
||||||
struct TwoPointers {
|
struct TwoPointers {
|
||||||
V *dest;
|
V *dest;
|
||||||
@@ -44,17 +66,57 @@ struct TwoPointers {
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TwoPointers<V> &ToAlsa50() noexcept {
|
||||||
|
*dest++ = src[0]; // front left
|
||||||
|
*dest++ = src[1]; // front right
|
||||||
|
*dest++ = src[3]; // surround left
|
||||||
|
*dest++ = src[4]; // surround right
|
||||||
|
*dest++ = src[2]; // front center
|
||||||
|
src += 5;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
TwoPointers<V> &ToAlsa51() noexcept {
|
TwoPointers<V> &ToAlsa51() noexcept {
|
||||||
return CopyTwo() // left+right
|
return CopyTwo() // left+right
|
||||||
.SwapTwoPairs(); // center, LFE, surround left+right
|
.SwapTwoPairs(); // center, LFE, surround left+right
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TwoPointers<V> &ToAlsa70() noexcept {
|
||||||
|
*dest++ = src[0]; // front left
|
||||||
|
*dest++ = src[1]; // front right
|
||||||
|
*dest++ = src[5]; // side left
|
||||||
|
*dest++ = src[6]; // side right
|
||||||
|
*dest++ = src[2]; // front center
|
||||||
|
*dest++ = src[3]; // LFE
|
||||||
|
*dest++ = src[4]; // back center
|
||||||
|
src += 7;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
TwoPointers<V> &ToAlsa71() noexcept {
|
TwoPointers<V> &ToAlsa71() noexcept {
|
||||||
return ToAlsa51()
|
return ToAlsa51()
|
||||||
.CopyTwo(); // side left+right
|
.CopyTwo(); // side left+right
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<typename V>
|
||||||
|
static void
|
||||||
|
ToAlsaChannelOrder50(V *dest, const V *src, size_t n) noexcept
|
||||||
|
{
|
||||||
|
TwoPointers<V> p{dest, src};
|
||||||
|
for (size_t i = 0; i != n; ++i)
|
||||||
|
p.ToAlsa50();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename V>
|
||||||
|
static inline ConstBuffer<V>
|
||||||
|
ToAlsaChannelOrder50(PcmBuffer &buffer, ConstBuffer<V> src) noexcept
|
||||||
|
{
|
||||||
|
auto dest = buffer.GetT<V>(src.size);
|
||||||
|
ToAlsaChannelOrder50(dest, src.data, src.size / 5);
|
||||||
|
return { dest, src.size };
|
||||||
|
}
|
||||||
|
|
||||||
template<typename V>
|
template<typename V>
|
||||||
static void
|
static void
|
||||||
ToAlsaChannelOrder51(V *dest, const V *src, size_t n) noexcept
|
ToAlsaChannelOrder51(V *dest, const V *src, size_t n) noexcept
|
||||||
@@ -73,6 +135,24 @@ ToAlsaChannelOrder51(PcmBuffer &buffer, ConstBuffer<V> src) noexcept
|
|||||||
return { dest, src.size };
|
return { dest, src.size };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename V>
|
||||||
|
static void
|
||||||
|
ToAlsaChannelOrder70(V *dest, const V *src, size_t n) noexcept
|
||||||
|
{
|
||||||
|
TwoPointers<V> p{dest, src};
|
||||||
|
for (size_t i = 0; i != n; ++i)
|
||||||
|
p.ToAlsa70();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename V>
|
||||||
|
static inline ConstBuffer<V>
|
||||||
|
ToAlsaChannelOrder70(PcmBuffer &buffer, ConstBuffer<V> src) noexcept
|
||||||
|
{
|
||||||
|
auto dest = buffer.GetT<V>(src.size);
|
||||||
|
ToAlsaChannelOrder70(dest, src.data, src.size / 7);
|
||||||
|
return { dest, src.size };
|
||||||
|
}
|
||||||
|
|
||||||
template<typename V>
|
template<typename V>
|
||||||
static void
|
static void
|
||||||
ToAlsaChannelOrder71(V *dest, const V *src, size_t n) noexcept
|
ToAlsaChannelOrder71(V *dest, const V *src, size_t n) noexcept
|
||||||
@@ -97,9 +177,15 @@ ToAlsaChannelOrderT(PcmBuffer &buffer, ConstBuffer<V> src,
|
|||||||
unsigned channels) noexcept
|
unsigned channels) noexcept
|
||||||
{
|
{
|
||||||
switch (channels) {
|
switch (channels) {
|
||||||
|
case 5: // 5.0
|
||||||
|
return ToAlsaChannelOrder50(buffer, src);
|
||||||
|
|
||||||
case 6: // 5.1
|
case 6: // 5.1
|
||||||
return ToAlsaChannelOrder51(buffer, src);
|
return ToAlsaChannelOrder51(buffer, src);
|
||||||
|
|
||||||
|
case 7: // 7.0
|
||||||
|
return ToAlsaChannelOrder70(buffer, src);
|
||||||
|
|
||||||
case 8: // 7.1
|
case 8: // 7.1
|
||||||
return ToAlsaChannelOrder71(buffer, src);
|
return ToAlsaChannelOrder71(buffer, src);
|
||||||
|
|
||||||
|
|||||||
@@ -22,11 +22,10 @@
|
|||||||
#include "Clamp.hxx"
|
#include "Clamp.hxx"
|
||||||
#include "Traits.hxx"
|
#include "Traits.hxx"
|
||||||
#include "util/Clamp.hxx"
|
#include "util/Clamp.hxx"
|
||||||
|
#include "util/Math.hxx"
|
||||||
|
|
||||||
#include "PcmDither.cxx" // including the .cxx file to get inlined templates
|
#include "PcmDither.cxx" // including the .cxx file to get inlined templates
|
||||||
|
|
||||||
#include <cmath>
|
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
template<SampleFormat F, class Traits=SampleTraits<F>>
|
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 = sin(M_PI_2 * portion1);
|
||||||
s *= s;
|
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);
|
vol1 = Clamp<int>(vol1, 0, PCM_VOLUME_1S);
|
||||||
|
|
||||||
return pcm_add_vol(dither, buffer1, buffer2, size,
|
return pcm_add_vol(dither, buffer1, buffer2, size,
|
||||||
|
|||||||
@@ -23,10 +23,9 @@
|
|||||||
#include "AudioFormat.hxx"
|
#include "AudioFormat.hxx"
|
||||||
#include "util/NumberParser.hxx"
|
#include "util/NumberParser.hxx"
|
||||||
#include "util/Domain.hxx"
|
#include "util/Domain.hxx"
|
||||||
|
#include "util/Math.hxx"
|
||||||
#include "Log.hxx"
|
#include "Log.hxx"
|
||||||
|
|
||||||
#include <cmath>
|
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
static constexpr Domain cross_fade_domain("cross_fade");
|
static constexpr Domain cross_fade_domain("cross_fade");
|
||||||
@@ -63,7 +62,7 @@ mixramp_interpolate(const char *ramp_list, float required_db) noexcept
|
|||||||
++ramp_list;
|
++ramp_list;
|
||||||
|
|
||||||
/* Check for exact match. */
|
/* Check for exact match. */
|
||||||
if (db == required_db) {
|
if (db >= required_db) {
|
||||||
return duration;
|
return duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,7 +111,7 @@ CrossFadeSettings::Calculate(SignedSongTime total_time,
|
|||||||
|
|
||||||
if (mixramp_delay <= FloatDuration::zero() ||
|
if (mixramp_delay <= FloatDuration::zero() ||
|
||||||
!mixramp_start || !mixramp_prev_end) {
|
!mixramp_start || !mixramp_prev_end) {
|
||||||
chunks = std::lround(duration / chunk_duration);
|
chunks = lround(duration / chunk_duration);
|
||||||
} else {
|
} else {
|
||||||
/* Calculate mixramp overlap. */
|
/* Calculate mixramp overlap. */
|
||||||
const auto mixramp_overlap_current =
|
const auto mixramp_overlap_current =
|
||||||
|
|||||||
@@ -964,6 +964,12 @@ Player::SongBorder() noexcept
|
|||||||
if (border_pause) {
|
if (border_pause) {
|
||||||
paused = true;
|
paused = true;
|
||||||
pc.listener.OnBorderPause();
|
pc.listener.OnBorderPause();
|
||||||
|
|
||||||
|
/* drain all outputs to guarantee the current song is
|
||||||
|
really being played to the end; without this, the
|
||||||
|
Pause() call would drop all ring buffers */
|
||||||
|
pc.outputs.Drain();
|
||||||
|
|
||||||
pc.outputs.Pause();
|
pc.outputs.Pause();
|
||||||
idle_add(IDLE_PLAYER);
|
idle_add(IDLE_PLAYER);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -162,6 +162,7 @@ static const char *const rss_suffixes[] = {
|
|||||||
|
|
||||||
static const char *const rss_mime_types[] = {
|
static const char *const rss_mime_types[] = {
|
||||||
"application/rss+xml",
|
"application/rss+xml",
|
||||||
|
"application/xml",
|
||||||
"text/xml",
|
"text/xml",
|
||||||
nullptr
|
nullptr
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -266,8 +266,8 @@ public:
|
|||||||
CommonExpatParser(ExpatNamespaceSeparator{'|'})
|
CommonExpatParser(ExpatNamespaceSeparator{'|'})
|
||||||
{
|
{
|
||||||
request.SetOption(CURLOPT_CUSTOMREQUEST, "PROPFIND");
|
request.SetOption(CURLOPT_CUSTOMREQUEST, "PROPFIND");
|
||||||
request.SetOption(CURLOPT_FOLLOWLOCATION, 1l);
|
request.SetOption(CURLOPT_FOLLOWLOCATION, 1L);
|
||||||
request.SetOption(CURLOPT_MAXREDIRS, 1l);
|
request.SetOption(CURLOPT_MAXREDIRS, 1L);
|
||||||
|
|
||||||
request_headers.Append(StringFormat<40>("depth: %u", depth));
|
request_headers.Append(StringFormat<40>("depth: %u", depth));
|
||||||
|
|
||||||
@@ -402,7 +402,7 @@ private:
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case State::HREF:
|
case State::HREF:
|
||||||
response.href.assign(s, len);
|
response.href.append(s, len);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case State::STATUS:
|
case State::STATUS:
|
||||||
@@ -482,7 +482,7 @@ class HttpListDirectoryOperation final : public PropfindOperation {
|
|||||||
public:
|
public:
|
||||||
HttpListDirectoryOperation(CurlGlobal &curl, const char *uri)
|
HttpListDirectoryOperation(CurlGlobal &curl, const char *uri)
|
||||||
:PropfindOperation(curl, uri, 1),
|
:PropfindOperation(curl, uri, 1),
|
||||||
base_path(UriPathOrSlash(uri)) {}
|
base_path(CurlUnescape(GetEasy(), UriPathOrSlash(uri))) {}
|
||||||
|
|
||||||
std::unique_ptr<StorageDirectoryReader> Perform() {
|
std::unique_ptr<StorageDirectoryReader> Perform() {
|
||||||
DeferStart();
|
DeferStart();
|
||||||
@@ -507,8 +507,7 @@ private:
|
|||||||
|
|
||||||
/* kludge: ignoring case in this comparison to avoid
|
/* kludge: ignoring case in this comparison to avoid
|
||||||
false negatives if the web server uses a different
|
false negatives if the web server uses a different
|
||||||
case in hex digits in escaped characters; TODO:
|
case */
|
||||||
implement properly */
|
|
||||||
path = StringAfterPrefixIgnoreCase(path, base_path.c_str());
|
path = StringAfterPrefixIgnoreCase(path, base_path.c_str());
|
||||||
if (path == nullptr || *path == 0)
|
if (path == nullptr || *path == 0)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@@ -531,11 +530,12 @@ protected:
|
|||||||
if (r.status != 200)
|
if (r.status != 200)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const auto escaped_name = HrefToEscapedName(r.href.c_str());
|
std::string href = CurlUnescape(GetEasy(), r.href.c_str());
|
||||||
if (escaped_name.IsNull())
|
const auto name = HrefToEscapedName(href.c_str());
|
||||||
|
if (name.IsNull())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
entries.emplace_front(CurlUnescape(GetEasy(), escaped_name));
|
entries.emplace_front(std::string(name.data, name.size));
|
||||||
|
|
||||||
auto &info = entries.front().info;
|
auto &info = entries.front().info;
|
||||||
info = StorageFileInfo(r.collection
|
info = StorageFileInfo(r.collection
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
EventLoop &GetEventLoop() noexcept {
|
EventLoop &GetEventLoop() const noexcept {
|
||||||
return defer_connect.GetEventLoop();
|
return defer_connect.GetEventLoop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EventLoop &GetEventLoop() noexcept {
|
EventLoop &GetEventLoop() const noexcept {
|
||||||
return defer_mount.GetEventLoop();
|
return defer_mount.GetEventLoop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -45,6 +45,10 @@ ApplyTagFallback(TagType type, F &&f) noexcept
|
|||||||
"AlbumArtist"/"ArtistSort" was found */
|
"AlbumArtist"/"ArtistSort" was found */
|
||||||
return f(TAG_ARTIST);
|
return f(TAG_ARTIST);
|
||||||
|
|
||||||
|
if (type == TAG_ALBUM_SORT)
|
||||||
|
/* fall back to "Album" if no "AlbumSort" was found */
|
||||||
|
return f(TAG_ALBUM);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
|
|
||||||
Mutex tag_pool_lock;
|
Mutex tag_pool_lock;
|
||||||
|
|
||||||
static constexpr size_t NUM_SLOTS = 4093;
|
static constexpr size_t NUM_SLOTS = 16127;
|
||||||
|
|
||||||
struct TagPoolSlot {
|
struct TagPoolSlot {
|
||||||
TagPoolSlot *next;
|
TagPoolSlot *next;
|
||||||
|
|||||||
@@ -77,15 +77,15 @@ static time_t
|
|||||||
GetTimeZoneOffset() noexcept
|
GetTimeZoneOffset() noexcept
|
||||||
{
|
{
|
||||||
time_t t = 1234567890;
|
time_t t = 1234567890;
|
||||||
struct tm tm;
|
|
||||||
tm.tm_isdst = 0;
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
struct tm *p = gmtime(&t);
|
struct tm *p = gmtime(&t);
|
||||||
#else
|
#else
|
||||||
|
struct tm tm;
|
||||||
|
tm.tm_isdst = 0;
|
||||||
struct tm *p = &tm;
|
struct tm *p = &tm;
|
||||||
gmtime_r(&t, p);
|
gmtime_r(&t, p);
|
||||||
#endif
|
#endif
|
||||||
return t - mktime(&tm);
|
return t - mktime(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* !__GLIBC__ */
|
#endif /* !__GLIBC__ */
|
||||||
|
|||||||
46
src/util/Math.hxx
Normal file
46
src/util/Math.hxx
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* C99 math can be optionally omitted with gcc's libstdc++.
|
||||||
|
* Use boost if unavailable.
|
||||||
|
*/
|
||||||
|
#if (defined(__GLIBCPP__) || defined(__GLIBCXX__)) && !defined(_GLIBCXX_USE_C99_MATH)
|
||||||
|
#include <boost/math/special_functions/round.hpp>
|
||||||
|
using boost::math::lround;
|
||||||
|
#else
|
||||||
|
using std::lround;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -202,7 +202,7 @@ struct CheckSequenceUTF8 {
|
|||||||
};
|
};
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
struct CheckSequenceUTF8<0u> {
|
struct CheckSequenceUTF8<0U> {
|
||||||
constexpr bool operator()(gcc_unused const char *p) const noexcept {
|
constexpr bool operator()(gcc_unused const char *p) const noexcept {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -215,7 +215,7 @@ InnerSequenceLengthUTF8(const char *p) noexcept
|
|||||||
{
|
{
|
||||||
return CheckSequenceUTF8<L>()(p)
|
return CheckSequenceUTF8<L>()(p)
|
||||||
? L + 1
|
? L + 1
|
||||||
: 0u;
|
: 0U;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t
|
size_t
|
||||||
|
|||||||
@@ -26,9 +26,7 @@ static unsigned
|
|||||||
FromAvahiWatchEvent(AvahiWatchEvent e)
|
FromAvahiWatchEvent(AvahiWatchEvent e)
|
||||||
{
|
{
|
||||||
return (e & AVAHI_WATCH_IN ? SocketMonitor::READ : 0) |
|
return (e & AVAHI_WATCH_IN ? SocketMonitor::READ : 0) |
|
||||||
(e & AVAHI_WATCH_OUT ? SocketMonitor::WRITE : 0) |
|
(e & AVAHI_WATCH_OUT ? SocketMonitor::WRITE : 0);
|
||||||
(e & AVAHI_WATCH_ERR ? SocketMonitor::ERROR : 0) |
|
|
||||||
(e & AVAHI_WATCH_HUP ? SocketMonitor::HANGUP : 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static AvahiWatchEvent
|
static AvahiWatchEvent
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
#include "decoder/DecoderList.hxx"
|
#include "decoder/DecoderList.hxx"
|
||||||
#include "decoder/DecoderPlugin.hxx"
|
#include "decoder/DecoderPlugin.hxx"
|
||||||
#include "fs/Path.hxx"
|
#include "fs/Path.hxx"
|
||||||
|
#include "fs/NarrowPath.hxx"
|
||||||
#include "fs/io/StdioOutputStream.hxx"
|
#include "fs/io/StdioOutputStream.hxx"
|
||||||
#include "fs/io/BufferedOutputStream.hxx"
|
#include "fs/io/BufferedOutputStream.hxx"
|
||||||
#include "util/UriUtil.hxx"
|
#include "util/UriUtil.hxx"
|
||||||
@@ -63,7 +64,7 @@ try {
|
|||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Path path = Path::FromFS(argv[1]);
|
const FromNarrowPath path = argv[1];
|
||||||
|
|
||||||
const ScopeDecoderPluginsInit decoder_plugins_init({});
|
const ScopeDecoderPluginsInit decoder_plugins_init({});
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
#include "ConfigGlue.hxx"
|
#include "ConfigGlue.hxx"
|
||||||
#include "tag/Config.hxx"
|
#include "tag/Config.hxx"
|
||||||
#include "fs/Path.hxx"
|
#include "fs/Path.hxx"
|
||||||
|
#include "fs/NarrowPath.hxx"
|
||||||
#include "event/Thread.hxx"
|
#include "event/Thread.hxx"
|
||||||
#include "util/ScopeExit.hxx"
|
#include "util/ScopeExit.hxx"
|
||||||
#include "util/PrintException.hxx"
|
#include "util/PrintException.hxx"
|
||||||
@@ -107,7 +108,7 @@ try {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Path config_path = Path::FromFS(argv[1]);
|
const FromNarrowPath config_path = argv[1];
|
||||||
const char *const plugin_name = argv[2];
|
const char *const plugin_name = argv[2];
|
||||||
|
|
||||||
const DatabasePlugin *plugin = GetDatabasePluginByName(plugin_name);
|
const DatabasePlugin *plugin = GetDatabasePluginByName(plugin_name);
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
#include "tag/ApeLoader.hxx"
|
#include "tag/ApeLoader.hxx"
|
||||||
#include "thread/Mutex.hxx"
|
#include "thread/Mutex.hxx"
|
||||||
#include "fs/Path.hxx"
|
#include "fs/Path.hxx"
|
||||||
|
#include "fs/NarrowPath.hxx"
|
||||||
#include "input/InputStream.hxx"
|
#include "input/InputStream.hxx"
|
||||||
#include "input/LocalOpen.hxx"
|
#include "input/LocalOpen.hxx"
|
||||||
#include "util/StringView.hxx"
|
#include "util/StringView.hxx"
|
||||||
@@ -58,7 +59,7 @@ try {
|
|||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Path path = Path::FromFS(argv[1]);
|
const FromNarrowPath path = argv[1];
|
||||||
|
|
||||||
Mutex mutex;
|
Mutex mutex;
|
||||||
|
|
||||||
|
|||||||
@@ -29,8 +29,8 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
ShutdownHandler::ShutdownHandler(EventLoop &loop) {}
|
inline ShutdownHandler::ShutdownHandler(EventLoop &) {}
|
||||||
ShutdownHandler::~ShutdownHandler() {}
|
inline ShutdownHandler::~ShutdownHandler() {}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "fs/io/FileOutputStream.hxx"
|
#include "fs/io/FileOutputStream.hxx"
|
||||||
|
#include "fs/NarrowPath.hxx"
|
||||||
#include "util/PrintException.hxx"
|
#include "util/PrintException.hxx"
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
@@ -54,7 +55,7 @@ try {
|
|||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Path path = Path::FromFS(argv[1]);
|
const FromNarrowPath path = argv[1];
|
||||||
|
|
||||||
FileOutputStream fos(path);
|
FileOutputStream fos(path);
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
#include "playlist/PlaylistRegistry.hxx"
|
#include "playlist/PlaylistRegistry.hxx"
|
||||||
#include "playlist/PlaylistPlugin.hxx"
|
#include "playlist/PlaylistPlugin.hxx"
|
||||||
#include "fs/Path.hxx"
|
#include "fs/Path.hxx"
|
||||||
|
#include "fs/NarrowPath.hxx"
|
||||||
#include "fs/io/BufferedOutputStream.hxx"
|
#include "fs/io/BufferedOutputStream.hxx"
|
||||||
#include "fs/io/StdioOutputStream.hxx"
|
#include "fs/io/StdioOutputStream.hxx"
|
||||||
#include "thread/Cond.hxx"
|
#include "thread/Cond.hxx"
|
||||||
@@ -54,7 +55,7 @@ try {
|
|||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Path config_path = Path::FromFS(argv[1]);
|
const FromNarrowPath config_path = argv[1];
|
||||||
uri = argv[2];
|
uri = argv[2];
|
||||||
|
|
||||||
/* initialize MPD */
|
/* initialize MPD */
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ gtest_dep = declare_dependency(
|
|||||||
)
|
)
|
||||||
|
|
||||||
subdir('net')
|
subdir('net')
|
||||||
|
subdir('time')
|
||||||
|
|
||||||
executable(
|
executable(
|
||||||
'read_conf',
|
'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(
|
test('TestRewindInputStream', executable(
|
||||||
'TestRewindInputStream',
|
'TestRewindInputStream',
|
||||||
'TestRewindInputStream.cxx',
|
'TestRewindInputStream.cxx',
|
||||||
@@ -348,6 +336,11 @@ if curl_dep.found()
|
|||||||
include_directories: inc,
|
include_directories: inc,
|
||||||
dependencies: [
|
dependencies: [
|
||||||
curl_dep,
|
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