Sound Mega-commit. Expect further cleanup until code freeze.
For a slightly thorough explaination, please refer to [1] http://people.freebsd.org/~ariff/SOUND_4.TXT.html . Summary of changes includes: 1 Volume Per-Channel (vpc). Provides private / standalone volume control unique per-stream pcm channel without touching master volume / pcm. Applications can directly use SNDCTL_DSP_[GET|SET][PLAY|REC]VOL, or for backwards compatibility, SOUND_MIXER_PCM through the opened dsp device instead of /dev/mixer. Special "bypass" mode is enabled through /dev/mixer which will automatically detect if the adjustment is made through /dev/mixer and forward its request to this private volume controller. Changes to this volume object will not interfere with other channels. Requirements: - SNDCTL_DSP_[GET|SET][PLAY|REC]_VOL are newer ioctls (OSSv4) which require specific application modifications (preferred). - No modifications required for using bypass mode, so applications like mplayer or xmms should work out of the box. Kernel hints: - hint.pcm.%d.vpc (0 = disable vpc). Kernel sysctls: - hw.snd.vpc_mixer_bypass (default: 1). Enable or disable /dev/mixer bypass mode. - hw.snd.vpc_autoreset (default: 1). By default, closing/opening /dev/dsp will reset the volume back to 0 db gain/attenuation. Setting this to 0 will preserve its settings across device closing/opening. - hw.snd.vpc_reset (default: 0). Panic/reset button to reset all volume settings back to 0 db. - hw.snd.vpc_0db (default: 45). 0 db relative to linear mixer value. 2 High quality fixed-point Bandlimited SINC sampling rate converter, based on Julius O'Smith's Digital Audio Resampling - http://ccrma.stanford.edu/~jos/resample/. It includes a filter design script written in awk (the clumsiest joke I've ever written) - 100% 32bit fixed-point, 64bit accumulator. - Possibly among the fastest (if not fastest) of its kind. - Resampling quality is tunable, either runtime or during kernel compilation (FEEDER_RATE_PRESETS). - Quality can be further customized during kernel compilation by defining FEEDER_RATE_PRESETS in /etc/make.conf. Kernel sysctls: - hw.snd.feeder_rate_quality. 0 - Zero-order Hold (ZOH). Fastest, bad quality. 1 - Linear Interpolation (LINEAR). Slightly slower than ZOH, better quality but still does not eliminate aliasing. 2 - (and above) - Sinc Interpolation(SINC). Best quality. SINC quality always start from 2 and above. Rough quality comparisons: - http://people.freebsd.org/~ariff/z_comparison/ 3 Bit-perfect mode. Bypasses all feeder/dsp effects. Pure sound will be directly fed into the hardware. 4 Parametric (compile time) Software Equalizer (Bass/Treble mixer). Can be customized by defining FEEDER_EQ_PRESETS in /etc/make.conf. 5 Transparent/Adaptive Virtual Channel. Now you don't have to disable vchans in order to make digital format pass through. It also makes vchans more dynamic by choosing a better format/rate among all the concurrent streams, which means that dev.pcm.X.play.vchanformat/rate becomes sort of optional. 6 Exclusive Stream, with special open() mode O_EXCL. This will "mute" other concurrent vchan streams and only allow a single channel with O_EXCL set to keep producing sound. Other Changes: * most feeder_* stuffs are compilable in userland. Let's not speculate whether we should go all out for it (save that for FreeBSD 16.0-RELEASE). * kobj signature fixups, thanks to Andriy Gapon <avg@freebsd.org> * pull out channel mixing logic out of vchan.c and create its own feeder_mixer for world justice. * various refactoring here and there, for good or bad. * activation of few more OSSv4 ioctls() (see [1] above). * opt_snd.h for possible compile time configuration: (mostly for debugging purposes, don't try these at home) SND_DEBUG SND_DIAGNOSTIC SND_FEEDER_MULTIFORMAT SND_FEEDER_FULL_MULTIFORMAT SND_FEEDER_RATE_HP SND_PCM_64 SND_OLDSTEREO Manual page updates are on the way. Tested by: joel, Olivier SMEDTS <olivier at gid0 d org>, too many unsung / unnamed heroes.
This commit is contained in:
parent
0a276edef9
commit
90da2b2859
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=193640
@ -65,6 +65,21 @@ p17v-alsa%diked.h optional snd_emu10kx pci \
|
||||
compile-with "CC='${CC}' AWK=${AWK} sh $S/tools/emu10k1-mkalsa.sh $S/gnu/dev/sound/pci/p17v-alsa.h p17v-alsa%diked.h" \
|
||||
no-obj no-implicit-rule before-depend \
|
||||
clean "p17v-alsa%diked.h"
|
||||
feeder_eq_gen.h optional sound \
|
||||
dependency "$S/tools/feeder_eq_mkfilter.awk" \
|
||||
compile-with "${AWK} -f $S/tools/feeder_eq_mkfilter.awk -- ${FEEDER_EQ_PRESETS} > feeder_eq_gen.h" \
|
||||
no-obj no-implicit-rule before-depend \
|
||||
clean "feeder_eq_gen.h"
|
||||
feeder_rate_gen.h optional sound \
|
||||
dependency "$S/tools/feeder_rate_mkfilter.awk" \
|
||||
compile-with "${AWK} -f $S/tools/feeder_rate_mkfilter.awk -- ${FEEDER_RATE_PRESETS} > feeder_rate_gen.h" \
|
||||
no-obj no-implicit-rule before-depend \
|
||||
clean "feeder_rate_gen.h"
|
||||
snd_fxdiv_gen.h optional sound \
|
||||
dependency "$S/tools/snd_fxdiv_gen.awk" \
|
||||
compile-with "${AWK} -f $S/tools/snd_fxdiv_gen.awk -- > snd_fxdiv_gen.h" \
|
||||
no-obj no-implicit-rule before-depend \
|
||||
clean "snd_fxdiv_gen.h"
|
||||
miidevs.h optional miibus | mii \
|
||||
dependency "$S/tools/miidevs2h.awk $S/dev/mii/miidevs" \
|
||||
compile-with "${AWK} -f $S/tools/miidevs2h.awk $S/dev/mii/miidevs" \
|
||||
@ -1433,16 +1448,28 @@ dev/sound/pci/hda/hdac.c optional snd_hda pci
|
||||
dev/sound/pcm/ac97.c optional sound
|
||||
dev/sound/pcm/ac97_if.m optional sound
|
||||
dev/sound/pcm/ac97_patch.c optional sound
|
||||
dev/sound/pcm/buffer.c optional sound
|
||||
dev/sound/pcm/buffer.c optional sound \
|
||||
dependency "snd_fxdiv_gen.h"
|
||||
dev/sound/pcm/channel.c optional sound
|
||||
dev/sound/pcm/channel_if.m optional sound
|
||||
dev/sound/pcm/dsp.c optional sound
|
||||
dev/sound/pcm/fake.c optional sound
|
||||
dev/sound/pcm/feeder.c optional sound
|
||||
dev/sound/pcm/feeder_fmt.c optional sound
|
||||
dev/sound/pcm/feeder_chain.c optional sound
|
||||
dev/sound/pcm/feeder_eq.c optional sound \
|
||||
dependency "feeder_eq_gen.h" \
|
||||
dependency "snd_fxdiv_gen.h"
|
||||
dev/sound/pcm/feeder_if.m optional sound
|
||||
dev/sound/pcm/feeder_rate.c optional sound
|
||||
dev/sound/pcm/feeder_volume.c optional sound
|
||||
dev/sound/pcm/feeder_format.c optional sound \
|
||||
dependency "snd_fxdiv_gen.h"
|
||||
dev/sound/pcm/feeder_matrix.c optional sound \
|
||||
dependency "snd_fxdiv_gen.h"
|
||||
dev/sound/pcm/feeder_mixer.c optional sound \
|
||||
dependency "snd_fxdiv_gen.h"
|
||||
dev/sound/pcm/feeder_rate.c optional sound \
|
||||
dependency "feeder_rate_gen.h" \
|
||||
dependency "snd_fxdiv_gen.h"
|
||||
dev/sound/pcm/feeder_volume.c optional sound \
|
||||
dependency "snd_fxdiv_gen.h"
|
||||
dev/sound/pcm/mixer.c optional sound
|
||||
dev/sound/pcm/mixer_if.m optional sound
|
||||
dev/sound/pcm/sndstat.c optional sound
|
||||
|
@ -830,3 +830,12 @@ VIMAGE_GLOBALS opt_global.h
|
||||
# Common Flash Interface (CFI) options
|
||||
CFI_SUPPORT_STRATAFLASH opt_cfi.h
|
||||
CFI_ARMEDANDDANGEROUS opt_cfi.h
|
||||
|
||||
# Sound options
|
||||
SND_DEBUG opt_snd.h
|
||||
SND_DIAGNOSTIC opt_snd.h
|
||||
SND_FEEDER_MULTIFORMAT opt_snd.h
|
||||
SND_FEEDER_FULL_MULTIFORMAT opt_snd.h
|
||||
SND_FEEDER_RATE_HP opt_snd.h
|
||||
SND_PCM_64 opt_snd.h
|
||||
SND_OLDSTEREO opt_snd.h
|
||||
|
@ -33,6 +33,10 @@
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/proc.h>
|
||||
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#if defined(SND_DIAGNOSTIC) || defined(SND_DEBUG)
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
#endif
|
||||
@ -88,9 +92,9 @@ struct snd_clone {
|
||||
#define SND_CLONE_ASSERT(x, y) do { \
|
||||
if (!(x)) \
|
||||
panic y; \
|
||||
} while(0)
|
||||
} while (0)
|
||||
#else
|
||||
#define SND_CLONE_ASSERT(x...) KASSERT(x)
|
||||
#define SND_CLONE_ASSERT(...) KASSERT(__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
@ -26,6 +26,10 @@
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
|
||||
static int
|
||||
@ -38,7 +42,7 @@ snd_modevent(module_t mod, int type, void *data)
|
||||
case MOD_UNLOAD:
|
||||
break;
|
||||
default:
|
||||
return (EOPNOTSUPP);
|
||||
return (ENOTSUP);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
|
@ -26,6 +26,10 @@
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
#include <dev/sound/isa/ad1816.h>
|
||||
|
||||
@ -62,14 +66,14 @@ struct ad1816_info {
|
||||
};
|
||||
|
||||
static u_int32_t ad1816_fmt[] = {
|
||||
AFMT_U8,
|
||||
AFMT_STEREO | AFMT_U8,
|
||||
AFMT_S16_LE,
|
||||
AFMT_STEREO | AFMT_S16_LE,
|
||||
AFMT_MU_LAW,
|
||||
AFMT_STEREO | AFMT_MU_LAW,
|
||||
AFMT_A_LAW,
|
||||
AFMT_STEREO | AFMT_A_LAW,
|
||||
SND_FORMAT(AFMT_U8, 1, 0),
|
||||
SND_FORMAT(AFMT_U8, 2, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 1, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 2, 0),
|
||||
SND_FORMAT(AFMT_MU_LAW, 1, 0),
|
||||
SND_FORMAT(AFMT_MU_LAW, 2, 0),
|
||||
SND_FORMAT(AFMT_A_LAW, 1, 0),
|
||||
SND_FORMAT(AFMT_A_LAW, 2, 0),
|
||||
0
|
||||
};
|
||||
|
||||
@ -269,7 +273,7 @@ ad1816mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
|
||||
return left | (right << 8);
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
ad1816mix_setrecsrc(struct snd_mixer *m, u_int32_t src)
|
||||
{
|
||||
struct ad1816_info *ad1816 = mix_getdevinfo(m);
|
||||
@ -303,7 +307,7 @@ static kobj_method_t ad1816mixer_methods[] = {
|
||||
KOBJMETHOD(mixer_init, ad1816mix_init),
|
||||
KOBJMETHOD(mixer_set, ad1816mix_set),
|
||||
KOBJMETHOD(mixer_setrecsrc, ad1816mix_setrecsrc),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
MIXER_DECLARE(ad1816mixer);
|
||||
|
||||
@ -315,25 +319,21 @@ ad1816chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channe
|
||||
struct ad1816_info *ad1816 = devinfo;
|
||||
struct ad1816_chinfo *ch = (dir == PCMDIR_PLAY)? &ad1816->pch : &ad1816->rch;
|
||||
|
||||
ch->dir = dir;
|
||||
ch->parent = ad1816;
|
||||
ch->channel = c;
|
||||
ch->buffer = b;
|
||||
if (sndbuf_alloc(ch->buffer, ad1816->parent_dmat, 0, ad1816->bufsize) != 0)
|
||||
return NULL;
|
||||
|
||||
sndbuf_dmasetup(ch->buffer, (dir == PCMDIR_PLAY) ? ad1816->drq1 :
|
||||
ad1816->drq2);
|
||||
if (SND_DMA(ch->buffer))
|
||||
sndbuf_dmasetdir(ch->buffer, dir);
|
||||
|
||||
return ch;
|
||||
}
|
||||
|
||||
static int
|
||||
ad1816chan_setdir(kobj_t obj, void *data, int dir)
|
||||
{
|
||||
struct ad1816_chinfo *ch = data;
|
||||
struct ad1816_info *ad1816 = ch->parent;
|
||||
|
||||
sndbuf_dmasetup(ch->buffer, (dir == PCMDIR_PLAY)? ad1816->drq1 : ad1816->drq2);
|
||||
ch->dir = dir;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ad1816chan_setformat(kobj_t obj, void *data, u_int32_t format)
|
||||
{
|
||||
@ -351,7 +351,7 @@ ad1816chan_setformat(kobj_t obj, void *data, u_int32_t format)
|
||||
ad1816_write(ad1816, 10, 0x0000);
|
||||
ad1816_write(ad1816, 11, 0x0000);
|
||||
}
|
||||
switch (format & ~AFMT_STEREO) {
|
||||
switch (AFMT_ENCODING(format)) {
|
||||
case AFMT_A_LAW:
|
||||
fmt = AD1816_ALAW;
|
||||
break;
|
||||
@ -372,7 +372,7 @@ ad1816chan_setformat(kobj_t obj, void *data, u_int32_t format)
|
||||
fmt = AD1816_U8;
|
||||
break;
|
||||
}
|
||||
if (format & AFMT_STEREO) fmt |= AD1816_STEREO;
|
||||
if (AFMT_CHANNEL(format) > 1) fmt |= AD1816_STEREO;
|
||||
io_wr(ad1816, reg, fmt);
|
||||
ad1816_unlock(ad1816);
|
||||
#if 0
|
||||
@ -382,7 +382,7 @@ ad1816chan_setformat(kobj_t obj, void *data, u_int32_t format)
|
||||
#endif
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
ad1816chan_setspeed(kobj_t obj, void *data, u_int32_t speed)
|
||||
{
|
||||
struct ad1816_chinfo *ch = data;
|
||||
@ -395,7 +395,7 @@ ad1816chan_setspeed(kobj_t obj, void *data, u_int32_t speed)
|
||||
return speed;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
ad1816chan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
|
||||
{
|
||||
struct ad1816_chinfo *ch = data;
|
||||
@ -456,7 +456,7 @@ ad1816chan_trigger(kobj_t obj, void *data, int go)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
ad1816chan_getptr(kobj_t obj, void *data)
|
||||
{
|
||||
struct ad1816_chinfo *ch = data;
|
||||
@ -471,14 +471,13 @@ ad1816chan_getcaps(kobj_t obj, void *data)
|
||||
|
||||
static kobj_method_t ad1816chan_methods[] = {
|
||||
KOBJMETHOD(channel_init, ad1816chan_init),
|
||||
KOBJMETHOD(channel_setdir, ad1816chan_setdir),
|
||||
KOBJMETHOD(channel_setformat, ad1816chan_setformat),
|
||||
KOBJMETHOD(channel_setspeed, ad1816chan_setspeed),
|
||||
KOBJMETHOD(channel_setblocksize, ad1816chan_setblocksize),
|
||||
KOBJMETHOD(channel_trigger, ad1816chan_trigger),
|
||||
KOBJMETHOD(channel_getptr, ad1816chan_getptr),
|
||||
KOBJMETHOD(channel_getcaps, ad1816chan_getcaps),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
CHANNEL_DECLARE(ad1816chan);
|
||||
|
||||
|
@ -29,6 +29,10 @@
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
|
||||
#include <dev/sound/isa/sb.h>
|
||||
@ -50,28 +54,28 @@ SND_DECLARE_FILE("$FreeBSD$");
|
||||
#define ESS18XX_NEWSPEED
|
||||
|
||||
static u_int32_t ess_pfmt[] = {
|
||||
AFMT_U8,
|
||||
AFMT_STEREO | AFMT_U8,
|
||||
AFMT_S8,
|
||||
AFMT_STEREO | AFMT_S8,
|
||||
AFMT_S16_LE,
|
||||
AFMT_STEREO | AFMT_S16_LE,
|
||||
AFMT_U16_LE,
|
||||
AFMT_STEREO | AFMT_U16_LE,
|
||||
SND_FORMAT(AFMT_U8, 1, 0),
|
||||
SND_FORMAT(AFMT_U8, 2, 0),
|
||||
SND_FORMAT(AFMT_S8, 1, 0),
|
||||
SND_FORMAT(AFMT_S8, 2, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 1, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 2, 0),
|
||||
SND_FORMAT(AFMT_U16_LE, 1, 0),
|
||||
SND_FORMAT(AFMT_U16_LE, 2, 0),
|
||||
0
|
||||
};
|
||||
|
||||
static struct pcmchan_caps ess_playcaps = {6000, 48000, ess_pfmt, 0};
|
||||
|
||||
static u_int32_t ess_rfmt[] = {
|
||||
AFMT_U8,
|
||||
AFMT_STEREO | AFMT_U8,
|
||||
AFMT_S8,
|
||||
AFMT_STEREO | AFMT_S8,
|
||||
AFMT_S16_LE,
|
||||
AFMT_STEREO | AFMT_S16_LE,
|
||||
AFMT_U16_LE,
|
||||
AFMT_STEREO | AFMT_U16_LE,
|
||||
SND_FORMAT(AFMT_U8, 1, 0),
|
||||
SND_FORMAT(AFMT_U8, 2, 0),
|
||||
SND_FORMAT(AFMT_S8, 1, 0),
|
||||
SND_FORMAT(AFMT_S8, 2, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 1, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 2, 0),
|
||||
SND_FORMAT(AFMT_U16_LE, 1, 0),
|
||||
SND_FORMAT(AFMT_U16_LE, 2, 0),
|
||||
0
|
||||
};
|
||||
|
||||
@ -458,7 +462,7 @@ ess_setupch(struct ess_info *sc, int ch, int dir, int spd, u_int32_t fmt, int le
|
||||
{
|
||||
int play = (dir == PCMDIR_PLAY)? 1 : 0;
|
||||
int b16 = (fmt & AFMT_16BIT)? 1 : 0;
|
||||
int stereo = (fmt & AFMT_STEREO)? 1 : 0;
|
||||
int stereo = (AFMT_CHANNEL(fmt) > 1)? 1 : 0;
|
||||
int unsign = (fmt == AFMT_U8 || fmt == AFMT_U16_LE)? 1 : 0;
|
||||
u_int8_t spdval, fmtval;
|
||||
|
||||
@ -583,7 +587,7 @@ esschan_setformat(kobj_t obj, void *data, u_int32_t format)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
esschan_setspeed(kobj_t obj, void *data, u_int32_t speed)
|
||||
{
|
||||
struct ess_chinfo *ch = data;
|
||||
@ -597,7 +601,7 @@ esschan_setspeed(kobj_t obj, void *data, u_int32_t speed)
|
||||
return ch->spd;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
esschan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
|
||||
{
|
||||
struct ess_chinfo *ch = data;
|
||||
@ -630,7 +634,7 @@ esschan_trigger(kobj_t obj, void *data, int go)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
esschan_getptr(kobj_t obj, void *data)
|
||||
{
|
||||
struct ess_chinfo *ch = data;
|
||||
@ -654,7 +658,7 @@ static kobj_method_t esschan_methods[] = {
|
||||
KOBJMETHOD(channel_trigger, esschan_trigger),
|
||||
KOBJMETHOD(channel_getptr, esschan_getptr),
|
||||
KOBJMETHOD(channel_getcaps, esschan_getcaps),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
CHANNEL_DECLARE(esschan);
|
||||
|
||||
@ -741,7 +745,7 @@ essmix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
|
||||
return left | (right << 8);
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
essmix_setrecsrc(struct snd_mixer *m, u_int32_t src)
|
||||
{
|
||||
struct ess_info *sc = mix_getdevinfo(m);
|
||||
@ -776,7 +780,7 @@ static kobj_method_t essmixer_methods[] = {
|
||||
KOBJMETHOD(mixer_init, essmix_init),
|
||||
KOBJMETHOD(mixer_set, essmix_set),
|
||||
KOBJMETHOD(mixer_setrecsrc, essmix_setrecsrc),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
MIXER_DECLARE(essmixer);
|
||||
|
||||
|
@ -34,7 +34,11 @@
|
||||
#include <machine/resource.h>
|
||||
#include <machine/bus.h>
|
||||
#include <sys/rman.h>
|
||||
#include <sys/soundcard.h>
|
||||
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
#include <dev/sound/chip.h>
|
||||
#include "bus_if.h"
|
||||
@ -301,11 +305,9 @@ static int
|
||||
gusc_attach(device_t dev)
|
||||
{
|
||||
sc_p scp;
|
||||
int unit;
|
||||
void *ih;
|
||||
|
||||
scp = device_get_softc(dev);
|
||||
unit = device_get_unit(dev);
|
||||
|
||||
bzero(scp, sizeof(*scp));
|
||||
|
||||
@ -580,16 +582,14 @@ alloc_resource(sc_p scp)
|
||||
static int
|
||||
release_resource(sc_p scp)
|
||||
{
|
||||
int i, lid, flags;
|
||||
int i, lid;
|
||||
device_t dev;
|
||||
|
||||
flags = 0;
|
||||
if (isa_get_vendorid(scp->dev))
|
||||
lid = isa_get_logicalid(scp->dev);
|
||||
else {
|
||||
else
|
||||
lid = LOGICALID_NOPNP;
|
||||
flags = device_get_flags(scp->dev);
|
||||
}
|
||||
|
||||
switch(lid) {
|
||||
case LOGICALID_PCM:
|
||||
case LOGICALID_NOPNP: /* XXX Non-PnP */
|
||||
|
@ -27,6 +27,10 @@
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
|
||||
SND_DECLARE_FILE("$FreeBSD$");
|
||||
@ -128,34 +132,34 @@ static int pnpmss_attach(device_t dev);
|
||||
static driver_intr_t opti931_intr;
|
||||
|
||||
static u_int32_t mss_fmt[] = {
|
||||
AFMT_U8,
|
||||
AFMT_STEREO | AFMT_U8,
|
||||
AFMT_S16_LE,
|
||||
AFMT_STEREO | AFMT_S16_LE,
|
||||
AFMT_MU_LAW,
|
||||
AFMT_STEREO | AFMT_MU_LAW,
|
||||
AFMT_A_LAW,
|
||||
AFMT_STEREO | AFMT_A_LAW,
|
||||
SND_FORMAT(AFMT_U8, 1, 0),
|
||||
SND_FORMAT(AFMT_U8, 2, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 1, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 2, 0),
|
||||
SND_FORMAT(AFMT_MU_LAW, 1, 0),
|
||||
SND_FORMAT(AFMT_MU_LAW, 2, 0),
|
||||
SND_FORMAT(AFMT_A_LAW, 1, 0),
|
||||
SND_FORMAT(AFMT_A_LAW, 2, 0),
|
||||
0
|
||||
};
|
||||
static struct pcmchan_caps mss_caps = {4000, 48000, mss_fmt, 0};
|
||||
|
||||
static u_int32_t guspnp_fmt[] = {
|
||||
AFMT_U8,
|
||||
AFMT_STEREO | AFMT_U8,
|
||||
AFMT_S16_LE,
|
||||
AFMT_STEREO | AFMT_S16_LE,
|
||||
AFMT_A_LAW,
|
||||
AFMT_STEREO | AFMT_A_LAW,
|
||||
SND_FORMAT(AFMT_U8, 1, 0),
|
||||
SND_FORMAT(AFMT_U8, 2, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 1, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 2, 0),
|
||||
SND_FORMAT(AFMT_A_LAW, 1, 0),
|
||||
SND_FORMAT(AFMT_A_LAW, 2, 0),
|
||||
0
|
||||
};
|
||||
static struct pcmchan_caps guspnp_caps = {4000, 48000, guspnp_fmt, 0};
|
||||
|
||||
static u_int32_t opti931_fmt[] = {
|
||||
AFMT_U8,
|
||||
AFMT_STEREO | AFMT_U8,
|
||||
AFMT_S16_LE,
|
||||
AFMT_STEREO | AFMT_S16_LE,
|
||||
SND_FORMAT(AFMT_U8, 1, 0),
|
||||
SND_FORMAT(AFMT_U8, 2, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 1, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 2, 0),
|
||||
0
|
||||
};
|
||||
static struct pcmchan_caps opti931_caps = {4000, 48000, opti931_fmt, 0};
|
||||
@ -520,7 +524,7 @@ mssmix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
|
||||
return left | (right << 8);
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
mssmix_setrecsrc(struct snd_mixer *m, u_int32_t src)
|
||||
{
|
||||
struct mss_info *mss = mix_getdevinfo(m);
|
||||
@ -535,7 +539,7 @@ static kobj_method_t mssmix_mixer_methods[] = {
|
||||
KOBJMETHOD(mixer_init, mssmix_init),
|
||||
KOBJMETHOD(mixer_set, mssmix_set),
|
||||
KOBJMETHOD(mixer_setrecsrc, mssmix_setrecsrc),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
MIXER_DECLARE(mssmix_mixer);
|
||||
|
||||
@ -604,7 +608,7 @@ ymmix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
|
||||
return left | (right << 8);
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
ymmix_setrecsrc(struct snd_mixer *m, u_int32_t src)
|
||||
{
|
||||
struct mss_info *mss = mix_getdevinfo(m);
|
||||
@ -618,7 +622,7 @@ static kobj_method_t ymmix_mixer_methods[] = {
|
||||
KOBJMETHOD(mixer_init, ymmix_init),
|
||||
KOBJMETHOD(mixer_set, ymmix_set),
|
||||
KOBJMETHOD(mixer_setrecsrc, ymmix_setrecsrc),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
MIXER_DECLARE(ymmix_mixer);
|
||||
|
||||
@ -997,7 +1001,7 @@ static int
|
||||
mss_format(struct mss_chinfo *ch, u_int32_t format)
|
||||
{
|
||||
struct mss_info *mss = ch->parent;
|
||||
int i, arg = format & ~AFMT_STEREO;
|
||||
int i, arg = AFMT_ENCODING(format);
|
||||
|
||||
/*
|
||||
* The data format uses 3 bits (just 2 on the 1848). For each
|
||||
@ -1014,7 +1018,7 @@ mss_format(struct mss_chinfo *ch, u_int32_t format)
|
||||
ch->fmt = format;
|
||||
for (i = 0; i < 8; i++) if (arg == fmts[i]) break;
|
||||
arg = i << 1;
|
||||
if (format & AFMT_STEREO) arg |= 1;
|
||||
if (AFMT_CHANNEL(format) > 1) arg |= 1;
|
||||
arg <<= 4;
|
||||
ad_enter_MCE(mss);
|
||||
ad_write(mss, 8, (ad_read(mss, 8) & 0x0f) | arg);
|
||||
@ -1035,7 +1039,7 @@ mss_trigger(struct mss_chinfo *ch, int go)
|
||||
int retry, wr, cnt, ss;
|
||||
|
||||
ss = 1;
|
||||
ss <<= (ch->fmt & AFMT_STEREO)? 1 : 0;
|
||||
ss <<= (AFMT_CHANNEL(ch->fmt) > 1)? 1 : 0;
|
||||
ss <<= (ch->fmt & AFMT_16BIT)? 1 : 0;
|
||||
|
||||
wr = (ch->dir == PCMDIR_PLAY)? 1 : 0;
|
||||
@ -1170,12 +1174,12 @@ msschan_setformat(kobj_t obj, void *data, u_int32_t format)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
msschan_setspeed(kobj_t obj, void *data, u_int32_t speed)
|
||||
{
|
||||
struct mss_chinfo *ch = data;
|
||||
struct mss_info *mss = ch->parent;
|
||||
int r;
|
||||
u_int32_t r;
|
||||
|
||||
mss_lock(mss);
|
||||
r = mss_speed(ch, speed);
|
||||
@ -1184,7 +1188,7 @@ msschan_setspeed(kobj_t obj, void *data, u_int32_t speed)
|
||||
return r;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
msschan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
|
||||
{
|
||||
struct mss_chinfo *ch = data;
|
||||
@ -1211,7 +1215,7 @@ msschan_trigger(kobj_t obj, void *data, int go)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
msschan_getptr(kobj_t obj, void *data)
|
||||
{
|
||||
struct mss_chinfo *ch = data;
|
||||
@ -1247,7 +1251,7 @@ static kobj_method_t msschan_methods[] = {
|
||||
KOBJMETHOD(channel_trigger, msschan_trigger),
|
||||
KOBJMETHOD(channel_getptr, msschan_getptr),
|
||||
KOBJMETHOD(channel_getcaps, msschan_getcaps),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
CHANNEL_DECLARE(msschan);
|
||||
|
||||
|
@ -29,6 +29,10 @@
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
|
||||
#include <dev/sound/isa/sb.h>
|
||||
@ -44,24 +48,24 @@ SND_DECLARE_FILE("$FreeBSD$");
|
||||
#define PLAIN_SB16(x) ((((x)->bd_flags) & (BD_F_SB16|BD_F_SB16X)) == BD_F_SB16)
|
||||
|
||||
static u_int32_t sb16_fmt8[] = {
|
||||
AFMT_U8,
|
||||
AFMT_STEREO | AFMT_U8,
|
||||
SND_FORMAT(AFMT_U8, 1, 0),
|
||||
SND_FORMAT(AFMT_U8, 2, 0),
|
||||
0
|
||||
};
|
||||
static struct pcmchan_caps sb16_caps8 = {5000, 45000, sb16_fmt8, 0};
|
||||
|
||||
static u_int32_t sb16_fmt16[] = {
|
||||
AFMT_S16_LE,
|
||||
AFMT_STEREO | AFMT_S16_LE,
|
||||
SND_FORMAT(AFMT_S16_LE, 1, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 2, 0),
|
||||
0
|
||||
};
|
||||
static struct pcmchan_caps sb16_caps16 = {5000, 45000, sb16_fmt16, 0};
|
||||
|
||||
static u_int32_t sb16x_fmt[] = {
|
||||
AFMT_U8,
|
||||
AFMT_STEREO | AFMT_U8,
|
||||
AFMT_S16_LE,
|
||||
AFMT_STEREO | AFMT_S16_LE,
|
||||
SND_FORMAT(AFMT_U8, 1, 0),
|
||||
SND_FORMAT(AFMT_U8, 2, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 1, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 2, 0),
|
||||
0
|
||||
};
|
||||
static struct pcmchan_caps sb16x_caps = {5000, 49000, sb16x_fmt, 0};
|
||||
@ -366,7 +370,7 @@ sb16mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
|
||||
return left | (right << 8);
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
sb16mix_setrecsrc(struct snd_mixer *m, u_int32_t src)
|
||||
{
|
||||
struct sb_info *sb = mix_getdevinfo(m);
|
||||
@ -420,7 +424,7 @@ static kobj_method_t sb16mix_mixer_methods[] = {
|
||||
KOBJMETHOD(mixer_init, sb16mix_init),
|
||||
KOBJMETHOD(mixer_set, sb16mix_set),
|
||||
KOBJMETHOD(mixer_setrecsrc, sb16mix_setrecsrc),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
MIXER_DECLARE(sb16mix_mixer);
|
||||
|
||||
@ -633,7 +637,7 @@ sb_setup(struct sb_info *sb)
|
||||
v |= (ch->fmt & AFMT_16BIT)? DSP_DMA16 : DSP_DMA8;
|
||||
sb_cmd(sb, v);
|
||||
|
||||
v = (ch->fmt & AFMT_STEREO)? DSP_F16_STEREO : 0;
|
||||
v = (AFMT_CHANNEL(ch->fmt) > 1)? DSP_F16_STEREO : 0;
|
||||
v |= (ch->fmt & AFMT_SIGNED)? DSP_F16_SIGNED : 0;
|
||||
sb_cmd2(sb, v, l);
|
||||
sndbuf_dma(ch->buffer, PCMTRIG_START);
|
||||
@ -658,7 +662,7 @@ sb_setup(struct sb_info *sb)
|
||||
v |= (ch->fmt & AFMT_16BIT)? DSP_DMA16 : DSP_DMA8;
|
||||
sb_cmd(sb, v);
|
||||
|
||||
v = (ch->fmt & AFMT_STEREO)? DSP_F16_STEREO : 0;
|
||||
v = (AFMT_CHANNEL(ch->fmt) > 1)? DSP_F16_STEREO : 0;
|
||||
v |= (ch->fmt & AFMT_SIGNED)? DSP_F16_SIGNED : 0;
|
||||
sb_cmd2(sb, v, l);
|
||||
sndbuf_dma(ch->buffer, PCMTRIG_START);
|
||||
@ -700,7 +704,7 @@ sb16chan_setformat(kobj_t obj, void *data, u_int32_t format)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
sb16chan_setspeed(kobj_t obj, void *data, u_int32_t speed)
|
||||
{
|
||||
struct sb_chinfo *ch = data;
|
||||
@ -709,7 +713,7 @@ sb16chan_setspeed(kobj_t obj, void *data, u_int32_t speed)
|
||||
return speed;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
sb16chan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
|
||||
{
|
||||
struct sb_chinfo *ch = data;
|
||||
@ -737,7 +741,7 @@ sb16chan_trigger(kobj_t obj, void *data, int go)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
sb16chan_getptr(kobj_t obj, void *data)
|
||||
{
|
||||
struct sb_chinfo *ch = data;
|
||||
@ -777,7 +781,7 @@ static kobj_method_t sb16chan_methods[] = {
|
||||
KOBJMETHOD(channel_trigger, sb16chan_trigger),
|
||||
KOBJMETHOD(channel_getptr, sb16chan_getptr),
|
||||
KOBJMETHOD(channel_getcaps, sb16chan_getcaps),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
CHANNEL_DECLARE(sb16chan);
|
||||
|
||||
|
@ -29,6 +29,10 @@
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
|
||||
#include <dev/sound/isa/sb.h>
|
||||
@ -43,7 +47,7 @@ SND_DECLARE_FILE("$FreeBSD$");
|
||||
#define SB_DEFAULT_BUFSZ 4096
|
||||
|
||||
static u_int32_t sb_fmt[] = {
|
||||
AFMT_U8,
|
||||
SND_FORMAT(AFMT_U8, 1, 0),
|
||||
0
|
||||
};
|
||||
static struct pcmchan_caps sb200_playcaps = {4000, 23000, sb_fmt, 0};
|
||||
@ -52,8 +56,8 @@ static struct pcmchan_caps sb201_playcaps = {4000, 44100, sb_fmt, 0};
|
||||
static struct pcmchan_caps sb201_reccaps = {4000, 15000, sb_fmt, 0};
|
||||
|
||||
static u_int32_t sbpro_fmt[] = {
|
||||
AFMT_U8,
|
||||
AFMT_STEREO | AFMT_U8,
|
||||
SND_FORMAT(AFMT_U8, 1, 0),
|
||||
SND_FORMAT(AFMT_U8, 2, 0),
|
||||
0
|
||||
};
|
||||
static struct pcmchan_caps sbpro_playcaps = {4000, 44100, sbpro_fmt, 0};
|
||||
@ -372,7 +376,7 @@ sbpromix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
|
||||
return left | (right << 8);
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
sbpromix_setrecsrc(struct snd_mixer *m, u_int32_t src)
|
||||
{
|
||||
struct sb_info *sb = mix_getdevinfo(m);
|
||||
@ -395,7 +399,7 @@ static kobj_method_t sbpromix_mixer_methods[] = {
|
||||
KOBJMETHOD(mixer_init, sbpromix_init),
|
||||
KOBJMETHOD(mixer_set, sbpromix_set),
|
||||
KOBJMETHOD(mixer_setrecsrc, sbpromix_setrecsrc),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
MIXER_DECLARE(sbpromix_mixer);
|
||||
|
||||
@ -453,7 +457,7 @@ sbmix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
|
||||
return left | (left << 8);
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
sbmix_setrecsrc(struct snd_mixer *m, u_int32_t src)
|
||||
{
|
||||
return 0;
|
||||
@ -463,7 +467,7 @@ static kobj_method_t sbmix_mixer_methods[] = {
|
||||
KOBJMETHOD(mixer_init, sbmix_init),
|
||||
KOBJMETHOD(mixer_set, sbmix_set),
|
||||
KOBJMETHOD(mixer_setrecsrc, sbmix_setrecsrc),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
MIXER_DECLARE(sbmix_mixer);
|
||||
|
||||
@ -496,7 +500,7 @@ sb_speed(struct sb_chinfo *ch)
|
||||
{
|
||||
struct sb_info *sb = ch->parent;
|
||||
int play = (ch->dir == PCMDIR_PLAY)? 1 : 0;
|
||||
int stereo = (ch->fmt & AFMT_STEREO)? 1 : 0;
|
||||
int stereo = (AFMT_CHANNEL(ch->fmt) > 1)? 1 : 0;
|
||||
int speed, tmp, thresh, max;
|
||||
u_char tconst;
|
||||
|
||||
@ -537,7 +541,7 @@ sb_start(struct sb_chinfo *ch)
|
||||
{
|
||||
struct sb_info *sb = ch->parent;
|
||||
int play = (ch->dir == PCMDIR_PLAY)? 1 : 0;
|
||||
int stereo = (ch->fmt & AFMT_STEREO)? 1 : 0;
|
||||
int stereo = (AFMT_CHANNEL(ch->fmt) > 1)? 1 : 0;
|
||||
int l = ch->blksz;
|
||||
u_char i;
|
||||
|
||||
@ -614,7 +618,7 @@ sbchan_setformat(kobj_t obj, void *data, u_int32_t format)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
sbchan_setspeed(kobj_t obj, void *data, u_int32_t speed)
|
||||
{
|
||||
struct sb_chinfo *ch = data;
|
||||
@ -623,7 +627,7 @@ sbchan_setspeed(kobj_t obj, void *data, u_int32_t speed)
|
||||
return sb_speed(ch);
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
sbchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
|
||||
{
|
||||
struct sb_chinfo *ch = data;
|
||||
@ -648,7 +652,7 @@ sbchan_trigger(kobj_t obj, void *data, int go)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
sbchan_getptr(kobj_t obj, void *data)
|
||||
{
|
||||
struct sb_chinfo *ch = data;
|
||||
@ -677,7 +681,7 @@ static kobj_method_t sbchan_methods[] = {
|
||||
KOBJMETHOD(channel_trigger, sbchan_trigger),
|
||||
KOBJMETHOD(channel_getptr, sbchan_getptr),
|
||||
KOBJMETHOD(channel_getcaps, sbchan_getcaps),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
CHANNEL_DECLARE(sbchan);
|
||||
|
||||
|
@ -24,6 +24,10 @@
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/chip.h>
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
#include <dev/sound/isa/sb.h>
|
||||
|
@ -24,6 +24,10 @@
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
|
||||
#include <isa/isavar.h>
|
||||
|
@ -43,8 +43,14 @@
|
||||
#include <machine/bus.h>
|
||||
#include <sys/rman.h>
|
||||
#include <dev/ofw/ofw_bus.h>
|
||||
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
#include <dev/sound/macio/aoa.h>
|
||||
|
||||
#include "mixer_if.h"
|
||||
|
||||
struct aoa_dma {
|
||||
@ -138,7 +144,7 @@ aoa_dma_delete(struct aoa_dma *dma)
|
||||
free(dma, M_DEVBUF);
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
aoa_chan_setblocksize(kobj_t obj, void *data, u_int32_t blocksz)
|
||||
{
|
||||
struct aoa_dma *dma = data;
|
||||
@ -186,13 +192,13 @@ aoa_chan_setformat(kobj_t obj, void *data, u_int32_t format)
|
||||
{
|
||||
DPRINTF(("aoa_chan_setformat: format = %u\n", format));
|
||||
|
||||
if (format != (AFMT_STEREO | AFMT_S16_BE))
|
||||
if (format != SND_FORMAT(AFMT_S16_BE, 2, 0))
|
||||
return (EINVAL);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
aoa_chan_setspeed(kobj_t obj, void *data, u_int32_t speed)
|
||||
{
|
||||
DPRINTF(("aoa_chan_setspeed: speed = %u\n", speed));
|
||||
@ -200,7 +206,7 @@ aoa_chan_setspeed(kobj_t obj, void *data, u_int32_t speed)
|
||||
return (44100);
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
aoa_chan_getptr(kobj_t obj, void *data)
|
||||
{
|
||||
struct aoa_dma *dma = data;
|
||||
@ -332,7 +338,7 @@ aoa_interrupt(void *xsc)
|
||||
}
|
||||
|
||||
static u_int32_t sc_fmt[] = {
|
||||
AFMT_S16_BE | AFMT_STEREO,
|
||||
SND_FORMAT(AFMT_S16_BE, 2, 0),
|
||||
0
|
||||
};
|
||||
static struct pcmchan_caps aoa_caps = {44100, 44100, sc_fmt, 0};
|
||||
@ -352,7 +358,7 @@ static kobj_method_t aoa_chan_methods[] = {
|
||||
KOBJMETHOD(channel_trigger, aoa_chan_trigger),
|
||||
KOBJMETHOD(channel_getptr, aoa_chan_getptr),
|
||||
KOBJMETHOD(channel_getcaps, aoa_chan_getcaps),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
CHANNEL_DECLARE(aoa_chan);
|
||||
|
||||
|
@ -40,7 +40,13 @@
|
||||
#include <sys/rman.h>
|
||||
|
||||
#include <dev/ofw/ofw_bus.h>
|
||||
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
|
||||
#include <dev/sound/macio/aoa.h>
|
||||
#include <dev/sound/macio/davbusreg.h>
|
||||
|
||||
@ -115,7 +121,7 @@ static void burgundy_set_outputs(struct davbus_softc *d, u_int mask);
|
||||
static u_int burgundy_read_status(struct davbus_softc *d, u_int status);
|
||||
static int burgundy_set(struct snd_mixer *m, unsigned dev, unsigned left,
|
||||
unsigned right);
|
||||
static int burgundy_setrecsrc(struct snd_mixer *m, u_int32_t src);
|
||||
static u_int32_t burgundy_setrecsrc(struct snd_mixer *m, u_int32_t src);
|
||||
|
||||
static kobj_method_t burgundy_mixer_methods[] = {
|
||||
KOBJMETHOD(mixer_init, burgundy_init),
|
||||
@ -123,7 +129,7 @@ static kobj_method_t burgundy_mixer_methods[] = {
|
||||
KOBJMETHOD(mixer_reinit, burgundy_reinit),
|
||||
KOBJMETHOD(mixer_set, burgundy_set),
|
||||
KOBJMETHOD(mixer_setrecsrc, burgundy_setrecsrc),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
|
||||
MIXER_DECLARE(burgundy_mixer);
|
||||
@ -293,7 +299,7 @@ burgundy_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
burgundy_setrecsrc(struct snd_mixer *m, u_int32_t src)
|
||||
{
|
||||
return (0);
|
||||
@ -311,7 +317,7 @@ static void screamer_set_outputs(struct davbus_softc *d, u_int mask);
|
||||
static u_int screamer_read_status(struct davbus_softc *d, u_int status);
|
||||
static int screamer_set(struct snd_mixer *m, unsigned dev, unsigned left,
|
||||
unsigned right);
|
||||
static int screamer_setrecsrc(struct snd_mixer *m, u_int32_t src);
|
||||
static u_int32_t screamer_setrecsrc(struct snd_mixer *m, u_int32_t src);
|
||||
|
||||
static kobj_method_t screamer_mixer_methods[] = {
|
||||
KOBJMETHOD(mixer_init, screamer_init),
|
||||
@ -319,7 +325,7 @@ static kobj_method_t screamer_mixer_methods[] = {
|
||||
KOBJMETHOD(mixer_reinit, screamer_reinit),
|
||||
KOBJMETHOD(mixer_set, screamer_set),
|
||||
KOBJMETHOD(mixer_setrecsrc, screamer_setrecsrc),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
|
||||
MIXER_DECLARE(screamer_mixer);
|
||||
@ -479,7 +485,7 @@ screamer_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
screamer_setrecsrc(struct snd_mixer *m, u_int32_t src)
|
||||
{
|
||||
return (0);
|
||||
|
@ -72,8 +72,14 @@
|
||||
#include <machine/pio.h>
|
||||
#include <sys/rman.h>
|
||||
#include <dev/ofw/ofw_bus.h>
|
||||
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
#include <dev/sound/macio/aoa.h>
|
||||
|
||||
#include <powerpc/powermac/macgpiovar.h>
|
||||
|
||||
struct i2s_softc {
|
||||
@ -530,7 +536,7 @@ i2s_setup(struct i2s_softc *sc, u_int rate, u_int wordsize, u_int sclk_fs)
|
||||
* to set sane defaults (44100).
|
||||
*/
|
||||
printf("i2s_setup: changing format not supported yet.\n");
|
||||
return (EOPNOTSUPP);
|
||||
return (ENOTSUP);
|
||||
|
||||
#ifdef notyet
|
||||
if (obio_fcr_isset(OBIO_FCR1, I2S0CLKEN)) {
|
||||
|
@ -75,7 +75,13 @@
|
||||
#include <dev/iicbus/iicbus.h>
|
||||
#include <dev/iicbus/iiconf.h>
|
||||
#include <dev/ofw/ofw_bus.h>
|
||||
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
|
||||
#include "mixer_if.h"
|
||||
|
||||
extern kobj_class_t i2s_mixer_class;
|
||||
@ -94,7 +100,7 @@ static void snapper_uninit(struct snd_mixer *m);
|
||||
static int snapper_reinit(struct snd_mixer *m);
|
||||
static int snapper_set(struct snd_mixer *m, unsigned dev, unsigned left,
|
||||
unsigned right);
|
||||
static int snapper_setrecsrc(struct snd_mixer *m, u_int32_t src);
|
||||
static u_int32_t snapper_setrecsrc(struct snd_mixer *m, u_int32_t src);
|
||||
|
||||
static device_method_t snapper_methods[] = {
|
||||
/* Device interface. */
|
||||
@ -121,7 +127,7 @@ static kobj_method_t snapper_mixer_methods[] = {
|
||||
KOBJMETHOD(mixer_reinit, snapper_reinit),
|
||||
KOBJMETHOD(mixer_set, snapper_set),
|
||||
KOBJMETHOD(mixer_setrecsrc, snapper_setrecsrc),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
|
||||
MIXER_DECLARE(snapper_mixer);
|
||||
@ -478,7 +484,7 @@ snapper_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
snapper_setrecsrc(struct snd_mixer *m, u_int32_t src)
|
||||
{
|
||||
return (0);
|
||||
|
@ -75,7 +75,13 @@
|
||||
#include <dev/iicbus/iicbus.h>
|
||||
#include <dev/iicbus/iiconf.h>
|
||||
#include <dev/ofw/ofw_bus.h>
|
||||
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
|
||||
#include "mixer_if.h"
|
||||
|
||||
extern kobj_class_t i2s_mixer_class;
|
||||
@ -94,7 +100,7 @@ static void tumbler_uninit(struct snd_mixer *m);
|
||||
static int tumbler_reinit(struct snd_mixer *m);
|
||||
static int tumbler_set(struct snd_mixer *m, unsigned dev, unsigned left,
|
||||
unsigned right);
|
||||
static int tumbler_setrecsrc(struct snd_mixer *m, u_int32_t src);
|
||||
static u_int32_t tumbler_setrecsrc(struct snd_mixer *m, u_int32_t src);
|
||||
|
||||
static device_method_t tumbler_methods[] = {
|
||||
/* Device interface. */
|
||||
@ -121,7 +127,7 @@ static kobj_method_t tumbler_mixer_methods[] = {
|
||||
KOBJMETHOD(mixer_reinit, tumbler_reinit),
|
||||
KOBJMETHOD(mixer_set, tumbler_set),
|
||||
KOBJMETHOD(mixer_setrecsrc, tumbler_setrecsrc),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
|
||||
MIXER_DECLARE(tumbler_mixer);
|
||||
@ -424,7 +430,7 @@ tumbler_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
tumbler_setrecsrc(struct snd_mixer *m, u_int32_t src)
|
||||
{
|
||||
return (0);
|
||||
|
@ -58,8 +58,23 @@ __FBSDID("$FreeBSD$");
|
||||
#include <sys/poll.h>
|
||||
#include <sys/sbuf.h>
|
||||
#include <sys/kobj.h>
|
||||
#ifdef SND_DEBUG
|
||||
#undef KOBJMETHOD
|
||||
#define KOBJMETHOD(NAME, FUNC) \
|
||||
{ \
|
||||
&NAME##_desc, \
|
||||
(kobjop_t) ((FUNC != (NAME##_t *)NULL) ? FUNC : NULL) \
|
||||
}
|
||||
#endif
|
||||
#ifndef KOBJMETHOD_END
|
||||
#define KOBJMETHOD_END { NULL, NULL }
|
||||
#endif
|
||||
#include <sys/module.h>
|
||||
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/midi/midi.h>
|
||||
#include "mpu_if.h"
|
||||
|
||||
@ -145,7 +160,7 @@ static kobj_method_t midisynth_methods[] = {
|
||||
KOBJMETHOD(synth_alloc, midisynth_alloc),
|
||||
KOBJMETHOD(synth_controller, midisynth_controller),
|
||||
KOBJMETHOD(synth_bender, midisynth_bender),
|
||||
{0, 0}
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
|
||||
DEFINE_CLASS(midisynth, midisynth_methods, 0);
|
||||
@ -1367,6 +1382,7 @@ midi_destroy(struct snd_midi *m, int midiuninit)
|
||||
|
||||
MIDI_DEBUG(3, printf("midi_destroy\n"));
|
||||
m->dev->si_drv1 = NULL;
|
||||
mtx_unlock(&m->lock); /* XXX */
|
||||
destroy_dev(m->dev);
|
||||
TAILQ_REMOVE(&midi_devs, m, link);
|
||||
if (midiuninit)
|
||||
@ -1418,6 +1434,8 @@ midi_unload()
|
||||
goto exit1;
|
||||
}
|
||||
|
||||
mtx_unlock(&midistat_lock); /* XXX */
|
||||
|
||||
destroy_dev(midistat_dev);
|
||||
/*
|
||||
* Made it here then unload is complete
|
||||
|
@ -37,9 +37,24 @@ __FBSDID("$FreeBSD$");
|
||||
#include <sys/proc.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/kobj.h>
|
||||
#ifdef SND_DEBUG
|
||||
#undef KOBJMETHOD
|
||||
#define KOBJMETHOD(NAME, FUNC) \
|
||||
{ \
|
||||
&NAME##_desc, \
|
||||
(kobjop_t) ((FUNC != (NAME##_t *)NULL) ? FUNC : NULL) \
|
||||
}
|
||||
#endif
|
||||
#ifndef KOBJMETHOD_END
|
||||
#define KOBJMETHOD_END { NULL, NULL }
|
||||
#endif
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/bus.h> /* to get driver_intr_t */
|
||||
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/midi/mpu401.h>
|
||||
#include <dev/sound/midi/midi.h>
|
||||
|
||||
@ -75,14 +90,14 @@ struct mpu401 {
|
||||
static void mpu401_timeout(void *m);
|
||||
static mpu401_intr_t mpu401_intr;
|
||||
|
||||
static int mpu401_minit(kobj_t obj, struct mpu401 *m);
|
||||
static int mpu401_muninit(kobj_t obj, struct mpu401 *m);
|
||||
static int mpu401_minqsize(kobj_t obj, struct mpu401 *m);
|
||||
static int mpu401_moutqsize(kobj_t obj, struct mpu401 *m);
|
||||
static void mpu401_mcallback(kobj_t obj, struct mpu401 *m, int flags);
|
||||
static void mpu401_mcallbackp(kobj_t obj, struct mpu401 *m, int flags);
|
||||
static const char *mpu401_mdescr(kobj_t obj, struct mpu401 *m, int verbosity);
|
||||
static const char *mpu401_mprovider(kobj_t obj, struct mpu401 *m);
|
||||
static int mpu401_minit(struct snd_midi *, void *);
|
||||
static int mpu401_muninit(struct snd_midi *, void *);
|
||||
static int mpu401_minqsize(struct snd_midi *, void *);
|
||||
static int mpu401_moutqsize(struct snd_midi *, void *);
|
||||
static void mpu401_mcallback(struct snd_midi *, void *, int);
|
||||
static void mpu401_mcallbackp(struct snd_midi *, void *, int);
|
||||
static const char *mpu401_mdescr(struct snd_midi *, void *, int);
|
||||
static const char *mpu401_mprovider(struct snd_midi *, void *);
|
||||
|
||||
static kobj_method_t mpu401_methods[] = {
|
||||
KOBJMETHOD(mpu_init, mpu401_minit),
|
||||
@ -93,7 +108,7 @@ static kobj_method_t mpu401_methods[] = {
|
||||
KOBJMETHOD(mpu_callbackp, mpu401_mcallbackp),
|
||||
KOBJMETHOD(mpu_descr, mpu401_mdescr),
|
||||
KOBJMETHOD(mpu_provider, mpu401_mprovider),
|
||||
{0, 0}
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
|
||||
DEFINE_CLASS(mpu401, mpu401_methods, 0);
|
||||
@ -208,8 +223,9 @@ mpu401_uninit(struct mpu401 *m)
|
||||
}
|
||||
|
||||
static int
|
||||
mpu401_minit(kobj_t obj, struct mpu401 *m)
|
||||
mpu401_minit(struct snd_midi *sm, void *arg)
|
||||
{
|
||||
struct mpu401 *m = arg;
|
||||
int i;
|
||||
|
||||
CMD(m, MPU_RESET);
|
||||
@ -232,27 +248,29 @@ mpu401_minit(kobj_t obj, struct mpu401 *m)
|
||||
|
||||
|
||||
int
|
||||
mpu401_muninit(kobj_t obj, struct mpu401 *m)
|
||||
mpu401_muninit(struct snd_midi *sm, void *arg)
|
||||
{
|
||||
struct mpu401 *m = arg;
|
||||
|
||||
return MPUFOI_UNINIT(m, m->cookie);
|
||||
}
|
||||
|
||||
int
|
||||
mpu401_minqsize(kobj_t obj, struct mpu401 *m)
|
||||
mpu401_minqsize(struct snd_midi *sm, void *arg)
|
||||
{
|
||||
return 128;
|
||||
}
|
||||
|
||||
int
|
||||
mpu401_moutqsize(kobj_t obj, struct mpu401 *m)
|
||||
mpu401_moutqsize(struct snd_midi *sm, void *arg)
|
||||
{
|
||||
return 128;
|
||||
}
|
||||
|
||||
static void
|
||||
mpu401_mcallback(kobj_t obj, struct mpu401 *m, int flags)
|
||||
mpu401_mcallback(struct snd_midi *sm, void *arg, int flags)
|
||||
{
|
||||
struct mpu401 *m = arg;
|
||||
#if 0
|
||||
printf("mpu401_callback %s %s %s %s\n",
|
||||
flags & M_RX ? "M_RX" : "",
|
||||
@ -267,21 +285,21 @@ mpu401_mcallback(kobj_t obj, struct mpu401 *m, int flags)
|
||||
}
|
||||
|
||||
static void
|
||||
mpu401_mcallbackp(kobj_t obj, struct mpu401 *m, int flags)
|
||||
mpu401_mcallbackp(struct snd_midi *sm, void *arg, int flags)
|
||||
{
|
||||
/* printf("mpu401_callbackp\n"); */
|
||||
mpu401_mcallback(obj, m, flags);
|
||||
mpu401_mcallback(sm, arg, flags);
|
||||
}
|
||||
|
||||
static const char *
|
||||
mpu401_mdescr(kobj_t obj, struct mpu401 *m, int verbosity)
|
||||
mpu401_mdescr(struct snd_midi *sm, void *arg, int verbosity)
|
||||
{
|
||||
|
||||
return "descr mpu401";
|
||||
}
|
||||
|
||||
static const char *
|
||||
mpu401_mprovider(kobj_t obj, struct mpu401 *m)
|
||||
mpu401_mprovider(struct snd_midi *m, void *arg)
|
||||
{
|
||||
return "provider mpu401";
|
||||
}
|
||||
|
@ -66,6 +66,9 @@ __FBSDID("$FreeBSD$");
|
||||
#include <sys/unistd.h>
|
||||
#include <sys/selinfo.h>
|
||||
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/midi/midi.h>
|
||||
#include <dev/sound/midi/midiq.h>
|
||||
@ -258,13 +261,17 @@ midi_cmdtab cmdtab_seqccmn[] = {
|
||||
{-1, NULL},
|
||||
};
|
||||
|
||||
#ifndef KOBJMETHOD_END
|
||||
#define KOBJMETHOD_END { NULL, NULL }
|
||||
#endif
|
||||
|
||||
/*
|
||||
* static const char *mpu401_mprovider(kobj_t obj, struct mpu401 *m);
|
||||
*/
|
||||
|
||||
static kobj_method_t seq_methods[] = {
|
||||
/* KOBJMETHOD(mpu_provider,mpu401_mprovider), */
|
||||
{0, 0}
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
|
||||
DEFINE_CLASS(sequencer, seq_methods, 0);
|
||||
@ -459,7 +466,12 @@ seq_eventthread(void *arg)
|
||||
cv_broadcast(&scp->th_cv);
|
||||
mtx_unlock(&scp->seq_lock);
|
||||
SEQ_DEBUG(2, printf("seq_eventthread finished\n"));
|
||||
#if __FreeBSD_version >= 800002
|
||||
kproc_exit(0);
|
||||
#else
|
||||
mtx_lock(&Giant);
|
||||
kthread_exit(0);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
@ -574,7 +586,13 @@ seq_addunit(void)
|
||||
* TODO: Add to list of sequencers this module provides
|
||||
*/
|
||||
|
||||
ret = kproc_create(seq_eventthread, scp, NULL, RFHIGHPID, 0,
|
||||
ret =
|
||||
#if __FreeBSD_version >= 800002
|
||||
kproc_create
|
||||
#else
|
||||
kthread_create
|
||||
#endif
|
||||
(seq_eventthread, scp, NULL, RFHIGHPID, 0,
|
||||
"sequencer %02d", scp->unit);
|
||||
|
||||
if (ret)
|
||||
|
@ -57,7 +57,7 @@ extern int seq_debug;
|
||||
if (seq_debug >= y) { \
|
||||
(x); \
|
||||
} \
|
||||
} while(0)
|
||||
} while (0)
|
||||
|
||||
SYSCTL_DECL(_hw_midi);
|
||||
|
||||
|
@ -33,6 +33,10 @@
|
||||
* SB16 register descriptions.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
#include <dev/sound/isa/sb.h>
|
||||
#include <dev/sound/pci/als4000.h>
|
||||
@ -84,10 +88,10 @@ struct sc_info {
|
||||
/* Channel caps */
|
||||
|
||||
static u_int32_t als_format[] = {
|
||||
AFMT_U8,
|
||||
AFMT_STEREO | AFMT_U8,
|
||||
AFMT_S16_LE,
|
||||
AFMT_STEREO | AFMT_S16_LE,
|
||||
SND_FORMAT(AFMT_U8, 1, 0),
|
||||
SND_FORMAT(AFMT_U8, 2, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 1, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 2, 0),
|
||||
0
|
||||
};
|
||||
|
||||
@ -216,7 +220,7 @@ alschan_init(kobj_t obj, void *devinfo,
|
||||
ch->parent = sc;
|
||||
ch->channel = c;
|
||||
ch->bps = 1;
|
||||
ch->format = AFMT_U8;
|
||||
ch->format = SND_FORMAT(AFMT_U8, 1, 0);
|
||||
ch->speed = DSP_DEFAULT_SPEED;
|
||||
ch->buffer = b;
|
||||
snd_mtxunlock(sc->lock);
|
||||
@ -236,7 +240,7 @@ alschan_setformat(kobj_t obj, void *data, u_int32_t format)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
alschan_setspeed(kobj_t obj, void *data, u_int32_t speed)
|
||||
{
|
||||
struct sc_chinfo *ch = data, *other;
|
||||
@ -254,7 +258,7 @@ alschan_setspeed(kobj_t obj, void *data, u_int32_t speed)
|
||||
return speed;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
alschan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
|
||||
{
|
||||
struct sc_chinfo *ch = data;
|
||||
@ -267,7 +271,7 @@ alschan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
|
||||
return blocksize;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
alschan_getptr(kobj_t obj, void *data)
|
||||
{
|
||||
struct sc_chinfo *ch = data;
|
||||
@ -316,10 +320,10 @@ struct playback_command {
|
||||
u_int8_t dma_prog; /* sb16 dma program */
|
||||
u_int8_t dma_stop; /* sb16 stop register */
|
||||
} static const playback_cmds[] = {
|
||||
ALS_8BIT_CMD(AFMT_U8, DSP_MODE_U8MONO),
|
||||
ALS_8BIT_CMD(AFMT_U8 | AFMT_STEREO, DSP_MODE_U8STEREO),
|
||||
ALS_16BIT_CMD(AFMT_S16_LE, DSP_MODE_S16MONO),
|
||||
ALS_16BIT_CMD(AFMT_S16_LE | AFMT_STEREO, DSP_MODE_S16STEREO),
|
||||
ALS_8BIT_CMD(SND_FORMAT(AFMT_U8, 1, 0), DSP_MODE_U8MONO),
|
||||
ALS_8BIT_CMD(SND_FORMAT(AFMT_U8, 2, 0), DSP_MODE_U8STEREO),
|
||||
ALS_16BIT_CMD(SND_FORMAT(AFMT_S16_LE, 1, 0), DSP_MODE_S16MONO),
|
||||
ALS_16BIT_CMD(SND_FORMAT(AFMT_S16_LE, 2, 0), DSP_MODE_S16STEREO),
|
||||
};
|
||||
|
||||
static const struct playback_command*
|
||||
@ -418,7 +422,7 @@ static kobj_method_t alspchan_methods[] = {
|
||||
KOBJMETHOD(channel_trigger, alspchan_trigger),
|
||||
KOBJMETHOD(channel_getptr, alschan_getptr),
|
||||
KOBJMETHOD(channel_getcaps, alschan_getcaps),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
CHANNEL_DECLARE(alspchan);
|
||||
|
||||
@ -429,13 +433,13 @@ static u_int8_t
|
||||
als_get_fifo_format(struct sc_info *sc, u_int32_t format)
|
||||
{
|
||||
switch (format) {
|
||||
case AFMT_U8:
|
||||
case SND_FORMAT(AFMT_U8, 1, 0):
|
||||
return ALS_FIFO1_8BIT;
|
||||
case AFMT_U8 | AFMT_STEREO:
|
||||
case SND_FORMAT(AFMT_U8, 2, 0):
|
||||
return ALS_FIFO1_8BIT | ALS_FIFO1_STEREO;
|
||||
case AFMT_S16_LE:
|
||||
case SND_FORMAT(AFMT_S16_LE, 1, 0):
|
||||
return ALS_FIFO1_SIGNED;
|
||||
case AFMT_S16_LE | AFMT_STEREO:
|
||||
case SND_FORMAT(AFMT_S16_LE, 2, 0):
|
||||
return ALS_FIFO1_SIGNED | ALS_FIFO1_STEREO;
|
||||
}
|
||||
device_printf(sc->dev, "format not found: 0x%08x\n", format);
|
||||
@ -512,7 +516,7 @@ static kobj_method_t alsrchan_methods[] = {
|
||||
KOBJMETHOD(channel_trigger, alsrchan_trigger),
|
||||
KOBJMETHOD(channel_getptr, alschan_getptr),
|
||||
KOBJMETHOD(channel_getcaps, alschan_getcaps),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
CHANNEL_DECLARE(alsrchan);
|
||||
|
||||
@ -594,7 +598,7 @@ alsmix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
alsmix_setrecsrc(struct snd_mixer *m, u_int32_t src)
|
||||
{
|
||||
struct sc_info *sc = mix_getdevinfo(m);
|
||||
@ -621,7 +625,7 @@ static kobj_method_t als_mixer_methods[] = {
|
||||
KOBJMETHOD(mixer_init, alsmix_init),
|
||||
KOBJMETHOD(mixer_set, alsmix_set),
|
||||
KOBJMETHOD(mixer_setrecsrc, alsmix_setrecsrc),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
MIXER_DECLARE(als_mixer);
|
||||
|
||||
|
@ -53,6 +53,10 @@
|
||||
* random ninja hackery.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
#include <dev/sound/pcm/ac97.h>
|
||||
|
||||
@ -140,13 +144,13 @@ struct atiixp_info {
|
||||
#define atiixp_assert(_sc) snd_mtxassert((_sc)->lock)
|
||||
|
||||
static uint32_t atiixp_fmt_32bit[] = {
|
||||
AFMT_STEREO | AFMT_S16_LE,
|
||||
AFMT_STEREO | AFMT_S32_LE,
|
||||
SND_FORMAT(AFMT_S16_LE, 2, 0),
|
||||
SND_FORMAT(AFMT_S32_LE, 2, 0),
|
||||
0
|
||||
};
|
||||
|
||||
static uint32_t atiixp_fmt[] = {
|
||||
AFMT_STEREO | AFMT_S16_LE,
|
||||
SND_FORMAT(AFMT_S16_LE, 2, 0),
|
||||
0
|
||||
};
|
||||
|
||||
@ -187,13 +191,13 @@ static int atiixp_wrcd(kobj_t, void *, int, uint32_t);
|
||||
static void *atiixp_chan_init(kobj_t, void *, struct snd_dbuf *,
|
||||
struct pcm_channel *, int);
|
||||
static int atiixp_chan_setformat(kobj_t, void *, uint32_t);
|
||||
static int atiixp_chan_setspeed(kobj_t, void *, uint32_t);
|
||||
static int atiixp_chan_setfragments(kobj_t, void *, uint32_t, uint32_t);
|
||||
static int atiixp_chan_setblocksize(kobj_t, void *, uint32_t);
|
||||
static uint32_t atiixp_chan_setspeed(kobj_t, void *, uint32_t);
|
||||
static int atiixp_chan_setfragments(kobj_t, void *, uint32_t, uint32_t);
|
||||
static uint32_t atiixp_chan_setblocksize(kobj_t, void *, uint32_t);
|
||||
static void atiixp_buildsgdt(struct atiixp_chinfo *);
|
||||
static int atiixp_chan_trigger(kobj_t, void *, int);
|
||||
static __inline uint32_t atiixp_dmapos(struct atiixp_chinfo *);
|
||||
static int atiixp_chan_getptr(kobj_t, void *);
|
||||
static uint32_t atiixp_chan_getptr(kobj_t, void *);
|
||||
static struct pcmchan_caps *atiixp_chan_getcaps(kobj_t, void *);
|
||||
|
||||
static void atiixp_intr(void *);
|
||||
@ -420,7 +424,7 @@ atiixp_wrcd(kobj_t obj, void *devinfo, int reg, uint32_t data)
|
||||
static kobj_method_t atiixp_ac97_methods[] = {
|
||||
KOBJMETHOD(ac97_read, atiixp_rdcd),
|
||||
KOBJMETHOD(ac97_write, atiixp_wrcd),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
AC97_DECLARE(atiixp_ac97);
|
||||
|
||||
@ -515,7 +519,7 @@ atiixp_chan_setformat(kobj_t obj, void *data, uint32_t format)
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
static uint32_t
|
||||
atiixp_chan_setspeed(kobj_t obj, void *data, uint32_t spd)
|
||||
{
|
||||
/* XXX We're supposed to do VRA/DRA processing right here */
|
||||
@ -558,10 +562,10 @@ atiixp_chan_setfragments(kobj_t obj, void *data,
|
||||
ch->blksz = sndbuf_getblksz(ch->buffer);
|
||||
ch->blkcnt = sndbuf_getblkcnt(ch->buffer);
|
||||
|
||||
return (1);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
static uint32_t
|
||||
atiixp_chan_setblocksize(kobj_t obj, void *data, uint32_t blksz)
|
||||
{
|
||||
struct atiixp_chinfo *ch = data;
|
||||
@ -735,7 +739,7 @@ atiixp_chan_trigger(kobj_t obj, void *data, int go)
|
||||
ch->ptr = 0;
|
||||
ch->prevptr = 0;
|
||||
pollticks = ((uint64_t)hz * ch->blksz) /
|
||||
((uint64_t)sndbuf_getbps(ch->buffer) *
|
||||
((uint64_t)sndbuf_getalign(ch->buffer) *
|
||||
sndbuf_getspd(ch->buffer));
|
||||
pollticks >>= 2;
|
||||
if (pollticks > hz)
|
||||
@ -777,7 +781,7 @@ atiixp_chan_trigger(kobj_t obj, void *data, int go)
|
||||
else
|
||||
ch = &sc->rch;
|
||||
pollticks = ((uint64_t)hz * ch->blksz) /
|
||||
((uint64_t)sndbuf_getbps(ch->buffer) *
|
||||
((uint64_t)sndbuf_getalign(ch->buffer) *
|
||||
sndbuf_getspd(ch->buffer));
|
||||
pollticks >>= 2;
|
||||
if (pollticks > hz)
|
||||
@ -818,7 +822,7 @@ atiixp_chan_trigger(kobj_t obj, void *data, int go)
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
static uint32_t
|
||||
atiixp_chan_getptr(kobj_t obj, void *data)
|
||||
{
|
||||
struct atiixp_chinfo *ch = data;
|
||||
@ -854,7 +858,7 @@ static kobj_method_t atiixp_chan_methods[] = {
|
||||
KOBJMETHOD(channel_trigger, atiixp_chan_trigger),
|
||||
KOBJMETHOD(channel_getptr, atiixp_chan_getptr),
|
||||
KOBJMETHOD(channel_getcaps, atiixp_chan_getcaps),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
CHANNEL_DECLARE(atiixp_chan);
|
||||
|
||||
@ -954,7 +958,6 @@ atiixp_chip_pre_init(struct atiixp_info *sc)
|
||||
atiixp_unlock(sc);
|
||||
}
|
||||
|
||||
#ifdef SND_DYNSYSCTL
|
||||
static int
|
||||
sysctl_atiixp_polling(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
@ -994,7 +997,6 @@ sysctl_atiixp_polling(SYSCTL_HANDLER_ARGS)
|
||||
|
||||
return (err);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
atiixp_chip_post_init(void *arg)
|
||||
@ -1090,12 +1092,10 @@ atiixp_chip_post_init(void *arg)
|
||||
for (i = 0; i < ATI_IXP_NRCHAN; i++)
|
||||
pcm_addchan(sc->dev, PCMDIR_REC, &atiixp_chan_class, sc);
|
||||
|
||||
#ifdef SND_DYNSYSCTL
|
||||
SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->dev),
|
||||
SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), OID_AUTO,
|
||||
"polling", CTLTYPE_INT | CTLFLAG_RW, sc->dev, sizeof(sc->dev),
|
||||
sysctl_atiixp_polling, "I", "Enable polling mode");
|
||||
#endif
|
||||
|
||||
snprintf(status, SND_STATUSLEN, "at memory 0x%lx irq %ld %s",
|
||||
rman_get_start(sc->reg), rman_get_start(sc->irq),
|
||||
|
@ -24,6 +24,10 @@
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
#include <dev/sound/pcm/ac97.h>
|
||||
#include <dev/sound/pci/aureal.h>
|
||||
@ -38,19 +42,19 @@ SND_DECLARE_FILE("$FreeBSD$");
|
||||
|
||||
/* channel interface */
|
||||
static u_int32_t au_playfmt[] = {
|
||||
AFMT_U8,
|
||||
AFMT_STEREO | AFMT_U8,
|
||||
AFMT_S16_LE,
|
||||
AFMT_STEREO | AFMT_S16_LE,
|
||||
SND_FORMAT(AFMT_U8, 1, 0),
|
||||
SND_FORMAT(AFMT_U8, 2, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 1, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 2, 0),
|
||||
0
|
||||
};
|
||||
static struct pcmchan_caps au_playcaps = {4000, 48000, au_playfmt, 0};
|
||||
|
||||
static u_int32_t au_recfmt[] = {
|
||||
AFMT_U8,
|
||||
AFMT_STEREO | AFMT_U8,
|
||||
AFMT_S16_LE,
|
||||
AFMT_STEREO | AFMT_S16_LE,
|
||||
SND_FORMAT(AFMT_U8, 1, 0),
|
||||
SND_FORMAT(AFMT_U8, 2, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 1, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 2, 0),
|
||||
0
|
||||
};
|
||||
static struct pcmchan_caps au_reccaps = {4000, 48000, au_recfmt, 0};
|
||||
@ -167,7 +171,7 @@ au_wrcd(kobj_t obj, void *arg, int regno, u_int32_t data)
|
||||
static kobj_method_t au_ac97_methods[] = {
|
||||
KOBJMETHOD(ac97_read, au_rdcd),
|
||||
KOBJMETHOD(ac97_write, au_wrcd),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
AC97_DECLARE(au_ac97);
|
||||
|
||||
@ -242,13 +246,13 @@ static void
|
||||
au_prepareoutput(struct au_chinfo *ch, u_int32_t format)
|
||||
{
|
||||
struct au_info *au = ch->parent;
|
||||
int i, stereo = (format & AFMT_STEREO)? 1 : 0;
|
||||
int i, stereo = (AFMT_CHANNEL(format) > 1)? 1 : 0;
|
||||
u_int32_t baseaddr = sndbuf_getbufaddr(ch->buffer);
|
||||
|
||||
au_wr(au, 0, 0x1061c, 0, 4);
|
||||
au_wr(au, 0, 0x10620, 0, 4);
|
||||
au_wr(au, 0, 0x10624, 0, 4);
|
||||
switch(format & ~AFMT_STEREO) {
|
||||
switch(AFMT_ENCODING(format)) {
|
||||
case 1:
|
||||
i=0xb000;
|
||||
break;
|
||||
@ -382,7 +386,7 @@ static kobj_method_t auchan_methods[] = {
|
||||
KOBJMETHOD(channel_trigger, auchan_trigger),
|
||||
KOBJMETHOD(channel_getptr, auchan_getptr),
|
||||
KOBJMETHOD(channel_getcaps, auchan_getcaps),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
CHANNEL_DECLARE(auchan);
|
||||
|
||||
|
@ -42,6 +42,10 @@
|
||||
* those that don't.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
#include <dev/sound/pci/cmireg.h>
|
||||
#include <dev/sound/isa/sb.h>
|
||||
@ -129,10 +133,10 @@ struct sc_info {
|
||||
/* Channel caps */
|
||||
|
||||
static u_int32_t cmi_fmt[] = {
|
||||
AFMT_U8,
|
||||
AFMT_STEREO | AFMT_U8,
|
||||
AFMT_S16_LE,
|
||||
AFMT_STEREO | AFMT_S16_LE,
|
||||
SND_FORMAT(AFMT_U8, 1, 0),
|
||||
SND_FORMAT(AFMT_U8, 2, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 1, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 2, 0),
|
||||
0
|
||||
};
|
||||
|
||||
@ -348,7 +352,7 @@ cmichan_init(kobj_t obj, void *devinfo,
|
||||
ch->parent = sc;
|
||||
ch->channel = c;
|
||||
ch->bps = 1;
|
||||
ch->fmt = AFMT_U8;
|
||||
ch->fmt = SND_FORMAT(AFMT_U8, 1, 0);
|
||||
ch->spd = DSP_DEFAULT_SPEED;
|
||||
ch->buffer = b;
|
||||
ch->dma_active = 0;
|
||||
@ -384,7 +388,7 @@ cmichan_setformat(kobj_t obj, void *data, u_int32_t format)
|
||||
ch->bps = 1;
|
||||
}
|
||||
|
||||
if (format & AFMT_STEREO) {
|
||||
if (AFMT_CHANNEL(format) > 1) {
|
||||
f |= CMPCI_REG_FORMAT_STEREO;
|
||||
ch->bps *= 2;
|
||||
} else {
|
||||
@ -411,7 +415,7 @@ cmichan_setformat(kobj_t obj, void *data, u_int32_t format)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
cmichan_setspeed(kobj_t obj, void *data, u_int32_t speed)
|
||||
{
|
||||
struct sc_chinfo *ch = data;
|
||||
@ -457,7 +461,7 @@ cmichan_setspeed(kobj_t obj, void *data, u_int32_t speed)
|
||||
return ch->spd;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
cmichan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
|
||||
{
|
||||
struct sc_chinfo *ch = data;
|
||||
@ -507,7 +511,7 @@ cmichan_trigger(kobj_t obj, void *data, int go)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
cmichan_getptr(kobj_t obj, void *data)
|
||||
{
|
||||
struct sc_chinfo *ch = data;
|
||||
@ -589,7 +593,7 @@ static kobj_method_t cmichan_methods[] = {
|
||||
KOBJMETHOD(channel_trigger, cmichan_trigger),
|
||||
KOBJMETHOD(channel_getptr, cmichan_getptr),
|
||||
KOBJMETHOD(channel_getcaps, cmichan_getcaps),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
CHANNEL_DECLARE(cmichan);
|
||||
|
||||
@ -716,7 +720,7 @@ cmimix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
cmimix_setrecsrc(struct snd_mixer *m, u_int32_t src)
|
||||
{
|
||||
struct sc_info *sc = mix_getdevinfo(m);
|
||||
@ -748,7 +752,6 @@ cmimix_setrecsrc(struct snd_mixer *m, u_int32_t src)
|
||||
static int
|
||||
cmi_initsys(struct sc_info* sc)
|
||||
{
|
||||
#ifdef SND_DYNSYSCTL
|
||||
/* XXX: an user should be able to set this with a control tool,
|
||||
if not done before 7.0-RELEASE, this needs to be converted
|
||||
to a device specific sysctl "dev.pcm.X.yyy" via
|
||||
@ -759,7 +762,7 @@ cmi_initsys(struct sc_info* sc)
|
||||
OID_AUTO, "spdif_enabled", CTLFLAG_RW,
|
||||
&sc->spdif_enabled, 0,
|
||||
"enable SPDIF output at 44.1 kHz and above");
|
||||
#endif /* SND_DYNSYSCTL */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -768,7 +771,7 @@ static kobj_method_t cmi_mixer_methods[] = {
|
||||
KOBJMETHOD(mixer_init, cmimix_init),
|
||||
KOBJMETHOD(mixer_set, cmimix_set),
|
||||
KOBJMETHOD(mixer_setrecsrc, cmimix_setrecsrc),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
MIXER_DECLARE(cmi_mixer);
|
||||
|
||||
@ -777,7 +780,7 @@ MIXER_DECLARE(cmi_mixer);
|
||||
*/
|
||||
|
||||
static unsigned char
|
||||
cmi_mread(void *arg, struct sc_info *sc, int reg)
|
||||
cmi_mread(struct mpu401 *arg, void *sc, int reg)
|
||||
{
|
||||
unsigned int d;
|
||||
|
||||
@ -788,15 +791,16 @@ cmi_mread(void *arg, struct sc_info *sc, int reg)
|
||||
}
|
||||
|
||||
static void
|
||||
cmi_mwrite(void *arg, struct sc_info *sc, int reg, unsigned char b)
|
||||
cmi_mwrite(struct mpu401 *arg, void *sc, int reg, unsigned char b)
|
||||
{
|
||||
|
||||
bus_space_write_1(0,0,0x330 + reg , b);
|
||||
}
|
||||
|
||||
static int
|
||||
cmi_muninit(void *arg, struct sc_info *sc)
|
||||
cmi_muninit(struct mpu401 *arg, void *cookie)
|
||||
{
|
||||
struct sc_info *sc = cookie;
|
||||
|
||||
snd_mtxlock(sc->lock);
|
||||
sc->mpu_intr = 0;
|
||||
@ -810,7 +814,7 @@ static kobj_method_t cmi_mpu_methods[] = {
|
||||
KOBJMETHOD(mpufoi_read, cmi_mread),
|
||||
KOBJMETHOD(mpufoi_write, cmi_mwrite),
|
||||
KOBJMETHOD(mpufoi_uninit, cmi_muninit),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
|
||||
static DEFINE_CLASS(cmi_mpu, cmi_mpu_methods, 0);
|
||||
|
@ -31,6 +31,10 @@
|
||||
* contributed towards power management.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
#include <dev/sound/pcm/ac97.h>
|
||||
|
||||
@ -119,18 +123,18 @@ static u_int32_t cs4281_format_to_bps(u_int32_t);
|
||||
/* formats (do not add formats without editing cs_fmt_tab) */
|
||||
|
||||
static u_int32_t cs4281_fmts[] = {
|
||||
AFMT_U8,
|
||||
AFMT_U8 | AFMT_STEREO,
|
||||
AFMT_S8,
|
||||
AFMT_S8 | AFMT_STEREO,
|
||||
AFMT_S16_LE,
|
||||
AFMT_S16_LE | AFMT_STEREO,
|
||||
AFMT_U16_LE,
|
||||
AFMT_U16_LE | AFMT_STEREO,
|
||||
AFMT_S16_BE,
|
||||
AFMT_S16_BE | AFMT_STEREO,
|
||||
AFMT_U16_BE,
|
||||
AFMT_U16_BE | AFMT_STEREO,
|
||||
SND_FORMAT(AFMT_U8, 1, 0),
|
||||
SND_FORMAT(AFMT_U8, 2, 0),
|
||||
SND_FORMAT(AFMT_S8, 1, 0),
|
||||
SND_FORMAT(AFMT_S8, 2, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 1, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 2, 0),
|
||||
SND_FORMAT(AFMT_U16_LE, 1, 0),
|
||||
SND_FORMAT(AFMT_U16_LE, 2, 0),
|
||||
SND_FORMAT(AFMT_S16_BE, 1, 0),
|
||||
SND_FORMAT(AFMT_S16_BE, 2, 0),
|
||||
SND_FORMAT(AFMT_U16_BE, 1, 0),
|
||||
SND_FORMAT(AFMT_U16_BE, 2, 0),
|
||||
0
|
||||
};
|
||||
|
||||
@ -173,7 +177,7 @@ cs4281_waitset(struct sc_info *sc, int regno, u_int32_t mask, int tries)
|
||||
{
|
||||
u_int32_t v;
|
||||
|
||||
while(tries > 0) {
|
||||
while (tries > 0) {
|
||||
DELAY(100);
|
||||
v = cs4281_rd(sc, regno);
|
||||
if ((v & mask) == mask) break;
|
||||
@ -187,7 +191,7 @@ cs4281_waitclr(struct sc_info *sc, int regno, u_int32_t mask, int tries)
|
||||
{
|
||||
u_int32_t v;
|
||||
|
||||
while(tries > 0) {
|
||||
while (tries > 0) {
|
||||
DELAY(100);
|
||||
v = ~ cs4281_rd(sc, regno);
|
||||
if (v & mask) break;
|
||||
@ -231,7 +235,7 @@ cs4281_format_to_dmr(u_int32_t format)
|
||||
{
|
||||
u_int32_t dmr = 0;
|
||||
if (AFMT_8BIT & format) dmr |= CS4281PCI_DMR_SIZE8;
|
||||
if (!(AFMT_STEREO & format)) dmr |= CS4281PCI_DMR_MONO;
|
||||
if (AFMT_CHANNEL(format) < 2) dmr |= CS4281PCI_DMR_MONO;
|
||||
if (AFMT_BIGENDIAN & format) dmr |= CS4281PCI_DMR_BEND;
|
||||
if (!(AFMT_SIGNED & format)) dmr |= CS4281PCI_DMR_USIGN;
|
||||
return dmr;
|
||||
@ -240,13 +244,14 @@ cs4281_format_to_dmr(u_int32_t format)
|
||||
static inline u_int32_t
|
||||
cs4281_format_to_bps(u_int32_t format)
|
||||
{
|
||||
return ((AFMT_8BIT & format) ? 1 : 2) * ((AFMT_STEREO & format) ? 2 : 1);
|
||||
return ((AFMT_8BIT & format) ? 1 : 2) *
|
||||
((AFMT_CHANNEL(format) > 1) ? 2 : 1);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/* ac97 codec */
|
||||
|
||||
static u_int32_t
|
||||
static int
|
||||
cs4281_rdcd(kobj_t obj, void *devinfo, int regno)
|
||||
{
|
||||
struct sc_info *sc = (struct sc_info *)devinfo;
|
||||
@ -268,19 +273,19 @@ cs4281_rdcd(kobj_t obj, void *devinfo, int regno)
|
||||
/* Wait for read to complete */
|
||||
if (cs4281_waitclr(sc, CS4281PCI_ACCTL, CS4281PCI_ACCTL_DCV, 250) == 0) {
|
||||
device_printf(sc->dev, "cs4281_rdcd: DCV did not go\n");
|
||||
return 0xffffffff;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Wait for valid status */
|
||||
if (cs4281_waitset(sc, CS4281PCI_ACSTS, CS4281PCI_ACSTS_VSTS, 250) == 0) {
|
||||
device_printf(sc->dev,"cs4281_rdcd: VSTS did not come\n");
|
||||
return 0xffffffff;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return cs4281_rd(sc, CS4281PCI_ACSDA);
|
||||
}
|
||||
|
||||
static void
|
||||
static int
|
||||
cs4281_wrcd(kobj_t obj, void *devinfo, int regno, u_int32_t data)
|
||||
{
|
||||
struct sc_info *sc = (struct sc_info *)devinfo;
|
||||
@ -297,12 +302,14 @@ cs4281_wrcd(kobj_t obj, void *devinfo, int regno, u_int32_t data)
|
||||
if (cs4281_waitclr(sc, CS4281PCI_ACCTL, CS4281PCI_ACCTL_DCV, 250) == 0) {
|
||||
device_printf(sc->dev,"cs4281_wrcd: DCV did not go\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static kobj_method_t cs4281_ac97_methods[] = {
|
||||
KOBJMETHOD(ac97_read, cs4281_rdcd),
|
||||
KOBJMETHOD(ac97_write, cs4281_wrcd),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
AC97_DECLARE(cs4281_ac97);
|
||||
|
||||
@ -322,7 +329,7 @@ cs4281chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channe
|
||||
ch->parent = sc;
|
||||
ch->channel = c;
|
||||
|
||||
ch->fmt = AFMT_U8;
|
||||
ch->fmt = SND_FORMAT(AFMT_U8, 1, 0);
|
||||
ch->spd = DSP_DEFAULT_SPEED;
|
||||
ch->bps = 1;
|
||||
ch->blksz = sndbuf_getsize(ch->buffer);
|
||||
@ -336,7 +343,7 @@ cs4281chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channe
|
||||
return ch;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
cs4281chan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
|
||||
{
|
||||
struct sc_chinfo *ch = data;
|
||||
@ -358,7 +365,7 @@ cs4281chan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
|
||||
return ch->blksz;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
cs4281chan_setspeed(kobj_t obj, void *data, u_int32_t speed)
|
||||
{
|
||||
struct sc_chinfo *ch = data;
|
||||
@ -401,7 +408,7 @@ cs4281chan_setformat(kobj_t obj, void *data, u_int32_t format)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
cs4281chan_getptr(kobj_t obj, void *data)
|
||||
{
|
||||
struct sc_chinfo *ch = data;
|
||||
@ -453,7 +460,7 @@ static kobj_method_t cs4281chan_methods[] = {
|
||||
KOBJMETHOD(channel_trigger, cs4281chan_trigger),
|
||||
KOBJMETHOD(channel_getptr, cs4281chan_getptr),
|
||||
KOBJMETHOD(channel_getcaps, cs4281chan_getcaps),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
CHANNEL_DECLARE(cs4281chan);
|
||||
|
||||
|
@ -37,7 +37,11 @@
|
||||
#include <machine/resource.h>
|
||||
#include <machine/bus.h>
|
||||
#include <sys/rman.h>
|
||||
#include <sys/soundcard.h>
|
||||
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
#include <dev/sound/chip.h>
|
||||
#include <dev/sound/pci/csareg.h>
|
||||
@ -884,13 +888,13 @@ int
|
||||
csa_readcodec(csa_res *resp, u_long offset, u_int32_t *data)
|
||||
{
|
||||
int i;
|
||||
u_int32_t acsda, acctl, acsts;
|
||||
u_int32_t acctl, acsts;
|
||||
|
||||
/*
|
||||
* Make sure that there is not data sitting around from a previous
|
||||
* uncompleted access. ACSDA = Status Data Register = 47Ch
|
||||
*/
|
||||
acsda = csa_readio(resp, BA0_ACSDA);
|
||||
csa_readio(resp, BA0_ACSDA);
|
||||
|
||||
/*
|
||||
* Setup the AC97 control registers on the CS461x to send the
|
||||
|
@ -28,7 +28,10 @@
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/soundcard.h>
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
#include <dev/sound/pcm/ac97.h>
|
||||
#include <dev/sound/chip.h>
|
||||
@ -94,21 +97,21 @@ static void csa_ac97_suspend(struct csa_info *csa);
|
||||
static void csa_ac97_resume(struct csa_info *csa);
|
||||
|
||||
static u_int32_t csa_playfmt[] = {
|
||||
AFMT_U8,
|
||||
AFMT_STEREO | AFMT_U8,
|
||||
AFMT_S8,
|
||||
AFMT_STEREO | AFMT_S8,
|
||||
AFMT_S16_LE,
|
||||
AFMT_STEREO | AFMT_S16_LE,
|
||||
AFMT_S16_BE,
|
||||
AFMT_STEREO | AFMT_S16_BE,
|
||||
SND_FORMAT(AFMT_U8, 1, 0),
|
||||
SND_FORMAT(AFMT_U8, 2, 0),
|
||||
SND_FORMAT(AFMT_S8, 1, 0),
|
||||
SND_FORMAT(AFMT_S8, 2, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 1, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 2, 0),
|
||||
SND_FORMAT(AFMT_S16_BE, 1, 0),
|
||||
SND_FORMAT(AFMT_S16_BE, 2, 0),
|
||||
0
|
||||
};
|
||||
static struct pcmchan_caps csa_playcaps = {8000, 48000, csa_playfmt, 0};
|
||||
|
||||
static u_int32_t csa_recfmt[] = {
|
||||
AFMT_S16_LE,
|
||||
AFMT_STEREO | AFMT_S16_LE,
|
||||
SND_FORMAT(AFMT_S16_LE, 1, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 2, 0),
|
||||
0
|
||||
};
|
||||
static struct pcmchan_caps csa_reccaps = {11025, 48000, csa_recfmt, 0};
|
||||
@ -163,7 +166,7 @@ csa_wrcd(kobj_t obj, void *devinfo, int regno, u_int32_t data)
|
||||
static kobj_method_t csa_ac97_methods[] = {
|
||||
KOBJMETHOD(ac97_read, csa_rdcd),
|
||||
KOBJMETHOD(ac97_write, csa_wrcd),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
AC97_DECLARE(csa_ac97);
|
||||
|
||||
@ -489,7 +492,7 @@ csa_setupchan(struct csa_chinfo *ch)
|
||||
csa->pfie |= 0x8000;
|
||||
if (ch->fmt & AFMT_BIGENDIAN)
|
||||
csa->pfie |= 0x4000;
|
||||
if (!(ch->fmt & AFMT_STEREO))
|
||||
if (AFMT_CHANNEL(ch->fmt) < 2)
|
||||
csa->pfie |= 0x2000;
|
||||
if (ch->fmt & AFMT_8BIT)
|
||||
csa->pfie |= 0x1000;
|
||||
@ -498,7 +501,7 @@ csa_setupchan(struct csa_chinfo *ch)
|
||||
tmp = 4;
|
||||
if (ch->fmt & AFMT_16BIT)
|
||||
tmp <<= 1;
|
||||
if (ch->fmt & AFMT_STEREO)
|
||||
if (AFMT_CHANNEL(ch->fmt) > 1)
|
||||
tmp <<= 1;
|
||||
tmp--;
|
||||
|
||||
@ -548,7 +551,7 @@ csachan_setformat(kobj_t obj, void *data, u_int32_t format)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
csachan_setspeed(kobj_t obj, void *data, u_int32_t speed)
|
||||
{
|
||||
struct csa_chinfo *ch = data;
|
||||
@ -557,7 +560,7 @@ csachan_setspeed(kobj_t obj, void *data, u_int32_t speed)
|
||||
return ch->spd; /* XXX calc real speed */
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
csachan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
|
||||
{
|
||||
return CS461x_BUFFSIZE / 2;
|
||||
@ -589,13 +592,13 @@ csachan_trigger(kobj_t obj, void *data, int go)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
csachan_getptr(kobj_t obj, void *data)
|
||||
{
|
||||
struct csa_chinfo *ch = data;
|
||||
struct csa_info *csa = ch->parent;
|
||||
csa_res *resp;
|
||||
int ptr;
|
||||
u_int32_t ptr;
|
||||
|
||||
resp = &csa->res;
|
||||
|
||||
@ -627,7 +630,7 @@ static kobj_method_t csachan_methods[] = {
|
||||
KOBJMETHOD(channel_trigger, csachan_trigger),
|
||||
KOBJMETHOD(channel_getptr, csachan_getptr),
|
||||
KOBJMETHOD(channel_getcaps, csachan_getcaps),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
CHANNEL_DECLARE(csachan);
|
||||
|
||||
|
@ -24,6 +24,10 @@
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
#include <dev/sound/pcm/ac97.h>
|
||||
|
||||
@ -167,23 +171,23 @@ static void ds_wr(struct sc_info *, int, u_int32_t, int);
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
static u_int32_t ds_recfmt[] = {
|
||||
AFMT_U8,
|
||||
AFMT_STEREO | AFMT_U8,
|
||||
AFMT_S8,
|
||||
AFMT_STEREO | AFMT_S8,
|
||||
AFMT_S16_LE,
|
||||
AFMT_STEREO | AFMT_S16_LE,
|
||||
AFMT_U16_LE,
|
||||
AFMT_STEREO | AFMT_U16_LE,
|
||||
SND_FORMAT(AFMT_U8, 1, 0),
|
||||
SND_FORMAT(AFMT_U8, 2, 0),
|
||||
SND_FORMAT(AFMT_S8, 1, 0),
|
||||
SND_FORMAT(AFMT_S8, 2, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 1, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 2, 0),
|
||||
SND_FORMAT(AFMT_U16_LE, 1, 0),
|
||||
SND_FORMAT(AFMT_U16_LE, 2, 0),
|
||||
0
|
||||
};
|
||||
static struct pcmchan_caps ds_reccaps = {4000, 48000, ds_recfmt, 0};
|
||||
|
||||
static u_int32_t ds_playfmt[] = {
|
||||
AFMT_U8,
|
||||
AFMT_STEREO | AFMT_U8,
|
||||
/* AFMT_S16_LE, */
|
||||
AFMT_STEREO | AFMT_S16_LE,
|
||||
SND_FORMAT(AFMT_U8, 1, 0),
|
||||
SND_FORMAT(AFMT_U8, 2, 0),
|
||||
/* SND_FORMAT(AFMT_S16_LE, 1, 0), */
|
||||
SND_FORMAT(AFMT_S16_LE, 2, 0),
|
||||
0
|
||||
};
|
||||
static struct pcmchan_caps ds_playcaps = {4000, 96000, ds_playfmt, 0};
|
||||
@ -323,7 +327,7 @@ static kobj_method_t ds_ac97_methods[] = {
|
||||
KOBJMETHOD(ac97_init, ds_initcd),
|
||||
KOBJMETHOD(ac97_read, ds_rdcd),
|
||||
KOBJMETHOD(ac97_write, ds_wrcd),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
AC97_DECLARE(ds_ac97);
|
||||
|
||||
@ -432,7 +436,7 @@ ds_setuppch(struct sc_pchinfo *ch)
|
||||
int stereo, b16, c, sz;
|
||||
bus_addr_t addr;
|
||||
|
||||
stereo = (ch->fmt & AFMT_STEREO)? 1 : 0;
|
||||
stereo = (AFMT_CHANNEL(ch->fmt) > 1)? 1 : 0;
|
||||
b16 = (ch->fmt & AFMT_16BIT)? 1 : 0;
|
||||
c = stereo? 1 : 0;
|
||||
addr = sndbuf_getbufaddr(ch->buffer);
|
||||
@ -452,7 +456,7 @@ ds_setuprch(struct sc_rchinfo *ch)
|
||||
u_int32_t x, y;
|
||||
bus_addr_t addr;
|
||||
|
||||
stereo = (ch->fmt & AFMT_STEREO)? 1 : 0;
|
||||
stereo = (AFMT_CHANNEL(ch->fmt) > 1)? 1 : 0;
|
||||
b16 = (ch->fmt & AFMT_16BIT)? 1 : 0;
|
||||
addr = sndbuf_getbufaddr(ch->buffer);
|
||||
sz = sndbuf_getsize(ch->buffer);
|
||||
@ -487,7 +491,7 @@ ds1pchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel
|
||||
ch->parent = sc;
|
||||
ch->channel = c;
|
||||
ch->dir = dir;
|
||||
ch->fmt = AFMT_U8;
|
||||
ch->fmt = SND_FORMAT(AFMT_U8, 1, 0);
|
||||
ch->spd = 8000;
|
||||
ch->run = 0;
|
||||
if (sndbuf_alloc(ch->buffer, sc->buffer_dmat, 0, sc->bufsz) != 0)
|
||||
@ -512,7 +516,7 @@ ds1pchan_setformat(kobj_t obj, void *data, u_int32_t format)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
ds1pchan_setspeed(kobj_t obj, void *data, u_int32_t speed)
|
||||
{
|
||||
struct sc_pchinfo *ch = data;
|
||||
@ -522,7 +526,7 @@ ds1pchan_setspeed(kobj_t obj, void *data, u_int32_t speed)
|
||||
return speed;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
ds1pchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
|
||||
{
|
||||
struct sc_pchinfo *ch = data;
|
||||
@ -530,7 +534,7 @@ ds1pchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
|
||||
int drate;
|
||||
|
||||
/* irq rate is fixed at 187.5hz */
|
||||
drate = ch->spd * sndbuf_getbps(ch->buffer);
|
||||
drate = ch->spd * sndbuf_getalign(ch->buffer);
|
||||
blocksize = roundup2((drate << 8) / DS1_IRQHZ, 4);
|
||||
sndbuf_resize(ch->buffer, sc->bufsz / blocksize, blocksize);
|
||||
|
||||
@ -547,7 +551,7 @@ ds1pchan_trigger(kobj_t obj, void *data, int go)
|
||||
|
||||
if (!PCMTRIG_COMMON(go))
|
||||
return 0;
|
||||
stereo = (ch->fmt & AFMT_STEREO)? 1 : 0;
|
||||
stereo = (AFMT_CHANNEL(ch->fmt) > 1)? 1 : 0;
|
||||
if (go == PCMTRIG_START) {
|
||||
ch->run = 1;
|
||||
ds_setuppch(ch);
|
||||
@ -566,7 +570,7 @@ ds1pchan_trigger(kobj_t obj, void *data, int go)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
ds1pchan_getptr(kobj_t obj, void *data)
|
||||
{
|
||||
struct sc_pchinfo *ch = data;
|
||||
@ -575,7 +579,7 @@ ds1pchan_getptr(kobj_t obj, void *data)
|
||||
int ss;
|
||||
u_int32_t ptr;
|
||||
|
||||
ss = (ch->fmt & AFMT_STEREO)? 1 : 0;
|
||||
ss = (AFMT_CHANNEL(ch->fmt) > 1)? 1 : 0;
|
||||
ss += (ch->fmt & AFMT_16BIT)? 1 : 0;
|
||||
|
||||
bank = ch->lslot + sc->currbank;
|
||||
@ -599,7 +603,7 @@ static kobj_method_t ds1pchan_methods[] = {
|
||||
KOBJMETHOD(channel_trigger, ds1pchan_trigger),
|
||||
KOBJMETHOD(channel_getptr, ds1pchan_getptr),
|
||||
KOBJMETHOD(channel_getcaps, ds1pchan_getcaps),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
CHANNEL_DECLARE(ds1pchan);
|
||||
|
||||
@ -619,7 +623,7 @@ ds1rchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel
|
||||
ch->parent = sc;
|
||||
ch->channel = c;
|
||||
ch->dir = dir;
|
||||
ch->fmt = AFMT_U8;
|
||||
ch->fmt = SND_FORMAT(AFMT_U8, 1, 0);
|
||||
ch->spd = 8000;
|
||||
if (sndbuf_alloc(ch->buffer, sc->buffer_dmat, 0, sc->bufsz) != 0)
|
||||
return NULL;
|
||||
@ -640,7 +644,7 @@ ds1rchan_setformat(kobj_t obj, void *data, u_int32_t format)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
ds1rchan_setspeed(kobj_t obj, void *data, u_int32_t speed)
|
||||
{
|
||||
struct sc_rchinfo *ch = data;
|
||||
@ -650,7 +654,7 @@ ds1rchan_setspeed(kobj_t obj, void *data, u_int32_t speed)
|
||||
return speed;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
ds1rchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
|
||||
{
|
||||
struct sc_rchinfo *ch = data;
|
||||
@ -658,7 +662,7 @@ ds1rchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
|
||||
int drate;
|
||||
|
||||
/* irq rate is fixed at 187.5hz */
|
||||
drate = ch->spd * sndbuf_getbps(ch->buffer);
|
||||
drate = ch->spd * sndbuf_getalign(ch->buffer);
|
||||
blocksize = roundup2((drate << 8) / DS1_IRQHZ, 4);
|
||||
sndbuf_resize(ch->buffer, sc->bufsz / blocksize, blocksize);
|
||||
|
||||
@ -696,7 +700,7 @@ ds1rchan_trigger(kobj_t obj, void *data, int go)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
ds1rchan_getptr(kobj_t obj, void *data)
|
||||
{
|
||||
struct sc_rchinfo *ch = data;
|
||||
@ -719,7 +723,7 @@ static kobj_method_t ds1rchan_methods[] = {
|
||||
KOBJMETHOD(channel_trigger, ds1rchan_trigger),
|
||||
KOBJMETHOD(channel_getptr, ds1rchan_getptr),
|
||||
KOBJMETHOD(channel_getcaps, ds1rchan_getcaps),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
CHANNEL_DECLARE(ds1rchan);
|
||||
|
||||
|
@ -26,6 +26,10 @@
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
#include <dev/sound/pcm/ac97.h>
|
||||
#include "emu10k1-alsa%diked.h"
|
||||
@ -168,18 +172,18 @@ static void emu_wr(struct sc_info *, int, u_int32_t, int);
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
static u_int32_t emu_rfmt_ac97[] = {
|
||||
AFMT_S16_LE,
|
||||
AFMT_STEREO | AFMT_S16_LE,
|
||||
SND_FORMAT(AFMT_S16_LE, 1, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 2, 0),
|
||||
0
|
||||
};
|
||||
|
||||
static u_int32_t emu_rfmt_mic[] = {
|
||||
AFMT_U8,
|
||||
SND_FORMAT(AFMT_U8, 1, 0),
|
||||
0
|
||||
};
|
||||
|
||||
static u_int32_t emu_rfmt_efx[] = {
|
||||
AFMT_STEREO | AFMT_S16_LE,
|
||||
SND_FORMAT(AFMT_S16_LE, 2, 0),
|
||||
0
|
||||
};
|
||||
|
||||
@ -190,10 +194,10 @@ static struct pcmchan_caps emu_reccaps[3] = {
|
||||
};
|
||||
|
||||
static u_int32_t emu_pfmt[] = {
|
||||
AFMT_U8,
|
||||
AFMT_STEREO | AFMT_U8,
|
||||
AFMT_S16_LE,
|
||||
AFMT_STEREO | AFMT_S16_LE,
|
||||
SND_FORMAT(AFMT_U8, 1, 0),
|
||||
SND_FORMAT(AFMT_U8, 2, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 1, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 2, 0),
|
||||
0
|
||||
};
|
||||
|
||||
@ -307,7 +311,7 @@ emu_wrcd(kobj_t obj, void *devinfo, int regno, u_int32_t data)
|
||||
static kobj_method_t emu_ac97_methods[] = {
|
||||
KOBJMETHOD(ac97_read, emu_rdcd),
|
||||
KOBJMETHOD(ac97_write, emu_wrcd),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
AC97_DECLARE(emu_ac97);
|
||||
|
||||
@ -324,7 +328,7 @@ emu_settimer(struct sc_info *sc)
|
||||
for (i = 0; i < sc->nchans; i++) {
|
||||
pch = &sc->pch[i];
|
||||
if (pch->buffer) {
|
||||
tmp = (pch->spd * sndbuf_getbps(pch->buffer))
|
||||
tmp = (pch->spd * sndbuf_getalign(pch->buffer))
|
||||
/ pch->blksz;
|
||||
if (tmp > rate)
|
||||
rate = tmp;
|
||||
@ -334,7 +338,7 @@ emu_settimer(struct sc_info *sc)
|
||||
for (i = 0; i < 3; i++) {
|
||||
rch = &sc->rch[i];
|
||||
if (rch->buffer) {
|
||||
tmp = (rch->spd * sndbuf_getbps(rch->buffer))
|
||||
tmp = (rch->spd * sndbuf_getalign(rch->buffer))
|
||||
/ rch->blksz;
|
||||
if (tmp > rate)
|
||||
rate = tmp;
|
||||
@ -533,7 +537,7 @@ emu_vsetup(struct sc_pchinfo *ch)
|
||||
|
||||
if (ch->fmt) {
|
||||
v->b16 = (ch->fmt & AFMT_16BIT) ? 1 : 0;
|
||||
v->stereo = (ch->fmt & AFMT_STEREO) ? 1 : 0;
|
||||
v->stereo = (AFMT_CHANNEL(ch->fmt) > 1) ? 1 : 0;
|
||||
if (v->slave != NULL) {
|
||||
v->slave->b16 = v->b16;
|
||||
v->slave->stereo = v->stereo;
|
||||
@ -729,7 +733,7 @@ emupchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
|
||||
ch->parent = sc;
|
||||
ch->channel = c;
|
||||
ch->blksz = sc->bufsz / 2;
|
||||
ch->fmt = AFMT_U8;
|
||||
ch->fmt = SND_FORMAT(AFMT_U8, 1, 0);
|
||||
ch->spd = 8000;
|
||||
snd_mtxlock(sc->lock);
|
||||
ch->master = emu_valloc(sc);
|
||||
@ -764,7 +768,7 @@ emupchan_setformat(kobj_t obj, void *data, u_int32_t format)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
emupchan_setspeed(kobj_t obj, void *data, u_int32_t speed)
|
||||
{
|
||||
struct sc_pchinfo *ch = data;
|
||||
@ -773,7 +777,7 @@ emupchan_setspeed(kobj_t obj, void *data, u_int32_t speed)
|
||||
return ch->spd;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
emupchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
|
||||
{
|
||||
struct sc_pchinfo *ch = data;
|
||||
@ -785,7 +789,7 @@ emupchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
|
||||
emu_settimer(sc);
|
||||
irqrate = 48000 / sc->timerinterval;
|
||||
snd_mtxunlock(sc->lock);
|
||||
blksz = (ch->spd * sndbuf_getbps(ch->buffer)) / irqrate;
|
||||
blksz = (ch->spd * sndbuf_getalign(ch->buffer)) / irqrate;
|
||||
return blocksize;
|
||||
}
|
||||
|
||||
@ -819,7 +823,7 @@ emupchan_trigger(kobj_t obj, void *data, int go)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
emupchan_getptr(kobj_t obj, void *data)
|
||||
{
|
||||
struct sc_pchinfo *ch = data;
|
||||
@ -848,7 +852,7 @@ static kobj_method_t emupchan_methods[] = {
|
||||
KOBJMETHOD(channel_trigger, emupchan_trigger),
|
||||
KOBJMETHOD(channel_getptr, emupchan_getptr),
|
||||
KOBJMETHOD(channel_getcaps, emupchan_getcaps),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
CHANNEL_DECLARE(emupchan);
|
||||
|
||||
@ -866,7 +870,7 @@ emurchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
|
||||
ch->parent = sc;
|
||||
ch->channel = c;
|
||||
ch->blksz = sc->bufsz / 2;
|
||||
ch->fmt = AFMT_U8;
|
||||
ch->fmt = SND_FORMAT(AFMT_U8, 1, 0);
|
||||
ch->spd = 8000;
|
||||
ch->num = sc->rnum;
|
||||
switch(sc->rnum) {
|
||||
@ -915,7 +919,7 @@ emurchan_setformat(kobj_t obj, void *data, u_int32_t format)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
emurchan_setspeed(kobj_t obj, void *data, u_int32_t speed)
|
||||
{
|
||||
struct sc_rchinfo *ch = data;
|
||||
@ -934,7 +938,7 @@ emurchan_setspeed(kobj_t obj, void *data, u_int32_t speed)
|
||||
return ch->spd;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
emurchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
|
||||
{
|
||||
struct sc_rchinfo *ch = data;
|
||||
@ -946,7 +950,7 @@ emurchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
|
||||
emu_settimer(sc);
|
||||
irqrate = 48000 / sc->timerinterval;
|
||||
snd_mtxunlock(sc->lock);
|
||||
blksz = (ch->spd * sndbuf_getbps(ch->buffer)) / irqrate;
|
||||
blksz = (ch->spd * sndbuf_getalign(ch->buffer)) / irqrate;
|
||||
return blocksize;
|
||||
}
|
||||
|
||||
@ -994,12 +998,12 @@ emurchan_trigger(kobj_t obj, void *data, int go)
|
||||
if (ch->num == 0) {
|
||||
if (sc->audigy) {
|
||||
val = A_ADCCR_LCHANENABLE;
|
||||
if (ch->fmt & AFMT_STEREO)
|
||||
if (AFMT_CHANNEL(ch->fmt) > 1)
|
||||
val |= A_ADCCR_RCHANENABLE;
|
||||
val |= audigy_recval(ch->spd);
|
||||
} else {
|
||||
val = ADCCR_LCHANENABLE;
|
||||
if (ch->fmt & AFMT_STEREO)
|
||||
if (AFMT_CHANNEL(ch->fmt) > 1)
|
||||
val |= ADCCR_RCHANENABLE;
|
||||
val |= emu_recval(ch->spd);
|
||||
}
|
||||
@ -1033,7 +1037,7 @@ emurchan_trigger(kobj_t obj, void *data, int go)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
emurchan_getptr(kobj_t obj, void *data)
|
||||
{
|
||||
struct sc_rchinfo *ch = data;
|
||||
@ -1063,29 +1067,30 @@ static kobj_method_t emurchan_methods[] = {
|
||||
KOBJMETHOD(channel_trigger, emurchan_trigger),
|
||||
KOBJMETHOD(channel_getptr, emurchan_getptr),
|
||||
KOBJMETHOD(channel_getcaps, emurchan_getcaps),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
CHANNEL_DECLARE(emurchan);
|
||||
|
||||
static unsigned char
|
||||
emu_mread(void *arg, struct sc_info *sc, int reg)
|
||||
emu_mread(struct mpu401 *arg, void *sc, int reg)
|
||||
{
|
||||
unsigned int d;
|
||||
|
||||
d = emu_rd(sc, 0x18 + reg, 1);
|
||||
d = emu_rd((struct sc_info *)sc, 0x18 + reg, 1);
|
||||
return d;
|
||||
}
|
||||
|
||||
static void
|
||||
emu_mwrite(void *arg, struct sc_info *sc, int reg, unsigned char b)
|
||||
emu_mwrite(struct mpu401 *arg, void *sc, int reg, unsigned char b)
|
||||
{
|
||||
|
||||
emu_wr(sc, 0x18 + reg, b, 1);
|
||||
emu_wr((struct sc_info *)sc, 0x18 + reg, b, 1);
|
||||
}
|
||||
|
||||
static int
|
||||
emu_muninit(void *arg, struct sc_info *sc)
|
||||
emu_muninit(struct mpu401 *arg, void *cookie)
|
||||
{
|
||||
struct sc_info *sc = cookie;
|
||||
|
||||
snd_mtxlock(sc->lock);
|
||||
sc->mpu_intr = 0;
|
||||
@ -1098,7 +1103,7 @@ static kobj_method_t emu_mpu_methods[] = {
|
||||
KOBJMETHOD(mpufoi_read, emu_mread),
|
||||
KOBJMETHOD(mpufoi_write, emu_mwrite),
|
||||
KOBJMETHOD(mpufoi_uninit, emu_muninit),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
|
||||
static DEFINE_CLASS(emu_mpu, emu_mpu_methods, 0);
|
||||
|
@ -39,6 +39,10 @@
|
||||
#include <sys/lock.h>
|
||||
#include <sys/mutex.h>
|
||||
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/chip.h>
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
|
||||
@ -65,8 +69,9 @@ static uint32_t emu_midi_card_intr(void *p, uint32_t arg);
|
||||
static devclass_t emu_midi_devclass;
|
||||
|
||||
static unsigned char
|
||||
emu_mread(void *arg __unused, struct emu_midi_softc *sc, int reg)
|
||||
emu_mread(struct mpu401 *arg __unused, void *cookie, int reg)
|
||||
{
|
||||
struct emu_midi_softc *sc = cookie;
|
||||
unsigned int d;
|
||||
|
||||
d = 0;
|
||||
@ -79,8 +84,9 @@ emu_mread(void *arg __unused, struct emu_midi_softc *sc, int reg)
|
||||
}
|
||||
|
||||
static void
|
||||
emu_mwrite(void *arg __unused, struct emu_midi_softc *sc, int reg, unsigned char b)
|
||||
emu_mwrite(struct mpu401 *arg __unused, void *cookie, int reg, unsigned char b)
|
||||
{
|
||||
struct emu_midi_softc *sc = cookie;
|
||||
|
||||
if (sc->is_emu10k1)
|
||||
emu_wr(sc->card, 0x18 + reg, b, 1);
|
||||
@ -89,8 +95,9 @@ emu_mwrite(void *arg __unused, struct emu_midi_softc *sc, int reg, unsigned char
|
||||
}
|
||||
|
||||
static int
|
||||
emu_muninit(void *arg __unused, struct emu_midi_softc *sc)
|
||||
emu_muninit(struct mpu401 *arg __unused, void *cookie)
|
||||
{
|
||||
struct emu_midi_softc *sc = cookie;
|
||||
|
||||
mtx_lock(&sc->mtx);
|
||||
sc->mpu_intr = NULL;
|
||||
@ -103,7 +110,7 @@ static kobj_method_t emu_mpu_methods[] = {
|
||||
KOBJMETHOD(mpufoi_read, emu_mread),
|
||||
KOBJMETHOD(mpufoi_write, emu_mwrite),
|
||||
KOBJMETHOD(mpufoi_uninit, emu_muninit),
|
||||
{0, 0}
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
static DEFINE_CLASS(emu_mpu, emu_mpu_methods, 0);
|
||||
|
||||
|
@ -39,6 +39,10 @@
|
||||
#include <sys/lock.h>
|
||||
#include <sys/mutex.h>
|
||||
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/chip.h>
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
#include <dev/sound/pcm/ac97.h>
|
||||
@ -112,8 +116,8 @@ struct emu_pcm_info {
|
||||
|
||||
|
||||
static uint32_t emu_rfmt_adc[] = {
|
||||
AFMT_S16_LE,
|
||||
AFMT_STEREO | AFMT_S16_LE,
|
||||
SND_FORMAT(AFMT_S16_LE, 1, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 2, 0),
|
||||
0
|
||||
};
|
||||
static struct pcmchan_caps emu_reccaps_adc = {
|
||||
@ -121,7 +125,7 @@ static struct pcmchan_caps emu_reccaps_adc = {
|
||||
};
|
||||
|
||||
static uint32_t emu_rfmt_efx[] = {
|
||||
AFMT_S16_LE,
|
||||
SND_FORMAT(AFMT_S16_LE, 1, 0),
|
||||
0
|
||||
};
|
||||
|
||||
@ -142,15 +146,15 @@ static int emu_rates_audigy[] = {
|
||||
};
|
||||
|
||||
static uint32_t emu_pfmt[] = {
|
||||
AFMT_U8,
|
||||
AFMT_STEREO | AFMT_U8,
|
||||
AFMT_S16_LE,
|
||||
AFMT_STEREO | AFMT_S16_LE,
|
||||
SND_FORMAT(AFMT_U8, 1, 0),
|
||||
SND_FORMAT(AFMT_U8, 2, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 1, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 2, 0),
|
||||
0
|
||||
};
|
||||
static uint32_t emu_pfmt_mono[] = {
|
||||
AFMT_U8,
|
||||
AFMT_S16_LE,
|
||||
SND_FORMAT(AFMT_U8, 1, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 1, 0),
|
||||
0
|
||||
};
|
||||
|
||||
@ -385,7 +389,7 @@ emu_dspmixer_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned righ
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
emu_dspmixer_setrecsrc(struct snd_mixer *m, u_int32_t src)
|
||||
{
|
||||
struct emu_pcm_info *sc;
|
||||
@ -467,7 +471,7 @@ static kobj_method_t emudspmixer_methods[] = {
|
||||
KOBJMETHOD(mixer_uninit, emu_dspmixer_uninit),
|
||||
KOBJMETHOD(mixer_set, emu_dspmixer_set),
|
||||
KOBJMETHOD(mixer_setrecsrc, emu_dspmixer_setrecsrc),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
MIXER_DECLARE(emudspmixer);
|
||||
|
||||
@ -486,7 +490,7 @@ emu_efxmixer_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned righ
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
emu_efxmixer_setrecsrc(struct snd_mixer *m __unused, u_int32_t src __unused)
|
||||
{
|
||||
return (SOUND_MASK_MONITOR);
|
||||
@ -496,7 +500,7 @@ static kobj_method_t emuefxmixer_methods[] = {
|
||||
KOBJMETHOD(mixer_init, emu_efxmixer_init),
|
||||
KOBJMETHOD(mixer_set, emu_efxmixer_set),
|
||||
KOBJMETHOD(mixer_setrecsrc, emu_efxmixer_setrecsrc),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
MIXER_DECLARE(emuefxmixer);
|
||||
|
||||
@ -642,7 +646,7 @@ emu_ewrcd(kobj_t obj __unused, void *devinfo, int regno, uint32_t data)
|
||||
static kobj_method_t emu_eac97_methods[] = {
|
||||
KOBJMETHOD(ac97_read, emu_erdcd),
|
||||
KOBJMETHOD(ac97_write, emu_ewrcd),
|
||||
{0, 0}
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
AC97_DECLARE(emu_eac97);
|
||||
|
||||
@ -673,7 +677,7 @@ emu_wrcd(kobj_t obj __unused, void *devinfo, int regno, uint32_t data)
|
||||
static kobj_method_t emu_ac97_methods[] = {
|
||||
KOBJMETHOD(ac97_read, emu_rdcd),
|
||||
KOBJMETHOD(ac97_write, emu_wrcd),
|
||||
{0, 0}
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
AC97_DECLARE(emu_ac97);
|
||||
|
||||
@ -718,7 +722,7 @@ emupchan_init(kobj_t obj __unused, void *devinfo, struct snd_dbuf *b, struct pcm
|
||||
ch->pcm = sc;
|
||||
ch->channel = c;
|
||||
ch->blksz = sc->bufsz;
|
||||
ch->fmt = AFMT_U8;
|
||||
ch->fmt = SND_FORMAT(AFMT_U8, 1, 0);
|
||||
ch->spd = 8000;
|
||||
ch->master = emu_valloc(sc->card);
|
||||
/*
|
||||
@ -753,7 +757,7 @@ emupchan_setformat(kobj_t obj __unused, void *c_devinfo, uint32_t format)
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
static uint32_t
|
||||
emupchan_setspeed(kobj_t obj __unused, void *c_devinfo, uint32_t speed)
|
||||
{
|
||||
struct emu_pcm_pchinfo *ch = c_devinfo;
|
||||
@ -762,7 +766,7 @@ emupchan_setspeed(kobj_t obj __unused, void *c_devinfo, uint32_t speed)
|
||||
return (ch->spd);
|
||||
}
|
||||
|
||||
static int
|
||||
static uint32_t
|
||||
emupchan_setblocksize(kobj_t obj __unused, void *c_devinfo, uint32_t blocksize)
|
||||
{
|
||||
struct emu_pcm_pchinfo *ch = c_devinfo;
|
||||
@ -772,7 +776,7 @@ emupchan_setblocksize(kobj_t obj __unused, void *c_devinfo, uint32_t blocksize)
|
||||
blocksize = ch->pcm->bufsz;
|
||||
snd_mtxlock(sc->lock);
|
||||
ch->blksz = blocksize;
|
||||
emu_timer_set(sc->card, ch->timer, ch->blksz / sndbuf_getbps(ch->buffer));
|
||||
emu_timer_set(sc->card, ch->timer, ch->blksz / sndbuf_getalign(ch->buffer));
|
||||
snd_mtxunlock(sc->lock);
|
||||
return (ch->blksz);
|
||||
}
|
||||
@ -789,12 +793,12 @@ emupchan_trigger(kobj_t obj __unused, void *c_devinfo, int go)
|
||||
snd_mtxlock(sc->lock); /* XXX can we trigger on parallel threads ? */
|
||||
if (go == PCMTRIG_START) {
|
||||
emu_vsetup(ch->master, ch->fmt, ch->spd);
|
||||
if ((ch->fmt & AFMT_STEREO) == AFMT_STEREO)
|
||||
if (AFMT_CHANNEL(ch->fmt) > 1)
|
||||
emu_vroute(sc->card, &(sc->rt), ch->master);
|
||||
else
|
||||
emu_vroute(sc->card, &(sc->rt_mono), ch->master);
|
||||
emu_vwrite(sc->card, ch->master);
|
||||
emu_timer_set(sc->card, ch->timer, ch->blksz / sndbuf_getbps(ch->buffer));
|
||||
emu_timer_set(sc->card, ch->timer, ch->blksz / sndbuf_getalign(ch->buffer));
|
||||
emu_timer_enable(sc->card, ch->timer, 1);
|
||||
}
|
||||
/* PCM interrupt handler will handle PCMTRIG_STOP event */
|
||||
@ -804,7 +808,7 @@ emupchan_trigger(kobj_t obj __unused, void *c_devinfo, int go)
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
static uint32_t
|
||||
emupchan_getptr(kobj_t obj __unused, void *c_devinfo)
|
||||
{
|
||||
struct emu_pcm_pchinfo *ch = c_devinfo;
|
||||
@ -848,7 +852,7 @@ static kobj_method_t emupchan_methods[] = {
|
||||
KOBJMETHOD(channel_trigger, emupchan_trigger),
|
||||
KOBJMETHOD(channel_getptr, emupchan_getptr),
|
||||
KOBJMETHOD(channel_getcaps, emupchan_getcaps),
|
||||
{0, 0}
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
CHANNEL_DECLARE(emupchan);
|
||||
|
||||
@ -864,7 +868,7 @@ emurchan_init(kobj_t obj __unused, void *devinfo, struct snd_dbuf *b, struct pcm
|
||||
ch->pcm = sc;
|
||||
ch->channel = c;
|
||||
ch->blksz = sc->bufsz / 2; /* We rise interrupt for half-full buffer */
|
||||
ch->fmt = AFMT_U8;
|
||||
ch->fmt = SND_FORMAT(AFMT_U8, 1, 0);
|
||||
ch->spd = 8000;
|
||||
ch->idxreg = sc->is_emu10k1 ? ADCIDX : A_ADCIDX;
|
||||
ch->basereg = ADCBA;
|
||||
@ -902,7 +906,7 @@ emurchan_setformat(kobj_t obj __unused, void *c_devinfo, uint32_t format)
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
static uint32_t
|
||||
emurchan_setspeed(kobj_t obj __unused, void *c_devinfo, uint32_t speed)
|
||||
{
|
||||
struct emu_pcm_rchinfo *ch = c_devinfo;
|
||||
@ -916,7 +920,7 @@ emurchan_setspeed(kobj_t obj __unused, void *c_devinfo, uint32_t speed)
|
||||
return (ch->spd);
|
||||
}
|
||||
|
||||
static int
|
||||
static uint32_t
|
||||
emurchan_setblocksize(kobj_t obj __unused, void *c_devinfo, uint32_t blocksize)
|
||||
{
|
||||
struct emu_pcm_rchinfo *ch = c_devinfo;
|
||||
@ -929,7 +933,7 @@ emurchan_setblocksize(kobj_t obj __unused, void *c_devinfo, uint32_t blocksize)
|
||||
* (and use) timer interrupts. Otherwise channel will be marked dead.
|
||||
*/
|
||||
if (ch->blksz < (ch->pcm->bufsz / 2)) {
|
||||
emu_timer_set(sc->card, ch->timer, ch->blksz / sndbuf_getbps(ch->buffer));
|
||||
emu_timer_set(sc->card, ch->timer, ch->blksz / sndbuf_getalign(ch->buffer));
|
||||
emu_timer_enable(sc->card, ch->timer, 1);
|
||||
} else {
|
||||
emu_timer_enable(sc->card, ch->timer, 0);
|
||||
@ -973,7 +977,7 @@ emurchan_trigger(kobj_t obj __unused, void *c_devinfo, int go)
|
||||
ch->run = 1;
|
||||
emu_wrptr(sc->card, 0, ch->sizereg, sz);
|
||||
val = sc->is_emu10k1 ? ADCCR_LCHANENABLE : A_ADCCR_LCHANENABLE;
|
||||
if (ch->fmt & AFMT_STEREO)
|
||||
if (AFMT_CHANNEL(ch->fmt) > 1)
|
||||
val |= sc->is_emu10k1 ? ADCCR_RCHANENABLE : A_ADCCR_RCHANENABLE;
|
||||
val |= sc->is_emu10k1 ? emu_k1_recval(ch->spd) : emu_k2_recval(ch->spd);
|
||||
emu_wrptr(sc->card, 0, ch->setupreg, 0);
|
||||
@ -1001,7 +1005,7 @@ emurchan_trigger(kobj_t obj __unused, void *c_devinfo, int go)
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
static uint32_t
|
||||
emurchan_getptr(kobj_t obj __unused, void *c_devinfo)
|
||||
{
|
||||
struct emu_pcm_rchinfo *ch = c_devinfo;
|
||||
@ -1028,7 +1032,7 @@ static kobj_method_t emurchan_methods[] = {
|
||||
KOBJMETHOD(channel_trigger, emurchan_trigger),
|
||||
KOBJMETHOD(channel_getptr, emurchan_getptr),
|
||||
KOBJMETHOD(channel_getcaps, emurchan_getcaps),
|
||||
{0, 0}
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
CHANNEL_DECLARE(emurchan);
|
||||
|
||||
@ -1043,7 +1047,7 @@ emufxrchan_init(kobj_t obj __unused, void *devinfo, struct snd_dbuf *b, struct p
|
||||
if (sc == NULL) return (NULL);
|
||||
|
||||
ch = &(sc->rch_efx);
|
||||
ch->fmt = AFMT_S16_LE;
|
||||
ch->fmt = SND_FORMAT(AFMT_S16_LE, 1, 0);
|
||||
ch->spd = sc->is_emu10k1 ? 48000*32 : 48000 * 64;
|
||||
ch->idxreg = FXIDX;
|
||||
ch->basereg = FXBA;
|
||||
@ -1067,11 +1071,11 @@ emufxrchan_init(kobj_t obj __unused, void *devinfo, struct snd_dbuf *b, struct p
|
||||
static int
|
||||
emufxrchan_setformat(kobj_t obj __unused, void *c_devinfo __unused, uint32_t format)
|
||||
{
|
||||
if (format == AFMT_S16_LE) return (0);
|
||||
if (format == SND_FORMAT(AFMT_S16_LE, 1, 0)) return (0);
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
static int
|
||||
static uint32_t
|
||||
emufxrchan_setspeed(kobj_t obj __unused, void *c_devinfo, uint32_t speed)
|
||||
{
|
||||
struct emu_pcm_rchinfo *ch = c_devinfo;
|
||||
@ -1080,7 +1084,7 @@ emufxrchan_setspeed(kobj_t obj __unused, void *c_devinfo, uint32_t speed)
|
||||
return (ch->spd);
|
||||
}
|
||||
|
||||
static int
|
||||
static uint32_t
|
||||
emufxrchan_setblocksize(kobj_t obj __unused, void *c_devinfo, uint32_t blocksize)
|
||||
{
|
||||
struct emu_pcm_rchinfo *ch = c_devinfo;
|
||||
@ -1171,7 +1175,7 @@ emufxrchan_trigger(kobj_t obj __unused, void *c_devinfo, int go)
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
static uint32_t
|
||||
emufxrchan_getptr(kobj_t obj __unused, void *c_devinfo)
|
||||
{
|
||||
struct emu_pcm_rchinfo *ch = c_devinfo;
|
||||
@ -1218,7 +1222,7 @@ static kobj_method_t emufxrchan_methods[] = {
|
||||
KOBJMETHOD(channel_getptr, emufxrchan_getptr),
|
||||
KOBJMETHOD(channel_getcaps, emufxrchan_getcaps),
|
||||
KOBJMETHOD(channel_getrates, emufxrchan_getrates),
|
||||
{0, 0}
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
CHANNEL_DECLARE(emufxrchan);
|
||||
|
||||
|
@ -45,6 +45,10 @@
|
||||
|
||||
#include <machine/clock.h> /* for DELAY */
|
||||
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/chip.h>
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
#include <dev/sound/pcm/ac97.h>
|
||||
@ -1325,7 +1329,7 @@ emu_vsetup(struct emu_voice *v, int fmt, int spd)
|
||||
{
|
||||
if (fmt) {
|
||||
v->b16 = (fmt & AFMT_16BIT) ? 1 : 0;
|
||||
v->stereo = (fmt & AFMT_STEREO) ? 1 : 0;
|
||||
v->stereo = (AFMT_CHANNEL(fmt) > 1) ? 1 : 0;
|
||||
if (v->slave != NULL) {
|
||||
v->slave->b16 = v->b16;
|
||||
v->slave->stereo = v->stereo;
|
||||
@ -2313,7 +2317,7 @@ emu10kx_dev_init(struct emu_sc_info *sc)
|
||||
mtx_init(&sc->emu10kx_lock, device_get_nameunit(sc->dev), "kxdevlock", 0);
|
||||
unit = device_get_unit(sc->dev);
|
||||
|
||||
sc->cdev = make_dev(&emu10kx_cdevsw, unit, UID_ROOT, GID_WHEEL, 0640, "emu10kx%d", unit);
|
||||
sc->cdev = make_dev(&emu10kx_cdevsw, PCMMINOR(unit), UID_ROOT, GID_WHEEL, 0640, "emu10kx%d", unit);
|
||||
if (sc->cdev != NULL) {
|
||||
sc->cdev->si_drv1 = sc;
|
||||
return (0);
|
||||
@ -3193,7 +3197,11 @@ emu_pci_attach(device_t dev)
|
||||
|
||||
i = 0;
|
||||
sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &i, RF_ACTIVE | RF_SHAREABLE);
|
||||
if ((sc->irq == NULL) || bus_setup_intr(dev, sc->irq, INTR_MPSAFE | INTR_TYPE_AV, NULL, emu_intr, sc, &sc->ih)) {
|
||||
if ((sc->irq == NULL) || bus_setup_intr(dev, sc->irq, INTR_MPSAFE | INTR_TYPE_AV,
|
||||
#if __FreeBSD_version >= 700031
|
||||
NULL,
|
||||
#endif
|
||||
emu_intr, sc, &sc->ih)) {
|
||||
device_printf(dev, "unable to map interrupt\n");
|
||||
goto bad;
|
||||
}
|
||||
|
@ -25,6 +25,10 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
#include <dev/sound/pcm/ac97.h>
|
||||
#include <dev/sound/pci/spicds.h>
|
||||
@ -51,7 +55,7 @@ struct sc_info;
|
||||
|
||||
#define ENVY24_TIMEOUT 1000
|
||||
|
||||
#define ENVY24_DEFAULT_FORMAT (AFMT_STEREO | AFMT_S16_LE)
|
||||
#define ENVY24_DEFAULT_FORMAT SND_FORMAT(AFMT_S16_LE, 2, 0)
|
||||
|
||||
#define ENVY24_NAMELEN 32
|
||||
|
||||
@ -187,10 +191,10 @@ static void envy24_r32sl(struct sc_chinfo *);
|
||||
/* channel interface */
|
||||
static void *envy24chan_init(kobj_t, void *, struct snd_dbuf *, struct pcm_channel *, int);
|
||||
static int envy24chan_setformat(kobj_t, void *, u_int32_t);
|
||||
static int envy24chan_setspeed(kobj_t, void *, u_int32_t);
|
||||
static int envy24chan_setblocksize(kobj_t, void *, u_int32_t);
|
||||
static u_int32_t envy24chan_setspeed(kobj_t, void *, u_int32_t);
|
||||
static u_int32_t envy24chan_setblocksize(kobj_t, void *, u_int32_t);
|
||||
static int envy24chan_trigger(kobj_t, void *, int);
|
||||
static int envy24chan_getptr(kobj_t, void *);
|
||||
static u_int32_t envy24chan_getptr(kobj_t, void *);
|
||||
static struct pcmchan_caps *envy24chan_getcaps(kobj_t, void *);
|
||||
|
||||
/* mixer interface */
|
||||
@ -358,16 +362,16 @@ static struct cfg_info cfg_table[] = {
|
||||
};
|
||||
|
||||
static u_int32_t envy24_recfmt[] = {
|
||||
AFMT_STEREO | AFMT_S16_LE,
|
||||
AFMT_STEREO | AFMT_S32_LE,
|
||||
SND_FORMAT(AFMT_S16_LE, 2, 0),
|
||||
SND_FORMAT(AFMT_S32_LE, 2, 0),
|
||||
0
|
||||
};
|
||||
static struct pcmchan_caps envy24_reccaps = {8000, 96000, envy24_recfmt, 0};
|
||||
|
||||
static u_int32_t envy24_playfmt[] = {
|
||||
AFMT_STEREO | AFMT_U8,
|
||||
AFMT_STEREO | AFMT_S16_LE,
|
||||
AFMT_STEREO | AFMT_S32_LE,
|
||||
SND_FORMAT(AFMT_U8, 2, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 2, 0),
|
||||
SND_FORMAT(AFMT_S32_LE, 2, 0),
|
||||
0
|
||||
};
|
||||
|
||||
@ -380,15 +384,15 @@ struct envy24_emldma {
|
||||
};
|
||||
|
||||
static struct envy24_emldma envy24_pemltab[] = {
|
||||
{AFMT_STEREO | AFMT_U8, envy24_p8u, 2},
|
||||
{AFMT_STEREO | AFMT_S16_LE, envy24_p16sl, 4},
|
||||
{AFMT_STEREO | AFMT_S32_LE, envy24_p32sl, 8},
|
||||
{SND_FORMAT(AFMT_U8, 2, 0), envy24_p8u, 2},
|
||||
{SND_FORMAT(AFMT_S16_LE, 2, 0), envy24_p16sl, 4},
|
||||
{SND_FORMAT(AFMT_S32_LE, 2, 0), envy24_p32sl, 8},
|
||||
{0, NULL, 0}
|
||||
};
|
||||
|
||||
static struct envy24_emldma envy24_remltab[] = {
|
||||
{AFMT_STEREO | AFMT_S16_LE, envy24_r16sl, 4},
|
||||
{AFMT_STEREO | AFMT_S32_LE, envy24_r32sl, 8},
|
||||
{SND_FORMAT(AFMT_S16_LE, 2, 0), envy24_r16sl, 4},
|
||||
{SND_FORMAT(AFMT_S32_LE, 2, 0), envy24_r32sl, 8},
|
||||
{0, NULL, 0}
|
||||
};
|
||||
|
||||
@ -730,7 +734,7 @@ envy24_wrcd(kobj_t obj, void *devinfo, int regno, u_int16_t data)
|
||||
static kobj_method_t envy24_ac97_methods[] = {
|
||||
KOBJMETHOD(ac97_read, envy24_rdcd),
|
||||
KOBJMETHOD(ac97_write, envy24_wrcd),
|
||||
{0, 0}
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
AC97_DECLARE(envy24_ac97);
|
||||
#endif
|
||||
@ -1087,7 +1091,7 @@ static struct {
|
||||
{0, 0x10}
|
||||
};
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
envy24_setspeed(struct sc_info *sc, u_int32_t speed) {
|
||||
u_int32_t code;
|
||||
int i = 0;
|
||||
@ -1691,7 +1695,7 @@ envy24chan_setformat(kobj_t obj, void *data, u_int32_t format)
|
||||
start triggerd, some other channel is running, and that channel's
|
||||
speed isn't same with, then trigger function will fail.
|
||||
*/
|
||||
static int
|
||||
static u_int32_t
|
||||
envy24chan_setspeed(kobj_t obj, void *data, u_int32_t speed)
|
||||
{
|
||||
struct sc_chinfo *ch = data;
|
||||
@ -1716,7 +1720,7 @@ envy24chan_setspeed(kobj_t obj, void *data, u_int32_t speed)
|
||||
return ch->speed;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
envy24chan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
|
||||
{
|
||||
struct sc_chinfo *ch = data;
|
||||
@ -1871,13 +1875,12 @@ envy24chan_trigger(kobj_t obj, void *data, int go)
|
||||
return (error);
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
envy24chan_getptr(kobj_t obj, void *data)
|
||||
{
|
||||
struct sc_chinfo *ch = data;
|
||||
struct sc_info *sc = ch->parent;
|
||||
u_int32_t ptr;
|
||||
int rtn;
|
||||
u_int32_t ptr, rtn;
|
||||
|
||||
#if(0)
|
||||
device_printf(sc->dev, "envy24chan_getptr()\n");
|
||||
@ -1931,7 +1934,7 @@ static kobj_method_t envy24chan_methods[] = {
|
||||
KOBJMETHOD(channel_trigger, envy24chan_trigger),
|
||||
KOBJMETHOD(channel_getptr, envy24chan_getptr),
|
||||
KOBJMETHOD(channel_getcaps, envy24chan_getcaps),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
CHANNEL_DECLARE(envy24chan);
|
||||
|
||||
@ -2051,7 +2054,7 @@ static kobj_method_t envy24mixer_methods[] = {
|
||||
KOBJMETHOD(mixer_uninit, envy24mixer_uninit),
|
||||
KOBJMETHOD(mixer_set, envy24mixer_set),
|
||||
KOBJMETHOD(mixer_setrecsrc, envy24mixer_setrecsrc),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
MIXER_DECLARE(envy24mixer);
|
||||
|
||||
|
@ -37,6 +37,10 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
#include <dev/sound/pcm/ac97.h>
|
||||
#include <dev/sound/pci/spicds.h>
|
||||
@ -63,7 +67,7 @@ struct sc_info;
|
||||
|
||||
#define ENVY24HT_TIMEOUT 1000
|
||||
|
||||
#define ENVY24HT_DEFAULT_FORMAT (AFMT_STEREO | AFMT_S16_LE)
|
||||
#define ENVY24HT_DEFAULT_FORMAT SND_FORMAT(AFMT_S16_LE, 2, 0)
|
||||
|
||||
#define ENVY24HT_NAMELEN 32
|
||||
|
||||
@ -186,10 +190,10 @@ static void envy24ht_r32sl(struct sc_chinfo *);
|
||||
/* channel interface */
|
||||
static void *envy24htchan_init(kobj_t, void *, struct snd_dbuf *, struct pcm_channel *, int);
|
||||
static int envy24htchan_setformat(kobj_t, void *, u_int32_t);
|
||||
static int envy24htchan_setspeed(kobj_t, void *, u_int32_t);
|
||||
static int envy24htchan_setblocksize(kobj_t, void *, u_int32_t);
|
||||
static u_int32_t envy24htchan_setspeed(kobj_t, void *, u_int32_t);
|
||||
static u_int32_t envy24htchan_setblocksize(kobj_t, void *, u_int32_t);
|
||||
static int envy24htchan_trigger(kobj_t, void *, int);
|
||||
static int envy24htchan_getptr(kobj_t, void *);
|
||||
static u_int32_t envy24htchan_getptr(kobj_t, void *);
|
||||
static struct pcmchan_caps *envy24htchan_getcaps(kobj_t, void *);
|
||||
|
||||
/* mixer interface */
|
||||
@ -411,16 +415,16 @@ static struct cfg_info cfg_table[] = {
|
||||
};
|
||||
|
||||
static u_int32_t envy24ht_recfmt[] = {
|
||||
AFMT_STEREO | AFMT_S16_LE,
|
||||
AFMT_STEREO | AFMT_S32_LE,
|
||||
SND_FORMAT(AFMT_S16_LE, 2, 0),
|
||||
SND_FORMAT(AFMT_S32_LE, 2, 0),
|
||||
0
|
||||
};
|
||||
static struct pcmchan_caps envy24ht_reccaps = {8000, 96000, envy24ht_recfmt, 0};
|
||||
|
||||
static u_int32_t envy24ht_playfmt[] = {
|
||||
AFMT_STEREO | AFMT_U8,
|
||||
AFMT_STEREO | AFMT_S16_LE,
|
||||
AFMT_STEREO | AFMT_S32_LE,
|
||||
SND_FORMAT(AFMT_U8, 2, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 2, 0),
|
||||
SND_FORMAT(AFMT_S32_LE, 2, 0),
|
||||
0
|
||||
};
|
||||
|
||||
@ -433,15 +437,15 @@ struct envy24ht_emldma {
|
||||
};
|
||||
|
||||
static struct envy24ht_emldma envy24ht_pemltab[] = {
|
||||
{AFMT_STEREO | AFMT_U8, envy24ht_p8u, 2},
|
||||
{AFMT_STEREO | AFMT_S16_LE, envy24ht_p16sl, 4},
|
||||
{AFMT_STEREO | AFMT_S32_LE, envy24ht_p32sl, 8},
|
||||
{SND_FORMAT(AFMT_U8, 2, 0), envy24ht_p8u, 2},
|
||||
{SND_FORMAT(AFMT_S16_LE, 2, 0), envy24ht_p16sl, 4},
|
||||
{SND_FORMAT(AFMT_S32_LE, 2, 0), envy24ht_p32sl, 8},
|
||||
{0, NULL, 0}
|
||||
};
|
||||
|
||||
static struct envy24ht_emldma envy24ht_remltab[] = {
|
||||
{AFMT_STEREO | AFMT_S16_LE, envy24ht_r16sl, 4},
|
||||
{AFMT_STEREO | AFMT_S32_LE, envy24ht_r32sl, 8},
|
||||
{SND_FORMAT(AFMT_S16_LE, 2, 0), envy24ht_r16sl, 4},
|
||||
{SND_FORMAT(AFMT_S32_LE, 2, 0), envy24ht_r32sl, 8},
|
||||
{0, NULL, 0}
|
||||
};
|
||||
|
||||
@ -809,7 +813,7 @@ envy24ht_wrcd(kobj_t obj, void *devinfo, int regno, u_int16_t data)
|
||||
static kobj_method_t envy24ht_ac97_methods[] = {
|
||||
KOBJMETHOD(ac97_read, envy24ht_rdcd),
|
||||
KOBJMETHOD(ac97_write, envy24ht_wrcd),
|
||||
{0, 0}
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
AC97_DECLARE(envy24ht_ac97);
|
||||
#endif
|
||||
@ -1037,7 +1041,7 @@ static struct {
|
||||
{0, 0x10}
|
||||
};
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
envy24ht_setspeed(struct sc_info *sc, u_int32_t speed) {
|
||||
u_int32_t code, i2sfmt;
|
||||
int i = 0;
|
||||
@ -1602,7 +1606,7 @@ envy24htchan_setformat(kobj_t obj, void *data, u_int32_t format)
|
||||
start triggerd, some other channel is running, and that channel's
|
||||
speed isn't same with, then trigger function will fail.
|
||||
*/
|
||||
static int
|
||||
static u_int32_t
|
||||
envy24htchan_setspeed(kobj_t obj, void *data, u_int32_t speed)
|
||||
{
|
||||
struct sc_chinfo *ch = data;
|
||||
@ -1627,7 +1631,7 @@ envy24htchan_setspeed(kobj_t obj, void *data, u_int32_t speed)
|
||||
return ch->speed;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
envy24htchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
|
||||
{
|
||||
struct sc_chinfo *ch = data;
|
||||
@ -1780,13 +1784,12 @@ envy24htchan_trigger(kobj_t obj, void *data, int go)
|
||||
return (error);
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
envy24htchan_getptr(kobj_t obj, void *data)
|
||||
{
|
||||
struct sc_chinfo *ch = data;
|
||||
struct sc_info *sc = ch->parent;
|
||||
u_int32_t ptr;
|
||||
int rtn;
|
||||
u_int32_t ptr, rtn;
|
||||
|
||||
#if(0)
|
||||
device_printf(sc->dev, "envy24htchan_getptr()\n");
|
||||
@ -1840,7 +1843,7 @@ static kobj_method_t envy24htchan_methods[] = {
|
||||
KOBJMETHOD(channel_trigger, envy24htchan_trigger),
|
||||
KOBJMETHOD(channel_getptr, envy24htchan_getptr),
|
||||
KOBJMETHOD(channel_getcaps, envy24htchan_getcaps),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
CHANNEL_DECLARE(envy24htchan);
|
||||
|
||||
@ -1965,7 +1968,7 @@ static kobj_method_t envy24htmixer_methods[] = {
|
||||
KOBJMETHOD(mixer_uninit, envy24htmixer_uninit),
|
||||
KOBJMETHOD(mixer_set, envy24htmixer_set),
|
||||
KOBJMETHOD(mixer_setrecsrc, envy24htmixer_setrecsrc),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
MIXER_DECLARE(envy24htmixer);
|
||||
|
||||
|
@ -48,6 +48,10 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
#include <dev/sound/pcm/ac97.h>
|
||||
#include <dev/sound/pci/es137x.h>
|
||||
@ -216,10 +220,10 @@ static int es1370_init(struct es_info *);
|
||||
static int es1370_wrcodec(struct es_info *, unsigned char, unsigned char);
|
||||
|
||||
static uint32_t es_fmt[] = {
|
||||
AFMT_U8,
|
||||
AFMT_STEREO | AFMT_U8,
|
||||
AFMT_S16_LE,
|
||||
AFMT_STEREO | AFMT_S16_LE,
|
||||
SND_FORMAT(AFMT_U8, 1, 0),
|
||||
SND_FORMAT(AFMT_U8, 2, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 1, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 2, 0),
|
||||
0
|
||||
};
|
||||
static struct pcmchan_caps es_caps = {4000, 48000, es_fmt, 0};
|
||||
@ -349,7 +353,7 @@ es1370_mixset(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
|
||||
return (l | (r << 8));
|
||||
}
|
||||
|
||||
static int
|
||||
static uint32_t
|
||||
es1370_mixsetrecsrc(struct snd_mixer *m, uint32_t src)
|
||||
{
|
||||
struct es_info *es;
|
||||
@ -380,7 +384,7 @@ static kobj_method_t es1370_mixer_methods[] = {
|
||||
KOBJMETHOD(mixer_init, es1370_mixinit),
|
||||
KOBJMETHOD(mixer_set, es1370_mixset),
|
||||
KOBJMETHOD(mixer_setrecsrc, es1370_mixsetrecsrc),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
MIXER_DECLARE(es1370_mixer);
|
||||
|
||||
@ -513,20 +517,20 @@ eschan_setformat(kobj_t obj, void *data, uint32_t format)
|
||||
es->sctrl &= ~SCTRL_P1FMT;
|
||||
if (format & AFMT_S16_LE)
|
||||
es->sctrl |= SCTRL_P1SEB;
|
||||
if (format & AFMT_STEREO)
|
||||
if (AFMT_CHANNEL(format) > 1)
|
||||
es->sctrl |= SCTRL_P1SMB;
|
||||
} else {
|
||||
es->sctrl &= ~SCTRL_P2FMT;
|
||||
if (format & AFMT_S16_LE)
|
||||
es->sctrl |= SCTRL_P2SEB;
|
||||
if (format & AFMT_STEREO)
|
||||
if (AFMT_CHANNEL(format) > 1)
|
||||
es->sctrl |= SCTRL_P2SMB;
|
||||
}
|
||||
} else {
|
||||
es->sctrl &= ~SCTRL_R1FMT;
|
||||
if (format & AFMT_S16_LE)
|
||||
es->sctrl |= SCTRL_R1SEB;
|
||||
if (format & AFMT_STEREO)
|
||||
if (AFMT_CHANNEL(format) > 1)
|
||||
es->sctrl |= SCTRL_R1SMB;
|
||||
}
|
||||
es_wr(es, ES1370_REG_SERIAL_CONTROL, es->sctrl, 4);
|
||||
@ -535,7 +539,7 @@ eschan_setformat(kobj_t obj, void *data, uint32_t format)
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
static uint32_t
|
||||
eschan1370_setspeed(kobj_t obj, void *data, uint32_t speed)
|
||||
{
|
||||
struct es_chinfo *ch = data;
|
||||
@ -580,7 +584,7 @@ eschan1370_setspeed(kobj_t obj, void *data, uint32_t speed)
|
||||
return (speed);
|
||||
}
|
||||
|
||||
static int
|
||||
static uint32_t
|
||||
eschan1371_setspeed(kobj_t obj, void *data, uint32_t speed)
|
||||
{
|
||||
struct es_chinfo *ch = data;
|
||||
@ -636,10 +640,10 @@ eschan_setfragments(kobj_t obj, void *data, uint32_t blksz, uint32_t blkcnt)
|
||||
ch->blksz = sndbuf_getblksz(ch->buffer);
|
||||
ch->blkcnt = sndbuf_getblkcnt(ch->buffer);
|
||||
|
||||
return (1);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
static uint32_t
|
||||
eschan_setblocksize(kobj_t obj, void *data, uint32_t blksz)
|
||||
{
|
||||
struct es_chinfo *ch = data;
|
||||
@ -733,10 +737,10 @@ eschan_trigger(kobj_t obj, void *data, int go)
|
||||
return 0;
|
||||
|
||||
ES_LOCK(es);
|
||||
cnt = (ch->blksz / sndbuf_getbps(ch->buffer)) - 1;
|
||||
cnt = (ch->blksz / sndbuf_getalign(ch->buffer)) - 1;
|
||||
if (ch->fmt & AFMT_16BIT)
|
||||
b |= 0x02;
|
||||
if (ch->fmt & AFMT_STEREO)
|
||||
if (AFMT_CHANNEL(ch->fmt) > 1)
|
||||
b |= 0x01;
|
||||
if (ch->dir == PCMDIR_PLAY) {
|
||||
if (go == PCMTRIG_START) {
|
||||
@ -820,7 +824,7 @@ eschan_trigger(kobj_t obj, void *data, int go)
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
static uint32_t
|
||||
eschan_getptr(kobj_t obj, void *data)
|
||||
{
|
||||
struct es_chinfo *ch = data;
|
||||
@ -867,7 +871,7 @@ static kobj_method_t eschan1370_methods[] = {
|
||||
KOBJMETHOD(channel_trigger, eschan_trigger),
|
||||
KOBJMETHOD(channel_getptr, eschan_getptr),
|
||||
KOBJMETHOD(channel_getcaps, eschan_getcaps),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
CHANNEL_DECLARE(eschan1370);
|
||||
|
||||
@ -880,7 +884,7 @@ static kobj_method_t eschan1371_methods[] = {
|
||||
KOBJMETHOD(channel_trigger, eschan_trigger),
|
||||
KOBJMETHOD(channel_getptr, eschan_getptr),
|
||||
KOBJMETHOD(channel_getcaps, eschan_getcaps),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
CHANNEL_DECLARE(eschan1371);
|
||||
|
||||
@ -1163,7 +1167,7 @@ es1371_rdcd(kobj_t obj, void *s, int addr)
|
||||
static kobj_method_t es1371_ac97_methods[] = {
|
||||
KOBJMETHOD(ac97_read, es1371_rdcd),
|
||||
KOBJMETHOD(ac97_write, es1371_wrcd),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
AC97_DECLARE(es1371_ac97);
|
||||
|
||||
@ -1362,7 +1366,6 @@ es_pci_probe(device_t dev)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SND_DYNSYSCTL
|
||||
static int
|
||||
sysctl_es137x_spdif_enable(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
@ -1595,12 +1598,10 @@ sysctl_es_polling(SYSCTL_HANDLER_ARGS)
|
||||
|
||||
return (err);
|
||||
}
|
||||
#endif /* SND_DYNSYSCTL */
|
||||
|
||||
static void
|
||||
es_init_sysctls(device_t dev)
|
||||
{
|
||||
#ifdef SND_DYNSYSCTL
|
||||
struct es_info *es;
|
||||
int r, devid, revid;
|
||||
|
||||
@ -1673,7 +1674,6 @@ es_init_sysctls(device_t dev)
|
||||
"polling", CTLTYPE_INT | CTLFLAG_RW, dev, sizeof(dev),
|
||||
sysctl_es_polling, "I",
|
||||
"Enable polling mode");
|
||||
#endif /* SND_DYNSYSCTL */
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -24,6 +24,10 @@
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
#include <dev/sound/pcm/ac97.h>
|
||||
#include <dev/pci/pcireg.h>
|
||||
@ -106,10 +110,10 @@ static int fm801ch_setup(struct pcm_channel *c);
|
||||
*/
|
||||
|
||||
static u_int32_t fmts[] = {
|
||||
AFMT_U8,
|
||||
AFMT_STEREO | AFMT_U8,
|
||||
AFMT_S16_LE,
|
||||
AFMT_STEREO | AFMT_S16_LE,
|
||||
SND_FORMAT(AFMT_U8, 1, 0),
|
||||
SND_FORMAT(AFMT_U8, 2, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 1, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 2, 0),
|
||||
0
|
||||
};
|
||||
|
||||
@ -272,7 +276,7 @@ fm801_wrcd(kobj_t obj, void *devinfo, int regno, u_int32_t data)
|
||||
static kobj_method_t fm801_ac97_methods[] = {
|
||||
KOBJMETHOD(ac97_read, fm801_rdcd),
|
||||
KOBJMETHOD(ac97_write, fm801_wrcd),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
AC97_DECLARE(fm801_ac97);
|
||||
|
||||
@ -346,19 +350,20 @@ fm801ch_setformat(kobj_t obj, void *data, u_int32_t format)
|
||||
struct fm801_info *fm801 = ch->parent;
|
||||
|
||||
DPRINT("fm801ch_setformat 0x%x : %s, %s, %s, %s\n", format,
|
||||
(format & AFMT_STEREO)?"stereo":"mono",
|
||||
(format & (AFMT_S16_LE | AFMT_S16_BE | AFMT_U16_LE | AFMT_U16_BE)) ? "16bit":"8bit",
|
||||
(AFMT_CHANNEL(format) > 1)?"stereo":"mono",
|
||||
(format & AFMT_16BIT) ? "16bit":"8bit",
|
||||
(format & AFMT_SIGNED)? "signed":"unsigned",
|
||||
(format & AFMT_BIGENDIAN)?"bigendiah":"littleendian" );
|
||||
|
||||
if(ch->dir == PCMDIR_PLAY) {
|
||||
fm801->play_fmt = (format & AFMT_STEREO)? FM_PLAY_STEREO : 0;
|
||||
fm801->play_fmt =
|
||||
(AFMT_CHANNEL(format) > 1)? FM_PLAY_STEREO : 0;
|
||||
fm801->play_fmt |= (format & AFMT_16BIT) ? FM_PLAY_16BIT : 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(ch->dir == PCMDIR_REC ) {
|
||||
fm801->rec_fmt = (format & AFMT_STEREO)? FM_REC_STEREO:0;
|
||||
fm801->rec_fmt = (AFMT_CHANNEL(format) > 1)? FM_REC_STEREO:0;
|
||||
fm801->rec_fmt |= (format & AFMT_16BIT) ? FM_PLAY_16BIT : 0;
|
||||
return 0;
|
||||
}
|
||||
@ -367,8 +372,8 @@ fm801ch_setformat(kobj_t obj, void *data, u_int32_t format)
|
||||
}
|
||||
|
||||
struct {
|
||||
int limit;
|
||||
int rate;
|
||||
u_int32_t limit;
|
||||
u_int32_t rate;
|
||||
} fm801_rates[11] = {
|
||||
{ 6600, 5500 },
|
||||
{ 8750, 8000 },
|
||||
@ -384,7 +389,7 @@ struct {
|
||||
/* anything above -> 48000 */
|
||||
};
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
fm801ch_setspeed(kobj_t obj, void *data, u_int32_t speed)
|
||||
{
|
||||
struct fm801_chinfo *ch = data;
|
||||
@ -411,7 +416,7 @@ fm801ch_setspeed(kobj_t obj, void *data, u_int32_t speed)
|
||||
return fm801_rates[i].rate;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
fm801ch_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
|
||||
{
|
||||
struct fm801_chinfo *ch = data;
|
||||
@ -489,12 +494,12 @@ fm801ch_trigger(kobj_t obj, void *data, int go)
|
||||
}
|
||||
|
||||
/* Almost ALSA copy */
|
||||
static int
|
||||
static u_int32_t
|
||||
fm801ch_getptr(kobj_t obj, void *data)
|
||||
{
|
||||
struct fm801_chinfo *ch = data;
|
||||
struct fm801_info *fm801 = ch->parent;
|
||||
int result = 0;
|
||||
u_int32_t result = 0;
|
||||
|
||||
if (ch->dir == PCMDIR_PLAY) {
|
||||
result = fm801_rd(fm801,
|
||||
@ -525,7 +530,7 @@ static kobj_method_t fm801ch_methods[] = {
|
||||
KOBJMETHOD(channel_trigger, fm801ch_trigger),
|
||||
KOBJMETHOD(channel_getptr, fm801ch_getptr),
|
||||
KOBJMETHOD(channel_getcaps, fm801ch_getcaps),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
CHANNEL_DECLARE(fm801ch);
|
||||
|
||||
|
@ -69,6 +69,10 @@
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*/
|
||||
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
#include <dev/pci/pcireg.h>
|
||||
#include <dev/pci/pcivar.h>
|
||||
@ -91,13 +95,13 @@ SND_DECLARE_FILE("$FreeBSD$");
|
||||
if (bootverbose != 0 || snd_verbose > 3) { \
|
||||
stmt \
|
||||
} \
|
||||
} while(0)
|
||||
} while (0)
|
||||
|
||||
#define HDA_BOOTHVERBOSE(stmt) do { \
|
||||
if (snd_verbose > 3) { \
|
||||
stmt \
|
||||
} \
|
||||
} while(0)
|
||||
} while (0)
|
||||
|
||||
#if 1
|
||||
#undef HDAC_INTR_EXTRA
|
||||
@ -463,7 +467,7 @@ const char *HDA_CONNS[4] = {"Jack", "None", "Fixed", "Both"};
|
||||
|
||||
/* Default */
|
||||
static uint32_t hdac_fmt[] = {
|
||||
AFMT_STEREO | AFMT_S16_LE,
|
||||
SND_FORMAT(AFMT_S16_LE, 2, 0),
|
||||
0
|
||||
};
|
||||
|
||||
@ -2905,8 +2909,7 @@ hdac_poll_reinit(struct hdac_softc *sc)
|
||||
continue;
|
||||
ch = &sc->chans[i];
|
||||
pollticks = ((uint64_t)hz * ch->blksz) /
|
||||
((uint64_t)sndbuf_getbps(ch->b) *
|
||||
sndbuf_getspd(ch->b));
|
||||
((uint64_t)sndbuf_getalign(ch->b) * sndbuf_getspd(ch->b));
|
||||
pollticks >>= 1;
|
||||
if (pollticks > hz)
|
||||
pollticks = hz;
|
||||
@ -3370,7 +3373,7 @@ hdac_channel_setformat(kobj_t obj, void *data, uint32_t format)
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
static int
|
||||
static uint32_t
|
||||
hdac_channel_setspeed(kobj_t obj, void *data, uint32_t speed)
|
||||
{
|
||||
struct hdac_chan *ch = data;
|
||||
@ -3426,11 +3429,9 @@ hdac_stream_setup(struct hdac_chan *ch)
|
||||
}
|
||||
}
|
||||
|
||||
if (ch->fmt & (AFMT_STEREO | AFMT_AC3)) {
|
||||
totalchn = AFMT_CHANNEL(ch->fmt);
|
||||
if (totalchn > 1)
|
||||
fmt |= 1;
|
||||
totalchn = 2;
|
||||
} else
|
||||
totalchn = 1;
|
||||
|
||||
HDAC_WRITE_2(&sc->mem, ch->off + HDAC_SDFMT, fmt);
|
||||
|
||||
@ -3515,10 +3516,10 @@ hdac_channel_setfragments(kobj_t obj, void *data,
|
||||
ch->blksz = sndbuf_getblksz(ch->b);
|
||||
ch->blkcnt = sndbuf_getblkcnt(ch->b);
|
||||
|
||||
return (1);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
static uint32_t
|
||||
hdac_channel_setblocksize(kobj_t obj, void *data, uint32_t blksz)
|
||||
{
|
||||
struct hdac_chan *ch = data;
|
||||
@ -3592,7 +3593,7 @@ hdac_channel_trigger(kobj_t obj, void *data, int go)
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
static uint32_t
|
||||
hdac_channel_getptr(kobj_t obj, void *data)
|
||||
{
|
||||
struct hdac_chan *ch = data;
|
||||
@ -3632,7 +3633,7 @@ static kobj_method_t hdac_channel_methods[] = {
|
||||
KOBJMETHOD(channel_trigger, hdac_channel_trigger),
|
||||
KOBJMETHOD(channel_getptr, hdac_channel_getptr),
|
||||
KOBJMETHOD(channel_getcaps, hdac_channel_getcaps),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
CHANNEL_DECLARE(hdac_channel);
|
||||
|
||||
@ -3710,7 +3711,7 @@ hdac_audio_ctl_ossmixer_init(struct snd_mixer *m)
|
||||
}
|
||||
|
||||
/* Declare soft PCM volume if needed. */
|
||||
if (pdevinfo->play >= 0 && !pdevinfo->digital) {
|
||||
if (pdevinfo->play >= 0) {
|
||||
ctl = NULL;
|
||||
if ((mask & SOUND_MASK_PCM) == 0 ||
|
||||
(devinfo->function.audio.quirks & HDA_QUIRK_SOFTPCMVOL)) {
|
||||
@ -3963,7 +3964,7 @@ static kobj_method_t hdac_audio_ctl_ossmixer_methods[] = {
|
||||
KOBJMETHOD(mixer_init, hdac_audio_ctl_ossmixer_init),
|
||||
KOBJMETHOD(mixer_set, hdac_audio_ctl_ossmixer_set),
|
||||
KOBJMETHOD(mixer_setrecsrc, hdac_audio_ctl_ossmixer_setrecsrc),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
MIXER_DECLARE(hdac_audio_ctl_ossmixer);
|
||||
|
||||
@ -6446,17 +6447,20 @@ hdac_pcmchannel_setup(struct hdac_chan *ch)
|
||||
else if (HDA_PARAM_SUPP_PCM_SIZE_RATE_20BIT(pcmcap))
|
||||
ch->bit32 = 2;
|
||||
if (!(devinfo->function.audio.quirks & HDA_QUIRK_FORCESTEREO))
|
||||
ch->fmtlist[i++] = AFMT_S16_LE;
|
||||
ch->fmtlist[i++] = AFMT_S16_LE | AFMT_STEREO;
|
||||
ch->fmtlist[i++] =
|
||||
SND_FORMAT(AFMT_S16_LE, 1, 0);
|
||||
ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 2, 0);
|
||||
if (ch->bit32 > 0) {
|
||||
if (!(devinfo->function.audio.quirks &
|
||||
HDA_QUIRK_FORCESTEREO))
|
||||
ch->fmtlist[i++] = AFMT_S32_LE;
|
||||
ch->fmtlist[i++] = AFMT_S32_LE | AFMT_STEREO;
|
||||
ch->fmtlist[i++] =
|
||||
SND_FORMAT(AFMT_S32_LE, 1, 0);
|
||||
ch->fmtlist[i++] =
|
||||
SND_FORMAT(AFMT_S32_LE, 2, 0);
|
||||
}
|
||||
}
|
||||
if (HDA_PARAM_SUPP_STREAM_FORMATS_AC3(fmtcap)) {
|
||||
ch->fmtlist[i++] = AFMT_AC3;
|
||||
ch->fmtlist[i++] = SND_FORMAT(AFMT_AC3, 2, 0);
|
||||
}
|
||||
ch->fmtlist[i] = 0;
|
||||
i = 0;
|
||||
@ -7181,7 +7185,6 @@ hdac_config_fetch(struct hdac_softc *sc, uint32_t *on, uint32_t *off)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SND_DYNSYSCTL
|
||||
static int
|
||||
sysctl_hdac_polling(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
@ -7409,7 +7412,6 @@ sysctl_hdac_pindump(SYSCTL_HANDLER_ARGS)
|
||||
hdac_unlock(sc);
|
||||
return (0);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
hdac_attach2(void *arg)
|
||||
@ -7657,7 +7659,6 @@ hdac_attach2(void *arg)
|
||||
|
||||
bus_generic_attach(sc->dev);
|
||||
|
||||
#ifdef SND_DYNSYSCTL
|
||||
SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->dev),
|
||||
SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), OID_AUTO,
|
||||
"polling", CTLTYPE_INT | CTLFLAG_RW, sc->dev, sizeof(sc->dev),
|
||||
@ -7671,7 +7672,6 @@ hdac_attach2(void *arg)
|
||||
SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), OID_AUTO,
|
||||
"pindump", CTLTYPE_INT | CTLFLAG_RW, sc->dev, sizeof(sc->dev),
|
||||
sysctl_hdac_pindump, "I", "Dump pin states/data");
|
||||
#endif
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
|
@ -25,6 +25,10 @@
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
#include <dev/sound/pcm/ac97.h>
|
||||
#include <dev/sound/pci/ich.h>
|
||||
@ -80,7 +84,7 @@ SND_DECLARE_FILE("$FreeBSD$");
|
||||
#if 0
|
||||
#define ICH_DEBUG(stmt) do { \
|
||||
stmt \
|
||||
} while(0)
|
||||
} while (0)
|
||||
#else
|
||||
#define ICH_DEBUG(...)
|
||||
#endif
|
||||
@ -198,7 +202,7 @@ struct sc_info {
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
static uint32_t ich_fmt[] = {
|
||||
AFMT_STEREO | AFMT_S16_LE,
|
||||
SND_FORMAT(AFMT_S16_LE, 2, 0),
|
||||
0
|
||||
};
|
||||
static struct pcmchan_caps ich_vrcaps = {8000, 48000, ich_fmt, 0};
|
||||
@ -269,7 +273,7 @@ ich_rdcd(kobj_t obj, void *devinfo, int regno)
|
||||
}
|
||||
|
||||
static int
|
||||
ich_wrcd(kobj_t obj, void *devinfo, int regno, uint16_t data)
|
||||
ich_wrcd(kobj_t obj, void *devinfo, int regno, uint32_t data)
|
||||
{
|
||||
struct sc_info *sc = (struct sc_info *)devinfo;
|
||||
|
||||
@ -283,7 +287,7 @@ ich_wrcd(kobj_t obj, void *devinfo, int regno, uint16_t data)
|
||||
static kobj_method_t ich_ac97_methods[] = {
|
||||
KOBJMETHOD(ac97_read, ich_rdcd),
|
||||
KOBJMETHOD(ac97_write, ich_wrcd),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
AC97_DECLARE(ich_ac97);
|
||||
|
||||
@ -439,7 +443,7 @@ ichchan_setformat(kobj_t obj, void *data, uint32_t format)
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
static uint32_t
|
||||
ichchan_setspeed(kobj_t obj, void *data, uint32_t speed)
|
||||
{
|
||||
struct sc_chinfo *ch = data;
|
||||
@ -473,7 +477,7 @@ ichchan_setspeed(kobj_t obj, void *data, uint32_t speed)
|
||||
return (ch->spd);
|
||||
}
|
||||
|
||||
static int
|
||||
static uint32_t
|
||||
ichchan_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
|
||||
{
|
||||
struct sc_chinfo *ch = data;
|
||||
@ -535,7 +539,7 @@ ichchan_trigger(kobj_t obj, void *data, int go)
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
static uint32_t
|
||||
ichchan_getptr(kobj_t obj, void *data)
|
||||
{
|
||||
struct sc_chinfo *ch = data;
|
||||
@ -583,7 +587,7 @@ static kobj_method_t ichchan_methods[] = {
|
||||
KOBJMETHOD(channel_trigger, ichchan_trigger),
|
||||
KOBJMETHOD(channel_getptr, ichchan_getptr),
|
||||
KOBJMETHOD(channel_getcaps, ichchan_getcaps),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
CHANNEL_DECLARE(ichchan);
|
||||
|
||||
@ -665,7 +669,6 @@ ich_intr(void *p)
|
||||
static int
|
||||
ich_initsys(struct sc_info* sc)
|
||||
{
|
||||
#ifdef SND_DYNSYSCTL
|
||||
/* XXX: this should move to a device specific sysctl "dev.pcm.X.yyy"
|
||||
via device_get_sysctl_*() as discussed on multimedia@ in msg-id
|
||||
<861wujij2q.fsf@xps.des.no> */
|
||||
@ -674,7 +677,7 @@ ich_initsys(struct sc_info* sc)
|
||||
OID_AUTO, "ac97rate", CTLFLAG_RW,
|
||||
&sc->ac97rate, 48000,
|
||||
"AC97 link rate (default = 48000)");
|
||||
#endif /* SND_DYNSYSCTL */
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
@ -47,6 +47,10 @@
|
||||
* John Baldwin <jhb@freebsd.org>.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
#include <dev/sound/pcm/ac97.h>
|
||||
#include <dev/pci/pcireg.h>
|
||||
@ -157,9 +161,7 @@ struct agg_info {
|
||||
bus_dma_tag_t stat_dmat;
|
||||
|
||||
/* FreeBSD SMPng related */
|
||||
#ifdef USING_MUTEX
|
||||
struct mtx lock; /* mutual exclusion */
|
||||
#endif
|
||||
/* FreeBSD newpcm related */
|
||||
struct ac97_info *codec;
|
||||
|
||||
@ -278,11 +280,7 @@ agg_sleep(struct agg_info *sc, const char *wmesg, int msec)
|
||||
timo = msec * hz / 1000;
|
||||
if (timo == 0)
|
||||
timo = 1;
|
||||
#ifdef USING_MUTEX
|
||||
msleep(sc, &sc->lock, PWAIT, wmesg, timo);
|
||||
#else
|
||||
tsleep(sc, PWAIT, wmesg, timo);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@ -1266,7 +1264,7 @@ static kobj_method_t agg_ac97_methods[] = {
|
||||
KOBJMETHOD(ac97_init, agg_ac97_init),
|
||||
KOBJMETHOD(ac97_read, agg_ac97_read),
|
||||
KOBJMETHOD(ac97_write, agg_ac97_write),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
AC97_DECLARE(agg_ac97);
|
||||
|
||||
@ -1370,7 +1368,7 @@ aggpch_setformat(kobj_t obj, void *data, u_int32_t format)
|
||||
if (format & AFMT_BIGENDIAN || format & AFMT_U16_LE)
|
||||
return EINVAL;
|
||||
ch->stereo = ch->qs16 = ch->us = 0;
|
||||
if (format & AFMT_STEREO)
|
||||
if (AFMT_CHANNEL(format) > 1)
|
||||
ch->stereo = 1;
|
||||
|
||||
if (format & AFMT_U8 || format & AFMT_S8) {
|
||||
@ -1381,13 +1379,16 @@ aggpch_setformat(kobj_t obj, void *data, u_int32_t format)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
aggpch_setspeed(kobj_t obj, void *data, u_int32_t speed)
|
||||
{
|
||||
return ((struct agg_chinfo*)data)->speed = speed;
|
||||
|
||||
((struct agg_chinfo*)data)->speed = speed;
|
||||
|
||||
return (speed);
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
aggpch_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
|
||||
{
|
||||
struct agg_chinfo *ch = data;
|
||||
@ -1430,11 +1431,11 @@ aggpch_trigger(kobj_t obj, void *data, int go)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
aggpch_getptr(kobj_t obj, void *data)
|
||||
{
|
||||
struct agg_chinfo *ch = data;
|
||||
u_int cp;
|
||||
u_int32_t cp;
|
||||
|
||||
agg_lock(ch->parent);
|
||||
cp = wp_rdapu(ch->parent, (ch->num << 1) | 32, APUREG_CURPTR);
|
||||
@ -1449,12 +1450,12 @@ static struct pcmchan_caps *
|
||||
aggpch_getcaps(kobj_t obj, void *data)
|
||||
{
|
||||
static u_int32_t playfmt[] = {
|
||||
AFMT_U8,
|
||||
AFMT_STEREO | AFMT_U8,
|
||||
AFMT_S8,
|
||||
AFMT_STEREO | AFMT_S8,
|
||||
AFMT_S16_LE,
|
||||
AFMT_STEREO | AFMT_S16_LE,
|
||||
SND_FORMAT(AFMT_U8, 1, 0),
|
||||
SND_FORMAT(AFMT_U8, 2, 0),
|
||||
SND_FORMAT(AFMT_S8, 1, 0),
|
||||
SND_FORMAT(AFMT_S8, 2, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 1, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 2, 0),
|
||||
0
|
||||
};
|
||||
static struct pcmchan_caps playcaps = {8000, 48000, playfmt, 0};
|
||||
@ -1472,7 +1473,7 @@ static kobj_method_t aggpch_methods[] = {
|
||||
KOBJMETHOD(channel_trigger, aggpch_trigger),
|
||||
KOBJMETHOD(channel_getptr, aggpch_getptr),
|
||||
KOBJMETHOD(channel_getcaps, aggpch_getcaps),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
CHANNEL_DECLARE(aggpch);
|
||||
|
||||
@ -1519,20 +1520,23 @@ aggrch_setformat(kobj_t obj, void *data, u_int32_t format)
|
||||
|
||||
if (!(format & AFMT_S16_LE))
|
||||
return EINVAL;
|
||||
if (format & AFMT_STEREO)
|
||||
if (AFMT_CHANNEL(format) > 1)
|
||||
ch->stereo = 1;
|
||||
else
|
||||
ch->stereo = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
aggrch_setspeed(kobj_t obj, void *data, u_int32_t speed)
|
||||
{
|
||||
return ((struct agg_rchinfo*)data)->speed = speed;
|
||||
|
||||
((struct agg_rchinfo*)data)->speed = speed;
|
||||
|
||||
return (speed);
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
aggrch_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
|
||||
{
|
||||
struct agg_rchinfo *ch = data;
|
||||
@ -1579,7 +1583,7 @@ aggrch_trigger(kobj_t obj, void *sc, int go)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
aggrch_getptr(kobj_t obj, void *sc)
|
||||
{
|
||||
struct agg_rchinfo *ch = sc;
|
||||
@ -1591,8 +1595,8 @@ static struct pcmchan_caps *
|
||||
aggrch_getcaps(kobj_t obj, void *sc)
|
||||
{
|
||||
static u_int32_t recfmt[] = {
|
||||
AFMT_S16_LE,
|
||||
AFMT_STEREO | AFMT_S16_LE,
|
||||
SND_FORMAT(AFMT_S16_LE, 1, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 2, 0),
|
||||
0
|
||||
};
|
||||
static struct pcmchan_caps reccaps = {8000, 48000, recfmt, 0};
|
||||
@ -1609,7 +1613,7 @@ static kobj_method_t aggrch_methods[] = {
|
||||
KOBJMETHOD(channel_trigger, aggrch_trigger),
|
||||
KOBJMETHOD(channel_getptr, aggrch_getptr),
|
||||
KOBJMETHOD(channel_getcaps, aggrch_getcaps),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
CHANNEL_DECLARE(aggrch);
|
||||
|
||||
@ -1776,7 +1780,6 @@ agg_attach(device_t dev)
|
||||
ess = malloc(sizeof(*ess), M_DEVBUF, M_WAITOK | M_ZERO);
|
||||
ess->dev = dev;
|
||||
|
||||
#ifdef USING_MUTEX
|
||||
mtx_init(&ess->lock, device_get_desc(dev), "snd_maestro softc",
|
||||
MTX_DEF | MTX_RECURSE);
|
||||
if (!mtx_initialized(&ess->lock)) {
|
||||
@ -1784,7 +1787,6 @@ agg_attach(device_t dev)
|
||||
ret = ENOMEM;
|
||||
goto bad;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (resource_int_value(device_get_name(dev), device_get_unit(dev),
|
||||
"dac", &dacn) == 0) {
|
||||
@ -1941,10 +1943,8 @@ agg_attach(device_t dev)
|
||||
bus_dma_tag_destroy(ess->stat_dmat);
|
||||
if (ess->buf_dmat != NULL)
|
||||
bus_dma_tag_destroy(ess->buf_dmat);
|
||||
#ifdef USING_MUTEX
|
||||
if (mtx_initialized(&ess->lock))
|
||||
mtx_destroy(&ess->lock);
|
||||
#endif
|
||||
free(ess, M_DEVBUF);
|
||||
}
|
||||
|
||||
@ -1985,9 +1985,7 @@ agg_detach(device_t dev)
|
||||
dma_free(ess->stat_dmat, ess->stat);
|
||||
bus_dma_tag_destroy(ess->stat_dmat);
|
||||
bus_dma_tag_destroy(ess->buf_dmat);
|
||||
#ifdef USING_MUTEX
|
||||
mtx_destroy(&ess->lock);
|
||||
#endif
|
||||
free(ess, M_DEVBUF);
|
||||
return 0;
|
||||
}
|
||||
@ -1996,18 +1994,11 @@ static int
|
||||
agg_suspend(device_t dev)
|
||||
{
|
||||
struct agg_info *ess = pcm_getdevinfo(dev);
|
||||
#ifndef USING_MUTEX
|
||||
int x;
|
||||
|
||||
x = spltty();
|
||||
#endif
|
||||
AGG_WR(ess, PORT_HOSTINT_CTRL, 0, 2);
|
||||
agg_lock(ess);
|
||||
agg_power(ess, PCI_POWERSTATE_D3);
|
||||
agg_unlock(ess);
|
||||
#ifndef USING_MUTEX
|
||||
splx(x);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -2017,11 +2008,7 @@ agg_resume(device_t dev)
|
||||
{
|
||||
int i;
|
||||
struct agg_info *ess = pcm_getdevinfo(dev);
|
||||
#ifndef USING_MUTEX
|
||||
int x;
|
||||
|
||||
x = spltty();
|
||||
#endif
|
||||
for (i = 0; i < ess->playchns; i++)
|
||||
if (ess->active & (1 << i))
|
||||
aggch_start_dac(ess->pch + i);
|
||||
@ -2032,9 +2019,6 @@ agg_resume(device_t dev)
|
||||
if (!ess->active)
|
||||
agg_power(ess, powerstate_init);
|
||||
agg_unlock(ess);
|
||||
#ifndef USING_MUTEX
|
||||
splx(x);
|
||||
#endif
|
||||
|
||||
if (mixer_reinit(dev)) {
|
||||
device_printf(dev, "unable to reinitialize the mixer\n");
|
||||
|
@ -52,6 +52,10 @@
|
||||
* http://virgo.caltech.edu/~dmoore/maestro3.pdf.gz
|
||||
*/
|
||||
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
#include <dev/sound/pcm/ac97.h>
|
||||
|
||||
@ -162,8 +166,8 @@ struct sc_info {
|
||||
static void *m3_pchan_init(kobj_t, void *, struct snd_dbuf *, struct pcm_channel *, int);
|
||||
static int m3_pchan_free(kobj_t, void *);
|
||||
static int m3_pchan_setformat(kobj_t, void *, u_int32_t);
|
||||
static int m3_pchan_setspeed(kobj_t, void *, u_int32_t);
|
||||
static int m3_pchan_setblocksize(kobj_t, void *, u_int32_t);
|
||||
static u_int32_t m3_pchan_setspeed(kobj_t, void *, u_int32_t);
|
||||
static u_int32_t m3_pchan_setblocksize(kobj_t, void *, u_int32_t);
|
||||
static int m3_pchan_trigger(kobj_t, void *, int);
|
||||
static int m3_pchan_trigger_locked(kobj_t, void *, int);
|
||||
static u_int32_t m3_pchan_getptr_internal(struct sc_pchinfo *);
|
||||
@ -174,8 +178,8 @@ static struct pcmchan_caps *m3_pchan_getcaps(kobj_t, void *);
|
||||
static void *m3_rchan_init(kobj_t, void *, struct snd_dbuf *, struct pcm_channel *, int);
|
||||
static int m3_rchan_free(kobj_t, void *);
|
||||
static int m3_rchan_setformat(kobj_t, void *, u_int32_t);
|
||||
static int m3_rchan_setspeed(kobj_t, void *, u_int32_t);
|
||||
static int m3_rchan_setblocksize(kobj_t, void *, u_int32_t);
|
||||
static u_int32_t m3_rchan_setspeed(kobj_t, void *, u_int32_t);
|
||||
static u_int32_t m3_rchan_setblocksize(kobj_t, void *, u_int32_t);
|
||||
static int m3_rchan_trigger(kobj_t, void *, int);
|
||||
static int m3_rchan_trigger_locked(kobj_t, void *, int);
|
||||
static u_int32_t m3_rchan_getptr_internal(struct sc_rchinfo *);
|
||||
@ -185,7 +189,7 @@ static struct pcmchan_caps *m3_rchan_getcaps(kobj_t, void *);
|
||||
static int m3_chan_active(struct sc_info *);
|
||||
|
||||
/* talk to the codec - called from ac97.c */
|
||||
static int m3_initcd(kobj_t, void *);
|
||||
static u_int32_t m3_initcd(kobj_t, void *);
|
||||
static int m3_rdcd(kobj_t, void *, int);
|
||||
static int m3_wrcd(kobj_t, void *, int, u_int32_t);
|
||||
|
||||
@ -206,7 +210,7 @@ static kobj_method_t m3_codec_methods[] = {
|
||||
KOBJMETHOD(ac97_init, m3_initcd),
|
||||
KOBJMETHOD(ac97_read, m3_rdcd),
|
||||
KOBJMETHOD(ac97_write, m3_wrcd),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
AC97_DECLARE(m3_codec);
|
||||
|
||||
@ -214,10 +218,10 @@ AC97_DECLARE(m3_codec);
|
||||
/* channel descriptors */
|
||||
|
||||
static u_int32_t m3_playfmt[] = {
|
||||
AFMT_U8,
|
||||
AFMT_STEREO | AFMT_U8,
|
||||
AFMT_S16_LE,
|
||||
AFMT_STEREO | AFMT_S16_LE,
|
||||
SND_FORMAT(AFMT_U8, 1, 0),
|
||||
SND_FORMAT(AFMT_U8, 2, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 1, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 2, 0),
|
||||
0
|
||||
};
|
||||
static struct pcmchan_caps m3_playcaps = {8000, 48000, m3_playfmt, 0};
|
||||
@ -231,15 +235,15 @@ static kobj_method_t m3_pch_methods[] = {
|
||||
KOBJMETHOD(channel_getptr, m3_pchan_getptr),
|
||||
KOBJMETHOD(channel_getcaps, m3_pchan_getcaps),
|
||||
KOBJMETHOD(channel_free, m3_pchan_free),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
CHANNEL_DECLARE(m3_pch);
|
||||
|
||||
static u_int32_t m3_recfmt[] = {
|
||||
AFMT_U8,
|
||||
AFMT_STEREO | AFMT_U8,
|
||||
AFMT_S16_LE,
|
||||
AFMT_STEREO | AFMT_S16_LE,
|
||||
SND_FORMAT(AFMT_U8, 1, 0),
|
||||
SND_FORMAT(AFMT_U8, 2, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 1, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 2, 0),
|
||||
0
|
||||
};
|
||||
static struct pcmchan_caps m3_reccaps = {8000, 48000, m3_recfmt, 0};
|
||||
@ -253,7 +257,7 @@ static kobj_method_t m3_rch_methods[] = {
|
||||
KOBJMETHOD(channel_getptr, m3_rchan_getptr),
|
||||
KOBJMETHOD(channel_getcaps, m3_rchan_getcaps),
|
||||
KOBJMETHOD(channel_free, m3_rchan_free),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
CHANNEL_DECLARE(m3_rch);
|
||||
|
||||
@ -309,7 +313,7 @@ m3_wait(struct sc_info *sc)
|
||||
/* -------------------------------------------------------------------- */
|
||||
/* ac97 codec */
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
m3_initcd(kobj_t kobj, void *devinfo)
|
||||
{
|
||||
struct sc_info *sc = (struct sc_info *)devinfo;
|
||||
@ -404,7 +408,7 @@ m3_pchan_init(kobj_t kobj, void *devinfo, struct snd_dbuf *b, struct pcm_channel
|
||||
ch->buffer = b;
|
||||
ch->parent = sc;
|
||||
ch->channel = c;
|
||||
ch->fmt = AFMT_U8;
|
||||
ch->fmt = SND_FORMAT(AFMT_U8, 1, 0);
|
||||
ch->spd = DSP_DEFAULT_SPEED;
|
||||
M3_UNLOCK(sc); /* XXX */
|
||||
if (sndbuf_alloc(ch->buffer, sc->parent_dmat, 0, sc->bufsz) != 0) {
|
||||
@ -516,10 +520,10 @@ m3_pchan_setformat(kobj_t kobj, void *chdata, u_int32_t format)
|
||||
("m3_pchan_setformat(dac=%d, format=0x%x{%s-%s})\n",
|
||||
ch->dac_idx, format,
|
||||
format & (AFMT_U8|AFMT_S8) ? "8bit":"16bit",
|
||||
format & AFMT_STEREO ? "STEREO":"MONO"));
|
||||
(AFMT_CHANNEL(format) > 1) ? "STEREO":"MONO"));
|
||||
|
||||
/* mono word */
|
||||
data = (format & AFMT_STEREO) ? 0 : 1;
|
||||
data = (AFMT_CHANNEL(format) > 1)? 0 : 1;
|
||||
m3_wr_assp_data(sc, ch->dac_data + SRC3_MODE_OFFSET, data);
|
||||
|
||||
/* 8bit word */
|
||||
@ -532,7 +536,7 @@ m3_pchan_setformat(kobj_t kobj, void *chdata, u_int32_t format)
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
m3_pchan_setspeed(kobj_t kobj, void *chdata, u_int32_t speed)
|
||||
{
|
||||
struct sc_pchinfo *ch = chdata;
|
||||
@ -555,7 +559,7 @@ m3_pchan_setspeed(kobj_t kobj, void *chdata, u_int32_t speed)
|
||||
return (speed);
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
m3_pchan_setblocksize(kobj_t kobj, void *chdata, u_int32_t blocksize)
|
||||
{
|
||||
struct sc_pchinfo *ch = chdata;
|
||||
@ -756,7 +760,7 @@ m3_rchan_init(kobj_t kobj, void *devinfo, struct snd_dbuf *b, struct pcm_channel
|
||||
ch->buffer = b;
|
||||
ch->parent = sc;
|
||||
ch->channel = c;
|
||||
ch->fmt = AFMT_U8;
|
||||
ch->fmt = SND_FORMAT(AFMT_U8, 1, 0);
|
||||
ch->spd = DSP_DEFAULT_SPEED;
|
||||
M3_UNLOCK(sc); /* XXX */
|
||||
if (sndbuf_alloc(ch->buffer, sc->parent_dmat, 0, sc->bufsz) != 0) {
|
||||
@ -863,10 +867,10 @@ m3_rchan_setformat(kobj_t kobj, void *chdata, u_int32_t format)
|
||||
("m3_rchan_setformat(dac=%d, format=0x%x{%s-%s})\n",
|
||||
ch->adc_idx, format,
|
||||
format & (AFMT_U8|AFMT_S8) ? "8bit":"16bit",
|
||||
format & AFMT_STEREO ? "STEREO":"MONO"));
|
||||
(AFMT_CHANNEL(format) > 1) ? "STEREO":"MONO"));
|
||||
|
||||
/* mono word */
|
||||
data = (format & AFMT_STEREO) ? 0 : 1;
|
||||
data = (AFMT_CHANNEL(format) > 1) ? 0 : 1;
|
||||
m3_wr_assp_data(sc, ch->adc_data + SRC3_MODE_OFFSET, data);
|
||||
|
||||
/* 8bit word */
|
||||
@ -878,7 +882,7 @@ m3_rchan_setformat(kobj_t kobj, void *chdata, u_int32_t format)
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
m3_rchan_setspeed(kobj_t kobj, void *chdata, u_int32_t speed)
|
||||
{
|
||||
struct sc_rchinfo *ch = chdata;
|
||||
@ -901,7 +905,7 @@ m3_rchan_setspeed(kobj_t kobj, void *chdata, u_int32_t speed)
|
||||
return (speed);
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
m3_rchan_setblocksize(kobj_t kobj, void *chdata, u_int32_t blocksize)
|
||||
{
|
||||
struct sc_rchinfo *ch = chdata;
|
||||
|
@ -26,6 +26,10 @@
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
#include <dev/sound/pcm/ac97.h>
|
||||
#include <dev/sound/pci/neomagic.h>
|
||||
@ -113,10 +117,10 @@ static int samplerates[9] = {
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
static u_int32_t nm_fmt[] = {
|
||||
AFMT_U8,
|
||||
AFMT_STEREO | AFMT_U8,
|
||||
AFMT_S16_LE,
|
||||
AFMT_STEREO | AFMT_S16_LE,
|
||||
SND_FORMAT(AFMT_U8, 1, 0),
|
||||
SND_FORMAT(AFMT_U8, 2, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 1, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 2, 0),
|
||||
0
|
||||
};
|
||||
static struct pcmchan_caps nm_caps = {4000, 48000, nm_fmt, 0};
|
||||
@ -277,7 +281,7 @@ static kobj_method_t nm_ac97_methods[] = {
|
||||
KOBJMETHOD(ac97_init, nm_initcd),
|
||||
KOBJMETHOD(ac97_read, nm_rdcd),
|
||||
KOBJMETHOD(ac97_write, nm_wrcd),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
AC97_DECLARE(nm_ac97);
|
||||
|
||||
@ -334,7 +338,7 @@ nm_setch(struct sc_chinfo *ch)
|
||||
x <<= 4;
|
||||
x &= NM_RATE_MASK;
|
||||
if (ch->fmt & AFMT_16BIT) x |= NM_RATE_BITS_16;
|
||||
if (ch->fmt & AFMT_STEREO) x |= NM_RATE_STEREO;
|
||||
if (AFMT_CHANNEL(ch->fmt) > 1) x |= NM_RATE_STEREO;
|
||||
|
||||
base = (ch->dir == PCMDIR_PLAY)? NM_PLAYBACK_REG_OFFSET : NM_RECORD_REG_OFFSET;
|
||||
nm_wr(sc, base + NM_RATE_REG_OFFSET, x, 1);
|
||||
@ -380,7 +384,7 @@ nmchan_setformat(kobj_t obj, void *data, u_int32_t format)
|
||||
return nm_setch(ch);
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
nmchan_setspeed(kobj_t obj, void *data, u_int32_t speed)
|
||||
{
|
||||
struct sc_chinfo *ch = data;
|
||||
@ -389,7 +393,7 @@ nmchan_setspeed(kobj_t obj, void *data, u_int32_t speed)
|
||||
return nm_setch(ch)? 0 : ch->spd;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
nmchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
|
||||
{
|
||||
struct sc_chinfo *ch = data;
|
||||
@ -410,7 +414,7 @@ nmchan_trigger(kobj_t obj, void *data, int go)
|
||||
return 0;
|
||||
|
||||
ssz = (ch->fmt & AFMT_16BIT)? 2 : 1;
|
||||
if (ch->fmt & AFMT_STEREO)
|
||||
if (AFMT_CHANNEL(ch->fmt) > 1)
|
||||
ssz <<= 1;
|
||||
|
||||
if (ch->dir == PCMDIR_PLAY) {
|
||||
@ -447,7 +451,7 @@ nmchan_trigger(kobj_t obj, void *data, int go)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
nmchan_getptr(kobj_t obj, void *data)
|
||||
{
|
||||
struct sc_chinfo *ch = data;
|
||||
@ -474,7 +478,7 @@ static kobj_method_t nmchan_methods[] = {
|
||||
KOBJMETHOD(channel_trigger, nmchan_trigger),
|
||||
KOBJMETHOD(channel_getptr, nmchan_getptr),
|
||||
KOBJMETHOD(channel_getcaps, nmchan_getcaps),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
CHANNEL_DECLARE(nmchan);
|
||||
|
||||
|
@ -23,6 +23,10 @@
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
|
||||
#include <dev/pci/pcireg.h>
|
||||
@ -48,14 +52,14 @@ SND_DECLARE_FILE("$FreeBSD$");
|
||||
#define ESS18XX_MPSAFE 1
|
||||
|
||||
static u_int32_t ess_playfmt[] = {
|
||||
AFMT_U8,
|
||||
AFMT_STEREO | AFMT_U8,
|
||||
AFMT_S8,
|
||||
AFMT_STEREO | AFMT_S8,
|
||||
AFMT_S16_LE,
|
||||
AFMT_STEREO | AFMT_S16_LE,
|
||||
AFMT_U16_LE,
|
||||
AFMT_STEREO | AFMT_U16_LE,
|
||||
SND_FORMAT(AFMT_U8, 1, 0),
|
||||
SND_FORMAT(AFMT_U8, 2, 0),
|
||||
SND_FORMAT(AFMT_S8, 1, 0),
|
||||
SND_FORMAT(AFMT_S8, 2, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 1, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 2, 0),
|
||||
SND_FORMAT(AFMT_U16_LE, 1, 0),
|
||||
SND_FORMAT(AFMT_U16_LE, 2, 0),
|
||||
0
|
||||
};
|
||||
static struct pcmchan_caps ess_playcaps = {6000, 48000, ess_playfmt, 0};
|
||||
@ -64,14 +68,14 @@ static struct pcmchan_caps ess_playcaps = {6000, 48000, ess_playfmt, 0};
|
||||
* Recording output is byte-swapped
|
||||
*/
|
||||
static u_int32_t ess_recfmt[] = {
|
||||
AFMT_U8,
|
||||
AFMT_STEREO | AFMT_U8,
|
||||
AFMT_S8,
|
||||
AFMT_STEREO | AFMT_S8,
|
||||
AFMT_S16_BE,
|
||||
AFMT_STEREO | AFMT_S16_BE,
|
||||
AFMT_U16_BE,
|
||||
AFMT_STEREO | AFMT_U16_BE,
|
||||
SND_FORMAT(AFMT_U8, 1, 0),
|
||||
SND_FORMAT(AFMT_U8, 2, 0),
|
||||
SND_FORMAT(AFMT_S8, 1, 0),
|
||||
SND_FORMAT(AFMT_S8, 2, 0),
|
||||
SND_FORMAT(AFMT_S16_BE, 1, 0),
|
||||
SND_FORMAT(AFMT_S16_BE, 2, 0),
|
||||
SND_FORMAT(AFMT_U16_BE, 1, 0),
|
||||
SND_FORMAT(AFMT_U16_BE, 2, 0),
|
||||
0
|
||||
};
|
||||
static struct pcmchan_caps ess_reccaps = {6000, 48000, ess_recfmt, 0};
|
||||
@ -424,8 +428,8 @@ ess_setupch(struct ess_info *sc, int ch, int dir, int spd, u_int32_t fmt, int le
|
||||
{
|
||||
int play = (dir == PCMDIR_PLAY)? 1 : 0;
|
||||
int b16 = (fmt & AFMT_16BIT)? 1 : 0;
|
||||
int stereo = (fmt & AFMT_STEREO)? 1 : 0;
|
||||
int unsign = (fmt == AFMT_U8 || fmt == AFMT_U16_LE || fmt == AFMT_U16_BE)? 1 : 0;
|
||||
int stereo = (AFMT_CHANNEL(fmt) > 1)? 1 : 0;
|
||||
int unsign = (!(fmt & AFMT_SIGNED))? 1 : 0;
|
||||
u_int8_t spdval, fmtval;
|
||||
|
||||
DEB(printf("ess_setupch\n"));
|
||||
@ -556,7 +560,7 @@ esschan_setformat(kobj_t obj, void *data, u_int32_t format)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
esschan_setspeed(kobj_t obj, void *data, u_int32_t speed)
|
||||
{
|
||||
struct ess_chinfo *ch = data;
|
||||
@ -570,7 +574,7 @@ esschan_setspeed(kobj_t obj, void *data, u_int32_t speed)
|
||||
return ch->spd;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
esschan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
|
||||
{
|
||||
struct ess_chinfo *ch = data;
|
||||
@ -608,12 +612,12 @@ esschan_trigger(kobj_t obj, void *data, int go)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
esschan_getptr(kobj_t obj, void *data)
|
||||
{
|
||||
struct ess_chinfo *ch = data;
|
||||
struct ess_info *sc = ch->parent;
|
||||
int ret;
|
||||
u_int32_t ret;
|
||||
|
||||
ess_lock(sc);
|
||||
ret = ess_dmapos(sc, ch->hwch);
|
||||
@ -637,7 +641,7 @@ static kobj_method_t esschan_methods[] = {
|
||||
KOBJMETHOD(channel_trigger, esschan_trigger),
|
||||
KOBJMETHOD(channel_getptr, esschan_getptr),
|
||||
KOBJMETHOD(channel_getcaps, esschan_getcaps),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
CHANNEL_DECLARE(esschan);
|
||||
|
||||
@ -720,7 +724,7 @@ essmix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
|
||||
return left | (right << 8);
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
essmix_setrecsrc(struct snd_mixer *m, u_int32_t src)
|
||||
{
|
||||
struct ess_info *sc = mix_getdevinfo(m);
|
||||
@ -755,7 +759,7 @@ static kobj_method_t solomixer_methods[] = {
|
||||
KOBJMETHOD(mixer_init, essmix_init),
|
||||
KOBJMETHOD(mixer_set, essmix_set),
|
||||
KOBJMETHOD(mixer_setrecsrc, essmix_setrecsrc),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
MIXER_DECLARE(solomixer);
|
||||
|
||||
|
@ -27,6 +27,10 @@
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
|
||||
#include <dev/sound/pci/spicds.h>
|
||||
|
@ -24,6 +24,10 @@
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
#include <dev/sound/pcm/ac97.h>
|
||||
#include <dev/sound/pci/t4dwave.h>
|
||||
@ -103,27 +107,27 @@ struct tr_info {
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
static u_int32_t tr_recfmt[] = {
|
||||
AFMT_U8,
|
||||
AFMT_STEREO | AFMT_U8,
|
||||
AFMT_S8,
|
||||
AFMT_STEREO | AFMT_S8,
|
||||
AFMT_S16_LE,
|
||||
AFMT_STEREO | AFMT_S16_LE,
|
||||
AFMT_U16_LE,
|
||||
AFMT_STEREO | AFMT_U16_LE,
|
||||
SND_FORMAT(AFMT_U8, 1, 0),
|
||||
SND_FORMAT(AFMT_U8, 2, 0),
|
||||
SND_FORMAT(AFMT_S8, 1, 0),
|
||||
SND_FORMAT(AFMT_S8, 2, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 1, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 2, 0),
|
||||
SND_FORMAT(AFMT_U16_LE, 1, 0),
|
||||
SND_FORMAT(AFMT_U16_LE, 2, 0),
|
||||
0
|
||||
};
|
||||
static struct pcmchan_caps tr_reccaps = {4000, 48000, tr_recfmt, 0};
|
||||
|
||||
static u_int32_t tr_playfmt[] = {
|
||||
AFMT_U8,
|
||||
AFMT_STEREO | AFMT_U8,
|
||||
AFMT_S8,
|
||||
AFMT_STEREO | AFMT_S8,
|
||||
AFMT_S16_LE,
|
||||
AFMT_STEREO | AFMT_S16_LE,
|
||||
AFMT_U16_LE,
|
||||
AFMT_STEREO | AFMT_U16_LE,
|
||||
SND_FORMAT(AFMT_U8, 1, 0),
|
||||
SND_FORMAT(AFMT_U8, 2, 0),
|
||||
SND_FORMAT(AFMT_S8, 1, 0),
|
||||
SND_FORMAT(AFMT_S8, 2, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 1, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 2, 0),
|
||||
SND_FORMAT(AFMT_U16_LE, 1, 0),
|
||||
SND_FORMAT(AFMT_U16_LE, 2, 0),
|
||||
0
|
||||
};
|
||||
static struct pcmchan_caps tr_playcaps = {4000, 48000, tr_playfmt, 0};
|
||||
@ -289,7 +293,7 @@ tr_wrcd(kobj_t obj, void *devinfo, int regno, u_int32_t data)
|
||||
static kobj_method_t tr_ac97_methods[] = {
|
||||
KOBJMETHOD(ac97_read, tr_rdcd),
|
||||
KOBJMETHOD(ac97_write, tr_wrcd),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
AC97_DECLARE(tr_ac97);
|
||||
|
||||
@ -473,7 +477,7 @@ tr_fmttobits(u_int32_t fmt)
|
||||
|
||||
bits = 0;
|
||||
bits |= (fmt & AFMT_SIGNED)? 0x2 : 0;
|
||||
bits |= (fmt & AFMT_STEREO)? 0x4 : 0;
|
||||
bits |= (AFMT_CHANNEL(fmt) > 1)? 0x4 : 0;
|
||||
bits |= (fmt & AFMT_16BIT)? 0x8 : 0;
|
||||
|
||||
return bits;
|
||||
@ -510,7 +514,7 @@ trpchan_setformat(kobj_t obj, void *data, u_int32_t format)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
trpchan_setspeed(kobj_t obj, void *data, u_int32_t speed)
|
||||
{
|
||||
struct tr_chinfo *ch = data;
|
||||
@ -519,7 +523,7 @@ trpchan_setspeed(kobj_t obj, void *data, u_int32_t speed)
|
||||
return (ch->delta * 48000) >> 12;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
trpchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
|
||||
{
|
||||
struct tr_chinfo *ch = data;
|
||||
@ -543,7 +547,7 @@ trpchan_trigger(kobj_t obj, void *data, int go)
|
||||
ch->alpha = 0;
|
||||
ch->lba = sndbuf_getbufaddr(ch->buffer);
|
||||
ch->cso = 0;
|
||||
ch->eso = (sndbuf_getsize(ch->buffer) / sndbuf_getbps(ch->buffer)) - 1;
|
||||
ch->eso = (sndbuf_getsize(ch->buffer) / sndbuf_getalign(ch->buffer)) - 1;
|
||||
ch->rvol = ch->cvol = 0x7f;
|
||||
ch->gvsel = 0;
|
||||
ch->pan = 0;
|
||||
@ -561,13 +565,13 @@ trpchan_trigger(kobj_t obj, void *data, int go)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
trpchan_getptr(kobj_t obj, void *data)
|
||||
{
|
||||
struct tr_chinfo *ch = data;
|
||||
|
||||
tr_rdch(ch);
|
||||
return ch->cso * sndbuf_getbps(ch->buffer);
|
||||
return ch->cso * sndbuf_getalign(ch->buffer);
|
||||
}
|
||||
|
||||
static struct pcmchan_caps *
|
||||
@ -584,7 +588,7 @@ static kobj_method_t trpchan_methods[] = {
|
||||
KOBJMETHOD(channel_trigger, trpchan_trigger),
|
||||
KOBJMETHOD(channel_getptr, trpchan_getptr),
|
||||
KOBJMETHOD(channel_getcaps, trpchan_getcaps),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
CHANNEL_DECLARE(trpchan);
|
||||
|
||||
@ -627,7 +631,7 @@ trrchan_setformat(kobj_t obj, void *data, u_int32_t format)
|
||||
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
trrchan_setspeed(kobj_t obj, void *data, u_int32_t speed)
|
||||
{
|
||||
struct tr_rchinfo *ch = data;
|
||||
@ -641,7 +645,7 @@ trrchan_setspeed(kobj_t obj, void *data, u_int32_t speed)
|
||||
return (48000 << 12) / ch->delta;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
trrchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
|
||||
{
|
||||
struct tr_rchinfo *ch = data;
|
||||
@ -683,7 +687,7 @@ trrchan_trigger(kobj_t obj, void *data, int go)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
trrchan_getptr(kobj_t obj, void *data)
|
||||
{
|
||||
struct tr_rchinfo *ch = data;
|
||||
@ -707,7 +711,7 @@ static kobj_method_t trrchan_methods[] = {
|
||||
KOBJMETHOD(channel_trigger, trrchan_trigger),
|
||||
KOBJMETHOD(channel_getptr, trrchan_getptr),
|
||||
KOBJMETHOD(channel_getcaps, trrchan_getcaps),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
CHANNEL_DECLARE(trrchan);
|
||||
|
||||
|
@ -35,6 +35,10 @@
|
||||
* ordering.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
#include <dev/sound/pcm/ac97.h>
|
||||
|
||||
@ -123,10 +127,10 @@ struct via_info {
|
||||
};
|
||||
|
||||
static uint32_t via_fmt[] = {
|
||||
AFMT_U8,
|
||||
AFMT_STEREO | AFMT_U8,
|
||||
AFMT_S16_LE,
|
||||
AFMT_STEREO | AFMT_S16_LE,
|
||||
SND_FORMAT(AFMT_U8, 1, 0),
|
||||
SND_FORMAT(AFMT_U8, 2, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 1, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 2, 0),
|
||||
0
|
||||
};
|
||||
|
||||
@ -150,7 +154,6 @@ via_chan_active(struct via_info *via)
|
||||
return (ret);
|
||||
}
|
||||
|
||||
#ifdef SND_DYNSYSCTL
|
||||
static int
|
||||
sysctl_via8233_spdif_enable(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
@ -243,12 +246,10 @@ sysctl_via_polling(SYSCTL_HANDLER_ARGS)
|
||||
|
||||
return (err);
|
||||
}
|
||||
#endif /* SND_DYNSYSCTL */
|
||||
|
||||
static void
|
||||
via_init_sysctls(device_t dev)
|
||||
{
|
||||
#ifdef SND_DYNSYSCTL
|
||||
/* XXX: an user should be able to set this with a control tool,
|
||||
if not done before 7.0-RELEASE, this needs to be converted to
|
||||
a device specific sysctl "dev.pcm.X.yyy" via device_get_sysctl_*()
|
||||
@ -268,7 +269,6 @@ via_init_sysctls(device_t dev)
|
||||
"polling", CTLTYPE_INT | CTLFLAG_RW, dev, sizeof(dev),
|
||||
sysctl_via_polling, "I",
|
||||
"Enable polling mode");
|
||||
#endif
|
||||
}
|
||||
|
||||
static __inline uint32_t
|
||||
@ -374,7 +374,7 @@ via_read_codec(kobj_t obj, void *addr, int reg)
|
||||
static kobj_method_t via_ac97_methods[] = {
|
||||
KOBJMETHOD(ac97_read, via_read_codec),
|
||||
KOBJMETHOD(ac97_write, via_write_codec),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
AC97_DECLARE(via_ac97);
|
||||
|
||||
@ -408,7 +408,7 @@ via8233wr_setformat(kobj_t obj, void *data, uint32_t format)
|
||||
|
||||
uint32_t f = WR_FORMAT_STOP_INDEX;
|
||||
|
||||
if (format & AFMT_STEREO)
|
||||
if (AFMT_CHANNEL(format) > 1)
|
||||
f |= WR_FORMAT_STEREO;
|
||||
if (format & AFMT_S16_LE)
|
||||
f |= WR_FORMAT_16BIT;
|
||||
@ -431,7 +431,7 @@ via8233dxs_setformat(kobj_t obj, void *data, uint32_t format)
|
||||
v = via_rd(via, r, 4);
|
||||
|
||||
v &= ~(VIA8233_DXS_RATEFMT_STEREO | VIA8233_DXS_RATEFMT_16BIT);
|
||||
if (format & AFMT_STEREO)
|
||||
if (AFMT_CHANNEL(format) > 1)
|
||||
v |= VIA8233_DXS_RATEFMT_STEREO;
|
||||
if (format & AFMT_16BIT)
|
||||
v |= VIA8233_DXS_RATEFMT_16BIT;
|
||||
@ -450,7 +450,7 @@ via8233msgd_setformat(kobj_t obj, void *data, uint32_t format)
|
||||
uint32_t s = 0xff000000;
|
||||
uint8_t v = (format & AFMT_S16_LE) ? MC_SGD_16BIT : MC_SGD_8BIT;
|
||||
|
||||
if (format & AFMT_STEREO) {
|
||||
if (AFMT_CHANNEL(format) > 1) {
|
||||
v |= MC_SGD_CHANNELS(2);
|
||||
s |= SLOT3(1) | SLOT4(2);
|
||||
} else {
|
||||
@ -469,7 +469,7 @@ via8233msgd_setformat(kobj_t obj, void *data, uint32_t format)
|
||||
/* -------------------------------------------------------------------- */
|
||||
/* Speed setting functions */
|
||||
|
||||
static int
|
||||
static uint32_t
|
||||
via8233wr_setspeed(kobj_t obj, void *data, uint32_t speed)
|
||||
{
|
||||
struct via_chinfo *ch = data;
|
||||
@ -481,7 +481,7 @@ via8233wr_setspeed(kobj_t obj, void *data, uint32_t speed)
|
||||
return (48000);
|
||||
}
|
||||
|
||||
static int
|
||||
static uint32_t
|
||||
via8233dxs_setspeed(kobj_t obj, void *data, uint32_t speed)
|
||||
{
|
||||
struct via_chinfo *ch = data;
|
||||
@ -501,7 +501,7 @@ via8233dxs_setspeed(kobj_t obj, void *data, uint32_t speed)
|
||||
return (speed);
|
||||
}
|
||||
|
||||
static int
|
||||
static uint32_t
|
||||
via8233msgd_setspeed(kobj_t obj, void *data, uint32_t speed)
|
||||
{
|
||||
struct via_chinfo *ch = data;
|
||||
@ -596,10 +596,10 @@ via8233chan_setfragments(kobj_t obj, void *data,
|
||||
ch->blksz = sndbuf_getblksz(ch->buffer);
|
||||
ch->blkcnt = sndbuf_getblkcnt(ch->buffer);
|
||||
|
||||
return (1);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
static uint32_t
|
||||
via8233chan_setblocksize(kobj_t obj, void *data, uint32_t blksz)
|
||||
{
|
||||
struct via_chinfo *ch = data;
|
||||
@ -610,13 +610,12 @@ via8233chan_setblocksize(kobj_t obj, void *data, uint32_t blksz)
|
||||
return (ch->blksz);
|
||||
}
|
||||
|
||||
static int
|
||||
static uint32_t
|
||||
via8233chan_getptr(kobj_t obj, void *data)
|
||||
{
|
||||
struct via_chinfo *ch = data;
|
||||
struct via_info *via = ch->parent;
|
||||
uint32_t v, index, count;
|
||||
int ptr;
|
||||
uint32_t v, index, count, ptr;
|
||||
|
||||
snd_mtxlock(via->lock);
|
||||
if (via->polling != 0) {
|
||||
@ -852,7 +851,7 @@ via_poll_ticks(struct via_info *via)
|
||||
if (ch->channel == NULL || ch->active == 0)
|
||||
continue;
|
||||
pollticks = ((uint64_t)hz * ch->blksz) /
|
||||
((uint64_t)sndbuf_getbps(ch->buffer) *
|
||||
((uint64_t)sndbuf_getalign(ch->buffer) *
|
||||
sndbuf_getspd(ch->buffer));
|
||||
pollticks >>= 2;
|
||||
if (pollticks > hz)
|
||||
@ -868,7 +867,7 @@ via_poll_ticks(struct via_info *via)
|
||||
if (ch->channel == NULL || ch->active == 0)
|
||||
continue;
|
||||
pollticks = ((uint64_t)hz * ch->blksz) /
|
||||
((uint64_t)sndbuf_getbps(ch->buffer) *
|
||||
((uint64_t)sndbuf_getalign(ch->buffer) *
|
||||
sndbuf_getspd(ch->buffer));
|
||||
pollticks >>= 2;
|
||||
if (pollticks > hz)
|
||||
@ -902,7 +901,7 @@ via8233chan_trigger(kobj_t obj, void* data, int go)
|
||||
ch->ptr = 0;
|
||||
ch->prevptr = 0;
|
||||
pollticks = ((uint64_t)hz * ch->blksz) /
|
||||
((uint64_t)sndbuf_getbps(ch->buffer) *
|
||||
((uint64_t)sndbuf_getalign(ch->buffer) *
|
||||
sndbuf_getspd(ch->buffer));
|
||||
pollticks >>= 2;
|
||||
if (pollticks > hz)
|
||||
@ -974,7 +973,7 @@ static kobj_method_t via8233wr_methods[] = {
|
||||
KOBJMETHOD(channel_setfragments, via8233chan_setfragments),
|
||||
KOBJMETHOD(channel_trigger, via8233chan_trigger),
|
||||
KOBJMETHOD(channel_getptr, via8233chan_getptr),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
CHANNEL_DECLARE(via8233wr);
|
||||
|
||||
@ -987,7 +986,7 @@ static kobj_method_t via8233dxs_methods[] = {
|
||||
KOBJMETHOD(channel_setfragments, via8233chan_setfragments),
|
||||
KOBJMETHOD(channel_trigger, via8233chan_trigger),
|
||||
KOBJMETHOD(channel_getptr, via8233chan_getptr),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
CHANNEL_DECLARE(via8233dxs);
|
||||
|
||||
@ -1000,7 +999,7 @@ static kobj_method_t via8233msgd_methods[] = {
|
||||
KOBJMETHOD(channel_setfragments, via8233chan_setfragments),
|
||||
KOBJMETHOD(channel_trigger, via8233chan_trigger),
|
||||
KOBJMETHOD(channel_getptr, via8233chan_getptr),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
CHANNEL_DECLARE(via8233msgd);
|
||||
|
||||
|
@ -24,6 +24,10 @@
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
#include <dev/sound/pcm/ac97.h>
|
||||
|
||||
@ -90,10 +94,10 @@ struct via_info {
|
||||
};
|
||||
|
||||
static u_int32_t via_fmt[] = {
|
||||
AFMT_U8,
|
||||
AFMT_STEREO | AFMT_U8,
|
||||
AFMT_S16_LE,
|
||||
AFMT_STEREO | AFMT_S16_LE,
|
||||
SND_FORMAT(AFMT_U8, 1, 0),
|
||||
SND_FORMAT(AFMT_U8, 2, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 1, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 2, 0),
|
||||
0
|
||||
};
|
||||
static struct pcmchan_caps via_vracaps = {4000, 48000, via_fmt, 0};
|
||||
@ -207,7 +211,7 @@ via_read_codec(kobj_t obj, void *addr, int reg)
|
||||
static kobj_method_t via_ac97_methods[] = {
|
||||
KOBJMETHOD(ac97_read, via_read_codec),
|
||||
KOBJMETHOD(ac97_write, via_write_codec),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
AC97_DECLARE(via_ac97);
|
||||
|
||||
@ -284,7 +288,7 @@ viachan_setformat(kobj_t obj, void *data, u_int32_t format)
|
||||
int mode, mode_set;
|
||||
|
||||
mode_set = 0;
|
||||
if (format & AFMT_STEREO)
|
||||
if (AFMT_CHANNEL(format) > 1)
|
||||
mode_set |= VIA_RPMODE_STEREO;
|
||||
if (format & AFMT_S16_LE)
|
||||
mode_set |= VIA_RPMODE_16BIT;
|
||||
@ -300,7 +304,7 @@ viachan_setformat(kobj_t obj, void *data, u_int32_t format)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
viachan_setspeed(kobj_t obj, void *data, u_int32_t speed)
|
||||
{
|
||||
struct via_chinfo *ch = data;
|
||||
@ -323,7 +327,7 @@ viachan_setspeed(kobj_t obj, void *data, u_int32_t speed)
|
||||
return 48000;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
viachan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
|
||||
{
|
||||
struct via_chinfo *ch = data;
|
||||
@ -361,14 +365,14 @@ viachan_trigger(kobj_t obj, void *data, int go)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
viachan_getptr(kobj_t obj, void *data)
|
||||
{
|
||||
struct via_chinfo *ch = data;
|
||||
struct via_info *via = ch->parent;
|
||||
struct via_dma_op *ado;
|
||||
bus_addr_t sgd_addr = ch->sgd_addr;
|
||||
int ptr, base, base1, len, seg;
|
||||
u_int32_t ptr, base, base1, len, seg;
|
||||
|
||||
ado = ch->sgd_table;
|
||||
snd_mtxlock(via->lock);
|
||||
@ -396,7 +400,7 @@ viachan_getptr(kobj_t obj, void *data)
|
||||
ptr = ptr & ~0x1f;
|
||||
}
|
||||
|
||||
DEB(printf("return ptr=%d\n", ptr));
|
||||
DEB(printf("return ptr=%u\n", ptr));
|
||||
return ptr;
|
||||
}
|
||||
|
||||
@ -417,7 +421,7 @@ static kobj_method_t viachan_methods[] = {
|
||||
KOBJMETHOD(channel_trigger, viachan_trigger),
|
||||
KOBJMETHOD(channel_getptr, viachan_getptr),
|
||||
KOBJMETHOD(channel_getcaps, viachan_getcaps),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
CHANNEL_DECLARE(viachan);
|
||||
|
||||
|
@ -30,6 +30,10 @@
|
||||
* muting.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
#include <dev/sound/pci/vibes.h>
|
||||
|
||||
@ -99,10 +103,10 @@ struct sc_info {
|
||||
};
|
||||
|
||||
static u_int32_t sc_fmt[] = {
|
||||
AFMT_U8,
|
||||
AFMT_U8 | AFMT_STEREO,
|
||||
AFMT_S16_LE,
|
||||
AFMT_S16_LE | AFMT_STEREO,
|
||||
SND_FORMAT(AFMT_U8, 1, 0),
|
||||
SND_FORMAT(AFMT_U8, 2, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 1, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 2, 0),
|
||||
0
|
||||
};
|
||||
|
||||
@ -199,7 +203,7 @@ svchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c
|
||||
return NULL;
|
||||
}
|
||||
ch->buffer = b;
|
||||
ch->fmt = AFMT_U8;
|
||||
ch->fmt = SND_FORMAT(AFMT_U8, 1, 0);
|
||||
ch->spd = DSP_DEFAULT_SPEED;
|
||||
ch->dma_active = ch->dma_was_active = 0;
|
||||
|
||||
@ -212,7 +216,7 @@ svchan_getcaps(kobj_t obj, void *data)
|
||||
return &sc_caps;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
svchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
|
||||
{
|
||||
struct sc_chinfo *ch = data;
|
||||
@ -231,12 +235,12 @@ svchan_setformat(kobj_t obj, void *data, u_int32_t format)
|
||||
struct sc_chinfo *ch = data;
|
||||
/* NB Just note format here as setting format register
|
||||
* generates noise if dma channel is inactive. */
|
||||
ch->fmt = (format & AFMT_STEREO) ? SV_AFMT_STEREO : SV_AFMT_MONO;
|
||||
ch->fmt = (AFMT_CHANNEL(format) > 1) ? SV_AFMT_STEREO : SV_AFMT_MONO;
|
||||
ch->fmt |= (format & AFMT_16BIT) ? SV_AFMT_S16 : SV_AFMT_U8;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
svchan_setspeed(kobj_t obj, void *data, u_int32_t speed)
|
||||
{
|
||||
struct sc_chinfo *ch = data;
|
||||
@ -349,7 +353,7 @@ svrchan_trigger(kobj_t obj, void *data, int go)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
svrchan_getptr(kobj_t obj, void *data)
|
||||
{
|
||||
struct sc_chinfo *ch = data;
|
||||
@ -370,7 +374,7 @@ static kobj_method_t svrchan_methods[] = {
|
||||
KOBJMETHOD(channel_trigger, svrchan_trigger),
|
||||
KOBJMETHOD(channel_getptr, svrchan_getptr),
|
||||
KOBJMETHOD(channel_getcaps, svchan_getcaps),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
CHANNEL_DECLARE(svrchan);
|
||||
|
||||
@ -426,7 +430,7 @@ svpchan_trigger(kobj_t obj, void *data, int go)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
svpchan_getptr(kobj_t obj, void *data)
|
||||
{
|
||||
struct sc_chinfo *ch = data;
|
||||
@ -447,7 +451,7 @@ static kobj_method_t svpchan_methods[] = {
|
||||
KOBJMETHOD(channel_trigger, svpchan_trigger),
|
||||
KOBJMETHOD(channel_getptr, svpchan_getptr),
|
||||
KOBJMETHOD(channel_getcaps, svchan_getcaps),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
CHANNEL_DECLARE(svpchan);
|
||||
|
||||
@ -538,7 +542,7 @@ sv_mix_set(struct snd_mixer *m, u_int32_t dev, u_int32_t left, u_int32_t right)
|
||||
return sv_gain(sc, dev, left, right);
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
sv_mix_setrecsrc(struct snd_mixer *m, u_int32_t mask)
|
||||
{
|
||||
struct sc_info *sc = mix_getdevinfo(m);
|
||||
@ -559,7 +563,7 @@ static kobj_method_t sv_mixer_methods[] = {
|
||||
KOBJMETHOD(mixer_init, sv_mix_init),
|
||||
KOBJMETHOD(mixer_set, sv_mix_set),
|
||||
KOBJMETHOD(mixer_setrecsrc, sv_mix_setrecsrc),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
MIXER_DECLARE(sv_mixer);
|
||||
|
||||
|
@ -24,6 +24,10 @@
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
#include <dev/sound/pcm/ac97.h>
|
||||
#include <dev/sound/pcm/ac97_patch.h>
|
||||
@ -822,7 +826,7 @@ struct ac97_info *
|
||||
ac97_create(device_t dev, void *devinfo, kobj_class_t cls)
|
||||
{
|
||||
struct ac97_info *codec;
|
||||
int eapdinv;
|
||||
int i;
|
||||
|
||||
codec = malloc(sizeof(*codec), M_AC97, M_WAITOK | M_ZERO);
|
||||
snprintf(codec->name, sizeof(codec->name), "%s:ac97",
|
||||
@ -832,11 +836,15 @@ ac97_create(device_t dev, void *devinfo, kobj_class_t cls)
|
||||
codec->dev = dev;
|
||||
codec->devinfo = devinfo;
|
||||
codec->flags = 0;
|
||||
|
||||
if (resource_int_value(device_get_name(dev), device_get_unit(dev),
|
||||
"eapdinv", &eapdinv) == 0) {
|
||||
if (eapdinv != 0)
|
||||
codec->flags |= AC97_F_EAPD_INV;
|
||||
}
|
||||
"eapdinv", &i) == 0 && i != 0)
|
||||
codec->flags |= AC97_F_EAPD_INV;
|
||||
|
||||
if (resource_int_value(device_get_name(dev), device_get_unit(dev),
|
||||
"softpcmvol", &i) == 0 && i != 0)
|
||||
pcm_setflags(dev, pcm_getflags(dev) | SD_F_SOFTPCMVOL);
|
||||
|
||||
return codec;
|
||||
}
|
||||
|
||||
@ -864,7 +872,6 @@ ac97_getflags(struct ac97_info *codec)
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
#ifdef SND_DYNSYSCTL
|
||||
static int
|
||||
sysctl_hw_snd_ac97_eapd(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
@ -892,12 +899,10 @@ sysctl_hw_snd_ac97_eapd(SYSCTL_HANDLER_ARGS)
|
||||
}
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
ac97_init_sysctl(struct ac97_info *codec)
|
||||
{
|
||||
#ifdef SND_DYNSYSCTL
|
||||
u_int16_t orig, val;
|
||||
|
||||
if (codec == NULL || codec->dev == NULL)
|
||||
@ -915,7 +920,6 @@ ac97_init_sysctl(struct ac97_info *codec)
|
||||
OID_AUTO, "eapd", CTLTYPE_INT | CTLFLAG_RW,
|
||||
codec, sizeof(codec), sysctl_hw_snd_ac97_eapd,
|
||||
"I", "AC97 External Amplifier");
|
||||
#endif
|
||||
}
|
||||
|
||||
static int
|
||||
@ -984,7 +988,6 @@ ac97mix_init(struct snd_mixer *m)
|
||||
case 0x434d4978: /* CMI9761 */
|
||||
case 0x434d4982: /* CMI9761 */
|
||||
case 0x434d4983: /* CMI9761 */
|
||||
ac97_wrcd(codec, AC97_MIX_PCM, 0);
|
||||
bzero(&codec->mix[SOUND_MIXER_PCM],
|
||||
sizeof(codec->mix[SOUND_MIXER_PCM]));
|
||||
pcm_setflags(codec->dev, pcm_getflags(codec->dev) |
|
||||
@ -995,6 +998,8 @@ ac97mix_init(struct snd_mixer *m)
|
||||
break;
|
||||
}
|
||||
|
||||
if (pcm_getflags(codec->dev) & SD_F_SOFTPCMVOL)
|
||||
ac97_wrcd(codec, AC97_MIX_PCM, 0);
|
||||
#if 0
|
||||
/* XXX For the sake of debugging purposes */
|
||||
mix_setparentchild(m, SOUND_MIXER_VOLUME,
|
||||
@ -1053,7 +1058,7 @@ ac97mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
|
||||
return ac97_setmixer(codec, dev, left, right);
|
||||
}
|
||||
|
||||
static int
|
||||
static u_int32_t
|
||||
ac97mix_setrecsrc(struct snd_mixer *m, u_int32_t src)
|
||||
{
|
||||
int i;
|
||||
@ -1064,7 +1069,7 @@ ac97mix_setrecsrc(struct snd_mixer *m, u_int32_t src)
|
||||
for (i = 0; i < AC97_MIXER_SIZE; i++)
|
||||
if ((src & (1 << i)) != 0)
|
||||
break;
|
||||
return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1;
|
||||
return (ac97_setrecsrc(codec, i) == 0)? 1U << i : 0xffffffffU;
|
||||
}
|
||||
|
||||
static kobj_method_t ac97mixer_methods[] = {
|
||||
@ -1073,7 +1078,7 @@ static kobj_method_t ac97mixer_methods[] = {
|
||||
KOBJMETHOD(mixer_reinit, ac97mix_reinit),
|
||||
KOBJMETHOD(mixer_set, ac97mix_set),
|
||||
KOBJMETHOD(mixer_setrecsrc, ac97mix_setrecsrc),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
MIXER_DECLARE(ac97mixer);
|
||||
|
||||
|
@ -24,6 +24,10 @@
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
#include <dev/sound/pcm/ac97.h>
|
||||
#include <dev/sound/pcm/ac97_patch.h>
|
||||
|
@ -1,5 +1,7 @@
|
||||
/*-
|
||||
* Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
|
||||
* Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org>
|
||||
* Portions Copyright (c) Ryan Beasley <ryan.beasley@gmail.com> - GSoC 2006
|
||||
* Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -24,10 +26,17 @@
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
|
||||
#include "feeder_if.h"
|
||||
|
||||
#define SND_USE_FXDIV
|
||||
#include "snd_fxdiv_gen.h"
|
||||
|
||||
SND_DECLARE_FILE("$FreeBSD$");
|
||||
|
||||
struct snd_dbuf *
|
||||
@ -154,7 +163,7 @@ sndbuf_resize(struct snd_dbuf *b, unsigned int blkcnt, unsigned int blksz)
|
||||
unsigned int bufsize, allocsize;
|
||||
u_int8_t *tmpbuf;
|
||||
|
||||
chn_lock(b->channel);
|
||||
CHN_LOCK(b->channel);
|
||||
if (b->maxsize == 0)
|
||||
goto out;
|
||||
if (blkcnt == 0)
|
||||
@ -162,7 +171,7 @@ sndbuf_resize(struct snd_dbuf *b, unsigned int blkcnt, unsigned int blksz)
|
||||
if (blksz == 0)
|
||||
blksz = b->blksz;
|
||||
if (blkcnt < 2 || blksz < 16 || (blkcnt * blksz) > b->maxsize) {
|
||||
chn_unlock(b->channel);
|
||||
CHN_UNLOCK(b->channel);
|
||||
return EINVAL;
|
||||
}
|
||||
if (blkcnt == b->blkcnt && blksz == b->blksz)
|
||||
@ -173,9 +182,9 @@ sndbuf_resize(struct snd_dbuf *b, unsigned int blkcnt, unsigned int blksz)
|
||||
if (bufsize > b->allocsize ||
|
||||
bufsize < (b->allocsize >> SNDBUF_CACHE_SHIFT)) {
|
||||
allocsize = round_page(bufsize);
|
||||
chn_unlock(b->channel);
|
||||
CHN_UNLOCK(b->channel);
|
||||
tmpbuf = malloc(allocsize, M_DEVBUF, M_WAITOK);
|
||||
chn_lock(b->channel);
|
||||
CHN_LOCK(b->channel);
|
||||
if (snd_verbose > 3)
|
||||
printf("%s(): b=%p %p -> %p [%d -> %d : %d]\n",
|
||||
__func__, b, b->tmpbuf, tmpbuf,
|
||||
@ -194,7 +203,7 @@ sndbuf_resize(struct snd_dbuf *b, unsigned int blkcnt, unsigned int blksz)
|
||||
|
||||
sndbuf_reset(b);
|
||||
out:
|
||||
chn_unlock(b->channel);
|
||||
CHN_UNLOCK(b->channel);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -212,11 +221,11 @@ sndbuf_remalloc(struct snd_dbuf *b, unsigned int blkcnt, unsigned int blksz)
|
||||
if (bufsize > b->allocsize ||
|
||||
bufsize < (b->allocsize >> SNDBUF_CACHE_SHIFT)) {
|
||||
allocsize = round_page(bufsize);
|
||||
chn_unlock(b->channel);
|
||||
CHN_UNLOCK(b->channel);
|
||||
buf = malloc(allocsize, M_DEVBUF, M_WAITOK);
|
||||
tmpbuf = malloc(allocsize, M_DEVBUF, M_WAITOK);
|
||||
shadbuf = malloc(allocsize, M_DEVBUF, M_WAITOK);
|
||||
chn_lock(b->channel);
|
||||
CHN_LOCK(b->channel);
|
||||
if (b->buf != NULL)
|
||||
free(b->buf, M_DEVBUF);
|
||||
b->buf = buf;
|
||||
@ -334,14 +343,17 @@ int
|
||||
sndbuf_setfmt(struct snd_dbuf *b, u_int32_t fmt)
|
||||
{
|
||||
b->fmt = fmt;
|
||||
b->bps = 1;
|
||||
b->bps <<= (b->fmt & AFMT_STEREO)? 1 : 0;
|
||||
b->bps = AFMT_BPS(b->fmt);
|
||||
b->align = AFMT_ALIGN(b->fmt);
|
||||
#if 0
|
||||
b->bps = AFMT_CHANNEL(b->fmt);
|
||||
if (b->fmt & AFMT_16BIT)
|
||||
b->bps <<= 1;
|
||||
else if (b->fmt & AFMT_24BIT)
|
||||
b->bps *= 3;
|
||||
else if (b->fmt & AFMT_32BIT)
|
||||
b->bps <<= 2;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -360,9 +372,7 @@ sndbuf_setspd(struct snd_dbuf *b, unsigned int spd)
|
||||
unsigned int
|
||||
sndbuf_getalign(struct snd_dbuf *b)
|
||||
{
|
||||
static int align[] = {0, 1, 1, 2, 2, 2, 2, 3};
|
||||
|
||||
return align[b->bps - 1];
|
||||
return (b->align);
|
||||
}
|
||||
|
||||
unsigned int
|
||||
@ -515,7 +525,7 @@ sndbuf_getfreeptr(struct snd_dbuf *b)
|
||||
return (b->rp + b->rl) % b->bufsize;
|
||||
}
|
||||
|
||||
unsigned int
|
||||
u_int64_t
|
||||
sndbuf_getblocks(struct snd_dbuf *b)
|
||||
{
|
||||
SNDBUF_LOCKASSERT(b);
|
||||
@ -523,7 +533,7 @@ sndbuf_getblocks(struct snd_dbuf *b)
|
||||
return b->total / b->blksz;
|
||||
}
|
||||
|
||||
unsigned int
|
||||
u_int64_t
|
||||
sndbuf_getprevblocks(struct snd_dbuf *b)
|
||||
{
|
||||
SNDBUF_LOCKASSERT(b);
|
||||
@ -531,7 +541,7 @@ sndbuf_getprevblocks(struct snd_dbuf *b)
|
||||
return b->prev_total / b->blksz;
|
||||
}
|
||||
|
||||
unsigned int
|
||||
u_int64_t
|
||||
sndbuf_gettotal(struct snd_dbuf *b)
|
||||
{
|
||||
SNDBUF_LOCKASSERT(b);
|
||||
@ -539,6 +549,14 @@ sndbuf_gettotal(struct snd_dbuf *b)
|
||||
return b->total;
|
||||
}
|
||||
|
||||
u_int64_t
|
||||
sndbuf_getprevtotal(struct snd_dbuf *b)
|
||||
{
|
||||
SNDBUF_LOCKASSERT(b);
|
||||
|
||||
return b->prev_total;
|
||||
}
|
||||
|
||||
void
|
||||
sndbuf_updateprevtotal(struct snd_dbuf *b)
|
||||
{
|
||||
@ -577,14 +595,14 @@ sndbuf_xbytes(unsigned int v, struct snd_dbuf *from, struct snd_dbuf *to)
|
||||
if (from == NULL || to == NULL || v == 0)
|
||||
return 0;
|
||||
|
||||
return snd_xbytes(v, sndbuf_getbps(from) * sndbuf_getspd(from),
|
||||
sndbuf_getbps(to) * sndbuf_getspd(to));
|
||||
return snd_xbytes(v, sndbuf_getalign(from) * sndbuf_getspd(from),
|
||||
sndbuf_getalign(to) * sndbuf_getspd(to));
|
||||
}
|
||||
|
||||
u_int8_t
|
||||
sndbuf_zerodata(u_int32_t fmt)
|
||||
{
|
||||
if (fmt & AFMT_SIGNED)
|
||||
if (fmt & (AFMT_SIGNED | AFMT_PASSTHROUGH))
|
||||
return (0x00);
|
||||
else if (fmt & AFMT_MU_LAW)
|
||||
return (0x7f);
|
||||
@ -670,26 +688,55 @@ sndbuf_dispose(struct snd_dbuf *b, u_int8_t *to, unsigned int count)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef SND_DIAGNOSTIC
|
||||
static uint32_t snd_feeder_maxfeed = 0;
|
||||
SYSCTL_INT(_hw_snd, OID_AUTO, feeder_maxfeed, CTLFLAG_RD,
|
||||
&snd_feeder_maxfeed, 0, "maximum feeder count request");
|
||||
|
||||
static uint32_t snd_feeder_maxcycle = 0;
|
||||
SYSCTL_INT(_hw_snd, OID_AUTO, feeder_maxcycle, CTLFLAG_RD,
|
||||
&snd_feeder_maxcycle, 0, "maximum feeder cycle");
|
||||
#endif
|
||||
|
||||
/* count is number of bytes we want added to destination buffer */
|
||||
int
|
||||
sndbuf_feed(struct snd_dbuf *from, struct snd_dbuf *to, struct pcm_channel *channel, struct pcm_feeder *feeder, unsigned int count)
|
||||
{
|
||||
unsigned int cnt;
|
||||
unsigned int cnt, maxfeed;
|
||||
#ifdef SND_DIAGNOSTIC
|
||||
unsigned int cycle;
|
||||
|
||||
if (count > snd_feeder_maxfeed)
|
||||
snd_feeder_maxfeed = count;
|
||||
|
||||
cycle = 0;
|
||||
#endif
|
||||
|
||||
KASSERT(count > 0, ("can't feed 0 bytes"));
|
||||
|
||||
if (sndbuf_getfree(to) < count)
|
||||
return EINVAL;
|
||||
return (EINVAL);
|
||||
|
||||
maxfeed = SND_FXROUND(SND_FXDIV_MAX, sndbuf_getalign(to));
|
||||
|
||||
do {
|
||||
cnt = FEEDER_FEED(feeder, channel, to->tmpbuf, count, from);
|
||||
if (cnt) {
|
||||
sndbuf_acquire(to, to->tmpbuf, cnt);
|
||||
count -= cnt;
|
||||
}
|
||||
} while (count && cnt);
|
||||
cnt = FEEDER_FEED(feeder, channel, to->tmpbuf,
|
||||
min(count, maxfeed), from);
|
||||
if (cnt == 0)
|
||||
break;
|
||||
sndbuf_acquire(to, to->tmpbuf, cnt);
|
||||
count -= cnt;
|
||||
#ifdef SND_DIAGNOSTIC
|
||||
cycle++;
|
||||
#endif
|
||||
} while (count != 0);
|
||||
|
||||
return 0;
|
||||
#ifdef SND_DIAGNOSTIC
|
||||
if (cycle > snd_feeder_maxcycle)
|
||||
snd_feeder_maxcycle = cycle;
|
||||
#endif
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/************************************************************/
|
||||
@ -703,7 +750,7 @@ sndbuf_dump(struct snd_dbuf *b, char *s, u_int32_t what)
|
||||
if (what & 0x02)
|
||||
printf(" dl: %d, rp: %d, rl: %d, hp: %d", b->dl, b->rp, b->rl, b->hp);
|
||||
if (what & 0x04)
|
||||
printf(" total: %d, prev_total: %d, xrun: %d", b->total, b->prev_total, b->xrun);
|
||||
printf(" total: %ju, prev_total: %ju, xrun: %d", (uintmax_t)b->total, (uintmax_t)b->prev_total, b->xrun);
|
||||
if (what & 0x08)
|
||||
printf(" fmt: 0x%x, spd: %d", b->fmt, b->spd);
|
||||
if (what & 0x10)
|
||||
|
@ -46,9 +46,9 @@ struct snd_dbuf {
|
||||
volatile int rp; /* pointers to the ready area */
|
||||
volatile int rl; /* length of ready area */
|
||||
volatile int hp;
|
||||
volatile u_int32_t total, prev_total;
|
||||
volatile u_int64_t total, prev_total;
|
||||
int dmachan, dir; /* dma channel */
|
||||
u_int32_t fmt, spd, bps;
|
||||
u_int32_t fmt, spd, bps, align;
|
||||
unsigned int blksz, blkcnt;
|
||||
int xrun;
|
||||
u_int32_t flags;
|
||||
@ -107,9 +107,10 @@ unsigned int sndbuf_getfree(struct snd_dbuf *b);
|
||||
unsigned int sndbuf_getfreeptr(struct snd_dbuf *b);
|
||||
unsigned int sndbuf_getready(struct snd_dbuf *b);
|
||||
unsigned int sndbuf_getreadyptr(struct snd_dbuf *b);
|
||||
unsigned int sndbuf_getblocks(struct snd_dbuf *b);
|
||||
unsigned int sndbuf_getprevblocks(struct snd_dbuf *b);
|
||||
unsigned int sndbuf_gettotal(struct snd_dbuf *b);
|
||||
u_int64_t sndbuf_getblocks(struct snd_dbuf *b);
|
||||
u_int64_t sndbuf_getprevblocks(struct snd_dbuf *b);
|
||||
u_int64_t sndbuf_gettotal(struct snd_dbuf *b);
|
||||
u_int64_t sndbuf_getprevtotal(struct snd_dbuf *b);
|
||||
unsigned int snd_xbytes(unsigned int v, unsigned int from, unsigned int to);
|
||||
unsigned int sndbuf_xbytes(unsigned int v, struct snd_dbuf *from, struct snd_dbuf *to);
|
||||
u_int8_t sndbuf_zerodata(u_int32_t fmt);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,7 @@
|
||||
/*-
|
||||
* Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
|
||||
* Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org>
|
||||
* Portions Copyright (c) Ryan Beasley <ryan.beasley@gmail.com> - GSoC 2006
|
||||
* Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -32,6 +34,17 @@ struct pcmchan_caps {
|
||||
u_int32_t caps;
|
||||
};
|
||||
|
||||
struct pcmchan_matrix {
|
||||
int id;
|
||||
uint8_t channels, ext;
|
||||
struct {
|
||||
int type;
|
||||
uint32_t members;
|
||||
} map[SND_CHN_T_MAX + 1];
|
||||
uint32_t mask;
|
||||
int8_t offset[SND_CHN_T_MAX];
|
||||
};
|
||||
|
||||
/* Forward declarations */
|
||||
struct pcm_channel;
|
||||
struct pcmchan_syncgroup;
|
||||
@ -63,7 +76,10 @@ struct pcmchan_syncmember {
|
||||
struct pcm_channel *ch;
|
||||
};
|
||||
|
||||
#define CHN_NAMELEN 32
|
||||
#define CHN_NAMELEN 32
|
||||
#define CHN_COMM_UNUSED "<UNUSED>"
|
||||
#define CHN_COMM_UNKNOWN "<UNKNOWN>"
|
||||
|
||||
struct pcm_channel {
|
||||
kobj_t methods;
|
||||
|
||||
@ -72,13 +88,12 @@ struct pcm_channel {
|
||||
struct pcm_feeder *feeder;
|
||||
u_int32_t align;
|
||||
|
||||
int volume;
|
||||
int latency;
|
||||
u_int32_t speed;
|
||||
u_int32_t format;
|
||||
u_int32_t flags;
|
||||
u_int32_t feederflags;
|
||||
u_int32_t blocks;
|
||||
u_int64_t blocks;
|
||||
|
||||
int direction;
|
||||
unsigned int interrupts, xruns, feedcount;
|
||||
@ -90,6 +105,7 @@ struct pcm_channel {
|
||||
device_t dev;
|
||||
int unit;
|
||||
char name[CHN_NAMELEN];
|
||||
char comm[MAXCOMLEN + 1];
|
||||
struct mtx *lock;
|
||||
int trigger;
|
||||
/**
|
||||
@ -139,9 +155,16 @@ struct pcm_channel {
|
||||
struct {
|
||||
SLIST_ENTRY(pcm_channel) link;
|
||||
} busy;
|
||||
struct {
|
||||
SLIST_ENTRY(pcm_channel) link;
|
||||
} opened;
|
||||
} pcm;
|
||||
} channels;
|
||||
|
||||
struct pcmchan_matrix matrix;
|
||||
|
||||
int volume[SND_VOL_C_MAX][SND_CHN_T_VOL_MAX];
|
||||
|
||||
void *data1, *data2;
|
||||
};
|
||||
|
||||
@ -172,10 +195,9 @@ struct pcm_channel {
|
||||
if (t == y) \
|
||||
break; \
|
||||
} \
|
||||
if (t != y) { \
|
||||
if (t != y) \
|
||||
CHN_INSERT_HEAD(x, y, z); \
|
||||
} \
|
||||
} while(0)
|
||||
} while (0)
|
||||
|
||||
#define CHN_INSERT_AFTER_SAFE(w, x, y, z) do { \
|
||||
struct pcm_channel *t = NULL; \
|
||||
@ -183,10 +205,9 @@ struct pcm_channel {
|
||||
if (t == y) \
|
||||
break; \
|
||||
} \
|
||||
if (t != y) { \
|
||||
if (t != y) \
|
||||
CHN_INSERT_AFTER(x, y, z); \
|
||||
} \
|
||||
} while(0)
|
||||
} while (0)
|
||||
|
||||
#define CHN_REMOVE_SAFE(x, y, z) do { \
|
||||
struct pcm_channel *t = NULL; \
|
||||
@ -194,10 +215,26 @@ struct pcm_channel {
|
||||
if (t == y) \
|
||||
break; \
|
||||
} \
|
||||
if (t == y) { \
|
||||
if (t == y) \
|
||||
CHN_REMOVE(x, y, z); \
|
||||
} while (0)
|
||||
|
||||
#define CHN_INSERT_SORT(w, x, y, z) do { \
|
||||
struct pcm_channel *t, *a = NULL; \
|
||||
CHN_FOREACH(t, x, z) { \
|
||||
if ((y)->unit w t->unit) \
|
||||
a = t; \
|
||||
else \
|
||||
break; \
|
||||
} \
|
||||
} while(0)
|
||||
if (a != NULL) \
|
||||
CHN_INSERT_AFTER(a, y, z); \
|
||||
else \
|
||||
CHN_INSERT_HEAD(x, y, z); \
|
||||
} while (0)
|
||||
|
||||
#define CHN_INSERT_SORT_ASCEND(x, y, z) CHN_INSERT_SORT(>, x, y, z)
|
||||
#define CHN_INSERT_SORT_DESCEND(x, y, z) CHN_INSERT_SORT(<, x, y, z)
|
||||
|
||||
#define CHN_UNIT(x) (snd_unit2u((x)->unit))
|
||||
#define CHN_DEV(x) (snd_unit2d((x)->unit))
|
||||
@ -211,7 +248,7 @@ struct pcm_channel {
|
||||
#define CHN_BROADCAST(x) do { \
|
||||
if ((x)->cv_waiters != 0) \
|
||||
cv_broadcastpri(x, PRIBIO); \
|
||||
} while(0)
|
||||
} while (0)
|
||||
|
||||
#include "channel_if.h"
|
||||
|
||||
@ -225,79 +262,72 @@ int chn_poll(struct pcm_channel *c, int ev, struct thread *td);
|
||||
|
||||
int chn_init(struct pcm_channel *c, void *devinfo, int dir, int direction);
|
||||
int chn_kill(struct pcm_channel *c);
|
||||
int chn_setdir(struct pcm_channel *c, int dir);
|
||||
int chn_reset(struct pcm_channel *c, u_int32_t fmt);
|
||||
int chn_reset(struct pcm_channel *c, u_int32_t fmt, u_int32_t spd);
|
||||
int chn_setvolume(struct pcm_channel *c, int left, int right);
|
||||
int chn_setspeed(struct pcm_channel *c, int speed);
|
||||
int chn_setformat(struct pcm_channel *c, u_int32_t fmt);
|
||||
int chn_setvolume_multi(struct pcm_channel *c, int vc, int left, int right,
|
||||
int center);
|
||||
int chn_setvolume_matrix(struct pcm_channel *c, int vc, int vt, int val);
|
||||
int chn_getvolume_matrix(struct pcm_channel *c, int vc, int vt);
|
||||
void chn_vpc_reset(struct pcm_channel *c, int vc, int force);
|
||||
int chn_setparam(struct pcm_channel *c, uint32_t format, uint32_t speed);
|
||||
int chn_setspeed(struct pcm_channel *c, uint32_t speed);
|
||||
int chn_setformat(struct pcm_channel *c, uint32_t format);
|
||||
int chn_setblocksize(struct pcm_channel *c, int blkcnt, int blksz);
|
||||
int chn_setlatency(struct pcm_channel *c, int latency);
|
||||
void chn_syncstate(struct pcm_channel *c);
|
||||
int chn_trigger(struct pcm_channel *c, int go);
|
||||
int chn_getptr(struct pcm_channel *c);
|
||||
struct pcmchan_caps *chn_getcaps(struct pcm_channel *c);
|
||||
u_int32_t chn_getformats(struct pcm_channel *c);
|
||||
|
||||
struct pcmchan_matrix *chn_getmatrix(struct pcm_channel *);
|
||||
int chn_setmatrix(struct pcm_channel *, struct pcmchan_matrix *);
|
||||
|
||||
int chn_oss_getorder(struct pcm_channel *, unsigned long long *);
|
||||
int chn_oss_setorder(struct pcm_channel *, unsigned long long *);
|
||||
int chn_oss_getmask(struct pcm_channel *, uint32_t *);
|
||||
|
||||
void chn_resetbuf(struct pcm_channel *c);
|
||||
void chn_intr_locked(struct pcm_channel *c);
|
||||
void chn_intr(struct pcm_channel *c);
|
||||
int chn_wrfeed(struct pcm_channel *c);
|
||||
int chn_rdfeed(struct pcm_channel *c);
|
||||
int chn_abort(struct pcm_channel *c);
|
||||
|
||||
void chn_wrupdate(struct pcm_channel *c);
|
||||
void chn_rdupdate(struct pcm_channel *c);
|
||||
|
||||
int chn_notify(struct pcm_channel *c, u_int32_t flags);
|
||||
void chn_lock(struct pcm_channel *c);
|
||||
void chn_unlock(struct pcm_channel *c);
|
||||
|
||||
int chn_getrates(struct pcm_channel *c, int **rates);
|
||||
int chn_syncdestroy(struct pcm_channel *c);
|
||||
|
||||
#define CHN_SETVOLUME(...) chn_setvolume_matrix(__VA_ARGS__)
|
||||
#if defined(SND_DIAGNOSTIC) || defined(INVARIANTS)
|
||||
#define CHN_GETVOLUME(...) chn_getvolume_matrix(__VA_ARGS__)
|
||||
#else
|
||||
#define CHN_GETVOLUME(x, y, z) ((x)->volume[y][z])
|
||||
#endif
|
||||
|
||||
#ifdef OSSV4_EXPERIMENT
|
||||
int chn_getpeaks(struct pcm_channel *c, int *lpeak, int *rpeak);
|
||||
#endif
|
||||
|
||||
#ifdef USING_MUTEX
|
||||
#define CHN_LOCK_OWNED(c) mtx_owned((struct mtx *)((c)->lock))
|
||||
#define CHN_LOCK(c) mtx_lock((struct mtx *)((c)->lock))
|
||||
#define CHN_UNLOCK(c) mtx_unlock((struct mtx *)((c)->lock))
|
||||
#define CHN_TRYLOCK(c) mtx_trylock((struct mtx *)((c)->lock))
|
||||
#define CHN_LOCKASSERT(c) mtx_assert((struct mtx *)((c)->lock), MA_OWNED)
|
||||
#else
|
||||
#define CHN_LOCK_OWNED(c) 0
|
||||
#define CHN_LOCK(c)
|
||||
#define CHN_UNLOCK(c)
|
||||
#define CHN_TRYLOCK(c)
|
||||
#define CHN_LOCKASSERT(c)
|
||||
#endif
|
||||
#define CHN_LOCKOWNED(c) mtx_owned((c)->lock)
|
||||
#define CHN_LOCK(c) mtx_lock((c)->lock)
|
||||
#define CHN_UNLOCK(c) mtx_unlock((c)->lock)
|
||||
#define CHN_TRYLOCK(c) mtx_trylock((c)->lock)
|
||||
#define CHN_LOCKASSERT(c) mtx_assert((c)->lock, MA_OWNED)
|
||||
#define CHN_UNLOCKASSERT(c) mtx_assert((c)->lock, MA_NOTOWNED)
|
||||
|
||||
int fmtvalid(u_int32_t fmt, u_int32_t *fmtlist);
|
||||
int snd_fmtvalid(uint32_t fmt, uint32_t *fmtlist);
|
||||
|
||||
#define AFMTSTR_NONE 0 /* "s16le" */
|
||||
#define AFMTSTR_SIMPLE 1 /* "s16le:s" */
|
||||
#define AFMTSTR_NUM 2 /* "s16le:2" */
|
||||
#define AFMTSTR_FULL 3 /* "s16le:stereo" */
|
||||
uint32_t snd_str2afmt(const char *);
|
||||
uint32_t snd_afmt2str(uint32_t, char *, size_t);
|
||||
|
||||
#define AFMTSTR_MAXSZ 13 /* include null terminator */
|
||||
#define AFMTSTR_LEN 16
|
||||
|
||||
#define AFMTSTR_MONO_RETURN 0
|
||||
#define AFMTSTR_STEREO_RETURN 1
|
||||
|
||||
struct afmtstr_table {
|
||||
char *fmtstr;
|
||||
u_int32_t format;
|
||||
};
|
||||
|
||||
int afmtstr_swap_sign(char *);
|
||||
int afmtstr_swap_endian(char *);
|
||||
u_int32_t afmtstr2afmt(struct afmtstr_table *, const char *, int);
|
||||
u_int32_t afmt2afmtstr(struct afmtstr_table *, u_int32_t, char *, size_t, int, int);
|
||||
|
||||
extern int chn_latency;
|
||||
extern int chn_latency_profile;
|
||||
extern int report_soft_formats;
|
||||
extern int report_soft_matrix;
|
||||
|
||||
#define PCMDIR_FAKE 0
|
||||
#define PCMDIR_PLAY 1
|
||||
#define PCMDIR_PLAY_VIRTUAL 2
|
||||
#define PCMDIR_REC -1
|
||||
@ -313,26 +343,60 @@ extern int report_soft_formats;
|
||||
(x) == PCMTRIG_STOP || \
|
||||
(x) == PCMTRIG_ABORT)
|
||||
|
||||
#define CHN_F_CLOSING 0x00000004 /* a pending close */
|
||||
#define CHN_F_ABORTING 0x00000008 /* a pending abort */
|
||||
#define CHN_F_RUNNING 0x00000010 /* dma is running */
|
||||
#define CHN_F_TRIGGERED 0x00000020
|
||||
#define CHN_F_NOTRIGGER 0x00000040
|
||||
#define CHN_F_SLEEPING 0x00000080
|
||||
#define CHN_F_CLOSING 0x00000001 /* a pending close */
|
||||
#define CHN_F_ABORTING 0x00000002 /* a pending abort */
|
||||
#define CHN_F_RUNNING 0x00000004 /* dma is running */
|
||||
#define CHN_F_TRIGGERED 0x00000008
|
||||
#define CHN_F_NOTRIGGER 0x00000010
|
||||
#define CHN_F_SLEEPING 0x00000020
|
||||
|
||||
#define CHN_F_BUSY 0x00001000 /* has been opened */
|
||||
#define CHN_F_HAS_SIZE 0x00002000 /* user set block size */
|
||||
#define CHN_F_NBIO 0x00004000 /* do non-blocking i/o */
|
||||
#define CHN_F_MAPPED 0x00010000 /* has been mmap()ed */
|
||||
#define CHN_F_DEAD 0x00020000
|
||||
#define CHN_F_BADSETTING 0x00040000
|
||||
#define CHN_F_SETBLOCKSIZE 0x00080000
|
||||
#define CHN_F_HAS_VCHAN 0x00100000
|
||||
#define CHN_F_NBIO 0x00000040 /* do non-blocking i/o */
|
||||
#define CHN_F_MMAP 0x00000080 /* has been mmap()ed */
|
||||
|
||||
#define CHN_F_BUSY 0x00000100 /* has been opened */
|
||||
#define CHN_F_DIRTY 0x00000200 /* need re-config */
|
||||
#define CHN_F_DEAD 0x00000400 /* too many errors, dead, mdk */
|
||||
#define CHN_F_SILENCE 0x00000800 /* silence, nil, null, yada */
|
||||
|
||||
#define CHN_F_HAS_SIZE 0x00001000 /* user set block size */
|
||||
#define CHN_F_HAS_VCHAN 0x00002000 /* vchan master */
|
||||
|
||||
#define CHN_F_VCHAN_PASSTHROUGH 0x00004000 /* digital ac3/dts passthrough */
|
||||
#define CHN_F_VCHAN_ADAPTIVE 0x00008000 /* adaptive format/rate selection */
|
||||
#define CHN_F_VCHAN_DYNAMIC (CHN_F_VCHAN_PASSTHROUGH | CHN_F_VCHAN_ADAPTIVE)
|
||||
|
||||
#define CHN_F_VIRTUAL 0x10000000 /* not backed by hardware */
|
||||
#define CHN_F_BITPERFECT 0x20000000 /* un-cooked, Heh.. */
|
||||
#define CHN_F_PASSTHROUGH 0x40000000 /* passthrough re-config */
|
||||
#define CHN_F_EXCLUSIVE 0x80000000 /* exclusive access */
|
||||
|
||||
#define CHN_F_BITS "\020" \
|
||||
"\001CLOSING" \
|
||||
"\002ABORTING" \
|
||||
"\003RUNNING" \
|
||||
"\004TRIGGERED" \
|
||||
"\005NOTRIGGER" \
|
||||
"\006SLEEPING" \
|
||||
"\007NBIO" \
|
||||
"\010MMAP" \
|
||||
"\011BUSY" \
|
||||
"\012DIRTY" \
|
||||
"\013DEAD" \
|
||||
"\014SILENCE" \
|
||||
"\015HAS_SIZE" \
|
||||
"\016HAS_VCHAN" \
|
||||
"\017VCHAN_PASSTHROUGH" \
|
||||
"\020VCHAN_ADAPTIVE" \
|
||||
"\035VIRTUAL" \
|
||||
"\036BITPERFECT" \
|
||||
"\037PASSTHROUGH" \
|
||||
"\040EXCLUSIVE"
|
||||
|
||||
|
||||
#define CHN_F_RESET (CHN_F_BUSY | CHN_F_DEAD | \
|
||||
CHN_F_HAS_VCHAN | CHN_F_VIRTUAL)
|
||||
CHN_F_VIRTUAL | CHN_F_HAS_VCHAN | \
|
||||
CHN_F_VCHAN_DYNAMIC | \
|
||||
CHN_F_PASSTHROUGH | CHN_F_EXCLUSIVE)
|
||||
|
||||
#define CHN_F_MMAP_INVALID (CHN_F_DEAD | CHN_F_RUNNING)
|
||||
|
||||
@ -359,6 +423,8 @@ extern int report_soft_formats;
|
||||
#define CHN_STOPPED(c) (!CHN_STARTED(c))
|
||||
#define CHN_DIRSTR(c) (((c)->direction == PCMDIR_PLAY) ? \
|
||||
"PCMDIR_PLAY" : "PCMDIR_REC")
|
||||
#define CHN_BITPERFECT(c) ((c)->flags & CHN_F_BITPERFECT)
|
||||
#define CHN_PASSTHROUGH(c) ((c)->flags & CHN_F_PASSTHROUGH)
|
||||
|
||||
#define CHN_TIMEOUT 5
|
||||
#define CHN_TIMEOUT_MIN 1
|
||||
@ -375,4 +441,15 @@ extern int report_soft_formats;
|
||||
/* The size of a whole secondary bufhard. */
|
||||
#define CHN_2NDBUFMAXSIZE (131072)
|
||||
|
||||
#ifdef SND_DEBUG
|
||||
#define CHANNEL_DECLARE(channel) \
|
||||
static struct kobj_class channel##_class = { \
|
||||
.name = #channel, \
|
||||
.methods = channel##_methods, \
|
||||
.size = sizeof(struct kobj), \
|
||||
.baseclasses = NULL, \
|
||||
.refs = 0 \
|
||||
}
|
||||
#else
|
||||
#define CHANNEL_DECLARE(name) static DEFINE_CLASS(name, name ## _methods, sizeof(struct kobj))
|
||||
#endif
|
||||
|
@ -1,7 +1,9 @@
|
||||
#-
|
||||
# KOBJ
|
||||
#
|
||||
# Copyright (c) 2000 Cameron Grant <cg@freebsd.org>
|
||||
# Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org>
|
||||
# Portions Copyright (c) Ryan Beasley <ryan.beasley@gmail.com> - GSoC 2006
|
||||
# Copyright (c) 2000 Cameron Grant <cg@FreeBSD.org>
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
@ -34,12 +36,6 @@ INTERFACE channel;
|
||||
|
||||
CODE {
|
||||
|
||||
static int
|
||||
channel_nosetdir(kobj_t obj, void *data, int dir)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
channel_noreset(kobj_t obj, void *data)
|
||||
{
|
||||
@ -86,9 +82,21 @@ CODE {
|
||||
static int
|
||||
channel_nosetfragments(kobj_t obj, void *data, u_int32_t blocksize, u_int32_t blockcount)
|
||||
{
|
||||
return 0;
|
||||
return ENOTSUP;
|
||||
}
|
||||
|
||||
static struct pcmchan_matrix *
|
||||
channel_nogetmatrix(kobj_t obj, void *data, u_int32_t format)
|
||||
{
|
||||
format = feeder_matrix_default_format(format);
|
||||
return (feeder_matrix_format_map(format));
|
||||
}
|
||||
|
||||
static int
|
||||
channel_nosetmatrix(kobj_t obj, void *data, struct pcmchan_matrix *m)
|
||||
{
|
||||
return ENOTSUP;
|
||||
}
|
||||
};
|
||||
|
||||
METHOD void* init {
|
||||
@ -114,13 +122,7 @@ METHOD int resetdone {
|
||||
void *data;
|
||||
} DEFAULT channel_noresetdone;
|
||||
|
||||
METHOD int setdir {
|
||||
kobj_t obj;
|
||||
void *data;
|
||||
int dir;
|
||||
} DEFAULT channel_nosetdir;
|
||||
|
||||
METHOD u_int32_t setformat {
|
||||
METHOD int setformat {
|
||||
kobj_t obj;
|
||||
void *data;
|
||||
u_int32_t format;
|
||||
@ -217,3 +219,15 @@ METHOD int getrates {
|
||||
void *data;
|
||||
int **rates;
|
||||
} DEFAULT channel_nogetrates;
|
||||
|
||||
METHOD struct pcmchan_matrix * getmatrix {
|
||||
kobj_t obj;
|
||||
void *data;
|
||||
u_int32_t format;
|
||||
} DEFAULT channel_nogetmatrix;
|
||||
|
||||
METHOD int setmatrix {
|
||||
kobj_t obj;
|
||||
void *data;
|
||||
struct pcmchan_matrix *m;
|
||||
} DEFAULT channel_nosetmatrix;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,7 @@
|
||||
/*-
|
||||
* Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
|
||||
* Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org>
|
||||
* Portions Copyright (c) Ryan Beasley <ryan.beasley@gmail.com> - GSoC 2006
|
||||
* Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
|
@ -1,164 +0,0 @@
|
||||
/*-
|
||||
* Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
|
||||
SND_DECLARE_FILE("$FreeBSD$");
|
||||
|
||||
static u_int32_t fk_fmt[] = {
|
||||
AFMT_MU_LAW,
|
||||
AFMT_STEREO | AFMT_MU_LAW,
|
||||
AFMT_A_LAW,
|
||||
AFMT_STEREO | AFMT_A_LAW,
|
||||
AFMT_U8,
|
||||
AFMT_STEREO | AFMT_U8,
|
||||
AFMT_S8,
|
||||
AFMT_STEREO | AFMT_S8,
|
||||
AFMT_S16_LE,
|
||||
AFMT_STEREO | AFMT_S16_LE,
|
||||
AFMT_U16_LE,
|
||||
AFMT_STEREO | AFMT_U16_LE,
|
||||
AFMT_S16_BE,
|
||||
AFMT_STEREO | AFMT_S16_BE,
|
||||
AFMT_U16_BE,
|
||||
AFMT_STEREO | AFMT_U16_BE,
|
||||
AFMT_S24_LE,
|
||||
AFMT_STEREO | AFMT_S24_LE,
|
||||
AFMT_U24_LE,
|
||||
AFMT_STEREO | AFMT_U24_LE,
|
||||
AFMT_S24_BE,
|
||||
AFMT_STEREO | AFMT_S24_BE,
|
||||
AFMT_U24_BE,
|
||||
AFMT_STEREO | AFMT_U24_BE,
|
||||
AFMT_S32_LE,
|
||||
AFMT_STEREO | AFMT_S32_LE,
|
||||
AFMT_U32_LE,
|
||||
AFMT_STEREO | AFMT_U32_LE,
|
||||
AFMT_S32_BE,
|
||||
AFMT_STEREO | AFMT_S32_BE,
|
||||
AFMT_U32_BE,
|
||||
AFMT_STEREO | AFMT_U32_BE,
|
||||
0
|
||||
};
|
||||
static struct pcmchan_caps fk_caps = {0, 1000000, fk_fmt, 0};
|
||||
|
||||
#define FKBUFSZ 4096
|
||||
static char fakebuf[FKBUFSZ];
|
||||
|
||||
/* channel interface */
|
||||
static void *
|
||||
fkchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir)
|
||||
{
|
||||
sndbuf_setup(b, fakebuf, FKBUFSZ);
|
||||
return (void *)0xbabef00d;
|
||||
}
|
||||
|
||||
static int
|
||||
fkchan_free(kobj_t obj, void *data)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
fkchan_setformat(kobj_t obj, void *data, u_int32_t format)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
fkchan_setspeed(kobj_t obj, void *data, u_int32_t speed)
|
||||
{
|
||||
return speed;
|
||||
}
|
||||
|
||||
static int
|
||||
fkchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
|
||||
{
|
||||
return blocksize;
|
||||
}
|
||||
|
||||
static int
|
||||
fkchan_trigger(kobj_t obj, void *data, int go)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
fkchan_getptr(kobj_t obj, void *data)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct pcmchan_caps *
|
||||
fkchan_getcaps(kobj_t obj, void *data)
|
||||
{
|
||||
return &fk_caps;
|
||||
}
|
||||
|
||||
static kobj_method_t fkchan_methods[] = {
|
||||
KOBJMETHOD(channel_init, fkchan_init),
|
||||
KOBJMETHOD(channel_free, fkchan_free),
|
||||
KOBJMETHOD(channel_setformat, fkchan_setformat),
|
||||
KOBJMETHOD(channel_setspeed, fkchan_setspeed),
|
||||
KOBJMETHOD(channel_setblocksize, fkchan_setblocksize),
|
||||
KOBJMETHOD(channel_trigger, fkchan_trigger),
|
||||
KOBJMETHOD(channel_getptr, fkchan_getptr),
|
||||
KOBJMETHOD(channel_getcaps, fkchan_getcaps),
|
||||
{ 0, 0 }
|
||||
};
|
||||
CHANNEL_DECLARE(fkchan);
|
||||
|
||||
struct pcm_channel *
|
||||
fkchan_setup(device_t dev)
|
||||
{
|
||||
struct snddev_info *d = device_get_softc(dev);
|
||||
struct pcm_channel *c;
|
||||
|
||||
c = malloc(sizeof(*c), M_DEVBUF, M_WAITOK | M_ZERO);
|
||||
c->methods = kobj_create(&fkchan_class, M_DEVBUF, M_WAITOK);
|
||||
c->parentsnddev = d;
|
||||
/*
|
||||
* Fake channel is such a blessing in disguise. Using this,
|
||||
* we can keep track prefered virtual channel speed / format without
|
||||
* querying kernel hint repetitively (see vchan_create / vchan.c).
|
||||
*/
|
||||
c->speed = 0;
|
||||
c->format = 0;
|
||||
snprintf(c->name, CHN_NAMELEN, "%s:fake", device_get_nameunit(dev));
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
int
|
||||
fkchan_kill(struct pcm_channel *c)
|
||||
{
|
||||
kobj_delete(c->methods, M_DEVBUF);
|
||||
c->methods = NULL;
|
||||
free(c, M_DEVBUF);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
/*-
|
||||
* Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
|
||||
* Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org>
|
||||
* Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -24,6 +25,10 @@
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
|
||||
#include "feeder_if.h"
|
||||
@ -35,43 +40,6 @@ MALLOC_DEFINE(M_FEEDER, "feeder", "pcm feeder");
|
||||
#define MAXFEEDERS 256
|
||||
#undef FEEDER_DEBUG
|
||||
|
||||
int feeder_buffersize = FEEDBUFSZ;
|
||||
TUNABLE_INT("hw.snd.feeder_buffersize", &feeder_buffersize);
|
||||
|
||||
#ifdef SND_DEBUG
|
||||
static int
|
||||
sysctl_hw_snd_feeder_buffersize(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
int i, err, val;
|
||||
|
||||
val = feeder_buffersize;
|
||||
err = sysctl_handle_int(oidp, &val, 0, req);
|
||||
|
||||
if (err != 0 || req->newptr == NULL)
|
||||
return err;
|
||||
|
||||
if (val < FEEDBUFSZ_MIN || val > FEEDBUFSZ_MAX)
|
||||
return EINVAL;
|
||||
|
||||
i = 0;
|
||||
while (val >> i)
|
||||
i++;
|
||||
i = 1 << i;
|
||||
if (i > val && (i >> 1) > 0 && (i >> 1) >= ((val * 3) >> 2))
|
||||
i >>= 1;
|
||||
|
||||
feeder_buffersize = i;
|
||||
|
||||
return err;
|
||||
}
|
||||
SYSCTL_PROC(_hw_snd, OID_AUTO, feeder_buffersize, CTLTYPE_INT | CTLFLAG_RW,
|
||||
0, sizeof(int), sysctl_hw_snd_feeder_buffersize, "I",
|
||||
"feeder buffer size");
|
||||
#else
|
||||
SYSCTL_INT(_hw_snd, OID_AUTO, feeder_buffersize, CTLFLAG_RD,
|
||||
&feeder_buffersize, FEEDBUFSZ, "feeder buffer size");
|
||||
#endif
|
||||
|
||||
struct feedertab_entry {
|
||||
SLIST_ENTRY(feedertab_entry) link;
|
||||
struct feeder_class *feederclass;
|
||||
@ -131,10 +99,6 @@ feeder_register(void *p)
|
||||
chn_latency_profile > CHN_LATENCY_PROFILE_MAX)
|
||||
chn_latency_profile = CHN_LATENCY_PROFILE_DEFAULT;
|
||||
|
||||
if (feeder_buffersize < FEEDBUFSZ_MIN ||
|
||||
feeder_buffersize > FEEDBUFSZ_MAX)
|
||||
feeder_buffersize = FEEDBUFSZ;
|
||||
|
||||
if (feeder_rate_min < FEEDRATE_MIN ||
|
||||
feeder_rate_max < FEEDRATE_MIN ||
|
||||
feeder_rate_min > FEEDRATE_MAX ||
|
||||
@ -150,11 +114,11 @@ feeder_register(void *p)
|
||||
|
||||
if (bootverbose)
|
||||
printf("%s: snd_unit=%d snd_maxautovchans=%d "
|
||||
"latency=%d feeder_buffersize=%d "
|
||||
"latency=%d "
|
||||
"feeder_rate_min=%d feeder_rate_max=%d "
|
||||
"feeder_rate_round=%d\n",
|
||||
__func__, snd_unit, snd_maxautovchans,
|
||||
chn_latency, feeder_buffersize,
|
||||
chn_latency,
|
||||
feeder_rate_min, feeder_rate_max,
|
||||
feeder_rate_round);
|
||||
|
||||
@ -227,7 +191,6 @@ feeder_create(struct feeder_class *fc, struct pcm_feederdesc *desc)
|
||||
if (f == NULL)
|
||||
return NULL;
|
||||
|
||||
f->align = fc->align;
|
||||
f->data = fc->data;
|
||||
f->source = NULL;
|
||||
f->parent = NULL;
|
||||
@ -280,11 +243,6 @@ chn_addfeeder(struct pcm_channel *c, struct feeder_class *fc, struct pcm_feederd
|
||||
|
||||
nf->source = c->feeder;
|
||||
|
||||
/* XXX we should use the lowest common denominator for align */
|
||||
if (nf->align > 0)
|
||||
c->align += nf->align;
|
||||
else if (nf->align < 0 && c->align < -nf->align)
|
||||
c->align = -nf->align;
|
||||
if (c->feeder != NULL)
|
||||
c->feeder->parent = nf;
|
||||
c->feeder = nf;
|
||||
@ -321,39 +279,6 @@ chn_findfeeder(struct pcm_channel *c, u_int32_t type)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
chainok(struct pcm_feeder *test, struct pcm_feeder *stop)
|
||||
{
|
||||
u_int32_t visited[MAXFEEDERS / 32];
|
||||
u_int32_t idx, mask;
|
||||
|
||||
bzero(visited, sizeof(visited));
|
||||
while (test && (test != stop)) {
|
||||
idx = test->desc->idx;
|
||||
if (idx < 0)
|
||||
panic("bad idx %d", idx);
|
||||
if (idx >= MAXFEEDERS)
|
||||
panic("bad idx %d", idx);
|
||||
mask = 1 << (idx & 31);
|
||||
idx >>= 5;
|
||||
if (visited[idx] & mask)
|
||||
return 0;
|
||||
visited[idx] |= mask;
|
||||
test = test->source;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* See feeder_fmtchain() for the mumbo-jumbo ridiculous explanation
|
||||
* of what the heck is this FMT_Q_*
|
||||
*/
|
||||
#define FMT_Q_UP 1
|
||||
#define FMT_Q_DOWN 2
|
||||
#define FMT_Q_EQ 3
|
||||
#define FMT_Q_MULTI 4
|
||||
|
||||
/*
|
||||
* 14bit format scoring
|
||||
* --------------------
|
||||
@ -384,11 +309,13 @@ chainok(struct pcm_feeder *test, struct pcm_feeder *stop)
|
||||
#define score_signeq(s1, s2) (((s1) & 0x1) == ((s2) & 0x1))
|
||||
#define score_endianeq(s1, s2) (((s1) & 0x2) == ((s2) & 0x2))
|
||||
#define score_cheq(s1, s2) (((s1) & 0xfc) == ((s2) & 0xfc))
|
||||
#define score_chgt(s1, s2) (((s1) & 0xfc) > ((s2) & 0xfc))
|
||||
#define score_chlt(s1, s2) (((s1) & 0xfc) < ((s2) & 0xfc))
|
||||
#define score_val(s1) ((s1) & 0x3f00)
|
||||
#define score_cse(s1) ((s1) & 0x7f)
|
||||
|
||||
u_int32_t
|
||||
chn_fmtscore(u_int32_t fmt)
|
||||
snd_fmtscore(u_int32_t fmt)
|
||||
{
|
||||
u_int32_t ret;
|
||||
|
||||
@ -397,10 +324,11 @@ chn_fmtscore(u_int32_t fmt)
|
||||
ret |= 1 << 0;
|
||||
if (fmt & AFMT_BIGENDIAN)
|
||||
ret |= 1 << 1;
|
||||
if (fmt & AFMT_STEREO)
|
||||
/*if (fmt & AFMT_STEREO)
|
||||
ret |= (2 & 0x3f) << 2;
|
||||
else
|
||||
ret |= (1 & 0x3f) << 2;
|
||||
ret |= (1 & 0x3f) << 2;*/
|
||||
ret |= (AFMT_CHANNEL(fmt) & 0x3f) << 2;
|
||||
if (fmt & AFMT_A_LAW)
|
||||
ret |= 1 << 8;
|
||||
else if (fmt & AFMT_MU_LAW)
|
||||
@ -418,7 +346,7 @@ chn_fmtscore(u_int32_t fmt)
|
||||
}
|
||||
|
||||
static u_int32_t
|
||||
chn_fmtbestfunc(u_int32_t fmt, u_int32_t *fmts, int cheq)
|
||||
snd_fmtbestfunc(u_int32_t fmt, u_int32_t *fmts, int cheq)
|
||||
{
|
||||
u_int32_t best, score, score2, oldscore;
|
||||
int i;
|
||||
@ -426,16 +354,18 @@ chn_fmtbestfunc(u_int32_t fmt, u_int32_t *fmts, int cheq)
|
||||
if (fmt == 0 || fmts == NULL || fmts[0] == 0)
|
||||
return 0;
|
||||
|
||||
if (fmtvalid(fmt, fmts))
|
||||
if (snd_fmtvalid(fmt, fmts))
|
||||
return fmt;
|
||||
|
||||
best = 0;
|
||||
score = chn_fmtscore(fmt);
|
||||
score = snd_fmtscore(fmt);
|
||||
oldscore = 0;
|
||||
for (i = 0; fmts[i] != 0; i++) {
|
||||
score2 = chn_fmtscore(fmts[i]);
|
||||
if (cheq && !score_cheq(score, score2))
|
||||
continue;
|
||||
score2 = snd_fmtscore(fmts[i]);
|
||||
if (cheq && !score_cheq(score, score2) &&
|
||||
(score_chlt(score2, score) ||
|
||||
(oldscore != 0 && score_chgt(score2, oldscore))))
|
||||
continue;
|
||||
if (oldscore == 0 ||
|
||||
(score_val(score2) == score_val(score)) ||
|
||||
(score_val(score2) == score_val(oldscore)) ||
|
||||
@ -461,36 +391,37 @@ chn_fmtbestfunc(u_int32_t fmt, u_int32_t *fmts, int cheq)
|
||||
}
|
||||
|
||||
u_int32_t
|
||||
chn_fmtbestbit(u_int32_t fmt, u_int32_t *fmts)
|
||||
snd_fmtbestbit(u_int32_t fmt, u_int32_t *fmts)
|
||||
{
|
||||
return chn_fmtbestfunc(fmt, fmts, 0);
|
||||
return snd_fmtbestfunc(fmt, fmts, 0);
|
||||
}
|
||||
|
||||
u_int32_t
|
||||
chn_fmtbeststereo(u_int32_t fmt, u_int32_t *fmts)
|
||||
snd_fmtbestchannel(u_int32_t fmt, u_int32_t *fmts)
|
||||
{
|
||||
return chn_fmtbestfunc(fmt, fmts, 1);
|
||||
return snd_fmtbestfunc(fmt, fmts, 1);
|
||||
}
|
||||
|
||||
u_int32_t
|
||||
chn_fmtbest(u_int32_t fmt, u_int32_t *fmts)
|
||||
snd_fmtbest(u_int32_t fmt, u_int32_t *fmts)
|
||||
{
|
||||
u_int32_t best1, best2;
|
||||
u_int32_t score, score1, score2;
|
||||
|
||||
if (fmtvalid(fmt, fmts))
|
||||
if (snd_fmtvalid(fmt, fmts))
|
||||
return fmt;
|
||||
|
||||
best1 = chn_fmtbeststereo(fmt, fmts);
|
||||
best2 = chn_fmtbestbit(fmt, fmts);
|
||||
best1 = snd_fmtbestchannel(fmt, fmts);
|
||||
best2 = snd_fmtbestbit(fmt, fmts);
|
||||
|
||||
if (best1 != 0 && best2 != 0 && best1 != best2) {
|
||||
if (fmt & AFMT_STEREO)
|
||||
/*if (fmt & AFMT_STEREO)*/
|
||||
if (AFMT_CHANNEL(fmt) > 1)
|
||||
return best1;
|
||||
else {
|
||||
score = score_val(chn_fmtscore(fmt));
|
||||
score1 = score_val(chn_fmtscore(best1));
|
||||
score2 = score_val(chn_fmtscore(best2));
|
||||
score = score_val(snd_fmtscore(fmt));
|
||||
score1 = score_val(snd_fmtscore(best1));
|
||||
score2 = score_val(snd_fmtscore(best2));
|
||||
if (score1 == score2 || score1 == score)
|
||||
return best1;
|
||||
else if (score2 == score)
|
||||
@ -505,309 +436,6 @@ chn_fmtbest(u_int32_t fmt, u_int32_t *fmts)
|
||||
return best2;
|
||||
}
|
||||
|
||||
static struct pcm_feeder *
|
||||
feeder_fmtchain(u_int32_t *to, struct pcm_feeder *source, struct pcm_feeder *stop, int maxdepth)
|
||||
{
|
||||
struct feedertab_entry *fte, *ftebest;
|
||||
struct pcm_feeder *try, *ret;
|
||||
uint32_t fl, qout, qsrc, qdst;
|
||||
int qtype;
|
||||
|
||||
if (to == NULL || to[0] == 0)
|
||||
return NULL;
|
||||
|
||||
DEB(printf("trying %s (0x%08x -> 0x%08x)...\n", source->class->name, source->desc->in, source->desc->out));
|
||||
if (fmtvalid(source->desc->out, to)) {
|
||||
DEB(printf("got it\n"));
|
||||
return source;
|
||||
}
|
||||
|
||||
if (maxdepth < 0)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* WARNING: THIS IS _NOT_ FOR THE FAINT HEART
|
||||
* Disclaimer: I don't expect anybody could understand this
|
||||
* without deep logical and mathematical analysis
|
||||
* involving various unnamed probability theorem.
|
||||
*
|
||||
* This "Best Fit Random Chain Selection" (BLEHBLEHWHATEVER) algorithm
|
||||
* is **extremely** difficult to digest especially when applied to
|
||||
* large sets / numbers of random chains (feeders), each with
|
||||
* unique characteristic providing different sets of in/out format.
|
||||
*
|
||||
* Basically, our FEEDER_FMT (see feeder_fmt.c) chains characteristic:
|
||||
* 1) Format chains
|
||||
* 1.1 "8bit to any, not to 8bit"
|
||||
* 1.1.1 sign can remain consistent, e.g: u8 -> u16[le|be]
|
||||
* 1.1.2 sign can be changed, e.g: u8 -> s16[le|be]
|
||||
* 1.1.3 endian can be changed, e.g: u8 -> u16[le|be]
|
||||
* 1.1.4 both can be changed, e.g: u8 -> [u|s]16[le|be]
|
||||
* 1.2 "Any to 8bit, not from 8bit"
|
||||
* 1.2.1 sign can remain consistent, e.g: s16le -> s8
|
||||
* 1.2.2 sign can be changed, e.g: s16le -> u8
|
||||
* 1.2.3 source endian can be anything e.g: s16[le|be] -> s8
|
||||
* 1.2.4 source endian / sign can be anything e.g: [u|s]16[le|be] -> u8
|
||||
* 1.3 "Any to any where BOTH input and output either 8bit or non-8bit"
|
||||
* 1.3.1 endian MUST remain consistent
|
||||
* 1.3.2 sign CAN be changed
|
||||
* 1.4 "Long jump" is allowed, e.g: from 16bit to 32bit, excluding
|
||||
* 16bit to 24bit .
|
||||
* 2) Channel chains (mono <-> stereo)
|
||||
* 2.1 Both endian and sign MUST remain consistent
|
||||
* 3) Endian chains (big endian <-> little endian)
|
||||
* 3.1 Channels and sign MUST remain consistent
|
||||
* 4) Sign chains (signed <-> unsigned)
|
||||
* 4.1 Channels and endian MUST remain consistent
|
||||
*
|
||||
* .. and the mother of all chaining rules:
|
||||
*
|
||||
* Rules 0: Source and destination MUST not contain multiple selections.
|
||||
* (qtype != FMT_Q_MULTI)
|
||||
*
|
||||
* First of all, our caller ( chn_fmtchain() ) will reduce the possible
|
||||
* multiple from/to formats to a single best format using chn_fmtbest().
|
||||
* Then, using chn_fmtscore(), we determine the chaining characteristic.
|
||||
* Our main goal is to narrow it down until it reach FMT_Q_EQ chaining
|
||||
* type while still adhering above chaining rules.
|
||||
*
|
||||
* The need for this complicated chaining procedures is inevitable,
|
||||
* since currently we have more than 200 different types of FEEDER_FMT
|
||||
* doing various unique format conversion. Without this (the old way),
|
||||
* it is possible to generate broken chain since it doesn't do any
|
||||
* sanity checking to ensure that the output format is "properly aligned"
|
||||
* with the direction of conversion (quality up/down/equal).
|
||||
*
|
||||
* Conversion: s24le to s32le
|
||||
* Possible chain: 1) s24le -> s32le (correct, optimized)
|
||||
* 2) s24le -> s16le -> s32le
|
||||
* (since we have feeder_24to16 and feeder_16to32)
|
||||
* +-- obviously broken!
|
||||
*
|
||||
* Using scoring mechanisme, this will ensure that the chaining
|
||||
* process do the right thing, or at least, give the best chain
|
||||
* possible without causing quality (the 'Q') degradation.
|
||||
*/
|
||||
|
||||
qdst = chn_fmtscore(to[0]);
|
||||
qsrc = chn_fmtscore(source->desc->out);
|
||||
|
||||
#define score_q(s1) score_val(s1)
|
||||
#define score_8bit(s1) ((s1) & 0x700)
|
||||
#define score_non8bit(s1) (!score_8bit(s1))
|
||||
#define score_across8bit(s1, s2) ((score_8bit(s1) && score_non8bit(s2)) || \
|
||||
(score_8bit(s2) && score_non8bit(s1)))
|
||||
|
||||
#define FMT_CHAIN_Q_UP(s1, s2) (score_q(s1) < score_q(s2))
|
||||
#define FMT_CHAIN_Q_DOWN(s1, s2) (score_q(s1) > score_q(s2))
|
||||
#define FMT_CHAIN_Q_EQ(s1, s2) (score_q(s1) == score_q(s2))
|
||||
#define FMT_Q_DOWN_FLAGS(s1, s2) (0x1 | (score_across8bit(s1, s2) ? \
|
||||
0x2 : 0x0))
|
||||
#define FMT_Q_UP_FLAGS(s1, s2) FMT_Q_DOWN_FLAGS(s1, s2)
|
||||
#define FMT_Q_EQ_FLAGS(s1, s2) (0x3ffc | \
|
||||
((score_cheq(s1, s2) && \
|
||||
score_endianeq(s1, s2)) ? \
|
||||
0x1 : 0x0) | \
|
||||
((score_cheq(s1, s2) && \
|
||||
score_signeq(s1, s2)) ? \
|
||||
0x2 : 0x0))
|
||||
|
||||
/* Determine chaining direction and set matching flag */
|
||||
fl = 0x3fff;
|
||||
if (to[1] != 0) {
|
||||
qtype = FMT_Q_MULTI;
|
||||
printf("%s: WARNING: FMT_Q_MULTI chaining. Expect the unexpected.\n", __func__);
|
||||
} else if (FMT_CHAIN_Q_DOWN(qsrc, qdst)) {
|
||||
qtype = FMT_Q_DOWN;
|
||||
fl = FMT_Q_DOWN_FLAGS(qsrc, qdst);
|
||||
} else if (FMT_CHAIN_Q_UP(qsrc, qdst)) {
|
||||
qtype = FMT_Q_UP;
|
||||
fl = FMT_Q_UP_FLAGS(qsrc, qdst);
|
||||
} else {
|
||||
qtype = FMT_Q_EQ;
|
||||
fl = FMT_Q_EQ_FLAGS(qsrc, qdst);
|
||||
}
|
||||
|
||||
ftebest = NULL;
|
||||
|
||||
SLIST_FOREACH(fte, &feedertab, link) {
|
||||
if (fte->desc == NULL)
|
||||
continue;
|
||||
if (fte->desc->type != FEEDER_FMT)
|
||||
continue;
|
||||
qout = chn_fmtscore(fte->desc->out);
|
||||
#define FMT_Q_MULTI_VALIDATE(qt) ((qt) == FMT_Q_MULTI)
|
||||
#define FMT_Q_FL_MATCH(qfl, s1, s2) (((s1) & (qfl)) == ((s2) & (qfl)))
|
||||
#define FMT_Q_UP_VALIDATE(qt, s1, s2, s3) ((qt) == FMT_Q_UP && \
|
||||
score_q(s3) >= score_q(s1) && \
|
||||
score_q(s3) <= score_q(s2))
|
||||
#define FMT_Q_DOWN_VALIDATE(qt, s1, s2, s3) ((qt) == FMT_Q_DOWN && \
|
||||
score_q(s3) <= score_q(s1) && \
|
||||
score_q(s3) >= score_q(s2))
|
||||
#define FMT_Q_EQ_VALIDATE(qt, s1, s2) ((qt) == FMT_Q_EQ && \
|
||||
score_q(s1) == score_q(s2))
|
||||
if (fte->desc->in == source->desc->out &&
|
||||
(FMT_Q_MULTI_VALIDATE(qtype) ||
|
||||
(FMT_Q_FL_MATCH(fl, qout, qdst) &&
|
||||
(FMT_Q_UP_VALIDATE(qtype, qsrc, qdst, qout) ||
|
||||
FMT_Q_DOWN_VALIDATE(qtype, qsrc, qdst, qout) ||
|
||||
FMT_Q_EQ_VALIDATE(qtype, qdst, qout))))) {
|
||||
try = feeder_create(fte->feederclass, fte->desc);
|
||||
if (try) {
|
||||
try->source = source;
|
||||
ret = chainok(try, stop) ? feeder_fmtchain(to, try, stop, maxdepth - 1) : NULL;
|
||||
if (ret != NULL)
|
||||
return ret;
|
||||
feeder_destroy(try);
|
||||
}
|
||||
} else if (fte->desc->in == source->desc->out) {
|
||||
/* XXX quality must be considered! */
|
||||
if (ftebest == NULL)
|
||||
ftebest = fte;
|
||||
}
|
||||
}
|
||||
|
||||
if (ftebest != NULL) {
|
||||
try = feeder_create(ftebest->feederclass, ftebest->desc);
|
||||
if (try) {
|
||||
try->source = source;
|
||||
ret = chainok(try, stop) ? feeder_fmtchain(to, try, stop, maxdepth - 1) : NULL;
|
||||
if (ret != NULL)
|
||||
return ret;
|
||||
feeder_destroy(try);
|
||||
}
|
||||
}
|
||||
|
||||
/* printf("giving up %s...\n", source->class->name); */
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
u_int32_t
|
||||
chn_fmtchain(struct pcm_channel *c, u_int32_t *to)
|
||||
{
|
||||
struct pcm_feeder *try, *del, *stop;
|
||||
u_int32_t tmpfrom[2], tmpto[2], best, *from;
|
||||
int i, max, bestmax;
|
||||
|
||||
KASSERT(c != NULL, ("c == NULL"));
|
||||
KASSERT(c->feeder != NULL, ("c->feeder == NULL"));
|
||||
KASSERT(to != NULL, ("to == NULL"));
|
||||
KASSERT(to[0] != 0, ("to[0] == 0"));
|
||||
|
||||
if (c == NULL || c->feeder == NULL || to == NULL || to[0] == 0)
|
||||
return 0;
|
||||
|
||||
stop = c->feeder;
|
||||
best = 0;
|
||||
|
||||
if (c->direction == PCMDIR_REC && c->feeder->desc->type == FEEDER_ROOT) {
|
||||
from = chn_getcaps(c)->fmtlist;
|
||||
if (from[1] != 0) {
|
||||
best = chn_fmtbest(to[0], from);
|
||||
if (best != 0) {
|
||||
tmpfrom[0] = best;
|
||||
tmpfrom[1] = 0;
|
||||
from = tmpfrom;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
tmpfrom[0] = c->feeder->desc->out;
|
||||
tmpfrom[1] = 0;
|
||||
from = tmpfrom;
|
||||
if (to[1] != 0) {
|
||||
best = chn_fmtbest(from[0], to);
|
||||
if (best != 0) {
|
||||
tmpto[0] = best;
|
||||
tmpto[1] = 0;
|
||||
to = tmpto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define FEEDER_FMTCHAIN_MAXDEPTH 8
|
||||
|
||||
try = NULL;
|
||||
|
||||
if (to[0] != 0 && from[0] != 0 &&
|
||||
to[1] == 0 && from[1] == 0) {
|
||||
max = 0;
|
||||
best = from[0];
|
||||
c->feeder->desc->out = best;
|
||||
do {
|
||||
try = feeder_fmtchain(to, c->feeder, stop, max);
|
||||
DEB(if (try != NULL) {
|
||||
printf("%s: 0x%08x -> 0x%08x (maxdepth: %d)\n",
|
||||
__func__, from[0], to[0], max);
|
||||
});
|
||||
} while (try == NULL && max++ < FEEDER_FMTCHAIN_MAXDEPTH);
|
||||
} else {
|
||||
printf("%s: Using the old-way format chaining!\n", __func__);
|
||||
i = 0;
|
||||
best = 0;
|
||||
bestmax = 100;
|
||||
while (from[i] != 0) {
|
||||
c->feeder->desc->out = from[i];
|
||||
try = NULL;
|
||||
max = 0;
|
||||
do {
|
||||
try = feeder_fmtchain(to, c->feeder, stop, max);
|
||||
} while (try == NULL && max++ < FEEDER_FMTCHAIN_MAXDEPTH);
|
||||
if (try != NULL && max < bestmax) {
|
||||
bestmax = max;
|
||||
best = from[i];
|
||||
}
|
||||
while (try != NULL && try != stop) {
|
||||
del = try;
|
||||
try = try->source;
|
||||
feeder_destroy(del);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (best == 0)
|
||||
return 0;
|
||||
|
||||
c->feeder->desc->out = best;
|
||||
try = feeder_fmtchain(to, c->feeder, stop, bestmax);
|
||||
}
|
||||
if (try == NULL)
|
||||
return 0;
|
||||
|
||||
c->feeder = try;
|
||||
c->align = 0;
|
||||
#ifdef FEEDER_DEBUG
|
||||
printf("\n\nchain: ");
|
||||
#endif
|
||||
while (try && (try != stop)) {
|
||||
#ifdef FEEDER_DEBUG
|
||||
printf("%s [%d]", try->class->name, try->desc->idx);
|
||||
if (try->source)
|
||||
printf(" -> ");
|
||||
#endif
|
||||
if (try->source)
|
||||
try->source->parent = try;
|
||||
if (try->align > 0)
|
||||
c->align += try->align;
|
||||
else if (try->align < 0 && c->align < -try->align)
|
||||
c->align = -try->align;
|
||||
try = try->source;
|
||||
}
|
||||
#ifdef FEEDER_DEBUG
|
||||
printf("%s [%d]\n", try->class->name, try->desc->idx);
|
||||
#endif
|
||||
|
||||
if (c->direction == PCMDIR_REC) {
|
||||
try = c->feeder;
|
||||
while (try != NULL) {
|
||||
if (try->desc->type == FEEDER_ROOT)
|
||||
return try->desc->out;
|
||||
try = try->source;
|
||||
}
|
||||
return best;
|
||||
} else
|
||||
return c->feeder->desc->out;
|
||||
}
|
||||
|
||||
void
|
||||
feeder_printchain(struct pcm_feeder *head)
|
||||
{
|
||||
@ -831,8 +459,6 @@ feed_root(struct pcm_feeder *feeder, struct pcm_channel *ch, u_int8_t *buffer, u
|
||||
int l, offset;
|
||||
|
||||
KASSERT(count > 0, ("feed_root: count == 0"));
|
||||
/* count &= ~((1 << ch->align) - 1); */
|
||||
KASSERT(count > 0, ("feed_root: aligned count == 0 (align = %d)", ch->align));
|
||||
|
||||
if (++ch->feedcount == 0)
|
||||
ch->feedcount = 2;
|
||||
@ -882,13 +508,12 @@ feed_root(struct pcm_feeder *feeder, struct pcm_channel *ch, u_int8_t *buffer, u
|
||||
|
||||
static kobj_method_t feeder_root_methods[] = {
|
||||
KOBJMETHOD(feeder_feed, feed_root),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
static struct feeder_class feeder_root_class = {
|
||||
.name = "feeder_root",
|
||||
.methods = feeder_root_methods,
|
||||
.size = sizeof(struct pcm_feeder),
|
||||
.align = 0,
|
||||
.desc = NULL,
|
||||
.data = NULL,
|
||||
};
|
||||
|
@ -1,5 +1,6 @@
|
||||
/*-
|
||||
* Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
|
||||
* Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org>
|
||||
* Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -35,7 +36,6 @@ struct pcm_feederdesc {
|
||||
|
||||
struct feeder_class {
|
||||
KOBJ_CLASS_FIELDS;
|
||||
int align;
|
||||
struct pcm_feederdesc *desc;
|
||||
void *data;
|
||||
};
|
||||
@ -53,60 +53,160 @@ struct pcm_feeder {
|
||||
void feeder_register(void *p);
|
||||
struct feeder_class *feeder_getclass(struct pcm_feederdesc *desc);
|
||||
|
||||
u_int32_t chn_fmtscore(u_int32_t fmt);
|
||||
u_int32_t chn_fmtbestbit(u_int32_t fmt, u_int32_t *fmts);
|
||||
u_int32_t chn_fmtbeststereo(u_int32_t fmt, u_int32_t *fmts);
|
||||
u_int32_t chn_fmtbest(u_int32_t fmt, u_int32_t *fmts);
|
||||
u_int32_t chn_fmtchain(struct pcm_channel *c, u_int32_t *to);
|
||||
int chn_addfeeder(struct pcm_channel *c, struct feeder_class *fc, struct pcm_feederdesc *desc);
|
||||
u_int32_t snd_fmtscore(u_int32_t fmt);
|
||||
u_int32_t snd_fmtbestbit(u_int32_t fmt, u_int32_t *fmts);
|
||||
u_int32_t snd_fmtbestchannel(u_int32_t fmt, u_int32_t *fmts);
|
||||
u_int32_t snd_fmtbest(u_int32_t fmt, u_int32_t *fmts);
|
||||
|
||||
int chn_addfeeder(struct pcm_channel *c, struct feeder_class *fc,
|
||||
struct pcm_feederdesc *desc);
|
||||
int chn_removefeeder(struct pcm_channel *c);
|
||||
struct pcm_feeder *chn_findfeeder(struct pcm_channel *c, u_int32_t type);
|
||||
void feeder_printchain(struct pcm_feeder *head);
|
||||
int feeder_chain(struct pcm_channel *);
|
||||
|
||||
#define FEEDER_DECLARE(feeder, palign, pdata) \
|
||||
static struct feeder_class feeder ## _class = { \
|
||||
.name = #feeder, \
|
||||
.methods = feeder ## _methods, \
|
||||
.size = sizeof(struct pcm_feeder), \
|
||||
.align = palign, \
|
||||
.desc = feeder ## _desc, \
|
||||
.data = pdata, \
|
||||
}; \
|
||||
SYSINIT(feeder, SI_SUB_DRIVERS, SI_ORDER_ANY, feeder_register, &feeder ## _class);
|
||||
#define FEEDER_DECLARE(feeder, pdata) \
|
||||
static struct feeder_class feeder ## _class = { \
|
||||
.name = #feeder, \
|
||||
.methods = feeder ## _methods, \
|
||||
.size = sizeof(struct pcm_feeder), \
|
||||
.desc = feeder ## _desc, \
|
||||
.data = pdata, \
|
||||
}; \
|
||||
SYSINIT(feeder, SI_SUB_DRIVERS, SI_ORDER_ANY, feeder_register, \
|
||||
&feeder ## _class)
|
||||
|
||||
#define FEEDER_ROOT 0
|
||||
#define FEEDER_FMT 1
|
||||
#define FEEDER_MIXER 2
|
||||
#define FEEDER_RATE 3
|
||||
#define FEEDER_FILTER 4
|
||||
#define FEEDER_VOLUME 5
|
||||
#define FEEDER_SWAPLR 6
|
||||
#define FEEDER_LAST 32
|
||||
enum {
|
||||
FEEDER_ROOT,
|
||||
FEEDER_FORMAT,
|
||||
FEEDER_MIXER,
|
||||
FEEDER_RATE,
|
||||
FEEDER_EQ,
|
||||
FEEDER_VOLUME,
|
||||
FEEDER_MATRIX,
|
||||
FEEDER_LAST,
|
||||
};
|
||||
|
||||
#define FEEDRATE_SRC 1
|
||||
#define FEEDRATE_DST 2
|
||||
/* feeder_format */
|
||||
enum {
|
||||
FEEDFORMAT_CHANNELS
|
||||
};
|
||||
|
||||
/* feeder_mixer */
|
||||
enum {
|
||||
FEEDMIXER_CHANNELS
|
||||
};
|
||||
|
||||
/* feeder_rate */
|
||||
enum {
|
||||
FEEDRATE_SRC,
|
||||
FEEDRATE_DST,
|
||||
FEEDRATE_QUALITY,
|
||||
FEEDRATE_CHANNELS
|
||||
};
|
||||
|
||||
#define FEEDRATE_RATEMIN 1
|
||||
#define FEEDRATE_RATEMAX 2016000 /* 48000 * 42 */
|
||||
|
||||
#define FEEDRATE_RATEMAX 2016000 /* 48000 * 42 */
|
||||
#define FEEDRATE_MIN 1
|
||||
#define FEEDRATE_MAX 0x7fffff /* sign 24bit ~ 8ghz ! */
|
||||
|
||||
#define FEEDRATE_ROUNDHZ 25
|
||||
#define FEEDRATE_ROUNDHZ_MIN 0
|
||||
#define FEEDRATE_ROUNDHZ_MAX 500
|
||||
|
||||
/*
|
||||
* Default buffer size for feeder processing.
|
||||
*
|
||||
* Big = less sndbuf_feed(), more memory usage.
|
||||
* Small = aggresive sndbuf_feed() (perhaps too much), less memory usage.
|
||||
*/
|
||||
#define FEEDBUFSZ 16384
|
||||
#define FEEDBUFSZ_MIN 2048
|
||||
#define FEEDBUFSZ_MAX 131072
|
||||
|
||||
extern int feeder_rate_min;
|
||||
extern int feeder_rate_max;
|
||||
extern int feeder_rate_round;
|
||||
extern int feeder_buffersize;
|
||||
extern int feeder_rate_quality;
|
||||
|
||||
/* feeder_eq */
|
||||
enum {
|
||||
FEEDEQ_CHANNELS,
|
||||
FEEDEQ_RATE,
|
||||
FEEDEQ_TREBLE,
|
||||
FEEDEQ_BASS,
|
||||
FEEDEQ_PREAMP,
|
||||
FEEDEQ_STATE,
|
||||
FEEDEQ_DISABLE,
|
||||
FEEDEQ_ENABLE,
|
||||
FEEDEQ_BYPASS,
|
||||
FEEDEQ_UNKNOWN
|
||||
};
|
||||
|
||||
int feeder_eq_validrate(uint32_t);
|
||||
void feeder_eq_initsys(device_t);
|
||||
|
||||
/* feeder_volume */
|
||||
enum {
|
||||
FEEDVOLUME_CLASS,
|
||||
FEEDVOLUME_CHANNELS,
|
||||
FEEDVOLUME_STATE,
|
||||
FEEDVOLUME_ENABLE,
|
||||
FEEDVOLUME_BYPASS
|
||||
};
|
||||
|
||||
int feeder_volume_apply_matrix(struct pcm_feeder *, struct pcmchan_matrix *);
|
||||
|
||||
/* feeder_matrix */
|
||||
int feeder_matrix_default_id(uint32_t);
|
||||
struct pcmchan_matrix *feeder_matrix_default_channel_map(uint32_t);
|
||||
|
||||
uint32_t feeder_matrix_default_format(uint32_t);
|
||||
|
||||
int feeder_matrix_format_id(uint32_t);
|
||||
struct pcmchan_matrix *feeder_matrix_format_map(uint32_t);
|
||||
|
||||
struct pcmchan_matrix *feeder_matrix_id_map(int);
|
||||
|
||||
int feeder_matrix_setup(struct pcm_feeder *, struct pcmchan_matrix *,
|
||||
struct pcmchan_matrix *);
|
||||
int feeder_matrix_compare(struct pcmchan_matrix *, struct pcmchan_matrix *);
|
||||
|
||||
/* 4Front OSS stuffs */
|
||||
int feeder_matrix_oss_get_channel_order(struct pcmchan_matrix *,
|
||||
unsigned long long *);
|
||||
int feeder_matrix_oss_set_channel_order(struct pcmchan_matrix *,
|
||||
unsigned long long *);
|
||||
|
||||
#if 0
|
||||
/* feeder_matrix */
|
||||
enum {
|
||||
FEEDMATRIX_TYPE,
|
||||
FEEDMATRIX_RESET,
|
||||
FEEDMATRIX_CHANNELS_IN,
|
||||
FEEDMATRIX_CHANNELS_OUT,
|
||||
FEEDMATRIX_SET_MAP
|
||||
};
|
||||
|
||||
enum {
|
||||
FEEDMATRIX_TYPE_NONE,
|
||||
FEEDMATRIX_TYPE_AUTO,
|
||||
FEEDMATRIX_TYPE_2X1,
|
||||
FEEDMATRIX_TYPE_1X2,
|
||||
FEEDMATRIX_TYPE_2X2
|
||||
};
|
||||
|
||||
#define FEEDMATRIX_TYPE_STEREO_TO_MONO FEEDMATRIX_TYPE_2X1
|
||||
#define FEEDMATRIX_TYPE_MONO_TO_STEREO FEEDMATRIX_TYPE_1X2
|
||||
#define FEEDMATRIX_TYPE_SWAP_STEREO FEEDMATRIX_TYPE_2X2
|
||||
#define FEEDMATRIX_MAP(x, y) ((((x) & 0x3f) << 6) | ((y) & 0x3f))
|
||||
#define FEEDMATRIX_MAP_SRC(x) ((x) & 0x3f)
|
||||
#define FEEDMATRIX_MAP_DST(x) (((x) >> 6) & 0x3f)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* By default, various feeders only deal with sign 16/32 bit native-endian
|
||||
* since it should provide the fastest processing path. Processing 8bit samples
|
||||
* is too noisy due to limited dynamic range, while 24bit is quite slow due to
|
||||
* unnatural per-byte read/write. However, for debugging purposes, ensuring
|
||||
* implementation correctness and torture test, the following can be defined:
|
||||
*
|
||||
* SND_FEEDER_MULTIFORMAT - Compile all type of converters, but force
|
||||
* 8bit samples to be converted to 16bit
|
||||
* native-endian for better dynamic range.
|
||||
* Process 24bit samples natively.
|
||||
* SND_FEEDER_FULL_MULTIFORMAT - Ditto, but process 8bit samples natively.
|
||||
*/
|
||||
#ifdef SND_FEEDER_FULL_MULTIFORMAT
|
||||
#undef SND_FEEDER_MULTIFORMAT
|
||||
#define SND_FEEDER_MULTIFORMAT 1
|
||||
#endif
|
||||
|
843
sys/dev/sound/pcm/feeder_chain.c
Normal file
843
sys/dev/sound/pcm/feeder_chain.c
Normal file
@ -0,0 +1,843 @@
|
||||
/*-
|
||||
* Copyright (c) 2008-2009 Ariff Abdullah <ariff@FreeBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
|
||||
#include "feeder_if.h"
|
||||
|
||||
SND_DECLARE_FILE("$FreeBSD$");
|
||||
|
||||
/* chain state */
|
||||
struct feeder_chain_state {
|
||||
uint32_t afmt; /* audio format */
|
||||
uint32_t rate; /* sampling rate */
|
||||
struct pcmchan_matrix *matrix; /* matrix map */
|
||||
};
|
||||
|
||||
/*
|
||||
* chain descriptor that will be passed around from the beginning until the
|
||||
* end of chain process.
|
||||
*/
|
||||
struct feeder_chain_desc {
|
||||
struct feeder_chain_state origin; /* original state */
|
||||
struct feeder_chain_state current; /* current state */
|
||||
struct feeder_chain_state target; /* target state */
|
||||
struct pcm_feederdesc desc; /* feeder descriptor */
|
||||
uint32_t afmt_ne; /* prefered native endian */
|
||||
int mode; /* chain mode */
|
||||
int use_eq; /* need EQ? */
|
||||
int use_matrix; /* need channel matrixing? */
|
||||
int use_volume; /* need softpcmvol? */
|
||||
int dummy; /* dummy passthrough */
|
||||
int expensive; /* possibly expensive */
|
||||
};
|
||||
|
||||
#define FEEDER_CHAIN_LEAN 0
|
||||
#define FEEDER_CHAIN_16 1
|
||||
#define FEEDER_CHAIN_32 2
|
||||
#define FEEDER_CHAIN_MULTI 3
|
||||
#define FEEDER_CHAIN_FULLMULTI 4
|
||||
#define FEEDER_CHAIN_LAST 5
|
||||
|
||||
#if defined(SND_FEEDER_FULL_MULTIFORMAT)
|
||||
#define FEEDER_CHAIN_DEFAULT FEEDER_CHAIN_FULLMULTI
|
||||
#elif defined(SND_FEEDER_MULTIFORMAT)
|
||||
#define FEEDER_CHAIN_DEFAULT FEEDER_CHAIN_MULTI
|
||||
#else
|
||||
#define FEEDER_CHAIN_DEFAULT FEEDER_CHAIN_LEAN
|
||||
#endif
|
||||
|
||||
/*
|
||||
* List of prefered formats that might be required during
|
||||
* processing. It will be decided through snd_fmtbest().
|
||||
*/
|
||||
|
||||
/* 'Lean' mode, signed 16 or 32 bit native endian. */
|
||||
static uint32_t feeder_chain_formats_lean[] = {
|
||||
AFMT_S16_NE, AFMT_S32_NE,
|
||||
0
|
||||
};
|
||||
|
||||
/* Force everything to signed 16 bit native endian. */
|
||||
static uint32_t feeder_chain_formats_16[] = {
|
||||
AFMT_S16_NE,
|
||||
0
|
||||
};
|
||||
|
||||
/* Force everything to signed 32 bit native endian. */
|
||||
static uint32_t feeder_chain_formats_32[] = {
|
||||
AFMT_S32_NE,
|
||||
0
|
||||
};
|
||||
|
||||
/* Multiple choices, all except 8 bit. */
|
||||
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,
|
||||
0
|
||||
};
|
||||
|
||||
/* Everything that is convertible. */
|
||||
static uint32_t feeder_chain_formats_fullmulti[] = {
|
||||
AFMT_S8, AFMT_U8,
|
||||
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,
|
||||
0
|
||||
};
|
||||
|
||||
static uint32_t *feeder_chain_formats[FEEDER_CHAIN_LAST] = {
|
||||
[FEEDER_CHAIN_LEAN] = feeder_chain_formats_lean,
|
||||
[FEEDER_CHAIN_16] = feeder_chain_formats_16,
|
||||
[FEEDER_CHAIN_32] = feeder_chain_formats_32,
|
||||
[FEEDER_CHAIN_MULTI] = feeder_chain_formats_multi,
|
||||
[FEEDER_CHAIN_FULLMULTI] = feeder_chain_formats_fullmulti
|
||||
};
|
||||
|
||||
static int feeder_chain_mode = FEEDER_CHAIN_DEFAULT;
|
||||
|
||||
#if defined(_KERNEL) && defined(SND_DEBUG) && defined(SND_FEEDER_FULL_MULTIFORMAT)
|
||||
TUNABLE_INT("hw.snd.feeder_chain_mode", &feeder_chain_mode);
|
||||
SYSCTL_INT(_hw_snd, OID_AUTO, feeder_chain_mode, CTLFLAG_RW,
|
||||
&feeder_chain_mode, 0,
|
||||
"feeder chain mode "
|
||||
"(0=lean, 1=16bit, 2=32bit, 3=multiformat, 4=fullmultiformat)");
|
||||
#endif
|
||||
|
||||
/*
|
||||
* feeder_build_format(): Chain any format converter.
|
||||
*/
|
||||
static int
|
||||
feeder_build_format(struct pcm_channel *c, struct feeder_chain_desc *cdesc)
|
||||
{
|
||||
struct feeder_class *fc;
|
||||
struct pcm_feederdesc *desc;
|
||||
int ret;
|
||||
|
||||
desc = &(cdesc->desc);
|
||||
desc->type = FEEDER_FORMAT;
|
||||
desc->in = 0;
|
||||
desc->out = 0;
|
||||
desc->flags = 0;
|
||||
|
||||
fc = feeder_getclass(desc);
|
||||
if (fc == NULL) {
|
||||
device_printf(c->dev,
|
||||
"%s(): can't find feeder_format\n", __func__);
|
||||
return (ENOTSUP);
|
||||
}
|
||||
|
||||
desc->in = cdesc->current.afmt;
|
||||
desc->out = cdesc->target.afmt;
|
||||
|
||||
ret = chn_addfeeder(c, fc, desc);
|
||||
if (ret != 0) {
|
||||
device_printf(c->dev,
|
||||
"%s(): can't add feeder_format\n", __func__);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
c->feederflags |= 1 << FEEDER_FORMAT;
|
||||
|
||||
cdesc->current.afmt = cdesc->target.afmt;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* feeder_build_formatne(): Chain format converter that suite best for native
|
||||
* endian format.
|
||||
*/
|
||||
static int
|
||||
feeder_build_formatne(struct pcm_channel *c, struct feeder_chain_desc *cdesc)
|
||||
{
|
||||
struct feeder_chain_state otarget;
|
||||
int ret;
|
||||
|
||||
if (cdesc->afmt_ne == 0 ||
|
||||
AFMT_ENCODING(cdesc->current.afmt) == cdesc->afmt_ne)
|
||||
return (0);
|
||||
|
||||
otarget = cdesc->target;
|
||||
cdesc->target = cdesc->current;
|
||||
cdesc->target.afmt = SND_FORMAT(cdesc->afmt_ne,
|
||||
cdesc->current.matrix->channels, cdesc->current.matrix->ext);
|
||||
|
||||
ret = feeder_build_format(c, cdesc);
|
||||
if (ret != 0)
|
||||
return (ret);
|
||||
|
||||
cdesc->target = otarget;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* feeder_build_rate(): Chain sample rate converter.
|
||||
*/
|
||||
static int
|
||||
feeder_build_rate(struct pcm_channel *c, struct feeder_chain_desc *cdesc)
|
||||
{
|
||||
struct feeder_class *fc;
|
||||
struct pcm_feeder *f;
|
||||
struct pcm_feederdesc *desc;
|
||||
int ret;
|
||||
|
||||
ret = feeder_build_formatne(c, cdesc);
|
||||
if (ret != 0)
|
||||
return (ret);
|
||||
|
||||
desc = &(cdesc->desc);
|
||||
desc->type = FEEDER_RATE;
|
||||
desc->in = 0;
|
||||
desc->out = 0;
|
||||
desc->flags = 0;
|
||||
|
||||
fc = feeder_getclass(desc);
|
||||
if (fc == NULL) {
|
||||
device_printf(c->dev,
|
||||
"%s(): can't find feeder_rate\n", __func__);
|
||||
return (ENOTSUP);
|
||||
}
|
||||
|
||||
desc->in = cdesc->current.afmt;
|
||||
desc->out = desc->in;
|
||||
|
||||
ret = chn_addfeeder(c, fc, desc);
|
||||
if (ret != 0) {
|
||||
device_printf(c->dev,
|
||||
"%s(): can't add feeder_rate\n", __func__);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
f = c->feeder;
|
||||
|
||||
/*
|
||||
* If in 'dummy' mode (possibly due to passthrough mode), set the
|
||||
* conversion quality to the lowest possible (should be fastest) since
|
||||
* listener won't be hearing anything. Theoretically we can just
|
||||
* disable it, but that will cause weird runtime behaviour:
|
||||
* application appear to play something that is either too fast or too
|
||||
* slow.
|
||||
*/
|
||||
if (cdesc->dummy != 0) {
|
||||
ret = FEEDER_SET(f, FEEDRATE_QUALITY, 0);
|
||||
if (ret != 0) {
|
||||
device_printf(c->dev,
|
||||
"%s(): can't set resampling quality\n", __func__);
|
||||
return (ret);
|
||||
}
|
||||
}
|
||||
|
||||
ret = FEEDER_SET(f, FEEDRATE_SRC, cdesc->current.rate);
|
||||
if (ret != 0) {
|
||||
device_printf(c->dev,
|
||||
"%s(): can't set source rate\n", __func__);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
ret = FEEDER_SET(f, FEEDRATE_DST, cdesc->target.rate);
|
||||
if (ret != 0) {
|
||||
device_printf(c->dev,
|
||||
"%s(): can't set destination rate\n", __func__);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
c->feederflags |= 1 << FEEDER_RATE;
|
||||
|
||||
cdesc->current.rate = cdesc->target.rate;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* feeder_build_matrix(): Chain channel matrixing converter.
|
||||
*/
|
||||
static int
|
||||
feeder_build_matrix(struct pcm_channel *c, struct feeder_chain_desc *cdesc)
|
||||
{
|
||||
struct feeder_class *fc;
|
||||
struct pcm_feeder *f;
|
||||
struct pcm_feederdesc *desc;
|
||||
int ret;
|
||||
|
||||
ret = feeder_build_formatne(c, cdesc);
|
||||
if (ret != 0)
|
||||
return (ret);
|
||||
|
||||
desc = &(cdesc->desc);
|
||||
desc->type = FEEDER_MATRIX;
|
||||
desc->in = 0;
|
||||
desc->out = 0;
|
||||
desc->flags = 0;
|
||||
|
||||
fc = feeder_getclass(desc);
|
||||
if (fc == NULL) {
|
||||
device_printf(c->dev,
|
||||
"%s(): can't find feeder_matrix\n", __func__);
|
||||
return (ENOTSUP);
|
||||
}
|
||||
|
||||
desc->in = cdesc->current.afmt;
|
||||
desc->out = SND_FORMAT(cdesc->current.afmt,
|
||||
cdesc->target.matrix->channels, cdesc->target.matrix->ext);
|
||||
|
||||
ret = chn_addfeeder(c, fc, desc);
|
||||
if (ret != 0) {
|
||||
device_printf(c->dev,
|
||||
"%s(): can't add feeder_matrix\n", __func__);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
f = c->feeder;
|
||||
ret = feeder_matrix_setup(f, cdesc->current.matrix,
|
||||
cdesc->target.matrix);
|
||||
if (ret != 0) {
|
||||
device_printf(c->dev,
|
||||
"%s(): feeder_matrix_setup() failed\n", __func__);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
c->feederflags |= 1 << FEEDER_MATRIX;
|
||||
|
||||
cdesc->current.afmt = desc->out;
|
||||
cdesc->current.matrix = cdesc->target.matrix;
|
||||
cdesc->use_matrix = 0;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* feeder_build_volume(): Chain soft volume.
|
||||
*/
|
||||
static int
|
||||
feeder_build_volume(struct pcm_channel *c, struct feeder_chain_desc *cdesc)
|
||||
{
|
||||
struct feeder_class *fc;
|
||||
struct pcm_feeder *f;
|
||||
struct pcm_feederdesc *desc;
|
||||
int ret;
|
||||
|
||||
ret = feeder_build_formatne(c, cdesc);
|
||||
if (ret != 0)
|
||||
return (ret);
|
||||
|
||||
desc = &(cdesc->desc);
|
||||
desc->type = FEEDER_VOLUME;
|
||||
desc->in = 0;
|
||||
desc->out = 0;
|
||||
desc->flags = 0;
|
||||
|
||||
fc = feeder_getclass(desc);
|
||||
if (fc == NULL) {
|
||||
device_printf(c->dev,
|
||||
"%s(): can't find feeder_volume\n", __func__);
|
||||
return (ENOTSUP);
|
||||
}
|
||||
|
||||
desc->in = cdesc->current.afmt;
|
||||
desc->out = desc->in;
|
||||
|
||||
ret = chn_addfeeder(c, fc, desc);
|
||||
if (ret != 0) {
|
||||
device_printf(c->dev,
|
||||
"%s(): can't add feeder_volume\n", __func__);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
f = c->feeder;
|
||||
|
||||
/*
|
||||
* If in 'dummy' mode (possibly due to passthrough mode), set BYPASS
|
||||
* mode since listener won't be hearing anything. Theoretically we can
|
||||
* just disable it, but that will confuse volume per channel mixer.
|
||||
*/
|
||||
if (cdesc->dummy != 0) {
|
||||
ret = FEEDER_SET(f, FEEDVOLUME_STATE, FEEDVOLUME_BYPASS);
|
||||
if (ret != 0) {
|
||||
device_printf(c->dev,
|
||||
"%s(): can't set volume bypass\n", __func__);
|
||||
return (ret);
|
||||
}
|
||||
}
|
||||
|
||||
ret = feeder_volume_apply_matrix(f, cdesc->current.matrix);
|
||||
if (ret != 0) {
|
||||
device_printf(c->dev,
|
||||
"%s(): feeder_volume_apply_matrix() failed\n", __func__);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
c->feederflags |= 1 << FEEDER_VOLUME;
|
||||
|
||||
cdesc->use_volume = 0;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* feeder_build_eq(): Chain parametric software equalizer.
|
||||
*/
|
||||
static int
|
||||
feeder_build_eq(struct pcm_channel *c, struct feeder_chain_desc *cdesc)
|
||||
{
|
||||
struct feeder_class *fc;
|
||||
struct pcm_feeder *f;
|
||||
struct pcm_feederdesc *desc;
|
||||
int ret;
|
||||
|
||||
ret = feeder_build_formatne(c, cdesc);
|
||||
if (ret != 0)
|
||||
return (ret);
|
||||
|
||||
desc = &(cdesc->desc);
|
||||
desc->type = FEEDER_EQ;
|
||||
desc->in = 0;
|
||||
desc->out = 0;
|
||||
desc->flags = 0;
|
||||
|
||||
fc = feeder_getclass(desc);
|
||||
if (fc == NULL) {
|
||||
device_printf(c->dev,
|
||||
"%s(): can't find feeder_eq\n", __func__);
|
||||
return (ENOTSUP);
|
||||
}
|
||||
|
||||
desc->in = cdesc->current.afmt;
|
||||
desc->out = desc->in;
|
||||
|
||||
ret = chn_addfeeder(c, fc, desc);
|
||||
if (ret != 0) {
|
||||
device_printf(c->dev,
|
||||
"%s(): can't add feeder_eq\n", __func__);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
f = c->feeder;
|
||||
|
||||
ret = FEEDER_SET(f, FEEDEQ_RATE, cdesc->current.rate);
|
||||
if (ret != 0) {
|
||||
device_printf(c->dev,
|
||||
"%s(): can't set rate on feeder_eq\n", __func__);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
c->feederflags |= 1 << FEEDER_EQ;
|
||||
|
||||
cdesc->use_eq = 0;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* feeder_build_root(): Chain root feeder, the top, father of all.
|
||||
*/
|
||||
static int
|
||||
feeder_build_root(struct pcm_channel *c, struct feeder_chain_desc *cdesc)
|
||||
{
|
||||
struct feeder_class *fc;
|
||||
int ret;
|
||||
|
||||
fc = feeder_getclass(NULL);
|
||||
if (fc == NULL) {
|
||||
device_printf(c->dev,
|
||||
"%s(): can't find feeder_root\n", __func__);
|
||||
return (ENOTSUP);
|
||||
}
|
||||
|
||||
ret = chn_addfeeder(c, fc, NULL);
|
||||
if (ret != 0) {
|
||||
device_printf(c->dev,
|
||||
"%s(): can't add feeder_root\n", __func__);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
c->feederflags |= 1 << FEEDER_ROOT;
|
||||
|
||||
c->feeder->desc->in = cdesc->current.afmt;
|
||||
c->feeder->desc->out = cdesc->current.afmt;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* feeder_build_mixer(): Chain software mixer for virtual channels.
|
||||
*/
|
||||
static int
|
||||
feeder_build_mixer(struct pcm_channel *c, struct feeder_chain_desc *cdesc)
|
||||
{
|
||||
struct feeder_class *fc;
|
||||
struct pcm_feederdesc *desc;
|
||||
int ret;
|
||||
|
||||
desc = &(cdesc->desc);
|
||||
desc->type = FEEDER_MIXER;
|
||||
desc->in = 0;
|
||||
desc->out = 0;
|
||||
desc->flags = 0;
|
||||
|
||||
fc = feeder_getclass(desc);
|
||||
if (fc == NULL) {
|
||||
device_printf(c->dev,
|
||||
"%s(): can't find feeder_mixer\n", __func__);
|
||||
return (ENOTSUP);
|
||||
}
|
||||
|
||||
desc->in = cdesc->current.afmt;
|
||||
desc->out = desc->in;
|
||||
|
||||
ret = chn_addfeeder(c, fc, desc);
|
||||
if (ret != 0) {
|
||||
device_printf(c->dev,
|
||||
"%s(): can't add feeder_mixer\n", __func__);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
c->feederflags |= 1 << FEEDER_MIXER;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Macrosses to ease our job doing stuffs later. */
|
||||
#define FEEDER_BW(c, t) ((c)->t.matrix->channels * (c)->t.rate)
|
||||
|
||||
#define FEEDRATE_UP(c) ((c)->target.rate > (c)->current.rate)
|
||||
#define FEEDRATE_DOWN(c) ((c)->target.rate < (c)->current.rate)
|
||||
#define FEEDRATE_REQUIRED(c) (FEEDRATE_UP(c) || FEEDRATE_DOWN(c))
|
||||
|
||||
#define FEEDMATRIX_UP(c) ((c)->target.matrix->channels > \
|
||||
(c)->current.matrix->channels)
|
||||
#define FEEDMATRIX_DOWN(c) ((c)->target.matrix->channels < \
|
||||
(c)->current.matrix->channels)
|
||||
#define FEEDMATRIX_REQUIRED(c) (FEEDMATRIX_UP(c) || \
|
||||
FEEDMATRIX_DOWN(c) || (c)->use_matrix != 0)
|
||||
|
||||
#define FEEDFORMAT_REQUIRED(c) (AFMT_ENCODING((c)->current.afmt) != \
|
||||
AFMT_ENCODING((c)->target.afmt))
|
||||
|
||||
#define FEEDVOLUME_REQUIRED(c) ((c)->use_volume != 0)
|
||||
|
||||
#define FEEDEQ_VALIDRATE(c, t) (feeder_eq_validrate((c)->t.rate) != 0)
|
||||
#define FEEDEQ_ECONOMY(c) (FEEDER_BW(c, current) < FEEDER_BW(c, target))
|
||||
#define FEEDEQ_REQUIRED(c) ((c)->use_eq != 0 && \
|
||||
FEEDEQ_VALIDRATE(c, current))
|
||||
|
||||
#define FEEDFORMAT_NE_REQUIRED(c) \
|
||||
((c)->afmt_ne != AFMT_S32_NE && \
|
||||
(((c)->mode == FEEDER_CHAIN_16 && \
|
||||
AFMT_ENCODING((c)->current.afmt) != AFMT_S16_NE) || \
|
||||
((c)->mode == FEEDER_CHAIN_32 && \
|
||||
AFMT_ENCODING((c)->current.afmt) != AFMT_S32_NE) || \
|
||||
(c)->mode == FEEDER_CHAIN_FULLMULTI || \
|
||||
((c)->mode == FEEDER_CHAIN_MULTI && \
|
||||
((c)->current.afmt & AFMT_8BIT)) || \
|
||||
((c)->mode == FEEDER_CHAIN_LEAN && \
|
||||
!((c)->current.afmt & (AFMT_S16_NE | AFMT_S32_NE)))))
|
||||
|
||||
int
|
||||
feeder_chain(struct pcm_channel *c)
|
||||
{
|
||||
struct snddev_info *d;
|
||||
struct pcmchan_caps *caps;
|
||||
struct feeder_chain_desc cdesc;
|
||||
struct pcmchan_matrix *hwmatrix, *softmatrix;
|
||||
uint32_t hwfmt, softfmt;
|
||||
int ret;
|
||||
|
||||
CHN_LOCKASSERT(c);
|
||||
|
||||
/* Remove everything first. */
|
||||
while (chn_removefeeder(c) == 0)
|
||||
;
|
||||
|
||||
KASSERT(c->feeder == NULL, ("feeder chain not empty"));
|
||||
|
||||
/* clear and populate chain descriptor. */
|
||||
bzero(&cdesc, sizeof(cdesc));
|
||||
|
||||
switch (feeder_chain_mode) {
|
||||
case FEEDER_CHAIN_LEAN:
|
||||
case FEEDER_CHAIN_16:
|
||||
case FEEDER_CHAIN_32:
|
||||
#if defined(SND_FEEDER_MULTIFORMAT) || defined(SND_FEEDER_FULL_MULTIFORMAT)
|
||||
case FEEDER_CHAIN_MULTI:
|
||||
#endif
|
||||
#if defined(SND_FEEDER_FULL_MULTIFORMAT)
|
||||
case FEEDER_CHAIN_FULLMULTI:
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
feeder_chain_mode = FEEDER_CHAIN_DEFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
cdesc.mode = feeder_chain_mode;
|
||||
cdesc.expensive = 1; /* XXX faster.. */
|
||||
|
||||
#define VCHAN_PASSTHROUGH(c) (((c)->flags & (CHN_F_VIRTUAL | \
|
||||
CHN_F_PASSTHROUGH)) == \
|
||||
(CHN_F_VIRTUAL | CHN_F_PASSTHROUGH))
|
||||
|
||||
/* Get the best possible hardware format. */
|
||||
if (VCHAN_PASSTHROUGH(c))
|
||||
hwfmt = c->parentchannel->format;
|
||||
else {
|
||||
caps = chn_getcaps(c);
|
||||
if (caps == NULL || caps->fmtlist == NULL) {
|
||||
device_printf(c->dev,
|
||||
"%s(): failed to get channel caps\n", __func__);
|
||||
return (ENODEV);
|
||||
}
|
||||
|
||||
if ((c->format & AFMT_PASSTHROUGH) &&
|
||||
!snd_fmtvalid(c->format, caps->fmtlist))
|
||||
return (ENODEV);
|
||||
|
||||
hwfmt = snd_fmtbest(c->format, caps->fmtlist);
|
||||
if (hwfmt == 0 || !snd_fmtvalid(hwfmt, caps->fmtlist)) {
|
||||
device_printf(c->dev,
|
||||
"%s(): invalid hardware format 0x%08x\n",
|
||||
__func__, hwfmt);
|
||||
{
|
||||
int i;
|
||||
for (i = 0; caps->fmtlist[i] != 0; i++)
|
||||
printf("0x%08x\n", caps->fmtlist[i]);
|
||||
printf("Req: 0x%08x\n", c->format);
|
||||
}
|
||||
return (ENODEV);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The 'hardware' possibly have different intepretation of channel
|
||||
* matrixing, so get it first .....
|
||||
*/
|
||||
hwmatrix = CHANNEL_GETMATRIX(c->methods, c->devinfo, hwfmt);
|
||||
if (hwmatrix == NULL) {
|
||||
device_printf(c->dev,
|
||||
"%s(): failed to acquire hw matrix [0x%08x]\n",
|
||||
__func__, hwfmt);
|
||||
return (ENODEV);
|
||||
}
|
||||
/* ..... and rebuild hwfmt. */
|
||||
hwfmt = SND_FORMAT(hwfmt, hwmatrix->channels, hwmatrix->ext);
|
||||
|
||||
/* Reset and rebuild default channel format/matrix map. */
|
||||
softfmt = c->format;
|
||||
softmatrix = &c->matrix;
|
||||
if (softmatrix->channels != AFMT_CHANNEL(softfmt) ||
|
||||
softmatrix->ext != AFMT_EXTCHANNEL(softfmt)) {
|
||||
softmatrix = feeder_matrix_format_map(softfmt);
|
||||
if (softmatrix == NULL) {
|
||||
device_printf(c->dev,
|
||||
"%s(): failed to acquire soft matrix [0x%08x]\n",
|
||||
__func__, softfmt);
|
||||
return (ENODEV);
|
||||
}
|
||||
c->matrix = *softmatrix;
|
||||
c->matrix.id = SND_CHN_MATRIX_PCMCHANNEL;
|
||||
}
|
||||
softfmt = SND_FORMAT(softfmt, softmatrix->channels, softmatrix->ext);
|
||||
if (softfmt != c->format)
|
||||
device_printf(c->dev,
|
||||
"%s(): WARNING: %s Soft format 0x%08x -> 0x%08x\n",
|
||||
__func__, CHN_DIRSTR(c), c->format, softfmt);
|
||||
|
||||
/*
|
||||
* PLAY and REC are opposite.
|
||||
*/
|
||||
if (c->direction == PCMDIR_PLAY) {
|
||||
cdesc.origin.afmt = softfmt;
|
||||
cdesc.origin.matrix = softmatrix;
|
||||
cdesc.origin.rate = c->speed;
|
||||
cdesc.target.afmt = hwfmt;
|
||||
cdesc.target.matrix = hwmatrix;
|
||||
cdesc.target.rate = sndbuf_getspd(c->bufhard);
|
||||
} else {
|
||||
cdesc.origin.afmt = hwfmt;
|
||||
cdesc.origin.matrix = hwmatrix;
|
||||
cdesc.origin.rate = sndbuf_getspd(c->bufhard);
|
||||
cdesc.target.afmt = softfmt;
|
||||
cdesc.target.matrix = softmatrix;
|
||||
cdesc.target.rate = c->speed;
|
||||
}
|
||||
|
||||
d = c->parentsnddev;
|
||||
|
||||
/*
|
||||
* If channel is in bitperfect or passthrough mode, make it appear
|
||||
* that 'origin' and 'target' identical, skipping mostly chain
|
||||
* procedures.
|
||||
*/
|
||||
if (CHN_BITPERFECT(c) || (c->format & AFMT_PASSTHROUGH)) {
|
||||
if (c->direction == PCMDIR_PLAY)
|
||||
cdesc.origin = cdesc.target;
|
||||
else
|
||||
cdesc.target = cdesc.origin;
|
||||
c->format = cdesc.target.afmt;
|
||||
c->speed = cdesc.target.rate;
|
||||
} else {
|
||||
/* hwfmt is not convertible, so 'dummy' it. */
|
||||
if (hwfmt & AFMT_PASSTHROUGH)
|
||||
cdesc.dummy = 1;
|
||||
|
||||
if ((softfmt & AFMT_CONVERTIBLE) &&
|
||||
(((d->flags & SD_F_VPC) && !(c->flags & CHN_F_HAS_VCHAN)) ||
|
||||
(!(d->flags & SD_F_VPC) && (d->flags & SD_F_SOFTPCMVOL) &&
|
||||
!(c->flags & CHN_F_VIRTUAL))))
|
||||
cdesc.use_volume = 1;
|
||||
|
||||
if (feeder_matrix_compare(cdesc.origin.matrix,
|
||||
cdesc.target.matrix) != 0)
|
||||
cdesc.use_matrix = 1;
|
||||
|
||||
/* Soft EQ only applicable for PLAY. */
|
||||
if (cdesc.dummy == 0 &&
|
||||
c->direction == PCMDIR_PLAY && (d->flags & SD_F_EQ) &&
|
||||
(((d->flags & SD_F_EQ_PC) &&
|
||||
!(c->flags & CHN_F_HAS_VCHAN)) ||
|
||||
(!(d->flags & SD_F_EQ_PC) && !(c->flags & CHN_F_VIRTUAL))))
|
||||
cdesc.use_eq = 1;
|
||||
|
||||
if (FEEDFORMAT_NE_REQUIRED(&cdesc)) {
|
||||
cdesc.afmt_ne =
|
||||
(cdesc.dummy != 0) ?
|
||||
snd_fmtbest(AFMT_ENCODING(softfmt),
|
||||
feeder_chain_formats[cdesc.mode]) :
|
||||
snd_fmtbest(AFMT_ENCODING(cdesc.target.afmt),
|
||||
feeder_chain_formats[cdesc.mode]);
|
||||
if (cdesc.afmt_ne == 0) {
|
||||
device_printf(c->dev,
|
||||
"%s(): snd_fmtbest failed!\n", __func__);
|
||||
cdesc.afmt_ne =
|
||||
(((cdesc.dummy != 0) ? softfmt :
|
||||
cdesc.target.afmt) &
|
||||
(AFMT_24BIT | AFMT_32BIT)) ?
|
||||
AFMT_S32_NE : AFMT_S16_NE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cdesc.current = cdesc.origin;
|
||||
|
||||
/* Build everything. */
|
||||
|
||||
c->feederflags = 0;
|
||||
|
||||
#define FEEDER_BUILD(t) do { \
|
||||
ret = feeder_build_##t(c, &cdesc); \
|
||||
if (ret != 0) \
|
||||
return (ret); \
|
||||
} while (0)
|
||||
|
||||
if (!(c->flags & CHN_F_HAS_VCHAN) || c->direction == PCMDIR_REC)
|
||||
FEEDER_BUILD(root);
|
||||
else if (c->direction == PCMDIR_PLAY && (c->flags & CHN_F_HAS_VCHAN))
|
||||
FEEDER_BUILD(mixer);
|
||||
else
|
||||
return (ENOTSUP);
|
||||
|
||||
/*
|
||||
* The basic idea is: The smaller the bandwidth, the cheaper the
|
||||
* conversion process, with following constraints:-
|
||||
*
|
||||
* 1) Almost all feeders work best in 16/32 native endian.
|
||||
* 2) Try to avoid 8bit feeders due to poor dynamic range.
|
||||
* 3) Avoid volume, format, matrix and rate in BITPERFECT or
|
||||
* PASSTHROUGH mode.
|
||||
* 4) Try putting volume before EQ or rate. Should help to
|
||||
* avoid/reduce possible clipping.
|
||||
* 5) EQ require specific, valid rate, unless it allow sloppy
|
||||
* conversion.
|
||||
*/
|
||||
if (FEEDMATRIX_UP(&cdesc)) {
|
||||
if (FEEDEQ_REQUIRED(&cdesc) &&
|
||||
(!FEEDEQ_VALIDRATE(&cdesc, target) ||
|
||||
(cdesc.expensive == 0 && FEEDEQ_ECONOMY(&cdesc))))
|
||||
FEEDER_BUILD(eq);
|
||||
if (FEEDRATE_REQUIRED(&cdesc))
|
||||
FEEDER_BUILD(rate);
|
||||
FEEDER_BUILD(matrix);
|
||||
if (FEEDVOLUME_REQUIRED(&cdesc))
|
||||
FEEDER_BUILD(volume);
|
||||
if (FEEDEQ_REQUIRED(&cdesc))
|
||||
FEEDER_BUILD(eq);
|
||||
} else if (FEEDMATRIX_DOWN(&cdesc)) {
|
||||
FEEDER_BUILD(matrix);
|
||||
if (FEEDVOLUME_REQUIRED(&cdesc))
|
||||
FEEDER_BUILD(volume);
|
||||
if (FEEDEQ_REQUIRED(&cdesc) &&
|
||||
(!FEEDEQ_VALIDRATE(&cdesc, target) ||
|
||||
FEEDEQ_ECONOMY(&cdesc)))
|
||||
FEEDER_BUILD(eq);
|
||||
if (FEEDRATE_REQUIRED(&cdesc))
|
||||
FEEDER_BUILD(rate);
|
||||
if (FEEDEQ_REQUIRED(&cdesc))
|
||||
FEEDER_BUILD(eq);
|
||||
} else {
|
||||
if (FEEDRATE_DOWN(&cdesc)) {
|
||||
if (FEEDEQ_REQUIRED(&cdesc) &&
|
||||
!FEEDEQ_VALIDRATE(&cdesc, target)) {
|
||||
if (FEEDVOLUME_REQUIRED(&cdesc))
|
||||
FEEDER_BUILD(volume);
|
||||
FEEDER_BUILD(eq);
|
||||
}
|
||||
FEEDER_BUILD(rate);
|
||||
}
|
||||
if (FEEDMATRIX_REQUIRED(&cdesc))
|
||||
FEEDER_BUILD(matrix);
|
||||
if (FEEDVOLUME_REQUIRED(&cdesc))
|
||||
FEEDER_BUILD(volume);
|
||||
if (FEEDRATE_UP(&cdesc)) {
|
||||
if (FEEDEQ_REQUIRED(&cdesc) &&
|
||||
!FEEDEQ_VALIDRATE(&cdesc, target))
|
||||
FEEDER_BUILD(eq);
|
||||
FEEDER_BUILD(rate);
|
||||
}
|
||||
if (FEEDEQ_REQUIRED(&cdesc))
|
||||
FEEDER_BUILD(eq);
|
||||
}
|
||||
|
||||
if (FEEDFORMAT_REQUIRED(&cdesc))
|
||||
FEEDER_BUILD(format);
|
||||
|
||||
if (c->direction == PCMDIR_REC && (c->flags & CHN_F_HAS_VCHAN))
|
||||
FEEDER_BUILD(mixer);
|
||||
|
||||
sndbuf_setfmt(c->bufsoft, c->format);
|
||||
sndbuf_setspd(c->bufsoft, c->speed);
|
||||
|
||||
sndbuf_setfmt(c->bufhard, hwfmt);
|
||||
|
||||
chn_syncstate(c);
|
||||
|
||||
return (0);
|
||||
}
|
703
sys/dev/sound/pcm/feeder_eq.c
Normal file
703
sys/dev/sound/pcm/feeder_eq.c
Normal file
@ -0,0 +1,703 @@
|
||||
/*-
|
||||
* Copyright (c) 2008-2009 Ariff Abdullah <ariff@FreeBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* feeder_eq: Parametric (compile time) Software Equalizer. Though accidental,
|
||||
* it proves good enough for educational and general consumption.
|
||||
*
|
||||
* "Cookbook formulae for audio EQ biquad filter coefficients"
|
||||
* by Robert Bristow-Johnson <rbj@audioimagination.com>
|
||||
* - http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
|
||||
*/
|
||||
|
||||
#ifdef _KERNEL
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
#include <dev/sound/pcm/pcm.h>
|
||||
#include "feeder_if.h"
|
||||
|
||||
#define SND_USE_FXDIV
|
||||
#include "snd_fxdiv_gen.h"
|
||||
|
||||
SND_DECLARE_FILE("$FreeBSD$");
|
||||
#endif
|
||||
|
||||
#include "feeder_eq_gen.h"
|
||||
|
||||
#define FEEDEQ_LEVELS \
|
||||
(((FEEDEQ_GAIN_MAX - FEEDEQ_GAIN_MIN) * \
|
||||
(FEEDEQ_GAIN_DIV / FEEDEQ_GAIN_STEP)) + 1)
|
||||
|
||||
#define FEEDEQ_L2GAIN(v) \
|
||||
((int)min(((v) * FEEDEQ_LEVELS) / 100, FEEDEQ_LEVELS - 1))
|
||||
|
||||
#define FEEDEQ_PREAMP_IPART(x) (abs(x) >> FEEDEQ_GAIN_SHIFT)
|
||||
#define FEEDEQ_PREAMP_FPART(x) (abs(x) & FEEDEQ_GAIN_FMASK)
|
||||
#define FEEDEQ_PREAMP_SIGNVAL(x) ((x) < 0 ? -1 : 1)
|
||||
#define FEEDEQ_PREAMP_SIGNMARK(x) (((x) < 0) ? '-' : '+')
|
||||
|
||||
#define FEEDEQ_PREAMP_IMIN -192
|
||||
#define FEEDEQ_PREAMP_IMAX 192
|
||||
#define FEEDEQ_PREAMP_FMIN 0
|
||||
#define FEEDEQ_PREAMP_FMAX 9
|
||||
|
||||
#define FEEDEQ_PREAMP_INVALID INT_MAX
|
||||
|
||||
#define FEEDEQ_IF2PREAMP(i, f) \
|
||||
((abs(i) << FEEDEQ_GAIN_SHIFT) | \
|
||||
(((abs(f) / FEEDEQ_GAIN_STEP) * FEEDEQ_GAIN_STEP) & \
|
||||
FEEDEQ_GAIN_FMASK))
|
||||
|
||||
#define FEEDEQ_PREAMP_MIN \
|
||||
(FEEDEQ_PREAMP_SIGNVAL(FEEDEQ_GAIN_MIN) * \
|
||||
FEEDEQ_IF2PREAMP(FEEDEQ_GAIN_MIN, 0))
|
||||
|
||||
#define FEEDEQ_PREAMP_MAX \
|
||||
(FEEDEQ_PREAMP_SIGNVAL(FEEDEQ_GAIN_MAX) * \
|
||||
FEEDEQ_IF2PREAMP(FEEDEQ_GAIN_MAX, 0))
|
||||
|
||||
#define FEEDEQ_PREAMP_DEFAULT FEEDEQ_IF2PREAMP(0, 0)
|
||||
|
||||
#define FEEDEQ_PREAMP2IDX(v) \
|
||||
((int32_t)((FEEDEQ_GAIN_MAX * (FEEDEQ_GAIN_DIV / \
|
||||
FEEDEQ_GAIN_STEP)) + (FEEDEQ_PREAMP_SIGNVAL(v) * \
|
||||
FEEDEQ_PREAMP_IPART(v) * (FEEDEQ_GAIN_DIV / \
|
||||
FEEDEQ_GAIN_STEP)) + (FEEDEQ_PREAMP_SIGNVAL(v) * \
|
||||
(FEEDEQ_PREAMP_FPART(v) / FEEDEQ_GAIN_STEP))))
|
||||
|
||||
static int feeder_eq_exact_rate = 0;
|
||||
|
||||
#ifdef _KERNEL
|
||||
static const char feeder_eq_presets[] = FEEDER_EQ_PRESETS;
|
||||
SYSCTL_STRING(_hw_snd, OID_AUTO, feeder_eq_presets, CTLFLAG_RD,
|
||||
&feeder_eq_presets, 0, "compile-time eq presets");
|
||||
|
||||
TUNABLE_INT("hw.snd.feeder_eq_exact_rate", &feeder_eq_exact_rate);
|
||||
SYSCTL_INT(_hw_snd, OID_AUTO, feeder_eq_exact_rate, CTLFLAG_RW,
|
||||
&feeder_eq_exact_rate, 0, "force exact rate validation");
|
||||
#endif
|
||||
|
||||
struct feed_eq_info;
|
||||
|
||||
typedef void (*feed_eq_t)(struct feed_eq_info *, uint8_t *, uint32_t);
|
||||
|
||||
struct feed_eq_tone {
|
||||
intpcm_t o1[SND_CHN_MAX];
|
||||
intpcm_t o2[SND_CHN_MAX];
|
||||
intpcm_t i1[SND_CHN_MAX];
|
||||
intpcm_t i2[SND_CHN_MAX];
|
||||
int gain;
|
||||
};
|
||||
|
||||
struct feed_eq_info {
|
||||
struct feed_eq_tone treble;
|
||||
struct feed_eq_tone bass;
|
||||
struct feed_eq_coeff *coeff;
|
||||
feed_eq_t biquad;
|
||||
uint32_t channels;
|
||||
uint32_t rate;
|
||||
uint32_t align;
|
||||
int32_t preamp;
|
||||
int state;
|
||||
};
|
||||
|
||||
#if !defined(_KERNEL) && defined(FEEDEQ_ERR_CLIP)
|
||||
#define FEEDEQ_ERR_CLIP_CHECK(t, v) do { \
|
||||
if ((v) < PCM_S32_MIN || (v) > PCM_S32_MAX) \
|
||||
errx(1, "\n\n%s(): ["#t"] Sample clipping: %jd\n", \
|
||||
__func__, (intmax_t)(v)); \
|
||||
} while (0)
|
||||
#else
|
||||
#define FEEDEQ_ERR_CLIP_CHECK(...)
|
||||
#endif
|
||||
|
||||
#define FEEDEQ_CLAMP(v) (((v) > PCM_S32_MAX) ? PCM_S32_MAX : \
|
||||
(((v) < PCM_S32_MIN) ? PCM_S32_MIN : \
|
||||
(v)))
|
||||
|
||||
#define FEEDEQ_DECLARE(SIGN, BIT, ENDIAN) \
|
||||
static void \
|
||||
feed_eq_biquad_##SIGN##BIT##ENDIAN(struct feed_eq_info *info, \
|
||||
uint8_t *dst, uint32_t count) \
|
||||
{ \
|
||||
struct feed_eq_coeff_tone *treble, *bass; \
|
||||
intpcm64_t w; \
|
||||
intpcm_t v; \
|
||||
uint32_t i, j; \
|
||||
int32_t pmul, pshift; \
|
||||
\
|
||||
pmul = feed_eq_preamp[info->preamp].mul; \
|
||||
pshift = feed_eq_preamp[info->preamp].shift; \
|
||||
\
|
||||
if (info->state == FEEDEQ_DISABLE) { \
|
||||
j = count * info->channels; \
|
||||
dst += j * PCM_##BIT##_BPS; \
|
||||
do { \
|
||||
dst -= PCM_##BIT##_BPS; \
|
||||
v = _PCM_READ_##SIGN##BIT##_##ENDIAN(dst); \
|
||||
v = ((intpcm64_t)pmul * v) >> pshift; \
|
||||
_PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, v); \
|
||||
} while (--j != 0); \
|
||||
\
|
||||
return; \
|
||||
} \
|
||||
\
|
||||
treble = &(info->coeff[info->treble.gain].treble); \
|
||||
bass = &(info->coeff[info->bass.gain].bass); \
|
||||
\
|
||||
do { \
|
||||
i = 0; \
|
||||
j = info->channels; \
|
||||
do { \
|
||||
v = _PCM_READ_##SIGN##BIT##_##ENDIAN(dst); \
|
||||
v <<= 32 - BIT; \
|
||||
v = ((intpcm64_t)pmul * v) >> pshift; \
|
||||
\
|
||||
w = (intpcm64_t)v * treble->b0; \
|
||||
w += (intpcm64_t)info->treble.i1[i] * treble->b1; \
|
||||
w += (intpcm64_t)info->treble.i2[i] * treble->b2; \
|
||||
w -= (intpcm64_t)info->treble.o1[i] * treble->a1; \
|
||||
w -= (intpcm64_t)info->treble.o2[i] * treble->a2; \
|
||||
info->treble.i2[i] = info->treble.i1[i]; \
|
||||
info->treble.i1[i] = v; \
|
||||
info->treble.o2[i] = info->treble.o1[i]; \
|
||||
w >>= FEEDEQ_COEFF_SHIFT; \
|
||||
FEEDEQ_ERR_CLIP_CHECK(treble, w); \
|
||||
v = FEEDEQ_CLAMP(w); \
|
||||
info->treble.o1[i] = v; \
|
||||
\
|
||||
w = (intpcm64_t)v * bass->b0; \
|
||||
w += (intpcm64_t)info->bass.i1[i] * bass->b1; \
|
||||
w += (intpcm64_t)info->bass.i2[i] * bass->b2; \
|
||||
w -= (intpcm64_t)info->bass.o1[i] * bass->a1; \
|
||||
w -= (intpcm64_t)info->bass.o2[i] * bass->a2; \
|
||||
info->bass.i2[i] = info->bass.i1[i]; \
|
||||
info->bass.i1[i] = v; \
|
||||
info->bass.o2[i] = info->bass.o1[i]; \
|
||||
w >>= FEEDEQ_COEFF_SHIFT; \
|
||||
FEEDEQ_ERR_CLIP_CHECK(bass, w); \
|
||||
v = FEEDEQ_CLAMP(w); \
|
||||
info->bass.o1[i] = v; \
|
||||
\
|
||||
v >>= 32 - BIT; \
|
||||
_PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, v); \
|
||||
dst += PCM_##BIT##_BPS; \
|
||||
i++; \
|
||||
} while (--j != 0); \
|
||||
} while (--count != 0); \
|
||||
}
|
||||
|
||||
#if BYTE_ORDER == LITTLE_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
|
||||
FEEDEQ_DECLARE(S, 16, LE)
|
||||
FEEDEQ_DECLARE(S, 32, LE)
|
||||
#endif
|
||||
#if BYTE_ORDER == BIG_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
|
||||
FEEDEQ_DECLARE(S, 16, BE)
|
||||
FEEDEQ_DECLARE(S, 32, BE)
|
||||
#endif
|
||||
#ifdef SND_FEEDER_MULTIFORMAT
|
||||
FEEDEQ_DECLARE(S, 8, NE)
|
||||
FEEDEQ_DECLARE(S, 24, LE)
|
||||
FEEDEQ_DECLARE(S, 24, BE)
|
||||
FEEDEQ_DECLARE(U, 8, NE)
|
||||
FEEDEQ_DECLARE(U, 16, LE)
|
||||
FEEDEQ_DECLARE(U, 24, LE)
|
||||
FEEDEQ_DECLARE(U, 32, LE)
|
||||
FEEDEQ_DECLARE(U, 16, BE)
|
||||
FEEDEQ_DECLARE(U, 24, BE)
|
||||
FEEDEQ_DECLARE(U, 32, BE)
|
||||
#endif
|
||||
|
||||
#define FEEDEQ_ENTRY(SIGN, BIT, ENDIAN) \
|
||||
{ \
|
||||
AFMT_##SIGN##BIT##_##ENDIAN, \
|
||||
feed_eq_biquad_##SIGN##BIT##ENDIAN \
|
||||
}
|
||||
|
||||
|
||||
static const struct {
|
||||
uint32_t format;
|
||||
feed_eq_t biquad;
|
||||
} feed_eq_biquad_tab[] = {
|
||||
#if BYTE_ORDER == LITTLE_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
|
||||
FEEDEQ_ENTRY(S, 16, LE),
|
||||
FEEDEQ_ENTRY(S, 32, LE),
|
||||
#endif
|
||||
#if BYTE_ORDER == BIG_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
|
||||
FEEDEQ_ENTRY(S, 16, BE),
|
||||
FEEDEQ_ENTRY(S, 32, BE),
|
||||
#endif
|
||||
#ifdef SND_FEEDER_MULTIFORMAT
|
||||
FEEDEQ_ENTRY(S, 8, NE),
|
||||
FEEDEQ_ENTRY(S, 24, LE),
|
||||
FEEDEQ_ENTRY(S, 24, BE),
|
||||
FEEDEQ_ENTRY(U, 8, NE),
|
||||
FEEDEQ_ENTRY(U, 16, LE),
|
||||
FEEDEQ_ENTRY(U, 24, LE),
|
||||
FEEDEQ_ENTRY(U, 32, LE),
|
||||
FEEDEQ_ENTRY(U, 16, BE),
|
||||
FEEDEQ_ENTRY(U, 24, BE),
|
||||
FEEDEQ_ENTRY(U, 32, BE)
|
||||
#endif
|
||||
};
|
||||
|
||||
#define FEEDEQ_BIQUAD_TAB_SIZE \
|
||||
((int32_t)(sizeof(feed_eq_biquad_tab) / sizeof(feed_eq_biquad_tab[0])))
|
||||
|
||||
static struct feed_eq_coeff *
|
||||
feed_eq_coeff_rate(uint32_t rate)
|
||||
{
|
||||
uint32_t spd, threshold;
|
||||
int i;
|
||||
|
||||
if (rate < FEEDEQ_RATE_MIN || rate > FEEDEQ_RATE_MAX)
|
||||
return (NULL);
|
||||
|
||||
/*
|
||||
* Not all rates are supported. Choose the best rate that we can to
|
||||
* allow 'sloppy' conversion. Good enough for naive listeners.
|
||||
*/
|
||||
for (i = 0; i < FEEDEQ_TAB_SIZE; i++) {
|
||||
spd = feed_eq_tab[i].rate;
|
||||
threshold = spd + ((i < (FEEDEQ_TAB_SIZE - 1) &&
|
||||
feed_eq_tab[i + 1].rate > spd) ?
|
||||
((feed_eq_tab[i + 1].rate - spd) >> 1) : 0);
|
||||
if (rate == spd ||
|
||||
(feeder_eq_exact_rate == 0 && rate <= threshold))
|
||||
return (feed_eq_tab[i].coeff);
|
||||
}
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
int
|
||||
feeder_eq_validrate(uint32_t rate)
|
||||
{
|
||||
|
||||
if (feed_eq_coeff_rate(rate) != NULL)
|
||||
return (1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
feed_eq_reset(struct feed_eq_info *info)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < info->channels; i++) {
|
||||
info->treble.i1[i] = 0;
|
||||
info->treble.i2[i] = 0;
|
||||
info->treble.o1[i] = 0;
|
||||
info->treble.o2[i] = 0;
|
||||
info->bass.i1[i] = 0;
|
||||
info->bass.i2[i] = 0;
|
||||
info->bass.o1[i] = 0;
|
||||
info->bass.o2[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
feed_eq_setup(struct feed_eq_info *info)
|
||||
{
|
||||
|
||||
info->coeff = feed_eq_coeff_rate(info->rate);
|
||||
if (info->coeff == NULL)
|
||||
return (EINVAL);
|
||||
|
||||
feed_eq_reset(info);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
feed_eq_init(struct pcm_feeder *f)
|
||||
{
|
||||
struct feed_eq_info *info;
|
||||
feed_eq_t biquad_op;
|
||||
int i;
|
||||
|
||||
if (f->desc->in != f->desc->out)
|
||||
return (EINVAL);
|
||||
|
||||
biquad_op = NULL;
|
||||
|
||||
for (i = 0; i < FEEDEQ_BIQUAD_TAB_SIZE && biquad_op == NULL; i++) {
|
||||
if (AFMT_ENCODING(f->desc->in) == feed_eq_biquad_tab[i].format)
|
||||
biquad_op = feed_eq_biquad_tab[i].biquad;
|
||||
}
|
||||
|
||||
if (biquad_op == NULL)
|
||||
return (EINVAL);
|
||||
|
||||
info = malloc(sizeof(*info), M_DEVBUF, M_NOWAIT | M_ZERO);
|
||||
if (info == NULL)
|
||||
return (ENOMEM);
|
||||
|
||||
info->channels = AFMT_CHANNEL(f->desc->in);
|
||||
info->align = info->channels * AFMT_BPS(f->desc->in);
|
||||
|
||||
info->rate = FEEDEQ_RATE_MIN;
|
||||
info->treble.gain = FEEDEQ_L2GAIN(50);
|
||||
info->bass.gain = FEEDEQ_L2GAIN(50);
|
||||
info->preamp = FEEDEQ_PREAMP2IDX(FEEDEQ_PREAMP_DEFAULT);
|
||||
info->state = FEEDEQ_UNKNOWN;
|
||||
|
||||
info->biquad = biquad_op;
|
||||
|
||||
f->data = info;
|
||||
|
||||
return (feed_eq_setup(info));
|
||||
}
|
||||
|
||||
static int
|
||||
feed_eq_set(struct pcm_feeder *f, int what, int value)
|
||||
{
|
||||
struct feed_eq_info *info;
|
||||
|
||||
info = f->data;
|
||||
|
||||
switch (what) {
|
||||
case FEEDEQ_CHANNELS:
|
||||
if (value < SND_CHN_MIN || value > SND_CHN_MAX)
|
||||
return (EINVAL);
|
||||
info->channels = (uint32_t)value;
|
||||
info->align = info->channels * AFMT_BPS(f->desc->in);
|
||||
feed_eq_reset(info);
|
||||
break;
|
||||
case FEEDEQ_RATE:
|
||||
if (feeder_eq_validrate(value) == 0)
|
||||
return (EINVAL);
|
||||
info->rate = (uint32_t)value;
|
||||
if (info->state == FEEDEQ_UNKNOWN)
|
||||
info->state = FEEDEQ_ENABLE;
|
||||
return (feed_eq_setup(info));
|
||||
break;
|
||||
case FEEDEQ_TREBLE:
|
||||
case FEEDEQ_BASS:
|
||||
if (value < 0 || value > 100)
|
||||
return (EINVAL);
|
||||
if (what == FEEDEQ_TREBLE)
|
||||
info->treble.gain = FEEDEQ_L2GAIN(value);
|
||||
else
|
||||
info->bass.gain = FEEDEQ_L2GAIN(value);
|
||||
break;
|
||||
case FEEDEQ_PREAMP:
|
||||
if (value < FEEDEQ_PREAMP_MIN || value > FEEDEQ_PREAMP_MAX)
|
||||
return (EINVAL);
|
||||
info->preamp = FEEDEQ_PREAMP2IDX(value);
|
||||
break;
|
||||
case FEEDEQ_STATE:
|
||||
if (!(value == FEEDEQ_BYPASS || value == FEEDEQ_ENABLE ||
|
||||
value == FEEDEQ_DISABLE))
|
||||
return (EINVAL);
|
||||
info->state = value;
|
||||
feed_eq_reset(info);
|
||||
break;
|
||||
default:
|
||||
return (EINVAL);
|
||||
break;
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
feed_eq_free(struct pcm_feeder *f)
|
||||
{
|
||||
struct feed_eq_info *info;
|
||||
|
||||
info = f->data;
|
||||
if (info != NULL)
|
||||
free(info, M_DEVBUF);
|
||||
|
||||
f->data = NULL;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
feed_eq_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
|
||||
uint32_t count, void *source)
|
||||
{
|
||||
struct feed_eq_info *info;
|
||||
uint32_t j;
|
||||
uint8_t *dst;
|
||||
|
||||
info = f->data;
|
||||
|
||||
/*
|
||||
* 3 major states:
|
||||
* FEEDEQ_BYPASS - Bypass entirely, nothing happened.
|
||||
* FEEDEQ_ENABLE - Preamp+biquad filtering.
|
||||
* FEEDEQ_DISABLE - Preamp only.
|
||||
*/
|
||||
if (info->state == FEEDEQ_BYPASS)
|
||||
return (FEEDER_FEED(f->source, c, b, count, source));
|
||||
|
||||
dst = b;
|
||||
count = SND_FXROUND(count, info->align);
|
||||
|
||||
do {
|
||||
if (count < info->align)
|
||||
break;
|
||||
|
||||
j = SND_FXDIV(FEEDER_FEED(f->source, c, dst, count, source),
|
||||
info->align);
|
||||
if (j == 0)
|
||||
break;
|
||||
|
||||
info->biquad(info, dst, j);
|
||||
|
||||
j *= info->align;
|
||||
dst += j;
|
||||
count -= j;
|
||||
|
||||
} while (count != 0);
|
||||
|
||||
return (dst - b);
|
||||
}
|
||||
|
||||
static struct pcm_feederdesc feeder_eq_desc[] = {
|
||||
{ FEEDER_EQ, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
static kobj_method_t feeder_eq_methods[] = {
|
||||
KOBJMETHOD(feeder_init, feed_eq_init),
|
||||
KOBJMETHOD(feeder_free, feed_eq_free),
|
||||
KOBJMETHOD(feeder_set, feed_eq_set),
|
||||
KOBJMETHOD(feeder_feed, feed_eq_feed),
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
|
||||
FEEDER_DECLARE(feeder_eq, NULL);
|
||||
|
||||
static int32_t
|
||||
feed_eq_scan_preamp_arg(const char *s)
|
||||
{
|
||||
int r, i, f;
|
||||
size_t len;
|
||||
char buf[32];
|
||||
|
||||
bzero(buf, sizeof(buf));
|
||||
|
||||
/* XXX kind of ugly, but works for now.. */
|
||||
|
||||
r = sscanf(s, "%d.%d", &i, &f);
|
||||
|
||||
if (r == 1 && !(i < FEEDEQ_PREAMP_IMIN || i > FEEDEQ_PREAMP_IMAX)) {
|
||||
snprintf(buf, sizeof(buf), "%c%d",
|
||||
FEEDEQ_PREAMP_SIGNMARK(i), abs(i));
|
||||
f = 0;
|
||||
} else if (r == 2 &&
|
||||
!(i < FEEDEQ_PREAMP_IMIN || i > FEEDEQ_PREAMP_IMAX ||
|
||||
f < FEEDEQ_PREAMP_FMIN || f > FEEDEQ_PREAMP_FMAX))
|
||||
snprintf(buf, sizeof(buf), "%c%d.%d",
|
||||
FEEDEQ_PREAMP_SIGNMARK(i), abs(i), f);
|
||||
else
|
||||
return (FEEDEQ_PREAMP_INVALID);
|
||||
|
||||
len = strlen(s);
|
||||
if (len > 2 && strcasecmp(s + len - 2, "dB") == 0)
|
||||
strlcat(buf, "dB", sizeof(buf));
|
||||
|
||||
if (i == 0 && *s == '-')
|
||||
*buf = '-';
|
||||
|
||||
if (strcasecmp(buf + ((*s >= '0' && *s <= '9') ? 1 : 0), s) != 0)
|
||||
return (FEEDEQ_PREAMP_INVALID);
|
||||
|
||||
while ((f / FEEDEQ_GAIN_DIV) > 0)
|
||||
f /= FEEDEQ_GAIN_DIV;
|
||||
|
||||
return (((i < 0 || *buf == '-') ? -1 : 1) * FEEDEQ_IF2PREAMP(i, f));
|
||||
}
|
||||
|
||||
#ifdef _KERNEL
|
||||
static int
|
||||
sysctl_dev_pcm_eq(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
struct snddev_info *d;
|
||||
struct pcm_channel *c;
|
||||
struct pcm_feeder *f;
|
||||
int err, val, oval;
|
||||
|
||||
d = oidp->oid_arg1;
|
||||
if (!PCM_REGISTERED(d))
|
||||
return (ENODEV);
|
||||
|
||||
PCM_LOCK(d);
|
||||
PCM_WAIT(d);
|
||||
if (d->flags & SD_F_EQ_BYPASSED)
|
||||
val = 2;
|
||||
else if (d->flags & SD_F_EQ_ENABLED)
|
||||
val = 1;
|
||||
else
|
||||
val = 0;
|
||||
PCM_ACQUIRE(d);
|
||||
PCM_UNLOCK(d);
|
||||
|
||||
oval = val;
|
||||
err = sysctl_handle_int(oidp, &val, 0, req);
|
||||
|
||||
if (err == 0 && req->newptr != NULL && val != oval) {
|
||||
if (!(val == 0 || val == 1 || val == 2)) {
|
||||
PCM_RELEASE_QUICK(d);
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
PCM_LOCK(d);
|
||||
|
||||
d->flags &= ~(SD_F_EQ_ENABLED | SD_F_EQ_BYPASSED);
|
||||
if (val == 2) {
|
||||
val = FEEDEQ_BYPASS;
|
||||
d->flags |= SD_F_EQ_BYPASSED;
|
||||
} else if (val == 1) {
|
||||
val = FEEDEQ_ENABLE;
|
||||
d->flags |= SD_F_EQ_ENABLED;
|
||||
} else
|
||||
val = FEEDEQ_DISABLE;
|
||||
|
||||
CHN_FOREACH(c, d, channels.pcm.busy) {
|
||||
CHN_LOCK(c);
|
||||
f = chn_findfeeder(c, FEEDER_EQ);
|
||||
if (f != NULL)
|
||||
(void)FEEDER_SET(f, FEEDEQ_STATE, val);
|
||||
CHN_UNLOCK(c);
|
||||
}
|
||||
|
||||
PCM_RELEASE(d);
|
||||
PCM_UNLOCK(d);
|
||||
} else
|
||||
PCM_RELEASE_QUICK(d);
|
||||
|
||||
return (err);
|
||||
}
|
||||
|
||||
static int
|
||||
sysctl_dev_pcm_eq_preamp(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
struct snddev_info *d;
|
||||
struct pcm_channel *c;
|
||||
struct pcm_feeder *f;
|
||||
int err, val, oval;
|
||||
char buf[32];
|
||||
|
||||
d = oidp->oid_arg1;
|
||||
if (!PCM_REGISTERED(d))
|
||||
return (ENODEV);
|
||||
|
||||
PCM_LOCK(d);
|
||||
PCM_WAIT(d);
|
||||
val = d->eqpreamp;
|
||||
bzero(buf, sizeof(buf));
|
||||
(void)snprintf(buf, sizeof(buf), "%c%d.%ddB",
|
||||
FEEDEQ_PREAMP_SIGNMARK(val), FEEDEQ_PREAMP_IPART(val),
|
||||
FEEDEQ_PREAMP_FPART(val));
|
||||
PCM_ACQUIRE(d);
|
||||
PCM_UNLOCK(d);
|
||||
|
||||
oval = val;
|
||||
err = sysctl_handle_string(oidp, buf, sizeof(buf), req);
|
||||
|
||||
if (err == 0 && req->newptr != NULL) {
|
||||
val = feed_eq_scan_preamp_arg(buf);
|
||||
if (val == FEEDEQ_PREAMP_INVALID) {
|
||||
PCM_RELEASE_QUICK(d);
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
PCM_LOCK(d);
|
||||
|
||||
if (val != oval) {
|
||||
if (val < FEEDEQ_PREAMP_MIN)
|
||||
val = FEEDEQ_PREAMP_MIN;
|
||||
else if (val > FEEDEQ_PREAMP_MAX)
|
||||
val = FEEDEQ_PREAMP_MAX;
|
||||
|
||||
d->eqpreamp = val;
|
||||
|
||||
CHN_FOREACH(c, d, channels.pcm.busy) {
|
||||
CHN_LOCK(c);
|
||||
f = chn_findfeeder(c, FEEDER_EQ);
|
||||
if (f != NULL)
|
||||
(void)FEEDER_SET(f, FEEDEQ_PREAMP, val);
|
||||
CHN_UNLOCK(c);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
PCM_RELEASE(d);
|
||||
PCM_UNLOCK(d);
|
||||
} else
|
||||
PCM_RELEASE_QUICK(d);
|
||||
|
||||
return (err);
|
||||
}
|
||||
|
||||
void
|
||||
feeder_eq_initsys(device_t dev)
|
||||
{
|
||||
struct snddev_info *d;
|
||||
const char *preamp;
|
||||
char buf[64];
|
||||
|
||||
d = device_get_softc(dev);
|
||||
|
||||
if (!(resource_string_value(device_get_name(dev), device_get_unit(dev),
|
||||
"eq_preamp", &preamp) == 0 &&
|
||||
(d->eqpreamp = feed_eq_scan_preamp_arg(preamp)) !=
|
||||
FEEDEQ_PREAMP_INVALID))
|
||||
d->eqpreamp = FEEDEQ_PREAMP_DEFAULT;
|
||||
|
||||
if (d->eqpreamp < FEEDEQ_PREAMP_MIN)
|
||||
d->eqpreamp = FEEDEQ_PREAMP_MIN;
|
||||
else if (d->eqpreamp > FEEDEQ_PREAMP_MAX)
|
||||
d->eqpreamp = FEEDEQ_PREAMP_MAX;
|
||||
|
||||
SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
|
||||
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
|
||||
"eq", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d),
|
||||
sysctl_dev_pcm_eq, "I",
|
||||
"Bass/Treble Equalizer (0=disable, 1=enable, 2=bypass)");
|
||||
|
||||
bzero(buf, sizeof(buf));
|
||||
|
||||
(void)snprintf(buf, sizeof(buf), "Bass/Treble Equalizer Preamp "
|
||||
"(-/+ %d.0dB , %d.%ddB step)",
|
||||
FEEDEQ_GAIN_MAX, FEEDEQ_GAIN_STEP / FEEDEQ_GAIN_DIV,
|
||||
FEEDEQ_GAIN_STEP - ((FEEDEQ_GAIN_STEP / FEEDEQ_GAIN_DIV) *
|
||||
FEEDEQ_GAIN_DIV));
|
||||
|
||||
SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
|
||||
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
|
||||
"eq_preamp", CTLTYPE_STRING | CTLFLAG_RW, d, sizeof(d),
|
||||
sysctl_dev_pcm_eq_preamp, "A", buf);
|
||||
}
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
300
sys/dev/sound/pcm/feeder_format.c
Normal file
300
sys/dev/sound/pcm/feeder_format.c
Normal file
@ -0,0 +1,300 @@
|
||||
/*-
|
||||
* Copyright (c) 2008-2009 Ariff Abdullah <ariff@FreeBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* feeder_format: New generation of generic, any-to-any format converter, as
|
||||
* long as the sample values can be read _and_ write.
|
||||
*/
|
||||
|
||||
#ifdef _KERNEL
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
#include <dev/sound/pcm/pcm.h>
|
||||
#include <dev/sound/pcm/g711.h>
|
||||
#include <dev/sound/pcm/intpcm.h>
|
||||
#include "feeder_if.h"
|
||||
|
||||
#define SND_USE_FXDIV
|
||||
#include "snd_fxdiv_gen.h"
|
||||
|
||||
SND_DECLARE_FILE("$FreeBSD$");
|
||||
#endif
|
||||
|
||||
#define FEEDFORMAT_RESERVOIR (SND_CHN_MAX * PCM_32_BPS)
|
||||
|
||||
INTPCM_DECLARE(intpcm_conv_tables)
|
||||
|
||||
struct feed_format_info {
|
||||
uint32_t ibps, obps;
|
||||
uint32_t ialign, oalign, channels;
|
||||
intpcm_read_t *read;
|
||||
intpcm_write_t *write;
|
||||
uint8_t reservoir[FEEDFORMAT_RESERVOIR];
|
||||
};
|
||||
|
||||
/*
|
||||
* dummy ac3/dts passthrough, etc.
|
||||
* XXX assume as s16le.
|
||||
*/
|
||||
static __inline intpcm_t
|
||||
intpcm_read_null(uint8_t *src __unused)
|
||||
{
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static __inline void
|
||||
intpcm_write_null(uint8_t *dst, intpcm_t v __unused)
|
||||
{
|
||||
|
||||
_PCM_WRITE_S16_LE(dst, 0);
|
||||
}
|
||||
|
||||
#define FEEDFORMAT_ENTRY(SIGN, BIT, ENDIAN) \
|
||||
{ \
|
||||
AFMT_##SIGN##BIT##_##ENDIAN, \
|
||||
intpcm_read_##SIGN##BIT##ENDIAN, \
|
||||
intpcm_write_##SIGN##BIT##ENDIAN \
|
||||
}
|
||||
|
||||
static const struct {
|
||||
uint32_t format;
|
||||
intpcm_read_t *read;
|
||||
intpcm_write_t *write;
|
||||
} feed_format_ops[] = {
|
||||
FEEDFORMAT_ENTRY(S, 8, NE),
|
||||
FEEDFORMAT_ENTRY(S, 16, LE),
|
||||
FEEDFORMAT_ENTRY(S, 24, LE),
|
||||
FEEDFORMAT_ENTRY(S, 32, LE),
|
||||
FEEDFORMAT_ENTRY(S, 16, BE),
|
||||
FEEDFORMAT_ENTRY(S, 24, BE),
|
||||
FEEDFORMAT_ENTRY(S, 32, BE),
|
||||
FEEDFORMAT_ENTRY(U, 8, NE),
|
||||
FEEDFORMAT_ENTRY(U, 16, LE),
|
||||
FEEDFORMAT_ENTRY(U, 24, LE),
|
||||
FEEDFORMAT_ENTRY(U, 32, LE),
|
||||
FEEDFORMAT_ENTRY(U, 16, BE),
|
||||
FEEDFORMAT_ENTRY(U, 24, BE),
|
||||
FEEDFORMAT_ENTRY(U, 32, BE),
|
||||
{
|
||||
AFMT_MU_LAW,
|
||||
intpcm_read_ulaw, intpcm_write_ulaw
|
||||
},
|
||||
{
|
||||
AFMT_A_LAW,
|
||||
intpcm_read_alaw, intpcm_write_alaw
|
||||
},
|
||||
{
|
||||
AFMT_AC3,
|
||||
intpcm_read_null, intpcm_write_null
|
||||
}
|
||||
};
|
||||
|
||||
#define FEEDFORMAT_TAB_SIZE \
|
||||
((int32_t)(sizeof(feed_format_ops) / sizeof(feed_format_ops[0])))
|
||||
|
||||
static int
|
||||
feed_format_init(struct pcm_feeder *f)
|
||||
{
|
||||
struct feed_format_info *info;
|
||||
intpcm_read_t *rd_op;
|
||||
intpcm_write_t *wr_op;
|
||||
int i;
|
||||
|
||||
if (f->desc->in == f->desc->out ||
|
||||
AFMT_CHANNEL(f->desc->in) != AFMT_CHANNEL(f->desc->out))
|
||||
return (EINVAL);
|
||||
|
||||
rd_op = NULL;
|
||||
wr_op = NULL;
|
||||
|
||||
for (i = 0; i < FEEDFORMAT_TAB_SIZE &&
|
||||
(rd_op == NULL || wr_op == NULL); i++) {
|
||||
if (rd_op == NULL &&
|
||||
AFMT_ENCODING(f->desc->in) == feed_format_ops[i].format)
|
||||
rd_op = feed_format_ops[i].read;
|
||||
if (wr_op == NULL &&
|
||||
AFMT_ENCODING(f->desc->out) == feed_format_ops[i].format)
|
||||
wr_op = feed_format_ops[i].write;
|
||||
}
|
||||
|
||||
if (rd_op == NULL || wr_op == NULL) {
|
||||
printf("%s(): failed to initialize io ops "
|
||||
"in=0x%08x out=0x%08x\n",
|
||||
__func__, f->desc->in, f->desc->out);
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
info = malloc(sizeof(*info), M_DEVBUF, M_NOWAIT | M_ZERO);
|
||||
if (info == NULL)
|
||||
return (ENOMEM);
|
||||
|
||||
info->channels = AFMT_CHANNEL(f->desc->in);
|
||||
|
||||
info->ibps = AFMT_BPS(f->desc->in);
|
||||
info->ialign = info->ibps * info->channels;
|
||||
info->read = rd_op;
|
||||
|
||||
info->obps = AFMT_BPS(f->desc->out);
|
||||
info->oalign = info->obps * info->channels;
|
||||
info->write = wr_op;
|
||||
|
||||
f->data = info;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
feed_format_free(struct pcm_feeder *f)
|
||||
{
|
||||
struct feed_format_info *info;
|
||||
|
||||
info = f->data;
|
||||
if (info != NULL)
|
||||
free(info, M_DEVBUF);
|
||||
|
||||
f->data = NULL;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
feed_format_set(struct pcm_feeder *f, int what, int value)
|
||||
{
|
||||
struct feed_format_info *info;
|
||||
|
||||
info = f->data;
|
||||
|
||||
switch (what) {
|
||||
case FEEDFORMAT_CHANNELS:
|
||||
if (value < SND_CHN_MIN || value > SND_CHN_MAX)
|
||||
return (EINVAL);
|
||||
info->channels = (uint32_t)value;
|
||||
info->ialign = info->ibps * info->channels;
|
||||
info->oalign = info->obps * info->channels;
|
||||
break;
|
||||
default:
|
||||
return (EINVAL);
|
||||
break;
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
feed_format_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
|
||||
uint32_t count, void *source)
|
||||
{
|
||||
struct feed_format_info *info;
|
||||
intpcm_t v;
|
||||
uint32_t j;
|
||||
uint8_t *src, *dst;
|
||||
|
||||
info = f->data;
|
||||
dst = b;
|
||||
count = SND_FXROUND(count, info->oalign);
|
||||
|
||||
do {
|
||||
if (count < info->oalign)
|
||||
break;
|
||||
|
||||
if (count < info->ialign) {
|
||||
src = info->reservoir;
|
||||
j = info->ialign;
|
||||
} else {
|
||||
if (info->ialign == info->oalign)
|
||||
j = count;
|
||||
else if (info->ialign > info->oalign)
|
||||
j = SND_FXROUND(count, info->ialign);
|
||||
else
|
||||
j = SND_FXDIV(count, info->oalign) *
|
||||
info->ialign;
|
||||
src = dst + count - j;
|
||||
}
|
||||
|
||||
j = SND_FXDIV(FEEDER_FEED(f->source, c, src, j, source),
|
||||
info->ialign);
|
||||
if (j == 0)
|
||||
break;
|
||||
|
||||
j *= info->channels;
|
||||
count -= j * info->obps;
|
||||
|
||||
do {
|
||||
v = info->read(src);
|
||||
info->write(dst, v);
|
||||
dst += info->obps;
|
||||
src += info->ibps;
|
||||
} while (--j != 0);
|
||||
|
||||
} while (count != 0);
|
||||
|
||||
return (dst - b);
|
||||
}
|
||||
|
||||
static struct pcm_feederdesc feeder_format_desc[] = {
|
||||
{ FEEDER_FORMAT, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
static kobj_method_t feeder_format_methods[] = {
|
||||
KOBJMETHOD(feeder_init, feed_format_init),
|
||||
KOBJMETHOD(feeder_free, feed_format_free),
|
||||
KOBJMETHOD(feeder_set, feed_format_set),
|
||||
KOBJMETHOD(feeder_feed, feed_format_feed),
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
|
||||
FEEDER_DECLARE(feeder_format, NULL);
|
||||
|
||||
/* Extern */
|
||||
intpcm_read_t *
|
||||
feeder_format_read_op(uint32_t format)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < FEEDFORMAT_TAB_SIZE; i++) {
|
||||
if (AFMT_ENCODING(format) == feed_format_ops[i].format)
|
||||
return (feed_format_ops[i].read);
|
||||
}
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
intpcm_write_t *
|
||||
feeder_format_write_op(uint32_t format)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < FEEDFORMAT_TAB_SIZE; i++) {
|
||||
if (AFMT_ENCODING(format) == feed_format_ops[i].format)
|
||||
return (feed_format_ops[i].write);
|
||||
}
|
||||
|
||||
return (NULL);
|
||||
}
|
825
sys/dev/sound/pcm/feeder_matrix.c
Normal file
825
sys/dev/sound/pcm/feeder_matrix.c
Normal file
@ -0,0 +1,825 @@
|
||||
/*-
|
||||
* Copyright (c) 2008-2009 Ariff Abdullah <ariff@FreeBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* feeder_matrix: Generic any-to-any channel matrixing. Probably not the
|
||||
* accurate way of doing things, but it should be fast and
|
||||
* transparent enough, not to mention capable of handling
|
||||
* possible non-standard way of multichannel interleaving
|
||||
* order. In other words, it is tough to break.
|
||||
*
|
||||
* The Good:
|
||||
* + very generic and compact, provided that the supplied matrix map is in a
|
||||
* sane form.
|
||||
* + should be fast enough.
|
||||
*
|
||||
* The Bad:
|
||||
* + somebody might disagree with it.
|
||||
* + 'matrix' is kind of 0x7a69, due to prolong mental block.
|
||||
*/
|
||||
|
||||
#ifdef _KERNEL
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
#include <dev/sound/pcm/pcm.h>
|
||||
#include "feeder_if.h"
|
||||
|
||||
#define SND_USE_FXDIV
|
||||
#include "snd_fxdiv_gen.h"
|
||||
|
||||
SND_DECLARE_FILE("$FreeBSD$");
|
||||
#endif
|
||||
|
||||
#define FEEDMATRIX_RESERVOIR (SND_CHN_MAX * PCM_32_BPS)
|
||||
|
||||
#define SND_CHN_T_EOF 0x00e0fe0f
|
||||
#define SND_CHN_T_NULL 0x0e0e0e0e
|
||||
|
||||
struct feed_matrix_info;
|
||||
|
||||
typedef void (*feed_matrix_t)(struct feed_matrix_info *, uint8_t *,
|
||||
uint8_t *, uint32_t);
|
||||
|
||||
struct feed_matrix_info {
|
||||
uint32_t bps;
|
||||
uint32_t ialign, oalign;
|
||||
uint32_t in, out;
|
||||
feed_matrix_t apply;
|
||||
#ifdef FEEDMATRIX_GENERIC
|
||||
intpcm_read_t *rd;
|
||||
intpcm_write_t *wr;
|
||||
#endif
|
||||
struct {
|
||||
int chn[SND_CHN_T_MAX + 1];
|
||||
int mul, shift;
|
||||
} matrix[SND_CHN_T_MAX + 1];
|
||||
uint8_t reservoir[FEEDMATRIX_RESERVOIR];
|
||||
};
|
||||
|
||||
static struct pcmchan_matrix feeder_matrix_maps[SND_CHN_MATRIX_MAX] = {
|
||||
[SND_CHN_MATRIX_1_0] = SND_CHN_MATRIX_MAP_1_0,
|
||||
[SND_CHN_MATRIX_2_0] = SND_CHN_MATRIX_MAP_2_0,
|
||||
[SND_CHN_MATRIX_2_1] = SND_CHN_MATRIX_MAP_2_1,
|
||||
[SND_CHN_MATRIX_3_0] = SND_CHN_MATRIX_MAP_3_0,
|
||||
[SND_CHN_MATRIX_4_0] = SND_CHN_MATRIX_MAP_4_0,
|
||||
[SND_CHN_MATRIX_4_1] = SND_CHN_MATRIX_MAP_4_1,
|
||||
[SND_CHN_MATRIX_5_0] = SND_CHN_MATRIX_MAP_5_0,
|
||||
[SND_CHN_MATRIX_5_1] = SND_CHN_MATRIX_MAP_5_1,
|
||||
[SND_CHN_MATRIX_6_0] = SND_CHN_MATRIX_MAP_6_0,
|
||||
[SND_CHN_MATRIX_6_1] = SND_CHN_MATRIX_MAP_6_1,
|
||||
[SND_CHN_MATRIX_7_1] = SND_CHN_MATRIX_MAP_7_1
|
||||
};
|
||||
|
||||
static int feeder_matrix_default_ids[9] = {
|
||||
[0] = SND_CHN_MATRIX_UNKNOWN,
|
||||
[1] = SND_CHN_MATRIX_1,
|
||||
[2] = SND_CHN_MATRIX_2,
|
||||
[3] = SND_CHN_MATRIX_3,
|
||||
[4] = SND_CHN_MATRIX_4,
|
||||
[5] = SND_CHN_MATRIX_5,
|
||||
[6] = SND_CHN_MATRIX_6,
|
||||
[7] = SND_CHN_MATRIX_7,
|
||||
[8] = SND_CHN_MATRIX_8
|
||||
};
|
||||
|
||||
#ifdef _KERNEL
|
||||
#define FEEDMATRIX_CLIP_CHECK(...)
|
||||
#else
|
||||
#define FEEDMATRIX_CLIP_CHECK(v, BIT) do { \
|
||||
if ((v) < PCM_S##BIT##_MIN || (v) > PCM_S##BIT##_MAX) \
|
||||
errx(1, "\n\n%s(): Sample clipping: %jd\n", \
|
||||
__func__, (intmax_t)(v)); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#define FEEDMATRIX_DECLARE(SIGN, BIT, ENDIAN) \
|
||||
static void \
|
||||
feed_matrix_##SIGN##BIT##ENDIAN(struct feed_matrix_info *info, \
|
||||
uint8_t *src, uint8_t *dst, uint32_t count) \
|
||||
{ \
|
||||
intpcm64_t accum; \
|
||||
intpcm_t v; \
|
||||
int i, j; \
|
||||
\
|
||||
do { \
|
||||
for (i = 0; info->matrix[i].chn[0] != SND_CHN_T_EOF; \
|
||||
i++) { \
|
||||
if (info->matrix[i].chn[0] == SND_CHN_T_NULL) { \
|
||||
_PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, \
|
||||
0); \
|
||||
dst += PCM_##BIT##_BPS; \
|
||||
continue; \
|
||||
} else if (info->matrix[i].chn[1] == \
|
||||
SND_CHN_T_EOF) { \
|
||||
v = _PCM_READ_##SIGN##BIT##_##ENDIAN( \
|
||||
src + info->matrix[i].chn[0]); \
|
||||
_PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, \
|
||||
v); \
|
||||
dst += PCM_##BIT##_BPS; \
|
||||
continue; \
|
||||
} \
|
||||
\
|
||||
accum = 0; \
|
||||
for (j = 0; \
|
||||
info->matrix[i].chn[j] != SND_CHN_T_EOF; \
|
||||
j++) { \
|
||||
v = _PCM_READ_##SIGN##BIT##_##ENDIAN( \
|
||||
src + info->matrix[i].chn[j]); \
|
||||
accum += v; \
|
||||
} \
|
||||
\
|
||||
accum = (accum * info->matrix[i].mul) >> \
|
||||
info->matrix[i].shift; \
|
||||
\
|
||||
FEEDMATRIX_CLIP_CHECK(accum, BIT); \
|
||||
\
|
||||
v = (accum > PCM_S##BIT##_MAX) ? \
|
||||
PCM_S##BIT##_MAX : \
|
||||
((accum < PCM_S##BIT##_MIN) ? \
|
||||
PCM_S##BIT##_MIN : \
|
||||
accum); \
|
||||
_PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, v); \
|
||||
dst += PCM_##BIT##_BPS; \
|
||||
} \
|
||||
src += info->ialign; \
|
||||
} while (--count != 0); \
|
||||
}
|
||||
|
||||
#if BYTE_ORDER == LITTLE_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
|
||||
FEEDMATRIX_DECLARE(S, 16, LE)
|
||||
FEEDMATRIX_DECLARE(S, 32, LE)
|
||||
#endif
|
||||
#if BYTE_ORDER == BIG_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
|
||||
FEEDMATRIX_DECLARE(S, 16, BE)
|
||||
FEEDMATRIX_DECLARE(S, 32, BE)
|
||||
#endif
|
||||
#ifdef SND_FEEDER_MULTIFORMAT
|
||||
FEEDMATRIX_DECLARE(S, 8, NE)
|
||||
FEEDMATRIX_DECLARE(S, 24, LE)
|
||||
FEEDMATRIX_DECLARE(S, 24, BE)
|
||||
FEEDMATRIX_DECLARE(U, 8, NE)
|
||||
FEEDMATRIX_DECLARE(U, 16, LE)
|
||||
FEEDMATRIX_DECLARE(U, 24, LE)
|
||||
FEEDMATRIX_DECLARE(U, 32, LE)
|
||||
FEEDMATRIX_DECLARE(U, 16, BE)
|
||||
FEEDMATRIX_DECLARE(U, 24, BE)
|
||||
FEEDMATRIX_DECLARE(U, 32, BE)
|
||||
#endif
|
||||
|
||||
#define FEEDMATRIX_ENTRY(SIGN, BIT, ENDIAN) \
|
||||
{ \
|
||||
AFMT_##SIGN##BIT##_##ENDIAN, \
|
||||
feed_matrix_##SIGN##BIT##ENDIAN \
|
||||
}
|
||||
|
||||
static const struct {
|
||||
uint32_t format;
|
||||
feed_matrix_t apply;
|
||||
} feed_matrix_tab[] = {
|
||||
#if BYTE_ORDER == LITTLE_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
|
||||
FEEDMATRIX_ENTRY(S, 16, LE),
|
||||
FEEDMATRIX_ENTRY(S, 32, LE),
|
||||
#endif
|
||||
#if BYTE_ORDER == BIG_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
|
||||
FEEDMATRIX_ENTRY(S, 16, BE),
|
||||
FEEDMATRIX_ENTRY(S, 32, BE),
|
||||
#endif
|
||||
#ifdef SND_FEEDER_MULTIFORMAT
|
||||
FEEDMATRIX_ENTRY(S, 8, NE),
|
||||
FEEDMATRIX_ENTRY(S, 24, LE),
|
||||
FEEDMATRIX_ENTRY(S, 24, BE),
|
||||
FEEDMATRIX_ENTRY(U, 8, NE),
|
||||
FEEDMATRIX_ENTRY(U, 16, LE),
|
||||
FEEDMATRIX_ENTRY(U, 24, LE),
|
||||
FEEDMATRIX_ENTRY(U, 32, LE),
|
||||
FEEDMATRIX_ENTRY(U, 16, BE),
|
||||
FEEDMATRIX_ENTRY(U, 24, BE),
|
||||
FEEDMATRIX_ENTRY(U, 32, BE)
|
||||
#endif
|
||||
};
|
||||
|
||||
static void
|
||||
feed_matrix_reset(struct feed_matrix_info *info)
|
||||
{
|
||||
uint32_t i, j;
|
||||
|
||||
for (i = 0; i < (sizeof(info->matrix) / sizeof(info->matrix[0])); i++) {
|
||||
for (j = 0;
|
||||
j < (sizeof(info->matrix[i].chn) /
|
||||
sizeof(info->matrix[i].chn[0])); j++) {
|
||||
info->matrix[i].chn[j] = SND_CHN_T_EOF;
|
||||
}
|
||||
info->matrix[i].mul = 1;
|
||||
info->matrix[i].shift = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef FEEDMATRIX_GENERIC
|
||||
static void
|
||||
feed_matrix_apply_generic(struct feed_matrix_info *info,
|
||||
uint8_t *src, uint8_t *dst, uint32_t count)
|
||||
{
|
||||
intpcm64_t accum;
|
||||
intpcm_t v;
|
||||
int i, j;
|
||||
|
||||
do {
|
||||
for (i = 0; info->matrix[i].chn[0] != SND_CHN_T_EOF;
|
||||
i++) {
|
||||
if (info->matrix[i].chn[0] == SND_CHN_T_NULL) {
|
||||
info->wr(dst, 0);
|
||||
dst += info->bps;
|
||||
continue;
|
||||
} else if (info->matrix[i].chn[1] ==
|
||||
SND_CHN_T_EOF) {
|
||||
v = info->rd(src + info->matrix[i].chn[0]);
|
||||
info->wr(dst, v);
|
||||
dst += info->bps;
|
||||
continue;
|
||||
}
|
||||
|
||||
accum = 0;
|
||||
for (j = 0;
|
||||
info->matrix[i].chn[j] != SND_CHN_T_EOF;
|
||||
j++) {
|
||||
v = info->rd(src + info->matrix[i].chn[j]);
|
||||
accum += v;
|
||||
}
|
||||
|
||||
accum = (accum * info->matrix[i].mul) >>
|
||||
info->matrix[i].shift;
|
||||
|
||||
FEEDMATRIX_CLIP_CHECK(accum, 32);
|
||||
|
||||
v = (accum > PCM_S32_MAX) ? PCM_S32_MAX :
|
||||
((accum < PCM_S32_MIN) ? PCM_S32_MIN : accum);
|
||||
info->wr(dst, v);
|
||||
dst += info->bps;
|
||||
}
|
||||
src += info->ialign;
|
||||
} while (--count != 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
feed_matrix_setup(struct feed_matrix_info *info, struct pcmchan_matrix *m_in,
|
||||
struct pcmchan_matrix *m_out)
|
||||
{
|
||||
uint32_t i, j, ch, in_mask, merge_mask;
|
||||
int mul, shift;
|
||||
|
||||
|
||||
if (info == NULL || m_in == NULL || m_out == NULL ||
|
||||
AFMT_CHANNEL(info->in) != m_in->channels ||
|
||||
AFMT_CHANNEL(info->out) != m_out->channels ||
|
||||
m_in->channels < SND_CHN_MIN || m_in->channels > SND_CHN_MAX ||
|
||||
m_out->channels < SND_CHN_MIN || m_out->channels > SND_CHN_MAX)
|
||||
return (EINVAL);
|
||||
|
||||
feed_matrix_reset(info);
|
||||
|
||||
/*
|
||||
* If both in and out are part of standard matrix and identical, skip
|
||||
* everything alltogether.
|
||||
*/
|
||||
if (m_in->id == m_out->id && !(m_in->id < SND_CHN_MATRIX_BEGIN ||
|
||||
m_in->id > SND_CHN_MATRIX_END))
|
||||
return (0);
|
||||
|
||||
/*
|
||||
* Special case for mono input matrix. If the output supports
|
||||
* possible 'center' channel, route it there. Otherwise, let it be
|
||||
* matrixed to left/right.
|
||||
*/
|
||||
if (m_in->id == SND_CHN_MATRIX_1_0) {
|
||||
if (m_out->id == SND_CHN_MATRIX_1_0)
|
||||
in_mask = SND_CHN_T_MASK_FL;
|
||||
else if (m_out->mask & SND_CHN_T_MASK_FC)
|
||||
in_mask = SND_CHN_T_MASK_FC;
|
||||
else
|
||||
in_mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR;
|
||||
} else
|
||||
in_mask = m_in->mask;
|
||||
|
||||
/* Merge, reduce, expand all possibilites. */
|
||||
for (ch = SND_CHN_T_BEGIN; ch <= SND_CHN_T_END &&
|
||||
m_out->map[ch].type != SND_CHN_T_MAX; ch += SND_CHN_T_STEP) {
|
||||
merge_mask = m_out->map[ch].members & in_mask;
|
||||
if (merge_mask == 0) {
|
||||
info->matrix[ch].chn[0] = SND_CHN_T_NULL;
|
||||
continue;
|
||||
}
|
||||
|
||||
j = 0;
|
||||
for (i = SND_CHN_T_BEGIN; i <= SND_CHN_T_END;
|
||||
i += SND_CHN_T_STEP) {
|
||||
if (merge_mask & (1 << i)) {
|
||||
if (m_in->offset[i] >= 0 &&
|
||||
m_in->offset[i] < (int)m_in->channels)
|
||||
info->matrix[ch].chn[j++] =
|
||||
m_in->offset[i] * info->bps;
|
||||
else {
|
||||
info->matrix[ch].chn[j++] =
|
||||
SND_CHN_T_EOF;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define FEEDMATRIX_ATTN_SHIFT 16
|
||||
|
||||
if (j > 1) {
|
||||
/*
|
||||
* XXX For channel that require accumulation from
|
||||
* multiple channels, apply a slight attenuation to
|
||||
* avoid clipping.
|
||||
*/
|
||||
mul = (1 << (FEEDMATRIX_ATTN_SHIFT - 1)) + 143 - j;
|
||||
shift = FEEDMATRIX_ATTN_SHIFT;
|
||||
while ((mul & 1) == 0 && shift > 0) {
|
||||
mul >>= 1;
|
||||
shift--;
|
||||
}
|
||||
info->matrix[ch].mul = mul;
|
||||
info->matrix[ch].shift = shift;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef _KERNEL
|
||||
fprintf(stderr, "Total: %d\n", ch);
|
||||
|
||||
for (i = 0; info->matrix[i].chn[0] != SND_CHN_T_EOF; i++) {
|
||||
fprintf(stderr, "%d: [", i);
|
||||
for (j = 0; info->matrix[i].chn[j] != SND_CHN_T_EOF; j++) {
|
||||
if (j != 0)
|
||||
fprintf(stderr, ", ");
|
||||
fprintf(stderr, "%d",
|
||||
(info->matrix[i].chn[j] == SND_CHN_T_NULL) ?
|
||||
0xffffffff : info->matrix[i].chn[j] / info->bps);
|
||||
}
|
||||
fprintf(stderr, "] attn: (x * %d) >> %d\n",
|
||||
info->matrix[i].mul, info->matrix[i].shift);
|
||||
}
|
||||
#endif
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
feed_matrix_init(struct pcm_feeder *f)
|
||||
{
|
||||
struct feed_matrix_info *info;
|
||||
struct pcmchan_matrix *m_in, *m_out;
|
||||
uint32_t i;
|
||||
int ret;
|
||||
|
||||
if (AFMT_ENCODING(f->desc->in) != AFMT_ENCODING(f->desc->out))
|
||||
return (EINVAL);
|
||||
|
||||
info = malloc(sizeof(*info), M_DEVBUF, M_NOWAIT | M_ZERO);
|
||||
if (info == NULL)
|
||||
return (ENOMEM);
|
||||
|
||||
info->in = f->desc->in;
|
||||
info->out = f->desc->out;
|
||||
info->bps = AFMT_BPS(info->in);
|
||||
info->ialign = AFMT_ALIGN(info->in);
|
||||
info->oalign = AFMT_ALIGN(info->out);
|
||||
info->apply = NULL;
|
||||
|
||||
for (i = 0; info->apply == NULL &&
|
||||
i < (sizeof(feed_matrix_tab) / sizeof(feed_matrix_tab[0])); i++) {
|
||||
if (AFMT_ENCODING(info->in) == feed_matrix_tab[i].format)
|
||||
info->apply = feed_matrix_tab[i].apply;
|
||||
}
|
||||
|
||||
if (info->apply == NULL) {
|
||||
#ifdef FEEDMATRIX_GENERIC
|
||||
info->rd = feeder_format_read_op(info->in);
|
||||
info->wr = feeder_format_write_op(info->out);
|
||||
if (info->rd == NULL || info->wr == NULL) {
|
||||
free(info, M_DEVBUF);
|
||||
return (EINVAL);
|
||||
}
|
||||
info->apply = feed_matrix_apply_generic;
|
||||
#else
|
||||
free(info, M_DEVBUF);
|
||||
return (EINVAL);
|
||||
#endif
|
||||
}
|
||||
|
||||
m_in = feeder_matrix_format_map(info->in);
|
||||
m_out = feeder_matrix_format_map(info->out);
|
||||
|
||||
ret = feed_matrix_setup(info, m_in, m_out);
|
||||
if (ret != 0) {
|
||||
free(info, M_DEVBUF);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
f->data = info;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
feed_matrix_free(struct pcm_feeder *f)
|
||||
{
|
||||
struct feed_matrix_info *info;
|
||||
|
||||
info = f->data;
|
||||
if (info != NULL)
|
||||
free(info, M_DEVBUF);
|
||||
|
||||
f->data = NULL;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
feed_matrix_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
|
||||
uint32_t count, void *source)
|
||||
{
|
||||
struct feed_matrix_info *info;
|
||||
uint32_t j, inmax;
|
||||
uint8_t *src, *dst;
|
||||
|
||||
info = f->data;
|
||||
if (info->matrix[0].chn[0] == SND_CHN_T_EOF)
|
||||
return (FEEDER_FEED(f->source, c, b, count, source));
|
||||
|
||||
dst = b;
|
||||
count = SND_FXROUND(count, info->oalign);
|
||||
inmax = info->ialign + info->oalign;
|
||||
|
||||
/*
|
||||
* This loop might look simmilar to other feeder_* loops, but be
|
||||
* advised: matrixing might involve overlapping (think about
|
||||
* swapping end to front or something like that). In this regard it
|
||||
* might be simmilar to feeder_format, but feeder_format works on
|
||||
* 'sample' domain where it can be fitted into single 32bit integer
|
||||
* while matrixing works on 'sample frame' domain.
|
||||
*/
|
||||
do {
|
||||
if (count < info->oalign)
|
||||
break;
|
||||
|
||||
if (count < inmax) {
|
||||
src = info->reservoir;
|
||||
j = info->ialign;
|
||||
} else {
|
||||
if (info->ialign == info->oalign)
|
||||
j = count - info->oalign;
|
||||
else if (info->ialign > info->oalign)
|
||||
j = SND_FXROUND(count - info->oalign,
|
||||
info->ialign);
|
||||
else
|
||||
j = (SND_FXDIV(count, info->oalign) - 1) *
|
||||
info->ialign;
|
||||
src = dst + count - j;
|
||||
}
|
||||
|
||||
j = SND_FXDIV(FEEDER_FEED(f->source, c, src, j, source),
|
||||
info->ialign);
|
||||
if (j == 0)
|
||||
break;
|
||||
|
||||
info->apply(info, src, dst, j);
|
||||
|
||||
j *= info->oalign;
|
||||
dst += j;
|
||||
count -= j;
|
||||
|
||||
} while (count != 0);
|
||||
|
||||
return (dst - b);
|
||||
}
|
||||
|
||||
static struct pcm_feederdesc feeder_matrix_desc[] = {
|
||||
{ FEEDER_MATRIX, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
static kobj_method_t feeder_matrix_methods[] = {
|
||||
KOBJMETHOD(feeder_init, feed_matrix_init),
|
||||
KOBJMETHOD(feeder_free, feed_matrix_free),
|
||||
KOBJMETHOD(feeder_feed, feed_matrix_feed),
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
|
||||
FEEDER_DECLARE(feeder_matrix, NULL);
|
||||
|
||||
/* External */
|
||||
int
|
||||
feeder_matrix_setup(struct pcm_feeder *f, struct pcmchan_matrix *m_in,
|
||||
struct pcmchan_matrix *m_out)
|
||||
{
|
||||
|
||||
if (f == NULL || f->desc == NULL || f->desc->type != FEEDER_MATRIX ||
|
||||
f->data == NULL)
|
||||
return (EINVAL);
|
||||
|
||||
return (feed_matrix_setup(f->data, m_in, m_out));
|
||||
}
|
||||
|
||||
/*
|
||||
* feeder_matrix_default_id(): For a given number of channels, return
|
||||
* default prefered id (example: both 5.1 and
|
||||
* 6.0 are simply 6 channels, but 5.1 is more
|
||||
* preferable).
|
||||
*/
|
||||
int
|
||||
feeder_matrix_default_id(uint32_t ch)
|
||||
{
|
||||
|
||||
if (ch < feeder_matrix_maps[SND_CHN_MATRIX_BEGIN].channels ||
|
||||
ch > feeder_matrix_maps[SND_CHN_MATRIX_END].channels)
|
||||
return (SND_CHN_MATRIX_UNKNOWN);
|
||||
|
||||
return (feeder_matrix_maps[feeder_matrix_default_ids[ch]].id);
|
||||
}
|
||||
|
||||
/*
|
||||
* feeder_matrix_default_channel_map(): Ditto, but return matrix map
|
||||
* instead.
|
||||
*/
|
||||
struct pcmchan_matrix *
|
||||
feeder_matrix_default_channel_map(uint32_t ch)
|
||||
{
|
||||
|
||||
if (ch < feeder_matrix_maps[SND_CHN_MATRIX_BEGIN].channels ||
|
||||
ch > feeder_matrix_maps[SND_CHN_MATRIX_END].channels)
|
||||
return (NULL);
|
||||
|
||||
return (&feeder_matrix_maps[feeder_matrix_default_ids[ch]]);
|
||||
}
|
||||
|
||||
/*
|
||||
* feeder_matrix_default_format(): For a given audio format, return the
|
||||
* proper audio format based on preferable
|
||||
* matrix.
|
||||
*/
|
||||
uint32_t
|
||||
feeder_matrix_default_format(uint32_t format)
|
||||
{
|
||||
struct pcmchan_matrix *m;
|
||||
uint32_t i, ch, ext;
|
||||
|
||||
ch = AFMT_CHANNEL(format);
|
||||
ext = AFMT_EXTCHANNEL(format);
|
||||
|
||||
if (ext != 0) {
|
||||
for (i = SND_CHN_MATRIX_BEGIN; i <= SND_CHN_MATRIX_END; i++) {
|
||||
if (feeder_matrix_maps[i].channels == ch &&
|
||||
feeder_matrix_maps[i].ext == ext)
|
||||
return (SND_FORMAT(format, ch, ext));
|
||||
}
|
||||
}
|
||||
|
||||
m = feeder_matrix_default_channel_map(ch);
|
||||
if (m == NULL)
|
||||
return (0x00000000);
|
||||
|
||||
return (SND_FORMAT(format, ch, m->ext));
|
||||
}
|
||||
|
||||
/*
|
||||
* feeder_matrix_format_id(): For a given audio format, return its matrix
|
||||
* id.
|
||||
*/
|
||||
int
|
||||
feeder_matrix_format_id(uint32_t format)
|
||||
{
|
||||
uint32_t i, ch, ext;
|
||||
|
||||
ch = AFMT_CHANNEL(format);
|
||||
ext = AFMT_EXTCHANNEL(format);
|
||||
|
||||
for (i = SND_CHN_MATRIX_BEGIN; i <= SND_CHN_MATRIX_END; i++) {
|
||||
if (feeder_matrix_maps[i].channels == ch &&
|
||||
feeder_matrix_maps[i].ext == ext)
|
||||
return (feeder_matrix_maps[i].id);
|
||||
}
|
||||
|
||||
return (SND_CHN_MATRIX_UNKNOWN);
|
||||
}
|
||||
|
||||
/*
|
||||
* feeder_matrix_format_map(): For a given audio format, return its matrix
|
||||
* map.
|
||||
*/
|
||||
struct pcmchan_matrix *
|
||||
feeder_matrix_format_map(uint32_t format)
|
||||
{
|
||||
uint32_t i, ch, ext;
|
||||
|
||||
ch = AFMT_CHANNEL(format);
|
||||
ext = AFMT_EXTCHANNEL(format);
|
||||
|
||||
for (i = SND_CHN_MATRIX_BEGIN; i <= SND_CHN_MATRIX_END; i++) {
|
||||
if (feeder_matrix_maps[i].channels == ch &&
|
||||
feeder_matrix_maps[i].ext == ext)
|
||||
return (&feeder_matrix_maps[i]);
|
||||
}
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* feeder_matrix_id_map(): For a given matrix id, return its matrix map.
|
||||
*/
|
||||
struct pcmchan_matrix *
|
||||
feeder_matrix_id_map(int id)
|
||||
{
|
||||
|
||||
if (id < SND_CHN_MATRIX_BEGIN || id > SND_CHN_MATRIX_END)
|
||||
return (NULL);
|
||||
|
||||
return (&feeder_matrix_maps[id]);
|
||||
}
|
||||
|
||||
/*
|
||||
* feeder_matrix_compare(): Compare the simmilarities of matrices.
|
||||
*/
|
||||
int
|
||||
feeder_matrix_compare(struct pcmchan_matrix *m_in, struct pcmchan_matrix *m_out)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
if (m_in == m_out)
|
||||
return (0);
|
||||
|
||||
if (m_in->channels != m_out->channels || m_in->ext != m_out->ext ||
|
||||
m_in->mask != m_out->mask)
|
||||
return (1);
|
||||
|
||||
for (i = 0; i < (sizeof(m_in->map) / sizeof(m_in->map[0])); i++) {
|
||||
if (m_in->map[i].type != m_out->map[i].type)
|
||||
return (1);
|
||||
if (m_in->map[i].type == SND_CHN_T_MAX)
|
||||
break;
|
||||
if (m_in->map[i].members != m_out->map[i].members)
|
||||
return (1);
|
||||
if (i <= SND_CHN_T_END) {
|
||||
if (m_in->offset[m_in->map[i].type] !=
|
||||
m_out->offset[m_out->map[i].type])
|
||||
return (1);
|
||||
}
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* XXX 4front intepretation of "surround" is ambigous and sort of
|
||||
* conflicting with "rear"/"back". Map it to "side". Well..
|
||||
* who cares?
|
||||
*/
|
||||
static int snd_chn_to_oss[SND_CHN_T_MAX] = {
|
||||
[SND_CHN_T_FL] = CHID_L,
|
||||
[SND_CHN_T_FR] = CHID_R,
|
||||
[SND_CHN_T_FC] = CHID_C,
|
||||
[SND_CHN_T_LF] = CHID_LFE,
|
||||
[SND_CHN_T_SL] = CHID_LS,
|
||||
[SND_CHN_T_SR] = CHID_RS,
|
||||
[SND_CHN_T_BL] = CHID_LR,
|
||||
[SND_CHN_T_BR] = CHID_RR
|
||||
};
|
||||
|
||||
#define SND_CHN_OSS_VALIDMASK \
|
||||
(SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \
|
||||
SND_CHN_T_MASK_FC | SND_CHN_T_MASK_LF | \
|
||||
SND_CHN_T_MASK_SL | SND_CHN_T_MASK_SR | \
|
||||
SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR)
|
||||
|
||||
#define SND_CHN_OSS_MAX 8
|
||||
#define SND_CHN_OSS_BEGIN CHID_L
|
||||
#define SND_CHN_OSS_END CHID_RR
|
||||
|
||||
static int oss_to_snd_chn[SND_CHN_OSS_END + 1] = {
|
||||
[CHID_L] = SND_CHN_T_FL,
|
||||
[CHID_R] = SND_CHN_T_FR,
|
||||
[CHID_C] = SND_CHN_T_FC,
|
||||
[CHID_LFE] = SND_CHN_T_LF,
|
||||
[CHID_LS] = SND_CHN_T_SL,
|
||||
[CHID_RS] = SND_CHN_T_SR,
|
||||
[CHID_LR] = SND_CHN_T_BL,
|
||||
[CHID_RR] = SND_CHN_T_BR
|
||||
};
|
||||
|
||||
/*
|
||||
* Used by SNDCTL_DSP_GET_CHNORDER.
|
||||
*/
|
||||
int
|
||||
feeder_matrix_oss_get_channel_order(struct pcmchan_matrix *m,
|
||||
unsigned long long *map)
|
||||
{
|
||||
unsigned long long tmpmap;
|
||||
uint32_t i;
|
||||
|
||||
if (m == NULL || map == NULL || (m->mask & ~SND_CHN_OSS_VALIDMASK) ||
|
||||
m->channels > SND_CHN_OSS_MAX)
|
||||
return (EINVAL);
|
||||
|
||||
tmpmap = 0x0000000000000000ULL;
|
||||
|
||||
for (i = 0; m->map[i].type != SND_CHN_T_MAX &&
|
||||
i < SND_CHN_OSS_MAX; i++) {
|
||||
if ((1 << m->map[i].type) & ~SND_CHN_OSS_VALIDMASK)
|
||||
return (EINVAL);
|
||||
tmpmap |=
|
||||
(unsigned long long)snd_chn_to_oss[m->map[i].type] <<
|
||||
(i * 4);
|
||||
}
|
||||
|
||||
*map = tmpmap;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Used by SNDCTL_DSP_SET_CHNORDER.
|
||||
*/
|
||||
int
|
||||
feeder_matrix_oss_set_channel_order(struct pcmchan_matrix *m,
|
||||
unsigned long long *map)
|
||||
{
|
||||
struct pcmchan_matrix tmp;
|
||||
uint32_t chmask, i;
|
||||
int ch, cheof;
|
||||
|
||||
if (m == NULL || map == NULL || (m->mask & ~SND_CHN_OSS_VALIDMASK) ||
|
||||
m->channels > SND_CHN_OSS_MAX || (*map & 0xffffffff00000000ULL))
|
||||
return (EINVAL);
|
||||
|
||||
tmp = *m;
|
||||
tmp.channels = 0;
|
||||
tmp.ext = 0;
|
||||
tmp.mask = 0;
|
||||
memset(tmp.offset, -1, sizeof(tmp.offset));
|
||||
cheof = 0;
|
||||
|
||||
for (i = 0; i < SND_CHN_OSS_MAX; i++) {
|
||||
ch = (*map >> (i * 4)) & 0xf;
|
||||
if (ch < SND_CHN_OSS_BEGIN) {
|
||||
if (cheof == 0 && m->map[i].type != SND_CHN_T_MAX)
|
||||
return (EINVAL);
|
||||
cheof++;
|
||||
tmp.map[i] = m->map[i];
|
||||
continue;
|
||||
} else if (ch > SND_CHN_OSS_END)
|
||||
return (EINVAL);
|
||||
else if (cheof != 0)
|
||||
return (EINVAL);
|
||||
ch = oss_to_snd_chn[ch];
|
||||
chmask = 1 << ch;
|
||||
/* channel not exist in matrix */
|
||||
if (!(chmask & m->mask))
|
||||
return (EINVAL);
|
||||
/* duplicated channel */
|
||||
if (chmask & tmp.mask)
|
||||
return (EINVAL);
|
||||
tmp.map[i] = m->map[m->offset[ch]];
|
||||
if (tmp.map[i].type != ch)
|
||||
return (EINVAL);
|
||||
tmp.offset[ch] = i;
|
||||
tmp.mask |= chmask;
|
||||
tmp.channels++;
|
||||
if (chmask & SND_CHN_T_MASK_LF)
|
||||
tmp.ext++;
|
||||
}
|
||||
|
||||
if (tmp.channels != m->channels || tmp.ext != m->ext ||
|
||||
tmp.mask != m->mask ||
|
||||
tmp.map[m->channels].type != SND_CHN_T_MAX)
|
||||
return (EINVAL);
|
||||
|
||||
*m = tmp;
|
||||
|
||||
return (0);
|
||||
}
|
402
sys/dev/sound/pcm/feeder_mixer.c
Normal file
402
sys/dev/sound/pcm/feeder_mixer.c
Normal file
@ -0,0 +1,402 @@
|
||||
/*-
|
||||
* Copyright (c) 2008-2009 Ariff Abdullah <ariff@FreeBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifdef _KERNEL
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
#include <dev/sound/pcm/pcm.h>
|
||||
#include <dev/sound/pcm/vchan.h>
|
||||
#include "feeder_if.h"
|
||||
|
||||
#define SND_USE_FXDIV
|
||||
#include "snd_fxdiv_gen.h"
|
||||
|
||||
SND_DECLARE_FILE("$FreeBSD$");
|
||||
#endif
|
||||
|
||||
#undef SND_FEEDER_MULTIFORMAT
|
||||
#define SND_FEEDER_MULTIFORMAT 1
|
||||
|
||||
typedef void (*feed_mixer_t)(uint8_t *, uint8_t *, uint32_t);
|
||||
|
||||
#define FEEDMIXER_DECLARE(SIGN, BIT, ENDIAN) \
|
||||
static void \
|
||||
feed_mixer_##SIGN##BIT##ENDIAN(uint8_t *src, uint8_t *dst, \
|
||||
uint32_t count) \
|
||||
{ \
|
||||
intpcm##BIT##_t z; \
|
||||
intpcm_t x, y; \
|
||||
\
|
||||
src += count; \
|
||||
dst += count; \
|
||||
\
|
||||
do { \
|
||||
src -= PCM_##BIT##_BPS; \
|
||||
dst -= PCM_##BIT##_BPS; \
|
||||
count -= PCM_##BIT##_BPS; \
|
||||
x = PCM_READ_##SIGN##BIT##_##ENDIAN(src); \
|
||||
y = PCM_READ_##SIGN##BIT##_##ENDIAN(dst); \
|
||||
z = INTPCM##BIT##_T(x) + y; \
|
||||
x = PCM_CLAMP_##SIGN##BIT(z); \
|
||||
_PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, x); \
|
||||
} while (count != 0); \
|
||||
}
|
||||
|
||||
#if BYTE_ORDER == LITTLE_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
|
||||
FEEDMIXER_DECLARE(S, 16, LE)
|
||||
FEEDMIXER_DECLARE(S, 32, LE)
|
||||
#endif
|
||||
#if BYTE_ORDER == BIG_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
|
||||
FEEDMIXER_DECLARE(S, 16, BE)
|
||||
FEEDMIXER_DECLARE(S, 32, BE)
|
||||
#endif
|
||||
#ifdef SND_FEEDER_MULTIFORMAT
|
||||
FEEDMIXER_DECLARE(S, 8, NE)
|
||||
FEEDMIXER_DECLARE(S, 24, LE)
|
||||
FEEDMIXER_DECLARE(S, 24, BE)
|
||||
FEEDMIXER_DECLARE(U, 8, NE)
|
||||
FEEDMIXER_DECLARE(U, 16, LE)
|
||||
FEEDMIXER_DECLARE(U, 24, LE)
|
||||
FEEDMIXER_DECLARE(U, 32, LE)
|
||||
FEEDMIXER_DECLARE(U, 16, BE)
|
||||
FEEDMIXER_DECLARE(U, 24, BE)
|
||||
FEEDMIXER_DECLARE(U, 32, BE)
|
||||
#endif
|
||||
|
||||
struct feed_mixer_info {
|
||||
uint32_t format;
|
||||
int bps;
|
||||
feed_mixer_t mix;
|
||||
};
|
||||
|
||||
#define FEEDMIXER_ENTRY(SIGN, BIT, ENDIAN) \
|
||||
{ \
|
||||
AFMT_##SIGN##BIT##_##ENDIAN, PCM_##BIT##_BPS, \
|
||||
feed_mixer_##SIGN##BIT##ENDIAN \
|
||||
}
|
||||
|
||||
static struct feed_mixer_info feed_mixer_info_tab[] = {
|
||||
FEEDMIXER_ENTRY(S, 8, NE),
|
||||
#if BYTE_ORDER == LITTLE_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
|
||||
FEEDMIXER_ENTRY(S, 16, LE),
|
||||
FEEDMIXER_ENTRY(S, 32, LE),
|
||||
#endif
|
||||
#if BYTE_ORDER == BIG_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
|
||||
FEEDMIXER_ENTRY(S, 16, BE),
|
||||
FEEDMIXER_ENTRY(S, 32, BE),
|
||||
#endif
|
||||
#ifdef SND_FEEDER_MULTIFORMAT
|
||||
FEEDMIXER_ENTRY(S, 24, LE),
|
||||
FEEDMIXER_ENTRY(S, 24, BE),
|
||||
FEEDMIXER_ENTRY(U, 8, NE),
|
||||
FEEDMIXER_ENTRY(U, 16, LE),
|
||||
FEEDMIXER_ENTRY(U, 24, LE),
|
||||
FEEDMIXER_ENTRY(U, 32, LE),
|
||||
FEEDMIXER_ENTRY(U, 16, BE),
|
||||
FEEDMIXER_ENTRY(U, 24, BE),
|
||||
FEEDMIXER_ENTRY(U, 32, BE),
|
||||
#endif
|
||||
{ AFMT_AC3, PCM_16_BPS, NULL },
|
||||
{ AFMT_MU_LAW, PCM_8_BPS, feed_mixer_U8NE }, /* dummy */
|
||||
{ AFMT_A_LAW, PCM_8_BPS, feed_mixer_U8NE } /* dummy */
|
||||
};
|
||||
|
||||
#define FEEDMIXER_TAB_SIZE ((int32_t) \
|
||||
(sizeof(feed_mixer_info_tab) / \
|
||||
sizeof(feed_mixer_info_tab[0])))
|
||||
|
||||
#define FEEDMIXER_DATA(i, c) ((void *) \
|
||||
((uintptr_t)((((i) & 0x1f) << 5) | \
|
||||
((c) & 0x1f))))
|
||||
#define FEEDMIXER_INFOIDX(d) ((uint32_t)((uintptr_t)(d) >> 5) & 0x1f)
|
||||
#define FEEDMIXER_CHANNELS(d) ((uint32_t)((uintptr_t)(d)) & 0x1f)
|
||||
|
||||
static int
|
||||
feed_mixer_init(struct pcm_feeder *f)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (f->desc->in != f->desc->out)
|
||||
return (EINVAL);
|
||||
|
||||
for (i = 0; i < FEEDMIXER_TAB_SIZE; i++) {
|
||||
if (AFMT_ENCODING(f->desc->in) ==
|
||||
feed_mixer_info_tab[i].format) {
|
||||
f->data =
|
||||
FEEDMIXER_DATA(i, AFMT_CHANNEL(f->desc->in));
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
static int
|
||||
feed_mixer_set(struct pcm_feeder *f, int what, int value)
|
||||
{
|
||||
|
||||
switch (what) {
|
||||
case FEEDMIXER_CHANNELS:
|
||||
if (value < SND_CHN_MIN || value > SND_CHN_MAX)
|
||||
return (EINVAL);
|
||||
f->data = FEEDMIXER_DATA(FEEDMIXER_INFOIDX(f->data), value);
|
||||
break;
|
||||
default:
|
||||
return (EINVAL);
|
||||
break;
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static __inline int
|
||||
feed_mixer_rec(struct pcm_channel *c)
|
||||
{
|
||||
struct pcm_channel *ch;
|
||||
struct snd_dbuf *b, *bs;
|
||||
uint32_t cnt, maxfeed;
|
||||
int rdy;
|
||||
|
||||
/*
|
||||
* Reset ready and moving pointer. We're not using bufsoft
|
||||
* anywhere since its sole purpose is to become the primary
|
||||
* distributor for the recorded buffer and also as an interrupt
|
||||
* threshold progress indicator.
|
||||
*/
|
||||
b = c->bufsoft;
|
||||
b->rp = 0;
|
||||
b->rl = 0;
|
||||
cnt = sndbuf_getsize(b);
|
||||
maxfeed = SND_FXROUND(SND_FXDIV_MAX, sndbuf_getalign(b));
|
||||
|
||||
do {
|
||||
cnt = FEEDER_FEED(c->feeder->source, c, b->tmpbuf,
|
||||
min(cnt, maxfeed), c->bufhard);
|
||||
if (cnt != 0) {
|
||||
sndbuf_acquire(b, b->tmpbuf, cnt);
|
||||
cnt = sndbuf_getfree(b);
|
||||
}
|
||||
} while (cnt != 0);
|
||||
|
||||
/* Not enough data */
|
||||
if (b->rl < sndbuf_getalign(b)) {
|
||||
b->rl = 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Keep track of ready and moving pointer since we will use
|
||||
* bufsoft over and over again, pretending nothing has happened.
|
||||
*/
|
||||
rdy = b->rl;
|
||||
|
||||
CHN_FOREACH(ch, c, children.busy) {
|
||||
CHN_LOCK(ch);
|
||||
if (CHN_STOPPED(ch) || (ch->flags & CHN_F_DIRTY)) {
|
||||
CHN_UNLOCK(ch);
|
||||
continue;
|
||||
}
|
||||
#ifdef SND_DEBUG
|
||||
if ((c->flags & CHN_F_DIRTY) && VCHAN_SYNC_REQUIRED(ch)) {
|
||||
if (vchan_sync(ch) != 0) {
|
||||
CHN_UNLOCK(ch);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
bs = ch->bufsoft;
|
||||
if (ch->flags & CHN_F_MMAP)
|
||||
sndbuf_dispose(bs, NULL, sndbuf_getready(bs));
|
||||
cnt = sndbuf_getfree(bs);
|
||||
if (cnt < sndbuf_getalign(bs)) {
|
||||
CHN_UNLOCK(ch);
|
||||
continue;
|
||||
}
|
||||
maxfeed = SND_FXROUND(SND_FXDIV_MAX, sndbuf_getalign(bs));
|
||||
do {
|
||||
cnt = FEEDER_FEED(ch->feeder, ch, bs->tmpbuf,
|
||||
min(cnt, maxfeed), b);
|
||||
if (cnt != 0) {
|
||||
sndbuf_acquire(bs, bs->tmpbuf, cnt);
|
||||
cnt = sndbuf_getfree(bs);
|
||||
}
|
||||
} while (cnt != 0);
|
||||
/*
|
||||
* Not entirely flushed out...
|
||||
*/
|
||||
if (b->rl != 0)
|
||||
ch->xruns++;
|
||||
CHN_UNLOCK(ch);
|
||||
/*
|
||||
* Rewind buffer position for next virtual channel.
|
||||
*/
|
||||
b->rp = 0;
|
||||
b->rl = rdy;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set ready pointer to indicate that our children are ready
|
||||
* to be woken up, also as an interrupt threshold progress
|
||||
* indicator.
|
||||
*/
|
||||
b->rl = 1;
|
||||
|
||||
c->flags &= ~CHN_F_DIRTY;
|
||||
|
||||
/*
|
||||
* Return 0 to bail out early from sndbuf_feed() loop.
|
||||
* No need to increase feedcount counter since part of this
|
||||
* feeder chains already include feed_root().
|
||||
*/
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
feed_mixer_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
|
||||
uint32_t count, void *source)
|
||||
{
|
||||
struct feed_mixer_info *info;
|
||||
struct snd_dbuf *src = source;
|
||||
struct pcm_channel *ch;
|
||||
uint32_t cnt, mcnt, rcnt, sz;
|
||||
int passthrough;
|
||||
uint8_t *tmp;
|
||||
|
||||
if (c->direction == PCMDIR_REC)
|
||||
return (feed_mixer_rec(c));
|
||||
|
||||
sz = sndbuf_getsize(src);
|
||||
if (sz < count)
|
||||
count = sz;
|
||||
|
||||
info = &feed_mixer_info_tab[FEEDMIXER_INFOIDX(f->data)];
|
||||
sz = info->bps * FEEDMIXER_CHANNELS(f->data);
|
||||
count = SND_FXROUND(count, sz);
|
||||
if (count < sz)
|
||||
return (0);
|
||||
|
||||
/*
|
||||
* We are going to use our source as a temporary buffer since it's
|
||||
* got no other purpose. We obtain our data by traversing the channel
|
||||
* list of children and calling mixer function to mix count bytes from
|
||||
* each into our destination buffer, b.
|
||||
*/
|
||||
tmp = sndbuf_getbuf(src);
|
||||
rcnt = 0;
|
||||
mcnt = 0;
|
||||
passthrough = 0; /* 'passthrough' / 'exclusive' marker */
|
||||
|
||||
CHN_FOREACH(ch, c, children.busy) {
|
||||
CHN_LOCK(ch);
|
||||
if (CHN_STOPPED(ch) || (ch->flags & CHN_F_DIRTY)) {
|
||||
CHN_UNLOCK(ch);
|
||||
continue;
|
||||
}
|
||||
#ifdef SND_DEBUG
|
||||
if ((c->flags & CHN_F_DIRTY) && VCHAN_SYNC_REQUIRED(ch)) {
|
||||
if (vchan_sync(ch) != 0) {
|
||||
CHN_UNLOCK(ch);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if ((ch->flags & CHN_F_MMAP) && !(ch->flags & CHN_F_CLOSING))
|
||||
sndbuf_acquire(ch->bufsoft, NULL,
|
||||
sndbuf_getfree(ch->bufsoft));
|
||||
if (info->mix == NULL) {
|
||||
/*
|
||||
* Passthrough. Dump the first digital/passthrough
|
||||
* channel into destination buffer, and the rest into
|
||||
* nothingness (mute effect).
|
||||
*/
|
||||
if (passthrough == 0 &&
|
||||
(ch->format & AFMT_PASSTHROUGH)) {
|
||||
rcnt = SND_FXROUND(FEEDER_FEED(ch->feeder, ch,
|
||||
b, count, ch->bufsoft), sz);
|
||||
passthrough = 1;
|
||||
} else
|
||||
FEEDER_FEED(ch->feeder, ch, tmp, count,
|
||||
ch->bufsoft);
|
||||
} else if (c->flags & CHN_F_EXCLUSIVE) {
|
||||
/*
|
||||
* Exclusive. Dump the first 'exclusive' channel into
|
||||
* destination buffer, and the rest into nothingness
|
||||
* (mute effect).
|
||||
*/
|
||||
if (passthrough == 0 && (ch->flags & CHN_F_EXCLUSIVE)) {
|
||||
rcnt = SND_FXROUND(FEEDER_FEED(ch->feeder, ch,
|
||||
b, count, ch->bufsoft), sz);
|
||||
passthrough = 1;
|
||||
} else
|
||||
FEEDER_FEED(ch->feeder, ch, tmp, count,
|
||||
ch->bufsoft);
|
||||
} else {
|
||||
if (rcnt == 0) {
|
||||
rcnt = SND_FXROUND(FEEDER_FEED(ch->feeder, ch,
|
||||
b, count, ch->bufsoft), sz);
|
||||
mcnt = count - rcnt;
|
||||
} else {
|
||||
cnt = SND_FXROUND(FEEDER_FEED(ch->feeder, ch,
|
||||
tmp, count, ch->bufsoft), sz);
|
||||
if (cnt != 0) {
|
||||
if (mcnt != 0) {
|
||||
memset(b + rcnt,
|
||||
sndbuf_zerodata(
|
||||
f->desc->out), mcnt);
|
||||
mcnt = 0;
|
||||
}
|
||||
info->mix(tmp, b, cnt);
|
||||
if (cnt > rcnt)
|
||||
rcnt = cnt;
|
||||
}
|
||||
}
|
||||
}
|
||||
CHN_UNLOCK(ch);
|
||||
}
|
||||
|
||||
if (++c->feedcount == 0)
|
||||
c->feedcount = 2;
|
||||
|
||||
c->flags &= ~CHN_F_DIRTY;
|
||||
|
||||
return (rcnt);
|
||||
}
|
||||
|
||||
static struct pcm_feederdesc feeder_mixer_desc[] = {
|
||||
{ FEEDER_MIXER, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
static kobj_method_t feeder_mixer_methods[] = {
|
||||
KOBJMETHOD(feeder_init, feed_mixer_init),
|
||||
KOBJMETHOD(feeder_set, feed_mixer_set),
|
||||
KOBJMETHOD(feeder_feed, feed_mixer_feed),
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
|
||||
FEEDER_DECLARE(feeder_mixer, NULL);
|
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,5 @@
|
||||
/*-
|
||||
* Copyright (c) 2005 Ariff Abdullah <ariff@FreeBSD.org>
|
||||
* Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -26,159 +26,316 @@
|
||||
|
||||
/* feeder_volume, a long 'Lost Technology' rather than a new feature. */
|
||||
|
||||
#ifdef _KERNEL
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
#include <dev/sound/pcm/pcm.h>
|
||||
#include "feeder_if.h"
|
||||
|
||||
#define SND_USE_FXDIV
|
||||
#include "snd_fxdiv_gen.h"
|
||||
|
||||
SND_DECLARE_FILE("$FreeBSD$");
|
||||
#endif
|
||||
|
||||
#define FVOL_OSS_SCALE 100
|
||||
#define FVOL_RESOLUTION PCM_FXSHIFT
|
||||
#define FVOL_CLAMP(val) (((val) << FVOL_RESOLUTION) / FVOL_OSS_SCALE)
|
||||
#define FVOL_LEFT(val) FVOL_CLAMP((val) & 0x7f)
|
||||
#define FVOL_RIGHT(val) FVOL_LEFT((val) >> 8)
|
||||
#define FVOL_MAX (1 << FVOL_RESOLUTION)
|
||||
#define FVOL_CALC(sval, vval) (((sval) * (vval)) >> FVOL_RESOLUTION)
|
||||
typedef void (*feed_volume_t)(int *, int *, uint32_t, uint8_t *, uint32_t);
|
||||
|
||||
typedef uint32_t (*feed_volume_filter)(uint8_t *, int *, uint32_t);
|
||||
#define FEEDVOLUME_CALC8(s, v) (SND_VOL_CALC_SAMPLE((intpcm_t) \
|
||||
(s) << 8, v) >> 8)
|
||||
#define FEEDVOLUME_CALC16(s, v) SND_VOL_CALC_SAMPLE((intpcm_t)(s), v)
|
||||
#define FEEDVOLUME_CALC24(s, v) SND_VOL_CALC_SAMPLE((intpcm64_t)(s), v)
|
||||
#define FEEDVOLUME_CALC32(s, v) SND_VOL_CALC_SAMPLE((intpcm64_t)(s), v)
|
||||
|
||||
#define FEEDER_VOLUME_FILTER(FMTBIT, VOL_INTCAST, SIGN, SIGNS, ENDIAN, ENDIANS) \
|
||||
static uint32_t \
|
||||
feed_volume_filter_##SIGNS##FMTBIT##ENDIANS(uint8_t *b, int *vol, \
|
||||
uint32_t count) \
|
||||
{ \
|
||||
int32_t j; \
|
||||
int i; \
|
||||
\
|
||||
i = count; \
|
||||
b += i; \
|
||||
\
|
||||
do { \
|
||||
b -= PCM_##FMTBIT##_BPS; \
|
||||
i -= PCM_##FMTBIT##_BPS; \
|
||||
j = PCM_READ_##SIGN##FMTBIT##_##ENDIAN(b); \
|
||||
j = FVOL_CALC((VOL_INTCAST)j, \
|
||||
vol[(i / PCM_##FMTBIT##_BPS) & 1]); \
|
||||
PCM_WRITE_##SIGN##FMTBIT##_##ENDIAN(b, j); \
|
||||
} while (i != 0); \
|
||||
\
|
||||
return (count); \
|
||||
#define FEEDVOLUME_DECLARE(SIGN, BIT, ENDIAN) \
|
||||
static void \
|
||||
feed_volume_##SIGN##BIT##ENDIAN(int *vol, int *matrix, \
|
||||
uint32_t channels, uint8_t *dst, uint32_t count) \
|
||||
{ \
|
||||
intpcm##BIT##_t v; \
|
||||
intpcm_t x; \
|
||||
uint32_t i; \
|
||||
\
|
||||
dst += count * PCM_##BIT##_BPS * channels; \
|
||||
do { \
|
||||
i = channels; \
|
||||
do { \
|
||||
dst -= PCM_##BIT##_BPS; \
|
||||
i--; \
|
||||
x = PCM_READ_##SIGN##BIT##_##ENDIAN(dst); \
|
||||
v = FEEDVOLUME_CALC##BIT(x, vol[matrix[i]]); \
|
||||
x = PCM_CLAMP_##SIGN##BIT(v); \
|
||||
_PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, x); \
|
||||
} while (i != 0); \
|
||||
} while (--count != 0); \
|
||||
}
|
||||
|
||||
FEEDER_VOLUME_FILTER(8, int32_t, S, s, NE, ne)
|
||||
FEEDER_VOLUME_FILTER(16, int32_t, S, s, LE, le)
|
||||
FEEDER_VOLUME_FILTER(24, int32_t, S, s, LE, le)
|
||||
FEEDER_VOLUME_FILTER(32, intpcm_t, S, s, LE, le)
|
||||
FEEDER_VOLUME_FILTER(16, int32_t, S, s, BE, be)
|
||||
FEEDER_VOLUME_FILTER(24, int32_t, S, s, BE, be)
|
||||
FEEDER_VOLUME_FILTER(32, intpcm_t, S, s, BE, be)
|
||||
FEEDER_VOLUME_FILTER(8, int32_t, U, u, NE, ne)
|
||||
FEEDER_VOLUME_FILTER(16, int32_t, U, u, LE, le)
|
||||
FEEDER_VOLUME_FILTER(24, int32_t, U, u, LE, le)
|
||||
FEEDER_VOLUME_FILTER(32, intpcm_t, U, u, LE, le)
|
||||
FEEDER_VOLUME_FILTER(16, int32_t, U, u, BE, be)
|
||||
FEEDER_VOLUME_FILTER(24, int32_t, U, u, BE, be)
|
||||
FEEDER_VOLUME_FILTER(32, intpcm_t, U, u, BE, be)
|
||||
#if BYTE_ORDER == LITTLE_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
|
||||
FEEDVOLUME_DECLARE(S, 16, LE)
|
||||
FEEDVOLUME_DECLARE(S, 32, LE)
|
||||
#endif
|
||||
#if BYTE_ORDER == BIG_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
|
||||
FEEDVOLUME_DECLARE(S, 16, BE)
|
||||
FEEDVOLUME_DECLARE(S, 32, BE)
|
||||
#endif
|
||||
#ifdef SND_FEEDER_MULTIFORMAT
|
||||
FEEDVOLUME_DECLARE(S, 8, NE)
|
||||
FEEDVOLUME_DECLARE(S, 24, LE)
|
||||
FEEDVOLUME_DECLARE(S, 24, BE)
|
||||
FEEDVOLUME_DECLARE(U, 8, NE)
|
||||
FEEDVOLUME_DECLARE(U, 16, LE)
|
||||
FEEDVOLUME_DECLARE(U, 24, LE)
|
||||
FEEDVOLUME_DECLARE(U, 32, LE)
|
||||
FEEDVOLUME_DECLARE(U, 16, BE)
|
||||
FEEDVOLUME_DECLARE(U, 24, BE)
|
||||
FEEDVOLUME_DECLARE(U, 32, BE)
|
||||
#endif
|
||||
|
||||
struct feed_volume_info {
|
||||
uint32_t bps, channels;
|
||||
feed_volume_t apply;
|
||||
int volume_class;
|
||||
int state;
|
||||
int matrix[SND_CHN_MAX];
|
||||
};
|
||||
|
||||
#define FEEDVOLUME_ENTRY(SIGN, BIT, ENDIAN) \
|
||||
{ \
|
||||
AFMT_##SIGN##BIT##_##ENDIAN, \
|
||||
feed_volume_##SIGN##BIT##ENDIAN \
|
||||
}
|
||||
|
||||
static const struct {
|
||||
uint32_t format;
|
||||
int bps;
|
||||
feed_volume_filter filter;
|
||||
feed_volume_t apply;
|
||||
} feed_volume_info_tab[] = {
|
||||
#if BYTE_ORDER == LITTLE_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
|
||||
FEEDVOLUME_ENTRY(S, 16, LE),
|
||||
FEEDVOLUME_ENTRY(S, 32, LE),
|
||||
#endif
|
||||
#if BYTE_ORDER == BIG_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
|
||||
FEEDVOLUME_ENTRY(S, 16, BE),
|
||||
FEEDVOLUME_ENTRY(S, 32, BE),
|
||||
#endif
|
||||
#ifdef SND_FEEDER_MULTIFORMAT
|
||||
FEEDVOLUME_ENTRY(S, 8, NE),
|
||||
FEEDVOLUME_ENTRY(S, 24, LE),
|
||||
FEEDVOLUME_ENTRY(S, 24, BE),
|
||||
FEEDVOLUME_ENTRY(U, 8, NE),
|
||||
FEEDVOLUME_ENTRY(U, 16, LE),
|
||||
FEEDVOLUME_ENTRY(U, 24, LE),
|
||||
FEEDVOLUME_ENTRY(U, 32, LE),
|
||||
FEEDVOLUME_ENTRY(U, 16, BE),
|
||||
FEEDVOLUME_ENTRY(U, 24, BE),
|
||||
FEEDVOLUME_ENTRY(U, 32, BE)
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct feed_volume_info feed_volume_tbl[] = {
|
||||
{ AFMT_S8, PCM_8_BPS, feed_volume_filter_s8ne },
|
||||
{ AFMT_S16_LE, PCM_16_BPS, feed_volume_filter_s16le },
|
||||
{ AFMT_S24_LE, PCM_24_BPS, feed_volume_filter_s24le },
|
||||
{ AFMT_S32_LE, PCM_32_BPS, feed_volume_filter_s32le },
|
||||
{ AFMT_S16_BE, PCM_16_BPS, feed_volume_filter_s16be },
|
||||
{ AFMT_S24_BE, PCM_24_BPS, feed_volume_filter_s24be },
|
||||
{ AFMT_S32_BE, PCM_32_BPS, feed_volume_filter_s32be },
|
||||
{ AFMT_U8, PCM_8_BPS, feed_volume_filter_u8ne },
|
||||
{ AFMT_U16_LE, PCM_16_BPS, feed_volume_filter_u16le },
|
||||
{ AFMT_U24_LE, PCM_24_BPS, feed_volume_filter_u24le },
|
||||
{ AFMT_U32_LE, PCM_32_BPS, feed_volume_filter_u32le },
|
||||
{ AFMT_U16_BE, PCM_16_BPS, feed_volume_filter_u16be },
|
||||
{ AFMT_U24_BE, PCM_24_BPS, feed_volume_filter_u24be },
|
||||
{ AFMT_U32_BE, PCM_32_BPS, feed_volume_filter_u32be },
|
||||
};
|
||||
|
||||
#define FVOL_DATA(i, c) ((intptr_t)((((i) & 0x1f) << 4) | ((c) & 0xf)))
|
||||
#define FVOL_INFOIDX(m) (((m) >> 4) & 0x1f)
|
||||
#define FVOL_CHANNELS(m) ((m) & 0xf)
|
||||
#define FEEDVOLUME_TAB_SIZE ((int32_t) \
|
||||
(sizeof(feed_volume_info_tab) / \
|
||||
sizeof(feed_volume_info_tab[0])))
|
||||
|
||||
static int
|
||||
feed_volume_init(struct pcm_feeder *f)
|
||||
{
|
||||
int i, channels;
|
||||
struct feed_volume_info *info;
|
||||
struct pcmchan_matrix *m;
|
||||
uint32_t i;
|
||||
int ret;
|
||||
|
||||
if (f->desc->in != f->desc->out)
|
||||
if (f->desc->in != f->desc->out ||
|
||||
AFMT_CHANNEL(f->desc->in) > SND_CHN_MAX)
|
||||
return (EINVAL);
|
||||
|
||||
/* For now, this is mandatory! */
|
||||
if (!(f->desc->out & AFMT_STEREO))
|
||||
return (EINVAL);
|
||||
for (i = 0; i < FEEDVOLUME_TAB_SIZE; i++) {
|
||||
if (AFMT_ENCODING(f->desc->in) ==
|
||||
feed_volume_info_tab[i].format) {
|
||||
info = malloc(sizeof(*info), M_DEVBUF,
|
||||
M_NOWAIT | M_ZERO);
|
||||
if (info == NULL)
|
||||
return (ENOMEM);
|
||||
|
||||
channels = 2;
|
||||
info->bps = AFMT_BPS(f->desc->in);
|
||||
info->channels = AFMT_CHANNEL(f->desc->in);
|
||||
info->apply = feed_volume_info_tab[i].apply;
|
||||
info->volume_class = SND_VOL_C_PCM;
|
||||
info->state = FEEDVOLUME_ENABLE;
|
||||
|
||||
for (i = 0; i < sizeof(feed_volume_tbl) / sizeof(feed_volume_tbl[0]);
|
||||
i++) {
|
||||
if ((f->desc->out & ~AFMT_STEREO) ==
|
||||
feed_volume_tbl[i].format) {
|
||||
f->data = (void *)FVOL_DATA(i, channels);
|
||||
return (0);
|
||||
f->data = info;
|
||||
m = feeder_matrix_default_channel_map(info->channels);
|
||||
if (m == NULL) {
|
||||
free(info, M_DEVBUF);
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
ret = feeder_volume_apply_matrix(f, m);
|
||||
if (ret != 0)
|
||||
free(info, M_DEVBUF);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
}
|
||||
|
||||
return (-1);
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
static int
|
||||
feed_volume(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
|
||||
uint32_t count, void *source)
|
||||
feed_volume_free(struct pcm_feeder *f)
|
||||
{
|
||||
struct feed_volume_info *info;
|
||||
int vol[2];
|
||||
int k, smpsz;
|
||||
|
||||
vol[0] = FVOL_LEFT(c->volume);
|
||||
vol[1] = FVOL_RIGHT(c->volume);
|
||||
info = f->data;
|
||||
if (info != NULL)
|
||||
free(info, M_DEVBUF);
|
||||
|
||||
if (vol[0] == FVOL_MAX && vol[1] == FVOL_MAX)
|
||||
f->data = NULL;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
feed_volume_set(struct pcm_feeder *f, int what, int value)
|
||||
{
|
||||
struct feed_volume_info *info;
|
||||
struct pcmchan_matrix *m;
|
||||
int ret;
|
||||
|
||||
info = f->data;
|
||||
ret = 0;
|
||||
|
||||
switch (what) {
|
||||
case FEEDVOLUME_CLASS:
|
||||
if (value < SND_VOL_C_BEGIN || value > SND_VOL_C_END)
|
||||
return (EINVAL);
|
||||
info->volume_class = value;
|
||||
break;
|
||||
case FEEDVOLUME_CHANNELS:
|
||||
if (value < SND_CHN_MIN || value > SND_CHN_MAX)
|
||||
return (EINVAL);
|
||||
m = feeder_matrix_default_channel_map(value);
|
||||
if (m == NULL)
|
||||
return (EINVAL);
|
||||
ret = feeder_volume_apply_matrix(f, m);
|
||||
break;
|
||||
case FEEDVOLUME_STATE:
|
||||
if (!(value == FEEDVOLUME_ENABLE || value == FEEDVOLUME_BYPASS))
|
||||
return (EINVAL);
|
||||
info->state = value;
|
||||
break;
|
||||
default:
|
||||
return (EINVAL);
|
||||
break;
|
||||
}
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
static int
|
||||
feed_volume_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
|
||||
uint32_t count, void *source)
|
||||
{
|
||||
struct feed_volume_info *info;
|
||||
uint32_t j, align;
|
||||
int i, *vol, *matrix;
|
||||
uint8_t *dst;
|
||||
|
||||
/*
|
||||
* Fetch filter data operation.
|
||||
*/
|
||||
info = f->data;
|
||||
|
||||
if (info->state == FEEDVOLUME_BYPASS)
|
||||
return (FEEDER_FEED(f->source, c, b, count, source));
|
||||
|
||||
info = &feed_volume_tbl[FVOL_INFOIDX((intptr_t)f->data)];
|
||||
smpsz = info->bps * FVOL_CHANNELS((intptr_t)f->data);
|
||||
if (count < smpsz)
|
||||
return (0);
|
||||
vol = c->volume[SND_VOL_C_VAL(info->volume_class)];
|
||||
matrix = info->matrix;
|
||||
|
||||
k = FEEDER_FEED(f->source, c, b, count - (count % smpsz), source);
|
||||
if (k < smpsz)
|
||||
return (0);
|
||||
/*
|
||||
* First, let see if we really need to apply gain at all.
|
||||
*/
|
||||
j = 0;
|
||||
i = info->channels;
|
||||
do {
|
||||
if (vol[matrix[--i]] != SND_VOL_FLAT) {
|
||||
j = 1;
|
||||
break;
|
||||
}
|
||||
} while (i != 0);
|
||||
|
||||
k -= k % smpsz;
|
||||
return (info->filter(b, vol, k));
|
||||
/* Nope, just bypass entirely. */
|
||||
if (j == 0)
|
||||
return (FEEDER_FEED(f->source, c, b, count, source));
|
||||
|
||||
dst = b;
|
||||
align = info->bps * info->channels;
|
||||
|
||||
do {
|
||||
if (count < align)
|
||||
break;
|
||||
|
||||
j = SND_FXDIV(FEEDER_FEED(f->source, c, dst, count, source),
|
||||
align);
|
||||
if (j == 0)
|
||||
break;
|
||||
|
||||
info->apply(vol, matrix, info->channels, dst, j);
|
||||
|
||||
j *= align;
|
||||
dst += j;
|
||||
count -= j;
|
||||
|
||||
} while (count != 0);
|
||||
|
||||
return (dst - b);
|
||||
}
|
||||
|
||||
static struct pcm_feederdesc feeder_volume_desc[] = {
|
||||
{FEEDER_VOLUME, AFMT_S8 | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0},
|
||||
{FEEDER_VOLUME, AFMT_S16_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0},
|
||||
{FEEDER_VOLUME, AFMT_S24_LE | AFMT_STEREO, AFMT_S24_LE | AFMT_STEREO, 0},
|
||||
{FEEDER_VOLUME, AFMT_S32_LE | AFMT_STEREO, AFMT_S32_LE | AFMT_STEREO, 0},
|
||||
{FEEDER_VOLUME, AFMT_S16_BE | AFMT_STEREO, AFMT_S16_BE | AFMT_STEREO, 0},
|
||||
{FEEDER_VOLUME, AFMT_S24_BE | AFMT_STEREO, AFMT_S24_BE | AFMT_STEREO, 0},
|
||||
{FEEDER_VOLUME, AFMT_S32_BE | AFMT_STEREO, AFMT_S32_BE | AFMT_STEREO, 0},
|
||||
{FEEDER_VOLUME, AFMT_U8 | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0},
|
||||
{FEEDER_VOLUME, AFMT_U16_LE | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0},
|
||||
{FEEDER_VOLUME, AFMT_U24_LE | AFMT_STEREO, AFMT_U24_LE | AFMT_STEREO, 0},
|
||||
{FEEDER_VOLUME, AFMT_U32_LE | AFMT_STEREO, AFMT_U32_LE | AFMT_STEREO, 0},
|
||||
{FEEDER_VOLUME, AFMT_U16_BE | AFMT_STEREO, AFMT_U16_BE | AFMT_STEREO, 0},
|
||||
{FEEDER_VOLUME, AFMT_U24_BE | AFMT_STEREO, AFMT_U24_BE | AFMT_STEREO, 0},
|
||||
{FEEDER_VOLUME, AFMT_U32_BE | AFMT_STEREO, AFMT_U32_BE | AFMT_STEREO, 0},
|
||||
{0, 0, 0, 0},
|
||||
{ FEEDER_VOLUME, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
static kobj_method_t feeder_volume_methods[] = {
|
||||
KOBJMETHOD(feeder_init, feed_volume_init),
|
||||
KOBJMETHOD(feeder_feed, feed_volume),
|
||||
{0, 0}
|
||||
KOBJMETHOD(feeder_free, feed_volume_free),
|
||||
KOBJMETHOD(feeder_set, feed_volume_set),
|
||||
KOBJMETHOD(feeder_feed, feed_volume_feed),
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
FEEDER_DECLARE(feeder_volume, 2, NULL);
|
||||
|
||||
FEEDER_DECLARE(feeder_volume, NULL);
|
||||
|
||||
/* Extern */
|
||||
|
||||
/*
|
||||
* feeder_volume_apply_matrix(): For given matrix map, apply its configuration
|
||||
* to feeder_volume matrix structure. There are
|
||||
* possibilites that feeder_volume be inserted
|
||||
* before or after feeder_matrix, which in this
|
||||
* case feeder_volume must be in a good terms
|
||||
* with _current_ matrix.
|
||||
*/
|
||||
int
|
||||
feeder_volume_apply_matrix(struct pcm_feeder *f, struct pcmchan_matrix *m)
|
||||
{
|
||||
struct feed_volume_info *info;
|
||||
uint32_t i;
|
||||
|
||||
if (f == NULL || f->desc == NULL || f->desc->type != FEEDER_VOLUME ||
|
||||
f->data == NULL || m == NULL || m->channels < SND_CHN_MIN ||
|
||||
m->channels > SND_CHN_MAX)
|
||||
return (EINVAL);
|
||||
|
||||
info = f->data;
|
||||
|
||||
for (i = 0; i < (sizeof(info->matrix) / sizeof(info->matrix[0])); i++) {
|
||||
if (i < m->channels)
|
||||
info->matrix[i] = m->map[i].type;
|
||||
else
|
||||
info->matrix[i] = SND_CHN_T_FL;
|
||||
}
|
||||
|
||||
info->channels = m->channels;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
225
sys/dev/sound/pcm/g711.h
Normal file
225
sys/dev/sound/pcm/g711.h
Normal file
@ -0,0 +1,225 @@
|
||||
/*-
|
||||
* Copyright (c) 2008-2009 Ariff Abdullah <ariff@FreeBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _SND_G711_H_
|
||||
#define _SND_G711_H_
|
||||
|
||||
#define G711_TABLE_SIZE 256
|
||||
|
||||
#define ULAW_TO_U8 { \
|
||||
3, 7, 11, 15, 19, 23, 27, 31, \
|
||||
35, 39, 43, 47, 51, 55, 59, 63, \
|
||||
66, 68, 70, 72, 74, 76, 78, 80, \
|
||||
82, 84, 86, 88, 90, 92, 94, 96, \
|
||||
98, 99, 100, 101, 102, 103, 104, 105, \
|
||||
106, 107, 108, 109, 110, 111, 112, 113, \
|
||||
113, 114, 114, 115, 115, 116, 116, 117, \
|
||||
117, 118, 118, 119, 119, 120, 120, 121, \
|
||||
121, 121, 122, 122, 122, 122, 123, 123, \
|
||||
123, 123, 124, 124, 124, 124, 125, 125, \
|
||||
125, 125, 125, 125, 126, 126, 126, 126, \
|
||||
126, 126, 126, 126, 127, 127, 127, 127, \
|
||||
127, 127, 127, 127, 127, 127, 127, 127, \
|
||||
128, 128, 128, 128, 128, 128, 128, 128, \
|
||||
128, 128, 128, 128, 128, 128, 128, 128, \
|
||||
128, 128, 128, 128, 128, 128, 128, 128, \
|
||||
253, 249, 245, 241, 237, 233, 229, 225, \
|
||||
221, 217, 213, 209, 205, 201, 197, 193, \
|
||||
190, 188, 186, 184, 182, 180, 178, 176, \
|
||||
174, 172, 170, 168, 166, 164, 162, 160, \
|
||||
158, 157, 156, 155, 154, 153, 152, 151, \
|
||||
150, 149, 148, 147, 146, 145, 144, 143, \
|
||||
143, 142, 142, 141, 141, 140, 140, 139, \
|
||||
139, 138, 138, 137, 137, 136, 136, 135, \
|
||||
135, 135, 134, 134, 134, 134, 133, 133, \
|
||||
133, 133, 132, 132, 132, 132, 131, 131, \
|
||||
131, 131, 131, 131, 130, 130, 130, 130, \
|
||||
130, 130, 130, 130, 129, 129, 129, 129, \
|
||||
129, 129, 129, 129, 129, 129, 129, 129, \
|
||||
128, 128, 128, 128, 128, 128, 128, 128, \
|
||||
128, 128, 128, 128, 128, 128, 128, 128, \
|
||||
128, 128, 128, 128, 128, 128, 128, 128, \
|
||||
}
|
||||
|
||||
#define ALAW_TO_U8 { \
|
||||
108, 109, 106, 107, 112, 113, 110, 111, \
|
||||
100, 101, 98, 99, 104, 105, 102, 103, \
|
||||
118, 118, 117, 117, 120, 120, 119, 119, \
|
||||
114, 114, 113, 113, 116, 116, 115, 115, \
|
||||
43, 47, 35, 39, 59, 63, 51, 55, \
|
||||
11, 15, 3, 7, 27, 31, 19, 23, \
|
||||
86, 88, 82, 84, 94, 96, 90, 92, \
|
||||
70, 72, 66, 68, 78, 80, 74, 76, \
|
||||
127, 127, 127, 127, 127, 127, 127, 127, \
|
||||
127, 127, 127, 127, 127, 127, 127, 127, \
|
||||
128, 128, 128, 128, 128, 128, 128, 128, \
|
||||
128, 128, 128, 128, 128, 128, 128, 128, \
|
||||
123, 123, 123, 123, 124, 124, 124, 124, \
|
||||
121, 121, 121, 121, 122, 122, 122, 122, \
|
||||
126, 126, 126, 126, 126, 126, 126, 126, \
|
||||
125, 125, 125, 125, 125, 125, 125, 125, \
|
||||
148, 147, 150, 149, 144, 143, 146, 145, \
|
||||
156, 155, 158, 157, 152, 151, 154, 153, \
|
||||
138, 138, 139, 139, 136, 136, 137, 137, \
|
||||
142, 142, 143, 143, 140, 140, 141, 141, \
|
||||
213, 209, 221, 217, 197, 193, 205, 201, \
|
||||
245, 241, 253, 249, 229, 225, 237, 233, \
|
||||
170, 168, 174, 172, 162, 160, 166, 164, \
|
||||
186, 184, 190, 188, 178, 176, 182, 180, \
|
||||
129, 129, 129, 129, 129, 129, 129, 129, \
|
||||
129, 129, 129, 129, 129, 129, 129, 129, \
|
||||
128, 128, 128, 128, 128, 128, 128, 128, \
|
||||
128, 128, 128, 128, 128, 128, 128, 128, \
|
||||
133, 133, 133, 133, 132, 132, 132, 132, \
|
||||
135, 135, 135, 135, 134, 134, 134, 134, \
|
||||
130, 130, 130, 130, 130, 130, 130, 130, \
|
||||
131, 131, 131, 131, 131, 131, 131, 131, \
|
||||
}
|
||||
|
||||
#define U8_TO_ULAW { \
|
||||
0, 0, 0, 0, 0, 1, 1, 1, \
|
||||
1, 2, 2, 2, 2, 3, 3, 3, \
|
||||
3, 4, 4, 4, 4, 5, 5, 5, \
|
||||
5, 6, 6, 6, 6, 7, 7, 7, \
|
||||
7, 8, 8, 8, 8, 9, 9, 9, \
|
||||
9, 10, 10, 10, 10, 11, 11, 11, \
|
||||
11, 12, 12, 12, 12, 13, 13, 13, \
|
||||
13, 14, 14, 14, 14, 15, 15, 15, \
|
||||
15, 16, 16, 17, 17, 18, 18, 19, \
|
||||
19, 20, 20, 21, 21, 22, 22, 23, \
|
||||
23, 24, 24, 25, 25, 26, 26, 27, \
|
||||
27, 28, 28, 29, 29, 30, 30, 31, \
|
||||
31, 32, 33, 34, 35, 36, 37, 38, \
|
||||
39, 40, 41, 42, 43, 44, 45, 46, \
|
||||
47, 49, 51, 53, 55, 57, 59, 61, \
|
||||
63, 66, 70, 74, 78, 84, 92, 104, \
|
||||
254, 231, 219, 211, 205, 201, 197, 193, \
|
||||
190, 188, 186, 184, 182, 180, 178, 176, \
|
||||
175, 174, 173, 172, 171, 170, 169, 168, \
|
||||
167, 166, 165, 164, 163, 162, 161, 160, \
|
||||
159, 159, 158, 158, 157, 157, 156, 156, \
|
||||
155, 155, 154, 154, 153, 153, 152, 152, \
|
||||
151, 151, 150, 150, 149, 149, 148, 148, \
|
||||
147, 147, 146, 146, 145, 145, 144, 144, \
|
||||
143, 143, 143, 143, 142, 142, 142, 142, \
|
||||
141, 141, 141, 141, 140, 140, 140, 140, \
|
||||
139, 139, 139, 139, 138, 138, 138, 138, \
|
||||
137, 137, 137, 137, 136, 136, 136, 136, \
|
||||
135, 135, 135, 135, 134, 134, 134, 134, \
|
||||
133, 133, 133, 133, 132, 132, 132, 132, \
|
||||
131, 131, 131, 131, 130, 130, 130, 130, \
|
||||
129, 129, 129, 129, 128, 128, 128, 128, \
|
||||
}
|
||||
|
||||
#define U8_TO_ALAW { \
|
||||
42, 42, 42, 42, 42, 43, 43, 43, \
|
||||
43, 40, 40, 40, 40, 41, 41, 41, \
|
||||
41, 46, 46, 46, 46, 47, 47, 47, \
|
||||
47, 44, 44, 44, 44, 45, 45, 45, \
|
||||
45, 34, 34, 34, 34, 35, 35, 35, \
|
||||
35, 32, 32, 32, 32, 33, 33, 33, \
|
||||
33, 38, 38, 38, 38, 39, 39, 39, \
|
||||
39, 36, 36, 36, 36, 37, 37, 37, \
|
||||
37, 58, 58, 59, 59, 56, 56, 57, \
|
||||
57, 62, 62, 63, 63, 60, 60, 61, \
|
||||
61, 50, 50, 51, 51, 48, 48, 49, \
|
||||
49, 54, 54, 55, 55, 52, 52, 53, \
|
||||
53, 10, 11, 8, 9, 14, 15, 12, \
|
||||
13, 2, 3, 0, 1, 6, 7, 4, \
|
||||
5, 24, 30, 28, 18, 16, 22, 20, \
|
||||
106, 110, 98, 102, 122, 114, 75, 90, \
|
||||
213, 197, 245, 253, 229, 225, 237, 233, \
|
||||
149, 151, 145, 147, 157, 159, 153, 155, \
|
||||
133, 132, 135, 134, 129, 128, 131, 130, \
|
||||
141, 140, 143, 142, 137, 136, 139, 138, \
|
||||
181, 181, 180, 180, 183, 183, 182, 182, \
|
||||
177, 177, 176, 176, 179, 179, 178, 178, \
|
||||
189, 189, 188, 188, 191, 191, 190, 190, \
|
||||
185, 185, 184, 184, 187, 187, 186, 186, \
|
||||
165, 165, 165, 165, 164, 164, 164, 164, \
|
||||
167, 167, 167, 167, 166, 166, 166, 166, \
|
||||
161, 161, 161, 161, 160, 160, 160, 160, \
|
||||
163, 163, 163, 163, 162, 162, 162, 162, \
|
||||
173, 173, 173, 173, 172, 172, 172, 172, \
|
||||
175, 175, 175, 175, 174, 174, 174, 174, \
|
||||
169, 169, 169, 169, 168, 168, 168, 168, \
|
||||
171, 171, 171, 171, 170, 170, 170, 170, \
|
||||
}
|
||||
|
||||
|
||||
#define _G711_TO_INTPCM(t, v) ((intpcm_t) \
|
||||
((int8_t)((t)[(uint8_t)(v)] ^ 0x80)))
|
||||
|
||||
#define _INTPCM_TO_G711(t, v) ((t)[(uint8_t)((v) ^ 0x80)])
|
||||
|
||||
|
||||
#define G711_DECLARE_TABLE(t) \
|
||||
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]; \
|
||||
} t = { \
|
||||
ULAW_TO_U8, ALAW_TO_U8, \
|
||||
U8_TO_ULAW, U8_TO_ALAW \
|
||||
}
|
||||
|
||||
#define G711_DECLARE_OP(t) \
|
||||
static __inline intpcm_t \
|
||||
pcm_read_ulaw(uint8_t v) \
|
||||
{ \
|
||||
\
|
||||
return (_G711_TO_INTPCM((t).ulaw_to_u8, v)); \
|
||||
} \
|
||||
\
|
||||
static __inline intpcm_t \
|
||||
pcm_read_alaw(uint8_t v) \
|
||||
{ \
|
||||
\
|
||||
return (_G711_TO_INTPCM((t).alaw_to_u8, v)); \
|
||||
} \
|
||||
\
|
||||
static __inline void \
|
||||
pcm_write_ulaw(uint8_t *dst, intpcm_t v) \
|
||||
{ \
|
||||
\
|
||||
*dst = _INTPCM_TO_G711((t).u8_to_ulaw, v); \
|
||||
} \
|
||||
\
|
||||
static __inline void \
|
||||
pcm_write_alaw(uint8_t *dst, intpcm_t v) \
|
||||
{ \
|
||||
\
|
||||
*dst = _INTPCM_TO_G711((t).u8_to_alaw, v); \
|
||||
}
|
||||
|
||||
#define G711_DECLARE(t) \
|
||||
G711_DECLARE_TABLE(t); \
|
||||
G711_DECLARE_OP(t)
|
||||
|
||||
#endif /* !_SND_G711_H_ */
|
136
sys/dev/sound/pcm/intpcm.h
Normal file
136
sys/dev/sound/pcm/intpcm.h
Normal file
@ -0,0 +1,136 @@
|
||||
/*-
|
||||
* Copyright (c) 2008-2009 Ariff Abdullah <ariff@FreeBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _SND_INTPCM_H_
|
||||
#define _SND_INTPCM_H_
|
||||
|
||||
typedef intpcm_t intpcm_read_t(uint8_t *);
|
||||
typedef void intpcm_write_t(uint8_t *, intpcm_t);
|
||||
|
||||
extern intpcm_read_t *feeder_format_read_op(uint32_t);
|
||||
extern intpcm_write_t *feeder_format_write_op(uint32_t);
|
||||
|
||||
#define INTPCM_DECLARE_OP_WRITE(SIGN, BIT, ENDIAN, SHIFT) \
|
||||
static __inline void \
|
||||
intpcm_write_##SIGN##BIT##ENDIAN(uint8_t *dst, intpcm_t v) \
|
||||
{ \
|
||||
\
|
||||
_PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, v >> SHIFT); \
|
||||
}
|
||||
|
||||
#define INTPCM_DECLARE_OP_8(SIGN, ENDIAN) \
|
||||
static __inline intpcm_t \
|
||||
intpcm_read_##SIGN##8##ENDIAN(uint8_t *src) \
|
||||
{ \
|
||||
\
|
||||
return (_PCM_READ_##SIGN##8##_##ENDIAN(src) << 24); \
|
||||
} \
|
||||
INTPCM_DECLARE_OP_WRITE(SIGN, 8, ENDIAN, 24)
|
||||
|
||||
#define INTPCM_DECLARE_OP_16(SIGN, ENDIAN) \
|
||||
static __inline intpcm_t \
|
||||
intpcm_read_##SIGN##16##ENDIAN(uint8_t *src) \
|
||||
{ \
|
||||
\
|
||||
return (_PCM_READ_##SIGN##16##_##ENDIAN(src) << 16); \
|
||||
} \
|
||||
INTPCM_DECLARE_OP_WRITE(SIGN, 16, ENDIAN, 16)
|
||||
|
||||
#define INTPCM_DECLARE_OP_24(SIGN, ENDIAN) \
|
||||
static __inline intpcm_t \
|
||||
intpcm_read_##SIGN##24##ENDIAN(uint8_t *src) \
|
||||
{ \
|
||||
\
|
||||
return (_PCM_READ_##SIGN##24##_##ENDIAN(src) << 8); \
|
||||
} \
|
||||
INTPCM_DECLARE_OP_WRITE(SIGN, 24, ENDIAN, 8)
|
||||
|
||||
#define INTPCM_DECLARE_OP_32(SIGN, ENDIAN) \
|
||||
static __inline intpcm_t \
|
||||
intpcm_read_##SIGN##32##ENDIAN(uint8_t *src) \
|
||||
{ \
|
||||
\
|
||||
return (_PCM_READ_##SIGN##32##_##ENDIAN(src)); \
|
||||
} \
|
||||
\
|
||||
static __inline void \
|
||||
intpcm_write_##SIGN##32##ENDIAN(uint8_t *dst, intpcm_t v) \
|
||||
{ \
|
||||
\
|
||||
_PCM_WRITE_##SIGN##32##_##ENDIAN(dst, v); \
|
||||
}
|
||||
|
||||
|
||||
#define INTPCM_DECLARE(t) \
|
||||
\
|
||||
G711_DECLARE_TABLE(t); \
|
||||
\
|
||||
static __inline intpcm_t \
|
||||
intpcm_read_ulaw(uint8_t *src) \
|
||||
{ \
|
||||
\
|
||||
return (_G711_TO_INTPCM((t).ulaw_to_u8, *src) << 24); \
|
||||
} \
|
||||
\
|
||||
static __inline intpcm_t \
|
||||
intpcm_read_alaw(uint8_t *src) \
|
||||
{ \
|
||||
\
|
||||
return (_G711_TO_INTPCM((t).alaw_to_u8, *src) << 24); \
|
||||
} \
|
||||
\
|
||||
static __inline void \
|
||||
intpcm_write_ulaw(uint8_t *dst, intpcm_t v) \
|
||||
{ \
|
||||
\
|
||||
*dst = _INTPCM_TO_G711((t).u8_to_ulaw, v >> 24); \
|
||||
} \
|
||||
\
|
||||
static __inline void \
|
||||
intpcm_write_alaw(uint8_t *dst, intpcm_t v) \
|
||||
{ \
|
||||
\
|
||||
*dst = _INTPCM_TO_G711((t).u8_to_alaw, v >> 24); \
|
||||
} \
|
||||
\
|
||||
INTPCM_DECLARE_OP_8(S, NE) \
|
||||
INTPCM_DECLARE_OP_16(S, LE) \
|
||||
INTPCM_DECLARE_OP_16(S, BE) \
|
||||
INTPCM_DECLARE_OP_24(S, LE) \
|
||||
INTPCM_DECLARE_OP_24(S, BE) \
|
||||
INTPCM_DECLARE_OP_32(S, LE) \
|
||||
INTPCM_DECLARE_OP_32(S, BE) \
|
||||
INTPCM_DECLARE_OP_8(U, NE) \
|
||||
INTPCM_DECLARE_OP_16(U, LE) \
|
||||
INTPCM_DECLARE_OP_16(U, BE) \
|
||||
INTPCM_DECLARE_OP_24(U, LE) \
|
||||
INTPCM_DECLARE_OP_24(U, BE) \
|
||||
INTPCM_DECLARE_OP_32(U, LE) \
|
||||
INTPCM_DECLARE_OP_32(U, BE)
|
||||
|
||||
#endif /* !_SND_INTPCM_H_ */
|
218
sys/dev/sound/pcm/matrix.h
Normal file
218
sys/dev/sound/pcm/matrix.h
Normal file
@ -0,0 +1,218 @@
|
||||
/*-
|
||||
* Copyright (c) 2007-2009 Ariff Abdullah <ariff@FreeBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _SND_MATRIX_H_
|
||||
#define _SND_MATRIX_H_
|
||||
|
||||
#undef SND_MULTICHANNEL
|
||||
#ifndef SND_OLDSTEREO
|
||||
#define SND_MULTICHANNEL 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
* XXX = unused, but part of the definition (will be used someday, maybe).
|
||||
*/
|
||||
#define SND_CHN_T_FL 0 /* Front Left */
|
||||
#define SND_CHN_T_FR 1 /* Front Right */
|
||||
#define SND_CHN_T_FC 2 /* Front Center */
|
||||
#define SND_CHN_T_LF 3 /* Low Frequency */
|
||||
#define SND_CHN_T_BL 4 /* Back Left */
|
||||
#define SND_CHN_T_BR 5 /* Back Right */
|
||||
#define SND_CHN_T_FLC 6 /* Front Left Center XXX */
|
||||
#define SND_CHN_T_FRC 7 /* Front Right Center XXX */
|
||||
#define SND_CHN_T_BC 8 /* Back Center */
|
||||
#define SND_CHN_T_SL 9 /* Side Left */
|
||||
#define SND_CHN_T_SR 10 /* Side Right */
|
||||
#define SND_CHN_T_TC 11 /* Top Center XXX */
|
||||
#define SND_CHN_T_TFL 12 /* Top Front Left XXX */
|
||||
#define SND_CHN_T_TFC 13 /* Top Front Center XXX */
|
||||
#define SND_CHN_T_TFR 14 /* Top Front Right XXX */
|
||||
#define SND_CHN_T_TBL 15 /* Top Back Left XXX */
|
||||
#define SND_CHN_T_TBC 16 /* Top Back Center XXX */
|
||||
#define SND_CHN_T_TBR 17 /* Top Back Right XXX */
|
||||
#define SND_CHN_T_MAX 18 /* Maximum channels */
|
||||
|
||||
#define SND_CHN_T_ZERO (SND_CHN_T_MAX + 1) /* Zero samples */
|
||||
|
||||
#define SND_CHN_T_LABELS { \
|
||||
"fl", "fr", "fc", "lf", "bl", "br", \
|
||||
"flc", "frc", "bc", "sl", "sr", "tc", \
|
||||
"tfl", "tfc", "tfr", "tbl", "tbc", "tbr" \
|
||||
}
|
||||
|
||||
#define SND_CHN_T_NAMES { \
|
||||
"Front Left", "Front Right", "Front Center", \
|
||||
"Low Frequency Effects", \
|
||||
"Back Left", "Back Right", \
|
||||
"Front Left Center", "Front Right Center", \
|
||||
"Back Center", \
|
||||
"Side Left", "Side Right", \
|
||||
"Top Center", \
|
||||
"Top Front Left", "Top Front Center", "Top Front Right", \
|
||||
"Top Back Left", "Top Back Center", "Top Back Right" \
|
||||
}
|
||||
|
||||
#define SND_CHN_T_MASK_FL (1 << SND_CHN_T_FL)
|
||||
#define SND_CHN_T_MASK_FR (1 << SND_CHN_T_FR)
|
||||
#define SND_CHN_T_MASK_FC (1 << SND_CHN_T_FC)
|
||||
#define SND_CHN_T_MASK_LF (1 << SND_CHN_T_LF)
|
||||
#define SND_CHN_T_MASK_BL (1 << SND_CHN_T_BL)
|
||||
#define SND_CHN_T_MASK_BR (1 << SND_CHN_T_BR)
|
||||
#define SND_CHN_T_MASK_FLC (1 << SND_CHN_T_FLC)
|
||||
#define SND_CHN_T_MASK_FRC (1 << SND_CHN_T_FRC)
|
||||
#define SND_CHN_T_MASK_BC (1 << SND_CHN_T_BC)
|
||||
#define SND_CHN_T_MASK_SL (1 << SND_CHN_T_SL)
|
||||
#define SND_CHN_T_MASK_SR (1 << SND_CHN_T_SR)
|
||||
#define SND_CHN_T_MASK_TC (1 << SND_CHN_T_TC)
|
||||
#define SND_CHN_T_MASK_TFL (1 << SND_CHN_T_TFL)
|
||||
#define SND_CHN_T_MASK_TFC (1 << SND_CHN_T_TFC)
|
||||
#define SND_CHN_T_MASK_TFR (1 << SND_CHN_T_TFR)
|
||||
#define SND_CHN_T_MASK_TBL (1 << SND_CHN_T_TBL)
|
||||
#define SND_CHN_T_MASK_TBC (1 << SND_CHN_T_TBC)
|
||||
#define SND_CHN_T_MASK_TBR (1 << SND_CHN_T_TBR)
|
||||
|
||||
#define SND_CHN_LEFT_MASK (SND_CHN_T_MASK_FL | \
|
||||
SND_CHN_T_MASK_BL | \
|
||||
SND_CHN_T_MASK_FLC | \
|
||||
SND_CHN_T_MASK_SL | \
|
||||
SND_CHN_T_MASK_TFL | \
|
||||
SND_CHN_T_MASK_TBL)
|
||||
|
||||
#define SND_CHN_RIGHT_MASK (SND_CHN_T_MASK_FR | \
|
||||
SND_CHN_T_MASK_BR | \
|
||||
SND_CHN_T_MASK_FRC | \
|
||||
SND_CHN_T_MASK_SR | \
|
||||
SND_CHN_T_MASK_TFR | \
|
||||
SND_CHN_T_MASK_TBR)
|
||||
|
||||
#define SND_CHN_CENTER_MASK (SND_CHN_T_MASK_FC | \
|
||||
SND_CHN_T_MASK_BC | \
|
||||
SND_CHN_T_MASK_TC | \
|
||||
SND_CHN_T_MASK_TFC | \
|
||||
SND_CHN_T_MASK_TBC | \
|
||||
SND_CHN_T_MASK_LF) /* XXX what?!? */
|
||||
|
||||
/*
|
||||
* Matrix identity.
|
||||
*/
|
||||
|
||||
/* 1 @ Mono 1.0 */
|
||||
#define SND_CHN_MATRIX_1_0 0
|
||||
#define SND_CHN_MATRIX_1 SND_CHN_MATRIX_1_0
|
||||
|
||||
/* 2 @ Stereo 2.0 */
|
||||
#define SND_CHN_MATRIX_2_0 1
|
||||
#define SND_CHN_MATRIX_2 SND_CHN_MATRIX_2_0
|
||||
|
||||
/* 3 @ 2.1 (lfe), 3.0 (rear center, DEFAULT) */
|
||||
#define SND_CHN_MATRIX_2_1 2
|
||||
#define SND_CHN_MATRIX_3_0 3
|
||||
#define SND_CHN_MATRIX_3 SND_CHN_MATRIX_3_0
|
||||
|
||||
/* 4 @ 4.0 Quadraphonic */
|
||||
#define SND_CHN_MATRIX_4_0 4
|
||||
#define SND_CHN_MATRIX_4 SND_CHN_MATRIX_4_0
|
||||
|
||||
/* 5 @ 4.1 (lfe), 5.0 (center, DEFAULT) */
|
||||
#define SND_CHN_MATRIX_4_1 5
|
||||
#define SND_CHN_MATRIX_5_0 6
|
||||
#define SND_CHN_MATRIX_5 SND_CHN_MATRIX_5_0
|
||||
|
||||
/* 6 @ 5.1 (lfe, DEFAULT), 6.0 (rear center) */
|
||||
#define SND_CHN_MATRIX_5_1 7
|
||||
#define SND_CHN_MATRIX_6_0 8
|
||||
#define SND_CHN_MATRIX_6 SND_CHN_MATRIX_5_1
|
||||
|
||||
/* 7 @ 6.1 (lfe) */
|
||||
#define SND_CHN_MATRIX_6_1 9
|
||||
#define SND_CHN_MATRIX_7 SND_CHN_MATRIX_6_1
|
||||
|
||||
/* 8 @ 7.1 (lfe) */
|
||||
#define SND_CHN_MATRIX_7_1 10
|
||||
#define SND_CHN_MATRIX_8 SND_CHN_MATRIX_7_1
|
||||
|
||||
#define SND_CHN_MATRIX_MAX 11
|
||||
|
||||
#define SND_CHN_MATRIX_BEGIN SND_CHN_MATRIX_1_0
|
||||
#define SND_CHN_MATRIX_END SND_CHN_MATRIX_7_1
|
||||
|
||||
/* Custom matrix identity */
|
||||
#define SND_CHN_MATRIX_DRV -4 /* driver own identity */
|
||||
#define SND_CHN_MATRIX_PCMCHANNEL -3 /* PCM channel identity */
|
||||
#define SND_CHN_MATRIX_MISC -2 /* misc, custom defined */
|
||||
#define SND_CHN_MATRIX_UNKNOWN -1 /* unknown */
|
||||
|
||||
#define SND_CHN_T_VOL_0DB SND_CHN_T_MAX
|
||||
#define SND_CHN_T_VOL_MAX (SND_CHN_T_VOL_0DB + 1)
|
||||
|
||||
#define SND_CHN_T_BEGIN SND_CHN_T_FL
|
||||
#define SND_CHN_T_END SND_CHN_T_TBR
|
||||
#define SND_CHN_T_STEP 1
|
||||
#define SND_CHN_MIN 1
|
||||
|
||||
#ifdef SND_MULTICHANNEL
|
||||
#define SND_CHN_MAX 8
|
||||
#else
|
||||
#define SND_CHN_MAX 2
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Multichannel interleaved volume matrix. Each calculated value relative
|
||||
* to master and 0db will be stored in each CLASS + 1 as long as
|
||||
* chn_setvolume_matrix() or the equivalent CHN_SETVOLUME() macros is
|
||||
* used (see channel.c).
|
||||
*/
|
||||
#define SND_VOL_C_MASTER 0
|
||||
#define SND_VOL_C_PCM 1
|
||||
#define SND_VOL_C_PCM_VAL 2
|
||||
#define SND_VOL_C_MAX 3
|
||||
|
||||
#define SND_VOL_C_BEGIN SND_VOL_C_PCM
|
||||
#define SND_VOL_C_END SND_VOL_C_PCM
|
||||
#define SND_VOL_C_STEP 2
|
||||
|
||||
#define SND_VOL_C_VAL(x) ((x) + 1)
|
||||
|
||||
#define SND_VOL_0DB_MIN 1
|
||||
#define SND_VOL_0DB_MAX 100
|
||||
|
||||
#define SND_VOL_0DB_MASTER 100
|
||||
#define SND_VOL_0DB_PCM 45
|
||||
|
||||
#define SND_VOL_RESOLUTION 8
|
||||
#define SND_VOL_FLAT (1 << SND_VOL_RESOLUTION)
|
||||
|
||||
#define SND_VOL_CALC_SAMPLE(x, y) (((x) * (y)) >> SND_VOL_RESOLUTION)
|
||||
|
||||
#define SND_VOL_CALC_VAL(x, y, z) \
|
||||
(((((x)[y][z] << SND_VOL_RESOLUTION) / \
|
||||
(x)[y][SND_CHN_T_VOL_0DB]) * \
|
||||
(x)[SND_VOL_C_MASTER][z]) / \
|
||||
(x)[SND_VOL_C_MASTER][SND_CHN_T_VOL_0DB]) \
|
||||
|
||||
#endif /* !_SND_MATRIX_H_ */
|
567
sys/dev/sound/pcm/matrix_map.h
Normal file
567
sys/dev/sound/pcm/matrix_map.h
Normal file
@ -0,0 +1,567 @@
|
||||
/*-
|
||||
* Copyright (c) 2009 Ariff Abdullah <ariff@FreeBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _SND_MATRIX_MAP_H_
|
||||
#define _SND_MATRIX_MAP_H_
|
||||
|
||||
/*
|
||||
* Standard matrix maps:
|
||||
*
|
||||
* struct pcmchan_matrix {
|
||||
* .id = Matrix identity (see matrix.h). Custom defined should use
|
||||
* one of SND_CHN_MATRIX_MISC (for whatever purposes) or
|
||||
* SND_CHN_MATRIX_DRV (hardware driver).
|
||||
* .channels = Total number of channels, including whatever 'extended'
|
||||
* (the X.ext notions, mostly LFE).
|
||||
* .ext = Total number of extended channels (LFE).
|
||||
* .map = {
|
||||
* Sequences of channel type and interleave structure.
|
||||
* [interleave offset] = {
|
||||
* .type = channel type (see matrix.h).
|
||||
* .members = Masks of channels that is acceptable as a
|
||||
* member of this channel type.
|
||||
* },
|
||||
* [total channels] = {
|
||||
* .type = Maximum channels marker (SND_CHN_T_MAX).
|
||||
* .members = 0 (no channels allowed here).
|
||||
* },
|
||||
* },
|
||||
* .mask = Mask of channels that exist in this map.
|
||||
* .offset = {
|
||||
* channel offset that directly translate to the above interleave
|
||||
* offset according to SND_CHN_T_* definitions.
|
||||
* }
|
||||
* };
|
||||
*
|
||||
* Rule of thumb: Avoid using SND_CHN_T_* that is marked with XXX (matrix.h),
|
||||
* or be prepared for the horror to come.
|
||||
*
|
||||
*/
|
||||
|
||||
#define SND_CHN_MATRIX_MAP_1_0 { \
|
||||
.id = SND_CHN_MATRIX_1_0, \
|
||||
.channels = 1, \
|
||||
.ext = 0, \
|
||||
.map = { \
|
||||
/* Mono, center, etc. */ \
|
||||
[0] = { \
|
||||
.type = SND_CHN_T_FL, \
|
||||
.members = \
|
||||
SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \
|
||||
SND_CHN_T_MASK_FC | SND_CHN_T_MASK_LF | \
|
||||
SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR | \
|
||||
SND_CHN_T_MASK_BC | SND_CHN_T_MASK_SL | \
|
||||
SND_CHN_T_MASK_SR \
|
||||
}, \
|
||||
[1] = { \
|
||||
.type = SND_CHN_T_MAX, \
|
||||
.members = 0 \
|
||||
} \
|
||||
}, \
|
||||
.mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \
|
||||
SND_CHN_T_MASK_FC, \
|
||||
.offset = { 0, 0, 0, 0, 0, 0, -1, -1, 0, \
|
||||
0, 0, -1, -1, -1, -1, -1, -1, -1 } \
|
||||
}
|
||||
|
||||
#define SND_CHN_MATRIX_MAP_2_0 { \
|
||||
.id = SND_CHN_MATRIX_2_0, \
|
||||
.channels = 2, \
|
||||
.ext = 0, \
|
||||
.map = { \
|
||||
/* Left */ \
|
||||
[0] = { \
|
||||
.type = SND_CHN_T_FL, \
|
||||
.members = \
|
||||
SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FC | \
|
||||
SND_CHN_T_MASK_LF | SND_CHN_T_MASK_BL | \
|
||||
SND_CHN_T_MASK_BC | SND_CHN_T_MASK_SL \
|
||||
}, \
|
||||
/* Right */ \
|
||||
[1] = { \
|
||||
.type = SND_CHN_T_FR, \
|
||||
.members = \
|
||||
SND_CHN_T_MASK_FR | SND_CHN_T_MASK_FC | \
|
||||
SND_CHN_T_MASK_LF | SND_CHN_T_MASK_BR | \
|
||||
SND_CHN_T_MASK_BC | SND_CHN_T_MASK_SR \
|
||||
}, \
|
||||
[2] = { \
|
||||
.type = SND_CHN_T_MAX, \
|
||||
.members = 0 \
|
||||
} \
|
||||
}, \
|
||||
.mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR, \
|
||||
.offset = { 0, 1, -1, -1, -1, -1, -1, -1, -1, \
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1 } \
|
||||
}
|
||||
|
||||
#define SND_CHN_MATRIX_MAP_2_1 { \
|
||||
.id = SND_CHN_MATRIX_2_1, \
|
||||
.channels = 3, \
|
||||
.ext = 1, \
|
||||
.map = { \
|
||||
/* Left */ \
|
||||
[0] = { \
|
||||
.type = SND_CHN_T_FL, \
|
||||
.members = \
|
||||
SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FC | \
|
||||
SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BC | \
|
||||
SND_CHN_T_MASK_SL \
|
||||
}, \
|
||||
/* Right */ \
|
||||
[1] = { \
|
||||
.type = SND_CHN_T_FR, \
|
||||
.members = \
|
||||
SND_CHN_T_MASK_FR | SND_CHN_T_MASK_FC | \
|
||||
SND_CHN_T_MASK_BR | SND_CHN_T_MASK_BC | \
|
||||
SND_CHN_T_MASK_SR \
|
||||
}, \
|
||||
/* LFE */ \
|
||||
[2] = { \
|
||||
.type = SND_CHN_T_LF, \
|
||||
.members = SND_CHN_T_MASK_LF \
|
||||
}, \
|
||||
[3] = { \
|
||||
.type = SND_CHN_T_MAX, \
|
||||
.members = 0 \
|
||||
} \
|
||||
}, \
|
||||
.mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \
|
||||
SND_CHN_T_MASK_LF, \
|
||||
.offset = { 0, 1, -1, 2, -1, -1, -1, -1, -1, \
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1 } \
|
||||
}
|
||||
|
||||
#define SND_CHN_MATRIX_MAP_3_0 { /* 3 channels default */ \
|
||||
.id = SND_CHN_MATRIX_3_0, \
|
||||
.channels = 3, \
|
||||
.ext = 0, \
|
||||
.map = { \
|
||||
/* Left */ \
|
||||
[0] = { \
|
||||
.type = SND_CHN_T_FL, \
|
||||
.members = \
|
||||
SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FC | \
|
||||
SND_CHN_T_MASK_LF | SND_CHN_T_MASK_SL \
|
||||
}, \
|
||||
/* Right */ \
|
||||
[1] = { \
|
||||
.type = SND_CHN_T_FR, \
|
||||
.members = \
|
||||
SND_CHN_T_MASK_FR | SND_CHN_T_MASK_FC | \
|
||||
SND_CHN_T_MASK_LF | SND_CHN_T_MASK_SR \
|
||||
}, \
|
||||
/* Rear Center */ \
|
||||
[2] = { \
|
||||
.type = SND_CHN_T_BC, \
|
||||
.members = \
|
||||
SND_CHN_T_MASK_LF | SND_CHN_T_MASK_BL | \
|
||||
SND_CHN_T_MASK_BR | SND_CHN_T_MASK_BC | \
|
||||
SND_CHN_T_MASK_SL | SND_CHN_T_MASK_SR \
|
||||
}, \
|
||||
[3] = { \
|
||||
.type = SND_CHN_T_MAX, \
|
||||
.members = 0 \
|
||||
} \
|
||||
}, \
|
||||
.mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \
|
||||
SND_CHN_T_MASK_BC, \
|
||||
.offset = { 0, 1, -1, -1, -1, -1, -1, -1, 2, \
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1 } \
|
||||
}
|
||||
|
||||
#define SND_CHN_MATRIX_MAP_4_0 { \
|
||||
.id = SND_CHN_MATRIX_4_0, \
|
||||
.channels = 4, \
|
||||
.ext = 0, \
|
||||
.map = { \
|
||||
/* Left */ \
|
||||
[0] = { \
|
||||
.type = SND_CHN_T_FL, \
|
||||
.members = \
|
||||
SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FC | \
|
||||
SND_CHN_T_MASK_LF | SND_CHN_T_MASK_SL \
|
||||
}, \
|
||||
/* Right */ \
|
||||
[1] = { \
|
||||
.type = SND_CHN_T_FR, \
|
||||
.members = \
|
||||
SND_CHN_T_MASK_FR | SND_CHN_T_MASK_FC | \
|
||||
SND_CHN_T_MASK_LF | SND_CHN_T_MASK_SR \
|
||||
}, \
|
||||
/* Rear Left */ \
|
||||
[2] = { \
|
||||
.type = SND_CHN_T_BL, \
|
||||
.members = \
|
||||
SND_CHN_T_MASK_LF | SND_CHN_T_MASK_BL | \
|
||||
SND_CHN_T_MASK_BC | SND_CHN_T_MASK_SL \
|
||||
}, \
|
||||
/* Rear Right */ \
|
||||
[3] = { \
|
||||
.type = SND_CHN_T_BR, \
|
||||
.members = \
|
||||
SND_CHN_T_MASK_LF | SND_CHN_T_MASK_BR | \
|
||||
SND_CHN_T_MASK_BC | SND_CHN_T_MASK_SR \
|
||||
}, \
|
||||
[4] = { \
|
||||
.type = SND_CHN_T_MAX, \
|
||||
.members = 0 \
|
||||
} \
|
||||
}, \
|
||||
.mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \
|
||||
SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR, \
|
||||
.offset = { 0, 1, -1, -1, 2, 3, -1, -1, -1, \
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1 } \
|
||||
}
|
||||
|
||||
#define SND_CHN_MATRIX_MAP_4_1 { \
|
||||
.id = SND_CHN_MATRIX_4_1, \
|
||||
.channels = 5, \
|
||||
.ext = 1, \
|
||||
.map = { \
|
||||
/* Left */ \
|
||||
[0] = { \
|
||||
.type = SND_CHN_T_FL, \
|
||||
.members = \
|
||||
SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FC | \
|
||||
SND_CHN_T_MASK_SL \
|
||||
}, \
|
||||
/* Right */ \
|
||||
[1] = { \
|
||||
.type = SND_CHN_T_FR, \
|
||||
.members = \
|
||||
SND_CHN_T_MASK_FR | SND_CHN_T_MASK_FC | \
|
||||
SND_CHN_T_MASK_SR \
|
||||
}, \
|
||||
/* Rear Left */ \
|
||||
[2] = { \
|
||||
.type = SND_CHN_T_BL, \
|
||||
.members = \
|
||||
SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BC | \
|
||||
SND_CHN_T_MASK_SL \
|
||||
}, \
|
||||
/* Rear Right */ \
|
||||
[3] = { \
|
||||
.type = SND_CHN_T_BR, \
|
||||
.members = \
|
||||
SND_CHN_T_MASK_BR | SND_CHN_T_MASK_BC | \
|
||||
SND_CHN_T_MASK_SR \
|
||||
}, \
|
||||
/* LFE */ \
|
||||
[4] = { \
|
||||
.type = SND_CHN_T_LF, \
|
||||
.members = SND_CHN_T_MASK_LF \
|
||||
}, \
|
||||
[5] = { \
|
||||
.type = SND_CHN_T_MAX, \
|
||||
.members = 0 \
|
||||
} \
|
||||
}, \
|
||||
.mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \
|
||||
SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR | \
|
||||
SND_CHN_T_MASK_LF, \
|
||||
.offset = { 0, 1, -1, 4, 2, 3, -1, -1, -1, \
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1 } \
|
||||
}
|
||||
|
||||
#define SND_CHN_MATRIX_MAP_5_0 { /* 5 channels default */ \
|
||||
.id = SND_CHN_MATRIX_5_0, \
|
||||
.channels = 5, \
|
||||
.ext = 0, \
|
||||
.map = { \
|
||||
/* Left */ \
|
||||
[0] = { \
|
||||
.type = SND_CHN_T_FL, \
|
||||
.members = \
|
||||
SND_CHN_T_MASK_FL | SND_CHN_T_MASK_LF | \
|
||||
SND_CHN_T_MASK_SL \
|
||||
}, \
|
||||
/* Right */ \
|
||||
[1] = { \
|
||||
.type = SND_CHN_T_FR, \
|
||||
.members = \
|
||||
SND_CHN_T_MASK_FR | SND_CHN_T_MASK_LF | \
|
||||
SND_CHN_T_MASK_SR \
|
||||
}, \
|
||||
/* Rear Left */ \
|
||||
[2] = { \
|
||||
.type = SND_CHN_T_BL, \
|
||||
.members = \
|
||||
SND_CHN_T_MASK_LF | SND_CHN_T_MASK_BL | \
|
||||
SND_CHN_T_MASK_BC | SND_CHN_T_MASK_SL \
|
||||
}, \
|
||||
/* Rear Right */ \
|
||||
[3] = { \
|
||||
.type = SND_CHN_T_BR, \
|
||||
.members = \
|
||||
SND_CHN_T_MASK_LF | SND_CHN_T_MASK_BR | \
|
||||
SND_CHN_T_MASK_BC | SND_CHN_T_MASK_SR \
|
||||
}, \
|
||||
/* Center */ \
|
||||
[4] = { \
|
||||
.type = SND_CHN_T_FC, \
|
||||
.members = SND_CHN_T_MASK_FC \
|
||||
}, \
|
||||
[5] = { \
|
||||
.type = SND_CHN_T_MAX, \
|
||||
.members = 0 \
|
||||
} \
|
||||
}, \
|
||||
.mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \
|
||||
SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR | \
|
||||
SND_CHN_T_MASK_FC, \
|
||||
.offset = { 0, 1, 4, -1, 2, 3, -1, -1, -1, \
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1 } \
|
||||
}
|
||||
|
||||
#define SND_CHN_MATRIX_MAP_5_1 { /* 6 channels default */ \
|
||||
.id = SND_CHN_MATRIX_5_1, \
|
||||
.channels = 6, \
|
||||
.ext = 1, \
|
||||
.map = { \
|
||||
/* Left */ \
|
||||
[0] = { \
|
||||
.type = SND_CHN_T_FL, \
|
||||
.members = \
|
||||
SND_CHN_T_MASK_FL | SND_CHN_T_MASK_SL \
|
||||
}, \
|
||||
/* Right */ \
|
||||
[1] = { \
|
||||
.type = SND_CHN_T_FR, \
|
||||
.members = \
|
||||
SND_CHN_T_MASK_FR | SND_CHN_T_MASK_SR \
|
||||
}, \
|
||||
/* Rear Left */ \
|
||||
[2] = { \
|
||||
.type = SND_CHN_T_BL, \
|
||||
.members = \
|
||||
SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BC | \
|
||||
SND_CHN_T_MASK_SL \
|
||||
}, \
|
||||
/* Rear Right */ \
|
||||
[3] = { \
|
||||
.type = SND_CHN_T_BR, \
|
||||
.members = \
|
||||
SND_CHN_T_MASK_BR | SND_CHN_T_MASK_BC | \
|
||||
SND_CHN_T_MASK_SR \
|
||||
}, \
|
||||
/* Center */ \
|
||||
[4] = { \
|
||||
.type = SND_CHN_T_FC, \
|
||||
.members = SND_CHN_T_MASK_FC \
|
||||
}, \
|
||||
/* LFE */ \
|
||||
[5] = { \
|
||||
.type = SND_CHN_T_LF, \
|
||||
.members = SND_CHN_T_MASK_LF \
|
||||
}, \
|
||||
[6] = { \
|
||||
.type = SND_CHN_T_MAX, \
|
||||
.members = 0 \
|
||||
} \
|
||||
}, \
|
||||
.mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \
|
||||
SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR | \
|
||||
SND_CHN_T_MASK_FC | SND_CHN_T_MASK_LF, \
|
||||
.offset = { 0, 1, 4, 5, 2, 3, -1, -1, -1, \
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1 } \
|
||||
}
|
||||
|
||||
#define SND_CHN_MATRIX_MAP_6_0 { \
|
||||
.id = SND_CHN_MATRIX_6_0, \
|
||||
.channels = 6, \
|
||||
.ext = 0, \
|
||||
.map = { \
|
||||
/* Left */ \
|
||||
[0] = { \
|
||||
.type = SND_CHN_T_FL, \
|
||||
.members = \
|
||||
SND_CHN_T_MASK_FL | SND_CHN_T_MASK_LF | \
|
||||
SND_CHN_T_MASK_SL \
|
||||
}, \
|
||||
/* Right */ \
|
||||
[1] = { \
|
||||
.type = SND_CHN_T_FR, \
|
||||
.members = \
|
||||
SND_CHN_T_MASK_FR | SND_CHN_T_MASK_LF | \
|
||||
SND_CHN_T_MASK_SR \
|
||||
}, \
|
||||
/* Rear Left */ \
|
||||
[2] = { \
|
||||
.type = SND_CHN_T_BL, \
|
||||
.members = \
|
||||
SND_CHN_T_MASK_BL | SND_CHN_T_MASK_LF | \
|
||||
SND_CHN_T_MASK_SL \
|
||||
}, \
|
||||
/* Rear Right */ \
|
||||
[3] = { \
|
||||
.type = SND_CHN_T_BR, \
|
||||
.members = \
|
||||
SND_CHN_T_MASK_BR | SND_CHN_T_MASK_LF | \
|
||||
SND_CHN_T_MASK_SR \
|
||||
}, \
|
||||
/* Center */ \
|
||||
[4] = { \
|
||||
.type = SND_CHN_T_FC, \
|
||||
.members = SND_CHN_T_MASK_FC \
|
||||
}, \
|
||||
/* Rear Center */ \
|
||||
[5] = { \
|
||||
.type = SND_CHN_T_BC, \
|
||||
.members = SND_CHN_T_MASK_BC \
|
||||
}, \
|
||||
[6] = { \
|
||||
.type = SND_CHN_T_MAX, \
|
||||
.members = 0 \
|
||||
} \
|
||||
}, \
|
||||
.mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \
|
||||
SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR | \
|
||||
SND_CHN_T_MASK_FC | SND_CHN_T_MASK_BC, \
|
||||
.offset = { 0, 1, 4, -1, 2, 3, -1, -1, 5, \
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1 } \
|
||||
}
|
||||
|
||||
#define SND_CHN_MATRIX_MAP_6_1 { \
|
||||
.id = SND_CHN_MATRIX_6_1, \
|
||||
.channels = 7, \
|
||||
.ext = 1, \
|
||||
.map = { \
|
||||
/* Left */ \
|
||||
[0] = { \
|
||||
.type = SND_CHN_T_FL, \
|
||||
.members = \
|
||||
SND_CHN_T_MASK_FL | SND_CHN_T_MASK_SL \
|
||||
}, \
|
||||
/* Right */ \
|
||||
[1] = { \
|
||||
.type = SND_CHN_T_FR, \
|
||||
.members = \
|
||||
SND_CHN_T_MASK_FR | SND_CHN_T_MASK_SR \
|
||||
}, \
|
||||
/* Rear Left */ \
|
||||
[2] = { \
|
||||
.type = SND_CHN_T_BL, \
|
||||
.members = \
|
||||
SND_CHN_T_MASK_BL | SND_CHN_T_MASK_SL \
|
||||
}, \
|
||||
/* Rear Right */ \
|
||||
[3] = { \
|
||||
.type = SND_CHN_T_BR, \
|
||||
.members = \
|
||||
SND_CHN_T_MASK_BR | SND_CHN_T_MASK_SR \
|
||||
}, \
|
||||
/* Center */ \
|
||||
[4] = { \
|
||||
.type = SND_CHN_T_FC, \
|
||||
.members = SND_CHN_T_MASK_FC \
|
||||
}, \
|
||||
/* LFE */ \
|
||||
[5] = { \
|
||||
.type = SND_CHN_T_LF, \
|
||||
.members = SND_CHN_T_MASK_LF \
|
||||
}, \
|
||||
/* Rear Center */ \
|
||||
[6] = { \
|
||||
.type = SND_CHN_T_BC, \
|
||||
.members = SND_CHN_T_MASK_BC \
|
||||
}, \
|
||||
[7] = { \
|
||||
.type = SND_CHN_T_MAX, \
|
||||
.members = 0 \
|
||||
} \
|
||||
}, \
|
||||
.mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \
|
||||
SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR | \
|
||||
SND_CHN_T_MASK_FC | SND_CHN_T_MASK_LF | \
|
||||
SND_CHN_T_MASK_BC, \
|
||||
.offset = { 0, 1, 4, 5, 2, 3, -1, -1, 6, \
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1 } \
|
||||
}
|
||||
|
||||
#define SND_CHN_MATRIX_MAP_7_1 { \
|
||||
.id = SND_CHN_MATRIX_7_1, \
|
||||
.channels = 8, \
|
||||
.ext = 1, \
|
||||
.map = { \
|
||||
/* Left */ \
|
||||
[0] = { \
|
||||
.type = SND_CHN_T_FL, \
|
||||
.members = SND_CHN_T_MASK_FL \
|
||||
}, \
|
||||
/* Right */ \
|
||||
[1] = { \
|
||||
.type = SND_CHN_T_FR, \
|
||||
.members = SND_CHN_T_MASK_FR \
|
||||
}, \
|
||||
/* Rear Left */ \
|
||||
[2] = { \
|
||||
.type = SND_CHN_T_BL, \
|
||||
.members = \
|
||||
SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BC \
|
||||
}, \
|
||||
/* Rear Right */ \
|
||||
[3] = { \
|
||||
.type = SND_CHN_T_BR, \
|
||||
.members = \
|
||||
SND_CHN_T_MASK_BR | SND_CHN_T_MASK_BC \
|
||||
}, \
|
||||
/* Center */ \
|
||||
[4] = { \
|
||||
.type = SND_CHN_T_FC, \
|
||||
.members = SND_CHN_T_MASK_FC \
|
||||
}, \
|
||||
/* LFE */ \
|
||||
[5] = { \
|
||||
.type = SND_CHN_T_LF, \
|
||||
.members = SND_CHN_T_MASK_LF \
|
||||
}, \
|
||||
/* Side Left */ \
|
||||
[6] = { \
|
||||
.type = SND_CHN_T_SL, \
|
||||
.members = SND_CHN_T_MASK_SL \
|
||||
}, \
|
||||
/* Side Right */ \
|
||||
[7] = { \
|
||||
.type = SND_CHN_T_SR, \
|
||||
.members = SND_CHN_T_MASK_SR \
|
||||
}, \
|
||||
[8] = { \
|
||||
.type = SND_CHN_T_MAX, \
|
||||
.members = 0 \
|
||||
} \
|
||||
}, \
|
||||
.mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \
|
||||
SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR | \
|
||||
SND_CHN_T_MASK_FC | SND_CHN_T_MASK_LF | \
|
||||
SND_CHN_T_MASK_SL | SND_CHN_T_MASK_SR, \
|
||||
.offset = { 0, 1, 4, 5, 2, 3, -1, -1, -1, \
|
||||
6, 7, -1, -1, -1, -1, -1, -1, -1 } \
|
||||
}
|
||||
|
||||
#endif /* !_SND_MATRIX_MAP_H_ */
|
@ -1,5 +1,7 @@
|
||||
/*-
|
||||
* Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
|
||||
* Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org>
|
||||
* Portions Copyright (c) Ryan Beasley <ryan.beasley@gmail.com> - GSoC 2006
|
||||
* Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -24,14 +26,25 @@
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
|
||||
#include "feeder_if.h"
|
||||
#include "mixer_if.h"
|
||||
|
||||
SND_DECLARE_FILE("$FreeBSD$");
|
||||
|
||||
MALLOC_DEFINE(M_MIXER, "mixer", "mixer");
|
||||
|
||||
static int mixer_bypass = 1;
|
||||
TUNABLE_INT("hw.snd.vpc_mixer_bypass", &mixer_bypass);
|
||||
SYSCTL_INT(_hw_snd, OID_AUTO, vpc_mixer_bypass, CTLFLAG_RW,
|
||||
&mixer_bypass, 0,
|
||||
"control channel pcm/rec volume, bypassing real mixer device");
|
||||
|
||||
#define MIXER_NAMELEN 16
|
||||
struct snd_mixer {
|
||||
KOBJ_FIELDS;
|
||||
@ -98,9 +111,7 @@ static struct cdevsw mixer_cdevsw = {
|
||||
*/
|
||||
int mixer_count = 0;
|
||||
|
||||
#ifdef USING_DEVFS
|
||||
static eventhandler_tag mixer_ehtag = NULL;
|
||||
#endif
|
||||
|
||||
static struct cdev *
|
||||
mixer_get_devt(device_t dev)
|
||||
@ -112,7 +123,6 @@ mixer_get_devt(device_t dev)
|
||||
return snddev->mixer_dev;
|
||||
}
|
||||
|
||||
#ifdef SND_DYNSYSCTL
|
||||
static int
|
||||
mixer_lookup(char *devname)
|
||||
{
|
||||
@ -124,21 +134,20 @@ mixer_lookup(char *devname)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#define MIXER_SET_UNLOCK(x, y) do { \
|
||||
if ((y) != 0) \
|
||||
snd_mtxunlock((x)->lock); \
|
||||
} while(0)
|
||||
} while (0)
|
||||
|
||||
#define MIXER_SET_LOCK(x, y) do { \
|
||||
if ((y) != 0) \
|
||||
snd_mtxlock((x)->lock); \
|
||||
} while(0)
|
||||
} while (0)
|
||||
|
||||
static int
|
||||
mixer_set_softpcmvol(struct snd_mixer *m, struct snddev_info *d,
|
||||
unsigned left, unsigned right)
|
||||
u_int left, u_int right)
|
||||
{
|
||||
struct pcm_channel *c;
|
||||
int dropmtx, acquiremtx;
|
||||
@ -166,22 +175,13 @@ mixer_set_softpcmvol(struct snd_mixer *m, struct snddev_info *d,
|
||||
MIXER_SET_UNLOCK(m, dropmtx);
|
||||
MIXER_SET_LOCK(d, acquiremtx);
|
||||
|
||||
if (CHN_EMPTY(d, channels.pcm.busy)) {
|
||||
CHN_FOREACH(c, d, channels.pcm) {
|
||||
CHN_LOCK(c);
|
||||
if (c->direction == PCMDIR_PLAY &&
|
||||
(c->feederflags & (1 << FEEDER_VOLUME)))
|
||||
chn_setvolume(c, left, right);
|
||||
CHN_UNLOCK(c);
|
||||
}
|
||||
} else {
|
||||
CHN_FOREACH(c, d, channels.pcm.busy) {
|
||||
CHN_LOCK(c);
|
||||
if (c->direction == PCMDIR_PLAY &&
|
||||
(c->feederflags & (1 << FEEDER_VOLUME)))
|
||||
chn_setvolume(c, left, right);
|
||||
CHN_UNLOCK(c);
|
||||
}
|
||||
CHN_FOREACH(c, d, channels.pcm.busy) {
|
||||
CHN_LOCK(c);
|
||||
if (c->direction == PCMDIR_PLAY &&
|
||||
(c->feederflags & (1 << FEEDER_VOLUME)))
|
||||
chn_setvolume_multi(c, SND_VOL_C_MASTER, left, right,
|
||||
(left + right) >> 1);
|
||||
CHN_UNLOCK(c);
|
||||
}
|
||||
|
||||
MIXER_SET_UNLOCK(d, acquiremtx);
|
||||
@ -191,10 +191,62 @@ mixer_set_softpcmvol(struct snd_mixer *m, struct snddev_info *d,
|
||||
}
|
||||
|
||||
static int
|
||||
mixer_set(struct snd_mixer *m, unsigned dev, unsigned lev)
|
||||
mixer_set_eq(struct snd_mixer *m, struct snddev_info *d,
|
||||
u_int dev, u_int level)
|
||||
{
|
||||
struct pcm_channel *c;
|
||||
struct pcm_feeder *f;
|
||||
int tone, dropmtx, acquiremtx;
|
||||
|
||||
if (dev == SOUND_MIXER_TREBLE)
|
||||
tone = FEEDEQ_TREBLE;
|
||||
else if (dev == SOUND_MIXER_BASS)
|
||||
tone = FEEDEQ_BASS;
|
||||
else
|
||||
return (EINVAL);
|
||||
|
||||
if (!PCM_REGISTERED(d))
|
||||
return (EINVAL);
|
||||
|
||||
if (mtx_owned(m->lock))
|
||||
dropmtx = 1;
|
||||
else
|
||||
dropmtx = 0;
|
||||
|
||||
if (!(d->flags & SD_F_MPSAFE) || mtx_owned(d->lock) != 0)
|
||||
acquiremtx = 0;
|
||||
else
|
||||
acquiremtx = 1;
|
||||
|
||||
/*
|
||||
* Be careful here. If we're coming from cdev ioctl, it is OK to
|
||||
* not doing locking AT ALL (except on individual channel) since
|
||||
* we've been heavily guarded by pcm cv, or if we're still
|
||||
* under Giant influence. Since we also have mix_* calls, we cannot
|
||||
* assume such protection and just do the lock as usuall.
|
||||
*/
|
||||
MIXER_SET_UNLOCK(m, dropmtx);
|
||||
MIXER_SET_LOCK(d, acquiremtx);
|
||||
|
||||
CHN_FOREACH(c, d, channels.pcm.busy) {
|
||||
CHN_LOCK(c);
|
||||
f = chn_findfeeder(c, FEEDER_EQ);
|
||||
if (f != NULL)
|
||||
(void)FEEDER_SET(f, tone, level);
|
||||
CHN_UNLOCK(c);
|
||||
}
|
||||
|
||||
MIXER_SET_UNLOCK(d, acquiremtx);
|
||||
MIXER_SET_LOCK(m, dropmtx);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
mixer_set(struct snd_mixer *m, u_int dev, u_int lev)
|
||||
{
|
||||
struct snddev_info *d;
|
||||
unsigned l, r, tl, tr;
|
||||
u_int l, r, tl, tr;
|
||||
u_int32_t parent = SOUND_MIXER_NONE, child = 0;
|
||||
u_int32_t realdev;
|
||||
int i, dropmtx;
|
||||
@ -243,7 +295,8 @@ mixer_set(struct snd_mixer *m, unsigned dev, unsigned lev)
|
||||
realdev = m->realdev[i];
|
||||
tl = (l * (m->level[i] & 0x00ff)) / 100;
|
||||
tr = (r * ((m->level[i] & 0xff00) >> 8)) / 100;
|
||||
if (i == SOUND_MIXER_PCM && (d->flags & SD_F_SOFTPCMVOL))
|
||||
if (i == SOUND_MIXER_PCM &&
|
||||
(d->flags & SD_F_SOFTPCMVOL))
|
||||
(void)mixer_set_softpcmvol(m, d, tl, tr);
|
||||
else if (realdev != SOUND_MIXER_NONE)
|
||||
MIXER_SET(m, realdev, tl, tr);
|
||||
@ -257,6 +310,9 @@ mixer_set(struct snd_mixer *m, unsigned dev, unsigned lev)
|
||||
} else {
|
||||
if (dev == SOUND_MIXER_PCM && (d->flags & SD_F_SOFTPCMVOL))
|
||||
(void)mixer_set_softpcmvol(m, d, l, r);
|
||||
else if ((dev == SOUND_MIXER_TREBLE ||
|
||||
dev == SOUND_MIXER_BASS) && (d->flags & SD_F_EQ))
|
||||
(void)mixer_set_eq(m, d, dev, (l + r) >> 1);
|
||||
else if (realdev != SOUND_MIXER_NONE &&
|
||||
MIXER_SET(m, realdev, l, r) < 0) {
|
||||
MIXER_SET_LOCK(m, dropmtx);
|
||||
@ -264,10 +320,10 @@ mixer_set(struct snd_mixer *m, unsigned dev, unsigned lev)
|
||||
}
|
||||
}
|
||||
|
||||
m->level[dev] = l | (r << 8);
|
||||
|
||||
MIXER_SET_LOCK(m, dropmtx);
|
||||
|
||||
m->level[dev] = l | (r << 8);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -284,6 +340,7 @@ static int
|
||||
mixer_setrecsrc(struct snd_mixer *mixer, u_int32_t src)
|
||||
{
|
||||
struct snddev_info *d;
|
||||
u_int32_t recsrc;
|
||||
int dropmtx;
|
||||
|
||||
d = device_get_softc(mixer->dev);
|
||||
@ -298,8 +355,11 @@ mixer_setrecsrc(struct snd_mixer *mixer, u_int32_t src)
|
||||
src = SOUND_MASK_MIC;
|
||||
/* It is safe to drop this mutex due to Giant. */
|
||||
MIXER_SET_UNLOCK(mixer, dropmtx);
|
||||
mixer->recsrc = MIXER_SETRECSRC(mixer, src);
|
||||
recsrc = MIXER_SETRECSRC(mixer, src);
|
||||
MIXER_SET_LOCK(mixer, dropmtx);
|
||||
|
||||
mixer->recsrc = recsrc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -398,6 +458,8 @@ mix_setdevs(struct snd_mixer *m, u_int32_t v)
|
||||
d = device_get_softc(m->dev);
|
||||
if (d != NULL && (d->flags & SD_F_SOFTPCMVOL))
|
||||
v |= SOUND_MASK_PCM;
|
||||
if (d != NULL && (d->flags & SD_F_EQ))
|
||||
v |= SOUND_MASK_TREBLE | SOUND_MASK_BASS;
|
||||
for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
|
||||
if (m->parent[i] < SOUND_MIXER_NRDEVICES)
|
||||
v |= 1 << m->parent[i];
|
||||
@ -623,6 +685,20 @@ mixer_init(device_t dev, kobj_class_t cls, void *devinfo)
|
||||
struct cdev *pdev;
|
||||
int i, unit, devunit, val;
|
||||
|
||||
snddev = device_get_softc(dev);
|
||||
if (snddev == NULL)
|
||||
return (-1);
|
||||
|
||||
if (resource_int_value(device_get_name(dev),
|
||||
device_get_unit(dev), "eq", &val) == 0 && val != 0) {
|
||||
snddev->flags |= SD_F_EQ;
|
||||
if ((val & SD_F_EQ_MASK) == val)
|
||||
snddev->flags |= val;
|
||||
else
|
||||
snddev->flags |= SD_F_EQ_DEFAULT;
|
||||
snddev->eqpreamp = 0;
|
||||
}
|
||||
|
||||
m = mixer_obj_create(dev, cls, devinfo, MIXER_TYPE_PRIMARY, NULL);
|
||||
if (m == NULL)
|
||||
return (-1);
|
||||
@ -644,10 +720,9 @@ mixer_init(device_t dev, kobj_class_t cls, void *devinfo)
|
||||
|
||||
unit = device_get_unit(dev);
|
||||
devunit = snd_mkunit(unit, SND_DEV_CTL, 0);
|
||||
pdev = make_dev(&mixer_cdevsw, devunit,
|
||||
pdev = make_dev(&mixer_cdevsw, PCMMINOR(devunit),
|
||||
UID_ROOT, GID_WHEEL, 0666, "mixer%d", unit);
|
||||
pdev->si_drv1 = m;
|
||||
snddev = device_get_softc(dev);
|
||||
snddev->mixer_dev = pdev;
|
||||
|
||||
++mixer_count;
|
||||
@ -674,6 +749,8 @@ mixer_init(device_t dev, kobj_class_t cls, void *devinfo)
|
||||
}
|
||||
if (snddev->flags & SD_F_SOFTPCMVOL)
|
||||
device_printf(dev, "Soft PCM mixer ENABLED\n");
|
||||
if (snddev->flags & SD_F_EQ)
|
||||
device_printf(dev, "EQ Treble/Bass ENABLED\n");
|
||||
}
|
||||
|
||||
return (0);
|
||||
@ -760,7 +837,6 @@ mixer_reinit(device_t dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef SND_DYNSYSCTL
|
||||
static int
|
||||
sysctl_hw_snd_hwvol_mixer(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
@ -788,7 +864,6 @@ sysctl_hw_snd_hwvol_mixer(SYSCTL_HANDLER_ARGS)
|
||||
snd_mtxunlock(m->lock);
|
||||
return error;
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
mixer_hwvol_init(device_t dev)
|
||||
@ -801,7 +876,6 @@ mixer_hwvol_init(device_t dev)
|
||||
|
||||
m->hwvol_mixer = SOUND_MIXER_VOLUME;
|
||||
m->hwvol_step = 5;
|
||||
#ifdef SND_DYNSYSCTL
|
||||
SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
|
||||
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
|
||||
OID_AUTO, "hwvol_step", CTLFLAG_RW, &m->hwvol_step, 0, "");
|
||||
@ -809,7 +883,6 @@ mixer_hwvol_init(device_t dev)
|
||||
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
|
||||
OID_AUTO, "hwvol_mixer", CTLTYPE_STRING | CTLFLAG_RW, m, 0,
|
||||
sysctl_hw_snd_hwvol_mixer, "A", "");
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -985,6 +1058,114 @@ mixer_close(struct cdev *i_dev, int flags, int mode, struct thread *td)
|
||||
return (ret);
|
||||
}
|
||||
|
||||
static int
|
||||
mixer_ioctl_channel(struct cdev *dev, u_long cmd, caddr_t arg, int mode,
|
||||
struct thread *td, int from)
|
||||
{
|
||||
struct snddev_info *d;
|
||||
struct snd_mixer *m;
|
||||
struct pcm_channel *c, *rdch, *wrch;
|
||||
pid_t pid;
|
||||
int j, ret;
|
||||
|
||||
if (td == NULL || td->td_proc == NULL)
|
||||
return (-1);
|
||||
|
||||
m = dev->si_drv1;
|
||||
d = device_get_softc(m->dev);
|
||||
j = cmd & 0xff;
|
||||
|
||||
switch (j) {
|
||||
case SOUND_MIXER_PCM:
|
||||
case SOUND_MIXER_RECLEV:
|
||||
case SOUND_MIXER_DEVMASK:
|
||||
case SOUND_MIXER_CAPS:
|
||||
case SOUND_MIXER_STEREODEVS:
|
||||
break;
|
||||
default:
|
||||
return (-1);
|
||||
break;
|
||||
}
|
||||
|
||||
pid = td->td_proc->p_pid;
|
||||
rdch = NULL;
|
||||
wrch = NULL;
|
||||
c = NULL;
|
||||
ret = -1;
|
||||
|
||||
/*
|
||||
* This is unfair. Imagine single proc opening multiple
|
||||
* instances of same direction. What we do right now
|
||||
* is looking for the first matching proc/pid, and just
|
||||
* that. Nothing more. Consider it done.
|
||||
*
|
||||
* The better approach of controlling specific channel
|
||||
* pcm or rec volume is by doing mixer ioctl
|
||||
* (SNDCTL_DSP_[SET|GET][PLAY|REC]VOL / SOUND_MIXER_[PCM|RECLEV]
|
||||
* on its open fd, rather than cracky mixer bypassing here.
|
||||
*/
|
||||
CHN_FOREACH(c, d, channels.pcm.opened) {
|
||||
CHN_LOCK(c);
|
||||
if (c->pid != pid ||
|
||||
!(c->feederflags & (1 << FEEDER_VOLUME))) {
|
||||
CHN_UNLOCK(c);
|
||||
continue;
|
||||
}
|
||||
if (rdch == NULL && c->direction == PCMDIR_REC) {
|
||||
rdch = c;
|
||||
if (j == SOUND_MIXER_RECLEV)
|
||||
goto mixer_ioctl_channel_proc;
|
||||
} else if (wrch == NULL && c->direction == PCMDIR_PLAY) {
|
||||
wrch = c;
|
||||
if (j == SOUND_MIXER_PCM)
|
||||
goto mixer_ioctl_channel_proc;
|
||||
}
|
||||
CHN_UNLOCK(c);
|
||||
if (rdch != NULL && wrch != NULL)
|
||||
break;
|
||||
}
|
||||
|
||||
if (rdch == NULL && wrch == NULL)
|
||||
return (-1);
|
||||
|
||||
if ((j == SOUND_MIXER_DEVMASK || j == SOUND_MIXER_CAPS ||
|
||||
j == SOUND_MIXER_STEREODEVS) &&
|
||||
(cmd & MIXER_READ(0)) == MIXER_READ(0)) {
|
||||
snd_mtxlock(m->lock);
|
||||
*(int *)arg = mix_getdevs(m);
|
||||
snd_mtxunlock(m->lock);
|
||||
if (rdch != NULL)
|
||||
*(int *)arg |= SOUND_MASK_RECLEV;
|
||||
if (wrch != NULL)
|
||||
*(int *)arg |= SOUND_MASK_PCM;
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return (ret);
|
||||
|
||||
mixer_ioctl_channel_proc:
|
||||
|
||||
KASSERT(c != NULL, ("%s(): NULL channel", __func__));
|
||||
CHN_LOCKASSERT(c);
|
||||
|
||||
if ((cmd & MIXER_WRITE(0)) == MIXER_WRITE(0)) {
|
||||
int left, right, center;
|
||||
|
||||
left = *(int *)arg & 0x7f;
|
||||
right = (*(int *)arg >> 8) & 0x7f;
|
||||
center = (left + right) >> 1;
|
||||
chn_setvolume_multi(c, SND_VOL_C_PCM, left, right, center);
|
||||
} else if ((cmd & MIXER_READ(0)) == MIXER_READ(0)) {
|
||||
*(int *)arg = CHN_GETVOLUME(c, SND_VOL_C_PCM, SND_CHN_T_FL);
|
||||
*(int *)arg |=
|
||||
CHN_GETVOLUME(c, SND_VOL_C_PCM, SND_CHN_T_FR) << 8;
|
||||
}
|
||||
|
||||
CHN_UNLOCK(c);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
mixer_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode,
|
||||
struct thread *td)
|
||||
@ -1002,7 +1183,15 @@ mixer_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode,
|
||||
PCM_GIANT_ENTER(d);
|
||||
PCM_ACQUIRE_QUICK(d);
|
||||
|
||||
ret = mixer_ioctl_cmd(i_dev, cmd, arg, mode, td, MIXER_CMD_CDEV);
|
||||
ret = -1;
|
||||
|
||||
if (mixer_bypass != 0 && (d->flags & SD_F_VPC))
|
||||
ret = mixer_ioctl_channel(i_dev, cmd, arg, mode, td,
|
||||
MIXER_CMD_CDEV);
|
||||
|
||||
if (ret == -1)
|
||||
ret = mixer_ioctl_cmd(i_dev, cmd, arg, mode, td,
|
||||
MIXER_CMD_CDEV);
|
||||
|
||||
PCM_RELEASE_QUICK(d);
|
||||
PCM_GIANT_LEAVE(d);
|
||||
@ -1012,7 +1201,7 @@ mixer_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode,
|
||||
|
||||
/*
|
||||
* XXX Make sure you can guarantee concurrency safety before calling this
|
||||
* function, be it through Giant, PCM_CV_*, etc !
|
||||
* function, be it through Giant, PCM_*, etc !
|
||||
*/
|
||||
int
|
||||
mixer_ioctl_cmd(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode,
|
||||
@ -1112,7 +1301,6 @@ mixer_ioctl_cmd(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode,
|
||||
return (ret);
|
||||
}
|
||||
|
||||
#ifdef USING_DEVFS
|
||||
static void
|
||||
mixer_clone(void *arg,
|
||||
#if __FreeBSD_version >= 600034
|
||||
@ -1152,7 +1340,6 @@ mixer_sysuninit(void *p)
|
||||
|
||||
SYSINIT(mixer_sysinit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, mixer_sysinit, NULL);
|
||||
SYSUNINIT(mixer_sysuninit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, mixer_sysuninit, NULL);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Handler for SNDCTL_MIXERINFO
|
||||
@ -1204,8 +1391,8 @@ mixer_oss_mixerinfo(struct cdev *i_dev, oss_mixerinfo *mi)
|
||||
/* XXX Need Giant magic entry */
|
||||
|
||||
/* See the note in function docblock. */
|
||||
mtx_assert(d->lock, MA_NOTOWNED);
|
||||
pcm_lock(d);
|
||||
PCM_UNLOCKASSERT(d);
|
||||
PCM_LOCK(d);
|
||||
|
||||
if (d->mixer_dev != NULL && d->mixer_dev->si_drv1 != NULL &&
|
||||
((mi->dev == -1 && d->mixer_dev == i_dev) ||
|
||||
@ -1288,7 +1475,7 @@ mixer_oss_mixerinfo(struct cdev *i_dev, oss_mixerinfo *mi)
|
||||
} else
|
||||
++nmix;
|
||||
|
||||
pcm_unlock(d);
|
||||
PCM_UNLOCK(d);
|
||||
|
||||
if (m != NULL)
|
||||
return (0);
|
||||
|
@ -1,5 +1,6 @@
|
||||
/*-
|
||||
* Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
|
||||
* Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org>
|
||||
* Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -73,4 +74,15 @@ extern int mixer_count;
|
||||
#define MIXER_SIZE (512 + sizeof(struct kobj) + \
|
||||
sizeof(oss_mixer_enuminfo))
|
||||
|
||||
#ifdef SND_DEBUG
|
||||
#define MIXER_DECLARE(mixer) \
|
||||
static struct kobj_class mixer##_class = { \
|
||||
.name = #mixer, \
|
||||
.methods = mixer##_methods, \
|
||||
.size = MIXER_SIZE, \
|
||||
.baseclasses = NULL, \
|
||||
.refs = 0 \
|
||||
}
|
||||
#else
|
||||
#define MIXER_DECLARE(name) static DEFINE_CLASS(name, name ## _methods, MIXER_SIZE)
|
||||
#endif
|
||||
|
438
sys/dev/sound/pcm/pcm.h
Normal file
438
sys/dev/sound/pcm/pcm.h
Normal file
@ -0,0 +1,438 @@
|
||||
/*-
|
||||
* Copyright (c) 2006-2009 Ariff Abdullah <ariff@FreeBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _SND_PCM_H_
|
||||
#define _SND_PCM_H_
|
||||
|
||||
#include <sys/param.h>
|
||||
|
||||
/*
|
||||
* 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 SND_PCM_64 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 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))
|
||||
|
||||
#if BYTE_ORDER == LITTLE_ENDIAN
|
||||
#define _PCM_READ_S16_LE(b8) INTPCM_T(*((int16_t *)(b8)))
|
||||
#define _PCM_READ_S32_LE(b8) INTPCM_T(*((int32_t *)(b8)))
|
||||
#define _PCM_READ_S16_BE(b8) \
|
||||
INTPCM_T((b8)[1] | (((int8_t)((b8)[0])) << 8))
|
||||
#define _PCM_READ_S32_BE(b8) \
|
||||
INTPCM_T((b8)[3] | ((b8)[2] << 8) | ((b8)[1] << 16) | \
|
||||
(((int8_t)((b8)[0])) << 24))
|
||||
|
||||
#define _PCM_WRITE_S16_LE(b8, val) do { \
|
||||
*((int16_t *)(b8)) = (val); \
|
||||
} while (0)
|
||||
#define _PCM_WRITE_S32_LE(b8, val) do { \
|
||||
*((int32_t *)(b8)) = (val); \
|
||||
} while (0)
|
||||
#define _PCM_WRITE_S16_BE(bb8, vval) do { \
|
||||
intpcm_t val = (vval); \
|
||||
uint8_t *b8 = (bb8); \
|
||||
b8[1] = val; \
|
||||
b8[0] = val >> 8; \
|
||||
} while (0)
|
||||
#define _PCM_WRITE_S32_BE(bb8, vval) do { \
|
||||
intpcm_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) \
|
||||
INTPCM_T((int16_t)(*((uint16_t *)(b8)) ^ 0x8000))
|
||||
#define _PCM_READ_U32_LE(b8) \
|
||||
INTPCM_T((int32_t)(*((uint32_t *)(b8)) ^ 0x80000000))
|
||||
#define _PCM_READ_U16_BE(b8) \
|
||||
INTPCM_T((b8)[1] | (((int8_t)((b8)[0] ^ 0x80)) << 8))
|
||||
#define _PCM_READ_U32_BE(b8) \
|
||||
INTPCM_T((b8)[3] | ((b8)[2] << 8) | ((b8)[1] << 16) | \
|
||||
(((int8_t)((b8)[0] ^ 0x80)) << 24))
|
||||
|
||||
#define _PCM_WRITE_U16_LE(b8, val) do { \
|
||||
*((uint16_t *)(b8)) = (val) ^ 0x8000; \
|
||||
} while (0)
|
||||
#define _PCM_WRITE_U32_LE(b8, val) do { \
|
||||
*((uint32_t *)(b8)) = (val) ^ 0x80000000; \
|
||||
} while (0)
|
||||
#define _PCM_WRITE_U16_BE(bb8, vval) do { \
|
||||
intpcm_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 { \
|
||||
intpcm_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)
|
||||
|
||||
#define _PCM_READ_S16_NE(b8) _PCM_READ_S16_LE(b8)
|
||||
#define _PCM_READ_U16_NE(b8) _PCM_READ_U16_LE(b8)
|
||||
#define _PCM_READ_S32_NE(b8) _PCM_READ_S32_LE(b8)
|
||||
#define _PCM_READ_U32_NE(b8) _PCM_READ_U32_LE(b8)
|
||||
#define _PCM_WRITE_S16_NE(b6) _PCM_WRITE_S16_LE(b8)
|
||||
#define _PCM_WRITE_U16_NE(b6) _PCM_WRITE_U16_LE(b8)
|
||||
#define _PCM_WRITE_S32_NE(b6) _PCM_WRITE_S32_LE(b8)
|
||||
#define _PCM_WRITE_U32_NE(b6) _PCM_WRITE_U32_LE(b8)
|
||||
#else /* !LITTLE_ENDIAN */
|
||||
#define _PCM_READ_S16_LE(b8) \
|
||||
INTPCM_T((b8)[0] | (((int8_t)((b8)[1])) << 8))
|
||||
#define _PCM_READ_S32_LE(b8) \
|
||||
INTPCM_T((b8)[0] | ((b8)[1] << 8) | ((b8)[2] << 16) | \
|
||||
(((int8_t)((b8)[3])) << 24))
|
||||
#define _PCM_READ_S16_BE(b8) INTPCM_T(*((int16_t *)(b8)))
|
||||
#define _PCM_READ_S32_BE(b8) INTPCM_T(*((int32_t *)(b8)))
|
||||
|
||||
#define _PCM_WRITE_S16_LE(bb8, vval) do { \
|
||||
intpcm_t val = (vval); \
|
||||
uint8_t *b8 = (bb8); \
|
||||
b8[0] = val; \
|
||||
b8[1] = val >> 8; \
|
||||
} while (0)
|
||||
#define _PCM_WRITE_S32_LE(bb8, vval) do { \
|
||||
intpcm_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) do { \
|
||||
*((int16_t *)(b8)) = (val); \
|
||||
} while (0)
|
||||
#define _PCM_WRITE_S32_BE(b8, val) do { \
|
||||
*((int32_t *)(b8)) = (val); \
|
||||
} while (0)
|
||||
|
||||
#define _PCM_READ_U16_LE(b8) \
|
||||
INTPCM_T((b8)[0] | (((int8_t)((b8)[1] ^ 0x80)) << 8))
|
||||
#define _PCM_READ_U32_LE(b8) \
|
||||
INTPCM_T((b8)[0] | ((b8)[1] << 8) | ((b8)[2] << 16) | \
|
||||
(((int8_t)((b8)[3] ^ 0x80)) << 24))
|
||||
#define _PCM_READ_U16_BE(b8) \
|
||||
INTPCM_T((int16_t)(*((uint16_t *)(b8)) ^ 0x8000))
|
||||
#define _PCM_READ_U32_BE(b8) \
|
||||
INTPCM_T((int32_t)(*((uint32_t *)(b8)) ^ 0x80000000))
|
||||
|
||||
#define _PCM_WRITE_U16_LE(bb8, vval) do { \
|
||||
intpcm_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 { \
|
||||
intpcm_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) do { \
|
||||
*((uint16_t *)(b8)) = (val) ^ 0x8000; \
|
||||
} while (0)
|
||||
#define _PCM_WRITE_U32_BE(b8, val) do { \
|
||||
*((uint32_t *)(b8)) = (val) ^ 0x80000000; \
|
||||
} while (0)
|
||||
|
||||
#define _PCM_READ_S16_NE(b8) _PCM_READ_S16_BE(b8)
|
||||
#define _PCM_READ_U16_NE(b8) _PCM_READ_U16_BE(b8)
|
||||
#define _PCM_READ_S32_NE(b8) _PCM_READ_S32_BE(b8)
|
||||
#define _PCM_READ_U32_NE(b8) _PCM_READ_U32_BE(b8)
|
||||
#define _PCM_WRITE_S16_NE(b6) _PCM_WRITE_S16_BE(b8)
|
||||
#define _PCM_WRITE_U16_NE(b6) _PCM_WRITE_U16_BE(b8)
|
||||
#define _PCM_WRITE_S32_NE(b6) _PCM_WRITE_S32_BE(b8)
|
||||
#define _PCM_WRITE_U32_NE(b6) _PCM_WRITE_U32_BE(b8)
|
||||
#endif /* LITTLE_ENDIAN */
|
||||
|
||||
#define _PCM_READ_S24_LE(b8) \
|
||||
INTPCM_T((b8)[0] | ((b8)[1] << 8) | (((int8_t)((b8)[2])) << 16))
|
||||
#define _PCM_READ_S24_BE(b8) \
|
||||
INTPCM_T((b8)[2] | ((b8)[1] << 8) | (((int8_t)((b8)[0])) << 16))
|
||||
|
||||
#define _PCM_WRITE_S24_LE(bb8, vval) do { \
|
||||
intpcm_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 { \
|
||||
intpcm_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) \
|
||||
INTPCM_T((b8)[0] | ((b8)[1] << 8) | \
|
||||
(((int8_t)((b8)[2] ^ 0x80)) << 16))
|
||||
#define _PCM_READ_U24_BE(b8) \
|
||||
INTPCM_T((b8)[2] | ((b8)[1] << 8) | \
|
||||
(((int8_t)((b8)[0] ^ 0x80)) << 16))
|
||||
|
||||
#define _PCM_WRITE_U24_LE(bb8, vval) do { \
|
||||
intpcm_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 { \
|
||||
intpcm_t val = (vval); \
|
||||
uint8_t *b8 = (bb8); \
|
||||
b8[2] = val; \
|
||||
b8[1] = val >> 8; \
|
||||
b8[0] = (val >> 16) ^ 0x80; \
|
||||
} while (0)
|
||||
|
||||
#if BYTE_ORDER == LITTLE_ENDIAN
|
||||
#define _PCM_READ_S24_NE(b8) _PCM_READ_S24_LE(b8)
|
||||
#define _PCM_READ_U24_NE(b8) _PCM_READ_U24_LE(b8)
|
||||
#define _PCM_WRITE_S24_NE(b6) _PCM_WRITE_S24_LE(b8)
|
||||
#define _PCM_WRITE_U24_NE(b6) _PCM_WRITE_U24_LE(b8)
|
||||
#else /* !LITTLE_ENDIAN */
|
||||
#define _PCM_READ_S24_NE(b8) _PCM_READ_S24_BE(b8)
|
||||
#define _PCM_READ_U24_NE(b8) _PCM_READ_U24_BE(b8)
|
||||
#define _PCM_WRITE_S24_NE(b6) _PCM_WRITE_S24_BE(b8)
|
||||
#define _PCM_WRITE_U24_NE(b6) _PCM_WRITE_U24_BE(b8)
|
||||
#endif /* LITTLE_ENDIAN */
|
||||
/*
|
||||
* 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_NE(b8) INTPCM_T(*((int8_t *)(b8)))
|
||||
#define _PCM_READ_U8_NE(b8) \
|
||||
INTPCM_T((int8_t)(*((uint8_t *)(b8)) ^ 0x80))
|
||||
|
||||
#define _PCM_WRITE_S8_NE(b8, val) do { \
|
||||
*((int8_t *)(b8)) = (val); \
|
||||
} while (0)
|
||||
#define _PCM_WRITE_U8_NE(b8, val) do { \
|
||||
*((uint8_t *)(b8)) = (val) ^ 0x80; \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Common macross. Use this instead of "_", unless we want
|
||||
* the real sample value.
|
||||
*/
|
||||
|
||||
/* 8bit */
|
||||
#define PCM_READ_S8_NE(b8) _PCM_READ_S8_NE(b8)
|
||||
#define PCM_READ_U8_NE(b8) _PCM_READ_U8_NE(b8)
|
||||
#define PCM_WRITE_S8_NE(b8, val) _PCM_WRITE_S8_NE(b8, val)
|
||||
#define PCM_WRITE_U8_NE(b8, val) _PCM_WRITE_U8_NE(b8, val)
|
||||
|
||||
/* 16bit */
|
||||
#define PCM_READ_S16_LE(b8) _PCM_READ_S16_LE(b8)
|
||||
#define PCM_READ_S16_BE(b8) _PCM_READ_S16_BE(b8)
|
||||
#define PCM_READ_U16_LE(b8) _PCM_READ_U16_LE(b8)
|
||||
#define PCM_READ_U16_BE(b8) _PCM_READ_U16_BE(b8)
|
||||
|
||||
#define PCM_WRITE_S16_LE(b8, val) _PCM_WRITE_S16_LE(b8, val)
|
||||
#define PCM_WRITE_S16_BE(b8, val) _PCM_WRITE_S16_BE(b8, val)
|
||||
#define PCM_WRITE_U16_LE(b8, val) _PCM_WRITE_U16_LE(b8, val)
|
||||
#define PCM_WRITE_U16_BE(b8, val) _PCM_WRITE_U16_BE(b8, val)
|
||||
|
||||
#define PCM_READ_S16_NE(b8) _PCM_READ_S16_NE(b8)
|
||||
#define PCM_READ_U16_NE(b8) _PCM_READ_U16_NE(b8)
|
||||
#define PCM_WRITE_S16_NE(b8) _PCM_WRITE_S16_NE(b8)
|
||||
#define PCM_WRITE_U16_NE(b8) _PCM_WRITE_U16_NE(b8)
|
||||
|
||||
/* 24bit */
|
||||
#define PCM_READ_S24_LE(b8) _PCM_READ_S24_LE(b8)
|
||||
#define PCM_READ_S24_BE(b8) _PCM_READ_S24_BE(b8)
|
||||
#define PCM_READ_U24_LE(b8) _PCM_READ_U24_LE(b8)
|
||||
#define PCM_READ_U24_BE(b8) _PCM_READ_U24_BE(b8)
|
||||
|
||||
#define PCM_WRITE_S24_LE(b8, val) _PCM_WRITE_S24_LE(b8, val)
|
||||
#define PCM_WRITE_S24_BE(b8, val) _PCM_WRITE_S24_BE(b8, val)
|
||||
#define PCM_WRITE_U24_LE(b8, val) _PCM_WRITE_U24_LE(b8, val)
|
||||
#define PCM_WRITE_U24_BE(b8, val) _PCM_WRITE_U24_BE(b8, val)
|
||||
|
||||
#define PCM_READ_S24_NE(b8) _PCM_READ_S24_NE(b8)
|
||||
#define PCM_READ_U24_NE(b8) _PCM_READ_U24_NE(b8)
|
||||
#define PCM_WRITE_S24_NE(b8) _PCM_WRITE_S24_NE(b8)
|
||||
#define PCM_WRITE_U24_NE(b8) _PCM_WRITE_U24_NE(b8)
|
||||
|
||||
/* 32bit */
|
||||
#ifdef SND_PCM_64
|
||||
#define PCM_READ_S32_LE(b8) _PCM_READ_S32_LE(b8)
|
||||
#define PCM_READ_S32_BE(b8) _PCM_READ_S32_BE(b8)
|
||||
#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_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_WRITE_U32_LE(b8, val) _PCM_WRITE_U32_LE(b8, val)
|
||||
#define PCM_WRITE_U32_BE(b8, val) _PCM_WRITE_U32_BE(b8, val)
|
||||
|
||||
#define PCM_READ_S32_NE(b8) _PCM_READ_S32_NE(b8)
|
||||
#define PCM_READ_U32_NE(b8) _PCM_READ_U32_NE(b8)
|
||||
#define PCM_WRITE_S32_NE(b8) _PCM_WRITE_S32_NE(b8)
|
||||
#define PCM_WRITE_U32_NE(b8) _PCM_WRITE_U32_NE(b8)
|
||||
#else /* !SND_PCM_64 */
|
||||
/*
|
||||
* 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_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_READ_S32_NE(b8) (_PCM_READ_S32_NE(b8) >> PCM_FXSHIFT)
|
||||
#define PCM_READ_U32_NE(b8) (_PCM_READ_U32_NE(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_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)
|
||||
|
||||
#define PCM_WRITE_S32_NE(b8, val) \
|
||||
_PCM_WRITE_S32_NE(b8, (val) << PCM_FXSHIFT)
|
||||
#define PCM_WRITE_U32_NE(b8, val) \
|
||||
_PCM_WRITE_U32_NE(b8, (val) << PCM_FXSHIFT)
|
||||
#endif /* SND_PCM_64 */
|
||||
|
||||
#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 SND_PCM_64
|
||||
#define PCM_CLAMP_S32(val) \
|
||||
(((val) > PCM_S32_MAX) ? PCM_S32_MAX : \
|
||||
(((val) < PCM_S32_MIN) ? PCM_S32_MIN : (val)))
|
||||
#else /* !SND_PCM_64 */
|
||||
#define PCM_CLAMP_S32(val) \
|
||||
(((val) > PCM_S24_MAX) ? PCM_S32_MAX : \
|
||||
(((val) < PCM_S24_MIN) ? PCM_S32_MIN : \
|
||||
((val) << PCM_FXSHIFT)))
|
||||
#endif /* SND_PCM_64 */
|
||||
|
||||
#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)
|
||||
|
||||
#endif /* !_SND_PCM_H_ */
|
@ -1,5 +1,6 @@
|
||||
/*-
|
||||
* Copyright (c) 2001 Cameron Grant <cg@freebsd.org>
|
||||
* Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org>
|
||||
* Copyright (c) 2001 Cameron Grant <cg@FreeBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -24,13 +25,15 @@
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
#include <dev/sound/pcm/vchan.h>
|
||||
#include <dev/sound/version.h>
|
||||
#ifdef USING_MUTEX
|
||||
#include <sys/sx.h>
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
#include <dev/sound/pcm/pcm.h>
|
||||
#include <dev/sound/version.h>
|
||||
#include <sys/sx.h>
|
||||
|
||||
SND_DECLARE_FILE("$FreeBSD$");
|
||||
|
||||
#define SS_TYPE_MODULE 0
|
||||
@ -60,9 +63,7 @@ struct sndstat_entry {
|
||||
int type, unit;
|
||||
};
|
||||
|
||||
#ifdef USING_MUTEX
|
||||
static struct mtx sndstat_lock;
|
||||
#endif
|
||||
static struct sbuf sndstat_sbuf;
|
||||
static struct cdev *sndstat_dev = NULL;
|
||||
static int sndstat_bufptr = -1;
|
||||
@ -76,16 +77,12 @@ static int sndstat_files = 0;
|
||||
sbuf_delete(&sndstat_sbuf); \
|
||||
sndstat_bufptr = -1; \
|
||||
} \
|
||||
} while(0)
|
||||
} while (0)
|
||||
|
||||
static SLIST_HEAD(, sndstat_entry) sndstat_devlist = SLIST_HEAD_INITIALIZER(none);
|
||||
|
||||
int snd_verbose = 1;
|
||||
#ifdef USING_MUTEX
|
||||
TUNABLE_INT("hw.snd.verbose", &snd_verbose);
|
||||
#else
|
||||
TUNABLE_INT_DECL("hw.snd.verbose", 1, snd_verbose);
|
||||
#endif
|
||||
|
||||
#ifdef SND_DEBUG
|
||||
static int
|
||||
@ -354,7 +351,7 @@ sndstat_prepare(struct sbuf *s)
|
||||
int i, j;
|
||||
|
||||
sbuf_printf(s, "FreeBSD Audio Driver (newpcm: %ubit %d/%s)\n",
|
||||
(u_int)sizeof(intpcm_t) << 3, SND_DRV_VERSION, MACHINE_ARCH);
|
||||
(u_int)sizeof(intpcm32_t) << 3, SND_DRV_VERSION, MACHINE_ARCH);
|
||||
if (SLIST_EMPTY(&sndstat_devlist)) {
|
||||
sbuf_printf(s, "No devices installed.\n");
|
||||
sbuf_finish(s);
|
||||
|
163
sys/dev/sound/pcm/sndstat.h
Normal file
163
sys/dev/sound/pcm/sndstat.h
Normal file
@ -0,0 +1,163 @@
|
||||
/*-
|
||||
* Copyright (c) 2007-2009 Ariff Abdullah <ariff@FreeBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _SND_SNDSTAT_H_
|
||||
#define _SND_SNDSTAT_H_
|
||||
|
||||
#define SNDSTAT_PREPARE_PCM_ARGS \
|
||||
struct sbuf *s, device_t dev, int verbose
|
||||
|
||||
#define SNDSTAT_PREPARE_PCM_BEGIN() do { \
|
||||
struct snddev_info *d; \
|
||||
struct pcm_channel *c; \
|
||||
struct pcm_feeder *f; \
|
||||
\
|
||||
if (verbose < 1) \
|
||||
return (0); \
|
||||
\
|
||||
d = device_get_softc(dev); \
|
||||
PCM_BUSYASSERT(d); \
|
||||
\
|
||||
if (CHN_EMPTY(d, channels.pcm)) { \
|
||||
sbuf_printf(s, " (mixer only)"); \
|
||||
return (0); \
|
||||
} \
|
||||
\
|
||||
sbuf_printf(s, " (%dp:%dv/%dr:%dv channels %splex%s)", \
|
||||
d->playcount, d->pvchancount, d->reccount, d->rvchancount, \
|
||||
(d->flags & SD_F_SIMPLEX) ? "sim" : "du", \
|
||||
(device_get_unit(dev) == snd_unit) ? " default" : "")
|
||||
|
||||
|
||||
#define SNDSTAT_PREPARE_PCM_END() \
|
||||
if (verbose <= 1) \
|
||||
return (0); \
|
||||
\
|
||||
sbuf_printf(s, "\n\t"); \
|
||||
sbuf_printf(s, "snddev flags=0x%b", d->flags, SD_F_BITS); \
|
||||
\
|
||||
CHN_FOREACH(c, d, channels.pcm) { \
|
||||
\
|
||||
KASSERT(c->bufhard != NULL && c->bufsoft != NULL, \
|
||||
("hosed pcm channel setup")); \
|
||||
\
|
||||
sbuf_printf(s, "\n\t"); \
|
||||
\
|
||||
sbuf_printf(s, "%s[%s]: ", \
|
||||
(c->parentchannel != NULL) ? \
|
||||
c->parentchannel->name : "", c->name); \
|
||||
sbuf_printf(s, "spd %d", c->speed); \
|
||||
if (c->speed != sndbuf_getspd(c->bufhard)) \
|
||||
sbuf_printf(s, "/%d", \
|
||||
sndbuf_getspd(c->bufhard)); \
|
||||
sbuf_printf(s, ", fmt 0x%08x", c->format); \
|
||||
if (c->format != sndbuf_getfmt(c->bufhard)) \
|
||||
sbuf_printf(s, "/0x%08x", \
|
||||
sndbuf_getfmt(c->bufhard)); \
|
||||
sbuf_printf(s, ", flags 0x%08x, 0x%08x", \
|
||||
c->flags, c->feederflags); \
|
||||
if (c->pid != -1) \
|
||||
sbuf_printf(s, ", pid %d (%s)", \
|
||||
c->pid, c->comm); \
|
||||
sbuf_printf(s, "\n\t"); \
|
||||
\
|
||||
sbuf_printf(s, "interrupts %d, ", c->interrupts); \
|
||||
\
|
||||
if (c->direction == PCMDIR_REC) \
|
||||
sbuf_printf(s, \
|
||||
"overruns %d, feed %u, hfree %d, " \
|
||||
"sfree %d [b:%d/%d/%d|bs:%d/%d/%d]", \
|
||||
c->xruns, c->feedcount, \
|
||||
sndbuf_getfree(c->bufhard), \
|
||||
sndbuf_getfree(c->bufsoft), \
|
||||
sndbuf_getsize(c->bufhard), \
|
||||
sndbuf_getblksz(c->bufhard), \
|
||||
sndbuf_getblkcnt(c->bufhard), \
|
||||
sndbuf_getsize(c->bufsoft), \
|
||||
sndbuf_getblksz(c->bufsoft), \
|
||||
sndbuf_getblkcnt(c->bufsoft)); \
|
||||
else \
|
||||
sbuf_printf(s, \
|
||||
"underruns %d, feed %u, ready %d " \
|
||||
"[b:%d/%d/%d|bs:%d/%d/%d]", \
|
||||
c->xruns, c->feedcount, \
|
||||
sndbuf_getready(c->bufsoft), \
|
||||
sndbuf_getsize(c->bufhard), \
|
||||
sndbuf_getblksz(c->bufhard), \
|
||||
sndbuf_getblkcnt(c->bufhard), \
|
||||
sndbuf_getsize(c->bufsoft), \
|
||||
sndbuf_getblksz(c->bufsoft), \
|
||||
sndbuf_getblkcnt(c->bufsoft)); \
|
||||
sbuf_printf(s, "\n\t"); \
|
||||
\
|
||||
sbuf_printf(s, "channel flags=0x%b", c->flags, \
|
||||
CHN_F_BITS); \
|
||||
sbuf_printf(s, "\n\t"); \
|
||||
\
|
||||
sbuf_printf(s, "{%s}", \
|
||||
(c->direction == PCMDIR_REC) ? "hardware" : \
|
||||
"userland"); \
|
||||
sbuf_printf(s, " -> "); \
|
||||
f = c->feeder; \
|
||||
while (f->source != NULL) \
|
||||
f = f->source; \
|
||||
while (f != NULL) { \
|
||||
sbuf_printf(s, "%s", f->class->name); \
|
||||
if (f->desc->type == FEEDER_FORMAT) \
|
||||
sbuf_printf(s, "(0x%08x -> 0x%08x)", \
|
||||
f->desc->in, f->desc->out); \
|
||||
else if (f->desc->type == FEEDER_MATRIX) \
|
||||
sbuf_printf(s, "(%d.%d -> %d.%d)", \
|
||||
AFMT_CHANNEL(f->desc->in) - \
|
||||
AFMT_EXTCHANNEL(f->desc->in), \
|
||||
AFMT_EXTCHANNEL(f->desc->in), \
|
||||
AFMT_CHANNEL(f->desc->out) - \
|
||||
AFMT_EXTCHANNEL(f->desc->out), \
|
||||
AFMT_EXTCHANNEL(f->desc->out)); \
|
||||
else if (f->desc->type == FEEDER_RATE) \
|
||||
sbuf_printf(s, \
|
||||
"(0x%08x q:%d %d -> %d)", \
|
||||
f->desc->out, \
|
||||
FEEDER_GET(f, FEEDRATE_QUALITY), \
|
||||
FEEDER_GET(f, FEEDRATE_SRC), \
|
||||
FEEDER_GET(f, FEEDRATE_DST)); \
|
||||
else \
|
||||
sbuf_printf(s, "(0x%08x)", \
|
||||
f->desc->out); \
|
||||
sbuf_printf(s, " -> "); \
|
||||
f = f->parent; \
|
||||
} \
|
||||
sbuf_printf(s, "{%s}", \
|
||||
(c->direction == PCMDIR_REC) ? "userland" : \
|
||||
"hardware"); \
|
||||
} \
|
||||
\
|
||||
return (0); \
|
||||
} while (0)
|
||||
|
||||
#endif /* !_SND_SNDSTAT_H_ */
|
@ -1,5 +1,7 @@
|
||||
/*-
|
||||
* Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
|
||||
* Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org>
|
||||
* Portions Copyright (c) Ryan Beasley <ryan.beasley@gmail.com> - GSoC 2006
|
||||
* Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org>
|
||||
* Copyright (c) 1997 Luigi Rizzo
|
||||
* All rights reserved.
|
||||
*
|
||||
@ -25,10 +27,15 @@
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
#include <dev/sound/pcm/ac97.h>
|
||||
#include <dev/sound/pcm/vchan.h>
|
||||
#include <dev/sound/pcm/dsp.h>
|
||||
#include <dev/sound/pcm/sndstat.h>
|
||||
#include <dev/sound/version.h>
|
||||
#include <sys/limits.h>
|
||||
#include <sys/sysctl.h>
|
||||
@ -41,10 +48,8 @@ devclass_t pcm_devclass;
|
||||
|
||||
int pcm_veto_load = 1;
|
||||
|
||||
#ifdef USING_DEVFS
|
||||
int snd_unit = -1;
|
||||
TUNABLE_INT("hw.snd.default_unit", &snd_unit);
|
||||
#endif
|
||||
|
||||
static int snd_unit_auto = 0;
|
||||
TUNABLE_INT("hw.snd.default_auto", &snd_unit_auto);
|
||||
@ -66,83 +71,56 @@ SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver");
|
||||
static const char snd_driver_version[] =
|
||||
__XSTRING(SND_DRV_VERSION)"/"MACHINE_ARCH;
|
||||
SYSCTL_STRING(_hw_snd, OID_AUTO, version, CTLFLAG_RD, &snd_driver_version,
|
||||
0, "Driver version/arch");
|
||||
0, "driver version/arch");
|
||||
|
||||
/**
|
||||
* @brief Unit number allocator for syncgroup IDs
|
||||
*/
|
||||
struct unrhdr *pcmsg_unrhdr = NULL;
|
||||
|
||||
static int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose);
|
||||
static int
|
||||
sndstat_prepare_pcm(SNDSTAT_PREPARE_PCM_ARGS)
|
||||
{
|
||||
SNDSTAT_PREPARE_PCM_BEGIN();
|
||||
SNDSTAT_PREPARE_PCM_END();
|
||||
}
|
||||
|
||||
void *
|
||||
snd_mtxcreate(const char *desc, const char *type)
|
||||
{
|
||||
#ifdef USING_MUTEX
|
||||
struct mtx *m;
|
||||
|
||||
m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO);
|
||||
mtx_init(m, desc, type, MTX_DEF);
|
||||
return m;
|
||||
#else
|
||||
return (void *)0xcafebabe;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
snd_mtxfree(void *m)
|
||||
{
|
||||
#ifdef USING_MUTEX
|
||||
struct mtx *mtx = m;
|
||||
|
||||
/* mtx_assert(mtx, MA_OWNED); */
|
||||
mtx_destroy(mtx);
|
||||
free(mtx, M_DEVBUF);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
snd_mtxassert(void *m)
|
||||
{
|
||||
#ifdef USING_MUTEX
|
||||
#ifdef INVARIANTS
|
||||
struct mtx *mtx = m;
|
||||
|
||||
mtx_assert(mtx, MA_OWNED);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
/*
|
||||
void
|
||||
snd_mtxlock(void *m)
|
||||
{
|
||||
#ifdef USING_MUTEX
|
||||
struct mtx *mtx = m;
|
||||
|
||||
mtx_lock(mtx);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
snd_mtxunlock(void *m)
|
||||
{
|
||||
#ifdef USING_MUTEX
|
||||
struct mtx *mtx = m;
|
||||
|
||||
mtx_unlock(mtx);
|
||||
#endif
|
||||
}
|
||||
*/
|
||||
int
|
||||
snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep)
|
||||
{
|
||||
struct snddev_info *d;
|
||||
#ifdef USING_MUTEX
|
||||
|
||||
flags &= INTR_MPSAFE;
|
||||
flags |= INTR_TYPE_AV;
|
||||
#else
|
||||
flags = INTR_TYPE_AV;
|
||||
#endif
|
||||
d = device_get_softc(dev);
|
||||
if (d != NULL && (flags & INTR_MPSAFE))
|
||||
d->flags |= SD_F_MPSAFE;
|
||||
@ -154,26 +132,6 @@ snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand
|
||||
hand, param, cookiep);
|
||||
}
|
||||
|
||||
#ifndef PCM_DEBUG_MTX
|
||||
void
|
||||
pcm_lock(struct snddev_info *d)
|
||||
{
|
||||
snd_mtxlock(d->lock);
|
||||
}
|
||||
|
||||
void
|
||||
pcm_unlock(struct snddev_info *d)
|
||||
{
|
||||
snd_mtxunlock(d->lock);
|
||||
}
|
||||
#endif
|
||||
|
||||
struct pcm_channel *
|
||||
pcm_getfakechan(struct snddev_info *d)
|
||||
{
|
||||
return d->fakechan;
|
||||
}
|
||||
|
||||
static void
|
||||
pcm_clonereset(struct snddev_info *d)
|
||||
{
|
||||
@ -183,20 +141,21 @@ pcm_clonereset(struct snddev_info *d)
|
||||
|
||||
cmax = d->playcount + d->reccount - 1;
|
||||
if (d->pvchancount > 0)
|
||||
cmax += MAX(d->pvchancount, snd_maxautovchans) - 1;
|
||||
cmax += max(d->pvchancount, snd_maxautovchans) - 1;
|
||||
if (d->rvchancount > 0)
|
||||
cmax += MAX(d->rvchancount, snd_maxautovchans) - 1;
|
||||
cmax += max(d->rvchancount, snd_maxautovchans) - 1;
|
||||
if (cmax > PCMMAXCLONE)
|
||||
cmax = PCMMAXCLONE;
|
||||
(void)snd_clone_gc(d->clones);
|
||||
(void)snd_clone_setmaxunit(d->clones, cmax);
|
||||
}
|
||||
|
||||
static int
|
||||
int
|
||||
pcm_setvchans(struct snddev_info *d, int direction, int newcnt, int num)
|
||||
{
|
||||
struct pcm_channel *c, *ch, *nch;
|
||||
int err, vcnt;
|
||||
struct pcmchan_caps *caps;
|
||||
int i, err, vcnt;
|
||||
|
||||
PCM_BUSYASSERT(d);
|
||||
|
||||
@ -228,9 +187,33 @@ pcm_setvchans(struct snddev_info *d, int direction, int newcnt, int num)
|
||||
CHN_LOCK(c);
|
||||
if (c->direction == direction &&
|
||||
((c->flags & CHN_F_HAS_VCHAN) || (vcnt == 0 &&
|
||||
c->refcount < 1 &&
|
||||
!(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL))))) {
|
||||
ch = c;
|
||||
break;
|
||||
/*
|
||||
* Reuse hw channel with vchans already
|
||||
* created.
|
||||
*/
|
||||
if (c->flags & CHN_F_HAS_VCHAN) {
|
||||
ch = c;
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* No vchans ever created, look for
|
||||
* channels with supported formats.
|
||||
*/
|
||||
caps = chn_getcaps(c);
|
||||
if (caps == NULL) {
|
||||
CHN_UNLOCK(c);
|
||||
continue;
|
||||
}
|
||||
for (i = 0; caps->fmtlist[i] != 0; i++) {
|
||||
if (caps->fmtlist[i] & AFMT_CONVERTIBLE)
|
||||
break;
|
||||
}
|
||||
if (caps->fmtlist[i] != 0) {
|
||||
ch = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
CHN_UNLOCK(c);
|
||||
}
|
||||
@ -267,11 +250,13 @@ pcm_setvchans(struct snddev_info *d, int direction, int newcnt, int num)
|
||||
}
|
||||
CHN_FOREACH_SAFE(ch, c, nch, children) {
|
||||
CHN_LOCK(ch);
|
||||
if (!(ch->flags & CHN_F_BUSY)) {
|
||||
if (vcnt == 1 && c->refcount > 0) {
|
||||
CHN_UNLOCK(ch);
|
||||
CHN_UNLOCK(c);
|
||||
break;
|
||||
}
|
||||
if (!(ch->flags & CHN_F_BUSY) &&
|
||||
ch->refcount < 1) {
|
||||
err = vchan_destroy(ch);
|
||||
CHN_LOCK(c);
|
||||
if (err == 0)
|
||||
vcnt--;
|
||||
} else
|
||||
@ -291,10 +276,10 @@ pcm_setvchans(struct snddev_info *d, int direction, int newcnt, int num)
|
||||
/* return error status and a locked channel */
|
||||
int
|
||||
pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction,
|
||||
pid_t pid, int devunit)
|
||||
pid_t pid, char *comm, int devunit)
|
||||
{
|
||||
struct pcm_channel *c;
|
||||
int err, vchancount;
|
||||
int err, vchancount, vchan_num;
|
||||
|
||||
KASSERT(d != NULL && ch != NULL && (devunit == -1 ||
|
||||
!(devunit & ~(SND_U_MASK | SND_D_MASK | SND_C_MASK))) &&
|
||||
@ -309,35 +294,51 @@ pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction,
|
||||
case SND_DEV_DSPHW_PLAY:
|
||||
case SND_DEV_DSPHW_VPLAY:
|
||||
if (direction != PCMDIR_PLAY)
|
||||
return (EOPNOTSUPP);
|
||||
return (ENOTSUP);
|
||||
break;
|
||||
case SND_DEV_DSPHW_REC:
|
||||
case SND_DEV_DSPHW_VREC:
|
||||
if (direction != PCMDIR_REC)
|
||||
return (EOPNOTSUPP);
|
||||
return (ENOTSUP);
|
||||
break;
|
||||
default:
|
||||
if (!(direction == PCMDIR_PLAY ||
|
||||
direction == PCMDIR_REC))
|
||||
return (EOPNOTSUPP);
|
||||
return (ENOTSUP);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*ch = NULL;
|
||||
vchan_num = 0;
|
||||
vchancount = (direction == PCMDIR_PLAY) ? d->pvchancount :
|
||||
d->rvchancount;
|
||||
|
||||
retry_chnalloc:
|
||||
err = EOPNOTSUPP;
|
||||
err = ENOTSUP;
|
||||
/* scan for a free channel */
|
||||
CHN_FOREACH(c, d, channels.pcm) {
|
||||
CHN_LOCK(c);
|
||||
if (devunit == -1 && c->direction == direction &&
|
||||
(c->flags & CHN_F_VIRTUAL)) {
|
||||
if (vchancount < snd_maxautovchans &&
|
||||
vchan_num < CHN_CHAN(c)) {
|
||||
CHN_UNLOCK(c);
|
||||
goto vchan_alloc;
|
||||
}
|
||||
vchan_num++;
|
||||
}
|
||||
if (c->direction == direction && !(c->flags & CHN_F_BUSY) &&
|
||||
(devunit == -1 || devunit == -2 || c->unit == devunit)) {
|
||||
c->flags |= CHN_F_BUSY;
|
||||
c->pid = pid;
|
||||
strlcpy(c->comm, (comm != NULL) ? comm :
|
||||
CHN_COMM_UNKNOWN, sizeof(c->comm));
|
||||
*ch = c;
|
||||
return (0);
|
||||
} else if (c->unit == devunit) {
|
||||
if (c->direction != direction)
|
||||
err = EOPNOTSUPP;
|
||||
err = ENOTSUP;
|
||||
else if (c->flags & CHN_F_BUSY)
|
||||
err = EBUSY;
|
||||
else
|
||||
@ -353,13 +354,10 @@ pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction,
|
||||
if (devunit == -2)
|
||||
return (err);
|
||||
|
||||
vchan_alloc:
|
||||
/* no channel available */
|
||||
if (devunit == -1 || snd_unit2d(devunit) == SND_DEV_DSPHW_VPLAY ||
|
||||
snd_unit2d(devunit) == SND_DEV_DSPHW_VREC) {
|
||||
if (direction == PCMDIR_PLAY)
|
||||
vchancount = d->pvchancount;
|
||||
else
|
||||
vchancount = d->rvchancount;
|
||||
if (!(vchancount > 0 && vchancount < snd_maxautovchans) &&
|
||||
(devunit == -1 || snd_unit2c(devunit) < snd_maxautovchans))
|
||||
return (err);
|
||||
@ -384,6 +382,7 @@ pcm_chnrelease(struct pcm_channel *c)
|
||||
|
||||
c->flags &= ~CHN_F_BUSY;
|
||||
c->pid = -1;
|
||||
strlcpy(c->comm, CHN_COMM_UNUSED, sizeof(c->comm));
|
||||
CHN_UNLOCK(c);
|
||||
|
||||
return (0);
|
||||
@ -403,7 +402,7 @@ pcm_chnref(struct pcm_channel *c, int ref)
|
||||
int
|
||||
pcm_inprog(struct snddev_info *d, int delta)
|
||||
{
|
||||
snd_mtxassert(d->lock);
|
||||
PCM_LOCKASSERT(d);
|
||||
|
||||
d->inprog += delta;
|
||||
|
||||
@ -431,7 +430,6 @@ pcm_setmaxautovchans(struct snddev_info *d, int num)
|
||||
pcm_clonereset(d);
|
||||
}
|
||||
|
||||
#ifdef USING_DEVFS
|
||||
static int
|
||||
sysctl_hw_snd_default_unit(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
@ -451,7 +449,6 @@ sysctl_hw_snd_default_unit(SYSCTL_HANDLER_ARGS)
|
||||
/* XXX: do we need a way to let the user change the default unit? */
|
||||
SYSCTL_PROC(_hw_snd, OID_AUTO, default_unit, CTLTYPE_INT | CTLFLAG_RW,
|
||||
0, sizeof(int), sysctl_hw_snd_default_unit, "I", "default sound device");
|
||||
#endif
|
||||
|
||||
static int
|
||||
sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)
|
||||
@ -491,7 +488,7 @@ pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t c
|
||||
char *dirs, *devname, buf[CHN_NAMELEN];
|
||||
|
||||
PCM_BUSYASSERT(d);
|
||||
snd_mtxassert(d->lock);
|
||||
PCM_LOCKASSERT(d);
|
||||
KASSERT(num >= -1, ("invalid num=%d", num));
|
||||
|
||||
|
||||
@ -570,11 +567,12 @@ pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t c
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
pcm_unlock(d);
|
||||
PCM_UNLOCK(d);
|
||||
ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
|
||||
ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK | M_ZERO);
|
||||
ch->unit = udc;
|
||||
ch->pid = -1;
|
||||
strlcpy(ch->comm, CHN_COMM_UNUSED, sizeof(ch->comm));
|
||||
ch->parentsnddev = d;
|
||||
ch->parentchannel = parent;
|
||||
ch->dev = d->dev;
|
||||
@ -583,7 +581,7 @@ pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t c
|
||||
device_get_nameunit(ch->dev), dirs, devname);
|
||||
|
||||
err = chn_init(ch, devinfo, dir, direction);
|
||||
pcm_lock(d);
|
||||
PCM_LOCK(d);
|
||||
if (err) {
|
||||
device_printf(d->dev, "chn_init(%s) failed: err = %d\n",
|
||||
ch->name, err);
|
||||
@ -620,46 +618,12 @@ pcm_chn_destroy(struct pcm_channel *ch)
|
||||
int
|
||||
pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch)
|
||||
{
|
||||
struct pcm_channel *tmp, *after;
|
||||
int num;
|
||||
|
||||
PCM_BUSYASSERT(d);
|
||||
snd_mtxassert(d->lock);
|
||||
PCM_LOCKASSERT(d);
|
||||
KASSERT(ch != NULL && (ch->direction == PCMDIR_PLAY ||
|
||||
ch->direction == PCMDIR_REC), ("Invalid pcm channel"));
|
||||
|
||||
after = NULL;
|
||||
tmp = NULL;
|
||||
num = 0;
|
||||
|
||||
/*
|
||||
* Look for possible device collision.
|
||||
*/
|
||||
CHN_FOREACH(tmp, d, channels.pcm) {
|
||||
if (tmp->unit == ch->unit) {
|
||||
device_printf(d->dev, "%s(): Device collision "
|
||||
"old=%p new=%p devunit=0x%08x\n",
|
||||
__func__, tmp, ch, ch->unit);
|
||||
return (ENODEV);
|
||||
}
|
||||
if (CHN_DEV(tmp) < CHN_DEV(ch)) {
|
||||
if (num == 0)
|
||||
after = tmp;
|
||||
continue;
|
||||
} else if (CHN_DEV(tmp) > CHN_DEV(ch))
|
||||
break;
|
||||
num++;
|
||||
if (CHN_CHAN(tmp) < CHN_CHAN(ch))
|
||||
after = tmp;
|
||||
else if (CHN_CHAN(tmp) > CHN_CHAN(ch))
|
||||
break;
|
||||
}
|
||||
|
||||
if (after != NULL) {
|
||||
CHN_INSERT_AFTER(after, ch, channels.pcm);
|
||||
} else {
|
||||
CHN_INSERT_HEAD(d, ch, channels.pcm);
|
||||
}
|
||||
CHN_INSERT_SORT_ASCEND(d, ch, channels.pcm);
|
||||
|
||||
switch (CHN_DEV(ch)) {
|
||||
case SND_DEV_DSPHW_PLAY:
|
||||
@ -689,7 +653,7 @@ pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch)
|
||||
struct pcm_channel *tmp;
|
||||
|
||||
PCM_BUSYASSERT(d);
|
||||
snd_mtxassert(d->lock);
|
||||
PCM_LOCKASSERT(d);
|
||||
|
||||
tmp = NULL;
|
||||
|
||||
@ -734,17 +698,17 @@ pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
|
||||
|
||||
PCM_BUSYASSERT(d);
|
||||
|
||||
pcm_lock(d);
|
||||
PCM_LOCK(d);
|
||||
ch = pcm_chn_create(d, NULL, cls, dir, -1, devinfo);
|
||||
if (!ch) {
|
||||
device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n",
|
||||
cls->name, dir, devinfo);
|
||||
pcm_unlock(d);
|
||||
PCM_UNLOCK(d);
|
||||
return (ENODEV);
|
||||
}
|
||||
|
||||
err = pcm_chn_add(d, ch);
|
||||
pcm_unlock(d);
|
||||
PCM_UNLOCK(d);
|
||||
if (err) {
|
||||
device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n",
|
||||
ch->name, err);
|
||||
@ -765,9 +729,9 @@ pcm_killchan(device_t dev)
|
||||
|
||||
ch = CHN_FIRST(d, channels.pcm);
|
||||
|
||||
pcm_lock(d);
|
||||
PCM_LOCK(d);
|
||||
error = pcm_chn_remove(d, ch);
|
||||
pcm_unlock(d);
|
||||
PCM_UNLOCK(d);
|
||||
if (error)
|
||||
return (error);
|
||||
return (pcm_chn_destroy(ch));
|
||||
@ -793,7 +757,7 @@ pcm_setstatus(device_t dev, char *str)
|
||||
|
||||
strlcpy(d->status, str, SND_STATUSLEN);
|
||||
|
||||
pcm_lock(d);
|
||||
PCM_LOCK(d);
|
||||
|
||||
/* Last stage, enable cloning. */
|
||||
if (d->clones != NULL)
|
||||
@ -804,7 +768,7 @@ pcm_setstatus(device_t dev, char *str)
|
||||
|
||||
PCM_RELEASE(d);
|
||||
|
||||
pcm_unlock(d);
|
||||
PCM_UNLOCK(d);
|
||||
|
||||
if (snd_unit < 0 || snd_unit_auto != 0)
|
||||
snd_unit = device_get_unit(dev);
|
||||
@ -866,7 +830,44 @@ pcm_getbuffersize(device_t dev, unsigned int minbufsz, unsigned int deflt, unsig
|
||||
return sz;
|
||||
}
|
||||
|
||||
#if defined(SND_DYNSYSCTL) && defined(SND_DEBUG)
|
||||
static int
|
||||
sysctl_dev_pcm_bitperfect(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
struct snddev_info *d;
|
||||
int err, val;
|
||||
|
||||
d = oidp->oid_arg1;
|
||||
if (!PCM_REGISTERED(d))
|
||||
return (ENODEV);
|
||||
|
||||
PCM_LOCK(d);
|
||||
PCM_WAIT(d);
|
||||
val = (d->flags & SD_F_BITPERFECT) ? 1 : 0;
|
||||
PCM_ACQUIRE(d);
|
||||
PCM_UNLOCK(d);
|
||||
|
||||
err = sysctl_handle_int(oidp, &val, 0, req);
|
||||
|
||||
if (err == 0 && req->newptr != NULL) {
|
||||
if (!(val == 0 || val == 1)) {
|
||||
PCM_RELEASE_QUICK(d);
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
PCM_LOCK(d);
|
||||
|
||||
d->flags &= ~SD_F_BITPERFECT;
|
||||
d->flags |= (val != 0) ? SD_F_BITPERFECT : 0;
|
||||
|
||||
PCM_RELEASE(d);
|
||||
PCM_UNLOCK(d);
|
||||
} else
|
||||
PCM_RELEASE_QUICK(d);
|
||||
|
||||
return (err);
|
||||
}
|
||||
|
||||
#ifdef SND_DEBUG
|
||||
static int
|
||||
sysctl_dev_pcm_clone_flags(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
@ -981,6 +982,7 @@ int
|
||||
pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
|
||||
{
|
||||
struct snddev_info *d;
|
||||
int i;
|
||||
|
||||
if (pcm_veto_load) {
|
||||
device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load);
|
||||
@ -1010,6 +1012,15 @@ pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
|
||||
*/
|
||||
d->flags = 0;
|
||||
#endif
|
||||
i = 0;
|
||||
if (resource_int_value(device_get_name(dev), device_get_unit(dev),
|
||||
"vpc", &i) != 0 || i != 0)
|
||||
d->flags |= SD_F_VPC;
|
||||
|
||||
if (resource_int_value(device_get_name(dev), device_get_unit(dev),
|
||||
"bitperfect", &i) == 0 && i != 0)
|
||||
d->flags |= SD_F_BITPERFECT;
|
||||
|
||||
d->devinfo = devinfo;
|
||||
d->devcount = 0;
|
||||
d->reccount = 0;
|
||||
@ -1024,7 +1035,7 @@ pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
|
||||
|
||||
/*
|
||||
* Create clone manager, disabled by default. Cloning will be
|
||||
* enabled during final stage of driver iniialization through
|
||||
* enabled during final stage of driver initialization through
|
||||
* pcm_setstatus().
|
||||
*/
|
||||
d->clones = snd_clone_create(SND_U_MASK | SND_D_MASK, PCMMAXCLONE,
|
||||
@ -1041,15 +1052,12 @@ pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
|
||||
|
||||
CHN_INIT(d, channels.pcm);
|
||||
CHN_INIT(d, channels.pcm.busy);
|
||||
CHN_INIT(d, channels.pcm.opened);
|
||||
|
||||
/* XXX This is incorrect, but lets play along for now. */
|
||||
if ((numplay == 0 || numrec == 0) && numplay != numrec)
|
||||
d->flags |= SD_F_SIMPLEX;
|
||||
|
||||
d->fakechan = fkchan_setup(dev);
|
||||
chn_init(d->fakechan, NULL, 0, 0);
|
||||
|
||||
#ifdef SND_DYNSYSCTL
|
||||
sysctl_ctx_init(&d->play_sysctl_ctx);
|
||||
d->play_sysctl_tree = SYSCTL_ADD_NODE(&d->play_sysctl_ctx,
|
||||
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "play",
|
||||
@ -1063,6 +1071,11 @@ pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
|
||||
SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
|
||||
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
|
||||
OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "allocated buffer size");
|
||||
SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
|
||||
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
|
||||
"bitperfect", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d),
|
||||
sysctl_dev_pcm_bitperfect, "I",
|
||||
"bit-perfect playback/recording (0=disable, 1=enable)");
|
||||
#ifdef SND_DEBUG
|
||||
SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
|
||||
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
|
||||
@ -1079,7 +1092,6 @@ pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
|
||||
"clone_gc", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d),
|
||||
sysctl_dev_pcm_clone_gc, "I",
|
||||
"clone garbage collector");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (numplay > 0 || numrec > 0) {
|
||||
@ -1087,6 +1099,9 @@ pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
|
||||
vchan_initsys(dev);
|
||||
}
|
||||
|
||||
if (d->flags & SD_F_EQ)
|
||||
feeder_eq_initsys(dev);
|
||||
|
||||
sndstat_register(dev, d->status, sndstat_prepare_pcm);
|
||||
|
||||
return 0;
|
||||
@ -1113,18 +1128,18 @@ pcm_unregister(device_t dev)
|
||||
return (EBUSY);
|
||||
}
|
||||
|
||||
pcm_lock(d);
|
||||
PCM_LOCK(d);
|
||||
PCM_WAIT(d);
|
||||
|
||||
if (d->inprog != 0) {
|
||||
device_printf(dev, "unregister: operation in progress\n");
|
||||
pcm_unlock(d);
|
||||
PCM_UNLOCK(d);
|
||||
sndstat_release(td);
|
||||
return (EBUSY);
|
||||
}
|
||||
|
||||
PCM_ACQUIRE(d);
|
||||
pcm_unlock(d);
|
||||
PCM_UNLOCK(d);
|
||||
|
||||
CHN_FOREACH(ch, d, channels.pcm) {
|
||||
CHN_LOCK(ch);
|
||||
@ -1147,27 +1162,27 @@ pcm_unregister(device_t dev)
|
||||
sndstat_release(td);
|
||||
return (EBUSY);
|
||||
} else {
|
||||
pcm_lock(d);
|
||||
PCM_LOCK(d);
|
||||
(void)snd_clone_disable(d->clones);
|
||||
pcm_unlock(d);
|
||||
PCM_UNLOCK(d);
|
||||
}
|
||||
}
|
||||
|
||||
if (mixer_uninit(dev) == EBUSY) {
|
||||
device_printf(dev, "unregister: mixer busy\n");
|
||||
pcm_lock(d);
|
||||
PCM_LOCK(d);
|
||||
if (d->clones != NULL)
|
||||
(void)snd_clone_enable(d->clones);
|
||||
PCM_RELEASE(d);
|
||||
pcm_unlock(d);
|
||||
PCM_UNLOCK(d);
|
||||
sndstat_release(td);
|
||||
return (EBUSY);
|
||||
}
|
||||
|
||||
pcm_lock(d);
|
||||
PCM_LOCK(d);
|
||||
d->flags |= SD_F_DYING;
|
||||
d->flags &= ~SD_F_REGISTERED;
|
||||
pcm_unlock(d);
|
||||
PCM_UNLOCK(d);
|
||||
|
||||
/*
|
||||
* No lock being held, so this thing can be flushed without
|
||||
@ -1178,7 +1193,6 @@ pcm_unregister(device_t dev)
|
||||
d->clones = NULL;
|
||||
}
|
||||
|
||||
#ifdef SND_DYNSYSCTL
|
||||
if (d->play_sysctl_tree != NULL) {
|
||||
sysctl_ctx_free(&d->play_sysctl_ctx);
|
||||
d->play_sysctl_tree = NULL;
|
||||
@ -1187,20 +1201,16 @@ pcm_unregister(device_t dev)
|
||||
sysctl_ctx_free(&d->rec_sysctl_ctx);
|
||||
d->rec_sysctl_tree = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
while (!CHN_EMPTY(d, channels.pcm))
|
||||
pcm_killchan(dev);
|
||||
|
||||
chn_kill(d->fakechan);
|
||||
fkchan_kill(d->fakechan);
|
||||
|
||||
dsp_cdevinfo_flush(d);
|
||||
|
||||
pcm_lock(d);
|
||||
PCM_LOCK(d);
|
||||
PCM_RELEASE(d);
|
||||
cv_destroy(&d->cv);
|
||||
pcm_unlock(d);
|
||||
PCM_UNLOCK(d);
|
||||
snd_mtxfree(d->lock);
|
||||
sndstat_unregister(dev);
|
||||
sndstat_release(td);
|
||||
@ -1228,162 +1238,6 @@ pcm_unregister(device_t dev)
|
||||
|
||||
/************************************************************************/
|
||||
|
||||
static int
|
||||
sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
|
||||
{
|
||||
struct snddev_info *d;
|
||||
struct pcm_channel *c;
|
||||
struct pcm_feeder *f;
|
||||
|
||||
if (verbose < 1)
|
||||
return 0;
|
||||
|
||||
d = device_get_softc(dev);
|
||||
if (!d)
|
||||
return ENXIO;
|
||||
|
||||
PCM_BUSYASSERT(d);
|
||||
|
||||
if (CHN_EMPTY(d, channels.pcm)) {
|
||||
sbuf_printf(s, " (mixer only)");
|
||||
return 0;
|
||||
}
|
||||
|
||||
sbuf_printf(s, " (%dp:%dv/%dr:%dv channels%s%s)",
|
||||
d->playcount, d->pvchancount,
|
||||
d->reccount, d->rvchancount,
|
||||
(d->flags & SD_F_SIMPLEX)? "" : " duplex",
|
||||
#ifdef USING_DEVFS
|
||||
(device_get_unit(dev) == snd_unit)? " default" : ""
|
||||
#else
|
||||
""
|
||||
#endif
|
||||
);
|
||||
|
||||
if (verbose <= 1)
|
||||
return 0;
|
||||
|
||||
CHN_FOREACH(c, d, channels.pcm) {
|
||||
|
||||
KASSERT(c->bufhard != NULL && c->bufsoft != NULL,
|
||||
("hosed pcm channel setup"));
|
||||
|
||||
sbuf_printf(s, "\n\t");
|
||||
|
||||
/* it would be better to indent child channels */
|
||||
sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name);
|
||||
sbuf_printf(s, "spd %d", c->speed);
|
||||
if (c->speed != sndbuf_getspd(c->bufhard))
|
||||
sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard));
|
||||
sbuf_printf(s, ", fmt 0x%08x", c->format);
|
||||
if (c->format != sndbuf_getfmt(c->bufhard))
|
||||
sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard));
|
||||
sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags);
|
||||
if (c->pid != -1)
|
||||
sbuf_printf(s, ", pid %d", c->pid);
|
||||
sbuf_printf(s, "\n\t");
|
||||
|
||||
sbuf_printf(s, "interrupts %d, ", c->interrupts);
|
||||
if (c->direction == PCMDIR_REC)
|
||||
sbuf_printf(s, "overruns %d, feed %u, hfree %d, sfree %d [b:%d/%d/%d|bs:%d/%d/%d]",
|
||||
c->xruns, c->feedcount, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft),
|
||||
sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard),
|
||||
sndbuf_getblkcnt(c->bufhard),
|
||||
sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft),
|
||||
sndbuf_getblkcnt(c->bufsoft));
|
||||
else
|
||||
sbuf_printf(s, "underruns %d, feed %u, ready %d [b:%d/%d/%d|bs:%d/%d/%d]",
|
||||
c->xruns, c->feedcount, sndbuf_getready(c->bufsoft),
|
||||
sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard),
|
||||
sndbuf_getblkcnt(c->bufhard),
|
||||
sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft),
|
||||
sndbuf_getblkcnt(c->bufsoft));
|
||||
sbuf_printf(s, "\n\t");
|
||||
|
||||
sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland");
|
||||
sbuf_printf(s, " -> ");
|
||||
f = c->feeder;
|
||||
while (f->source != NULL)
|
||||
f = f->source;
|
||||
while (f != NULL) {
|
||||
sbuf_printf(s, "%s", f->class->name);
|
||||
if (f->desc->type == FEEDER_FMT)
|
||||
sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out);
|
||||
if (f->desc->type == FEEDER_RATE)
|
||||
sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST));
|
||||
if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER ||
|
||||
f->desc->type == FEEDER_VOLUME)
|
||||
sbuf_printf(s, "(0x%08x)", f->desc->out);
|
||||
sbuf_printf(s, " -> ");
|
||||
f = f->parent;
|
||||
}
|
||||
sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
|
||||
#ifdef SND_DYNSYSCTL
|
||||
int
|
||||
sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
struct snddev_info *d;
|
||||
int direction, vchancount;
|
||||
int err, cnt;
|
||||
|
||||
d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
|
||||
if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN))
|
||||
return (EINVAL);
|
||||
|
||||
pcm_lock(d);
|
||||
PCM_WAIT(d);
|
||||
|
||||
switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
|
||||
case VCHAN_PLAY:
|
||||
direction = PCMDIR_PLAY;
|
||||
vchancount = d->pvchancount;
|
||||
cnt = d->playcount;
|
||||
break;
|
||||
case VCHAN_REC:
|
||||
direction = PCMDIR_REC;
|
||||
vchancount = d->rvchancount;
|
||||
cnt = d->reccount;
|
||||
break;
|
||||
default:
|
||||
pcm_unlock(d);
|
||||
return (EINVAL);
|
||||
break;
|
||||
}
|
||||
|
||||
if (cnt < 1) {
|
||||
pcm_unlock(d);
|
||||
return (ENODEV);
|
||||
}
|
||||
|
||||
PCM_ACQUIRE(d);
|
||||
pcm_unlock(d);
|
||||
|
||||
cnt = vchancount;
|
||||
err = sysctl_handle_int(oidp, &cnt, 0, req);
|
||||
|
||||
if (err == 0 && req->newptr != NULL && vchancount != cnt) {
|
||||
if (cnt < 0)
|
||||
cnt = 0;
|
||||
if (cnt > SND_MAXVCHANS)
|
||||
cnt = SND_MAXVCHANS;
|
||||
err = pcm_setvchans(d, direction, cnt, -1);
|
||||
}
|
||||
|
||||
PCM_RELEASE_QUICK(d);
|
||||
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
/************************************************************************/
|
||||
|
||||
/**
|
||||
* @brief Handle OSSv4 SNDCTL_SYSINFO ioctl.
|
||||
*
|
||||
@ -1438,14 +1292,14 @@ sound_oss_sysinfo(oss_sysinfo *si)
|
||||
/* XXX Need Giant magic entry ??? */
|
||||
|
||||
/* See note in function's docblock */
|
||||
mtx_assert(d->lock, MA_NOTOWNED);
|
||||
pcm_lock(d);
|
||||
PCM_UNLOCKASSERT(d);
|
||||
PCM_LOCK(d);
|
||||
|
||||
si->numaudios += d->devcount;
|
||||
++ncards;
|
||||
|
||||
CHN_FOREACH(c, d, channels.pcm) {
|
||||
mtx_assert(c->lock, MA_NOTOWNED);
|
||||
CHN_UNLOCKASSERT(c);
|
||||
CHN_LOCK(c);
|
||||
if (c->flags & CHN_F_BUSY)
|
||||
si->openedaudio[j / intnbits] |=
|
||||
@ -1454,7 +1308,7 @@ sound_oss_sysinfo(oss_sysinfo *si)
|
||||
j++;
|
||||
}
|
||||
|
||||
pcm_unlock(d);
|
||||
PCM_UNLOCK(d);
|
||||
}
|
||||
si->numaudioengines = si->numaudios;
|
||||
|
||||
@ -1510,8 +1364,8 @@ sound_oss_card_info(oss_card_info *si)
|
||||
if (ncards++ != si->card)
|
||||
continue;
|
||||
|
||||
mtx_assert(d->lock, MA_NOTOWNED);
|
||||
pcm_lock(d);
|
||||
PCM_UNLOCKASSERT(d);
|
||||
PCM_LOCK(d);
|
||||
|
||||
strlcpy(si->shortname, device_get_nameunit(d->dev),
|
||||
sizeof(si->shortname));
|
||||
@ -1519,7 +1373,9 @@ sound_oss_card_info(oss_card_info *si)
|
||||
sizeof(si->longname));
|
||||
strlcpy(si->hw_info, d->status, sizeof(si->hw_info));
|
||||
si->intr_count = si->ack_count = 0;
|
||||
pcm_unlock(d);
|
||||
|
||||
PCM_UNLOCK(d);
|
||||
|
||||
return (0);
|
||||
}
|
||||
return (ENXIO);
|
||||
@ -1551,7 +1407,7 @@ sound_modevent(module_t mod, int type, void *data)
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ret = EOPNOTSUPP;
|
||||
ret = ENOTSUP;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@ -1,5 +1,6 @@
|
||||
/*-
|
||||
* Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
|
||||
* Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org>
|
||||
* Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org>
|
||||
* Copyright (c) 1995 Hannu Savolainen
|
||||
* All rights reserved.
|
||||
*
|
||||
@ -65,32 +66,32 @@
|
||||
#include <sys/soundcard.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/kobj.h>
|
||||
#ifdef SND_DEBUG
|
||||
#undef KOBJMETHOD
|
||||
#define KOBJMETHOD(NAME, FUNC) \
|
||||
{ \
|
||||
&NAME##_desc, \
|
||||
(kobjop_t) ((FUNC != (NAME##_t *)NULL) ? FUNC : NULL) \
|
||||
}
|
||||
#endif
|
||||
#ifndef KOBJMETHOD_END
|
||||
#define KOBJMETHOD_END { NULL, NULL }
|
||||
#endif
|
||||
#include <vm/vm.h>
|
||||
#include <vm/pmap.h>
|
||||
|
||||
#undef USING_MUTEX
|
||||
#undef USING_DEVFS
|
||||
|
||||
#if __FreeBSD_version > 500000
|
||||
#include <sys/lock.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/condvar.h>
|
||||
|
||||
#define USING_MUTEX
|
||||
#define USING_DEVFS
|
||||
#else
|
||||
#define INTR_TYPE_AV INTR_TYPE_TTY
|
||||
#define INTR_MPSAFE 0
|
||||
#endif
|
||||
|
||||
#define SND_DYNSYSCTL
|
||||
|
||||
struct pcm_channel;
|
||||
struct pcm_feeder;
|
||||
struct snd_dbuf;
|
||||
struct snd_mixer;
|
||||
|
||||
#include <dev/sound/pcm/buffer.h>
|
||||
#include <dev/sound/pcm/matrix.h>
|
||||
#include <dev/sound/pcm/matrix_map.h>
|
||||
#include <dev/sound/pcm/channel.h>
|
||||
#include <dev/sound/pcm/feeder.h>
|
||||
#include <dev/sound/pcm/mixer.h>
|
||||
@ -98,11 +99,11 @@ struct snd_mixer;
|
||||
#include <dev/sound/clone.h>
|
||||
#include <dev/sound/unit.h>
|
||||
|
||||
#define PCM_SOFTC_SIZE 512
|
||||
#define PCM_SOFTC_SIZE (sizeof(struct snddev_info))
|
||||
|
||||
#define SND_STATUSLEN 64
|
||||
|
||||
#define SOUND_MODVER 2
|
||||
#define SOUND_MODVER 5
|
||||
|
||||
#define SOUND_MINVER SOUND_MODVER
|
||||
#define SOUND_PREFVER SOUND_MODVER
|
||||
@ -125,6 +126,13 @@ struct snd_mixer;
|
||||
#define PCMDEV(x) (snd_unit2d(dev2unit(x)))
|
||||
#define PCMCHAN(x) (snd_unit2c(dev2unit(x)))
|
||||
|
||||
/* XXX unit2minor compat */
|
||||
#if __FreeBSD_version >= 800062
|
||||
#define PCMMINOR(x) (x)
|
||||
#else
|
||||
#define PCMMINOR(x) unit2minor(x)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* By design, limit possible channels for each direction.
|
||||
*/
|
||||
@ -134,13 +142,28 @@ struct snd_mixer;
|
||||
#define SD_F_SIMPLEX 0x00000001
|
||||
#define SD_F_AUTOVCHAN 0x00000002
|
||||
#define SD_F_SOFTPCMVOL 0x00000004
|
||||
/*
|
||||
* Obsolete due to better matrixing
|
||||
*/
|
||||
#if 0
|
||||
#define SD_F_PSWAPLR 0x00000008
|
||||
#define SD_F_RSWAPLR 0x00000010
|
||||
#define SD_F_DYING 0x00000020
|
||||
#define SD_F_SUICIDE 0x00000040
|
||||
#define SD_F_BUSY 0x00000080
|
||||
#define SD_F_MPSAFE 0x00000100
|
||||
#define SD_F_REGISTERED 0x00000200
|
||||
#endif
|
||||
#define SD_F_DYING 0x00000008
|
||||
#define SD_F_SUICIDE 0x00000010
|
||||
#define SD_F_BUSY 0x00000020
|
||||
#define SD_F_MPSAFE 0x00000040
|
||||
#define SD_F_REGISTERED 0x00000080
|
||||
#define SD_F_BITPERFECT 0x00000100
|
||||
#define SD_F_VPC 0x00000200 /* volume-per-channel */
|
||||
#define SD_F_EQ 0x00000400 /* EQ */
|
||||
#define SD_F_EQ_ENABLED 0x00000800 /* EQ enabled */
|
||||
#define SD_F_EQ_BYPASSED 0x00001000 /* EQ bypassed */
|
||||
#define SD_F_EQ_PC 0x00002000 /* EQ per-channel */
|
||||
|
||||
#define SD_F_EQ_DEFAULT (SD_F_EQ | SD_F_EQ_ENABLED)
|
||||
#define SD_F_EQ_MASK (SD_F_EQ | SD_F_EQ_ENABLED | \
|
||||
SD_F_EQ_BYPASSED | SD_F_EQ_PC)
|
||||
|
||||
#define SD_F_PRIO_RD 0x10000000
|
||||
#define SD_F_PRIO_WR 0x20000000
|
||||
@ -148,6 +171,25 @@ struct snd_mixer;
|
||||
#define SD_F_DIR_SET 0x40000000
|
||||
#define SD_F_TRANSIENT 0xf0000000
|
||||
|
||||
#define SD_F_BITS "\020" \
|
||||
"\001SIMPLEX" \
|
||||
"\002AUTOVCHAN" \
|
||||
"\003SOFTPCMVOL" \
|
||||
"\004DYING" \
|
||||
"\005SUICIDE" \
|
||||
"\006BUSY" \
|
||||
"\007MPSAFE" \
|
||||
"\010REGISTERED" \
|
||||
"\011BITPERFECT" \
|
||||
"\012VPC" \
|
||||
"\013EQ" \
|
||||
"\014EQ_ENABLED" \
|
||||
"\015EQ_BYPASSED" \
|
||||
"\016EQ_PC" \
|
||||
"\035PRIO_RD" \
|
||||
"\036PRIO_WR" \
|
||||
"\037DIR_SET"
|
||||
|
||||
#define PCM_ALIVE(x) ((x) != NULL && (x)->lock != NULL && \
|
||||
!((x)->flags & SD_F_DYING))
|
||||
#define PCM_REGISTERED(x) (PCM_ALIVE(x) && \
|
||||
@ -158,293 +200,97 @@ struct snd_mixer;
|
||||
(((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)
|
||||
#define AFMT_16BIT (AFMT_S16_LE | AFMT_S16_BE | AFMT_U16_LE | AFMT_U16_BE)
|
||||
#define AFMT_8BIT (AFMT_MU_LAW | AFMT_A_LAW | AFMT_U8 | AFMT_S8)
|
||||
#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 | \
|
||||
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)
|
||||
|
||||
struct pcm_channel *fkchan_setup(device_t dev);
|
||||
int fkchan_kill(struct pcm_channel *c);
|
||||
#define AFMT_CONVERTIBLE (AFMT_8BIT | AFMT_16BIT | AFMT_24BIT | \
|
||||
AFMT_32BIT)
|
||||
|
||||
/* Supported vchan mixing formats */
|
||||
#define AFMT_VCHAN (AFMT_CONVERTIBLE & ~AFMT_G711)
|
||||
|
||||
#define AFMT_PASSTHROUGH AFMT_AC3
|
||||
#define AFMT_PASSTHROUGH_RATE 48000
|
||||
#define AFMT_PASSTHROUGH_CHANNEL 2
|
||||
#define AFMT_PASSTHROUGH_EXTCHANNEL 0
|
||||
|
||||
/*
|
||||
* We're simply using unused, contiguous bits from various AFMT_ definitions.
|
||||
* ~(0xb00ff7ff)
|
||||
*/
|
||||
#define AFMT_ENCODING_MASK 0xf00fffff
|
||||
#define AFMT_CHANNEL_MASK 0x01f00000
|
||||
#define AFMT_CHANNEL_SHIFT 20
|
||||
#define AFMT_EXTCHANNEL_MASK 0x0e000000
|
||||
#define AFMT_EXTCHANNEL_SHIFT 25
|
||||
|
||||
#define AFMT_ENCODING(v) ((v) & AFMT_ENCODING_MASK)
|
||||
|
||||
#define AFMT_EXTCHANNEL(v) (((v) & AFMT_EXTCHANNEL_MASK) >> \
|
||||
AFMT_EXTCHANNEL_SHIFT)
|
||||
|
||||
#define AFMT_CHANNEL(v) (((v) & AFMT_CHANNEL_MASK) >> \
|
||||
AFMT_CHANNEL_SHIFT)
|
||||
|
||||
#define AFMT_BIT(v) (((v) & AFMT_32BIT) ? 32 : \
|
||||
(((v) & AFMT_24BIT) ? 24 : \
|
||||
((((v) & AFMT_16BIT) || \
|
||||
((v) & AFMT_PASSTHROUGH)) ? 16 : 8)))
|
||||
|
||||
#define AFMT_BPS(v) (AFMT_BIT(v) >> 3)
|
||||
#define AFMT_ALIGN(v) (AFMT_BPS(v) * AFMT_CHANNEL(v))
|
||||
|
||||
#define SND_FORMAT(f, c, e) (AFMT_ENCODING(f) | \
|
||||
(((c) << AFMT_CHANNEL_SHIFT) & \
|
||||
AFMT_CHANNEL_MASK) | \
|
||||
(((e) << AFMT_EXTCHANNEL_SHIFT) & \
|
||||
AFMT_EXTCHANNEL_MASK))
|
||||
|
||||
#define AFMT_U8_NE AFMT_U8
|
||||
#define AFMT_S8_NE AFMT_S8
|
||||
|
||||
#undef AFMT_S16_NE
|
||||
|
||||
#if BYTE_ORDER == LITTLE_ENDIAN
|
||||
#define AFMT_S16_NE AFMT_S16_LE
|
||||
#define AFMT_S24_NE AFMT_S24_LE
|
||||
#define AFMT_S32_NE AFMT_S32_LE
|
||||
#define AFMT_U16_NE AFMT_U16_LE
|
||||
#define AFMT_U24_NE AFMT_U24_LE
|
||||
#define AFMT_U32_NE AFMT_U32_LE
|
||||
#define AFMT_S16_OE AFMT_S16_BE
|
||||
#define AFMT_S24_OE AFMT_S24_BE
|
||||
#define AFMT_S32_OE AFMT_S32_BE
|
||||
#define AFMT_U16_OE AFMT_U16_BE
|
||||
#define AFMT_U24_OE AFMT_U24_BE
|
||||
#define AFMT_U32_OE AFMT_U32_BE
|
||||
#else
|
||||
#define AFMT_S16_OE AFMT_S16_LE
|
||||
#define AFMT_S24_OE AFMT_S24_LE
|
||||
#define AFMT_S32_OE AFMT_S32_LE
|
||||
#define AFMT_U16_OE AFMT_U16_LE
|
||||
#define AFMT_U24_OE AFMT_U24_LE
|
||||
#define AFMT_U32_OE AFMT_U32_LE
|
||||
#define AFMT_S16_NE AFMT_S16_BE
|
||||
#define AFMT_S24_NE AFMT_S24_BE
|
||||
#define AFMT_S32_NE AFMT_S32_BE
|
||||
#define AFMT_U16_NE AFMT_U16_BE
|
||||
#define AFMT_U24_NE AFMT_U24_BE
|
||||
#define AFMT_U32_NE AFMT_U32_BE
|
||||
#endif
|
||||
|
||||
#define AFMT_SIGNED_NE (AFMT_S8_NE | AFMT_S16_NE | AFMT_S24_NE | AFMT_S32_NE)
|
||||
|
||||
#define AFMT_NE (AFMT_SIGNED_NE | AFMT_U8_NE | AFMT_U16_NE | \
|
||||
AFMT_U24_NE | AFMT_U32_NE)
|
||||
|
||||
/*
|
||||
* Minor numbers for the sound driver.
|
||||
@ -475,9 +321,18 @@ int fkchan_kill(struct pcm_channel *c);
|
||||
|
||||
#define SND_DEV_DSPHW_CD 15 /* s16le/stereo 44100Hz CD */
|
||||
|
||||
#define SND_DEV_DSP_MMAP 16 /* OSSv4 compatible /dev/dsp_mmap */
|
||||
/*
|
||||
* OSSv4 compatible device. For now, it serve no purpose and
|
||||
* the cloning itself will forward the request to ordinary /dev/dsp
|
||||
* instead.
|
||||
*/
|
||||
#define SND_DEV_DSP_MMAP 16 /* /dev/dsp_mmap */
|
||||
#define SND_DEV_DSP_AC3 17 /* /dev/dsp_ac3 */
|
||||
#define SND_DEV_DSP_MULTICH 18 /* /dev/dsp_multich */
|
||||
#define SND_DEV_DSP_SPDIFOUT 19 /* /dev/dsp_spdifout */
|
||||
#define SND_DEV_DSP_SPDIFIN 20 /* /dev/dsp_spdifin */
|
||||
|
||||
#define SND_DEV_LAST SND_DEV_DSP_MMAP
|
||||
#define SND_DEV_LAST SND_DEV_DSP_SPDIFIN
|
||||
#define SND_DEV_MAX PCMMAXDEV
|
||||
|
||||
#define DSP_DEFAULT_SPEED 8000
|
||||
@ -505,8 +360,9 @@ extern struct unrhdr *pcmsg_unrhdr;
|
||||
|
||||
SYSCTL_DECL(_hw_snd);
|
||||
|
||||
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 devunit);
|
||||
int pcm_setvchans(struct snddev_info *d, int direction, int newcnt, int num);
|
||||
int pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction,
|
||||
pid_t pid, char *comm, int devunit);
|
||||
int pcm_chnrelease(struct pcm_channel *c);
|
||||
int pcm_chnref(struct pcm_channel *c, int ref);
|
||||
int pcm_inprog(struct snddev_info *d, int delta);
|
||||
@ -535,8 +391,6 @@ void snd_mtxassert(void *m);
|
||||
#define snd_mtxlock(m) mtx_lock(m)
|
||||
#define snd_mtxunlock(m) mtx_unlock(m)
|
||||
|
||||
int sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS);
|
||||
|
||||
typedef int (*sndstat_handler)(struct sbuf *s, device_t dev, int verbose);
|
||||
int sndstat_acquire(struct thread *td);
|
||||
int sndstat_release(struct thread *td);
|
||||
@ -564,8 +418,6 @@ int sndstat_unregisterfile(char *str);
|
||||
#define DV_F_DEV_MASK 0x0000ff00 /* force device type/class */
|
||||
#define DV_F_DEV_SHIFT 8 /* force device type/class */
|
||||
|
||||
#define PCM_DEBUG_MTX
|
||||
|
||||
/*
|
||||
* this is rather kludgey- we need to duplicate these struct def'ns from sound.c
|
||||
* so that the macro versions of pcm_{,un}lock can dereference them.
|
||||
@ -579,11 +431,13 @@ struct snddev_info {
|
||||
struct {
|
||||
SLIST_HEAD(, pcm_channel) head;
|
||||
} busy;
|
||||
struct {
|
||||
SLIST_HEAD(, pcm_channel) head;
|
||||
} opened;
|
||||
} pcm;
|
||||
} channels;
|
||||
TAILQ_HEAD(dsp_cdevinfo_linkhead, dsp_cdevinfo) dsp_cdevinfo_pool;
|
||||
struct snd_clone *clones;
|
||||
struct pcm_channel *fakechan;
|
||||
unsigned devcount, playcount, reccount, pvchancount, rvchancount ;
|
||||
unsigned flags;
|
||||
int inprog;
|
||||
@ -595,6 +449,7 @@ struct snddev_info {
|
||||
struct cdev *mixer_dev;
|
||||
uint32_t pvchanrate, pvchanformat;
|
||||
uint32_t rvchanrate, rvchanformat;
|
||||
int32_t eqpreamp;
|
||||
struct sysctl_ctx_list play_sysctl_ctx, rec_sysctl_ctx;
|
||||
struct sysctl_oid *play_sysctl_tree, *rec_sysctl_tree;
|
||||
struct cv cv;
|
||||
@ -603,21 +458,20 @@ struct snddev_info {
|
||||
void sound_oss_sysinfo(oss_sysinfo *);
|
||||
int sound_oss_card_info(oss_card_info *);
|
||||
|
||||
#ifdef PCM_DEBUG_MTX
|
||||
#define pcm_lock(d) mtx_lock(((struct snddev_info *)(d))->lock)
|
||||
#define pcm_unlock(d) mtx_unlock(((struct snddev_info *)(d))->lock)
|
||||
#else
|
||||
void pcm_lock(struct snddev_info *d);
|
||||
void pcm_unlock(struct snddev_info *d);
|
||||
#endif
|
||||
#define PCM_LOCKOWNED(d) mtx_owned((d)->lock)
|
||||
#define PCM_LOCK(d) mtx_lock((d)->lock)
|
||||
#define PCM_UNLOCK(d) mtx_unlock((d)->lock)
|
||||
#define PCM_TRYLOCK(d) mtx_trylock((d)->lock)
|
||||
#define PCM_LOCKASSERT(d) mtx_assert((d)->lock, MA_OWNED)
|
||||
#define PCM_UNLOCKASSERT(d) mtx_assert((d)->lock, MA_NOTOWNED)
|
||||
|
||||
/*
|
||||
* For PCM_CV_[WAIT | ACQUIRE | RELEASE], be sure to surround these
|
||||
* with pcm_lock/unlock() sequence, or I'll come to gnaw upon you!
|
||||
* For PCM_[WAIT | ACQUIRE | RELEASE], be sure to surround these
|
||||
* with PCM_LOCK/UNLOCK() sequence, or I'll come to gnaw upon you!
|
||||
*/
|
||||
#ifdef SND_DIAGNOSTIC
|
||||
#define PCM_WAIT(x) do { \
|
||||
if (mtx_owned((x)->lock) == 0) \
|
||||
if (!PCM_LOCKOWNED(x)) \
|
||||
panic("%s(%d): [PCM WAIT] Mutex not owned!", \
|
||||
__func__, __LINE__); \
|
||||
while ((x)->flags & SD_F_BUSY) { \
|
||||
@ -627,20 +481,20 @@ void pcm_unlock(struct snddev_info *d);
|
||||
__func__, __LINE__); \
|
||||
cv_wait(&(x)->cv, (x)->lock); \
|
||||
} \
|
||||
} while(0)
|
||||
} while (0)
|
||||
|
||||
#define PCM_ACQUIRE(x) do { \
|
||||
if (mtx_owned((x)->lock) == 0) \
|
||||
if (!PCM_LOCKOWNED(x)) \
|
||||
panic("%s(%d): [PCM ACQUIRE] Mutex not owned!", \
|
||||
__func__, __LINE__); \
|
||||
if ((x)->flags & SD_F_BUSY) \
|
||||
panic("%s(%d): [PCM ACQUIRE] " \
|
||||
"Trying to acquire BUSY cv!", __func__, __LINE__); \
|
||||
(x)->flags |= SD_F_BUSY; \
|
||||
} while(0)
|
||||
} while (0)
|
||||
|
||||
#define PCM_RELEASE(x) do { \
|
||||
if (mtx_owned((x)->lock) == 0) \
|
||||
if (!PCM_LOCKOWNED(x)) \
|
||||
panic("%s(%d): [PCM RELEASE] Mutex not owned!", \
|
||||
__func__, __LINE__); \
|
||||
if ((x)->flags & SD_F_BUSY) { \
|
||||
@ -657,37 +511,37 @@ void pcm_unlock(struct snddev_info *d);
|
||||
} else \
|
||||
panic("%s(%d): [PCM RELEASE] Releasing non-BUSY cv!", \
|
||||
__func__, __LINE__); \
|
||||
} while(0)
|
||||
} while (0)
|
||||
|
||||
/* Quick version, for shorter path. */
|
||||
#define PCM_ACQUIRE_QUICK(x) do { \
|
||||
if (mtx_owned((x)->lock) != 0) \
|
||||
if (PCM_LOCKOWNED(x)) \
|
||||
panic("%s(%d): [PCM ACQUIRE QUICK] Mutex owned!", \
|
||||
__func__, __LINE__); \
|
||||
pcm_lock(x); \
|
||||
PCM_LOCK(x); \
|
||||
PCM_WAIT(x); \
|
||||
PCM_ACQUIRE(x); \
|
||||
pcm_unlock(x); \
|
||||
} while(0)
|
||||
PCM_UNLOCK(x); \
|
||||
} while (0)
|
||||
|
||||
#define PCM_RELEASE_QUICK(x) do { \
|
||||
if (mtx_owned((x)->lock) != 0) \
|
||||
if (PCM_LOCKOWNED(x)) \
|
||||
panic("%s(%d): [PCM RELEASE QUICK] Mutex owned!", \
|
||||
__func__, __LINE__); \
|
||||
pcm_lock(x); \
|
||||
PCM_LOCK(x); \
|
||||
PCM_RELEASE(x); \
|
||||
pcm_unlock(x); \
|
||||
} while(0)
|
||||
PCM_UNLOCK(x); \
|
||||
} while (0)
|
||||
|
||||
#define PCM_BUSYASSERT(x) do { \
|
||||
if (!((x) != NULL && ((x)->flags & SD_F_BUSY))) \
|
||||
panic("%s(%d): [PCM BUSYASSERT] " \
|
||||
"Failed, snddev_info=%p", __func__, __LINE__, x); \
|
||||
} while(0)
|
||||
} while (0)
|
||||
|
||||
#define PCM_GIANT_ENTER(x) do { \
|
||||
int _pcm_giant = 0; \
|
||||
if (mtx_owned((x)->lock) != 0) \
|
||||
if (PCM_LOCKOWNED(x)) \
|
||||
panic("%s(%d): [GIANT ENTER] PCM lock owned!", \
|
||||
__func__, __LINE__); \
|
||||
if (mtx_owned(&Giant) != 0 && snd_verbose > 3) \
|
||||
@ -698,10 +552,10 @@ void pcm_unlock(struct snddev_info *d);
|
||||
do { \
|
||||
mtx_lock(&Giant); \
|
||||
_pcm_giant = 1; \
|
||||
} while(0)
|
||||
} while (0)
|
||||
|
||||
#define PCM_GIANT_EXIT(x) do { \
|
||||
if (mtx_owned((x)->lock) != 0) \
|
||||
if (PCM_LOCKOWNED(x)) \
|
||||
panic("%s(%d): [GIANT EXIT] PCM lock owned!", \
|
||||
__func__, __LINE__); \
|
||||
if (!(_pcm_giant == 0 || _pcm_giant == 1)) \
|
||||
@ -723,47 +577,47 @@ void pcm_unlock(struct snddev_info *d);
|
||||
_pcm_giant = 0; \
|
||||
mtx_unlock(&Giant); \
|
||||
} \
|
||||
} while(0)
|
||||
} while (0)
|
||||
#else /* SND_DIAGNOSTIC */
|
||||
#define PCM_WAIT(x) do { \
|
||||
mtx_assert((x)->lock, MA_OWNED); \
|
||||
PCM_LOCKASSERT(x); \
|
||||
while ((x)->flags & SD_F_BUSY) \
|
||||
cv_wait(&(x)->cv, (x)->lock); \
|
||||
} while(0)
|
||||
} while (0)
|
||||
|
||||
#define PCM_ACQUIRE(x) do { \
|
||||
mtx_assert((x)->lock, MA_OWNED); \
|
||||
PCM_LOCKASSERT(x); \
|
||||
KASSERT(!((x)->flags & SD_F_BUSY), \
|
||||
("%s(%d): [PCM ACQUIRE] Trying to acquire BUSY cv!", \
|
||||
__func__, __LINE__)); \
|
||||
(x)->flags |= SD_F_BUSY; \
|
||||
} while(0)
|
||||
} while (0)
|
||||
|
||||
#define PCM_RELEASE(x) do { \
|
||||
mtx_assert((x)->lock, MA_OWNED); \
|
||||
PCM_LOCKASSERT(x); \
|
||||
KASSERT((x)->flags & SD_F_BUSY, \
|
||||
("%s(%d): [PCM RELEASE] Releasing non-BUSY cv!", \
|
||||
__func__, __LINE__)); \
|
||||
(x)->flags &= ~SD_F_BUSY; \
|
||||
if ((x)->cv.cv_waiters != 0) \
|
||||
cv_broadcast(&(x)->cv); \
|
||||
} while(0)
|
||||
} while (0)
|
||||
|
||||
/* Quick version, for shorter path. */
|
||||
#define PCM_ACQUIRE_QUICK(x) do { \
|
||||
mtx_assert((x)->lock, MA_NOTOWNED); \
|
||||
pcm_lock(x); \
|
||||
PCM_UNLOCKASSERT(x); \
|
||||
PCM_LOCK(x); \
|
||||
PCM_WAIT(x); \
|
||||
PCM_ACQUIRE(x); \
|
||||
pcm_unlock(x); \
|
||||
} while(0)
|
||||
PCM_UNLOCK(x); \
|
||||
} while (0)
|
||||
|
||||
#define PCM_RELEASE_QUICK(x) do { \
|
||||
mtx_assert((x)->lock, MA_NOTOWNED); \
|
||||
pcm_lock(x); \
|
||||
PCM_UNLOCKASSERT(x); \
|
||||
PCM_LOCK(x); \
|
||||
PCM_RELEASE(x); \
|
||||
pcm_unlock(x); \
|
||||
} while(0)
|
||||
PCM_UNLOCK(x); \
|
||||
} while (0)
|
||||
|
||||
#define PCM_BUSYASSERT(x) KASSERT(x != NULL && \
|
||||
((x)->flags & SD_F_BUSY), \
|
||||
@ -773,15 +627,15 @@ void pcm_unlock(struct snddev_info *d);
|
||||
|
||||
#define PCM_GIANT_ENTER(x) do { \
|
||||
int _pcm_giant = 0; \
|
||||
mtx_assert((x)->lock, MA_NOTOWNED); \
|
||||
PCM_UNLOCKASSERT(x); \
|
||||
if (!((x)->flags & SD_F_MPSAFE) && mtx_owned(&Giant) == 0) \
|
||||
do { \
|
||||
mtx_lock(&Giant); \
|
||||
_pcm_giant = 1; \
|
||||
} while(0)
|
||||
} while (0)
|
||||
|
||||
#define PCM_GIANT_EXIT(x) do { \
|
||||
mtx_assert((x)->lock, MA_NOTOWNED); \
|
||||
PCM_UNLOCKASSERT(x); \
|
||||
KASSERT(_pcm_giant == 0 || _pcm_giant == 1, \
|
||||
("%s(%d): [GIANT EXIT] _pcm_giant screwed!", \
|
||||
__func__, __LINE__)); \
|
||||
@ -794,12 +648,12 @@ void pcm_unlock(struct snddev_info *d);
|
||||
_pcm_giant = 0; \
|
||||
mtx_unlock(&Giant); \
|
||||
} \
|
||||
} while(0)
|
||||
} while (0)
|
||||
#endif /* !SND_DIAGNOSTIC */
|
||||
|
||||
#define PCM_GIANT_LEAVE(x) \
|
||||
PCM_GIANT_EXIT(x); \
|
||||
} while(0)
|
||||
} while (0)
|
||||
|
||||
#ifdef KLD_MODULE
|
||||
#define PCM_KLDSTRING(a) ("kld " # a)
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,6 @@
|
||||
/*-
|
||||
* Copyright (c) 2001 Cameron Grant <cg@freebsd.org>
|
||||
* Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org>
|
||||
* Copyright (c) 2001 Cameron Grant <cg@FreeBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -26,16 +27,31 @@
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
int vchan_create(struct pcm_channel *parent, int num);
|
||||
int vchan_destroy(struct pcm_channel *c);
|
||||
int vchan_initsys(device_t dev);
|
||||
#ifndef _SND_VCHAN_H_
|
||||
#define _SND_VCHAN_H_
|
||||
|
||||
int vchan_create(struct pcm_channel *, int);
|
||||
int vchan_destroy(struct pcm_channel *);
|
||||
|
||||
#ifdef SND_DEBUG
|
||||
int vchan_passthrough(struct pcm_channel *, const char *);
|
||||
#define vchan_sync(c) vchan_passthrough(c, __func__)
|
||||
#else
|
||||
int vchan_sync(struct pcm_channel *);
|
||||
#endif
|
||||
|
||||
#define VCHAN_SYNC_REQUIRED(c) \
|
||||
(((c)->flags & CHN_F_VIRTUAL) && (((c)->flags & CHN_F_DIRTY) || \
|
||||
sndbuf_getfmt((c)->bufhard) != (c)->parentchannel->format || \
|
||||
sndbuf_getspd((c)->bufhard) != (c)->parentchannel->speed))
|
||||
|
||||
void vchan_initsys(device_t);
|
||||
|
||||
/*
|
||||
* Default speed / format
|
||||
* Default format / rate
|
||||
*/
|
||||
#define VCHAN_DEFAULT_SPEED 48000
|
||||
#define VCHAN_DEFAULT_AFMT (AFMT_S16_LE | AFMT_STEREO)
|
||||
#define VCHAN_DEFAULT_STRFMT "s16le"
|
||||
#define VCHAN_DEFAULT_FORMAT SND_FORMAT(AFMT_S16_LE, 2, 0)
|
||||
#define VCHAN_DEFAULT_RATE 48000
|
||||
|
||||
#define VCHAN_PLAY 0
|
||||
#define VCHAN_REC 1
|
||||
@ -50,3 +66,5 @@ int vchan_initsys(device_t dev);
|
||||
#define VCHAN_SYSCTL_DATA_SIZE sizeof(void *)
|
||||
#define VCHAN_SYSCTL_UNIT(x) ((int)(((intptr_t)(x) >> 2) & 0xfff) - 1)
|
||||
#define VCHAN_SYSCTL_DIR(x) ((int)((intptr_t)(x) & 0x3) - 1)
|
||||
|
||||
#endif /* _SND_VCHAN_H_ */
|
||||
|
@ -50,6 +50,10 @@ __FBSDID("$FreeBSD$");
|
||||
#include <machine/bus.h>
|
||||
#include <machine/ofw_machdep.h>
|
||||
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
#include <dev/sound/sbus/apcdmareg.h>
|
||||
#include <dev/sound/sbus/cs4231.h>
|
||||
@ -260,18 +264,18 @@ MODULE_VERSION(snd_audiocs, 1);
|
||||
|
||||
|
||||
static u_int32_t cs4231_fmt[] = {
|
||||
AFMT_U8,
|
||||
AFMT_STEREO | AFMT_U8,
|
||||
AFMT_MU_LAW,
|
||||
AFMT_STEREO | AFMT_MU_LAW,
|
||||
AFMT_A_LAW,
|
||||
AFMT_STEREO | AFMT_A_LAW,
|
||||
AFMT_IMA_ADPCM,
|
||||
AFMT_STEREO | AFMT_IMA_ADPCM,
|
||||
AFMT_S16_LE,
|
||||
AFMT_STEREO | AFMT_S16_LE,
|
||||
AFMT_S16_BE,
|
||||
AFMT_STEREO | AFMT_S16_BE,
|
||||
SND_FORMAT(AFMT_U8, 1, 0),
|
||||
SND_FORMAT(AFMT_U8, 2, 0),
|
||||
SND_FORMAT(AFMT_MU_LAW, 1, 0),
|
||||
SND_FORMAT(AFMT_MU_LAW, 2, 0),
|
||||
SND_FORMAT(AFMT_A_LAW, 1, 0),
|
||||
SND_FORMAT(AFMT_A_LAW, 2, 0),
|
||||
SND_FORMAT(AFMT_IMA_ADPCM, 1, 0),
|
||||
SND_FORMAT(AFMT_IMA_ADPCM, 2, 0),
|
||||
SND_FORMAT(AFMT_S16_LE, 1, 0),
|
||||
SND_FORMAT(S16_LE, 2, 0),
|
||||
SND_FORMAT(AFMT_S16_BE, 1, 0),
|
||||
SND_FORMAT(AFMT_S16_BE, 2, 0),
|
||||
0
|
||||
};
|
||||
|
||||
@ -288,7 +292,7 @@ static kobj_method_t cs4231_chan_methods[] = {
|
||||
KOBJMETHOD(channel_trigger, cs4231_chan_trigger),
|
||||
KOBJMETHOD(channel_getptr, cs4231_chan_getptr),
|
||||
KOBJMETHOD(channel_getcaps, cs4231_chan_getcaps),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
CHANNEL_DECLARE(cs4231_chan);
|
||||
|
||||
@ -299,7 +303,7 @@ static kobj_method_t cs4231_mixer_methods[] = {
|
||||
KOBJMETHOD(mixer_init, cs4231_mixer_init),
|
||||
KOBJMETHOD(mixer_set, cs4231_mixer_set),
|
||||
KOBJMETHOD(mixer_setrecsrc, cs4231_mixer_setrecsrc),
|
||||
{ 0, 0 }
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
MIXER_DECLARE(cs4231_mixer);
|
||||
|
||||
@ -1057,7 +1061,7 @@ cs4231_chan_setformat(kobj_t obj, void *data, u_int32_t format)
|
||||
return (0);
|
||||
}
|
||||
|
||||
encoding = format & ~AFMT_STEREO;
|
||||
encoding = AFMT_ENCODING(format);
|
||||
fs = 0;
|
||||
switch (encoding) {
|
||||
case AFMT_U8:
|
||||
@ -1084,7 +1088,7 @@ cs4231_chan_setformat(kobj_t obj, void *data, u_int32_t format)
|
||||
break;
|
||||
}
|
||||
|
||||
if (format & AFMT_STEREO)
|
||||
if (AFMT_CHANNEL(format) > 1)
|
||||
fs |= CS_AFMT_STEREO;
|
||||
|
||||
DPRINTF(("FORMAT: %s : 0x%x\n", ch->dir == PCMDIR_PLAY ? "playback" :
|
||||
|
@ -29,6 +29,10 @@
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/unit.h>
|
||||
|
||||
/*
|
||||
@ -65,7 +69,7 @@ static int snd_unit_initialized = 0;
|
||||
#define SND_UNIT_ASSERT() do { \
|
||||
if (snd_unit_initialized == 0) \
|
||||
panic("%s(): Uninitialized sound unit!", __func__); \
|
||||
} while(0)
|
||||
} while (0)
|
||||
#else
|
||||
#define SND_UNIT_ASSERT() KASSERT(snd_unit_initialized != 0, \
|
||||
("%s(): Uninitialized sound unit!", \
|
||||
|
@ -67,6 +67,10 @@
|
||||
|
||||
#include <sys/reboot.h> /* for bootverbose */
|
||||
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
#include <dev/sound/usb/uaudioreg.h>
|
||||
#include <dev/sound/usb/uaudio.h>
|
||||
@ -664,7 +668,7 @@ uaudio_attach_sub(device_t dev, kobj_class_t mixer_class, kobj_class_t chan_clas
|
||||
|
||||
if (sc->sc_uq_audio_swap_lr) {
|
||||
DPRINTF("hardware has swapped left and right\n");
|
||||
uaudio_pcm_setflags(dev, SD_F_PSWAPLR);
|
||||
/* uaudio_pcm_setflags(dev, SD_F_PSWAPLR); */
|
||||
}
|
||||
if (!(sc->sc_mix_info & SOUND_MASK_PCM)) {
|
||||
|
||||
@ -688,6 +692,8 @@ uaudio_attach_sub(device_t dev, kobj_class_t mixer_class, kobj_class_t chan_clas
|
||||
sc->sc_rec_chan.valid ? 1 : 0)) {
|
||||
goto detach;
|
||||
}
|
||||
|
||||
uaudio_pcm_setflags(dev, SD_F_MPSAFE);
|
||||
sc->sc_pcm_registered = 1;
|
||||
|
||||
if (sc->sc_play_chan.valid) {
|
||||
@ -1336,11 +1342,13 @@ uaudio_chan_init(struct uaudio_softc *sc, struct snd_dbuf *b,
|
||||
ch->pcm_cap.minspeed = ch->sample_rate;
|
||||
ch->pcm_cap.maxspeed = ch->sample_rate;
|
||||
|
||||
ch->pcm_cap.fmtlist[0] = ch->p_fmt->freebsd_fmt;
|
||||
if (ch->p_asf1d->bNrChannels >= 2)
|
||||
ch->pcm_cap.fmtlist[0] =
|
||||
SND_FORMAT(ch->p_fmt->freebsd_fmt, 2, 0);
|
||||
else
|
||||
ch->pcm_cap.fmtlist[0] =
|
||||
SND_FORMAT(ch->p_fmt->freebsd_fmt, 1, 0);
|
||||
|
||||
if (ch->p_asf1d->bNrChannels >= 2) {
|
||||
ch->pcm_cap.fmtlist[0] |= AFMT_STEREO;
|
||||
}
|
||||
ch->pcm_cap.fmtlist[1] = 0;
|
||||
|
||||
|
||||
@ -1452,6 +1460,51 @@ uaudio_chan_getcaps(struct uaudio_chan *ch)
|
||||
return (&ch->pcm_cap);
|
||||
}
|
||||
|
||||
static struct pcmchan_matrix uaudio_chan_matrix_swap_2_0 = {
|
||||
.id = SND_CHN_MATRIX_DRV,
|
||||
.channels = 2,
|
||||
.ext = 0,
|
||||
.map = {
|
||||
/* Right */
|
||||
[0] = {
|
||||
.type = SND_CHN_T_FR,
|
||||
.members =
|
||||
SND_CHN_T_MASK_FR | SND_CHN_T_MASK_FC |
|
||||
SND_CHN_T_MASK_LF | SND_CHN_T_MASK_BR |
|
||||
SND_CHN_T_MASK_BC | SND_CHN_T_MASK_SR
|
||||
},
|
||||
/* Left */
|
||||
[1] = {
|
||||
.type = SND_CHN_T_FL,
|
||||
.members =
|
||||
SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FC |
|
||||
SND_CHN_T_MASK_LF | SND_CHN_T_MASK_BL |
|
||||
SND_CHN_T_MASK_BC | SND_CHN_T_MASK_SL
|
||||
},
|
||||
[2] = {
|
||||
.type = SND_CHN_T_MAX,
|
||||
.members = 0
|
||||
}
|
||||
},
|
||||
.mask = SND_CHN_T_MASK_FR | SND_CHN_T_MASK_FL,
|
||||
.offset = { 1, 0, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1 }
|
||||
};
|
||||
|
||||
struct pcmchan_matrix *
|
||||
uaudio_chan_getmatrix(struct uaudio_chan *ch, uint32_t format)
|
||||
{
|
||||
struct uaudio_softc *sc;
|
||||
|
||||
sc = ch->priv_sc;
|
||||
|
||||
if (sc != NULL && sc->sc_uq_audio_swap_lr != 0 &&
|
||||
AFMT_CHANNEL(format) == 2)
|
||||
return (&uaudio_chan_matrix_swap_2_0);
|
||||
|
||||
return (feeder_matrix_format_map(format));
|
||||
}
|
||||
|
||||
int
|
||||
uaudio_chan_set_param_format(struct uaudio_chan *ch, uint32_t format)
|
||||
{
|
||||
|
@ -47,6 +47,8 @@ extern int uaudio_chan_set_param_speed(struct uaudio_chan *ch,
|
||||
uint32_t speed);
|
||||
extern int uaudio_chan_getptr(struct uaudio_chan *ch);
|
||||
extern struct pcmchan_caps *uaudio_chan_getcaps(struct uaudio_chan *ch);
|
||||
extern struct pcmchan_matrix *uaudio_chan_getmatrix(struct uaudio_chan *ch,
|
||||
uint32_t format);
|
||||
extern int uaudio_chan_set_param_format(struct uaudio_chan *ch,
|
||||
uint32_t format);
|
||||
extern int uaudio_chan_start(struct uaudio_chan *ch);
|
||||
|
@ -27,7 +27,10 @@
|
||||
*/
|
||||
|
||||
|
||||
#include <sys/soundcard.h>
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
#include <dev/sound/chip.h>
|
||||
#include <dev/sound/usb/uaudio.h>
|
||||
@ -57,13 +60,13 @@ ua_chan_setformat(kobj_t obj, void *data, uint32_t format)
|
||||
return (uaudio_chan_set_param_format(data, format));
|
||||
}
|
||||
|
||||
static int
|
||||
static uint32_t
|
||||
ua_chan_setspeed(kobj_t obj, void *data, uint32_t speed)
|
||||
{
|
||||
return (uaudio_chan_set_param_speed(data, speed));
|
||||
}
|
||||
|
||||
static int
|
||||
static uint32_t
|
||||
ua_chan_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
|
||||
{
|
||||
return (uaudio_chan_set_param_blocksize(data, blocksize));
|
||||
@ -88,7 +91,7 @@ ua_chan_trigger(kobj_t obj, void *data, int go)
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
static uint32_t
|
||||
ua_chan_getptr(kobj_t obj, void *data)
|
||||
{
|
||||
return (uaudio_chan_getptr(data));
|
||||
@ -100,6 +103,12 @@ ua_chan_getcaps(kobj_t obj, void *data)
|
||||
return (uaudio_chan_getcaps(data));
|
||||
}
|
||||
|
||||
static struct pcmchan_matrix *
|
||||
ua_chan_getmatrix(kobj_t obj, void *data, uint32_t format)
|
||||
{
|
||||
return (uaudio_chan_getmatrix(data, format));
|
||||
}
|
||||
|
||||
static kobj_method_t ua_chan_methods[] = {
|
||||
KOBJMETHOD(channel_init, ua_chan_init),
|
||||
KOBJMETHOD(channel_free, ua_chan_free),
|
||||
@ -110,7 +119,8 @@ static kobj_method_t ua_chan_methods[] = {
|
||||
KOBJMETHOD(channel_trigger, ua_chan_trigger),
|
||||
KOBJMETHOD(channel_getptr, ua_chan_getptr),
|
||||
KOBJMETHOD(channel_getcaps, ua_chan_getcaps),
|
||||
{0, 0}
|
||||
KOBJMETHOD(channel_getmatrix, ua_chan_getmatrix),
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
|
||||
CHANNEL_DECLARE(ua_chan);
|
||||
@ -141,7 +151,7 @@ ua_mixer_set(struct snd_mixer *m, unsigned type, unsigned left, unsigned right)
|
||||
return (left | (right << 8));
|
||||
}
|
||||
|
||||
static int
|
||||
static uint32_t
|
||||
ua_mixer_setrecsrc(struct snd_mixer *m, uint32_t src)
|
||||
{
|
||||
struct mtx *mtx = mixer_get_lock(m);
|
||||
@ -172,8 +182,7 @@ static kobj_method_t ua_mixer_methods[] = {
|
||||
KOBJMETHOD(mixer_uninit, ua_mixer_uninit),
|
||||
KOBJMETHOD(mixer_set, ua_mixer_set),
|
||||
KOBJMETHOD(mixer_setrecsrc, ua_mixer_setrecsrc),
|
||||
|
||||
{0, 0}
|
||||
KOBJMETHOD_END
|
||||
};
|
||||
|
||||
MIXER_DECLARE(ua_mixer);
|
||||
|
@ -37,6 +37,6 @@
|
||||
* Last 2 decimal places reserved for daily versioning, starting
|
||||
* with 0.
|
||||
*/
|
||||
#define SND_DRV_VERSION 2007061600
|
||||
#define SND_DRV_VERSION 2009060800
|
||||
|
||||
#endif /* !_SND_VERSION_H_ */
|
||||
|
@ -9,13 +9,27 @@ KMOD= sound
|
||||
SRCS= device_if.h bus_if.h isa_if.h pci_if.h opt_isa.h
|
||||
SRCS+= ac97_if.h channel_if.h feeder_if.h mixer_if.h
|
||||
SRCS+= ac97_if.c channel_if.c feeder_if.c mixer_if.c
|
||||
SRCS+= feeder.c feeder_rate.c feeder_volume.c
|
||||
SRCS+= feeder_chain.c feeder_eq.c feeder_format.c
|
||||
SRCS+= feeder_matrix.c feeder_mixer.c
|
||||
SRCS+= feeder_eq_gen.h feeder_rate_gen.h snd_fxdiv_gen.h
|
||||
SRCS+= mpu_if.h mpufoi_if.h synth_if.h
|
||||
SRCS+= mpu_if.c mpufoi_if.c synth_if.c
|
||||
SRCS+= ac97.c ac97_patch.c buffer.c channel.c clone.c dsp.c
|
||||
SRCS+= fake.c feeder.c feeder_fmt.c feeder_rate.c feeder_volume.c
|
||||
SRCS+= mixer.c sndstat.c sound.c unit.c vchan.c
|
||||
SRCS+= midi.c mpu401.c sequencer.c
|
||||
|
||||
feeder_eq_gen.h:
|
||||
${AWK} -f @/tools/feeder_eq_mkfilter.awk -- ${FEEDER_EQ_PRESETS} > ${.TARGET}
|
||||
|
||||
feeder_rate_gen.h:
|
||||
${AWK} -f @/tools/feeder_rate_mkfilter.awk -- ${FEEDER_RATE_PRESETS} > ${.TARGET}
|
||||
|
||||
snd_fxdiv_gen.h:
|
||||
${AWK} -f @/tools/snd_fxdiv_gen.awk -- > ${.TARGET}
|
||||
|
||||
CLEANFILES+= feeder_eq_gen.h feeder_rate_gen.h snd_fxdiv_gen.h
|
||||
|
||||
EXPORT_SYMS= YES # XXX evaluate
|
||||
|
||||
.if ${MACHINE_ARCH} == "sparc64" || ${MACHINE_ARCH} == "powerpc"
|
||||
|
@ -1665,7 +1665,8 @@ typedef struct
|
||||
#define SNDCTL_DSP_GET_CHNORDER _IOR ('P', 42, unsigned long long)
|
||||
#define SNDCTL_DSP_SET_CHNORDER _IOWR('P', 42, unsigned long long)
|
||||
# define CHID_UNDEF 0
|
||||
# define CHID_L 1 # define CHID_R 2
|
||||
# define CHID_L 1
|
||||
# define CHID_R 2
|
||||
# define CHID_C 3
|
||||
# define CHID_LFE 4
|
||||
# define CHID_LS 5
|
||||
@ -1681,6 +1682,25 @@ typedef unsigned short oss_peaks_t[MAX_PEAK_CHANNELS];
|
||||
#define SNDCTL_DSP_GETOPEAKS _IOR('P', 44, oss_peaks_t)
|
||||
#define SNDCTL_DSP_POLICY _IOW('P', 45, int) /* See the manual */
|
||||
|
||||
/*
|
||||
****************************************************************************
|
||||
* Few ioctl calls that are not official parts of OSS. They have been used
|
||||
* by few freeware implementations of OSS.
|
||||
*/
|
||||
#define SNDCTL_DSP_GETCHANNELMASK _IOWR('P', 64, int)
|
||||
#define SNDCTL_DSP_BIND_CHANNEL _IOWR('P', 65, int)
|
||||
#define DSP_BIND_QUERY 0x00000000
|
||||
#define DSP_BIND_FRONT 0x00000001
|
||||
#define DSP_BIND_SURR 0x00000002
|
||||
#define DSP_BIND_CENTER_LFE 0x00000004
|
||||
#define DSP_BIND_HANDSET 0x00000008
|
||||
#define DSP_BIND_MIC 0x00000010
|
||||
#define DSP_BIND_MODEM1 0x00000020
|
||||
#define DSP_BIND_MODEM2 0x00000040
|
||||
#define DSP_BIND_I2S 0x00000080
|
||||
#define DSP_BIND_SPDIF 0x00000100
|
||||
#define DSP_BIND_REAR 0x00000200
|
||||
|
||||
/*
|
||||
* OSS_SYSIFO is obsolete. Use SNDCTL_SYSINFO insteads.
|
||||
*/
|
||||
|
467
sys/tools/feeder_eq_mkfilter.awk
Normal file
467
sys/tools/feeder_eq_mkfilter.awk
Normal file
@ -0,0 +1,467 @@
|
||||
#!/usr/bin/awk -f
|
||||
#
|
||||
# Copyright (c) 2008-2009 Ariff Abdullah <ariff@FreeBSD.org>
|
||||
# All rights reserved.
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# $FreeBSD$
|
||||
#
|
||||
|
||||
#
|
||||
# Biquad coefficients generator for Parametric Software Equalizer. Not as ugly
|
||||
# as 'feeder_rate_mkfilter.awk'
|
||||
#
|
||||
# Based on:
|
||||
#
|
||||
# "Cookbook formulae for audio EQ biquad filter coefficients"
|
||||
# by Robert Bristow-Johnson <rbj@audioimagination.com>
|
||||
#
|
||||
# - http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
|
||||
#
|
||||
|
||||
|
||||
|
||||
#
|
||||
# Some basic Math functions.
|
||||
#
|
||||
function abs(x)
|
||||
{
|
||||
return (((x < 0) ? -x : x) + 0);
|
||||
}
|
||||
|
||||
function fabs(x)
|
||||
{
|
||||
return (((x < 0.0) ? -x : x) + 0.0);
|
||||
}
|
||||
|
||||
function floor(x, r)
|
||||
{
|
||||
r = int(x);
|
||||
if (r > x)
|
||||
r--;
|
||||
return (r + 0);
|
||||
}
|
||||
|
||||
function pow(x, y)
|
||||
{
|
||||
return (exp(1.0 * y * log(1.0 * x)));
|
||||
}
|
||||
|
||||
#
|
||||
# What the hell...
|
||||
#
|
||||
function shl(x, y)
|
||||
{
|
||||
while (y > 0) {
|
||||
x *= 2;
|
||||
y--;
|
||||
}
|
||||
return (x);
|
||||
}
|
||||
|
||||
function feedeq_w0(fc, rate)
|
||||
{
|
||||
return ((2.0 * M_PI * fc) / (1.0 * rate));
|
||||
}
|
||||
|
||||
function feedeq_A(gain, A)
|
||||
{
|
||||
if (FEEDEQ_TYPE == FEEDEQ_TYPE_PEQ || FEEDEQ_TYPE == FEEDEQ_TYPE_SHELF)
|
||||
A = pow(10, gain / 40.0);
|
||||
else
|
||||
A = sqrt(pow(10, gain / 20.0));
|
||||
|
||||
return (A);
|
||||
}
|
||||
|
||||
function feedeq_alpha(w0, A, QS)
|
||||
{
|
||||
if (FEEDEQ_TYPE == FEEDEQ_TYPE_PEQ)
|
||||
alpha = sin(w0) / (2.0 * QS);
|
||||
else if (FEEDEQ_TYPE == FEEDEQ_TYPE_SHELF)
|
||||
alpha = sin(w0) * 0.5 * sqrt(A + ((1.0 / A) * \
|
||||
((1.0 / QS) - 1.0)) + 2.0);
|
||||
else
|
||||
alpha = 0.0;
|
||||
|
||||
return (alpha);
|
||||
}
|
||||
|
||||
function feedeq_fx_floor(v, r)
|
||||
{
|
||||
if (fabs(v) < fabs(smallest))
|
||||
smallest = v;
|
||||
if (fabs(v) > fabs(largest))
|
||||
largest = v;
|
||||
|
||||
r = floor((v * FEEDEQ_COEFF_ONE) + 0.5);
|
||||
|
||||
if (r < INT32_MIN || r > INT32_MAX)
|
||||
printf("\n#error overflow v=%f, " \
|
||||
"please reduce FEEDEQ_COEFF_SHIFT\n", v);
|
||||
|
||||
return (r);
|
||||
}
|
||||
|
||||
function feedeq_gen_biquad_coeffs(coeffs, rate, gain, \
|
||||
w0, A, alpha, a0, a1, a2, b0, b1, b2)
|
||||
{
|
||||
w0 = feedeq_w0(FEEDEQ_TREBLE_SFREQ, 1.0 * rate);
|
||||
A = feedeq_A(1.0 * gain);
|
||||
alpha = feedeq_alpha(w0, A, FEEDEQ_TREBLE_SLOPE);
|
||||
|
||||
if (FEEDEQ_TYPE == FEEDEQ_TYPE_PEQ) {
|
||||
b0 = 1.0 + (alpha * A);
|
||||
b1 = -2.0 * cos(w0);
|
||||
b2 = 1.0 - (alpha * A);
|
||||
a0 = 1.0 + (alpha / A);
|
||||
a1 = -2.0 * cos(w0);
|
||||
a2 = 1.0 - (alpha / A);
|
||||
} else if (FEEDEQ_TYPE == FEEDEQ_TYPE_SHELF) {
|
||||
b0 = A*((A+1.0)+((A-1.0)*cos(w0))+(2.0*sqrt(A)*alpha));
|
||||
b1 = -2.0*A*((A-1.0)+((A+1.0)*cos(w0)) );
|
||||
b2 = A*((A+1.0)+((A-1.0)*cos(w0))-(2.0*sqrt(A)*alpha));
|
||||
a0 = (A+1.0)-((A-1.0)*cos(w0))+(2.0*sqrt(A)*alpha );
|
||||
a1 = 2.0 * ((A-1.0)-((A+1.0)*cos(w0)) );
|
||||
a2 = (A+1.0)-((A-1.0)*cos(w0))-(2.0*sqrt(A)*alpha );
|
||||
} else
|
||||
b0 = b1 = b2 = a0 = a1 = a2 = 0.0;
|
||||
|
||||
b0 /= a0;
|
||||
b1 /= a0;
|
||||
b2 /= a0;
|
||||
a1 /= a0;
|
||||
a2 /= a0;
|
||||
|
||||
coeffs["treble", gain, 0] = feedeq_fx_floor(a0);
|
||||
coeffs["treble", gain, 1] = feedeq_fx_floor(a1);
|
||||
coeffs["treble", gain, 2] = feedeq_fx_floor(a2);
|
||||
coeffs["treble", gain, 3] = feedeq_fx_floor(b0);
|
||||
coeffs["treble", gain, 4] = feedeq_fx_floor(b1);
|
||||
coeffs["treble", gain, 5] = feedeq_fx_floor(b2);
|
||||
|
||||
w0 = feedeq_w0(FEEDEQ_BASS_SFREQ, 1.0 * rate);
|
||||
A = feedeq_A(1.0 * gain);
|
||||
alpha = feedeq_alpha(w0, A, FEEDEQ_BASS_SLOPE);
|
||||
|
||||
if (FEEDEQ_TYPE == FEEDEQ_TYPE_PEQ) {
|
||||
b0 = 1.0 + (alpha * A);
|
||||
b1 = -2.0 * cos(w0);
|
||||
b2 = 1.0 - (alpha * A);
|
||||
a0 = 1.0 + (alpha / A);
|
||||
a1 = -2.0 * cos(w0);
|
||||
a2 = 1.0 - (alpha / A);
|
||||
} else if (FEEDEQ_TYPE == FEEDEQ_TYPE_SHELF) {
|
||||
b0 = A*((A+1.0)-((A-1.0)*cos(w0))+(2.0*sqrt(A)*alpha));
|
||||
b1 = 2.0*A*((A-1.0)-((A+1.0)*cos(w0)) );
|
||||
b2 = A*((A+1.0)-((A-1.0)*cos(w0))-(2.0*sqrt(A)*alpha));
|
||||
a0 = (A+1.0)+((A-1.0)*cos(w0))+(2.0*sqrt(A)*alpha );
|
||||
a1 = -2.0 * ((A-1.0)+((A+1.0)*cos(w0)) );
|
||||
a2 = (A+1.0)+((A-1.0)*cos(w0))-(2.0*sqrt(A)*alpha );
|
||||
} else
|
||||
b0 = b1 = b2 = a0 = a1 = a2 = 0.0;
|
||||
|
||||
b0 /= a0;
|
||||
b1 /= a0;
|
||||
b2 /= a0;
|
||||
a1 /= a0;
|
||||
a2 /= a0;
|
||||
|
||||
coeffs["bass", gain, 0] = feedeq_fx_floor(a0);
|
||||
coeffs["bass", gain, 1] = feedeq_fx_floor(a1);
|
||||
coeffs["bass", gain, 2] = feedeq_fx_floor(a2);
|
||||
coeffs["bass", gain, 3] = feedeq_fx_floor(b0);
|
||||
coeffs["bass", gain, 4] = feedeq_fx_floor(b1);
|
||||
coeffs["bass", gain, 5] = feedeq_fx_floor(b2);
|
||||
}
|
||||
|
||||
function feedeq_gen_freq_coeffs(frq, g, i, v)
|
||||
{
|
||||
coeffs[0] = 0;
|
||||
|
||||
for (g = (FEEDEQ_GAIN_MIN * FEEDEQ_GAIN_DIV); \
|
||||
g <= (FEEDEQ_GAIN_MAX * FEEDEQ_GAIN_DIV); \
|
||||
g += FEEDEQ_GAIN_STEP) {
|
||||
feedeq_gen_biquad_coeffs(coeffs, frq, \
|
||||
g * FEEDEQ_GAIN_RECIPROCAL);
|
||||
}
|
||||
|
||||
printf("\nstatic struct feed_eq_coeff eq_%d[%d] " \
|
||||
"= {\n", frq, FEEDEQ_LEVELS);
|
||||
for (g = (FEEDEQ_GAIN_MIN * FEEDEQ_GAIN_DIV); \
|
||||
g <= (FEEDEQ_GAIN_MAX * FEEDEQ_GAIN_DIV); \
|
||||
g += FEEDEQ_GAIN_STEP) {
|
||||
printf(" {{ ");
|
||||
for (i = 1; i < 6; i++) {
|
||||
v = coeffs["treble", g * FEEDEQ_GAIN_RECIPROCAL, i];
|
||||
printf("%s0x%08x%s", \
|
||||
(v < 0) ? "-" : " ", abs(v), \
|
||||
(i == 5) ? " " : ", ");
|
||||
}
|
||||
printf("},\n { ");
|
||||
for (i = 1; i < 6; i++) {
|
||||
v = coeffs["bass", g * FEEDEQ_GAIN_RECIPROCAL, i];
|
||||
printf("%s0x%08x%s", \
|
||||
(v < 0) ? "-" : " ", abs(v), \
|
||||
(i == 5) ? " " : ", ");
|
||||
}
|
||||
printf("}}%s\n", \
|
||||
(g < (FEEDEQ_GAIN_MAX * FEEDEQ_GAIN_DIV)) ? "," : "");
|
||||
}
|
||||
printf("};\n");
|
||||
}
|
||||
|
||||
function feedeq_calc_preamp(norm, gain, shift, mul, bit, attn)
|
||||
{
|
||||
shift = FEEDEQ_PREAMP_SHIFT;
|
||||
|
||||
if (floor(FEEDEQ_PREAMP_BITDB) == 6 && \
|
||||
(1.0 * floor(gain)) == gain && (floor(gain) % 6) == 0) {
|
||||
mul = 1;
|
||||
shift = floor(floor(gain) / 6);
|
||||
} else {
|
||||
bit = 32.0 - ((1.0 * gain) / (1.0 * FEEDEQ_PREAMP_BITDB));
|
||||
attn = pow(2.0, bit) / pow(2.0, 32.0);
|
||||
mul = floor((attn * FEEDEQ_PREAMP_ONE) + 0.5);
|
||||
}
|
||||
|
||||
while ((mul % 2) == 0 && shift > 0) {
|
||||
mul = floor(mul / 2);
|
||||
shift--;
|
||||
}
|
||||
|
||||
norm["mul"] = mul;
|
||||
norm["shift"] = shift;
|
||||
}
|
||||
|
||||
BEGIN {
|
||||
M_PI = atan2(0.0, -1.0);
|
||||
|
||||
INT32_MAX = 1 + ((shl(1, 30) - 1) * 2);
|
||||
INT32_MIN = -1 - INT32_MAX;
|
||||
|
||||
FEEDEQ_TYPE_PEQ = 0;
|
||||
FEEDEQ_TYPE_SHELF = 1;
|
||||
|
||||
FEEDEQ_TYPE = FEEDEQ_TYPE_PEQ;
|
||||
|
||||
FEEDEQ_COEFF_SHIFT = 24;
|
||||
FEEDEQ_COEFF_ONE = shl(1, FEEDEQ_COEFF_SHIFT);
|
||||
|
||||
FEEDEQ_PREAMP_SHIFT = 31;
|
||||
FEEDEQ_PREAMP_ONE = shl(1, FEEDEQ_PREAMP_SHIFT);
|
||||
FEEDEQ_PREAMP_BITDB = 6; # 20.0 * (log(2.0) / log(10.0));
|
||||
|
||||
FEEDEQ_GAIN_DIV = 10;
|
||||
i = 0;
|
||||
j = 1;
|
||||
while (j < FEEDEQ_GAIN_DIV) {
|
||||
j *= 2;
|
||||
i++;
|
||||
}
|
||||
FEEDEQ_GAIN_SHIFT = i;
|
||||
FEEDEQ_GAIN_FMASK = shl(1, FEEDEQ_GAIN_SHIFT) - 1;
|
||||
|
||||
FEEDEQ_GAIN_RECIPROCAL = 1.0 / FEEDEQ_GAIN_DIV;
|
||||
|
||||
if (ARGC == 2) {
|
||||
i = 1;
|
||||
split(ARGV[1], arg, ":");
|
||||
while (match(arg[i], "^[^0-9]*$")) {
|
||||
if (arg[i] == "PEQ") {
|
||||
FEEDEQ_TYPE = FEEDEQ_TYPE_PEQ;
|
||||
} else if (arg[i] == "SHELF") {
|
||||
FEEDEQ_TYPE = FEEDEQ_TYPE_SHELF;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
split(arg[i++], subarg, ",");
|
||||
FEEDEQ_TREBLE_SFREQ = 1.0 * subarg[1];
|
||||
FEEDEQ_TREBLE_SLOPE = 1.0 * subarg[2];
|
||||
split(arg[i++], subarg, ",");
|
||||
FEEDEQ_BASS_SFREQ = 1.0 * subarg[1];
|
||||
FEEDEQ_BASS_SLOPE = 1.0 * subarg[2];
|
||||
split(arg[i++], subarg, ",");
|
||||
FEEDEQ_GAIN_MIN = floor(1.0 * subarg[1]);
|
||||
FEEDEQ_GAIN_MAX = floor(1.0 * subarg[2]);
|
||||
if (length(subarg) > 2) {
|
||||
j = floor(1.0 * FEEDEQ_GAIN_DIV * subarg[3]);
|
||||
if (j < 2)
|
||||
j = 1;
|
||||
else if (j < 5)
|
||||
j = 2;
|
||||
else if (j < 10)
|
||||
j = 5;
|
||||
else
|
||||
j = 10;
|
||||
if (j > FEEDEQ_GAIN_DIV || (FEEDEQ_GAIN_DIV % j) != 0)
|
||||
j = FEEDEQ_GAIN_DIV;
|
||||
FEEDEQ_GAIN_STEP = j;
|
||||
} else
|
||||
FEEDEQ_GAIN_STEP = FEEDEQ_GAIN_DIV;
|
||||
split(arg[i], subarg, ",");
|
||||
for (i = 1; i <= length(subarg); i++)
|
||||
allfreq[i - 1] = floor(1.0 * subarg[i]);
|
||||
} else {
|
||||
FEEDEQ_TREBLE_SFREQ = 16000.0;
|
||||
FEEDEQ_TREBLE_SLOPE = 0.25;
|
||||
FEEDEQ_BASS_SFREQ = 62.0;
|
||||
FEEDEQ_BASS_SLOPE = 0.25;
|
||||
|
||||
FEEDEQ_GAIN_MIN = -9;
|
||||
FEEDEQ_GAIN_MAX = 9;
|
||||
|
||||
FEEDEQ_GAIN_STEP = FEEDEQ_GAIN_DIV;
|
||||
|
||||
|
||||
allfreq[0] = 44100;
|
||||
allfreq[1] = 48000;
|
||||
allfreq[2] = 88200;
|
||||
allfreq[3] = 96000;
|
||||
allfreq[4] = 176400;
|
||||
allfreq[5] = 192000;
|
||||
}
|
||||
|
||||
FEEDEQ_LEVELS = ((FEEDEQ_GAIN_MAX - FEEDEQ_GAIN_MIN) * \
|
||||
floor(FEEDEQ_GAIN_DIV / FEEDEQ_GAIN_STEP)) + 1;
|
||||
|
||||
FEEDEQ_ERR_CLIP = 0;
|
||||
|
||||
smallest = 10.000000;
|
||||
largest = 0.000010;
|
||||
|
||||
printf("#ifndef _FEEDER_EQ_GEN_H_\n");
|
||||
printf("#define _FEEDER_EQ_GEN_H_\n\n");
|
||||
printf("/*\n");
|
||||
printf(" * Generated using feeder_eq_mkfilter.awk, heaven, wind and awesome.\n");
|
||||
printf(" *\n");
|
||||
printf(" * DO NOT EDIT!\n");
|
||||
printf(" */\n\n");
|
||||
printf("/*\n");
|
||||
printf(" * EQ: %s\n", (FEEDEQ_TYPE == FEEDEQ_TYPE_SHELF) ? \
|
||||
"Shelving" : "Peaking EQ");
|
||||
printf(" */\n");
|
||||
printf("#define FEEDER_EQ_PRESETS\t\"");
|
||||
printf("%s:%d,%.4f,%d,%.4f:%d,%d,%.1f:", \
|
||||
(FEEDEQ_TYPE == FEEDEQ_TYPE_SHELF) ? "SHELF" : "PEQ", \
|
||||
FEEDEQ_TREBLE_SFREQ, FEEDEQ_TREBLE_SLOPE, \
|
||||
FEEDEQ_BASS_SFREQ, FEEDEQ_BASS_SLOPE, \
|
||||
FEEDEQ_GAIN_MIN, FEEDEQ_GAIN_MAX, \
|
||||
FEEDEQ_GAIN_STEP * FEEDEQ_GAIN_RECIPROCAL);
|
||||
for (i = 0; i < length(allfreq); i++) {
|
||||
if (i != 0)
|
||||
printf(",");
|
||||
printf("%d", allfreq[i]);
|
||||
}
|
||||
printf("\"\n\n");
|
||||
printf("struct feed_eq_coeff_tone {\n");
|
||||
printf("\tint32_t a1, a2;\n");
|
||||
printf("\tint32_t b0, b1, b2;\n");
|
||||
printf("};\n\n");
|
||||
printf("struct feed_eq_coeff {\n");
|
||||
#printf("\tstruct {\n");
|
||||
#printf("\t\tint32_t a1, a2;\n");
|
||||
#printf("\t\tint32_t b0, b1, b2;\n");
|
||||
#printf("\t} treble, bass;\n");
|
||||
printf("\tstruct feed_eq_coeff_tone treble;\n");
|
||||
printf("\tstruct feed_eq_coeff_tone bass;\n");
|
||||
#printf("\tstruct {\n");
|
||||
#printf("\t\tint32_t a1, a2;\n");
|
||||
#printf("\t\tint32_t b0, b1, b2;\n");
|
||||
#printf("\t} bass;\n");
|
||||
printf("};\n");
|
||||
for (i = 0; i < length(allfreq); i++)
|
||||
feedeq_gen_freq_coeffs(allfreq[i]);
|
||||
printf("\n");
|
||||
printf("static const struct {\n");
|
||||
printf("\tuint32_t rate;\n");
|
||||
printf("\tstruct feed_eq_coeff *coeff;\n");
|
||||
printf("} feed_eq_tab[] = {\n");
|
||||
for (i = 0; i < length(allfreq); i++) {
|
||||
printf("\t{ %6d, eq_%-6d },\n", allfreq[i], allfreq[i]);
|
||||
}
|
||||
printf("};\n");
|
||||
|
||||
printf("\n#define FEEDEQ_RATE_MIN\t\t%d\n", allfreq[0]);
|
||||
printf("#define FEEDEQ_RATE_MAX\t\t%d\n", allfreq[length(allfreq) - 1]);
|
||||
printf("\n#define FEEDEQ_TAB_SIZE\t\t\t\t\t\t\t\\\n");
|
||||
printf("\t((int32_t)(sizeof(feed_eq_tab) / sizeof(feed_eq_tab[0])))\n");
|
||||
|
||||
printf("\nstatic const struct {\n");
|
||||
printf("\tint32_t mul, shift;\n");
|
||||
printf("} feed_eq_preamp[] = {\n");
|
||||
for (i = (FEEDEQ_GAIN_MAX * 2 * FEEDEQ_GAIN_DIV); i >= 0; \
|
||||
i -= FEEDEQ_GAIN_STEP) {
|
||||
feedeq_calc_preamp(norm, i * FEEDEQ_GAIN_RECIPROCAL);
|
||||
dbgain = ((FEEDEQ_GAIN_MAX * FEEDEQ_GAIN_DIV) - i) * \
|
||||
FEEDEQ_GAIN_RECIPROCAL;
|
||||
printf("\t{ 0x%08x, 0x%08x },\t/* %+5.1f dB */\n", \
|
||||
norm["mul"], norm["shift"], dbgain);
|
||||
}
|
||||
printf("};\n");
|
||||
|
||||
printf("\n#define FEEDEQ_GAIN_MIN\t\t%d", FEEDEQ_GAIN_MIN);
|
||||
printf("\n#define FEEDEQ_GAIN_MAX\t\t%d\n", FEEDEQ_GAIN_MAX);
|
||||
|
||||
printf("\n#define FEEDEQ_GAIN_SHIFT\t%d\n", FEEDEQ_GAIN_SHIFT);
|
||||
printf("#define FEEDEQ_GAIN_DIV\t\t%d\n", FEEDEQ_GAIN_DIV);
|
||||
printf("#define FEEDEQ_GAIN_FMASK\t0x%08x\n", FEEDEQ_GAIN_FMASK);
|
||||
printf("#define FEEDEQ_GAIN_STEP\t%d\n", FEEDEQ_GAIN_STEP);
|
||||
|
||||
#printf("\n#define FEEDEQ_PREAMP_MIN\t-%d\n", \
|
||||
# shl(FEEDEQ_GAIN_MAX, FEEDEQ_GAIN_SHIFT));
|
||||
#printf("#define FEEDEQ_PREAMP_MAX\t%d\n", \
|
||||
# shl(FEEDEQ_GAIN_MAX, FEEDEQ_GAIN_SHIFT));
|
||||
|
||||
printf("\n#define FEEDEQ_COEFF_SHIFT\t%d\n", FEEDEQ_COEFF_SHIFT);
|
||||
|
||||
#feedeq_calc_preamp(norm, FEEDEQ_GAIN_MAX);
|
||||
|
||||
#printf("#define FEEDEQ_COEFF_NORM(v)\t(");
|
||||
#if (norm["mul"] == 1)
|
||||
# printf("(v) >> %d", norm["shift"]);
|
||||
#else
|
||||
# printf("(0x%xLL * (v)) >> %d", norm["mul"], norm["shift"]);
|
||||
#printf(")\n");
|
||||
|
||||
#printf("\n#define FEEDEQ_LEVELS\t\t%d\n", FEEDEQ_LEVELS);
|
||||
if (FEEDEQ_ERR_CLIP != 0)
|
||||
printf("\n#define FEEDEQ_ERR_CLIP\t\t%d\n", FEEDEQ_ERR_CLIP);
|
||||
printf("\n/*\n");
|
||||
printf(" * volume level mapping (0 - 100):\n");
|
||||
printf(" *\n");
|
||||
|
||||
for (i = 0; i <= 100; i++) {
|
||||
ind = floor((i * FEEDEQ_LEVELS) / 100);
|
||||
if (ind >= FEEDEQ_LEVELS)
|
||||
ind = FEEDEQ_LEVELS - 1;
|
||||
printf(" *\t%3d -> %3d (%+5.1f dB)\n", \
|
||||
i, ind, FEEDEQ_GAIN_MIN + \
|
||||
(ind * (FEEDEQ_GAIN_RECIPROCAL * FEEDEQ_GAIN_STEP)));
|
||||
}
|
||||
|
||||
printf(" */\n");
|
||||
printf("\n/*\n * smallest: %.32f\n * largest: %.32f\n */\n", \
|
||||
smallest, largest);
|
||||
printf("\n#endif\t/* !_FEEDER_EQ_GEN_H_ */\n");
|
||||
}
|
767
sys/tools/feeder_rate_mkfilter.awk
Normal file
767
sys/tools/feeder_rate_mkfilter.awk
Normal file
@ -0,0 +1,767 @@
|
||||
#!/usr/bin/awk -f
|
||||
#
|
||||
# Copyright (c) 2007-2009 Ariff Abdullah <ariff@FreeBSD.org>
|
||||
# All rights reserved.
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# $FreeBSD$
|
||||
#
|
||||
|
||||
#
|
||||
# FIR filter design by windowing method. This might become one of the
|
||||
# funniest joke I've ever written due to too many tricks being applied to
|
||||
# ensure maximum precision (well, in fact this is already have the same
|
||||
# precision granularity compared to its C counterpart). Nevertheless, it's
|
||||
# working, precise, dynamically tunable based on "presets".
|
||||
#
|
||||
# XXX EXPECT TOTAL REWRITE! DON'T ARGUE!
|
||||
#
|
||||
# TODO: Using ultraspherical window might be a good idea.
|
||||
#
|
||||
# Based on:
|
||||
#
|
||||
# "Digital Audio Resampling" by Julius O. Smith III
|
||||
#
|
||||
# - http://ccrma.stanford.edu/~jos/resample/
|
||||
#
|
||||
|
||||
#
|
||||
# Some basic Math functions.
|
||||
#
|
||||
function abs(x)
|
||||
{
|
||||
return (((x < 0) ? -x : x) + 0);
|
||||
}
|
||||
|
||||
function fabs(x)
|
||||
{
|
||||
return (((x < 0.0) ? -x : x) + 0.0);
|
||||
}
|
||||
|
||||
function ceil(x, r)
|
||||
{
|
||||
r = int(x);
|
||||
if (r < x)
|
||||
r++;
|
||||
return (r + 0);
|
||||
}
|
||||
|
||||
function floor(x, r)
|
||||
{
|
||||
r = int(x);
|
||||
if (r > x)
|
||||
r--;
|
||||
return (r + 0);
|
||||
}
|
||||
|
||||
function pow(x, y)
|
||||
{
|
||||
return (exp(1.0 * y * log(1.0 * x)));
|
||||
}
|
||||
|
||||
#
|
||||
# What the hell...
|
||||
#
|
||||
function shl(x, y)
|
||||
{
|
||||
while (y > 0) {
|
||||
x *= 2;
|
||||
y--;
|
||||
}
|
||||
return (x);
|
||||
}
|
||||
|
||||
function shr(x, y)
|
||||
{
|
||||
while (y > 0 && x != 0) {
|
||||
x = floor(x / 2);
|
||||
y--;
|
||||
}
|
||||
return (x);
|
||||
}
|
||||
|
||||
function fx_floor(v, o, r)
|
||||
{
|
||||
if (fabs(v) < fabs(smallest))
|
||||
smallest = v;
|
||||
if (fabs(v) > fabs(largest))
|
||||
largest = v;
|
||||
|
||||
r = floor((v * o) + 0.5);
|
||||
if (r < INT32_MIN || r > INT32_MAX)
|
||||
printf("\n#error overflow v=%f, please reduce %d\n", v, o);
|
||||
|
||||
return (r);
|
||||
}
|
||||
|
||||
#
|
||||
# Kaiser linear piecewise functions.
|
||||
#
|
||||
function kaiserAttn2Beta(attn, beta)
|
||||
{
|
||||
if (attn < 0.0)
|
||||
return (Z_KAISER_BETA_DEFAULT);
|
||||
|
||||
if (attn > 50.0)
|
||||
beta = 0.1102 * ((1.0 * attn) - 8.7);
|
||||
else if (attn > 21.0)
|
||||
beta = (0.5842 * pow((1.0 * attn) - 21.0, 0.4)) + \
|
||||
(0.07886 * ((1.0 * attn) - 21.0));
|
||||
else
|
||||
beta = 0.0;
|
||||
|
||||
return (beta);
|
||||
}
|
||||
|
||||
function kaiserBeta2Attn(beta, x, y, i, attn, xbeta)
|
||||
{
|
||||
if (beta < Z_WINDOW_KAISER)
|
||||
return (Z_KAISER_ATTN_DEFAULT);
|
||||
|
||||
if (beta > kaiserAttn2Beta(50.0))
|
||||
attn = ((1.0 * beta) / 0.1102) + 8.7;
|
||||
else {
|
||||
x = 21.0;
|
||||
y = 50.0;
|
||||
attn = 0.5 * (x + y);
|
||||
for (i = 0; i < 128; i++) {
|
||||
xbeta = kaiserAttn2Beta(attn)
|
||||
if (beta == xbeta || \
|
||||
(i > 63 && \
|
||||
fabs(beta - xbeta) < Z_KAISER_EPSILON))
|
||||
break;
|
||||
if (beta > xbeta)
|
||||
x = attn;
|
||||
else
|
||||
y = attn;
|
||||
attn = 0.5 * (x + y);
|
||||
}
|
||||
}
|
||||
|
||||
return (attn);
|
||||
}
|
||||
|
||||
function kaiserRolloff(len, attn)
|
||||
{
|
||||
return (1.0 - (((1.0 * attn) - 7.95) / (((1.0 * len) - 1.0) * 14.36)));
|
||||
}
|
||||
|
||||
#
|
||||
# 0th order modified Bessel function of the first kind.
|
||||
#
|
||||
function I0(x, s, u, n, h, t)
|
||||
{
|
||||
s = n = u = 1.0;
|
||||
h = x * 0.5;
|
||||
|
||||
do {
|
||||
t = h / n;
|
||||
n += 1.0;
|
||||
t *= t;
|
||||
u *= t;
|
||||
s += u;
|
||||
} while (u >= (I0_EPSILON * s));
|
||||
|
||||
return (s);
|
||||
}
|
||||
|
||||
function wname(beta)
|
||||
{
|
||||
if (beta >= Z_WINDOW_KAISER)
|
||||
return ("Kaiser");
|
||||
else if (beta == Z_WINDOW_BLACKMAN_NUTTALL)
|
||||
return ("Blackman - Nuttall");
|
||||
else if (beta == Z_WINDOW_NUTTALL)
|
||||
return ("Nuttall");
|
||||
else if (beta == Z_WINDOW_BLACKMAN_HARRIS)
|
||||
return ("Blackman - Harris");
|
||||
else if (beta == Z_WINDOW_BLACKMAN)
|
||||
return ("Blackman");
|
||||
else if (beta == Z_WINDOW_HAMMING)
|
||||
return ("Hamming");
|
||||
else if (beta == Z_WINDOW_HANN)
|
||||
return ("Hann");
|
||||
else
|
||||
return ("What The Hell !?!?");
|
||||
}
|
||||
|
||||
function rolloff_round(x)
|
||||
{
|
||||
if (x < 0.67)
|
||||
x = 0.67;
|
||||
else if (x > 1.0)
|
||||
x = 1.0;
|
||||
|
||||
return (x);
|
||||
}
|
||||
|
||||
function tap_round(x, y)
|
||||
{
|
||||
y = floor(x + 3);
|
||||
y -= y % 4;
|
||||
return (y);
|
||||
}
|
||||
|
||||
function lpf(imp, n, rolloff, beta, num, i, j, x, nm, ibeta, w)
|
||||
{
|
||||
rolloff = rolloff_round(rolloff + (Z_NYQUIST_HOVER * (1.0 - rolloff)));
|
||||
imp[0] = rolloff;
|
||||
|
||||
#
|
||||
# Generate ideal sinc impulses, locate the last zero-crossing and pad
|
||||
# the remaining with 0.
|
||||
#
|
||||
# Note that there are other (faster) ways of calculating this without
|
||||
# the misery of traversing the entire sinc given the fact that the
|
||||
# distance between each zero crossings is actually the bandwidth of
|
||||
# the impulses, but it seems having 0.0001% chances of failure due to
|
||||
# limited precision.
|
||||
#
|
||||
j = n;
|
||||
for (i = 1; i < n; i++) {
|
||||
x = (M_PI * i) / (1.0 * num);
|
||||
imp[i] = sin(x * rolloff) / x;
|
||||
if (i != 1 && (imp[i] * imp[i - 1]) <= 0.0)
|
||||
j = i;
|
||||
}
|
||||
|
||||
for (i = j; i < n; i++)
|
||||
imp[i] = 0.0;
|
||||
|
||||
nm = 1.0 * (j - 1);
|
||||
|
||||
if (beta >= Z_WINDOW_KAISER)
|
||||
ibeta = I0(beta);
|
||||
|
||||
for (i = 1; i < j; i++) {
|
||||
if (beta >= Z_WINDOW_KAISER) {
|
||||
# Kaiser window...
|
||||
x = (1.0 * i) / nm;
|
||||
w = I0(beta * sqrt(1.0 - (x * x))) / ibeta;
|
||||
} else {
|
||||
# Cosined windows...
|
||||
x = (M_PI * i) / nm;
|
||||
if (beta == Z_WINDOW_BLACKMAN_NUTTALL) {
|
||||
# Blackman - Nuttall
|
||||
w = 0.36335819 + (0.4891775 * cos(x)) + \
|
||||
(0.1365995 * cos(2 * x)) + \
|
||||
(0.0106411 * cos(3 * x));
|
||||
} else if (beta == Z_WINDOW_NUTTALL) {
|
||||
# Nuttall
|
||||
w = 0.355768 + (0.487396 * cos(x)) + \
|
||||
(0.144232 * cos(2 * x)) + \
|
||||
(0.012604 * cos(3 * x));
|
||||
} else if (beta == Z_WINDOW_BLACKMAN_HARRIS) {
|
||||
# Blackman - Harris
|
||||
w = 0.422323 + (0.49755 * cos(x)) + \
|
||||
(0.07922 * cos(2 * x));
|
||||
} else if (beta == Z_WINDOW_BLACKMAN) {
|
||||
# Blackman
|
||||
w = 0.42 + (0.50 * cos(x)) + \
|
||||
(0.08 * cos(2 * x));
|
||||
} else if (beta == Z_WINDOW_HAMMING) {
|
||||
# Hamming
|
||||
w = 0.54 + (0.46 * cos(x));
|
||||
} else if (beta == Z_WINDOW_HANN) {
|
||||
# Hann
|
||||
w = 0.50 + (0.50 * cos(x));
|
||||
} else {
|
||||
# What The Hell !?!?
|
||||
w = 0.0;
|
||||
}
|
||||
}
|
||||
imp[i] *= w;
|
||||
}
|
||||
|
||||
imp["impulse_length"] = j;
|
||||
imp["rolloff"] = rolloff;
|
||||
}
|
||||
|
||||
function mkfilter(imp, nmult, rolloff, beta, num, \
|
||||
nwing, mwing, nrolloff, i, dcgain, v, quality)
|
||||
{
|
||||
nwing = floor((nmult * num) / 2) + 1;
|
||||
|
||||
lpf(imp, nwing, rolloff, beta, num);
|
||||
|
||||
mwing = imp["impulse_length"];
|
||||
nrolloff = imp["rolloff"];
|
||||
quality = imp["quality"];
|
||||
|
||||
dcgain = 0.0;
|
||||
for (i = num; i < mwing; i += num)
|
||||
dcgain += imp[i];
|
||||
dcgain *= 2.0;
|
||||
dcgain += imp[0];
|
||||
|
||||
for (i = 0; i < nwing; i++)
|
||||
imp[i] /= dcgain;
|
||||
|
||||
if (quality > 2)
|
||||
printf("\n");
|
||||
printf("/*\n");
|
||||
printf(" * quality = %d\n", quality);
|
||||
printf(" * window = %s\n", wname(beta));
|
||||
if (beta >= Z_WINDOW_KAISER) {
|
||||
printf(" * beta: %.2f\n", beta);
|
||||
printf(" * stop: -%.2f dB\n", \
|
||||
kaiserBeta2Attn(beta));
|
||||
}
|
||||
printf(" * length = %d\n", nmult);
|
||||
printf(" * bandwidth = %.2f%%", rolloff * 100.0);
|
||||
if (rolloff != nrolloff) {
|
||||
printf(" + %.2f%% = %.2f%% (nyquist hover: %.2f%%)", \
|
||||
(nrolloff - rolloff) * 100.0, nrolloff * 100.0, \
|
||||
Z_NYQUIST_HOVER * 100.0);
|
||||
}
|
||||
printf("\n");
|
||||
printf(" * drift = %d\n", num);
|
||||
printf(" * width = %d\n", mwing);
|
||||
printf(" */\n");
|
||||
printf("static int32_t z_coeff_q%d[%d] = {", \
|
||||
quality, nwing + (Z_COEFF_OFFSET * 2));
|
||||
for (i = 0; i < (nwing + (Z_COEFF_OFFSET * 2)); i++) {
|
||||
if ((i % 5) == 0)
|
||||
printf("\n ");
|
||||
if (i < Z_COEFF_OFFSET)
|
||||
v = fx_floor(imp[Z_COEFF_OFFSET - i], Z_COEFF_ONE);
|
||||
else if ((i - Z_COEFF_OFFSET) >= nwing)
|
||||
v = fx_floor( \
|
||||
imp[nwing + nwing - i + Z_COEFF_OFFSET - 1],\
|
||||
Z_COEFF_ONE);
|
||||
else
|
||||
v = fx_floor(imp[i - Z_COEFF_OFFSET], Z_COEFF_ONE);
|
||||
printf(" %s0x%08x,", (v < 0) ? "-" : " ", abs(v));
|
||||
}
|
||||
printf("\n};\n\n");
|
||||
printf("/*\n");
|
||||
printf(" * interpolated q%d differences.\n", quality);
|
||||
printf(" */\n");
|
||||
printf("static int32_t z_dcoeff_q%d[%d] = {", quality, nwing);
|
||||
for (i = 1; i <= nwing; i++) {
|
||||
if ((i % 5) == 1)
|
||||
printf("\n ");
|
||||
v = -imp[i - 1];
|
||||
if (i != nwing)
|
||||
v += imp[i];
|
||||
v = fx_floor(v, Z_INTERP_COEFF_ONE);
|
||||
if (abs(v) > abs(largest_interp))
|
||||
largest_interp = v;
|
||||
printf(" %s0x%08x,", (v < 0) ? "-" : " ", abs(v));
|
||||
}
|
||||
printf("\n};\n");
|
||||
|
||||
return (nwing);
|
||||
}
|
||||
|
||||
function filter_parse(s, a, i, attn, alen)
|
||||
{
|
||||
split(s, a, ":");
|
||||
alen = length(a);
|
||||
|
||||
if (alen == 1 || alen == 2) {
|
||||
if (a[1] == "nyquist_hover") {
|
||||
i = 1.0 * a[2];
|
||||
Z_NYQUIST_HOVER = (i > 0.0 && i < 1.0) ? i : 0.0;
|
||||
return (-1);
|
||||
}
|
||||
i = 1;
|
||||
if (alen == 1) {
|
||||
attn = Z_KAISER_ATTN_DEFAULT;
|
||||
Popts["beta"] = Z_KAISER_BETA_DEFAULT;
|
||||
} else if (Z_WINDOWS[a[1]] < Z_WINDOW_KAISER) {
|
||||
Popts["beta"] = Z_WINDOWS[a[1]];
|
||||
i = tap_round(a[2]);
|
||||
Popts["nmult"] = i;
|
||||
if (i < 28)
|
||||
i = 28;
|
||||
i = 1.0 - (6.44 / i);
|
||||
Popts["rolloff"] = rolloff_round(i);
|
||||
return (0);
|
||||
} else {
|
||||
attn = 1.0 * a[i++];
|
||||
Popts["beta"] = kaiserAttn2Beta(attn);
|
||||
}
|
||||
i = tap_round(a[i]);
|
||||
Popts["nmult"] = i;
|
||||
if (i > 7 && i < 28)
|
||||
i = 27;
|
||||
i = kaiserRolloff(i, attn);
|
||||
Popts["rolloff"] = rolloff_round(i);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (!(alen == 3 || alen == 4))
|
||||
return (-1);
|
||||
|
||||
i = 2;
|
||||
|
||||
if (a[1] == "kaiser") {
|
||||
if (alen > 2)
|
||||
Popts["beta"] = 1.0 * a[i++];
|
||||
else
|
||||
Popts["beta"] = Z_KAISER_BETA_DEFAULT;
|
||||
} else if (Z_WINDOWS[a[1]] < Z_WINDOW_KAISER)
|
||||
Popts["beta"] = Z_WINDOWS[a[1]];
|
||||
else if (1.0 * a[1] < Z_WINDOW_KAISER)
|
||||
return (-1);
|
||||
else
|
||||
Popts["beta"] = kaiserAttn2Beta(1.0 * a[1]);
|
||||
Popts["nmult"] = tap_round(a[i++]);
|
||||
if (a[1] == "kaiser" && alen == 3)
|
||||
i = kaiserRolloff(Popts["nmult"], \
|
||||
kaiserBeta2Attn(Popts["beta"]));
|
||||
else
|
||||
i = 1.0 * a[i];
|
||||
Popts["rolloff"] = rolloff_round(i);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
function genscale(bit, s1, s2, scale)
|
||||
{
|
||||
s1 = Z_COEFF_SHIFT - (32 - bit);
|
||||
s2 = Z_SHIFT + (32 - bit);
|
||||
|
||||
if (s1 == 0)
|
||||
scale = "v";
|
||||
else if (s1 < 0)
|
||||
scale = sprintf("(v) << %d", abs(s1));
|
||||
else
|
||||
scale = sprintf("(v) >> %d", s1);
|
||||
|
||||
scale = sprintf("(%s) * Z_SCALE_CAST(s)", scale);
|
||||
|
||||
if (s2 != 0)
|
||||
scale = sprintf("(%s) >> %d", scale, s2);
|
||||
|
||||
printf("#define Z_SCALE_%d(v, s)\t%s(%s)\n", \
|
||||
bit, (bit < 10) ? "\t" : "", scale);
|
||||
}
|
||||
|
||||
function genlerp(bit, use64, lerp)
|
||||
{
|
||||
if ((bit + Z_LINEAR_SHIFT) <= 32) {
|
||||
lerp = sprintf("(((y) - (x)) * (z)) >> %d", Z_LINEAR_SHIFT);
|
||||
} else if (use64 != 0) {
|
||||
if ((bit + Z_LINEAR_SHIFT) <= 64) {
|
||||
lerp = sprintf( \
|
||||
"(((int64_t)(y) - (x)) * (z)) " \
|
||||
">> %d", \
|
||||
Z_LINEAR_SHIFT);
|
||||
} else {
|
||||
lerp = sprintf( \
|
||||
"((int64_t)((y) >> %d) - ((x) >> %d)) * ", \
|
||||
"(z)" \
|
||||
bit + Z_LINEAR_SHIFT - 64, \
|
||||
bit + Z_LINEAR_SHIFT - 64);
|
||||
if ((64 - bit) != 0)
|
||||
lerp = sprintf("(%s) >> %d", lerp, 64 - bit);
|
||||
}
|
||||
} else {
|
||||
lerp = sprintf( \
|
||||
"(((y) >> %d) - ((x) >> %d)) * (z)", \
|
||||
bit + Z_LINEAR_SHIFT - 32, \
|
||||
bit + Z_LINEAR_SHIFT - 32);
|
||||
if ((32 - bit) != 0)
|
||||
lerp = sprintf("(%s) >> %d", lerp, 32 - bit);
|
||||
}
|
||||
|
||||
printf("#define Z_LINEAR_INTERPOLATE_%d(z, x, y)" \
|
||||
"\t\t\t\t%s\\\n\t((x) + (%s))\n", \
|
||||
bit, (bit < 10) ? "\t" : "", lerp);
|
||||
}
|
||||
|
||||
BEGIN {
|
||||
I0_EPSILON = 1e-21;
|
||||
M_PI = atan2(0.0, -1.0);
|
||||
|
||||
INT32_MAX = 1 + ((shl(1, 30) - 1) * 2);
|
||||
INT32_MIN = -1 - INT32_MAX;
|
||||
|
||||
Z_COEFF_OFFSET = 5;
|
||||
|
||||
Z_FULL_SHIFT = 30;
|
||||
Z_FULL_ONE = shl(1, Z_FULL_SHIFT);
|
||||
|
||||
Z_COEFF_SHIFT = 28;
|
||||
Z_COEFF_ONE = shl(1, Z_COEFF_SHIFT);
|
||||
|
||||
Z_INTERP_COEFF_SHIFT = 24;
|
||||
Z_INTERP_COEFF_ONE = shl(1, Z_INTERP_COEFF_SHIFT);
|
||||
|
||||
#
|
||||
# Filter oversampling factor.
|
||||
#
|
||||
# 6, 7, or 8 depending on how much you can trade off between memory
|
||||
# consumption (due to large tables) and precision / quality.
|
||||
#
|
||||
Z_DRIFT_SHIFT = 7;
|
||||
Z_DRIFT_ONE = shl(1, Z_DRIFT_SHIFT);
|
||||
|
||||
Z_SHIFT = Z_FULL_SHIFT - Z_DRIFT_SHIFT;
|
||||
Z_ONE = shl(1, Z_SHIFT);
|
||||
Z_MASK = Z_ONE - 1;
|
||||
|
||||
Z_LINEAR_FULL_SHIFT = Z_FULL_SHIFT;
|
||||
Z_LINEAR_FULL_ONE = shl(1, Z_LINEAR_FULL_SHIFT);
|
||||
Z_LINEAR_SHIFT = 8;
|
||||
Z_LINEAR_UNSHIFT = Z_LINEAR_FULL_SHIFT - Z_LINEAR_SHIFT;
|
||||
Z_LINEAR_ONE = shl(1, Z_LINEAR_SHIFT)
|
||||
|
||||
# meehhhh... let it overflow...
|
||||
#Z_SCALE_SHIFT = 31;
|
||||
#Z_SCALE_ONE = shl(1, Z_SCALE_SHIFT);
|
||||
|
||||
Z_WINDOW_KAISER = 0.0;
|
||||
Z_WINDOW_BLACKMAN_NUTTALL = -1.0;
|
||||
Z_WINDOW_NUTTALL = -2.0;
|
||||
Z_WINDOW_BLACKMAN_HARRIS = -3.0;
|
||||
Z_WINDOW_BLACKMAN = -4.0;
|
||||
Z_WINDOW_HAMMING = -5.0;
|
||||
Z_WINDOW_HANN = -6.0;
|
||||
|
||||
Z_WINDOWS["blackman_nuttall"] = Z_WINDOW_BLACKMAN_NUTTALL;
|
||||
Z_WINDOWS["nuttall"] = Z_WINDOW_NUTTALL;
|
||||
Z_WINDOWS["blackman_harris"] = Z_WINDOW_BLACKMAN_HARRIS;
|
||||
Z_WINDOWS["blackman"] = Z_WINDOW_BLACKMAN;
|
||||
Z_WINDOWS["hamming"] = Z_WINDOW_HAMMING;
|
||||
Z_WINDOWS["hann"] = Z_WINDOW_HANN;
|
||||
|
||||
Z_KAISER_2_BLACKMAN_BETA = 8.568611;
|
||||
Z_KAISER_2_BLACKMAN_NUTTALL_BETA = 11.98;
|
||||
|
||||
Z_KAISER_ATTN_DEFAULT = 100;
|
||||
Z_KAISER_BETA_DEFAULT = kaiserAttn2Beta(Z_KAISER_ATTN_DEFAULT);
|
||||
|
||||
Z_KAISER_EPSILON = 1e-21;
|
||||
|
||||
#
|
||||
# This is practically a joke.
|
||||
#
|
||||
Z_NYQUIST_HOVER = 0.0;
|
||||
|
||||
smallest = 10.000000;
|
||||
largest = 0.000010;
|
||||
largest_interp = 0;
|
||||
|
||||
if (ARGC < 2) {
|
||||
ARGC = 1;
|
||||
ARGV[ARGC++] = "100:8:0.85";
|
||||
ARGV[ARGC++] = "100:36:0.90";
|
||||
ARGV[ARGC++] = "100:164:0.97";
|
||||
#ARGV[ARGC++] = "100:8";
|
||||
#ARGV[ARGC++] = "100:16";
|
||||
#ARGV[ARGC++] = "100:32:0.7929";
|
||||
#ARGV[ARGC++] = "100:64:0.8990";
|
||||
#ARGV[ARGC++] = "100:128:0.9499";
|
||||
}
|
||||
|
||||
printf("#ifndef _FEEDER_RATE_GEN_H_\n");
|
||||
printf("#define _FEEDER_RATE_GEN_H_\n\n");
|
||||
printf("/*\n");
|
||||
printf(" * Generated using feeder_rate_mkfilter.awk, heaven, wind and awesome.\n");
|
||||
printf(" *\n");
|
||||
printf(" * DO NOT EDIT!\n");
|
||||
printf(" */\n\n");
|
||||
printf("#define FEEDER_RATE_PRESETS\t\"");
|
||||
for (i = 1; i < ARGC; i++)
|
||||
printf("%s%s", (i == 1) ? "" : " ", ARGV[i]);
|
||||
printf("\"\n\n");
|
||||
imp["quality"] = 2;
|
||||
for (i = 1; i < ARGC; i++) {
|
||||
if (filter_parse(ARGV[i]) == 0) {
|
||||
beta = Popts["beta"];
|
||||
nmult = Popts["nmult"];
|
||||
rolloff = Popts["rolloff"];
|
||||
ztab[imp["quality"] - 2] = \
|
||||
mkfilter(imp, nmult, rolloff, beta, Z_DRIFT_ONE);
|
||||
imp["quality"]++;
|
||||
}
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
#
|
||||
# XXX
|
||||
#
|
||||
#if (length(ztab) > 0) {
|
||||
# j = 0;
|
||||
# for (i = 0; i < length(ztab); i++) {
|
||||
# if (ztab[i] > j)
|
||||
# j = ztab[i];
|
||||
# }
|
||||
# printf("static int32_t z_coeff_zero[%d] = {", j);
|
||||
# for (i = 0; i < j; i++) {
|
||||
# if ((i % 19) == 0)
|
||||
# printf("\n");
|
||||
# printf(" 0,");
|
||||
# }
|
||||
# printf("\n};\n\n");
|
||||
#}
|
||||
#
|
||||
# XXX
|
||||
#
|
||||
printf("static const struct {\n");
|
||||
printf("\tint32_t len;\n");
|
||||
printf("\tint32_t *coeff;\n");
|
||||
printf("\tint32_t *dcoeff;\n");
|
||||
printf("} z_coeff_tab[] = {\n");
|
||||
if (length(ztab) > 0) {
|
||||
j = 0;
|
||||
for (i = 0; i < length(ztab); i++) {
|
||||
if (ztab[i] > j)
|
||||
j = ztab[i];
|
||||
}
|
||||
j = length(sprintf("%d", j));
|
||||
lfmt = sprintf("%%%dd", j);
|
||||
j = length(sprintf("z_coeff_q%d", length(ztab) + 1));
|
||||
zcfmt = sprintf("%%-%ds", j);
|
||||
zdcfmt = sprintf("%%-%ds", j + 1);
|
||||
|
||||
for (i = 0; i < length(ztab); i++) {
|
||||
l = sprintf(lfmt, ztab[i]);
|
||||
zc = sprintf("z_coeff_q%d", i + 2);
|
||||
zc = sprintf(zcfmt, zc);
|
||||
zdc = sprintf("z_dcoeff_q%d", i + 2);
|
||||
zdc = sprintf(zdcfmt, zdc);
|
||||
printf("\t{ %s, %s, %s },\n", l, zc, zdc);
|
||||
}
|
||||
} else
|
||||
printf("\t{ 0, NULL, NULL }\n");
|
||||
printf("};\n\n");
|
||||
|
||||
#Z_UNSHIFT = 0;
|
||||
#v = shr(Z_ONE - 1, Z_UNSHIFT) * abs(largest_interp);
|
||||
#while (v < 0 || v > INT32_MAX) {
|
||||
# Z_UNSHIFT += 1;
|
||||
# v = shr(Z_ONE - 1, Z_UNSHIFT) * abs(largest_interp);
|
||||
#}
|
||||
v = ((Z_ONE - 1) * abs(largest_interp)) / INT32_MAX;
|
||||
Z_UNSHIFT = ceil(log(v) / log(2.0));
|
||||
Z_INTERP_SHIFT = Z_SHIFT - Z_UNSHIFT + Z_INTERP_COEFF_SHIFT;
|
||||
|
||||
Z_INTERP_UNSHIFT = (Z_SHIFT - Z_UNSHIFT) + Z_INTERP_COEFF_SHIFT \
|
||||
- Z_COEFF_SHIFT;
|
||||
|
||||
printf("#define Z_COEFF_TAB_SIZE\t\t\t\t\t\t\\\n");
|
||||
printf("\t((int32_t)(sizeof(z_coeff_tab) /");
|
||||
printf(" sizeof(z_coeff_tab[0])))\n\n");
|
||||
printf("#define Z_COEFF_OFFSET\t\t%d\n\n", Z_COEFF_OFFSET);
|
||||
printf("#define Z_RSHIFT(x, y)\t\t(((x) + " \
|
||||
"(1 << ((y) - 1))) >> (y))\n");
|
||||
printf("#define Z_RSHIFT_L(x, y)\t(((x) + " \
|
||||
"(1LL << ((y) - 1))) >> (y))\n\n");
|
||||
printf("#define Z_FULL_SHIFT\t\t%d\n", Z_FULL_SHIFT);
|
||||
printf("#define Z_FULL_ONE\t\t0x%08x%s\n", Z_FULL_ONE, \
|
||||
(Z_FULL_ONE > INT32_MAX) ? "U" : "");
|
||||
printf("\n");
|
||||
printf("#define Z_DRIFT_SHIFT\t\t%d\n", Z_DRIFT_SHIFT);
|
||||
#printf("#define Z_DRIFT_ONE\t\t0x%08x\n", Z_DRIFT_ONE);
|
||||
printf("\n");
|
||||
printf("#define Z_SHIFT\t\t\t%d\n", Z_SHIFT);
|
||||
printf("#define Z_ONE\t\t\t0x%08x\n", Z_ONE);
|
||||
printf("#define Z_MASK\t\t\t0x%08x\n", Z_MASK);
|
||||
printf("\n");
|
||||
printf("#define Z_COEFF_SHIFT\t\t%d\n", Z_COEFF_SHIFT);
|
||||
zinterphp = "(z) * (d)";
|
||||
zinterpunshift = Z_SHIFT + Z_INTERP_COEFF_SHIFT - Z_COEFF_SHIFT;
|
||||
if (zinterpunshift > 0) {
|
||||
v = (Z_ONE - 1) * abs(largest_interp);
|
||||
if (v < INT32_MIN || v > INT32_MAX)
|
||||
zinterphp = sprintf("(int64_t)%s", zinterphp);
|
||||
zinterphp = sprintf("(%s) >> %d", zinterphp, zinterpunshift);
|
||||
} else if (zinterpunshift < 0)
|
||||
zinterphp = sprintf("(%s) << %d", zinterphp, \
|
||||
abs(zinterpunshift));
|
||||
if (Z_UNSHIFT == 0)
|
||||
zinterp = "z";
|
||||
else
|
||||
zinterp = sprintf("(z) >> %d", Z_UNSHIFT);
|
||||
zinterp = sprintf("(%s) * (d)", zinterp);
|
||||
if (Z_INTERP_UNSHIFT < 0)
|
||||
zinterp = sprintf("(%s) << %d", zinterp, \
|
||||
abs(Z_INTERP_UNSHIFT));
|
||||
else if (Z_INTERP_UNSHIFT > 0)
|
||||
zinterp = sprintf("(%s) >> %d", zinterp, Z_INTERP_UNSHIFT);
|
||||
if (zinterphp != zinterp) {
|
||||
printf("\n#ifdef SND_FEEDER_RATE_HP\n");
|
||||
printf("#define Z_COEFF_INTERPOLATE(z, c, d)" \
|
||||
"\t\t\t\t\t\\\n\t((c) + (%s))\n", zinterphp);
|
||||
printf("#else\n");
|
||||
printf("#define Z_COEFF_INTERPOLATE(z, c, d)" \
|
||||
"\t\t\t\t\t\\\n\t((c) + (%s))\n", zinterp);
|
||||
printf("#endif\n");
|
||||
} else
|
||||
printf("#define Z_COEFF_INTERPOLATE(z, c, d)" \
|
||||
"\t\t\t\t\t\\\n\t((c) + (%s))\n", zinterp);
|
||||
#printf("\n");
|
||||
#printf("#define Z_SCALE_SHIFT\t\t%d\n", Z_SCALE_SHIFT);
|
||||
#printf("#define Z_SCALE_ONE\t\t0x%08x%s\n", Z_SCALE_ONE, \
|
||||
# (Z_SCALE_ONE > INT32_MAX) ? "U" : "");
|
||||
printf("\n");
|
||||
printf("#define Z_SCALE_CAST(s)\t\t((uint32_t)(s))\n");
|
||||
genscale(8);
|
||||
genscale(16);
|
||||
genscale(24);
|
||||
genscale(32);
|
||||
printf("\n");
|
||||
printf("#define Z_LINEAR_FULL_ONE\t0x%08xU\n", Z_LINEAR_FULL_ONE);
|
||||
printf("#define Z_LINEAR_SHIFT\t\t%d\n", Z_LINEAR_SHIFT);
|
||||
printf("#define Z_LINEAR_UNSHIFT\t%d\n", Z_LINEAR_UNSHIFT);
|
||||
printf("#define Z_LINEAR_ONE\t\t0x%08x\n", Z_LINEAR_ONE);
|
||||
printf("\n");
|
||||
printf("#ifdef SND_PCM_64\n");
|
||||
genlerp(8, 1);
|
||||
genlerp(16, 1);
|
||||
genlerp(24, 1);
|
||||
genlerp(32, 1);
|
||||
printf("#else\t/* !SND_PCM_64 */\n");
|
||||
genlerp(8, 0);
|
||||
genlerp(16, 0);
|
||||
genlerp(24, 0);
|
||||
genlerp(32, 0);
|
||||
printf("#endif\t/* SND_PCM_64 */\n");
|
||||
printf("\n");
|
||||
printf("#define Z_QUALITY_ZOH\t\t0\n");
|
||||
printf("#define Z_QUALITY_LINEAR\t1\n");
|
||||
printf("#define Z_QUALITY_SINC\t\t%d\n", \
|
||||
floor((length(ztab) - 1) / 2) + 2);
|
||||
printf("\n");
|
||||
printf("#define Z_QUALITY_MIN\t\t0\n");
|
||||
printf("#define Z_QUALITY_MAX\t\t%d\n", length(ztab) + 1);
|
||||
printf("\n/*\n * smallest: %.32f\n * largest: %.32f\n *\n", \
|
||||
smallest, largest);
|
||||
printf(" * z_unshift=%d, z_interp_shift=%d\n *\n", \
|
||||
Z_UNSHIFT, Z_INTERP_SHIFT);
|
||||
v = shr(Z_ONE - 1, Z_UNSHIFT) * abs(largest_interp);
|
||||
printf(" * largest interpolation multiplication: %d\n */\n", v);
|
||||
if (v < INT32_MIN || v > INT32_MAX) {
|
||||
printf("\n#ifndef SND_FEEDER_RATE_HP\n");
|
||||
printf("#error interpolation overflow, please reduce" \
|
||||
" Z_INTERP_SHIFT\n");
|
||||
printf("#endif\n");
|
||||
}
|
||||
|
||||
printf("\n#endif /* !_FEEDER_RATE_GEN_H_ */\n");
|
||||
}
|
142
sys/tools/snd_fxdiv_gen.awk
Normal file
142
sys/tools/snd_fxdiv_gen.awk
Normal file
@ -0,0 +1,142 @@
|
||||
#!/usr/bin/awk -f
|
||||
#
|
||||
# Copyright (c) 2008-2009 Ariff Abdullah <ariff@FreeBSD.org>
|
||||
# All rights reserved.
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# $FreeBSD$
|
||||
#
|
||||
|
||||
function floor(x, r)
|
||||
{
|
||||
r = int(x);
|
||||
if (r > x)
|
||||
r--;
|
||||
return (r + 0);
|
||||
}
|
||||
|
||||
function shl(x, y)
|
||||
{
|
||||
while (y > 0) {
|
||||
x *= 2;
|
||||
y--;
|
||||
}
|
||||
return (x);
|
||||
}
|
||||
|
||||
function shr(x, y)
|
||||
{
|
||||
while (y > 0 && x != 0) {
|
||||
x = floor(x / 2);
|
||||
y--;
|
||||
}
|
||||
return (x);
|
||||
}
|
||||
|
||||
function calcdiv(r, x, y, z)
|
||||
{
|
||||
y = floor(FXONE / x);
|
||||
z = FXSHIFT;
|
||||
|
||||
while (shr((y * x), z) < 1)
|
||||
y++;
|
||||
|
||||
while ((y % 2) == 0 && z > 0) {
|
||||
y = floor(y / 2);
|
||||
z--;
|
||||
}
|
||||
|
||||
r["mul"] = y;
|
||||
r["shift"] = z;
|
||||
}
|
||||
|
||||
BEGIN {
|
||||
FXSHIFT = 16;
|
||||
FXONE = shl(1, FXSHIFT);
|
||||
|
||||
SND_CHN_MAX = 18;
|
||||
|
||||
PCM_8_BPS = 1;
|
||||
PCM_16_BPS = 2;
|
||||
PCM_24_BPS = 3;
|
||||
PCM_32_BPS = 4;
|
||||
|
||||
SND_MAX_ALIGN = SND_CHN_MAX * PCM_32_BPS;
|
||||
|
||||
for (i = 1; i <= SND_CHN_MAX; i++) {
|
||||
aligns[PCM_8_BPS * i] = 1;
|
||||
aligns[PCM_16_BPS * i] = 1;
|
||||
aligns[PCM_24_BPS * i] = 1;
|
||||
aligns[PCM_32_BPS * i] = 1;
|
||||
}
|
||||
|
||||
printf("#ifndef _SND_FXDIV_GEN_H_\n");
|
||||
printf("#define _SND_FXDIV_GEN_H_\n\n");
|
||||
|
||||
printf("/*\n");
|
||||
printf(" * Generated using snd_fxdiv_gen.awk, heaven, wind and awesome.\n");
|
||||
printf(" *\n");
|
||||
printf(" * DO NOT EDIT!\n");
|
||||
printf(" */\n\n");
|
||||
printf("#ifdef SND_USE_FXDIV\n\n");
|
||||
|
||||
printf("/*\n");
|
||||
printf(" * Fast unsigned 32bit integer division and rounding, accurate for\n");
|
||||
printf(" * x = 1 - %d. This table should be enough to handle possible\n", FXONE);
|
||||
printf(" * division for 1 - 72 (more can be generated though..).\n");
|
||||
printf(" *\n");
|
||||
printf(" * 72 = SND_CHN_MAX * PCM_32_BPS, which is why....\n");
|
||||
printf(" */\n\n");
|
||||
|
||||
printf("static const uint32_t snd_fxdiv_table[][2] = {\n");
|
||||
|
||||
for (i = 1; i <= SND_MAX_ALIGN; i++) {
|
||||
if (aligns[i] != 1)
|
||||
continue;
|
||||
calcdiv(r, i);
|
||||
printf("\t[0x%02x] = { 0x%04x, 0x%02x },", \
|
||||
i, r["mul"], r["shift"]);
|
||||
printf("\t/* x / %-2d = (x * %-5d) >> %-2d */\n", \
|
||||
i, r["mul"], r["shift"]);
|
||||
}
|
||||
|
||||
printf("};\n\n");
|
||||
|
||||
printf("#define SND_FXDIV_MAX\t\t0x%08x\n", FXONE);
|
||||
printf("#define SND_FXDIV(x, y)\t\t(((uint32_t)(x) *\t\t\t\\\n");
|
||||
printf("\t\t\t\t snd_fxdiv_table[y][0]) >>\t\t\\\n");
|
||||
printf("\t\t\t\t snd_fxdiv_table[y][1])\n");
|
||||
printf("#define SND_FXROUND(x, y)\t(SND_FXDIV(x, y) * (y))\n");
|
||||
printf("#define SND_FXMOD(x, y)\t\t((x) - SND_FXROUND(x, y))\n\n");
|
||||
|
||||
printf("#else\t/* !SND_USE_FXDIV */\n\n");
|
||||
|
||||
printf("#define SND_FXDIV_MAX\t\t0x%08x\n", 131072);
|
||||
printf("#define SND_FXDIV(x, y)\t\t((x) / (y))\n");
|
||||
printf("#define SND_FXROUND(x, y)\t((x) - ((x) %% (y)))\n");
|
||||
printf("#define SND_FXMOD(x, y)\t\t((x) %% (y))\n\n");
|
||||
|
||||
printf("#endif\t/* SND_USE_FXDIV */\n\n");
|
||||
|
||||
printf("#endif\t/* !_SND_FXDIV_GEN_H_ */\n");
|
||||
}
|
Loading…
Reference in New Issue
Block a user