summaryrefslogtreecommitdiff
path: root/tutorials/sndkit/samples
diff options
context:
space:
mode:
Diffstat (limited to 'tutorials/sndkit/samples')
-rw-r--r--tutorials/sndkit/samples/Makefile26
-rw-r--r--tutorials/sndkit/samples/Readme7
-rw-r--r--tutorials/sndkit/samples/audiolevel.c202
-rw-r--r--tutorials/sndkit/samples/dsp_geterror_demo.c154
-rw-r--r--tutorials/sndkit/samples/fulldup.c816
-rw-r--r--tutorials/sndkit/samples/midi.c67
-rw-r--r--tutorials/sndkit/samples/midiin.c55
-rw-r--r--tutorials/sndkit/samples/mixer_applet.c404
-rw-r--r--tutorials/sndkit/samples/mixer_test.txt108
-rw-r--r--tutorials/sndkit/samples/mixext.c270
-rw-r--r--tutorials/sndkit/samples/mixext.readme232
-rw-r--r--tutorials/sndkit/samples/mmap_duplex.c333
-rw-r--r--tutorials/sndkit/samples/mmap_test.c290
-rw-r--r--tutorials/sndkit/samples/playtgt.c93
-rw-r--r--tutorials/sndkit/samples/recsrc.c97
-rw-r--r--tutorials/sndkit/samples/singen.c195
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);
+}