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.
This commit is contained in:
Max Kellermann 2008-10-23 20:00:51 +02:00
parent 098991f8e8
commit 6b1c54ef96
8 changed files with 308 additions and 176 deletions

View File

@ -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])],

View File

@ -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

26
src/pcm_resample.c Normal file
View File

@ -0,0 +1,26 @@
/* the Music Player Daemon (MPD)
* Copyright (C) 2008 Max Kellermann <max@duempel.org>
* 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 <string.h>
void pcm_resample_init(struct pcm_resample_state *state)
{
memset(state, 0, sizeof(*state));
}

63
src/pcm_resample.h Normal file
View File

@ -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 <max@duempel.org>
* 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 <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#ifdef HAVE_LIBSAMPLERATE
#include <samplerate.h>
#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

View File

@ -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 <max@duempel.org>
* 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;
}

View File

@ -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 <max@duempel.org>
* 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 <stdlib.h>
#include <string.h>
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;
}

View File

@ -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);
}

View File

@ -19,28 +19,16 @@
#ifndef PCM_UTILS_H
#define PCM_UTILS_H
#include "../config.h"
#include "pcm_resample.h"
#include "pcm_dither.h"
#include <stdint.h>
#include <stddef.h>
#ifdef HAVE_LIBSAMPLERATE
#include <samplerate.h>
#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;