Compare commits

...

51 Commits

Author SHA1 Message Date
Max Kellermann
f591193dda release v0.23.6 2022-03-14 18:55:47 +01:00
Max Kellermann
434869900e android/build.py: fix typo in error message
Closes https://github.com/MusicPlayerDaemon/MPD/issues/1379
2022-03-14 18:49:50 +01:00
Max Kellermann
2aed7378cc TagAny: support CUE tracks
Closes https://github.com/MusicPlayerDaemon/MPD/issues/1482
2022-03-14 18:42:31 +01:00
Max Kellermann
71cd6e6248 lib/xiph/meson.build: define FLAC__NO_DLL for static libFLAC build (Windows)
In libFLAC 0.3.4 (commit c9530118a4), the "dllimport" check has been
changed from "_MSC_VER" to "_WIN32", and now the MPD build is affected
by it.

Defining FLAC__NO_DLL disables the use of "dllimport", which allows
linking properly to the static libFLAC build.
2022-03-14 15:08:59 +01:00
Max Kellermann
c83294916a python/build/libs.py: update Boost to 1.78.0 2022-03-14 14:52:24 +01:00
Max Kellermann
603bbe0afd python/build/libs.py: update libnfs to 5.0.1 2022-03-14 14:52:24 +01:00
Max Kellermann
c361e235eb python/build/libs.py: update CURL to 7.82.0 2022-03-14 14:52:24 +01:00
Max Kellermann
8a59493d96 python/build/libs.py: update OpenSSL to 3.0.1 2022-03-14 14:50:06 +01:00
Max Kellermann
7ef86cbf9f python/build/libs.py: update FFmpeg to 5.0 2022-03-14 14:50:06 +01:00
Max Kellermann
c9530118a4 python/build/libs.py: update FLAC to 1.3.4 2022-03-14 14:31:13 +01:00
Max Kellermann
878d9abeb7 python/build/libs.py: update libogg to 1.3.5 2022-03-14 14:29:59 +01:00
Max Kellermann
2d705efe1c python/build/libs.py: update libmpdclient to 2.20 2022-03-14 14:29:22 +01:00
Richard Schorrig
aeaef85507 WasapiOutputPlugin pause bug fix
Wasapi output plugin won't start playing after being paused

The cause is that the scope guard in the WASAPI work thread
(WasapiOutputPlugin.cxx, function WasapiOutputThread::Work(), in the
while (true) loop) is set up too 'late' in the execution. There is one
condition ("if (data_in_frames >= buffer_size_in_frames)") when it is
hit, the loop will continue without executing the scope guard. This
scope guard is responsible for emptying the buffer again, and if the
buffer is not emptied, the above mentioned condition will stay true.

Closes https://github.com/MusicPlayerDaemon/MPD/issues/1451
2022-03-14 14:26:00 +01:00
nick black
ebae25d175 plugins/FfmpegIO: include libavutil/mem.h
ffmpeg from current git master no longer exposes
av_malloc() nor av_free() through other included
headers. directly include libavutil/mem.h to fix
compilation with (as-yet-unreleased) ffmpeg.
2022-03-14 14:11:31 +01:00
jcorporation
5ad1a01d7a Remove bmp, tiff and add webp for coverimage filenames
- supporting bmp and tiff seems outdated
- webp is more widely used for coverimages
2022-03-14 14:09:23 +01:00
Max Kellermann
8f84e1befd decoder/plugins/FfmpegIo: return AVERROR_EOF at end of file
This part of the AVIOContext API is not documented :-(

Closes https://github.com/MusicPlayerDaemon/MPD/issues/1448
2022-03-14 14:00:28 +01:00
Max Kellermann
9975905faf output/PipeWire: initialize field "stream" in Open()
Must be initialized for the check in SetVolume().
2022-03-09 14:29:46 +01:00
Max Kellermann
233184568c doc/protocol.rst: describe the FILTER argument to playlist{find,search} 2022-02-14 09:11:41 +01:00
Wolfgang Müller
59da778009 doc/user.rst: Clarify how MPD reads metadata
The writing and reading of metadata involves lots of different programs
and libraries. Therefore it is prudent to point out how exactly MPD
receives metadata. Ideally this helps to point users to the right place
if their tags are not picked up correctly.
2022-02-14 09:11:11 +01:00
Max Kellermann
108ce95b7c android/Receiver: fix indent 2022-01-26 14:43:47 +01:00
Max Kellermann
86e9ed5f3a decoder/opus: fix "readpicture" on Opus files
Don't return early from ScanOpusTags() if only
TagHandler::WantPicture() is set.

Closes https://github.com/MusicPlayerDaemon/MPD/issues/1413
2022-01-26 14:43:45 +01:00
Sam Bazley
fbecb05bf4 Fix Android build error: needs_exe_wrapper
lib/src/libmpdclient-2.19/meson.build:1:0: ERROR: Unknown options: "needs_exe_wrapper"

The "needs_exe_wrapper" option was incorrectly set under
[built-in options] rather than [properties].
2022-01-11 20:33:48 +01:00
Sam Bazley
4983703375 Android: Detect output change with ACTION_AUDIO_BECOMING_NOISY
Improves the changes made in 57687779be by
using AudioManager.ACTION_AUDIO_BECOMING_NOISY rather than listening for
wired headset unplug events or Bluetooth headset disconnect events. This
method is more flexible, allowing the feature to work on other types of
audio output device, as well as Bluetooth devices that don't set their
device class correctly. This change also has the benefit of being more
responsive, pausing the audio before it is rerouted to the built-in
speaker.

https://developer.android.com/guide/topics/media-apps/volume-and-earphones
2022-01-04 16:42:53 +01:00
aeolio
3856224df9 lib/alsa/Error: add missing #include 2021-12-15 11:14:38 +01:00
aeolio
6d4bedfc56 lib/alsa/Error: fix typo 2021-12-15 11:14:34 +01:00
Max Kellermann
bea821f194 doc/user.rst: add MixRamp documentation 2021-12-06 21:32:39 +01:00
Rosen Penev
4e276256c0 more braced init list conversion
Signed-off-by: Rosen Penev <rosenp@gmail.com>
2021-12-06 09:16:04 +01:00
Tim Siegel
d0f9062b56 mpdconf.example: fix a few spelling typos 2021-12-05 22:58:45 +01:00
Max Kellermann
b9cc036703 .github/workflows/build.yml: rebuild branch v0.23.x 2021-12-03 23:00:42 +01:00
Max Kellermann
4e9b88559b SingleMode: convert "pure" to "const" 2021-12-03 16:09:34 +01:00
Max Kellermann
3452682a42 IcyMetaDataParser: move to tag/ 2021-12-03 16:07:39 +01:00
Max Kellermann
9262b24504 AudioCompress: move to pcm/ 2021-12-03 16:04:59 +01:00
Max Kellermann
a5fa43b526 fs/io: move to io/ 2021-12-03 14:35:41 +01:00
Max Kellermann
8681a3d74c replace TextFile references with LineReader 2021-12-03 14:22:56 +01:00
Max Kellermann
f9c4d88b12 fs/io/TextFile: add interface LineReader 2021-12-03 14:20:29 +01:00
Max Kellermann
799032505e io/uring/Queue: add method RequireSubmitEntry()
Fixes assertion failure when the submit queue is empty.
2021-12-03 13:58:39 +01:00
Max Kellermann
c8f174ac92 io/uring/Operation: disallow copying 2021-12-03 13:52:04 +01:00
Max Kellermann
047e169f3e util/BindMethod: merge MakeBind{Method,Function}Wrapper(), they are identical now 2021-12-03 13:51:56 +01:00
Max Kellermann
687327c9e8 util/BindMethod: merge structs {Method,Function}SignatureHelper into one 2021-12-03 13:51:56 +01:00
Max Kellermann
26dc37bd76 util/BindMethod: merge structs {Method,Function}WrapperGenerator into one 2021-12-03 13:51:55 +01:00
Max Kellermann
c693e4aa64 util/BindMethod: remove unused struct MethodWithSignature 2021-12-03 13:51:55 +01:00
Max Kellermann
acab731fef util/BindMethod: simplify MakeBindFunctionWrapper() 2021-12-03 13:51:55 +01:00
Max Kellermann
7e4ba3cb72 util/BindMethod: add MethodSignatureHelper::function_pointer 2021-12-03 13:51:55 +01:00
Max Kellermann
172c4d9c7d util/BindMethod: remove unnecessary template arguments from BindMethodWrapperGenerator 2021-12-03 13:51:55 +01:00
Max Kellermann
bd5f6cbc7b util/BindMethod: simplify more templates using "auto" template arguments 2021-12-03 13:51:55 +01:00
Max Kellermann
6fcd1c734b util/BindMethod: eliminate struct BindMethodWrapperGenerator2 2021-12-03 13:51:55 +01:00
Max Kellermann
eca097dbfb util/BindMethod: simplify more templates using "auto" template arguments 2021-12-03 13:51:55 +01:00
Max Kellermann
51ffafa011 util/BindMethod: use std::remove_reference_t 2021-12-03 13:51:25 +01:00
Max Kellermann
8dca602346 util/BindMethod: simplify BindMethod() 2021-12-03 13:51:18 +01:00
Max Kellermann
0ed24f3a05 util/IntrusiveList: disallow copying IntrusiveListHook 2021-12-03 13:50:05 +01:00
Max Kellermann
e25e0030e7 increment version number to 0.23.6 2021-12-01 20:01:22 +01:00
111 changed files with 450 additions and 368 deletions
.github/workflows
NEWS
android
doc
meson.build
python/build
src
LocateUri.cxxPlaylistDatabase.cxxPlaylistDatabase.hxxPlaylistFile.cxxPlaylistSave.cxxSingleMode.hxxSongSave.cxxSongSave.hxxStateFile.cxxTagAny.cxxTagSave.cxx
command
config
db
decoder
encoder
filter
fs
input
io
lib
mixer
output
pcm
queue
storage
tag
util
test

@@ -12,7 +12,7 @@ on:
- 'win32/**'
branches:
- master
- actions
- v0.23.x
pull_request:
paths-ignore:
- 'android/**'
@@ -24,6 +24,7 @@ on:
- 'win32/**'
branches:
- master
- v0.23.x
jobs:
build-linux:

11
NEWS

@@ -1,3 +1,14 @@
ver 0.23.6 (2022/03/14)
* protocol
- support filename "cover.webp" for "albumart" command
- support "readcomments" and "readpicture" on CUE tracks
* decoder
- ffmpeg: fix end-of-file check (update stuck at empty files)
- opus: fix "readpicture" on Opus files
* output
- pipewire: fix crash bug if setting volume before playback starts
- wasapi: fix resume after pause
ver 0.23.5 (2021/12/01)
* protocol
- support relative offsets for "searchadd"

@@ -17,8 +17,6 @@
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<application android:allowBackup="true"
android:requestLegacyExternalStorage="true"
@@ -43,7 +41,6 @@
<receiver android:name=".Receiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.HEADSET_PLUG" />
</intent-filter>
</receiver>
<service android:name=".Main" android:process=":main"/>

@@ -13,7 +13,7 @@ android_abi = sys.argv[3]
configure_args = sys.argv[4:]
if not os.path.isfile(os.path.join(sdk_path, 'tools', 'android')):
print("SDK not found in", ndk_path, file=sys.stderr)
print("SDK not found in", sdk_path, file=sys.stderr)
sys.exit(1)
if not os.path.isdir(ndk_path):

@@ -24,14 +24,13 @@ import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothClass;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.media.AudioManager;
import android.os.Build;
import android.os.IBinder;
import android.os.PowerManager;
@@ -200,24 +199,14 @@ public class Main extends Service implements Runnable {
return;
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_HEADSET_PLUG);
filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED);
filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
filter.addAction(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (!mPauseOnHeadphonesDisconnect) {
if (!mPauseOnHeadphonesDisconnect)
return;
}
if (intent.getAction().equals(Intent.ACTION_HEADSET_PLUG)) {
if (intent.hasExtra("state") && intent.getIntExtra("state", 0) == 0)
pause();
} else {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (device.getBluetoothClass().hasService(BluetoothClass.Service.AUDIO))
pause();
}
if (intent.getAction() == AudioManager.ACTION_AUDIO_BECOMING_NOISY)
pause();
}
}, filter);

