/* * 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 #include #include #include #include #include #include #include #ifdef _AIX #include #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); }