Compare commits
44 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
2ab6c40ff1 | ||
![]() |
68bb738af2 | ||
![]() |
6b968beede | ||
![]() |
f68dd1bffb | ||
![]() |
f92b71ca99 | ||
![]() |
2b79fe2d6a | ||
![]() |
44dd9af276 | ||
![]() |
d3013d4f8c | ||
![]() |
678524ad21 | ||
![]() |
32a64481f2 | ||
![]() |
1776015c6c | ||
![]() |
f1c71a26e3 | ||
![]() |
e78ab767d3 | ||
![]() |
f01eb2f95d | ||
![]() |
1450e45d97 | ||
![]() |
ec8cba369c | ||
![]() |
f4c248f406 | ||
![]() |
f3b2a58646 | ||
![]() |
c6f89c42b2 | ||
![]() |
5e93cfdd9e | ||
![]() |
d91d5a3ab5 | ||
![]() |
907c045f33 | ||
![]() |
90f189eb54 | ||
![]() |
4abd5b2112 | ||
![]() |
df9a665994 | ||
![]() |
7a098ca0ed | ||
![]() |
33716732a1 | ||
![]() |
97ae594375 | ||
![]() |
3f321ae9a0 | ||
![]() |
161d32a7e7 | ||
![]() |
d7137586a9 | ||
![]() |
cd0c06ba6e | ||
![]() |
899ab63d91 | ||
![]() |
1097820a5a | ||
![]() |
39114f91a7 | ||
![]() |
4f01387edf | ||
![]() |
de3e0585f1 | ||
![]() |
f85f25ba82 | ||
![]() |
10a2c179f9 | ||
![]() |
6eea56861b | ||
![]() |
21fd2064ae | ||
![]() |
dcbab8e37a | ||
![]() |
5677278251 | ||
![]() |
a83bee993d |
2
AUTHORS
2
AUTHORS
@@ -5,7 +5,7 @@ The following people have contributed code to MPD:
|
|||||||
|
|
||||||
Warren Dukes <warren.dukes@gmail.com>
|
Warren Dukes <warren.dukes@gmail.com>
|
||||||
Avuton Olrich <avuton@gmail.com>
|
Avuton Olrich <avuton@gmail.com>
|
||||||
Max Kellermann <max@duempel.org>
|
Max Kellermann <max.kellermann@gmail.com>
|
||||||
Laszlo Ashin <kodest@gmail.com>
|
Laszlo Ashin <kodest@gmail.com>
|
||||||
Viliam Mateicka <viliam.mateicka@gmail.com>
|
Viliam Mateicka <viliam.mateicka@gmail.com>
|
||||||
Eric Wollesen <encoded@xmtp.net>
|
Eric Wollesen <encoded@xmtp.net>
|
||||||
|
12
Makefile.am
12
Makefile.am
@@ -415,6 +415,7 @@ libutil_a_SOURCES = \
|
|||||||
src/util/CharUtil.hxx \
|
src/util/CharUtil.hxx \
|
||||||
src/util/NumberParser.hxx \
|
src/util/NumberParser.hxx \
|
||||||
src/util/MimeType.cxx src/util/MimeType.hxx \
|
src/util/MimeType.cxx src/util/MimeType.hxx \
|
||||||
|
src/util/StringBuffer.hxx \
|
||||||
src/util/StringPointer.hxx \
|
src/util/StringPointer.hxx \
|
||||||
src/util/StringView.cxx src/util/StringView.hxx \
|
src/util/StringView.cxx src/util/StringView.hxx \
|
||||||
src/util/AllocatedString.cxx src/util/AllocatedString.hxx \
|
src/util/AllocatedString.cxx src/util/AllocatedString.hxx \
|
||||||
@@ -539,6 +540,10 @@ ICU_LDADD = libicu.a $(ICU_LIBS)
|
|||||||
# PCM library
|
# PCM library
|
||||||
|
|
||||||
libpcm_a_SOURCES = \
|
libpcm_a_SOURCES = \
|
||||||
|
src/CheckAudioFormat.cxx src/CheckAudioFormat.hxx \
|
||||||
|
src/AudioFormat.cxx src/AudioFormat.hxx \
|
||||||
|
src/AudioParser.cxx src/AudioParser.hxx \
|
||||||
|
src/pcm/SampleFormat.cxx src/pcm/SampleFormat.hxx \
|
||||||
src/pcm/Traits.hxx \
|
src/pcm/Traits.hxx \
|
||||||
src/pcm/Interleave.cxx src/pcm/Interleave.hxx \
|
src/pcm/Interleave.cxx src/pcm/Interleave.hxx \
|
||||||
src/pcm/PcmBuffer.cxx src/pcm/PcmBuffer.hxx \
|
src/pcm/PcmBuffer.cxx src/pcm/PcmBuffer.hxx \
|
||||||
@@ -615,7 +620,8 @@ libxiph_a_SOURCES += \
|
|||||||
src/lib/xiph/OggStreamState.hxx
|
src/lib/xiph/OggStreamState.hxx
|
||||||
endif
|
endif
|
||||||
|
|
||||||
XIPH_LIBS = libxiph.a
|
XIPH_LIBS = libxiph.a \
|
||||||
|
$(OGG_LIBS)
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
@@ -871,9 +877,6 @@ ARCHIVE_LIBS =
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
libbasic_a_SOURCES = \
|
libbasic_a_SOURCES = \
|
||||||
src/CheckAudioFormat.cxx src/CheckAudioFormat.hxx \
|
|
||||||
src/AudioFormat.cxx src/AudioFormat.hxx \
|
|
||||||
src/AudioParser.cxx src/AudioParser.hxx \
|
|
||||||
src/ReplayGainConfig.hxx \
|
src/ReplayGainConfig.hxx \
|
||||||
src/ReplayGainMode.cxx src/ReplayGainMode.hxx \
|
src/ReplayGainMode.cxx src/ReplayGainMode.hxx \
|
||||||
src/ReplayGainInfo.cxx src/ReplayGainInfo.hxx
|
src/ReplayGainInfo.cxx src/ReplayGainInfo.hxx
|
||||||
@@ -2227,6 +2230,7 @@ test_test_icy_parser_LDADD = \
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
test_test_pcm_SOURCES = \
|
test_test_pcm_SOURCES = \
|
||||||
|
test/TestAudioFormat.cxx test/TestAudioFormat.hxx \
|
||||||
test/test_pcm_util.hxx \
|
test/test_pcm_util.hxx \
|
||||||
test/test_pcm_dither.cxx \
|
test/test_pcm_dither.cxx \
|
||||||
test/test_pcm_pack.cxx \
|
test/test_pcm_pack.cxx \
|
||||||
|
12
NEWS
12
NEWS
@@ -1,3 +1,15 @@
|
|||||||
|
ver 0.20.3 (2017/01/25)
|
||||||
|
* protocol
|
||||||
|
- "playlistadd" creates new playlist if it does not exist, as documented
|
||||||
|
* database
|
||||||
|
- proxy: fix error "terminate called after throwing ..."
|
||||||
|
- proxy: make connect errors during startup non-fatal
|
||||||
|
* neighbor
|
||||||
|
- upnp: fix premature expiry
|
||||||
|
* replay gain: don't reset ReplayGain levels when unpausing playback
|
||||||
|
* silence surround channels when converting from stereo
|
||||||
|
* use shortcuts such as "dsd64" in log messages
|
||||||
|
|
||||||
ver 0.20.2 (2017/01/15)
|
ver 0.20.2 (2017/01/15)
|
||||||
* input
|
* input
|
||||||
- alsa: fix crash bug
|
- alsa: fix crash bug
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
AC_PREREQ(2.60)
|
AC_PREREQ(2.60)
|
||||||
|
|
||||||
AC_INIT(mpd, 0.20.2, musicpd-dev-team@lists.sourceforge.net)
|
AC_INIT(mpd, 0.20.3, musicpd-dev-team@lists.sourceforge.net)
|
||||||
|
|
||||||
VERSION_MAJOR=0
|
VERSION_MAJOR=0
|
||||||
VERSION_MINOR=20
|
VERSION_MINOR=20
|
||||||
VERSION_REVISION=1
|
VERSION_REVISION=3
|
||||||
VERSION_EXTRA=0
|
VERSION_EXTRA=0
|
||||||
|
|
||||||
AC_CONFIG_SRCDIR([src/Main.cxx])
|
AC_CONFIG_SRCDIR([src/Main.cxx])
|
||||||
|
@@ -50,6 +50,6 @@ If you find a bug, please report it at
|
|||||||
.br
|
.br
|
||||||
<\fBhttp://bugs.musicpd.org/bug_report_page.php\fP>.
|
<\fBhttp://bugs.musicpd.org/bug_report_page.php\fP>.
|
||||||
.SH AUTHORS
|
.SH AUTHORS
|
||||||
Max Kellermann <max@duempel.org>
|
Max Kellermann <max.kellermann@gmail.com>
|
||||||
|
|
||||||
Special thanks to all the people that provided feedback and patches.
|
Special thanks to all the people that provided feedback and patches.
|
||||||
|
@@ -573,7 +573,12 @@
|
|||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
<varname>audio</varname>:
|
<varname>audio</varname>:
|
||||||
<returnvalue>sampleRate:bits:channels</returnvalue>
|
<returnvalue>
|
||||||
|
The format emitted by the decoder plugin during
|
||||||
|
playback, format:
|
||||||
|
"<replaceable>samplerate:bits:channels</replaceable>".
|
||||||
|
Check the user manual for a detailed explanation.
|
||||||
|
</returnvalue>
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
<listitem>
|
<listitem>
|
||||||
|
103
doc/user.xml
103
doc/user.xml
@@ -576,10 +576,11 @@ systemctl start mpd.socket</programlisting>
|
|||||||
</entry>
|
</entry>
|
||||||
<entry>
|
<entry>
|
||||||
<para>
|
<para>
|
||||||
Always open the audio output with the specified audio
|
Always open the audio output with the specified
|
||||||
format (samplerate:bits:channels), regardless of the
|
audio format
|
||||||
format of the input file. This is optional for most
|
(<replaceable>samplerate:bits:channels</replaceable>),
|
||||||
plugins.
|
regardless of the format of the input file. This is
|
||||||
|
optional for most plugins.
|
||||||
</para>
|
</para>
|
||||||
<para>
|
<para>
|
||||||
Any of the three attributes may be an asterisk to
|
Any of the three attributes may be an asterisk to
|
||||||
@@ -596,7 +597,20 @@ systemctl start mpd.socket</programlisting>
|
|||||||
24 bit integer samples padded to 32 bit),
|
24 bit integer samples padded to 32 bit),
|
||||||
<varname>32</varname> (signed 32 bit integer
|
<varname>32</varname> (signed 32 bit integer
|
||||||
samples), <varname>f</varname> (32 bit floating
|
samples), <varname>f</varname> (32 bit floating
|
||||||
point, -1.0 to 1.0).
|
point, -1.0 to 1.0), "<varname>dsd</varname>" means
|
||||||
|
DSD (Direct Stream Digital). For DSD, there are
|
||||||
|
special cases such as "<varname>dsd64</varname>",
|
||||||
|
which allows you to omit the sample rate
|
||||||
|
(e.g. <parameter>dsd512:2</parameter> for stereo
|
||||||
|
DSD512, i.e. 22.5792 MHz).
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
The sample rate is special for DSD:
|
||||||
|
<application>MPD</application> counts the number of
|
||||||
|
bytes, not bits. Thus, a DSD "bit" rate of 22.5792
|
||||||
|
MHz (DSD512) is 2822400 from
|
||||||
|
<application>MPD</application>'s point of view
|
||||||
|
(44100*512/8).
|
||||||
</para>
|
</para>
|
||||||
</entry>
|
</entry>
|
||||||
</row>
|
</row>
|
||||||
@@ -742,9 +756,11 @@ systemctl start mpd.socket</programlisting>
|
|||||||
<title>Configuring playlist plugins</title>
|
<title>Configuring playlist plugins</title>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
Playlist plugins are used to load remote playlists. This is
|
Playlist plugins are used to load remote playlists (protocol
|
||||||
not related to <application>MPD</application>'s playlist
|
commands <command>load</command>,
|
||||||
directory.
|
<command>listplaylist</command> and
|
||||||
|
<command>listplaylistinfo</command>). This is not related to
|
||||||
|
<application>MPD</application>'s playlist directory.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
@@ -1800,6 +1816,13 @@ run</programlisting>
|
|||||||
database.
|
database.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Note that unless overridden by the below settings (e.g. by
|
||||||
|
setting them to a blank value), general curl configuration
|
||||||
|
from environment variables such as http_proxy or specified
|
||||||
|
in ~/.curlrc will be in effect.
|
||||||
|
</para>
|
||||||
|
|
||||||
<informaltable>
|
<informaltable>
|
||||||
<tgroup cols="2">
|
<tgroup cols="2">
|
||||||
<thead>
|
<thead>
|
||||||
@@ -4193,6 +4216,22 @@ run</programlisting>
|
|||||||
<section id="playlist_plugins">
|
<section id="playlist_plugins">
|
||||||
<title>Playlist plugins</title>
|
<title>Playlist plugins</title>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<title><varname>asx</varname></title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Reads <filename>.asx</filename> playlist files.
|
||||||
|
</para>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<title><varname>cue</varname></title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Reads <filename>.cue</filename> files.
|
||||||
|
</para>
|
||||||
|
</section>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<title><varname>embcue</varname></title>
|
<title><varname>embcue</varname></title>
|
||||||
|
|
||||||
@@ -4217,6 +4256,15 @@ run</programlisting>
|
|||||||
</para>
|
</para>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<title><varname>flac</varname></title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Reads the <varname>cuesheet</varname> metablock from a FLAC
|
||||||
|
file.
|
||||||
|
</para>
|
||||||
|
</section>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<title><varname>pls</varname></title>
|
<title><varname>pls</varname></title>
|
||||||
|
|
||||||
@@ -4225,6 +4273,45 @@ run</programlisting>
|
|||||||
</para>
|
</para>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<title><varname>rss</varname></title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Reads music links from <filename>.rss</filename> files.
|
||||||
|
</para>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<title><varname>soundcloud</varname></title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Download playlist from SoundCloud. It accepts URIs starting
|
||||||
|
with <filename>soundcloud://</filename>.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<informaltable>
|
||||||
|
<tgroup cols="2">
|
||||||
|
<thead>
|
||||||
|
<row>
|
||||||
|
<entry>Setting</entry>
|
||||||
|
<entry>Description</entry>
|
||||||
|
</row>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<row>
|
||||||
|
<entry>
|
||||||
|
<varname>apikey</varname>
|
||||||
|
<parameter>KEY</parameter>
|
||||||
|
</entry>
|
||||||
|
<entry>
|
||||||
|
An API key to access the SoundCloud servers.
|
||||||
|
</entry>
|
||||||
|
</row>
|
||||||
|
</tbody>
|
||||||
|
</tgroup>
|
||||||
|
</informaltable>
|
||||||
|
</section>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<title><varname>xspf</varname></title>
|
<title><varname>xspf</varname></title>
|
||||||
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
# Check if "struct ucred" is available.
|
# Check if "struct ucred" is available.
|
||||||
#
|
#
|
||||||
# Author: Max Kellermann <max@duempel.org>
|
# Author: Max Kellermann <max.kellermann@gmail.com>
|
||||||
|
|
||||||
AC_DEFUN([STRUCT_UCRED],[
|
AC_DEFUN([STRUCT_UCRED],[
|
||||||
AC_MSG_CHECKING([for struct ucred])
|
AC_MSG_CHECKING([for struct ucred])
|
||||||
|
@@ -18,6 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "AudioFormat.hxx"
|
#include "AudioFormat.hxx"
|
||||||
|
#include "util/StringBuffer.hxx"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -40,46 +41,24 @@ AudioFormat::ApplyMask(AudioFormat mask)
|
|||||||
assert(IsValid());
|
assert(IsValid());
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *
|
StringBuffer<24>
|
||||||
sample_format_to_string(SampleFormat format)
|
ToString(const AudioFormat af)
|
||||||
{
|
{
|
||||||
switch (format) {
|
StringBuffer<24> buffer;
|
||||||
case SampleFormat::UNDEFINED:
|
|
||||||
return "?";
|
|
||||||
|
|
||||||
case SampleFormat::S8:
|
if (af.format == SampleFormat::DSD && af.sample_rate > 0 &&
|
||||||
return "8";
|
af.sample_rate % 44100 == 0) {
|
||||||
|
/* use shortcuts such as "dsd64" which implies the
|
||||||
case SampleFormat::S16:
|
sample rate */
|
||||||
return "16";
|
snprintf(buffer.data(), buffer.capacity(), "dsd%u:%u",
|
||||||
|
af.sample_rate * 8 / 44100,
|
||||||
case SampleFormat::S24_P32:
|
af.channels);
|
||||||
return "24";
|
return buffer;
|
||||||
|
|
||||||
case SampleFormat::S32:
|
|
||||||
return "32";
|
|
||||||
|
|
||||||
case SampleFormat::FLOAT:
|
|
||||||
return "f";
|
|
||||||
|
|
||||||
case SampleFormat::DSD:
|
|
||||||
return "dsd";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* unreachable */
|
snprintf(buffer.data(), buffer.capacity(), "%u:%s:%u",
|
||||||
assert(false);
|
|
||||||
gcc_unreachable();
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *
|
|
||||||
audio_format_to_string(const AudioFormat af,
|
|
||||||
struct audio_format_string *s)
|
|
||||||
{
|
|
||||||
assert(s != nullptr);
|
|
||||||
|
|
||||||
snprintf(s->buffer, sizeof(s->buffer), "%u:%s:%u",
|
|
||||||
af.sample_rate, sample_format_to_string(af.format),
|
af.sample_rate, sample_format_to_string(af.format),
|
||||||
af.channels);
|
af.channels);
|
||||||
|
|
||||||
return s->buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
@@ -20,47 +20,14 @@
|
|||||||
#ifndef MPD_AUDIO_FORMAT_HXX
|
#ifndef MPD_AUDIO_FORMAT_HXX
|
||||||
#define MPD_AUDIO_FORMAT_HXX
|
#define MPD_AUDIO_FORMAT_HXX
|
||||||
|
|
||||||
|
#include "pcm/SampleFormat.hxx"
|
||||||
#include "Compiler.h"
|
#include "Compiler.h"
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
#if defined(WIN32) && GCC_CHECK_VERSION(4,6)
|
template<size_t CAPACITY> class StringBuffer;
|
||||||
/* on WIN32, "FLOAT" is already defined, and this triggers -Wshadow */
|
|
||||||
#pragma GCC diagnostic push
|
|
||||||
#pragma GCC diagnostic ignored "-Wshadow"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
enum class SampleFormat : uint8_t {
|
|
||||||
UNDEFINED = 0,
|
|
||||||
|
|
||||||
S8,
|
|
||||||
S16,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Signed 24 bit integer samples, packed in 32 bit integers
|
|
||||||
* (the most significant byte is filled with the sign bit).
|
|
||||||
*/
|
|
||||||
S24_P32,
|
|
||||||
|
|
||||||
S32,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 32 bit floating point samples in the host's format. The
|
|
||||||
* range is -1.0f to +1.0f.
|
|
||||||
*/
|
|
||||||
FLOAT,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Direct Stream Digital. 1-bit samples; each frame has one
|
|
||||||
* byte (8 samples) per channel.
|
|
||||||
*/
|
|
||||||
DSD,
|
|
||||||
};
|
|
||||||
|
|
||||||
#if defined(WIN32) && GCC_CHECK_VERSION(4,6)
|
|
||||||
#pragma GCC diagnostic pop
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static constexpr unsigned MAX_CHANNELS = 8;
|
static constexpr unsigned MAX_CHANNELS = 8;
|
||||||
|
|
||||||
@@ -183,13 +150,6 @@ struct AudioFormat {
|
|||||||
double GetTimeToSize() const;
|
double GetTimeToSize() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Buffer for audio_format_string().
|
|
||||||
*/
|
|
||||||
struct audio_format_string {
|
|
||||||
char buffer[24];
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether the sample rate is valid.
|
* Checks whether the sample rate is valid.
|
||||||
*
|
*
|
||||||
@@ -201,28 +161,6 @@ audio_valid_sample_rate(unsigned sample_rate)
|
|||||||
return sample_rate > 0 && sample_rate < (1 << 30);
|
return sample_rate > 0 && sample_rate < (1 << 30);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks whether the sample format is valid.
|
|
||||||
*/
|
|
||||||
static inline bool
|
|
||||||
audio_valid_sample_format(SampleFormat format)
|
|
||||||
{
|
|
||||||
switch (format) {
|
|
||||||
case SampleFormat::S8:
|
|
||||||
case SampleFormat::S16:
|
|
||||||
case SampleFormat::S24_P32:
|
|
||||||
case SampleFormat::S32:
|
|
||||||
case SampleFormat::FLOAT:
|
|
||||||
case SampleFormat::DSD:
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case SampleFormat::UNDEFINED:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether the number of channels is valid.
|
* Checks whether the number of channels is valid.
|
||||||
*/
|
*/
|
||||||
@@ -258,34 +196,6 @@ AudioFormat::IsMaskValid() const
|
|||||||
(channels == 0 || audio_valid_channel_count(channels));
|
(channels == 0 || audio_valid_channel_count(channels));
|
||||||
}
|
}
|
||||||
|
|
||||||
gcc_const
|
|
||||||
static inline unsigned
|
|
||||||
sample_format_size(SampleFormat format)
|
|
||||||
{
|
|
||||||
switch (format) {
|
|
||||||
case SampleFormat::S8:
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
case SampleFormat::S16:
|
|
||||||
return 2;
|
|
||||||
|
|
||||||
case SampleFormat::S24_P32:
|
|
||||||
case SampleFormat::S32:
|
|
||||||
case SampleFormat::FLOAT:
|
|
||||||
return 4;
|
|
||||||
|
|
||||||
case SampleFormat::DSD:
|
|
||||||
/* each frame has 8 samples per channel */
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
case SampleFormat::UNDEFINED:
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(false);
|
|
||||||
gcc_unreachable();
|
|
||||||
}
|
|
||||||
|
|
||||||
inline unsigned
|
inline unsigned
|
||||||
AudioFormat::GetSampleSize() const
|
AudioFormat::GetSampleSize() const
|
||||||
{
|
{
|
||||||
@@ -304,28 +214,15 @@ AudioFormat::GetTimeToSize() const
|
|||||||
return sample_rate * GetFrameSize();
|
return sample_rate * GetFrameSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders a #SampleFormat enum into a string, e.g. for printing it
|
|
||||||
* in a log file.
|
|
||||||
*
|
|
||||||
* @param format a #SampleFormat enum value
|
|
||||||
* @return the string
|
|
||||||
*/
|
|
||||||
gcc_pure gcc_malloc
|
|
||||||
const char *
|
|
||||||
sample_format_to_string(SampleFormat format);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders the #AudioFormat object into a string, e.g. for printing
|
* Renders the #AudioFormat object into a string, e.g. for printing
|
||||||
* it in a log file.
|
* it in a log file.
|
||||||
*
|
*
|
||||||
* @param af the #AudioFormat object
|
* @param af the #AudioFormat object
|
||||||
* @param s a buffer to print into
|
* @return the string buffer
|
||||||
* @return the string, or nullptr if the #AudioFormat object is invalid
|
|
||||||
*/
|
*/
|
||||||
gcc_pure gcc_malloc
|
gcc_const
|
||||||
const char *
|
StringBuffer<24>
|
||||||
audio_format_to_string(AudioFormat af,
|
ToString(AudioFormat af);
|
||||||
struct audio_format_string *s);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@@ -137,6 +137,26 @@ ParseAudioFormat(const char *src, bool mask)
|
|||||||
AudioFormat dest;
|
AudioFormat dest;
|
||||||
dest.Clear();
|
dest.Clear();
|
||||||
|
|
||||||
|
if (strncmp(src, "dsd", 3) == 0) {
|
||||||
|
/* allow format specifications such as "dsd64" which
|
||||||
|
implies the sample rate */
|
||||||
|
|
||||||
|
char *endptr;
|
||||||
|
auto dsd = strtoul(src + 3, &endptr, 10);
|
||||||
|
if (endptr > src + 3 && *endptr == ':' &&
|
||||||
|
dsd >= 32 && dsd <= 4096 && dsd % 2 == 0) {
|
||||||
|
dest.sample_rate = dsd * 44100 / 8;
|
||||||
|
dest.format = SampleFormat::DSD;
|
||||||
|
|
||||||
|
src = endptr + 1;
|
||||||
|
dest.channels = ParseChannelCount(src, mask, &src);
|
||||||
|
if (*src != 0)
|
||||||
|
throw FormatRuntimeError("Extra data after channel count: %s", src);
|
||||||
|
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* parse sample rate */
|
/* parse sample rate */
|
||||||
|
|
||||||
dest.sample_rate = ParseSampleRate(src, mask, &src);
|
dest.sample_rate = ParseSampleRate(src, mask, &src);
|
||||||
|
@@ -107,7 +107,7 @@ static void version(void)
|
|||||||
"\n"
|
"\n"
|
||||||
"\n"
|
"\n"
|
||||||
"Copyright (C) 2003-2007 Warren Dukes <warren.dukes@gmail.com>\n"
|
"Copyright (C) 2003-2007 Warren Dukes <warren.dukes@gmail.com>\n"
|
||||||
"Copyright (C) 2008-2015 Max Kellermann <max@duempel.org>\n"
|
"Copyright 2008-2017 Max Kellermann <max.kellermann@gmail.com>\n"
|
||||||
"This is free software; see the source for copying conditions. There is NO\n"
|
"This is free software; see the source for copying conditions. There is NO\n"
|
||||||
"warranty; not even MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
|
"warranty; not even MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
|
||||||
|
|
||||||
|
@@ -63,18 +63,18 @@ class DetachedSong {
|
|||||||
|
|
||||||
Tag tag;
|
Tag tag;
|
||||||
|
|
||||||
time_t mtime;
|
time_t mtime = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start of this sub-song within the file.
|
* Start of this sub-song within the file.
|
||||||
*/
|
*/
|
||||||
SongTime start_time;
|
SongTime start_time = SongTime::zero();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* End of this sub-song within the file.
|
* End of this sub-song within the file.
|
||||||
* Unused if zero.
|
* Unused if zero.
|
||||||
*/
|
*/
|
||||||
SongTime end_time;
|
SongTime end_time = SongTime::zero();
|
||||||
|
|
||||||
explicit DetachedSong(const LightSong &other);
|
explicit DetachedSong(const LightSong &other);
|
||||||
|
|
||||||
@@ -82,26 +82,18 @@ public:
|
|||||||
explicit DetachedSong(const DetachedSong &) = default;
|
explicit DetachedSong(const DetachedSong &) = default;
|
||||||
|
|
||||||
explicit DetachedSong(const char *_uri)
|
explicit DetachedSong(const char *_uri)
|
||||||
:uri(_uri),
|
:uri(_uri) {}
|
||||||
mtime(0),
|
|
||||||
start_time(SongTime::zero()), end_time(SongTime::zero()) {}
|
|
||||||
|
|
||||||
explicit DetachedSong(const std::string &_uri)
|
explicit DetachedSong(const std::string &_uri)
|
||||||
:uri(_uri),
|
:uri(_uri) {}
|
||||||
mtime(0),
|
|
||||||
start_time(SongTime::zero()), end_time(SongTime::zero()) {}
|
|
||||||
|
|
||||||
explicit DetachedSong(std::string &&_uri)
|
explicit DetachedSong(std::string &&_uri)
|
||||||
:uri(std::move(_uri)),
|
:uri(std::move(_uri)) {}
|
||||||
mtime(0),
|
|
||||||
start_time(SongTime::zero()), end_time(SongTime::zero()) {}
|
|
||||||
|
|
||||||
template<typename U>
|
template<typename U>
|
||||||
DetachedSong(U &&_uri, Tag &&_tag)
|
DetachedSong(U &&_uri, Tag &&_tag)
|
||||||
:uri(std::forward<U>(_uri)),
|
:uri(std::forward<U>(_uri)),
|
||||||
tag(std::move(_tag)),
|
tag(std::move(_tag)) {}
|
||||||
mtime(0),
|
|
||||||
start_time(SongTime::zero()), end_time(SongTime::zero()) {}
|
|
||||||
|
|
||||||
DetachedSong(DetachedSong &&) = default;
|
DetachedSong(DetachedSong &&) = default;
|
||||||
|
|
||||||
|
@@ -203,7 +203,11 @@ glue_db_init_and_load(void)
|
|||||||
"because the database does not need it");
|
"because the database does not need it");
|
||||||
}
|
}
|
||||||
|
|
||||||
instance->database->Open();
|
try {
|
||||||
|
instance->database->Open();
|
||||||
|
} catch (...) {
|
||||||
|
std::throw_with_nested(std::runtime_error("Failed to open database plugin"));
|
||||||
|
}
|
||||||
|
|
||||||
if (!instance->database->IsPlugin(simple_db_plugin))
|
if (!instance->database->IsPlugin(simple_db_plugin))
|
||||||
return true;
|
return true;
|
||||||
|
@@ -335,7 +335,7 @@ try {
|
|||||||
const auto path_fs = spl_map_to_fs(utf8path);
|
const auto path_fs = spl_map_to_fs(utf8path);
|
||||||
assert(!path_fs.IsNull());
|
assert(!path_fs.IsNull());
|
||||||
|
|
||||||
FileOutputStream fos(path_fs, FileOutputStream::Mode::APPEND_EXISTING);
|
FileOutputStream fos(path_fs, FileOutputStream::Mode::APPEND_OR_CREATE);
|
||||||
|
|
||||||
if (fos.Tell() / (MPD_PATH_MAX + 1) >= playlist_max_length)
|
if (fos.Tell() / (MPD_PATH_MAX + 1) >= playlist_max_length)
|
||||||
throw PlaylistError(PlaylistResult::TOO_LARGE,
|
throw PlaylistError(PlaylistResult::TOO_LARGE,
|
||||||
|
@@ -30,6 +30,7 @@
|
|||||||
#include "Instance.hxx"
|
#include "Instance.hxx"
|
||||||
#include "Idle.hxx"
|
#include "Idle.hxx"
|
||||||
#include "AudioFormat.hxx"
|
#include "AudioFormat.hxx"
|
||||||
|
#include "util/StringBuffer.hxx"
|
||||||
#include "util/ScopeExit.hxx"
|
#include "util/ScopeExit.hxx"
|
||||||
#include "util/Exception.hxx"
|
#include "util/Exception.hxx"
|
||||||
|
|
||||||
@@ -171,13 +172,9 @@ handle_status(Client &client, gcc_unused Request args, Response &r)
|
|||||||
r.Format("duration: %1.3f\n",
|
r.Format("duration: %1.3f\n",
|
||||||
player_status.total_time.ToDoubleS());
|
player_status.total_time.ToDoubleS());
|
||||||
|
|
||||||
if (player_status.audio_format.IsDefined()) {
|
if (player_status.audio_format.IsDefined())
|
||||||
struct audio_format_string af_string;
|
|
||||||
|
|
||||||
r.Format(COMMAND_STATUS_AUDIO ": %s\n",
|
r.Format(COMMAND_STATUS_AUDIO ": %s\n",
|
||||||
audio_format_to_string(player_status.audio_format,
|
ToString(player_status.audio_format).c_str());
|
||||||
&af_string));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ENABLE_DATABASE
|
#ifdef ENABLE_DATABASE
|
||||||
|
@@ -37,5 +37,10 @@ DatabaseGlobalInit(EventLoop &loop, DatabaseListener &listener,
|
|||||||
throw FormatRuntimeError("No such database plugin: %s",
|
throw FormatRuntimeError("No such database plugin: %s",
|
||||||
plugin_name);
|
plugin_name);
|
||||||
|
|
||||||
return plugin->create(loop, listener, block);
|
try {
|
||||||
|
return plugin->create(loop, listener, block);
|
||||||
|
} catch (...) {
|
||||||
|
std::throw_with_nested(FormatRuntimeError("Failed to initialize database plugin '%s'",
|
||||||
|
plugin_name));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -34,6 +34,7 @@
|
|||||||
#include "tag/TagBuilder.hxx"
|
#include "tag/TagBuilder.hxx"
|
||||||
#include "tag/Tag.hxx"
|
#include "tag/Tag.hxx"
|
||||||
#include "util/ScopeExit.hxx"
|
#include "util/ScopeExit.hxx"
|
||||||
|
#include "util/RuntimeError.hxx"
|
||||||
#include "protocol/Ack.hxx"
|
#include "protocol/Ack.hxx"
|
||||||
#include "event/SocketMonitor.hxx"
|
#include "event/SocketMonitor.hxx"
|
||||||
#include "event/IdleMonitor.hxx"
|
#include "event/IdleMonitor.hxx"
|
||||||
@@ -46,7 +47,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <list>
|
#include <list>
|
||||||
|
|
||||||
class LibmpdclientError final : std::runtime_error {
|
class LibmpdclientError final : public std::runtime_error {
|
||||||
enum mpd_error code;
|
enum mpd_error code;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -108,8 +109,8 @@ public:
|
|||||||
static Database *Create(EventLoop &loop, DatabaseListener &listener,
|
static Database *Create(EventLoop &loop, DatabaseListener &listener,
|
||||||
const ConfigBlock &block);
|
const ConfigBlock &block);
|
||||||
|
|
||||||
virtual void Open() override;
|
void Open() override;
|
||||||
virtual void Close() override;
|
void Close() override;
|
||||||
const LightSong *GetSong(const char *uri_utf8) const override;
|
const LightSong *GetSong(const char *uri_utf8) const override;
|
||||||
void ReturnSong(const LightSong *song) const override;
|
void ReturnSong(const LightSong *song) const override;
|
||||||
|
|
||||||
@@ -126,7 +127,7 @@ public:
|
|||||||
|
|
||||||
unsigned Update(const char *uri_utf8, bool discard) override;
|
unsigned Update(const char *uri_utf8, bool discard) override;
|
||||||
|
|
||||||
virtual time_t GetUpdateStamp() const override {
|
time_t GetUpdateStamp() const override {
|
||||||
return update_stamp;
|
return update_stamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,10 +139,10 @@ private:
|
|||||||
void Disconnect();
|
void Disconnect();
|
||||||
|
|
||||||
/* virtual methods from SocketMonitor */
|
/* virtual methods from SocketMonitor */
|
||||||
virtual bool OnSocketReady(unsigned flags) override;
|
bool OnSocketReady(unsigned flags) override;
|
||||||
|
|
||||||
/* virtual methods from IdleMonitor */
|
/* virtual methods from IdleMonitor */
|
||||||
virtual void OnIdle() override;
|
void OnIdle() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr struct {
|
static constexpr struct {
|
||||||
@@ -345,9 +346,15 @@ ProxyDatabase::Create(EventLoop &loop, DatabaseListener &listener,
|
|||||||
void
|
void
|
||||||
ProxyDatabase::Open()
|
ProxyDatabase::Open()
|
||||||
{
|
{
|
||||||
Connect();
|
|
||||||
|
|
||||||
update_stamp = 0;
|
update_stamp = 0;
|
||||||
|
|
||||||
|
try {
|
||||||
|
Connect();
|
||||||
|
} catch (const std::runtime_error &error) {
|
||||||
|
/* this error is non-fatal, because this plugin will
|
||||||
|
attempt to reconnect again automatically */
|
||||||
|
LogError(error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -371,7 +378,10 @@ ProxyDatabase::Connect()
|
|||||||
mpd_connection_free(connection);
|
mpd_connection_free(connection);
|
||||||
connection = nullptr;
|
connection = nullptr;
|
||||||
|
|
||||||
throw;
|
std::throw_with_nested(host.empty()
|
||||||
|
? std::runtime_error("Failed to connect to remote MPD")
|
||||||
|
: FormatRuntimeError("Failed to connect to remote MPD '%s'",
|
||||||
|
host.c_str()));
|
||||||
}
|
}
|
||||||
|
|
||||||
#if LIBMPDCLIENT_CHECK_VERSION(2, 10, 0)
|
#if LIBMPDCLIENT_CHECK_VERSION(2, 10, 0)
|
||||||
|
@@ -108,8 +108,8 @@ public:
|
|||||||
bool Unmount(const char *uri);
|
bool Unmount(const char *uri);
|
||||||
|
|
||||||
/* virtual methods from class Database */
|
/* virtual methods from class Database */
|
||||||
virtual void Open() override;
|
void Open() override;
|
||||||
virtual void Close() override;
|
void Close() override;
|
||||||
|
|
||||||
const LightSong *GetSong(const char *uri_utf8) const override;
|
const LightSong *GetSong(const char *uri_utf8) const override;
|
||||||
void ReturnSong(const LightSong *song) const override;
|
void ReturnSong(const LightSong *song) const override;
|
||||||
@@ -125,7 +125,7 @@ public:
|
|||||||
|
|
||||||
DatabaseStats GetStats(const DatabaseSelection &selection) const override;
|
DatabaseStats GetStats(const DatabaseSelection &selection) const override;
|
||||||
|
|
||||||
virtual time_t GetUpdateStamp() const override {
|
time_t GetUpdateStamp() const override {
|
||||||
return mtime;
|
return mtime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -180,7 +180,7 @@ UpnpDatabase::ReturnSong(const LightSong *_song) const
|
|||||||
const LightSong *
|
const LightSong *
|
||||||
UpnpDatabase::GetSong(const char *uri) const
|
UpnpDatabase::GetSong(const char *uri) const
|
||||||
{
|
{
|
||||||
auto vpath = stringToTokens(uri, "/", true);
|
auto vpath = stringToTokens(uri, '/');
|
||||||
if (vpath.size() < 2)
|
if (vpath.size() < 2)
|
||||||
throw DatabaseError(DatabaseErrorCode::NOT_FOUND,
|
throw DatabaseError(DatabaseErrorCode::NOT_FOUND,
|
||||||
"No such song");
|
"No such song");
|
||||||
@@ -577,7 +577,7 @@ UpnpDatabase::Visit(const DatabaseSelection &selection,
|
|||||||
VisitSong visit_song,
|
VisitSong visit_song,
|
||||||
VisitPlaylist visit_playlist) const
|
VisitPlaylist visit_playlist) const
|
||||||
{
|
{
|
||||||
auto vpath = stringToTokens(selection.uri, "/", true);
|
auto vpath = stringToTokens(selection.uri, '/');
|
||||||
if (vpath.empty()) {
|
if (vpath.empty()) {
|
||||||
for (const auto &server : discovery->GetDirectories()) {
|
for (const auto &server : discovery->GetDirectories()) {
|
||||||
if (visit_directory) {
|
if (visit_directory) {
|
||||||
|
@@ -32,6 +32,7 @@
|
|||||||
#include "Log.hxx"
|
#include "Log.hxx"
|
||||||
#include "input/InputStream.hxx"
|
#include "input/InputStream.hxx"
|
||||||
#include "util/ConstBuffer.hxx"
|
#include "util/ConstBuffer.hxx"
|
||||||
|
#include "util/StringBuffer.hxx"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -246,15 +247,13 @@ void
|
|||||||
DecoderBridge::Ready(const AudioFormat audio_format,
|
DecoderBridge::Ready(const AudioFormat audio_format,
|
||||||
bool seekable, SignedSongTime duration)
|
bool seekable, SignedSongTime duration)
|
||||||
{
|
{
|
||||||
struct audio_format_string af_string;
|
|
||||||
|
|
||||||
assert(convert == nullptr);
|
assert(convert == nullptr);
|
||||||
assert(stream_tag == nullptr);
|
assert(stream_tag == nullptr);
|
||||||
assert(decoder_tag == nullptr);
|
assert(decoder_tag == nullptr);
|
||||||
assert(!seeking);
|
assert(!seeking);
|
||||||
|
|
||||||
FormatDebug(decoder_domain, "audio_format=%s, seekable=%s",
|
FormatDebug(decoder_domain, "audio_format=%s, seekable=%s",
|
||||||
audio_format_to_string(audio_format, &af_string),
|
ToString(audio_format).c_str(),
|
||||||
seekable ? "true" : "false");
|
seekable ? "true" : "false");
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -264,8 +263,7 @@ DecoderBridge::Ready(const AudioFormat audio_format,
|
|||||||
|
|
||||||
if (dc.in_audio_format != dc.out_audio_format) {
|
if (dc.in_audio_format != dc.out_audio_format) {
|
||||||
FormatDebug(decoder_domain, "converting to %s",
|
FormatDebug(decoder_domain, "converting to %s",
|
||||||
audio_format_to_string(dc.out_audio_format,
|
ToString(dc.out_audio_format).c_str());
|
||||||
&af_string));
|
|
||||||
|
|
||||||
convert = new PcmConvert();
|
convert = new PcmConvert();
|
||||||
|
|
||||||
|
@@ -24,6 +24,7 @@
|
|||||||
#include "filter/FilterRegistry.hxx"
|
#include "filter/FilterRegistry.hxx"
|
||||||
#include "AudioFormat.hxx"
|
#include "AudioFormat.hxx"
|
||||||
#include "util/ConstBuffer.hxx"
|
#include "util/ConstBuffer.hxx"
|
||||||
|
#include "util/StringBuffer.hxx"
|
||||||
#include "util/RuntimeError.hxx"
|
#include "util/RuntimeError.hxx"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@@ -108,10 +109,9 @@ PreparedChainFilter::Child::Open(const AudioFormat &prev_audio_format)
|
|||||||
if (conv_audio_format != prev_audio_format) {
|
if (conv_audio_format != prev_audio_format) {
|
||||||
delete new_filter;
|
delete new_filter;
|
||||||
|
|
||||||
struct audio_format_string s;
|
|
||||||
throw FormatRuntimeError("Audio format not supported by filter '%s': %s",
|
throw FormatRuntimeError("Audio format not supported by filter '%s': %s",
|
||||||
name,
|
name,
|
||||||
audio_format_to_string(prev_audio_format, &s));
|
ToString(prev_audio_format).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
return new_filter;
|
return new_filter;
|
||||||
|
@@ -33,7 +33,6 @@
|
|||||||
#include "util/RuntimeError.hxx"
|
#include "util/RuntimeError.hxx"
|
||||||
#include "util/StringCompare.hxx"
|
#include "util/StringCompare.hxx"
|
||||||
#include "util/ReusableArray.hxx"
|
#include "util/ReusableArray.hxx"
|
||||||
#include "util/ScopeExit.hxx"
|
|
||||||
|
|
||||||
#include "Log.hxx"
|
#include "Log.hxx"
|
||||||
#include "event/MultiSocketMonitor.hxx"
|
#include "event/MultiSocketMonitor.hxx"
|
||||||
@@ -287,13 +286,7 @@ ConfigureCapture(snd_pcm_t *capture_handle,
|
|||||||
int err;
|
int err;
|
||||||
|
|
||||||
snd_pcm_hw_params_t *hw_params;
|
snd_pcm_hw_params_t *hw_params;
|
||||||
if ((err = snd_pcm_hw_params_malloc(&hw_params)) < 0)
|
snd_pcm_hw_params_alloca(&hw_params);
|
||||||
throw FormatRuntimeError("Cannot allocate hardware parameter structure (%s)",
|
|
||||||
snd_strerror(err));
|
|
||||||
|
|
||||||
AtScopeExit(hw_params) {
|
|
||||||
snd_pcm_hw_params_free(hw_params);
|
|
||||||
};
|
|
||||||
|
|
||||||
if ((err = snd_pcm_hw_params_any(capture_handle, hw_params)) < 0)
|
if ((err = snd_pcm_hw_params_any(capture_handle, hw_params)) < 0)
|
||||||
throw FormatRuntimeError("Cannot initialize hardware parameter structure (%s)",
|
throw FormatRuntimeError("Cannot initialize hardware parameter structure (%s)",
|
||||||
@@ -373,14 +366,10 @@ ConfigureCapture(snd_pcm_t *capture_handle,
|
|||||||
(unsigned)alsa_buffer_size, (unsigned)alsa_period_size);
|
(unsigned)alsa_buffer_size, (unsigned)alsa_period_size);
|
||||||
|
|
||||||
snd_pcm_sw_params_t *sw_params;
|
snd_pcm_sw_params_t *sw_params;
|
||||||
|
snd_pcm_sw_params_alloca(&sw_params);
|
||||||
|
|
||||||
snd_pcm_sw_params_malloc(&sw_params);
|
|
||||||
snd_pcm_sw_params_current(capture_handle, sw_params);
|
snd_pcm_sw_params_current(capture_handle, sw_params);
|
||||||
|
|
||||||
AtScopeExit(sw_params) {
|
|
||||||
snd_pcm_sw_params_free(sw_params);
|
|
||||||
};
|
|
||||||
|
|
||||||
if ((err = snd_pcm_sw_params(capture_handle, sw_params)) < 0)
|
if ((err = snd_pcm_sw_params(capture_handle, sw_params)) < 0)
|
||||||
throw FormatRuntimeError("unable to install sw params (%s)",
|
throw FormatRuntimeError("unable to install sw params (%s)",
|
||||||
snd_strerror(err));
|
snd_strerror(err));
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2010-2011 Max Kellermann <max@duempel.org>
|
* Copyright (C) 2010-2011 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
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2010-2012 Max Kellermann <max@duempel.org>
|
* Copyright (C) 2010-2012 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
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2010-2014 Max Kellermann <max@duempel.org>
|
* Copyright (C) 2010-2014 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
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2010-2014 Max Kellermann <max@duempel.org>
|
* Copyright (C) 2010-2014 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
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2010-2011 Max Kellermann <max@duempel.org>
|
* Copyright (C) 2010-2011 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
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2010-2011 Max Kellermann <max@duempel.org>
|
* Copyright (C) 2010-2011 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
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2010-2011 Max Kellermann <max@duempel.org>
|
* Copyright (C) 2010-2011 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
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2010-2011 Max Kellermann <max@duempel.org>
|
* Copyright (C) 2010-2011 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
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2010-2011 Max Kellermann <max@duempel.org>
|
* Copyright (C) 2010-2011 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
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2010-2011 Max Kellermann <max@duempel.org>
|
* Copyright (C) 2010-2011 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
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2016 Max Kellermann <max@duempel.org>
|
* Copyright (C) 2016 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
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2008-2016 Max Kellermann <max@duempel.org>
|
* Copyright (C) 2008-2016 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
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2008-2016 Max Kellermann <max@duempel.org>
|
* Copyright (C) 2008-2016 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
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2008-2017 Max Kellermann <max@duempel.org>
|
* Copyright (C) 2008-2017 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
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2016 Max Kellermann <max@duempel.org>
|
* Copyright (C) 2016 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
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2008-2017 Max Kellermann <max@duempel.org>
|
* Copyright (C) 2008-2017 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
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2008-2017 Max Kellermann <max@duempel.org>
|
* Copyright (C) 2008-2017 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
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2008-2017 Max Kellermann <max@duempel.org>
|
* Copyright (C) 2008-2017 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
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2017 Max Kellermann <max@duempel.org>
|
* Copyright (C) 2017 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
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2017 Max Kellermann <max@duempel.org>
|
* Copyright (C) 2017 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
|
||||||
|
@@ -62,7 +62,7 @@ class UPnPDeviceDirectory final : UpnpCallback {
|
|||||||
DiscoveredTask(const Upnp_Discovery *disco)
|
DiscoveredTask(const Upnp_Discovery *disco)
|
||||||
:url(disco->Location),
|
:url(disco->Location),
|
||||||
device_id(disco->DeviceId),
|
device_id(disco->DeviceId),
|
||||||
expires(disco->Expires) {}
|
expires(std::chrono::seconds(disco->Expires)) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -71,20 +71,19 @@ path_getfather(const std::string &s)
|
|||||||
|
|
||||||
std::list<std::string>
|
std::list<std::string>
|
||||||
stringToTokens(const std::string &str,
|
stringToTokens(const std::string &str,
|
||||||
const char *delims, bool skipinit)
|
const char delim)
|
||||||
{
|
{
|
||||||
std::list<std::string> tokens;
|
std::list<std::string> tokens;
|
||||||
|
|
||||||
std::string::size_type startPos = 0;
|
std::string::size_type startPos = str.find_first_not_of(delim, 0);
|
||||||
|
|
||||||
// Skip initial delims, return empty if this eats all.
|
// Skip initial delims, return empty if this eats all.
|
||||||
if (skipinit &&
|
if (startPos == std::string::npos)
|
||||||
(startPos = str.find_first_not_of(delims, 0)) == std::string::npos)
|
|
||||||
return tokens;
|
return tokens;
|
||||||
|
|
||||||
while (startPos < str.size()) {
|
while (startPos < str.size()) {
|
||||||
// Find next delimiter or end of string (end of token)
|
// Find next delimiter or end of string (end of token)
|
||||||
auto pos = str.find_first_of(delims, startPos);
|
auto pos = str.find_first_of(delim, startPos);
|
||||||
|
|
||||||
// Add token to the vector and adjust start
|
// Add token to the vector and adjust start
|
||||||
if (pos == std::string::npos) {
|
if (pos == std::string::npos) {
|
||||||
|
@@ -33,8 +33,7 @@ path_getfather(const std::string &s);
|
|||||||
|
|
||||||
gcc_pure
|
gcc_pure
|
||||||
std::list<std::string>
|
std::list<std::string>
|
||||||
stringToTokens(const std::string &str,
|
stringToTokens(const std::string &str, char delim);
|
||||||
const char *delims = "/", bool skipinit = true);
|
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
bool
|
bool
|
||||||
|
@@ -52,11 +52,11 @@ class WorkQueue {
|
|||||||
|
|
||||||
// Status
|
// Status
|
||||||
// Worker threads having called exit
|
// Worker threads having called exit
|
||||||
unsigned n_workers_exited;
|
unsigned n_workers_exited = 0;
|
||||||
bool ok;
|
bool ok = false;
|
||||||
|
|
||||||
unsigned n_threads;
|
unsigned n_threads = 0;
|
||||||
pthread_t *threads;
|
pthread_t *threads = nullptr;
|
||||||
|
|
||||||
// Synchronization
|
// Synchronization
|
||||||
std::queue<T> queue;
|
std::queue<T> queue;
|
||||||
@@ -68,11 +68,8 @@ public:
|
|||||||
/** Create a WorkQueue
|
/** Create a WorkQueue
|
||||||
* @param _name for message printing
|
* @param _name for message printing
|
||||||
*/
|
*/
|
||||||
WorkQueue(const char *_name)
|
explicit WorkQueue(const char *_name)
|
||||||
:name(_name),
|
:name(_name)
|
||||||
n_workers_exited(0),
|
|
||||||
ok(false),
|
|
||||||
n_threads(0), threads(nullptr)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,6 +77,9 @@ public:
|
|||||||
setTerminateAndWait();
|
setTerminateAndWait();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WorkQueue(const WorkQueue &) = delete;
|
||||||
|
WorkQueue &operator=(const WorkQueue &) = delete;
|
||||||
|
|
||||||
/** Start the worker threads.
|
/** Start the worker threads.
|
||||||
*
|
*
|
||||||
* @param nworkers number of threads copies to start.
|
* @param nworkers number of threads copies to start.
|
||||||
@@ -97,6 +97,7 @@ public:
|
|||||||
assert(n_threads == 0);
|
assert(n_threads == 0);
|
||||||
assert(threads == nullptr);
|
assert(threads == nullptr);
|
||||||
|
|
||||||
|
ok = true;
|
||||||
n_threads = nworkers;
|
n_threads = nworkers;
|
||||||
threads = new pthread_t[n_threads];
|
threads = new pthread_t[n_threads];
|
||||||
|
|
||||||
@@ -109,7 +110,6 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ok = true;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2012-2015 Max Kellermann <max@duempel.org>
|
* Copyright (C) 2012-2015 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
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2012-2015 Max Kellermann <max@duempel.org>
|
* Copyright (C) 2012-2015 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
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2012-2015 Max Kellermann <max@duempel.org>
|
* Copyright (C) 2012-2015 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
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2012-2015 Max Kellermann <max@duempel.org>
|
* Copyright (C) 2012-2015 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
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2012-2015 Max Kellermann <max@duempel.org>
|
* Copyright (C) 2012-2015 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
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2012-2015 Max Kellermann <max@duempel.org>
|
* Copyright (C) 2012-2015 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
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2011-2015 Max Kellermann <max@duempel.org>
|
* Copyright (C) 2011-2015 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
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2011-2015 Max Kellermann <max@duempel.org>
|
* Copyright (C) 2011-2015 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
|
||||||
|
@@ -35,6 +35,7 @@
|
|||||||
#include "thread/Slack.hxx"
|
#include "thread/Slack.hxx"
|
||||||
#include "thread/Name.hxx"
|
#include "thread/Name.hxx"
|
||||||
#include "util/ConstBuffer.hxx"
|
#include "util/ConstBuffer.hxx"
|
||||||
|
#include "util/StringBuffer.hxx"
|
||||||
#include "util/ScopeExit.hxx"
|
#include "util/ScopeExit.hxx"
|
||||||
#include "util/RuntimeError.hxx"
|
#include "util/RuntimeError.hxx"
|
||||||
#include "Log.hxx"
|
#include "Log.hxx"
|
||||||
@@ -154,14 +155,11 @@ AudioOutput::Open()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (f != source.GetInputAudioFormat() || f != out_audio_format) {
|
if (f != source.GetInputAudioFormat() || f != out_audio_format)
|
||||||
struct audio_format_string afs1, afs2, afs3;
|
|
||||||
FormatDebug(output_domain, "converting in=%s -> f=%s -> out=%s",
|
FormatDebug(output_domain, "converting in=%s -> f=%s -> out=%s",
|
||||||
audio_format_to_string(source.GetInputAudioFormat(),
|
ToString(source.GetInputAudioFormat()).c_str(),
|
||||||
&afs1),
|
ToString(f).c_str(),
|
||||||
audio_format_to_string(f, &afs2),
|
ToString(out_audio_format).c_str());
|
||||||
audio_format_to_string(out_audio_format, &afs3));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -176,11 +174,10 @@ AudioOutput::OpenOutputAndConvert(AudioFormat desired_audio_format)
|
|||||||
name, plugin.name));
|
name, plugin.name));
|
||||||
}
|
}
|
||||||
|
|
||||||
struct audio_format_string af_string;
|
|
||||||
FormatDebug(output_domain,
|
FormatDebug(output_domain,
|
||||||
"opened plugin=%s name=\"%s\" audio_format=%s",
|
"opened plugin=%s name=\"%s\" audio_format=%s",
|
||||||
plugin.name, name,
|
plugin.name, name,
|
||||||
audio_format_to_string(out_audio_format, &af_string));
|
ToString(out_audio_format).c_str());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
convert_filter_set(convert_filter.Get(), out_audio_format);
|
convert_filter_set(convert_filter.Get(), out_audio_format);
|
||||||
|
@@ -95,13 +95,17 @@ try {
|
|||||||
assert(audio_format.IsValid());
|
assert(audio_format.IsValid());
|
||||||
|
|
||||||
/* the replay_gain filter cannot fail here */
|
/* the replay_gain filter cannot fail here */
|
||||||
if (prepared_replay_gain_filter != nullptr)
|
if (prepared_replay_gain_filter != nullptr) {
|
||||||
|
replay_gain_serial = 0;
|
||||||
replay_gain_filter_instance =
|
replay_gain_filter_instance =
|
||||||
prepared_replay_gain_filter->Open(audio_format);
|
prepared_replay_gain_filter->Open(audio_format);
|
||||||
|
}
|
||||||
|
|
||||||
if (prepared_other_replay_gain_filter != nullptr)
|
if (prepared_other_replay_gain_filter != nullptr) {
|
||||||
|
other_replay_gain_serial = 0;
|
||||||
other_replay_gain_filter_instance =
|
other_replay_gain_filter_instance =
|
||||||
prepared_other_replay_gain_filter->Open(audio_format);
|
prepared_other_replay_gain_filter->Open(audio_format);
|
||||||
|
}
|
||||||
|
|
||||||
filter_instance = prepared_filter->Open(audio_format);
|
filter_instance = prepared_filter->Open(audio_format);
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
|
@@ -64,13 +64,13 @@ class AudioOutputSource {
|
|||||||
* The serial number of the last replay gain info. 0 means no
|
* The serial number of the last replay gain info. 0 means no
|
||||||
* replay gain info was available.
|
* replay gain info was available.
|
||||||
*/
|
*/
|
||||||
unsigned replay_gain_serial = 0;
|
unsigned replay_gain_serial;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The serial number of the last replay gain info by the
|
* The serial number of the last replay gain info by the
|
||||||
* "other" chunk during cross-fading.
|
* "other" chunk during cross-fading.
|
||||||
*/
|
*/
|
||||||
unsigned other_replay_gain_serial = 0;
|
unsigned other_replay_gain_serial;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The replay_gain_filter_plugin instance of this audio
|
* The replay_gain_filter_plugin instance of this audio
|
||||||
|
@@ -50,7 +50,9 @@ static constexpr unsigned MPD_ALSA_BUFFER_TIME_US = 500000;
|
|||||||
|
|
||||||
static constexpr unsigned MPD_ALSA_RETRY_NR = 5;
|
static constexpr unsigned MPD_ALSA_RETRY_NR = 5;
|
||||||
|
|
||||||
struct AlsaOutput {
|
class AlsaOutput {
|
||||||
|
friend struct AudioOutputWrapper<AlsaOutput>;
|
||||||
|
|
||||||
AudioOutput base;
|
AudioOutput base;
|
||||||
|
|
||||||
Manual<PcmExport> pcm_export;
|
Manual<PcmExport> pcm_export;
|
||||||
@@ -121,6 +123,7 @@ struct AlsaOutput {
|
|||||||
*/
|
*/
|
||||||
uint8_t *silence;
|
uint8_t *silence;
|
||||||
|
|
||||||
|
public:
|
||||||
AlsaOutput(const ConfigBlock &block);
|
AlsaOutput(const ConfigBlock &block);
|
||||||
|
|
||||||
~AlsaOutput() {
|
~AlsaOutput() {
|
||||||
@@ -147,6 +150,14 @@ struct AlsaOutput {
|
|||||||
void Cancel();
|
void Cancel();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
/**
|
||||||
|
* Set up the snd_pcm_t object which was opened by the caller.
|
||||||
|
* Set up the configured settings and the audio format.
|
||||||
|
*
|
||||||
|
* Throws #std::runtime_error on error.
|
||||||
|
*/
|
||||||
|
void Setup(AudioFormat &audio_format, PcmExport::Params ¶ms);
|
||||||
|
|
||||||
#ifdef ENABLE_DSD
|
#ifdef ENABLE_DSD
|
||||||
void SetupDop(AudioFormat audio_format,
|
void SetupDop(AudioFormat audio_format,
|
||||||
PcmExport::Params ¶ms);
|
PcmExport::Params ¶ms);
|
||||||
@@ -477,50 +488,44 @@ AlsaSetupFormat(snd_pcm_t *pcm, snd_pcm_hw_params_t *hwparams,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set up the snd_pcm_t object which was opened by the caller. Set up
|
* Wrapper for snd_pcm_hw_params().
|
||||||
* the configured settings and the audio format.
|
|
||||||
*
|
*
|
||||||
* Throws #std::runtime_error on error.
|
* @param buffer_time the configured buffer time, or 0 if not configured
|
||||||
|
* @param period_time the configured period time, or 0 if not configured
|
||||||
|
* @param audio_format an #AudioFormat to be configured (or modified)
|
||||||
|
* by this function
|
||||||
|
* @param params to be modified by this function
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
AlsaSetup(AlsaOutput *ad, AudioFormat &audio_format,
|
AlsaSetupHw(snd_pcm_t *pcm, snd_pcm_hw_params_t *hwparams,
|
||||||
PcmExport::Params ¶ms)
|
unsigned buffer_time, unsigned period_time,
|
||||||
|
AudioFormat &audio_format, PcmExport::Params ¶ms)
|
||||||
{
|
{
|
||||||
unsigned int channels = audio_format.channels;
|
|
||||||
int err;
|
int err;
|
||||||
unsigned retry = MPD_ALSA_RETRY_NR;
|
unsigned retry = MPD_ALSA_RETRY_NR;
|
||||||
unsigned int period_time, period_time_ro;
|
unsigned int period_time_ro = period_time;
|
||||||
unsigned int buffer_time;
|
|
||||||
|
|
||||||
period_time_ro = period_time = ad->period_time;
|
|
||||||
configure_hw:
|
configure_hw:
|
||||||
/* configure HW params */
|
/* configure HW params */
|
||||||
snd_pcm_hw_params_t *hwparams;
|
err = snd_pcm_hw_params_any(pcm, hwparams);
|
||||||
snd_pcm_hw_params_alloca(&hwparams);
|
|
||||||
err = snd_pcm_hw_params_any(ad->pcm, hwparams);
|
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
throw FormatRuntimeError("snd_pcm_hw_params_any() failed: %s",
|
throw FormatRuntimeError("snd_pcm_hw_params_any() failed: %s",
|
||||||
snd_strerror(-err));
|
snd_strerror(-err));
|
||||||
|
|
||||||
err = snd_pcm_hw_params_set_access(ad->pcm, hwparams,
|
err = snd_pcm_hw_params_set_access(pcm, hwparams,
|
||||||
SND_PCM_ACCESS_RW_INTERLEAVED);
|
SND_PCM_ACCESS_RW_INTERLEAVED);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
throw FormatRuntimeError("snd_pcm_hw_params_set_access() failed: %s",
|
throw FormatRuntimeError("snd_pcm_hw_params_set_access() failed: %s",
|
||||||
snd_strerror(-err));
|
snd_strerror(-err));
|
||||||
|
|
||||||
err = AlsaSetupFormat(ad->pcm, hwparams, audio_format, params);
|
err = AlsaSetupFormat(pcm, hwparams, audio_format, params);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
throw FormatRuntimeError("Failed to configure format %s: %s",
|
throw FormatRuntimeError("Failed to configure format %s: %s",
|
||||||
sample_format_to_string(audio_format.format),
|
sample_format_to_string(audio_format.format),
|
||||||
snd_strerror(-err));
|
snd_strerror(-err));
|
||||||
|
|
||||||
snd_pcm_format_t format;
|
unsigned int channels = audio_format.channels;
|
||||||
if (snd_pcm_hw_params_get_format(hwparams, &format) == 0)
|
err = snd_pcm_hw_params_set_channels_near(pcm, hwparams,
|
||||||
FormatDebug(alsa_output_domain,
|
|
||||||
"format=%s (%s)", snd_pcm_format_name(format),
|
|
||||||
snd_pcm_format_description(format));
|
|
||||||
|
|
||||||
err = snd_pcm_hw_params_set_channels_near(ad->pcm, hwparams,
|
|
||||||
&channels);
|
&channels);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
throw FormatRuntimeError("Failed to configure %i channels: %s",
|
throw FormatRuntimeError("Failed to configure %i channels: %s",
|
||||||
@@ -533,7 +538,7 @@ configure_hw:
|
|||||||
params.CalcOutputSampleRate(audio_format.sample_rate);
|
params.CalcOutputSampleRate(audio_format.sample_rate);
|
||||||
unsigned output_sample_rate = requested_sample_rate;
|
unsigned output_sample_rate = requested_sample_rate;
|
||||||
|
|
||||||
err = snd_pcm_hw_params_set_rate_near(ad->pcm, hwparams,
|
err = snd_pcm_hw_params_set_rate_near(pcm, hwparams,
|
||||||
&output_sample_rate, nullptr);
|
&output_sample_rate, nullptr);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
throw FormatRuntimeError("Failed to configure sample rate %u Hz: %s",
|
throw FormatRuntimeError("Failed to configure sample rate %u Hz: %s",
|
||||||
@@ -567,9 +572,8 @@ configure_hw:
|
|||||||
(unsigned)period_size_min, (unsigned)period_size_max,
|
(unsigned)period_size_min, (unsigned)period_size_max,
|
||||||
period_time_min, period_time_max);
|
period_time_min, period_time_max);
|
||||||
|
|
||||||
if (ad->buffer_time > 0) {
|
if (buffer_time > 0) {
|
||||||
buffer_time = ad->buffer_time;
|
err = snd_pcm_hw_params_set_buffer_time_near(pcm, hwparams,
|
||||||
err = snd_pcm_hw_params_set_buffer_time_near(ad->pcm, hwparams,
|
|
||||||
&buffer_time, nullptr);
|
&buffer_time, nullptr);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
throw FormatRuntimeError("snd_pcm_hw_params_set_buffer_time_near() failed: %s",
|
throw FormatRuntimeError("snd_pcm_hw_params_set_buffer_time_near() failed: %s",
|
||||||
@@ -591,14 +595,14 @@ configure_hw:
|
|||||||
|
|
||||||
if (period_time_ro > 0) {
|
if (period_time_ro > 0) {
|
||||||
period_time = period_time_ro;
|
period_time = period_time_ro;
|
||||||
err = snd_pcm_hw_params_set_period_time_near(ad->pcm, hwparams,
|
err = snd_pcm_hw_params_set_period_time_near(pcm, hwparams,
|
||||||
&period_time, nullptr);
|
&period_time, nullptr);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
throw FormatRuntimeError("snd_pcm_hw_params_set_period_time_near() failed: %s",
|
throw FormatRuntimeError("snd_pcm_hw_params_set_period_time_near() failed: %s",
|
||||||
snd_strerror(-err));
|
snd_strerror(-err));
|
||||||
}
|
}
|
||||||
|
|
||||||
err = snd_pcm_hw_params(ad->pcm, hwparams);
|
err = snd_pcm_hw_params(pcm, hwparams);
|
||||||
if (err == -EPIPE && --retry > 0 && period_time_ro > 0) {
|
if (err == -EPIPE && --retry > 0 && period_time_ro > 0) {
|
||||||
period_time_ro = period_time_ro >> 1;
|
period_time_ro = period_time_ro >> 1;
|
||||||
goto configure_hw;
|
goto configure_hw;
|
||||||
@@ -608,9 +612,59 @@ configure_hw:
|
|||||||
if (retry != MPD_ALSA_RETRY_NR)
|
if (retry != MPD_ALSA_RETRY_NR)
|
||||||
FormatDebug(alsa_output_domain,
|
FormatDebug(alsa_output_domain,
|
||||||
"ALSA period_time set to %d", period_time);
|
"ALSA period_time set to %d", period_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper for snd_pcm_sw_params().
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
AlsaSetupSw(snd_pcm_t *pcm, snd_pcm_uframes_t start_threshold,
|
||||||
|
snd_pcm_uframes_t avail_min)
|
||||||
|
{
|
||||||
|
snd_pcm_sw_params_t *swparams;
|
||||||
|
snd_pcm_sw_params_alloca(&swparams);
|
||||||
|
|
||||||
|
int err = snd_pcm_sw_params_current(pcm, swparams);
|
||||||
|
if (err < 0)
|
||||||
|
throw FormatRuntimeError("snd_pcm_sw_params_current() failed: %s",
|
||||||
|
snd_strerror(-err));
|
||||||
|
|
||||||
|
err = snd_pcm_sw_params_set_start_threshold(pcm, swparams,
|
||||||
|
start_threshold);
|
||||||
|
if (err < 0)
|
||||||
|
throw FormatRuntimeError("snd_pcm_sw_params_set_start_threshold() failed: %s",
|
||||||
|
snd_strerror(-err));
|
||||||
|
|
||||||
|
err = snd_pcm_sw_params_set_avail_min(pcm, swparams, avail_min);
|
||||||
|
if (err < 0)
|
||||||
|
throw FormatRuntimeError("snd_pcm_sw_params_set_avail_min() failed: %s",
|
||||||
|
snd_strerror(-err));
|
||||||
|
|
||||||
|
err = snd_pcm_sw_params(pcm, swparams);
|
||||||
|
if (err < 0)
|
||||||
|
throw FormatRuntimeError("snd_pcm_sw_params() failed: %s",
|
||||||
|
snd_strerror(-err));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void
|
||||||
|
AlsaOutput::Setup(AudioFormat &audio_format,
|
||||||
|
PcmExport::Params ¶ms)
|
||||||
|
{
|
||||||
|
snd_pcm_hw_params_t *hwparams;
|
||||||
|
snd_pcm_hw_params_alloca(&hwparams);
|
||||||
|
|
||||||
|
AlsaSetupHw(pcm, hwparams,
|
||||||
|
buffer_time, period_time,
|
||||||
|
audio_format, params);
|
||||||
|
|
||||||
|
snd_pcm_format_t format;
|
||||||
|
if (snd_pcm_hw_params_get_format(hwparams, &format) == 0)
|
||||||
|
FormatDebug(alsa_output_domain,
|
||||||
|
"format=%s (%s)", snd_pcm_format_name(format),
|
||||||
|
snd_pcm_format_description(format));
|
||||||
|
|
||||||
snd_pcm_uframes_t alsa_buffer_size;
|
snd_pcm_uframes_t alsa_buffer_size;
|
||||||
err = snd_pcm_hw_params_get_buffer_size(hwparams, &alsa_buffer_size);
|
int err = snd_pcm_hw_params_get_buffer_size(hwparams, &alsa_buffer_size);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
throw FormatRuntimeError("snd_pcm_hw_params_get_buffer_size() failed: %s",
|
throw FormatRuntimeError("snd_pcm_hw_params_get_buffer_size() failed: %s",
|
||||||
snd_strerror(-err));
|
snd_strerror(-err));
|
||||||
@@ -622,32 +676,8 @@ configure_hw:
|
|||||||
throw FormatRuntimeError("snd_pcm_hw_params_get_period_size() failed: %s",
|
throw FormatRuntimeError("snd_pcm_hw_params_get_period_size() failed: %s",
|
||||||
snd_strerror(-err));
|
snd_strerror(-err));
|
||||||
|
|
||||||
/* configure SW params */
|
AlsaSetupSw(pcm, alsa_buffer_size - alsa_period_size,
|
||||||
snd_pcm_sw_params_t *swparams;
|
alsa_period_size);
|
||||||
snd_pcm_sw_params_alloca(&swparams);
|
|
||||||
|
|
||||||
err = snd_pcm_sw_params_current(ad->pcm, swparams);
|
|
||||||
if (err < 0)
|
|
||||||
throw FormatRuntimeError("snd_pcm_sw_params_current() failed: %s",
|
|
||||||
snd_strerror(-err));
|
|
||||||
|
|
||||||
err = snd_pcm_sw_params_set_start_threshold(ad->pcm, swparams,
|
|
||||||
alsa_buffer_size -
|
|
||||||
alsa_period_size);
|
|
||||||
if (err < 0)
|
|
||||||
throw FormatRuntimeError("snd_pcm_sw_params_set_start_threshold() failed: %s",
|
|
||||||
snd_strerror(-err));
|
|
||||||
|
|
||||||
err = snd_pcm_sw_params_set_avail_min(ad->pcm, swparams,
|
|
||||||
alsa_period_size);
|
|
||||||
if (err < 0)
|
|
||||||
throw FormatRuntimeError("snd_pcm_sw_params_set_avail_min() failed: %s",
|
|
||||||
snd_strerror(-err));
|
|
||||||
|
|
||||||
err = snd_pcm_sw_params(ad->pcm, swparams);
|
|
||||||
if (err < 0)
|
|
||||||
throw FormatRuntimeError("snd_pcm_sw_params() failed: %s",
|
|
||||||
snd_strerror(-err));
|
|
||||||
|
|
||||||
FormatDebug(alsa_output_domain, "buffer_size=%u period_size=%u",
|
FormatDebug(alsa_output_domain, "buffer_size=%u period_size=%u",
|
||||||
(unsigned)alsa_buffer_size, (unsigned)alsa_period_size);
|
(unsigned)alsa_buffer_size, (unsigned)alsa_period_size);
|
||||||
@@ -660,13 +690,12 @@ configure_hw:
|
|||||||
happen again. */
|
happen again. */
|
||||||
alsa_period_size = 1;
|
alsa_period_size = 1;
|
||||||
|
|
||||||
ad->period_frames = alsa_period_size;
|
period_frames = alsa_period_size;
|
||||||
ad->period_position = 0;
|
period_position = 0;
|
||||||
|
|
||||||
ad->silence = new uint8_t[snd_pcm_frames_to_bytes(ad->pcm,
|
silence = new uint8_t[snd_pcm_frames_to_bytes(pcm, alsa_period_size)];
|
||||||
alsa_period_size)];
|
snd_pcm_format_set_silence(format, silence,
|
||||||
snd_pcm_format_set_silence(format, ad->silence,
|
alsa_period_size * audio_format.channels);
|
||||||
alsa_period_size * channels);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -686,7 +715,7 @@ AlsaOutput::SetupDop(const AudioFormat audio_format,
|
|||||||
|
|
||||||
const AudioFormat check = dop_format;
|
const AudioFormat check = dop_format;
|
||||||
|
|
||||||
AlsaSetup(this, dop_format, params);
|
Setup(dop_format, params);
|
||||||
|
|
||||||
/* if the device allows only 32 bit, shift all DoP
|
/* if the device allows only 32 bit, shift all DoP
|
||||||
samples left by 8 bit and leave the lower 8 bit cleared;
|
samples left by 8 bit and leave the lower 8 bit cleared;
|
||||||
@@ -725,7 +754,7 @@ AlsaOutput::SetupOrDop(AudioFormat &audio_format, PcmExport::Params ¶ms)
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
#endif
|
#endif
|
||||||
AlsaSetup(this, audio_format, params);
|
Setup(audio_format, params);
|
||||||
#ifdef ENABLE_DSD
|
#ifdef ENABLE_DSD
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
if (dop_error)
|
if (dop_error)
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2011-2012 Max Kellermann <max@duempel.org>
|
* Copyright (C) 2011-2012 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
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2011-2012 Max Kellermann <max@duempel.org>
|
* Copyright (C) 2011-2012 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
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2011-2012 Max Kellermann <max@duempel.org>
|
* Copyright (C) 2011-2012 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
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2011-2012 Max Kellermann <max@duempel.org>
|
* Copyright (C) 2011-2012 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
|
||||||
|
@@ -21,7 +21,7 @@
|
|||||||
#define MPD_PCM_CHANNELS_CONVERTER_HXX
|
#define MPD_PCM_CHANNELS_CONVERTER_HXX
|
||||||
|
|
||||||
#include "check.h"
|
#include "check.h"
|
||||||
#include "AudioFormat.hxx"
|
#include "SampleFormat.hxx"
|
||||||
#include "PcmBuffer.hxx"
|
#include "PcmBuffer.hxx"
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
|
@@ -21,7 +21,7 @@
|
|||||||
#define MPD_PCM_FORMAT_CONVERTER_HXX
|
#define MPD_PCM_FORMAT_CONVERTER_HXX
|
||||||
|
|
||||||
#include "check.h"
|
#include "check.h"
|
||||||
#include "AudioFormat.hxx"
|
#include "SampleFormat.hxx"
|
||||||
#include "PcmBuffer.hxx"
|
#include "PcmBuffer.hxx"
|
||||||
#include "PcmDither.hxx"
|
#include "PcmDither.hxx"
|
||||||
|
|
||||||
|
@@ -21,7 +21,7 @@
|
|||||||
#define MPD_PCM_ORDER_HXX
|
#define MPD_PCM_ORDER_HXX
|
||||||
|
|
||||||
#include "check.h"
|
#include "check.h"
|
||||||
#include "AudioFormat.hxx"
|
#include "SampleFormat.hxx"
|
||||||
|
|
||||||
class PcmBuffer;
|
class PcmBuffer;
|
||||||
template<typename T> struct ConstBuffer;
|
template<typename T> struct ConstBuffer;
|
||||||
|
@@ -20,9 +20,14 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "PcmChannels.hxx"
|
#include "PcmChannels.hxx"
|
||||||
#include "PcmBuffer.hxx"
|
#include "PcmBuffer.hxx"
|
||||||
|
#include "Silence.hxx"
|
||||||
#include "Traits.hxx"
|
#include "Traits.hxx"
|
||||||
#include "AudioFormat.hxx"
|
#include "AudioFormat.hxx"
|
||||||
#include "util/ConstBuffer.hxx"
|
#include "util/ConstBuffer.hxx"
|
||||||
|
#include "util/WritableBuffer.hxx"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
@@ -90,6 +95,38 @@ NToStereo(typename Traits::pointer_type dest,
|
|||||||
return dest;
|
return dest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert stereo to N channels (where N > 2). Left and right map to
|
||||||
|
* the first two channels (front left and front right), and the
|
||||||
|
* remaining (surround) channels are filled with silence.
|
||||||
|
*/
|
||||||
|
template<SampleFormat F, class Traits=SampleTraits<F>>
|
||||||
|
static typename Traits::pointer_type
|
||||||
|
StereoToN(typename Traits::pointer_type dest,
|
||||||
|
unsigned dest_channels,
|
||||||
|
typename Traits::const_pointer_type src,
|
||||||
|
typename Traits::const_pointer_type end)
|
||||||
|
{
|
||||||
|
assert(dest_channels > 2);
|
||||||
|
assert((end - src) % 2 == 0);
|
||||||
|
|
||||||
|
std::array<typename Traits::value_type, MAX_CHANNELS - 2> silence;
|
||||||
|
PcmSilence({&silence.front(), sizeof(silence)}, F);
|
||||||
|
|
||||||
|
while (src != end) {
|
||||||
|
/* copy left/right to front-left/front-right, which is
|
||||||
|
the first two channels in all multi-channel
|
||||||
|
configurations **/
|
||||||
|
*dest++ = *src++;
|
||||||
|
*dest++ = *src++;
|
||||||
|
|
||||||
|
/* all other channels are silent */
|
||||||
|
dest = std::copy_n(silence.begin(), dest_channels - 2, dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
template<SampleFormat F, class Traits=SampleTraits<F>>
|
template<SampleFormat F, class Traits=SampleTraits<F>>
|
||||||
static typename Traits::pointer_type
|
static typename Traits::pointer_type
|
||||||
NToM(typename Traits::pointer_type dest,
|
NToM(typename Traits::pointer_type dest,
|
||||||
@@ -133,6 +170,9 @@ ConvertChannels(PcmBuffer &buffer,
|
|||||||
StereoToMono<F>(dest, src.begin(), src.end());
|
StereoToMono<F>(dest, src.begin(), src.end());
|
||||||
else if (dest_channels == 2)
|
else if (dest_channels == 2)
|
||||||
NToStereo<F>(dest, src_channels, src.begin(), src.end());
|
NToStereo<F>(dest, src_channels, src.begin(), src.end());
|
||||||
|
else if (src_channels == 2 && dest_channels > 2)
|
||||||
|
StereoToN<F, Traits>(dest, dest_channels,
|
||||||
|
src.begin(), src.end());
|
||||||
else
|
else
|
||||||
NToM<F>(dest, dest_channels,
|
NToM<F>(dest, dest_channels,
|
||||||
src_channels, src.begin(), src.end());
|
src_channels, src.begin(), src.end());
|
||||||
|
@@ -20,7 +20,6 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "PcmConvert.hxx"
|
#include "PcmConvert.hxx"
|
||||||
#include "ConfiguredResampler.hxx"
|
#include "ConfiguredResampler.hxx"
|
||||||
#include "AudioFormat.hxx"
|
|
||||||
#include "util/ConstBuffer.hxx"
|
#include "util/ConstBuffer.hxx"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "PcmExport.hxx"
|
#include "PcmExport.hxx"
|
||||||
|
#include "AudioFormat.hxx"
|
||||||
#include "Order.hxx"
|
#include "Order.hxx"
|
||||||
#include "PcmPack.hxx"
|
#include "PcmPack.hxx"
|
||||||
#include "util/ByteReverse.hxx"
|
#include "util/ByteReverse.hxx"
|
||||||
|
@@ -21,10 +21,11 @@
|
|||||||
#define PCM_EXPORT_HXX
|
#define PCM_EXPORT_HXX
|
||||||
|
|
||||||
#include "check.h"
|
#include "check.h"
|
||||||
|
#include "SampleFormat.hxx"
|
||||||
#include "PcmBuffer.hxx"
|
#include "PcmBuffer.hxx"
|
||||||
#include "AudioFormat.hxx"
|
|
||||||
|
|
||||||
template<typename T> struct ConstBuffer;
|
template<typename T> struct ConstBuffer;
|
||||||
|
struct AudioFormat;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An object that handles export of PCM samples to some instance
|
* An object that handles export of PCM samples to some instance
|
||||||
|
@@ -20,7 +20,7 @@
|
|||||||
#ifndef MPD_PCM_FORMAT_HXX
|
#ifndef MPD_PCM_FORMAT_HXX
|
||||||
#define MPD_PCM_FORMAT_HXX
|
#define MPD_PCM_FORMAT_HXX
|
||||||
|
|
||||||
#include "AudioFormat.hxx"
|
#include "SampleFormat.hxx"
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
@@ -21,7 +21,6 @@
|
|||||||
#include "PcmMix.hxx"
|
#include "PcmMix.hxx"
|
||||||
#include "Volume.hxx"
|
#include "Volume.hxx"
|
||||||
#include "PcmUtils.hxx"
|
#include "PcmUtils.hxx"
|
||||||
#include "AudioFormat.hxx"
|
|
||||||
#include "Traits.hxx"
|
#include "Traits.hxx"
|
||||||
#include "util/Clamp.hxx"
|
#include "util/Clamp.hxx"
|
||||||
|
|
||||||
|
@@ -20,7 +20,7 @@
|
|||||||
#ifndef MPD_PCM_MIX_HXX
|
#ifndef MPD_PCM_MIX_HXX
|
||||||
#define MPD_PCM_MIX_HXX
|
#define MPD_PCM_MIX_HXX
|
||||||
|
|
||||||
#include "AudioFormat.hxx"
|
#include "SampleFormat.hxx"
|
||||||
#include "Compiler.h"
|
#include "Compiler.h"
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
53
src/pcm/SampleFormat.cxx
Normal file
53
src/pcm/SampleFormat.cxx
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2003-2017 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 "SampleFormat.hxx"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
const char *
|
||||||
|
sample_format_to_string(SampleFormat format)
|
||||||
|
{
|
||||||
|
switch (format) {
|
||||||
|
case SampleFormat::UNDEFINED:
|
||||||
|
return "?";
|
||||||
|
|
||||||
|
case SampleFormat::S8:
|
||||||
|
return "8";
|
||||||
|
|
||||||
|
case SampleFormat::S16:
|
||||||
|
return "16";
|
||||||
|
|
||||||
|
case SampleFormat::S24_P32:
|
||||||
|
return "24";
|
||||||
|
|
||||||
|
case SampleFormat::S32:
|
||||||
|
return "32";
|
||||||
|
|
||||||
|
case SampleFormat::FLOAT:
|
||||||
|
return "f";
|
||||||
|
|
||||||
|
case SampleFormat::DSD:
|
||||||
|
return "dsd";
|
||||||
|
}
|
||||||
|
|
||||||
|
/* unreachable */
|
||||||
|
assert(false);
|
||||||
|
gcc_unreachable();
|
||||||
|
}
|
123
src/pcm/SampleFormat.hxx
Normal file
123
src/pcm/SampleFormat.hxx
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2003-2017 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_PCM_SAMPLE_FORMAT_HXX
|
||||||
|
#define MPD_PCM_SAMPLE_FORMAT_HXX
|
||||||
|
|
||||||
|
#include "Compiler.h"
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#if defined(WIN32) && GCC_CHECK_VERSION(4,6)
|
||||||
|
/* on WIN32, "FLOAT" is already defined, and this triggers -Wshadow */
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wshadow"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum class SampleFormat : uint8_t {
|
||||||
|
UNDEFINED = 0,
|
||||||
|
|
||||||
|
S8,
|
||||||
|
S16,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signed 24 bit integer samples, packed in 32 bit integers
|
||||||
|
* (the most significant byte is filled with the sign bit).
|
||||||
|
*/
|
||||||
|
S24_P32,
|
||||||
|
|
||||||
|
S32,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 32 bit floating point samples in the host's format. The
|
||||||
|
* range is -1.0f to +1.0f.
|
||||||
|
*/
|
||||||
|
FLOAT,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Direct Stream Digital. 1-bit samples; each frame has one
|
||||||
|
* byte (8 samples) per channel.
|
||||||
|
*/
|
||||||
|
DSD,
|
||||||
|
};
|
||||||
|
|
||||||
|
#if defined(WIN32) && GCC_CHECK_VERSION(4,6)
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the sample format is valid.
|
||||||
|
*/
|
||||||
|
static constexpr inline bool
|
||||||
|
audio_valid_sample_format(SampleFormat format)
|
||||||
|
{
|
||||||
|
switch (format) {
|
||||||
|
case SampleFormat::S8:
|
||||||
|
case SampleFormat::S16:
|
||||||
|
case SampleFormat::S24_P32:
|
||||||
|
case SampleFormat::S32:
|
||||||
|
case SampleFormat::FLOAT:
|
||||||
|
case SampleFormat::DSD:
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case SampleFormat::UNDEFINED:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr inline unsigned
|
||||||
|
sample_format_size(SampleFormat format)
|
||||||
|
{
|
||||||
|
switch (format) {
|
||||||
|
case SampleFormat::S8:
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
case SampleFormat::S16:
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
case SampleFormat::S24_P32:
|
||||||
|
case SampleFormat::S32:
|
||||||
|
case SampleFormat::FLOAT:
|
||||||
|
return 4;
|
||||||
|
|
||||||
|
case SampleFormat::DSD:
|
||||||
|
/* each frame has 8 samples per channel */
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
case SampleFormat::UNDEFINED:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
gcc_unreachable();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders a #SampleFormat enum into a string, e.g. for printing it
|
||||||
|
* in a log file.
|
||||||
|
*
|
||||||
|
* @param format a #SampleFormat enum value
|
||||||
|
* @return the string
|
||||||
|
*/
|
||||||
|
gcc_pure gcc_malloc
|
||||||
|
const char *
|
||||||
|
sample_format_to_string(SampleFormat format);
|
||||||
|
|
||||||
|
#endif
|
@@ -19,7 +19,8 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "Silence.hxx"
|
#include "Silence.hxx"
|
||||||
#include "AudioFormat.hxx"
|
#include "Traits.hxx"
|
||||||
|
#include "SampleFormat.hxx"
|
||||||
#include "util/WritableBuffer.hxx"
|
#include "util/WritableBuffer.hxx"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -29,7 +30,7 @@ PcmSilence(WritableBuffer<void> dest, SampleFormat format)
|
|||||||
{
|
{
|
||||||
uint8_t pattern = 0;
|
uint8_t pattern = 0;
|
||||||
if (format == SampleFormat::DSD)
|
if (format == SampleFormat::DSD)
|
||||||
pattern = 0x69;
|
pattern = SampleTraits<SampleFormat::DSD>::SILENCE;
|
||||||
|
|
||||||
memset(dest.data, pattern, dest.size);
|
memset(dest.data, pattern, dest.size);
|
||||||
}
|
}
|
||||||
|
@@ -21,7 +21,7 @@
|
|||||||
#define MPD_PCM_TRAITS_HXX
|
#define MPD_PCM_TRAITS_HXX
|
||||||
|
|
||||||
#include "check.h"
|
#include "check.h"
|
||||||
#include "AudioFormat.hxx"
|
#include "SampleFormat.hxx"
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
@@ -85,6 +85,11 @@ struct SampleTraits<SampleFormat::S8> {
|
|||||||
* The maximum sample value.
|
* The maximum sample value.
|
||||||
*/
|
*/
|
||||||
static constexpr value_type MAX = (sum_type(1) << (BITS - 1)) - 1;
|
static constexpr value_type MAX = (sum_type(1) << (BITS - 1)) - 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A value which represents "silence".
|
||||||
|
*/
|
||||||
|
static constexpr value_type SILENCE = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
@@ -101,6 +106,7 @@ struct SampleTraits<SampleFormat::S16> {
|
|||||||
|
|
||||||
static constexpr value_type MIN = -(sum_type(1) << (BITS - 1));
|
static constexpr value_type MIN = -(sum_type(1) << (BITS - 1));
|
||||||
static constexpr value_type MAX = (sum_type(1) << (BITS - 1)) - 1;
|
static constexpr value_type MAX = (sum_type(1) << (BITS - 1)) - 1;
|
||||||
|
static constexpr value_type SILENCE = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
@@ -117,6 +123,7 @@ struct SampleTraits<SampleFormat::S32> {
|
|||||||
|
|
||||||
static constexpr value_type MIN = -(sum_type(1) << (BITS - 1));
|
static constexpr value_type MIN = -(sum_type(1) << (BITS - 1));
|
||||||
static constexpr value_type MAX = (sum_type(1) << (BITS - 1)) - 1;
|
static constexpr value_type MAX = (sum_type(1) << (BITS - 1)) - 1;
|
||||||
|
static constexpr value_type SILENCE = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
@@ -133,6 +140,7 @@ struct SampleTraits<SampleFormat::S24_P32> {
|
|||||||
|
|
||||||
static constexpr value_type MIN = -(sum_type(1) << (BITS - 1));
|
static constexpr value_type MIN = -(sum_type(1) << (BITS - 1));
|
||||||
static constexpr value_type MAX = (sum_type(1) << (BITS - 1)) - 1;
|
static constexpr value_type MAX = (sum_type(1) << (BITS - 1)) - 1;
|
||||||
|
static constexpr value_type SILENCE = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
@@ -148,6 +156,18 @@ struct SampleTraits<SampleFormat::FLOAT> {
|
|||||||
|
|
||||||
static constexpr value_type MIN = -1;
|
static constexpr value_type MIN = -1;
|
||||||
static constexpr value_type MAX = 1;
|
static constexpr value_type MAX = 1;
|
||||||
|
static constexpr value_type SILENCE = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct SampleTraits<SampleFormat::DSD> {
|
||||||
|
typedef uint8_t value_type;
|
||||||
|
typedef value_type *pointer_type;
|
||||||
|
typedef const value_type *const_pointer_type;
|
||||||
|
|
||||||
|
static constexpr size_t SAMPLE_SIZE = sizeof(value_type);
|
||||||
|
|
||||||
|
static constexpr value_type SILENCE = 0x69;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@@ -27,6 +27,7 @@
|
|||||||
|
|
||||||
#include "PcmDither.cxx" // including the .cxx file to get inlined templates
|
#include "PcmDither.cxx" // including the .cxx file to get inlined templates
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
@@ -20,7 +20,7 @@
|
|||||||
#ifndef MPD_PCM_VOLUME_HXX
|
#ifndef MPD_PCM_VOLUME_HXX
|
||||||
#define MPD_PCM_VOLUME_HXX
|
#define MPD_PCM_VOLUME_HXX
|
||||||
|
|
||||||
#include "AudioFormat.hxx"
|
#include "SampleFormat.hxx"
|
||||||
#include "PcmBuffer.hxx"
|
#include "PcmBuffer.hxx"
|
||||||
#include "PcmDither.hxx"
|
#include "PcmDither.hxx"
|
||||||
|
|
||||||
|
@@ -238,8 +238,7 @@ try {
|
|||||||
bool done = false;
|
bool done = false;
|
||||||
|
|
||||||
while (!done) {
|
while (!done) {
|
||||||
char buffer[4096];
|
unsigned char buffer[4096];
|
||||||
unsigned char *ubuffer = (unsigned char *)buffer;
|
|
||||||
const size_t nbytes =
|
const size_t nbytes =
|
||||||
input_stream->Read(buffer, sizeof(buffer));
|
input_stream->Read(buffer, sizeof(buffer));
|
||||||
if (nbytes == 0)
|
if (nbytes == 0)
|
||||||
@@ -248,10 +247,10 @@ try {
|
|||||||
if (done) {
|
if (done) {
|
||||||
stat = yajl_complete_parse(hand);
|
stat = yajl_complete_parse(hand);
|
||||||
} else
|
} else
|
||||||
stat = yajl_parse(hand, ubuffer, nbytes);
|
stat = yajl_parse(hand, buffer, nbytes);
|
||||||
|
|
||||||
if (stat != yajl_status_ok) {
|
if (stat != yajl_status_ok) {
|
||||||
unsigned char *str = yajl_get_error(hand, 1, ubuffer, nbytes);
|
unsigned char *str = yajl_get_error(hand, 1, buffer, nbytes);
|
||||||
LogError(soundcloud_domain, (const char *)str);
|
LogError(soundcloud_domain, (const char *)str);
|
||||||
yajl_free_error(hand, str);
|
yajl_free_error(hand, str);
|
||||||
break;
|
break;
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2011-2015 Max Kellermann <max@duempel.org>,
|
* Copyright (C) 2011-2015 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
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2013-2015 Max Kellermann <max@duempel.org>
|
* Copyright (C) 2013-2015 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
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2012-2015 Max Kellermann <max@duempel.org>
|
* Copyright (C) 2012-2015 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
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2012-2015 Max Kellermann <max@duempel.org>
|
* Copyright (C) 2012-2015 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
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2009-2015 Max Kellermann <max@duempel.org>
|
* Copyright (C) 2009-2015 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
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2009-2013 Max Kellermann <max@duempel.org>
|
* Copyright (C) 2009-2013 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
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2009-2016 Max Kellermann <max@duempel.org>
|
* Copyright (C) 2009-2016 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
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2009-2015 Max Kellermann <max@duempel.org>
|
* Copyright (C) 2009-2015 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
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2009-2015 Max Kellermann <max@duempel.org>
|
* Copyright (C) 2009-2015 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
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2014-2016 Max Kellermann <max@duempel.org>
|
* Copyright (C) 2014-2016 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
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2014-2016 Max Kellermann <max@duempel.org>
|
* Copyright (C) 2014-2016 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
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2009-2013 Max Kellermann <max@duempel.org>
|
* Copyright (C) 2009-2013 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
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2013 Max Kellermann <max@duempel.org>
|
* Copyright (C) 2013 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
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2010-2016 Max Kellermann <max@duempel.org>
|
* Copyright (C) 2010-2016 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
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 Max Kellermann <max@duempel.org>
|
* Copyright (C) 2015 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
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 Max Kellermann <max@duempel.org>
|
* Copyright (C) 2015 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
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2016 Max Kellermann <max@duempel.org>
|
* Copyright (C) 2016 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
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2013-2014 Max Kellermann <max@duempel.org>
|
* Copyright (C) 2013-2014 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
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2011-2014 Max Kellermann <max@duempel.org>
|
* Copyright (C) 2011-2014 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
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2014 Max Kellermann <max@duempel.org>
|
* Copyright (C) 2014 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
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user