add support for testing audio devices. the usage of audiocfg changes:

audiocfg list
  audiocfg default [index]
  audiocfg test [index]

'list' shows the available audio devices, 'default' sets the default
audio device, and 'test' will play a tone for 2 seconds on each
channel. example:

  $ ./audiocfg list
  0: [*] audio0: ICH2 AC97 (2 playback channels)
  1: [ ] audio1: Pseudo Audio (2 playback channels)
  $ ./audiocfg test 0
  testing channel 0...
  testing channel 1...
  $
This commit is contained in:
jmcneill 2010-09-01 09:04:16 +00:00
parent bd524aa578
commit e405ac8d24
6 changed files with 237 additions and 19 deletions

View File

@ -1,9 +1,11 @@
# $NetBSD: Makefile,v 1.3 2010/09/01 09:04:16 jmcneill Exp $
PROG= audiocfg
SRCS= audiodev.c drvctl.c
SRCS= audiodev.c drvctl.c dtmf.c
SRCS+= main.c
WARNS= 3
LDADD+= -lprop
DPADD+= $(LIBPROP)
LDADD+= -lprop -lm
DPADD+= $(LIBPROP) $(LIBM)
.include <bsd.prog.mk>

View File

@ -1,4 +1,4 @@
/* $NetBSD: audiodev.c,v 1.1.1.1 2010/08/30 02:19:47 mrg Exp $ */
/* $NetBSD: audiodev.c,v 1.2 2010/09/01 09:04:16 jmcneill Exp $ */
/*
* Copyright (c) 2010 Jared D. McNeill <jmcneill@invisible.ca>
@ -40,10 +40,42 @@
#include "audiodev.h"
#include "drvctl.h"
#include "dtmf.h"
static TAILQ_HEAD(audiodevhead, audiodev) audiodevlist =
TAILQ_HEAD_INITIALIZER(audiodevlist);
#define AUDIODEV_SAMPLE_RATE 44100
static unsigned int
audiodev_probe_pchans(struct audiodev *adev)
{
audio_info_t info;
unsigned int nchans = 0, n;
int error;
AUDIO_INITINFO(&info);
info.play.sample_rate = AUDIODEV_SAMPLE_RATE;
info.play.precision = 16;
info.play.encoding = AUDIO_ENCODING_SLINEAR_LE;
info.play.channels = 1;
info.mode = AUMODE_PLAY;
error = ioctl(adev->fd, AUDIO_SETINFO, &info);
if (error == -1)
return 0;
nchans = 1;
for (n = 2; n <= 16; n += 2) {
info.play.channels = n;
error = ioctl(adev->fd, AUDIO_SETINFO, &info);
if (error == -1)
break;
nchans = info.play.channels;
}
return nchans;
}
static int
audiodev_getinfo(struct audiodev *adev)
{
@ -64,6 +96,8 @@ audiodev_getinfo(struct audiodev *adev)
return -1;
}
adev->pchan = audiodev_probe_pchans(adev);
return 0;
}
@ -107,7 +141,7 @@ audiodev_refresh(void)
struct audiodev *adev;
int fd, error;
fd = open(DRVCTLDEV, O_RDWR);
fd = open(DRVCTLDEV, O_RDONLY);
if (fd == -1) {
perror("open " DRVCTLDEV);
return -1;
@ -202,3 +236,49 @@ audiodev_set_default(struct audiodev *adev)
return 0;
}
int
audiodev_test(struct audiodev *adev, unsigned int chanmask)
{
audio_info_t info;
int16_t *buf;
size_t buflen;
off_t off;
AUDIO_INITINFO(&info);
info.play.sample_rate = AUDIODEV_SAMPLE_RATE;
info.play.channels = adev->pchan;
info.play.precision = 16;
info.play.encoding = AUDIO_ENCODING_SLINEAR_LE;
info.mode = AUMODE_PLAY;
if (ioctl(adev->fd, AUDIO_SETINFO, &info) == -1) {
perror("ioctl AUDIO_SETINFO");
return -1;
}
if (ioctl(adev->fd, AUDIO_GETINFO, &info) == -1) {
perror("ioctl AUDIO_GETINFO");
return -1;
}
dtmf_new(&buf, &buflen, info.play.sample_rate, 2,
adev->pchan, chanmask, 350.0, 440.0);
if (buf == NULL)
return -1;
off = 0;
while (buflen > 0) {
size_t wlen = info.play.buffer_size;
if (wlen > buflen)
wlen = buflen;
write(adev->fd, (char *)buf + off, wlen);
off += wlen;
buflen -= wlen;
}
if (ioctl(adev->fd, AUDIO_DRAIN) == -1)
perror("ioctl AUDIO_DRAIN");
free(buf);
return 0;
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: audiodev.h,v 1.1.1.1 2010/08/30 02:19:47 mrg Exp $ */
/* $NetBSD: audiodev.h,v 1.2 2010/09/01 09:04:16 jmcneill Exp $ */
/*
* Copyright (c) 2010 Jared D. McNeill <jmcneill@invisible.ca>
@ -44,6 +44,8 @@ struct audiodev {
dev_t dev;
bool defaultdev;
int pchan;
audio_device_t audio_device;
TAILQ_ENTRY(audiodev) next;
@ -53,5 +55,6 @@ int audiodev_refresh(void);
unsigned int audiodev_count(void);
struct audiodev * audiodev_get(unsigned int);
int audiodev_set_default(struct audiodev *);
int audiodev_test(struct audiodev *, unsigned int);
#endif /* !_HAVE_AUDIODEV_H */

