/*-
* Copyright (c) 2009 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 <oss_grc3.c>
#ifndef SND_USE_FXDIV
#define SND_USE_FXDIV
#include "snd_fxdiv_gen.h"
#endif
#define Z_QUALITY_MIN CONFIG_OSS_GRC_MIN_QUALITY
#define Z_QUALITY_MAX CONFIG_OSS_GRC_MAX_QUALITY
#define Z_QUALITY_ZOH Z_QUALITY_MIN
#define Z_QUALITY_LINEAR Z_QUALITY_MIN
#define Z_QUALITY_SINC DEFAULT_GRC_QUALITY
#define Z_GRC3_BUFSZ (8192 * sizeof(int32_t))
#define Z_GRC3_MAXCHAN 8
#define Z_MULTIFORMAT 1
#define AFMT_S_NE AFMT_S32_NE
struct feeder_grc3_info {
uint32_t src;
uint32_t dst;
uint32_t channels;
uint32_t align;
uint32_t maxfeed;
int bit;
int quality;
grc3state_t grc[Z_GRC3_MAXCHAN];
uint8_t buffer[Z_GRC3_BUFSZ];
};
static int
feeder_grc3_reset(struct feeder_grc3_info *info)
{
uint32_t i;
info->align = (info->bit >> 3) * info->channels;
info->maxfeed = SND_FXDIV(sizeof(info->buffer), info->align);
for (i = 0; i < info->channels; i++) {
grc3_reset(&(info->grc[i]));
grc3_setup(&(info->grc[i]), info->src, info->dst);
}
return (0);
}
static int
feeder_grc3_get(struct pcm_feeder *f, int what)
{
struct feeder_grc3_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_grc3_set(struct pcm_feeder *f, int what, int32_t value)
{
struct feeder_grc3_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 (value < Z_QUALITY_MIN || value > Z_QUALITY_MAX)
return (EINVAL);
if (info->quality == value)
return (0);
info->quality = value;
break;
case FEEDRATE_CHANNELS:
if (value > Z_GRC3_MAXCHAN)
return (E2BIG);
if (info->channels == (uint32_t)value)
return (0);
info->channels = (uint32_t)value;
break;
default:
return (EINVAL);
break;
}
return (feeder_grc3_reset(info));
}
static int
feeder_grc3_init(struct pcm_feeder *f)
{
struct feeder_grc3_info *info;
int error;
if (f->desc->in != f->desc->out ||
!(AFMT_ENCODING(f->desc->in) == AFMT_S16_NE ||
AFMT_ENCODING(f->desc->in) == AFMT_S32_NE) ||
AFMT_CHANNEL(f->desc->in) > Z_GRC3_MAXCHAN)
return (EINVAL);
info = malloc(sizeof(*info), M_DEVBUF, M_NOWAIT | M_ZERO);
if (info == NULL)
return (ENOMEM);
info->src = 48000;
info->dst = 48000;
info->channels = AFMT_CHANNEL(f->desc->in);
info->bit = AFMT_BIT(f->desc->out);
info->quality = Z_QUALITY_SINC;
error = feeder_grc3_reset(info);
if (error != 0) {
free(info, M_DEVBUF);
return (error);
}
f->data = info;
return (error);
}
static int
feeder_grc3_free(struct pcm_feeder *f)
{
struct feeder_grc3_info *info;
info = f->data;
if (info != NULL)
free(info, M_DEVBUF);
return (0);
}
static int
feeder_grc3_feed(struct pcm_feeder *f, struct pcm_channel *c,
uint8_t *b, uint32_t count, void *source)
{
struct feeder_grc3_info *info;
uint32_t i, 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);
#if 0
/* 32bit -> 24bit */
for (i = 0; info->bit == 32 && i < (isz * info->channels); i++)
((int32_t *)info->buffer)[i] >>= 8;
#endif
for (i = 0; i < info->channels; i++)
grc3_convert(&(info->grc[i]), info->bit, info->quality,
info->buffer, b, isz, osz, info->channels, i);
#if 0
/* 24bit -> 32bit */
for (i = 0; info->bit == 32 &&
i < (info->grc[0].outsz * info->channels); i++)
((int32_t *)b)[i] <<= 8;
#endif
return (info->grc[0].outsz * info->align);
}
static struct pcm_feederdesc feeder_rate_desc[] = {
{ FEEDER_RATE, AFMT_S16_NE, AFMT_S16_NE, 0, 0 },
{ FEEDER_RATE, AFMT_S32_NE, AFMT_S32_NE, 0, 0 },
{ 0, 0, 0, 0, 0 },
};
static kobj_method_t feeder_rate_methods[] = {
KOBJMETHOD(feeder_init, feeder_grc3_init),
KOBJMETHOD(feeder_free, feeder_grc3_free),
KOBJMETHOD(feeder_set, feeder_grc3_set),
KOBJMETHOD(feeder_get, feeder_grc3_get),
KOBJMETHOD(feeder_feed, feeder_grc3_feed),
KOBJMETHOD_END
};
FEEDER_DECLARE(feeder_rate, NULL);