Subject: Re: audio/mixer controls
To: None <brook@biology.nmsu.edu>
From: Chris Wareham <chris.wareham@iosystems.co.uk>
List: netbsd-help
Date: 08/12/2004 10:07:46
This is a multi-part message in MIME format.
--------------050407070308040107090206
Content-Type: text/plain; charset=us-ascii; format=flowed
Content-Transfer-Encoding: 7bit
brook@biology.nmsu.edu wrote:
> I am porting an audio application (rat4) to NetBSD. Its GUI provides
> one output volume control, one input volume control, and one loopback
> volume control. I am mapping these controls to appropriate mixer
> devices through a lookup table of possible devices; the first match in
> the table is the device used for the GUI controls.
>
> Not being completely familiar with all the manifestations of NetBSD
> audio drivers, I am a bit unsure about what should be in the lookup
> tables and in what order the entries should appear. Currently, I am
> using the following:
>
> static struct mixer_devices mixer_outputs_master [] = {
> { AudioCoutputs, AudioNmaster },
> { NULL, NULL }
> };
>
> static struct mixer_devices mixer_record_volume [] = {
> { AudioCrecord, AudioNmicrophone },
> { AudioCinputs, AudioNmicrophone },
> { AudioCrecord, AudioNvolume },
> { NULL, NULL }
> };
>
> static struct mixer_devices mixer_loopback_volume [] = {
> { AudioCinputs, AudioNdac },
> { NULL, NULL }
> };
>
> Does anyone have any guidance for a plausible hierarchy of mixer
> devices to try for each of the GUI controls?
>
> By the way, another similar application (using the OSS audio system)
> maps the input volume control to the record.mic mixer device and the
> output volume control to the set of {inputs.speaker, inputs.aux,
> inputs.dac} mixer devices.
>
> Any design suggestions are welcome, especially those that come with
> some logical reasoning.
>
> Thanks for your help.
>
> Cheers,
> Brook
>
If you don't mind using the OSS emulation, then you can "walk" the
available mixer controls, checking to see which are recording sources. I
have written a simple GUI mixer that does that, and the relevant code is
attached.
Chris
--------------050407070308040107090206
Content-Type: text/plain;
name="functions.h"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="functions.h"
/*
* Copyright (c) 2004 Chris Wareham. 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.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef FUNCTIONS_H
#define FUNCTIONS_H
typedef struct Device {
int id, left_value, right_value;
char *name;
int stereo : 1;
int record : 1;
int record_enabled : 1;
} Device;
typedef struct Mixer {
int fd, ndevices;
Device *devices;
int excl_input : 1;
} Mixer;
Mixer *open_mixer(const char *);
int set_mixer_value(Mixer *, int, int, int);
int set_mixer_record(Mixer *, int, int);
void close_mixer(Mixer *mixer);
void dump_mixer(Mixer *mixer);
#endif /* !FUNCTIONS_H */
--------------050407070308040107090206
Content-Type: text/plain;
name="functions.c"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="functions.c"
/*
* Copyright (c) 2004 Chris Wareham. 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.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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 <sys/ioctl.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#ifdef __NetBSD__
#include <soundcard.h>
#else
#include <sys/soundcard.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "functions.h"
static char *device_labels[] = SOUND_DEVICE_LABELS;
static void strip(char *);
Mixer *
open_mixer(const char *device)
{
int fd, devmask, recmask, recsrc, caps, stereodevs, value, i;
Mixer *mixer;
if ((fd = open(device, O_WRONLY)) < 0) {
fprintf(stderr, "Unable to open %s: %s\n", device, strerror(errno));
return NULL;
}
mixer = calloc(1, sizeof(Mixer));
mixer->fd = fd;
mixer->devices = calloc(SOUND_MIXER_NRDEVICES, sizeof(Device));
if (ioctl(fd, SOUND_MIXER_READ_DEVMASK, &devmask) < 0) {
fprintf(stderr, "Unable to get device mask: %s\n", strerror(errno));
return mixer;
}
if (ioctl(fd, SOUND_MIXER_READ_RECMASK, &recmask) < 0) {
fprintf(stderr, "Unable to get record mask: %s\n", strerror(errno));
return mixer;
}
if (ioctl(fd, SOUND_MIXER_READ_RECSRC, &recsrc) < 0) {
fprintf(stderr, "Unable to get record sources: %s\n", strerror(errno));
return mixer;
}
if (ioctl(fd, SOUND_MIXER_READ_CAPS, &caps) < 0) {
fprintf(stderr, "Unable to get capabilities: %s\n", strerror(errno));
return mixer;
}
if (ioctl(fd, SOUND_MIXER_READ_STEREODEVS, &stereodevs) < 0) {
fprintf(stderr, "Unable to get stereo devices: %s\n", strerror(errno));
return mixer;
}
for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
if (devmask & (1 << i)) {
mixer->devices[mixer->ndevices].id = i;
mixer->devices[mixer->ndevices].name = strdup(device_labels[i]);
strip(mixer->devices[mixer->ndevices].name);
if (ioctl(fd, MIXER_READ(i), &value) < 0) {
fprintf(stderr, "Unable to read value of %s: %s\n",
device_labels[i], strerror(errno));
} else {
mixer->devices[mixer->ndevices].left_value = value & 0xff;
mixer->devices[mixer->ndevices].right_value = (value >> 8) & 0xff;
}
if (stereodevs & (1 << i))
mixer->devices[mixer->ndevices].stereo = 1;
if (recmask & (1 << i)) {
mixer->devices[mixer->ndevices].record = 1;
if (recsrc & (1 << i))
mixer->devices[mixer->ndevices].record_enabled = 1;
}
mixer->ndevices++;
}
}
mixer->excl_input = (caps & SOUND_CAP_EXCL_INPUT) ? 1 : 0;
return mixer;
}
int
set_mixer_value(Mixer *mixer, int id, int left_value, int right_value)
{
int value, i;
value = (right_value << 8) | left_value;
if (ioctl(mixer->fd, MIXER_WRITE(id), &value) < 0) {
fprintf(stderr, "Unable to write value of %s: %s\n",
device_labels[id], strerror(errno));
return 1;
}
for (i = 0; i < mixer->ndevices; i++) {
if (mixer->devices[i].id == id) {
mixer->devices->left_value = left_value;
mixer->devices->right_value = right_value;
break;
}
}
return 0;
}
int
set_mixer_record(Mixer *mixer, int id, int state)
{
int recsrc, i;
if (mixer->excl_input) {
recsrc = state ? (1 << id) : 0;
} else {
if (ioctl(mixer->fd, SOUND_MIXER_READ_RECSRC, &recsrc) < 0) {
fprintf(stderr, "Unable to get record sources: %s\n", strerror(errno));
return 1;
}
if (state)
recsrc |= (1 << id);
else
recsrc &= ~(1 << id);
}
if (ioctl(mixer->fd, SOUND_MIXER_WRITE_RECSRC, &recsrc) < 0) {
fprintf(stderr, "Unable to set record sources: %s\n", strerror(errno));
return 1;
}
for (i = 0; i < mixer->ndevices; i++) {
if (mixer->devices[i].id == id) {
mixer->devices->record_enabled = state ? 1 : 0;
if (!mixer->excl_input)
break;
} else if (mixer->excl_input && state) {
mixer->devices->record_enabled = 0;
}
}
return 0;
}
void
close_mixer(Mixer *mixer)
{
int i;
close(mixer->fd);
for (i = 0; i < mixer->ndevices; i++)
free(mixer->devices[i].name);
free(mixer->devices);
free(mixer);
}
void
dump_mixer(Mixer *mixer)
{
int i;
for (i = 0; i < mixer->ndevices; i++) {
printf("%d - %s value %d:%d", mixer->devices[i].id, mixer->devices[i].name,
mixer->devices[i].left_value, mixer->devices[i].right_value);
if (mixer->devices[i].stereo)
printf(", stereo");
if (mixer->devices[i].record)
printf(", record%s", mixer->devices[i].record_enabled ? " (enabled)" : "");
printf("\n");
}
}
void
strip(char *str)
{
int i;
for (i = strlen(str); i && isspace(str[i - 1]); i--)
str[i - 1] = '\0';
}
--------------050407070308040107090206--