aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/sound/pcm/sound.h
diff options
context:
space:
mode:
authorAriff Abdullah <ariff@FreeBSD.org>2006-11-26 12:24:06 +0000
committerAriff Abdullah <ariff@FreeBSD.org>2006-11-26 12:24:06 +0000
commita580b31a54a7d147994d0036c97bba9056c6b38f (patch)
treebe8f05cc6b2e41975dd084107c4db8978e7c9734 /sys/dev/sound/pcm/sound.h
parent4bc4dc4c4ba2a75a53aa3d9e43300c2b3fc11946 (diff)
downloadsrc-a580b31a54a7d147994d0036c97bba9056c6b38f.tar.gz
src-a580b31a54a7d147994d0036c97bba9056c6b38f.zip
Welcome to Once-a-year Sound Mega-Commit. Enjoy numerous updates and fixes
in every sense. General ------- - Multichannel safe, endian safe, format safe * Large part of critical pcm filters such as vchan.c, feeder_rate.c, feeder_volume.c, feeder_fmt.c and feeder.c has been rewritten so that using them does not cause the pcm data to be converted to 16bit little endian. * Macrosses for accessing pcm data safely are defined within sound.h in the form of PCM_READ_* / PCM_WRITE_* * Currently, most of them are probably limited for mono/stereo handling, but the future addition of true multichannel will be much easier. - Low latency operation * Well, this require lot more works to do not just within sound driver, but we're heading towards right direction. Buffer/block sizing within channel.c is rewritten to calculate precise allocation for various combination of sample/data/rate size. As a result, applying correct SNDCTL_DSP_POLICY value will achive expected latency behaviour simmilar to what commercial 4front driver do. * Signal handling fix. ctrl+c of "cat /dev/zero > /dev/dsp" does not result long delay. * Eliminate sound truncation if the sound data is too small. DIY: 1) Download / extract http://people.freebsd.org/~ariff/lowlatency/shortfiles.tar.gz 2) Do a comparison between "cat state*.au > /dev/dsp" and "for x in state*.au ; do cat $x > /dev/dsp ; done" - there should be no "perceivable" differences. Double close for PR kern/31445. CAVEAT: Low latency come with (unbearable) price especially for poorly written applications. Applications that trying to act smarter by requesting (wrong) blocksize/blockcount will suffer the most. Fixup samples/patches can be found at: http://people.freebsd.org/~ariff/ports/ - Switch minimum/maximum sampling rate limit to "1" and "2016000" (48k * 42) due to closer compatibility with 4front driver. Discussed with: marcus@ (long time ago?) - All driver specific sysctls in the form of "hw.snd.pcm%d.*" have been moved to their own dev sysctl nodes, notably: hw.snd.pcm%d.vchans -> dev.pcm.%d.vchans Bump __FreeBSD_version. Driver specific --------------- - Ditto for sysctls. - snd_atiixp, snd_es137x, snd_via8233, snd_hda * Numerous cleanups and fixes. * _EXPERIMENTAL_ polling mode support using simple callout_* mechanisme. This was intended for pure debugging and latency measurement, but proven good enough in few unexpected and rare cases (such as problematic shared IRQ with GIANT devices - USB). Polling can be enabled/disabled through dev.pcm.0.polling. Disabled by default. - snd_ich * Fix possible overflow during speed calibration. Delay final initialization (pcm_setstatus) after calibration finished. PR: kern/100169 Tested by: Kevin Overman <oberman@es.net> * Inverted EAPD for few Nec VersaPro. PR: kern/104715 Submitted by: KAWATA Masahiko <kawata@mta.biglobe.ne.jp> Thanks to various people, notably Joel Dahl, Yuriy Tsibizov, Kevin Oberman, those at #freebsd-azalia @ freenode and others for testing. Joel Dahl will do the manpage update.
Notes
Notes: svn path=/head/; revision=164614
Diffstat (limited to 'sys/dev/sound/pcm/sound.h')
-rw-r--r--sys/dev/sound/pcm/sound.h287
1 files changed, 280 insertions, 7 deletions
diff --git a/sys/dev/sound/pcm/sound.h b/sys/dev/sound/pcm/sound.h
index eeb5c20101f8..b62b1a557a6a 100644
--- a/sys/dev/sound/pcm/sound.h
+++ b/sys/dev/sound/pcm/sound.h
@@ -58,6 +58,7 @@
#include <machine/resource.h>
#include <machine/bus.h>
#include <sys/rman.h>
+#include <sys/limits.h>
#include <sys/mman.h>
#include <sys/poll.h>
#include <sys/sbuf.h>
@@ -148,6 +149,281 @@ nomenclature:
(((var)<(low))? (low) : ((var)>(high))? (high) : (var))
#define DSP_BUFFSIZE (8192)
+/*
+ * Macros for reading/writing PCM sample / int 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. To avoid overflow,
+ * we truncate 32bit (and only 32bit) samples down to 24bit (see below
+ * for the reason), unless PCM_USE_64BIT_ARITH is defined.
+ */
+
+/*
+ * Automatically turn on 64bit arithmetic on suitable archs
+ * (amd64 64bit, ia64, etc..) for wider 32bit samples / integer processing.
+ */
+#if LONG_BIT >= 64
+#undef PCM_USE_64BIT_ARITH
+#define PCM_USE_64BIT_ARITH 1
+#else
+#if 0
+#undef PCM_USE_64BIT_ARITH
+#define PCM_USE_64BIT_ARITH 1
+#endif
+#endif
+
+#ifdef PCM_USE_64BIT_ARITH
+typedef int64_t intpcm_t;
+#else
+typedef int32_t intpcm_t;
+#endif
+
+/* 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 PCM_USE_64BIT_ARITH
+#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
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+#define PCM_READ_S16_LE(b8) *((int16_t *)(b8))
+#define _PCM_READ_S32_LE(b8) *((int32_t *)(b8))
+#define PCM_READ_S16_BE(b8) \
+ ((int32_t)((b8)[1] | ((int8_t)((b8)[0])) << 8))
+#define _PCM_READ_S32_BE(b8) \
+ ((int32_t)((b8)[3] | (b8)[2] << 8 | (b8)[1] << 16 | \
+ ((int8_t)((b8)[0])) << 24))
+
+#define PCM_WRITE_S16_LE(b8, val) *((int16_t *)(b8)) = (val)
+#define _PCM_WRITE_S32_LE(b8, val) *((int32_t *)(b8)) = (val)
+#define PCM_WRITE_S16_BE(bb8, vval) do { \
+ int32_t val = (vval); \
+ uint8_t *b8 = (bb8); \
+ b8[1] = val; \
+ b8[0] = val >> 8; \
+ } while(0)
+#define _PCM_WRITE_S32_BE(bb8, vval) do { \
+ int32_t val = (vval); \
+ uint8_t *b8 = (bb8); \
+ b8[3] = val; \
+ b8[2] = val >> 8; \
+ b8[1] = val >> 16; \
+ b8[0] = val >> 24; \
+ } while(0)
+
+#define PCM_READ_U16_LE(b8) ((int16_t)(*((uint16_t *)(b8)) ^ 0x8000))
+#define _PCM_READ_U32_LE(b8) ((int32_t)(*((uint32_t *)(b8)) ^ 0x80000000))
+#define PCM_READ_U16_BE(b8) \
+ ((int32_t)((b8)[1] | ((int8_t)((b8)[0] ^ 0x80)) << 8))
+#define _PCM_READ_U32_BE(b8) \
+ ((int32_t)((b8)[3] | (b8)[2] << 8 | (b8)[1] << 16 | \
+ ((int8_t)((b8)[0] ^ 0x80)) << 24))
+
+#define PCM_WRITE_U16_LE(b8, val) *((uint16_t *)(b8)) = (val) ^ 0x8000
+#define _PCM_WRITE_U32_LE(b8, val) *((uint32_t *)(b8)) = (val) ^ 0x80000000
+#define PCM_WRITE_U16_BE(bb8, vval) do { \
+ int32_t val = (vval); \
+ uint8_t *b8 = (bb8); \
+ b8[1] = val; \
+ b8[0] = (val >> 8) ^ 0x80; \
+ } while(0)
+#define _PCM_WRITE_U32_BE(bb8, vval) do { \
+ int32_t val = (vval); \
+ uint8_t *b8 = (bb8); \
+ b8[3] = val; \
+ b8[2] = val >> 8; \
+ b8[1] = val >> 16; \
+ b8[0] = (val >> 24) ^ 0x80; \
+ } while(0)
+#else /* !LITTLE_ENDIAN */
+#define PCM_READ_S16_LE(b8) \
+ ((int32_t)((b8)[0] | ((int8_t)((b8)[1])) << 8))
+#define _PCM_READ_S32_LE(b8) \
+ ((int32_t)((b8)[0] | (b8)[1] << 8 | (b8)[2] << 16 | \
+ ((int8_t)((b8)[3])) << 24))
+#define PCM_READ_S16_BE(b8) *((int16_t *)(b8))
+#define _PCM_READ_S32_BE(b8) *((int32_t *)(b8))
+
+#define PCM_WRITE_S16_LE(bb8, vval) do { \
+ int32_t val = (vval); \
+ uint8_t *b8 = (bb8); \
+ b8[0] = val; \
+ b8[1] = val >> 8; \
+ } while(0)
+#define _PCM_WRITE_S32_LE(bb8, vval) do { \
+ int32_t val = (vval); \
+ uint8_t *b8 = (bb8); \
+ b8[0] = val; \
+ b8[1] = val >> 8; \
+ b8[2] = val >> 16; \
+ b8[3] = val >> 24; \
+ } while(0)
+#define PCM_WRITE_S16_BE(b8, val) *((int16_t *)(b8)) = (val)
+#define _PCM_WRITE_S32_BE(b8, val) *((int32_t *)(b8)) = (val)
+
+#define PCM_READ_U16_LE(b8) \
+ ((int32_t)((b8)[0] | ((int8_t)((b8)[1] ^ 0x80)) << 8))
+#define _PCM_READ_U32_LE(b8) \
+ ((int32_t)((b8)[0] | (b8)[1] << 8 | (b8)[2] << 16 | \
+ ((int8_t)((b8)[3] ^ 0x80)) << 24))
+#define PCM_READ_U16_BE(b8) ((int16_t)(*((uint16_t *)(b8)) ^ 0x8000))
+#define _PCM_READ_U32_BE(b8) ((int32_t)(*((uint32_t *)(b8)) ^ 0x80000000))
+
+#define PCM_WRITE_U16_LE(bb8, vval) do { \
+ int32_t val = (vval); \
+ uint8_t *b8 = (bb8); \
+ b8[0] = val; \
+ b8[1] = (val >> 8) ^ 0x80; \
+ } while(0)
+#define _PCM_WRITE_U32_LE(bb8, vval) do { \
+ int32_t val = (vval); \
+ uint8_t *b8 = (bb8); \
+ b8[0] = val; \
+ b8[1] = val >> 8; \
+ b8[2] = val >> 16; \
+ b8[3] = (val >> 24) ^ 0x80; \
+ } while(0)
+#define PCM_WRITE_U16_BE(b8, val) *((uint16_t *)(b8)) = (val) ^ 0x8000
+#define _PCM_WRITE_U32_BE(b8, val) *((uint32_t *)(b8)) = (val) ^ 0x80000000
+#endif
+
+#define PCM_READ_S24_LE(b8) \
+ ((int32_t)((b8)[0] | (b8)[1] << 8 | ((int8_t)((b8)[2])) << 16))
+#define PCM_READ_S24_BE(b8) \
+ ((int32_t)((b8)[2] | (b8)[1] << 8 | ((int8_t)((b8)[0])) << 16))
+
+#define PCM_WRITE_S24_LE(bb8, vval) do { \
+ int32_t val = (vval); \
+ uint8_t *b8 = (bb8); \
+ b8[0] = val; \
+ b8[1] = val >> 8; \
+ b8[2] = val >> 16; \
+ } while(0)
+#define PCM_WRITE_S24_BE(bb8, vval) do { \
+ int32_t val = (vval); \
+ uint8_t *b8 = (bb8); \
+ b8[2] = val; \
+ b8[1] = val >> 8; \
+ b8[0] = val >> 16; \
+ } while(0)
+
+#define PCM_READ_U24_LE(b8) \
+ ((int32_t)((b8)[0] | (b8)[1] << 8 | \
+ ((int8_t)((b8)[2] ^ 0x80)) << 16))
+#define PCM_READ_U24_BE(b8) \
+ ((int32_t)((b8)[2] | (b8)[1] << 8 | \
+ ((int8_t)((b8)[0] ^ 0x80)) << 16))
+
+#define PCM_WRITE_U24_LE(bb8, vval) do { \
+ int32_t val = (vval); \
+ uint8_t *b8 = (bb8); \
+ b8[0] = val; \
+ b8[1] = val >> 8; \
+ b8[2] = (val >> 16) ^ 0x80; \
+ } while(0)
+#define PCM_WRITE_U24_BE(bb8, vval) do { \
+ int32_t val = (vval); \
+ uint8_t *b8 = (bb8); \
+ b8[2] = val; \
+ b8[1] = val >> 8; \
+ b8[0] = (val >> 16) ^ 0x80; \
+ } while(0)
+
+#ifdef PCM_USE_64BIT_ARITH
+#define PCM_READ_S32_LE(b8) _PCM_READ_S32_LE(b8)
+#define PCM_READ_S32_BE(b8) _PCM_READ_S32_BE(b8)
+#define PCM_WRITE_S32_LE(b8, val) _PCM_WRITE_S32_LE(b8, val)
+#define PCM_WRITE_S32_BE(b8, val) _PCM_WRITE_S32_BE(b8, val)
+
+#define PCM_READ_U32_LE(b8) _PCM_READ_U32_LE(b8)
+#define PCM_READ_U32_BE(b8) _PCM_READ_U32_BE(b8)
+#define PCM_WRITE_U32_LE(b8, val) _PCM_WRITE_U32_LE(b8, val)
+#define PCM_WRITE_U32_BE(b8, val) _PCM_WRITE_U32_BE(b8, val)
+#else /* !PCM_USE_64BIT_ARITH */
+/*
+ * 24bit integer ?!? This is quite unfortunate, eh? Get the fact straight:
+ * Dynamic range for:
+ * 1) Human =~ 140db
+ * 2) 16bit = 96db (close enough)
+ * 3) 24bit = 144db (perfect)
+ * 4) 32bit = 196db (way too much)
+ * 5) Bugs Bunny = Gazillion!@%$Erbzzztt-EINVAL db
+ * Since we're not Bugs Bunny ..uh..err.. avoiding 64bit arithmetic, 24bit
+ * is pretty much sufficient for our signed integer processing.
+ */
+#define PCM_READ_S32_LE(b8) (_PCM_READ_S32_LE(b8) >> PCM_FXSHIFT)
+#define PCM_READ_S32_BE(b8) (_PCM_READ_S32_BE(b8) >> PCM_FXSHIFT)
+#define PCM_WRITE_S32_LE(b8, val) _PCM_WRITE_S32_LE(b8, (val) << PCM_FXSHIFT)
+#define PCM_WRITE_S32_BE(b8, val) _PCM_WRITE_S32_BE(b8, (val) << PCM_FXSHIFT)
+
+#define PCM_READ_U32_LE(b8) (_PCM_READ_U32_LE(b8) >> PCM_FXSHIFT)
+#define PCM_READ_U32_BE(b8) (_PCM_READ_U32_BE(b8) >> PCM_FXSHIFT)
+#define PCM_WRITE_U32_LE(b8, val) _PCM_WRITE_U32_LE(b8, (val) << PCM_FXSHIFT)
+#define PCM_WRITE_U32_BE(b8, val) _PCM_WRITE_U32_BE(b8, (val) << PCM_FXSHIFT)
+#endif
+
+/*
+ * 8bit sample is pretty much useless since it doesn't provide
+ * sufficient dynamic range throughout our filtering process.
+ * For the sake of completeness, declare it anyway.
+ */
+#define PCM_READ_S8(b8) *((int8_t *)(b8))
+#define PCM_READ_S8_NE(b8) PCM_READ_S8(b8)
+#define PCM_READ_U8(b8) ((int8_t)(*((uint8_t *)(b8)) ^ 0x80))
+#define PCM_READ_U8_NE(b8) PCM_READ_U8(b8)
+
+#define PCM_WRITE_S8(b8, val) *((int8_t *)(b8)) = (val)
+#define PCM_WRITE_S8_NE(b8, val) PCM_WRITE_S8(b8, val)
+#define PCM_WRITE_U8(b8, val) *((uint8_t *)(b8)) = (val) ^ 0x80
+#define PCM_WRITE_U8_NE(b8, val) PCM_WRITE_U8(b8, val)
+
+#define PCM_CLAMP_S8(val) \
+ (((val) > PCM_S8_MAX) ? PCM_S8_MAX : \
+ (((val) < PCM_S8_MIN) ? PCM_S8_MIN : (val)))
+#define PCM_CLAMP_S16(val) \
+ (((val) > PCM_S16_MAX) ? PCM_S16_MAX : \
+ (((val) < PCM_S16_MIN) ? PCM_S16_MIN : (val)))
+#define PCM_CLAMP_S24(val) \
+ (((val) > PCM_S24_MAX) ? PCM_S24_MAX : \
+ (((val) < PCM_S24_MIN) ? PCM_S24_MIN : (val)))
+
+#ifdef PCM_USE_64BIT_ARITH
+#define PCM_CLAMP_S32(val) \
+ (((val) > PCM_S32_MAX) ? PCM_S32_MAX : \
+ (((val) < PCM_S32_MIN) ? PCM_S32_MIN : (val)))
+#else
+#define PCM_CLAMP_S32(val) \
+ (((val) > PCM_S24_MAX) ? PCM_S32_MAX : \
+ (((val) < PCM_S24_MIN) ? PCM_S32_MIN : \
+ ((val) << PCM_FXSHIFT)))
+#endif
+
+#define PCM_CLAMP_U8(val) PCM_CLAMP_S8(val)
+#define PCM_CLAMP_U16(val) PCM_CLAMP_S16(val)
+#define PCM_CLAMP_U24(val) PCM_CLAMP_S24(val)
+#define PCM_CLAMP_U32(val) PCM_CLAMP_S32(val)
+
/* 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_24BIT (AFMT_S24_LE | AFMT_S24_BE | AFMT_U24_LE | AFMT_U24_BE)
@@ -185,7 +461,7 @@ int fkchan_kill(struct pcm_channel *c);
#define SND_DEV_SNDPROC 9 /* /dev/sndproc for programmable devices */
#define SND_DEV_PSS SND_DEV_SNDPROC /* ? */
#define SND_DEV_NORESET 10
-#define SND_DEV_DSPREC 11 /* recording channels */
+#define SND_DEV_DSPHW 11 /* specific channel request */
#define DSP_DEFAULT_SPEED 8000
@@ -194,6 +470,8 @@ int fkchan_kill(struct pcm_channel *c);
extern int pcm_veto_load;
extern int snd_unit;
+extern int snd_maxautovchans;
+extern int snd_verbose;
extern devclass_t pcm_devclass;
extern struct unrhdr *pcmsg_unrhdr;
@@ -210,9 +488,6 @@ extern struct unrhdr *pcmsg_unrhdr;
SYSCTL_DECL(_hw_snd);
-struct sysctl_ctx_list *snd_sysctl_tree(device_t dev);
-struct sysctl_oid *snd_sysctl_tree_top(device_t dev);
-
struct pcm_channel *pcm_getfakechan(struct snddev_info *d);
int pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction, pid_t pid, int chnum);
int pcm_chnrelease(struct pcm_channel *c);
@@ -287,7 +562,7 @@ struct snddev_channel {
struct cdev *dsp_devt;
struct cdev *dspW_devt;
struct cdev *audio_devt;
- struct cdev *dspr_devt;
+ struct cdev *dspHW_devt;
};
struct snddev_info {
@@ -300,8 +575,6 @@ struct snddev_info {
void *devinfo;
device_t dev;
char status[SND_STATUSLEN];
- struct sysctl_ctx_list sysctl_tree;
- struct sysctl_oid *sysctl_tree_top;
struct mtx *lock;
struct cdev *mixer_dev;