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)
|
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)
|
|||||||
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)
|
|||||||
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 @@
|
|||||||
#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:
|
|||||||
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 {
|
||||||
|
@ -62,25 +62,13 @@ db_get_directory(const char *name);
|
|||||||
bool
|
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)
|
|||||||
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);
|
|||||||
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 @@
|
|||||||
|
|
||||||
#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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
void
|
||||||
stats_print(Client &client)
|
stats_print(Client &client)
|
||||||
{
|
{
|
||||||
client_printf(client,
|
client_printf(client,
|
||||||
"artists: %u\n"
|
"uptime: %lu\n"
|
||||||
"albums: %u\n"
|
"playtime: %lu\n",
|
||||||
"songs: %i\n"
|
(unsigned long)g_timer_elapsed(uptime, NULL),
|
||||||
"uptime: %li\n"
|
(unsigned long)(client.player_control.GetTotalPlayTime() + 0.5));
|
||||||
"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);
|
|
||||||
|
|
||||||
if (db_is_simple())
|
if (GetDatabase() != nullptr)
|
||||||
client_printf(client,
|
db_stats_print(client);
|
||||||
"db_update: %li\n",
|
|
||||||
(long)db_get_mtime());
|
|
||||||
}
|
}
|
||||||
|
@ -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,
|
|||||||
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 {
|
|||||||
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 ¶m,
|
static Database *Create(const config_param ¶m,
|
||||||
Error &error);
|
Error &error);
|
||||||
@ -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 ¶m, Error &error);
|
bool Configure(const config_param ¶m, 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,
|
|||||||
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 @@
|
|||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
struct Directory;
|
struct Directory;
|
||||||
|
|
||||||
class SimpleDatabase : public Database {
|
class SimpleDatabase : public Database {
|
||||||
@ -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 ¶m,
|
static Database *Create(const config_param ¶m,
|
||||||
Error &error);
|
Error &error);
|
||||||
|
|
||||||
@ -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 ¶m, Error &error);
|
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))
|
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,
|
|||||||
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,
|
|||||||
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 @@
|
|||||||
#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:
|
|||||||
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)
|
|||||||
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 ¶m, 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,
|
|||||||
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 {
|
|||||||
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,
|
|||||||
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 @@
|
|||||||
|
|
||||||
#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,
|
|||||||
|
|
||||||
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)
|
|||||||
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 @@
|
|||||||
#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]);
|
|||||||
|
|
||||||
#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).
|
||||||
|
Loading…
Reference in New Issue
Block a user