From 6b1c54ef9646abddfbd4960c207b32426300a90c Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Thu, 23 Oct 2008 20:00:51 +0200 Subject: pcm_utils: moved code to pcm_resample.c Separate the resampling code from the rest of pcm_utils.c. Create two sub-libraries: pcm_resample_libsamplerate.c and pcm_resample_fallback.c. --- configure.ac | 2 + src/Makefile.am | 8 ++ src/pcm_resample.c | 26 ++++++ src/pcm_resample.h | 63 +++++++++++++++ src/pcm_resample_fallback.c | 61 ++++++++++++++ src/pcm_resample_libsamplerate.c | 140 ++++++++++++++++++++++++++++++++ src/pcm_utils.c | 168 ++------------------------------------- src/pcm_utils.h | 16 +--- 8 files changed, 308 insertions(+), 176 deletions(-) create mode 100644 src/pcm_resample.c create mode 100644 src/pcm_resample.h create mode 100644 src/pcm_resample_fallback.c create mode 100644 src/pcm_resample_libsamplerate.c diff --git a/configure.ac b/configure.ac index 8fdec792f..76f4badb8 100644 --- a/configure.ac +++ b/configure.ac @@ -390,6 +390,8 @@ if test x$enable_lsr = xyes; then [enable_lsr=no;AC_MSG_WARN([libsamplerate not found -- disabling])]) fi +AM_CONDITIONAL(HAVE_LIBSAMPLERATE, test x$enable_lsr = xyes) + if test x$enable_fifo = xyes; then AC_CHECK_FUNC([mkfifo], [enable_fifo=yes;AC_DEFINE([HAVE_FIFO], 1, [Define to enable support for writing audio to a FIFO])], diff --git a/src/Makefile.am b/src/Makefile.am index f2bf55eb1..47a121484 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -66,6 +66,7 @@ mpd_headers = \ path.h \ mapper.h \ pcm_utils.h \ + pcm_resample.h \ pcm_dither.h \ permission.h \ player_thread.h \ @@ -146,6 +147,7 @@ mpd_SOURCES = \ path.c \ mapper.c \ pcm_utils.c \ + pcm_resample.c \ pcm_dither.c \ permission.c \ player_thread.c \ @@ -176,6 +178,12 @@ mpd_SOURCES = \ stored_playlist.c \ timer.c +if HAVE_LIBSAMPLERATE +mpd_SOURCES += pcm_resample_libsamplerate.c +else +mpd_SOURCES += pcm_resample_fallback.c +endif + if HAVE_ID3TAG mpd_SOURCES += tag_id3.c endif diff --git a/src/pcm_resample.c b/src/pcm_resample.c new file mode 100644 index 000000000..097e13929 --- /dev/null +++ b/src/pcm_resample.c @@ -0,0 +1,26 @@ +/* the Music Player Daemon (MPD) + * Copyright (C) 2008 Max Kellermann + * This project's homepage is: 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "pcm_resample.h" + +#include + +void pcm_resample_init(struct pcm_resample_state *state) +{ + memset(state, 0, sizeof(*state)); +} diff --git a/src/pcm_resample.h b/src/pcm_resample.h new file mode 100644 index 000000000..d6001550a --- /dev/null +++ b/src/pcm_resample.h @@ -0,0 +1,63 @@ +/* the Music Player Daemon (MPD) + * Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com) + * Copyright (C) 2008 Max Kellermann + * This project's homepage is: 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MPD_PCM_RESAMPLE_H +#define MPD_PCM_RESAMPLE_H + +#include "config.h" + +#include +#include +#include + +#ifdef HAVE_LIBSAMPLERATE +#include +#endif + +struct pcm_resample_state { +#ifdef HAVE_LIBSAMPLERATE + SRC_STATE *state; + SRC_DATA data; + size_t data_in_size; + size_t data_out_size; + + struct { + unsigned src_rate; + unsigned dest_rate; + uint8_t channels; + } prev; + + bool error; +#else + /* struct must not be empty */ + int dummy; +#endif +}; + +void pcm_resample_init(struct pcm_resample_state *state); + +size_t +pcm_resample_16(uint8_t channels, + unsigned src_rate, + const int16_t *src_buffer, size_t src_size, + unsigned dest_rate, + int16_t *dest_buffer, size_t dest_size, + struct pcm_resample_state *state); + +#endif diff --git a/src/pcm_resample_fallback.c b/src/pcm_resample_fallback.c new file mode 100644 index 000000000..4ab586160 --- /dev/null +++ b/src/pcm_resample_fallback.c @@ -0,0 +1,61 @@ +/* the Music Player Daemon (MPD) + * Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com) + * Copyright (C) 2008 Max Kellermann + * This project's homepage is: 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "pcm_resample.h" +#include "gcc.h" + +/* resampling code blatantly ripped from ESD */ +size_t +pcm_resample_16(uint8_t channels, + unsigned src_rate, + const int16_t *src_buffer, mpd_unused size_t src_size, + unsigned dest_rate, + int16_t *dest_buffer, size_t dest_size, + mpd_unused struct pcm_resample_state *state) +{ + unsigned src_pos, dest_pos = 0; + unsigned dest_samples = dest_size / 2; + int16_t lsample, rsample; + + switch (channels) { + case 1: + while (dest_pos < dest_samples) { + src_pos = dest_pos * src_rate / dest_rate; + + lsample = src_buffer[src_pos++]; + + dest_buffer[dest_pos++] = lsample; + } + break; + case 2: + while (dest_pos < dest_samples) { + src_pos = dest_pos * src_rate / dest_rate; + src_pos &= ~1; + + lsample = src_buffer[src_pos++]; + rsample = src_buffer[src_pos++]; + + dest_buffer[dest_pos++] = lsample; + dest_buffer[dest_pos++] = rsample; + } + break; + } + + return dest_size; +} diff --git a/src/pcm_resample_libsamplerate.c b/src/pcm_resample_libsamplerate.c new file mode 100644 index 000000000..5321c0e0b --- /dev/null +++ b/src/pcm_resample_libsamplerate.c @@ -0,0 +1,140 @@ +/* the Music Player Daemon (MPD) + * Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com) + * Copyright (C) 2008 Max Kellermann + * This project's homepage is: 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "pcm_resample.h" +#include "conf.h" +#include "log.h" +#include "utils.h" + +#include +#include + +static int pcm_resample_get_converter(void) +{ + const char *conf = getConfigParamValue(CONF_SAMPLERATE_CONVERTER); + long convalgo; + char *test; + const char *test2; + size_t len; + + if (!conf) { + convalgo = SRC_SINC_FASTEST; + goto out; + } + + convalgo = strtol(conf, &test, 10); + if (*test == '\0' && src_get_name(convalgo)) + goto out; + + len = strlen(conf); + for (convalgo = 0 ; ; convalgo++) { + test2 = src_get_name(convalgo); + if (!test2) { + convalgo = SRC_SINC_FASTEST; + break; + } + if (strncasecmp(test2, conf, len) == 0) + goto out; + } + + ERROR("unknown samplerate converter \"%s\"\n", conf); +out: + DEBUG("selecting samplerate converter \"%s\"\n", + src_get_name(convalgo)); + + return convalgo; +} + +size_t +pcm_resample_16(uint8_t channels, + unsigned src_rate, + const int16_t *src_buffer, size_t src_size, + unsigned dest_rate, + int16_t *dest_buffer, size_t dest_size, + struct pcm_resample_state *state) +{ + static int convalgo = -1; + SRC_DATA *data = &state->data; + size_t data_in_size; + size_t data_out_size; + int error; + + if (convalgo < 0) + convalgo = pcm_resample_get_converter(); + + /* (re)set the state/ratio if the in or out format changed */ + if (channels != state->prev.channels || + src_rate != state->prev.src_rate || + dest_rate != state->prev.dest_rate) { + state->error = false; + state->prev.channels = channels; + state->prev.src_rate = src_rate; + state->prev.dest_rate = dest_rate; + + if (state->state) + state->state = src_delete(state->state); + + state->state = src_new(convalgo, channels, &error); + if (!state->state) { + ERROR("cannot create new libsamplerate state: %s\n", + src_strerror(error)); + state->error = true; + return 0; + } + + data->src_ratio = (double)dest_rate / (double)src_rate; + DEBUG("setting samplerate conversion ratio to %.2lf\n", + data->src_ratio); + src_set_ratio(state->state, data->src_ratio); + } + + /* there was an error previously, and nothing has changed */ + if (state->error) + return 0; + + data->input_frames = src_size / 2 / channels; + data_in_size = data->input_frames * sizeof(float) * channels; + if (data_in_size > state->data_in_size) { + state->data_in_size = data_in_size; + data->data_in = xrealloc(data->data_in, data_in_size); + } + + data->output_frames = dest_size / 2 / channels; + data_out_size = data->output_frames * sizeof(float) * channels; + if (data_out_size > state->data_out_size) { + state->data_out_size = data_out_size; + data->data_out = xrealloc(data->data_out, data_out_size); + } + + src_short_to_float_array(src_buffer, data->data_in, + data->input_frames * channels); + + error = src_process(state->state, data); + if (error) { + ERROR("error processing samples with libsamplerate: %s\n", + src_strerror(error)); + state->error = true; + return 0; + } + + src_float_to_short_array(data->data_out, dest_buffer, + data->output_frames_gen * channels); + + return data->output_frames_gen * 2 * channels; +} diff --git a/src/pcm_utils.c b/src/pcm_utils.c index 8d5bed990..6d76aba90 100644 --- a/src/pcm_utils.c +++ b/src/pcm_utils.c @@ -209,166 +209,10 @@ void pcm_convert_init(struct pcm_convert_state *state) { memset(state, 0, sizeof(*state)); + pcm_resample_init(&state->resample); pcm_dither_24_init(&state->dither); } -#ifdef HAVE_LIBSAMPLERATE -static int pcm_resample_get_converter(void) -{ - const char *conf = getConfigParamValue(CONF_SAMPLERATE_CONVERTER); - long convalgo; - char *test; - const char *test2; - size_t len; - - if (!conf) { - convalgo = SRC_SINC_FASTEST; - goto out; - } - - convalgo = strtol(conf, &test, 10); - if (*test == '\0' && src_get_name(convalgo)) - goto out; - - len = strlen(conf); - for (convalgo = 0 ; ; convalgo++) { - test2 = src_get_name(convalgo); - if (!test2) { - convalgo = SRC_SINC_FASTEST; - break; - } - if (strncasecmp(test2, conf, len) == 0) - goto out; - } - - ERROR("unknown samplerate converter \"%s\"\n", conf); -out: - DEBUG("selecting samplerate converter \"%s\"\n", - src_get_name(convalgo)); - - return convalgo; -} -#endif - -#ifdef HAVE_LIBSAMPLERATE -static size_t pcm_resample(int8_t channels, uint32_t inSampleRate, - const int16_t *src, size_t src_size, - uint32_t outSampleRate, int16_t *outBuffer, - size_t outSize, - struct pcm_convert_state *convState) -{ - static int convalgo = -1; - SRC_DATA *data = &convState->data; - size_t dataInSize; - size_t dataOutSize; - int error; - - if (convalgo < 0) - convalgo = pcm_resample_get_converter(); - - /* (re)set the state/ratio if the in or out format changed */ - if ((channels != convState->lastChannels) || - (inSampleRate != convState->lastInSampleRate) || - (outSampleRate != convState->lastOutSampleRate)) { - convState->error = 0; - convState->lastChannels = channels; - convState->lastInSampleRate = inSampleRate; - convState->lastOutSampleRate = outSampleRate; - - if (convState->state) - convState->state = src_delete(convState->state); - - convState->state = src_new(convalgo, channels, &error); - if (!convState->state) { - ERROR("cannot create new libsamplerate state: %s\n", - src_strerror(error)); - convState->error = 1; - return 0; - } - - data->src_ratio = (double)outSampleRate / (double)inSampleRate; - DEBUG("setting samplerate conversion ratio to %.2lf\n", - data->src_ratio); - src_set_ratio(convState->state, data->src_ratio); - } - - /* there was an error previously, and nothing has changed */ - if (convState->error) - return 0; - - data->input_frames = src_size / 2 / channels; - dataInSize = data->input_frames * sizeof(float) * channels; - if (dataInSize > convState->dataInSize) { - convState->dataInSize = dataInSize; - data->data_in = xrealloc(data->data_in, dataInSize); - } - - data->output_frames = outSize / 2 / channels; - dataOutSize = data->output_frames * sizeof(float) * channels; - if (dataOutSize > convState->dataOutSize) { - convState->dataOutSize = dataOutSize; - data->data_out = xrealloc(data->data_out, dataOutSize); - } - - src_short_to_float_array((const short *)src, data->data_in, - data->input_frames * channels); - - error = src_process(convState->state, data); - if (error) { - ERROR("error processing samples with libsamplerate: %s\n", - src_strerror(error)); - convState->error = 1; - return 0; - } - - src_float_to_short_array(data->data_out, (short *)outBuffer, - data->output_frames_gen * channels); - - return data->output_frames_gen * 2 * channels; -} -#else /* !HAVE_LIBSAMPLERATE */ -/* resampling code blatantly ripped from ESD */ -static size_t pcm_resample(int8_t channels, uint32_t inSampleRate, - const int16_t *src, mpd_unused size_t src_size, - uint32_t outSampleRate, char *outBuffer, - size_t outSize, - mpd_unused struct pcm_convert_state *convState) -{ - uint32_t rd_dat = 0; - uint32_t wr_dat = 0; - const int16_t *in = (const int16_t *)src; - int16_t *out = (int16_t *)outBuffer; - uint32_t nlen = outSize / 2; - int16_t lsample, rsample; - - switch (channels) { - case 1: - while (wr_dat < nlen) { - rd_dat = wr_dat * inSampleRate / outSampleRate; - - lsample = in[rd_dat++]; - - out[wr_dat++] = lsample; - } - break; - case 2: - while (wr_dat < nlen) { - rd_dat = wr_dat * inSampleRate / outSampleRate; - rd_dat &= ~1; - - lsample = in[rd_dat++]; - rsample = in[rd_dat++]; - - out[wr_dat++] = lsample; - out[wr_dat++] = rsample; - } - break; - } - - return outSize; -} -#endif /* !HAVE_LIBSAMPLERATE */ - static void pcm_convert_channels_1_to_2(int16_t *dest, const int16_t *src, unsigned num_frames) @@ -542,11 +386,11 @@ size_t pcm_convert(const struct audio_format *inFormat, assert(dest_size >= len); memcpy(outBuffer, buf, len); } else { - len = pcm_resample(outFormat->channels, - inFormat->sample_rate, buf, len, - outFormat->sample_rate, - (int16_t*)outBuffer, - dest_size, convState); + len = pcm_resample_16(outFormat->channels, + inFormat->sample_rate, buf, len, + outFormat->sample_rate, + (int16_t*)outBuffer, + dest_size, &convState->resample); if (len == 0) exit(EXIT_FAILURE); } diff --git a/src/pcm_utils.h b/src/pcm_utils.h index 835ba187f..3599b7076 100644 --- a/src/pcm_utils.h +++ b/src/pcm_utils.h @@ -19,28 +19,16 @@ #ifndef PCM_UTILS_H #define PCM_UTILS_H -#include "../config.h" +#include "pcm_resample.h" #include "pcm_dither.h" #include #include -#ifdef HAVE_LIBSAMPLERATE -#include -#endif - struct audio_format; struct pcm_convert_state { -#ifdef HAVE_LIBSAMPLERATE - SRC_STATE *state; - SRC_DATA data; - size_t dataInSize; - size_t dataOutSize; - int8_t lastChannels; - uint32_t lastInSampleRate; - uint32_t lastOutSampleRate; -#endif + struct pcm_resample_state resample; struct pcm_dither_24 dither; -- cgit v1.2.3