#ifndef _SND_PCM_H_
#define _SND_PCM_H_
#include <sys/param.h>
#include <dev/sound/pcm/g711.h>
#ifndef _KERNEL
#include <assert.h>
#endif
#if LONG_BIT >= 64
#undef SND_PCM_64
#define SND_PCM_64 1
#endif
typedef int32_t intpcm_t;
typedef int32_t intpcm8_t;
typedef int32_t intpcm16_t;
typedef int32_t intpcm24_t;
typedef uint32_t uintpcm_t;
typedef uint32_t uintpcm8_t;
typedef uint32_t uintpcm16_t;
typedef uint32_t uintpcm24_t;
#ifdef SND_PCM_64
typedef int64_t intpcm32_t;
typedef uint64_t uintpcm32_t;
#else
typedef int32_t intpcm32_t;
typedef uint32_t uintpcm32_t;
#endif
typedef int64_t intpcm64_t;
typedef uint64_t uintpcm64_t;
#define PCM_FXSHIFT 8
#define PCM_S8_MAX 0x7f
#define PCM_S8_MIN -0x80
#define PCM_S16_MAX 0x7fff
#define PCM_S16_MIN -0x8000
#define PCM_S24_MAX 0x7fffff
#define PCM_S24_MIN -0x800000
#ifdef SND_PCM_64
#if LONG_BIT >= 64
#define PCM_S32_MAX 0x7fffffffL
#define PCM_S32_MIN -0x80000000L
#else
#define PCM_S32_MAX 0x7fffffffLL
#define PCM_S32_MIN -0x80000000LL
#endif
#else
#define PCM_S32_MAX 0x7fffffff
#define PCM_S32_MIN (-0x7fffffff - 1)
#endif
#define PCM_8_BPS 1
#define PCM_16_BPS 2
#define PCM_24_BPS 3
#define PCM_32_BPS 4
#define INTPCM_T(v) ((intpcm_t)(v))
#define INTPCM8_T(v) ((intpcm8_t)(v))
#define INTPCM16_T(v) ((intpcm16_t)(v))
#define INTPCM24_T(v) ((intpcm24_t)(v))
#define INTPCM32_T(v) ((intpcm32_t)(v))
static const struct {
const uint8_t ulaw_to_u8[G711_TABLE_SIZE];
const uint8_t alaw_to_u8[G711_TABLE_SIZE];
const uint8_t u8_to_ulaw[G711_TABLE_SIZE];
const uint8_t u8_to_alaw[G711_TABLE_SIZE];
} xlaw_conv_tables = {
ULAW_TO_U8,
ALAW_TO_U8,
U8_TO_ULAW,
U8_TO_ALAW
};
static __always_inline __unused intpcm_t
pcm_sample_read(const uint8_t *src, uint32_t fmt)
{
intpcm_t v, e, m;
bool s;
fmt = AFMT_ENCODING(fmt);
switch (fmt) {
case AFMT_AC3:
v = 0;
break;
case AFMT_MU_LAW:
v = _G711_TO_INTPCM(xlaw_conv_tables.ulaw_to_u8, *src);
break;
case AFMT_A_LAW:
v = _G711_TO_INTPCM(xlaw_conv_tables.alaw_to_u8, *src);
break;
case AFMT_S8:
v = INTPCM_T((int8_t)*src);
break;
case AFMT_U8:
v = INTPCM_T((int8_t)(*src ^ 0x80));
break;
case AFMT_S16_LE:
v = INTPCM_T(src[0] | (int8_t)src[1] << 8);
break;
case AFMT_S16_BE:
v = INTPCM_T(src[1] | (int8_t)src[0] << 8);
break;
case AFMT_U16_LE:
v = INTPCM_T(src[0] | (int8_t)(src[1] ^ 0x80) << 8);
break;
case AFMT_U16_BE:
v = INTPCM_T(src[1] | (int8_t)(src[0] ^ 0x80) << 8);
break;
case AFMT_S24_LE:
v = INTPCM_T(src[0] | src[1] << 8 | (int8_t)src[2] << 16);
break;
case AFMT_S24_BE:
v = INTPCM_T(src[2] | src[1] << 8 | (int8_t)src[0] << 16);
break;
case AFMT_U24_LE:
v = INTPCM_T(src[0] | src[1] << 8 |
(int8_t)(src[2] ^ 0x80) << 16);
break;
case AFMT_U24_BE:
v = INTPCM_T(src[2] | src[1] << 8 |
(int8_t)(src[0] ^ 0x80) << 16);
break;
case AFMT_S32_LE:
v = INTPCM_T(src[0] | src[1] << 8 | src[2] << 16 |
(int8_t)src[3] << 24);
break;
case AFMT_S32_BE:
v = INTPCM_T(src[3] | src[2] << 8 | src[1] << 16 |
(int8_t)src[0] << 24);
break;
case AFMT_U32_LE:
v = INTPCM_T(src[0] | src[1] << 8 | src[2] << 16 |
(int8_t)(src[3] ^ 0x80) << 24);
break;
case AFMT_U32_BE:
v = INTPCM_T(src[3] | src[2] << 8 | src[1] << 16 |
(int8_t)(src[0] ^ 0x80) << 24);
break;
case AFMT_F32_LE:
case AFMT_F32_BE:
if (fmt == AFMT_F32_LE) {
v = INTPCM_T(src[0] | src[1] << 8 | src[2] << 16 |
(int8_t)src[3] << 24);
} else {
v = INTPCM_T(src[3] | src[2] << 8 | src[1] << 16 |
(int8_t)src[0] << 24);
}
e = (v >> 23) & 0xff;
if (e == 0xff || e < 96) {
v = INTPCM_T(0);
break;
}
s = v & 0x80000000U;
if (e > 126) {
v = INTPCM_T((s == 0) ? PCM_S32_MAX : PCM_S32_MIN);
break;
}
m = 0x800000 | (v & 0x7fffff);
e += 8 - 127;
if (e < 0)
m >>= -e;
else
m <<= e;
v = INTPCM_T((s == 0) ? m : -m);
break;
default:
v = 0;
printf("%s(): unknown format: 0x%08x\n", __func__, fmt);
__assert_unreachable();
}
return (v);
}
static __always_inline __unused intpcm_t
pcm_sample_read_norm(const uint8_t *src, uint32_t fmt)
{
return (pcm_sample_read(src, fmt) << (32 - AFMT_BIT(fmt)));
}
static __always_inline __unused intpcm_t
pcm_sample_read_calc(const uint8_t *src, uint32_t fmt)
{
intpcm_t v;
v = pcm_sample_read(src, fmt);
#ifndef SND_PCM_64
if (fmt & AFMT_32BIT)
v >>= PCM_FXSHIFT;
#endif
return (v);
}
static __always_inline __unused void
pcm_sample_write(uint8_t *dst, intpcm_t v, uint32_t fmt)
{
intpcm_t r, e;
fmt = AFMT_ENCODING(fmt);
if (fmt & (AFMT_F32_LE | AFMT_F32_BE)) {
if (v == 0)
r = 0;
else if (v == PCM_S32_MAX)
r = 0x3f800000;
else if (v == PCM_S32_MIN)
r = 0x80000000U | 0x3f800000;
else {
r = 0;
if (v < 0) {
r |= 0x80000000U;
v = -v;
}
e = 127 - 8;
while ((v & 0x7f000000) != 0) {
v >>= 1;
e++;
}
while ((v & 0x7f800000) == 0) {
v <<= 1;
e--;
}
r |= (e & 0xff) << 23;
r |= v & 0x7fffff;
}
v = r;
}
switch (fmt) {
case AFMT_AC3:
*(int16_t *)dst = 0;
break;
case AFMT_MU_LAW:
*dst = _INTPCM_TO_G711(xlaw_conv_tables.u8_to_ulaw, v);
break;
case AFMT_A_LAW:
*dst = _INTPCM_TO_G711(xlaw_conv_tables.u8_to_alaw, v);
break;
case AFMT_S8:
*(int8_t *)dst = v;
break;
case AFMT_U8:
*(int8_t *)dst = v ^ 0x80;
break;
case AFMT_S16_LE:
dst[0] = v;
dst[1] = v >> 8;
break;
case AFMT_S16_BE:
dst[1] = v;
dst[0] = v >> 8;
break;
case AFMT_U16_LE:
dst[0] = v;
dst[1] = (v >> 8) ^ 0x80;
break;
case AFMT_U16_BE:
dst[1] = v;
dst[0] = (v >> 8) ^ 0x80;
break;
case AFMT_S24_LE:
dst[0] = v;
dst[1] = v >> 8;
dst[2] = v >> 16;
break;
case AFMT_S24_BE:
dst[2] = v;
dst[1] = v >> 8;
dst[0] = v >> 16;
break;
case AFMT_U24_LE:
dst[0] = v;
dst[1] = v >> 8;
dst[2] = (v >> 16) ^ 0x80;
break;
case AFMT_U24_BE:
dst[2] = v;
dst[1] = v >> 8;
dst[0] = (v >> 16) ^ 0x80;
break;
case AFMT_S32_LE:
case AFMT_F32_LE:
dst[0] = v;
dst[1] = v >> 8;
dst[2] = v >> 16;
dst[3] = v >> 24;
break;
case AFMT_S32_BE:
case AFMT_F32_BE:
dst[3] = v;
dst[2] = v >> 8;
dst[1] = v >> 16;
dst[0] = v >> 24;
break;
case AFMT_U32_LE:
dst[0] = v;
dst[1] = v >> 8;
dst[2] = v >> 16;
dst[3] = (v >> 24) ^ 0x80;
break;
case AFMT_U32_BE:
dst[3] = v;
dst[2] = v >> 8;
dst[1] = v >> 16;
dst[0] = (v >> 24) ^ 0x80;
break;
default:
printf("%s(): unknown format: 0x%08x\n", __func__, fmt);
__assert_unreachable();
}
}
static __always_inline __unused void
pcm_sample_write_norm(uint8_t *dst, intpcm_t v, uint32_t fmt)
{
pcm_sample_write(dst, v >> (32 - AFMT_BIT(fmt)), fmt);
}
static __always_inline __unused void
pcm_sample_write_calc(uint8_t *dst, intpcm_t v, uint32_t fmt)
{
#ifndef SND_PCM_64
if (fmt & AFMT_32BIT)
v <<= PCM_FXSHIFT;
#endif
pcm_sample_write(dst, v, fmt);
}
static __always_inline __unused intpcm_t
pcm_clamp(intpcm32_t sample, uint32_t fmt)
{
fmt = AFMT_ENCODING(fmt);
switch (AFMT_BIT(fmt)) {
case 8:
return ((sample > PCM_S8_MAX) ? PCM_S8_MAX :
((sample < PCM_S8_MIN) ? PCM_S8_MIN : sample));
case 16:
return ((sample > PCM_S16_MAX) ? PCM_S16_MAX :
((sample < PCM_S16_MIN) ? PCM_S16_MIN : sample));
case 24:
return ((sample > PCM_S24_MAX) ? PCM_S24_MAX :
((sample < PCM_S24_MIN) ? PCM_S24_MIN : sample));
case 32:
return ((sample > PCM_S32_MAX) ? PCM_S32_MAX :
((sample < PCM_S32_MIN) ? PCM_S32_MIN : sample));
default:
printf("%s(): unknown format: 0x%08x\n", __func__, fmt);
__assert_unreachable();
}
}
static __always_inline __unused intpcm_t
pcm_clamp_calc(intpcm32_t sample, uint32_t fmt)
{
#ifndef SND_PCM_64
if (fmt & AFMT_32BIT) {
return ((sample > PCM_S24_MAX) ? PCM_S32_MAX :
((sample < PCM_S24_MIN) ? PCM_S32_MIN :
sample << PCM_FXSHIFT));
}
#endif
return (pcm_clamp(sample, fmt));
}
#endif