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:
parent
098991f8e8
commit
6b1c54ef96
@ -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])],
|
||||
|
@ -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
26
src/pcm_resample.c
Normal 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
63
src/pcm_resample.h
Normal 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
|
61
src/pcm_resample_fallback.c
Normal file
61
src/pcm_resample_fallback.c
Normal 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;
|
||||
}
|
140
src/pcm_resample_libsamplerate.c
Normal file
140
src/pcm_resample_libsamplerate.c
Normal 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;
|
||||
}
|
168
src/pcm_utils.c
168
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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user