Commit c38e9e6d authored by Alexey Morsov's avatar Alexey Morsov

Merge commit 'v0.17.0-git050212' into alt

parents 41bd98a5 e15b4f40
......@@ -34,11 +34,13 @@ missing
mkinstalldirs
mpd
mpd.exe
mpd.service
stamp-h1
tags
*~
.#*
.stgit*
src/dsd2pcm/dsd2pcm
doc/doxygen.conf
doc/protocol.html
doc/protocol
......@@ -62,3 +64,6 @@ test/tmp
test/run_inotify
test/test_queue_priority
test/run_ntp_server
test/run_resolver
test/run_tcp_connect
test/test_pcm
......@@ -2,27 +2,66 @@ ver 0.17 (2011/??/??)
* protocol:
- support client-to-client communication
- "update" and "rescan" need only "CONTROL" permission
- new command "seekcur" for simpler seeking within current song
* input:
- cdio_paranoia: new input plugin to play audio CDs
- curl: enable CURLOPT_NETRC
- curl: non-blocking I/O
- soup: new input plugin based on libsoup
- ffmpeg: support libavformat 0.7
* decoder:
- mpg123: implement seeking
- ffmpeg: drop support for pre-0.5 ffmpeg
- ffmpeg: support libavformat 0.7
- oggflac: delete this obsolete plugin
- dsdiff: new decoder plugin
* output:
- httpd: support for streaming to a DLNA client
- openal: improve buffer cancellation
- osx: allow user to specify other audio devices
- osx: implement 32 bit playback
- raop: new output plugin
- shout: add possibility to set url
- roar: new output plugin for RoarAudio
- winmm: fail if wrong device specified instead of using default device
* mixer:
- alsa: listen for external volume changes
* playlist:
- allow references to songs outside the music directory
* state_file: add option "restore_paused"
* cue: show CUE track numbers
* allow port specification in "bind_to_address" settings
* support floating point samples
ver 0.16.7 (2012/02/04)
* input:
- ffmpeg: support libavformat 0.7
* decoder:
- ffmpeg: support libavformat 0.8, libavcodec 0.9
- ffmpeg: support all MPD tags
* output:
- httpd: fix excessive buffering
- openal: force 16 bit playback, as 8 bit doesn't work
- osx: remove sleep call from render callback
- osx: clear render buffer when there's not enough data
* fix moving after current song
ver 0.16.6 (2011/12/01)
* decoder:
- fix assertion failure when resuming streams
- ffmpeg: work around bogus channel count
* encoder:
- flac, null, wave: fix buffer corruption bug
- wave: support packed 24 bit samples
* mapper: fix the bogus "not a directory" error message
* mapper: check "x" and "r" permissions on music directory
* log: print reason for failure
* event_pipe: fix WIN32 regression
* define WINVER in ./configure
* WIN32: autodetect filesystem encoding
ver 0.16.5 (2010/??/??)
ver 0.16.5 (2011/10/09)
* configure.ac
- disable assertions in the non-debugging build
- show solaris plugin result correctly
......@@ -33,7 +72,17 @@ ver 0.16.5 (2010/??/??)
* decoder:
- ffmpeg: higher precision timestamps
- ffmpeg: don't require key frame for seeking
- fix CUE track seeking
* output:
- openal: auto-fallback to mono if channel count is unsupported
* player:
- make seeking to CUE track more reliable
- the "seek" command works when MPD is stopped
- restore song position from state file (bug fix)
- fix crash that sometimes occurred when audio device fails on startup
- fix absolute path support in playlists
* WIN32: close sockets properly
* install systemd service file if systemd is available
ver 0.16.4 (2011/09/01)
......
......@@ -16,7 +16,7 @@ if test -n "$AM_FORCE_VERSION"
then
AM_VERSIONS="$AM_FORCE_VERSION"
else
AM_VERSIONS='1.11 1.10'
AM_VERSIONS='1.11'
fi
if test -n "$AC_FORCE_VERSION"
then
......
......@@ -481,7 +481,7 @@ FILE_VERSION_FILTER =
# The QUIET tag can be used to turn on/off the messages that are generated
# by doxygen. Possible values are YES and NO. If left blank NO is used.
QUIET = NO
QUIET = YES
# The WARNINGS tag can be used to turn on/off the warning messages that are
# generated by doxygen. Possible values are YES and NO. If left blank
......
......@@ -83,6 +83,10 @@ This specifies which address mpd binds to and listens on. Multiple
bind_to_address parameters may be specified. The default is "any", which binds
to all available addresses.
You can set a port that is different from the global port setting,
e.g. "localhost:6602". IPv6 addresses must be enclosed in square
brackets if you want to configure a port, e.g. "[::1]:6602".
To bind to a Unix domain socket, specify an absolute path. For a
system-wide MPD, we suggest the path "\fB/var/run/mpd/socket\fP".
.TP
......
......@@ -737,7 +737,11 @@
Sets the replay gain mode. One of
<parameter>off</parameter>,
<parameter>track</parameter>,
<parameter>album</parameter>.
<parameter>album</parameter>,
<parameter>auto</parameter><footnote
id="replay_gain_auto_since_0_16">
<simpara>added in MPD 0.16</simpara>
</footnote>.
</para>
<para>
Changing the mode during playback may take several
......@@ -874,6 +878,23 @@
</para>
</listitem>
</varlistentry>
<varlistentry id="command_seekcur">
<term>
<cmdsynopsis>
<command>seekcur</command>
<arg choice="req"><replaceable>TIME</replaceable></arg>
</cmdsynopsis>
</term>
<listitem>
<para>
Seeks to the position <varname>TIME</varname> within the
current song. If prefixed by '+' or '-', then the time
is relative to the current playing position.
</para>
</listitem>
</varlistentry>
<varlistentry id="command_stop">
<term>
<cmdsynopsis>
......@@ -1781,6 +1802,7 @@ OK
<term>
<cmdsynopsis>
<command>disableoutput</command>
<arg choice="req"><replaceable>ID</replaceable></arg>
</cmdsynopsis>
</term>
<listitem>
......@@ -1793,6 +1815,7 @@ OK
<term>
<cmdsynopsis>
<command>enableoutput</command>
<arg choice="req"><replaceable>ID</replaceable></arg>
</cmdsynopsis>
</term>
<listitem>
......
......@@ -332,7 +332,8 @@ cd mpd-version</programlisting>
<varname>24_3</varname> (signed 24 bit integer
samples, no padding, 3 bytes per sample),
<varname>32</varname> (signed 32 bit integer
samples).
samples), <varname>f</varname> (32 bit floating
point, -1.0 to 1.0).
</para>
</entry>
</row>
......@@ -737,6 +738,37 @@ cd mpd-version</programlisting>
<title>Decoder plugins</title>
<section>
<title><varname>dsdiff</varname></title>
<para>
Decodes DFF files containing DSDIFF data (e.g. SACD rips).
</para>
<informaltable>
<tgroup cols="2">
<thead>
<row>
<entry>Setting</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>
<varname>lsbitfirst</varname>
<parameter>yes|no</parameter>
</entry>
<entry>
Decode the least significant bit first. Default is
"no".
</entry>
</row>
</tbody>
</tgroup>
</informaltable>
</section>
<section>
<title><varname>mikmod</varname></title>
<para>
......
......@@ -39,8 +39,8 @@ if test x$enable_aac = xyes; then
oldcflags=$CFLAGS
oldlibs=$LIBS
oldcppflags=$CPPFLAGS
CFLAGS="$CFLAGS $MPD_CFLAGS $FAAD_CFLAGS -I."
LIBS="$LIBS $MPD_LIBS $FAAD_LIBS"
CFLAGS="$CFLAGS $FAAD_CFLAGS -I."
LIBS="$LIBS $FAAD_LIBS"
CPPFLAGS=$CFLAGS
AC_CHECK_HEADER(faad.h,,enable_aac=no)
if test x$enable_aac = xyes; then
......@@ -50,10 +50,10 @@ if test x$enable_aac = xyes; then
AC_CHECK_DECL(faacDecInit2,,enable_aac=no,[#include <faad.h>])
fi
if test x$enable_aac = xyes; then
AC_CHECK_LIB(faad,faacDecInit2,[MPD_LIBS="$MPD_LIBS $FAAD_LIBS";MPD_CFLAGS="$MPD_CFLAGS $FAAD_CFLAGS"],enable_aac=no)
AC_CHECK_LIB(faad,faacDecInit2,,enable_aac=no)
if test x$enable_aac = xno; then
enable_aac=yes
AC_CHECK_LIB(faad,NeAACDecInit2,[MPD_LIBS="$MPD_LIBS $FAAD_LIBS";MPD_CFLAGS="$MPD_CFLAGS $FAAD_CFLAGS"],enable_aac=no)
AC_CHECK_LIB(faad,NeAACDecInit2,,enable_aac=no)
fi
fi
if test x$enable_aac = xyes; then
......@@ -131,8 +131,8 @@ if test x$enable_aac = xyes; then
oldcflags=$CFLAGS
oldlibs=$LIBS
oldcppflags=$CPPFLAGS
CFLAGS="$CFLAGS $MPD_CFLAGS $FAAD_CFLAGS -Werror"
LIBS="$LIBS $MPD_LIBS $FAAD_LIBS"
CFLAGS="$CFLAGS $FAAD_CFLAGS -Werror"
LIBS="$LIBS $FAAD_LIBS"
CPPFLAGS=$CFLAGS
AC_MSG_CHECKING(for broken libfaad headers)
......@@ -188,5 +188,11 @@ if test x$enable_aac = xyes; then
CPPFLAGS=$oldcppflags
else
enable_mp4=no
FAAD_CFLAGS=""
FAAD_LIBS=""
fi
AC_SUBST(FAAD_CFLAGS)
AC_SUBST(FAAD_LIBS)
])
......@@ -20,7 +20,7 @@ AC_DEFUN([STRUCT_UCRED],[
mpd_cv_have_struct_ucred=no)
if test x$mpd_cv_have_struct_ucred = xyes; then
# :(
MPD_CFLAGS="$MPD_CFLAGS -D_GNU_SOURCE"
CFLAGS="$CFLAGS -D_GNU_SOURCE"
fi
fi
])
......
[Unit]
Description=Music Player Daemon
After=sound.target
[Service]
ExecStart=@prefix@/bin/mpd --no-daemon
[Install]
WantedBy=multi-user.target
......@@ -28,6 +28,7 @@
/**
* The GLib quark used for errors reported by this library.
*/
G_GNUC_CONST
static inline GQuark
audio_format_quark(void)
{
......
......@@ -18,7 +18,7 @@
*/
#include "config.h"
#include "audio.h"
#include "audio_config.h"
#include "audio_format.h"
#include "audio_parser.h"
#include "output_internal.h"
......
......@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPD_AUDIO_H
#define MPD_AUDIO_H
#ifndef MPD_AUDIO_CONFIG_H
#define MPD_AUDIO_CONFIG_H
#include <stdbool.h>
......
......@@ -28,6 +28,25 @@
#define REVERSE_ENDIAN_SUFFIX "_be"
#endif
void
audio_format_mask_apply(struct audio_format *af,
const struct audio_format *mask)
{
assert(audio_format_valid(af));
assert(audio_format_mask_valid(mask));
if (mask->sample_rate != 0)
af->sample_rate = mask->sample_rate;
if (mask->format != SAMPLE_FORMAT_UNDEFINED)
af->format = mask->format;
if (mask->channels != 0)
af->channels = mask->channels;
assert(audio_format_valid(af));
}
const char *
sample_format_to_string(enum sample_format format)
{
......@@ -49,6 +68,9 @@ sample_format_to_string(enum sample_format format)
case SAMPLE_FORMAT_S32:
return "32";
case SAMPLE_FORMAT_FLOAT:
return "f";
}
/* unreachable */
......
......@@ -20,6 +20,7 @@
#ifndef MPD_AUDIO_FORMAT_H
#define MPD_AUDIO_FORMAT_H
#include <glib.h>
#include <stdint.h>
#include <stdbool.h>
#include <assert.h>
......@@ -42,8 +43,16 @@ enum sample_format {
SAMPLE_FORMAT_S24_P32,
SAMPLE_FORMAT_S32,
/**
* 32 bit floating point samples in the host's format. The
* range is -1.0f to +1.0f.
*/
SAMPLE_FORMAT_FLOAT,
};
static const unsigned MAX_CHANNELS = 8;
/**
* This structure describes the format of a raw PCM stream.
*/
......@@ -72,7 +81,7 @@ struct audio_format {
* nonzero, then samples are stored in the reverse host byte
* order.
*/
uint8_t reverse_endian;
bool reverse_endian;
};
/**
......@@ -91,7 +100,7 @@ static inline void audio_format_clear(struct audio_format *af)
af->sample_rate = 0;
af->format = SAMPLE_FORMAT_UNDEFINED;
af->channels = 0;
af->reverse_endian = 0;
af->reverse_endian = false;
}
/**
......@@ -105,7 +114,7 @@ static inline void audio_format_init(struct audio_format *af,
af->sample_rate = sample_rate;
af->format = (uint8_t)format;
af->channels = channels;
af->reverse_endian = 0;
af->reverse_endian = false;
}
/**
......@@ -165,6 +174,7 @@ audio_valid_sample_format(enum sample_format format)
case SAMPLE_FORMAT_S24:
case SAMPLE_FORMAT_S24_P32:
case SAMPLE_FORMAT_S32:
case SAMPLE_FORMAT_FLOAT:
return true;
case SAMPLE_FORMAT_UNDEFINED:
......@@ -180,13 +190,14 @@ audio_valid_sample_format(enum sample_format format)
static inline bool
audio_valid_channel_count(unsigned channels)
{
return channels >= 1 && channels <= 8;
return channels >= 1 && channels <= MAX_CHANNELS;
}
/**
* Returns false if the format is not valid for playback with MPD.
* This function performs some basic validity checks.
*/
G_GNUC_PURE
static inline bool audio_format_valid(const struct audio_format *af)
{
return audio_valid_sample_rate(af->sample_rate) &&
......@@ -198,6 +209,7 @@ static inline bool audio_format_valid(const struct audio_format *af)
* Returns false if the format mask is not valid for playback with
* MPD. This function performs some basic validity checks.
*/
G_GNUC_PURE
static inline bool audio_format_mask_valid(const struct audio_format *af)
{
return (af->sample_rate == 0 ||
......@@ -216,31 +228,15 @@ static inline bool audio_format_equals(const struct audio_format *a,
a->reverse_endian == b->reverse_endian;
}
static inline void
void
audio_format_mask_apply(struct audio_format *af,
const struct audio_format *mask)
{
assert(audio_format_valid(af));
assert(audio_format_mask_valid(mask));
if (mask->sample_rate != 0)
af->sample_rate = mask->sample_rate;
const struct audio_format *mask);
if (mask->format != SAMPLE_FORMAT_UNDEFINED)
af->format = mask->format;
if (mask->channels != 0)
af->channels = mask->channels;
assert(audio_format_valid(af));
}
/**
* Returns the size of each (mono) sample in bytes.
*/
static inline unsigned audio_format_sample_size(const struct audio_format *af)
G_GNUC_CONST
static inline unsigned
sample_format_size(enum sample_format format)
{
switch (af->format) {
switch (format) {
case SAMPLE_FORMAT_S8:
return 1;
......@@ -252,18 +248,30 @@ static inline unsigned audio_format_sample_size(const struct audio_format *af)
case SAMPLE_FORMAT_S24_P32:
case SAMPLE_FORMAT_S32:
case SAMPLE_FORMAT_FLOAT:
return 4;
case SAMPLE_FORMAT_UNDEFINED:
break;
return 0;
}
assert(false);
return 0;
}
/**
* Returns the size of each (mono) sample in bytes.
*/
G_GNUC_PURE
static inline unsigned audio_format_sample_size(const struct audio_format *af)
{
return sample_format_size((enum sample_format)af->format);
}
/**
* Returns the size of each full frame in bytes.
*/
G_GNUC_PURE
static inline unsigned
audio_format_frame_size(const struct audio_format *af)
{
......@@ -274,6 +282,7 @@ audio_format_frame_size(const struct audio_format *af)
* Returns the floating point factor which converts a time span to a
* storage size in bytes.
*/
G_GNUC_PURE
static inline double audio_format_time_to_size(const struct audio_format *af)
{
return af->sample_rate * audio_format_frame_size(af);
......@@ -286,6 +295,7 @@ static inline double audio_format_time_to_size(const struct audio_format *af)
* @param format a #sample_format enum value
* @return the string
*/
G_GNUC_PURE G_GNUC_MALLOC
const char *
sample_format_to_string(enum sample_format format);
......@@ -297,6 +307,7 @@ sample_format_to_string(enum sample_format format);
* @param s a buffer to print into
* @return the string, or NULL if the #audio_format object is invalid
*/
G_GNUC_PURE G_GNUC_MALLOC
const char *
audio_format_to_string(const struct audio_format *af,
struct audio_format_string *s);
......
......@@ -81,6 +81,12 @@ parse_sample_format(const char *src, bool mask,
return true;
}
if (*src == 'f') {
*sample_format_r = SAMPLE_FORMAT_FLOAT;
*endptr_r = src + 1;
return true;
}
value = strtoul(src, &endptr, 10);
if (endptr == src) {
g_set_error(error_r, audio_parser_quark(), 0,
......
......@@ -59,7 +59,7 @@ void
client_message_copy(struct client_message *dest,
const struct client_message *src);
G_GNUC_MALLOC G_GNUC_PURE
G_GNUC_MALLOC
struct client_message *
client_message_dup(const struct client_message *src);
......
......@@ -194,8 +194,6 @@ parse_cmdline(int argc, char **argv, struct options *options,
if(g_file_test(system_path,
G_FILE_TEST_IS_REGULAR)) {
ret = config_read_file(system_path,error_r);
g_free(system_path);
g_free(&system_config_dirs);
break;
}
++i;;
......
......@@ -45,6 +45,7 @@
#include "db_error.h"
#include "db_print.h"
#include "db_selection.h"
#include "db_lock.h"
#include "tag.h"
#include "client.h"
#include "client_idle.h"
......@@ -1532,6 +1533,21 @@ handle_seekid(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
}
static enum command_return
handle_seekcur(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
{
const char *p = argv[1];
bool relative = *p == '+' || *p == '-';
int seek_time;
if (!check_int(client, &seek_time, p, check_integer, p))
return COMMAND_RETURN_ERROR;
enum playlist_result result =
playlist_seek_current(&g_playlist, client->player_control,
seek_time, relative);
return print_playlist_result(client, result);
}
static enum command_return
handle_listallinfo(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
{
const char *directory = "";
......@@ -1877,8 +1893,10 @@ handle_sticker_song(struct client *client, int argc, char *argv[])
.name = argv[4],
};
db_lock();
directory = db_get_directory(argv[3]);
if (directory == NULL) {
db_unlock();
command_error(client, ACK_ERROR_NO_EXIST,
"no such directory");
return COMMAND_RETURN_ERROR;
......@@ -1886,6 +1904,7 @@ handle_sticker_song(struct client *client, int argc, char *argv[])
success = sticker_song_find(directory, data.name,
sticker_song_find_print_cb, &data);
db_unlock();
if (!success) {
command_error(client, ACK_ERROR_SYSTEM,
"failed to set search sticker database");
......@@ -2159,6 +2178,7 @@ static const struct command commands[] = {
{ "save", PERMISSION_CONTROL, 1, 1, handle_save },
{ "search", PERMISSION_READ, 2, -1, handle_search },
{ "seek", PERMISSION_CONTROL, 2, 2, handle_seek },
{ "seekcur", PERMISSION_CONTROL, 1, 1, handle_seekcur },
{ "seekid", PERMISSION_CONTROL, 2, 2, handle_seekid },
{ "sendmessage", PERMISSION_CONTROL, 2, 2, handle_send_message },
{ "setvol", PERMISSION_CONTROL, 1, 1, handle_setvol },
......
......@@ -19,7 +19,6 @@
#include "config.h"
#include "crossfade.h"
#include "pcm_mix.h"
#include "chunk.h"
#include "audio_format.h"
#include "tag.h"
......
......@@ -92,7 +92,9 @@ db_get_directory(const char *name)
if (name == NULL)
return music_root;
return directory_lookup_directory(music_root, name);
struct directory *directory =
directory_lookup_directory(music_root, name);
return directory;
}
struct song *
......
......@@ -50,6 +50,9 @@ db_finish(void);
struct directory *
db_get_root(void);
/**
* Caller must lock the #db_mutex.
*/
gcc_nonnull(1)
struct directory *
db_get_directory(const char *name);
......
......@@ -24,6 +24,7 @@
#include "db_selection.h"
#include "db_visitor.h"
#include "db_save.h"
#include "db_lock.h"
#include "conf.h"
#include "glib_compat.h"
#include "directory.h"
......@@ -58,7 +59,11 @@ simple_db_lookup_directory(const struct simple_db *db, const char *uri)
assert(db->root != NULL);
assert(uri != NULL);
return directory_lookup_directory(db->root, uri);
db_lock();
struct directory *directory =
directory_lookup_directory(db->root, uri);
db_unlock();
return directory;
}
static struct db *
......@@ -68,7 +73,7 @@ simple_db_init(const struct config_param *param, GError **error_r)
db_base_init(&db->base, &simple_db_plugin);
GError *error = NULL;
db->path = config_dup_block_path(param, "path", error_r);
db->path = config_dup_block_path(param, "path", &error);
if (db->path == NULL) {
g_free(db);
if (error != NULL)
......@@ -199,7 +204,7 @@ simple_db_open(struct db *_db, G_GNUC_UNUSED GError **error_r)
{
struct simple_db *db = (struct simple_db *)_db;
db->root = directory_new("", NULL);
db->root = directory_new_root();
db->mtime = 0;
GError *error = NULL;
......@@ -212,7 +217,7 @@ simple_db_open(struct db *_db, G_GNUC_UNUSED GError **error_r)
if (!simple_db_check(db, error_r))
return false;
db->root = directory_new("", NULL);
db->root = directory_new_root();
}
return true;
......@@ -235,7 +240,9 @@ simple_db_get_song(struct db *_db, const char *uri, GError **error_r)
assert(db->root != NULL);
db_lock();
struct song *song = directory_lookup_song(db->root, uri);
db_unlock();
if (song == NULL)
g_set_error(error_r, db_quark(), DB_NOT_FOUND,
"No such song: %s", uri);
......@@ -266,8 +273,11 @@ simple_db_visit(struct db *_db, const struct db_selection *selection,
!visitor->directory(directory, ctx, error_r))
return false;
return directory_walk(directory, selection->recursive,
visitor, ctx, error_r);
db_lock();
bool ret = directory_walk(directory, selection->recursive,
visitor, ctx, error_r);
db_unlock();
return ret;
}
const struct db_plugin simple_db_plugin = {
......@@ -297,13 +307,16 @@ simple_db_save(struct db *_db, GError **error_r)
struct simple_db *db = (struct simple_db *)_db;
struct directory *music_root = db->root;
db_lock();
g_debug("removing empty directories from DB");
directory_prune_empty(music_root);
g_debug("sorting DB");
directory_sort(music_root);
db_unlock();
g_debug("writing DB");
FILE *fp = fopen(db->path, "w");
......
......@@ -17,37 +17,11 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPD_DIRVEC_H
#define MPD_DIRVEC_H
#include "config.h"
#include "db_lock.h"
#include <stddef.h>
GStaticMutex db_mutex = G_STATIC_MUTEX_INIT;
struct dirvec {
struct directory **base;
size_t nr;
};
void dirvec_init(void);
void dirvec_deinit(void);
void dirvec_sort(struct dirvec *dv);
struct directory *dirvec_find(const struct dirvec *dv, const char *path);
int dirvec_delete(struct dirvec *dv, struct directory *del);
void dirvec_add(struct dirvec *dv, struct directory *add);
static inline void
dirvec_clear(struct dirvec *dv)
{
dv->nr = 0;
}
void dirvec_destroy(struct dirvec *dv);
int dirvec_for_each(const struct dirvec *dv,
int (*fn)(struct directory *, void *), void *arg);
#endif /* DIRVEC_H */
#ifndef NDEBUG
GThread *db_mutex_holder;
#endif
/*
* Copyright (C) 2003-2011 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.
*/
/** \file
*
* Support for locking data structures from the database, for safe
* multi-threading.
*/
#ifndef MPD_DB_LOCK_H
#define MPD_DB_LOCK_H
#include "check.h"
#include <glib.h>
#include <assert.h>
#include <stdbool.h>
extern GStaticMutex db_mutex;
#ifndef NDEBUG
extern GThread *db_mutex_holder;
/**
* Does the current thread hold the database lock?
*/
G_GNUC_PURE
static inline bool
holding_db_lock(void)
{
return db_mutex_holder == g_thread_self();
}
#endif
/**
* Obtain the global database lock. This is needed before
* dereferencing a #song or #directory. It is not recursive.
*/
static inline void
db_lock(void)
{
assert(!holding_db_lock());
g_static_mutex_lock(&db_mutex);
assert(db_mutex_holder == NULL);
#ifndef NDEBUG
db_mutex_holder = g_thread_self();
#endif
}
/**
* Release the global database lock.
*/
static inline void
db_unlock(void)
{
assert(holding_db_lock());
#ifndef NDEBUG
db_mutex_holder = NULL;
#endif
g_static_mutex_unlock(&db_mutex);
}
#endif
......@@ -73,21 +73,35 @@ print_visitor_song_info(struct song *song, void *data,
return true;
}
static void
print_playlist_in_directory(struct client *client,
const struct directory *directory,
const char *name_utf8)
{
if (directory_is_root(directory))
client_printf(client, "playlist: %s\n", name_utf8);
else
client_printf(client, "playlist: %s/%s\n",
directory_get_path(directory), name_utf8);
}
static bool
print_visitor_playlist(const struct playlist_metadata *playlist, void *ctx,
print_visitor_playlist(const struct playlist_metadata *playlist,
const struct directory *directory, void *ctx,
G_GNUC_UNUSED GError **error_r)
{
struct client *client = ctx;
client_printf(client, "playlist: %s\n", playlist->name);
print_playlist_in_directory(client, directory, playlist->name);
return true;
}
static bool
print_visitor_playlist_info(const struct playlist_metadata *playlist,
const struct directory *directory,
void *ctx, G_GNUC_UNUSED GError **error_r)
{
struct client *client = ctx;
client_printf(client, "playlist: %s\n", playlist->name);
print_playlist_in_directory(client, directory, playlist->name);
#ifndef G_OS_WIN32
struct tm tm;
......
......@@ -19,6 +19,7 @@
#include "config.h"
#include "db_save.h"
#include "db_lock.h"
#include "directory.h"
#include "directory_save.h"
#include "song.h"
......@@ -169,7 +170,9 @@ db_load_internal(FILE *fp, struct directory *music_root, GError **error)
g_debug("reading DB");
db_lock();
success = directory_load(fp, music_root, buffer, error);
db_unlock();
g_string_free(buffer, true);
return success;
......
......@@ -43,9 +43,11 @@ struct db_visitor {
/**
* Visit a playlist. Optional method.
*
* @param directory the directory the playlist resides in
* @return true to continue the operation, false on error (set error_r)
*/
bool (*playlist)(const struct playlist_metadata *playlist, void *ctx,
bool (*playlist)(const struct playlist_metadata *playlist,
const struct directory *directory, void *ctx,
GError **error_r);
};
......
......@@ -17,35 +17,9 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPD_SONGVEC_H
#define MPD_SONGVEC_H
#ifndef MPD_DECODER_DSDIFF_H
#define MPD_DECODER_DSDIFF_H
#include <stddef.h>
extern const struct decoder_plugin dsdiff_decoder_plugin;
struct songvec {
struct song **base;
size_t nr;
};
void songvec_init(void);
void songvec_deinit(void);
void songvec_sort(struct songvec *sv);
struct song *
songvec_find(const struct songvec *sv, const char *uri);
int
songvec_delete(struct songvec *sv, const struct song *del);
void
songvec_add(struct songvec *sv, struct song *add);
void songvec_destroy(struct songvec *sv);
int
songvec_for_each(const struct songvec *sv,
int (*fn)(struct song *, void *), void *arg);
#endif /* SONGVEC_H */
#endif
......@@ -102,6 +102,7 @@ flac_convert(void *dest,
break;
case SAMPLE_FORMAT_S24:
case SAMPLE_FORMAT_FLOAT:
case SAMPLE_FORMAT_UNDEFINED:
/* unreachable */
assert(false);
......
......@@ -94,6 +94,12 @@ mp4_read(void *user_data, void *buffer, uint32_t length)
{
struct mp4ff_input_stream *mis = user_data;
if (length == 0)
/* libmp4ff is known to attempt to read 0 bytes - make
this a special case, because the input_stream API
would not allow this */
return 0;
return decoder_read(mis->decoder, mis->input_stream, buffer, length);
}
......
......@@ -23,6 +23,7 @@
#include <glib.h>
#include <unistd.h>
#include <stdio.h> /* for SEEK_SET */
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "pcm"
......@@ -30,21 +31,35 @@
static void
pcm_stream_decode(struct decoder *decoder, struct input_stream *is)
{
static const struct audio_format audio_format = {
static const struct audio_format host_audio_format = {
.sample_rate = 44100,
.format = SAMPLE_FORMAT_S16,
.channels = 2,
};
static const struct audio_format reverse_audio_format = {
.sample_rate = 44100,
.format = SAMPLE_FORMAT_S16,
.channels = 2,
.reverse_endian = true,
};
const struct audio_format *audio_format =
(is->mime == NULL ||
strcmp(is->mime, "audio/x-mpd-cdda-pcm-reverse") != 0)
? &host_audio_format
: &reverse_audio_format;
GError *error = NULL;
enum decoder_command cmd;
double time_to_size = audio_format_time_to_size(&audio_format);
double time_to_size = audio_format_time_to_size(audio_format);
float total_time = -1;
if (is->size >= 0)
total_time = is->size / time_to_size;
decoder_initialized(decoder, &audio_format, is->seekable, total_time);
decoder_initialized(decoder, audio_format, is->seekable, total_time);
do {
char buffer[4096];
......@@ -79,6 +94,10 @@ pcm_stream_decode(struct decoder *decoder, struct input_stream *is)
static const char *const pcm_mime_types[] = {
/* for streams obtained by the cdio_paranoia input plugin */
"audio/x-mpd-cdda-pcm",
/* same as above, but with reverse byte order */
"audio/x-mpd-cdda-pcm-reverse",
NULL
};
......
......@@ -111,12 +111,11 @@ static void
format_samples_float(G_GNUC_UNUSED int bytes_per_sample, void *buffer,
uint32_t count)
{
int32_t *dst = buffer;
float *src = buffer;
assert_static(sizeof(*dst) <= sizeof(*src));
float *p = buffer;
while (count--) {
*dst++ = (int32_t)(*src++ + 0.5f);
*p /= (1 << 23);
++p;
}
}
......@@ -127,7 +126,7 @@ static enum sample_format
wavpack_bits_to_sample_format(bool is_float, int bytes_per_sample)
{
if (is_float)
return SAMPLE_FORMAT_S24_P32;
return SAMPLE_FORMAT_FLOAT;
switch (bytes_per_sample) {
case 1:
......@@ -176,7 +175,7 @@ wavpack_decode(struct decoder *decoder, WavpackContext *wpc, bool can_seek)
return;
}
if ((WavpackGetMode(wpc) & MODE_FLOAT) == MODE_FLOAT) {
if (is_float) {
format_samples = format_samples_float;
} else {
format_samples = format_samples_int;
......
......@@ -21,7 +21,7 @@
#include "decoder_api.h"
#include "decoder_internal.h"
#include "decoder_control.h"
#include "audio.h"
#include "audio_config.h"
#include "song.h"
#include "buffer.h"
#include "pipe.h"
......@@ -56,6 +56,9 @@ decoder_initialized(struct decoder *decoder,
dc->in_audio_format = *audio_format;
getOutputAudioFormat(audio_format, &dc->out_audio_format);
/* force host byte order, even if the decoder supplies reverse
endian */
dc->out_audio_format.reverse_endian = false;
dc->seekable = seekable;
dc->total_time = total_time;
......@@ -76,15 +79,70 @@ decoder_initialized(struct decoder *decoder,
&af_string));
}
enum decoder_command decoder_get_command(G_GNUC_UNUSED struct decoder * decoder)
/**
* Checks if we need an "initial seek". If so, then the initial seek
* is prepared, and the function returns true.
*/
G_GNUC_PURE
static bool
decoder_prepare_initial_seek(struct decoder *decoder)
{
const struct decoder_control *dc = decoder->dc;
assert(dc->pipe != NULL);
if (decoder->initial_seek_running)
/* initial seek has already begun - override any other
command */
return true;
if (decoder->initial_seek_pending) {
if (!dc->seekable) {
/* seeking is not possible */
decoder->initial_seek_pending = false;
return false;
}
if (dc->command == DECODE_COMMAND_NONE) {
/* begin initial seek */
decoder->initial_seek_pending = false;
decoder->initial_seek_running = true;
return true;
}
/* skip initial seek when there's another command
(e.g. STOP) */
decoder->initial_seek_pending = false;
}
return false;
}
/**
* Returns the current decoder command. May return a "virtual"
* synthesized command, e.g. to seek to the beginning of the CUE
* track.
*/
G_GNUC_PURE
static enum decoder_command
decoder_get_virtual_command(struct decoder *decoder)
{
const struct decoder_control *dc = decoder->dc;
assert(dc->pipe != NULL);
if (decoder_prepare_initial_seek(decoder))
return DECODE_COMMAND_SEEK;
return dc->command;
}
enum decoder_command
decoder_get_command(struct decoder *decoder)
{
return decoder_get_virtual_command(decoder);
}
void
decoder_command_finished(struct decoder *decoder)
{
......@@ -92,11 +150,24 @@ decoder_command_finished(struct decoder *decoder)
decoder_lock(dc);
assert(dc->command != DECODE_COMMAND_NONE);
assert(dc->command != DECODE_COMMAND_NONE ||
decoder->initial_seek_running);
assert(dc->command != DECODE_COMMAND_SEEK ||
decoder->initial_seek_running ||
dc->seek_error || decoder->seeking);
assert(dc->pipe != NULL);
if (decoder->initial_seek_running) {
assert(!decoder->seeking);
assert(decoder->chunk == NULL);
assert(music_pipe_empty(dc->pipe));
decoder->initial_seek_running = false;
decoder->timestamp = dc->start_ms / 1000.;
decoder_unlock(dc);
return;
}
if (decoder->seeking) {
decoder->seeking = false;
......@@ -121,9 +192,13 @@ double decoder_seek_where(G_GNUC_UNUSED struct decoder * decoder)
{
const struct decoder_control *dc = decoder->dc;
assert(dc->command == DECODE_COMMAND_SEEK);
assert(dc->pipe != NULL);
if (decoder->initial_seek_running)
return dc->start_ms / 1000.;
assert(dc->command == DECODE_COMMAND_SEEK);
decoder->seeking = true;
return dc->seek_where;
......@@ -133,9 +208,17 @@ void decoder_seek_error(struct decoder * decoder)
{
struct decoder_control *dc = decoder->dc;
assert(dc->command == DECODE_COMMAND_SEEK);
assert(dc->pipe != NULL);
if (decoder->initial_seek_running) {
/* d'oh, we can't seek to the sub-song start position,
what now? - no idea, ignoring the problem for now. */
decoder->initial_seek_running = false;
return;
}
assert(dc->command == DECODE_COMMAND_SEEK);
dc->seek_error = true;
decoder->seeking = false;
......@@ -289,7 +372,7 @@ decoder_data(struct decoder *decoder,
assert(length % audio_format_frame_size(&dc->in_audio_format) == 0);
decoder_lock(dc);
cmd = dc->command;
cmd = decoder_get_virtual_command(decoder);
decoder_unlock(dc);
if (cmd == DECODE_COMMAND_STOP || cmd == DECODE_COMMAND_SEEK ||
......@@ -376,8 +459,8 @@ decoder_data(struct decoder *decoder,
decoder->timestamp += (double)nbytes /
audio_format_time_to_size(&dc->out_audio_format);
if (dc->song->end_ms > 0 &&
decoder->timestamp >= dc->song->end_ms / 1000.0)
if (dc->end_ms > 0 &&
decoder->timestamp >= dc->end_ms / 1000.0)
/* the end of this range has been reached:
stop decoding */
return DECODE_COMMAND_STOP;
......@@ -407,6 +490,14 @@ decoder_tag(G_GNUC_UNUSED struct decoder *decoder, struct input_stream *is,
update_stream_tag(decoder, is);
/* check if we're seeking */
if (decoder_prepare_initial_seek(decoder))
/* during initial seek, no music chunk must be created
until seeking is finished; skip the rest of the
function here */
return DECODE_COMMAND_SEEK;
/* send tag to music pipe */
if (decoder->stream_tag != NULL) {
......
......@@ -96,6 +96,7 @@ dc_command_async(struct decoder_control *dc, enum decoder_command cmd)
void
dc_start(struct decoder_control *dc, struct song *song,
unsigned start_ms, unsigned end_ms,
struct music_buffer *buffer, struct music_pipe *pipe)
{
assert(song != NULL);
......@@ -104,6 +105,8 @@ dc_start(struct decoder_control *dc, struct song *song,
assert(music_pipe_empty(pipe));
dc->song = song;
dc->start_ms = start_ms;
dc->end_ms = end_ms;
dc->buffer = buffer;
dc->pipe = pipe;
dc_command(dc, DECODE_COMMAND_START);
......
......@@ -85,6 +85,23 @@ struct decoder_control {
*/
const struct song *song;
/**
* The initial seek position (in milliseconds), e.g. to the
* start of a sub-track described by a CUE file.
*
* This attribute is set by dc_start().
*/
unsigned start_ms;
/**
* The decoder will stop when it reaches this position (in
* milliseconds). 0 means don't stop before the end of the
* file.
*
* This attribute is set by dc_start().
*/
unsigned end_ms;
float total_time;
/** the #music_chunk allocator */
......@@ -229,11 +246,14 @@ decoder_current_song(const struct decoder_control *dc)
*
* @param the decoder
* @param song the song to be decoded
* @param start_ms see #decoder_control
* @param end_ms see #decoder_control
* @param pipe the pipe which receives the decoded chunks (owned by
* the caller)
*/
void
dc_start(struct decoder_control *dc, struct song *song,
unsigned start_ms, unsigned end_ms,
struct music_buffer *buffer, struct music_pipe *pipe);
void
......
......@@ -36,6 +36,25 @@ struct decoder {
*/
double timestamp;
/**
* Is the initial seek (to the start position of the sub-song)
* pending, or has it been performed already?
*/
bool initial_seek_pending;
/**
* Is the initial seek currently running? During this time,
* the decoder command is SEEK. This flag is set by
* decoder_get_virtual_command(), when the virtual SEEK
* command is generated for the first time.
*/
bool initial_seek_running;
/**
* This flag is set by decoder_seek_where(), and checked by
* decoder_command_finished(). It is used to clean up after
* seeking.
*/
bool seeking;
/**
......
......@@ -24,6 +24,7 @@
#include "conf.h"
#include "mpd_error.h"
#include "decoder/pcm_decoder_plugin.h"
#include "decoder/dsdiff_decoder_plugin.h"
#include <glib.h>
......@@ -70,6 +71,7 @@ const struct decoder_plugin *const decoder_plugins[] = {
#ifdef HAVE_AUDIOFILE
&audiofile_decoder_plugin,
#endif
&dsdiff_decoder_plugin,
#ifdef HAVE_FAAD
&faad_decoder_plugin,
#endif
......
......@@ -37,6 +37,7 @@
#include <glib.h>
#include <unistd.h>
#include <stdio.h> /* for SEEK_SET */
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "decoder_thread"
......@@ -380,6 +381,8 @@ decoder_run_song(struct decoder_control *dc,
{
struct decoder decoder = {
.dc = dc,
.initial_seek_pending = dc->start_ms > 0,
.initial_seek_running = false,
};
int ret;
......
......@@ -20,8 +20,11 @@
#include "config.h"
#include "directory.h"
#include "song.h"
#include "song_sort.h"
#include "path.h"
#include "util/list_sort.h"
#include "db_visitor.h"
#include "db_lock.h"
#include <glib.h>
......@@ -40,6 +43,8 @@ directory_new(const char *path, struct directory *parent)
directory = g_malloc0(sizeof(*directory) -
sizeof(directory->path) + pathlen + 1);
INIT_LIST_HEAD(&directory->children);
INIT_LIST_HEAD(&directory->songs);
directory->parent = parent;
memcpy(directory->path, path, pathlen + 1);
......@@ -53,84 +58,167 @@ directory_free(struct directory *directory)
{
playlist_vector_deinit(&directory->playlists);
for (unsigned i = 0; i < directory->songs.nr; ++i)
song_free(directory->songs.base[i]);
struct song *song, *ns;
directory_for_each_song_safe(song, ns, directory)
song_free(song);
for (unsigned i = 0; i < directory->children.nr; ++i)
directory_free(directory->children.base[i]);
struct directory *child, *n;
directory_for_each_child_safe(child, n, directory)
directory_free(child);
dirvec_destroy(&directory->children);
songvec_destroy(&directory->songs);
g_free(directory);
/* this resets last dir returned */
/*directory_get_path(NULL); */
}
void
directory_delete(struct directory *directory)
{
assert(holding_db_lock());
assert(directory != NULL);
assert(directory->parent != NULL);
list_del(&directory->siblings);
directory_free(directory);
}
const char *
directory_get_name(const struct directory *directory)
{
return g_basename(directory->path);
}
struct directory *
directory_new_child(struct directory *parent, const char *name_utf8)
{
assert(holding_db_lock());
assert(parent != NULL);
assert(name_utf8 != NULL);
assert(*name_utf8 != 0);
char *allocated;
const char *path_utf8;
if (directory_is_root(parent)) {
allocated = NULL;
path_utf8 = name_utf8;
} else {
allocated = g_strconcat(directory_get_path(parent),
"/", name_utf8, NULL);
path_utf8 = allocated;
}
struct directory *directory = directory_new(path_utf8, parent);
g_free(allocated);
list_add(&directory->siblings, &parent->children);
return directory;
}
struct directory *
directory_get_child(const struct directory *directory, const char *name)
{
assert(holding_db_lock());
struct directory *child;
directory_for_each_child(child, directory)
if (strcmp(directory_get_name(child), name) == 0)
return child;
return NULL;
}
void
directory_prune_empty(struct directory *directory)
{
int i;
struct dirvec *dv = &directory->children;
for (i = dv->nr; --i >= 0; ) {
struct directory *child = dv->base[i];
assert(holding_db_lock());
struct directory *child, *n;
directory_for_each_child_safe(child, n, directory) {
directory_prune_empty(child);
if (directory_is_empty(child)) {
dirvec_delete(dv, child);
directory_free(child);
}
if (directory_is_empty(child))
directory_delete(child);
}
if (!dv->nr)
dirvec_destroy(dv);
}
struct directory *
directory_lookup_directory(struct directory *directory, const char *uri)
{
struct directory *cur = directory;
struct directory *found = NULL;
char *duplicated;
char *locate;
assert(holding_db_lock());
assert(uri != NULL);
if (isRootDirectory(uri))
return directory;
duplicated = g_strdup(uri);
locate = strchr(duplicated, '/');
char *duplicated = g_strdup(uri), *name = duplicated;
while (1) {
if (locate)
*locate = '\0';
if (!(found = directory_get_child(cur, duplicated)))
char *slash = strchr(name, '/');
if (slash == name) {
directory = NULL;
break;
assert(cur == found->parent);
cur = found;
if (!locate)
}
if (slash != NULL)
*slash = '\0';
directory = directory_get_child(directory, name);
if (directory == NULL || slash == NULL)
break;
*locate = '/';
locate = strchr(locate + 1, '/');
name = slash + 1;
}
g_free(duplicated);
return found;
return directory;
}
void
directory_add_song(struct directory *directory, struct song *song)
{
assert(directory != NULL);
assert(song != NULL);
assert(song->parent == directory);
list_add_tail(&song->siblings, &directory->songs);
}
void
directory_remove_song(G_GNUC_UNUSED struct directory *directory,
struct song *song)
{
assert(directory != NULL);
assert(song != NULL);
assert(song->parent == directory);
list_del(&song->siblings);
}
struct song *
directory_get_song(const struct directory *directory, const char *name_utf8)
{
assert(holding_db_lock());
assert(directory != NULL);
assert(name_utf8 != NULL);
struct song *song;
directory_for_each_song(song, directory) {
assert(song->parent == directory);
if (strcmp(song->uri, name_utf8) == 0)
return song;
}
return NULL;
}
struct song *
directory_lookup_song(struct directory *directory, const char *uri)
{
char *duplicated, *base;
struct song *song;
assert(holding_db_lock());
assert(directory != NULL);
assert(uri != NULL);
......@@ -147,7 +235,7 @@ directory_lookup_song(struct directory *directory, const char *uri)
} else
base = duplicated;
song = songvec_find(&directory->songs, base);
struct song *song = directory_get_song(directory, base);
assert(song == NULL || song->parent == directory);
g_free(duplicated);
......@@ -155,17 +243,26 @@ directory_lookup_song(struct directory *directory, const char *uri)
}
static int
directory_cmp(G_GNUC_UNUSED void *priv,
struct list_head *_a, struct list_head *_b)
{
const struct directory *a = (const struct directory *)_a;
const struct directory *b = (const struct directory *)_b;
return g_utf8_collate(a->path, b->path);
}
void
directory_sort(struct directory *directory)
{
int i;
struct dirvec *dv = &directory->children;
assert(holding_db_lock());
dirvec_sort(dv);
songvec_sort(&directory->songs);
list_sort(NULL, &directory->children, directory_cmp);
song_list_sort(&directory->songs);
for (i = dv->nr; --i >= 0; )
directory_sort(dv->base[i]);
struct directory *child;
directory_for_each_child(child, directory)
directory_sort(child);
}
bool
......@@ -178,9 +275,9 @@ directory_walk(const struct directory *directory, bool recursive,
assert(error_r == NULL || *error_r == NULL);
if (visitor->song != NULL) {
const struct songvec *sv = &directory->songs;
for (size_t i = 0; i < sv->nr; ++i)
if (!visitor->song(sv->base[i], ctx, error_r))
struct song *song;
directory_for_each_song(song, directory)
if (!visitor->song(song, ctx, error_r))
return false;
}
......@@ -188,14 +285,12 @@ directory_walk(const struct directory *directory, bool recursive,
const struct playlist_vector *pv = &directory->playlists;
for (const struct playlist_metadata *i = pv->head;
i != NULL; i = i->next)
if (!visitor->playlist(i, ctx, error_r))
if (!visitor->playlist(i, directory, ctx, error_r))
return false;
}
const struct dirvec *dv = &directory->children;
for (size_t i = 0; i < dv->nr; ++i) {
struct directory *child = dv->base[i];
struct directory *child;
directory_for_each_child(child, directory) {
if (visitor->directory != NULL &&
!visitor->directory(child, ctx, error_r))
return false;
......
......@@ -21,8 +21,7 @@
#define MPD_DIRECTORY_H
#include "check.h"
#include "dirvec.h"
#include "songvec.h"
#include "util/list.h"
#include "playlist_vector.h"
#include <glib.h>
......@@ -34,11 +33,47 @@
#define DEVICE_INARCHIVE (dev_t)(-1)
#define DEVICE_CONTAINER (dev_t)(-2)
#define directory_for_each_child(pos, directory) \
list_for_each_entry(pos, &directory->children, siblings)
#define directory_for_each_child_safe(pos, n, directory) \
list_for_each_entry_safe(pos, n, &directory->children, siblings)
#define directory_for_each_song(pos, directory) \
list_for_each_entry(pos, &directory->songs, siblings)
#define directory_for_each_song_safe(pos, n, directory) \
list_for_each_entry_safe(pos, n, &directory->songs, siblings)
struct song;
struct db_visitor;
struct directory {
struct dirvec children;
struct songvec songs;
/**
* Pointers to the siblings of this directory within the
* parent directory. It is unused (undefined) in the root
* directory.
*
* This attribute is protected with the global #db_mutex.
* Read access in the update thread does not need protection.
*/
struct list_head siblings;
/**
* A doubly linked list of child directories.
*
* This attribute is protected with the global #db_mutex.
* Read access in the update thread does not need protection.
*/
struct list_head children;
/**
* A doubly linked list of songs within this directory.
*
* This attribute is protected with the global #db_mutex.
* Read access in the update thread does not need protection.
*/
struct list_head songs;
struct playlist_vector playlists;
......@@ -46,7 +81,7 @@ struct directory {
time_t mtime;
ino_t inode;
dev_t device;
unsigned stat; /* not needed if ino_t == dev_t == 0 is impossible */
bool have_stat; /* not needed if ino_t == dev_t == 0 is impossible */
char path[sizeof(long)];
};
......@@ -56,16 +91,44 @@ isRootDirectory(const char *name)
return name[0] == 0 || (name[0] == '/' && name[1] == 0);
}
/**
* Generic constructor for #directory object.
*/
G_GNUC_MALLOC
struct directory *
directory_new(const char *dirname, struct directory *parent);
/**
* Create a new root #directory object.
*/
G_GNUC_MALLOC
static inline struct directory *
directory_new_root(void)
{
return directory_new("", NULL);
}
/**
* Free this #directory object (and the whole object tree within it),
* assuming it was already removed from the parent.
*/
void
directory_free(struct directory *directory);
/**
* Remove this #directory object from its parent and free it. This
* must not be called with the root directory.
*
* Caller must lock the #db_mutex.
*/
void
directory_delete(struct directory *directory);
static inline bool
directory_is_empty(const struct directory *directory)
{
return directory->children.nr == 0 && directory->songs.nr == 0 &&
return list_empty(&directory->children) &&
list_empty(&directory->songs) &&
playlist_vector_is_empty(&directory->playlists);
}
......@@ -87,23 +150,47 @@ directory_is_root(const struct directory *directory)
/**
* Returns the base name of the directory.
*/
G_GNUC_PURE
const char *
directory_get_name(const struct directory *directory);
static inline struct directory *
directory_get_child(const struct directory *directory, const char *name)
{
return dirvec_find(&directory->children, name);
}
/**
* Caller must lock the #db_mutex.
*/
G_GNUC_PURE
struct directory *
directory_get_child(const struct directory *directory, const char *name);
/**
* Create a new #directory object as a child of the given one.
*
* Caller must lock the #db_mutex.
*
* @param parent the parent directory the new one will be added to
* @param name_utf8 the UTF-8 encoded name of the new sub directory
*/
G_GNUC_MALLOC
struct directory *
directory_new_child(struct directory *parent, const char *name_utf8);
/**
* Look up a sub directory, and create the object if it does not
* exist.
*
* Caller must lock the #db_mutex.
*/
static inline struct directory *
directory_new_child(struct directory *directory, const char *name)
directory_make_child(struct directory *directory, const char *name_utf8)
{
struct directory *subdir = directory_new(name, directory);
dirvec_add(&directory->children, subdir);
return subdir;
struct directory *child = directory_get_child(directory, name_utf8);
if (child == NULL)
child = directory_new_child(directory, name_utf8);
return child;
}
/**
* Caller must lock the #db_mutex.
*/
void
directory_prune_empty(struct directory *directory);
......@@ -118,8 +205,34 @@ struct directory *
directory_lookup_directory(struct directory *directory, const char *uri);
/**
* Add a song object to this directory. Its "parent" attribute must
* be set already.
*/
void
directory_add_song(struct directory *directory, struct song *song);
/**
* Remove a song object from this directory (which effectively
* invalidates the song object, because the "parent" attribute becomes
* stale), but does not free it.
*/
void
directory_remove_song(struct directory *directory, struct song *song);
/**
* Look up a song in this directory by its name.
*
* Caller must lock the #db_mutex.
*/
G_GNUC_PURE
struct song *
directory_get_song(const struct directory *directory, const char *name_utf8);
/**
* Looks up a song by its relative URI.
*
* Caller must lock the #db_mutex.
*
* @param directory the parent (or grandparent, ...) directory
* @param uri the relative URI
* @return the song, or NULL if none was found
......@@ -127,9 +240,17 @@ directory_lookup_directory(struct directory *directory, const char *uri);
struct song *
directory_lookup_song(struct directory *directory, const char *uri);
/**
* Sort all directory entries recursively.
*
* Caller must lock the #db_mutex.
*/
void
directory_sort(struct directory *directory);
/**
* Caller must lock #db_mutex.
*/
bool
directory_walk(const struct directory *directory, bool recursive,
const struct db_visitor *visitor, void *ctx,
......
......@@ -44,8 +44,6 @@ directory_quark(void)
void
directory_save(FILE *fp, const struct directory *directory)
{
size_t i;
if (!directory_is_root(directory)) {
fprintf(fp, DIRECTORY_MTIME "%lu\n",
(unsigned long)directory->mtime);
......@@ -54,9 +52,8 @@ directory_save(FILE *fp, const struct directory *directory)
directory_get_path(directory));
}
const struct dirvec *children = &directory->children;
for (i = 0; i < children->nr; ++i) {
const struct directory *cur = children->base[i];
struct directory *cur;
directory_for_each_child(cur, directory) {
char *base = g_path_get_basename(cur->path);
fprintf(fp, DIRECTORY_DIR "%s\n", base);
......@@ -68,7 +65,9 @@ directory_save(FILE *fp, const struct directory *directory)
return;
}
songvec_save(fp, &directory->songs);
struct song *song;
directory_for_each_song(song, directory)
song_save(fp, song);
playlist_vector_save(fp, &directory->playlists);
......@@ -81,7 +80,6 @@ static struct directory *
directory_load_subdir(FILE *fp, struct directory *parent, const char *name,
GString *buffer, GError **error_r)
{
struct directory *directory;
const char *line;
bool success;
......@@ -91,20 +89,13 @@ directory_load_subdir(FILE *fp, struct directory *parent, const char *name,
return NULL;
}
if (directory_is_root(parent)) {
directory = directory_new(name, parent);
} else {
char *path = g_strconcat(directory_get_path(parent), "/",
name, NULL);
directory = directory_new(path, parent);
g_free(path);
}
struct directory *directory = directory_new_child(parent, name);
line = read_text_line(fp, buffer);
if (line == NULL) {
g_set_error(error_r, directory_quark(), 0,
"Unexpected end of file");
directory_free(directory);
directory_delete(directory);
return NULL;
}
......@@ -117,7 +108,7 @@ directory_load_subdir(FILE *fp, struct directory *parent, const char *name,
if (line == NULL) {
g_set_error(error_r, directory_quark(), 0,
"Unexpected end of file");
directory_free(directory);
directory_delete(directory);
return NULL;
}
}
......@@ -125,13 +116,13 @@ directory_load_subdir(FILE *fp, struct directory *parent, const char *name,
if (!g_str_has_prefix(line, DIRECTORY_BEGIN)) {
g_set_error(error_r, directory_quark(), 0,
"Malformed line: %s", line);
directory_free(directory);
directory_delete(directory);
return NULL;
}
success = directory_load(fp, directory, buffer, error_r);
if (!success) {
directory_free(directory);
directory_delete(directory);
return NULL;
}
......@@ -153,13 +144,11 @@ directory_load(FILE *fp, struct directory *directory,
buffer, error);
if (subdir == NULL)
return false;
dirvec_add(&directory->children, subdir);
} else if (g_str_has_prefix(line, SONG_BEGIN)) {
const char *name = line + sizeof(SONG_BEGIN) - 1;
struct song *song;
if (songvec_find(&directory->songs, name) != NULL) {
if (directory_get_song(directory, name) != NULL) {
g_set_error(error, directory_quark(), 0,
"Duplicate song '%s'", name);
return NULL;
......@@ -170,7 +159,7 @@ directory_load(FILE *fp, struct directory *directory,
if (song == NULL)
return false;
songvec_add(&directory->songs, song);
directory_add_song(directory, song);
} else if (g_str_has_prefix(line, PLAYLIST_META_BEGIN)) {
/* duplicate the name, because
playlist_metadata_load() will overwrite the
......
/*
* Copyright (C) 2003-2011 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 "config.h"
#include "dirvec.h"
#include "directory.h"
#include <glib.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
static GMutex *nr_lock = NULL;
static size_t dv_size(const struct dirvec *dv)
{
return dv->nr * sizeof(struct directory *);
}
/* Only used for sorting/searching a dirvec, not general purpose compares */
static int dirvec_cmp(const void *d1, const void *d2)
{
const struct directory *a = ((const struct directory * const *)d1)[0];
const struct directory *b = ((const struct directory * const *)d2)[0];
return g_utf8_collate(a->path, b->path);
}
void dirvec_init(void)
{
g_assert(nr_lock == NULL);
nr_lock = g_mutex_new();
}
void dirvec_deinit(void)
{
g_assert(nr_lock != NULL);
g_mutex_free(nr_lock);
nr_lock = NULL;
}
void dirvec_sort(struct dirvec *dv)
{
g_mutex_lock(nr_lock);
qsort(dv->base, dv->nr, sizeof(struct directory *), dirvec_cmp);
g_mutex_unlock(nr_lock);
}
struct directory *dirvec_find(const struct dirvec *dv, const char *path)
{
char *base;
int i;
struct directory *ret = NULL;
base = g_path_get_basename(path);
g_mutex_lock(nr_lock);
for (i = dv->nr; --i >= 0; )
if (!strcmp(directory_get_name(dv->base[i]), base)) {
ret = dv->base[i];
break;
}
g_mutex_unlock(nr_lock);
g_free(base);
return ret;
}
int dirvec_delete(struct dirvec *dv, struct directory *del)
{
size_t i;
g_mutex_lock(nr_lock);
for (i = 0; i < dv->nr; ++i) {
if (dv->base[i] != del)
continue;
/* we _don't_ call directory_free() here */
if (!--dv->nr) {
g_mutex_unlock(nr_lock);
g_free(dv->base);
dv->base = NULL;
return i;
} else {
memmove(&dv->base[i], &dv->base[i + 1],
(dv->nr - i) * sizeof(struct directory *));
dv->base = g_realloc(dv->base, dv_size(dv));
}
break;
}
g_mutex_unlock(nr_lock);
return i;
}
void dirvec_add(struct dirvec *dv, struct directory *add)
{
g_mutex_lock(nr_lock);
++dv->nr;
dv->base = g_realloc(dv->base, dv_size(dv));
dv->base[dv->nr - 1] = add;
g_mutex_unlock(nr_lock);
}
void dirvec_destroy(struct dirvec *dv)
{
g_mutex_lock(nr_lock);
dv->nr = 0;
g_mutex_unlock(nr_lock);
if (dv->base) {
g_free(dv->base);
dv->base = NULL;
}
}
int dirvec_for_each(const struct dirvec *dv,
int (*fn)(struct directory *, void *), void *arg)
{
size_t i;
size_t prev_nr;
g_mutex_lock(nr_lock);
for (i = 0; i < dv->nr; ) {
struct directory *dir = dv->base[i];
assert(dir);
prev_nr = dv->nr;
g_mutex_unlock(nr_lock);
if (fn(dir, arg) < 0)
return -1;
g_mutex_lock(nr_lock); /* dv->nr may change in fn() */
if (prev_nr == dv->nr)
++i;
}
g_mutex_unlock(nr_lock);
return 0;
}
#include <stdlib.h>
#include <string.h>
#include "dsd2pcm.h"
#define HTAPS 48 /* number of FIR constants */
#define FIFOSIZE 16 /* must be a power of two */
#define FIFOMASK (FIFOSIZE-1) /* bit mask for FIFO offsets */
#define CTABLES ((HTAPS+7)/8) /* number of "8 MACs" lookup tables */
#if FIFOSIZE*8 < HTAPS*2
#error "FIFOSIZE too small"
#endif
/*
* Properties of this 96-tap lowpass filter when applied on a signal
* with sampling rate of 44100*64 Hz:
*
* () has a delay of 17 microseconds.
*
* () flat response up to 48 kHz
*
* () if you downsample afterwards by a factor of 8, the
* spectrum below 70 kHz is practically alias-free.
*
* () stopband rejection is about 160 dB
*
* The coefficient tables ("ctables") take only 6 Kibi Bytes and
* should fit into a modern processor's fast cache.
*/
/*
* The 2nd half (48 coeffs) of a 96-tap symmetric lowpass filter
*/
static const double htaps[HTAPS] = {
0.09950731974056658,
0.09562845727714668,
0.08819647126516944,
0.07782552527068175,
0.06534876523171299,
0.05172629311427257,
0.0379429484910187,
0.02490921351762261,
0.0133774746265897,
0.003883043418804416,
-0.003284703416210726,
-0.008080250212687497,
-0.01067241812471033,
-0.01139427235000863,
-0.0106813877974587,
-0.009007905078766049,
-0.006828859761015335,
-0.004535184322001496,
-0.002425035959059578,
-0.0006922187080790708,
0.0005700762133516592,
0.001353838005269448,
0.001713709169690937,
0.001742046839472948,
0.001545601648013235,
0.001226696225277855,
0.0008704322683580222,
0.0005381636200535649,
0.000266446345425276,
7.002968738383528e-05,
-5.279407053811266e-05,
-0.0001140625650874684,
-0.0001304796361231895,
-0.0001189970287491285,
-9.396247155265073e-05,
-6.577634378272832e-05,
-4.07492895872535e-05,
-2.17407957554587e-05,
-9.163058931391722e-06,
-2.017460145032201e-06,
1.249721855219005e-06,
2.166655190537392e-06,
1.930520892991082e-06,
1.319400334374195e-06,
7.410039764949091e-07,
3.423230509967409e-07,
1.244182214744588e-07,
3.130441005359396e-08
};
static float ctables[CTABLES][256];
static unsigned char bitreverse[256];
static int precalculated = 0;
static void precalc(void)
{
int t, e, m, k;
double acc;
if (precalculated) return;
for (t=0, e=0; t<256; ++t) {
bitreverse[t] = e;
for (m=128; m && !((e^=m)&m); m>>=1)
;
}
for (t=0; t<CTABLES; ++t) {
k = HTAPS - t*8;
if (k>8) k=8;
for (e=0; e<256; ++e) {
acc = 0.0;
for (m=0; m<k; ++m) {
acc += (((e >> (7-m)) & 1)*2-1) * htaps[t*8+m];
}
ctables[CTABLES-1-t][e] = (float)acc;
}
}
precalculated = 1;
}
struct dsd2pcm_ctx_s
{
unsigned char fifo[FIFOSIZE];
unsigned fifopos;
};
extern dsd2pcm_ctx* dsd2pcm_init(void)
{
dsd2pcm_ctx* ptr;
if (!precalculated) precalc();
ptr = (dsd2pcm_ctx*) malloc(sizeof(dsd2pcm_ctx));
if (ptr) dsd2pcm_reset(ptr);
return ptr;
}
extern void dsd2pcm_destroy(dsd2pcm_ctx* ptr)
{
free(ptr);
}
extern dsd2pcm_ctx* dsd2pcm_clone(dsd2pcm_ctx* ptr)
{
dsd2pcm_ctx* p2;
p2 = (dsd2pcm_ctx*) malloc(sizeof(dsd2pcm_ctx));
if (p2) {
memcpy(p2,ptr,sizeof(dsd2pcm_ctx));
}
return p2;
}
extern void dsd2pcm_reset(dsd2pcm_ctx* ptr)
{
int i;
for (i=0; i<FIFOSIZE; ++i)
ptr->fifo[i] = 0x69; /* my favorite silence pattern */
ptr->fifopos = 0;
/* 0x69 = 01101001
* This pattern "on repeat" makes a low energy 352.8 kHz tone
* and a high energy 1.0584 MHz tone which should be filtered
* out completely by any playback system --> silence
*/
}
extern void dsd2pcm_translate(
dsd2pcm_ctx* ptr,
size_t samples,
const unsigned char *src, ptrdiff_t src_stride,
int lsbf,
float *dst, ptrdiff_t dst_stride)
{
unsigned ffp;
unsigned i;
unsigned bite1, bite2;
unsigned char* p;
double acc;
ffp = ptr->fifopos;
lsbf = lsbf ? 1 : 0;
while (samples-- > 0) {
bite1 = *src & 0xFFu;
if (lsbf) bite1 = bitreverse[bite1];
ptr->fifo[ffp] = bite1; src += src_stride;
p = ptr->fifo + ((ffp-CTABLES) & FIFOMASK);
*p = bitreverse[*p & 0xFF];
acc = 0;
for (i=0; i<CTABLES; ++i) {
bite1 = ptr->fifo[(ffp -i) & FIFOMASK] & 0xFF;
bite2 = ptr->fifo[(ffp-(CTABLES*2-1)+i) & FIFOMASK] & 0xFF;
acc += ctables[i][bite1] + ctables[i][bite2];
}
*dst = (float)acc; dst += dst_stride;
ffp = (ffp + 1) & FIFOMASK;
}
ptr->fifopos = ffp;
}
#ifndef DSD2PCM_H_INCLUDED
#define DSD2PCM_H_INCLUDED
#include <stddef.h>
#include <string.h>
#ifdef __cplusplus
extern "C" {
#endif
struct dsd2pcm_ctx_s;
typedef struct dsd2pcm_ctx_s dsd2pcm_ctx;
/**
* initializes a "dsd2pcm engine" for one channel
* (precomputes tables and allocates memory)
*
* This is the only function that is not thread-safe in terms of the
* POSIX thread-safety definition because it modifies global state
* (lookup tables are computed during the first call)
*/
extern dsd2pcm_ctx* dsd2pcm_init(void);
/**
* deinitializes a "dsd2pcm engine"
* (releases memory, don't forget!)
*/
extern void dsd2pcm_destroy(dsd2pcm_ctx *ctx);
/**
* clones the context and returns a pointer to the
* newly allocated copy
*/
extern dsd2pcm_ctx* dsd2pcm_clone(dsd2pcm_ctx *ctx);
/**
* resets the internal state for a fresh new stream
*/
extern void dsd2pcm_reset(dsd2pcm_ctx *ctx);
/**
* "translates" a stream of octets to a stream of floats
* (8:1 decimation)
* @param ctx -- pointer to abstract context (buffers)
* @param samples -- number of octets/samples to "translate"
* @param src -- pointer to first octet (input)
* @param src_stride -- src pointer increment
* @param lsbitfirst -- bitorder, 0=msb first, 1=lsbfirst
* @param dst -- pointer to first float (output)
* @param dst_stride -- dst pointer increment
*/
extern void dsd2pcm_translate(dsd2pcm_ctx *ctx,
size_t samples,
const unsigned char *src, ptrdiff_t src_stride,
int lsbitfirst,
float *dst, ptrdiff_t dst_stride);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* include guard DSD2PCM_H_INCLUDED */
#ifndef DSD2PCM_HXX_INCLUDED
#define DSD2PCM_HXX_INCLUDED
#include <algorithm>
#include <stdexcept>
#include "dsd2pcm.h"
/**
* C++ PImpl Wrapper for the dsd2pcm C library
*/
class dxd
{
dsd2pcm_ctx *handle;
public:
dxd() : handle(dsd2pcm_init())
{ if (!handle) throw std::runtime_error("wtf?!"); }
dxd(dxd const& x) : handle(dsd2pcm_clone(x.handle))
{ if (!handle) throw std::runtime_error("wtf?!"); }
~dxd() { dsd2pcm_destroy(handle); }
friend void swap(dxd & a, dxd & b)
{ std::swap(a.handle,b.handle); }
dxd& operator=(dxd x)
{ swap(*this,x); return *this; }
void translate(size_t samples,
const unsigned char *src, ptrdiff_t src_stride,
bool lsbitfirst,
float *dst, ptrdiff_t dst_stride)
{
dsd2pcm_translate(handle,samples,src,src_stride,
lsbitfirst,dst,dst_stride);
}
};
#endif // DSD2PCM_HXX_INCLUDED
You downloaded the source code for "dsd2pcm" which is a simple little
"filter" program, that takes a DSD data stream on stdin and converts
it to a PCM stream (352.8 kHz, either 16 or 24 bits) and writes it to
stdout. The code is split into three modules:
(1) dsd2pcm
This is where the 8:1 decimation magic happens. It's an
implementation of a symmetric 96-taps FIR lowpass filter
optimized for DSD inputs. If you feed this converter with
DSD64 you get a PCM stream at 352.8 kHz and floating point
samples. This module is independent and can be reused.
(2) noiseshape
A module for applying generic noise shaping filters. It's
used for the 16-bit output mode in "main" to preserve the
dynamic range. This module is independent and can be reused.
(3) main.cpp (file contains the main function and handles I/O)
The first two modules are pure C for maximum portability. In addition,
there are C++ wrapper headers for convenient use of these modules in
C++. The main application is a C++ application and makes use of the
C++ headers to access the functionality of the first two modules.
Under Linux this program is easily compiled by typing
g++ *.c *.cpp -O3 -o dsd2pcm
provided you have GCC installed. That's why I didn't bother writing
any makefiles. :-p
Cheers!
SG
#include <iostream>
#include <vector>
#include <cstring>
#include "dsd2pcm.hpp"
#include "noiseshape.hpp"
namespace {
const float my_ns_coeffs[] = {
// b1 b2 a1 a2
-1.62666423, 0.79410094, 0.61367127, 0.23311013, // section 1
-1.44870017, 0.54196219, 0.03373857, 0.70316556 // section 2
};
const int my_ns_soscount = sizeof(my_ns_coeffs)/(sizeof(my_ns_coeffs[0])*4);
inline long myround(float x)
{
return static_cast<long>(x + (x>=0 ? 0.5f : -0.5f));
}
template<typename T>
struct id { typedef T type; };
template<typename T>
inline T clip(
typename id<T>::type min,
T v,
typename id<T>::type max)
{
if (v<min) return min;
if (v>max) return max;
return v;
}
inline void write_intel16(unsigned char * ptr, unsigned word)
{
ptr[0] = word & 0xFF;
ptr[1] = (word >> 8) & 0xFF;
}
inline void write_intel24(unsigned char * ptr, unsigned long word)
{
ptr[0] = word & 0xFF;
ptr[1] = (word >> 8) & 0xFF;
ptr[2] = (word >> 16) & 0xFF;
}
} // anonymous namespace
using std::vector;
using std::cin;
using std::cout;
using std::cerr;
int main(int argc, char *argv[])
{
const int block = 16384;
int channels = -1;
int lsbitfirst = -1;
int bits = -1;
if (argc==4) {
if ('1'<=argv[1][0] && argv[1][0]<='9') channels = 1 + (argv[1][0]-'1');
if (argv[2][0]=='m' || argv[2][0]=='M') lsbitfirst=0;
if (argv[2][0]=='l' || argv[2][0]=='L') lsbitfirst=1;
if (!strcmp(argv[3],"16")) bits = 16;
if (!strcmp(argv[3],"24")) bits = 24;
}
if (channels<1 || lsbitfirst<0 || bits<0) {
cerr << "\n"
"DSD2PCM filter (raw DSD64 --> 352 kHz raw PCM)\n"
"(c) 2009 Sebastian Gesemann\n\n"
"(filter as in \"reads data from stdin and writes to stdout\")\n\n"
"Syntax: dsd2pcm <channels> <bitorder> <bitdepth>\n"
"channels = 1,2,3,...,9 (number of channels in DSD stream)\n"
"bitorder = L (lsb first), M (msb first) (DSD stream option)\n"
"bitdepth = 16 or 24 (intel byte order, output option)\n\n"
"Note: At 16 bits/sample a noise shaper kicks in that can preserve\n"
"a dynamic range of 135 dB below 30 kHz.\n\n";
return 1;
}
int bytespersample = bits/8;
vector<dxd> dxds (channels);
vector<noise_shaper> ns;
if (bits==16) {
ns.resize(channels, noise_shaper(my_ns_soscount, my_ns_coeffs) );
}
vector<unsigned char> dsd_data (block * channels);
vector<float> float_data (block);
vector<unsigned char> pcm_data (block * channels * bytespersample);
char * const dsd_in = reinterpret_cast<char*>(&dsd_data[0]);
char * const pcm_out = reinterpret_cast<char*>(&pcm_data[0]);
while (cin.read(dsd_in,block * channels)) {
for (int c=0; c<channels; ++c) {
dxds[c].translate(block,&dsd_data[0]+c,channels,
lsbitfirst,
&float_data[0],1);
unsigned char * out = &pcm_data[0] + c*bytespersample;
if (bits==16) {
for (int s=0; s<block; ++s) {
float r = float_data[s]*32768 + ns[c].get();
long smp = clip(-32768,myround(r),32767);
ns[c].update( clip(-1,smp-r,1) );
write_intel16(out,smp);
out += channels*bytespersample;
}
} else {
for (int s=0; s<block; ++s) {
float r = float_data[s]*8388608;
long smp = clip(-8388608,myround(r),8388607);
write_intel24(out,smp);
out += channels*bytespersample;
}
}
}
cout.write(pcm_out,block*channels*bytespersample);
}
}
#include <stdlib.h>
#include <string.h>
#include "noiseshape.h"
extern int noise_shape_init(
noise_shape_ctx *ctx,
int sos_count,
const float *coeffs)
{
int i;
ctx->sos_count = sos_count;
ctx->bbaa = coeffs;
ctx->t1 = (float*) malloc(sizeof(float)*sos_count);
if (!ctx->t1) goto escape1;
ctx->t2 = (float*) malloc(sizeof(float)*sos_count);
if (!ctx->t2) goto escape2;
for (i=0; i<sos_count; ++i) {
ctx->t1[i] = 0.f;
ctx->t2[i] = 0.f;
}
return 0;
escape2:
free(ctx->t1);
escape1:
return -1;
}
extern void noise_shape_destroy(
noise_shape_ctx *ctx)
{
free(ctx->t1);
free(ctx->t2);
}
extern int noise_shape_clone(
const noise_shape_ctx *from,
noise_shape_ctx *to)
{
to->sos_count = from->sos_count;
to->bbaa = from->bbaa;
to->t1 = (float*) malloc(sizeof(float)*to->sos_count);
if (!to->t1) goto error1;
to->t2 = (float*) malloc(sizeof(float)*to->sos_count);
if (!to->t2) goto error2;
memcpy(to->t1,from->t1,sizeof(float)*to->sos_count);
memcpy(to->t2,from->t2,sizeof(float)*to->sos_count);
return 0;
error2:
free(to->t1);
error1:
return -1;
}
extern float noise_shape_get(noise_shape_ctx *ctx)
{
int i;
float acc;
const float *c;
acc = 0.0;
c = ctx->bbaa;
for (i=0; i<ctx->sos_count; ++i) {
float t1i = ctx->t1[i];
float t2i = ctx->t2[i];
ctx->t2[i] = acc -= t1i * c[2] + t2i * c[3];
acc += t1i * c[0] + t2i * c[1];
c += 4;
}
return acc;
}
extern void noise_shape_update(noise_shape_ctx *ctx, float qerror)
{
float *p;
int i;
for (i=0; i<ctx->sos_count; ++i) {
ctx->t2[i] += qerror;
}
p = ctx->t1;
ctx->t1 = ctx->t2;
ctx->t2 = p;
}
#ifndef NOISE_SHAPE_H_INCLUDED
#define NOISE_SHAPE_H_INCLUDED
#ifdef __cplusplus
extern "C" {
#endif
typedef struct noise_shape_ctx_s {
int sos_count; /* number of second order sections */
const float *bbaa; /* filter coefficients, owned by user */
float *t1, *t2; /* filter state, owned by ns library */
} noise_shape_ctx;
/**
* initializes a noise_shaper context
* returns an error code or 0
*/
extern int noise_shape_init(
noise_shape_ctx *ctx,
int sos_count,
const float *coeffs);
/**
* destroys a noise_shaper context
*/
extern void noise_shape_destroy(
noise_shape_ctx *ctx);
/**
* initializes a noise_shaper context so that its state
* is a copy of a given context
* returns an error code or 0
*/
extern int noise_shape_clone(
const noise_shape_ctx *from, noise_shape_ctx *to);
/**
* computes the next "noise shaping sample". Note: This call
* alters the internal state. xxx_get and xxx_update must be
* called in an alternating manner.
*/
extern float noise_shape_get(
noise_shape_ctx *ctx);
/**
* updates the noise shaper's state with the
* last quantization error
*/
extern void noise_shape_update(
noise_shape_ctx *ctx, float qerror);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* NOISE_SHAPE_H_INCLUDED */
#ifndef NOISE_SHAPE_HXX_INCLUDED
#define NOISE_SHAPE_HXX_INCLUDED
#include <stdexcept>
#include "noiseshape.h"
/**
* C++ wrapper for the noiseshape C library
*/
class noise_shaper
{
noise_shape_ctx ctx;
public:
noise_shaper(int sos_count, const float *bbaa)
{
if (noise_shape_init(&ctx,sos_count,bbaa))
throw std::runtime_error("noise shaper initialization failed");
}
noise_shaper(noise_shaper const& x)
{
if (noise_shape_clone(&x.ctx,&ctx))
throw std::runtime_error("noise shaper initialization failed");
}
~noise_shaper()
{ noise_shape_destroy(&ctx); }
noise_shaper& operator=(noise_shaper const& x)
{
if (this != &x) {
noise_shape_destroy(&ctx);
if (noise_shape_clone(&x.ctx,&ctx))
throw std::runtime_error("noise shaper initialization failed");
}
return *this;
}
float get() { return noise_shape_get(&ctx); }
void update(float error) { noise_shape_update(&ctx,error); }
};
#endif /* NOISE_SHAPE_HXX_INCLUDED */
/*
* Copyright (C) 2003-2011 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.
*/
/*
* Just a dummy C++ file that is linked to work around an automake
* bug: automake uses CXXLD when at least one source is C++, but when
* you link a static library with a C++ source, it uses CCLD. This
* causes linker problems (undefined reference to 'operator
* delete(void*)'), because CCLD does not link with libstdc++.
*
* By linking with this empty C++ source, automake decides to use
* CXXLD.
*
*/
......@@ -22,6 +22,8 @@
#include "encoder_plugin.h"
#include "audio_format.h"
#include "pcm_buffer.h"
#include "fifo_buffer.h"
#include "growing_fifo.h"
#include <assert.h>
#include <string.h>
......@@ -38,8 +40,11 @@ struct flac_encoder {
struct pcm_buffer expand_buffer;
struct pcm_buffer buffer;
size_t buffer_length;
/**
* This buffer will hold encoded data from libFLAC until it is
* picked up with flac_encoder_read().
*/
struct fifo_buffer *output_buffer;
};
extern const struct encoder_plugin flac_encoder_plugin;
......@@ -140,11 +145,8 @@ flac_write_callback(G_GNUC_UNUSED const FLAC__StreamEncoder *fse,
{
struct flac_encoder *encoder = (struct flac_encoder *) client_data;
char *buffer = pcm_buffer_get(&encoder->buffer, encoder->buffer_length + bytes);
//transfer data to buffer
memcpy( buffer + encoder->buffer_length, data, bytes);
encoder->buffer_length += bytes;
growing_fifo_append(&encoder->output_buffer, data, bytes);
return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
}
......@@ -156,8 +158,8 @@ flac_encoder_close(struct encoder *_encoder)
FLAC__stream_encoder_delete(encoder->fse);
pcm_buffer_deinit(&encoder->buffer);
pcm_buffer_deinit(&encoder->expand_buffer);
fifo_buffer_free(encoder->output_buffer);
}
static bool
......@@ -201,10 +203,10 @@ flac_encoder_open(struct encoder *_encoder, struct audio_format *audio_format,
return false;
}
encoder->buffer_length = 0;
pcm_buffer_init(&encoder->buffer);
pcm_buffer_init(&encoder->expand_buffer);
encoder->output_buffer = growing_fifo_new();
/* this immediately outputs data through callback */
#if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT <= 7
......@@ -325,16 +327,18 @@ static size_t
flac_encoder_read(struct encoder *_encoder, void *dest, size_t length)
{
struct flac_encoder *encoder = (struct flac_encoder *)_encoder;
char *buffer = pcm_buffer_get(&encoder->buffer, encoder->buffer_length);
if (length > encoder->buffer_length)
length = encoder->buffer_length;
memcpy(dest, buffer, length);
size_t max_length;
const char *src = fifo_buffer_read(encoder->output_buffer,
&max_length);
if (src == NULL)
return 0;
encoder->buffer_length -= length;
memmove(buffer, buffer + length, encoder->buffer_length);
if (length > max_length)
length = max_length;
memcpy(dest, src, length);
fifo_buffer_consume(encoder->output_buffer, length);
return length;
}
......
......@@ -20,7 +20,8 @@
#include "config.h"
#include "encoder_api.h"
#include "encoder_plugin.h"
#include "pcm_buffer.h"
#include "fifo_buffer.h"
#include "growing_fifo.h"
#include <assert.h>
#include <string.h>
......@@ -28,8 +29,7 @@
struct null_encoder {
struct encoder encoder;
struct pcm_buffer buffer;
size_t buffer_length;
struct fifo_buffer *buffer;
};
extern const struct encoder_plugin null_encoder_plugin;
......@@ -65,7 +65,7 @@ null_encoder_close(struct encoder *_encoder)
{
struct null_encoder *encoder = (struct null_encoder *)_encoder;
pcm_buffer_deinit(&encoder->buffer);
fifo_buffer_free(encoder->buffer);
}
......@@ -76,9 +76,7 @@ null_encoder_open(struct encoder *_encoder,
{
struct null_encoder *encoder = (struct null_encoder *)_encoder;
encoder->buffer_length = 0;
pcm_buffer_init(&encoder->buffer);
encoder->buffer = growing_fifo_new();
return true;
}
......@@ -88,28 +86,26 @@ null_encoder_write(struct encoder *_encoder,
G_GNUC_UNUSED GError **error)
{
struct null_encoder *encoder = (struct null_encoder *)_encoder;
char *buffer = pcm_buffer_get(&encoder->buffer, encoder->buffer_length + length);
memcpy(buffer+encoder->buffer_length, data, length);
encoder->buffer_length += length;
return true;
growing_fifo_append(&encoder->buffer, data, length);
return length;
}
static size_t
null_encoder_read(struct encoder *_encoder, void *dest, size_t length)
{
struct null_encoder *encoder = (struct null_encoder *)_encoder;
char *buffer = pcm_buffer_get(&encoder->buffer, encoder->buffer_length);
if (length > encoder->buffer_length)
length = encoder->buffer_length;
memcpy(dest, buffer, length);
size_t max_length;
const void *src = fifo_buffer_read(encoder->buffer, &max_length);
if (src == NULL)
return 0;
encoder->buffer_length -= length;
memmove(buffer, buffer + length, encoder->buffer_length);
if (length > max_length)
length = max_length;
memcpy(dest, src, length);
fifo_buffer_consume(encoder->buffer, length);
return length;
}
......
......@@ -20,7 +20,8 @@
#include "config.h"
#include "encoder_api.h"
#include "encoder_plugin.h"
#include "pcm_buffer.h"
#include "fifo_buffer.h"
#include "growing_fifo.h"
#include <assert.h>
#include <string.h>
......@@ -29,8 +30,7 @@ struct wave_encoder {
struct encoder encoder;
unsigned bits;
struct pcm_buffer buffer;
size_t buffer_length;
struct fifo_buffer *buffer;
};
struct wave_header {
......@@ -92,7 +92,6 @@ wave_encoder_init(G_GNUC_UNUSED const struct config_param *param,
encoder = g_new(struct wave_encoder, 1);
encoder_struct_init(&encoder->encoder, &wave_encoder_plugin);
pcm_buffer_init(&encoder->buffer);
return &encoder->encoder;
}
......@@ -102,7 +101,6 @@ wave_encoder_finish(struct encoder *_encoder)
{
struct wave_encoder *encoder = (struct wave_encoder *)_encoder;
pcm_buffer_deinit(&encoder->buffer);
g_free(encoder);
}
......@@ -112,7 +110,6 @@ wave_encoder_open(struct encoder *_encoder,
G_GNUC_UNUSED GError **error)
{
struct wave_encoder *encoder = (struct wave_encoder *)_encoder;
void *buffer;
assert(audio_format_valid(audio_format));
......@@ -125,6 +122,11 @@ wave_encoder_open(struct encoder *_encoder,
encoder->bits = 16;
break;
case SAMPLE_FORMAT_S24:
audio_format->format = SAMPLE_FORMAT_S24_P32;
encoder->bits = 24;
break;
case SAMPLE_FORMAT_S24_P32:
encoder->bits = 24;
break;
......@@ -139,19 +141,29 @@ wave_encoder_open(struct encoder *_encoder,
break;
}
buffer = pcm_buffer_get(&encoder->buffer, sizeof(struct wave_header) );
encoder->buffer = growing_fifo_new();
struct wave_header *header =
growing_fifo_write(&encoder->buffer, sizeof(*header));
/* create PCM wave header in initial buffer */
fill_wave_header((struct wave_header *) buffer,
fill_wave_header(header,
audio_format->channels,
encoder->bits,
audio_format->sample_rate,
(encoder->bits / 8) * audio_format->channels );
fifo_buffer_append(encoder->buffer, sizeof(*header));
encoder->buffer_length = sizeof(struct wave_header);
return true;
}
static void
wave_encoder_close(struct encoder *_encoder)
{
struct wave_encoder *encoder = (struct wave_encoder *)_encoder;
fifo_buffer_free(encoder->buffer);
}
static inline size_t
pcm16_to_wave(uint16_t *dst16, const uint16_t *src16, size_t length)
{
......@@ -198,9 +210,8 @@ wave_encoder_write(struct encoder *_encoder,
G_GNUC_UNUSED GError **error)
{
struct wave_encoder *encoder = (struct wave_encoder *)_encoder;
void *dst;
dst = pcm_buffer_get(&encoder->buffer, encoder->buffer_length + length);
void *dst = growing_fifo_write(&encoder->buffer, length);
#if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
switch (encoder->bits) {
......@@ -232,7 +243,7 @@ wave_encoder_write(struct encoder *_encoder,
#error G_BYTE_ORDER set to G_PDP_ENDIAN is not supported by wave_encoder
#endif
encoder->buffer_length += length;
fifo_buffer_append(encoder->buffer, length);
return true;
}
......@@ -240,16 +251,17 @@ static size_t
wave_encoder_read(struct encoder *_encoder, void *dest, size_t length)
{
struct wave_encoder *encoder = (struct wave_encoder *)_encoder;
uint8_t *buffer = pcm_buffer_get(&encoder->buffer, encoder->buffer_length );
if (length > encoder->buffer_length)
length = encoder->buffer_length;
memcpy(dest, buffer, length);
size_t max_length;
const void *src = fifo_buffer_read(encoder->buffer, &max_length);
if (src == NULL)
return 0;
encoder->buffer_length -= length;
memmove(buffer, buffer + length, encoder->buffer_length);
if (length > max_length)
length = max_length;
memcpy(dest, src, length);
fifo_buffer_consume(encoder->buffer, length);
return length;
}
......@@ -264,6 +276,7 @@ const struct encoder_plugin wave_encoder_plugin = {
.init = wave_encoder_init,
.finish = wave_encoder_finish,
.open = wave_encoder_open,
.close = wave_encoder_close,
.write = wave_encoder_write,
.read = wave_encoder_read,
.get_mime_type = wave_encoder_get_mime_type,
......
......@@ -21,7 +21,6 @@
#include "event_pipe.h"
#include "fd_util.h"
#include "mpd_error.h"
#include "glib_socket.h"
#include <stdbool.h>
#include <assert.h>
......@@ -95,7 +94,11 @@ void event_pipe_init(void)
if (ret < 0)
MPD_ERROR("Couldn't open pipe: %s", strerror(errno));
channel = g_io_channel_new_socket(event_pipe[0]);
#ifndef G_OS_WIN32
channel = g_io_channel_unix_new(event_pipe[0]);
#else
channel = g_io_channel_win32_new_fd(event_pipe[0]);
#endif
g_io_channel_set_encoding(channel, NULL, NULL);
g_io_channel_set_buffered(channel, false);
......
......@@ -58,6 +58,39 @@ fifo_buffer_new(size_t size)
return buffer;
}
static void
fifo_buffer_move(struct fifo_buffer *buffer);
struct fifo_buffer *
fifo_buffer_realloc(struct fifo_buffer *buffer, size_t new_size)
{
if (buffer == NULL)
return new_size > 0
? fifo_buffer_new(new_size)
: NULL;
/* existing data must fit in new size */
assert(new_size >= buffer->end - buffer->start);
if (new_size == 0) {
fifo_buffer_free(buffer);
return NULL;
}
/* compress the buffer when we're shrinking and the tail of
the buffer would exceed the new size */
if (buffer->end > new_size)
fifo_buffer_move(buffer);
/* existing data must fit in new size: second check */
assert(buffer->end <= new_size);
buffer = g_realloc(buffer, sizeof(*buffer) - sizeof(buffer->buffer) +
new_size);
buffer->size = new_size;
return buffer;
}
void
fifo_buffer_free(struct fifo_buffer *buffer)
{
......@@ -66,6 +99,22 @@ fifo_buffer_free(struct fifo_buffer *buffer)
g_free(buffer);
}
size_t
fifo_buffer_capacity(const struct fifo_buffer *buffer)
{
assert(buffer != NULL);
return buffer->size;
}
size_t
fifo_buffer_available(const struct fifo_buffer *buffer)
{
assert(buffer != NULL);
return buffer->end - buffer->start;
}
void
fifo_buffer_clear(struct fifo_buffer *buffer)
{
......
......@@ -57,12 +57,37 @@ struct fifo_buffer *
fifo_buffer_new(size_t size);
/**
* Change the capacity of the #fifo_buffer, while preserving existing
* data.
*
* @param buffer the old buffer, may be NULL
* @param new_size the requested new size of the #fifo_buffer; must
* not be smaller than the data which is stored in the old buffer
* @return the new buffer, may be NULL if the requested new size is 0
*/
struct fifo_buffer *
fifo_buffer_realloc(struct fifo_buffer *buffer, size_t new_size);
/**
* Frees the resources consumed by this #fifo_buffer object.
*/
void
fifo_buffer_free(struct fifo_buffer *buffer);
/**
* Return the capacity of the buffer, i.e. the size that was passed to
* fifo_buffer_new().
*/
size_t
fifo_buffer_capacity(const struct fifo_buffer *buffer);
/**
* Return the number of bytes currently stored in the buffer.
*/
size_t
fifo_buffer_available(const struct fifo_buffer *buffer);
/**
* Clears all data currently in this #fifo_buffer object. This does
* not overwrite the actuall buffer; it just resets the internal
* pointers.
......
......@@ -141,7 +141,7 @@ convert_filter_set(struct filter *_filter,
assert(audio_format_valid(&filter->out_audio_format));
assert(out_audio_format != NULL);
assert(audio_format_valid(out_audio_format));
assert(filter->in_audio_format.reverse_endian == 0);
assert(!filter->in_audio_format.reverse_endian);
filter->out_audio_format = *out_audio_format;
}
......@@ -195,7 +195,7 @@ replay_gain_filter_filter(struct filter *_filter,
memcpy(dest, src, src_size);
success = pcm_volume(dest, src_size, &filter->audio_format,
success = pcm_volume(dest, src_size, filter->audio_format.format,
filter->volume);
if (!success) {
g_set_error(error_r, replay_gain_quark(), 0,
......
......@@ -116,7 +116,7 @@ volume_filter_filter(struct filter *_filter, const void *src, size_t src_size,
memcpy(dest, src, src_size);
success = pcm_volume(dest, src_size, &filter->audio_format,
success = pcm_volume(dest, src_size, filter->audio_format.format,
filter->volume);
if (!success) {
g_set_error(error_r, volume_quark(), 0,
......
/*
* Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "growing_fifo.h"
#include "fifo_buffer.h"
#include <assert.h>
#include <string.h>
/**
* Align buffer sizes at 8 kB boundaries. Must be a power of two.
*/
static const size_t GROWING_FIFO_ALIGN = 8192;
/**
* Align the specified size to the next #GROWING_FIFO_ALIGN boundary.
*/
static size_t
align(size_t size)
{
return ((size - 1) | (GROWING_FIFO_ALIGN - 1)) + 1;
}
struct fifo_buffer *
growing_fifo_new(void)
{
return fifo_buffer_new(GROWING_FIFO_ALIGN);
}
void *
growing_fifo_write(struct fifo_buffer **buffer_p, size_t length)
{
assert(buffer_p != NULL);
struct fifo_buffer *buffer = *buffer_p;
assert(buffer != NULL);
size_t max_length;
void *p = fifo_buffer_write(buffer, &max_length);
if (p != NULL && max_length >= length)
return p;
/* grow */
size_t new_size = fifo_buffer_available(buffer) + length;
assert(new_size > fifo_buffer_capacity(buffer));
*buffer_p = buffer = fifo_buffer_realloc(buffer, align(new_size));
/* try again */
p = fifo_buffer_write(buffer, &max_length);
assert(p != NULL);
assert(max_length >= length);
return p;
}
void
growing_fifo_append(struct fifo_buffer **buffer_p,
const void *data, size_t length)
{
void *p = growing_fifo_write(buffer_p, length);
memcpy(p, data, length);
fifo_buffer_append(*buffer_p, length);
}
/*
* Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/** \file
*
* Helper functions for our FIFO buffer library (fifo_buffer.h) that
* allows growing the buffer on demand.
*
* This library is not thread safe.
*/
#ifndef MPD_GROWING_FIFO_H
#define MPD_GROWING_FIFO_H
#include <stddef.h>
struct fifo_buffer;
/**
* Allocate a new #fifo_buffer with the default size.
*/
struct fifo_buffer *
growing_fifo_new(void);
/**
* Prepares writing to the buffer, see fifo_buffer_write() for
* details. The difference is that this function will automatically
* grow the buffer if it is too small.
*
* The caller is responsible for limiting the capacity of the buffer.
*
* @param length the number of bytes that will be written
* @return a pointer to the end of the buffer (will not be NULL)
*/
void *
growing_fifo_write(struct fifo_buffer **buffer_p, size_t length);
/**
* A helper function that combines growing_fifo_write(), memcpy(),
* fifo_buffer_append().
*/
void
growing_fifo_append(struct fifo_buffer **buffer_p,
const void *data, size_t length);
#endif
......@@ -26,7 +26,6 @@
#include "input_internal.h"
#include "input_plugin.h"
#include "refcount.h"
#include "pcm_buffer.h"
#include <stdio.h>
#include <stdint.h>
......@@ -46,8 +45,6 @@ struct input_cdio_paranoia {
CdIo_t *cdio;
cdrom_paranoia_t *para;
int endian;
lsn_t lsn_from, lsn_to;
int lsn_relofs;
......@@ -55,8 +52,6 @@ struct input_cdio_paranoia {
char buffer[CDIO_CD_FRAMESIZE_RAW];
int buffer_lsn;
struct pcm_buffer conv_buffer;
};
static inline GQuark
......@@ -70,8 +65,6 @@ input_cdio_close(struct input_stream *is)
{
struct input_cdio_paranoia *i = (struct input_cdio_paranoia *)is;
pcm_buffer_deinit(&i->conv_buffer);
if (i->para)
cdio_paranoia_free(i->para);
if (i->drv)
......@@ -168,7 +161,6 @@ input_cdio_open(const char *uri,
i->cdio = NULL;
i->para = NULL;
i->trackno = parsed_uri.track;
pcm_buffer_init(&i->conv_buffer);
/* get list of CD's supporting CD-DA */
char *device = parsed_uri.device[0] != 0
......@@ -202,21 +194,24 @@ input_cdio_open(const char *uri,
return NULL;
}
i->endian = data_bigendianp(i->drv);
switch (i->endian) {
bool reverse_endian;
switch (data_bigendianp(i->drv)) {
case -1:
g_debug("cdda: drive returns unknown audio data, assuming Little Endian");
i->endian = 0;
g_debug("cdda: drive returns unknown audio data");
reverse_endian = false;
break;
case 0:
g_debug("cdda: drive returns audio data Little Endian.");
reverse_endian = G_BYTE_ORDER == G_BIG_ENDIAN;
break;
case 1:
g_debug("cdda: drive returns audio data Big Endian.");
reverse_endian = G_BYTE_ORDER == G_LITTLE_ENDIAN;
break;
default:
g_set_error(error_r, cdio_quark(), 0,
"Drive returns unknown data type %d", i->endian);
"Drive returns unknown data type %d",
data_bigendianp(i->drv));
input_cdio_close(&i->base);
return NULL;
}
......@@ -244,7 +239,9 @@ input_cdio_open(const char *uri,
i->base.size = (i->lsn_to - i->lsn_from + 1) * CDIO_CD_FRAMESIZE_RAW;
/* hack to make MPD select the "pcm" decoder plugin */
i->base.mime = g_strdup("audio/x-mpd-cdda-pcm");
i->base.mime = g_strdup(reverse_endian
? "audio/x-mpd-cdda-pcm-reverse"
: "audio/x-mpd-cdda-pcm");
return &i->base;
}
......@@ -287,17 +284,6 @@ input_cdio_seek(struct input_stream *is,
return true;
}
static inline size_t
pcm16_to_wave(uint16_t *dst16, const uint16_t *src16, size_t length)
{
size_t cnt = length >> 1;
while (cnt > 0) {
*dst16++ = GUINT16_TO_LE(*src16++);
cnt--;
}
return length;
}
static size_t
input_cdio_read(struct input_stream *is, void *ptr, size_t length,
GError **error_r)
......@@ -335,13 +321,6 @@ input_cdio_read(struct input_stream *is, void *ptr, size_t length,
"paranoia read error. Stopping.");
return 0;
}
//do the swapping if nessesary
if (cis->endian != 0) {
uint16_t *conv_buffer = pcm_buffer_get(&cis->conv_buffer, CDIO_CD_FRAMESIZE_RAW );
/* do endian conversion ! */
pcm16_to_wave( conv_buffer, (uint16_t*) rbuf, CDIO_CD_FRAMESIZE_RAW);
rbuf = (int16_t *)conv_buffer;
}
//store current buffer
memcpy(cis->buffer, rbuf, CDIO_CD_FRAMESIZE_RAW);
cis->buffer_lsn = cis->lsn_relofs;
......
......@@ -28,6 +28,10 @@
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "input_ffmpeg"
#ifndef AV_VERSION_INT
#define AV_VERSION_INT(a, b, c) (a<<16 | b<<8 | c)
#endif
struct input_ffmpeg {
struct input_stream base;
......
......@@ -280,7 +280,20 @@ input_soup_open(const char *uri,
s->current_consumed = 0;
s->total_buffered = 0;
#if GCC_CHECK_VERSION(4,6)
#pragma GCC diagnostic push
/* the libsoup macro SOUP_METHOD_GET discards the "const"
attribute of the g_intern_static_string() return value;
don't make the gcc warning fatal: */
#pragma GCC diagnostic ignored "-Wcast-qual"
#endif
s->msg = soup_message_new(SOUP_METHOD_GET, uri);
#if GCC_CHECK_VERSION(4,6)
#pragma GCC diagnostic pop
#endif
soup_message_set_flags(s->msg, SOUP_MESSAGE_NO_REDIRECT);
soup_message_headers_append(s->msg->request_headers, "User-Agent",
......
......@@ -34,6 +34,7 @@ static struct {
void
io_thread_run(void)
{
assert(io_thread_inside());
assert(io.context != NULL);
assert(io.loop != NULL);
......@@ -43,6 +44,11 @@ io_thread_run(void)
static gpointer
io_thread_func(G_GNUC_UNUSED gpointer arg)
{
/* lock+unlock to synchronize with io_thread_start(), to be
sure that io.thread is set */
g_mutex_lock(io.mutex);
g_mutex_unlock(io.mutex);
io_thread_run();
return NULL;
}
......@@ -67,7 +73,9 @@ io_thread_start(GError **error_r)
assert(io.loop != NULL);
assert(io.thread == NULL);
g_mutex_lock(io.mutex);
io.thread = g_thread_create(io_thread_func, NULL, true, error_r);
g_mutex_unlock(io.mutex);
if (io.thread == NULL)
return false;
......@@ -123,15 +131,23 @@ io_thread_idle_add(GSourceFunc function, gpointer data)
return id;
}
guint
GSource *
io_thread_timeout_add(guint interval_ms, GSourceFunc function, gpointer data)
{
GSource *source = g_timeout_source_new(interval_ms);
g_source_set_callback(source, function, data, NULL);
g_source_attach(source, io.context);
return source;
}
GSource *
io_thread_timeout_add_seconds(guint interval,
GSourceFunc function, gpointer data)
{
GSource *source = g_timeout_source_new_seconds(interval);
g_source_set_callback(source, function, data, NULL);
guint id = g_source_attach(source, io.context);
g_source_unref(source);
return id;
g_source_attach(source, io.context);
return source;
}
struct call_data {
......
......@@ -62,7 +62,12 @@ io_thread_inside(void);
guint
io_thread_idle_add(GSourceFunc function, gpointer data);
guint
G_GNUC_MALLOC
GSource *
io_thread_timeout_add(guint interval_ms, GSourceFunc function, gpointer data);
G_GNUC_MALLOC
GSource *
io_thread_timeout_add_seconds(guint interval,
GSourceFunc function, gpointer data);
......
......@@ -39,11 +39,12 @@
#include "player_control.h"
#include "stats.h"
#include "sig_handlers.h"
#include "audio.h"
#include "audio_config.h"
#include "output_all.h"
#include "volume.h"
#include "log.h"
#include "permission.h"
#include "pcm_resample.h"
#include "replay_gain_config.h"
#include "decoder_list.h"
#include "input_init.h"
......@@ -53,8 +54,6 @@
#include "dbUtils.h"
#include "zeroconf.h"
#include "event_pipe.h"
#include "dirvec.h"
#include "songvec.h"
#include "tag_pool.h"
#include "mpd_error.h"
......@@ -345,8 +344,6 @@ int mpd_main(int argc, char *argv[])
io_thread_init();
winsock_init();
idle_init();
dirvec_init();
songvec_init();
tag_pool_init();
config_global_init();
......@@ -403,6 +400,13 @@ int mpd_main(int argc, char *argv[])
#ifdef ENABLE_ARCHIVE
archive_plugin_init_all();
#endif
if (!pcm_resample_global_init(&error)) {
g_warning("%s", error->message);
g_error_free(error);
return EXIT_FAILURE;
}
decoder_plugin_init_all();
update_global_init();
......@@ -526,8 +530,6 @@ int mpd_main(int argc, char *argv[])
#endif
config_global_finish();
tag_pool_deinit();
songvec_deinit();
dirvec_deinit();
idle_deinit();
stats_global_finish();
io_thread_deinit();
......
......@@ -27,7 +27,6 @@
#include <glib.h>
#define WINVER 0x0501
#include <windows.h>
static int service_argc;
......
......@@ -31,6 +31,10 @@
#include <assert.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <dirent.h>
static char *music_dir;
static size_t music_dir_length;
......@@ -52,24 +56,50 @@ strdup_chop_slash(const char *path_fs)
}
static void
check_directory(const char *path)
{
struct stat st;
if (stat(path, &st) < 0) {
g_warning("Failed to stat directory \"%s\": %s",
path, g_strerror(errno));
return;
}
if (!S_ISDIR(st.st_mode)) {
g_warning("Not a directory: %s", path);
return;
}
#ifndef WIN32
char *x = g_build_filename(path, ".", NULL);
if (stat(x, &st) < 0 && errno == EACCES)
g_warning("No permission to traverse (\"execute\") directory: %s",
path);
g_free(x);
#endif
DIR *dir = opendir(path);
if (dir == NULL && errno == EACCES)
g_warning("No permission to read directory: %s", path);
else
closedir(dir);
}
static void
mapper_set_music_dir(const char *path)
{
check_directory(path);
music_dir = strdup_chop_slash(path);
music_dir_length = strlen(music_dir);
if (!g_file_test(music_dir, G_FILE_TEST_IS_DIR))
g_warning("music directory is not a directory: \"%s\"",
music_dir);
}
static void
mapper_set_playlist_dir(const char *path)
{
playlist_dir = g_strdup(path);
check_directory(path);
if (!g_file_test(playlist_dir, G_FILE_TEST_IS_DIR))
g_warning("playlist directory is not a directory: \"%s\"",
playlist_dir);
playlist_dir = g_strdup(path);
}
void mapper_init(const char *_music_dir, const char *_playlist_dir)
......
......@@ -20,6 +20,7 @@
#include "config.h"
#include "mixer_api.h"
#include "output_api.h"
#include "event_pipe.h"
#include <glib.h>
#include <alsa/asoundlib.h>
......@@ -28,6 +29,15 @@
#define VOLUME_MIXER_ALSA_CONTROL_DEFAULT "PCM"
#define VOLUME_MIXER_ALSA_INDEX_DEFAULT 0
struct alsa_mixer_source {
GSource source;
snd_mixer_t *mixer;
/** a linked list of all registered GPollFD objects */
GSList *fds;
};
struct alsa_mixer {
/** the base mixer class */
struct mixer base;
......@@ -41,6 +51,8 @@ struct alsa_mixer {
long volume_min;
long volume_max;
int volume_set;
struct alsa_mixer_source *source;
};
/**
......@@ -52,6 +64,161 @@ alsa_mixer_quark(void)
return g_quark_from_static_string("alsa_mixer");
}
/*
* GSource helper functions
*
*/
static GSList **
find_fd(GSList **list_r, int fd)
{
while (true) {
GSList *list = *list_r;
if (list == NULL)
return NULL;
GPollFD *p = list->data;
if (p->fd == fd)
return list_r;
list_r = &list->next;
}
}
static void
alsa_mixer_update_fd(struct alsa_mixer_source *source, const struct pollfd *p,
GSList **old_r)
{
GSList **found_r = find_fd(old_r, p->fd);
if (found_r == NULL) {
/* new fd */
GPollFD *q = g_new(GPollFD, 1);
q->fd = p->fd;
q->events = p->events;
g_source_add_poll(&source->source, q);
source->fds = g_slist_prepend(source->fds, q);
return;
}
GSList *found = *found_r;
*found_r = found->next;
GPollFD *q = found->data;
if (q->events != p->events) {
/* refresh events */
g_source_remove_poll(&source->source, q);
q->events = p->events;
g_source_add_poll(&source->source, q);
}
found->next = source->fds;
source->fds = found;
}
static void
alsa_mixer_update_fds(struct alsa_mixer_source *source)
{
int count = snd_mixer_poll_descriptors_count(source->mixer);
if (count < 0)
count = 0;
struct pollfd *pfds = g_new(struct pollfd, count);
count = snd_mixer_poll_descriptors(source->mixer, pfds, count);
if (count < 0)
count = 0;
GSList *old = source->fds;
source->fds = NULL;
for (int i = 0; i < count; ++i)
alsa_mixer_update_fd(source, &pfds[i], &old);
g_free(pfds);
for (; old != NULL; old = old->next) {
GPollFD *q = old->data;
g_source_remove_poll(&source->source, q);
g_free(q);
}
g_slist_free(old);
}
/*
* GSource methods
*
*/
static gboolean
alsa_mixer_source_prepare(GSource *_source, G_GNUC_UNUSED gint *timeout_r)
{
struct alsa_mixer_source *source = (struct alsa_mixer_source *)_source;
alsa_mixer_update_fds(source);
return false;
}
static gboolean
alsa_mixer_source_check(GSource *_source)
{
struct alsa_mixer_source *source = (struct alsa_mixer_source *)_source;
for (const GSList *i = source->fds; i != NULL; i = i->next) {
const GPollFD *poll_fd = i->data;
if (poll_fd->revents != 0)
return true;
}
return false;
}
static gboolean
alsa_mixer_source_dispatch(GSource *_source,
G_GNUC_UNUSED GSourceFunc callback,
G_GNUC_UNUSED gpointer user_data)
{
struct alsa_mixer_source *source = (struct alsa_mixer_source *)_source;
snd_mixer_handle_events(source->mixer);
return true;
}
static void
alsa_mixer_source_finalize(GSource *_source)
{
struct alsa_mixer_source *source = (struct alsa_mixer_source *)_source;
for (GSList *i = source->fds; i != NULL; i = i->next)
g_free(i->data);
g_slist_free(source->fds);
}
static GSourceFuncs alsa_mixer_source_funcs = {
.prepare = alsa_mixer_source_prepare,
.check = alsa_mixer_source_check,
.dispatch = alsa_mixer_source_dispatch,
.finalize = alsa_mixer_source_finalize,
};
/*
* libasound callbacks
*
*/
static int
alsa_mixer_elem_callback(G_GNUC_UNUSED snd_mixer_elem_t *elem, unsigned mask)
{
if (mask & SND_CTL_EVENT_MASK_VALUE)
event_pipe_emit(PIPE_EVENT_MIXER);
return 0;
}
/*
* mixer_plugin methods
*
*/
static struct mixer *
alsa_mixer_init(G_GNUC_UNUSED void *ao, const struct config_param *param,
G_GNUC_UNUSED GError **error_r)
......@@ -81,34 +248,28 @@ alsa_mixer_finish(struct mixer *data)
snd_config_update_free_global();
}
static void
alsa_mixer_close(struct mixer *data)
G_GNUC_PURE
static snd_mixer_elem_t *
alsa_mixer_lookup_elem(snd_mixer_t *handle, const char *name, unsigned idx)
{
struct alsa_mixer *am = (struct alsa_mixer *)data;
assert(am->handle != NULL);
for (snd_mixer_elem_t *elem = snd_mixer_first_elem(handle);
elem != NULL; elem = snd_mixer_elem_next(elem)) {
if (snd_mixer_elem_get_type(elem) == SND_MIXER_ELEM_SIMPLE &&
g_ascii_strcasecmp(snd_mixer_selem_get_name(elem),
name) == 0 &&
snd_mixer_selem_get_index(elem) == idx)
return elem;
}
snd_mixer_close(am->handle);
return NULL;
}
static bool
alsa_mixer_open(struct mixer *data, GError **error_r)
alsa_mixer_setup(struct alsa_mixer *am, GError **error_r)
{
struct alsa_mixer *am = (struct alsa_mixer *)data;
int err;
snd_mixer_elem_t *elem;
am->volume_set = -1;
err = snd_mixer_open(&am->handle, 0);
if (err < 0) {
g_set_error(error_r, alsa_mixer_quark(), err,
"snd_mixer_open() failed: %s", snd_strerror(err));
return false;
}
if ((err = snd_mixer_attach(am->handle, am->device)) < 0) {
alsa_mixer_close(data);
g_set_error(error_r, alsa_mixer_quark(), err,
"failed to attach to %s: %s",
am->device, snd_strerror(err));
......@@ -117,7 +278,6 @@ alsa_mixer_open(struct mixer *data, GError **error_r)
if ((err = snd_mixer_selem_register(am->handle, NULL,
NULL)) < 0) {
alsa_mixer_close(data);
g_set_error(error_r, alsa_mixer_quark(), err,
"snd_mixer_selem_register() failed: %s",
snd_strerror(err));
......@@ -125,38 +285,69 @@ alsa_mixer_open(struct mixer *data, GError **error_r)
}
if ((err = snd_mixer_load(am->handle)) < 0) {
alsa_mixer_close(data);
g_set_error(error_r, alsa_mixer_quark(), err,
"snd_mixer_load() failed: %s\n",
snd_strerror(err));
return false;
}
elem = snd_mixer_first_elem(am->handle);
am->elem = alsa_mixer_lookup_elem(am->handle, am->control, am->index);
if (am->elem == NULL) {
g_set_error(error_r, alsa_mixer_quark(), 0,
"no such mixer control: %s", am->control);
return false;
}
snd_mixer_selem_get_playback_volume_range(am->elem,
&am->volume_min,
&am->volume_max);
snd_mixer_elem_set_callback(am->elem, alsa_mixer_elem_callback);
while (elem) {
if (snd_mixer_elem_get_type(elem) == SND_MIXER_ELEM_SIMPLE) {
if ((g_ascii_strcasecmp(am->control,
snd_mixer_selem_get_name(elem)) == 0) &&
(am->index == snd_mixer_selem_get_index(elem))) {
break;
}
}
elem = snd_mixer_elem_next(elem);
am->source = (struct alsa_mixer_source *)
g_source_new(&alsa_mixer_source_funcs, sizeof(*am->source));
am->source->mixer = am->handle;
am->source->fds = NULL;
g_source_attach(&am->source->source, g_main_context_default());
return true;
}
static bool
alsa_mixer_open(struct mixer *data, GError **error_r)
{
struct alsa_mixer *am = (struct alsa_mixer *)data;
int err;
am->volume_set = -1;
err = snd_mixer_open(&am->handle, 0);
if (err < 0) {
g_set_error(error_r, alsa_mixer_quark(), err,
"snd_mixer_open() failed: %s", snd_strerror(err));
return false;
}
if (elem) {
am->elem = elem;
snd_mixer_selem_get_playback_volume_range(am->elem,
&am->volume_min,
&am->volume_max);
return true;
if (!alsa_mixer_setup(am, error_r)) {
snd_mixer_close(am->handle);
return false;
}
alsa_mixer_close(data);
g_set_error(error_r, alsa_mixer_quark(), 0,
"no such mixer control: %s", am->control);
return false;
return true;
}
static void
alsa_mixer_close(struct mixer *data)
{
struct alsa_mixer *am = (struct alsa_mixer *)data;
assert(am->handle != NULL);
g_source_destroy(&am->source->source);
g_source_unref(&am->source->source);
snd_mixer_elem_set_callback(am->elem, NULL);
snd_mixer_close(am->handle);
}
static int
......
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment