summaryrefslogtreecommitdiff
path: root/tutorials/sndkit/samples/mixer_applet.c
diff options
context:
space:
mode:
Diffstat (limited to 'tutorials/sndkit/samples/mixer_applet.c')
-rw-r--r--tutorials/sndkit/samples/mixer_applet.c404
1 files changed, 404 insertions, 0 deletions
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);
+}