[Sumover-dev] [svn commit] r3750 - rat/trunk
sumover-dev at cs.ucl.ac.uk
sumover-dev at cs.ucl.ac.uk
Tue May 23 17:55:28 BST 2006
Author: socrates
Date: Tue May 23 17:54:42 2006
New Revision: 3750
Modified:
rat/trunk/auddev_netbsd.c
rat/trunk/auddev_netbsd.h
rat/trunk/main_control.c
rat/trunk/mbus_engine.c
rat/trunk/parameters.c
rat/trunk/tcltk.c
rat/trunk/ui_send_audio.c
Log:
Contributions from Brook Milligan <brook at biology.nmsu.edu>.
**UNTESTED: 23/05/2006**
Summary:
1. New NetBSD audio driver
2. Other NetBSD patches
Details:
1. Completely rewritten NetBSD audio driver, very flexible and
pretty robust. It now takes full advantage of all the information
available from the kernel and will support anything the kernel does,
including the full mixer interface.
2a.Improve the OSS driver to dynamicly determine the available record
ports. Mainly, this uses the OSS kernel ioctl(2) interface to
dynamically determine the set of input devices. Previously, this was
hardcoded, and does not necessarily correspond to the actual kernel
devices.
2b.Handle switching output ports in the same way as switching input
ports.
2c.Add some casts to avoid compiler warnings from gcc v3.
Modified: rat/trunk/auddev_netbsd.c
==============================================================================
--- rat/trunk/auddev_netbsd.c (original)
+++ rat/trunk/auddev_netbsd.c Tue May 23 17:54:42 2006
@@ -5,7 +5,7 @@
*
* $Id$
*
- * Copyright (c) 2002-2004 Brook Milligan
+ * Copyright (c) 2002-2006 Brook Milligan
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -42,9 +42,11 @@
#include <sys/types.h>
#include <sys/time.h>
-#include <sys/audioio.h>
+#include <sys/audioio.h>
+#include <ctype.h>
#include <err.h>
#include <errno.h>
+#include <fcntl.h>
#include <string.h>
#include <unistd.h>
@@ -56,7 +58,7 @@
#include "memory.h"
#include "debug.h"
-#define DEBUG_MIXER 0
+#define DEBUG_MIXER 1
/*
* Path to audio/mixer devices. In addition to the base names given
@@ -65,68 +67,150 @@
* (e.g., /dev/sound0-/dev/sound7 for min_devices=9), but continues
* beyond that until the first failure when opening a device.
*/
-static char audio_path[MAXPATHLEN] = "/dev/sound";
-static char mixer_path[MAXPATHLEN] = "/dev/mixer";
-static int min_devices = 1 + 8;
+static char audio_path[MAXPATHLEN] = "/dev/sound";
+static char mixer_path[MAXPATHLEN] = "/dev/mixer";
+static int min_devices = 1 + 8;
/*
* Information on each audio device
*/
typedef struct audio_dinfo {
- char *dev_name; /* Audio device name */
- int fd; /* File descriptor */
- audio_device_t audio_dev; /* Kernel device info. */
- int audio_props; /* Audio device properties */
+ char *dev_name; /* Audio device name */
+ int fd; /* File descriptor */
+ int audio_props; /* Audio device properties */
+ audio_device_t audio_dev; /* Kernel device names */
+ audio_info_t audio_info; /* Kernel audio info. */
audio_encoding_t *audio_enc; /* Supported kernel encodings */
-} audio_dinfo_t;
+ const audio_encoding_t *play_encoding; /* Current encodings */
+ const audio_encoding_t *record_encoding;
+} audio_dinfo_t;
+
/*
* Information on each mixer device
*/
typedef struct mixer_dinfo {
- char *dev_name; /* Mixer device name */
- int fd; /* File descriptor */
- mixer_ctrl_t play_gain; /* Kernel gain controls */
- mixer_ctrl_t record_gain;
- mixer_ctrl_t loopback_gain;
- int max_play_gain; /* Maximum gains */
- int max_record_gain;
- int max_loopback_gain;
- int preamp_check; /* Check if preamp is turned off? */
-} mixer_dinfo_t;
+ char *dev_name; /* Mixer device name */
+ int fd; /* File descriptor */
+} mixer_dinfo_t;
+
+/*
+ * Information on each record volume control port.
+ *
+ * The mixer devices provide a rich interface to the input devices.
+ * In many cases, a mute and preamp device are available in addition
+ * to the gain control device itself. Furthermore, selection of the
+ * input device is done via the record.source mixer device.
+ * Consequently, for each record volume control, this set of four
+ * mixer devices is required. Only the gain control is updated
+ * regularly; the other three are constants used to select the
+ * appropriate feature.
+ *
+ * Note that audio devices never provide such a rich interface, so
+ * only one "mixer control", the gain field, is used to store the
+ * required gain information. XXX - This should really be handled by
+ * a union.
+ *
+ * Unused mixer devices are flagged with a type field equal to
+ * AUDIO_INVALID_PORT.
+ */
+typedef struct record_gain_ctrl {
+ mixer_ctrl_t gain;
+ mixer_ctrl_t source;
+ mixer_ctrl_t mute;
+ mixer_ctrl_t preamp;
+} record_gain_ctrl_t;
+
+/*
+ * Array of available record controls.
+ */
+typedef struct record_gain {
+ int port; /* selected port */
+ int n;
+ record_gain_ctrl_t *gain_ctrl;
+} record_gain_t;
+
+/*
+ * Array of available play controls.
+ *
+ * The interface for play controls is much simpler than that for record
+ * controls, as only one is required even for mixer controls.
+ */
+typedef struct play_gain {
+ int port; /* selected port */
+ int n;
+ mixer_ctrl_t *gain_ctrl;
+} play_gain_t;
+
/*
* Table of all detected audio devices and their corresponding mixer devices
*/
typedef struct audio_devices {
- char *full_name;
- audio_dinfo_t audio_info;
- mixer_dinfo_t mixer_info;
-} audio_devices_t;
+ char *full_name;
+ audio_dinfo_t audio_info;
+ mixer_dinfo_t mixer_info;
+ play_gain_t play;
+ record_gain_t record;
+} audio_devices_t;
+static int n_devices = 0;
audio_devices_t *audio_devices = NULL;
-static int n_devices = 0;
/*
- * I/O port mappings between the kernel and rat.
+ * Rat names for I/O ports.
+ *
+ * These tables map the I/O ports known to rat into the set of kernel
+ * audio/mixer devices. XXX - These tables should be included within
+ * the device table.
*/
-static audio_port_details_t in_ports[] = {
- {AUDIO_MICROPHONE, AUDIO_PORT_MICROPHONE},
- {AUDIO_LINE_IN, AUDIO_PORT_LINE_IN},
- {AUDIO_CD, AUDIO_PORT_CD}
+static int n_rat_in_ports = 0;
+static audio_port_details_t *rat_in_ports = NULL;
+
+static int n_rat_out_ports = 0;
+static audio_port_details_t *rat_out_ports = NULL;
+
+
+/* extra port types in addition to those defined in audio(4) */
+#define AUDIO_INVALID_PORT (-1)
+#define AUDIO_PORT (-2)
+
+#define INVALID_FD (-1)
+
+
+/*
+ * Potential kernel audio I/O ports
+ *
+ * The AUDIO_GETINFO ioctl(2) returns masks of valid I/O ports in the
+ * audio_info.play.avail_ports and audio_info.record.avail_ports
+ * fields. These tables map each of the potential ports to a label
+ * that can be used as a rat device name. This is unnecessary for
+ * mixer devices, because the AUDIO_MIXER_DEVINFO ioctl(2) provides
+ * access to the kernel-defined label for each mixer device. See
+ * audio(4) for more details.
+ */
+static audio_port_details_t netbsd_in_ports[] = {
+ {AUDIO_MICROPHONE, AudioNmicrophone},
+ {AUDIO_LINE_IN, AudioNline},
+ {AUDIO_CD, AudioNcd},
+ {0, AudioNmaster}
};
-#define NETBSD_NUM_INPORTS (sizeof(in_ports) / sizeof(in_ports[0]))
+#define NETBSD_NUM_INPORTS \
+ (sizeof(netbsd_in_ports) / sizeof(netbsd_in_ports[0]))
-static audio_port_details_t out_ports[] = {
- {AUDIO_SPEAKER, AUDIO_PORT_SPEAKER},
- {AUDIO_HEADPHONE, AUDIO_PORT_HEADPHONE},
- {AUDIO_LINE_OUT, AUDIO_PORT_LINE_OUT}
+static audio_port_details_t netbsd_out_ports[] = {
+ {AUDIO_SPEAKER, AudioNspeaker},
+ {AUDIO_HEADPHONE, AudioNheadphone},
+ {AUDIO_LINE_OUT, AudioNline},
+ {0, AudioNmaster}
};
-#define NETBSD_NUM_OUTPORTS (sizeof(out_ports) / sizeof(out_ports[0]))
+#define NETBSD_NUM_OUTPORTS \
+ (sizeof(netbsd_out_ports) / sizeof(netbsd_out_ports[0]))
+
/*
* Encoding mappings between the kernel and rat
*/
struct audio_encoding_match {
- int netbsd_encoding;
- deve_e rat_encoding;
+ int netbsd_encoding;
+ deve_e rat_encoding;
};
static struct audio_encoding_match audio_encoding_match[] = {
{AUDIO_ENCODING_ULAW, DEV_PCMU},
@@ -142,88 +226,114 @@
{AUDIO_ENCODING_SLINEAR, DEV_S16},
{AUDIO_ENCODING_NONE, 0}
};
+
/*
* Gain control mixer devices
*/
-struct mixer_devices {
- const char *class;
- const char *device;
-};
+typedef struct mixer_devices {
+ const char *class;
+ const char *device;
+} mixer_devices_t;
+
/*
- * Possible mixer play gain devices
+ * Possible mixer play gain devices.
+ *
+ * Unlike the record devices which are listed by the kernel as enums
+ * in the record.source mixer device, the gain devices that
+ * potentially act to control the output level are not defined within
+ * the kernel. This table serves as a list of devices to consider for
+ * the purpose of identifying output gain controls.
*/
-static struct mixer_devices mixer_play_gain[] = {
- {AudioCinputs, AudioNdac},
+static mixer_devices_t mixer_play_gain[] = {
{AudioCoutputs, AudioNmaster},
+ {AudioCinputs, AudioNdac},
{NULL, NULL}
};
+
/*
- * Possible mixer record gain devices
- */
-static struct mixer_devices mixer_record_gain[] = {
- {AudioCrecord, AudioNmicrophone},
- {AudioCinputs, AudioNmicrophone},
- {AudioCrecord, AudioNvolume},
- {NULL, NULL}
-};
-/*
- * Possible loopback gain devices
+ * Possible loopback gain devices.
+ *
+ * XXX - Loopback handling is not yet supported. However, this table
+ * plays a role similar to the previous one for output gain controls.
+ * It lists the devices to consider for the purpose of identifying
+ * loopback gain controls.
*/
+#if 0 /* XXX */
static struct mixer_devices mixer_loopback_gain[] = {
{AudioCinputs, AudioNmixerout},
{AudioCinputs, AudioNspeaker},
{NULL, NULL}
};
+#endif
-static int probe_device(const char *, const char *);
-static int probe_audio_device(const char *, audio_dinfo_t *);
-static int probe_mixer_device(const char *, mixer_dinfo_t *);
-static mixer_ctrl_t
-match_mixer_device(int fd,
- mixer_devinfo_t * mixers, int n_mixers,
- struct mixer_devices * devices);
-static void set_mode(audio_desc_t ad);
-static void set_audio_properties(audio_desc_t ad);
-static int get_mixer_gain(int fd, mixer_ctrl_t * mixer_info);
-static void set_mixer_gain(int fd, mixer_ctrl_t * mixer_info, int gain);
-static u_char average_mixer_level(mixer_ctrl_t mixer_info);
-static void check_record_preamp(int fd);
-static audio_encoding_t *get_encoding(audio_desc_t ad, audio_format * fmt);
-static audio_encoding_t *
-set_encoding(audio_desc_t ad, audio_format * ifmt,
- audio_format * ofmt);
-static audio_encoding_t *
-set_alt_encoding(audio_desc_t ad, audio_format * ifmt,
- audio_format * ofmt);
-static int audio_select(audio_desc_t ad, int delay_us);
-
+static int probe_device(const char *, const char *);
+static int probe_audio_device(const char *, audio_dinfo_t *);
+static int probe_mixer_device(const char *, mixer_dinfo_t *);
+static audio_info_t set_audio_info(audio_format * ifmt, audio_format * ofmt);
+static const audio_encoding_t *match_encoding
+ (audio_format * fmt, const audio_encoding_t * encodings);
+static int audio_select(audio_desc_t ad, int delay_us);
+static void update_audio_info(audio_desc_t ad);
+static u_char average_mixer_level(mixer_level_t);
+
+static void find_gain_ctrl(audio_desc_t);
+static void find_audio_play_ctrl(audio_desc_t);
+static void find_audio_record_ctrl(audio_desc_t);
+static void find_mixer_play_ctrl(audio_desc_t, mixer_devinfo_t *);
+static void find_mixer_record_ctrl(audio_desc_t, mixer_devinfo_t *);
+
+static void map_netbsd_ports(audio_desc_t ad);
+static void fix_rat_names(void);
+
+static mixer_devinfo_t *mixer_devices(audio_desc_t ad);
+
+static void append_record_gain_ctrl(record_gain_t *);
+static void
+copy_record_gain_ctrl(record_gain_t * gains,
+ record_gain_ctrl_t * src);
+static void copy_rat_in_port(audio_port_details_t * src);
+
+#if DEBUG_MIXER > 0
+static void print_audio_properties(audio_desc_t ad);
+static void
+print_audio_formats(audio_desc_t ad,
+ audio_format * ifmt, audio_format * ofmt);
+static void print_gain_ctrl(audio_desc_t, mixer_devinfo_t *);
+#endif /* DEBUG_MIXER */
/*
- * Conversion to/from kernel device gains
- */
-#define netbsd_rat_to_device(gain,max_gain) \
- ((gain) * (max_gain - AUDIO_MIN_GAIN) / MAX_AMP + AUDIO_MIN_GAIN)
-#define netbsd_device_to_rat(gain,max_gain) \
- (((gain) - AUDIO_MIN_GAIN) * MAX_AMP / (max_gain - AUDIO_MIN_GAIN))
+ * Conversion between rat and kernel device gains.
+ *
+ * Rat uses a scale of 0-100 for gain controls, whereas the kernel
+ * uses a scale of AUDIO_MIN_GAIN-AUDIO_MAX_GAIN. Consequently, gains
+ * must be mapped from one scheme to the other.
+ */
+
+#define netbsd_rat_to_device(gain) \
+ ((gain) * (AUDIO_MAX_GAIN - AUDIO_MIN_GAIN) / MAX_AMP + AUDIO_MIN_GAIN)
+#define netbsd_device_to_rat(gain) \
+ (((gain) - AUDIO_MIN_GAIN) * MAX_AMP / (AUDIO_MAX_GAIN - AUDIO_MIN_GAIN))
+
+#define AUDIO_MID_GAIN ((AUDIO_MIN_GAIN + AUDIO_MAX_GAIN) / 2)
/*
* netbsd_audio_init: initialize data structures
*
* Determine how many audio/mixer devices are available. Return: TRUE
- * if number of devices is greater than 0, FALSE otherwise
+ * if number of devices is greater than 0, FALSE otherwise.
*/
int
netbsd_audio_init()
{
- int i, found;
- char audio_dev_name[MAXPATHLEN];
- char mixer_dev_name[MAXPATHLEN];
- char dev_index[MAXPATHLEN];
+ int i, found;
+ char audio_dev_name[MAXPATHLEN];
+ char mixer_dev_name[MAXPATHLEN];
+ char dev_index[MAXPATHLEN];
-#if DEBUG_MIXER
+#if DEBUG_MIXER > 1
warnx("netbsd_audio_init()");
#endif
probe_device(audio_path, mixer_path);
@@ -239,7 +349,7 @@
found = probe_device(audio_dev_name, mixer_dev_name);
++i;
}
-#if DEBUG_MIXER
+#if DEBUG_MIXER > 1
warnx("netbsd_audio_init(): n_devices=%d", n_devices);
#endif
return n_devices > 0;
@@ -261,7 +371,7 @@
* netbsd_audio_device_name: return the full audio device name
*/
-char *
+char *
netbsd_audio_device_name(audio_desc_t ad)
{
assert(audio_devices && n_devices > ad);
@@ -272,111 +382,89 @@
/*
* netbsd_audio_open: try to open the audio and mixer devices
*
- * Return: valid file descriptor if ok, -1 otherwise.
+ * Return: valid file descriptor if ok, INVALID_FD otherwise.
*/
int
netbsd_audio_open(audio_desc_t ad, audio_format * ifmt, audio_format * ofmt)
{
- int fd;
- int full_duplex = 1;
- audio_info_t dev_info;
- audio_encoding_t *encp;
+ int audio_fd;
+ int mixer_fd;
+ int full_duplex = 1;
assert(audio_devices && n_devices > ad
- && audio_devices[ad].audio_info.fd == -1);
+ && audio_devices[ad].audio_info.fd == INVALID_FD);
- warnx("opening %s (audio device %s, mixer device %s)",
- audio_devices[ad].full_name,
- audio_devices[ad].audio_info.dev_name,
- audio_devices[ad].mixer_info.dev_name);
debug_msg("Opening %s (audio device %s, mixer device %s)\n",
- audio_devices[ad].full_name,
- audio_devices[ad].audio_info.dev_name,
- audio_devices[ad].mixer_info.dev_name);
+ audio_devices[ad].full_name,
+ audio_devices[ad].audio_info.dev_name,
+ audio_devices[ad].mixer_info.dev_name);
+#if DEBUG_MIXER > 0
+ warnx("opening %s", audio_devices[ad].full_name);
+ warnx(" audio device: %s", audio_devices[ad].audio_info.dev_name);
+ if (audio_devices[ad].mixer_info.dev_name)
+ warnx(" mixer device: %s",
+ audio_devices[ad].mixer_info.dev_name);
+ print_audio_properties(ad);
+#endif
- fd = open(audio_devices[ad].audio_info.dev_name, O_RDWR | O_NONBLOCK);
- if (fd < 0) {
- /*
- * Because we opened the device with O_NONBLOCK, the
- * wait flag was not updated so update it manually.
- */
- debug_msg("netbsd_audio_open(): setting wait flag.\n");
- fd = open(audio_devices[ad].audio_info.dev_name, O_WRONLY);
- if (fd < 0) {
- AUDIO_INITINFO(&dev_info);
- dev_info.play.waiting = 1;
- (void) ioctl(fd, AUDIO_SETINFO, &dev_info);
- close(fd);
- }
- return -1;
- }
- audio_devices[ad].audio_info.fd = fd;
-
- if (!(audio_devices[ad].audio_info.audio_props
- & AUDIO_PROP_INDEPENDENT)
- && !audio_format_match(ifmt, ofmt)) {
- warnx("NetBSD audio: independent i/o formats not supported.");
- close(fd);
- audio_devices[ad].audio_info.fd = -1;
- return -1;
- }
- if (ifmt->bytes_per_block != ofmt->bytes_per_block) {
- warnx("NetBSD audio: "
- "independent i/o block sizes not supported.");
- close(fd);
- audio_devices[ad].audio_info.fd = -1;
- return -1;
- }
- if (ioctl(fd, AUDIO_FLUSH, NULL) < 0) {
- perror("netbsd_audio_open: flushing device");
- close(fd);
- audio_devices[ad].audio_info.fd = -1;
- return -1;
+ /* open audio device */
+ audio_fd = open(audio_devices[ad].audio_info.dev_name,
+ O_RDWR | O_NONBLOCK, 0);
+ if (audio_fd < 0) {
+ perror("opening audio device");
+ return INVALID_FD;
}
- if (ioctl(fd, AUDIO_SETFD, &full_duplex) < 0) {
+ audio_devices[ad].audio_info.fd = audio_fd;
+
+ /* set full duplex */
+ if (ioctl(audio_fd, AUDIO_SETFD, &full_duplex) < 0) {
perror("setting full duplex");
- return FALSE;
+ close(audio_fd);
+ audio_devices[ad].audio_info.fd = INVALID_FD;
+ return INVALID_FD;
+ }
+ /* set audio formats */
+ audio_devices[ad].audio_info.audio_info = set_audio_info(ifmt, ofmt);
+
+ /* find requested encoding */
+ audio_devices[ad].audio_info.play_encoding
+ = match_encoding(ofmt, audio_devices[ad].audio_info.audio_enc);
+ audio_devices[ad].audio_info.record_encoding
+ = match_encoding(ifmt, audio_devices[ad].audio_info.audio_enc);
+ audio_devices[ad].audio_info.audio_info.play.encoding
+ = audio_devices[ad].audio_info.play_encoding->encoding;
+ audio_devices[ad].audio_info.audio_info.record.encoding
+ = audio_devices[ad].audio_info.record_encoding->encoding;
+
+ /* notify kernel */
+ if (ioctl(audio_fd, AUDIO_SETINFO,
+ &audio_devices[ad].audio_info.audio_info) < 0) {
+ perror("setting audio info");
}
- if ((encp = set_encoding(ad, ifmt, ofmt)) == NULL) {
- if ((encp = set_alt_encoding(ad, ifmt, ofmt)) == NULL) {
- perror("netbsd_audio_open: "
- "no audio encodings supported");
- close(fd);
- audio_devices[ad].audio_info.fd = -1;
- return -1;
- }
- }
- warnx("NetBSD audio format: "
- "%d Hz, %d bits/sample, %d %s, "
- "requested rat encoding %d, "
- "mapped kernel encoding %s)",
- ifmt->sample_rate, ifmt->bits_per_sample,
- ifmt->channels, ifmt->channels == 1 ? "channel" : "channels",
- ifmt->encoding, encp->name);
- if (encp->flags & AUDIO_ENCODINGFLAG_EMULATED)
- warnx("NetBSD %s support is emulated", encp->name);
-
- set_mode(ad);
- set_audio_properties(ad);
- if (ioctl(fd, AUDIO_GETINFO, &dev_info) < 0) {
- perror("netbsd_audio_open: getting parameters");
- close(fd);
- audio_devices[ad].audio_info.fd = -1;
- return -1;
+#if DEBUG_MIXER > 0
+ print_audio_formats(ad, ifmt, ofmt);
+#endif
+
+ /* flush audio output */
+ if (ioctl(audio_fd, AUDIO_FLUSH, NULL) < 0) {
+ perror("flushing audio device");
+ close(audio_fd);
+ audio_devices[ad].audio_info.fd = INVALID_FD;
+ return INVALID_FD;
}
- audio_devices[ad].mixer_info.fd = -1;
+ /* open mixer device (if any) */
+ audio_devices[ad].mixer_info.fd = INVALID_FD;
if (audio_devices[ad].mixer_info.dev_name) {
- fd = open(audio_devices[ad].mixer_info.dev_name,
- O_RDWR | O_NONBLOCK);
- if (fd >= 0 && audio_devices[ad].mixer_info.preamp_check) {
- audio_devices[ad].mixer_info.fd = fd;
- if (audio_devices[ad].mixer_info.record_gain.type
- == AUDIO_MIXER_VALUE)
- check_record_preamp(fd);
- audio_devices[ad].mixer_info.preamp_check = FALSE;
+ mixer_fd = open(audio_devices[ad].mixer_info.dev_name,
+ O_RDONLY | O_NONBLOCK, 0);
+ if (mixer_fd < 0) {
+ perror("ignoring mixer device");
}
+ audio_devices[ad].mixer_info.fd = mixer_fd;
}
+ find_gain_ctrl(ad);
+
return audio_devices[ad].audio_info.fd;
}
@@ -391,26 +479,39 @@
assert(audio_devices && n_devices > ad);
if (audio_devices[ad].audio_info.fd >= 0) {
- /* Flush device first */
+ /* flush audio device first */
if (ioctl(audio_devices[ad].audio_info.fd, AUDIO_FLUSH,
- NULL) < 0)
+ NULL) < 0)
perror("netbsd_audio_close: flushing device");
(void) close(audio_devices[ad].audio_info.fd);
}
if (audio_devices[ad].mixer_info.fd >= 0) {
(void) close(audio_devices[ad].mixer_info.fd);
}
- warnx("closing %s (audio device %s, mixer device %s)",
- audio_devices[ad].full_name,
- audio_devices[ad].audio_info.dev_name,
- audio_devices[ad].mixer_info.dev_name);
+ free(audio_devices[ad].play.gain_ctrl);
+ audio_devices[ad].play.gain_ctrl = NULL;
+ audio_devices[ad].play.n = 0;
+ free(audio_devices[ad].record.gain_ctrl);
+ audio_devices[ad].record.gain_ctrl = NULL;
+ audio_devices[ad].record.n = 0;
+
+ free(rat_in_ports);
+ rat_in_ports = NULL;
+ n_rat_in_ports = 0;
+ free(rat_in_ports);
+ rat_out_ports = NULL;
+ n_rat_out_ports = 0;
+
+#if DEBUG_MIXER > 0
+ warnx("closing %s", audio_devices[ad].full_name);
+#endif
debug_msg("Closing %s (audio device %s, mixer device %s)\n",
- audio_devices[ad].full_name,
- audio_devices[ad].audio_info.dev_name,
- audio_devices[ad].mixer_info.dev_name);
+ audio_devices[ad].full_name,
+ audio_devices[ad].audio_info.dev_name,
+ audio_devices[ad].mixer_info.dev_name);
- audio_devices[ad].audio_info.fd = -1;
- audio_devices[ad].mixer_info.fd = -1;
+ audio_devices[ad].audio_info.fd = INVALID_FD;
+ audio_devices[ad].mixer_info.fd = INVALID_FD;
}
@@ -433,13 +534,13 @@
/*
* netbsd_audio_duplex: check duplex flag for audio device
*
- * Return: duplex flag, or 0 on failure (shouldn't happen)
+ * Return: duplex flag, or 0 on failure (which shouldn't happen)
*/
int
netbsd_audio_duplex(audio_desc_t ad)
{
- int duplex;
+ int duplex;
assert(audio_devices && n_devices > ad);
if (audio_devices[ad].audio_info.fd < 0)
@@ -462,9 +563,9 @@
int
netbsd_audio_read(audio_desc_t ad, u_char * buf, int buf_bytes)
{
- int fd;
- int bytes_read = 0;
- int this_read;
+ int fd;
+ int bytes_read = 0;
+ int this_read;
assert(audio_devices && n_devices > ad);
fd = audio_devices[ad].audio_info.fd;
@@ -473,7 +574,7 @@
while (buf_bytes > 0) {
this_read = read(audio_devices[ad].audio_info.fd,
- (char *) buf, buf_bytes);
+ (char *) buf, buf_bytes);
if (this_read < 0) {
if (errno != EAGAIN && errno != EINTR)
perror("netbsd_audio_read");
@@ -496,9 +597,9 @@
int
netbsd_audio_write(audio_desc_t ad, u_char * buf, int buf_bytes)
{
- int fd;
- int bytes_written = 0;
- int this_write;
+ int fd;
+ int bytes_written = 0;
+ int this_write;
assert(audio_devices && n_devices > ad);
fd = audio_devices[ad].audio_info.fd;
@@ -528,7 +629,7 @@
void
netbsd_audio_non_block(audio_desc_t ad)
{
- int on = 1; /* Enable non-blocking I/O */
+ int on = 1; /* enable non-blocking I/O */
assert(audio_devices && n_devices > ad);
if (audio_devices[ad].audio_info.fd < 0)
@@ -546,7 +647,7 @@
void
netbsd_audio_block(audio_desc_t ad)
{
- int on = 0; /* Disable non-blocking I/O */
+ int on = 0; /* disable non-blocking I/O */
assert(audio_devices && n_devices > ad);
if (audio_devices[ad].audio_info.fd < 0)
@@ -558,381 +659,422 @@
/*
- * netbsd_audio_iport_set: set input port
+ * netbsd_audio_iport_count: get number of available input ports.
*/
-void
-netbsd_audio_iport_set(audio_desc_t ad, audio_port_t port)
+int
+netbsd_audio_iport_count(audio_desc_t ad)
{
- int fd;
- audio_info_t dev_info;
+#if DEBUG_MIXER > 1
+ warnx("netbsd_audio_iport_count(%d): "
+ "n_rat_in_ports=%d, record_devices=%d",
+ ad, n_rat_in_ports, audio_devices[ad].record.n);
+#endif
assert(audio_devices && n_devices > ad);
- fd = audio_devices[ad].audio_info.fd;
- if (fd < 0)
- return;
+ if (audio_devices[ad].audio_info.fd < 0)
+ return 0;
- AUDIO_INITINFO(&dev_info);
- dev_info.record.port = port;
- if (ioctl(fd, AUDIO_SETINFO, &dev_info) < 0)
- warnx("netbsd_audio_iport_set: "
- "cannot set input port %d: %s",
- port, strerror(errno));
+ return n_rat_in_ports;
}
/*
- * netbsd_audio_iport_get: get information on input port
+ * netbsd_audio_oport_count: get number of available output ports
*/
-audio_port_t
-netbsd_audio_iport_get(audio_desc_t ad)
+int
+netbsd_audio_oport_count(audio_desc_t ad)
{
- int fd;
- audio_info_t dev_info;
+#if DEBUG_MIXER > 1
+ warnx("netbsd_audio_oport_count(%d): "
+ "n_rat_out_ports=%d, play_devices=%d",
+ ad, n_rat_out_ports, audio_devices[ad].play.n);
+#endif
assert(audio_devices && n_devices > ad);
- fd = audio_devices[ad].audio_info.fd;
- if (fd < 0)
+ if (audio_devices[ad].audio_info.fd < 0)
return 0;
- if (ioctl(fd, AUDIO_GETINFO, &dev_info) < 0) {
- perror("netbsd_audio_iport_get: getting device parameters");
- return 0;
- }
- return dev_info.record.port;
+ return n_rat_out_ports;
}
/*
- * netbsd_audio_oport_set: set output port
+ * netbsd_audio_iport_details: get kernel-rat device mappings for input port
*/
-void
-netbsd_audio_oport_set(audio_desc_t ad, audio_port_t port)
+const audio_port_details_t *
+netbsd_audio_iport_details(audio_desc_t ad, int idx)
{
- int fd;
- audio_info_t dev_info;
-
assert(audio_devices && n_devices > ad);
- fd = audio_devices[ad].audio_info.fd;
- if (fd < 0)
- return;
+ if (audio_devices[ad].audio_info.fd < 0)
+ return NULL;
+ assert(0 <= idx && idx < n_rat_in_ports);
- /*
- * Some drivers report no available ports, because the mixer cannot
- * change output ports.
- */
- if (ioctl(fd, AUDIO_GETINFO, &dev_info) < 0) {
- perror("netbsd_audio_oport_set: getting device parameters");
- return;
- }
- if (dev_info.play.avail_ports) {
- AUDIO_INITINFO(&dev_info);
- dev_info.play.port = port;
- if (ioctl(fd, AUDIO_SETINFO, &dev_info) < 0) {
- warnx("NetBSD audio driver cannot set output port");
- }
- }
+ return &rat_in_ports[idx];
}
/*
- * netbsd_audio_oport_get: get information on output port
+ * netbsd_audio_oport_details: get kernel-rat device mappings for output port
*/
-audio_port_t
-netbsd_audio_oport_get(audio_desc_t ad)
+const audio_port_details_t *
+netbsd_audio_oport_details(audio_desc_t ad, int idx)
{
- int fd;
- audio_info_t dev_info;
-
assert(audio_devices && n_devices > ad);
- fd = audio_devices[ad].audio_info.fd;
- if (fd < 0)
- return 0;
+ if (audio_devices[ad].audio_info.fd < 0)
+ return NULL;
+ assert(0 <= idx && idx < n_rat_out_ports);
- if (ioctl(fd, AUDIO_GETINFO, &dev_info) < 0) {
- perror("netbsd_audio_oport_get: getting device parameters");
- return 0;
- }
- return dev_info.play.port;
+ return &rat_out_ports[idx];
}
/*
- * netbsd_audio_set_igain: set record gain (percent (%) of maximum)
+ * netbsd_audio_get_iport: get information on input port
*/
-void
-netbsd_audio_set_igain(audio_desc_t ad, int gain)
+audio_port_t
+netbsd_audio_get_iport(audio_desc_t ad)
{
- int fd;
- audio_info_t audio_info;
- mixer_ctrl_t mixer_info;
- int max_gain;
+ audio_port_t port;
assert(audio_devices && n_devices > ad);
+ if (audio_devices[ad].audio_info.fd < 0)
+ return AUDIO_INVALID_PORT;
- fd = audio_devices[ad].mixer_info.fd;
- if (fd >= 0) {
- mixer_info = audio_devices[ad].mixer_info.record_gain;
- max_gain = audio_devices[ad].mixer_info.max_record_gain;
- set_mixer_gain(fd, &mixer_info,
- netbsd_rat_to_device(gain, max_gain));
- audio_devices[ad].mixer_info.record_gain = mixer_info;
- return;
- }
- fd = audio_devices[ad].audio_info.fd;
- if (fd >= 0) {
- AUDIO_INITINFO(&audio_info);
- audio_info.record.gain =
- netbsd_rat_to_device(gain, AUDIO_MAX_GAIN);
- if (ioctl(fd, AUDIO_SETINFO, &audio_info) < 0) {
- perror("netbsd_audio_set_igain: "
- "setting audio parameters");
- return;
- }
- }
+ update_audio_info(ad);
+
+ port = audio_devices[ad].record.port;
+#if DEBUG_MIXER > 1
+ warnx("netbsd_audio_get_iport(%d): iport=%d", ad, port);
+#endif
+ return port;
}
/*
- * netbsd_audio_get_igain: get record gain (percent (%) of maximum)
+ * netbsd_audio_get_oport: get information on output port
*/
-int
-netbsd_audio_get_igain(audio_desc_t ad)
+audio_port_t
+netbsd_audio_get_oport(audio_desc_t ad)
{
- int fd;
- audio_info_t audio_info;
- mixer_ctrl_t mixer_info;
- int gain, max_gain;
+ audio_port_t port;
assert(audio_devices && n_devices > ad);
+ if (audio_devices[ad].audio_info.fd < 0)
+ return AUDIO_INVALID_PORT;
- fd = audio_devices[ad].mixer_info.fd;
- if (fd >= 0) {
- mixer_info = audio_devices[ad].mixer_info.record_gain;
- if (ioctl(fd, AUDIO_MIXER_READ, &mixer_info) < 0) {
- perror("netbsd_audio_get_igain: "
- "getting mixer parameters");
- return 0;
- }
- audio_devices[ad].mixer_info.record_gain = mixer_info;
- gain = average_mixer_level(mixer_info);
- max_gain = audio_devices[ad].mixer_info.max_record_gain;
- return netbsd_device_to_rat(gain, max_gain);
- }
- fd = audio_devices[ad].audio_info.fd;
- if (fd >= 0) {
- if (ioctl(fd, AUDIO_GETINFO, &audio_info) < 0) {
- perror("netbsd_audio_get_igain: "
- "getting audio parameters");
- return 0;
- }
- return netbsd_device_to_rat(audio_info.record.gain,
- AUDIO_MAX_GAIN);
- }
- return 0;
+ update_audio_info(ad);
+
+ port = audio_devices[ad].play.port;
+#if DEBUG_MIXER > 1
+ warnx("netbsd_audio_get_oport(%d): oport=%d", ad, port);
+#endif
+ return port;
}
/*
- * netbsd_audio_set_ogain: set play (output) gain (percent (%) of maximum)
+ * netbsd_audio_set_iport: set input port
*/
void
-netbsd_audio_set_ogain(audio_desc_t ad, int gain)
+netbsd_audio_set_iport(audio_desc_t ad, audio_port_t port)
{
- int fd;
- audio_info_t audio_info;
- mixer_ctrl_t mixer_info;
- int max_gain;
+ int fd;
+ record_gain_ctrl_t *record_ctrl;
+ audio_info_t audio_info;
+ mixer_ctrl_t mixer_info;
+
+#if DEBUG_MIXER > 1
+ warnx("netbsd_audio_set_iport(%d,%d)", ad, port);
+#endif
assert(audio_devices && n_devices > ad);
+ if (audio_devices[ad].audio_info.fd < 0)
+ return;
- fd = audio_devices[ad].mixer_info.fd;
- if (fd >= 0) {
- mixer_info = audio_devices[ad].mixer_info.play_gain;
- if (mixer_info.type == AUDIO_MIXER_VALUE) {
- max_gain = audio_devices[ad].mixer_info.max_play_gain;
- set_mixer_gain(fd, &mixer_info,
- netbsd_rat_to_device(gain, max_gain));
- audio_devices[ad].mixer_info.play_gain = mixer_info;
+ update_audio_info(ad);
+ record_ctrl = &audio_devices[ad].record.gain_ctrl[port];
+
+ /* audio control */
+ if (record_ctrl->gain.type == AUDIO_PORT) {
+ fd = audio_devices[ad].audio_info.fd;
+ AUDIO_INITINFO(&audio_info);
+ audio_info.record.port = record_ctrl->gain.dev;
+ audio_info.record.gain
+ = average_mixer_level(record_ctrl->gain.un.value);
+ if (ioctl(fd, AUDIO_SETINFO, &audio_info) < 0) {
+ perror("netbsd_audio_set_iport: "
+ "setting audio parameters");
return;
}
}
- fd = audio_devices[ad].audio_info.fd;
- if (fd >= 0) {
- AUDIO_INITINFO(&audio_info);
- audio_info.play.gain =
- netbsd_rat_to_device(gain, AUDIO_MAX_GAIN);
- if (ioctl(fd, AUDIO_SETINFO, &audio_info) < 0) {
- perror("netbsd_audio_set_ogain: "
- "setting audio parameters");
+ /* mixer controls */
+ if (record_ctrl->gain.type != AUDIO_PORT) {
+ fd = audio_devices[ad].mixer_info.fd;
+ /* source */
+ mixer_info = record_ctrl->source;
+ if (ioctl(fd, AUDIO_MIXER_WRITE, &mixer_info) < 0) {
+ perror("netbsd_audio_set_iport: "
+ "setting mixer record source");
return;
}
+ /* mute */
+ if (record_ctrl->mute.dev != AUDIO_INVALID_PORT) {
+ mixer_info = record_ctrl->mute;
+ if (ioctl(fd, AUDIO_MIXER_WRITE, &mixer_info) < 0) {
+ perror("netbsd_audio_set_iport: "
+ "setting mixer mute");
+ return;
+ }
+ }
+ /* preamp */
+ if (record_ctrl->preamp.dev != AUDIO_INVALID_PORT) {
+ mixer_info = record_ctrl->preamp;
+ if (ioctl(fd, AUDIO_MIXER_WRITE, &mixer_info) < 0) {
+ perror("netbsd_audio_set_iport: "
+ "setting mixer preamp");
+ return;
+ }
+ }
}
+ audio_devices[ad].record.port = port;
}
/*
- * netbsd_audio_get_ogain: get play (output) gain (percent (%) of maximum)
+ * netbsd_audio_set_oport: set output port
*/
-int
-netbsd_audio_get_ogain(audio_desc_t ad)
+void
+netbsd_audio_set_oport(audio_desc_t ad, audio_port_t port)
{
- int fd;
- audio_info_t audio_info;
- mixer_ctrl_t mixer_info;
- int gain, max_gain;
+ int fd;
+ mixer_ctrl_t *play_ctrl;
+ audio_info_t audio_info;
+
+#if DEBUG_MIXER > 1
+ warnx("netbsd_audio_set_oport(%d, %d)", ad, port);
+#endif
assert(audio_devices && n_devices > ad);
+ if (audio_devices[ad].audio_info.fd < 0)
+ return;
- fd = audio_devices[ad].mixer_info.fd;
- if (fd >= 0) {
- mixer_info = audio_devices[ad].mixer_info.play_gain;
- if (mixer_info.type == AUDIO_MIXER_VALUE) {
- if (ioctl(fd, AUDIO_MIXER_READ, &mixer_info) < 0) {
- perror("netbsd_audio_get_ogain: "
- "getting mixer parameters");
- return 0;
- }
- audio_devices[ad].mixer_info.play_gain = mixer_info;
- gain = average_mixer_level(mixer_info);
- max_gain = audio_devices[ad].mixer_info.max_play_gain;
- return netbsd_device_to_rat(gain, max_gain);
- }
- }
- fd = audio_devices[ad].audio_info.fd;
- if (fd >= 0) {
- if (ioctl(fd, AUDIO_GETINFO, &audio_info) < 0) {
- perror("netbsd_audio_get_igain: "
- "getting audio parameters");
- return 0;
+ update_audio_info(ad);
+ play_ctrl = &audio_devices[ad].play.gain_ctrl[port];
+
+ /* inform kernel only if audio port */
+ if (play_ctrl->type == AUDIO_PORT) {
+ fd = audio_devices[ad].audio_info.fd;
+ AUDIO_INITINFO(&audio_info);
+ audio_info.play.port = play_ctrl->dev;
+ audio_info.play.gain = play_ctrl->un.value.level[0];
+ if (ioctl(fd, AUDIO_SETINFO, &audio_info) < 0) {
+ perror("netbsd_audio_set_oport: "
+ "setting audio parameters");
+ return;
}
- return netbsd_device_to_rat(audio_info.play.gain,
- AUDIO_MAX_GAIN);
}
- return 0;
+ audio_devices[ad].play.port = port;
}
/*
- * netbsd_audio_iport_details: get kernel-rat device mappings for input port
+ * netbsd_audio_get_igain: get record gain (percent (%) of maximum)
*/
-const audio_port_details_t *
-netbsd_audio_iport_details(audio_desc_t ad, int idx)
+int
+netbsd_audio_get_igain(audio_desc_t ad)
{
+ int port;
+ record_gain_ctrl_t *record_ctrl;
+ int gain;
+
assert(audio_devices && n_devices > ad);
- assert((unsigned) idx < NETBSD_NUM_INPORTS);
- return &in_ports[idx];
+ if (audio_devices[ad].audio_info.fd < 0)
+ return 0;
+
+ update_audio_info(ad);
+
+ port = audio_devices[ad].record.port;
+ record_ctrl = &audio_devices[ad].record.gain_ctrl[port];
+ gain = average_mixer_level(record_ctrl->gain.un.value);
+#if DEBUG_MIXER > 1
+ warnx("netbsd_audio_get_igain(%d): average igain=%d", ad, gain);
+#endif
+ gain = netbsd_device_to_rat(gain);
+ return gain;
}
/*
- * netbsd_audio_oport_details: get kernel-rat device mappings for output port
+ * netbsd_audio_get_ogain: get play (output) gain (percent (%) of maximum)
*/
-const audio_port_details_t *
-netbsd_audio_oport_details(audio_desc_t ad, int idx)
+int
+netbsd_audio_get_ogain(audio_desc_t ad)
{
+ int port;
+ mixer_ctrl_t *play_ctrl;
+ int gain;
+
assert(audio_devices && n_devices > ad);
- assert((unsigned) idx < NETBSD_NUM_OUTPORTS);
- return &out_ports[idx];
+ if (audio_devices[ad].audio_info.fd < 0)
+ return 0;
+
+ update_audio_info(ad);
+
+ port = audio_devices[ad].play.port;
+ play_ctrl = &audio_devices[ad].play.gain_ctrl[port];
+ gain = average_mixer_level(play_ctrl->un.value);
+#if DEBUG_MIXER > 1
+ warnx("netbsd_audio_get_ogain(%d): ogain=%d", ad, gain);
+#endif
+ gain = netbsd_device_to_rat(gain);
+ return gain;
}
/*
- * netbsd_audio_iport_count: get number of available input ports.
+ * netbsd_audio_set_igain: set record gain (percent (%) of maximum)
*/
-int
-netbsd_audio_iport_count(audio_desc_t ad)
+void
+netbsd_audio_set_igain(audio_desc_t ad, int gain)
{
- int fd;
- audio_info_t dev_info;
- audio_port_t n = 0;
+ int fd;
+ audio_info_t audio_info;
+ mixer_ctrl_t mixer_info;
+ int port;
+ record_gain_ctrl_t *record_ctrl;
+ int i;
+
+#if DEBUG_MIXER > 1
+ warnx("netbsd_audio_set_igain(%d,%d)", ad, gain);
+#endif
assert(audio_devices && n_devices > ad);
- fd = audio_devices[ad].audio_info.fd;
- if (fd < 0)
- return 0;
+ if (audio_devices[ad].audio_info.fd < 0)
+ return;
- if (ioctl(fd, AUDIO_GETINFO, &dev_info) < 0) {
- perror("netbsd_audio_iport_count: getting parameters");
- return 0;
+ update_audio_info(ad);
+
+ gain = netbsd_rat_to_device(gain);
+ port = audio_devices[ad].record.port;
+ record_ctrl = &audio_devices[ad].record.gain_ctrl[port];
+
+ /* audio control */
+ if (record_ctrl->gain.type == AUDIO_PORT) {
+ fd = audio_devices[ad].audio_info.fd;
+ AUDIO_INITINFO(&audio_info);
+ audio_info.record.gain = gain;
+ if (ioctl(fd, AUDIO_SETINFO, &audio_info) < 0) {
+ perror("netbsd_audio_set_igain: "
+ "setting audio parameters");
+ return;
+ }
}
- while (dev_info.record.avail_ports != 0) {
- if (dev_info.record.avail_ports & 1)
- n++;
- dev_info.record.avail_ports >>= 1;
+ /* mixer control */
+ if (record_ctrl->gain.type != AUDIO_PORT) {
+ fd = audio_devices[ad].mixer_info.fd;
+ mixer_info = record_ctrl->gain;
+ for (i = 0; i < record_ctrl->gain.un.value.num_channels; ++i)
+ mixer_info.un.value.level[i] = gain;
+ if (ioctl(fd, AUDIO_MIXER_WRITE, &mixer_info) < 0) {
+ perror("netbsd_audio_set_igain: "
+ "setting mixer parameters");
+ return;
+ }
}
- return n;
+ /* update record gain */
+ for (i = 0; i < record_ctrl->gain.un.value.num_channels; ++i)
+ record_ctrl->gain.un.value.level[i] = gain;
}
/*
- * netbsd_audio_oport_count: get number of available output ports
+ * netbsd_audio_set_ogain: set play (output) gain (percent (%) of maximum)
*/
-int
-netbsd_audio_oport_count(audio_desc_t ad)
+void
+netbsd_audio_set_ogain(audio_desc_t ad, int gain)
{
- int fd;
- audio_info_t dev_info;
- int n = 0;
+ int fd;
+ audio_info_t audio_info;
+ mixer_ctrl_t mixer_info;
+ int port;
+ mixer_ctrl_t *play_ctrl;
+ int i;
+
+#if DEBUG_MIXER > 1
+ warnx("netbsd_audio_set_ogain(%d, %d)", ad, gain);
+#endif
assert(audio_devices && n_devices > ad);
- fd = audio_devices[ad].audio_info.fd;
- if (fd < 0)
- return 0;
+ if (audio_devices[ad].audio_info.fd < 0)
+ return;
- if (ioctl(fd, AUDIO_GETINFO, &dev_info) < 0) {
- perror("netbsd_audio_oport_count: getting parameters");
- return 0;
+ update_audio_info(ad);
+
+ gain = netbsd_rat_to_device(gain);
+ port = audio_devices[ad].play.port;
+ play_ctrl = &audio_devices[ad].play.gain_ctrl[port];
+
+ /* audio control */
+ if (play_ctrl->type == AUDIO_PORT) {
+ fd = audio_devices[ad].audio_info.fd;
+ AUDIO_INITINFO(&audio_info);
+ audio_info.play.gain = gain;
+ if (ioctl(fd, AUDIO_SETINFO, &audio_info) < 0) {
+ perror("netbsd_audio_set_ogain: "
+ "setting audio parameters");
+ return;
+ }
}
- while (dev_info.play.avail_ports != 0) {
- if (dev_info.play.avail_ports & 1)
- n++;
- dev_info.play.avail_ports >>= 1;
+ /* mixer control */
+ if (play_ctrl->type != AUDIO_PORT) {
+ fd = audio_devices[ad].mixer_info.fd;
+ mixer_info = *play_ctrl;
+ for (i = 0; i < play_ctrl->un.value.num_channels; ++i)
+ mixer_info.un.value.level[i] = gain;
+ if (ioctl(fd, AUDIO_MIXER_WRITE, &mixer_info) < 0) {
+ perror("netbsd_audio_set_ogain: "
+ "setting mixer parameters");
+ return;
+ }
}
- /*
- * Some drivers report no available ports, because the mixer
- * cannot change output ports. Assume one port is available
- * in these cases.
- */
- if (n == 0)
- n = 1;
- return n;
+ /* update play gain */
+ for (i = 0; i < play_ctrl->un.value.num_channels; ++i)
+ play_ctrl->un.value.level[i] = gain;
}
/*
* netbsd_audio_loopback: set loopback gain (percent (%) of maximum)
+ *
+ * Note: nothing to do; loopback device is not yet supported.
*/
void
netbsd_audio_loopback(audio_desc_t ad, int gain)
{
- int fd;
- mixer_ctrl_t mixer_info;
- int max_gain;
+ int unused_gain;
+
+#if DEBUG_MIXER > 1
+ warnx("netbsd_audio_loopback(%d, %d)", ad, gain);
+#endif
assert(audio_devices && n_devices > ad);
+ if (audio_devices[ad].audio_info.fd < 0)
+ return;
- fd = audio_devices[ad].mixer_info.fd;
- if (fd >= 0) {
- mixer_info = audio_devices[ad].mixer_info.loopback_gain;
- max_gain = audio_devices[ad].mixer_info.max_loopback_gain;
- set_mixer_gain(fd, &mixer_info,
- netbsd_rat_to_device(gain, max_gain));
- audio_devices[ad].mixer_info.loopback_gain = mixer_info;
- }
- /* Nothing to do; loopback gain is not available via audio device */
+ unused_gain = gain; /* XXX - avoid compiler complaints */
}
@@ -973,9 +1115,8 @@
int
netbsd_audio_supports(audio_desc_t ad, audio_format * fmt)
{
- int fd;
- audio_info_t dev_info; /* Save state to restore later */
- audio_encoding_t *supported;
+ int fd;
+ const audio_encoding_t *encodings;
assert(audio_devices && n_devices > ad);
fd = audio_devices[ad].audio_info.fd;
@@ -992,40 +1133,8 @@
*/
audio_format_change_encoding(fmt, fmt->encoding);
- if (ioctl(fd, AUDIO_GETINFO, &dev_info) < 0) {
- perror("netbsd_audio_supports: getting device parameters");
- return FALSE;
- }
- supported = set_encoding(ad, fmt, fmt);
- debug_msg("NetBSD audio: "
- "%d Hz, %d bits/sample, %d %s, "
- "requested rat encoding %d, "
- "mapped kernel encoding %s)",
- fmt->sample_rate, fmt->bits_per_sample,
- fmt->channels, fmt->channels == 1 ? "channel" : "channels",
- fmt->encoding, supported ? supported->name : "none");
- return supported != NULL;
-}
-
-
-/*
- * auddev_netbsd_setfd: set full duplex mode
- *
- * This function is needed for OSS support, because the
- * SNDCTL_DSP_SETDUPLEX ioctl is not implemented in NetBSD.
- */
-
-int
-auddev_netbsd_setfd(int fd)
-{
- int full_duplex = 1;
- int error;
-
- error = ioctl(fd, AUDIO_SETFD, &full_duplex);
- if (error < 0)
- perror("setting full duplex");
-
- return error;
+ encodings = audio_devices[ad].audio_info.audio_enc;
+ return match_encoding(fmt, encodings) != NULL;
}
@@ -1036,26 +1145,31 @@
int
probe_device(const char *audio_dev_name, const char *mixer_dev_name)
{
- audio_dinfo_t audio_info;
- mixer_dinfo_t mixer_info;
+ audio_dinfo_t audio_info;
+ mixer_dinfo_t mixer_info;
if (!probe_audio_device(audio_dev_name, &audio_info))
return FALSE;
-
probe_mixer_device(mixer_dev_name, &mixer_info);
+
+#if DEBUG_MIXER > 1
+ warnx(" %s", audio_dev_name);
+#endif
+
audio_devices = (audio_devices_t *) realloc
- (audio_devices, (n_devices + 1) * sizeof(audio_devices_t));
+ (audio_devices, (n_devices + 1) * sizeof(audio_devices_t));
audio_devices[n_devices].full_name
- = (char *) malloc(strlen(audio_info.audio_dev.name)
- + strlen(audio_info.dev_name)
- + 4 + 1);
+ = (char *) malloc(strlen(audio_info.audio_dev.name)
+ + strlen(audio_info.dev_name)
+ + 4 + 1);
strcpy(audio_devices[n_devices].full_name,
- audio_info.audio_dev.name);
+ audio_info.audio_dev.name);
strcat(audio_devices[n_devices].full_name, " -- ");
strcat(audio_devices[n_devices].full_name, audio_info.dev_name);
audio_devices[n_devices].audio_info = audio_info;
audio_devices[n_devices].mixer_info = mixer_info;
++n_devices;
+
return TRUE;
}
@@ -1067,50 +1181,49 @@
int
probe_audio_device(const char *dev_name, audio_dinfo_t * audio_info)
{
- int fd;
- audio_device_t audio_dev;
- int audio_props;
- audio_encoding_t aenc;
- audio_encoding_t *audio_enc;
- int nenc;
- int i;
+ int fd;
+ audio_encoding_t encoding;
+ int n;
+ int i;
debug_msg("probe_audio_device(%s)\n", dev_name);
- if ((fd = open(dev_name, O_RDONLY | O_NONBLOCK)) == -1)
+
+ if ((fd = open(dev_name, O_RDWR | O_NONBLOCK, 0)) < 0)
return FALSE;
- if (ioctl(fd, AUDIO_GETDEV, &audio_dev) < 0) {
+ if (ioctl(fd, AUDIO_GETDEV, &audio_info->audio_dev) < 0) {
+ perror("getting audio device info");
close(fd);
return FALSE;
}
- if (ioctl(fd, AUDIO_GETPROPS, &audio_props) < 0) {
+ if (ioctl(fd, AUDIO_GETPROPS, &audio_info->audio_props) < 0) {
perror("getting audio device properties");
close(fd);
return FALSE;
}
- if (!(audio_props & AUDIO_PROP_FULLDUPLEX)) {
+ if (!(audio_info->audio_props & AUDIO_PROP_FULLDUPLEX)) {
warnx("skipping %s; only full duplex devices supported.",
- audio_info->dev_name);
+ audio_info->dev_name);
+ close(fd);
return FALSE;
}
- for (nenc = 0;; nenc++) {
- aenc.index = nenc;
- if (ioctl(fd, AUDIO_GETENC, &aenc) < 0)
+ /* get supported encodings */
+ for (n = 0;; n++) {
+ encoding.index = n;
+ if (ioctl(fd, AUDIO_GETENC, &encoding) < 0)
break;
}
- audio_enc = calloc(nenc + 1, sizeof *audio_enc);
-
- for (i = 0; i < nenc; i++) {
- audio_enc[i].index = i;
- ioctl(fd, AUDIO_GETENC, &audio_enc[i]);
+ audio_info->audio_enc = calloc(n + 1, sizeof *audio_info->audio_enc);
+ for (i = 0; i < n; i++) {
+ audio_info->audio_enc[i].index = i;
+ ioctl(fd, AUDIO_GETENC, &audio_info->audio_enc[i]);
}
+
close(fd);
audio_info->dev_name = strdup(dev_name);
- audio_info->fd = -1;
- audio_info->audio_dev = audio_dev;
- audio_info->audio_props = audio_props;
- audio_info->audio_enc = audio_enc;
+ audio_info->fd = INVALID_FD;
+ AUDIO_INITINFO(&audio_info->audio_info);
return TRUE;
}
@@ -1122,525 +1235,1036 @@
int
probe_mixer_device(const char *dev_name, mixer_dinfo_t * mixer_info)
{
- int fd;
- mixer_devinfo_t devinfo;
- mixer_devinfo_t *mixers;
- int n_mixers = 0;
- mixer_ctrl_t minfo;
- int i;
- int ok;
+ int fd;
debug_msg("probe_mixer_device(%s)\n", dev_name);
- memset(mixer_info, 1, sizeof(*mixer_info));
- mixer_info->fd = -1;
- mixer_info->preamp_check = TRUE;
- if ((fd = open(dev_name, O_RDONLY | O_NONBLOCK)) == -1)
- return FALSE;
-
- /* Count number of mixer devices */
- while (1) {
- devinfo.index = n_mixers;
- if (ioctl(fd, AUDIO_MIXER_DEVINFO, &devinfo) < 0)
- break;
- ++n_mixers;
- }
- if (n_mixers <= 0)
+ if ((fd = open(dev_name, O_RDONLY | O_NONBLOCK, 0)) < 0) {
return FALSE;
- mixers = calloc(n_mixers, sizeof(*mixers));
-
- /* Get kernel information on mixer devices */
- for (i = 0; i < n_mixers; i++) {
- mixers[i].index = i;
- ioctl(fd, AUDIO_MIXER_DEVINFO, &mixers[i]);
}
+ close(fd);
- /* Find suitable mixer devices */
- mixer_info->play_gain = match_mixer_device
- (fd, mixers, n_mixers, mixer_play_gain);
- mixer_info->record_gain = match_mixer_device
- (fd, mixers, n_mixers, mixer_record_gain);
- mixer_info->loopback_gain = match_mixer_device
- (fd, mixers, n_mixers, mixer_loopback_gain);
+ mixer_info->dev_name = strdup(dev_name);
+ mixer_info->fd = INVALID_FD;
+ return TRUE;
+}
- free(mixers);
+/*
+ * set_audio_info: set kernel data structure
+ *
+ * Return: initialized kernel data structure
+ */
- ok = mixer_info->play_gain.type == AUDIO_MIXER_VALUE
- || mixer_info->record_gain.type == AUDIO_MIXER_VALUE
- || mixer_info->loopback_gain.type == AUDIO_MIXER_VALUE;
+audio_info_t
+set_audio_info(audio_format * ifmt, audio_format * ofmt)
+{
+ audio_info_t audio_info;
+ int blocksize;
+ int i;
- if (ok)
- mixer_info->dev_name = strdup(dev_name);
+ AUDIO_INITINFO(&audio_info);
/*
- * Find maximum mixer gains. Note that the maximum gain for
- * any given mixer device is hardware dependent and may be
- * less than AUDIO_MAX_GAIN. The maximum gain is stored so
- * that the rat controls will act consistently across mixer
- * devices.
- */
- minfo = mixer_info->play_gain;
- if (minfo.type == AUDIO_MIXER_VALUE) {
- set_mixer_gain(fd, &minfo, AUDIO_MAX_GAIN);
- mixer_info->max_play_gain = get_mixer_gain(fd, &minfo);
- set_mixer_gain(fd, &minfo, AUDIO_MIN_GAIN);
- } else
- mixer_info->max_play_gain = 0;
- minfo = mixer_info->record_gain;
- if (minfo.type == AUDIO_MIXER_VALUE) {
- set_mixer_gain(fd, &minfo, AUDIO_MAX_GAIN);
- mixer_info->max_record_gain = get_mixer_gain(fd, &minfo);
- set_mixer_gain(fd, &minfo, AUDIO_MIN_GAIN);
- } else
- mixer_info->max_record_gain = 0;
- minfo = mixer_info->loopback_gain;
- if (minfo.type == AUDIO_MIXER_VALUE) {
- set_mixer_gain(fd, &minfo, AUDIO_MAX_GAIN);
- mixer_info->max_loopback_gain = get_mixer_gain(fd, &minfo);
- set_mixer_gain(fd, &minfo, AUDIO_MIN_GAIN);
- } else
- mixer_info->max_loopback_gain = 0;
+ * Note that AUMODE_PLAY_ALL is critical here to ensure continuous
+ * audio output. Rat attempts to manage the timing of the output
+ * audio stream; it is important that the kernel not attempt to do the
+ * same.
+ */
+ audio_info.mode = AUMODE_PLAY_ALL | AUMODE_RECORD;
- close(fd);
+ audio_info.play.sample_rate = ofmt->sample_rate;
+ audio_info.play.channels = ofmt->channels;
+ audio_info.play.precision = ofmt->bits_per_sample;
+ audio_info.play.encoding = ofmt->encoding;
- /* Report matched mixer devices */
- if (ok) {
- warnx("NetBSD mixer gain controls: %s", dev_name);
- if (mixer_info->play_gain.type == AUDIO_MIXER_VALUE) {
- int i = mixer_info->play_gain.dev;
-#if DEBUG_MIXER
- warnx(" output gain: %s.%s [%d]",
- mixers[mixers[i].mixer_class].label.name,
- mixers[i].label.name, i);
-#else
- warnx(" output gain: %s.%s",
- mixers[mixers[i].mixer_class].label.name,
- mixers[i].label.name);
-#endif
- }
- if (mixer_info->record_gain.type == AUDIO_MIXER_VALUE) {
- int i = mixer_info->record_gain.dev;
-#if DEBUG_MIXER
- warnx(" record gain: %s.%s [%d]",
- mixers[mixers[i].mixer_class].label.name,
- mixers[i].label.name, i);
-#else
- warnx(" record gain: %s.%s",
- mixers[mixers[i].mixer_class].label.name,
- mixers[i].label.name);
-#endif
- }
- if (mixer_info->loopback_gain.type == AUDIO_MIXER_VALUE) {
- int i = mixer_info->loopback_gain.dev;
-#if DEBUG_MIXER
- warnx(" loopback gain: %s.%s [%d]",
- mixers[mixers[i].mixer_class].label.name,
- mixers[i].label.name, i);
-#else
- warnx(" loopback gain: %s.%s",
- mixers[mixers[i].mixer_class].label.name,
- mixers[i].label.name);
-#endif
- }
- } else
- warnx("no NetBSD mixer gain contols found: %s", dev_name);
+ audio_info.record.sample_rate = ifmt->sample_rate;
+ audio_info.record.channels = ifmt->channels;
+ audio_info.record.precision = ifmt->bits_per_sample;
+ audio_info.record.encoding = ifmt->encoding;
- return ok;
-}
+ blocksize = max(ifmt->bytes_per_block, ofmt->bytes_per_block);
+ i = 1;
+ while (blocksize) {
+ blocksize >>= 1;
+ i <<= 1;
+ }
+ audio_info.blocksize = i; /* next power of 2 larger than
+ * blocksize */
+ return audio_info;
+}
/*
- * match_mixer_device
+ * match_encoding: get audio device encoding matching fmt
+ *
+ * Find the best match among supported encodings. Prefer encodings
+ * that are not emulated in the kernel.
*/
-mixer_ctrl_t
-match_mixer_device(int fd,
- mixer_devinfo_t * mixers, int n_mixers,
- struct mixer_devices * devices)
+const audio_encoding_t *
+match_encoding(audio_format * fmt, const audio_encoding_t * encodings)
{
- mixer_ctrl_t mixer_info;
- mixer_devinfo_t *m;
- struct mixer_devices *d;
- const char *class_name;
- const char *device_name;
+ const audio_encoding_t *best_encp = NULL;
+ struct audio_encoding_match *match_encp;
+ const audio_encoding_t *encp;
- for (d = devices; d->class != NULL; ++d) {
-#if DEBUG_MIXER
- warnx("match_mixer_device(): target=%s.%s",
- d->class, d->device);
-#endif
- for (m = mixers; m < mixers + n_mixers; ++m) {
- if (m->type != AUDIO_MIXER_VALUE)
- continue;
- class_name = mixers[m->mixer_class].label.name;
- device_name = m->label.name;
-#if DEBUG_MIXER
- warnx(" %d. %s.%s", m - mixers,
- class_name, device_name);
-#endif
- if (strcmp(class_name, d->class) != 0)
- continue;
- if (strcmp(device_name, d->device) != 0)
- continue;
-#if DEBUG_MIXER
- warnx("match_mixer_device(): matched");
-#endif
- mixer_info.dev = m->index;
- mixer_info.type = m->type;
- mixer_info.un.value.num_channels =
- m->un.v.num_channels;
- if (ioctl(fd, AUDIO_MIXER_READ, &mixer_info) < 0) {
- perror("match_mixer_device");
- continue;
+ for (match_encp = audio_encoding_match;
+ match_encp->netbsd_encoding != AUDIO_ENCODING_NONE;
+ ++match_encp) {
+
+ if (match_encp->rat_encoding != fmt->encoding)
+ continue; /* skip mismatched encodings */
+
+ /* scan supported encodings */
+ for (encp = encodings; encp->precision != 0; ++encp) {
+ if (encp->encoding == match_encp->netbsd_encoding
+ && encp->precision == fmt->bits_per_sample
+ && (best_encp == NULL
+ || (best_encp->flags & AUDIO_ENCODINGFLAG_EMULATED)
+ || !(encp->flags & AUDIO_ENCODINGFLAG_EMULATED))) {
+ best_encp = encp;
}
- return mixer_info;
}
}
- mixer_info.dev = 0;
- mixer_info.type = 0;
- return mixer_info;
+ return best_encp;
}
/*
- * set_mode: set kernel audio device mode
+ * audio_select: wait for delay_us microseconds
+ */
+
+int
+audio_select(audio_desc_t ad, int delay_us)
+{
+ int fd;
+ fd_set rfds;
+ struct timeval tv;
+
+ fd = audio_devices[ad].audio_info.fd;
+ tv.tv_sec = 0;
+ tv.tv_usec = delay_us;
+
+ FD_ZERO(&rfds);
+ FD_SET(fd, &rfds);
+
+ select(fd + 1, &rfds, NULL, NULL, &tv);
+
+ return FD_ISSET(fd, &rfds) ? TRUE : FALSE;
+}
+
+/*
+ * update_audio_info
*
- * Note that AUMODE_PLAY_ALL is critical here to ensure continuous
- * audio output. Rat attempts to manage the timing of the output
- * audio stream; it is important that the kernel not attempt to do the
- * same.
+ * Update the in-memory data structures from the kernel. This enables
+ * rat to respond to any audio or mixer device changes occuring via
+ * any other mechanism. Always update all audio/mixer devices so they
+ * are accurate when new ports are selected.
*/
void
-set_mode(audio_desc_t ad)
+update_audio_info(audio_desc_t ad)
{
- int fd = audio_devices[ad].audio_info.fd;
- audio_info_t dev_info;
+ int fd;
+ audio_info_t audio_info;
+ mixer_ctrl_t mixer_info;
+ mixer_ctrl_t *play_ctrl;
+ record_gain_ctrl_t *record_ctrl;
+ int i;
+ int j;
- if (ioctl(fd, AUDIO_GETINFO, &dev_info) < 0) {
- perror("set_mode: getting parameters");
- return;
+ /* audio device info */
+ fd = audio_devices[ad].audio_info.fd;
+ if (ioctl(fd, AUDIO_GETINFO, &audio_info) < 0)
+ err(1, "getting audio parameters");
+
+ /* play info */
+ for (i = 0; i < audio_devices[ad].play.n; ++i) {
+ play_ctrl = &audio_devices[ad].play.gain_ctrl[i];
+ /* audio control */
+ if (play_ctrl->type == AUDIO_PORT
+ && play_ctrl->dev == (int) audio_info.play.port) {
+ play_ctrl->un.value.level[0] = audio_info.play.gain;
+ }
+ /* mixer control */
+ if (play_ctrl->type != AUDIO_PORT) {
+ fd = audio_devices[ad].mixer_info.fd;
+ mixer_info = *play_ctrl;
+ if (ioctl(fd, AUDIO_MIXER_READ, &mixer_info) < 0)
+ err(1, "getting mixer parameters (%d)", i);
+ play_ctrl->un.value = mixer_info.un.value;
+ }
}
- if (!(dev_info.mode & AUMODE_PLAY_ALL)
- || !(dev_info.mode & AUMODE_RECORD)) {
- AUDIO_INITINFO(&dev_info);
- dev_info.mode = AUMODE_PLAY_ALL | AUMODE_RECORD;
- if (ioctl(fd, AUDIO_SETINFO, &dev_info) < 0) {
- perror("setting play/record mode");
+
+ /* record info */
+ for (i = 0; i < audio_devices[ad].record.n; ++i) {
+ record_ctrl = &audio_devices[ad].record.gain_ctrl[i];
+ if (record_ctrl->gain.type == AUDIO_PORT
+ && record_ctrl->gain.dev == (int) audio_info.record.port) {
+ for (j = 0; j < record_ctrl->gain.un.value.num_channels;
+ ++j) {
+ record_ctrl->gain.un.value.level[j]
+ = audio_info.record.gain;
+ }
+ }
+ /* mixer control */
+ if (record_ctrl->gain.type != AUDIO_PORT) {
+ fd = audio_devices[ad].mixer_info.fd;
+ mixer_info = record_ctrl->gain;
+ if (ioctl(fd, AUDIO_MIXER_READ, &mixer_info) < 0)
+ err(1, "getting mixer gain parameters (%d)", i);
+ record_ctrl->gain = mixer_info;
}
}
}
+/*
+ * average_mixer_level: calculate the average across channels
+ */
+
+u_char
+average_mixer_level(mixer_level_t mixer_level)
+{
+ int sum = 0;
+ int i;
+
+ for (i = 0; i < mixer_level.num_channels; ++i)
+ sum += mixer_level.level[i];
+ return sum / mixer_level.num_channels;
+}
/*
- * set_audio_properties: set audio device properties
- *
- * Initialize kernel audio device balance and record port. Note that
- * audio gains remain as before.
+ * find_gain_ctrl: construct dynamic lists of gain controls
*/
void
-set_audio_properties(audio_desc_t ad)
+find_gain_ctrl(audio_desc_t ad)
{
- int fd = audio_devices[ad].audio_info.fd;
- audio_info_t dev_info;
+ mixer_devinfo_t *mixers;
- AUDIO_INITINFO(&dev_info);
+ mixers = mixer_devices(ad);
- /* Input port setup */
- dev_info.record.port = AUDIO_MICROPHONE;
- dev_info.record.balance = AUDIO_MID_BALANCE;
+ audio_devices[ad].play.port = AUDIO_INVALID_PORT;
+ audio_devices[ad].play.n = 0;
+ audio_devices[ad].play.gain_ctrl = NULL;
+ audio_devices[ad].record.port = AUDIO_INVALID_PORT;
+ audio_devices[ad].record.n = 0;
+ audio_devices[ad].record.gain_ctrl = NULL;
+
+ find_audio_play_ctrl(ad);
+ find_audio_record_ctrl(ad);
+ find_mixer_play_ctrl(ad, mixers);
+ find_mixer_record_ctrl(ad, mixers);
+
+ if (audio_devices[ad].play.port == AUDIO_INVALID_PORT)
+ audio_devices[ad].play.port = 0;
+ if (audio_devices[ad].record.port == AUDIO_INVALID_PORT)
+ audio_devices[ad].record.port = 0;
- /* Output port setup */
- dev_info.play.balance = AUDIO_MID_BALANCE;
+ map_netbsd_ports(ad);
+ fix_rat_names();
- if (ioctl(fd, AUDIO_SETINFO, &dev_info) < 0) {
- perror("setting properties");
- return;
- }
-}
+#if DEBUG_MIXER > 0
+ print_gain_ctrl(ad, mixers);
+#endif
+ free(mixers);
+}
/*
- * get mixer gain (kernel units)
+ * find_audio_play_ctrl: append audio output gain controls to list
*/
-int
-get_mixer_gain(int fd, mixer_ctrl_t * mixer_info)
+void
+find_audio_play_ctrl(audio_desc_t ad)
{
- assert(fd >= 0);
- assert(mixer_info->type == AUDIO_MIXER_VALUE);
+ int fd;
+ audio_info_t audio_devinfo;
+ mixer_ctrl_t *mp;
+ audio_port_details_t *ap;
+ int n = 0;
- if (ioctl(fd, AUDIO_MIXER_READ, mixer_info) < 0) {
- perror("get_mixer_gain: getting mixer parameters");
- return 0;
+ fd = audio_devices[ad].audio_info.fd;
+ if (ioctl(fd, AUDIO_GETINFO, &audio_devinfo) < 0) {
+ perror("reading audio device information");
+ return;
+ }
+ for (ap = netbsd_out_ports;
+ ap < netbsd_out_ports + NETBSD_NUM_OUTPORTS;
+ ++ap) {
+ if (audio_devinfo.play.avail_ports & ap->port) {
+ ++n;
+ ++audio_devices[ad].play.n;
+ audio_devices[ad].play.gain_ctrl
+ = (mixer_ctrl_t *) realloc
+ (audio_devices[ad].play.gain_ctrl,
+ audio_devices[ad].play.n * sizeof(mixer_ctrl_t));
+ mp = audio_devices[ad].play.gain_ctrl
+ + audio_devices[ad].play.n - 1;
+ mp->dev = ap->port;
+ mp->type = AUDIO_PORT;
+ mp->un.value.num_channels = 1;
+ mp->un.value.level[0]
+ = (audio_devinfo.play.port == ap->port ?
+ audio_devinfo.play.gain : AUDIO_MID_GAIN);
+
+ /* set default audio play port */
+ if (ap->port == AUDIO_SPEAKER)
+ audio_devices[ad].play.port
+ = audio_devices[ad].play.n - 1;
+ }
}
- return average_mixer_level(*mixer_info);
-}
+ if (n == 0) { /* construct default output port */
+ ++n;
+ ++audio_devices[ad].play.n;
+ audio_devices[ad].play.gain_ctrl = (mixer_ctrl_t *) realloc
+ (audio_devices[ad].play.gain_ctrl,
+ audio_devices[ad].play.n * sizeof(mixer_ctrl_t));
+ mp = audio_devices[ad].play.gain_ctrl
+ + audio_devices[ad].play.n - 1;
+ mp->dev = 0;
+ mp->type = AUDIO_PORT;
+ mp->un.value.num_channels = 1;
+ mp->un.value.level[0] = audio_devinfo.play.gain;
+
+ /* set default audio play port */
+ audio_devices[ad].play.port = audio_devices[ad].play.n - 1;
+ }
+}
/*
- * set_mixer_gain (kernel units)
+ * find_audio_record_ctrl: append audio input gain controls to list
*/
void
-set_mixer_gain(int fd, mixer_ctrl_t * mixer_info, int gain)
+find_audio_record_ctrl(audio_desc_t ad)
{
- mixer_ctrl_t devinfo;
- int i;
-
- assert(fd >= 0);
- assert(mixer_info->type == AUDIO_MIXER_VALUE);
+ int fd;
+ audio_info_t audio_devinfo;
+ record_gain_ctrl_t *gp;
+ audio_port_details_t *ap;
+ int n = 0;
- devinfo = *mixer_info;
- if (ioctl(fd, AUDIO_MIXER_READ, &devinfo) < 0) {
- perror("set_mixer_gain: reading mixer parameters");
+ fd = audio_devices[ad].audio_info.fd;
+ if (ioctl(fd, AUDIO_GETINFO, &audio_devinfo) < 0) {
+ perror("reading audio device information");
return;
}
- for (i = 0; i < devinfo.un.value.num_channels; ++i)
- devinfo.un.value.level[i] = gain;
+ for (ap = netbsd_in_ports; ap < netbsd_in_ports + NETBSD_NUM_INPORTS;
+ ++ap) {
+ if (audio_devinfo.record.avail_ports & ap->port) {
+ ++n;
+ append_record_gain_ctrl(&audio_devices[ad].record);
+ gp = audio_devices[ad].record.gain_ctrl
+ + audio_devices[ad].record.n - 1;
+ gp->gain.dev = ap->port;
+ gp->gain.type = AUDIO_PORT;
+ gp->gain.un.value.num_channels = 1;
+ gp->gain.un.value.level[0]
+ = (audio_devinfo.record.port == ap->port ?
+ audio_devinfo.record.gain : AUDIO_MID_GAIN);
+ }
+ /* set default audio record port */
+ if (ap->port == AUDIO_MICROPHONE)
+ audio_devices[ad].record.port
+ = audio_devices[ad].record.n - 1;
+ }
+ if (n == 0) { /* construct default output port */
+ append_record_gain_ctrl(&audio_devices[ad].record);
+ gp = audio_devices[ad].record.gain_ctrl
+ + audio_devices[ad].record.n - 1;
+ gp->gain.dev = 0;
+ gp->gain.type = AUDIO_PORT;
+ gp->gain.un.value.num_channels = 1;
+ gp->gain.un.value.level[0] = audio_devinfo.record.gain;
- if (ioctl(fd, AUDIO_MIXER_WRITE, &devinfo) < 0) {
- perror("set_mixer_gain: setting mixer parameters");
- return;
+ /* set default audio record port */
+ audio_devices[ad].record.port = audio_devices[ad].record.n - 1;
}
- *mixer_info = devinfo;
}
-
/*
- * average_mixer_level: calculate the average across channels
+ * find_mixer_play_ctrl: append mixer output gain controls to list
*/
-u_char
-average_mixer_level(mixer_ctrl_t mixer_info)
+void
+find_mixer_play_ctrl(audio_desc_t ad, mixer_devinfo_t * mixers)
{
- int i;
- int num_channels = mixer_info.un.value.num_channels;
- int sum = 0;
+ const mixer_devinfo_t *mixer;
+ audio_devices_t *ap;
+ mixer_ctrl_t *mp;
+ int i;
+ mixer_devices_t *dp;
- for (i = 0; i < num_channels; ++i)
- sum += mixer_info.un.value.level[i];
- return sum / num_channels;
-}
+ ap = audio_devices + ad;
+ if (ap->mixer_info.fd < 0)
+ return;
+ /* find outputs.master or inputs.dac */
+ for (mixer = mixers, i = 0;
+ mixer->type != AUDIO_INVALID_PORT; ++mixer, ++i) {
+ for (dp = mixer_play_gain; dp->class; ++dp) {
+ if (strcmp(dp->class, mixers[mixer->mixer_class].
+ label.name) == 0
+ && strcmp(dp->device, mixer->label.name) == 0
+ && mixer->type == AUDIO_MIXER_VALUE) {
+ ++ap->play.n;
+ ap->play.gain_ctrl = (mixer_ctrl_t *) realloc
+ (ap->play.gain_ctrl, ap->play.n
+ * sizeof(mixer_ctrl_t));
+ mp = ap->play.gain_ctrl + ap->play.n - 1;
+ mp->dev = i;
+ mp->type = mixer->type;
+ mp->un.value.num_channels
+ = mixer->un.v.num_channels;
+
+ /* set default mixer play port */
+ audio_devices[ad].play.port = ap->play.n - 1;
+ break;
+ }
+ }
+ }
+}
/*
- * check_record_preamp: warn about potential preamps turned off
- *
- * Rat provides only a minimal interface to the mixer device. Because
- * there is no control for this, users are often unaware that the
- * microphone preamp is off when it should be on. This provides a
- * reminder.
+ * find_mixer_record_ctrl: append mixer input gain controls to list
*/
void
-check_record_preamp(int fd)
+find_mixer_record_ctrl(audio_desc_t ad, mixer_devinfo_t * mixers)
{
- int n_mixers = 0;
- mixer_devinfo_t *mixers;
- mixer_devinfo_t mixer;
- mixer_devinfo_t *m;
- mixer_ctrl_t mixer_info;
- const char *class_name;
- const char *parent_name;
- const char *device_name;
- int i, j;
- int off;
+ const mixer_devinfo_t *rec_src;
+ int rec_src_class;
+ const mixer_devinfo_t *rec_volume;
+ int rec_volume_class;
+ const mixer_devinfo_t *gain;
+ int gain_class;
+ const mixer_devinfo_t *first_child;
+ const mixer_devinfo_t *child;
+
+ audio_devices_t *ap;
+ record_gain_ctrl_t *mp;
+ int src;
+ int ord;
- /* Count number of mixer devices */
- while (1) {
- mixer.index = n_mixers;
- if (ioctl(fd, AUDIO_MIXER_DEVINFO, &mixer) < 0)
+ ap = audio_devices + ad;
+ if (ap->mixer_info.fd < 0)
+ return;
+
+ /* find record.source */
+ for (rec_src = mixers; rec_src->type != AUDIO_INVALID_PORT; ++rec_src) {
+ rec_src_class = rec_src->mixer_class;
+ if (strcmp(mixers[rec_src_class].label.name, AudioCrecord) == 0
+ && strcmp(rec_src->label.name, AudioNsource) == 0
+ && rec_src->type == AUDIO_MIXER_ENUM)
break;
- ++n_mixers;
}
- if (n_mixers <= 0)
+ if (rec_src->type == AUDIO_INVALID_PORT) /* no record.source
+ * found */
return;
- mixers = calloc(n_mixers, sizeof(*mixers));
-
- for (i = 0; i < n_mixers; i++) {
- mixers[i].index = i;
- ioctl(fd, AUDIO_MIXER_DEVINFO, &mixers[i]);
+ /* find record.volume */
+ for (rec_volume = mixers; rec_volume->type != AUDIO_INVALID_PORT;
+ ++rec_volume) {
+ rec_volume_class = rec_volume->mixer_class;
+ if (strcmp(mixers[rec_volume_class].label.name, AudioCrecord)
+ == 0
+ && strcmp(rec_volume->label.name, AudioNvolume) == 0
+ && rec_volume->type == AUDIO_MIXER_VALUE)
+ break;
}
- /* Search for preamp device */
- for (m = mixers; m < mixers + n_mixers; ++m) {
- parent_name = NULL;
- for (j = m->prev; j != AUDIO_MIXER_LAST; j = mixers[j].prev)
- parent_name = mixers[j].label.name;
- class_name = mixers[m->mixer_class].label.name;
- device_name = m->label.name;
- if (strcmp(device_name, AudioNpreamp) != 0)
- continue;
- if (m->type != AUDIO_MIXER_ENUM)
- continue;
- mixer_info.dev = m->index;
- mixer_info.type = m->type;
- if (ioctl(fd, AUDIO_MIXER_READ, &mixer_info) < 0) {
- perror("checking NetBSD preamp");
- continue;
- }
- if (mixer_info.type != AUDIO_MIXER_ENUM)
- continue;
- for (off = 0; off < m->un.e.num_mem; ++off)
- if (strcmp(m->un.e.member[off].label.name, AudioNoff)
- == 0)
+ /* for each possible source */
+ for (src = 0; src < rec_src->un.e.num_mem; ++src) {
+ /* is a gain port available? */
+ for (gain = mixers; gain->type != AUDIO_INVALID_PORT; ++gain) {
+ gain_class = gain->mixer_class;
+ if (strcmp(mixers[gain_class].label.name,
+ AudioCinputs) == 0
+ && strcmp(gain->label.name,
+ rec_src->un.e.member[src].label.name)
+ == 0
+ && gain->type == AUDIO_MIXER_VALUE)
break;
- /* Preamp off? */
- if (mixer_info.un.ord == m->un.e.member[off].ord) {
- warnx("mixerctl %s.%s.%s=%s; consider setting it %s",
- class_name, parent_name, device_name,
- AudioNoff, AudioNon);
- break;
}
- }
+ if (gain->type == AUDIO_INVALID_PORT) { /* no gain port */
+ if (rec_volume->type != AUDIO_INVALID_PORT)
+ gain = rec_volume; /* use record.volume
+ * instead */
+ else
+ continue; /* no gain port anywhere! */
+ }
+ /* extend table */
+ append_record_gain_ctrl(&ap->record);
+ mp = &ap->record.gain_ctrl[ap->record.n - 1];
+
+ /* gain info. */
+ mp->gain.dev = gain->index;
+ mp->gain.type = gain->type;
+ mp->gain.un.value.num_channels = gain->un.v.num_channels;
+
+ /* source info. */
+ mp->source.dev = rec_src->index;
+ mp->source.type = rec_src->type;
+ mp->source.un.ord = rec_src->un.e.member[src].ord;
+
+ /* find first in list */
+ first_child = gain;
+ while (first_child->prev != AUDIO_MIXER_LAST)
+ first_child = &mixers[first_child->prev];
+
+ /* mute info. */
+ for (child = first_child; child;
+ child = child->next != AUDIO_MIXER_LAST ?
+ &mixers[child->next] : NULL) {
+ if (strcmp(child->label.name, AudioNmute) == 0
+ && child->type == AUDIO_MIXER_ENUM) {
+ for (ord = 0; ord < child->un.e.num_mem; ++ord) {
+ if (strcmp(child->un.e.member[ord].
+ label.name, AudioNoff) == 0) {
+ mp->mute.dev = child->index;
+ mp->mute.type = child->type;
+ mp->mute.un.ord
+ = child->un.e.member[ord].ord;
+ break;
+ }
+ }
+ break;
+ }
+ }
- free(mixers);
-}
+ /*
+ * preamp info. XXX - this must come last, because it
+ * duplicates the current record for each preamp state
+ */
+ for (child = first_child; child;
+ child = child->next != AUDIO_MIXER_LAST ?
+ &mixers[child->next] : NULL) {
+ if (strcmp(child->label.name, AudioNpreamp) == 0
+ && child->type == AUDIO_MIXER_ENUM) {
+ assert(child->un.e.num_mem == 2); /* off, on */
+ mp->preamp.dev = child->index;
+ mp->preamp.type = child->type;
+ mp->preamp.un.ord = child->un.e.member[0].ord;
+ break;
+ }
+ }
+ /* set default mixer record port */
+ if (strcmp(mixers[gain->mixer_class].label.name,
+ AudioCinputs) == 0
+ && strcmp(gain->label.name, AudioNmicrophone) == 0)
+ audio_devices[ad].record.port
+ = audio_devices[ad].record.n - 1;
+ }
+}
/*
- * get_encoding: get audio device encoding matching fmt
+ * map_netbsd_ports
*
- * Find the best match among supported encodings. Prefer encodings
- * that are not emulated in the kernel.
+ * Map a selected subset of the kernel audio and mixer ports to
+ * entries in the rat device mapping table. Whenever the mixer device
+ * is available, it will duplicate ports that are also available via
+ * the audio devices much simpler interface. Consequently, we prefer
+ * to provide access to the mixer devices if they are available.
+ * Otherwise, map the audio devices for rat.
*/
-audio_encoding_t *
-get_encoding(audio_desc_t ad, audio_format * fmt)
+void
+map_netbsd_ports(audio_desc_t ad)
{
- audio_encoding_t *best_encp = NULL;
- struct audio_encoding_match *match_encp;
+ int mixer_fd;
+ mixer_devinfo_t mixer_info;
+ mixer_ctrl_t *source;
+ int i;
+ int j;
+ int len;
+ int m;
+ int n;
+ char name[MAX_AUDIO_DEV_LEN + 3 + 1];
+
+ /* give priority to mixer devices */
+ mixer_fd = audio_devices[ad].mixer_info.fd;
+ if (mixer_fd > 0) {
+ /* mixer output ports */
+ for (i = 0; i < audio_devices[ad].play.n; ++i) {
+ if (audio_devices[ad].play.gain_ctrl[i].type
+ == AUDIO_PORT)
+ continue;
+ mixer_info.index = audio_devices[ad].play.gain_ctrl[i].
+ dev;
+ if (ioctl(mixer_fd, AUDIO_MIXER_DEVINFO, &mixer_info) < 0) {
+ perror("map_netbsd_ports(): "
+ "getting play mixer device info");
+ continue;
+ }
+ ++n_rat_out_ports;
+ rat_out_ports = (audio_port_details_t *) realloc
+ (rat_out_ports, n_rat_out_ports
+ * sizeof(audio_port_details_t));
+ rat_out_ports[n_rat_out_ports - 1].port = i;
+ len = AUDIO_PORT_NAME_LENGTH;
+ rat_out_ports[n_rat_out_ports - 1].name[0] = '\0';
+ strncat(rat_out_ports[n_rat_out_ports - 1].name,
+ mixer_info.label.name, len);
+ }
+
+ /* mixer input ports */
+ for (i = 0; i < audio_devices[ad].record.n; ++i) {
+ if (audio_devices[ad].record.gain_ctrl[i].gain.type
+ == AUDIO_PORT)
+ continue;
+ source = &audio_devices[ad].record.gain_ctrl[i].source;
+ mixer_info.index = source->dev;
+ if (ioctl(mixer_fd, AUDIO_MIXER_DEVINFO, &mixer_info)
+ < 0) {
+ perror("map_netbsd_ports(): "
+ "getting record mixer device info");
+ continue;
+ }
+ ++n_rat_in_ports;
+ rat_in_ports = (audio_port_details_t *) realloc
+ (rat_in_ports, n_rat_in_ports
+ * sizeof(audio_port_details_t));
+ rat_in_ports[n_rat_in_ports - 1].port = i;
+ len = AUDIO_PORT_NAME_LENGTH;
+ rat_in_ports[n_rat_in_ports - 1].name[0] = '\0';
+ strncat(rat_in_ports[n_rat_in_ports - 1].name,
+ mixer_info.un.e.member[source->un.ord].label.name, len);
+ }
+
+ /* handle input ports with preamps */
+ n = audio_devices[ad].record.n;
+ for (i = 0; i < n; ++i) {
+ if (audio_devices[ad].record.gain_ctrl[i].gain.type
+ == AUDIO_PORT)
+ continue;
+ if (audio_devices[ad].record.gain_ctrl[i].preamp.dev < 0)
+ continue;
+ mixer_info.index = audio_devices[ad].record.gain_ctrl[i].
+ preamp.dev;
+ if (ioctl(mixer_fd, AUDIO_MIXER_DEVINFO, &mixer_info)
+ < 0) {
+ perror("map_netbsd_ports(): "
+ "getting record mixer preamp device info");
+ continue;
+ }
+ if (strcmp(mixer_info.label.name, AudioNpreamp) != 0
+ || mixer_info.type != AUDIO_MIXER_ENUM)
+ continue;
+ assert(mixer_info.un.e.num_mem == 2); /* off, on */
- match_encp = audio_encoding_match;
- while (match_encp->netbsd_encoding != AUDIO_ENCODING_NONE) {
- if (match_encp->rat_encoding == fmt->encoding) {
- audio_encoding_t *encp
- = audio_devices[ad].audio_info.audio_enc;
- while (encp->precision != 0) {
- if (encp->encoding
- == match_encp->netbsd_encoding
- && encp->precision == fmt->bits_per_sample
- && (best_encp == NULL
- || (best_encp->flags &
- AUDIO_ENCODINGFLAG_EMULATED)
- || !(encp->flags &
- AUDIO_ENCODINGFLAG_EMULATED)))
- best_encp = encp;
- ++encp;
+ /* update audio_devices */
+ copy_record_gain_ctrl(&audio_devices[ad].record,
+ &audio_devices[ad].record.gain_ctrl[i]);
+ m = audio_devices[ad].record.n - 1;
+ audio_devices[ad].record.gain_ctrl[i].preamp.un.ord =
+ mixer_info.un.e.member[0].ord;
+ audio_devices[ad].record.gain_ctrl[m].preamp.un.ord =
+ mixer_info.un.e.member[1].ord;
+ /* update rat port maps */
+ for (j = 0; j < n_rat_in_ports; ++j) {
+ if ((int) rat_in_ports[j].port != i)
+ continue;
+ copy_rat_in_port(&rat_in_ports[j]);
+ m = n_rat_in_ports - 1;
+ name[0] = '\0';
+ strcat(name, " (");
+ strcat(name, mixer_info.un.e.member[0].
+ label.name);
+ strcat(name, ")");
+ strncat(rat_in_ports[j].name, name,
+ AUDIO_PORT_NAME_LENGTH);
+ rat_in_ports[m].port
+ = audio_devices[ad].record.n - 1;
+ name[0] = '\0';
+ strcat(name, " (");
+ strcat(name, mixer_info.un.e.member[1].
+ label.name);
+ strcat(name, ")");
+ strncat(rat_in_ports[m].name, name,
+ AUDIO_PORT_NAME_LENGTH);
+ break;
}
}
- ++match_encp;
+ } else { /* otherwise settle for audio devices */
+ /* audio output ports */
+ for (i = 0; i < audio_devices[ad].play.n; ++i) {
+ if (audio_devices[ad].play.gain_ctrl[i].type
+ != AUDIO_PORT)
+ continue;
+ ++n_rat_out_ports;
+ rat_out_ports = (audio_port_details_t *) realloc
+ (rat_out_ports, n_rat_out_ports
+ * sizeof(audio_port_details_t));
+ rat_out_ports[n_rat_out_ports - 1].port = i;
+ len = AUDIO_PORT_NAME_LENGTH;
+ rat_out_ports[n_rat_out_ports - 1].name[0] = '\0';
+ strncat(rat_out_ports[n_rat_out_ports - 1].name,
+ netbsd_out_ports[i].name, len);
+ }
+
+ /* audio input ports */
+ for (i = 0; i < audio_devices[ad].record.n; ++i) {
+ if (audio_devices[ad].record.gain_ctrl[i].gain.type
+ != AUDIO_PORT)
+ continue;
+ ++n_rat_in_ports;
+ rat_in_ports = (audio_port_details_t *) realloc
+ (rat_in_ports, n_rat_in_ports
+ * sizeof(audio_port_details_t));
+ rat_in_ports[n_rat_in_ports - 1].port = i;
+ len = AUDIO_PORT_NAME_LENGTH;
+ rat_in_ports[n_rat_in_ports - 1].name[0] = '\0';
+ strncat(rat_in_ports[n_rat_in_ports - 1].name,
+ netbsd_in_ports[i].name, len);
+ }
}
- return best_encp;
}
-
/*
- * set_encoding: set audio device I/O encoding
+ * fix_rat_names
*
- * Inform kernel of selected input/output encodings, channels, precision, ... .
+ * Appropriately capitalize names in the rat device mapping table for
+ * the rat GUI
*/
-audio_encoding_t *
-set_encoding(audio_desc_t ad, audio_format * ifmt, audio_format * ofmt)
+void
+fix_rat_names()
{
- int fd = audio_devices[ad].audio_info.fd;
- audio_info_t dev_info;
- audio_encoding_t *ienc, *oenc;
+ int i;
+ char *k;
- AUDIO_INITINFO(&dev_info);
- assert(ifmt->bytes_per_block == ofmt->bytes_per_block);
- /* XXX - driver may not set blocksize? */
- dev_info.blocksize = ifmt->bytes_per_block;
+ /* play port names */
+ for (i = 0; i < n_rat_out_ports; ++i) {
+ k = rat_out_ports[i].name;
+ if (strcmp(k, "cd") == 0 || strcmp(k, "dac") == 0) {
+ while (*k) {
+ *k = toupper((int) *k);
+ ++k;
+ }
+ } else
+ *k = toupper((int) *k);
+ }
- /* Input port setup */
- dev_info.record.sample_rate = ifmt->sample_rate;
- dev_info.record.channels = ifmt->channels;
- dev_info.record.precision = ifmt->bits_per_sample;
- if ((ienc = get_encoding(ad, ifmt)) == NULL)
- return NULL;
- dev_info.record.encoding = ienc->encoding;
+ /* record port names */
+ for (i = 0; i < n_rat_in_ports; ++i) {
+ k = rat_in_ports[i].name;
+ if (strcmp(k, "cd") == 0 || strcmp(k, "dac") == 0) {
+ while (*k) {
+ *k = toupper((int) *k);
+ ++k;
+ }
+ } else
+ *k = toupper((int) *k);
+ }
+}
- /* Output port setup */
- dev_info.play.sample_rate = ofmt->sample_rate;
- dev_info.play.channels = ofmt->channels;
- dev_info.play.precision = ofmt->bits_per_sample;
- if ((oenc = get_encoding(ad, ofmt)) == NULL)
- return NULL;
- dev_info.play.encoding = oenc->encoding;
+/*
+ * mixer_devices: construct table of all mixer devices
+ */
+
+mixer_devinfo_t *
+mixer_devices(audio_desc_t ad)
+{
+ int fd;
+ mixer_devinfo_t devinfo;
+ mixer_devinfo_t *mixers;
+ int n;
+ int i;
- if (ioctl(fd, AUDIO_SETINFO, &dev_info) < 0)
+ fd = audio_devices[ad].mixer_info.fd;
+ if (fd < 0)
return NULL;
- if (ienc != oenc)
- warnx("mismatched NetBSD kernel input/output encoding: "
- "%s != %s", ienc->name, oenc->name);
+ /* count number of devices */
+ n = 0;
+ while (1) {
+ devinfo.index = n;
+ if (ioctl(fd, AUDIO_MIXER_DEVINFO, &devinfo) < 0)
+ break;
+ ++n;
+ }
+
+ mixers = (mixer_devinfo_t *) malloc((n + 1) * sizeof(mixer_devinfo_t));
+
+ /* get device info. */
+ for (i = 0; i < n; ++i) {
+ mixers[i].index = i;
+ if (ioctl(fd, AUDIO_MIXER_DEVINFO, &mixers[i]) < 0)
+ break;
+ }
+
+ mixers[n].type = AUDIO_INVALID_PORT;
+
+ return mixers;
+}
+
+/*
+ * append_record_gain_ctrl: append an empty record to a record_gain_t[]
+ */
+
+void
+append_record_gain_ctrl(record_gain_t * gp)
+{
+ record_gain_ctrl_t *cp;
- return ienc;
+ /* append a new record */
+ ++gp->n;
+ gp->gain_ctrl = (record_gain_ctrl_t *) realloc
+ (gp->gain_ctrl, gp->n * sizeof(record_gain_ctrl_t));
+
+ /* initialize new record */
+ cp = gp->gain_ctrl + gp->n - 1;
+ cp->source.dev = AUDIO_INVALID_PORT;
+ cp->gain.dev = AUDIO_INVALID_PORT;
+ cp->mute.dev = AUDIO_INVALID_PORT;
+ cp->preamp.dev = AUDIO_INVALID_PORT;
}
+/*
+ * copy_record_gain_ctrl
+ *
+ * Append a duplicate record (src) to a record_gain_t[]. This
+ * function is for creating duplicate entries for mixer controls that
+ * have preamps. The paired records will be used to correspond to the
+ * preamp being off and on.
+ */
+
+void
+copy_record_gain_ctrl(record_gain_t * gains, record_gain_ctrl_t * src)
+{
+ /* append a new record */
+ ++gains->n;
+ gains->gain_ctrl = (record_gain_ctrl_t *) realloc
+ (gains->gain_ctrl, gains->n * sizeof(*gains->gain_ctrl));
+
+ /* duplicate contents */
+ memcpy(gains->gain_ctrl + gains->n - 1, src,
+ sizeof(*gains->gain_ctrl));
+}
/*
- * set_alt_encoding: set alternate audio device I/O encoding
+ * copy_rat_in_port
*
- * Select an alternate supported encoding and inform kernel of same.
- * Prefer encodings that are not emulated in the kernel.
+ * Append a duplicate of src to the rat_in_ports array. This function
+ * is for creating duplicate entries in the rat device mapping table
+ * for mixer controls that have preamps. The paired records will be
+ * used to correspond to the preamp being off and on.
*/
-audio_encoding_t *
-set_alt_encoding(audio_desc_t ad, audio_format * ifmt, audio_format * ofmt)
+void
+copy_rat_in_port(audio_port_details_t * src)
{
- audio_encoding_t *best_encp = NULL;
- audio_format best_fmt, fmt;
- struct audio_encoding_match *match_encp;
+ /* append a new record */
+ ++n_rat_in_ports;
+ rat_in_ports = (audio_port_details_t *) realloc
+ (rat_in_ports, n_rat_in_ports * sizeof(*rat_in_ports));
- fmt = *ifmt;
- match_encp = audio_encoding_match;
- while (match_encp->netbsd_encoding != AUDIO_ENCODING_NONE) {
- audio_encoding_t *encp;
- audio_format_change_encoding(&fmt, match_encp->rat_encoding);
- encp = get_encoding(ad, &fmt);
- if (encp != NULL
- && netbsd_audio_supports(ad, &fmt)
- && (best_encp == NULL
- || (best_encp->flags & AUDIO_ENCODINGFLAG_EMULATED)
- || !(encp->flags & AUDIO_ENCODINGFLAG_EMULATED))) {
- best_encp = encp;
- best_fmt = fmt;
- }
- ++match_encp;
- }
- if (best_encp != NULL) {
- *ifmt = best_fmt;
- *ofmt = best_fmt;
- return set_encoding(ad, ifmt, ofmt);
- }
- return NULL;
+ /* duplicate contents */
+ memcpy(rat_in_ports + n_rat_in_ports - 1, src, sizeof(*rat_in_ports));
}
+#if DEBUG_MIXER > 0
/*
- * audio_select: wait for delay_us microseconds
+ * print_audio_properties: report encodings and audio properties
*/
-int
-audio_select(audio_desc_t ad, int delay_us)
+void
+print_audio_properties(audio_desc_t ad)
{
- int fd;
- fd_set rfds;
- struct timeval tv;
+ const audio_encoding_t *encp;
- fd = audio_devices[ad].audio_info.fd;
- tv.tv_sec = 0;
- tv.tv_usec = delay_us;
+ /* report supported encodings */
+ warnx("Supported encodings:");
+ encp = audio_devices[ad].audio_info.audio_enc;
+ while (encp->precision != 0) {
+ warnx(" %s: %d bits/sample %s",
+ encp->name, encp->precision,
+ encp->flags & AUDIO_ENCODINGFLAG_EMULATED ?
+ "(emulated)" : "");
+ ++encp;
+ }
- FD_ZERO(&rfds);
- FD_SET(fd, &rfds);
+ /* report device properties */
+ warnx("Device properties: ");
+ if (audio_devices[ad].audio_info.audio_props & AUDIO_PROP_INDEPENDENT)
+ warnx(" independent");
+ if (audio_devices[ad].audio_info.audio_props & AUDIO_PROP_FULLDUPLEX)
+ warnx(" full duplex");
+ if (audio_devices[ad].audio_info.audio_props & AUDIO_PROP_MMAP)
+ warnx(" mmap");
+}
- select(fd + 1, &rfds, NULL, NULL, &tv);
+/*
+ * print_audio_formats: report audio format types
+ */
- return FD_ISSET(fd, &rfds) ? TRUE : FALSE;
+void
+print_audio_formats(audio_desc_t ad, audio_format * ifmt, audio_format * ofmt)
+{
+ warnx("NetBSD audio input format:");
+ warnx(" %d Hz, %d bits/sample, %d %s",
+ audio_devices[ad].audio_info.audio_info.record.sample_rate,
+ audio_devices[ad].audio_info.audio_info.record.precision,
+ audio_devices[ad].audio_info.audio_info.record.channels,
+ audio_devices[ad].audio_info.audio_info.record.channels == 1 ?
+ "channel" : "channels");
+ warnx(" %d byte blocks",
+ audio_devices[ad].audio_info.audio_info.blocksize);
+ warnx(" requested rat encoding %d", ifmt->encoding);
+ warnx(" mapped kernel encoding %d: %s",
+ audio_devices[ad].audio_info.record_encoding->encoding,
+ audio_devices[ad].audio_info.record_encoding->name);
+ if (audio_devices[ad].audio_info.record_encoding->flags
+ & AUDIO_ENCODINGFLAG_EMULATED)
+ warnx(" NetBSD %s support is emulated",
+ audio_devices[ad].audio_info.play_encoding->name);
+
+ warnx("NetBSD audio output format:");
+ warnx(" %d Hz, %d bits/sample, %d %s",
+ audio_devices[ad].audio_info.audio_info.play.sample_rate,
+ audio_devices[ad].audio_info.audio_info.play.precision,
+ audio_devices[ad].audio_info.audio_info.play.channels,
+ audio_devices[ad].audio_info.audio_info.play.channels == 1 ?
+ "channel" : "channels");
+ warnx(" %d byte blocks",
+ audio_devices[ad].audio_info.audio_info.blocksize);
+ warnx(" requested rat encoding %d", ofmt->encoding);
+ warnx(" mapped kernel encoding %d: %s",
+ audio_devices[ad].audio_info.play_encoding->encoding,
+ audio_devices[ad].audio_info.play_encoding->name);
+ if (audio_devices[ad].audio_info.play_encoding->flags
+ & AUDIO_ENCODINGFLAG_EMULATED)
+ warnx(" NetBSD %s support is emulated",
+ audio_devices[ad].audio_info.play_encoding->name);
}
+
+/*
+ * print_gain_ctrl: report array of gain controls
+ */
+
+void
+print_gain_ctrl(audio_desc_t ad, mixer_devinfo_t * mixers)
+{
+ audio_port_details_t *ap = NULL;
+ int i;
+ int port;
+ record_gain_ctrl_t *iport;
+ mixer_ctrl_t *oport;
+ int gain_class;
+ int source_class;
+ int source;
+ int gain;
+ int mute;
+ int preamp;
+ int ord;
+
+
+ /* report audio/mixer play controls */
+ warnx("NetBSD play gain controls: %s", audio_devices[ad].full_name);
+ for (i = 0; i < n_rat_out_ports; ++i) {
+ port = rat_out_ports[i].port;
+ oport = &audio_devices[ad].play.gain_ctrl[port];
+ if (oport->type == AUDIO_PORT) {
+ for (ap = netbsd_out_ports;
+ ap < netbsd_out_ports + NETBSD_NUM_OUTPORTS;
+ ++ap) {
+ if ((int) ap->port == oport->dev)
+ warnx(" %2d. audio: %s%s (%d) ==> %s",
+ port, ap->name,
+ (port ==
+ audio_devices[ad].play.port ?
+ "*" : ""),
+ oport->dev,
+ rat_out_ports[i].name);
+ }
+ } else { /* mixer control */
+ gain = oport->dev;
+ gain_class = mixers[gain].mixer_class;
+ warnx(" %2d. mixer: %s.%s%s (%d) ==> %s", port,
+ mixers[gain_class].label.name,
+ mixers[gain].label.name,
+ (port == audio_devices[ad].play.port ? "*" : ""),
+ gain,
+ rat_out_ports[i].name);
+ }
+ }
+
+ /* report audio/mixer record controls */
+ warnx("NetBSD record gain controls: %s", audio_devices[ad].full_name);
+ for (i = 0; i < n_rat_in_ports; ++i) {
+ port = rat_in_ports[i].port;
+ iport = &audio_devices[ad].record.gain_ctrl[port];
+ if (iport->gain.type == AUDIO_PORT) {
+ for (ap = netbsd_in_ports;
+ ap < netbsd_in_ports + NETBSD_NUM_INPORTS;
+ ++ap) {
+ if ((int) ap->port
+ == iport->gain.dev)
+ warnx(" %2d. audio: %s%s (%d) ==> %s",
+ port, ap->name,
+ (port ==
+ audio_devices[ad].record.port ?
+ "*" : ""),
+ iport->gain.dev,
+ rat_in_ports[i].name);
+ }
+ } else { /* mixer control */
+ gain = iport->gain.dev;
+ gain_class = mixers[gain].mixer_class;
+ source = iport->source.dev;
+ source_class = mixers[source].mixer_class;
+ ord = iport->source.un.ord;
+ mute = iport->mute.dev;
+ preamp = iport->preamp.dev;
+ warnx(" %2d. mixer: gain=%s.%s%s (%d) ==> %s",
+ port,
+ mixers[gain_class].label.name,
+ mixers[gain].label.name,
+ (port == audio_devices[ad].record.port ? "*" : ""),
+ gain,
+ rat_in_ports[i].name);
+ warnx(" %s.%s=%s (%d: %d)",
+ mixers[source_class].label.name,
+ mixers[source].label.name,
+ mixers[source].un.e.member[ord].label.name,
+ source, ord);
+ if (mute != AUDIO_INVALID_PORT)
+ warnx(" mute=%s.%s.%s (%d)",
+ mixers[gain_class].label.name,
+ mixers[gain].label.name,
+ mixers[mute].label.name,
+ mute);
+ if (preamp != AUDIO_INVALID_PORT)
+ warnx(" preamp=%s.%s.%s (%d)",
+ mixers[gain_class].label.name,
+ mixers[gain].label.name,
+ mixers[preamp].label.name,
+ preamp);
+ if (preamp != AUDIO_INVALID_PORT) {
+ switch (iport->preamp.un.ord) {
+ case 0:
+ warnx(" preamp=%s (%d)",
+ AudioNoff,
+ iport->preamp.un.ord);
+ break;
+ case 1:
+ warnx(" preamp=%s (%d)",
+ AudioNon,
+ iport->preamp.un.ord);
+ break;
+ default:
+ warnx(" preamp=%s: (%d)",
+ "unknown state",
+ iport->preamp.un.ord);
+ break;
+ }
+ }
+ }
+ }
+}
+
+#endif /* DEBUG_MIXER */
Modified: rat/trunk/auddev_netbsd.h
==============================================================================
--- rat/trunk/auddev_netbsd.h (original)
+++ rat/trunk/auddev_netbsd.h Tue May 23 17:54:42 2006
@@ -5,7 +5,7 @@
*
* $Id$
*
- * Copyright (c) 2002-2004 Brook Milligan
+ * Copyright (c) 2002-2006 Brook Milligan
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -40,45 +40,40 @@
#include "audio_types.h"
-int netbsd_audio_init(void);
-int netbsd_audio_device_count(void);
-char* netbsd_audio_device_name (audio_desc_t ad);
-int netbsd_audio_open (audio_desc_t ad,
- audio_format* ifmt, audio_format* ofmt);
-void netbsd_audio_close (audio_desc_t ad);
-void netbsd_audio_drain (audio_desc_t ad);
-int netbsd_audio_duplex (audio_desc_t ad);
-int netbsd_audio_read (audio_desc_t ad, u_char *buf, int buf_len);
-int netbsd_audio_write (audio_desc_t ad, u_char *buf, int buf_len);
-void netbsd_audio_non_block (audio_desc_t ad);
-void netbsd_audio_block (audio_desc_t ad);
-
-void netbsd_audio_iport_set (audio_desc_t ad, audio_port_t port);
-audio_port_t
- netbsd_audio_iport_get (audio_desc_t ad);
-void netbsd_audio_oport_set (audio_desc_t ad, audio_port_t port);
-audio_port_t
- netbsd_audio_oport_get (audio_desc_t ad);
-
-void netbsd_audio_set_igain (audio_desc_t ad, int gain);
-int netbsd_audio_get_igain (audio_desc_t ad);
-void netbsd_audio_set_ogain (audio_desc_t ad, int vol);
-int netbsd_audio_get_ogain (audio_desc_t ad);
-
-const audio_port_details_t*
- netbsd_audio_iport_details (audio_desc_t ad, int idx);
-const audio_port_details_t*
- netbsd_audio_oport_details (audio_desc_t ad, int idx);
-
-int netbsd_audio_iport_count (audio_desc_t ad);
-int netbsd_audio_oport_count (audio_desc_t ad);
-
-void netbsd_audio_loopback (audio_desc_t ad, int gain);
-
-int netbsd_audio_is_ready (audio_desc_t ad);
-void netbsd_audio_wait_for (audio_desc_t ad, int delay_ms);
-int netbsd_audio_supports (audio_desc_t ad, audio_format *fmt);
+int netbsd_audio_init(void);
+int netbsd_audio_device_count(void);
+char *netbsd_audio_device_name(audio_desc_t);
+int netbsd_audio_open(audio_desc_t, audio_format *, audio_format *);
+void netbsd_audio_close(audio_desc_t);
+void netbsd_audio_drain(audio_desc_t);
+int netbsd_audio_duplex(audio_desc_t);
+int netbsd_audio_read(audio_desc_t, u_char *, int);
+int netbsd_audio_write(audio_desc_t, u_char *, int);
+void netbsd_audio_non_block(audio_desc_t);
+void netbsd_audio_block(audio_desc_t);
+
+int netbsd_audio_iport_count(audio_desc_t);
+int netbsd_audio_oport_count(audio_desc_t);
+
+const audio_port_details_t *
+ netbsd_audio_iport_details(audio_desc_t, int);
+const audio_port_details_t *
+ netbsd_audio_oport_details(audio_desc_t, int);
+
+audio_port_t netbsd_audio_get_iport(audio_desc_t);
+audio_port_t netbsd_audio_get_oport(audio_desc_t);
+void netbsd_audio_set_iport(audio_desc_t, audio_port_t);
+void netbsd_audio_set_oport(audio_desc_t, audio_port_t);
+
+int netbsd_audio_get_igain(audio_desc_t);
+int netbsd_audio_get_ogain(audio_desc_t);
+void netbsd_audio_set_igain(audio_desc_t, int);
+void netbsd_audio_set_ogain(audio_desc_t, int);
+
+void netbsd_audio_loopback(audio_desc_t, int);
+
+int netbsd_audio_is_ready(audio_desc_t);
+void netbsd_audio_wait_for(audio_desc_t, int);
+int netbsd_audio_supports(audio_desc_t, audio_format *);
-int auddev_netbsd_setfd (int fd);
-
-#endif /* _AUDDEV_NETBSD_H_ */
+#endif /* _AUDDEV_NETBSD_H_ */
Modified: rat/trunk/main_control.c
==============================================================================
--- rat/trunk/main_control.c (original)
+++ rat/trunk/main_control.c Tue May 23 17:54:42 2006
@@ -117,14 +117,14 @@
for (i = 1; i < argc; i++) {
if (argv[i][0] != '-' && argv[i][0] != '/') {
continue;
- } else if (tolower(argv[i][1]) == 'v') {
+ } else if (tolower((int)argv[i][1]) == 'v') { //SV-XXX: NetBSD
#ifdef WIN32
MessageBox(NULL, "RAT v" RAT_VERSION, "RAT Version", MB_OK);
#else
printf("%s\n", "RAT v" RAT_VERSION);
#endif
return FALSE;
- } else if (argv[i][1] == '?' || tolower(argv[i][1]) == 'h') {
+ } else if (argv[i][1] == '?' || tolower((int)argv[i][1]) == 'h') { //SV-XXX: NetBSD
usage(NULL);
return FALSE;
} else if (argv[i][1] == 't' && i + 1 < argc) {
Modified: rat/trunk/mbus_engine.c
==============================================================================
--- rat/trunk/mbus_engine.c (original)
+++ rat/trunk/mbus_engine.c Tue May 23 17:54:42 2006
@@ -477,6 +477,8 @@
}
mbus_parse_done(mp);
ui_send_audio_output_port(sp, sp->mbus_ui_addr);
+ ui_send_audio_output_mute(sp, sp->mbus_ui_addr); //SV-XXX: NetBSD
+ ui_send_audio_output_gain(sp, sp->mbus_ui_addr); //SV-XXX: NetBSD
}
static void rx_audio_channel_repair(char *srce, char *args, session_t *sp)
@@ -1389,7 +1391,7 @@
ccd = channel_get_coder_details(i);
if (strncasecmp(ccd->name, coding, 3) == 0) {
debug_msg("rx_audio_channel_coding: 0x%08x, %s\n", ccd->descriptor, &ccd->name);
- switch(tolower(ccd->name[0])) {
+ switch(tolower((int)ccd->name[0])) { //SV-XXX: NetBSD
case 'n': /* No channel coding */
sp->num_encodings = 1;
sp->layers = 1;
Modified: rat/trunk/parameters.c
==============================================================================
--- rat/trunk/parameters.c (original)
+++ rat/trunk/parameters.c Tue May 23 17:54:42 2006
@@ -258,7 +258,7 @@
int
sd_name_to_type(const char *name)
{
- switch(tolower(name[0])) {
+ switch(tolower((int)name[0])) { //SV-XXX: NetBSD
case 'a':
return SILENCE_DETECTION_AUTO;
case 'm':
Modified: rat/trunk/tcltk.c
==============================================================================
--- rat/trunk/tcltk.c (original)
+++ rat/trunk/tcltk.c Tue May 23 17:54:42 2006
@@ -339,8 +339,9 @@
{
engine_addr = xstrdup(mbus_engine_addr);
- Tcl_CreateCommand(interp, "mbus_send", mbus_send_cmd, (ClientData) mbus_ui, NULL);
- Tcl_CreateCommand(interp, "mbus_encode_str", mbus_encode_cmd, NULL, NULL);
+ //SV-XXX: NetBSD: added cast to (Tcl_CmdProc *) for 3rd argument
+ Tcl_CreateCommand(interp, "mbus_send", (Tcl_CmdProc *)mbus_send_cmd, (ClientData) mbus_ui, NULL);
+ Tcl_CreateCommand(interp, "mbus_encode_str", (Tcl_CmdProc *)mbus_encode_cmd, NULL, NULL);
/* Process Tcl/Tk events */
while (Tcl_DoOneEvent(TCL_DONT_WAIT | TCL_ALL_EVENTS)) {
Modified: rat/trunk/ui_send_audio.c
==============================================================================
--- rat/trunk/ui_send_audio.c (original)
+++ rat/trunk/ui_send_audio.c Tue May 23 17:54:42 2006
@@ -447,7 +447,7 @@
if (!sp->ui_on) return;
ccd = channel_get_coder_identity(sp->channel_coder);
- switch(tolower(ccd->name[0])) {
+ switch(tolower((int)ccd->name[0])) { //SV-XXX: NetBSD
case 'n':
mbus_qmsg(sp->mbus_engine, addr, "audio.channel.coding", "\"none\"", TRUE);
if (sp->logger != NULL) {
More information about the Sumover-dev
mailing list