diff options
Diffstat (limited to 'tutorials/sndkit/tests/ioctl_test.c')
-rw-r--r-- | tutorials/sndkit/tests/ioctl_test.c | 624 |
1 files changed, 624 insertions, 0 deletions
diff --git a/tutorials/sndkit/tests/ioctl_test.c b/tutorials/sndkit/tests/ioctl_test.c new file mode 100644 index 0000000..ea0e4b9 --- /dev/null +++ b/tutorials/sndkit/tests/ioctl_test.c @@ -0,0 +1,624 @@ +/* + * Purpose: This program has been used to verify that some of the ioctl calls work + * + * Description: + * This program can be used to debug some of the most common OSS audio ioctl calls (use the -m option to select): + * + * Output tests: + * -m0 : SNDCTL_DSP_GETOSPTR - The output buffer pointer + * -m1 : SNDCTL_DSP_GETODELAY - The output delay + * -m2 : SNDCTL_DSP_GETOSPACE - Space available in the output buffer + * + * Input tests: + * -m20 : SNDCTL_DSP_GETISPTR - The input buffer pointer + * -m21 : SNDCTL_DSP_GETISPACE - Data available in the input buffer + * + * CAUTION! + * + * This program is not an automated test that tells if the device/driver is working correctly or not. + * It simply displays the value returned by the ioctl call and gives some visual indication of the + * result. You will see a '*' walking left or right on some kind of bar display. + * + * Tho use this program you should have very detailed knowledge about the internals of OSS. Otherwise + * the results will not make any sense to you. + * + * NOTE! + * + * Output of this program is supposed to be redirected to a disk file that is to be examined after the + * test is finished. Output to a terminal will delay the program too much which gives wrong results. + * + * How it works: + * + * There are different tests in this program. All they write audio data to the audio device (no sound will be produced). + * After each write call the program will display the information specific to that test. The times displayed are derived + * from the number of bytes written to the device so far. It is important to understand that they are not real time. As + * long as the device buffer is not completely filled the time difference between two subsequent writes will be zero + * (unless the -D option is used). + * + * Device setup: + * + * Use the following command line options to select the setup parameters for the device: + * + * -s NNN Selects the sampling rate (in Hz or kHz) + * -c NNN Selects the number of channels (1, 2, ..., N) + * -b NNN Selects the number of bits (8, 16 or 32) + * + * -f NNN Selects the fragment size (fs = 2**NNN) + * -n NNN Selects the number of fragments (2 to N) + * -d NNN Selects the output device to use (/dev/dsp by default) + * + * -r Disables automatic format conversions performed by OSS + * -B Open the physical device directly (bypassing virtual mixer (vmix)) + * + * -w NNN Selects the number of bytes written during each loop. By default this is + * equal to the fragment size. + * -D NNN Delay NNN milliseconds after each write/read (before displaying the test output). + * This emulates the processing done by an application. Using too long + * delay times will cause buffer underruns. Use delays that are shorter than the fragment time + * reported by the program. Note that granularity of the system timer is about 10 ms in typical + * systems (HZ=100). This means that the delay time will always be rounded up to the nearest + * system timer tick aftere the requested time. Using -D1 may result in up to 10-20 milliseconds of + * delay. + */ +/* + * + * This file is part of Open Sound System. + * + * Copyright (C) 4Front Technologies 1996-2008. + * + * This this source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + */ + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <sys/time.h> +#include <sys/types.h> +#include <fcntl.h> +#include <string.h> +#include <soundcard.h> +#ifdef _AIX +#include <sys/select.h> +#endif + +typedef void (*measure_f_t) (int fd); + +measure_f_t measure_f = NULL, run_test = NULL; + +char *name = "Unknown"; + +char *dspdev = "/dev/dsp"; +int mode = 0; +int fd = -1; +int speed = 48000; +int bits = 16; +int fmt = AFMT_S16_LE; +int channels = 2; +unsigned char silence = 0; +int fragsize = 0; /* Use default */ +int fragcount = 0x7fff; /* Unlimited */ +int write_size = 0; +int write_byte = 0; +int raw_mode = 0; +int loop_delay = 0; + +long long prev_time = 0; + +int data_rate, buffer_size; + +static long long +get_usecs(void) +{ + struct timeval tv; + + gettimeofday(&tv, NULL); + + return (long long)tv.tv_sec * 1000000LL + (long long)tv.tv_usec; +} + +static void +player (int fd) +{ + char *buffer; + + if ((buffer = malloc (write_size)) == NULL) + { + fprintf (stderr, "Out of memory\n"); + exit (EXIT_FAILURE); + } + + memset (buffer, silence, write_size); + + while (1) + { + long long t, d; + + t = get_usecs (); + + d = t - prev_time; + prev_time = t; + + printf("d=%2lld.%03lldms ", d / 1000LL, d % 1000LL); + + if (write (fd, buffer, write_size) != write_size) + { + perror ("write"); + exit (EXIT_FAILURE); + } + + if (loop_delay > 0) + usleep (loop_delay * 1000); + + measure_f (fd); + + write_byte += write_size; + } +} + +static void +recorder (int fd) +{ + char *buffer; + + if ((buffer = malloc (write_size)) == NULL) + { + fprintf (stderr, "Out of memory\n"); + exit (EXIT_FAILURE); + } + + while (1) + { + long long t, d; + + t = get_usecs (); + + d = t - prev_time; + prev_time = t; + + printf("d=%2lld.%03lldms ", d / 1000LL, d % 1000LL); + + if (read (fd, buffer, write_size) != write_size) + { + perror ("read"); + exit (EXIT_FAILURE); + } + + if (loop_delay > 0) + usleep (loop_delay * 1000); + + measure_f (fd); + + write_byte += write_size; + } +} + +static void +print_spacing (int i) +{ + if ((i % 10) == 0) + { + printf ("%d", (i / 10) % 10); + return; + } + + if ((i % 5) == 0) + { + printf(","); + return; + } + + printf ("."); +} + +static void +error_check (int fd) +{ + audio_errinfo err; + + if (ioctl (fd, SNDCTL_DSP_GETERROR, &err) == -1) + return; + + if (err.play_underruns > 0) + printf (" %d underruns\n", err.play_underruns); + + if (err.rec_overruns > 0) + printf (" %d overruns\n", err.rec_overruns); +} + +static void +measure_getoptr (int fd) +{ + count_info ci; + int i, n; + + if (ioctl (fd, SNDCTL_DSP_GETOPTR, &ci) == -1) + { + perror ("SNDCTL_DSP_GETOPTR"); + exit (EXIT_FAILURE); + } + + n = (100 * ci.ptr + buffer_size / 2) / buffer_size; + + printf ("b=%8d, t=%8d ms, p=%6d : ", write_byte, + (1000 * write_byte + data_rate / 2) / data_rate, ci.ptr); + + for (i = 0; i < n; i++) + print_spacing (i); + printf ("*"); + + if (n < 100) + { + for (i = n + 1; i < 100; i++) + print_spacing (i); + printf ("%%"); + } + + error_check (fd); + printf ("\n"); + fflush (stdout); +} + +static void +measure_getiptr (int fd) +{ + count_info ci; + int i, n; + + if (ioctl (fd, SNDCTL_DSP_GETIPTR, &ci) == -1) + { + perror ("SNDCTL_DSP_GETIPTR"); + exit (EXIT_FAILURE); + } + + n = (100 * ci.ptr + buffer_size / 2) / buffer_size; + + printf ("b=%8d, t=%8d ms, p=%6d : ", write_byte, + (1000 * write_byte + data_rate / 2) / data_rate, ci.ptr); + + for (i = 0; i < n; i++) + print_spacing (i); + printf ("*"); + + if (n < 100) + { + for (i = n + 1; i < 100; i++) + print_spacing (i); + printf ("%%"); + } + + error_check (fd); + printf ("\n"); + fflush (stdout); +} + +static void +measure_getospace (int fd) +{ + audio_buf_info bi; + int i, n; + + if (ioctl (fd, SNDCTL_DSP_GETOSPACE, &bi) == -1) + { + perror ("SNDCTL_DSP_GETOSPACE"); + exit (EXIT_FAILURE); + } + + n = (100 * bi.bytes + buffer_size / 2) / buffer_size; + + printf ("b=%8d, t=%8d ms, p=%6d : ", write_byte, + (1000 * write_byte + data_rate / 2) / data_rate, bi.bytes); + + for (i = 0; i < n; i++) + print_spacing (i); + printf ("*"); + + if (n < 100) + { + for (i = n + 1; i < 100; i++) + print_spacing (i); + printf ("%%"); + } + + error_check (fd); + printf ("\n"); + fflush (stdout); +} + +static void +measure_getispace (int fd) +{ + audio_buf_info bi; + int i, n; + + if (ioctl (fd, SNDCTL_DSP_GETISPACE, &bi) == -1) + { + perror ("SNDCTL_DSP_GETISPACE"); + exit (EXIT_FAILURE); + } + + n = (100 * bi.bytes + buffer_size / 2) / buffer_size; + + printf ("b=%8d, t=%8d ms, p=%6d : ", write_byte, + (1000 * write_byte + data_rate / 2) / data_rate, bi.bytes); + + for (i = 0; i < n; i++) + print_spacing (i); + printf ("*"); + + if (n < 100) + { + for (i = n + 1; i < 100; i++) + print_spacing (i); + printf ("%%"); + } + + error_check (fd); + printf ("\n"); + fflush (stdout); +} + +static void +measure_getodelay (int fd) +{ + int d, i, n; + + if (ioctl (fd, SNDCTL_DSP_GETODELAY, &d) == -1) + { + perror ("SNDCTL_DSP_GETODELAY"); + exit (EXIT_FAILURE); + } + + n = (100 * d + buffer_size / 2) / buffer_size; + + printf ("b=%8d, t=%8d ms, d=%6d : ", write_byte, + (1000 * write_byte + data_rate / 2) / data_rate, d); + + for (i = 0; i < n; i++) + print_spacing (i); + printf ("*"); + + if (n < 100) + { + for (i = n + 1; i < 100; i++) + print_spacing (i); + printf ("%%"); + } + + error_check (fd); + printf ("\n"); + fflush (stdout); +} + +int +main (int argc, char *argv[]) +{ + int i; + int tmp; + int is_input = 0; + audio_buf_info bi; + + int open_mode = 0; + + measure_f = measure_getospace; + + while ((i = getopt (argc, argv, "rBd:m:s:c:b:f:n:w:D:")) != EOF) + switch (i) + { + case 'd': + dspdev = optarg; + break; + + case 'm': + mode = atoi (optarg); + break; + + case 's': + speed = atoi (optarg); + if (speed < 200) + speed *= 1000; + break; + + case 'c': + channels = atoi (optarg); + break; + + case 'b': + bits = atoi (optarg); + break; + + case 'f': + fragsize = atoi (optarg); + break; + + case 'n': + fragcount = atoi (optarg); + break; + + case 'w': + write_size = atoi (optarg); + break; + + case 'D': + loop_delay = atoi (optarg); + break; + + case 'r': + raw_mode = 1; + break; + + case 'B': + open_mode |= O_EXCL; + break; + } + + switch (bits) + { + case 8: + bits = AFMT_U8; + silence = 0x80; + break; + case 16: + bits = AFMT_S16_NE; + break; + case 32: + bits = AFMT_S32_LE; + break; + default: + fprintf (stderr, "Bad numer of bits %d\n", bits); + exit (EXIT_FAILURE); + } + + switch (mode) + { + case 0: + measure_f = measure_getoptr; + name = "Getoptr"; + run_test = player; + open_mode |= O_WRONLY; + break; + + case 1: + measure_f = measure_getodelay; + name = "Getodelay"; + run_test = player; + open_mode |= O_WRONLY; + break; + + case 2: + measure_f = measure_getospace; + name = "Getospace"; + run_test = player; + open_mode |= O_WRONLY; + break; + + case 20: + measure_f = measure_getiptr; + name = "Getiptr"; + run_test = recorder; + open_mode |= O_RDONLY; + is_input = 1; + break; + + case 21: + measure_f = measure_getispace; + name = "Getispace"; + run_test = recorder; + open_mode |= O_RDONLY; + is_input = 1; + break; + + default: + fprintf (stderr, "Bad mode -m %d\n", mode); + exit (EXIT_FAILURE); + } + + if ((fd = open (dspdev, open_mode, 0)) == -1) + { + perror (dspdev); + exit (EXIT_FAILURE); + } + + if (raw_mode) + { + tmp = 0; + ioctl (fd, SNDCTL_DSP_COOKEDMODE, &tmp); /* Ignore errors */ + } + + if (fragsize != 0) + { + fragsize = (fragsize & 0xffff) | ((fragcount & 0x7fff) << 16); + ioctl (fd, SNDCTL_DSP_SETFRAGMENT, &fragsize); /* Ignore errors */ + } + + tmp = fmt; + if (ioctl (fd, SNDCTL_DSP_SETFMT, &tmp) == -1) + { + perror ("SNDCTL_DSP_SETFMT"); + exit (EXIT_FAILURE); + } + + if (tmp != fmt) + { + fprintf (stderr, + "Failed to select the requested sample format (%x, %x)\n", fmt, + tmp); + exit (EXIT_FAILURE); + } + + tmp = channels; + if (ioctl (fd, SNDCTL_DSP_CHANNELS, &tmp) == -1) + { + perror ("SNDCTL_DSP_CHANNELS"); + exit (EXIT_FAILURE); + } + + if (tmp != channels) + { + fprintf (stderr, "Failed to select the requested #channels (%d, %d)\n", + channels, tmp); + exit (EXIT_FAILURE); + } + + tmp = speed; + if (ioctl (fd, SNDCTL_DSP_SPEED, &tmp) == -1) + { + perror ("SNDCTL_DSP_SPEED"); + exit (EXIT_FAILURE); + } + + if (tmp != speed) + { + fprintf (stderr, "Failed to select the requested rate (%d, %d)\n", + speed, tmp); + exit (EXIT_FAILURE); + } + + if (is_input) + { + if (ioctl (fd, SNDCTL_DSP_GETISPACE, &bi) == -1) + { + perror ("SNDCTL_DSP_GETISPACE"); + exit (EXIT_FAILURE); + } + } + else + { + if (ioctl (fd, SNDCTL_DSP_GETOSPACE, &bi) == -1) + { + perror ("SNDCTL_DSP_GETOSPACE"); + exit (EXIT_FAILURE); + } + } + + buffer_size = bi.fragsize * bi.fragstotal; + + data_rate = speed * channels * (bits / 8); + + printf ("fragsize %d, nfrags %d, total buffer %d (bytes)\n", bi.fragsize, + bi.fragstotal, buffer_size); + + if (write_size == 0) + write_size = bi.fragsize; + + printf ("Sample rate rate %d Hz, channels %d, bits %d\n", speed, channels, + bits); + printf ("Data rate %d bytes / second\n", data_rate); + printf ("Fragment time %d ms\n", + (1000 * bi.fragsize + data_rate / 2) / data_rate); + printf ("Buffer time %d ms\n", + (1000 * buffer_size + data_rate / 2) / data_rate); + printf ("Write size %d bytes, write time %d ms\n", write_size, + (1000 * write_size + data_rate / 2) / data_rate); + + printf ("\n"); + printf ("*** Starting test %d (%s)\n", mode, name); + printf ("\n"); + + prev_time = get_usecs(); + + run_test (fd); + exit (0); +} |