/*-
* Copyright (c) 2008 Ariff Abdullah <ariff@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <speex/speex_resampler.h>
#ifndef SND_USE_FXDIV
#define SND_USE_FXDIV
#include "snd_fxdiv_gen.h"
#endif
#ifdef FLOATING_POINT
#include <math.h>
#define AFMT_SPEEX_NE AFMT_S32_NE
#else
#define AFMT_SPEEX_NE AFMT_S16_NE
#endif
#define AFMT_S_NE AFMT_SPEEX_NE
#define Z_QUALITY_MIN SPEEX_RESAMPLER_QUALITY_MIN
#define Z_QUALITY_MAX SPEEX_RESAMPLER_QUALITY_MAX
#define Z_QUALITY_ZOH Z_QUALITY_MIN
#define Z_QUALITY_LINEAR Z_QUALITY_MIN
#define Z_QUALITY_SINC SPEEX_RESAMPLER_QUALITY_DEFAULT
#define Z_SPEEX_BUFSZ 8192
struct feeder_speex_info {
SpeexResamplerState *spx;
uint32_t src;
uint32_t dst;
uint32_t channels;
uint32_t align;
uint32_t maxfeed;
int quality;
#ifdef FLOATING_POINT
float buffer[Z_SPEEX_BUFSZ];
#else
spx_int16_t buffer[Z_SPEEX_BUFSZ];
#endif
};
static int
feeder_speex_resampler_reset(struct feeder_speex_info *info)
{
if (info->spx != NULL) {
speex_resampler_destroy(info->spx);
info->spx = NULL;
}
info->spx = speex_resampler_init(info->channels, info->src, info->dst,
info->quality, NULL);
if (info->spx == NULL)
return (ENOMEM);
info->align = sizeof(info->buffer[0]) * info->channels;
info->maxfeed = SND_FXDIV(sizeof(info->buffer) /
sizeof(info->buffer[0]), info->channels);
return (0);
}
static int
feeder_speex_resampler_get(struct pcm_feeder *f, int what)
{
struct feeder_speex_info *info;
int ret;
info = f->data;
switch (what) {
case FEEDRATE_SRC:
ret = info->src;
break;
case FEEDRATE_DST:
ret = info->dst;
break;
case FEEDRATE_QUALITY:
ret = info->quality;
break;
case FEEDRATE_CHANNELS:
ret = info->channels;
break;
default:
return (-1);
break;
}
return (ret);
}
static int
feeder_speex_resampler_set(struct pcm_feeder *f, int what, int32_t value)
{
struct feeder_speex_info *info;
info = f->data;
switch (what) {
case FEEDRATE_SRC:
if (info->src == (uint32_t)value)
return (0);
info->src = (uint32_t)value;
break;
case FEEDRATE_DST:
if (info->dst == (uint32_t)value)
return (0);
info->dst = (uint32_t)value;
break;
case FEEDRATE_QUALITY:
if (info->quality == value)
return (0);
info->quality = value;
break;
case FEEDRATE_CHANNELS:
if (info->channels == (uint32_t)value)
return (0);
info->channels = (uint32_t)value;
break;
default:
return (EINVAL);
break;
}
return (feeder_speex_resampler_reset(info));
}
static int
feeder_speex_resampler_init(struct pcm_feeder *f)
{
struct feeder_speex_info *info;
int error;
if (f->desc->in != f->desc->out ||
AFMT_ENCODING(f->desc->in) != AFMT_SPEEX_NE)
return (EINVAL);
info = malloc(sizeof(*info), M_DEVBUF, M_NOWAIT | M_ZERO);
if (info == NULL)
return (ENOMEM);
info->spx = NULL;
info->src = 44100;
info->dst = 48000;
info->channels = AFMT_CHANNEL(f->desc->in);
info->align = sizeof(info->buffer[0]) * info->channels;
info->quality = SPEEX_RESAMPLER_QUALITY_DEFAULT;
error = feeder_speex_resampler_reset(info);
if (error != 0) {
free(info, M_DEVBUF);
return (error);
}
f->data = info;
return (error);
}
static int
feeder_speex_resampler_free(struct pcm_feeder *f)
{
struct feeder_speex_info *info;
info = f->data;
if (info != NULL) {
if (info->spx != NULL) {
speex_resampler_destroy(info->spx);
info->spx = NULL;
}
free(info, M_DEVBUF);
}
return (0);
}
static int
feeder_speex_resampler_feed(struct pcm_feeder *f, struct pcm_channel *c,
uint8_t *b, uint32_t count, void *source)
{
struct feeder_speex_info *info;
uint32_t osz, isz;
info = f->data;
osz = SND_FXDIV(count, info->align);
isz = ((uint64_t)info->src * osz) / info->dst;
if (isz < 1)
return (0);
if (isz > info->maxfeed)
isz = info->maxfeed;
isz = SND_FXDIV(FEEDER_FEED(f->source, c, (uint8_t *)info->buffer,
isz * info->align, source), info->align);
#ifdef FLOATING_POINT
u_int i;
for (i = 0; i < isz * info->channels; i++)
info->buffer[i] = (double)(((spx_int32_t *)info->buffer)[i]) /
(1.0 + INT32_MAX);
speex_resampler_process_interleaved_float(info->spx, info->buffer,
&isz, (float *)b, &osz);
for (i = 0; i < osz * info->channels; i++) {
int64_t v;
v = llrint(((double)((float *)b)[i] * (1.0 + INT32_MAX)) +
0.5);
((spx_int32_t *)b)[i] = (v > INT32_MAX) ? INT32_MAX :
((v < INT32_MIN) ? INT32_MIN : v);
}
#else
speex_resampler_process_interleaved_int(info->spx, info->buffer,
&isz, (spx_int16_t *)b, &osz);
#endif
return (osz * info->align);
}
static struct pcm_feederdesc feeder_rate_desc[] = {
{ FEEDER_RATE, AFMT_SPEEX_NE, AFMT_SPEEX_NE, 0, 0 },
{ 0, 0, 0, 0, 0 },
};
static kobj_method_t feeder_rate_methods[] = {
KOBJMETHOD(feeder_init, feeder_speex_resampler_init),
KOBJMETHOD(feeder_free, feeder_speex_resampler_free),
KOBJMETHOD(feeder_set, feeder_speex_resampler_set),
KOBJMETHOD(feeder_get, feeder_speex_resampler_get),
KOBJMETHOD(feeder_feed, feeder_speex_resampler_feed),
KOBJMETHOD_END
};
FEEDER_DECLARE(feeder_rate, NULL);