[Python-checkins] python/nondist/sandbox/audiotest Makefile,NONE,1.1 audiotest.c,NONE,1.1
gward@users.sourceforge.net
gward@users.sourceforge.net
Sun, 01 Jun 2003 14:49:13 -0700
Update of /cvsroot/python/python/nondist/sandbox/audiotest
In directory sc8-pr-cvs1:/tmp/cvs-serv6377
Added Files:
Makefile audiotest.c
Log Message:
Initial checkin of pure C OSS test program.
--- NEW FILE: Makefile ---
CC = gcc
CFLAGS = -g -Wall
audiotest: audiotest.o
$(CC) -o $@ -lm $<
clean:
rm audiotest *.o
--- NEW FILE: audiotest.c ---
/*
* Pure C OSS test program. Useful as a quick sanity check to make
* sure that the audio hardware and device driver are behaving
* correctly when the ossaudiodev module is not.
*
* GPW 2003/03/11
*
* $Id: audiotest.c,v 1.1 2003/06/01 21:49:10 gward Exp $
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>
#include <math.h>
#include <errno.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/soundcard.h>
typedef struct {
char * name; /* eg. "/dev/dsp" */
int fd; /* fd returned by open() */
} audiodev_t;
/* Describes audio sampling parameters: needed to initialize audio
* hardware, and to generate a buffer.
*/
typedef struct {
int rate; /* sampling rate in Hz */
int size; /* sample size in bits (8 or 16) */
int sign; /* are sample values signed? */
} audiosample_t;
/* Describes physical characteristics of an audio signal such as a
* sine wave.
*/
typedef struct {
int freq; /* frequency of signal in Hz */
double ampl; /* amplitude of signal (0.0 .. 1.0) */
double dur; /* duration of signal in sec */
} audiosignal_t;
/* A buffer containing an audio signal ready to play (assuming the hardware
* has been initialized with the right sampling parameters).
*/
typedef struct {
audiosample_t * sample;
audiosignal_t * signal;
int cycle_len; /* number of samples in a cycle */
int buff_len; /* number of samples in buff */
int buff_size; /* size of buff in bytes */
char * buff;
} audiobuffer_t;
static void
writenow (char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vfprintf(stdout, fmt, args);
va_end(args);
fflush(stdout);
}
static void
error (char *fmt, ...)
{
va_list args;
va_start(args, fmt);
fprintf(stderr, "audiotest: error: ");
vfprintf(stderr, fmt, args);
fputc('\n', stderr);
va_end(args);
exit(1);
}
static void
_sampling_ioctl (audiodev_t * device, char * param, int cmd, int value)
{
int requested_value = value;
if (ioctl(device->fd, cmd, &value) == -1)
error("%s: unable to set %s: %s",
device->name, param, strerror(errno));
if (value != requested_value)
error("%s: unable to set %s to %d (got %d)",
device->name, param, requested_value, value);
}
static void
setparameters (audiodev_t * device, audiosample_t * sample)
{
int fmt;
if (sample->size == 8) {
if (sample->sign)
fmt = AFMT_S8;
else
fmt = AFMT_U8;
}
else if (sample->size == 16) {
if (sample->sign)
fmt = AFMT_S16_LE; /* XXX not handling endianness */
else
fmt = AFMT_U16_LE;
}
_sampling_ioctl(device, "sample format", SNDCTL_DSP_SETFMT, fmt);
_sampling_ioctl(device, "number of channels", SNDCTL_DSP_CHANNELS, 1);
_sampling_ioctl(device, "sampling rate", SNDCTL_DSP_SPEED, sample->rate);
}
static void
sync_audio (audiodev_t * device)
{
if (ioctl(device->fd, SNDCTL_DSP_SYNC, 0) == -1)
error("%s: unable to sync: %s", device->name, strerror(errno));
}
static audiosample_t *
mksample (int rate, int size, int sign)
{
audiosample_t * sample;
/* sanity-check args */
if (rate < 1 || rate > 1000000)
return NULL;
if (size % 8 != 0)
return NULL;
sample = (audiosample_t *) malloc(sizeof(audiosample_t));
sample->rate = rate;
sample->size = size;
sample->sign = sign;
return sample;
}
static void
printsample(FILE * f, audiosample_t * sample)
{
fprintf(f, "<audiosample at %p: rate=%d, size=%d, sign=%c>",
sample, sample->rate, sample->size, (sample->sign ? 'Y' : 'N'));
}
static void
freesample (audiosample_t * sample)
{
free(sample);
}
static audiosignal_t *
mksignal (int freq, double ampl, double dur)
{
audiosignal_t * signal;
if (freq < 1 || freq > 100000)
return NULL;
if (ampl < 0.0 || ampl > 1.0)
return NULL;
if (dur < 0 || dur > 3600)
return NULL;
signal = (audiosignal_t *) malloc(sizeof(audiosignal_t));
signal->freq = freq;
signal->ampl = ampl;
signal->dur = dur;
return signal;
}
static void
printsignal(FILE * f, audiosignal_t * signal)
{
fprintf(f, "<audiosignal at %p: freq=%d, ampl=%g, dur=%g>",
signal, signal->freq, signal->ampl, signal->dur);
}
static void
freesignal (audiosignal_t * signal)
{
free(signal);
}
static audiobuffer_t *
mkbuffer (audiosample_t * sample, audiosignal_t * signal)
{
audiobuffer_t * buffer = (audiobuffer_t *) malloc(sizeof(audiobuffer_t));
buffer->sample = sample;
buffer->signal = signal;
buffer->cycle_len = sample->rate / signal->freq;
buffer->buff_len = sample->rate * signal->dur;
buffer->buff_size = buffer->buff_len * sample->size / 8;
buffer->buff = (char *) malloc(buffer->buff_size);
memset(buffer->buff, 0, buffer->buff_size);
return buffer;
}
static void
printbuffer (FILE * f, audiobuffer_t * buffer)
{
fprintf(f, "<audiobuffer at %p: cycle_len=%d, buff_len=%d, buff_size=%d>",
buffer, buffer->cycle_len, buffer->buff_len, buffer->buff_size);
}
static void
dumpbuffer (FILE * f, audiobuffer_t * buffer)
{
printbuffer(f, buffer);
fputc('\n', f);
fprintf(f, " sample: ");
printsample(f, buffer->sample);
fputc('\n', f);
fprintf(f, " signal: ");
printsignal(f, buffer->signal);
fputc('\n', f);
}
static void
freebuffer (audiobuffer_t * buffer)
{
free(buffer->buff);
free(buffer);
}
static void
gen_sine (audiobuffer_t * buffer)
{
double step;
int offset;
int i;
/* Fill the first cycle of the buffer. */
step = 2*M_PI / buffer->cycle_len;
offset = 0; /* offset into buff */
for (i = 0; i < buffer->cycle_len; i++) {
double val_f;
//int val_i;
assert(offset < buffer->buff_len);
val_f = buffer->signal->ampl * sin(i * step);
if (buffer->sample->size == 8) {
/* first, convert for signed 8-bit:
-1.0 .. 1.0 -> -128 .. 127 (0x80 .. 0x7f) */
int val = (int) floor(val_f * 128);
if (val == 128) /* clamp 1.0 down to 127 */
val--;
/* store, or adjust-and-store */
if (buffer->sample->sign)
buffer->buff[offset] = (signed char) val;
else {
val += 128; /* -128 .. 127 -> 0 .. 255 */
buffer->buff[offset] = (unsigned char) val;
}
offset += 1;
}
else if (buffer->sample->size == 16) {
int val = (int) floor(val_f * 32768);
if (val == 32768)
val--;
if (buffer->sample->sign) {
((short *) buffer->buff)[offset] = (signed short) val;
}
else {
val += 32768;
((short *) buffer->buff)[offset] = (unsigned short) val;
}
offset += 2;
}
}
/* Now duplicate that data until 'buff' is full. */
while (offset < buffer->buff_size) {
for (i = 0; i < buffer->cycle_len; i++) {
buffer->buff[offset++] = buffer->buff[i];
if (offset == buffer->buff_size)
break;
}
}
}
static void
wait_user (int i, char * description)
{
char buf[2];
fprintf(stdout, "test %d: %s: press enter", i, description);
fflush(stdout);
fgets(buf, 2, stdin);
return;
}
static void
test_one_tone (int testnum, audiosample_t * sample, audiodev_t * device)
{
audiosignal_t * signal;
audiobuffer_t * buffer;
int nbytes;
signal = mksignal(1000, 0.75, 1.0);
buffer = mkbuffer(sample, signal);
gen_sine(buffer);
wait_user(testnum, "1 kHz sine wave, ampl 0.75, 1 sec");
nbytes = write(device->fd, buffer->buff, buffer->buff_size);
sync_audio(device);
writenow("%d bytes played\n", nbytes);
freesignal(signal);
freebuffer(buffer);
}
static void
test_two_tones (int testnum, audiosample_t * sample, audiodev_t * device)
{
audiosignal_t * signal1, * signal2;
audiobuffer_t * buffer1, * buffer2;
int nbytes;
signal1 = mksignal(523, 0.75, 1.0);
signal2 = mksignal(523*2, 0.75, 1.0);
buffer1 = mkbuffer(sample, signal1);
buffer2 = mkbuffer(sample, signal2);
gen_sine(buffer1);
gen_sine(buffer2);
wait_user(testnum, "middle C (523 Hz) for 1 sec, then jump an octave");
nbytes = write(device->fd, buffer1->buff, buffer1->buff_size);
nbytes += write(device->fd, buffer2->buff, buffer2->buff_size);
sync_audio(device);
writenow("%d bytes played\n", nbytes);
freesignal(signal1);
freesignal(signal2);
freebuffer(buffer1);
freebuffer(buffer2);
}
static void
play_scale (audiodev_t * device,
audiobuffer_t *buffers[],
int nbuffers)
{
int i;
int nbytes = 0;
writenow("frequencies: ");
for (i = 0; i < nbuffers; i++) {
writenow("%d ", buffers[i]->signal->freq);
nbytes += write(device->fd, buffers[i]->buff, buffers[i]->buff_size);
sync_audio(device);
}
//sync_audio(device);
writenow("%d bytes played\n", nbytes);
}
static void
free_buffers (audiobuffer_t *buffers[],
int nbuffers)
{
int i;
for (i = 0; i < nbuffers; i++) {
freesignal(buffers[i]->signal);
freebuffer(buffers[i]);
}
}
static void test_chromatic (int testnum,
audiosample_t * sample,
audiodev_t * device)
{
audiosignal_t * signal;
audiobuffer_t * buffers[13];
int i;
double factor = pow(2, (float) 1/12);
double freq = 523.25; /* middle C */
/* First create all the buffers */
for (i = 0; i < 13; i++) {
signal = mksignal((int) rint(freq), 0.75, 0.5);
/*printf("generating signal %d: ", i);
printsignal(stdout, signal);
fputc('\n', stdout);*/
buffers[i] = mkbuffer(sample, signal);
gen_sine(buffers[i]);
freq *= factor;
}
/* Now play them all, ie. play the chromatic scale. */
wait_user(testnum,
"chromatic (12-tone) scale from middle C, 0.5 sec per note");
play_scale(device, buffers, 13);
/* And free everything up. */
free_buffers(buffers, 13);
}
static void test_major_scale (int testnum,
audiosample_t * sample,
audiodev_t * device)
{
audiosignal_t * signal;
audiobuffer_t * buffers[8];
int i;
double factor = pow(2, (float) 1/12);
double steps[] = { factor*factor,
factor*factor,
factor,
factor*factor,
factor*factor,
factor*factor,
factor };
double freq = 523.25; /* middle C */
/* Generating the samples is a bit trickier for a major scale then
for a chromatic scale, because it's not a simple geometric
progression. The rule is: W W H W W W H where W is a whole step
(freq*factor*factor) and H is a half-step (freq*factor). */
for (i = 0; i < 8; i++) {
signal = mksignal((int) rint(freq), 0.75, 0.5);
buffers[i] = mkbuffer(sample, signal);
gen_sine(buffers[i]);
if (i < 7)
freq *= steps[i];
}
/* Play the scale and free it all up. */
wait_user(testnum, "C-major scale, 0.5 sec per note");
play_scale(device, buffers, 8);
free_buffers(buffers, 8);
}
static void
run_tests (audiodev_t * device)
{
audiosample_t * sample;
int testnum = 1;
/* Lowest-common denominator sampling parameters for now:
unsigned mono 8-bit samples at 8000 samples/sec. */
sample = mksample(8000, 8, 0);
setparameters(device, sample);
/* First test: simple 1kHz tone */
test_one_tone(testnum++, sample, device);
/* Second test: middle C (523 Hz), then jump an octave. */
test_two_tones(testnum++, sample, device);
/* For playing scales, 8000 samples/sec isn't good enough.
Hopefully most hardware can support 32000! */
sample->rate = 32000;
setparameters(device, sample);
/* Third test: chromatic (12-tone) scale, starting at middle C */
test_chromatic(testnum++, sample, device);
/* Fourth test: C-major scale */
test_major_scale(testnum++, sample, device);
}
int
main (int argc, char **argv)
{
audiodev_t device;
char usage[] = "usage: audiotest [device]\n";
if (argc == 1) { /* no args */
device.name = "/dev/dsp";
}
else if (argc == 2) { /* one arg */
device.name = argv[1];
}
else {
fprintf(stderr, usage);
exit(1);
}
printf("opening %s ...", device.name);
fflush(stdout);
device.fd = open(device.name, O_WRONLY, 0);
if (device.fd == -1) {
printf(" failed\n");
perror(device.name);
exit(2);
}
printf(" done\n");
run_tests(&device);
return 0;
}