@@ -25,16 +25,18 @@ import android.content.Intent;
import android.util.Log;
public class Receiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.d("Receiver", "onReceive: " + intent);
if (intent.getAction() == "android.intent.action.BOOT_COMPLETED") {
if (Settings.Preferences.getBoolean(context,
Settings.Preferences.KEY_RUN_ON_BOOT, false)) {
final boolean wakelock = Settings.Preferences.getBoolean(context,
Settings.Preferences.KEY_WAKELOCK, false);
Main.start(context, wakelock);
}
}
}
@Override
public void onReceive(Context context, Intent intent) {
Log.d("Receiver", "onReceive: " + intent);
if (intent.getAction() == "android.intent.action.BOOT_COMPLETED") {
if (Settings.Preferences.getBoolean(context,
Settings.Preferences.KEY_RUN_ON_BOOT,
false)) {
final boolean wakelock =
Settings.Preferences.getBoolean(context,
Settings.Preferences.KEY_WAKELOCK, false);
Main.start(context, wakelock);
}
}
}
}

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

@@ -79,7 +79,7 @@
# This setting sets the address for the daemon to listen on. Careful attention
# should be paid if this is assigned to anything other than the default, any.
# This setting can deny access to control of the daemon. Not effective if
# systemd socket activiation is in use.
# systemd socket activation is in use.
#
# For network
#bind_to_address "any"
@@ -185,7 +185,7 @@
# cache_directory "~/.local/share/mpd/cache"
#}
#
# An example of database config for a sattelite setup
# An example of database config for a satellite setup
#
#music_directory "nfs://fileserver.local/srv/mp3"
#database {

