Compare commits
19 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
66a8fac25e | ||
![]() |
1b902e00b4 | ||
![]() |
923e66738c | ||
![]() |
ff3e2c0514 | ||
![]() |
6922a2f55e | ||
![]() |
ca5a400dbe | ||
![]() |
63fe4d1d17 | ||
![]() |
ca06d9d3bf | ||
![]() |
ed2db04f43 | ||
![]() |
de0afa0e08 | ||
![]() |
f0d3227d7b | ||
![]() |
fb07a7cecc | ||
![]() |
c6b08a4d48 | ||
![]() |
040e87ad8d | ||
![]() |
d5521ead56 | ||
![]() |
f8468451c9 | ||
![]() |
65df6ca14e | ||
![]() |
36dec47bf7 | ||
![]() |
478cedcadf |
8
NEWS
8
NEWS
@@ -1,3 +1,11 @@
|
|||||||
|
ver 0.21.9 (2019/05/20)
|
||||||
|
* input
|
||||||
|
- buffer: fix deadlock bug
|
||||||
|
* Android
|
||||||
|
- fix crash on ARMv7
|
||||||
|
- request storage permission on Android 6+
|
||||||
|
* fix spurious "single" mode bug
|
||||||
|
|
||||||
ver 0.21.8 (2019/04/23)
|
ver 0.21.8 (2019/04/23)
|
||||||
* input
|
* input
|
||||||
- smbclient: download to buffer instead of throttling transfer
|
- smbclient: download to buffer instead of throttling transfer
|
||||||
|
@@ -2,8 +2,8 @@
|
|||||||
<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="30"
|
android:versionCode="32"
|
||||||
android:versionName="0.21.8">
|
android:versionName="0.21.9">
|
||||||
|
|
||||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="26"/>
|
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="26"/>
|
||||||
|
|
||||||
|
@@ -138,6 +138,12 @@ class AndroidNdkToolchain:
|
|||||||
libstdcxx_ldflags = libstdcxx_flags + ' -L' + libcxx_libs_path
|
libstdcxx_ldflags = libstdcxx_flags + ' -L' + libcxx_libs_path
|
||||||
libstdcxx_libs = '-lc++_static -lc++abi'
|
libstdcxx_libs = '-lc++_static -lc++abi'
|
||||||
|
|
||||||
|
if self.is_armv7:
|
||||||
|
# On 32 bit ARM, clang generates no ".eh_frame" section;
|
||||||
|
# instead, the LLVM unwinder library is used for unwinding
|
||||||
|
# the stack after a C++ exception was thrown
|
||||||
|
libstdcxx_libs += ' -lunwind'
|
||||||
|
|
||||||
if use_cxx:
|
if use_cxx:
|
||||||
self.cxxflags += ' ' + libstdcxx_cxxflags
|
self.cxxflags += ' ' + libstdcxx_cxxflags
|
||||||
self.ldflags += ' ' + libstdcxx_ldflags
|
self.ldflags += ' ' + libstdcxx_ldflags
|
||||||
|
@@ -6,7 +6,7 @@ android_sdk = get_option('android_sdk')
|
|||||||
android_abi = get_option('android_abi')
|
android_abi = get_option('android_abi')
|
||||||
|
|
||||||
android_sdk_build_tools_version = '27.0.0'
|
android_sdk_build_tools_version = '27.0.0'
|
||||||
android_sdk_platform = 'android-21'
|
android_sdk_platform = 'android-23'
|
||||||
|
|
||||||
android_build_tools_dir = join_paths(android_sdk, 'build-tools', android_sdk_build_tools_version)
|
android_build_tools_dir = join_paths(android_sdk, 'build-tools', android_sdk_build_tools_version)
|
||||||
android_sdk_platform_dir = join_paths(android_sdk, 'platforms', android_sdk_platform)
|
android_sdk_platform_dir = join_paths(android_sdk, 'platforms', android_sdk_platform)
|
||||||
|
@@ -21,10 +21,12 @@ package org.musicpd;
|
|||||||
|
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.content.SharedPreferences.Editor;
|
import android.content.SharedPreferences.Editor;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
@@ -178,6 +180,14 @@ public class Settings extends Activity {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
/* TODO: this sure is the wrong place to request
|
||||||
|
permissions - it will cause MPD to quit
|
||||||
|
immediately; we should request permissions when we
|
||||||
|
need them, but implementing that is complicated, so
|
||||||
|
for now, we do it here to give users a quick
|
||||||
|
solution for the problem */
|
||||||
|
requestAllPermissions();
|
||||||
|
|
||||||
setContentView(R.layout.settings);
|
setContentView(R.layout.settings);
|
||||||
mRunButton = (ToggleButton) findViewById(R.id.run);
|
mRunButton = (ToggleButton) findViewById(R.id.run);
|
||||||
mRunButton.setOnCheckedChangeListener(mOnRunChangeListener);
|
mRunButton.setOnCheckedChangeListener(mOnRunChangeListener);
|
||||||
@@ -203,6 +213,31 @@ public class Settings extends Activity {
|
|||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void checkRequestPermission(String permission) {
|
||||||
|
if (checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.requestPermissions(new String[]{permission}, 0);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "requestPermissions(" + permission + ") failed",
|
||||||
|
e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void requestAllPermissions() {
|
||||||
|
if (android.os.Build.VERSION.SDK_INT < 23)
|
||||||
|
/* we don't need to request permissions on
|
||||||
|
this old Android version */
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* starting with Android 6.0, we need to explicitly
|
||||||
|
request all permissions before using them;
|
||||||
|
mentioning them in the manifest is not enough */
|
||||||
|
|
||||||
|
checkRequestPermission(Manifest.permission.READ_EXTERNAL_STORAGE);
|
||||||
|
}
|
||||||
|
|
||||||
private void connectClient() {
|
private void connectClient() {
|
||||||
mClient = new Main.Client(this, new Main.Client.Callback() {
|
mClient = new Main.Client(this, new Main.Client.Callback() {
|
||||||
|
|
||||||
|
@@ -38,7 +38,7 @@ author = 'Max Kellermann'
|
|||||||
# built documents.
|
# built documents.
|
||||||
#
|
#
|
||||||
# The short X.Y version.
|
# The short X.Y version.
|
||||||
version = '0.21.8'
|
version = '0.21.9'
|
||||||
# The full version, including alpha/beta/rc tags.
|
# The full version, including alpha/beta/rc tags.
|
||||||
release = version
|
release = version
|
||||||
|
|
||||||
|
@@ -140,7 +140,6 @@ of database.
|
|||||||
.B auto_update_depth <N>
|
.B auto_update_depth <N>
|
||||||
Limit the depth of the directories being watched, 0 means only watch
|
Limit the depth of the directories being watched, 0 means only watch
|
||||||
the music directory itself. There is no limit by default.
|
the music directory itself. There is no limit by default.
|
||||||
.TP
|
|
||||||
.SH REQUIRED AUDIO OUTPUT PARAMETERS
|
.SH REQUIRED AUDIO OUTPUT PARAMETERS
|
||||||
.TP
|
.TP
|
||||||
.B type <type>
|
.B type <type>
|
||||||
@@ -164,57 +163,12 @@ Specifies how replay gain is applied. The default is "software",
|
|||||||
which uses an internal software volume control. "mixer" uses the
|
which uses an internal software volume control. "mixer" uses the
|
||||||
configured (hardware) mixer control. "none" disables replay gain on
|
configured (hardware) mixer control. "none" disables replay gain on
|
||||||
this audio output.
|
this audio output.
|
||||||
.SH OPTIONAL ALSA OUTPUT PARAMETERS
|
|
||||||
.TP
|
|
||||||
.B device <dev>
|
|
||||||
This specifies the device to use for audio output. The default is "default".
|
|
||||||
.TP
|
.TP
|
||||||
.B mixer_type <hardware, software or none>
|
.B mixer_type <hardware, software or none>
|
||||||
Specifies which mixer should be used for this audio output: the
|
Specifies which mixer should be used for this audio output: the
|
||||||
hardware mixer (available for ALSA, OSS and PulseAudio), the software
|
hardware mixer (available for ALSA, OSS and PulseAudio), the software
|
||||||
mixer or no mixer ("none"). By default, the hardware mixer is used
|
mixer or no mixer ("none"). By default, the hardware mixer is used
|
||||||
for devices which support it, and none for the others.
|
for devices which support it, and none for the others.
|
||||||
.TP
|
|
||||||
.B mixer_device <mixer dev>
|
|
||||||
This specifies which mixer to use. The default is "default". To use
|
|
||||||
the second sound card in a system, use "hw:1".
|
|
||||||
.TP
|
|
||||||
.B mixer_control <mixer ctrl>
|
|
||||||
This specifies which mixer control to use (sometimes referred to as
|
|
||||||
the "device"). The default is "PCM". Use "amixer scontrols" to see
|
|
||||||
the list of possible controls.
|
|
||||||
.TP
|
|
||||||
.B mixer_index <mixer index>
|
|
||||||
A number identifying the index of the named mixer control. This is
|
|
||||||
probably only useful if your alsa device has more than one
|
|
||||||
identically\-named mixer control. The default is "0". Use "amixer
|
|
||||||
scontrols" to see the list of controls with their indexes.
|
|
||||||
.TP
|
|
||||||
.B auto_resample <yes or no>
|
|
||||||
Setting this to "no" disables ALSA's software resampling, if the
|
|
||||||
hardware does not support a specific sample rate. This lets MPD do
|
|
||||||
the resampling. "yes" is the default and allows ALSA to resample.
|
|
||||||
.TP
|
|
||||||
.B auto_channels <yes or no>
|
|
||||||
Setting this to "no" disables ALSA's channel conversion, if the
|
|
||||||
hardware does not support a specific number of channels. Default: "yes".
|
|
||||||
.TP
|
|
||||||
.B auto_format <yes or no>
|
|
||||||
Setting this to "no" disables ALSA's sample format conversion, if the
|
|
||||||
hardware does not support a specific sample format. Default: "yes".
|
|
||||||
.TP
|
|
||||||
.B buffer_time <time in microseconds>
|
|
||||||
This sets the length of the hardware sample buffer in microseconds. Increasing
|
|
||||||
it may help to reduce or eliminate skipping on certain setups. Most users do
|
|
||||||
not need to change this. The default is 500000 microseconds (0.5 seconds).
|
|
||||||
.TP
|
|
||||||
.B period_time <time in microseconds>
|
|
||||||
This sets the time between hardware sample transfers in microseconds.
|
|
||||||
Increasing this can reduce CPU usage while lowering it can reduce underrun
|
|
||||||
errors on bandwidth-limited devices. Some users have reported good results
|
|
||||||
with this set to 50000, but not all devices support values this high. Most
|
|
||||||
users do not need to change this. The default is 256000000 / sample_rate(kHz),
|
|
||||||
or 5804 microseconds for CD-quality audio.
|
|
||||||
.SH FILES
|
.SH FILES
|
||||||
.TP
|
.TP
|
||||||
.BI ~/.mpdconf
|
.BI ~/.mpdconf
|
||||||
|
@@ -14,6 +14,9 @@ Once the client is connected to the server, they conduct a
|
|||||||
conversation until the client closes the connection. The
|
conversation until the client closes the connection. The
|
||||||
conversation flow is always initiated by the client.
|
conversation flow is always initiated by the client.
|
||||||
|
|
||||||
|
All data between the client and the server is encoded in
|
||||||
|
UTF-8.
|
||||||
|
|
||||||
The client transmits a command sequence, terminated by the
|
The client transmits a command sequence, terminated by the
|
||||||
newline character ``\n``. The server will
|
newline character ``\n``. The server will
|
||||||
respond with one or more lines, the last of which will be a
|
respond with one or more lines, the last of which will be a
|
||||||
@@ -42,9 +45,6 @@ quotation marks.
|
|||||||
Argument strings are separated from the command and any other
|
Argument strings are separated from the command and any other
|
||||||
arguments by linear white-space (' ' or '\\t').
|
arguments by linear white-space (' ' or '\\t').
|
||||||
|
|
||||||
All data between the client and the server is encoded in
|
|
||||||
UTF-8.
|
|
||||||
|
|
||||||
Responses
|
Responses
|
||||||
=========
|
=========
|
||||||
|
|
||||||
@@ -52,6 +52,28 @@ A command returns ``OK`` on completion or
|
|||||||
``ACK some error`` on failure. These
|
``ACK some error`` on failure. These
|
||||||
denote the end of command execution.
|
denote the end of command execution.
|
||||||
|
|
||||||
|
Some commands return more data before the response ends with ``OK``.
|
||||||
|
Each line is usually in the form ``NAME: VALUE``. Example::
|
||||||
|
|
||||||
|
foo: bar
|
||||||
|
OK
|
||||||
|
|
||||||
|
.. _binary:
|
||||||
|
|
||||||
|
Binary Responses
|
||||||
|
----------------
|
||||||
|
|
||||||
|
Some commands can return binary data. This is initiated by a line
|
||||||
|
containing ``binary: 1234`` (followed as usual by a newline). After
|
||||||
|
that, the specified number of bytes of binary data follows (without an
|
||||||
|
extra newline, because this binary data is not a text line), and
|
||||||
|
finally the ``OK`` line. Example::
|
||||||
|
|
||||||
|
foo: bar
|
||||||
|
binary: 42
|
||||||
|
<42 bytes>OK
|
||||||
|
|
||||||
|
|
||||||
Failure responses
|
Failure responses
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
@@ -112,9 +134,9 @@ list begins with `command_list_begin` or
|
|||||||
`command_list_ok_begin` and ends with
|
`command_list_ok_begin` and ends with
|
||||||
`command_list_end`.
|
`command_list_end`.
|
||||||
|
|
||||||
It does not execute any commands until the list has ended.
|
It does not execute any commands until the list has ended. The
|
||||||
The return value is whatever the return for a list of commands
|
response is a concatentation of all individual responses.
|
||||||
is. On success for all commands,
|
On success for all commands,
|
||||||
``OK`` is returned. If a command
|
``OK`` is returned. If a command
|
||||||
fails, no more commands are executed and the appropriate
|
fails, no more commands are executed and the appropriate
|
||||||
``ACK`` error is returned. If
|
``ACK`` error is returned. If
|
||||||
@@ -178,8 +200,9 @@ of:
|
|||||||
file's time stamp with the given value (ISO 8601 or UNIX
|
file's time stamp with the given value (ISO 8601 or UNIX
|
||||||
time stamp).
|
time stamp).
|
||||||
|
|
||||||
- ``(AudioFormat == 'SAMPLERATE:BITS:CHANNELS')``:
|
- ``(AudioFormat == 'SAMPLERATE:BITS:CHANNELS')``: compares the audio
|
||||||
compares the audio format with the given value.
|
format with the given value. See :ref:`audio_output_format` for a
|
||||||
|
detailed explanation.
|
||||||
|
|
||||||
- ``(AudioFormat =~ 'SAMPLERATE:BITS:CHANNELS')``:
|
- ``(AudioFormat =~ 'SAMPLERATE:BITS:CHANNELS')``:
|
||||||
matches the audio format with the given mask (i.e. one
|
matches the audio format with the given mask (i.e. one
|
||||||
@@ -423,7 +446,9 @@ Querying :program:`MPD`'s status
|
|||||||
- ``xfade``: ``crossfade`` in seconds
|
- ``xfade``: ``crossfade`` in seconds
|
||||||
- ``mixrampdb``: ``mixramp`` threshold in dB
|
- ``mixrampdb``: ``mixramp`` threshold in dB
|
||||||
- ``mixrampdelay``: ``mixrampdelay`` in seconds
|
- ``mixrampdelay``: ``mixrampdelay`` in seconds
|
||||||
- ``audio``: The format emitted by the decoder plugin during playback, format: ``*samplerate:bits:channels*``. Check the user manual for a detailed explanation.
|
- ``audio``: The format emitted by the decoder plugin during
|
||||||
|
playback, format: ``samplerate:bits:channels``. See
|
||||||
|
:ref:`audio_output_format` for a detailed explanation.
|
||||||
- ``updating_db``: ``job id``
|
- ``updating_db``: ``job id``
|
||||||
- ``error``: if there is an error, returns message here
|
- ``error``: if there is an error, returns message here
|
||||||
|
|
||||||
@@ -791,7 +816,7 @@ The music database
|
|||||||
|
|
||||||
Returns the file size and actual number
|
Returns the file size and actual number
|
||||||
of bytes read at the requested offset, followed
|
of bytes read at the requested offset, followed
|
||||||
by the chunk requested as raw bytes, then a
|
by the chunk requested as raw bytes (see :ref:`binary`), then a
|
||||||
newline and the completion code.
|
newline and the completion code.
|
||||||
|
|
||||||
Example::
|
Example::
|
||||||
@@ -799,8 +824,7 @@ The music database
|
|||||||
albumart
|
albumart
|
||||||
size: 1024768
|
size: 1024768
|
||||||
binary: 8192
|
binary: 8192
|
||||||
<8192 bytes>
|
<8192 bytes>OK
|
||||||
OK
|
|
||||||
|
|
||||||
:command:`count {FILTER} [group {GROUPTYPE}]`
|
:command:`count {FILTER} [group {GROUPTYPE}]`
|
||||||
Count the number of songs and their total playtime in
|
Count the number of songs and their total playtime in
|
||||||
|
38
doc/user.rst
38
doc/user.rst
@@ -402,14 +402,9 @@ The following table lists the audio_output options valid for all plugins:
|
|||||||
- The name of the plugin
|
- The name of the plugin
|
||||||
* - **name**
|
* - **name**
|
||||||
- The name of the audio output. It is visible to the client. Some plugins also use it internally, e.g. as a name registered in the PULSE server.
|
- The name of the audio output. It is visible to the client. Some plugins also use it internally, e.g. as a name registered in the PULSE server.
|
||||||
* - **format**
|
* - **format samplerate:bits:channels**
|
||||||
- Always open the audio output with the specified audio format samplerate:bits:channels), regardless of the format of the input file. This is optional for most plugins.
|
- Always open the audio output with the specified audio format, regardless of the format of the input file. This is optional for most plugins.
|
||||||
|
See :ref:`audio_output_format` for a detailed description of the value.
|
||||||
Any of the three attributes may be an asterisk to specify that this attribute should not be enforced, example: 48000:16:*. *:*:* is equal to not having a format specification.
|
|
||||||
|
|
||||||
The following values are valid for bits: 8 (signed 8 bit integer samples), 16, 24 (signed 24 bit integer samples padded to 32 bit), 32 (signed 32 bit integer samples), f (32 bit floating point, -1.0 to 1.0), "dsd" means DSD (Direct Stream Digital). For DSD, there are special cases such as "dsd64", which allows you to omit the sample rate (e.g. dsd512:2 for stereo DSD512, i.e. 22.5792 MHz).
|
|
||||||
|
|
||||||
The sample rate is special for DSD: :program:`MPD` counts the number of bytes, not bits. Thus, a DSD "bit" rate of 22.5792 MHz (DSD512) is 2822400 from :program:`MPD`'s point of view (44100*512/8).
|
|
||||||
* - **enabed yes|no**
|
* - **enabed yes|no**
|
||||||
- Specifies whether this audio output is enabled when :program:`MPD` is started. By default, all audio outputs are enabled. This is just the default setting when there is no state file; with a state file, the previous state is restored.
|
- Specifies whether this audio output is enabled when :program:`MPD` is started. By default, all audio outputs are enabled. This is just the default setting when there is no state file; with a state file, the previous state is restored.
|
||||||
* - **tags yes|no**
|
* - **tags yes|no**
|
||||||
@@ -504,10 +499,31 @@ reference.
|
|||||||
Audio Format Settings
|
Audio Format Settings
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
|
.. _audio_output_format:
|
||||||
|
|
||||||
Global Audio Format
|
Global Audio Format
|
||||||
~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
The setting audio_output_format forces :program:`MPD` to use one audio format for all outputs. Doing that is usually not a good idea. The values are the same as in format in the audio_output section.
|
The setting ``audio_output_format`` forces :program:`MPD` to use one
|
||||||
|
audio format for all outputs. Doing that is usually not a good idea.
|
||||||
|
|
||||||
|
The value is specified as ``samplerate:bits:channels``.
|
||||||
|
|
||||||
|
Any of the three attributes may be an asterisk to specify that this
|
||||||
|
attribute should not be enforced, example: ``48000:16:*``.
|
||||||
|
``*:*:*`` is equal to not having a format specification.
|
||||||
|
|
||||||
|
The following values are valid for bits: ``8`` (signed 8 bit integer
|
||||||
|
samples), ``16``, ``24`` (signed 24 bit integer samples padded to 32
|
||||||
|
bit), ``32`` (signed 32 bit integer samples), ``f`` (32 bit floating
|
||||||
|
point, -1.0 to 1.0), ``dsd`` means DSD (Direct Stream Digital). For
|
||||||
|
DSD, there are special cases such as ``dsd64``, which allows you to
|
||||||
|
omit the sample rate (e.g. ``dsd512:2`` for stereo DSD512,
|
||||||
|
i.e. 22.5792 MHz).
|
||||||
|
|
||||||
|
The sample rate is special for DSD: :program:`MPD` counts the number
|
||||||
|
of bytes, not bits. Thus, a DSD "bit" rate of 22.5792 MHz (DSD512) is
|
||||||
|
2822400 from :program:`MPD`'s point of view (44100*512/8).
|
||||||
|
|
||||||
Resampler
|
Resampler
|
||||||
~~~~~~~~~
|
~~~~~~~~~
|
||||||
@@ -885,7 +901,7 @@ To verify if :program:`MPD` converts the audio format, enable verbose logging, a
|
|||||||
.. code-block:: none
|
.. code-block:: none
|
||||||
|
|
||||||
decoder: audio_format=44100:24:2, seekable=true
|
decoder: audio_format=44100:24:2, seekable=true
|
||||||
output: opened plugin=alsa name="An ALSA output"audio_format=44100:16:2
|
output: opened plugin=alsa name="An ALSA output" audio_format=44100:16:2
|
||||||
output: converting from 44100:24:2
|
output: converting from 44100:24:2
|
||||||
|
|
||||||
This example shows that a 24 bit file is being played, but the sound chip cannot play 24 bit. It falls back to 16 bit, discarding 8 bit.
|
This example shows that a 24 bit file is being played, but the sound chip cannot play 24 bit. It falls back to 16 bit, discarding 8 bit.
|
||||||
@@ -912,7 +928,7 @@ Check list for bit-perfect playback:
|
|||||||
device (:samp:`hw:0,0` or similar).
|
device (:samp:`hw:0,0` or similar).
|
||||||
* Don't use software volume (setting :code:`mixer_type`).
|
* Don't use software volume (setting :code:`mixer_type`).
|
||||||
* Don't force :program:`MPD` to use a specific audio format (settings
|
* Don't force :program:`MPD` to use a specific audio format (settings
|
||||||
:code:`format`, :code:`audio_output_format`).
|
:code:`format`, :ref:`audio_output_format <audio_output_format>`).
|
||||||
* Verify that you are really doing bit-perfect playback using :program:`MPD`'s verbose log and :file:`/proc/asound/card*/pcm*p/sub*/hw_params`. Some DACs can also indicate the audio format.
|
* Verify that you are really doing bit-perfect playback using :program:`MPD`'s verbose log and :file:`/proc/asound/card*/pcm*p/sub*/hw_params`. Some DACs can also indicate the audio format.
|
||||||
|
|
||||||
Direct Stream Digital (DSD)
|
Direct Stream Digital (DSD)
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
project(
|
project(
|
||||||
'mpd',
|
'mpd',
|
||||||
['c', 'cpp'],
|
['c', 'cpp'],
|
||||||
version: '0.21.8',
|
version: '0.21.9',
|
||||||
meson_version: '>= 0.49.0',
|
meson_version: '>= 0.49.0',
|
||||||
default_options: [
|
default_options: [
|
||||||
'c_std=c99',
|
'c_std=c99',
|
||||||
|
@@ -59,6 +59,9 @@ BufferedInputStream::~BufferedInputStream() noexcept
|
|||||||
void
|
void
|
||||||
BufferedInputStream::Check()
|
BufferedInputStream::Check()
|
||||||
{
|
{
|
||||||
|
if (read_error)
|
||||||
|
std::rethrow_exception(read_error);
|
||||||
|
|
||||||
if (input)
|
if (input)
|
||||||
input->Check();
|
input->Check();
|
||||||
}
|
}
|
||||||
@@ -101,7 +104,7 @@ BufferedInputStream::IsEOF() noexcept
|
|||||||
bool
|
bool
|
||||||
BufferedInputStream::IsAvailable() noexcept
|
BufferedInputStream::IsAvailable() noexcept
|
||||||
{
|
{
|
||||||
return IsEOF() || buffer.Read(offset).HasData();
|
return IsEOF() || buffer.Read(offset).HasData() || read_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t
|
size_t
|
||||||
@@ -164,6 +167,32 @@ BufferedInputStream::RunThread() noexcept
|
|||||||
idle = false;
|
idle = false;
|
||||||
seek = false;
|
seek = false;
|
||||||
client_cond.signal();
|
client_cond.signal();
|
||||||
|
} else if (!idle && !read_error &&
|
||||||
|
offset != input->GetOffset() &&
|
||||||
|
!IsAvailable()) {
|
||||||
|
/* a past Seek() call was a no-op because data
|
||||||
|
was already available at that position, but
|
||||||
|
now we've reached a new position where
|
||||||
|
there is no more data in the buffer, and
|
||||||
|
our input is reading somewhere else (maybe
|
||||||
|
stuck at the end of the file); to find a
|
||||||
|
way out, we now seek our input to our
|
||||||
|
reading position to be able to fill our
|
||||||
|
buffer */
|
||||||
|
|
||||||
|
try {
|
||||||
|
input->Seek(offset);
|
||||||
|
} catch (...) {
|
||||||
|
/* this is really a seek error, but we
|
||||||
|
register it as a read_error,
|
||||||
|
because seek_error is only checked
|
||||||
|
by Seek(), and at our frontend (our
|
||||||
|
own InputStream interface) is in
|
||||||
|
"read" mode */
|
||||||
|
read_error = std::current_exception();
|
||||||
|
client_cond.signal();
|
||||||
|
InvokeOnAvailable();
|
||||||
|
}
|
||||||
} else if (!idle && !read_error &&
|
} else if (!idle && !read_error &&
|
||||||
input->IsAvailable() && !input->IsEOF()) {
|
input->IsAvailable() && !input->IsEOF()) {
|
||||||
const auto read_offset = input->GetOffset();
|
const auto read_offset = input->GetOffset();
|
||||||
|
@@ -996,7 +996,7 @@ Player::Run() noexcept
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dc.IsIdle() && queued && dc.pipe == pipe) {
|
if (dc.IsIdle() && queued) {
|
||||||
/* the decoder has finished the current song;
|
/* the decoder has finished the current song;
|
||||||
make it decode the next song */
|
make it decode the next song */
|
||||||
|
|
||||||
@@ -1058,6 +1058,16 @@ Player::Run() noexcept
|
|||||||
|
|
||||||
SongBorder();
|
SongBorder();
|
||||||
} else if (dc.IsIdle()) {
|
} else if (dc.IsIdle()) {
|
||||||
|
if (queued)
|
||||||
|
/* the decoder has just stopped,
|
||||||
|
between the two IsIdle() checks,
|
||||||
|
probably while UnlockCheckOutputs()
|
||||||
|
left the mutex unlocked; to restart
|
||||||
|
the decoder instead of stopping
|
||||||
|
playback completely, let's re-enter
|
||||||
|
this loop */
|
||||||
|
continue;
|
||||||
|
|
||||||
/* check the size of the pipe again, because
|
/* check the size of the pipe again, because
|
||||||
the decoder thread may have added something
|
the decoder thread may have added something
|
||||||
since we last checked */
|
since we last checked */
|
||||||
|
Reference in New Issue
Block a user