Merge branch 'v0.18.x'
This commit is contained in:
commit
99527051b5
16
NEWS
16
NEWS
@ -1,3 +1,19 @@
|
||||
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)
|
||||
* decoder
|
||||
- dsdiff: fix byte order bug
|
||||
|
@ -874,10 +874,12 @@ AM_CONDITIONAL(HAVE_FLAC, test x$enable_flac = xyes)
|
||||
enable_flac_encoder=$enable_flac
|
||||
|
||||
dnl -------------------------------- FluidSynth -------------------------------
|
||||
|
||||
MPD_AUTO_PKG(fluidsynth, FLUIDSYNTH, [fluidsynth >= 1.1],
|
||||
[fluidsynth decoder], [fluidsynth not found])
|
||||
|
||||
if test x$enable_fluidsynth = xyes; then
|
||||
PKG_CHECK_MODULES(FLUIDSYNTH, [fluidsynth >= 1.1],
|
||||
AC_DEFINE(ENABLE_FLUIDSYNTH, 1, [Define for fluidsynth support]),
|
||||
enable_fluidsynth=no)
|
||||
AC_DEFINE(ENABLE_FLUIDSYNTH, 1, [Define for fluidsynth support])
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL(ENABLE_FLUIDSYNTH, test x$enable_fluidsynth = xyes)
|
||||
|
@ -148,12 +148,12 @@ DatabaseGlobalOpen(Error &error)
|
||||
return true;
|
||||
}
|
||||
|
||||
time_t
|
||||
db_get_mtime(void)
|
||||
bool
|
||||
db_exists()
|
||||
{
|
||||
assert(db != nullptr);
|
||||
assert(db_is_open);
|
||||
assert(db_is_simple());
|
||||
|
||||
return ((SimpleDatabase *)db)->GetLastModified();
|
||||
return ((SimpleDatabase *)db)->GetUpdateStamp() > 0;
|
||||
}
|
||||
|
@ -30,6 +30,8 @@
|
||||
#include "tag/TagType.h"
|
||||
#include "Compiler.h"
|
||||
|
||||
#include <time.h>
|
||||
|
||||
struct config_param;
|
||||
struct DatabaseSelection;
|
||||
struct db_visitor;
|
||||
@ -132,6 +134,13 @@ public:
|
||||
virtual bool GetStats(const DatabaseSelection &selection,
|
||||
DatabaseStats &stats,
|
||||
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 {
|
||||
|
@ -62,25 +62,13 @@ db_get_directory(const char *name);
|
||||
bool
|
||||
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.
|
||||
*
|
||||
* May only be used if db_is_simple() returns true.
|
||||
*/
|
||||
gcc_pure
|
||||
static inline bool
|
||||
db_exists(void)
|
||||
{
|
||||
/* mtime is set only if the database file was loaded or saved
|
||||
successfully */
|
||||
return db_get_mtime() > 0;
|
||||
}
|
||||
bool
|
||||
db_exists();
|
||||
|
||||
#endif
|
||||
|
@ -133,7 +133,9 @@ void mapper_finish(void)
|
||||
const char *
|
||||
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 &
|
||||
|
@ -41,7 +41,8 @@ mapper_init(AllocatedPath &&music_dir, AllocatedPath &&playlist_dir);
|
||||
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
|
||||
const char *
|
||||
|
@ -30,59 +30,75 @@
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
struct stats stats;
|
||||
static GTimer *uptime;
|
||||
static DatabaseStats stats;
|
||||
|
||||
void stats_global_init(void)
|
||||
{
|
||||
stats.timer = g_timer_new();
|
||||
uptime = g_timer_new();
|
||||
}
|
||||
|
||||
void stats_global_finish(void)
|
||||
{
|
||||
g_timer_destroy(stats.timer);
|
||||
g_timer_destroy(uptime);
|
||||
}
|
||||
|
||||
void stats_update(void)
|
||||
{
|
||||
assert(GetDatabase() != nullptr);
|
||||
|
||||
Error error;
|
||||
|
||||
DatabaseStats stats2;
|
||||
|
||||
const DatabaseSelection selection("", true);
|
||||
if (GetDatabase()->GetStats(selection, stats2, error)) {
|
||||
stats.song_count = stats2.song_count;
|
||||
stats.song_duration = stats2.total_duration;
|
||||
stats.artist_count = stats2.artist_count;
|
||||
stats.album_count = stats2.album_count;
|
||||
stats = stats2;
|
||||
} else {
|
||||
LogError(error);
|
||||
|
||||
stats.song_count = 0;
|
||||
stats.song_duration = 0;
|
||||
stats.artist_count = 0;
|
||||
stats.album_count = 0;
|
||||
stats.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
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,
|
||||
"artists: %u\n"
|
||||
"albums: %u\n"
|
||||
"songs: %u\n"
|
||||
"db_playtime: %lu\n",
|
||||
stats.artist_count,
|
||||
stats.album_count,
|
||||
stats.song_count,
|
||||
stats.total_duration);
|
||||
|
||||
const time_t update_stamp = GetDatabase()->GetUpdateStamp();
|
||||
if (update_stamp > 0)
|
||||
client_printf(client,
|
||||
"db_update: %lu\n",
|
||||
(unsigned long)update_stamp);
|
||||
}
|
||||
|
||||
void
|
||||
stats_print(Client &client)
|
||||
{
|
||||
client_printf(client,
|
||||
"artists: %u\n"
|
||||
"albums: %u\n"
|
||||
"songs: %i\n"
|
||||
"uptime: %li\n"
|
||||
"playtime: %li\n"
|
||||
"db_playtime: %li\n",
|
||||
stats.artist_count,
|
||||
stats.album_count,
|
||||
stats.song_count,
|
||||
(long)g_timer_elapsed(stats.timer, NULL),
|
||||
(long)(client.player_control.GetTotalPlayTime() + 0.5),
|
||||
stats.song_duration);
|
||||
"uptime: %lu\n"
|
||||
"playtime: %lu\n",
|
||||
(unsigned long)g_timer_elapsed(uptime, NULL),
|
||||
(unsigned long)(client.player_control.GetTotalPlayTime() + 0.5));
|
||||
|
||||
if (db_is_simple())
|
||||
client_printf(client,
|
||||
"db_update: %li\n",
|
||||
(long)db_get_mtime());
|
||||
if (GetDatabase() != nullptr)
|
||||
db_stats_print(client);
|
||||
}
|
||||
|
@ -21,26 +21,6 @@
|
||||
#define MPD_STATS_HXX
|
||||
|
||||
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);
|
||||
|
||||
|
@ -190,18 +190,16 @@ iso9660_input_read(InputStream *is, void *ptr, size_t size,
|
||||
Error &error)
|
||||
{
|
||||
Iso9660InputStream *iis = (Iso9660InputStream *)is;
|
||||
int toread, readed = 0;
|
||||
int readed = 0;
|
||||
int no_blocks, cur_block;
|
||||
size_t left_bytes = iis->statbuf->size - is->offset;
|
||||
|
||||
size = (size * ISO_BLOCKSIZE) / ISO_BLOCKSIZE;
|
||||
|
||||
if (left_bytes < size) {
|
||||
toread = left_bytes;
|
||||
no_blocks = CEILING(left_bytes,ISO_BLOCKSIZE);
|
||||
} else {
|
||||
toread = size;
|
||||
no_blocks = toread / ISO_BLOCKSIZE;
|
||||
no_blocks = size / ISO_BLOCKSIZE;
|
||||
}
|
||||
if (no_blocks > 0) {
|
||||
|
||||
|
@ -46,6 +46,9 @@ class ProxyDatabase : public Database {
|
||||
struct mpd_connection *connection;
|
||||
Directory *root;
|
||||
|
||||
/* this is mutable because GetStats() must be "const" */
|
||||
mutable time_t update_stamp;
|
||||
|
||||
public:
|
||||
static Database *Create(const config_param ¶m,
|
||||
Error &error);
|
||||
@ -71,6 +74,10 @@ public:
|
||||
DatabaseStats &stats,
|
||||
Error &error) const override;
|
||||
|
||||
virtual time_t GetUpdateStamp() const override {
|
||||
return update_stamp;
|
||||
}
|
||||
|
||||
private:
|
||||
bool Configure(const config_param ¶m, Error &error);
|
||||
|
||||
@ -237,6 +244,7 @@ ProxyDatabase::Open(Error &error)
|
||||
return false;
|
||||
|
||||
root = Directory::NewRoot();
|
||||
update_stamp = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -631,6 +639,8 @@ ProxyDatabase::GetStats(const DatabaseSelection &selection,
|
||||
if (stats2 == nullptr)
|
||||
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.total_duration = mpd_stats_get_db_play_time(stats2);
|
||||
stats.artist_count = mpd_stats_get_number_of_artists(stats2);
|
||||
|
@ -26,8 +26,6 @@
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include <time.h>
|
||||
|
||||
struct Directory;
|
||||
|
||||
class SimpleDatabase : public Database {
|
||||
@ -55,11 +53,6 @@ public:
|
||||
|
||||
bool Save(Error &error);
|
||||
|
||||
gcc_pure
|
||||
time_t GetLastModified() const {
|
||||
return mtime;
|
||||
}
|
||||
|
||||
static Database *Create(const config_param ¶m,
|
||||
Error &error);
|
||||
|
||||
@ -85,6 +78,10 @@ public:
|
||||
DatabaseStats &stats,
|
||||
Error &error) const override;
|
||||
|
||||
virtual time_t GetUpdateStamp() const override {
|
||||
return mtime;
|
||||
}
|
||||
|
||||
protected:
|
||||
bool Configure(const config_param ¶m, Error &error);
|
||||
|
||||
|
@ -294,7 +294,6 @@ dsdiff_read_metadata_extra(Decoder *decoder, InputStream &is,
|
||||
if (!dsdiff_read_chunk_header(decoder, is, chunk_header))
|
||||
return false;
|
||||
}
|
||||
chunk_size = 0;
|
||||
}
|
||||
/* done processing chunk headers, process tags if any */
|
||||
|
||||
@ -385,10 +384,10 @@ dsdiff_decode_chunk(Decoder &decoder, InputStream &is,
|
||||
while (chunk_size > 0) {
|
||||
/* see how much aligned data from the remaining chunk
|
||||
fits into the local buffer */
|
||||
unsigned now_frames = buffer_frames;
|
||||
size_t now_size = buffer_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;
|
||||
}
|
||||
|
||||
|
@ -240,10 +240,10 @@ dsf_decode_chunk(Decoder &decoder, InputStream &is,
|
||||
while (chunk_size > 0) {
|
||||
/* see how much aligned data from the remaining chunk
|
||||
fits into the local buffer */
|
||||
unsigned now_frames = buffer_frames;
|
||||
size_t now_size = buffer_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;
|
||||
}
|
||||
|
||||
|
@ -22,6 +22,8 @@
|
||||
#include "OggSyncState.hxx"
|
||||
#include "util/Error.hxx"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
bool
|
||||
OggFindEOS(OggSyncState &oy, ogg_stream_state &os, ogg_packet &packet)
|
||||
{
|
||||
|
@ -273,12 +273,27 @@ public:
|
||||
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:
|
||||
static int TimerFunction(CURLM *multi, long timeout_ms, void *userp);
|
||||
|
||||
virtual void OnTimeout() override;
|
||||
};
|
||||
|
||||
/**
|
||||
* libcurl version number encoded in a 24 bit integer.
|
||||
*/
|
||||
static unsigned curl_version_num;
|
||||
|
||||
/** libcurl should accept "ICY 200 OK" */
|
||||
static struct curl_slist *http_200_aliases;
|
||||
|
||||
@ -330,6 +345,13 @@ input_curl_resume(struct input_curl *c)
|
||||
if (c->paused) {
|
||||
c->paused = false;
|
||||
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();
|
||||
}
|
||||
}
|
||||
@ -586,6 +608,16 @@ input_curl_init(const config_param ¶m, Error &error)
|
||||
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");
|
||||
|
||||
proxy = param.GetBlockValue("proxy");
|
||||
|
@ -148,3 +148,26 @@ PcmResampler::Resample32(unsigned channels, unsigned src_rate,
|
||||
src_rate, src_buffer, src_size,
|
||||
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 {
|
||||
const int32_t *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)
|
||||
{
|
||||
/* 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);
|
||||
}
|
||||
Error &error_r);
|
||||
};
|
||||
|
||||
bool
|
||||
|
@ -69,6 +69,15 @@ pcm_resample_lsr_32(PcmResampler *state,
|
||||
unsigned dest_rate, size_t *dest_size_r,
|
||||
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
|
||||
|
||||
const int16_t *
|
||||
|
@ -19,6 +19,7 @@
|
||||
|
||||
#include "config.h"
|
||||
#include "PcmResampleInternal.hxx"
|
||||
#include "PcmUtils.hxx"
|
||||
#include "util/ASCII.hxx"
|
||||
#include "util/Error.hxx"
|
||||
#include "util/Domain.hxx"
|
||||
@ -283,3 +284,27 @@ pcm_resample_lsr_32(PcmResampler *state,
|
||||
|
||||
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)
|
||||
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
|
||||
|
@ -33,11 +33,23 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#if defined(__i386__) || defined(__x86_64__) || defined(__ARMEL__)
|
||||
#define IS_LITTLE_ENDIAN true
|
||||
#define IS_BIG_ENDIAN false
|
||||
/* well-known little-endian */
|
||||
# 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
|
||||
#define IS_LITTLE_ENDIAN false
|
||||
#define IS_BIG_ENDIAN true
|
||||
/* generic compile-time check */
|
||||
# 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
|
||||
|
||||
static inline constexpr bool
|
||||
|
@ -104,6 +104,11 @@ socketpair_cloexec_nonblock(int domain, int type, int protocol, int sv[2]);
|
||||
|
||||
#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
|
||||
* (atomically if supported by the OS).
|
||||
|
Loading…
Reference in New Issue
Block a user