c329c38eef
http://mail-index.netbsd.org/tech-kern/2002/03/04/0005.html auconv.c: Add conversion functions audio.c: Sample alignment, calling conversion functions, etc. audio_if.h: Add four hw_* members to "struct audio_params" audiovar.h: Add conversion buffers, etc. auich and uaudio: Add conversion request code to *_set_params().
862 lines
22 KiB
C
862 lines
22 KiB
C
/* $NetBSD: auconv.c,v 1.8 2002/03/07 14:37:02 kent Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 1996 The NetBSD Foundation, Inc.
|
|
* 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.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by the Computer Systems
|
|
* Engineering Group at Lawrence Berkeley Laboratory.
|
|
* 4. Neither the name of the University nor of the Laboratory may be used
|
|
* to endorse or promote products derived from this software without
|
|
* specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 <sys/cdefs.h>
|
|
__KERNEL_RCSID(0, "$NetBSD: auconv.c,v 1.8 2002/03/07 14:37:02 kent Exp $");
|
|
|
|
#include <sys/systm.h>
|
|
#include <sys/types.h>
|
|
#include <sys/errno.h>
|
|
#include <sys/audioio.h>
|
|
|
|
#include "audio_if.h"
|
|
#include "auconv.h"
|
|
|
|
#ifdef AUCONV_DEBUG
|
|
#define DPRINTF(x) printf x
|
|
#else
|
|
#define DPRINTF(x)
|
|
#endif
|
|
|
|
static int auconv_play_slinear16_le(struct auconv_context *,
|
|
const struct audio_params *, uint8_t *, const uint8_t *, int);
|
|
static int auconv_play_slinear16_channels_le(struct auconv_context *,
|
|
const struct audio_params *, uint8_t *, const uint8_t *, int);
|
|
static int auconv_play_slinear24_le(struct auconv_context *,
|
|
const struct audio_params *, uint8_t *, const uint8_t *, int);
|
|
static int auconv_play_slinear24_channels_le(struct auconv_context *,
|
|
const struct audio_params *, uint8_t *, const uint8_t *, int);
|
|
|
|
static int auconv_record_slinear16_le(struct auconv_context *,
|
|
const struct audio_params *, uint8_t *, const uint8_t *, int);
|
|
static int auconv_record_slinear16_channels_le(struct auconv_context *,
|
|
const struct audio_params *, uint8_t *, const uint8_t *, int);
|
|
static int auconv_record_slinear24_le(struct auconv_context *,
|
|
const struct audio_params *, uint8_t *, const uint8_t *, int);
|
|
static int auconv_record_slinear24_channels_le(struct auconv_context *,
|
|
const struct audio_params *, uint8_t *, const uint8_t *, int);
|
|
|
|
|
|
void
|
|
change_sign8(void *v, u_char *p, int cc)
|
|
{
|
|
while (--cc >= 0) {
|
|
*p ^= 0x80;
|
|
++p;
|
|
}
|
|
}
|
|
|
|
void
|
|
change_sign16_le(void *v, u_char *p, int cc)
|
|
{
|
|
while ((cc -= 2) >= 0) {
|
|
p[1] ^= 0x80;
|
|
p += 2;
|
|
}
|
|
}
|
|
|
|
void
|
|
change_sign16_be(void *v, u_char *p, int cc)
|
|
{
|
|
while ((cc -= 2) >= 0) {
|
|
p[0] ^= 0x80;
|
|
p += 2;
|
|
}
|
|
}
|
|
|
|
void
|
|
swap_bytes(void *v, u_char *p, int cc)
|
|
{
|
|
u_char t;
|
|
|
|
while ((cc -= 2) >= 0) {
|
|
t = p[0];
|
|
p[0] = p[1];
|
|
p[1] = t;
|
|
p += 2;
|
|
}
|
|
}
|
|
|
|
void
|
|
swap_bytes_change_sign16_le(void *v, u_char *p, int cc)
|
|
{
|
|
u_char t;
|
|
|
|
while ((cc -= 2) >= 0) {
|
|
t = p[1];
|
|
p[1] = p[0] ^ 0x80;
|
|
p[0] = t;
|
|
p += 2;
|
|
}
|
|
}
|
|
|
|
void
|
|
swap_bytes_change_sign16_be(void *v, u_char *p, int cc)
|
|
{
|
|
u_char t;
|
|
|
|
while ((cc -= 2) >= 0) {
|
|
t = p[0];
|
|
p[0] = p[1] ^ 0x80;
|
|
p[1] = t;
|
|
p += 2;
|
|
}
|
|
}
|
|
|
|
void
|
|
change_sign16_swap_bytes_le(void *v, u_char *p, int cc)
|
|
{
|
|
swap_bytes_change_sign16_be(v, p, cc);
|
|
}
|
|
|
|
void
|
|
change_sign16_swap_bytes_be(void *v, u_char *p, int cc)
|
|
{
|
|
swap_bytes_change_sign16_le(v, p, cc);
|
|
}
|
|
|
|
void
|
|
linear8_to_linear16_le(void *v, u_char *p, int cc)
|
|
{
|
|
u_char *q = p;
|
|
|
|
p += cc;
|
|
q += cc * 2;
|
|
while (--cc >= 0) {
|
|
q -= 2;
|
|
q[1] = *--p;
|
|
q[0] = 0;
|
|
}
|
|
}
|
|
|
|
void
|
|
linear8_to_linear16_be(void *v, u_char *p, int cc)
|
|
{
|
|
u_char *q = p;
|
|
|
|
p += cc;
|
|
q += cc * 2;
|
|
while (--cc >= 0) {
|
|
q -= 2;
|
|
q[0] = *--p;
|
|
q[1] = 0;
|
|
}
|
|
}
|
|
|
|
void
|
|
linear16_to_linear8_le(void *v, u_char *p, int cc)
|
|
{
|
|
u_char *q = p;
|
|
|
|
while ((cc -= 2) >= 0) {
|
|
*q++ = p[1];
|
|
p += 2;
|
|
}
|
|
}
|
|
|
|
void
|
|
linear16_to_linear8_be(void *v, u_char *p, int cc)
|
|
{
|
|
u_char *q = p;
|
|
|
|
while ((cc -= 2) >= 0) {
|
|
*q++ = p[0];
|
|
p += 2;
|
|
}
|
|
}
|
|
|
|
void
|
|
ulinear8_to_slinear16_le(void *v, u_char *p, int cc)
|
|
{
|
|
u_char *q = p;
|
|
|
|
p += cc;
|
|
q += cc * 2;
|
|
while (--cc >= 0) {
|
|
q -= 2;
|
|
q[1] = *--p ^ 0x80;
|
|
q[0] = 0;
|
|
}
|
|
}
|
|
|
|
void
|
|
ulinear8_to_slinear16_be(void *v, u_char *p, int cc)
|
|
{
|
|
u_char *q = p;
|
|
|
|
p += cc;
|
|
q += cc * 2;
|
|
while (--cc >= 0) {
|
|
q -= 2;
|
|
q[0] = *--p ^ 0x80;
|
|
q[1] = 0;
|
|
}
|
|
}
|
|
|
|
void
|
|
slinear16_to_ulinear8_le(void *v, u_char *p, int cc)
|
|
{
|
|
u_char *q = p;
|
|
|
|
while ((cc -= 2) >= 0) {
|
|
*q++ = p[1] ^ 0x80;
|
|
p += 2;
|
|
}
|
|
}
|
|
|
|
void
|
|
slinear16_to_ulinear8_be(void *v, u_char *p, int cc)
|
|
{
|
|
u_char *q = p;
|
|
|
|
while ((cc -= 2) >= 0) {
|
|
*q++ = p[0] ^ 0x80;
|
|
p += 2;
|
|
}
|
|
}
|
|
|
|
int
|
|
auconv_check_params(const struct audio_params *params)
|
|
{
|
|
DPRINTF(("auconv_check_params: rate=%ld:%ld chan=%d:%d prec=%d:%d "
|
|
"enc=%d:%d\n", params->sample_rate, params->hw_sample_rate,
|
|
params->channels, params->hw_channels, params->precision,
|
|
params->hw_precision, params->encoding, params->hw_encoding));
|
|
if (params->hw_channels == params->channels
|
|
&& params->hw_sample_rate == params->sample_rate)
|
|
return 0; /* No conversion */
|
|
|
|
if (params->hw_encoding != AUDIO_ENCODING_SLINEAR_LE
|
|
|| (params->hw_precision != 16 && params->hw_precision != 24))
|
|
return (EINVAL);
|
|
|
|
/* Only 1:2 or 2:1 */
|
|
if (params->hw_channels != params->channels)
|
|
if (!((params->hw_channels == 1 && params->channels == 2)
|
|
|| (params->hw_channels == 2 && params->channels == 1)))
|
|
return (EINVAL);
|
|
|
|
if (params->hw_sample_rate != params->sample_rate)
|
|
if (params->hw_sample_rate <= 0 || params->sample_rate <= 0)
|
|
return (EINVAL);
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
auconv_init_context(struct auconv_context *context, long src_rate,
|
|
long dst_rate, uint8_t *start, uint8_t *end)
|
|
{
|
|
int i;
|
|
|
|
context->ring_start = start;
|
|
context->ring_end = end;
|
|
if (dst_rate > src_rate) {
|
|
context->count = src_rate;
|
|
} else {
|
|
context->count = 0;
|
|
}
|
|
for (i = 0; i < AUDIO_MAX_CHANNELS; i++)
|
|
context->prev[i] = 0;
|
|
}
|
|
|
|
/*
|
|
* src is a ring buffer.
|
|
*/
|
|
int
|
|
auconv_record(struct auconv_context *context,
|
|
const struct audio_params *params, uint8_t *dest,
|
|
const uint8_t *src, int srcsize)
|
|
{
|
|
if (params->hw_sample_rate == params->sample_rate
|
|
&& params->hw_channels == params->channels) {
|
|
int n;
|
|
|
|
n = context->ring_end - src;
|
|
if (srcsize <= n)
|
|
memcpy(dest, src, srcsize);
|
|
else {
|
|
memcpy(dest, src, n);
|
|
memcpy(dest + n, context->ring_start, srcsize - n);
|
|
}
|
|
return srcsize;
|
|
}
|
|
|
|
if (params->hw_encoding != AUDIO_ENCODING_SLINEAR_LE) {
|
|
/* This should be rejected in auconv_check_params() */
|
|
printf("auconv_record: unimplemented encoding: %d\n",
|
|
params->hw_encoding);
|
|
return 0;
|
|
}
|
|
switch (params->hw_precision) {
|
|
case 16:
|
|
if (params->hw_channels != params->channels)
|
|
return auconv_record_slinear16_channels_le(context,
|
|
params, dest, src, srcsize);
|
|
else
|
|
return auconv_record_slinear16_le(context,
|
|
params, dest, src, srcsize);
|
|
case 24:
|
|
if (params->hw_channels != params->channels)
|
|
return auconv_record_slinear24_channels_le(context,
|
|
params, dest, src, srcsize);
|
|
else
|
|
return auconv_record_slinear24_le(context,
|
|
params, dest, src, srcsize);
|
|
}
|
|
printf("auconv_record: unimplemented precision: %d\n",
|
|
params->hw_precision);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* dest is a ring buffer.
|
|
*/
|
|
int
|
|
auconv_play(struct auconv_context *context, const struct audio_params *params,
|
|
uint8_t *dest, const uint8_t *src, int srcsize)
|
|
{
|
|
int n;
|
|
|
|
if (params->hw_sample_rate == params->sample_rate
|
|
&& params->hw_channels == params->channels) {
|
|
n = context->ring_end - dest;
|
|
if (srcsize <= n) {
|
|
memcpy(dest, src, srcsize);
|
|
} else {
|
|
memcpy(dest, src, n);
|
|
memcpy(context->ring_start, src + n, srcsize - n);
|
|
}
|
|
return srcsize;
|
|
}
|
|
|
|
if (params->hw_encoding != AUDIO_ENCODING_SLINEAR_LE) {
|
|
/* This should be rejected in auconv_check_params() */
|
|
printf("auconv_play: unimplemented encoding: %d\n",
|
|
params->hw_encoding);
|
|
return 0;
|
|
}
|
|
switch (params->hw_precision) {
|
|
case 16:
|
|
if (params->hw_channels != params->channels)
|
|
return auconv_play_slinear16_channels_le(context,
|
|
params, dest, src, srcsize);
|
|
else
|
|
return auconv_play_slinear16_le(context,
|
|
params, dest, src, srcsize);
|
|
case 24:
|
|
if (params->hw_channels != params->channels)
|
|
return auconv_play_slinear24_channels_le(context,
|
|
params, dest, src, srcsize);
|
|
else
|
|
return auconv_play_slinear24_le(context,
|
|
params, dest, src, srcsize);
|
|
}
|
|
printf("auconv_play: unimplemented precision: %d\n",
|
|
params->hw_precision);
|
|
return 0;
|
|
}
|
|
|
|
|
|
#define RING_CHECK(C, V) \
|
|
do { \
|
|
if (V >= (C)->ring_end) \
|
|
V = (C)->ring_start; \
|
|
} while (0)
|
|
|
|
#if BYTE_ORDER == LITTLE_ENDIAN
|
|
# define READ_S16LE(P) *(int16_t*)(P)
|
|
# define WRITE_S16LE(P, V) *(int16_t*)(P) = V
|
|
#else
|
|
# define READ_S16LE(P) (int16_t)((P)[0] | ((P)[1]<<8))
|
|
# define WRITE_S16LE(P, V) \
|
|
do { \
|
|
int vv = V; \
|
|
(P)[0] = vv; \
|
|
(P)[1] = vv >> 8; \
|
|
} while (0)
|
|
#endif
|
|
#define READ_S24LE(P) (int32_t)((P)[0] | ((P)[1]<<8) | (((int8_t)((P)[2]))<<16))
|
|
#define WRITE_S24LE(P, V) \
|
|
do { \
|
|
int vvv = V; \
|
|
(P)[0] = vvv; \
|
|
(P)[1] = vvv >> 8; \
|
|
(P)[2] = vvv >> 16; \
|
|
} while (0)
|
|
|
|
#define P_READ_LR_S16LE(LV, RV, RP, PAR) \
|
|
do { \
|
|
LV = READ_S16LE(RP); \
|
|
RP += sizeof(int16_t); \
|
|
if ((PAR)->channels == 1) \
|
|
RV = LV; \
|
|
else { \
|
|
RV = READ_S16LE(RP); \
|
|
RP += sizeof(int16_t); \
|
|
} \
|
|
} while (0)
|
|
#define P_WRITE_LR_S16LE(LV, RV, WP, PAR, CON, WC) \
|
|
do { \
|
|
if ((PAR)->hw_channels == 1) { \
|
|
WRITE_S16LE(WP, (LV + RV) / 2); \
|
|
WP += sizeof(int16_t); \
|
|
WC += sizeof(int16_t); \
|
|
} else { \
|
|
WRITE_S16LE(WP, LV); \
|
|
WP += sizeof(int16_t); \
|
|
RING_CHECK(CON, WP); \
|
|
WRITE_S16LE(WP, RV); \
|
|
WP += sizeof(int16_t); \
|
|
WC += sizeof(int16_t) * 2; \
|
|
} \
|
|
RING_CHECK(CON, WP); \
|
|
} while (0)
|
|
#define P_READ_N_S16LE(V, RP, PAR) \
|
|
do { \
|
|
int i; \
|
|
for (i = 0; i < (PAR)->channels; i++) { \
|
|
(V)[i] = READ_S16LE(RP); \
|
|
RP += sizeof(int16_t); \
|
|
} \
|
|
} while (0)
|
|
#define P_WRITE_N_S16LE(V, WP, PAR, CON, WC) \
|
|
do { \
|
|
int i; \
|
|
for (i = 0; i < (PAR)->channels; i++) { \
|
|
WRITE_S16LE(WP, (V)[i]); \
|
|
WP += sizeof(int16_t); \
|
|
RING_CHECK(CON, WP); \
|
|
} \
|
|
WC += sizeof(int16_t) * i; \
|
|
} while (0)
|
|
#define P_READ_LR_S24LE(LV, RV, RP, PAR) \
|
|
do { \
|
|
LV = READ_S24LE(RP); \
|
|
RP += 3; \
|
|
if ((PAR)->channels == 1) \
|
|
RV = LV; \
|
|
else { \
|
|
RV = READ_S24LE(RP); \
|
|
RP += 3; \
|
|
} \
|
|
} while (0)
|
|
#define P_WRITE_LR_S24LE(LV, RV, WP, PAR, CON, WC) \
|
|
do { \
|
|
if ((PAR)->hw_channels == 1) { \
|
|
WRITE_S24LE(WP, (LV + RV) / 2); \
|
|
WP += 3; \
|
|
WC += 3; \
|
|
} else { \
|
|
WRITE_S24LE(WP, LV); \
|
|
WP += 3; \
|
|
RING_CHECK(CON, WP); \
|
|
WRITE_S24LE(WP, RV); \
|
|
WP += 3; \
|
|
WC += 3 * 2; \
|
|
} \
|
|
RING_CHECK(CON, WP); \
|
|
} while (0)
|
|
#define P_READ_N_S24LE(V, RP, PAR) \
|
|
do { \
|
|
int i; \
|
|
for (i = 0; i < (PAR)->channels; i++) { \
|
|
(V)[i] = READ_S24LE(RP); \
|
|
RP += 3; \
|
|
} \
|
|
} while (0)
|
|
#define P_WRITE_N_S24LE(V, WP, PAR, CON, WC) \
|
|
do { \
|
|
int i; \
|
|
for (i = 0; i < (PAR)->channels; i++) { \
|
|
WRITE_S24LE(WP, (V)[i]); \
|
|
WP += 3; \
|
|
RING_CHECK(CON, WP); \
|
|
} \
|
|
WC += 3 * i; \
|
|
} while (0)
|
|
|
|
#define R_READ_LR_S16LE(LV, RV, RP, PAR, CON, RC) \
|
|
do { \
|
|
LV = READ_S16LE(RP); \
|
|
RP += sizeof(int16_t); \
|
|
RING_CHECK(CON, RP); \
|
|
RC += sizeof(int16_t); \
|
|
if ((PAR)->hw_channels == 1) \
|
|
RV = LV; \
|
|
else { \
|
|
RV = READ_S16LE(RP); \
|
|
RP += sizeof(int16_t); \
|
|
RING_CHECK(CON, RP); \
|
|
RC += sizeof(int16_t); \
|
|
} \
|
|
} while (0)
|
|
#define R_WRITE_LR_S16LE(LV, RV, WP, PAR, WC) \
|
|
do { \
|
|
if ((PAR)->channels == 1) { \
|
|
WRITE_S16LE(WP, (LV + RV) / 2); \
|
|
WP += sizeof(int16_t); \
|
|
WC += sizeof(int16_t); \
|
|
} else { \
|
|
WRITE_S16LE(WP, LV); \
|
|
WP += sizeof(int16_t); \
|
|
WRITE_S16LE(WP, RV); \
|
|
WP += sizeof(int16_t); \
|
|
WC += sizeof(int16_t) * 2; \
|
|
} \
|
|
} while (0)
|
|
#define R_READ_N_S16LE(V, RP, PAR, CON, RC) \
|
|
do { \
|
|
int i; \
|
|
for (i = 0; i < (PAR)->channels; i++) { \
|
|
(V)[i] = READ_S16LE(RP); \
|
|
RP += sizeof(int16_t); \
|
|
RING_CHECK(CON, RP); \
|
|
RC += sizeof(int16_t); \
|
|
} \
|
|
} while (0)
|
|
#define R_WRITE_N_S16LE(V, WP, PAR, WC) \
|
|
do { \
|
|
int i; \
|
|
for (i = 0; i < (PAR)->channels; i++) { \
|
|
WRITE_S16LE(WP, (V)[i]); \
|
|
WP += sizeof(int16_t); \
|
|
} \
|
|
WC += sizeof(int16_t) * i; \
|
|
} while (0)
|
|
#define R_READ_LR_S24LE(LV, RV, RP, PAR, CON, RC) \
|
|
do { \
|
|
LV = READ_S24LE(RP); \
|
|
RP += 3; \
|
|
RING_CHECK(CON, RP); \
|
|
RC += 3; \
|
|
if ((PAR)->hw_channels == 1) \
|
|
RV = LV; \
|
|
else { \
|
|
RV = READ_S24LE(RP); \
|
|
RP += 3; \
|
|
RING_CHECK(CON, RP); \
|
|
RC += 3; \
|
|
} \
|
|
} while (0)
|
|
#define R_WRITE_LR_S24LE(LV, RV, WP, PAR, WC) \
|
|
do { \
|
|
if ((PAR)->channels == 1) { \
|
|
WRITE_S24LE(WP, (LV + RV) / 2); \
|
|
WP += 3; \
|
|
WC += 3; \
|
|
} else { \
|
|
WRITE_S24LE(WP, LV); \
|
|
WP += 3; \
|
|
WRITE_S24LE(WP, RV); \
|
|
WP += 3; \
|
|
WC += 3 * 2; \
|
|
} \
|
|
} while (0)
|
|
#define R_READ_N_S24LE(V, RP, PAR, CON, RC) \
|
|
do { \
|
|
int i; \
|
|
for (i = 0; i < (PAR)->channels; i++) { \
|
|
(V)[i] = READ_S24LE(RP); \
|
|
RP += 3; \
|
|
RING_CHECK(CON, RP); \
|
|
RC += 3; \
|
|
} \
|
|
} while (0)
|
|
#define R_WRITE_N_S24LE(V, WP, PAR, WC) \
|
|
do { \
|
|
int i; \
|
|
for (i = 0; i < (PAR)->channels; i++) { \
|
|
WRITE_S24LE(WP, (V)[i]); \
|
|
WP += 3; \
|
|
} \
|
|
WC += 3 * i; \
|
|
} while (0)
|
|
|
|
/*
|
|
* Function templates
|
|
*
|
|
* Source may be 1 sample. Destination buffer must have space for converted
|
|
* source.
|
|
* Don't use them for 32bit data because this linear interpolation overflows
|
|
* for 32bit data.
|
|
*/
|
|
#define AUCONV_PLAY_SLINEAR_CHANNELS_LE(BITS) \
|
|
static int \
|
|
auconv_play_slinear##BITS##_channels_le(struct auconv_context *context, \
|
|
const struct audio_params *params, \
|
|
uint8_t *dest, const uint8_t *src, \
|
|
int srcsize) \
|
|
{ \
|
|
int wrote; \
|
|
uint8_t *w; \
|
|
const uint8_t *r; \
|
|
const uint8_t *src_end; \
|
|
register int32_t lv, rv; \
|
|
int32_t prev_l, prev_r, next_l, next_r, c256; \
|
|
\
|
|
wrote = 0; \
|
|
w = dest; \
|
|
r = src; \
|
|
src_end = src + srcsize; \
|
|
if (params->sample_rate == params->hw_sample_rate) { \
|
|
while (r < src_end) { \
|
|
P_READ_LR_S##BITS##LE(lv, rv, r, params); \
|
|
P_WRITE_LR_S##BITS##LE(lv, rv, w, params, context, wrote); \
|
|
} \
|
|
} else if (params->hw_sample_rate < params->sample_rate) { \
|
|
for (;;) { \
|
|
do { \
|
|
if (r >= src_end) \
|
|
return wrote; \
|
|
P_READ_LR_S##BITS##LE(lv, rv, r, params); \
|
|
context->count += params->hw_sample_rate; \
|
|
} while (context->count < params->sample_rate); \
|
|
context->count -= params->sample_rate; \
|
|
P_WRITE_LR_S##BITS##LE(lv, rv, w, params, context, wrote); \
|
|
} \
|
|
} else { \
|
|
/* Initial value of context->count is params->sample_rate */ \
|
|
prev_l = context->prev[0]; \
|
|
prev_r = context->prev[1]; \
|
|
P_READ_LR_S##BITS##LE(next_l, next_r, r, params); \
|
|
for (;;) { \
|
|
c256 = context->count * 256 / params->hw_sample_rate; \
|
|
lv = (c256 * next_l + (256 - c256) * prev_l) >> 8; \
|
|
rv = (c256 * next_r + (256 - c256) * prev_r) >> 8; \
|
|
P_WRITE_LR_S##BITS##LE(lv, rv, w, params, context, wrote); \
|
|
context->count += params->sample_rate; \
|
|
if (context->count >= params->hw_sample_rate) { \
|
|
context->count -= params->hw_sample_rate; \
|
|
prev_l = next_l; \
|
|
prev_r = next_r; \
|
|
if (r >= src_end) \
|
|
break; \
|
|
P_READ_LR_S##BITS##LE(next_l, next_r, r, params); \
|
|
} \
|
|
} \
|
|
context->prev[0] = next_l; \
|
|
context->prev[1] = next_r; \
|
|
} \
|
|
return wrote; \
|
|
}
|
|
|
|
#define AUCONV_PLAY_SLINEAR_LE(BITS) \
|
|
static int \
|
|
auconv_play_slinear##BITS##_le(struct auconv_context *context, \
|
|
const struct audio_params *params, \
|
|
uint8_t *dest, const uint8_t *src, \
|
|
int srcsize) \
|
|
{ \
|
|
int wrote; \
|
|
uint8_t *w; \
|
|
const uint8_t *r; \
|
|
const uint8_t *src_end; \
|
|
int32_t v[AUDIO_MAX_CHANNELS]; \
|
|
int32_t prev[AUDIO_MAX_CHANNELS], next[AUDIO_MAX_CHANNELS], c256; \
|
|
int i, values_size; \
|
|
\
|
|
wrote = 0; \
|
|
w = dest; \
|
|
r = src; \
|
|
src_end = src + srcsize; \
|
|
if (params->sample_rate == params->hw_sample_rate) { \
|
|
while (r < src_end) { \
|
|
P_READ_N_S##BITS##LE(v, r, params); \
|
|
P_WRITE_N_S##BITS##LE(v, w, params, context, wrote); \
|
|
} \
|
|
} else if (params->hw_sample_rate < params->sample_rate) { \
|
|
for (;;) { \
|
|
do { \
|
|
if (r >= src_end) \
|
|
return wrote; \
|
|
P_READ_N_S##BITS##LE(v, r, params); \
|
|
context->count += params->hw_sample_rate; \
|
|
} while (context->count < params->sample_rate); \
|
|
context->count -= params->sample_rate; \
|
|
P_WRITE_N_S##BITS##LE(v, w, params, context, wrote); \
|
|
} \
|
|
} else { \
|
|
/* Initial value of context->count is params->sample_rate */ \
|
|
values_size = sizeof(int32_t) * params->channels; \
|
|
memcpy(prev, context->prev, values_size); \
|
|
P_READ_N_S##BITS##LE(next, r, params); \
|
|
for (;;) { \
|
|
c256 = context->count * 256 / params->hw_sample_rate; \
|
|
for (i = 0; i < params->channels; i++) \
|
|
v[i] = (c256 * next[i] + (256 - c256) * prev[i]) >> 8; \
|
|
P_WRITE_N_S##BITS##LE(v, w, params, context, wrote); \
|
|
context->count += params->sample_rate; \
|
|
if (context->count >= params->hw_sample_rate) { \
|
|
context->count -= params->hw_sample_rate; \
|
|
memcpy(prev, next, values_size); \
|
|
if (r >= src_end) \
|
|
break; \
|
|
P_READ_N_S##BITS##LE(next, r, params); \
|
|
} \
|
|
} \
|
|
memcpy(context->prev, next, values_size); \
|
|
} \
|
|
return wrote; \
|
|
}
|
|
|
|
#define AUCONV_RECORD_SLINEAR_CHANNELS_LE(BITS) \
|
|
static int \
|
|
auconv_record_slinear##BITS##_channels_le(struct auconv_context *context, \
|
|
const struct audio_params *params, \
|
|
uint8_t *dest, const uint8_t *src, \
|
|
int srcsize) \
|
|
{ \
|
|
int wrote, rsize; \
|
|
uint8_t *w; \
|
|
const uint8_t *r; \
|
|
register int32_t lv, rv; \
|
|
int32_t prev_l, prev_r, next_l, next_r, c256; \
|
|
\
|
|
wrote = 0; \
|
|
rsize = 0; \
|
|
w = dest; \
|
|
r = src; \
|
|
if (params->sample_rate == params->hw_sample_rate) { \
|
|
while (rsize < srcsize) { \
|
|
R_READ_LR_S##BITS##LE(lv, rv, r, params, context, rsize); \
|
|
R_WRITE_LR_S##BITS##LE(lv, rv, w, params, wrote); \
|
|
} \
|
|
} else if (params->sample_rate < params->hw_sample_rate) { \
|
|
for (;;) { \
|
|
do { \
|
|
if (rsize >= srcsize) \
|
|
return wrote; \
|
|
R_READ_LR_S##BITS##LE(lv, rv, r, params, \
|
|
context, rsize); \
|
|
context->count += params->sample_rate; \
|
|
} while (context->count < params->hw_sample_rate); \
|
|
context->count -= params->hw_sample_rate; \
|
|
R_WRITE_LR_S##BITS##LE(lv, rv, w, params, wrote); \
|
|
} \
|
|
} else { \
|
|
/* Initial value of context->count is params->hw_sample_rate */ \
|
|
prev_l = context->prev[0]; \
|
|
prev_r = context->prev[1]; \
|
|
R_READ_LR_S##BITS##LE(next_l, next_r, r, params, context, rsize); \
|
|
for (;;) { \
|
|
c256 = context->count * 256 / params->sample_rate; \
|
|
lv = (c256 * next_l + (256 - c256) * prev_l) >> 8; \
|
|
rv = (c256 * next_r + (256 - c256) * prev_r) >> 8; \
|
|
R_WRITE_LR_S##BITS##LE(lv, rv, w, params, wrote); \
|
|
context->count += params->hw_sample_rate; \
|
|
if (context->count >= params->sample_rate) { \
|
|
context->count -= params->sample_rate; \
|
|
prev_l = next_l; \
|
|
prev_r = next_r; \
|
|
if (rsize >= srcsize) \
|
|
break; \
|
|
R_READ_LR_S##BITS##LE(next_l, next_r, r, \
|
|
params, context, rsize); \
|
|
} \
|
|
} \
|
|
context->prev[0] = next_l; \
|
|
context->prev[1] = next_r; \
|
|
} \
|
|
return wrote; \
|
|
}
|
|
|
|
#define AUCONV_RECORD_SLINEAR_LE(BITS) \
|
|
static int \
|
|
auconv_record_slinear##BITS##_le(struct auconv_context *context, \
|
|
const struct audio_params *params, \
|
|
uint8_t *dest, const uint8_t *src, \
|
|
int srcsize) \
|
|
{ \
|
|
int wrote, rsize; \
|
|
uint8_t *w; \
|
|
const uint8_t *r; \
|
|
int32_t v[AUDIO_MAX_CHANNELS]; \
|
|
int32_t prev[AUDIO_MAX_CHANNELS], next[AUDIO_MAX_CHANNELS], c256; \
|
|
int i, values_size; \
|
|
\
|
|
wrote = 0; \
|
|
rsize = 0; \
|
|
w = dest; \
|
|
r = src; \
|
|
if (params->sample_rate == params->hw_sample_rate) { \
|
|
while (rsize < srcsize) { \
|
|
R_READ_N_S##BITS##LE(v, r, params, context, rsize); \
|
|
R_WRITE_N_S##BITS##LE(v, w, params, wrote); \
|
|
} \
|
|
} else if (params->sample_rate < params->hw_sample_rate) { \
|
|
for (;;) { \
|
|
do { \
|
|
if (rsize >= srcsize) \
|
|
return wrote; \
|
|
R_READ_N_S##BITS##LE(v, r, params, context, rsize); \
|
|
context->count += params->sample_rate; \
|
|
} while (context->count < params->hw_sample_rate); \
|
|
context->count -= params->hw_sample_rate; \
|
|
R_WRITE_N_S##BITS##LE(v, w, params, wrote); \
|
|
} \
|
|
} else { \
|
|
/* Initial value of context->count is params->hw_sample_rate */ \
|
|
values_size = sizeof(int32_t) * params->channels; \
|
|
memcpy(prev, context->prev, values_size); \
|
|
R_READ_N_S##BITS##LE(next, r, params, context, rsize); \
|
|
for (;;) { \
|
|
c256 = context->count * 256 / params->sample_rate; \
|
|
for (i = 0; i < params->channels; i++) \
|
|
v[i] = (c256 * next[i] + (256 - c256) * prev[i]) >> 8; \
|
|
R_WRITE_N_S##BITS##LE(v, w, params, wrote); \
|
|
context->count += params->hw_sample_rate; \
|
|
if (context->count >= params->sample_rate) { \
|
|
context->count -= params->sample_rate; \
|
|
memcpy(prev, next, values_size); \
|
|
if (rsize >= srcsize) \
|
|
break; \
|
|
R_READ_N_S##BITS##LE(next, r, params, context, rsize); \
|
|
} \
|
|
} \
|
|
memcpy(context->prev, next, values_size); \
|
|
} \
|
|
return wrote; \
|
|
}
|
|
|
|
AUCONV_PLAY_SLINEAR_LE(16)
|
|
AUCONV_PLAY_SLINEAR_LE(24)
|
|
AUCONV_PLAY_SLINEAR_CHANNELS_LE(16)
|
|
AUCONV_PLAY_SLINEAR_CHANNELS_LE(24)
|
|
AUCONV_RECORD_SLINEAR_LE(16)
|
|
AUCONV_RECORD_SLINEAR_LE(24)
|
|
AUCONV_RECORD_SLINEAR_CHANNELS_LE(16)
|
|
AUCONV_RECORD_SLINEAR_CHANNELS_LE(24)
|