diff options
author | Ariff Abdullah <ariff@FreeBSD.org> | 2006-11-26 12:24:06 +0000 |
---|---|---|
committer | Ariff Abdullah <ariff@FreeBSD.org> | 2006-11-26 12:24:06 +0000 |
commit | a580b31a54a7d147994d0036c97bba9056c6b38f (patch) | |
tree | be8f05cc6b2e41975dd084107c4db8978e7c9734 /sys/dev/sound/pcm/sound.h | |
parent | 4bc4dc4c4ba2a75a53aa3d9e43300c2b3fc11946 (diff) | |
download | src-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.h | 287 |
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; |