@@ -479,7 +479,7 @@ Querying :program:`MPD`'s status
current song in seconds, but with higher resolution.
- ``duration`` [#since_0_20]_: Duration of the current song in seconds.
- ``bitrate``: instantaneous bitrate in kbps
- ``xfade``: ``crossfade`` in seconds
- ``xfade``: ``crossfade`` in seconds (see :ref:`crossfading`)
- ``mixrampdb``: ``mixramp`` threshold in dB
- ``mixrampdelay``: ``mixrampdelay`` in seconds
- ``audio``: The format emitted by the decoder plugin during
@@ -519,17 +519,19 @@ Playback options
.. _command_crossfade:
:command:`crossfade {SECONDS}`
Sets crossfading between songs.
Sets crossfading between songs. See :ref:`crossfading`.
.. _command_mixrampdb:
:command:`mixrampdb {deciBels}`
Sets the threshold at which songs will be overlapped. Like crossfading but doesn't fade the track volume, just overlaps. The songs need to have MixRamp tags added by an external tool. 0dB is the normalized maximum volume so use negative values, I prefer -17dB. In the absence of mixramp tags crossfading will be used. See http://sourceforge.net/projects/mixramp
Sets the threshold at which songs will be overlapped.
See :ref:`mixramp`.
.. _command_mixrampdelay:
:command:`mixrampdelay {SECONDS}`
Additional time subtracted from the overlap calculated by mixrampdb. A value of "nan" disables MixRamp overlapping and falls back to crossfading.
See :ref:`mixramp`.
.. _command_random:
@@ -771,8 +773,8 @@ Whenever possible, ids should be used.
.. _command_playlistfind:
:command:`playlistfind {FILTER}`
Finds songs in the queue with strict
matching.
Search the queue for songs matching
``FILTER`` (see :ref:`Filters <filter_syntax>`).
.. _command_playlistid:
@@ -792,8 +794,10 @@ Whenever possible, ids should be used.
.. _command_playlistsearch:
:command:`playlistsearch {FILTER}`
Searches case-insensitively for partial matches in the
queue.
Search the queue for songs matching
``FILTER`` (see :ref:`Filters <filter_syntax>`).
Parameters have the same meaning as for :ref:`find
<command_playlistfind>`, except that search is not case sensitive.
.. _command_plchanges:

@@ -622,6 +622,51 @@ enabled by setting ``volume_normalization`` to ``yes``. It supports
16 bit PCM only.
.. _crossfading:
Cross-Fading
------------
If ``crossfade`` is set to a positive number, then adjacent songs are
cross-faded by this number of seconds. This is a run-time setting
:ref:`which can be controlled by clients <command_crossfade>`,
e.g. with :program:`mpc`::
mpc crossfade 10
mpc crossfade 0
Zero means cross-fading is disabled.
Cross-fading is only possible if both songs have the same audio
format. At the cost of quality loss and higher CPU usage, you can
make sure this is always given by configuring
:ref:`audio_output_format`.
.. _mixramp:
MixRamp
^^^^^^^
MixRamp tags describe the loudness levels at start and end of a song
and can be used by MPD to find the best time to begin cross-fading.
MPD enables MixRamp if:
- Cross-fade is enabled
- :ref:`mixrampdelay <command_mixrampdelay>` is set to a positive
value, e.g.::
mpc mixrampdelay 1
- :ref:`mixrampdb <command_mixrampdb>` is set to a reasonable value,
e.g.::
mpc mixrampdb -17
- both songs have MixRamp tags
- both songs have the same audio format (or :ref:`audio_output_format`
is configured)
The `MixRamp <http://sourceforge.net/projects/mixramp>`__ tool can be
used to add MixRamp tags to your song files.
Client Connections
------------------
@@ -1058,6 +1103,15 @@ See :ref:`tags` for a list of supported tags.
The :ref:`metadata_to_use <metadata_to_use>` setting can be used to
enable or disable certain tags.
Note that :program:`MPD` may not necessarily read metadata itself,
instead relying on data reported by the decoder that was used to read
a file. For example, this is the case for the FFmpeg decoder: both
:program:`MPD` and FFmpeg need to support a given metadata format in
order for metadata to be picked up correctly.
Only if a decoder does not have metadata support will :program:`MPD`
attempt to parse a song's metadata itself.
The queue
---------

@@ -1,7 +1,7 @@
project(
'mpd',
['c', 'cpp'],
version: '0.23.5',
version: '0.23.6',
meson_version: '>= 0.56.0',
default_options: [
'c_std=c11',

@@ -12,14 +12,14 @@ from build.boost import BoostProject
from build.jack import JackProject
libmpdclient = MesonProject(
'https://www.musicpd.org/download/libmpdclient/2/libmpdclient-2.19.tar.xz',
'158aad4c2278ab08e76a3f2b0166c99b39fae00ee17231bd225c5a36e977a189',
'https://www.musicpd.org/download/libmpdclient/2/libmpdclient-2.20.tar.xz',
'18793f68e939c3301e34d8fcadea1f7daa24143941263cecadb80126194e277d',
'lib/libmpdclient.a',
)
libogg = CmakeProject(
'http://downloads.xiph.org/releases/ogg/libogg-1.3.4.tar.xz',
'c163bc12bc300c401b6aa35907ac682671ea376f13ae0969a220f7ddf71893fe',
'http://downloads.xiph.org/releases/ogg/libogg-1.3.5.tar.xz',
'c4d91be36fc8e54deae7575241e03f4211eb102afb3fc0775fbbc1b740016705',
'lib/libogg.a',
[
'-DBUILD_SHARED_LIBS=OFF',
@@ -43,8 +43,8 @@ opus = AutotoolsProject(
)
flac = AutotoolsProject(
'http://downloads.xiph.org/releases/flac/flac-1.3.3.tar.xz',
'213e82bd716c9de6db2f98bcadbc4c24c7e2efe8c75939a1a84e28539c4e1748',
'http://downloads.xiph.org/releases/flac/flac-1.3.4.tar.xz',
'8ff0607e75a322dd7cd6ec48f4f225471404ae2730d0ea945127b1355155e737',
'lib/libFLAC.a',
[
'--disable-shared', '--enable-static',
@@ -151,8 +151,8 @@ gme = CmakeProject(
)
ffmpeg = FfmpegProject(
'http://ffmpeg.org/releases/ffmpeg-4.4.1.tar.xz',
'eadbad9e9ab30b25f5520fbfde99fae4a92a1ae3c0257a8d68569a4651e30e02',
'http://ffmpeg.org/releases/ffmpeg-5.0.tar.xz',
'51e919f7d205062c0fd4fae6243a84850391115104ccf1efc451733bc0ac7298',
'lib/libavcodec.a',
[
'--disable-shared', '--enable-static',
@@ -380,14 +380,14 @@ ffmpeg = FfmpegProject(
)
openssl = OpenSSLProject(
'https://www.openssl.org/source/openssl-3.0.0.tar.gz',
'59eedfcb46c25214c9bd37ed6078297b4df01d012267fe9e9eee31f61bc70536',
'https://www.openssl.org/source/openssl-3.0.1.tar.gz',
'c311ad853353bce796edad01a862c50a8a587f62e7e2100ef465ab53ec9b06d1',
'include/openssl/ossl_typ.h',
)
curl = CmakeProject(
'https://curl.se/download/curl-7.79.1.tar.xz',
'0606f74b1182ab732a17c11613cbbaf7084f2e6cca432642d0e3ad7c224c3689',
'https://curl.se/download/curl-7.82.0.tar.xz',
'0aaa12d7bd04b0966254f2703ce80dd5c38dbbd76af0297d3d690cdce58a583c',
'lib/libcurl.a',
[
'-DBUILD_CURL_EXE=OFF',
@@ -415,14 +415,14 @@ curl = CmakeProject(
'-DBUILD_TESTING=OFF',
],
windows_configure_args=[
'-DCMAKE_USE_SCHANNEL=ON',
'-DCURL_USE_SCHANNEL=ON',
],
patches='src/lib/curl/patches',
)
libnfs = AutotoolsProject(
'https://github.com/sahlberg/libnfs/archive/libnfs-4.0.0.tar.gz',
'6ee77e9fe220e2d3e3b1f53cfea04fb319828cc7dbb97dd9df09e46e901d797d',
'https://github.com/sahlberg/libnfs/archive/libnfs-5.0.1.tar.gz',
'7ef445410b42f36b9bad426608b53ccb9ccca4101e545c383f564c11db672ca8',
'lib/libnfs.a',
[
'--disable-shared', '--enable-static',
@@ -433,8 +433,7 @@ libnfs = AutotoolsProject(
'--disable-utils', '--disable-examples',
],
base='libnfs-libnfs-4.0.0',
patches='src/lib/nfs/patches',
base='libnfs-libnfs-5.0.1',
autoreconf=True,
)
@@ -445,7 +444,7 @@ jack = JackProject(
)
boost = BoostProject(
'https://boostorg.jfrog.io/artifactory/main/release/1.77.0/source/boost_1_77_0.tar.bz2',
'fc9f85fc030e233142908241af7a846e60630aa7388de9a5fafb1f3a26840854',
'https://boostorg.jfrog.io/artifactory/main/release/1.78.0/source/boost_1_78_0.tar.bz2',
'8681f175d4bdb26c52222665793eef08490d7758529330f98d3b29dd0735bccc',
'include/boost/version.hpp',
)

@@ -53,19 +53,21 @@ pkgconfig = '{toolchain.pkg_config}'
f.write(f"""
[properties]
root = '{toolchain.install_prefix}'
[built-in options]
c_args = {repr((toolchain.cppflags + ' ' + toolchain.cflags).split())}
c_link_args = {repr(toolchain.ldflags.split() + toolchain.libs.split())}
cpp_args = {repr((toolchain.cppflags + ' ' + toolchain.cxxflags).split())}
cpp_link_args = {repr(toolchain.ldflags.split() + toolchain.libs.split())}
""")
if 'android' in toolchain.arch:
f.write("""
# Keep Meson from executing Android-x86 test binariees
needs_exe_wrapper = true
""")
f.write(f"""
[built-in options]
c_args = {repr((toolchain.cppflags + ' ' + toolchain.cflags).split())}
c_link_args = {repr(toolchain.ldflags.split() + toolchain.libs.split())}
cpp_args = {repr((toolchain.cppflags + ' ' + toolchain.cxxflags).split())}
cpp_link_args = {repr(toolchain.ldflags.split() + toolchain.libs.split())}
""")
f.write(f"""

@@ -48,15 +48,14 @@ LocateFileUri(const char *uri, const Client *client
/* this path was relative to the music
directory */
// TODO: don't use suffix.data() (ok for now because we know it's null-terminated)
return LocatedUri(LocatedUri::Type::RELATIVE,
suffix.data());
return {LocatedUri::Type::RELATIVE, suffix.data()};
}
#endif
if (client != nullptr)
client->AllowFile(path);
return LocatedUri(LocatedUri::Type::PATH, uri, std::move(path));
return {LocatedUri::Type::PATH, uri, std::move(path)};
}
static LocatedUri
@@ -90,8 +89,7 @@ LocateAbsoluteUri(UriPluginKind kind, const char *uri
const auto suffix = storage->MapToRelativeUTF8(uri);
if (suffix.data() != nullptr)
// TODO: don't use suffix.data() (ok for now because we know it's null-terminated)
return LocatedUri(LocatedUri::Type::RELATIVE,
suffix.data());
return {LocatedUri::Type::RELATIVE, suffix.data()};
}
if (kind == UriPluginKind::STORAGE &&
@@ -99,7 +97,7 @@ LocateAbsoluteUri(UriPluginKind kind, const char *uri
throw std::invalid_argument("Unsupported URI scheme");
#endif
return LocatedUri(LocatedUri::Type::ABSOLUTE, uri);
return {LocatedUri::Type::ABSOLUTE, uri};
}
LocatedUri

@@ -19,8 +19,8 @@
#include "PlaylistDatabase.hxx"
#include "db/PlaylistVector.hxx"
#include "fs/io/TextFile.hxx"
#include "fs/io/BufferedOutputStream.hxx"
#include "io/LineReader.hxx"
#include "io/BufferedOutputStream.hxx"
#include "time/ChronoUtil.hxx"
#include "util/StringStrip.hxx"
#include "util/RuntimeError.hxx"
@@ -42,7 +42,7 @@ playlist_vector_save(BufferedOutputStream &os, const PlaylistVector &pv)
}
void
playlist_metadata_load(TextFile &file, PlaylistVector &pv, const char *name)
playlist_metadata_load(LineReader &file, PlaylistVector &pv, const char *name)
{
PlaylistInfo pm(name);

@@ -24,7 +24,7 @@
class PlaylistVector;
class BufferedOutputStream;
class TextFile;
class LineReader;
void
playlist_vector_save(BufferedOutputStream &os, const PlaylistVector &pv);
@@ -33,6 +33,7 @@ playlist_vector_save(BufferedOutputStream &os, const PlaylistVector &pv);
* Throws #std::runtime_error on error.
*/
void
playlist_metadata_load(TextFile &file, PlaylistVector &pv, const char *name);
playlist_metadata_load(LineReader &file, PlaylistVector &pv,
const char *name);
#endif

@@ -28,8 +28,8 @@
#include "Mapper.hxx"
#include "protocol/RangeArg.hxx"
#include "fs/io/TextFile.hxx"
#include "fs/io/FileOutputStream.hxx"
#include "fs/io/BufferedOutputStream.hxx"
#include "io/FileOutputStream.hxx"
#include "io/BufferedOutputStream.hxx"
#include "config/Data.hxx"
#include "config/Option.hxx"
#include "config/Defaults.hxx"

@@ -28,8 +28,8 @@
#include "fs/AllocatedPath.hxx"
#include "fs/Traits.hxx"
#include "fs/FileSystem.hxx"
#include "fs/io/FileOutputStream.hxx"
#include "fs/io/BufferedOutputStream.hxx"
#include "io/FileOutputStream.hxx"
#include "io/BufferedOutputStream.hxx"
#include "util/UriExtract.hxx"
static void

@@ -31,7 +31,7 @@ enum class SingleMode : uint8_t {
/**
* Return the string representation of a #SingleMode.
*/
[[gnu::pure]]
[[gnu::const]]
const char *
SingleToString(SingleMode mode) noexcept;

@@ -22,8 +22,8 @@
#include "db/plugins/simple/Song.hxx"
#include "song/DetachedSong.hxx"
#include "TagSave.hxx"
#include "fs/io/TextFile.hxx"
#include "fs/io/BufferedOutputStream.hxx"
#include "io/LineReader.hxx"
#include "io/BufferedOutputStream.hxx"
#include "tag/ParseName.hxx"
#include "tag/Tag.hxx"
#include "tag/Builder.hxx"
@@ -85,7 +85,7 @@ song_save(BufferedOutputStream &os, const DetachedSong &song)
}
DetachedSong
song_load(TextFile &file, const char *uri,
song_load(LineReader &file, const char *uri,
std::string *target_r)
{
DetachedSong song(uri);

@@ -28,7 +28,7 @@ struct Song;
struct AudioFormat;
class DetachedSong;
class BufferedOutputStream;
class TextFile;
class LineReader;
void
song_save(BufferedOutputStream &os, const Song &song);
@@ -43,7 +43,7 @@ song_save(BufferedOutputStream &os, const DetachedSong &song);
* Throws on error.
*/
DetachedSong
song_load(TextFile &file, const char *uri,
song_load(LineReader &file, const char *uri,
std::string *target_r=nullptr);
#endif

@@ -22,8 +22,8 @@
#include "output/State.hxx"
#include "queue/PlaylistState.hxx"
#include "fs/io/TextFile.hxx"
#include "fs/io/FileOutputStream.hxx"
#include "fs/io/BufferedOutputStream.hxx"
#include "io/FileOutputStream.hxx"
#include "io/BufferedOutputStream.hxx"
#include "storage/StorageState.hxx"
#include "Partition.hxx"
#include "Instance.hxx"

@@ -21,12 +21,16 @@
#include "TagStream.hxx"
#include "TagFile.hxx"
#include "tag/Generic.hxx"
#include "song/LightSong.hxx"
#include "db/Interface.hxx"
#include "storage/StorageInterface.hxx"
#include "client/Client.hxx"
#include "protocol/Ack.hxx"
#include "fs/AllocatedPath.hxx"
#include "input/InputStream.hxx"
#include "util/Compiler.h"
#include "util/ScopeExit.hxx"
#include "util/StringCompare.hxx"
#include "util/UriExtract.hxx"
#include "LocateUri.hxx"
@@ -51,10 +55,67 @@ TagScanFile(const Path path_fs, TagHandler &handler)
ScanGenericTags(path_fs, handler);
}
#ifdef ENABLE_DATABASE
/**
* Collapse "../" prefixes in a URI relative to the specified base
* URI.
*/
static std::string
ResolveUri(std::string_view base, const char *relative)
{
while (true) {
const char *rest = StringAfterPrefix(relative, "../");
if (rest == nullptr)
break;
if (base == ".")
throw ProtocolError(ACK_ERROR_NO_EXIST, "Bad real URI");
base = PathTraitsUTF8::GetParent(base);
relative = rest;
}
return PathTraitsUTF8::Build(base, relative);
}
/**
* Look up the specified song in the database and return its
* (resolved) "real" URI.
*/
static std::string
GetRealSongUri(Client &client, std::string_view uri)
{
const auto &db = client.GetDatabaseOrThrow();
const auto *song = db.GetSong(uri);
if (song == nullptr)
throw ProtocolError(ACK_ERROR_NO_EXIST, "No such song");
AtScopeExit(&db, song) { db.ReturnSong(song); };
if (song->real_uri == nullptr)
return {};
return ResolveUri(PathTraitsUTF8::GetParent(uri), song->real_uri);
}
#endif
static void
TagScanDatabase(Client &client, const char *uri, TagHandler &handler)
{
#ifdef ENABLE_DATABASE
const auto real_uri = GetRealSongUri(client, uri);
if (!real_uri.empty()) {
uri = real_uri.c_str();
// TODO: support absolute paths?
if (uri_has_scheme(uri))
return TagScanStream(uri, handler);
}
const Storage *storage = client.GetStorage();
if (storage == nullptr) {
#else

@@ -19,7 +19,7 @@
#include "TagSave.hxx"
#include "tag/Tag.hxx"
#include "fs/io/BufferedOutputStream.hxx"
#include "io/BufferedOutputStream.hxx"
#define SONG_TIME "Time: "

@@ -160,8 +160,7 @@ find_stream_art(std::string_view directory, Mutex &mutex)
static constexpr auto art_names = std::array {
"cover.png",
"cover.jpg",
"cover.tiff",
"cover.bmp",
"cover.webp",
};
for(const auto name : art_names) {

@@ -31,8 +31,8 @@
#include "fs/FileSystem.hxx"
#include "fs/List.hxx"
#include "fs/Path.hxx"
#include "fs/io/FileReader.hxx"
#include "fs/io/BufferedReader.hxx"
#include "io/FileReader.hxx"
#include "io/BufferedReader.hxx"
#include "Log.hxx"
#include <cassert>

@@ -20,8 +20,8 @@
#include "DatabaseSave.hxx"
#include "db/DatabaseLock.hxx"
#include "DirectorySave.hxx"
#include "fs/io/BufferedOutputStream.hxx"
#include "fs/io/TextFile.hxx"
#include "io/BufferedOutputStream.hxx"
#include "io/LineReader.hxx"
#include "tag/ParseName.hxx"
#include "tag/Settings.hxx"
#include "fs/Charset.hxx"
@@ -64,7 +64,7 @@ db_save_internal(BufferedOutputStream &os, const Directory &music_root)
}
void
db_load_internal(TextFile &file, Directory &music_root)
db_load_internal(LineReader &file, Directory &music_root)
{
char *line;
unsigned format = 0;

@@ -22,7 +22,7 @@
struct Directory;
class BufferedOutputStream;
class TextFile;
class LineReader;
void
db_save_internal(BufferedOutputStream &os, const Directory &root);
@@ -31,6 +31,6 @@ db_save_internal(BufferedOutputStream &os, const Directory &root);
* Throws #std::runtime_error on error.
*/
void
db_load_internal(TextFile &file, Directory &root);
db_load_internal(LineReader &file, Directory &root);
#endif

@@ -23,8 +23,8 @@
#include "SongSave.hxx"
#include "song/DetachedSong.hxx"
#include "PlaylistDatabase.hxx"
#include "fs/io/TextFile.hxx"
#include "fs/io/BufferedOutputStream.hxx"
#include "io/LineReader.hxx"
#include "io/BufferedOutputStream.hxx"
#include "time/ChronoUtil.hxx"
#include "util/StringAPI.hxx"
#include "util/StringCompare.hxx"
@@ -121,7 +121,7 @@ ParseLine(Directory &directory, const char *line)
}
static Directory *
directory_load_subdir(TextFile &file, Directory &parent, std::string_view name)
directory_load_subdir(LineReader &file, Directory &parent, std::string_view name)
{
if (parent.FindChild(name) != nullptr)
throw FormatRuntimeError("Duplicate subdirectory '%.*s'",
@@ -152,7 +152,7 @@ directory_load_subdir(TextFile &file, Directory &parent, std::string_view name)
}
void
directory_load(TextFile &file, Directory &directory)
directory_load(LineReader &file, Directory &directory)
{
const char *line;

@@ -21,7 +21,7 @@
#define MPD_DIRECTORY_SAVE_HXX
struct Directory;
class TextFile;
class LineReader;
class BufferedOutputStream;
void
@@ -31,6 +31,6 @@ directory_save(BufferedOutputStream &os, const Directory &directory);
* Throws #std::runtime_error on error.
*/
void
directory_load(TextFile &file, Directory &directory);
directory_load(LineReader &file, Directory &directory);
#endif

@@ -34,8 +34,8 @@
#include "db/DatabaseLock.hxx"
#include "db/DatabaseError.hxx"
#include "fs/io/TextFile.hxx"
#include "fs/io/BufferedOutputStream.hxx"
#include "fs/io/FileOutputStream.hxx"
#include "io/BufferedOutputStream.hxx"
#include "io/FileOutputStream.hxx"
#include "fs/FileInfo.hxx"
#include "config/Block.hxx"
#include "fs/FileSystem.hxx"
@@ -46,7 +46,7 @@
#include "Log.hxx"
#ifdef ENABLE_ZLIB
#include "fs/io/GzipOutputStream.hxx"
#include "lib/zlib/GzipOutputStream.hxx"
#endif
#include <cerrno>

@@ -20,7 +20,7 @@
#ifndef MPD_DECODER_READER_HXX
#define MPD_DECODER_READER_HXX
#include "fs/io/Reader.hxx"
#include "io/Reader.hxx"
class DecoderClient;
class InputStream;

@@ -21,6 +21,7 @@
#define __STDC_CONSTANT_MACROS
#include "FfmpegIo.hxx"
#include "libavutil/mem.h"
#include "../DecoderAPI.hxx"
#include "input/InputStream.hxx"
@@ -35,7 +36,11 @@ AvioStream::~AvioStream()
inline int
AvioStream::Read(void *dest, int size)
{
return decoder_read(client, input, dest, size);
const auto nbytes = decoder_read(client, input, dest, size);
if (nbytes == 0)
return AVERROR_EOF;
return nbytes;
}
inline int64_t

@@ -91,7 +91,8 @@ ScanOpusTags(const void *data, size_t size,
if (!r.Expect("OpusTags", 8))
return false;
if (!handler.WantPair() && !handler.WantTag())
if (!handler.WantPair() && !handler.WantTag() &&
!handler.WantPicture())
return true;
if (!r.SkipString())

@@ -27,7 +27,7 @@
#include "fs/AllocatedPath.hxx"
#include "lib/icu/Converter.hxx"
#ifdef HAVE_SIDPLAYFP
#include "fs/io/FileReader.hxx"
#include "io/FileReader.hxx"
#include "util/RuntimeError.hxx"
#endif
#include "util/StringFormat.hxx"

@@ -19,7 +19,7 @@
#include "ToOutputStream.hxx"
#include "EncoderInterface.hxx"
#include "fs/io/OutputStream.hxx"
#include "io/OutputStream.hxx"
void
EncoderToOutputStream(OutputStream &os, Encoder &encoder)

@@ -23,7 +23,7 @@
#include "filter/Prepared.hxx"
#include "pcm/Buffer.hxx"
#include "pcm/AudioFormat.hxx"
#include "AudioCompress/compress.h"
#include "pcm/AudioCompress/compress.h"
#include "util/ConstBuffer.hxx"
#include <string.h>

@@ -12,7 +12,6 @@ endif
filter_plugins = static_library(
'filter_plugins',
'../../AudioCompress/compress.c',
'NullFilterPlugin.cxx',
'TwoFilters.cxx',
'AutoConvertFilterPlugin.cxx',

@@ -41,7 +41,7 @@
#ifdef USE_XDG
#include "util/StringStrip.hxx"
#include "util/StringCompare.hxx"
#include "io/TextFile.hxx"
#include "fs/io/TextFile.hxx"
#include <string.h>
#include <utility>
#endif

@@ -18,9 +18,9 @@
*/
#include "TextFile.hxx"
#include "FileReader.hxx"
#include "AutoGunzipReader.hxx"
#include "BufferedReader.hxx"
#include "io/FileReader.hxx"
#include "io/BufferedReader.hxx"
#include "lib/zlib/AutoGunzipReader.hxx"
#include "fs/Path.hxx"
#include <cassert>

@@ -20,6 +20,7 @@
#ifndef MPD_TEXT_FILE_HXX
#define MPD_TEXT_FILE_HXX
#include "io/LineReader.hxx"
#include "config.h"
#include <memory>
@@ -29,7 +30,7 @@ class FileReader;
class AutoGunzipReader;
class BufferedReader;
class TextFile {
class TextFile final : public LineReader {
const std::unique_ptr<FileReader> file_reader;
#ifdef ENABLE_ZLIB
@@ -45,14 +46,8 @@ public:
~TextFile() noexcept;
/**
* Reads a line from the input file, and strips trailing
* space. There is a reasonable maximum line length, only to
* prevent denial of service.
*
* @return a pointer to the line, or nullptr on end-of-file
*/
char *ReadLine();
/* virtual methods from class LineReader */
char *ReadLine() override;
};
#endif

@@ -14,12 +14,7 @@ fs_sources = [
'CheckFile.cxx',
'LookupFile.cxx',
'DirectoryReader.cxx',
'io/PeekReader.cxx',
'io/FileReader.cxx',
'io/BufferedReader.cxx',
'io/TextFile.cxx',
'io/FileOutputStream.cxx',
'io/BufferedOutputStream.cxx',
]
if is_windows
@@ -28,14 +23,6 @@ else
shlwapi_dep = dependency('', required: false)
endif
if zlib_dep.found()
fs_sources += [
'io/GunzipReader.cxx',
'io/AutoGunzipReader.cxx',
'io/GzipOutputStream.cxx',
]
endif
fs = static_library(
'fs',
fs_sources,

@@ -18,7 +18,7 @@
*/
#include "IcyInputStream.hxx"
#include "IcyMetaDataParser.hxx"
#include "tag/IcyMetaDataParser.hxx"
#include "tag/Tag.hxx"
#include "util/UriExtract.hxx"
#include "util/UriQueryParser.hxx"

@@ -20,7 +20,7 @@
#ifndef MPD_INPUT_READER_HXX
#define MPD_INPUT_READER_HXX
#include "fs/io/Reader.hxx"
#include "io/Reader.hxx"
class InputStream;

@@ -27,7 +27,7 @@
#include "../MaybeBufferedInputStream.hxx"
#include "../AsyncInputStream.hxx"
#include "../IcyInputStream.hxx"
#include "IcyMetaDataParser.hxx"
#include "tag/IcyMetaDataParser.hxx"
#include "../InputPlugin.hxx"
#include "config/Block.hxx"
#include "tag/Builder.hxx"

@@ -21,7 +21,7 @@
#include "../InputStream.hxx"
#include "fs/Path.hxx"
#include "fs/FileInfo.hxx"
#include "fs/io/FileReader.hxx"
#include "io/FileReader.hxx"
#include "io/FileDescriptor.hxx"
#include "util/RuntimeError.hxx"

@@ -20,7 +20,7 @@ if curl_dep.found()
input_plugins_sources += [
'CurlInputPlugin.cxx',
'../IcyInputStream.cxx',
'../../IcyMetaDataParser.cxx',
'../../tag/IcyMetaDataParser.cxx',
]
endif

33
src/io/LineReader.hxx Normal file

@@ -0,0 +1,33 @@
/*
* Copyright 2003-2021 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.
*/
#pragma once
class LineReader
{
public:
/**
* Reads a line from the input file, and strips trailing
* space. There is a reasonable maximum line length, only to
* prevent denial of service.
*
* @return a pointer to the line, or nullptr on end-of-file
*/
virtual char *ReadLine() = 0;
};

@@ -2,6 +2,11 @@ io = static_library(
'io',
'FileDescriptor.cxx',
'Open.cxx',
'PeekReader.cxx',
'FileReader.cxx',
'BufferedReader.cxx',
'FileOutputStream.cxx',
'BufferedOutputStream.cxx',
include_directories: inc,
)

@@ -45,10 +45,15 @@ class Operation {
CancellableOperation *cancellable = nullptr;
public:
Operation() noexcept = default;
~Operation() noexcept {
CancelUring();
}
Operation(const Operation &) = delete;
Operation &operator=(const Operation &) = delete;
/**
* Are we waiting for the operation to complete?
*/

@@ -34,6 +34,8 @@
#include "CancellableOperation.hxx"
#include "util/DeleteDisposer.hxx"
#include <stdexcept>
namespace Uring {
Queue::Queue(unsigned entries, unsigned flags)
@@ -46,6 +48,23 @@ Queue::~Queue() noexcept
operations.clear_and_dispose(DeleteDisposer{});
}
struct io_uring_sqe &
Queue::RequireSubmitEntry()
{
auto *sqe = GetSubmitEntry();
if (sqe == nullptr) {
/* the submit queue is full; submit it to the kernel
and try again */
Submit();
sqe = GetSubmitEntry();
if (sqe == nullptr)
throw std::runtime_error{"io_uring_get_sqe() failed"};
}
return *sqe;
}
void
Queue::AddPending(struct io_uring_sqe &sqe,
Operation &operation) noexcept

@@ -63,6 +63,14 @@ public:
return ring.GetSubmitEntry();
}
/**
* Like GetSubmitEntry(), but call Submit() if the submit
* queue is full.
*
* May throw exceptions if Submit() fails.
*/
struct io_uring_sqe &RequireSubmitEntry();
bool HasPending() const noexcept {
return !operations.empty();
}

@@ -45,14 +45,13 @@ ReadOperation::Start(Queue &queue, FileDescriptor fd, off_t offset,
buffer = std::make_unique<std::byte[]>(size);
auto *s = queue.GetSubmitEntry();
assert(s != nullptr); // TODO: what if the submit queue is full?
auto &s = queue.RequireSubmitEntry();
iov.iov_base = buffer.get();
iov.iov_len = size;
io_uring_prep_readv(s, fd.Get(), &iov, 1, offset);
queue.Push(*s, *this);
io_uring_prep_readv(&s, fd.Get(), &iov, 1, offset);
queue.Push(s, *this);
}
void

@@ -29,6 +29,7 @@
#include "Error.hxx"
#include <stdarg.h>
#include <alsa/error.h>
namespace Alsa {

@@ -50,4 +50,4 @@ MakeError(int error, const char *msg) noexcept
return std::system_error(error, error_category, msg);
}
} // namespace Avahi
} // namespace Alsa

@@ -60,7 +60,7 @@ CurlUnescape(CURL *curl, StringView src) noexcept
int outlength;
CurlString tmp(curl_easy_unescape(curl, src.data, src.size,
&outlength));
return std::string(tmp.c_str(), outlength);
return {tmp.c_str(), size_t(outlength)};
}
std::string

@@ -1,14 +0,0 @@
Index: libnfs-libnfs-4.0.0/include/win32/win32_compat.h
===================================================================
--- libnfs-libnfs-4.0.0.orig/include/win32/win32_compat.h
+++ libnfs-libnfs-4.0.0/include/win32/win32_compat.h
@@ -133,7 +133,9 @@ struct pollfd {
/* Wrapper macros to call misc. functions win32 is missing */
#define poll(x, y, z) win32_poll(x, y, z)
+#ifndef __MINGW32__
#define snprintf sprintf_s
+#endif
#define inet_pton(x,y,z) win32_inet_pton(x,y,z)
#define open(x, y, z) _open(x, y, z)
#ifndef lseek

@@ -1 +0,0 @@
no_sprintf_s

@@ -18,7 +18,7 @@
*/
#include "OggSyncState.hxx"
#include "fs/io/Reader.hxx"
#include "io/Reader.hxx"
bool
OggSyncState::Feed(size_t size)

@@ -1,4 +1,11 @@
libflac_dep = dependency('flac', version: '>= 1.2', required: get_option('flac'))
if is_windows
# Our Windows build generates a static libFLAC build
libflac_dep = declare_dependency(compile_args: '-DFLAC__NO_DLL',
dependencies: libflac_dep)
endif
libopus_dep = dependency('opus', required: get_option('opus'))
if get_option('tremor').enabled()

@@ -20,7 +20,7 @@
#ifndef MPD_AUTO_GUNZIP_READER_HXX
#define MPD_AUTO_GUNZIP_READER_HXX
#include "PeekReader.hxx"
#include "io/PeekReader.hxx"
#include <memory>

@@ -30,7 +30,7 @@
#ifndef GUNZIP_READER_HXX
#define GUNZIP_READER_HXX
#include "Reader.hxx"
#include "io/Reader.hxx"
#include "util/StaticFifoBuffer.hxx"
#include <zlib.h>

@@ -30,7 +30,7 @@
#ifndef GZIP_OUTPUT_STREAM_HXX
#define GZIP_OUTPUT_STREAM_HXX
#include "OutputStream.hxx"
#include "io/OutputStream.hxx"
#include <zlib.h>

@@ -7,6 +7,9 @@ endif
zlib = static_library(
'zlib',
'Error.cxx',
'GunzipReader.cxx',
'GzipOutputStream.cxx',
'AutoGunzipReader.cxx',
include_directories: inc,
dependencies: [
zlib_dep,
@@ -17,5 +20,6 @@ zlib_dep = declare_dependency(
link_with: zlib,
dependencies: [
zlib_dep,
io_dep,
],
)

@@ -23,7 +23,7 @@
#include "util/StringCompare.hxx"
#include "util/Domain.hxx"
#include "system/PeriodClock.hxx"
#include "fs/io/BufferedOutputStream.hxx"
#include "io/BufferedOutputStream.hxx"
#include "Log.hxx"
#include <cassert>

@@ -26,7 +26,7 @@
#include "MultipleOutputs.hxx"
#include "Domain.hxx"
#include "Log.hxx"
#include "fs/io/BufferedOutputStream.hxx"
#include "io/BufferedOutputStream.hxx"
#include "util/StringCompare.hxx"
#include <stdlib.h>

@@ -336,6 +336,8 @@ PipeWireOutput::Enable()
throw MakeErrno("pw_thread_loop_new() failed");
pw_thread_loop_start(thread_loop);
stream = nullptr;
}
void

@@ -27,7 +27,7 @@
#include "config/Path.hxx"
#include "Log.hxx"
#include "fs/AllocatedPath.hxx"
#include "fs/io/FileOutputStream.hxx"
#include "io/FileOutputStream.hxx"
#include "util/Domain.hxx"
#include "util/ScopeExit.hxx"

@@ -471,6 +471,16 @@ try {
}
UINT32 write_in_frames = buffer_size_in_frames;
DWORD mode = 0;
AtScopeExit(&) {
render_client->ReleaseBuffer(write_in_frames, mode);
if (!started) {
Start(client);
started = true;
}
};
if (!is_exclusive) {
UINT32 data_in_frames =
GetCurrentPaddingFrames(client);
@@ -481,7 +491,6 @@ try {
}
BYTE *data;
DWORD mode = 0;
if (HRESULT result =
render_client->GetBuffer(write_in_frames, &data);
@@ -489,15 +498,6 @@ try {
throw MakeHResultError(result, "Failed to get buffer");
}
AtScopeExit(&) {
render_client->ReleaseBuffer(write_in_frames, mode);
if (!started) {
Start(client);
started = true;
}
};
const UINT32 write_size = write_in_frames * frame_size;
UINT32 new_data_size = 0;
new_data_size = spsc_buffer.pop(data, write_size);

@@ -140,8 +140,7 @@ LibsampleratePcmResampler::Resample2(ConstBuffer<float> src)
throw FormatRuntimeError("libsamplerate has failed: %s",
src_strerror(result));
return ConstBuffer<float>(data.data_out,
data.output_frames_gen * channels);
return {data.data_out, size_t(data.output_frames_gen * channels)};
}
ConstBuffer<void>

@@ -46,6 +46,7 @@ pcm_sources = [
'GlueResampler.cxx',
'FallbackResampler.cxx',
'ConfiguredResampler.cxx',
'AudioCompress/compress.c',
]
libsamplerate_dep = dependency('samplerate', version: '>= 0.1.3', required: get_option('libsamplerate'))

@@ -28,8 +28,8 @@
#include "SingleMode.hxx"
#include "StateFileConfig.hxx"
#include "queue/QueueSave.hxx"
#include "fs/io/TextFile.hxx"
#include "fs/io/BufferedOutputStream.hxx"
#include "io/LineReader.hxx"
#include "io/BufferedOutputStream.hxx"
#include "player/Control.hxx"
#include "util/CharUtil.hxx"
#include "util/StringAPI.hxx"
@@ -102,7 +102,7 @@ playlist_state_save(BufferedOutputStream &os, const struct playlist &playlist,
}
static void
playlist_state_load(TextFile &file, const SongLoader &song_loader,
playlist_state_load(LineReader &file, const SongLoader &song_loader,
struct playlist &playlist)
{
const char *line = file.ReadLine();
@@ -128,7 +128,7 @@ playlist_state_load(TextFile &file, const SongLoader &song_loader,
bool
playlist_state_restore(const StateFileConfig &config,
const char *line, TextFile &file,
const char *line, LineReader &file,
const SongLoader &song_loader,
struct playlist &playlist, PlayerControl &pc)
{

@@ -28,7 +28,7 @@
struct StateFileConfig;
struct playlist;
class PlayerControl;
class TextFile;
class LineReader;
class BufferedOutputStream;
class SongLoader;
@@ -38,7 +38,7 @@ playlist_state_save(BufferedOutputStream &os, const playlist &playlist,
bool
playlist_state_restore(const StateFileConfig &config,
const char *line, TextFile &file,
const char *line, LineReader &file,
const SongLoader &song_loader,
playlist &playlist, PlayerControl &pc);

@@ -23,8 +23,8 @@
#include "song/DetachedSong.hxx"
#include "SongSave.hxx"
#include "playlist/PlaylistSong.hxx"
#include "fs/io/TextFile.hxx"
#include "fs/io/BufferedOutputStream.hxx"
#include "io/LineReader.hxx"
#include "io/BufferedOutputStream.hxx"
#include "util/StringCompare.hxx"
#include "Log.hxx"
@@ -74,7 +74,7 @@ queue_save(BufferedOutputStream &os, const Queue &queue)
}
static DetachedSong
LoadQueueSong(TextFile &file, const char *line)
LoadQueueSong(LineReader &file, const char *line)
{
std::unique_ptr<DetachedSong> song;
@@ -94,7 +94,7 @@ LoadQueueSong(TextFile &file, const char *line)
}
void
queue_load_song(TextFile &file, const SongLoader &loader,
queue_load_song(LineReader &file, const SongLoader &loader,
const char *line, Queue &queue)
{
if (queue.IsFull())

@@ -27,7 +27,7 @@
struct Queue;
class BufferedOutputStream;
class TextFile;
class LineReader;
class SongLoader;
void
@@ -39,7 +39,7 @@ queue_save(BufferedOutputStream &os, const Queue &queue);
* Throws on error.
*/
void
queue_load_song(TextFile &file, const SongLoader &loader,
queue_load_song(LineReader &file, const SongLoader &loader,
const char *line, Queue &queue);
#endif

@@ -24,8 +24,8 @@
#include "StorageState.hxx"
#include "lib/fmt/ExceptionFormatter.hxx"
#include "fs/io/TextFile.hxx"
#include "fs/io/BufferedOutputStream.hxx"
#include "io/LineReader.hxx"
#include "io/BufferedOutputStream.hxx"
#include "storage/Registry.hxx"
#include "storage/CompositeStorage.hxx"
#include "db/plugins/simple/SimpleDatabasePlugin.hxx"
@@ -73,7 +73,7 @@ storage_state_save(BufferedOutputStream &os, const Instance &instance)
}
bool
storage_state_restore(const char *line, TextFile &file, Instance &instance)
storage_state_restore(const char *line, LineReader &file, Instance &instance)
{
if (!StringStartsWith(line, MOUNT_STATE_BEGIN))
return false;

@@ -27,13 +27,14 @@
struct Instance;
class BufferedOutputStream;
class TextFile;
class LineReader;
void
storage_state_save(BufferedOutputStream &os, const Instance &instance);
bool
storage_state_restore(const char *line, TextFile &file, Instance &instance);
storage_state_restore(const char *line, LineReader &file,
Instance &instance);
/**
* Generates a hash number for the current state of the composite storage.

@@ -1,5 +1,5 @@
/*
* Copyright 2016-2018 Max Kellermann <max.kellermann@gmail.com>
* Copyright 2016-2021 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
@@ -27,8 +27,7 @@
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef BIND_METHOD_HXX
#define BIND_METHOD_HXX
#pragma once
#include <type_traits>
#include <utility>
@@ -83,33 +82,15 @@ public:
namespace BindMethodDetail {
/**
* Helper class which converts a signature type to a method pointer
* type.
* Helper class which introspects a method/function pointer type.
*
* @param T the wrapped class
* @param S the function signature type (plain, without instance
* pointer)
*/
template<typename T, typename S>
struct MethodWithSignature;
template<typename T,
bool NoExcept,
typename R, typename... Args>
struct MethodWithSignature<T, R(Args...) noexcept(NoExcept)> {
typedef R (T::*method_pointer)(Args...) noexcept(NoExcept);
};
/**
* Helper class which introspects a method pointer type.
*
* @param M the method pointer type
* @param M the method/function pointer type
*/
template<typename M>
struct MethodSignatureHelper;
struct SignatureHelper;
template<typename R, bool NoExcept, typename T, typename... Args>
struct MethodSignatureHelper<R (T::*)(Args...) noexcept(NoExcept)> {
struct SignatureHelper<R (T::*)(Args...) noexcept(NoExcept)> {
/**
* The class which contains the given method (signature).
*/
@@ -120,126 +101,48 @@ struct MethodSignatureHelper<R (T::*)(Args...) noexcept(NoExcept)> {
* signature.
*/
typedef R plain_signature(Args...) noexcept(NoExcept);
typedef R (*function_pointer)(void *instance,
Args...) noexcept(NoExcept);
};
/**
* Helper class which converts a plain function signature type to a
* wrapper function pointer type.
*/
template<typename S>
struct MethodWrapperWithSignature;
template<typename R, bool NoExcept, typename... Args>
struct MethodWrapperWithSignature<R(Args...) noexcept(NoExcept)> {
struct SignatureHelper<R (*)(Args...) noexcept(NoExcept)> {
typedef R plain_signature(Args...) noexcept(NoExcept);
typedef R (*function_pointer)(void *instance,
Args...) noexcept(NoExcept);
};
/**
* Generate a wrapper function. Helper class for
* #BindMethodWrapperGenerator.
* Generate a wrapper function.
*
* @param T the containing class
* @param M the method pointer type
* @param method the method pointer
* @param R the return type
* @param Args the method arguments
* @param method the method/function pointer
*/
template<typename T, bool NoExcept, typename M, M method, typename R, typename... Args>
struct BindMethodWrapperGenerator2 {
template<typename M, auto method>
struct WrapperGenerator;
template<typename T, bool NoExcept,
auto method, typename R, typename... Args>
struct WrapperGenerator<R (T::*)(Args...) noexcept(NoExcept), method> {
static R Invoke(void *_instance, Args... args) noexcept(NoExcept) {
auto &t = *(T *)_instance;
return (t.*method)(std::forward<Args>(args)...);
}
};
/**
* Generate a wrapper function.
*
* @param T the containing class
* @param M the method pointer type
* @param method the method pointer
* @param S the plain function signature type
*/
template<typename T, typename M, M method, typename S>
struct BindMethodWrapperGenerator;
template<typename T, bool NoExcept,
typename M, M method, typename R, typename... Args>
struct BindMethodWrapperGenerator<T, M, method, R(Args...) noexcept(NoExcept)>
: BindMethodWrapperGenerator2<T, NoExcept, M, method, R, Args...> {
};
template<typename T, typename S,
typename MethodWithSignature<T, S>::method_pointer method>
typename MethodWrapperWithSignature<S>::function_pointer
MakeBindMethodWrapper() noexcept
{
return BindMethodWrapperGenerator<T, typename MethodWithSignature<T, S>::method_pointer, method, S>::Invoke;
}
/**
* Helper class which introspects a function pointer type.
*
* @param S the function type
*/
template<typename S>
struct FunctionTraits;
template<typename R, bool NoExcept, typename... Args>
struct FunctionTraits<R(Args...) noexcept(NoExcept)> {
/**
* A function type which describes the "plain" function
* signature.
*/
typedef R function_type(Args...) noexcept(NoExcept);
/**
* A function pointer type which describes the "plain"
* function signature.
*/
typedef R (*pointer)(Args...) noexcept(NoExcept);
};
/**
* Generate a wrapper function for a plain function which ignores the
* instance pointer. Helper class for
* #BindFunctionWrapperGenerator.
*
* @param F the function pointer type
* @param function the function pointer
* @param R the return type
* @param Args the function arguments
*/
template<bool NoExcept, typename F, F function, typename R, typename... Args>
struct BindFunctionWrapperGenerator2 {
template<auto function, bool NoExcept, typename R, typename... Args>
struct WrapperGenerator<R (*)(Args...) noexcept(NoExcept), function> {
static R Invoke(void *, Args... args) noexcept(NoExcept) {
return function(std::forward<Args>(args)...);
}
};
/**
* Generate a wrapper function.
*
* @param S the plain function signature type
* @param P the plain function pointer type
* @param function the function pointer
*/
template<typename S, typename P, P function>
struct BindFunctionWrapperGenerator;
template<typename P, P function, bool NoExcept, typename R, typename... Args>
struct BindFunctionWrapperGenerator<R(Args...) noexcept(NoExcept), P, function>
: BindFunctionWrapperGenerator2<NoExcept, P, function, R, Args...> {
};
template<typename T, typename T::pointer function>
typename MethodWrapperWithSignature<typename T::function_type>::function_pointer
MakeBindFunctionWrapper() noexcept
template<auto method>
typename SignatureHelper<decltype(method)>::function_pointer
MakeWrapperFunction() noexcept
{
return BindFunctionWrapperGenerator<typename T::function_type,
typename T::pointer,
function>::Invoke;
return WrapperGenerator<decltype(method), method>::Invoke;
}
} /* namespace BindMethodDetail */
@@ -247,18 +150,19 @@ MakeBindFunctionWrapper() noexcept
/**
* Construct a #BoundMethod instance.
*
* @param T the containing class
* @param S the plain function signature type
* @param method the method pointer
* @param instance the instance of #T to be bound
*/
template<typename T, typename S,
typename BindMethodDetail::MethodWithSignature<T, S>::method_pointer method>
constexpr BoundMethod<S>
BindMethod(T &_instance) noexcept
template<auto method>
constexpr auto
BindMethod(typename BindMethodDetail::SignatureHelper<decltype(method)>::class_type &instance) noexcept
{
return BoundMethod<S>(&_instance,
BindMethodDetail::MakeBindMethodWrapper<T, S, method>());
using H = BindMethodDetail::SignatureHelper<decltype(method)>;
using plain_signature = typename H::plain_signature;
return BoundMethod<plain_signature>{
&instance,
BindMethodDetail::MakeWrapperFunction<method>(),
};
}
/**
@@ -266,28 +170,29 @@ BindMethod(T &_instance) noexcept
* constructs a #BoundMethod instance.
*/
#define BIND_METHOD(instance, method) \
BindMethod<typename BindMethodDetail::MethodSignatureHelper<decltype(method)>::class_type, \
typename BindMethodDetail::MethodSignatureHelper<decltype(method)>::plain_signature, \
method>(instance)
BindMethod<method>(instance)
/**
* Shortcut wrapper for BIND_METHOD() which assumes "*this" is the
* instance to be bound.
*/
#define BIND_THIS_METHOD(method) BIND_METHOD(*this, &std::remove_reference<decltype(*this)>::type::method)
#define BIND_THIS_METHOD(method) BIND_METHOD(*this, &std::remove_reference_t<decltype(*this)>::method)
/**
* Construct a #BoundMethod instance for a plain function.
*
* @param T the #FunctionTraits class
* @param function the function pointer
*/
template<typename T, typename T::pointer function>
constexpr BoundMethod<typename T::function_type>
template<auto function>
constexpr auto
BindFunction() noexcept
{
return BoundMethod<typename T::function_type>(nullptr,
BindMethodDetail::MakeBindFunctionWrapper<T, function>());
using H = BindMethodDetail::SignatureHelper<decltype(function)>;
using plain_signature = typename H::plain_signature;
return BoundMethod<plain_signature>{
nullptr,
BindMethodDetail::MakeWrapperFunction<function>(),
};
}
/**
@@ -295,6 +200,4 @@ BindFunction() noexcept
* #BoundMethod instance.
*/
#define BIND_FUNCTION(function) \
BindFunction<typename BindMethodDetail::FunctionTraits<decltype(function)>, &function>()
#endif
BindFunction<&function>()

@@ -49,6 +49,11 @@ protected:
IntrusiveListNode siblings;
public:
IntrusiveListHook() noexcept = default;
IntrusiveListHook(const IntrusiveListHook &) = delete;
IntrusiveListHook &operator=(const IntrusiveListHook &) = delete;
void unlink() noexcept {
siblings.next->prev = siblings.prev;
siblings.prev->next = siblings.next;

Some files were not shown because too many files have changed in this diff Show More