mirror of
https://github.com/opnsense/src.git
synced 2026-06-17 04:29:12 -04:00
sound: Implement AFMT_FLOAT support
Even though the OSS manual [1] advises against using AFMT_FLOAT, there are applications that expect the sound driver to support it, and might not work properly without it. This patch adds AFMT_F32_LE|BE (as well as AFMT_FLOAT for OSS compatibility) in sys/soundcard.h and implements AFMT_F32_LE|BE <-> AFMT_S32_LE|BE conversion functions. As a result, applications can write/read floats to/from sound(4), but internally, because sound(4) works with integers, we convert floating point samples to integer ones, before doing any processing. The reason for encoding/decoding IEEE754s manually, instead of using fpu_kern(9), is that fpu_kern(9) is not supported by all architectures, and also introduces significant overhead. The IEEE754 encoding/decoding implementation has been written by Ariff Abdullah [2]. [1] http://manuals.opensound.com/developer/AFMT_FLOAT.html [2] https://people.freebsd.org/~ariff/utils/ieee754.c PR: 157050, 184380, 264973, 280612, 281390 Sponsored by: The FreeBSD Foundation MFC after: 1 week Reviewed by: emaste Differential Revision: https://reviews.freebsd.org/D47638
This commit is contained in:
parent
e372211be5
commit
e1bbaa71d6
8 changed files with 98 additions and 8 deletions
|
|
@ -977,9 +977,13 @@ static const struct {
|
|||
#if BYTE_ORDER == LITTLE_ENDIAN
|
||||
{ "s32le", "s32", "32", AFMT_S32_LE },
|
||||
{ "s32be", NULL, NULL, AFMT_S32_BE },
|
||||
{ "f32le", "f32", NULL, AFMT_F32_LE },
|
||||
{ "f32be", NULL, NULL, AFMT_F32_BE },
|
||||
#else
|
||||
{ "s32le", NULL, NULL, AFMT_S32_LE },
|
||||
{ "s32be", "s32", "32", AFMT_S32_BE },
|
||||
{ "f32le", NULL, NULL, AFMT_F32_LE },
|
||||
{ "f32be", "f32", NULL, AFMT_F32_BE },
|
||||
#endif
|
||||
{ "u32le", NULL, NULL, AFMT_U32_LE },
|
||||
{ "u32be", NULL, NULL, AFMT_U32_BE },
|
||||
|
|
|
|||
|
|
@ -102,6 +102,7 @@ static uint32_t feeder_chain_formats_multi[] = {
|
|||
AFMT_S16_LE, AFMT_S16_BE, AFMT_U16_LE, AFMT_U16_BE,
|
||||
AFMT_S24_LE, AFMT_S24_BE, AFMT_U24_LE, AFMT_U24_BE,
|
||||
AFMT_S32_LE, AFMT_S32_BE, AFMT_U32_LE, AFMT_U32_BE,
|
||||
AFMT_F32_LE, AFMT_F32_BE,
|
||||
0
|
||||
};
|
||||
|
||||
|
|
@ -111,6 +112,7 @@ static uint32_t feeder_chain_formats_fullmulti[] = {
|
|||
AFMT_S16_LE, AFMT_S16_BE, AFMT_U16_LE, AFMT_U16_BE,
|
||||
AFMT_S24_LE, AFMT_S24_BE, AFMT_U24_LE, AFMT_U24_BE,
|
||||
AFMT_S32_LE, AFMT_S32_BE, AFMT_U32_LE, AFMT_U32_BE,
|
||||
AFMT_F32_LE, AFMT_F32_BE,
|
||||
0
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -639,6 +639,8 @@ Z_DECLARE(U, 32, LE)
|
|||
Z_DECLARE(U, 16, BE)
|
||||
Z_DECLARE(U, 24, BE)
|
||||
Z_DECLARE(U, 32, BE)
|
||||
Z_DECLARE(F, 32, LE)
|
||||
Z_DECLARE(F, 32, BE)
|
||||
#endif
|
||||
|
||||
enum {
|
||||
|
|
@ -687,6 +689,8 @@ static const struct {
|
|||
Z_RESAMPLER_ENTRY(U, 16, BE),
|
||||
Z_RESAMPLER_ENTRY(U, 24, BE),
|
||||
Z_RESAMPLER_ENTRY(U, 32, BE),
|
||||
Z_RESAMPLER_ENTRY(F, 32, LE),
|
||||
Z_RESAMPLER_ENTRY(F, 32, BE),
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -93,6 +93,8 @@ FEEDVOLUME_DECLARE(U, 32, LE)
|
|||
FEEDVOLUME_DECLARE(U, 16, BE)
|
||||
FEEDVOLUME_DECLARE(U, 24, BE)
|
||||
FEEDVOLUME_DECLARE(U, 32, BE)
|
||||
FEEDVOLUME_DECLARE(F, 32, LE)
|
||||
FEEDVOLUME_DECLARE(F, 32, BE)
|
||||
#endif
|
||||
|
||||
struct feed_volume_info {
|
||||
|
|
|
|||
|
|
@ -128,7 +128,8 @@ static const struct {
|
|||
static __always_inline __unused intpcm_t
|
||||
pcm_sample_read(const uint8_t *src, uint32_t fmt)
|
||||
{
|
||||
intpcm_t v;
|
||||
intpcm_t v, e, m;
|
||||
bool s;
|
||||
|
||||
fmt = AFMT_ENCODING(fmt);
|
||||
|
||||
|
|
@ -190,6 +191,34 @@ pcm_sample_read(const uint8_t *src, uint32_t fmt)
|
|||
v = INTPCM_T(src[3] | src[2] << 8 | src[1] << 16 |
|
||||
(int8_t)(src[0] ^ 0x80) << 24);
|
||||
break;
|
||||
case AFMT_F32_LE: /* FALLTHROUGH */
|
||||
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;
|
||||
/* NaN, +/- Inf or too small */
|
||||
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);
|
||||
|
|
@ -241,8 +270,38 @@ pcm_sample_read_calc(const uint8_t *src, uint32_t fmt)
|
|||
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;
|
||||
|
|
@ -295,13 +354,15 @@ pcm_sample_write(uint8_t *dst, intpcm_t v, uint32_t fmt)
|
|||
dst[1] = v >> 8;
|
||||
dst[0] = (v >> 16) ^ 0x80;
|
||||
break;
|
||||
case AFMT_S32_LE:
|
||||
case AFMT_S32_LE: /* FALLTHROUGH */
|
||||
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_S32_BE: /* FALLTHROUGH */
|
||||
case AFMT_F32_BE:
|
||||
dst[3] = v;
|
||||
dst[2] = v >> 8;
|
||||
dst[1] = v >> 16;
|
||||
|
|
|
|||
|
|
@ -451,15 +451,17 @@ int sound_oss_card_info(oss_card_info *);
|
|||
#endif /* _KERNEL */
|
||||
|
||||
/* make figuring out what a format is easier. got AFMT_STEREO already */
|
||||
#define AFMT_32BIT (AFMT_S32_LE | AFMT_S32_BE | AFMT_U32_LE | AFMT_U32_BE)
|
||||
#define AFMT_32BIT (AFMT_S32_LE | AFMT_S32_BE | AFMT_U32_LE | AFMT_U32_BE | \
|
||||
AFMT_F32_LE | AFMT_F32_BE)
|
||||
#define AFMT_24BIT (AFMT_S24_LE | AFMT_S24_BE | AFMT_U24_LE | AFMT_U24_BE)
|
||||
#define AFMT_16BIT (AFMT_S16_LE | AFMT_S16_BE | AFMT_U16_LE | AFMT_U16_BE)
|
||||
#define AFMT_G711 (AFMT_MU_LAW | AFMT_A_LAW)
|
||||
#define AFMT_8BIT (AFMT_G711 | AFMT_U8 | AFMT_S8)
|
||||
#define AFMT_SIGNED (AFMT_S32_LE | AFMT_S32_BE | AFMT_S24_LE | AFMT_S24_BE | \
|
||||
#define AFMT_SIGNED (AFMT_S32_LE | AFMT_S32_BE | AFMT_F32_LE | AFMT_F32_BE | \
|
||||
AFMT_S24_LE | AFMT_S24_BE | \
|
||||
AFMT_S16_LE | AFMT_S16_BE | AFMT_S8)
|
||||
#define AFMT_BIGENDIAN (AFMT_S32_BE | AFMT_U32_BE | AFMT_S24_BE | AFMT_U24_BE | \
|
||||
AFMT_S16_BE | AFMT_U16_BE)
|
||||
#define AFMT_BIGENDIAN (AFMT_S32_BE | AFMT_U32_BE | AFMT_F32_BE | \
|
||||
AFMT_S24_BE | AFMT_U24_BE | AFMT_S16_BE | AFMT_U16_BE)
|
||||
|
||||
#define AFMT_CONVERTIBLE (AFMT_8BIT | AFMT_16BIT | AFMT_24BIT | \
|
||||
AFMT_32BIT)
|
||||
|
|
@ -509,7 +511,8 @@ int sound_oss_card_info(oss_card_info *);
|
|||
#define AFMT_U8_NE AFMT_U8
|
||||
#define AFMT_S8_NE AFMT_S8
|
||||
|
||||
#define AFMT_SIGNED_NE (AFMT_S8_NE | AFMT_S16_NE | AFMT_S24_NE | AFMT_S32_NE)
|
||||
#define AFMT_SIGNED_NE (AFMT_S8_NE | AFMT_S16_NE | AFMT_S24_NE | \
|
||||
AFMT_S32_NE | AFMT_F32_NE)
|
||||
|
||||
#define AFMT_NE (AFMT_SIGNED_NE | AFMT_U8_NE | AFMT_U16_NE | \
|
||||
AFMT_U24_NE | AFMT_U32_NE)
|
||||
|
|
|
|||
|
|
@ -184,6 +184,8 @@ struct snd_size {
|
|||
#define AFMT_S24_BE 0x00020000 /* Big endian signed 24-bit */
|
||||
#define AFMT_U24_LE 0x00040000 /* Little endian unsigned 24-bit */
|
||||
#define AFMT_U24_BE 0x00080000 /* Big endian unsigned 24-bit */
|
||||
#define AFMT_F32_LE 0x10000000 /* Little endian 32-bit floating point */
|
||||
#define AFMT_F32_BE 0x20000000 /* Big endian 32-bit floating point */
|
||||
|
||||
/* Machine dependent AFMT_* definitions. */
|
||||
#if BYTE_ORDER == LITTLE_ENDIAN
|
||||
|
|
@ -199,6 +201,8 @@ struct snd_size {
|
|||
#define AFMT_U16_OE AFMT_U16_BE
|
||||
#define AFMT_U24_OE AFMT_U24_BE
|
||||
#define AFMT_U32_OE AFMT_U32_BE
|
||||
#define AFMT_F32_NE AFMT_F32_LE
|
||||
#define AFMT_F32_OE AFMT_F32_BE
|
||||
#else
|
||||
#define AFMT_S16_OE AFMT_S16_LE
|
||||
#define AFMT_S24_OE AFMT_S24_LE
|
||||
|
|
@ -212,8 +216,12 @@ struct snd_size {
|
|||
#define AFMT_U16_NE AFMT_U16_BE
|
||||
#define AFMT_U24_NE AFMT_U24_BE
|
||||
#define AFMT_U32_NE AFMT_U32_BE
|
||||
#define AFMT_F32_NE AFMT_F32_BE
|
||||
#define AFMT_F32_OE AFMT_F32_LE
|
||||
#endif
|
||||
|
||||
#define AFMT_FLOAT AFMT_F32_NE /* compatibility alias */
|
||||
|
||||
#define AFMT_STEREO 0x10000000 /* can do/want stereo */
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -70,6 +70,12 @@ static struct afmt_test_data {
|
|||
{"u32be_1", {0x01, 0x02, 0x03, 0x04}, 4, AFMT_U32_BE, 0x81020304},
|
||||
{"u32be_2", {0x81, 0x82, 0x83, 0x84}, 4, AFMT_U32_BE, 0x01828384},
|
||||
|
||||
/* 32 bit floating point sample formats. */
|
||||
{"f32le_1", {0x00, 0x00, 0x00, 0x3f}, 4, AFMT_F32_LE, 0x40000000},
|
||||
{"f32le_2", {0x00, 0x00, 0x00, 0xbf}, 4, AFMT_F32_LE, 0xc0000000},
|
||||
{"f32be_1", {0x3f, 0x00, 0x00, 0x00}, 4, AFMT_F32_BE, 0x40000000},
|
||||
{"f32be_2", {0xbf, 0x00, 0x00, 0x00}, 4, AFMT_F32_BE, 0xc0000000},
|
||||
|
||||
/* u-law and A-law sample formats. */
|
||||
{"mulaw_1", {0x01, 0x00, 0x00, 0x00}, 1, AFMT_MU_LAW, 0xffffff87},
|
||||
{"mulaw_2", {0x81, 0x00, 0x00, 0x00}, 1, AFMT_MU_LAW, 0x00000079},
|
||||
|
|
|
|||
Loading…
Reference in a new issue