Commit 99527051 authored by Max Kellermann's avatar Max Kellermann

Merge branch 'v0.18.x'

parents bed98303 57e0cc54
ver 0.18.5 (2013/11/23)
* configuration
- fix crash when db_file is configured without music_directory
- fix crash on "stats" without db_file/music_directory
* database
- proxy: auto-reload statistics
- proxy: provide "db_update" in "stats" response
* input
- curl: work around stream resume bug (fixed in libcurl 7.32.0)
* decoder
- fluidsynth: auto-detect by default
* clip 24 bit data from libsamplerate
* fix ia64, mipsel and other little-endian architectures
* fix build failures due to missing includes
* fix build failure with static libmpdclient
ver 0.18.4 (2013/11/13) ver 0.18.4 (2013/11/13)
* decoder * decoder
- dsdiff: fix byte order bug - dsdiff: fix byte order bug
......
...@@ -874,10 +874,12 @@ AM_CONDITIONAL(HAVE_FLAC, test x$enable_flac = xyes) ...@@ -874,10 +874,12 @@ AM_CONDITIONAL(HAVE_FLAC, test x$enable_flac = xyes)
enable_flac_encoder=$enable_flac enable_flac_encoder=$enable_flac
dnl -------------------------------- FluidSynth ------------------------------- dnl -------------------------------- FluidSynth -------------------------------
MPD_AUTO_PKG(fluidsynth, FLUIDSYNTH, [fluidsynth >= 1.1],
[fluidsynth decoder], [fluidsynth not found])
if test x$enable_fluidsynth = xyes; then if test x$enable_fluidsynth = xyes; then
PKG_CHECK_MODULES(FLUIDSYNTH, [fluidsynth >= 1.1], AC_DEFINE(ENABLE_FLUIDSYNTH, 1, [Define for fluidsynth support])
AC_DEFINE(ENABLE_FLUIDSYNTH, 1, [Define for fluidsynth support]),
enable_fluidsynth=no)
fi fi
AM_CONDITIONAL(ENABLE_FLUIDSYNTH, test x$enable_fluidsynth = xyes) AM_CONDITIONAL(ENABLE_FLUIDSYNTH, test x$enable_fluidsynth = xyes)
......
...@@ -148,12 +148,12 @@ DatabaseGlobalOpen(Error &error) ...@@ -148,12 +148,12 @@ DatabaseGlobalOpen(Error &error)
return true; return true;
} }
time_t bool
db_get_mtime(void) db_exists()
{ {
assert(db != nullptr); assert(db != nullptr);
assert(db_is_open); assert(db_is_open);
assert(db_is_simple()); assert(db_is_simple());
return ((SimpleDatabase *)db)->GetLastModified(); return ((SimpleDatabase *)db)->GetUpdateStamp() > 0;
} }
...@@ -30,6 +30,8 @@ ...@@ -30,6 +30,8 @@
#include "tag/TagType.h" #include "tag/TagType.h"
#include "Compiler.h" #include "Compiler.h"
#include <time.h>
struct config_param; struct config_param;
struct DatabaseSelection; struct DatabaseSelection;
struct db_visitor; struct db_visitor;
...@@ -132,6 +134,13 @@ public: ...@@ -132,6 +134,13 @@ public:
virtual bool GetStats(const DatabaseSelection &selection, virtual bool GetStats(const DatabaseSelection &selection,
DatabaseStats &stats, DatabaseStats &stats,
Error &error) const = 0; Error &error) const = 0;
/**
* Returns the time stamp of the last database update.
* Returns 0 if that is not not known/available.
*/
gcc_pure
virtual time_t GetUpdateStamp() const = 0;
}; };
struct DatabasePlugin { struct DatabasePlugin {
......
...@@ -63,24 +63,12 @@ bool ...@@ -63,24 +63,12 @@ bool
db_save(Error &error); db_save(Error &error);
/** /**
* May only be used if db_is_simple() returns true.
*/
gcc_pure
time_t
db_get_mtime(void);
/**
* Returns true if there is a valid database file on the disk. * Returns true if there is a valid database file on the disk.
* *
* May only be used if db_is_simple() returns true. * May only be used if db_is_simple() returns true.
*/ */
gcc_pure gcc_pure
static inline bool bool
db_exists(void) db_exists();
{
/* mtime is set only if the database file was loaded or saved
successfully */
return db_get_mtime() > 0;
}
#endif #endif
...@@ -133,7 +133,9 @@ void mapper_finish(void) ...@@ -133,7 +133,9 @@ void mapper_finish(void)
const char * const char *
mapper_get_music_directory_utf8(void) mapper_get_music_directory_utf8(void)
{ {
return music_dir_utf8.c_str(); return music_dir_utf8.empty()
? nullptr
: music_dir_utf8.c_str();
} }
const AllocatedPath & const AllocatedPath &
......
...@@ -41,7 +41,8 @@ mapper_init(AllocatedPath &&music_dir, AllocatedPath &&playlist_dir); ...@@ -41,7 +41,8 @@ mapper_init(AllocatedPath &&music_dir, AllocatedPath &&playlist_dir);
void mapper_finish(void); void mapper_finish(void);
/** /**
* Return the absolute path of the music directory encoded in UTF-8. * Return the absolute path of the music directory encoded in UTF-8 or
* nullptr if no music directory was configured.
*/ */
gcc_const gcc_const
const char * const char *
......
...@@ -30,59 +30,75 @@ ...@@ -30,59 +30,75 @@
#include <glib.h> #include <glib.h>
struct stats stats; static GTimer *uptime;
static DatabaseStats stats;
void stats_global_init(void) void stats_global_init(void)
{ {
stats.timer = g_timer_new(); uptime = g_timer_new();
} }
void stats_global_finish(void) void stats_global_finish(void)
{ {
g_timer_destroy(stats.timer); g_timer_destroy(uptime);
} }
void stats_update(void) void stats_update(void)
{ {
assert(GetDatabase() != nullptr);
Error error; Error error;
DatabaseStats stats2; DatabaseStats stats2;
const DatabaseSelection selection("", true); const DatabaseSelection selection("", true);
if (GetDatabase()->GetStats(selection, stats2, error)) { if (GetDatabase()->GetStats(selection, stats2, error)) {
stats.song_count = stats2.song_count; stats = stats2;
stats.song_duration = stats2.total_duration;
stats.artist_count = stats2.artist_count;
stats.album_count = stats2.album_count;
} else { } else {
LogError(error); LogError(error);
stats.song_count = 0; stats.Clear();
stats.song_duration = 0;
stats.artist_count = 0;
stats.album_count = 0;
} }
} }
void static void
stats_print(Client &client) db_stats_print(Client &client)
{ {
assert(GetDatabase() != nullptr);
if (!db_is_simple())
/* reload statistics if we're using the "proxy"
database plugin */
/* TODO: move this into the "proxy" database plugin as
an "idle" handler */
stats_update();
client_printf(client, client_printf(client,
"artists: %u\n" "artists: %u\n"
"albums: %u\n" "albums: %u\n"
"songs: %i\n" "songs: %u\n"
"uptime: %li\n" "db_playtime: %lu\n",
"playtime: %li\n"
"db_playtime: %li\n",
stats.artist_count, stats.artist_count,
stats.album_count, stats.album_count,
stats.song_count, stats.song_count,
(long)g_timer_elapsed(stats.timer, NULL), stats.total_duration);
(long)(client.player_control.GetTotalPlayTime() + 0.5),
stats.song_duration); const time_t update_stamp = GetDatabase()->GetUpdateStamp();
if (update_stamp > 0)
client_printf(client,
"db_update: %lu\n",
(unsigned long)update_stamp);
}
if (db_is_simple()) void
stats_print(Client &client)
{
client_printf(client, client_printf(client,
"db_update: %li\n", "uptime: %lu\n"
(long)db_get_mtime()); "playtime: %lu\n",
(unsigned long)g_timer_elapsed(uptime, NULL),
(unsigned long)(client.player_control.GetTotalPlayTime() + 0.5));
if (GetDatabase() != nullptr)
db_stats_print(client);
} }
...@@ -21,26 +21,6 @@ ...@@ -21,26 +21,6 @@
#define MPD_STATS_HXX #define MPD_STATS_HXX
class Client; class Client;
typedef struct _GTimer GTimer;
struct stats {
GTimer *timer;
/** number of song files in the music directory */
unsigned song_count;
/** sum of all song durations in the music directory (in
seconds) */
unsigned long song_duration;
/** number of distinct artist names in the music directory */
unsigned artist_count;
/** number of distinct album names in the music directory */
unsigned album_count;
};
extern struct stats stats;
void stats_global_init(void); void stats_global_init(void);
......
...@@ -190,18 +190,16 @@ iso9660_input_read(InputStream *is, void *ptr, size_t size, ...@@ -190,18 +190,16 @@ iso9660_input_read(InputStream *is, void *ptr, size_t size,
Error &error) Error &error)
{ {
Iso9660InputStream *iis = (Iso9660InputStream *)is; Iso9660InputStream *iis = (Iso9660InputStream *)is;
int toread, readed = 0; int readed = 0;
int no_blocks, cur_block; int no_blocks, cur_block;
size_t left_bytes = iis->statbuf->size - is->offset; size_t left_bytes = iis->statbuf->size - is->offset;
size = (size * ISO_BLOCKSIZE) / ISO_BLOCKSIZE; size = (size * ISO_BLOCKSIZE) / ISO_BLOCKSIZE;
if (left_bytes < size) { if (left_bytes < size) {
toread = left_bytes;
no_blocks = CEILING(left_bytes,ISO_BLOCKSIZE); no_blocks = CEILING(left_bytes,ISO_BLOCKSIZE);
} else { } else {
toread = size; no_blocks = size / ISO_BLOCKSIZE;
no_blocks = toread / ISO_BLOCKSIZE;
} }
if (no_blocks > 0) { if (no_blocks > 0) {
......
...@@ -46,6 +46,9 @@ class ProxyDatabase : public Database { ...@@ -46,6 +46,9 @@ class ProxyDatabase : public Database {
struct mpd_connection *connection; struct mpd_connection *connection;
Directory *root; Directory *root;
/* this is mutable because GetStats() must be "const" */
mutable time_t update_stamp;
public: public:
static Database *Create(const config_param &param, static Database *Create(const config_param &param,
Error &error); Error &error);
...@@ -71,6 +74,10 @@ public: ...@@ -71,6 +74,10 @@ public:
DatabaseStats &stats, DatabaseStats &stats,
Error &error) const override; Error &error) const override;
virtual time_t GetUpdateStamp() const override {
return update_stamp;
}
private: private:
bool Configure(const config_param &param, Error &error); bool Configure(const config_param &param, Error &error);
...@@ -237,6 +244,7 @@ ProxyDatabase::Open(Error &error) ...@@ -237,6 +244,7 @@ ProxyDatabase::Open(Error &error)
return false; return false;
root = Directory::NewRoot(); root = Directory::NewRoot();
update_stamp = 0;
return true; return true;
} }
...@@ -631,6 +639,8 @@ ProxyDatabase::GetStats(const DatabaseSelection &selection, ...@@ -631,6 +639,8 @@ ProxyDatabase::GetStats(const DatabaseSelection &selection,
if (stats2 == nullptr) if (stats2 == nullptr)
return CheckError(connection, error); return CheckError(connection, error);
update_stamp = (time_t)mpd_stats_get_db_update_time(stats2);
stats.song_count = mpd_stats_get_number_of_songs(stats2); stats.song_count = mpd_stats_get_number_of_songs(stats2);
stats.total_duration = mpd_stats_get_db_play_time(stats2); stats.total_duration = mpd_stats_get_db_play_time(stats2);
stats.artist_count = mpd_stats_get_number_of_artists(stats2); stats.artist_count = mpd_stats_get_number_of_artists(stats2);
......
...@@ -26,8 +26,6 @@ ...@@ -26,8 +26,6 @@
#include <cassert> #include <cassert>
#include <time.h>
struct Directory; struct Directory;
class SimpleDatabase : public Database { class SimpleDatabase : public Database {
...@@ -55,11 +53,6 @@ public: ...@@ -55,11 +53,6 @@ public:
bool Save(Error &error); bool Save(Error &error);
gcc_pure
time_t GetLastModified() const {
return mtime;
}
static Database *Create(const config_param &param, static Database *Create(const config_param &param,
Error &error); Error &error);
...@@ -85,6 +78,10 @@ public: ...@@ -85,6 +78,10 @@ public:
DatabaseStats &stats, DatabaseStats &stats,
Error &error) const override; Error &error) const override;
virtual time_t GetUpdateStamp() const override {
return mtime;
}
protected: protected:
bool Configure(const config_param &param, Error &error); bool Configure(const config_param &param, Error &error);
......
...@@ -294,7 +294,6 @@ dsdiff_read_metadata_extra(Decoder *decoder, InputStream &is, ...@@ -294,7 +294,6 @@ dsdiff_read_metadata_extra(Decoder *decoder, InputStream &is,
if (!dsdiff_read_chunk_header(decoder, is, chunk_header)) if (!dsdiff_read_chunk_header(decoder, is, chunk_header))
return false; return false;
} }
chunk_size = 0;
} }
/* done processing chunk headers, process tags if any */ /* done processing chunk headers, process tags if any */
...@@ -385,10 +384,10 @@ dsdiff_decode_chunk(Decoder &decoder, InputStream &is, ...@@ -385,10 +384,10 @@ dsdiff_decode_chunk(Decoder &decoder, InputStream &is,
while (chunk_size > 0) { while (chunk_size > 0) {
/* see how much aligned data from the remaining chunk /* see how much aligned data from the remaining chunk
fits into the local buffer */ fits into the local buffer */
unsigned now_frames = buffer_frames;
size_t now_size = buffer_size; size_t now_size = buffer_size;
if (chunk_size < (uint64_t)now_size) { if (chunk_size < (uint64_t)now_size) {
now_frames = (unsigned)chunk_size / frame_size; unsigned now_frames =
(unsigned)chunk_size / frame_size;
now_size = now_frames * frame_size; now_size = now_frames * frame_size;
} }
......
...@@ -240,10 +240,10 @@ dsf_decode_chunk(Decoder &decoder, InputStream &is, ...@@ -240,10 +240,10 @@ dsf_decode_chunk(Decoder &decoder, InputStream &is,
while (chunk_size > 0) { while (chunk_size > 0) {
/* see how much aligned data from the remaining chunk /* see how much aligned data from the remaining chunk
fits into the local buffer */ fits into the local buffer */
unsigned now_frames = buffer_frames;
size_t now_size = buffer_size; size_t now_size = buffer_size;
if (chunk_size < (uint64_t)now_size) { if (chunk_size < (uint64_t)now_size) {
now_frames = (unsigned)chunk_size / frame_size; unsigned now_frames =
(unsigned)chunk_size / frame_size;
now_size = now_frames * frame_size; now_size = now_frames * frame_size;
} }
......
...@@ -22,6 +22,8 @@ ...@@ -22,6 +22,8 @@
#include "OggSyncState.hxx" #include "OggSyncState.hxx"
#include "util/Error.hxx" #include "util/Error.hxx"
#include <stdio.h>
bool bool
OggFindEOS(OggSyncState &oy, ogg_stream_state &os, ogg_packet &packet) OggFindEOS(OggSyncState &oy, ogg_stream_state &os, ogg_packet &packet)
{ {
......
...@@ -273,12 +273,27 @@ public: ...@@ -273,12 +273,27 @@ public:
SocketAction(CURL_SOCKET_TIMEOUT, 0); SocketAction(CURL_SOCKET_TIMEOUT, 0);
} }
/**
* This is a kludge to allow pausing/resuming a stream with
* libcurl < 7.32.0. Read the curl_easy_pause manpage for
* more information.
*/
void ResumeSockets() {
int running_handles;
curl_multi_socket_all(multi, &running_handles);
}
private: private:
static int TimerFunction(CURLM *multi, long timeout_ms, void *userp); static int TimerFunction(CURLM *multi, long timeout_ms, void *userp);
virtual void OnTimeout() override; virtual void OnTimeout() override;
}; };
/**
* libcurl version number encoded in a 24 bit integer.
*/
static unsigned curl_version_num;
/** libcurl should accept "ICY 200 OK" */ /** libcurl should accept "ICY 200 OK" */
static struct curl_slist *http_200_aliases; static struct curl_slist *http_200_aliases;
...@@ -330,6 +345,13 @@ input_curl_resume(struct input_curl *c) ...@@ -330,6 +345,13 @@ input_curl_resume(struct input_curl *c)
if (c->paused) { if (c->paused) {
c->paused = false; c->paused = false;
curl_easy_pause(c->easy, CURLPAUSE_CONT); curl_easy_pause(c->easy, CURLPAUSE_CONT);
if (curl_version_num < 0x072000)
/* libcurl older than 7.32.0 does not update
its sockets after curl_easy_pause(); force
libcurl to do it now */
curl_multi->ResumeSockets();
curl_multi->InvalidateSockets(); curl_multi->InvalidateSockets();
} }
} }
...@@ -586,6 +608,16 @@ input_curl_init(const config_param &param, Error &error) ...@@ -586,6 +608,16 @@ input_curl_init(const config_param &param, Error &error)
return false; return false;
} }
const auto version_info = curl_version_info(CURLVERSION_FIRST);
if (version_info != nullptr) {
FormatDebug(curl_domain, "version %s", version_info->version);
if (version_info->features & CURL_VERSION_SSL)
FormatDebug(curl_domain, "with %s",
version_info->ssl_version);
curl_version_num = version_info->version_num;
}
http_200_aliases = curl_slist_append(http_200_aliases, "ICY 200 OK"); http_200_aliases = curl_slist_append(http_200_aliases, "ICY 200 OK");
proxy = param.GetBlockValue("proxy"); proxy = param.GetBlockValue("proxy");
......
...@@ -148,3 +148,26 @@ PcmResampler::Resample32(unsigned channels, unsigned src_rate, ...@@ -148,3 +148,26 @@ PcmResampler::Resample32(unsigned channels, unsigned src_rate,
src_rate, src_buffer, src_size, src_rate, src_buffer, src_size,
dest_rate, dest_size_r); dest_rate, dest_size_r);
} }
const int32_t *
PcmResampler::Resample24(unsigned channels, unsigned src_rate,
const int32_t *src_buffer, size_t src_size,
unsigned dest_rate, size_t *dest_size_r,
Error &error_r)
{
#ifdef HAVE_LIBSAMPLERATE
if (pcm_resample_lsr_enabled())
return pcm_resample_lsr_24(this, channels,
src_rate, src_buffer, src_size,
dest_rate, dest_size_r,
error_r);
#else
(void)error_r;
#endif
/* reuse the 32 bit code - the resampler code doesn't care if
the upper 8 bits are actually used */
return pcm_resample_fallback_32(buffer, channels,
src_rate, src_buffer, src_size,
dest_rate, dest_size_r);
}
...@@ -124,13 +124,7 @@ struct PcmResampler { ...@@ -124,13 +124,7 @@ struct PcmResampler {
const int32_t *Resample24(unsigned channels, unsigned src_rate, const int32_t *Resample24(unsigned channels, unsigned src_rate,
const int32_t *src_buffer, size_t src_size, const int32_t *src_buffer, size_t src_size,
unsigned dest_rate, size_t *dest_size_r, unsigned dest_rate, size_t *dest_size_r,
Error &error_r) Error &error_r);
{
/* reuse the 32 bit code - the resampler code doesn't care if
the upper 8 bits are actually used */
return Resample32(channels, src_rate, src_buffer, src_size,
dest_rate, dest_size_r, error_r);
}
}; };
bool bool
......
...@@ -69,6 +69,15 @@ pcm_resample_lsr_32(PcmResampler *state, ...@@ -69,6 +69,15 @@ pcm_resample_lsr_32(PcmResampler *state,
unsigned dest_rate, size_t *dest_size_r, unsigned dest_rate, size_t *dest_size_r,
Error &error); Error &error);
const int32_t *
pcm_resample_lsr_24(PcmResampler *state,
unsigned channels,
unsigned src_rate,
const int32_t *src_buffer,
size_t src_size,
unsigned dest_rate, size_t *dest_size_r,
Error &error);
#endif #endif
const int16_t * const int16_t *
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include "config.h" #include "config.h"
#include "PcmResampleInternal.hxx" #include "PcmResampleInternal.hxx"
#include "PcmUtils.hxx"
#include "util/ASCII.hxx" #include "util/ASCII.hxx"
#include "util/Error.hxx" #include "util/Error.hxx"
#include "util/Domain.hxx" #include "util/Domain.hxx"
...@@ -283,3 +284,27 @@ pcm_resample_lsr_32(PcmResampler *state, ...@@ -283,3 +284,27 @@ pcm_resample_lsr_32(PcmResampler *state,
return dest_buffer; return dest_buffer;
} }
const int32_t *
pcm_resample_lsr_24(PcmResampler *state,
unsigned channels,
unsigned src_rate,
const int32_t *src_buffer, size_t src_size,
unsigned dest_rate, size_t *dest_size_r,
Error &error)
{
const auto result = pcm_resample_lsr_32(state, channels,
src_rate, src_buffer, src_size,
dest_rate, dest_size_r,
error);
if (result != nullptr)
/* src_float_to_int_array() clamps for 32 bit
integers; now make sure everything's fine for 24
bit */
/* TODO: eliminate the 32 bit clamp to reduce overhead */
PcmClampN<int32_t, int32_t, 24>(const_cast<int32_t *>(result),
result,
*dest_size_r / sizeof(*result));
return result;
}
...@@ -63,4 +63,16 @@ PcmClamp(U x) ...@@ -63,4 +63,16 @@ PcmClamp(U x)
return T(x); return T(x);
} }
/**
* Check if the values in this buffer are within the range of the
* provided bit size, and clamps them whenever necessary.
*/
template<typename T, typename U, unsigned bits>
static inline void
PcmClampN(T *dest, const U *src, unsigned n)
{
while (n-- > 0)
*dest++ = PcmClamp<T, U, bits>(*src++);
}
#endif #endif
...@@ -33,11 +33,23 @@ ...@@ -33,11 +33,23 @@
#include <stdint.h> #include <stdint.h>
#if defined(__i386__) || defined(__x86_64__) || defined(__ARMEL__) #if defined(__i386__) || defined(__x86_64__) || defined(__ARMEL__)
#define IS_LITTLE_ENDIAN true /* well-known little-endian */
#define IS_BIG_ENDIAN false # define IS_LITTLE_ENDIAN true
# define IS_BIG_ENDIAN false
#elif defined(__MIPSEB__)
/* well-known big-endian */
# define IS_LITTLE_ENDIAN false
# define IS_BIG_ENDIAN true
#else #else
#define IS_LITTLE_ENDIAN false /* generic compile-time check */
#define IS_BIG_ENDIAN true # include <endian.h>
# if __BYTE_ORDER == __LITTLE_ENDIAN
# define IS_LITTLE_ENDIAN true
# define IS_BIG_ENDIAN false
# else
# define IS_LITTLE_ENDIAN false
# define IS_BIG_ENDIAN true
# endif
#endif #endif
static inline constexpr bool static inline constexpr bool
......
...@@ -104,6 +104,11 @@ socketpair_cloexec_nonblock(int domain, int type, int protocol, int sv[2]); ...@@ -104,6 +104,11 @@ socketpair_cloexec_nonblock(int domain, int type, int protocol, int sv[2]);
#endif #endif
#ifdef HAVE_LIBMPDCLIENT
/* Avoid symbol conflict with statically linked libmpdclient */
#define socket_cloexec_nonblock socket_cloexec_nonblock_noconflict
#endif
/** /**
* Wrapper for socket(), which sets the CLOEXEC and the NONBLOCK flag * Wrapper for socket(), which sets the CLOEXEC and the NONBLOCK flag
* (atomically if supported by the OS). * (atomically if supported by the OS).
......
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