/*- * Copyright (c) 2008 Ariff Abdullah * 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 #ifndef SND_USE_FXDIV #define SND_USE_FXDIV #include "snd_fxdiv_gen.h" #endif #ifdef FLOATING_POINT #include #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);