75
usr.bin/audiocfg/dtmf.c Normal file
View File

@ -0,0 +1,75 @@
/* $NetBSD: dtmf.c,v 1.1 2010/09/01 09:04:16 jmcneill Exp $ */
/*
* Copyright (c) 2010 Jared D. McNeill <jmcneill@invisible.ca>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <fcntl.h>
#include <math.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include "dtmf.h"
#define PI2 (3.14159265358979323846f * 2)
static void
dtmf_create(int16_t *buf, unsigned int sample_rate,
unsigned short sample_length, unsigned short channels,
unsigned int chanmask, float freq1, float freq2)
{
int c, i;
size_t sample_count = sample_rate * sample_length;
for (i = 0; i < sample_count; i++) {
for (c = 0; c < channels; c++) {
if ((chanmask & (1 << c)) == 0)
continue;
buf[c] =
(sin(i * PI2 * (freq1 / sample_rate)) +
sin(i * PI2 * (freq2 / sample_rate))) * 16383;
}
buf += channels;
}
}
void
dtmf_new(int16_t **buf, size_t *buflen, unsigned int sample_rate,
unsigned short sample_length, unsigned short channels,
unsigned int chanmask, float rate1, float rate2)
{
*buflen = sample_rate * sizeof(int16_t) * sample_length * channels;
*buf = calloc(1, *buflen);
if (*buf == NULL) {
perror("calloc");
return;
}
dtmf_create(*buf, sample_rate, sample_length, channels, chanmask,
rate1, rate2);
}

37
usr.bin/audiocfg/dtmf.h Normal file
View File

@ -0,0 +1,37 @@
/* $NetBSD: dtmf.h,v 1.1 2010/09/01 09:04:16 jmcneill Exp $ */
/*
* Copyright (c) 2010 Jared D. McNeill <jmcneill@invisible.ca>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _HAVE_DTMF_H
#define _HAVE_DTMF_H
#include <stdint.h>
void dtmf_new(int16_t **, size_t *, unsigned int, unsigned short,
unsigned short, unsigned int, float, float);
#endif /* !_HAVE_DTMF_H */

View File

@ -1,4 +1,4 @@
/* $NetBSD: main.c,v 1.1.1.1 2010/08/30 02:19:47 mrg Exp $ */
/* $NetBSD: main.c,v 1.2 2010/09/01 09:04:16 jmcneill Exp $ */
/*
* Copyright (c) 2010 Jared D. McNeill <jmcneill@invisible.ca>
@ -39,7 +39,9 @@
static void
usage(const char *p)
{
fprintf(stderr, "usage: %s [index]\n", p);
fprintf(stderr, "usage: %s list\n", p);
fprintf(stderr, " %s default [index]\n", p);
fprintf(stderr, " %s test [index]\n", p);
exit(EXIT_FAILURE);
}
@ -52,22 +54,25 @@ main(int argc, char *argv[])
if (audiodev_refresh() == -1)
return EXIT_FAILURE;
switch (argc) {
case 1:
if (argc < 2)
usage(argv[0]);
/* NOTREACHED */
if (strcmp(argv[1], "list") == 0) {
n = audiodev_count();
for (i = 0; i < n; i++) {
adev = audiodev_get(i);
printf("%u: [%c] %s: %s\n",
printf("%u: [%c] %s: %s (%u playback channel%s)\n",
i, adev->defaultdev ? '*' : ' ',
adev->xname, adev->audio_device.name);
adev->xname, adev->audio_device.name,
adev->pchan, adev->pchan == 1 ? "" : "s");
}
break;
case 2:
if (*argv[1] < '0' || *argv[1] > '9')
} else if (strcmp(argv[1], "default") == 0 && argc == 3) {
if (*argv[2] < '0' || *argv[2] > '9')
usage(argv[0]);
/* NOTREACHED */
errno = 0;
i = strtoul(argv[1], NULL, 10);
i = strtoul(argv[2], NULL, 10);
if (errno)
usage(argv[0]);
/* NOTREACHED */
@ -81,11 +86,27 @@ main(int argc, char *argv[])
perror("couldn't set default device");
return EXIT_FAILURE;
}
break;
default:
} else if (strcmp(argv[1], "test") == 0 && argc == 3) {
if (*argv[2] < '0' || *argv[2] > '9')
usage(argv[0]);
/* NOTREACHED */
errno = 0;
i = strtoul(argv[2], NULL, 10);
if (errno)
usage(argv[0]);
/* NOTREACHED */
adev = audiodev_get(i);
if (adev == NULL) {
fprintf(stderr, "no such device\n");
return EXIT_FAILURE;
}
for (i = 0; i < adev->pchan; i++) {
printf("testing channel %d...\n", i);
audiodev_test(adev, 1 << i);
}
} else
usage(argv[0]);
/* NOTREACHED */
return EXIT_SUCCESS;
}