diff options
Diffstat (limited to 'tutorials/sndkit/samples')
-rw-r--r-- | tutorials/sndkit/samples/Makefile | 26 | ||||
-rw-r--r-- | tutorials/sndkit/samples/Readme | 7 | ||||
-rw-r--r-- | tutorials/sndkit/samples/audiolevel.c | 202 | ||||
-rw-r--r-- | tutorials/sndkit/samples/dsp_geterror_demo.c | 154 | ||||
-rw-r--r-- | tutorials/sndkit/samples/fulldup.c | 816 | ||||
-rw-r--r-- | tutorials/sndkit/samples/midi.c | 67 | ||||
-rw-r--r-- | tutorials/sndkit/samples/midiin.c | 55 | ||||
-rw-r--r-- | tutorials/sndkit/samples/mixer_applet.c | 404 | ||||
-rw-r--r-- | tutorials/sndkit/samples/mixer_test.txt | 108 | ||||
-rw-r--r-- | tutorials/sndkit/samples/mixext.c | 270 | ||||
-rw-r--r-- | tutorials/sndkit/samples/mixext.readme | 232 | ||||
-rw-r--r-- | tutorials/sndkit/samples/mmap_duplex.c | 333 | ||||
-rw-r--r-- | tutorials/sndkit/samples/mmap_test.c | 290 | ||||
-rw-r--r-- | tutorials/sndkit/samples/playtgt.c | 93 | ||||
-rw-r--r-- | tutorials/sndkit/samples/recsrc.c | 97 | ||||
-rw-r--r-- | tutorials/sndkit/samples/singen.c | 195 |
16 files changed, 3349 insertions, 0 deletions
diff --git a/tutorials/sndkit/samples/Makefile b/tutorials/sndkit/samples/Makefile new file mode 100644 index 0000000..a155391 --- /dev/null +++ b/tutorials/sndkit/samples/Makefile @@ -0,0 +1,26 @@ +# +# Note! In most systems soundcard.h located under <sys/> or <linux/> is for +# obsolete OSS 3.x version and these utilities will fail to compile with +# it. For safety reasons utilities located in this directory have been +# changed to include just <soundcard.h> instead of <sys/soundcard.h>. +# +# The correct version of soundcard.h is located in +# /usr/lib/oss/include/sys. However some operating systems have OSS4 +# included in the base system and /usr/include/sys/soundcard.h may be +# correct. +# +# You can use the OSSLIBDIR variable defined in /etc/oss.conf. If this +# file exists then include it in the Makefile. You can use +# -I(OSSLIBDIR)/include/sys + +CFLAGS=-I../../../include + +# Note2! Not all programs are included in TARGET. The missing programs are +# used to demonstrate OSS features that are not recommended. + +TARGETS=audiolevel dsp_geterror_demo fulldup midi midiin mixer_applet mixext mmap_duplex mmap_test playtgt recsrc singen + +all: $(TARGETS) + +clean: + rm -f $(TARGETS) *.o core core.* *.core x y z *~ diff --git a/tutorials/sndkit/samples/Readme b/tutorials/sndkit/samples/Readme new file mode 100644 index 0000000..0559695 --- /dev/null +++ b/tutorials/sndkit/samples/Readme @@ -0,0 +1,7 @@ +Sample and skeleton programs for programming with OSS +===================================================== + +Warning! These source files have not been checked for full OSS 4.0 + compatibility (work in progress). + +For more information about these programs contact hannu@opensound.com. diff --git a/tutorials/sndkit/samples/audiolevel.c b/tutorials/sndkit/samples/audiolevel.c new file mode 100644 index 0000000..38734e3 --- /dev/null +++ b/tutorials/sndkit/samples/audiolevel.c @@ -0,0 +1,202 @@ +/* + * Purpose: A simple program that does audio recording. + * Copyright (C) 4Front Technologies, 2002-2004. Released under GPLv2/CDDL. + * + * Description: + * This is a very minimal program that does audio recording. However to + * demonstrate processiong of audio data it computes the + * maximum value of the signal and displays a "LED" bars + * (using character mode console). + */ +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <fcntl.h> +#include <soundcard.h> + +int fd_in; +int sample_rate = 48000; + +/* + * The open_audio_device opens the audio device and initializes it + * for the required mode. The same routine is used in the other + * simple sample programs too. + */ + +static int +open_audio_device (char *name, int mode) +{ + int tmp, fd; + + if ((fd = open (name, mode, 0)) == -1) + { + perror (name); + exit (-1); + } + +/* + * Setup the device. Note that it's important to set the + * sample format, number of channels and sample rate exactly in this order. + * Some devices depend on the order. + */ + +/* + * Set the sample format + */ + tmp = AFMT_S16_NE; /* Native 16 bits */ + if (ioctl (fd, SNDCTL_DSP_SETFMT, &tmp) == -1) + { + perror ("SNDCTL_DSP_SETFMT"); + exit (-1); + } + + if (tmp != AFMT_S16_NE) + { + fprintf (stderr, + "The device doesn't support the 16 bit sample format.\n"); + exit (-1); + } + +/* + * Set the number of channels (mono) + */ + tmp = 1; + if (ioctl (fd, SNDCTL_DSP_CHANNELS, &tmp) == -1) + { + perror ("SNDCTL_DSP_CHANNELS"); + exit (-1); + } + + if (tmp != 1) + { + fprintf (stderr, "The device doesn't support mono mode.\n"); + exit (-1); + } + +/* + * Set the sample rate + */ + sample_rate = 48000; + if (ioctl (fd, SNDCTL_DSP_SPEED, &sample_rate) == -1) + { + perror ("SNDCTL_DSP_SPEED"); + exit (-1); + } + +/* + * No need for error checking because we will automatically adjust the + * signal based on the actual sample rate. However most application must + * check the value of sample_rate and compare it to the requested rate. + * + * Small differences between the rates (10% or less) are normal and the + * applications should usually tolerate them. However larger differences may + * cause annoying pitch problems (Mickey Mouse). + */ + + return fd; +} + +void +process_input (void) +{ + short buffer[1024]; + + int l, i, level; + +/* + * First read a block of audio samples with proper error checking. + */ + + if ((l = read (fd_in, buffer, sizeof (buffer))) == -1) + { + perror ("Audio read"); + exit (-1); /* Or return an error code */ + } + +/* + * We are using 16 bit samples so the number of bytes returned by read must be + * converted to the number of samples (2 bytes per a 16 bit sample). + * + * Some care must be taken if this program is converted from 1 channels + * (mono) to 2 channels (stereo) or more. + * + * Handling more than 1 channels is bit more complicated because the channels + * are interleaved. This will be demonstrated in some other programs. + */ + + l = l / 2; + +/* + * After this point this routine will perform the peak volume computations. + * The {!code l} variable contains the number of samples in the buffer. + * + * The remaining lines can be removed and replaced with the required + * application code. + */ + + level = 0; + + for (i = 0; i < l; i++) + { +/* + * Take the next sample (i) and compute it's absolute value. Check if it + * was larger than the previous peak value. + */ + int v = buffer[i]; + + if (v < 0) + v = -v; /* abs */ + + if (v > level) + level = v; + } + +/* + * Finally print the simple LED bar. The maximum value for a 16 bit + * sample is 32*1024-1. Convert this to 32 bars. + * + * This program uses linear scale for simplicity. Real world audio programs + * should probably use logarithmic scale (dB). + */ + + level = (level + 1) / 1024; + + for (i = 0; i < level; i++) + printf ("*"); + for (i = level; i < 32; i++) + printf ("."); + printf ("\r"); + fflush (stdout); +} + +int +main (int argc, char *argv[]) +{ +/* + * Use /dev/dsp as the default device because the system administrator + * may select the device using the {!xlink ossctl} program or some other + * methods + */ + char *name_in = "/dev/dsp"; + +/* + * It's recommended to provide some method for selecting some other + * device than the default. We use command line argument but in some cases + * an environment variable or some configuration file setting may be better. + */ + if (argc > 1) + name_in = argv[1]; + +/* + * It's mandatory to use O_RDONLY in programs that do only recording. Other + * modes may cause increased resource (memory) usage in the driver. It may + * also prevent other applications from using the same device for + * playback at the same time. + */ + fd_in = open_audio_device (name_in, O_RDONLY); + + while (1) + process_input (); + + exit (0); +} diff --git a/tutorials/sndkit/samples/dsp_geterror_demo.c b/tutorials/sndkit/samples/dsp_geterror_demo.c new file mode 100644 index 0000000..793de09 --- /dev/null +++ b/tutorials/sndkit/samples/dsp_geterror_demo.c @@ -0,0 +1,154 @@ +/* + * Purpose: A simple demonstration of + * Copyright (C) 4Front Technologies, 2007. All rights reserved. + * + * Description: + * This program is seriously broken. It's only purpose is to fail so that + * the !nlink SNDCTL_DSP_GETERROR} ioctl call can be tested. Otherwise this + * program is based on the {!nlink singen.c} program. + * + * However this program demonstrates how SNDCTL_DSP_GETERROR can be used in + * applications. + */ +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <fcntl.h> +#include <soundcard.h> + +int fd_out; + +static int +open_audio_device (char *name, int mode) +{ + int tmp, fd; + int sample_rate; + + if ((fd = open (name, mode, 0)) == -1) + { + perror (name); + exit (-1); + } + +/* + * Setup the device. Note that it's important to set the + * sample format, number of channels and sample rate exactly in this order. + * Some devices depend on the order. + */ + +/* + * Set the sample format + */ + tmp = AFMT_S16_NE; /* Native 16 bits */ + if (ioctl (fd, SNDCTL_DSP_SETFMT, &tmp) == -1) + { + perror ("SNDCTL_DSP_SETFMT"); + exit (-1); + } + + if (tmp != AFMT_S16_NE) + { + fprintf (stderr, + "The device doesn't support the 16 bit sample format.\n"); + exit (-1); + } + +/* + * Set the number of channels + */ + tmp = 1; + if (ioctl (fd, SNDCTL_DSP_CHANNELS, &tmp) == -1) + { + perror ("SNDCTL_DSP_CHANNELS"); + exit (-1); + } + + if (tmp != 1) + { + fprintf (stderr, "The device doesn't support mono mode.\n"); + exit (-1); + } + +/* + * Set the sample rate + */ + sample_rate = 48000; + if (ioctl (fd, SNDCTL_DSP_SPEED, &sample_rate) == -1) + { + perror ("SNDCTL_DSP_SPEED"); + exit (-1); + } + +/* + * No need for error checking because we will automatically adjust the + * signal based on the actual sample rate. However most application must + * check the value of sample_rate and compare it to the requested rate. + * + * Small differences between the rates (10% or less) are normal and the + * applications should usually tolerate them. However larger differences may + * cause annoying pitch problems (Mickey Mouse). + */ + + return fd; +} + +int +main (int argc, char *argv[]) +{ + char *name_out = "/dev/dsp"; + int tmp; + + audio_errinfo ei; + audio_buf_info bi; + + if (argc > 1) + name_out = argv[1]; + + fd_out = open_audio_device (name_out, O_WRONLY); + +/* + * Cause an intentional error by using wrong parameter to SNDCTL_DSP_STEREO + * which expects 0 or 1. Don't do error checking in this case because + * we would like to see two errors reported. + */ + tmp = 2; + ioctl (fd_out, SNDCTL_DSP_STEREO, &tmp); + +/* + * Cause an intentional failure by calling {!nlink SNDCTL_DSP_GETISPACE} + * which is not permitted on write-only devices. + */ + + if (ioctl (fd_out, SNDCTL_DSP_GETISPACE, &bi) == -1) + { + perror ("SNDCTL_DSP_GETISPACE"); /* Report the "primary" error first */ + + /* + * Next show the explanation to the user. + */ + fprintf (stderr, + "Audio error: Cannot obtain recorded byte count from the device.\n"); + fprintf (stderr, "\n"); + + /* + * Next call {!nlink SNDCTL_DSP_GETERROR} to see if there is + * any additional info available. + */ + + if (ioctl (fd_out, SNDCTL_DSP_GETERROR, &ei) != -1) + { + if (ei.play_errorcount > 0 && ei.play_lasterror != 0) + fprintf (stderr, "%d OSS play event(s), last=%05d:%d\n", + ei.play_errorcount, ei.play_lasterror, + ei.play_errorparm); + + if (ei.rec_errorcount > 0 && ei.rec_lasterror != 0) + fprintf (stderr, "%d OSS rec event(s), last=%05d:%d\n", + ei.rec_errorcount, ei.rec_lasterror, ei.rec_errorparm); + } + } + else + fprintf (stderr, "SNDCTL_DSP_GETISPACE didn't fail as expected.\n"); + + exit (0); +} diff --git a/tutorials/sndkit/samples/fulldup.c b/tutorials/sndkit/samples/fulldup.c new file mode 100644 index 0000000..91190c8 --- /dev/null +++ b/tutorials/sndkit/samples/fulldup.c @@ -0,0 +1,816 @@ +/* + * Purpose: Full duplex sample program using the single device approach. + * + * Description: + * This sample program explains how to use the one and twodevicefile based + * methods for full duplex. + * + * This program uses full duplex for echo-like processing (usefull for + * applications like guitar effect processors). However this task is + * actually very challenging. Applications that require almost zero + * latencies will need to be run on very high priority levels. The exact method + * required for this depends on the operating system and is beyond the scope + * of this simplistic sample program. + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/mman.h> +#include <sys/time.h> +#include <sys/select.h> +#include <soundcard.h> + +char *dspname = "/dev/dsp"; +char *dspname_in = NULL; + +int fd_out = -1, fd_in = -1; +char buffer[32 * 1024]; /* Max 32k local buffer */ +int rate = 48000; +int fragsize; + +static void +open_one_device (char *dspname) +{ +/* + * Open the device file. The one device full duplex scheme requires that + * the device file is opened with O_RDWR. See the description of the + * {!nlink open} system call for more info. + */ + oss_audioinfo ai; + int fd; + int tmp; + int devcaps; + int channels = 2; + int format; + int frag; + + if ((fd = open (dspname, O_RDWR, 0)) == -1) + { + perror (dspname); + exit (-1); + } + + ai.dev = -1; + if (ioctl (fd, SNDCTL_ENGINEINFO, &ai) != -1) + { + printf ("\nUsing audio engine %d=%s for duplex\n\n", ai.dev, ai.name); + } + +#ifdef USE_RAW_FORMATS +/* + * In some cases it's recommended that all sample rate and format + * conversions are disabled. These conversions may make timing very + * tricky. However the drawback of disabling format conversions is that + * the application must be able to handle the format and rate + * conversions itself. + * + * We don't do any error checking because SNDCTL_DSP_COOKEDMODE is an optional + * ioctl call that may not be supported by all OSS implementations (in such + * cases there are no format conversions anyway). + */ + + tmp = 0; + ioctl (fd, SNDCTL_DSP_COOKEDMODE, &tmp); +#endif + +/* + * Check that the device supports full duplex. Otherwise there is no point in + * continuing. + */ + + if (ioctl (fd, SNDCTL_DSP_GETCAPS, &devcaps) == -1) + { + perror ("SNDCTL_DSP_GETCAPS"); + exit (-1); + } + + if (!(devcaps & PCM_CAP_DUPLEX)) + { + fprintf (stderr, + "%s doesn't support one device based full duplex scheme\n", + dspname); + fprintf (stderr, "Please use the two device scheme.\n"); + exit (-1); + } + +#if 0 + /* + * There is no point in calling SNDCTL_DSP_SETDUPLEX any more. This call has not had any + * effect since SB16. + */ + if (ioctl (fd, SNDCTL_DSP_SETDUPLEX, NULL) == -1) + { + perror ("SNDCTL_DSP_SETDUPLEX"); + exit (-1); + } +#endif + +/* + * Try to set the fragment size to suitable level. + */ + + frag = 0x7fff000a; /* Unlimited number of 1k fragments */ + + if (ioctl (fd, SNDCTL_DSP_SETFRAGMENT, &frag) == -1) + { + perror ("SNDCTL_DSP_SETFRAGMENT"); + exit (-1); + } + +/* + * Set up the sampling rate and other sample parameters. + */ + + tmp = channels; + + if (ioctl (fd, SNDCTL_DSP_CHANNELS, &tmp) == -1) + { + perror ("SNDCTL_DSP_CHANNELS"); + exit (-1); + } + + if (tmp != channels) + { + fprintf (stderr, "%s doesn't support stereo (%d)\n", dspname, tmp); + exit (-1); + } + + /* + * Request 16 bit native endian sample format. + */ + tmp = AFMT_S16_NE; + + if (ioctl (fd, SNDCTL_DSP_SETFMT, &tmp) == -1) + { + perror ("SNDCTL_DSP_SETFMT"); + exit (-1); + } + +/* + * Note that most devices support only the usual litle endian (Intel) + * byte order. This may be a problem under big endian architectures such + * as Sparc. We accept also the opposite (alien) endianess but + * this may require different handling (byte swapping) in most applications. + * However we don't care about this issue because this applicaton doesn't + * do any processing on the data. + */ + + if (tmp != AFMT_S16_NE && tmp != AFMT_S16_OE) + { + fprintf (stderr, "%s doesn't support 16 bit sample format (%x)\n", + dspname, tmp); + exit (-1); + } + + format = tmp; + + if (format == AFMT_S16_OE) + { + fprintf (stderr, + "Warning: Using 16 bit sample format with wrong endianess.\n"); + } + + tmp = rate; + + if (ioctl (fd, SNDCTL_DSP_SPEED, &tmp) == -1) + { + perror ("SNDCTL_DSP_SPEED"); + exit (-1); + } + + if (tmp != rate) + { + fprintf (stderr, "%s doesn't support requested rate %d (%d)\n", dspname, + rate, tmp); + exit (-1); + } + +/* + * Get the actual fragment size. SNDCTL_DSP_GETBLKSIZE gives a good + * compromise. If cooked mode is not disabled then the fragment sizes + * used for input and output may be different. Using + * {!nlink SNDCTL_DSP_GETOSPACE} or {!nlink SNDCTL_DSP_GETISPACE} + * may return different values. In this case SNDCTL_DSP_GETBLKSIZE will + * return a value that is between them. + */ + + if (ioctl (fd, SNDCTL_DSP_GETBLKSIZE, &fragsize) == -1) + { + perror ("SNDCTL_DSP_GETBLKSIZE"); + exit (-1); + } + + if (fragsize > sizeof (buffer)) + { + fprintf (stderr, "Too large fragment size %d\n", fragsize); + exit (-1); + } + + printf ("Sample parameters set OK. Using fragment size %d\n", fragsize); + + fd_in = fd_out = fd; +} + +static void +open_two_devices (char *dspname_out, char *dspname_in) +{ +/* + * Open the device file. The one device full duplex scheme requires that + * the device file is opened with O_RDWR. See the description of the + * {!nlink open} system call for more info. + */ + oss_audioinfo ai_in, ai_out; + int tmp; + int devcaps; + int channels = 2; + int format; + int frag = 0x7fff000a; /* Unlimited number of 1k fragments */ + +/* + * Open the output device + */ + if ((fd_out = open (dspname_out, O_WRONLY, 0)) == -1) + { + perror (dspname_out); + exit (-1); + } + + ai_out.dev = -1; + if (ioctl (fd_out, SNDCTL_ENGINEINFO, &ai_out) != -1) + { + printf ("\nUsing audio engine %d=%s for output\n", ai_out.dev, + ai_out.name); + } + +/* + * Open the input device + */ + if ((fd_in = open (dspname_in, O_RDONLY, 0)) == -1) + { + perror (dspname_in); + exit (-1); + } + + ai_in.dev = -1; + if (ioctl (fd_in, SNDCTL_ENGINEINFO, &ai_in) != -1) + { + printf ("Using audio engine %d=%s for input\n\n", ai_in.dev, + ai_in.name); + } + + if (ai_in.rate_source != ai_out.rate_source) + { +/* + * Input and output devices should have their sampling rates derived from + * the same crystal clock. Otherwise there will be drift in the sampling rates + * which may cause dropouts/hiccup (unless the application can handle the rate + * error). So check that the rate sources are the same. + * + * However two devices may still be driven by the same clock even if the + * rate sources are different. OSS has no way to know if the user is using some + * common sample clock (word clock) generator to syncronize all devices + * together (which is _THE_ practice in production environments). So do not + * overreact. Just let the user to know about the potential problem. + */ + fprintf (stderr, + "Note! %s and %s are not necessarily driven by the same clock.\n", + dspname_out, dspname_in); + } + +#ifdef USE_RAW_FORMATS +/* + * In many cases it's recommended that all sample rate and format + * conversions are disabled. These conversions may make timing very + * tricky. However the drawback of disabling format conversions is that + * the application must be able to handle the format and rate + * conversions itself. + * + * We don't do any error checking because SNDCTL_DSP_COOKEDMODE is an optional + * ioctl call that may not be supported by all OSS implementations (in such + * cases there are no format conversions anyway). + */ + + tmp = 0; + ioctl (fd_out, SNDCTL_DSP_COOKEDMODE, &tmp); + tmp = 0; + ioctl (fd_in, SNDCTL_DSP_COOKEDMODE, &tmp); +#endif + +/* + * Check output device capabilities. + */ + if (ioctl (fd_out, SNDCTL_DSP_GETCAPS, &devcaps) == -1) + { + perror ("SNDCTL_DSP_GETCAPS"); + exit (-1); + } + + if (devcaps & PCM_CAP_DUPLEX) + { + fprintf (stderr, + "Device %s supports duplex so you may want to use the single device approach instead\n", + dspname_out); + } + + if (!(devcaps & PCM_CAP_OUTPUT)) + { + fprintf (stderr, "%s doesn't support output\n", dspname_out); + fprintf (stderr, "Please use different device.\n"); +#if 0 + /* + * NOTE! Earlier OSS versions don't necessarily support PCM_CAP_OUTPUT + * so don't panic. + */ + exit (-1); +#endif + } + +/* + * Check input device capabilities. + */ + if (ioctl (fd_in, SNDCTL_DSP_GETCAPS, &devcaps) == -1) + { + perror ("SNDCTL_DSP_GETCAPS"); + exit (-1); + } + + if (devcaps & PCM_CAP_DUPLEX) + { + fprintf (stderr, + "Device %s supports duplex so you may want to use the single device approach instead\n", + dspname_in); + } + + if (!(devcaps & PCM_CAP_INPUT)) + { + fprintf (stderr, "%s doesn't support input\n", dspname_in); + fprintf (stderr, "Please use different device.\n"); +#if 0 + /* + * NOTE! Earlier OSS versions don't necessarily support PCM_CAP_INPUT + * so don't panic. + */ + exit (-1); +#endif + } + +/* + * No need to turn on the full duplex mode when using separate input and output + * devices. In fact calling SNDCTL_DSP_SETDUPLEX in this mode would be a + * major mistake + */ + +/* + * Try to set the fragment size to suitable level. + */ + + if (ioctl (fd_out, SNDCTL_DSP_SETFRAGMENT, &frag) == -1) + { + perror ("SNDCTL_DSP_SETFRAGMENT"); + exit (-1); + } + if (ioctl (fd_in, SNDCTL_DSP_SETFRAGMENT, &frag) == -1) + { + perror ("SNDCTL_DSP_SETFRAGMENT"); + exit (-1); + } + +/* + * Set up the sampling rate and other sample parameters. + */ + + tmp = channels; + + if (ioctl (fd_out, SNDCTL_DSP_CHANNELS, &tmp) == -1) + { + perror ("SNDCTL_DSP_CHANNELS"); + exit (-1); + } + + if (tmp != channels) + { + fprintf (stderr, "%s doesn't support stereo (%d)\n", dspname_out, tmp); + exit (-1); + } + if (ioctl (fd_in, SNDCTL_DSP_CHANNELS, &tmp) == -1) + { + perror ("SNDCTL_DSP_CHANNELS"); + exit (-1); + } + + if (tmp != channels) + { + fprintf (stderr, "%s doesn't support stereo (%d)\n", dspname_in, tmp); + exit (-1); + } + + /* + * Request 16 bit native endian sample format. + */ + tmp = AFMT_S16_NE; + + if (ioctl (fd_out, SNDCTL_DSP_SETFMT, &tmp) == -1) + { + perror ("SNDCTL_DSP_SETFMT"); + exit (-1); + } + +/* + * Note that most devices support only the usual litle endian (Intel) + * byte order. This may be a problem under big endian architectures such + * as Sparc. We accept also the opposite (alien) endianess but + * this may require different handling (byte swapping) in most applications. + * However we don't care about this issue because this applicaton doesn't + * do any processing on the data. + */ + + if (tmp != AFMT_S16_NE && tmp != AFMT_S16_OE) + { + fprintf (stderr, "%s doesn't support 16 bit sample format (%x)\n", + dspname_out, tmp); + exit (-1); + } + + format = tmp; + if (ioctl (fd_in, SNDCTL_DSP_SETFMT, &tmp) == -1) + { + perror ("SNDCTL_DSP_SETFMT"); + exit (-1); + } + + if (tmp != format) + { + fprintf (stderr, + "Error: Input and output devices use different formats (%x/%x)\n", + tmp, format); + exit (-1); + } + + if (format == AFMT_S16_OE) + { + fprintf (stderr, + "Warning: Using 16 bit sample format with wrong endianess.\n"); + } + +/* + * It might be better to set the sampling rate firs on the input device and + * then use the same rate with the output device. The reason for this is that + * the vmix driver supports sample rate conversions for output. However input + * is locked to the master device rate. + */ + tmp = rate; + + if (ioctl (fd_in, SNDCTL_DSP_SPEED, &tmp) == -1) + { + perror ("SNDCTL_DSP_SPEED"); + exit (-1); + } + + if (tmp != rate) + { + fprintf (stderr, "%s doesn't support requested rate %d (%d)\n", + dspname_out, rate, tmp); + exit (-1); + } + + if (ioctl (fd_out, SNDCTL_DSP_SPEED, &tmp) == -1) + { + perror ("SNDCTL_DSP_SPEED"); + exit (-1); + } + + if (tmp != rate) + { + fprintf (stderr, "%s doesn't support the same rate %d!=%d as %s\n", + dspname_out, rate, tmp, dspname_in); + exit (-1); + } + +/* + * Get the actual fragment size. SNDCTL_DSP_GETBLKSIZE gives a good + * compromise. If cooked mode is not disabled then the fragment sizes + * used for input and output may be different. Using + * {!nlink SNDCTL_DSP_GETOSPACE} or {!nlink SNDCTL_DSP_GETISPACE} + * may return different values. In this case SNDCTL_DSP_GETBLKSIZE will + * return a value that is between them. + */ + + if (ioctl (fd_in, SNDCTL_DSP_GETBLKSIZE, &fragsize) == -1) + { + perror ("SNDCTL_DSP_GETBLKSIZE"); + exit (-1); + } + + if (fragsize > sizeof (buffer)) + { + fprintf (stderr, "Too large fragment size %d\n", fragsize); + exit (-1); + } + +/* + * Do not check the fragment sizes on both devices. It is perfectly normal + * that they are different. Instead check just the input fragment size because + * it is the one that matters. + */ + + printf ("Sample parameters set OK. Using fragment size %d\n", fragsize); + +} + +static void +method_0 (int fd_out, int fd_in) +{ +/* + * This function demonstrates how to implement an full duplex + * application in the easiest and most reliable way. This method uses + * blocking reads for synchronization with the device. + */ + + int l; + int loopcount = 0; + + /* + * Fragments can be as large as 32k (or even up to 64k) + * with certain devices. + */ + char silence[32 * 1024]; /* Buffer for one fragment of silence */ + +/* + * This is the record/play loop. This block uses simple model where recorded data + * is just read from the device and written back without use of any additional + * ioctl calls. This approach should be used if the application doesn't need + * to correlate the recorded samples with playback (for example for echo + * cancellation). + * + * In this approach OSS will automatically find out how large buffer is + * needed to handle the process without buffer underruns. However there will + * probably be few dropouts before the buffer gets adapted for the worst + * case latencies. + * + * There are two methods to avoid the initial dropouts. The easiest method is + * to write few milliseconds of silent sampels to the output before writing + * the first block of actual recorded data. Another approach is to warm up + * the device by replacing first seconds of recorded data with silecene + * before writing the data to the output. + * + * After few moments of run the output should settle. After that the lag + * between input and output should be very close to the minimum input-output + * latency that can be obtained without using applied woodoo. The latencies can + * possibly be reduced by optimizing the application itself (if it's CPU bound), + * by using shorter fragments (also check the max_intrate parameter in + * osscore.conf), by terminating unnecessary applications and by using + * higher (linear) priority. + */ + + while ((l = read (fd_in, buffer, fragsize)) == fragsize) + { + int delay; + float t; + + if (loopcount == 0) + { + /* + * Output one extra fragment filled with silence + * before starting to write the actual data. This must + * be done after we have recorded the first fragment + * Without this extra silence in the beginning playback + * data will run out microseconds before next recorded + * data becomes available. + * + * Number of silence bytes written doesn't need to be + * exactly one fragment or any multiple of it. Sometimes + * (under highly loaded system) more silence will be + * needed. Sometimes just few samples may be + * enough. + */ + + memset (silence, 0, fragsize); + if (write (fd_out, silence, fragsize) != fragsize) + { + perror ("write"); + exit (-1); + } + } + + /* + * Compute the output delay (in milliseconds) + */ + if (ioctl (fd_out, SNDCTL_DSP_GETODELAY, &delay) == -1) + delay = 0; + + delay /= 4; /* Get number of 16 bit stereo samples */ + + t = (float) delay / (float) rate; /* Get delay in seconds */ + + t *= 1000.0; /* Convert delay to milliseconds */ + + if ((l = write (fd_out, buffer, fragsize)) != fragsize) + { + perror ("write"); + exit (-1); + } + +#if 1 + /* + * Printing the delay level will slow down the application which + * makes the delay longer. So don't print it by default. + */ + printf ("\rDelay=%5.3g msec", t); + fflush (stdout); +#endif + + loopcount++; + } + + perror ("read"); + exit (-1); +} + +static void +method_1 (int fd_out, int fd_in) +{ +/* + * Many applications use select/poll or the equivivalent facilities provided + * by GUI libraries like GTK+ to handle I/O with multiple devices. We use + * select() in this example. + * + * This routine reads audio input, does some processing on the data and + * forwards it to the output device. Hitting ENTER terminates the program. + * + * The logic is that any input available on the device will be read. However + * the amount of space available on output is not checked because it's + * unnecessary. Checking both input and output status would be very tricky + * if not impossible. + */ + + char silence[32 * 1024]; /* Buffer for one fragment of silence */ + fd_set reads; + int n; + unsigned int trig; + int first_time = 1; + +/* + * First we have to start the recording engine. Otherwise select() will never + * report available input and the application will just iddle. + */ + trig = 0; /* Trigger OFF */ + if (ioctl (fd_in, SNDCTL_DSP_SETTRIGGER, &trig) == -1) + perror ("SETTRIGGER 0"); + + trig = PCM_ENABLE_INPUT; /* Trigger ON */ + + /* + * Trigger output too if using the single device mode. Otherwise all writes + * will fail. + */ + if (fd_in == fd_out) + trig |= PCM_ENABLE_OUTPUT; + + if (ioctl (fd_in, SNDCTL_DSP_SETTRIGGER, &trig) == -1) + perror ("SETTRIGGER 1"); + + while (1) /* Infinite loop */ + { + struct timeval time; + + FD_ZERO (&reads); + + FD_SET (0, &reads); /* stdin */ + FD_SET (fd_in, &reads); + + time.tv_sec = 1; + time.tv_usec = 0; + if ((n = select (fd_in + 1, &reads, NULL, NULL, &time)) == -1) + { + perror ("select"); + exit (-1); + } + + if (n == 0) + { + fprintf (stderr, "Timeout\n"); + continue; + } + + if (FD_ISSET (0, &reads)) /* Keyboard input */ + { + printf ("Finished.\n"); + exit (0); + } + + if (FD_ISSET (fd_in, &reads)) + { + /* + * Recorded data is available. + */ + int l, n; + struct audio_buf_info info; + + if (ioctl (fd_in, SNDCTL_DSP_GETISPACE, &info) == -1) + { + perror ("GETISPACE"); + exit (-1); + } + + n = info.bytes; /* How much */ + + if ((l = read (fd_in, buffer, n)) == n) + { + printf ("\r %5d bytes ", n); + fflush (stdout); + + if (first_time) + { + /* + * Write one fragment of silence before the actual data. + * This is necessary to avoid regular underruns while + * waiting for more data. + */ + memset (silence, 0, fragsize); + if (write (fd_out, silence, fragsize) != fragsize) + { + perror ("write"); + exit (-1); + } + first_time = 0; + } + + /* + * This is the place where you can add your processing. + * There are 'l' bytes of audio data stored in 'buffer'. This + * program uses 16 bit native endian format and two channels + * (stereo). + */ + if (write (fd_out, buffer, l) != l) + { + perror ("write"); + exit (-1); + } + continue; + } + } + + perror ("read"); + exit (-1); + } +} + +int +main (int argc, char *argv[]) +{ + + int method = 0; + +/* + * Check the working method + */ + if (argc > 1) + method = atoi (argv[1]); + +/* + * Check if the sampling rate was given on command line. + */ + if (argc > 2) + { + rate = atoi (argv[2]); + if (rate == 0) + rate = 48000; + } + +/* + * Check if the device name is given on command line. + */ + if (argc > 3) + dspname = argv[3]; + +/* + * Check if anotherdevice name is given for input. + */ + if (argc > 4) + dspname_in = argv[4]; + + if (dspname_in == NULL) + open_one_device (dspname); + else + open_two_devices (dspname, dspname_in); + + switch (method) + { + case 0: + method_0 (fd_out, fd_in); + break; + + case 1: + method_1 (fd_out, fd_in); + break; + + default: + fprintf (stderr, "Method %d not defined\n", method); + exit (-1); + } + + exit (0); +} diff --git a/tutorials/sndkit/samples/midi.c b/tutorials/sndkit/samples/midi.c new file mode 100644 index 0000000..8cdbb86 --- /dev/null +++ b/tutorials/sndkit/samples/midi.c @@ -0,0 +1,67 @@ +/* + * Purpose: A minimalistic MIDI output programming sample. + * Copyright (C) 4Front Technologies, 2002-2004. Released under GPLv2/CDDL. + * + * Description: + * This program does nothing but plays a note on MIDI channel 1 + * after sending a program change message. + * + * This program demonstrates how simple it's to write programs that play + * MIDI. All you need to do is assembling the MIDI message and writing + * it to the device. The MIDI format is defined in "MIDI 1.0 Detailed + * Specification" which is available from MIDI Manufacturs Association (MMA) + * (see {!hlink http://www.midi.org}). + * + * This program does timing by calling the sleep(3) system call. In some + * systems like Linux there may be better sleep routines like usleep(3) + * that provide better timing resolution. + * + * However application based timing may not be as precise as required in + * musical applications. For this reason the MIDI interface of OSS will + * provide a driver based timing approach in the near future. + * + * Please look at the "{!link MIDI}" section of the OSS Developer's + * manual for more info about MIDI programming. + */ + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <fcntl.h> +#include <soundcard.h> + +#define DEVICE "/dev/midi" + +int +main () +{ + int fd; + + unsigned char note_on[] = { 0xc0, 0, /* Program change */ + 0x90, 60, 60 + }; /* Note on */ + unsigned char note_off[] = { 0x80, 60, 60 }; /* Note off */ + + if ((fd = open (DEVICE, O_WRONLY, 0)) == -1) + { + perror ("open " DEVICE); + exit (-1); + } + + if (write (fd, note_on, sizeof (note_on)) == -1) + { + perror ("write " DEVICE); + exit (-1); + } + + sleep (1); /* Delay one second */ + + if (write (fd, note_off, sizeof (note_off)) == -1) + { + perror ("write " DEVICE); + exit (-1); + } + + close (fd); + exit (0); +} diff --git a/tutorials/sndkit/samples/midiin.c b/tutorials/sndkit/samples/midiin.c new file mode 100644 index 0000000..ab6ca26 --- /dev/null +++ b/tutorials/sndkit/samples/midiin.c @@ -0,0 +1,55 @@ +/* + * Purpose: A minimalistic MIDI input programming sample. + * Copyright (C) 4Front Technologies, 2002-2004. Released under GPLv2/CDDL. + * + * Description: + * This simple program opens a MIDI device file and displays everything + * it receives in hexadecimal format. + * + * Please look at the "{!link MIDI}" section of the OSS Developer's + * manual for more info about MIDI programming. + */ + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <fcntl.h> +#include <soundcard.h> + +#define DEVICE "/dev/midi" + +int +main () +{ + int fd; + unsigned char buf[128]; + int l; + + if ((fd = open (DEVICE, O_RDONLY, 0)) == -1) + { + perror ("open " DEVICE); + exit (-1); + } + +/* + * Note that read may return any number of bytes between 0 and sizeof(buf). + * -1 means that some error has occurred. + */ + + while ((l = read (fd, buf, sizeof (buf))) != -1) + { + int i; + + for (i = 0; i < l; i++) + { + if (buf[i] & 0x80) /* Status byte */ + printf ("\n"); + printf ("%02x ", buf[i]); + } + + fflush (stdout); + } + + close (fd); + exit (0); +} diff --git a/tutorials/sndkit/samples/mixer_applet.c b/tutorials/sndkit/samples/mixer_applet.c new file mode 100644 index 0000000..e443d13 --- /dev/null +++ b/tutorials/sndkit/samples/mixer_applet.c @@ -0,0 +1,404 @@ +/* + * Purpose: A sample program for developing a simple mixer applet. + * Copyright (C) 4Front Technologies, 2007. Released under GPLv2/CDDL. + * + * This program is not usefull by itself. It just demonstrates techniques that + * can be used when developing very simple mixer applets that control just + * the key volumes in the system. + * + * The full OSS 4.0 mixer API is rather complex and designed for allmighty + * master mixer applications. However there is a subset of the API that can be + * used rather easily. This subset is limited to control of the main output volume, + * audio/wave/pcm playback volume and/or recording input level. It cannot be + * used for anything else. + * + * This program demonstrates three main techniques to be used by mixer applets: + * + * 1) How to find the default mixer device that controls the primary + * sound card/device in the system. This device is connected to the + * primary (desktop) speakers and the default system sounds/beep are + * directed to it. Normally this device is the audio chip installed on + * the motherboard of the computer. + * + * 2) How to find out the main, pcm and recording volume controls for + * the given device. + * + * 3) How to read the current volume and how to change it. + * + */ + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <fcntl.h> +#include <soundcard.h> +#include <time.h> +#include <errno.h> + +oss_sysinfo sysinfo; + +#define MAX_CTL 50 + +static int mixer_dev = -1; /* Use the default mixer */ + +int +find_default_mixer (int mixer_fd) +{ + int default_mix = -1; + int best_pri = -2; + + oss_mixerinfo mi; + + int i; + +/* + * The default mixer device in the system can be found by checking the + * priority parameter of all mixer devices in the system. The device with the + * highest priority value is the winner. If there are multiple devices with the + * same priority then the first one should be selected. + * + * Note that there should be some method for selecting the mixer device number. + * In many cases the user actually wants to use some other mixer than the + * motherboard one. + */ + + for (i = 0; i < sysinfo.nummixers; i++) + { + mi.dev = i; + + if (ioctl (mixer_fd, SNDCTL_MIXERINFO, &mi) == -1) + { + perror ("SNDCTL_MIXERINFO"); + continue; + } + + if (mi.priority < -1) /* Not suitable default mixer */ + continue; + + if (mi.priority > best_pri) + { + default_mix = i; + best_pri = mi.priority; + } + } + + printf("Mixer device %d seems to be the most probable motherboard device\n", + default_mix); + + return default_mix; +} + +void +show_controls (int mixer_fd, char *heading, int mixer_dev, int ctls[], int count) +{ + oss_mixext ext; + oss_mixer_value val; + int ctl, i; + + printf("\n***** %s *****\n", heading); + + for (i=0;i<count;i++) + { + ctl = ctls[i]; +/* + * Obtain the mixer extension definition. It might be a good idea to cache + * this info in global variables so that doesn't need to be reloaded + * every time. + * + * Reloading this info every time may cause serious troubles because in that + * way the application cannot be noticed after the mixer interface has changed. + */ + + ext.dev = mixer_dev; + ext.ctrl = ctl; + + if (ioctl (mixer_fd, SNDCTL_MIX_EXTINFO, &ext) == -1) + { + perror ("SNDCTL_MIX_EXTINFO"); + exit (-1); + } + +/* + * Have to initialize the dev, ctl and timestamp fields before reading the + * actual value. + */ + + val.dev = mixer_dev; + val.ctrl = ctl; + val.timestamp = ext.timestamp; + + if (ioctl (mixer_fd, SNDCTL_MIX_READ, &val) == -1) + { + if (errno == EIDRM) + { +/* + * Getting errno=EIDRM tells that the mixer struicture has been changed. This + * may happen for example if new firmware gets loaded to the device. In such + * case the application should start from the beginning and to load all + * the information again. + * + */ + fprintf (stderr, "Mixer structure changed. Please try again\n"); + exit (-1); + } + + perror ("SNDCTL_MIX_READ"); + exit (-1); + } + + printf ("\t%3d: %s\t ", ctl, ext.extname); + + switch (ext.type) + { + case MIXT_MONOSLIDER: + printf ("monoslider %d ", val.value & 0xff); + break; + + case MIXT_STEREOSLIDER: + printf ("stereoslider %d:%d ", val.value & 0xff, + (val.value >> 8) & 0xff); + break; + + case MIXT_SLIDER: + printf ("slider %d ", val.value); + break; + + case MIXT_MONOSLIDER16: + printf ("monoslider %d ", val.value & 0xffff); + break; + + case MIXT_STEREOSLIDER16: + printf ("stereoslider %d:%d ", val.value & 0xffff, + (val.value >> 16) & 0xffff); + break; + +/* + * Sometimes there may be just a MUTE control instead of a slider. However + * it's also possible that there is both mute and a slider. + */ + case MIXT_ONOFF: + printf ("ONOFF %d ", val.value); + break; + + case MIXT_MUTE: + printf ("mute %d ", val.value); + break; + +/* + * Enumerated controls may be used for example for recording source + * selection. + */ + case MIXT_ENUM: + printf ("Selection %d ", val.value); + break; + + + default: + printf ("Unknown control type (%d), value=0x%08x ", ext.type, + val.value); + } + + printf ("\n"); + } + +} + +int +main (int argc, char *argv[]) +{ + int mixer_fd = -1; + int i, n; + + /* + * Bins for the mixer controls. + */ +#define ADD_TO_BIN(bin, ctl) \ + if (n_##bin >= MAX_CTL) \ + { \ + fprintf(stderr, #bin " table is full\n"); exit(-1); \ + } \ + bin##_ctls[n_##bin++] = ctl + + int mainvol_ctls[MAX_CTL]; + int pcmvol_ctls[MAX_CTL]; + int recvol_ctls[MAX_CTL]; + int monvol_ctls[MAX_CTL]; + int n_mainvol=0, n_pcmvol=0, n_recvol=0, n_monvol=0; + char *devmixer; + + if ((devmixer=getenv("OSS_MIXERDEV"))==NULL) + devmixer = "/dev/mixer"; + +/* + * Get the mixer device number from command line. + */ + if (argc > 1) + mixer_dev = atoi (argv[1]); + +/* + * Open /dev/mixer. This device file can be used regardless of the actual + * mixer device number. + */ + + if ((mixer_fd = open (devmixer, O_RDWR, 0)) == -1) + { + perror (devmixer); + exit (-1); + } + +/* + * Get OSS system info to a global buffer. + */ + + if (ioctl (mixer_fd, SNDCTL_SYSINFO, &sysinfo) == -1) + { + perror ("SNDCTL_SYSINFO"); + exit (-1); + } + +/* + * Check the mixer device number. + */ + + if (mixer_dev == -1) + mixer_dev = find_default_mixer (mixer_fd); + + if (mixer_dev < 0 || mixer_dev >= sysinfo.nummixers) + { + fprintf (stderr, "Nonexistent mixer device %d\n", mixer_dev); + exit (-1); + } + + printf ("Using OSS mixer device %d\n", mixer_dev); + +/* + * The second step is to find the main volume, audio/pcm playback volume and + * recording level controls. + * + * It's important to understand that many mixer devices don't have such + * controls. This is perfectly normal and the mixer applet must be able to + * handle this. Aborting or displaying loud error message should be avoided. + * + * It's also possible that some mixers have multiple main volume, pcm or + * record level controls. In such case the application can support all of + * of them or select just the first one. Having multiple controls means that + * the device hase multiple sets of speakers or audio devices and each of + * them has separate volume controls. + */ + + n = mixer_dev; + if (ioctl (mixer_fd, SNDCTL_MIX_NREXT, &n) == -1) + { + perror ("SNDCTL_MIX_NREXT"); + exit (-1); + } + + for (i = 0; i < n; i++) + { + oss_mixext ext; + + ext.dev = mixer_dev; + ext.ctrl = i; + + if (ioctl (mixer_fd, SNDCTL_MIX_EXTINFO, &ext) == -1) + { + perror ("SNDCTL_MIX_EXTINFO"); + exit (-1); + } + +/* + * The MIXF_MAINVOL, MIXF_PCMVOL, MIXF_MONVOL and MIXF_RECVOL flags are used to mark + * potential main volume, pcm and recording level controls. This makes it + * possible to implement support for these common types of controls without + * having to implement fully featured mixer program. + * + * Mixer applets using this simplified interface should ignore all mixer + * controls that don't have any of these three flags. However + * + * Note that while mixer controls should have at most one of thse flags defined + * it may happen that some devices violate this rule. It's up to + * application what it does with such controls. Preferably it gets added + * to all of the bins. + */ + + if (ext. + flags & (MIXF_MAINVOL | MIXF_PCMVOL | MIXF_RECVOL | MIXF_MONVOL)) + { + printf ("Mixer control %d is ", i); + + if (ext.flags & MIXF_MAINVOL) + { + printf ("Mainvol "); + + ADD_TO_BIN(mainvol, i); + } + + if (ext.flags & MIXF_PCMVOL) + { + printf ("PCMvol "); + + ADD_TO_BIN(pcmvol, i); + } + + if (ext.flags & MIXF_RECVOL) + { + printf ("Recvol "); + + ADD_TO_BIN(recvol, i); + } + + if (ext.flags & MIXF_MONVOL) + { + printf ("Monvol "); + + ADD_TO_BIN(monvol, i); + } +/* + * It is possible that many/most/all mixer controls don't have any of the above + * flags set. This means that such controls are for expert use only. It is + * recommended that mixer applets have an [Advanced options] button that is + * enabled if such controls are found. This button can launch ossxmix (or + * some configurable program). + */ + + printf ("%s\n", ext.extname); + } + } + +/* + * Now we have selected the mixer controls. Next show their values. + * Since setting the value is pretty much identical to reading them we don't + * demonstrate it in this program. + */ + printf ("\n"); + + if (n_mainvol > 0) + show_controls (mixer_fd, "Main volume controls", mixer_dev, mainvol_ctls, n_mainvol); + else + printf ("No main volume control available\n"); + + if (n_pcmvol > 0) + show_controls (mixer_fd, "Pcm volume controls", mixer_dev, pcmvol_ctls, n_pcmvol); + else + printf ("No pcm volume control available\n"); + + if (n_recvol > 0) + show_controls (mixer_fd, "Rec volume controls", mixer_dev, recvol_ctls, n_recvol); + else + printf ("No rec volume control available\n"); + + if (n_monvol > 0) + show_controls (mixer_fd, "Monitor volume controls", mixer_dev, monvol_ctls, n_monvol); + else + printf ("No monitor volume control available\n"); + + close (mixer_fd); + + if (n_mainvol + n_pcmvol + n_recvol + n_monvol == 0) + { + printf("\nNo 'simple' mixer controls available for this device\n"); + printf("It may be a good idea to start 'ossxmix -d %d' which can access advanced options.\n", mixer_dev); + } + + exit (0); +} diff --git a/tutorials/sndkit/samples/mixer_test.txt b/tutorials/sndkit/samples/mixer_test.txt new file mode 100644 index 0000000..32a5378 --- /dev/null +++ b/tutorials/sndkit/samples/mixer_test.txt @@ -0,0 +1,108 @@ +#ifdef DOCUMENT +Short guide for using the OSS mixer extension API +================================================= + +The mixer extension API is fully dynamic. It doesn't use fixexed +controller numbering. Instead the controllers are identified by their name +(which is different from the names displayed by ossmix). You need to read +the complete controller list and pick the controller numbers from it based +on the name (the numbers may change between OSS versions if new +controllers are added or some existing ones removed). + +You can read the controller list using the following code fragment. Look +at the names and pick the numbers of the controllers you need to use. You +will also need the possible range of the controller. + +You can ignore all other information. +#endif + + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/soundcard.h> + +#define MIXER_DEVICE_NUMBER=0 + +int +main(void) +{ + int i, n, dev; + int mixerfd=open("/dev/mixer", O_RDWR, 0); + + oss_mixext desc; + + dev=n=MIXER_DEVICE_NUMBER; + + if (ioctl(mixerfd, SNDCTL_MIX_NREXT, &n)==-1) + { + perror("SNDCTL_MIX_NREXT"); + if (errno == EINVAL) + fprintf(stderr, "Error: OSS version 3.9 or later is required\n"); + exit(-1); + } + + + for (i=0;i<n;i++) + { + desc.dev = dev; + desc.dctrl = i; + + if (ioctl(mixerfd, SNDCTL_MIX_EXTINFO, &desc)==-1) + { + perror("SNDCTL_MIX_EXTINFO"); + exit(-1); + } + + printf("Name %s, number %s, range %d-%d\n", + desc.id, i, desc.minvalue, desc.maxvalue); + /* + * desc.type gives the type of the controller if you need it. + * Possible types are defined in soundcard.h + */ + } + + +/* + * After you have collected the controller numbers you can write the value in + * the following way: + */ + + oss_mixer_value val; + + val.dev = MIXER_DEVICE_NUMBER; + val.ctrl = CONTROLLER_NUMBER; + val.value = NEW_VALUE_TO SET; + val.timestamp = THE_TIMESTAMP_FIELD_FROM_THE DESCRIPTOR_RECORD; + + if (ioctl(mixerfd, SNDCTL_MIX_WRITE, &val)==-1) + { + perror("SNDCTL_MIX_WRITE"); + exit(-1); + } + + exit(0); +} +/* +Reading the current value can be done using SNDCTL_MIX_READ instead of +SNDCTL_MIX_WRITE. + +Implement first just a program that prints the descriptor records. In this +way you can examine the controller names that are available. + +I'm not 100% sure if the controller names are unique. The list returned by +SNDCTL_MIX_EXTINFO is actually tree. Every entry is actually a node and +it's 'parent' field contains the number of the parent (group) node. You +may need to concatenate the names of the controller and it's parent to +get a unique name (something like ENVY24_GAIN.ENVY24_OUT1/2). + +I'm still planning to make few changes to the structs used by this API +(that's the reason why we have not documented the API yet). When or if +this happens you may have to recompile your application (hopefully not). +It's also possible that I make the controller names unique in which case +you will have to change the names you use. So make your program to check +that it finds names for all controllers it needs and give an error message +if something is missing. In this way you now when to modify your program +to use the new names. +*/ diff --git a/tutorials/sndkit/samples/mixext.c b/tutorials/sndkit/samples/mixext.c new file mode 100644 index 0000000..a70e638 --- /dev/null +++ b/tutorials/sndkit/samples/mixext.c @@ -0,0 +1,270 @@ +/* + * Purpose: A simple sample program for using the new mixer API + * Copyright (C) 4Front Technologies, 2002-2004. Released under GPLv2/CDDL. + * + * Description: + * This program is as simple as possible example for using the new mixer API + * of OSS 4.0 (and later) from applications. It shows how to read or + * change the settings. Please read the OSS API Developer's Manual for + * more info about this API. + * + * This mixer interface is designed to ne "human readable". The idea is that + * the mixer program just shows whatever controls are available and lets the + * user to adjust them as he/she likes. + * + * Please note that the control names and numbers are fully dynamic. There are + * some control names such as "spdif.enable" that are used by several different + * drivers. It's relatively safe to assume that such controls have always + * the same meaning. However there is no list of such control names + * available at this moment. Even this kind of controls are supported just by + * some of the devices (some devices just don't have that feature). + * + * Most control names are 100% non-portable between devices. In addition some + * settings may be available depending on the configuration. + * + * {!notice Applications using this mixer interface must not assume that + * certain controls are always available or that they have exactly defined + * meanings.} + * + * This program can be run without any command line arguments to + * list the available controls and their values. The mixer device number + * (0 by default) can be given on the command line. + * + * Another way is to give the mixer number, control ame and value + * on the command line ({!shell mixext 1 spdif.enable 1}). + */ +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <fcntl.h> +#include <soundcard.h> +#include <time.h> +#include <errno.h> + +static int mixer_dev = 0; /* Change this to access another mixer device */ +static int mixerfd = -1; + +typedef struct +{ + int num; + char *name; +} name_ent; + +#define TYPE_ENTRY(x) {x, #x} + +name_ent type_names[] = { + TYPE_ENTRY (MIXT_DEVROOT), + TYPE_ENTRY (MIXT_GROUP), + TYPE_ENTRY (MIXT_ONOFF), + TYPE_ENTRY (MIXT_MUTE), + TYPE_ENTRY (MIXT_ENUM), + TYPE_ENTRY (MIXT_MONOSLIDER), + TYPE_ENTRY (MIXT_STEREOSLIDER), + TYPE_ENTRY (MIXT_MESSAGE), + TYPE_ENTRY (MIXT_MONOVU), + TYPE_ENTRY (MIXT_STEREOVU), + TYPE_ENTRY (MIXT_MONOPEAK), + TYPE_ENTRY (MIXT_STEREOPEAK), + TYPE_ENTRY (MIXT_RADIOGROUP), + TYPE_ENTRY (MIXT_MARKER), + TYPE_ENTRY (MIXT_VALUE), + TYPE_ENTRY (MIXT_HEXVALUE), + TYPE_ENTRY (MIXT_MONODB), + TYPE_ENTRY (MIXT_STEREODB), + TYPE_ENTRY (MIXT_SLIDER), + TYPE_ENTRY (MIXT_3D), + TYPE_ENTRY (MIXT_MONOSLIDER16), + TYPE_ENTRY (MIXT_STEREOSLIDER16), + {-1, NULL} +}; + +static char * +mixt_name (int num) +{ + int i; + + i = 0; + + while (type_names[i].num != -1) + { + if (type_names[i].num == num) + return type_names[i].name; + i++; + } + return "Unknown type"; +} + +void +list_controls (int mixer_dev) +{ + int i, n, nn = 0; + oss_mixext ext; + int marker_seen = 0; + + n = mixer_dev; + + if (ioctl (mixerfd, SNDCTL_MIX_NREXT, &n) == -1) + { + perror ("SNDCTL_MIX_NREXT"); + if (errno == EINVAL) + fprintf (stderr, "Error: OSS version 3.9 or later is required\n"); + return; + } + + printf ("%d mixer controls available (including ordinary mixer controls)\n", + n); + + for (i = 0; i < n; i++) + { + oss_mixer_value val; + + ext.dev = mixer_dev; + ext.ctrl = i; + + if (ioctl (mixerfd, SNDCTL_MIX_EXTINFO, &ext) == -1) + { + if (errno == EINVAL) + { + fprintf (stderr, "SNDCTL_MIX_EXTINFO failed\n"); + fprintf (stderr, + "This is almost certainly caused by a too old or new OSS version.\n"); + return; + } + + perror ("SNDCTL_MIX_EXTINFO"); + return; + } + + if (ext.type == MIXT_MARKER) + { + marker_seen = 1; + continue; + } + else if (!marker_seen) + continue; + + val.dev = mixer_dev; + val.ctrl = i; + val.timestamp = ext.timestamp; + + val.value = 0; + + if (ext.type != MIXT_DEVROOT && ext.type != MIXT_GROUP + && ext.type != MIXT_MARKER) + if (ioctl (mixerfd, SNDCTL_MIX_READ, &val) == -1) + { + perror ("SNDCTL_MIX_READ"); + val.value = 0xffffffff; + } + + printf ("%3d: \"%s\" (\"%s\"), parent %d\n", i, ext.extname, ext.id, + ext.parent); + printf (" Type %d (%s), value %d (0x%08x) (max %d)\n", ext.type, + mixt_name (ext.type), val.value, val.value, ext.maxvalue); + printf (" Update counter %d\n", ext.update_counter); + nn++; + } + + printf ("%d controls accessible by this program\n", nn); +} + +int +set_control (int mixer_dev, char *name, int value) +{ + int i, n; + oss_mixext ext; + int marker_seen = 0; + + n = mixer_dev; + + if (ioctl (mixerfd, SNDCTL_MIX_NREXT, &n) == -1) + { + perror ("SNDCTL_MIX_NREXT"); + if (errno == EINVAL) + fprintf (stderr, "Error: OSS version 3.9 or later is required\n"); + return -4; + } + + for (i = 0; i < n; i++) + { + oss_mixer_value val; + + ext.dev = mixer_dev; + ext.ctrl = i; + + if (ioctl (mixerfd, SNDCTL_MIX_EXTINFO, &ext) == -1) + { + perror ("SNDCTL_MIX_EXTINFO"); + return -1; + } + + if (ext.type == MIXT_MARKER) + { + marker_seen = 1; + continue; + } + else if (!marker_seen) + continue; + + + if (strcmp (ext.extname, name) != 0) /* No match */ + continue; + + if (!(ext.flags & MIXF_WRITEABLE)) + return -2; + + val.dev = mixer_dev; + val.ctrl = i; + val.timestamp = ext.timestamp; + val.value = value; + + if (ioctl (mixerfd, SNDCTL_MIX_WRITE, &val) == -1) + { + perror ("SNDCTL_MIX_WRITE"); + return -3; + } + + return 1; + } + + return 0; +} + +int +main (int argc, char *argv[]) +{ + char *devmixer; + + if ((devmixer=getenv("OSS_MIXERDEV"))==NULL) + devmixer = "/dev/mixer"; + + if (argc > 1) + mixer_dev = atoi (argv[1]); + + if ((mixerfd = open (devmixer, O_RDWR, 0)) == -1) + { + perror (devmixer); + exit (-1); + } + + if (argc > 3) + { + int val; + + val = atoi (argv[3]); + + if (set_control (mixer_dev, argv[2], val) > 0) + { + fprintf (stderr, "OK\n"); + } + else + { + fprintf (stderr, "Failed!!!\n"); + } + exit (0); + } + + list_controls (mixer_dev); + + exit (0); +} diff --git a/tutorials/sndkit/samples/mixext.readme b/tutorials/sndkit/samples/mixext.readme new file mode 100644 index 0000000..53c74c7 --- /dev/null +++ b/tutorials/sndkit/samples/mixext.readme @@ -0,0 +1,232 @@ +OSS Mixer extension API sample program +====================================== + +This program (mixext.c) is a simple program that demonstrates how to +read and write certain parameters using the mixer extension API of OSS. + +This API will be released officially in OSS v4.0 and until that we +reserve all rights to do changes to it without notice. However such +changes should not require more than recompile of the application. + +It should be pointed out that the mixer extension API is fully dynamic +in nature. It's possible that structure of the mixer changes on fly if some +"master setting" gets changed by some other application or user. For this +reason the information obtained using the SNDCTL_MIX_EXTINFO ioctl is +not "cacheable". In particular the controls are identified by name. The ID +numbers assigned to them are only valid immediately after they have been read +with SNDCTL_MIX_EXTINFO. After that they may change without notice +(the time stamp feature used by the API prevents applications from setting +wrong controls if this happens). + +Another important point is that there is no naming standard for the controls. +This means that any application using this API will only work with low level +devices it's written for. Different devices may have similar settings but they +are under different names. Also the names used by the ossmix and ossxmix +applications included in OSS are created from the names returned by +SNDCTL_MIX_EXTINFO using some algorithm. + +Writing mixer applications that can handle the above problems is beyond the +scope of this file and the mixext.c program. Full document for doing +programs like ossxmix will be released later (no known dates yet). + + +So the purpose of this program is to demonstrate how to: + +- Find out the control names that are used by the current device. +- Set a value of a mixer control with given mame. + +The program intentionally skips all controls before special MIXT_MARKER +entry. Such controls are aliases created automatically for the "ordinary" +mixer controls. You should use the original SOUND_MIXER_* API to +access such controls instead of using this program (major modifications +will be required). + +Compiling the program +--------------------- + +cc mixext.c -o mixext + +Listing the controls +-------------------- + +Execute the mixext program as + +./mixext <mixerdev> + +This shows a list of control names, their types and their values. The +usefull mixer control types will be listed at the end of this document. +The <mixerdev> argument is an integer between 0 and N. See the printout +produced by cat /dev/sndstat for the list of available mixers. + +Changing a control +------------------ + +Execute the program as: + +./mixext <mixerdev> <name> <value> + +The <name> is one of the mixer control names shown by the mixext program in +the "list mode". Node that with some devices there may be several entries +with exactly the same name. This program can access only the first of them. +To make this program to work with such devices you need to do some hacking +(see the Theory section below). + +The <value> is a decimal value (some controls require hex value). + +Theory behind mixer extensions +------------------------------ + +Take a look at the printout produced by the ossmix program in list mode. +Also take a look at the mixext.c program since otherwise the following +description is not understandable. + +As you may notice the entries listed form a tree like structure. The entry +number 0 is always the device root (type=MIXT_DEVROOT). For all +control entries the parent field shows the parent node. In addition to +MIXT_DEVROOT also MIXT_GROUP can work as a parent node (other controls can't +be parents of any other control). The parent node has always smaller entry +number than any of the daughter controls that belong to them. + +Some of the extensions are just aliases for the "normal" mixer controls that +are accessible by the ordinary OSS mixer API (SOUND_MIXER_*). These +aliases are listed first and the "real" extensions come after the MIXT_MARKER +entry. There are many tricky parts in handling those aliases so it's +highly recommended to ignore everything before MIXT_MARKER. + +Most of the mixer controls have a name but not all of them (in particular +the ones before MIXT_MARKER). The ossmix program shipped with OSS uses +a special algorithm to convert the names provided by the SNDCTL_MIX_EXTINFO +ioctl to the "external" names. This algorithm use "." as the separator between +the parts of the name (the names may contain most other special characters +including (but not limiting to) /@,:). Names may not contain spaces. + +For example if a control is "ENVY24_RATELOCK" and it's parent is "ENVY24" +the name used by ossmix will become envy24.ratelock. There are many rules +ane exceptions so the required algorithm will later be published as a library +routine. + +The application should skip all records whose type is not known. + +The following kind of mixer extension types are available: + +MIXT_DEVROOT +MIXT_GROUP + +These are group entries that don't have any readable/writeable value. They +are just used to group other entries together. MIXT_DEVROOT is just +a special group that always has entry number 0. + +MIXT_MARKER is another special entry. It's used as a separator between the +"alias" controls and the controls that are only accessible with the +mixer extension API. To make your life easier you should always ignore +MIXT_MARKER and everything before it (except the root entry of course). + +MIXT_ONOFF +MIXT_MUTE +MIXT_ENUM + +These controls are quite similar. MIXT_ONOFF and MIXT_MUTE have only two +possible values that are 0 (OFF) and 1 (ON). MIXT_ENUM has more +alternatives (the available values are 0 to maxvalue (returned by +the SNDCTL_MIX_EXTINFO ioctl call. + +However there is still one more thing. Some of the enum values are not always +available. For this the application should check the enum_present field to see +which values are actually permitted (Note! the following is just pseudocode): + +#define enum_check(ix) (enum_present[ix/8] & (1<<(ix%8)))) + +if (enum_check(0)) printf("FRONT is permitted\n"); +if (enum_check(1)) printf("REAR is permitted\n"); + +Note! In some earlier OSS versions the field was called enum_mask and it had + different usage. + + +MIXT_MONOSLIDER +MIXT_STEREOSLIDER + +These are sliders used to control volumes and other similar things. The value +field has separate fields for left and right channels: + + left = value & 0xff; + right = (value >> 8) & 0xff; + +The maximum value for both fields is given in the maxvalue field. Both channel +fields are used also by MIXT_MONOSLIDER but only the left channel value +is valid (the right channel field is assumed to be set to the same value). + + +MIXT_MONOSLIDER16 +MIXT_STEREOSLIDER16 + +These are replacementsof MIXT_MONOSLIDER and MIXT_STEREOSLIDER. The only +difference is that their value range has been expanded to 0-32767. + + left = value & 0xffff; + right = (value >> 16) & 0xffff; + + +MIXT_SLIDER is yet another slider but at this time the whole int field is +allocated for a single value (there are no separate left and right fields). +The maxvalue field shows the maximum permitted value. + +MIXT_MONOPEAK +MIXT_STEREOPEAK + +These are peak meters (usually read only). These meters hold the maximum +signal values since the control was previously read. Reading the value +resets it to 0. This kind of controls can be used to implement LED bar +displays and similar things. MIXT_MONOPEAK is 1 channel +controls while MIXT_STEREOPEAK is stereo one (the channel +values are returned using the same value format than with MIXT_STEREOSLIDER). + +MIXT_MONODB +MIXT_STEREODB + +These are obsolete control types and will not be used by any OSS drivers. + +MIXT_VALUE +MIXT_HEXVALUE + +These types are used for controls that are integer values. The difference is +the radix that should be sued when presenting the value to the user. + + +The following control types are reserved for future use (if any). They should +not be used by any applications since they may be removed from future OSS +versions: + +MIXT_RADIOGROUP +MIXT_3D +MIXT_MESSAGE +MIXT_MONOVU +MIXT_STEREOVU + +Reading and writing the controls +-------------------------------- + +The SNDCTL_MIX_READ and SNDCTL_MIX_WRITE controls can be used to read or +write the controls (respectively). The following fields of the argument +structure should be set prior the call: + + dev is the mixer device to use (0 to N). + + ctrl is the controller number. + + timestamp should be copied from the structure returned by the + SNDCTL_MIX_EXTINFO call. + +The application should check the flags field returned by SNDCTL_MIX_EXTINFO +before trying to read or change the value. The MIXF_WRITEABLE and MIXF_READABLE +bits tell if the value is writeable or readable. + +OSS will compare the timestamp field against it's internal list of +currently available controls. If the timestamp doesn't match the ioctl +call will return -1 with errno=EIDRM. This happens if the mixer structure +has changed between SNDCTL_MIX_EXTINFO and the SNDCTL_MIX_READ/SNDCTL_MIX_WRITE +calls. The only way to recover is re-loading the SNDCTL_MIX_EXTINFO info +and trying again. + +In case of problems please don't hesitate to contact +hannu@opensound.com for info. diff --git a/tutorials/sndkit/samples/mmap_duplex.c b/tutorials/sndkit/samples/mmap_duplex.c new file mode 100644 index 0000000..42f4762 --- /dev/null +++ b/tutorials/sndkit/samples/mmap_duplex.c @@ -0,0 +1,333 @@ +/* + * Purpose: A simple sample program for doing dull duplex using mmap + * + * Description: + * + * This is a sample program for doing full duplex using mmap. + * + * Unfortunately this program doesn't work at this moment (under construction). + * + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/mman.h> +#include <sys/time.h> +#include <sys/types.h> +#include <soundcard.h> + +/* + * Note that the mmap approach bypasses the OSS drivers completely so you are + * on your own. You will need to write the application so that it + * can work with any sample rate or sample format one can imagine. + * + * We will try to use 48 kHz / 16 bits / stereo which is the most likely + * format supported by the consumer audio devices. + */ +#define SAMPLE_RATE 48000 +#define SAMPLE_FORMAT AFMT_S16_NE +#define NUM_CHANNELS 2 +/* + * NOTICE! + * + * The mmap() feature is not supported by all devices. Using it will make + * the application to fail with some devices (professional ones in particular). + * + * When using the mmap mode it's very important to disable any format + * conversions done by OSS. This can be done by calling SNDCTL_DSP_COOKEDMODE + * _IMMEDIATELY_ after opening the device. + */ + +int sample_rate, num_channels, sample_format; + +int +main (int argc, char *argv[]) +{ + int fd, sz, i, tmp, n, l, have_data = 0; + +/* + * /dev/dsp_mmap is the best default device. Another alternative is /dev/dsp + * but in some systems the user may want to use different devices with + * mmap applications than with the normal ones. + * + * /dev/dsp_mmap will not be present in pre OSS 4.0 systems. The application + * may automatically try to open /dev/dsp if /dev/dsp_mmap is missing. + * Another approach is asking the user to create /dev/dsp_mmap. + * + * ln -s /dev/dsp /dev/dsp_mmap + * + * It's recommended that there is some method for configuring or selecting + * the audio device (such as a command line option or an environment + * variable. + */ + char *audio_dev = "/dev/dsp_mmap"; + + struct buffmem_desc imemd, omemd; + caddr_t buf; + + char *ip, *op; + + struct audio_buf_info info; + + int frag = 0x00200008; /* 32 fragments of 2^8=256 bytes */ + + fd_set reads, writes; + +/* + * Getting the device name to open. First the command line method + * (simplified). + */ + + if (argc >= 2) + { + audio_dev = argv[1]; + } + else + { + /* + * No device given on command line. Try to see if an environment + * variable is set. + * + * We will use MYAPP_AUDIODEV as the variable name. Replace the + * MYAPP_ prefix with your application name. + */ + + char *p; + + if ((p = getenv ("MYAPP_AUDIODEV")) != NULL) + audio_dev = p; + } + + if ((fd = open (audio_dev, O_RDWR, 0)) == -1) + { + perror (audio_dev); + exit (-1); + } + /* + * Disable cooked mode to permit mmap() with some devices. + * Don't do any error checking since usually this call will fail. + * There is no need to care about the return value. + */ + tmp = 0; + ioctl (fd, SNDCTL_DSP_COOKEDMODE, &tmp); /* Don't check the error return */ + + ioctl (fd, SNDCTL_DSP_SETFRAGMENT, &frag); /* No error checking needed */ + +/* + * Set the sample format. AFMT_S16_NE is best because it is supported by + * practically all devices. Also it equals to the "short" data type. + */ + + sample_format = SAMPLE_FORMAT; + if (ioctl (fd, SNDCTL_DSP_SETFMT, &sample_format) == -1) + { /* Something really fatal occurred. */ + perror ("SNDCTL_DSP_SETFMT"); + exit (-1); + } + +/* + * Check that we can support the returned sample format. + */ + switch (sample_format) + { + case AFMT_S16_NE: + /* + * Everything is OK. The sample format equals to 16 bit native + * integer (short in C/C++) so we do not need to do any + * format conversions. + */ + break; + +#if 0 +/* + * We do not support any other formats. However you must be prepared to do + * so. Please take a look at the formats section of the OSS API + * manual. + * + * Here are the most common other formats. + */ + + case AFMT_S16_OE: + /* + * 16 bits but with alien endianess. We can use short as the data type + * but the samples must be "byte swapped" prior writing them to + * the device. + */ + break; + + case AFMT_U8: + /* 8 bits unsigned format (unsigned char). Note that signed + * 16 bit samples can be converted to this format by + * doing u8_sample = (s16_ne_sample >> 8) - 128. + */ + break; + + case AFMT_S24_NE: + case AFMT_S24_OE: + case AFMT_S32_NE: + case AFMT_S32_OE: + /* Please see the formats section of the OSS API + * manual. + */ + +#endif + + default: + fprintf (stderr, "The device doesn't support the requested format\n"); + fprintf (stderr, "0x%08x != 0x%08x\n", tmp, SAMPLE_FORMAT); + exit (-1); + } + +/* + * Set up the number of channels. + * This program will automatically support any number of channels by + * writing the same sample value to each of the channels (mono). + * + * Equally well stereo samples can be mixed for a 1 channel + * device by summing the channel values together. Stereo samples + * can be played with multi channel devices by setting the remaining + * channel samples to a silent value (0 with signed formats). + */ + + num_channels = NUM_CHANNELS; + if (ioctl (fd, SNDCTL_DSP_CHANNELS, &num_channels) == -1) + { + perror ("SNDCTL_DSP_CHANNELS"); + exit (-1); + } + +/* + * Set the sample rate. + */ + tmp = SAMPLE_RATE; + if (ioctl (fd, SNDCTL_DSP_SPEED, &tmp) == -1) + { + perror ("SNDCTL_DSP_SPEED"); + exit (-1); + } + + if (tmp != SAMPLE_RATE) + { + /* + * In some cases the device is not capable to support exactly + * the requested sampling rate. + * + * We will tolerate a 5% error in between the requested and + * granted sampling rates. + * + * If the error is larger then we cannot continue. + * + * NOTE! Applications written for the mass market must be prepared + * to support every possible sampling rate locally. + */ + int v; + + v = abs (tmp - SAMPLE_RATE); + + if (v > ((SAMPLE_RATE * 5) / 100)) + { + fprintf (stderr, + "The device doesn't support sampling rate of %d Hz.\n", + SAMPLE_RATE); + fprintf (stderr, "The nearest rate it supports is %d Hz\n", tmp); + exit (-1); + } + } + + if (ioctl (fd, SNDCTL_DSP_GETISPACE, &info) == -1) + { + perror ("GETISPACE"); + exit (-1); + } + + sz = info.fragstotal * info.fragsize; + + if ((buf = + mmap (NULL, sz, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, + 0)) == (caddr_t) - 1) + { + perror ("mmap (read)"); + exit (-1); + } + printf ("mmap (in) returned %08x\n", buf); + ip = buf; + + if ((buf = + mmap (NULL, sz, PROT_WRITE, MAP_FILE | MAP_PRIVATE, fd, + 0)) == (caddr_t) - 1) + { + perror ("mmap (write)"); + exit (-1); + } + printf ("mmap (out) returned %08x\n", buf); + op = buf; + +/* + * Prepare for launch. Set the trigger bits to 0 + */ + tmp = 0; + ioctl (fd, SNDCTL_DSP_GETTRIGGER, &tmp); + printf ("Trigger was %08x\n", tmp); + + tmp = PCM_ENABLE_OUTPUT; + ioctl (fd, SNDCTL_DSP_SETTRIGGER, &tmp); + printf ("Trigger set to %08x\n", tmp); + + for (i = 0; i < sz; i++) + op[i] = 0x00; + + while (1) + { + struct timeval time; + + FD_ZERO (&reads); + FD_ZERO (&writes); + + /* FD_SET(fd, &writes); */ + FD_SET (fd, &reads); + + time.tv_sec = 1; + time.tv_usec = 0; + if (select (fd + 1, &reads, &writes, NULL, &time) == -1) + { + perror ("select"); + exit (-1); + } + + if (FD_ISSET (fd, &reads)) + { + count_info count, ocount; + int l, p; + + if (ioctl (fd, SNDCTL_DSP_GETIPTR, &count) == -1) + perror ("GETIPTR"); + + if (ioctl (fd, SNDCTL_DSP_GETOPTR, &ocount) == -1) + perror ("GETOPTR"); + + printf ("read(%08x/%08x/%d)\n", count.bytes, count.ptr, + count.blocks); + printf ("write(%08x/%08x/%d)\n", ocount.bytes, ocount.ptr, + ocount.blocks); + + l = count.ptr; + p = ocount.ptr; + + if (l > (sz - ocount.ptr)) + l = sz - ocount.ptr; + + for (i = 0; i < l; i++) + op[i + p] = ip[i]; + } + + if (FD_ISSET (fd, &writes)) + { + printf ("write()\n"); + have_data = 0; + } + } + + exit (0); +} diff --git a/tutorials/sndkit/samples/mmap_test.c b/tutorials/sndkit/samples/mmap_test.c new file mode 100644 index 0000000..b1968ab --- /dev/null +++ b/tutorials/sndkit/samples/mmap_test.c @@ -0,0 +1,290 @@ +/* + * Purpose: A sample program for using mmap() + * Copyright (C) 4Front Technologies, 2002-2004. Released under GPLv2/CDDL. + * + * Description: + * This is a simple program which demonstrates use of mmapped DMA buffer + * of the sound driver directly from application program. + * + * This program tries to open a file called "smpl" in the current directory and + * play it. If present this file must be a "raw" audio file recorded with + * the same sample rate and format as this program uses. There is no checking + * for the format in this program. + * + * {!notice This program needs some fine tuning. At this moment it doesn't + * perform adequate error checkings.} + * + */ + +#define VERBOSE + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/mman.h> +#include <soundcard.h> +#include <sys/time.h> +#include <sys/ioctl.h> + +#ifndef MAP_FILE +#define MAP_FILE 0 +#endif + +static int sinebuf[48] = { + 0, 4276, 8480, 12539, 16383, 19947, 23169, 25995, + 28377, 30272, 31650, 32486, 32767, 32486, 31650, 30272, + 28377, 25995, 23169, 19947, 16383, 12539, 8480, 4276, + 0, -4276, -8480, -12539, -16383, -19947, -23169, -25995, + -28377, -30272, -31650, -32486, -32767, -32486, -31650, -30272, + -28377, -25995, -23169, -19947, -16383, -12539, -8480, -4276 +}; +static int sinep = 0; + +static void +produce_output (short *op, int offs, int len) +{ + int i; + + op += offs * 2; + + for (i = 0; i < len; i++) + { + int v = sinebuf[sinep]; + sinep = (sinep + 1) % 48; + + *op++ = v; /* Left channel */ + *op++ = v; /* Right channel */ + } +} + +int +main (int argc, char *argv[]) +{ + int fd, tmp; + int sz, fsz, num_samples; + int caps; + struct audio_buf_info info; + count_info ci; + caddr_t buf; + oss_audioinfo ai; + unsigned char *op; + int device_p, app_p; + +/* + * This program must use O_RDWR in some operating systems like Linux. + * However in some other operating systems it may need to be O_WRONLY. + * + * {!code /dev/dsp_mmap} is the default device for mmap applications. + */ + + if ((fd = open ("/dev/dsp_mmap", O_RDWR, 0)) == -1) + { + perror ("/dev/dsp_mmap"); + exit (-1); + } + + ai.dev = -1; + if (ioctl (fd, SNDCTL_ENGINEINFO, &ai) != -1) + { + printf ("Using audio device %s (engine %d)\n", ai.name, ai.dev); + } +/* + * Disable cooked mode to permit mmap() with some devices. + * Don't do any error checking since usually this call will fail. + * There is no need to care about the return value. + * + * Cooked mode must be disabled before setting the sample rate and format. + */ + tmp = 0; + ioctl (fd, SNDCTL_DSP_COOKEDMODE, &tmp); /* Don't check the error return */ + +/* + * Set up the sample format. We will use AFMT_S16_LE because it's the most + * common audio file format. AFMT_S16_NE is better in programs that + * generate the audio signal themselves. + */ + + tmp = AFMT_S16_LE; + if (ioctl (fd, SNDCTL_DSP_SETFMT, &tmp) == -1) + { + perror ("SNDCTL_DSP_SETFMT"); + exit (-1); + } + +/* + * Check the format returned by the driver. + * + * This program will simply refuse to work if it doesn't get the format it + * supports. Playing with incompatible formats will cause terrible noise so + * it must be avoided. + */ + if (tmp != AFMT_S16_LE) + { + fprintf (stderr, + "Error: The device doesn't support the requested sample format\n"); + exit (-1); + } + +/* + * Set the number of channels and the sample rate. We do not care about the + * returned values. They will just be reported to the user. + * + * {!notice Real applications must be prepared to support sampling rates + * between 8 kHz and 192 kHz (at least). Equally well the number of channels + * may be between 1 and 16 (or even more).} + * + * Two channels and 48 kHz is the most likely combination that works. + */ + tmp = 2; /* Stereo */ + if (ioctl (fd, SNDCTL_DSP_CHANNELS, &tmp) == -1) + { + perror ("SNDCTL_DSP_CHANNELS"); + exit (-1); + } + + printf ("Number of channels is %d\n", tmp); + + tmp = 44100; /* 48000 is the most recommended rate */ + if (ioctl (fd, SNDCTL_DSP_SPEED, &tmp) == -1) + { + perror ("SNDCTL_DSP_SPEED"); + exit (-1); + } + + printf ("Sample rate set to %d\n", tmp); + + if (ioctl (fd, SNDCTL_DSP_GETCAPS, &caps) == -1) + { + perror ("/dev/dsp"); + fprintf (stderr, "Sorry but your sound driver is too old\n"); + exit (-1); + } + + if (!(caps & PCM_CAP_TRIGGER)) + { + fprintf (stderr, "Sorry but your soundcard can't do this (TRIGGER)\n"); + exit (-1); + } + + if (!(caps & PCM_CAP_MMAP)) + { + fprintf (stderr, "Sorry but your soundcard can't do this (MMAP)\n"); + exit (-1); + } + +/* + * Compute total size of the buffer. It's important to use this value + * in mmap() call. + */ + + if (ioctl (fd, SNDCTL_DSP_GETOSPACE, &info) == -1) + { + perror ("GETOSPACE"); + exit (-1); + } + + sz = info.fragstotal * info.fragsize; + fsz = info.fragsize; + +/* + * Call mmap(). + * + * IMPORTANT NOTE!!!!!!!!!!! + * + * Full duplex audio devices have separate input and output buffers. + * It is not possible to map both of them at the same mmap() call. The buffer + * is selected based on the prot argument in the following way: + * + * - PROT_READ (alone) selects the input buffer. + * - PROT_WRITE (alone) selects the output buffer. + * - PROT_WRITE|PROT_READ together select the output buffer. This combination + * is required in BSD to make the buffer accessible. With just PROT_WRITE + * every attempt to access the returned buffer will result in segmentation/bus + * error. PROT_READ|PROT_WRITE is also permitted in Linux with OSS version + * 3.8-beta16 and later (earlier versions don't accept it). + * + * Non duplex devices have just one buffer. When an application wants to do both + * input and output it's recommended that the device is closed and re-opened when + * switching between modes. PROT_READ|PROT_WRITE can be used to open the buffer + * for both input and output (with OSS 3.8-beta16 and later) but the result may be + * unpredictable. + */ + + if ((buf = + mmap (NULL, sz, PROT_WRITE, MAP_FILE | MAP_SHARED, fd, + 0)) == (caddr_t) - 1) + { + perror ("mmap (write)"); + exit (-1); + } + printf ("mmap (out) returned %08lx\n", (long) buf); + op = buf; + +/* + * op contains now a pointer to the DMA buffer. Preload some audio data. + */ + + num_samples = sz / 4; + produce_output ((short *) op, 0, num_samples); + app_p = 0; + +/* + * Then it's time to start the engine. The driver doesn't allow read() and/or + * write() when the buffer is mapped. So the only way to start operation is + * to togle device's enable bits. First set them off. Setting them on enables + * recording and/or playback. + */ + + tmp = 0; + ioctl (fd, SNDCTL_DSP_SETTRIGGER, &tmp); + printf ("Trigger set to %08x\n", tmp); + +/* + * It might be usefull to write some data to the buffer before starting. + */ + + tmp = PCM_ENABLE_OUTPUT; + ioctl (fd, SNDCTL_DSP_SETTRIGGER, &tmp); + printf ("Trigger set to %08x\n", tmp); + +/* + * The machine is up and running now. Use SNDCTL_DSP_GETOPTR to get the + * buffer status. + * + * NOTE! The driver empties each buffer fragmen after they have been + * played. This prevents looping sound if there are some performance problems + * in the application side. For similar reasons it recommended that the + * application uses some amout of play ahead. It can rewrite the unplayed + * data later if necessary. + */ + + while (1) + { + usleep (50 * 1000); + + if (ioctl (fd, SNDCTL_DSP_GETOPTR, &ci) == -1) + { + perror ("SNDCTL_DSP_GETOPTR"); + exit (-1); + } + + device_p = ci.ptr / 4; + + if (device_p < app_p) + { + produce_output ((short *) op, app_p, num_samples - app_p); + app_p = 0; + } + + if (device_p > app_p) + { + produce_output ((short *) op, app_p, device_p - app_p); + app_p = device_p; + } + } + + exit (0); +} diff --git a/tutorials/sndkit/samples/playtgt.c b/tutorials/sndkit/samples/playtgt.c new file mode 100644 index 0000000..b216dd1 --- /dev/null +++ b/tutorials/sndkit/samples/playtgt.c @@ -0,0 +1,93 @@ +/* + * Purpose: A sample program for play target selection + * Copyright (C) 4Front Technologies, 2002-2007. Released under GPLv2/CDDL. + * + * Description: + * This program demonstrates the new ioctl call interface used to + * control the play target selection. + * + * The first command line argument is the audio device (/dev/dsp#). If there + * are no other arguments then the available choices will be printed. If the + * second argument is "-" then the current setting will be printed. + * Finally the source can be changed by giving it's name as the + * second argument. + * + * {!notice Please not that the change may stay in effect even after closing + * the device. However equally well it's possible that the device returns back + * to some default source. There is no way to predict how the device will + * behave and the application must not expect any particular behaviour.} + */ +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <soundcard.h> +#include <time.h> + +int +main (int argc, char *argv[]) +{ + int fd, i, src; + oss_mixer_enuminfo ei; + + if (argc < 2) + { + fprintf (stderr, "Usage: %s dspdev\n", argv[0]); + exit (-1); + } + + if ((fd = open (argv[1], O_WRONLY, 0)) == -1) + { + perror (argv[1]); + exit (-1); + } + + if (ioctl (fd, SNDCTL_DSP_GET_PLAYTGT_NAMES, &ei) == -1) + { + perror ("SNDCTL_DSP_GET_PLAYTGT_NAMES"); + exit (-1); + } + + if (argc == 2) + { + for (i = 0; i < ei.nvalues; i++) + printf ("Play target #%d = '%s'\n", i, ei.strings + ei.strindex[i]); + exit (0); + } + + if (strcmp (argv[2], "?") == 0 || strcmp (argv[2], "-") == 0) + { + if (ioctl (fd, SNDCTL_DSP_GET_PLAYTGT, &src) == -1) + { + perror ("SNDCTL_DSP_GET_PLAYTGT"); + exit (-1); + } + + printf ("Current play target is #%d\n", src); + printf ("Current play target is #%d (%s)\n", + src, ei.strings + ei.strindex[src]); + exit (0); + } + + src = 0; + + for (i = 0; i < ei.nvalues; i++) + { + if (strcmp (argv[2], ei.strings + ei.strindex[i]) == 0) + { + if (ioctl (fd, SNDCTL_DSP_SET_PLAYTGT, &src) == -1) + { + perror ("SNDCTL_DSP_SET_PLAYTGT"); + exit (-1); + } + + exit (0); + } + + src++; + } + + fprintf (stderr, "What?\n"); + exit (-1); +} diff --git a/tutorials/sndkit/samples/recsrc.c b/tutorials/sndkit/samples/recsrc.c new file mode 100644 index 0000000..cc30923 --- /dev/null +++ b/tutorials/sndkit/samples/recsrc.c @@ -0,0 +1,97 @@ +/* + * Purpose: A sample program for recording source selection + * Copyright (C) 4Front Technologies, 2002-2004. Released under GPLv2/CDDL. + * + * Description: + * This program demonstrates the new ioctl call interface used to + * control the recording source. + * + * The same program can be used for playback target selection simply by + * changing O_RDONLY to O_WRONLY in the open call and by replacing all + * "RECSRC" strings with "PLAYTGT" (see playtgt.c). + * + * The first command line argument is the audio device (/dev/dsp#). If there + * are no other arguments then the available choices will be printed. If the + * second argument is "-" then the current setting will be printed. + * Finally the source can be changed by giving it's name as the + * second argument. + * + * {!notice Please not that the change may stay in effect even after closing + * the device. However equally well it's possible that the device returns back + * to some default source. There is no way to predict how the device will + * behave and the application must not expect any particular behaviour.} + */ +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <soundcard.h> +#include <time.h> + +int +main (int argc, char *argv[]) +{ + int fd, i, src; + oss_mixer_enuminfo ei; + + if (argc < 2) + { + fprintf (stderr, "Usage: %s dspdev\n", argv[0]); + exit (-1); + } + + if ((fd = open (argv[1], O_RDONLY, 0)) == -1) + { + perror (argv[1]); + exit (-1); + } + + if (ioctl (fd, SNDCTL_DSP_GET_RECSRC_NAMES, &ei) == -1) + { + perror ("SNDCTL_DSP_GET_RECSRC_NAMES"); + exit (-1); + } + + if (argc == 2) + { + for (i = 0; i < ei.nvalues; i++) + printf ("Rec source #%d = '%s'\n", i, ei.strings + ei.strindex[i]); + exit (0); + } + + if (strcmp (argv[2], "?") == 0 || strcmp (argv[2], "-") == 0) + { + if (ioctl (fd, SNDCTL_DSP_GET_RECSRC, &src) == -1) + { + perror ("SNDCTL_DSP_GET_RECSRC"); + exit (-1); + } + + printf ("Current recording source is #%d\n", src); + printf ("Current recording source is #%d (%s)\n", + src, ei.strings + ei.strindex[src]); + exit (0); + } + + src = 0; + + for (i = 0; i < ei.nvalues; i++) + { + if (strcmp (argv[2], ei.strings + ei.strindex[i]) == 0) + { + if (ioctl (fd, SNDCTL_DSP_SET_RECSRC, &src) == -1) + { + perror ("SNDCTL_DSP_SET_RECSRC"); + exit (-1); + } + + exit (0); + } + + src++; + } + + fprintf (stderr, "What?\n"); + exit (-1); +} diff --git a/tutorials/sndkit/samples/singen.c b/tutorials/sndkit/samples/singen.c new file mode 100644 index 0000000..a9db39a --- /dev/null +++ b/tutorials/sndkit/samples/singen.c @@ -0,0 +1,195 @@ +/* + * Purpose: A simple audio playback program that plays continuous 1 kHz sine wave. + * Copyright (C) 4Front Technologies, 2002-2004. Released under GPLv2/CDDL. + * + * Description: + * This minimalistic program shows how to play udio with OSS. It outputs + * 1000 Hz sinewave signal (based on a 48 step lookup table). + * + * This is pretty much the simpliest possible audio playback program + * one can imagine. It could be possible to make it even simplier + * by removing all error checking but that is in no way recommended. + */ +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <fcntl.h> +#include <soundcard.h> + +int fd_out; +int sample_rate = 48000; + +static void +write_sinewave (void) +{ +/* + * This routine is a typical example of application routine that + * produces audio signal using synthesis. This is actually a very + * basic "wave table" algorithm (btw). It uses precomputed sine + * function values for a complete cycle of a sine function. + * This is much faster than calling the sin() function once for + * each sample. + * + * In other applications this routine can simply be replaced by + * whatever the application needs to do. + */ + + static unsigned int phase = 0; /* Phase of the sine wave */ + unsigned int p; + int i; + short buf[1024]; /* 1024 samples/write is a safe choice */ + + int outsz = sizeof (buf) / 2; + + static int sinebuf[48] = { + + 0, 4276, 8480, 12539, 16383, 19947, 23169, 25995, + 28377, 30272, 31650, 32486, 32767, 32486, 31650, 30272, + 28377, 25995, 23169, 19947, 16383, 12539, 8480, 4276, + 0, -4276, -8480, -12539, -16383, -19947, -23169, -25995, + -28377, -30272, -31650, -32486, -32767, -32486, -31650, -30272, + -28377, -25995, -23169, -19947, -16383, -12539, -8480, -4276 + }; + + for (i = 0; i < outsz; i++) + { +/* + * The sinebuf[] table was computed for 48000 Hz. We will use simple + * sample rate compensation. + * + * {!notice We must prevent the phase variable from groving too large + * because that would cause cause arihmetic overflows after certain time. + * This kind of error posibilities must be identified when writing audio + * programs that could be running for hours or even months or years without + * interruption. When computing (say) 192000 samples each second the 32 bit + * integer range may get overflown very quickly. The number of samples + * played at 192 kHz will cause an overflow after about 6 hours.} + */ + + p = (phase * sample_rate) / 48000; + + phase = (phase + 1) % 4800; + buf[i] = sinebuf[p % 48]; + } + +/* + * Proper error checking must be done when using write. It's also + * important to reporte the error code returned by the system. + */ + + if (write (fd_out, buf, sizeof (buf)) != sizeof (buf)) + { + perror ("Audio write"); + exit (-1); + } +} + +/* + * The open_audio_device opens the audio device and initializes it + * for the required mode. + */ + +static int +open_audio_device (char *name, int mode) +{ + int tmp, fd; + + if ((fd = open (name, mode, 0)) == -1) + { + perror (name); + exit (-1); + } + +/* + * Setup the device. Note that it's important to set the + * sample format, number of channels and sample rate exactly in this order. + * Some devices depend on the order. + */ + +/* + * Set the sample format + */ + tmp = AFMT_S16_NE; /* Native 16 bits */ + if (ioctl (fd, SNDCTL_DSP_SETFMT, &tmp) == -1) + { + perror ("SNDCTL_DSP_SETFMT"); + exit (-1); + } + + if (tmp != AFMT_S16_NE) + { + fprintf (stderr, + "The device doesn't support the 16 bit sample format.\n"); + exit (-1); + } + +/* + * Set the number of channels + */ + tmp = 1; + if (ioctl (fd, SNDCTL_DSP_CHANNELS, &tmp) == -1) + { + perror ("SNDCTL_DSP_CHANNELS"); + exit (-1); + } + + if (tmp != 1) + { + fprintf (stderr, "The device doesn't support mono mode.\n"); + exit (-1); + } + +/* + * Set the sample rate + */ + sample_rate = 48000; + if (ioctl (fd, SNDCTL_DSP_SPEED, &sample_rate) == -1) + { + perror ("SNDCTL_DSP_SPEED"); + exit (-1); + } + +/* + * No need for error checking because we will automatically adjust the + * signal based on the actual sample rate. However most application must + * check the value of sample_rate and compare it to the requested rate. + * + * Small differences between the rates (10% or less) are normal and the + * applications should usually tolerate them. However larger differences may + * cause annoying pitch problems (Mickey Mouse). + */ + + return fd; +} + +int +main (int argc, char *argv[]) +{ +/* + * Use /dev/dsp as the default device because the system administrator + * may select the device using the {!xlink ossctl} program or some other + * methods + */ + char *name_out = "/dev/dsp"; + +/* + * It's recommended to provide some method for selecting some other + * device than the default. We use command line argument but in some cases + * an environment variable or some configuration file setting may be better. + */ + if (argc > 1) + name_out = argv[1]; + +/* + * It's mandatory to use O_WRONLY in programs that do only playback. Other + * modes may cause increased resource (memory) usage in the driver. It may + * also prevent other applications from using the same device for + * recording at the same time. + */ + fd_out = open_audio_device (name_out, O_WRONLY); + + while (1) + write_sinewave (); + + exit (0); +} |