/*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2006-2009 Ariff Abdullah * All rights reserved. * Copyright (c) 2024-2025 The FreeBSD Foundation * * Portions of this software were developed by Christos Margiolis * under sponsorship from the FreeBSD Foundation. * * 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. */ #ifndef _SND_PCM_H_ #define _SND_PCM_H_ #include #include #ifndef _KERNEL #include /* for __assert_unreachable() */ #endif /* * Automatically turn on 64bit arithmetic on suitable archs * (amd64 64bit, etc..) for wider 32bit samples / integer processing. */ #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; /* 32bit fixed point shift */ #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 /* Bytes-per-sample definition */ #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 }; /* * Functions for reading/writing PCM integer sample values from bytes array. * Since every process is done using signed integer (and to make our life less * miserable), unsigned sample will be converted to its signed counterpart and * restored during writing back. */ 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: /* 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); __assert_unreachable(); } return (v); } /* * Read sample and normalize to 32-bit magnitude. */ 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))); } /* * Read sample and restrict magnitude to 24 bits. */ 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 /* * Dynamic range for humans: ~140db. * * 16bit = 96db (close enough) * 24bit = 144db (perfect) * 32bit = 196db (way too much) * * 24bit is pretty much sufficient for our signed integer processing. * Also, to avoid overflow, we truncate 32bit (and only 32bit) samples * down to 24bit (see below for the reason), unless SND_PCM_64 is * defined. */ 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: /* 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: /* FALLTHROUGH */ 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(); } } /* * Write sample and normalize to original magnitude. */ 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); } /* * To be used with pcm_sample_read_calc(). */ static __always_inline __unused void pcm_sample_write_calc(uint8_t *dst, intpcm_t v, uint32_t fmt) { #ifndef SND_PCM_64 /* Shift back to 32-bit magnitude. */ 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 /* !_SND_PCM_H_ */