summaryrefslogtreecommitdiff
path: root/tutorials
diff options
context:
space:
mode:
Diffstat (limited to 'tutorials')
-rw-r--r--tutorials/.nomake0
-rw-r--r--tutorials/drivers/myossdev0/.config25
-rw-r--r--tutorials/drivers/myossdev0/.devices18
-rw-r--r--tutorials/drivers/myossdev0/.params11
-rw-r--r--tutorials/drivers/myossdev0/README8
-rw-r--r--tutorials/drivers/myossdev0/myossdev.c143
-rw-r--r--tutorials/drivers/myossdev0/myossdev.h39
-rw-r--r--tutorials/sndkit/.depend0
-rw-r--r--tutorials/sndkit/.nomake0
-rw-r--r--tutorials/sndkit/Makefile9
-rw-r--r--tutorials/sndkit/README62
-rw-r--r--tutorials/sndkit/dsp/Makefile31
-rw-r--r--tutorials/sndkit/dsp/Readme13
-rw-r--r--tutorials/sndkit/dsp/help.c75
-rw-r--r--tutorials/sndkit/dsp/recplay.c401
-rw-r--r--tutorials/sndkit/dsp/str/Makefile34
-rw-r--r--tutorials/sndkit/dsp/str/Readme142
-rw-r--r--tutorials/sndkit/dsp/str/str.c295
-rw-r--r--tutorials/sndkit/dsp/str/str.h74
-rw-r--r--tutorials/sndkit/dsp/str/strplay.c575
-rw-r--r--tutorials/sndkit/dsp/vplay/Makefile20
-rw-r--r--tutorials/sndkit/dsp/vplay/Readme69
-rw-r--r--tutorials/sndkit/dsp/vplay/etc.magic.vplay16
-rw-r--r--tutorials/sndkit/dsp/vplay/fmtheaders.h108
-rw-r--r--tutorials/sndkit/dsp/vplay/vplay.c1012
-rw-r--r--tutorials/sndkit/dsp/vplay_vxworks/Makefile12
-rw-r--r--tutorials/sndkit/dsp/vplay_vxworks/fmtheaders.h119
-rw-r--r--tutorials/sndkit/dsp/vplay_vxworks/vplay_vxworks.c694
-rw-r--r--tutorials/sndkit/libossmix_demo/Makefile13
-rw-r--r--tutorials/sndkit/libossmix_demo/README5
-rw-r--r--tutorials/sndkit/libossmix_demo/ossmixlib_demo.c272
-rw-r--r--tutorials/sndkit/morse/Makefile27
-rw-r--r--tutorials/sndkit/morse/Vvcw1801
-rw-r--r--tutorials/sndkit/morse/charlist.h96
-rw-r--r--tutorials/sndkit/morse/loadcw.c163
-rw-r--r--tutorials/sndkit/morse/morse.c316
-rw-r--r--tutorials/sndkit/morse/morse2.c578
-rw-r--r--tutorials/sndkit/morse/morse3.c572
-rw-r--r--tutorials/sndkit/morse/randomcw2
-rw-r--r--tutorials/sndkit/morse/testgen.c65
-rw-r--r--tutorials/sndkit/ossmplay/Makefile31
-rw-r--r--tutorials/sndkit/ossmplay/mlib.c903
-rw-r--r--tutorials/sndkit/ossmplay/mlib.h87
-rw-r--r--tutorials/sndkit/ossmplay/ossmplay.c233
-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
-rw-r--r--tutorials/sndkit/sblive/Makefile53
-rw-r--r--tutorials/sndkit/sblive/README12
-rw-r--r--tutorials/sndkit/sblive/asm10k.c1206
-rw-r--r--tutorials/sndkit/sblive/emu10k.dsp378
-rw-r--r--tutorials/sndkit/sblive/emu10k1.mac51
-rw-r--r--tutorials/sndkit/sblive/emu10k2.mac71
-rwxr-xr-xtutorials/sndkit/sblive/emuasm13
-rwxr-xr-xtutorials/sndkit/sblive/emuasm23
-rw-r--r--tutorials/sndkit/sblive/equalizer.mac117
-rw-r--r--tutorials/sndkit/sblive/ld10k.c220
-rw-r--r--tutorials/sndkit/sblive/mkheader.c37
-rw-r--r--tutorials/sndkit/sblive/null.dsp23
-rw-r--r--tutorials/sndkit/sblive/testgen.c92
-rw-r--r--tutorials/sndkit/sblive/vu.mac27
-rw-r--r--tutorials/sndkit/sblive/writegpr.c48
-rw-r--r--tutorials/sndkit/softsynth/Makefile23
-rw-r--r--tutorials/sndkit/softsynth/README16
-rw-r--r--tutorials/sndkit/softsynth/softsynth.c651
-rw-r--r--tutorials/sndkit/softsynth/softsynth_gtk.c869
-rw-r--r--tutorials/sndkit/tests/Makefile11
-rw-r--r--tutorials/sndkit/tests/README.1ST11
-rw-r--r--tutorials/sndkit/tests/ioctl_test.c624
-rw-r--r--tutorials/sndkit/tests/iosync.c232
-rw-r--r--tutorials/sndkit/tests/multich16.c126
-rw-r--r--tutorials/sndkit/tests/multich32.c126
-rw-r--r--tutorials/sndkit/tests/musicin.c222
-rw-r--r--tutorials/sndkit/tests/seltest2.c113
-rw-r--r--tutorials/sndkit/tests/singen.inc38
-rw-r--r--tutorials/sndkit/tests/spdif_in_debug.c303
-rw-r--r--tutorials/sndkit/tests/sweepdown.c126
-rw-r--r--tutorials/sndkit/tests/sweepup.c122
-rw-r--r--tutorials/sndkit/tests/synctest.c96
-rw-r--r--tutorials/sndkit/userdev_demo/Makefile6
-rw-r--r--tutorials/sndkit/userdev_demo/udserver.c285
94 files changed, 18859 insertions, 0 deletions
diff --git a/tutorials/.nomake b/tutorials/.nomake
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tutorials/.nomake
diff --git a/tutorials/drivers/myossdev0/.config b/tutorials/drivers/myossdev0/.config
new file mode 100644
index 0000000..57d63df
--- /dev/null
+++ b/tutorials/drivers/myossdev0/.config
@@ -0,0 +1,25 @@
+#
+# The .config file is optional. All drivers will inherit the config
+# settings of the parent environment. The default device driver settings are
+# suitable for PCI devices that work under any operating system and any
+# processor architecture.
+#
+# Let's assume this sample driver is for a sound chip that is only used
+# on PC motherboards. Since it's known that it will not be present in
+# machines having Sparc or PPC processor it doesn't make any sense to
+# ship the driver in OSS packages for other processor architectures.
+# The platform=i86pc does the trick. i86pc means systems based on the
+# IA32 (AKA x86, i386..i686) architecture and it's 64 bit variants such as
+#x86_64 (AMD64, EMT64).
+#
+platform=i86pc
+#
+#
+# It is also possible to make the driver available only for a given operating
+# system for example if it depends on some kernel services not available
+# anywhere else. This can be done using the targetos keyword. For example
+# targetos=Linux and/or targetos=Solaris.
+#
+# If the driver is known to be incompatible with certain operating system
+# it's possible to exclude it using the forfetos keyword. For example
+# forgetos=Linux.
diff --git a/tutorials/drivers/myossdev0/.devices b/tutorials/drivers/myossdev0/.devices
new file mode 100644
index 0000000..4f21f1c
--- /dev/null
+++ b/tutorials/drivers/myossdev0/.devices
@@ -0,0 +1,18 @@
+#
+# Devices supported by the myossdev driver
+#
+# The lines beginning with '#' in column 1 are comments. Using '#' in any
+# other column is not permitted. Also empty lines are illegal.
+#
+# The pci666,777 entry refers to the primary PCI vendor/device ID with
+# vendor=0x666 and device=0x777. All devices with this ID will be mapped
+# to this driver.
+myossdev pci666,777 Generic Acme Laboratories Evil Audio device
+#
+# PCI subdevice IDs can be used to identify specific models of given device.
+# In most cases this is unnecessary but it makes it possible to show more
+# meaningful device names in possible configuration utilities. For example
+# pcs666,111 means PCI device with subvendor ID of 0x666 and subdevice ID
+# of 111.
+myossdev pcs666,111 Acme Laboratories Evil Audio basic
+myossdev pcs666,999 Acme Laboratories Evil Audio Super Deluxe Turbo
diff --git a/tutorials/drivers/myossdev0/.params b/tutorials/drivers/myossdev0/.params
new file mode 100644
index 0000000..6ee68b3
--- /dev/null
+++ b/tutorials/drivers/myossdev0/.params
@@ -0,0 +1,11 @@
+/*
+ * The initial comment is optional and gives information common to all config
+ * options.
+ */
+int myossdev_verbose = 0;
+/*
+ * Setting myossdev0_verbose to 1 enables various debugging messages
+ * displayed by the driver. This option can be used when debugging problems
+ * with the device.
+ */
+
diff --git a/tutorials/drivers/myossdev0/README b/tutorials/drivers/myossdev0/README
new file mode 100644
index 0000000..69b5c95
--- /dev/null
+++ b/tutorials/drivers/myossdev0/README
@@ -0,0 +1,8 @@
+This is the step 0 of the sample OSS driver project. This driver
+has support for loading/unloading (attach/detach) and nothing else.
+More features will be added in the subsequent steps.
+
+To compile this driver it must be copied to a directory with
+proper name. So you need to create kernel/drv/myossdev (inside the OSS
+source tree) and copy contents of this directory to it. Then follow the
+instructions for compiling and installing OSS.
diff --git a/tutorials/drivers/myossdev0/myossdev.c b/tutorials/drivers/myossdev0/myossdev.c
new file mode 100644
index 0000000..fbe5ce5
--- /dev/null
+++ b/tutorials/drivers/myossdev0/myossdev.c
@@ -0,0 +1,143 @@
+/*
+ * Purpose: Driver for the ACME Laboratories Evil Audio family of sound cards
+ * Description:
+ * This description section contains general information about the driver
+ * and the name and contact information of author and maintainer of the code.
+ *
+ * _NO_ copyright statement must be given in the comments since all drivers
+ * to be included in the OSS source tree must be compatible with the copying
+ * policy of the rest of OSS.
+ */
+
+/*
+ * Create a #define for the copying conditions. This define must be
+ * as above. Only the name of the copyright owner and the year(s) can be
+ * changed. This definition will be replaced by the actual OSS copyright
+ * statement by the build tools.
+ */
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+
+/*
+ * The _cfg.h file for the driver must be included before anything else
+ * by all OS drivers. This configuration file will be automatically generated
+ * based on the configuration files and operating system requirements.
+ */
+#include "myossdev_cfg.h"
+
+/*
+ * If the driver is split to multiple C source files then you should create
+ * a common header file for the driver and include it here.
+ */
+
+#include "myossdev.h"
+
+/*
+ * Drivers for PCI devices need to include <oss_pci.h>. Other drivers should
+ * not include it.
+ */
+#include <oss_pci.h>
+
+
+/*
+ * The attach routine for the device. This routine will be called once for
+ * each device instance found from the system. The osdev parameter is a handle
+ * to the device that will be needed by many subsequent OSS core functions.
+ */
+int
+myossdev_attach (oss_device_t * osdev)
+{
+ myossdev_devc_t *devc;
+
+ /*
+ * Some debugging printout. All OSS drivers must use cmn_err() instead
+ * of printf(), printk() or something else to print debugging and error
+ * messages.
+ *
+ * The cmn_err() call can be oput inside a DDB() macro. In this way the
+ * message will only be printed if OSS was started with detect_trace=1
+ * configuration option.
+ */
+ DDB (cmn_err (CE_CONT, "myossdev_attach called\n"));
+
+ /*
+ * First create the devc structure and initialize it to zeroes.
+ * The PMALLOC macro will allocate memory that will be automatically
+ * released when OSS gets unloaded. This kind of memory should be used
+ * for structures that will stay in use until the driver is unloaded.
+ */
+
+ if ((devc = PMALLOC (osdev, sizeof (*devc))) == NULL)
+ {
+ cmn_err (CE_WARN, "Cannot allocate devc\n");
+ return 0; /* Failure */
+ }
+
+ memset (devc, 0, sizeof (*devc));
+
+ /*
+ * Drivers must initialize osdev to have a link to the devc structure.
+ * Without this link the driver cannot be unloaded.
+ */
+ osdev->devc = devc;
+
+ /*
+ * Init the devc fields and create the mutex. Register the device.
+ */
+
+ devc->osdev = osdev;
+ MUTEX_INIT (devc->osdev, devc->mutex);
+ oss_register_device (osdev, "ACME Labs Evil Audio");
+
+ /*
+ * This is all for now. More will follow in the next episode.
+ */
+
+ return 1; /* Success */
+}
+
+/*
+ * Device detach routine. Note that this routine will be called once for
+ * each instance.
+ */
+
+int
+myossdev_detach (oss_device_t * osdev)
+{
+ myossdev_devc_t *devc = osdev->devc;
+ int err;
+
+ DDB (cmn_err (CE_CONT, "myossdev_detach called\n"));
+
+/*
+ * Check if the device is not busy.
+ */
+ if ((err = oss_disable_device (osdev)) < 0)
+ {
+ cmn_err (CE_WARN, "Cannot detach %s\n", osdev->nick);
+ return 0;
+ }
+
+ /*
+ * Uninitialize the instance variables. Do not try to free any
+ * memory allocated with PMALLOC().
+ */
+
+ MUTEX_CLEANUP (devc->mutex); /* Mutexes must be cleared */
+
+ /*
+ * Finally mark the device as inactive
+ */
+ oss_unregister_device (osdev);
+
+ return 1; /* Ok */
+}
diff --git a/tutorials/drivers/myossdev0/myossdev.h b/tutorials/drivers/myossdev0/myossdev.h
new file mode 100644
index 0000000..8c631fb
--- /dev/null
+++ b/tutorials/drivers/myossdev0/myossdev.h
@@ -0,0 +1,39 @@
+/*
+ * Purpose: Common definitions for the ACME Labs Evil Audio driver.
+ *
+ * The intial comment usually doesn't contain much information.
+ */
+
+/*
+ * As in the C sources you need to include a placeholder define for the
+ * copyright notice. To avoid getting multiple define warnings for the COPYING
+ * macro the header files should use macro name like COPYING2..COPYING9.
+ */
+
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+
+/*
+ * Each device instance should have a per-device data structure that contains
+ * variables common to all sub-devices of the card. By convenntion this
+ * structure is called devc. Driver designers may use different terminology.
+ * However use of devc is highly recomended in all OSS drivers because it
+ * will make maintenance of the code easier.
+ */
+
+typedef struct _myossdev_devc_t *myoss_devc_t;
+
+struct _myossdev_devc_t
+{
+ oss_device_t *osdev; /* A handle to the device given by the OSS core. */
+ oss_mutex_t *mutex; /* A lock/mutex variable for the device */
+};
diff --git a/tutorials/sndkit/.depend b/tutorials/sndkit/.depend
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tutorials/sndkit/.depend
diff --git a/tutorials/sndkit/.nomake b/tutorials/sndkit/.nomake
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tutorials/sndkit/.nomake
diff --git a/tutorials/sndkit/Makefile b/tutorials/sndkit/Makefile
new file mode 100644
index 0000000..75ac7a1
--- /dev/null
+++ b/tutorials/sndkit/Makefile
@@ -0,0 +1,9 @@
+TOPDIR=../..
+
+include $(TOPDIR)/make.defs
+
+SUBDIRS=dsp morse tests samples sblive softsynth userdev_demo
+
+all: subdirs
+
+clean: clean_local clean_subdirs
diff --git a/tutorials/sndkit/README b/tutorials/sndkit/README
new file mode 100644
index 0000000..9d0264c
--- /dev/null
+++ b/tutorials/sndkit/README
@@ -0,0 +1,62 @@
+Release notes for the sndkit collection
+=======================================
+
+This directory contains the sources for a set of utilities
+included in the OSS package. In addition there is an unsorted collection
+of many kind of programming experiments. Some of them don't even work.
+
+We have released these sources just because there is no reason to not to
+release them. We hope that somebody finds them interesting.
+
+There is no documentation available for these sources at this moment. They
+will be officially released at the some time with OSS 4.0. Most of the files
+are created to test recent OSS versions os they will not work with older
+implementations such as the kernel freeware OSS drivers (OSS/Free) or
+ALSA.
+
+Some of these programs do things that are not necessary in majority of
+OSS applications. Please refers to the OSS Programmer's Guide for
+proper usage of the API (http://www.opensound.com/pguide).
+
+Most programs have been written by 4Front Technologies but some of the
+oldest ones have different origins.
+
+Please contact hannu@opensound.com if you have any questions.
+
+**********************************************************************
+All these programs are released under GNU Public License (GPL) version
+2.0 or CDDL (if you prefer it). However there are few files that have
+a different license. In that case the copying conditions will be
+mentioned in the beginning of the files.
+**********************************************************************
+
+Brief description of the directories
+------------------------------------
+
+To compile most of the programs just go to the directory ane execute "make"
+or "make install". Some of the programs need to be compiled by hand. Use
+"make install" in the main directory to compile and install all of them.
+
+dsp Very very old utilities. Many of them are still functional.
+
+ossmixer.c The "reference implementation" of an OSS mixer program. This
+program uses the traditional OSS mixer API which is very limited.
+Superseded by the ossmix/ and ossxmix/ programs.
+
+ossmplay A MIDI player that uses the /dev/music API.
+
+The ossmix and ossxmix programs use the OSS mixer extension API that is to
+be officially released in OSS 4.0. Some minor changes are still planned to
+this API so please keep this in mind if using these sources.
+
+ossmix A command line OSS mixer.
+ossxmix A GTK+ based mixer/control panel program.
+ossrec A simple command line .wav file recording applet.
+ossplay A command line audio file player (for most file formats)
+samples Few OSS programming sample programs.
+awesfx SB AWE SoundFont Loader for EMU8000 synth.
+sblive DSP compiler/loader and .DSP file sources for the SB Live/Audigy
+ driver of OSS. May be very impossible to understand without
+ documentation. Sorry for the time being.
+tests Various test programs used to test this and that during
+ development of OSS. Can be used as programming examples.
diff --git a/tutorials/sndkit/dsp/Makefile b/tutorials/sndkit/dsp/Makefile
new file mode 100644
index 0000000..1cfa1ec
--- /dev/null
+++ b/tutorials/sndkit/dsp/Makefile
@@ -0,0 +1,31 @@
+#CC = gcc
+#CFLAGS = -O6 -m486 -funroll-loops -Wall
+CFLAGS = -O -I../../../include
+#LD = gcc
+LD = cc
+#LDFLAGS = -s -N
+LDFLAGS = -s
+
+INSTALLDIR = /usr/local/bin
+
+.c.o:
+# $(CC) -c $(CFLAGS) -o $*.o $<
+ $(CC) -c $(CFLAGS) $<
+
+all: srec
+
+install: all
+ cp srec $(INSTALLDIR)
+ ln -sf $(INSTALLDIR)/srec $(INSTALLDIR)/splay
+ chown root $(INSTALLDIR)/splay $(INSTALLDIR)/srec
+ chmod 755 $(INSTALLDIR)/splay $(INSTALLDIR)/srec
+ cd str;make install
+
+srec: recplay.o help.o
+ $(LD) $(LDFLAGS) recplay.o help.o -o srec
+
+clean:
+ rm -f $(OBJS) *.o srec a.out core
+ cd str;make clean
+
+recplay.o: recplay.c
diff --git a/tutorials/sndkit/dsp/Readme b/tutorials/sndkit/dsp/Readme
new file mode 100644
index 0000000..9410464
--- /dev/null
+++ b/tutorials/sndkit/dsp/Readme
@@ -0,0 +1,13 @@
+srec and splay utilities (recplay.c)
+====================================
+
+Run "make install" to install "splay" and "srec" into /usr/local/bin.
+
+These programs support recording and playback of digitized
+voice. There is several possible parameters to these programs.
+Try "srec -h" and "splay -h" for further information.
+
+The vplay -program in the vplay directory is a improved version of
+the recplay.c.
+
+NOTE! The str program has been moved to the subdirectory ./str
diff --git a/tutorials/sndkit/dsp/help.c b/tutorials/sndkit/dsp/help.c
new file mode 100644
index 0000000..5b0156c
--- /dev/null
+++ b/tutorials/sndkit/dsp/help.c
@@ -0,0 +1,75 @@
+/*
+ * help.c
+ *
+ * Some error printing routines.
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+#include <stdio.h>
+#include <errno.h>
+
+void
+describe_error (void)
+{
+ switch (errno)
+ {
+ case ENODEV:
+ fprintf (stderr, "The device file was found in /dev but\n"
+ "OSS is not loaded. You need to load it by executing\n"
+ "the soundon command.\n");
+ break;
+
+ case ENXIO:
+ fprintf (stderr, "There are no sound devices available.\n"
+ "The most likely reason is that the device you have\n"
+ "is malfunctioning or it's not supported by OSS.\n"
+ "\n"
+
+ "NOTE! It may be necessary to reboot the system after installing OSS\n"
+#ifdef LICENSED_VERSION
+ "\n"
+ "If you are a licensed customer then please fill the problem report at\n"
+ "http://www.opensound.com/support.cgi\n"
+#endif
+ );
+ break;
+
+ case ENOSPC:
+ fprintf (stderr, "Your system cannot allocate memory for the device\n"
+ "buffers. Reboot your machine and try again.\n");
+ break;
+
+ case ENOENT:
+ fprintf (stderr, "The device file is missing from /dev.\n"
+ "Perhaps you have not installed and started Open Sound System yet\n");
+ break;
+
+
+ case EBUSY:
+ fprintf (stderr, "The device is busy. There is some other application\n"
+ "using it.\n");
+
+ default:;
+ }
+}
diff --git a/tutorials/sndkit/dsp/recplay.c b/tutorials/sndkit/dsp/recplay.c
new file mode 100644
index 0000000..62fdcf0
--- /dev/null
+++ b/tutorials/sndkit/dsp/recplay.c
@@ -0,0 +1,401 @@
+/*
+ * recplay.c
+ *
+ * A simple recording and playback program for OSS 4.0
+ * or later.
+ *
+ * Copyright by Hannu Savolainen 1993-2004
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+/* #include <getopt.h> */
+#include <fcntl.h>
+#include <string.h>
+#include <soundcard.h>
+
+#define DEFAULT_DSP_SPEED 8000
+
+#define RECORD 0
+#define PLAY 1
+#define AUDIO "/dev/dsp"
+
+int prof = APF_NORMAL;
+int timelimit = 0, dsp_speed = DEFAULT_DSP_SPEED, dsp_channels = 1;
+int samplefmt = AFMT_S16_LE;
+int quiet_mode = 0;
+int audio, abuf_size;
+int direction, omode;
+int blksize = 0;
+int fragsize = 0;
+char *audiobuf, c;
+
+char audio_name[64] = AUDIO;
+
+void recplay (char *name);
+extern void describe_error (void);
+
+typedef struct
+{
+ char *name;
+ int fmt;
+ int bits;
+} sample_fmt_t;
+
+static const sample_fmt_t formats[] = {
+ {"AFMT_MU_LAW", AFMT_MU_LAW},
+ {"AFMT_A_LAW", AFMT_A_LAW},
+ {"AFMT_IMA_ADPCM", AFMT_IMA_ADPCM},
+ {"AFMT_U8", AFMT_U8, 8},
+ {"AFMT_S16_LE", AFMT_S16_LE, 16},
+ {"AFMT_S16_BE", AFMT_S16_BE, 16},
+ {"AFMT_S8", AFMT_S8, 8},
+ {"AFMT_U16_LE", AFMT_U16_LE, 16},
+ {"AFMT_U16_BE", AFMT_U16_BE, 16},
+ {"AFMT_MPEG", AFMT_MPEG},
+ {"AFMT_AC3", AFMT_AC3},
+ {"AFMT_VORBIS", AFMT_VORBIS},
+ {"AFMT_S32_LE", AFMT_S32_LE, 32},
+ {"AFMT_S32_BE", AFMT_S32_BE, 32},
+ {"AFMT_FLOAT", AFMT_FLOAT},
+ {"AFMT_S24_LE", AFMT_S24_LE, 24},
+ {"AFMT_S24_BE", AFMT_S24_BE, 24},
+ {"AFMT_SPDIF_RAW", AFMT_SPDIF_RAW},
+ {"AFMT_S24_PACKED", AFMT_S24_PACKED, 24},
+ {NULL, 0}
+};
+
+static int
+set_samplefmt (char *fmt)
+{
+ int i;
+
+ for (i = 0; formats[i].name != NULL; i++)
+ if (strcmp (formats[i].name, fmt) == 0)
+ {
+ return formats[i].fmt;
+ }
+
+ fprintf (stderr, "Error: Unrecognized sample format '%s'\n", fmt);
+ exit (-1);
+}
+
+int
+main (int argc, char *argv[])
+{
+
+ char *command;
+ int tmp;
+ int i;
+
+ command = argv[0];
+ if (strstr (argv[0], "srec"))
+ {
+ direction = RECORD;
+ omode = O_RDONLY;
+ }
+ else if (strstr (argv[0], "splay"))
+ {
+ direction = PLAY;
+ omode = O_WRONLY;
+ }
+ else
+ {
+ fprintf (stderr,
+ "Error: command should be named either srec or splay\n");
+ exit (1);
+ }
+
+ while ((c = getopt (argc, argv, "pqs:St:b:d:B:f:F:c:")) != EOF)
+ switch (c)
+ {
+ case 'S': /* Stereo is obsolete - use -c instead */
+ dsp_channels = 2;
+ break;
+ case 'c':
+ dsp_channels = atoi (optarg);
+ break;
+ case 'q':
+ quiet_mode = 1;
+ break;
+ case 's':
+ dsp_speed = atoi (optarg);
+ if (dsp_speed < 300)
+ dsp_speed *= 1000;
+ break;
+ case 't':
+ timelimit = atoi (optarg);
+ break;
+
+ case 'b': /* Bits (obsolete) supports only 8 and 16 */
+ samplefmt = atoi (optarg);
+ break;
+
+ case 'F':
+ samplefmt = set_samplefmt (optarg);
+ break;
+
+ case 'B':
+ blksize = atoi (optarg);
+ break;
+
+ case 'd':
+ strncpy (audio_name, optarg, sizeof (audio_name) - 1);
+ break;
+
+ case 'f':
+ fragsize = atoi (optarg);
+ if (fragsize == 0)
+ {
+ fprintf (stderr, "Bad fragment size (-f %s)\n", optarg);
+ exit (-1);
+ }
+ fragsize |= 0x7fff0000;
+ break;
+
+ case 'p':
+ prof = APF_CPUINTENS;
+ break;
+
+ default:
+ fprintf (stderr,
+ "Usage: %s [-q] [-c channels] [-t secs] [-s Hz] [-b 8|12|16] [-d device] [filename]\n",
+ command);
+ exit (-1);
+ }
+
+
+ audio = open (audio_name, omode, 0);
+ if (audio == -1)
+ {
+ perror (audio_name);
+ describe_error ();
+ exit (-1);
+ }
+
+ if (fragsize != 0)
+ if (ioctl (audio, SNDCTL_DSP_SETFRAGMENT, &fragsize) == -1)
+ {
+ perror ("SETFRAGMENT");
+ exit (-1);
+ }
+
+ tmp = samplefmt;
+ ioctl (audio, SNDCTL_DSP_SETFMT, &tmp);
+ if (tmp != samplefmt)
+ {
+ fprintf (stderr, "Unable to set the requested sample format\n");
+
+ for (i = 0; formats[i].fmt != 0; i++)
+ if (formats[i].fmt == samplefmt)
+ {
+ fprintf (stderr, "The required format is %s (%08x)\n",
+ formats[i].name, formats[i].fmt);
+ break;
+ }
+
+ for (i = 0; formats[i].fmt != 0; i++)
+ if (formats[i].fmt == tmp)
+ {
+ fprintf (stderr, "The device supports %s (%08x)\n",
+ formats[i].name, formats[i].fmt);
+ break;
+ }
+ exit (-1);
+ }
+
+ ioctl (audio, SNDCTL_DSP_PROFILE, &prof);
+
+ if (ioctl (audio, SNDCTL_DSP_CHANNELS, &dsp_channels) == -1)
+ {
+ fprintf (stderr, "%s: Unable to set the number of channels\n", command);
+ perror (audio_name);
+ exit (-1);
+ }
+
+ if (ioctl (audio, SNDCTL_DSP_SPEED, &dsp_speed) == -1)
+ {
+ fprintf (stderr, "%s: Unable to set audio speed\n", command);
+ perror (audio_name);
+ exit (-1);
+ }
+ if (!quiet_mode)
+ {
+ fprintf (stderr, "Speed %d Hz ", dsp_speed);
+ fprintf (stderr, "%d channels ", dsp_channels);
+
+ for (i = 0; formats[i].fmt != 0; i++)
+ if (formats[i].fmt == samplefmt)
+ {
+ fprintf (stderr, "Sample format is %s", formats[i].name);
+ if (formats[i].bits != 0)
+ fprintf (stderr, "(%d bits)", formats[i].bits);
+ break;
+ }
+ fprintf (stderr, "\n");
+ }
+
+ if (blksize > 0)
+ abuf_size = blksize;
+ else
+ {
+#if 0
+/*
+ * There is no idea in using SNDCTL_DSP_GETBLKSIZE in applications like this.
+ * Using some fixed local buffer size will work equally well.
+ */
+ ioctl (audio, SNDCTL_DSP_GETBLKSIZE, &abuf_size);
+ if (abuf_size < 1)
+ {
+ perror ("GETBLKSIZE");
+ exit (-1);
+ }
+#else
+ abuf_size = 1024;
+#endif
+ }
+
+ if ((audiobuf = malloc (abuf_size)) == NULL)
+ {
+ fprintf (stderr, "Unable to allocate input/output buffer\n");
+ exit (-1);
+ }
+
+ if (optind > argc - 1)
+ recplay (NULL);
+ else
+ while (optind <= argc - 1)
+ {
+ recplay (argv[optind++]);
+ }
+
+ close (audio);
+ return 0;
+}
+
+void
+recplay (char *name)
+{
+ int fd, l;
+
+ int count, c;
+
+ if (!timelimit)
+ count = 0x7fffffff;
+ else
+ {
+ count = timelimit * dsp_speed * dsp_channels;
+ if (samplefmt != AFMT_U8)
+ count *= 2; /* TODO: This is bogus because just few formats are 16 bits */
+ }
+
+ if (direction == PLAY)
+ {
+ if (!name)
+ {
+ fd = 0;
+ name = "stdin";
+ }
+ else
+ {
+ if ((fd = open (name, O_RDONLY, 0)) == -1)
+ {
+ perror (name);
+ exit (-1);
+ }
+ }
+
+ while (count)
+ {
+ c = count;
+
+ if (c > abuf_size)
+ c = abuf_size;
+
+ if ((l = read (fd, audiobuf, c)) > 0)
+ {
+ if (write (audio, audiobuf, l) != l)
+ {
+ perror (audio_name);
+ exit (-1);
+ }
+ count -= l;
+ }
+ else
+ {
+ if (l == -1)
+ {
+ perror (name);
+ exit (-1);
+ }
+ count = 0; /* Stop */
+ }
+
+ } /* while (count) */
+ if (fd != 0)
+ close (fd);
+ }
+ else
+ {
+ if (!name)
+ {
+ fd = 1;
+ name = "stdout";
+ }
+ else
+ {
+ if ((fd = open (name, O_WRONLY | O_CREAT, 0666)) == -1)
+ {
+ perror (name);
+ exit (-1);
+ }
+ }
+
+ while (count)
+ {
+ c = count;
+ if (c > abuf_size)
+ c = abuf_size;
+
+ if ((l = read (audio, audiobuf, c)) > 0)
+ {
+ if (write (fd, audiobuf, l) != l)
+ {
+ perror (name);
+ exit (-1);
+ }
+ count -= l;
+ }
+
+ if (l == -1)
+ {
+ perror (audio_name);
+ exit (-1);
+ }
+ } /* While count */
+
+ if (fd != 1)
+ close (fd);
+ }
+}
diff --git a/tutorials/sndkit/dsp/str/Makefile b/tutorials/sndkit/dsp/str/Makefile
new file mode 100644
index 0000000..d0fb544
--- /dev/null
+++ b/tutorials/sndkit/dsp/str/Makefile
@@ -0,0 +1,34 @@
+#CC = gcc
+#CFLAGS = -O6 -m486 -funroll-loops -Wall
+#LD = gcc
+#LDFLAGS = -s -N
+CC = cc
+CFLAGS = -O
+LD = cc
+LDFLAGS = -s
+
+INSTALLDIR = /usr/local/bin
+
+OBJS = str.o strplay.o ../help.o
+
+.c.o:
+# $(CC) -c $(CFLAGS) -o $*.o $<
+ $(CC) -c $(CFLAGS) $<
+
+all: str
+
+install: all
+ cp str $(INSTALLDIR)
+ chown root $(INSTALLDIR)/str
+ chmod 755 $(INSTALLDIR)/str
+ ln -f $(INSTALLDIR)/str $(INSTALLDIR)/str15
+ ln -f $(INSTALLDIR)/str $(INSTALLDIR)/str32
+
+str: $(OBJS)
+ $(LD) $(LDFLAGS) $(OBJS) -o str
+
+clean:
+ rm -f $(OBJS) str a.out core
+
+str.o: str.c str.h
+strplay.o: strplay.c str.h
diff --git a/tutorials/sndkit/dsp/str/Readme b/tutorials/sndkit/dsp/str/Readme
new file mode 100644
index 0000000..1d7b28f
--- /dev/null
+++ b/tutorials/sndkit/dsp/str/Readme
@@ -0,0 +1,142 @@
+SparcTracker ver. 1.2 by Liam Corner and Marc Espie
+===================================================
+
+NOTE! This is an old and obsolete version. The latest version is
+ called tracker-3.19.
+
+#ifdef linux
+This version contains some minor modifications for Linux by Hannu Savolainen
+(hannu@voxware.pp.fi) and Leif Kornstaedt (l_kornst@informatik.uni-kl.de).
+
+Have a look at the options in the Makefile (e.g. the -m486 option) and at
+the str.h header file, in which you may configure some things.
+
+Please edit DSP_SPEED in the file str.h before compiling. The default
+setting for output frequency is 23 kHz, which may be too fast for 386/386sx
+machines. At least you have to change it if there happens to be short
+pauses in playback.
+#endif
+
+Well, here they are the first tracker module players for the Sun
+Sparcstations. There are 2 players, one for the 15 sample modules and one for
+the 32 sample ones (or should that be 31 :-). The code is not that different,
+but I could not be bothered to write an auto detection routine that worked on
+standard input.
+
+
+Usage
+-----
+
+Usage is very simple, either give the player the filename of the module, or if
+the module is compressed(frozen) you can zcat(fcat) and pipe the result into
+the player thus :
+
+ str32 -h
+ str32 [options] module ...
+ zcat module.z | str32 [options]
+ fcat module.F | str32 [options]
+
+If you have a new module and are unsure of which player (str15 or str32) to
+use then try both. If you choose the wrong one then you may get an error
+message or nothing at all will happen (except maybe a core dump :-). For
+this reason I keep my trackers in separate directories so that I know which
+player to use.
+
+Not many effects are implemented, so the result may not be exactly the same as
+an Amiga player, but it is usually OK. The sound quality of the /dev/audio is
+not too hot, so some sounds are lost, usually the bass.
+
+Options
+-------
+
+This version acceps several command line options:
+
+ -s kHz Sets the playback speed. For example
+ str32 -s 20 plays the module with
+ 22 kHz sampling frequency.
+ -S Plays the module in stereo.
+ -c Non-stop mode.
+ -q Quiet.
+ -b bits Sets the sample size (8 or 16 bits).
+ -o file Redirects output to a file or
+ another device.
+
+Errors
+------
+
+The only error not totally self-explanatory is the 'corrupt input file' one.
+This sometimes indicates that you have used the wrong player, but
+occassionally it may be that the module is too short by a few bytes. I have
+only come accross this in a couple of cases and just appending a few null
+bytes to the end usually cures this.
+
+
+The Future
+----------
+
+I may get around to combining the players into a single program and enhancing
+the error detection a bit, but then again I might not :-). If anyone else
+wants to add these functions, add more effects or correct any errors there may
+be in the current programs then please go ahead - I hope the code is
+understandable enough for you.
+
+
+Copyright Stuff
+---------------
+
+Most of the code is copyright me. The convert routine is copyright Rich
+Gopstein and was borrowed from the iff2ulaw utility. The code is freely
+distributable as long as this message and the copyright messages in the source
+are included. You are welcome to update the source code.
+
+
+ Liam Corner - University of Warwick - 1st November 1991
+
+ csubt@csv.warwick.ac.uk
+ zenith@dcs.warwick.ac.uk
+
+
+
+Version 1.2 - 3rd November 1991
+-------------------------------
+
+Thanks to Marc Espie for doing most of the first update. There is now only
+one player str15 and a link from str32 to str15, so you will still have to
+choose the correct player for a given module. There is now output showing
+module name, sample names and progress through the module as it plays. More
+effects are implemented and a bug with the sample repeat loop has been fixed.
+
+
+Patch 08/27/92 by Hannu Savolainen
+----------------------------------
+
+Modified to use integer arithmetic.
+
+
+Version 1.5 - 31st January 1993 by Leif Kornstaedt
+--------------------------------------------------
+
+- Now all standard NoiseTracker 1.0 effects should be implemented correctly,
+ as well as some bugs fixed (and the step_table calculations more accurate).
+
+- Some command line options have been added, namely:
+
+ -v verbose: the score is shown while playing
+ -z zcat is applied to the files before playing
+
+- You can now give a default path to search modules when they are not found
+ in the current working directory. E. g. using /bin/bash do
+
+$ export MODPATH=/usr/local/lib/mod/tracks15:/usr/local/lib/mod/tracks32
+
+
+TODO:
+
+- automatically determine the number of instruments instead of having
+ str15 and str32 (by testing 'M.K.' sig)
+- insert a shuffle mode: let the player play all songs in the module
+ path, but shuffled like a CD player does.
+
+
+Please let me know if you find something to ameliorate. -- L. K.
+l_kornst@informatik.uni-kl.de
diff --git a/tutorials/sndkit/dsp/str/str.c b/tutorials/sndkit/dsp/str/str.c
new file mode 100644
index 0000000..4c999ca
--- /dev/null
+++ b/tutorials/sndkit/dsp/str/str.c
@@ -0,0 +1,295 @@
+/************************************************************************/
+/* */
+/* str.c - plays sound-/noisetracker files on a SparcStation */
+/* */
+/* Authors: */
+/* Liam Corner - zenith@dcs.warwick.ac.uk */
+/* Marc Espie - espie@dmi.ens.fr */
+/* Minor modificatios for Linux by */
+/* Hannu Savolainen - hannu@voxware.pp.fi */
+/* Command-line switches added by */
+/* Muhammad Saggaf - alsaggaf@erl.mit.edu */
+/* Leif Kornstaedt - l_kornst@informatik.uni-kl.de */
+/* Relaxed program name checking */
+/* Craig Metz - cmetz@thor.tjhsst.edu */
+/* Stereo and 16 bit support by */
+/* Hannu */
+/* Some effects and minor bugfixes/ameliorations by */
+/* Leif Kornstaedt - l_kornst@informatik.uni-kl.de */
+/* */
+/* Version: 1.20 - 3 November 1991 / 31 January 1993 */
+/* 01/31/93 effects added */
+/* by Leif Kornstaedt */
+/* 08/27/92 modified to use integer arithmetic */
+/* 08/31/92 SB Pro stereo support */
+/* by Hannu Savolainen */
+/* */
+/************************************************************************/
+
+#include <stdio.h>
+#include <malloc.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/stat.h>
+
+#include <sys/soundcard.h>
+#include "str.h"
+
+/* output file name or dsp device */
+char AUDIO[256] = AUDIONAME;
+
+int audio; /* file handle */
+unsigned char *audiobuf;
+int abuf_size;
+int abuf_ptr;
+
+/* for analyzing the command line: */
+char *command; /* the actual command name used (argv[0]) */
+int loop_forever = FALSE;
+int quiet_mode = FALSE, verbose_mode = FALSE;
+static int use_zcat = FALSE;
+int mute[4] = { FALSE, FALSE, FALSE, FALSE };
+int dsp_stereo = DSP_DEFAULT_STEREO;
+int dsp_samplesize = DSP_DEFAULT_SAMPLESIZE;
+long dsp_speed = DEFAULT_DSP_SPEED;
+
+FILE *fp;
+
+void play_song (void);
+extern void describe_error (void); /* from ../help.c */
+
+void
+usage (command)
+ char *command;
+{
+ fprintf (stderr, "Usage: %s [ -cqvzS1234 -b size -s kHz -o output_file ] "
+ "[ filename ... ]\n", command);
+ fprintf (stderr, "\t-c\t\tplay infinitely\n"
+ "\t-q\t\tquiet mode\n"
+ "\t-v\t\tverbose mode\n"
+ "\t-z\t\tuse zcat on file\n"
+ "\t-1234\t\tmute voices 1, 2, 3 or 4\n"
+ "\t-S\t\ttoggle stereo\n"
+ "\t-b size\t\tset sample size (8 or 16 bits)\n"
+ "\t-s kHz\t\tuse sampling speed kHz\n"
+ "\t-o file\t\tredirect output to a file\n");
+ exit (EXIT_FAILURE);
+}
+
+char *
+find_file (name)
+ char *name;
+{
+ char *dirs;
+ struct stat statbuffer;
+ static char fname[256];
+
+ strcpy (fname, name);
+ if (stat (fname, &statbuffer) != -1 && errno != ENOENT)
+ return fname;
+ else
+ {
+ dirs = getenv ("MODPATH");
+ while (dirs != NULL && strlen (dirs))
+ {
+ if (strchr (dirs, ':') != NULL)
+ {
+ strncpy (fname, dirs, strchr (dirs, ':') - dirs);
+ fname[strchr (dirs, ':') - dirs] = '\0';
+ }
+ else
+ strcpy (fname, dirs);
+ if (fname[strlen (fname) - 1] != '/')
+ strcat (fname, "/");
+ strcat (fname, name);
+ if (stat (fname, &statbuffer) != -1 && errno != ENOENT)
+ return fname;
+ if ((dirs = strchr (dirs, ':')) != NULL)
+ dirs++;
+ }
+ }
+
+ return NULL;
+}
+
+int
+main (argc, argv)
+ int argc;
+ char *argv[];
+{
+ int outdev, c;
+ struct stat statbuffer;
+ int opmode = O_WRONLY; /* mode for opening the output device */
+ char zcat_cmd[256];
+ char *currfile;
+
+ command = argv[0];
+
+ while ((c = getopt (argc, argv, "cqvz1234Sb:s:o:")) != EOF)
+ switch (c)
+ {
+ case 'c': /* play infinitely */
+ loop_forever = TRUE;
+ break;
+ case 'q': /* quiet mode */
+ quiet_mode = TRUE;
+ break;
+ case 'v': /* verbose mode */
+ verbose_mode = TRUE;
+ break;
+ case 'z': /* use zcat on file */
+ use_zcat = TRUE;
+ break;
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ mute[c - '1'] = TRUE;
+ break;
+ case 'S': /* toggle stereo */
+ dsp_stereo = !DSP_DEFAULT_STEREO;
+ break;
+ case 'b': /* sample size */
+ dsp_samplesize = atoi (optarg);
+ break;
+ case 's': /* dsp speed */
+ dsp_speed = atoi (optarg);
+ if (dsp_speed < 300)
+ dsp_speed *= 1000;
+ break;
+ case 'o': /* output file */
+ strcpy (AUDIO, optarg);
+ break;
+ default:
+ usage (command);
+ }
+
+ /* output to a character device (TRUE) or a file (FALSE)? */
+ outdev = !stat (AUDIO, &statbuffer) && S_ISCHR (statbuffer.st_mode);
+
+ if (!outdev)
+ opmode |= O_CREAT; /* file: create it */
+ if ((audio = open (AUDIO, opmode, 0)) == -1)
+ {
+ perror (AUDIO);
+ describe_error ();
+ exit (EXIT_FAILURE);
+ }
+
+
+ if (outdev)
+ { /* set dsp parameters */
+ if (ioctl (audio, SNDCTL_DSP_SETFMT, &dsp_samplesize) == -1)
+ dsp_samplesize = 8;
+ if (ioctl (audio, SNDCTL_DSP_STEREO, &dsp_stereo) == -1)
+ dsp_stereo = FALSE;
+ if (ioctl (audio, SNDCTL_DSP_SPEED, &dsp_speed) == -1)
+ {
+ fprintf (stderr, "%s: unable to set playback speed\n", command);
+ perror (AUDIO);
+ exit (EXIT_FAILURE);
+ }
+ if (!quiet_mode)
+ printf ("Playback speed %d Hz (%s) %d bits/sample.\n", dsp_speed,
+ dsp_stereo ? "stereo" : "mono", dsp_samplesize);
+ }
+
+ if (outdev)
+ { /* get the dsp buffer size */
+#if 0
+/*
+ * There is no idea in using SNDCTL_DSP_GETBLKSIZE in applications like this.
+ * Using some fixed local buffer size will work equally well.
+ */
+ ioctl (audio, SNDCTL_DSP_GETBLKSIZE, &abuf_size);
+ if (abuf_size < 1)
+ {
+ perror ("audio_size");
+ close (audio);
+ exit (EXIT_FAILURE);
+ }
+#else
+ abuf_size = 1024;
+#endif
+ }
+ else
+ { /* to a file: use block size of 1kB */
+ abuf_size = 1024;
+ loop_forever = 0;
+ }
+
+ if ((audiobuf = malloc (abuf_size)) == NULL)
+ {
+ fprintf (stderr, "%s: unable to allocate output buffer\n", command);
+ close (audio);
+ exit (EXIT_FAILURE);
+ }
+ abuf_ptr = 0;
+
+ if (optind > argc - 1)
+ {
+ if (use_zcat)
+ {
+ if ((fp = popen (ZCAT, "rb")) == NULL)
+ {
+ fprintf (stderr, "%s: can't execute " ZCAT ".\n", command);
+ return (EXIT_FAILURE);
+ }
+ }
+ else
+ fp = stdin;
+ play_song ();
+ if (use_zcat)
+ pclose (fp);
+ }
+ else
+ while (optind <= argc - 1)
+ if ((currfile = find_file (argv[optind])) == NULL)
+ {
+ fprintf (stderr, "%s: can't find file %s - skipping.\n",
+ command, argv[optind]);
+ optind++;
+ }
+ else if (use_zcat)
+ {
+ if ((fp = popen (strcat (strcpy (zcat_cmd, ZCAT " "),
+ currfile), "rb")) == NULL)
+ {
+ fprintf (stderr, "%s: can't execute " ZCAT " %s.\n",
+ command, currfile);
+ exit (EXIT_FAILURE);
+ }
+ else
+ {
+ play_song ();
+ pclose (fp);
+ if (!loop_forever)
+ optind++;
+ }
+ }
+ else
+ {
+ if ((fp = fopen (currfile, "rb")) == NULL)
+ {
+ fprintf (stderr, "%s: unable to open tune file %s.\n",
+ command, currfile);
+ exit (EXIT_FAILURE);
+ }
+ else
+ {
+ play_song ();
+ fclose (fp);
+ if (!loop_forever)
+ optind++;
+ }
+ }
+
+ if (abuf_ptr)
+ write (audio, audiobuf, abuf_ptr);
+ close (audio);
+
+ return EXIT_SUCCESS;
+}
diff --git a/tutorials/sndkit/dsp/str/str.h b/tutorials/sndkit/dsp/str/str.h
new file mode 100644
index 0000000..308d671
--- /dev/null
+++ b/tutorials/sndkit/dsp/str/str.h
@@ -0,0 +1,74 @@
+#define FALSE 0
+#define TRUE 1
+
+/* Default device for output */
+#define AUDIONAME "/dev/dsp"
+
+#define ZCAT "/usr/bin/zcat"
+
+/* Playback frequency in Hertz:
+ *
+ * 386SX -> 8000 (or 4000)
+ * 386/25 -> 12000
+ * 486/50 -> 23000
+ */
+#define DEFAULT_DSP_SPEED 44100
+
+#define DSP_DEFAULT_STEREO TRUE
+
+/* The default sample size can be 8 or 16 bits/sample */
+#define DSP_DEFAULT_SAMPLESIZE 8
+
+typedef struct
+{ /*************************************/
+ char *info; /* Sample data as a series of bytes */
+ unsigned int length; /* Length of sample in bytes */
+ int volume; /* Default volume 0-64 (min-max) */
+ unsigned int rep_start; /* Byte offset of repeat start */
+ unsigned int rep_end; /* Byte offset of repeat end */
+}
+Voice; /*************************************/
+
+typedef struct
+{ /*************************************/
+ int period[64][4]; /* Period (pitch) of note */
+ char sample[64][4]; /* Sample number to use */
+ char effect[64][4]; /* Effect number (command) */
+ unsigned char params[64][4]; /* Effect parameters */
+}
+Pattern; /*************************************/
+
+typedef struct
+{ /*************************************/
+ unsigned int pointer; /* Current position in sample */
+ unsigned int step; /* Sample offset increment */
+ int samp; /* Number of sample currently used */
+ int pitch; /* Current pitch */
+ int volume; /* Volume of current note (0-64) */
+ int doarp; /* TRUE if doing arpeggio */
+ int arp[3]; /* The three notes in the arpeggio */
+ int doslide; /* TRUE if doing slide */
+ int slide; /* Slide speed and direction */
+ int doporta; /* TRUE if doing portamento */
+ int pitchgoal; /* Pitch to reach */
+ int portarate; /* Rate at which to approach pitch */
+ int dovib; /* TRUE if doing vibrato */
+ int vibspeed; /* Speed of vibrato 0-15 << 2 */
+ int vibamp; /* Amplitude 0-15 */
+ int viboffs; /* Current offset in sine wave */
+ int doslidevol; /* TRUE if doing volume slide */
+ int volslide; /* Slide speed 0-64 */
+}
+Channel; /*************************************/
+
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+
+#define ABUF_SIZE abuf_size
+
+#ifndef EXIT_SUCCESS
+#define EXIT_SUCCESS 0
+#endif
+#ifndef EXIT_FAILURE
+#define EXIT_FAILURE 1
+#endif
diff --git a/tutorials/sndkit/dsp/str/strplay.c b/tutorials/sndkit/dsp/str/strplay.c
new file mode 100644
index 0000000..82a3cda
--- /dev/null
+++ b/tutorials/sndkit/dsp/str/strplay.c
@@ -0,0 +1,575 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#ifdef __STDC__
+#include <string.h>
+#else /* __STDC__ */
+#include <strings.h>
+#endif /* __STDC__ */
+#include <sys/types.h>
+
+#include <sys/soundcard.h>
+#include "str.h"
+
+extern int audio;
+extern unsigned char *audiobuf;
+extern int abuf_ptr;
+extern int abuf_size;
+
+extern char *command;
+extern int quiet_mode, verbose_mode;
+extern int mute[4];
+extern int dsp_stereo;
+extern int dsp_samplesize;
+extern long dsp_speed;
+
+extern FILE *fp;
+
+static int VSYNC; /* number of sample bytes to output in 1/50 sec */
+
+static int notes, channel, vsync, bytes, pat_num; /* loop variables */
+static int note, pat;
+static int end_pattern;
+
+/* song data and parameters */
+static int speed;
+static char songlength, songrepeat, tune[128];
+static int nvoices;
+static Voice voices[32];
+static char num_patterns;
+static Pattern patterns[128];
+static Channel ch[4];
+
+/* table for generating pitches */
+static int step_table[1024];
+
+/* sine wave table for the vibrato effect */
+static int sin_table[] = {
+ 0x00, 0x18, 0x31, 0x4A, 0x61, 0x78, 0x8D, 0xA1,
+ 0xB4, 0xC5, 0xD4, 0xE0, 0xEB, 0xF4, 0xFA, 0xFD,
+ 0xFF, 0xFD, 0xFA, 0xF4, 0xEB, 0xE0, 0xD4, 0xC5,
+ 0xB4, 0xA1, 0x8D, 0x78, 0x61, 0x4A, 0x31, 0x18
+};
+
+/* period table for notes C0-B2, used for arpeggio effect */
+static int periods[] = {
+ 0x358, 0x328, 0x2FA, 0x2D0, 0x2A6, 0x280,
+ 0x25C, 0x23A, 0x21A, 0x1FC, 0x1E0, 0x1C5,
+ 0x1AC, 0x194, 0x17D, 0x168, 0x153, 0x140,
+ 0x12E, 0x11D, 0x10D, 0x0FE, 0x0F0, 0x0E2,
+ 0x0D6, 0x0CA, 0x0BE, 0x0B4, 0x0AA, 0x0A0,
+ 0x097, 0x08F, 0x087, 0x07F, 0x078, 0x071
+};
+
+/* skip n bytes of file f - stdin has no fseek */
+void
+byteskip (f, n)
+ FILE *f;
+ int n;
+{
+ while (n--)
+ fgetc (f);
+}
+
+/* get string of length <len> from file f */
+char *
+getstring (f, len)
+ FILE *f;
+ int len;
+{
+ static char s[256];
+ int i;
+
+ for (i = 0; i < len; i++)
+ {
+ if ((s[i] = fgetc (f)) == '\1')
+ s[i] = '\0'; /* bug fix for some few modules */
+ if (s[i] < 32 && s[i])
+ {
+ fprintf (stderr, "This is not a string. Wrong format?\n");
+ exit (EXIT_FAILURE);
+ }
+ }
+ s[len] = '\0';
+
+ return s;
+}
+
+void
+audioput (c)
+ int c;
+{
+ audiobuf[abuf_ptr++] = c;
+ if (abuf_ptr >= ABUF_SIZE)
+ {
+#if 0
+ int count;
+
+ if (ioctl (audio, SNDCTL_DSP_GETODELAY, &count) == -1)
+ perror ("GETODELAY");
+ printf ("%6d\n", count);
+#endif
+ if (write (audio, audiobuf, abuf_ptr) == -1)
+ {
+ perror ("audio_write");
+ exit (EXIT_FAILURE);
+ }
+ abuf_ptr = 0;
+ }
+}
+
+void
+audioput16 (c)
+ int c;
+{
+ audiobuf[abuf_ptr++] = c & 0xFF;
+ audiobuf[abuf_ptr++] = c >> 8;
+ if ((abuf_ptr + 1) >= ABUF_SIZE)
+ {
+ if (write (audio, audiobuf, abuf_ptr) == -1)
+ {
+ perror ("audio_write");
+ exit (EXIT_FAILURE);
+ }
+ abuf_ptr = 0;
+ }
+}
+
+void
+play_song ()
+{
+ int i;
+
+ /* set up pitch table:
+ * 3576872 is the base clock frequency of the Amiga's Paula chip,
+ * the pitch i is the number of cycles between outputting two samples.
+ * => i / 3576872 is the delay between two bytes
+ * => our_freq * i / 3576872 is our delay.
+ * The 65536 are just there for accuracy of integer operations (<< 16).
+ */
+ step_table[0] = 0;
+ for (i = 1; i < 1024; i++)
+ step_table[i] = (int) (((3575872.0 / dsp_speed) / i) * 65536.0);
+
+ /* VSYNC is the number of bytes in 1/50 sec
+ * computed as freq * (1 / 50).
+ */
+ VSYNC = (dsp_speed + 25) / 50; /* with round */
+
+ /* read song name */
+ if (quiet_mode)
+ byteskip (fp, 20);
+ else
+ printf ("Module : %s\n\n", getstring (fp, 20));
+
+ /* determine number of voices from command name */
+ nvoices = strstr (command, "15") == NULL ? 31 : 15;
+
+ /* read in the sample information tables */
+ voices[0].length = 0;
+ for (i = 1; i <= nvoices; i++)
+ {
+ /* sample name */
+ if (quiet_mode)
+ byteskip (fp, 22);
+ else if (nvoices == 15)
+ printf ("%6d : %s\n", i, getstring (fp, 22));
+ else
+ {
+ printf ("%6d : %22s", i, getstring (fp, 22));
+ if (!(i & 1))
+ printf ("\n");
+ }
+ /* the sample length and repeat data are stored as numbers of words,
+ * we want the number of bytes << 16.
+ */
+ voices[i].length = (fgetc (fp) << 25) | (fgetc (fp) << 17);
+ fgetc (fp);
+ if ((voices[i].volume = fgetc (fp)) > 64)
+ voices[i].volume = 64;
+ voices[i].rep_start = (fgetc (fp) << 25) | (fgetc (fp) << 17);
+ voices[i].rep_end = (fgetc (fp) << 25) | (fgetc (fp) << 17);
+ if (voices[i].rep_end <= 4 << 16)
+ voices[i].rep_end = 0;
+ else
+ {
+ voices[i].rep_end += voices[i].rep_start;
+ if (voices[i].rep_end > voices[i].length)
+ voices[i].rep_end = voices[i].length;
+ }
+ }
+
+ /* read sequence data: length and repeat position */
+ songlength = fgetc (fp);
+ songrepeat = fgetc (fp);
+ if (songrepeat > songlength) /* this is often buggy in modules */
+ songrepeat = 0;
+
+ /* read in the sequence and determine the number of patterns
+ * by looking for the highest pattern number.
+ */
+ num_patterns = 0;
+ for (i = 0; i < 128; i++)
+ {
+ tune[i] = fgetc (fp);
+ if (tune[i] > num_patterns)
+ num_patterns = tune[i];
+ }
+ num_patterns++; /* pattern numbers begin at 0 */
+
+ /* skip over sig in new modules (usually M.K.).
+ * note: this sig could be used for determining whether the module
+ * is of an old type (15 samples max) or new (up to 31 samples)
+ */
+ if (nvoices == 31)
+ byteskip (fp, 4);
+
+ /* read in the patterns.
+ * Each pattern consists of 64 lines,
+ * each containing data for the four voices.
+ */
+ for (pat_num = 0; pat_num < num_patterns; pat_num++)
+ for (notes = 0; notes < 64; notes++)
+ for (channel = 0; channel < 4; channel++)
+ {
+ note = (fgetc (fp) << 8) | fgetc (fp);
+ patterns[pat_num].sample[notes][channel] = (note >> 8) & 0x10;
+ patterns[pat_num].period[notes][channel] = MIN (note & 0xFFF, 1023);
+ note = (fgetc (fp) << 8) | fgetc (fp);
+ patterns[pat_num].sample[notes][channel] |= note >> 12;
+ patterns[pat_num].effect[notes][channel] = (note >> 8) & 0xF;
+ patterns[pat_num].params[notes][channel] = note & 0xFF;
+ }
+
+ /* store each sample as an array of char */
+ for (i = 1; i <= nvoices; i++)
+ if (voices[i].length)
+ {
+ if ((voices[i].info = malloc (voices[i].length >> 16)) == NULL)
+ {
+ fprintf (stderr, "%s: can't allocate memory\n", command);
+ exit (EXIT_FAILURE);
+ }
+ fread (voices[i].info, 1, voices[i].length >> 16, fp);
+ if (voices[i].rep_end)
+ voices[i].length = voices[i].rep_end;
+ voices[i].rep_start -= voices[i].length;
+ }
+
+ /* initialize global and channel replay data */
+ speed = 6;
+ for (i = 0; i < 4; i++)
+ {
+ ch[i].pointer = 0;
+ ch[i].step = 0;
+ ch[i].volume = 0;
+ ch[i].pitch = 0;
+ }
+
+ if (!quiet_mode)
+ if (verbose_mode)
+ printf ("\n");
+ else
+ {
+ printf ("\nPosition (%d):", songlength);
+ fflush (stdout);
+ }
+
+
+ /*******************************/
+ /* Here begins the replay part */
+ /*******************************/
+
+ for (pat_num = 0; pat_num < songlength; pat_num++)
+ {
+ pat = tune[pat_num];
+
+ if (!quiet_mode)
+ if (verbose_mode)
+ printf (" --------------+--------------+--------------+-----"
+ "--------- %02X (%02X) = %02X\n", pat_num,
+ songlength, pat);
+ else
+ {
+ printf ("\r\t\t%3d", pat_num);
+ fflush (stdout);
+ }
+
+ end_pattern = FALSE;
+ for (notes = 0; notes < 64; notes++)
+ {
+ int samp, pitch, cmd, para;
+ Channel *curch;
+
+ if (!quiet_mode && verbose_mode)
+ printf ("%02X ", notes);
+
+ curch = ch;
+ for (channel = 0; channel < 4; channel++, curch++)
+ {
+ samp = patterns[pat].sample[notes][channel];
+ pitch = patterns[pat].period[notes][channel];
+ cmd = patterns[pat].effect[notes][channel];
+ para = patterns[pat].params[notes][channel];
+
+ if (verbose_mode && !quiet_mode)
+ if (pitch)
+ printf (" %03X %2X %X%02X%s", pitch, samp, cmd, para,
+ channel == 3 ? "\n" : " |");
+ else
+ printf (" --- %2X %X%02X%s", samp, cmd, para,
+ channel == 3 ? "\n" : " |");
+
+ if (mute[channel])
+ {
+ if (cmd == 0x0B || cmd == 0x0D || cmd == 0x0F)
+ switch (cmd)
+ {
+ case 0xB: /* Pattern jump */
+ end_pattern = TRUE;
+ pat_num = (para & 0xF) + (10 * (para >> 4));
+ break;
+ case 0xD: /* Pattern break */
+ end_pattern = TRUE;
+ break;
+ case 0xF: /* Set speed */
+ speed = para;
+ break;
+ }
+ continue;
+ }
+
+ /* if a sample number is given, it is loaded and the note
+ * is restarted
+ */
+ if (samp)
+ {
+ curch->samp = samp;
+ /* load new instrument */
+ curch->volume = voices[samp].volume;
+ }
+
+ if (samp || (pitch && cmd != 3))
+ {
+ if (pitch)
+ curch->pitch = curch->arp[0] = pitch;
+ curch->pointer = 0;
+ curch->viboffs = 0;
+ }
+
+ /* by default there is no effect */
+ curch->doarp = FALSE;
+ curch->doslide = FALSE;
+ curch->doporta = FALSE;
+ curch->dovib = FALSE;
+ curch->doslidevol = FALSE;
+
+ switch (cmd)
+ { /* Do effects */
+ case 0: /* Arpeggio */
+ if (para)
+ {
+ for (i = 0; i < 36 && periods[i] > curch->arp[0]; i++);
+ if (i + (para >> 4) < 36 && i + (para & 0xF) < 36)
+ {
+ curch->doarp = TRUE;
+ curch->arp[0] = periods[i];
+ curch->arp[1] = periods[i + (para >> 4)];
+ curch->arp[2] = periods[i + (para & 0xF)];
+ }
+ }
+ break;
+ case 1: /* Portamento up */
+ curch->doslide = TRUE;
+ if (para)
+ curch->slide = -para;
+ break;
+ case 2: /* Portamento down */
+ curch->doslide = TRUE;
+ if (para)
+ curch->slide = para;
+ break;
+ case 3: /* Note portamento */
+ curch->doporta = TRUE;
+ if (para)
+ curch->portarate = para;
+ if (pitch)
+ curch->pitchgoal = pitch;
+ break;
+ case 4: /* Vibrato */
+ curch->dovib = TRUE;
+ if (para)
+ {
+ curch->vibspeed = (para >> 2) & 0x3C;
+ curch->vibamp = para & 0xF;
+ }
+ break;
+ case 0xA: /* Volume slide */
+ curch->doslidevol = TRUE;
+ if (para)
+ {
+ if (!(para & 0xF0))
+ curch->volslide = -para;
+ else
+ curch->volslide = (para >> 4);
+ }
+ break;
+ case 0xB: /* Pattern jump */
+ end_pattern = TRUE;
+ pat_num = (para & 0xF) + (10 * (para >> 4));
+ break;
+ case 0xC: /* Set volume */
+ curch->volume = MIN (para, 64);
+ break;
+ case 0xD: /* Pattern break */
+ end_pattern = TRUE;
+ break;
+ case 0xF: /* Set speed */
+ speed = para;
+ break;
+ default:
+ /* printf(" [%d][%d] ", cmd, para); */
+ break;
+ }
+ }
+
+ {
+ register Channel *curch;
+ register Voice *curv;
+
+ for (vsync = 0; vsync < speed; vsync++)
+ {
+
+ ch[0].step = step_table[ch[0].pitch];
+ ch[1].step = step_table[ch[1].pitch];
+ ch[2].step = step_table[ch[2].pitch];
+ ch[3].step = step_table[ch[3].pitch];
+
+ for (bytes = 0; bytes < VSYNC; bytes++)
+ {
+ int byte[2] = { 0, 0 };
+
+ curch = ch;
+ for (channel = 0; channel < 4; channel++, curch++)
+ {
+ if (!curch->samp || mute[channel])
+ continue;
+
+ curv = &voices[(int) curch->samp];
+
+ /* test for end of sample */
+ if (curch->pointer >= curv->length)
+ if (!curv->rep_end)
+ continue;
+ else
+ curch->pointer += curv->rep_start;
+
+ /* mix samples.
+ * the sample is read and multiplied by the volume
+ */
+ if (curch->pointer < curv->length)
+ /* in stereo, channels 1 & 3 and 2 & 4 are mixed
+ * seperately
+ */
+ byte[channel & dsp_stereo] += (int)
+ ((curv->info[curch->pointer >> 16]) *
+ ((int) curch->volume << 2));
+ /* advance the sample pointer */
+ curch->pointer += curch->step;
+ }
+
+ /* output sample */
+ if (dsp_samplesize == 8)
+ {
+ if (dsp_stereo)
+ {
+ byte[0] >>= 9;
+ audioput (byte[0] + 128);
+ byte[1] >>= 9;
+ audioput (byte[1] + 128);
+ }
+ else
+ audioput ((byte[0] >> 10) + 128);
+ }
+ else
+ {
+ if (dsp_stereo)
+ {
+ audioput16 (byte[0] >> 1);
+ audioput16 (byte[1] >> 1);
+ }
+ else
+ audioput16 (byte[0] >> 2);
+ }
+ }
+
+ /* Do end of vsync */
+ if (vsync == 0)
+ continue;
+ curch = ch;
+ for (channel = 0; channel < 4; channel++, curch++)
+ {
+ if (mute[channel])
+ continue;
+ if (curch->doarp)
+ curch->pitch = curch->arp[vsync % 3];
+ else if (curch->doslide)
+ {
+ curch->arp[0] += curch->slide;
+ curch->arp[0] = MIN (curch->arp[0], 1023);
+ curch->arp[0] = MAX (curch->arp[0], 113);
+ curch->pitch = curch->arp[0];
+ }
+ else if (curch->doporta)
+ {
+ if (curch->arp[0] < curch->pitchgoal)
+ {
+ curch->arp[0] += curch->portarate;
+ if (curch->arp[0] > curch->pitchgoal)
+ curch->arp[0] = curch->pitchgoal;
+ }
+ else if (curch->arp[0] > curch->pitchgoal)
+ {
+ curch->arp[0] -= curch->portarate;
+ if (curch->arp[0] < curch->pitchgoal)
+ curch->arp[0] = curch->pitchgoal;
+ }
+ curch->pitch = curch->arp[0];
+ }
+ else if (curch->dovib)
+ {
+ if (curch->viboffs & 0x80)
+ curch->pitch = curch->arp[0] - ((sin_table
+ [(curch->
+ viboffs >> 2) &
+ 0x1F] *
+ curch->
+ vibamp) >> 6);
+ else
+ curch->pitch = curch->arp[0] + ((sin_table
+ [(curch->
+ viboffs >> 2) &
+ 0x1F] *
+ curch->
+ vibamp) >> 6);
+ curch->viboffs += curch->vibspeed;
+ }
+ else if (curch->doslidevol)
+ {
+ curch->volume += curch->volslide;
+ if (curch->volume < 0)
+ curch->volume = 0;
+ else if (curch->volume >= 64)
+ curch->volume = 64;
+ }
+ }
+ }
+ }
+ if (end_pattern == 1)
+ break;
+ }
+ }
+
+ if (!quiet_mode)
+ printf ("\n");
+}
diff --git a/tutorials/sndkit/dsp/vplay/Makefile b/tutorials/sndkit/dsp/vplay/Makefile
new file mode 100644
index 0000000..17bfd1e
--- /dev/null
+++ b/tutorials/sndkit/dsp/vplay/Makefile
@@ -0,0 +1,20 @@
+#CC=gcc
+#CFLAGS=-O6 -funroll-loops -Wall
+CC=cc
+CFLAGS=-O -Wall
+
+all: vplay vrec
+
+vplay: vplay.c fmtheaders.h
+ $(CC) $(CFLAGS) vplay.c -o vplay
+ strip vplay
+
+vrec: vplay
+ ln -s vplay vrec
+clean:
+ rm -f vplay vrec *.o core
+
+install: vplay
+ cp vplay /usr/local/bin
+ rm -f /usr/local/bin/vrec
+ ln -s /usr/local/bin/vplay /usr/local/bin/vrec
diff --git a/tutorials/sndkit/dsp/vplay/Readme b/tutorials/sndkit/dsp/vplay/Readme
new file mode 100644
index 0000000..881393d
--- /dev/null
+++ b/tutorials/sndkit/dsp/vplay/Readme
@@ -0,0 +1,69 @@
+Digitized Audio Utility for Linux ver. 0.3
+============================================
+
+This directory contains vplay.c, a modified version of recplay.c.
+
+vrec and vplay
+--------------
+
+These programs can be used for recording and playing:
+ CREATIVE LABS VOICE files
+ MICROSOFT WAVE file (old format only (new format is handled as raw data))
+ NeXT sound files (similar to Sun's .au format)
+ raw audio data.
+
+Both programs accept the same options:
+
+ vrec [-qvwrS] [-s speed] [-t seconds] -b bits [filename1 ...]
+ vplay [-qvwrS] [-s speed] [-t seconds] -b bits [filename1 ...]
+
+ -S Stereo (default is mono).
+ -s speed Sets the speed (default is 8 kHz). If the speed is
+ less than 300, it will be multiplied by 1000.
+ -t seconds Sets the recording (or playback) time in seconds.
+ (Default is no time limit).
+ -t bits Sets sample size (bits/sample). Possible values are
+ 8 and 16 (default 8).
+ -v record a CREATIVE LABS VOICE file (default)
+ -w record a MICROSOFT WAVE file
+ -r record raw data without header
+ -a record a NeXT sound file
+ -q quiet mode
+
+ The options for speed, time etc. take only effect if you playing
+ raw data files (or recording). VOC and WAVE-files include this
+ information in their headers/internal structure.
+ If no filenames are given, stdout (vrec) or stdin (vplay) is used.
+ The -t parameter applies to each files. For example
+
+ vrec -r -t 1 a b c
+
+ records one second of audio data to each of the files a, b, and c and
+
+ vplay -t 1 a b c
+
+ plays the first second of each of the files a, b and c (if its
+ raw audio).
+
+Don't use higher recording speeds than your card supports. This error is not
+always detected by the driver.
+
+vplay supports:
+ - the full CREATIVE LABS VOICE structure:
+ Silence, Repeat loops (on seekable input), Stereo, ASCII blocks,
+ blocks with different sampling rate
+ - on non-stereo cards (SB 1.0 - 2.0) 8 bit stereo files will be
+ played as mono (the first channel is used)
+ - on non-16-bit cards, 16 bit WAVE files will be played as 8 bit
+ (you can really play on a SB 1.0 a 16 bit stereo WAVE file, or
+ buy ...)
+
+unsupported:
+ - packed VOC files (because /dev/dsp can't it (yet ?))
+ - multi block WAVE files (if there exists like VOC files, my specs
+ says no but RIFF definition yes ???)
+ - not PCM coded WAVE files (because I don't know other methods)
+ - more channel WAVE files (somebody has a quadrophonic sample?)
+ - alaw- or mulaw-encoded NeXt sound files
+
+Michael Beck beck@informatik.hu-berlin.de
diff --git a/tutorials/sndkit/dsp/vplay/etc.magic.vplay b/tutorials/sndkit/dsp/vplay/etc.magic.vplay
new file mode 100644
index 0000000..a0c5c29
--- /dev/null
+++ b/tutorials/sndkit/dsp/vplay/etc.magic.vplay
@@ -0,0 +1,16 @@
+#
+# Creative Labs Voice file
+#
+0 string Creative\ Voice\ File Creative Labs Voice File
+>19 byte 0x1A
+>23 byte >0 - version %d
+>22 byte >0 \b.%d
+#
+# Microsoft WAVE format
+0 string RIFF Microsoft RIFF
+>8 string WAVE - WAVE format
+>34 short >0 %d bit
+>22 short =1 Mono
+>22 short =2 Stereo
+>22 short >2 %d Channels
+>24 long >0 %d Hz
diff --git a/tutorials/sndkit/dsp/vplay/fmtheaders.h b/tutorials/sndkit/dsp/vplay/fmtheaders.h
new file mode 100644
index 0000000..9afb0cd
--- /dev/null
+++ b/tutorials/sndkit/dsp/vplay/fmtheaders.h
@@ -0,0 +1,108 @@
+#ifndef _FMTHEADERS_H
+#define _FMTHEADERS_H 1
+
+#include <sys/types.h>
+
+/* Definitions for .VOC files */
+
+#define MAGIC_STRING "Creative Voice File\x1A"
+#define ACTUAL_VERSION 0x010A
+#define VOC_SAMPLESIZE 8
+
+#define MODE_MONO 0
+#define MODE_STEREO 1
+
+#define DATALEN(bp) ((u_long)(bp->datalen) | \
+ ((u_long)(bp->datalen_m) << 8) | \
+ ((u_long)(bp->datalen_h) << 16) )
+
+typedef struct _vocheader
+{
+ u_char magic[20]; /* must be MAGIC_STRING */
+ u_short headerlen; /* Headerlength, should be 0x1A */
+ u_short version; /* VOC-file version */
+ u_short coded_ver; /* 0x1233-version */
+}
+VocHeader;
+
+typedef struct _blocktype
+{
+ u_char type;
+ u_char datalen; /* low-byte */
+ u_char datalen_m; /* medium-byte */
+ u_char datalen_h; /* high-byte */
+}
+BlockType;
+
+typedef struct _voice_data
+{
+ u_char tc;
+ u_char pack;
+}
+Voice_data;
+
+typedef struct _ext_block
+{
+ u_short tc;
+ u_char pack;
+ u_char mode;
+}
+Ext_Block;
+
+
+/* Definitions for Microsoft WAVE format */
+
+#define RIFF 0x46464952
+#define WAVE 0x45564157
+#define FMT 0x20746D66
+#define DATA 0x61746164
+#define PCM_CODE 1
+#define WAVE_MONO 1
+#define WAVE_STEREO 2
+
+/* it's in chunks like .voc and AMIGA iff, but my source say there
+ are in only in this combination, so I combined them in one header;
+ it works on all WAVE-file I have
+*/
+typedef struct _waveheader
+{
+ u_long main_chunk; /* 'RIFF' */
+ u_long length; /* filelen */
+ u_long chunk_type; /* 'WAVE' */
+
+ u_long sub_chunk; /* 'fmt ' */
+ u_long sc_len; /* length of sub_chunk, =16 */
+ u_short format; /* should be 1 for PCM-code */
+ u_short modus; /* 1 Mono, 2 Stereo */
+ u_long sample_fq; /* frequence of sample */
+ u_long byte_p_sec;
+ u_short byte_p_spl; /* samplesize; 1 or 2 bytes */
+ u_short bit_p_spl; /* 8, 12 or 16 bit */
+
+ u_long data_chunk; /* 'data' */
+ u_long data_length; /* samplecount */
+}
+WaveHeader;
+
+typedef struct
+{
+ long magic; /* must be equal to SND_MAGIC */
+ long dataLocation; /* Offset or pointer to the raw data */
+ long dataSize; /* Number of bytes of data in the raw data */
+ long dataFormat; /* The data format code */
+ long samplingRate; /* The sampling rate */
+ long channelCount; /* The number of channels */
+}
+SndHeader;
+
+#define SND_MAGIC ((long int)0x2e736e64)
+
+#define SND_FORMAT_UNSPECIFIED (0)
+#define SND_FORMAT_MULAW_8 (1)
+#define SND_FORMAT_LINEAR_8 (2)
+#define SND_FORMAT_LINEAR_16 (3)
+#define SND_FORMAT_LINEAR_24 (4)
+#define SND_FORMAT_LINEAR_32 (5)
+#define SND_FORMAT_FLOAT (6)
+
+#endif
diff --git a/tutorials/sndkit/dsp/vplay/vplay.c b/tutorials/sndkit/dsp/vplay/vplay.c
new file mode 100644
index 0000000..afbc4d3
--- /dev/null
+++ b/tutorials/sndkit/dsp/vplay/vplay.c
@@ -0,0 +1,1012 @@
+/*
+ vplay.c - plays and records
+ CREATIVE LABS VOICE-files, Microsoft WAVE-files and raw data
+
+ Autor: Michael Beck - beck@informatik.hu-berlin.de
+*/
+
+#include <stdio.h>
+#include <malloc.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/soundcard.h>
+#include "fmtheaders.h"
+#include <netinet/in.h>
+
+#define DEFAULT_DSP_SPEED 8000
+
+#define RECORD 0
+#define PLAY 1
+#define AUDIO "/dev/dsp"
+
+#define min(a,b) ((a) <= (b) ? (a) : (b))
+#define d_printf(x) if (verbose_mode) fprintf x
+
+#define VOC_FMT 0
+#define WAVE_FMT 1
+#define RAW_DATA 2
+#define SND_FMT 3
+
+/* global data */
+
+int timelimit = 0, dsp_speed = DEFAULT_DSP_SPEED, dsp_stereo = 0;
+int samplesize = 8;
+int quiet_mode = 0;
+int verbose_mode = 0;
+int convert = 0;
+int record_type = VOC_FMT;
+u_long count;
+int audio, abuf_size, zbuf_size;
+int direction, omode;
+u_char *audiobuf, *zerobuf;
+char *command;
+int vocminor, vocmajor; /* .VOC version */
+
+/* defaults for playing raw data */
+struct
+{
+ int timelimit, dsp_speed, dsp_stereo, samplesize;
+}
+raw_info =
+{
+0, DEFAULT_DSP_SPEED, 0, 8};
+
+/* needed prototypes */
+
+void record_play (char *name);
+void start_voc (int fd, u_long count);
+void start_wave (int fd, u_long count);
+void start_snd (int fd, u_long count);
+void end_voc (int fd);
+void end_wave_raw (int fd);
+void end_snd (int fd);
+u_long calc_count ();
+
+struct fmt_record
+{
+ void (*start) (int fd, u_long count);
+ void (*end) (int fd);
+ char *what;
+}
+fmt_rec_table[] =
+{
+ {
+ start_voc, end_voc, "VOC"}
+ ,
+ {
+ start_wave, end_wave_raw, "WAVE"}
+ ,
+ {
+ NULL, end_wave_raw, "raw data"}
+ ,
+ {
+ start_snd, end_snd, "SND"}
+};
+
+int
+main (int argc, char *argv[])
+{
+ int c, n = 0;
+
+ command = argv[0];
+ if (strstr (argv[0], "vrec"))
+ {
+ direction = RECORD;
+ omode = O_RDONLY;
+ }
+ else if (strstr (argv[0], "vplay"))
+ {
+ direction = PLAY;
+ omode = O_WRONLY;
+ }
+ else
+ {
+ fprintf (stderr,
+ "Error: command should be named either vrec or vplay\n");
+ exit (1);
+ }
+
+ while ((c = getopt (argc, argv, "aqs:St:b:vrwd")) != EOF)
+ switch (c)
+ {
+ case 'S':
+ dsp_stereo = raw_info.dsp_stereo = 1;
+ break;
+ case 'q':
+ quiet_mode = 1;
+ break;
+ case 'r':
+ record_type = RAW_DATA;
+ break;
+ case 'v':
+ record_type = VOC_FMT;
+ break;
+ case 'w':
+ record_type = WAVE_FMT;
+ break;
+ case 'a':
+ record_type = SND_FMT;
+ break;
+ case 's':
+ dsp_speed = atoi (optarg);
+ if (dsp_speed < 300)
+ dsp_speed *= 1000;
+ raw_info.dsp_speed = dsp_speed;
+ break;
+ case 't':
+ timelimit = raw_info.timelimit = atoi (optarg);
+ break;
+ case 'b':
+ samplesize = raw_info.samplesize = atoi (optarg);
+ break;
+ case 'd':
+ verbose_mode = 1;
+ quiet_mode = 0;
+ break;
+ default:
+ fprintf (stderr,
+ "Usage: %s [-qvwraS] [-t secs] [-s Hz] [-b 8|12|16] [filename]\n",
+ command);
+ exit (-1);
+ }
+
+ audio = open (AUDIO, omode, 0);
+ if (audio == -1)
+ {
+ perror (AUDIO);
+ exit (-1);
+ }
+
+ if (optind > argc - 1)
+ record_play (NULL);
+ else
+ while (optind <= argc - 1)
+ {
+
+ if (n++ > 0)
+ if (ioctl (audio, SNDCTL_DSP_SYNC, NULL) < 0)
+ {
+ perror (AUDIO);
+ exit (-1);
+ }
+
+ record_play (argv[optind++]);
+ }
+ close (audio);
+ return 0;
+}
+
+/*
+ test, if it is a .VOC file and return >=0 if ok (this is the length of rest)
+ < 0 if not
+*/
+int
+test_vocfile (void *buffer)
+{
+ VocHeader *vp = buffer;
+ if (!strcmp ((const char *) vp->magic, MAGIC_STRING))
+ {
+ vocminor = vp->version & 0xFF;
+ vocmajor = vp->version / 256;
+ if (vp->version != (0x1233 - vp->coded_ver))
+ return -2; /* coded version mismatch */
+ return vp->headerlen - sizeof (VocHeader); /* 0 mostly */
+ }
+ return -1; /* magic string fail */
+}
+
+/*
+ test, if it's a .WAV file, 0 if ok (and set the speed, stereo etc.)
+ < 0 if not
+*/
+int
+test_wavefile (void *buffer)
+{
+ WaveHeader *wp = buffer;
+ if (wp->main_chunk == RIFF && wp->chunk_type == WAVE &&
+ wp->sub_chunk == FMT && wp->data_chunk == DATA)
+ {
+ if (wp->format != PCM_CODE)
+ {
+ fprintf (stderr, "%s: can't play not PCM-coded WAVE-files\n",
+ command);
+ exit (-1);
+ }
+ if (wp->modus > 2)
+ {
+ fprintf (stderr, "%s: can't play WAVE-files with %d tracks\n",
+ command, wp->modus);
+ exit (-1);
+ }
+ dsp_stereo = (wp->modus == WAVE_STEREO) ? 1 : 0;
+ samplesize = wp->bit_p_spl;
+ dsp_speed = wp->sample_fq;
+ count = wp->data_length;
+ return 0;
+ }
+ return -1;
+}
+
+/*
+* test, if it's a .SND file, 0 if ok (and set the speed, stereo etc.)
+* < 0 if not
+*/
+int
+test_sndfile (void *buffer, int fd)
+{
+ long infolen;
+ char *info;
+ SndHeader *snd = buffer;
+
+ if (snd->magic == SND_MAGIC)
+ convert = 0;
+ else
+ {
+ if (htonl (snd->magic) == SND_MAGIC)
+ {
+ convert = 1;
+ snd->dataLocation = htonl (snd->dataLocation);
+ snd->dataSize = htonl (snd->dataSize);
+ snd->dataFormat = htonl (snd->dataFormat);
+ snd->samplingRate = htonl (snd->samplingRate);
+ snd->channelCount = htonl (snd->channelCount);
+ }
+ else
+ {
+ /* No SoundFile */
+ return (-1);
+ }
+ }
+ switch (snd->dataFormat)
+ {
+ case SND_FORMAT_LINEAR_8:
+ samplesize = 8;
+ break;
+ case SND_FORMAT_LINEAR_16:
+ samplesize = 16;
+ break;
+ default:
+ fprintf (stderr, "%s: Unsupported SND_FORMAT\n", command);
+ exit (-1);
+ }
+
+ dsp_stereo = (snd->channelCount == 2) ? 1 : 0;
+ dsp_speed = snd->samplingRate;
+ count = snd->dataSize;
+
+ /* read Info-Strings */
+ infolen = snd->dataLocation - sizeof (SndHeader);
+ info = (char *) malloc (infolen);
+ read (fd, info, infolen);
+ if (!quiet_mode)
+ fprintf (stderr, "SoundFile Info: %s\n", info);
+ free (info);
+
+ return 0;
+}
+
+
+/*
+ writing zeros from the zerobuf to simulate silence,
+ perhaps it's enough to use a long var instead of zerobuf ?
+*/
+void
+write_zeros (unsigned x)
+{
+ unsigned l;
+
+ while (x)
+ {
+ l = min (x, zbuf_size);
+ if (write (audio, (char *) zerobuf, l) != l)
+ {
+ perror (AUDIO);
+ exit (-1);
+ }
+ x -= l;
+ }
+}
+
+/* if need a SYNC,
+ (is needed if we plan to change speed, stereo ... during output)
+*/
+void
+sync_dsp (void)
+{
+#if 0
+ if (ioctl (audio, SNDCTL_DSP_SYNC, NULL) < 0)
+ {
+ perror (AUDIO);
+ exit (-1);
+ }
+#endif
+}
+
+/* setting the speed for output */
+void
+set_dsp_speed (int *dsp_speed)
+{
+ if (ioctl (audio, SNDCTL_DSP_SPEED, dsp_speed) < 0)
+ {
+ fprintf (stderr, "%s: unable to set audio speed\n", command);
+ perror (AUDIO);
+ exit (-1);
+ }
+}
+
+/* if to_mono:
+ compress 8 bit stereo data 2:1, needed if we want play 'one track';
+ this is usefull, if you habe SB 1.0 - 2.0 (I have 1.0) and want
+ hear the sample (in Mono)
+ if to_8:
+ compress 16 bit to 8 by using hi-byte; wave-files use signed words,
+ so we need to convert it in "unsigned" sample (0x80 is now zero)
+
+ WARNING: this procedure can't compress 16 bit stereo to 16 bit mono,
+ because if you have a 16 (or 12) bit card you should have
+ stereo (or I'm wrong ?? )
+ */
+u_long
+one_channel (u_char * buf, u_long l, char to_mono, char to_8)
+{
+ register u_char *w = buf;
+ register u_char *w2 = buf;
+ char ofs = 0;
+ u_long incr = 0;
+ u_long c, ret;
+
+ if (to_mono)
+ ++incr;
+ if (to_8)
+ {
+ ++incr;
+ ++w2;
+ ofs = 128;
+ }
+ ret = c = l >> incr;
+ incr = incr << 1;
+
+ while (c--)
+ {
+ *w++ = *w2 + ofs;
+ w2 += incr;
+ }
+ return ret;
+}
+
+/*
+ ok, let's play a .voc file
+*/
+void
+vplay (int fd, int ofs, char *name)
+{
+ int l, real_l;
+ BlockType *bp;
+ Voice_data *vd;
+ Ext_Block *eb;
+ u_long nextblock, in_buffer;
+ u_char *data = audiobuf;
+ char was_extended = 0, output = 0;
+ u_short *sp, repeat = 0;
+ u_long silence;
+ int filepos = 0;
+ char one_chn = 0;
+
+#define COUNT(x) nextblock -= x; in_buffer -=x ;data += x
+
+ /* first SYNC the dsp */
+ sync_dsp ();
+
+ if (!quiet_mode)
+ fprintf (stderr, "Playing Creative Labs Voice file ...\n");
+
+ /* first we waste the rest of header, ugly but we don't need seek */
+ while (ofs > abuf_size)
+ {
+ read (fd, (char *) audiobuf, abuf_size);
+ ofs -= abuf_size;
+ }
+ if (ofs)
+ read (fd, audiobuf, ofs);
+
+ /* .voc files are 8 bit (now) */
+ samplesize = VOC_SAMPLESIZE;
+ ioctl (audio, SNDCTL_DSP_SETFMT, &samplesize);
+ if (samplesize != VOC_SAMPLESIZE)
+ {
+ fprintf (stderr, "%s: unable to set 8 bit sample size!\n", command);
+ exit (-1);
+ }
+ /* and there are MONO by default */
+ dsp_stereo = MODE_MONO;
+ ioctl (audio, SNDCTL_DSP_STEREO, &dsp_stereo);
+
+ in_buffer = nextblock = 0;
+ while (1)
+ {
+ Fill_the_buffer: /* need this for repeat */
+ if (in_buffer < 32)
+ {
+ /* move the rest of buffer to pos 0 and fill the audiobuf up */
+ if (in_buffer)
+ memcpy ((char *) audiobuf, data, in_buffer);
+ data = audiobuf;
+ if ((l =
+ read (fd, (char *) audiobuf + in_buffer,
+ abuf_size - in_buffer)) > 0)
+ in_buffer += l;
+ else if (!in_buffer)
+ {
+ /* the file is truncated, so simulate 'Terminator'
+ and reduce the datablock for save landing */
+ nextblock = audiobuf[0] = 0;
+ if (l == -1)
+ {
+ perror (name);
+ exit (-1);
+ }
+ }
+ }
+ while (!nextblock)
+ { /* this is a new block */
+ bp = (BlockType *) data;
+ COUNT (sizeof (BlockType));
+ nextblock = DATALEN (bp);
+ if (output && !quiet_mode)
+ fprintf (stderr, "\n"); /* write /n after ASCII-out */
+ output = 0;
+ switch (bp->type)
+ {
+ case 0:
+ d_printf ((stderr, "Terminator\n"));
+ return; /* VOC-file stop */
+ case 1:
+ vd = (Voice_data *) data;
+ COUNT (sizeof (Voice_data));
+ /* we need a SYNC, before we can set new SPEED, STEREO ... */
+ sync_dsp ();
+
+ if (!was_extended)
+ {
+ dsp_speed = (int) (vd->tc);
+ dsp_speed = 1000000 / (256 - dsp_speed);
+ d_printf ((stderr, "Voice data %d Hz\n", dsp_speed));
+ if (vd->pack)
+ { /* /dev/dsp can't it */
+ fprintf (stderr, "%s: can't play packed .voc files\n",
+ command);
+ return;
+ }
+ if (dsp_stereo)
+ { /* if we are in Stereo-Mode, switch back */
+ dsp_stereo = MODE_MONO;
+ ioctl (audio, SNDCTL_DSP_STEREO, &dsp_stereo);
+ }
+ }
+ else
+ { /* there was extended block */
+ if (one_chn) /* if one Stereo fails, why test another ? */
+ dsp_stereo = MODE_MONO;
+ else if (dsp_stereo)
+ { /* want Stereo */
+ /* shit, my MACRO dosn't work here */
+#ifdef OSS_VERSION
+ if (ioctl (audio, SNDCTL_DSP_STEREO, &dsp_stereo) < 0)
+ {
+#else
+ if (dsp_stereo !=
+ ioctl (audio, SNDCTL_DSP_STEREO, dsp_stereo))
+ {
+#endif
+ dsp_stereo = MODE_MONO;
+ fprintf (stderr,
+ "%s: can't play in Stereo; playing only one channel\n",
+ command);
+ one_chn = 1;
+ }
+ }
+ was_extended = 0;
+ }
+ set_dsp_speed (&dsp_speed);
+ break;
+ case 2: /* nothing to do, pure data */
+ d_printf ((stderr, "Voice continuation\n"));
+ break;
+ case 3: /* a silence block, no data, only a count */
+ sp = (u_short *) data;
+ COUNT (sizeof (u_short));
+ dsp_speed = (int) (*data);
+ COUNT (1);
+ dsp_speed = 1000000 / (256 - dsp_speed);
+ sync_dsp ();
+ set_dsp_speed (&dsp_speed);
+ silence = (((u_long) * sp) * 1000) / dsp_speed;
+ d_printf ((stderr, "Silence for %ld ms\n", silence));
+ write_zeros (*sp);
+ break;
+ case 4: /* a marker for syncronisation, no effect */
+ sp = (u_short *) data;
+ COUNT (sizeof (u_short));
+ d_printf ((stderr, "Marker %d\n", *sp));
+ break;
+ case 5: /* ASCII text, we copy to stderr */
+ output = 1;
+ d_printf ((stderr, "ASCII - text :\n"));
+ break;
+ case 6: /* repeat marker, says repeatcount */
+ /* my specs don't say it: maybe this can be recursive, but
+ I don't think somebody use it */
+ repeat = *(u_short *) data;
+ COUNT (sizeof (u_short));
+ d_printf ((stderr, "Repeat loop %d times\n", repeat));
+ if (filepos >= 0) /* if < 0, one seek fails, why test another */
+ if ((filepos = lseek (fd, 0, 1)) < 0)
+ {
+ fprintf (stderr,
+ "%s: can't play loops; %s isn't seekable\n",
+ command, name);
+ repeat = 0;
+ }
+ else
+ filepos -= in_buffer; /* set filepos after repeat */
+ else
+ repeat = 0;
+ break;
+ case 7: /* ok, lets repeat that be rewinding tape */
+ if (repeat)
+ {
+ if (repeat != 0xFFFF)
+ {
+ d_printf ((stderr, "Repeat loop %d\n", repeat));
+ --repeat;
+ }
+ else
+ d_printf ((stderr, "Neverending loop\n"));
+ lseek (fd, filepos, 0);
+ in_buffer = 0; /* clear the buffer */
+ goto Fill_the_buffer;
+ }
+ else
+ d_printf ((stderr, "End repeat loop\n"));
+ break;
+ case 8: /* the extension to play Stereo, I have SB 1.0 :-( */
+ was_extended = 1;
+ eb = (Ext_Block *) data;
+ COUNT (sizeof (Ext_Block));
+ dsp_speed = (int) (eb->tc);
+ dsp_speed = 256000000L / (65536 - dsp_speed);
+ dsp_stereo = eb->mode;
+ if (dsp_stereo == MODE_STEREO)
+ dsp_speed = dsp_speed >> 1;
+ if (eb->pack)
+ { /* /dev/dsp can't it */
+ fprintf (stderr, "%s: can't play packed .voc files\n",
+ command);
+ return;
+ }
+ d_printf ((stderr, "Extended block %s %d Hz\n",
+ (eb->mode ? "Stereo" : "Mono"), dsp_speed));
+ break;
+ default:
+ fprintf (stderr, "%s: unknown blocktype %d. terminate.\n",
+ command, bp->type);
+ return;
+ } /* switch (bp->type) */
+ } /* while (! nextblock) */
+ /* put nextblock data bytes to dsp */
+ l = min (in_buffer, nextblock);
+ if (l)
+ {
+ if (output && !quiet_mode)
+ write (2, data, l); /* to stderr */
+ else
+ {
+ real_l = one_chn ? one_channel (data, l, one_chn, 0) : l;
+ if (write (audio, data, real_l) != real_l)
+ {
+ perror (AUDIO);
+ exit (-1);
+ }
+ }
+ COUNT (l);
+ }
+ } /* while(1) */
+}
+
+/* that was a big one, perhaps somebody split it :-) */
+
+/* setting the globals for playing raw data */
+void
+init_raw_data (void)
+{
+ timelimit = raw_info.timelimit;
+ dsp_speed = raw_info.dsp_speed;
+ dsp_stereo = raw_info.dsp_stereo;
+ samplesize = raw_info.samplesize;
+}
+
+/* calculate the data count to read from/to dsp */
+u_long
+calc_count (void)
+{
+ u_long count;
+
+ if (!timelimit)
+ count = 0x7fffffff;
+ else
+ {
+ count = timelimit * dsp_speed;
+ if (dsp_stereo)
+ count *= 2;
+ if (samplesize != 8)
+ count *= 2;
+ }
+ return count;
+}
+
+/* write a .VOC-header */
+void
+start_voc (int fd, u_long cnt)
+{
+ VocHeader vh;
+ BlockType bt;
+ Voice_data vd;
+ Ext_Block eb;
+
+ strcpy ((char *) vh.magic, MAGIC_STRING);
+ vh.headerlen = sizeof (VocHeader);
+ vh.version = ACTUAL_VERSION;
+ vh.coded_ver = 0x1233 - ACTUAL_VERSION;
+
+ write (fd, &vh, sizeof (VocHeader));
+
+ if (dsp_stereo)
+ {
+ /* write a extended block */
+ bt.type = 8;
+ bt.datalen = 4;
+ bt.datalen_m = bt.datalen_h = 0;
+ write (fd, &bt, sizeof (BlockType));
+ eb.tc = (u_short) (65536 - 256000000L / (dsp_speed << 1));
+ eb.pack = 0;
+ eb.mode = 1;
+ write (fd, &eb, sizeof (Ext_Block));
+ }
+ bt.type = 1;
+ cnt += sizeof (Voice_data); /* Voice_data block follows */
+ bt.datalen = (u_char) (cnt & 0xFF);
+ bt.datalen_m = (u_char) ((cnt & 0xFF00) >> 8);
+ bt.datalen_h = (u_char) ((cnt & 0xFF0000) >> 16);
+ write (fd, &bt, sizeof (BlockType));
+ vd.tc = (u_char) (256 - (1000000 / dsp_speed));
+ vd.pack = 0;
+ write (fd, &vd, sizeof (Voice_data));
+}
+
+/* write a WAVE-header */
+void
+start_wave (int fd, u_long cnt)
+{
+ WaveHeader wh;
+
+ wh.main_chunk = RIFF;
+ wh.length = cnt + sizeof (WaveHeader) - 8;
+ wh.chunk_type = WAVE;
+ wh.sub_chunk = FMT;
+ wh.sc_len = 16;
+ wh.format = PCM_CODE;
+ wh.modus = dsp_stereo ? 2 : 1;
+ wh.sample_fq = dsp_speed;
+ wh.byte_p_spl = ((samplesize == 8) ? 1 : 2) * (dsp_stereo ? 2 : 1);
+ wh.byte_p_sec = dsp_speed * wh.modus * wh.byte_p_spl;
+ wh.bit_p_spl = samplesize;
+ wh.data_chunk = DATA;
+ wh.data_length = cnt;
+ write (fd, &wh, sizeof (WaveHeader));
+}
+
+/* closing .VOC */
+void
+end_voc (int fd)
+{
+ char dummy = 0; /* Write a Terminator */
+ write (fd, &dummy, 1);
+ if (fd != 1)
+ close (fd);
+}
+
+void
+end_wave_raw (int fd)
+{ /* only close output */
+ if (fd != 1)
+ close (fd);
+}
+
+void
+start_snd (int fd, u_long count)
+{
+ SndHeader snd;
+ char *sndinfo = "Recorded by vrec\000";
+
+ snd.magic = SND_MAGIC;
+ snd.dataLocation = sizeof (SndHeader) + strlen (sndinfo);
+ snd.dataSize = count;
+ switch (samplesize)
+ {
+ case 8:
+ snd.dataFormat = SND_FORMAT_LINEAR_8;
+ break;
+ case 16:
+ snd.dataFormat = SND_FORMAT_LINEAR_16;
+ break;
+ default:
+ fprintf (stderr,
+ "%d bit: unsupported sample size for NeXt sound file!\n",
+ samplesize);
+ exit (-1);
+ }
+ snd.samplingRate = dsp_speed;
+ snd.channelCount = dsp_stereo ? 2 : 1;
+
+ write (fd, &snd, sizeof (SndHeader));
+ write (fd, sndinfo, strlen (sndinfo));
+}
+
+void
+end_snd (int fd)
+{
+ if (fd != 1)
+ close (fd);
+}
+
+/* playing/recording raw data, this proc handels WAVE files and
+ recording .VOCs (as one block) */
+void
+recplay (int fd, int loaded, u_long count, int rtype, char *name)
+{
+ int l, real_l;
+ u_long c;
+ char one_chn = 0;
+ char to_8 = 0;
+ int tmps;
+
+ sync_dsp ();
+ tmps = samplesize;
+ ioctl (audio, SNDCTL_DSP_SETFMT, &tmps);
+ if (tmps != samplesize)
+ {
+ fprintf (stderr, "%s: unable to set %d bit sample size",
+ command, samplesize);
+ if (samplesize == 16)
+ {
+ samplesize = 8;
+ ioctl (audio, SNDCTL_DSP_SETFMT, &samplesize);
+ if (samplesize != 8)
+ {
+ fprintf (stderr, "%s: unable to set 8 bit sample size!\n",
+ command);
+ exit (-1);
+ }
+ fprintf (stderr, "; playing 8 bit\n");
+ to_8 = 1;
+ }
+ else
+ {
+ fprintf (stderr, "\n");
+ exit (-1);
+ }
+ }
+#ifdef OSS_VERSION
+ if (ioctl (audio, SNDCTL_DSP_STEREO, &dsp_stereo) < 0)
+ {
+#else
+ if (dsp_stereo != ioctl (audio, SNDCTL_DSP_STEREO, dsp_stereo))
+ {
+#endif
+ if (direction == PLAY)
+ {
+ fprintf (stderr,
+ "%s: can't play in Stereo; playing only one channel\n",
+ command);
+ dsp_stereo = MODE_MONO;
+ one_chn = 1;
+ }
+ else
+ {
+ fprintf (stderr, "%s: can't record in Stereo\n", command);
+ exit (-1);
+ }
+ }
+ set_dsp_speed (&dsp_speed);
+ if (!quiet_mode)
+ {
+ fprintf (stderr, "%s %s : ",
+ (direction == PLAY) ? "Playing" : "Recording",
+ fmt_rec_table[rtype].what);
+ if (samplesize != 8)
+ fprintf (stderr, "%d bit, ", samplesize);
+ fprintf (stderr, "Speed %d Hz ", dsp_speed);
+ fprintf (stderr, "%d bits ", samplesize);
+ fprintf (stderr, "%s ...\n", dsp_stereo ? "Stereo" : "Mono");
+ }
+
+ abuf_size = -1;
+#if 0
+/*
+ * There is no idea in using SNDCTL_DSP_GETBLKSIZE in applications like this.
+ * Using some fixed local buffer size will work equally well.
+ */
+ ioctl (audio, SNDCTL_DSP_GETBLKSIZE, &abuf_size);
+ if (abuf_size == -1)
+ {
+ perror (AUDIO);
+ exit (-1);
+ }
+#endif
+
+ zbuf_size = 256;
+ if ((audiobuf = (u_char *) malloc (abuf_size)) == NULL ||
+ (zerobuf = (u_char *) malloc (zbuf_size)) == NULL)
+ {
+ fprintf (stderr, "%s: unable to allocate input/output buffer\n",
+ command);
+ exit (-1);
+ }
+ memset ((char *) zerobuf, 128, zbuf_size);
+
+ if (direction == PLAY)
+ {
+ while (count)
+ {
+ c = count;
+
+ if (c > abuf_size)
+ c = abuf_size;
+
+ if ((l = read (fd, (char *) audiobuf + loaded, c - loaded)) > 0)
+ {
+ l += loaded;
+ loaded = 0; /* correct the count; ugly but ... */
+ real_l = (one_chn
+ || to_8) ? one_channel (audiobuf, l, one_chn,
+ to_8) : l;
+
+ /* change byte order if necessary */
+ if (convert && (samplesize == 16))
+ {
+ long i;
+
+ for (i = 0; i < real_l; i += 2)
+ *((short *) (audiobuf + i)) =
+ htons (*((short *) (audiobuf + i)));
+ }
+
+ if (write (audio, (char *) audiobuf, real_l) != real_l)
+ {
+ perror (AUDIO);
+ exit (-1);
+ }
+ count -= l;
+ }
+ else
+ {
+ if (l == -1)
+ {
+ perror (name);
+ exit (-1);
+ }
+ count = 0; /* Stop */
+ }
+ } /* while (count) */
+ }
+ else
+ { /* we are recording */
+
+ while (count)
+ {
+ c = count;
+ if (c > abuf_size)
+ c = abuf_size;
+
+ if ((l = read (audio, (char *) audiobuf, c)) > 0)
+ {
+ if (write (fd, (char *) audiobuf, l) != l)
+ {
+ perror (name);
+ exit (-1);
+ }
+ count -= l;
+ }
+
+ if (l == -1)
+ {
+ perror (AUDIO);
+ exit (-1);
+ }
+ } /* While count */
+ }
+}
+
+/*
+ let's play or record it (record_type says VOC/WAVE/raw)
+*/
+void
+record_play (char *name)
+{
+ int fd, ofs;
+
+ if (direction == PLAY)
+ {
+
+ if (!name)
+ {
+ fd = 0;
+ name = "stdin";
+ }
+ else if ((fd = open (name, O_RDONLY, 0)) == -1)
+ {
+ perror (name);
+ exit (-1);
+ }
+ /* Read the smallest header first, then the
+ missing bytes for the next, etc. */
+
+ /* read SND-header */
+ read (fd, (char *) audiobuf, sizeof (SndHeader));
+ if (test_sndfile (audiobuf, fd) >= 0)
+ recplay (fd, 0, count, SND_FMT, name);
+
+ else
+ {
+ /* read VOC-Header */
+ read (fd, (char *) audiobuf + sizeof (SndHeader),
+ sizeof (VocHeader) - sizeof (SndHeader));
+ if ((ofs = test_vocfile (audiobuf)) >= 0)
+ vplay (fd, ofs, name);
+
+ else
+ {
+ /* read bytes for WAVE-header */
+ read (fd, (char *) audiobuf + sizeof (VocHeader),
+ sizeof (WaveHeader) - sizeof (VocHeader));
+ if (test_wavefile (audiobuf) >= 0)
+ recplay (fd, 0, count, WAVE_FMT, name);
+ else
+ {
+ /* should be raw data */
+ init_raw_data ();
+ count = calc_count ();
+ recplay (fd, sizeof (WaveHeader), count, RAW_DATA, name);
+ }
+ }
+ }
+ if (fd != 0)
+ close (fd);
+ }
+ else
+ { /* recording ... */
+ if (!name)
+ {
+ fd = 1;
+ name = "stdout";
+ }
+ else
+ {
+ if ((fd = open (name, O_WRONLY | O_CREAT | O_TRUNC, 0666)) == -1)
+ {
+ perror (name);
+ exit (-1);
+ }
+ }
+ count = calc_count () & 0xFFFFFFFE;
+ /* WAVE-file should be even (I'm not sure), but wasting one byte
+ isn't a problem (this can only be in 8 bit mono) */
+ if (fmt_rec_table[record_type].start)
+ fmt_rec_table[record_type].start (fd, count);
+ recplay (fd, 0, count, record_type, name);
+ fmt_rec_table[record_type].end (fd);
+ }
+}
diff --git a/tutorials/sndkit/dsp/vplay_vxworks/Makefile b/tutorials/sndkit/dsp/vplay_vxworks/Makefile
new file mode 100644
index 0000000..80c0746
--- /dev/null
+++ b/tutorials/sndkit/dsp/vplay_vxworks/Makefile
@@ -0,0 +1,12 @@
+
+# Makefile for OSS/VxWorks
+#
+
+CPU = I80486
+TOOL = gnu
+
+include $(WIND_BASE)/target/h/make/defs.bsp
+include $(WIND_BASE)/target/h/make/make.$(CPU)$(TOOL)
+include $(WIND_BASE)/target/h/make/defs.$(WIND_HOST_TYPE)
+
+exe: vplay.o
diff --git a/tutorials/sndkit/dsp/vplay_vxworks/fmtheaders.h b/tutorials/sndkit/dsp/vplay_vxworks/fmtheaders.h
new file mode 100644
index 0000000..4ba1302
--- /dev/null
+++ b/tutorials/sndkit/dsp/vplay_vxworks/fmtheaders.h
@@ -0,0 +1,119 @@
+#ifndef _FMTHEADERS_H
+#define _FMTHEADERS_H 1
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+
+#include <sys/types.h>
+
+/* Definitions for .VOC files */
+
+#define MAGIC_STRING "Creative Voice File\x1A"
+#define ACTUAL_VERSION 0x010A
+#define VOC_SAMPLESIZE 8
+
+#define MODE_MONO 0
+#define MODE_STEREO 1
+
+#define DATALEN(bp) ((u_long)(bp->datalen) | \
+ ((u_long)(bp->datalen_m) << 8) | \
+ ((u_long)(bp->datalen_h) << 16) )
+
+typedef struct _vocheader
+{
+ u_char magic[20]; /* must be MAGIC_STRING */
+ u_short headerlen; /* Headerlength, should be 0x1A */
+ u_short version; /* VOC-file version */
+ u_short coded_ver; /* 0x1233-version */
+}
+VocHeader;
+
+typedef struct _blocktype
+{
+ u_char type;
+ u_char datalen; /* low-byte */
+ u_char datalen_m; /* medium-byte */
+ u_char datalen_h; /* high-byte */
+}
+BlockType;
+
+typedef struct _voice_data
+{
+ u_char tc;
+ u_char pack;
+}
+Voice_data;
+
+typedef struct _ext_block
+{
+ u_short tc;
+ u_char pack;
+ u_char mode;
+}
+Ext_Block;
+
+
+/* Definitions for Microsoft WAVE format */
+
+#define RIFF 0x46464952
+#define WAVE 0x45564157
+#define FMT 0x20746D66
+#define DATA 0x61746164
+#define PCM_CODE 1
+#define WAVE_MONO 1
+#define WAVE_STEREO 2
+
+/* it's in chunks like .voc and AMIGA iff, but my source say there
+ are in only in this combination, so I combined them in one header;
+ it works on all WAVE-file I have
+*/
+typedef struct _waveheader
+{
+ u_long main_chunk; /* 'RIFF' */
+ u_long length; /* filelen */
+ u_long chunk_type; /* 'WAVE' */
+
+ u_long sub_chunk; /* 'fmt ' */
+ u_long sc_len; /* length of sub_chunk, =16 */
+ u_short format; /* should be 1 for PCM-code */
+ u_short modus; /* 1 Mono, 2 Stereo */
+ u_long sample_fq; /* frequence of sample */
+ u_long byte_p_sec;
+ u_short byte_p_spl; /* samplesize; 1 or 2 bytes */
+ u_short bit_p_spl; /* 8, 12 or 16 bit */
+
+ u_long data_chunk; /* 'data' */
+ u_long data_length; /* samplecount */
+}
+WaveHeader;
+
+typedef struct
+{
+ long magic; /* must be equal to SND_MAGIC */
+ long dataLocation; /* Offset or pointer to the raw data */
+ long dataSize; /* Number of bytes of data in the raw data */
+ long dataFormat; /* The data format code */
+ long samplingRate; /* The sampling rate */
+ long channelCount; /* The number of channels */
+}
+SndHeader;
+
+#define SND_MAGIC ((long int)0x2e736e64)
+
+#define SND_FORMAT_UNSPECIFIED (0)
+#define SND_FORMAT_MULAW_8 (1)
+#define SND_FORMAT_LINEAR_8 (2)
+#define SND_FORMAT_LINEAR_16 (3)
+#define SND_FORMAT_LINEAR_24 (4)
+#define SND_FORMAT_LINEAR_32 (5)
+#define SND_FORMAT_FLOAT (6)
+
+#endif
diff --git a/tutorials/sndkit/dsp/vplay_vxworks/vplay_vxworks.c b/tutorials/sndkit/dsp/vplay_vxworks/vplay_vxworks.c
new file mode 100644
index 0000000..b10d35d
--- /dev/null
+++ b/tutorials/sndkit/dsp/vplay_vxworks/vplay_vxworks.c
@@ -0,0 +1,694 @@
+/*
+ vplay.c - plays
+ CREATIVE LABS VOICE-files, Microsoft WAVE-files and raw data
+
+ Autor: Michael Beck - beck@informatik.hu-berlin.de
+
+ Modified for VxWorks by 4Front Technologies
+
+ Usage: int play_wave (char *filename);
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <ioLib.h>
+#include <sys/ioctl.h>
+#include "soundcard.h" /* Edit this to contain the right location of soundcard.h */
+#include "fmtheaders.h"
+
+/*
+ * The following defines break support for .snd files. Implement these routines
+ * properly if you need to play them.
+ */
+#define htonl(x) x
+#define htons(x) x
+
+#define DEFAULT_DSP_SPEED 8000
+
+#define AUDIO "/oss/dsp"
+
+#ifndef min
+#define min(a,b) ((a) <= (b) ? (a) : (b))
+#endif
+#define d_printf(x) if (verbose_mode) fprintf x
+
+#define VOC_FMT 0
+#define WAVE_FMT 1
+#define RAW_DATA 2
+#define SND_FMT 3
+
+/* global data */
+
+static int timelimit = 0, dsp_speed = DEFAULT_DSP_SPEED, dsp_stereo = 0;
+static int samplesize = 8;
+static int quiet_mode = 0;
+static int verbose_mode = 0;
+static int convert = 0;
+static u_long count;
+static int audio, abuf_size;
+static int omode;
+static u_char audiobuf[512];
+static int vocminor, vocmajor; /* .VOC version */
+
+/* defaults for playing raw data */
+static struct
+{
+ int timelimit, dsp_speed, dsp_stereo, samplesize;
+}
+raw_info =
+{
+0, DEFAULT_DSP_SPEED, 0, 8};
+
+/* needed prototypes */
+
+static int player (char *name);
+static u_long calc_count ();
+
+int
+play_wave (char *filename)
+{
+ omode = O_WRONLY;
+
+ quiet_mode = 1;
+
+ audio = open (AUDIO, omode, 0);
+ if (audio == -1)
+ {
+ perror (AUDIO);
+ return -1;
+ }
+
+ if (player (filename) < 0)
+ return -1;
+ close (audio);
+ return 0;
+}
+
+/*
+ test, if it is a .VOC file and return >=0 if ok (this is the length of rest)
+ < 0 if not
+*/
+static int
+test_vocfile (void *buffer)
+{
+ VocHeader *vp = buffer;
+ if (!strcmp ((const char *) vp->magic, MAGIC_STRING))
+ {
+ vocminor = vp->version & 0xFF;
+ vocmajor = vp->version / 256;
+ if (vp->version != (0x1233 - vp->coded_ver))
+ return -2; /* coded version mismatch */
+ return vp->headerlen - sizeof (VocHeader); /* 0 mostly */
+ }
+ return -1; /* magic string fail */
+}
+
+/*
+ test, if it's a .WAV file, 0 if ok (and set the speed, stereo etc.)
+ < 0 if not
+*/
+static int
+test_wavefile (void *buffer)
+{
+ WaveHeader *wp = buffer;
+ if (wp->main_chunk == RIFF && wp->chunk_type == WAVE &&
+ wp->sub_chunk == FMT && wp->data_chunk == DATA)
+ {
+ if (wp->format != PCM_CODE)
+ {
+ fprintf (stderr, "Can't play non PCM-coded WAVE-files\n");
+ return -1;
+ }
+ if (wp->modus > 2)
+ {
+ fprintf (stderr, "Can't play WAVE-files with %d tracks\n",
+ wp->modus);
+ return -1;
+ }
+ dsp_stereo = (wp->modus == WAVE_STEREO) ? 1 : 0;
+ samplesize = wp->bit_p_spl;
+ dsp_speed = wp->sample_fq;
+ count = wp->data_length;
+ return 0;
+ }
+ return -1;
+}
+
+/*
+* test, if it's a .SND file, 0 if ok (and set the speed, stereo etc.)
+* < 0 if not
+*/
+static int
+test_sndfile (void *buffer, int fd)
+{
+ long infolen;
+ char *info;
+ SndHeader *snd = buffer;
+
+ if (snd->magic == SND_MAGIC)
+ convert = 0;
+ else
+ {
+ if (htonl (snd->magic) == SND_MAGIC)
+ {
+ convert = 1;
+ snd->dataLocation = htonl (snd->dataLocation);
+ snd->dataSize = htonl (snd->dataSize);
+ snd->dataFormat = htonl (snd->dataFormat);
+ snd->samplingRate = htonl (snd->samplingRate);
+ snd->channelCount = htonl (snd->channelCount);
+ }
+ else
+ {
+ /* No SoundFile */
+ return (-1);
+ }
+ }
+ switch (snd->dataFormat)
+ {
+ case SND_FORMAT_LINEAR_8:
+ samplesize = 8;
+ break;
+ case SND_FORMAT_LINEAR_16:
+ samplesize = 16;
+ break;
+ default:
+ fprintf (stderr, "Unsupported SND_FORMAT\n");
+ return -1;
+ }
+
+ dsp_stereo = (snd->channelCount == 2) ? 1 : 0;
+ dsp_speed = snd->samplingRate;
+ count = snd->dataSize;
+
+ /* read Info-Strings */
+ infolen = snd->dataLocation - sizeof (SndHeader);
+ info = (char *) malloc (infolen);
+ read (fd, info, infolen);
+ if (!quiet_mode)
+ fprintf (stderr, "SoundFile Info: %s\n", info);
+ free (info);
+
+ return 0;
+}
+
+
+/* if need a SYNC,
+ (is needed if we plan to change speed, stereo ... during output)
+*/
+static void
+sync_dsp (void)
+{
+}
+
+/* setting the speed for output */
+static int
+set_dsp_speed (int *dsp_speed)
+{
+ if (ioctl (audio, SNDCTL_DSP_SPEED, dsp_speed) < 0)
+ {
+ fprintf (stderr, "Unable to set audio speed\n");
+ perror (AUDIO);
+ return -1;
+ }
+ return 0;
+}
+
+/* if to_mono:
+ compress 8 bit stereo data 2:1, needed if we want play 'one track';
+ this is usefull, if you habe SB 1.0 - 2.0 (I have 1.0) and want
+ hear the sample (in Mono)
+ if to_8:
+ compress 16 bit to 8 by using hi-byte; wave-files use signed words,
+ so we need to convert it in "unsigned" sample (0x80 is now zero)
+
+ WARNING: this procedure can't compress 16 bit stereo to 16 bit mono,
+ because if you have a 16 (or 12) bit card you should have
+ stereo (or I'm wrong ?? )
+ */
+static u_long
+one_channel (u_char * buf, u_long l, char to_mono, char to_8)
+{
+ register u_char *w = buf;
+ register u_char *w2 = buf;
+ char ofs = 0;
+ u_long incr = 0;
+ u_long c, ret;
+
+ if (to_mono)
+ ++incr;
+ if (to_8)
+ {
+ ++incr;
+ ++w2;
+ ofs = 128;
+ }
+ ret = c = l >> incr;
+ incr = incr << 1;
+
+ while (c--)
+ {
+ *w++ = *w2 + ofs;
+ w2 += incr;
+ }
+ return ret;
+}
+
+/*
+ ok, let's play a .voc file
+*/
+static int
+vplay (int fd, int ofs, char *name)
+{
+ int l, real_l;
+ BlockType *bp;
+ Voice_data *vd;
+ Ext_Block *eb;
+ u_long nextblock, in_buffer;
+ u_char *data = audiobuf;
+ char was_extended = 0, output = 0;
+ u_short *sp, repeat = 0;
+ u_long silence;
+ int filepos = 0;
+ char one_chn = 0;
+
+#define COUNT(x) nextblock -= x; in_buffer -=x ;data += x
+
+ /* first SYNC the dsp */
+ sync_dsp ();
+
+ if (!quiet_mode)
+ fprintf (stderr, "Playing Creative Labs Voice file ...\n");
+
+ /* first we waste the rest of header, ugly but we don't need seek */
+ while (ofs > abuf_size)
+ {
+ read (fd, (char *) audiobuf, abuf_size);
+ ofs -= abuf_size;
+ }
+ if (ofs)
+ read (fd, audiobuf, ofs);
+
+ /* .voc files are 8 bit (now) */
+ samplesize = VOC_SAMPLESIZE;
+ ioctl (audio, SNDCTL_DSP_SETFMT, &samplesize);
+ if (samplesize != VOC_SAMPLESIZE)
+ {
+ fprintf (stderr, "Unable to set 8 bit sample size!\n");
+ return -1;
+ }
+ /* and there are MONO by default */
+ dsp_stereo = MODE_MONO;
+ ioctl (audio, SNDCTL_DSP_STEREO, &dsp_stereo);
+
+ in_buffer = nextblock = 0;
+ while (1)
+ {
+ Fill_the_buffer: /* need this for repeat */
+ if (in_buffer < 32)
+ {
+ /* move the rest of buffer to pos 0 and fill the audiobuf up */
+ if (in_buffer)
+ memcpy ((char *) audiobuf, data, in_buffer);
+ data = audiobuf;
+ if ((l =
+ read (fd, (char *) audiobuf + in_buffer,
+ abuf_size - in_buffer)) > 0)
+ in_buffer += l;
+ else if (!in_buffer)
+ {
+ /* the file is truncated, so simulate 'Terminator'
+ and reduce the datablock for save landing */
+ nextblock = audiobuf[0] = 0;
+ if (l == -1)
+ {
+ perror (name);
+ return -1;
+ }
+ }
+ }
+ while (!nextblock)
+ { /* this is a new block */
+ bp = (BlockType *) data;
+ COUNT (sizeof (BlockType));
+ nextblock = DATALEN (bp);
+ if (output && !quiet_mode)
+ fprintf (stderr, "\n"); /* write /n after ASCII-out */
+ output = 0;
+ switch (bp->type)
+ {
+ case 0:
+ d_printf ((stderr, "Terminator\n"));
+ return -1; /* VOC-file stop */
+ case 1:
+ vd = (Voice_data *) data;
+ COUNT (sizeof (Voice_data));
+ /* we need a SYNC, before we can set new SPEED, STEREO ... */
+ sync_dsp ();
+
+ if (!was_extended)
+ {
+ dsp_speed = (int) (vd->tc);
+ dsp_speed = 1000000 / (256 - dsp_speed);
+ d_printf ((stderr, "Voice data %d Hz\n", dsp_speed));
+ if (vd->pack)
+ { /* /dev/dsp can't it */
+ fprintf (stderr, "Can't play packed .voc files\n");
+ return -1;
+ }
+ if (dsp_stereo)
+ { /* if we are in Stereo-Mode, switch back */
+ dsp_stereo = MODE_MONO;
+ ioctl (audio, SNDCTL_DSP_STEREO, &dsp_stereo);
+ }
+ }
+ else
+ { /* there was extended block */
+ if (one_chn) /* if one Stereo fails, why test another ? */
+ dsp_stereo = MODE_MONO;
+ else if (dsp_stereo)
+ { /* want Stereo */
+ /* shit, my MACRO dosn't work here */
+#ifdef OSS_VERSION
+ if (ioctl (audio, SNDCTL_DSP_STEREO, &dsp_stereo) < 0)
+ {
+#else
+ if (dsp_stereo !=
+ ioctl (audio, SNDCTL_DSP_STEREO, dsp_stereo))
+ {
+#endif
+ dsp_stereo = MODE_MONO;
+ fprintf (stderr,
+ "Can't play in Stereo; playing only one channel\n");
+ one_chn = 1;
+ }
+ }
+ was_extended = 0;
+ }
+ if (set_dsp_speed (&dsp_speed) < 0)
+ return -1;
+ break;
+ case 2: /* nothing to do, pure data */
+ d_printf ((stderr, "Voice continuation\n"));
+ break;
+ case 3: /* a silence block, no data, only a count */
+ sp = (u_short *) data;
+ COUNT (sizeof (u_short));
+ dsp_speed = (int) (*data);
+ COUNT (1);
+ dsp_speed = 1000000 / (256 - dsp_speed);
+ sync_dsp ();
+ if (set_dsp_speed (&dsp_speed) < 0)
+ return -1;
+ silence = (((u_long) * sp) * 1000) / dsp_speed;
+ d_printf ((stderr, "Silence for %ld ms\n", silence));
+ break;
+ case 4: /* a marker for syncronisation, no effect */
+ sp = (u_short *) data;
+ COUNT (sizeof (u_short));
+ d_printf ((stderr, "Marker %d\n", *sp));
+ break;
+ case 5: /* ASCII text, we copy to stderr */
+ output = 1;
+ d_printf ((stderr, "ASCII - text :\n"));
+ break;
+ case 6: /* repeat marker, says repeatcount */
+ /* my specs don't say it: maybe this can be recursive, but
+ I don't think somebody use it */
+ repeat = *(u_short *) data;
+ COUNT (sizeof (u_short));
+ d_printf ((stderr, "Repeat loop %d times\n", repeat));
+ if (filepos >= 0) /* if < 0, one seek fails, why test another */
+ if ((filepos = lseek (fd, 0, 1)) < 0)
+ {
+ fprintf (stderr,
+ "Can't play loops; %s isn't seekable\n", name);
+ repeat = 0;
+ }
+ else
+ filepos -= in_buffer; /* set filepos after repeat */
+ else
+ repeat = 0;
+ break;
+ case 7: /* ok, lets repeat that be rewinding tape */
+ if (repeat)
+ {
+ if (repeat != 0xFFFF)
+ {
+ d_printf ((stderr, "Repeat loop %d\n", repeat));
+ --repeat;
+ }
+ else
+ d_printf ((stderr, "Neverending loop\n"));
+ lseek (fd, filepos, 0);
+ in_buffer = 0; /* clear the buffer */
+ goto Fill_the_buffer;
+ }
+ else
+ d_printf ((stderr, "End repeat loop\n"));
+ break;
+ case 8: /* the extension to play Stereo, I have SB 1.0 :-( */
+ was_extended = 1;
+ eb = (Ext_Block *) data;
+ COUNT (sizeof (Ext_Block));
+ dsp_speed = (int) (eb->tc);
+ dsp_speed = 256000000L / (65536 - dsp_speed);
+ dsp_stereo = eb->mode;
+ if (dsp_stereo == MODE_STEREO)
+ dsp_speed = dsp_speed >> 1;
+ if (eb->pack)
+ { /* /dev/dsp can't it */
+ fprintf (stderr, "Can't play packed .voc files\n");
+ return -1;
+ }
+ d_printf ((stderr, "Extended block %s %d Hz\n",
+ (eb->mode ? "Stereo" : "Mono"), dsp_speed));
+ break;
+ default:
+ fprintf (stderr, "Unknown blocktype %d. terminate.\n",
+ bp->type);
+ return -1;
+ } /* switch (bp->type) */
+ } /* while (! nextblock) */
+ /* put nextblock data bytes to dsp */
+ l = min (in_buffer, nextblock);
+ if (l)
+ {
+ if (output && !quiet_mode)
+ write (2, data, l); /* to stderr */
+ else
+ {
+ real_l = one_chn ? one_channel (data, l, one_chn, 0) : l;
+ if (write (audio, data, real_l) != real_l)
+ {
+ perror (AUDIO);
+ return -1;
+ }
+ }
+ COUNT (l);
+ }
+ } /* while(1) */
+}
+
+/* that was a big one, perhaps somebody split it :-) */
+
+/* setting the globals for playing raw data */
+static void
+init_raw_data (void)
+{
+ timelimit = raw_info.timelimit;
+ dsp_speed = raw_info.dsp_speed;
+ dsp_stereo = raw_info.dsp_stereo;
+ samplesize = raw_info.samplesize;
+}
+
+/* calculate the data count to read from/to dsp */
+static u_long
+calc_count (void)
+{
+ u_long count;
+
+ if (!timelimit)
+ count = 0x7fffffff;
+ else
+ {
+ count = timelimit * dsp_speed;
+ if (dsp_stereo)
+ count *= 2;
+ if (samplesize != 8)
+ count *= 2;
+ }
+ return count;
+}
+
+/* playing raw data, this proc handels WAVE files and
+ .VOCs (as one block) */
+static int
+do_play (int fd, int loaded, u_long count, int rtype, char *name)
+{
+ int l, real_l;
+ u_long c;
+ char one_chn = 0;
+ char to_8 = 0;
+ int tmps;
+
+ sync_dsp ();
+ tmps = samplesize;
+ ioctl (audio, SNDCTL_DSP_SETFMT, &tmps);
+ if (tmps != samplesize)
+ {
+ fprintf (stderr, "Unable to set %d bit sample size", samplesize);
+ if (samplesize == 16)
+ {
+ samplesize = 8;
+ ioctl (audio, SNDCTL_DSP_SETFMT, &samplesize);
+ if (samplesize != 8)
+ {
+ fprintf (stderr, "Unable to set 8 bit sample size!\n");
+ return -1;
+ }
+ fprintf (stderr, "; playing 8 bit\n");
+ to_8 = 1;
+ }
+ else
+ {
+ fprintf (stderr, "\n");
+ return -1;
+ }
+ }
+#ifdef OSS_VERSION
+ if (ioctl (audio, SNDCTL_DSP_STEREO, &dsp_stereo) < 0)
+ {
+#else
+ if (dsp_stereo != ioctl (audio, SNDCTL_DSP_STEREO, dsp_stereo))
+ {
+#endif
+ fprintf (stderr, "Can't play in Stereo; playing only one channel\n");
+ dsp_stereo = MODE_MONO;
+ one_chn = 1;
+ }
+ if (set_dsp_speed (&dsp_speed) < 0)
+ return -1;
+
+ abuf_size = 512;
+
+ while (count)
+ {
+ c = count;
+
+ if (c > abuf_size)
+ c = abuf_size;
+
+ if ((l = read (fd, (char *) audiobuf + loaded, c - loaded)) > 0)
+ {
+ l += loaded;
+ loaded = 0; /* correct the count; ugly but ... */
+ real_l = (one_chn
+ || to_8) ? one_channel (audiobuf, l, one_chn, to_8) : l;
+
+ /* change byte order if necessary */
+ if (convert && (samplesize == 16))
+ {
+ long i;
+
+ for (i = 0; i < real_l; i += 2)
+ *((short *) (audiobuf + i)) =
+ htons (*((short *) (audiobuf + i)));
+ }
+
+ if (write (audio, (char *) audiobuf, real_l) != real_l)
+ {
+ perror (AUDIO);
+ return -1;
+ }
+ count -= l;
+ }
+ else
+ {
+ if (l == -1)
+ {
+ perror (name);
+ return -1;
+ }
+ count = 0; /* Stop */
+ }
+ } /* while (count) */
+
+ return 0;
+}
+
+/*
+ let's play)
+*/
+static int
+player (char *name)
+{
+ int fd, ofs;
+
+ if (!name)
+ {
+ fd = 0;
+ name = "stdin";
+ }
+ else if ((fd = open (name, O_RDONLY, 0)) == -1)
+ {
+ perror (name);
+ return -1;
+ }
+ /* Read the smallest header first, then the
+ missing bytes for the next, etc. */
+
+ /* read SND-header */
+ read (fd, (char *) audiobuf, sizeof (SndHeader));
+ if (test_sndfile (audiobuf, fd) >= 0)
+ {
+ if (do_play (fd, 0, count, SND_FMT, name) < 0)
+ return -1;
+ }
+
+ else
+ {
+ /* read VOC-Header */
+ read (fd, (char *) audiobuf + sizeof (SndHeader),
+ sizeof (VocHeader) - sizeof (SndHeader));
+ if ((ofs = test_vocfile (audiobuf)) >= 0)
+ {
+ if (vplay (fd, ofs, name) < 0)
+ return -1;
+ }
+
+ else
+ {
+ /* read bytes for WAVE-header */
+ read (fd, (char *) audiobuf + sizeof (VocHeader),
+ sizeof (WaveHeader) - sizeof (VocHeader));
+ if (test_wavefile (audiobuf) >= 0)
+ {
+ if (do_play (fd, 0, count, WAVE_FMT, name) < 0)
+ return -1;
+ }
+ else
+ {
+ /* should be raw data */
+ init_raw_data ();
+ count = calc_count ();
+ if (do_play (fd, sizeof (WaveHeader), count, RAW_DATA, name) <
+ 0)
+ return -1;
+ }
+ }
+ }
+ if (fd != 0)
+ close (fd);
+ return 0;
+}
+
+#if 0
+int
+main (int agrc, char *argv[])
+{
+ exit (play_wave (argv[1]));
+}
+#endif
diff --git a/tutorials/sndkit/libossmix_demo/Makefile b/tutorials/sndkit/libossmix_demo/Makefile
new file mode 100644
index 0000000..fd7a3a6
--- /dev/null
+++ b/tutorials/sndkit/libossmix_demo/Makefile
@@ -0,0 +1,13 @@
+CFLAGS=-I/usr/lib/oss/include
+LDFLAGS=-lossmix
+
+# Comment out the following line under Solaris
+#LDFLAGS += -lnsl -lresolv -lsocket
+
+PROGRAMS=ossmixlib_demo
+
+all: $(PROGRAMS)
+
+clean:
+ rm -f *.o *~ x y z $(PROGRAMS)
+
diff --git a/tutorials/sndkit/libossmix_demo/README b/tutorials/sndkit/libossmix_demo/README
new file mode 100644
index 0000000..4610b23
--- /dev/null
+++ b/tutorials/sndkit/libossmix_demo/README
@@ -0,0 +1,5 @@
+This simple program demonstrates how to use the libossmix library
+to access mixer settings (instead of making ioctl calls directly).
+
+Use the -h <host> and -p <port> command line arguments of libossmix_demo to
+connect to a remote ossmixd server running in a remote host.
diff --git a/tutorials/sndkit/libossmix_demo/ossmixlib_demo.c b/tutorials/sndkit/libossmix_demo/ossmixlib_demo.c
new file mode 100644
index 0000000..aa6b8b6
--- /dev/null
+++ b/tutorials/sndkit/libossmix_demo/ossmixlib_demo.c
@@ -0,0 +1,272 @@
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <sys/soundcard.h>
+#include <sys/libossmix.h>
+
+static void
+event_callback(ossmix_callback_parm_t *parms)
+{
+ switch (parms->event)
+ {
+ case OSSMIX_EVENT_VALUE:
+ printf("Value change, mixer=%d, ctl=%d, value=0x%08x\n", parms->p1, parms->p2, parms->p3);
+ break;
+
+ case OSSMIX_EVENT_NEWMIXER:
+ printf("Number of mixers increased to %d\n", parms->p1);
+ break;
+
+ default:
+ printf("Event callback %d, %d, %d, %d, %d\n", parms->event, parms->p1, parms->p2, parms->p3, parms->p4);
+ }
+}
+
+static void
+command_parser(char *line)
+{
+ char *p = line + strlen(line)-1;
+
+ if (*p == '\n')
+ *p=0;
+
+ if (strcmp(line, "exit")==0 || strcmp(line, "q")==0)
+ exit(0);
+}
+
+static void
+interactive_mode(void)
+{
+ int libfd=-1, maxdev, ndevs;
+ fd_set readfds;
+ ossmix_select_poll_t pollfunc;
+ struct timeval tmout;
+
+ libfd=ossmix_get_fd(&pollfunc);
+
+ ossmix_set_callback(event_callback);
+
+ printf("libfd=%d, func=%p\n", libfd, pollfunc);
+ printf("\n");
+ printf("> ");fflush(stdout);
+
+ if (libfd >= 0)
+ {
+ tmout.tv_sec = 0;
+ tmout.tv_usec = 100000; /* 0.1 sec */
+ }
+ else
+ {
+ tmout.tv_sec = 0;
+ tmout.tv_usec = 0;
+ }
+
+ while (1)
+ {
+ FD_ZERO (&readfds);
+
+ maxdev = 0;
+
+ FD_SET (0, &readfds); // Stdin
+ if (libfd > 0)
+ FD_SET (libfd, &readfds);
+
+ if (libfd > maxdev)
+ maxdev = libfd;
+
+ if ((ndevs =
+ select (maxdev + 1, &readfds, NULL, NULL, &tmout)) == -1)
+ {
+ perror ("select");
+ exit (-1);
+ }
+
+ if (ndevs == 0)
+ {
+ ossmix_timertick();
+ tmout.tv_sec = 0;
+ tmout.tv_usec = 100000; /* 0.1 sec */
+ }
+
+ if (FD_ISSET (0, &readfds)) /* Stdio */
+ {
+ char line[128];
+
+ if (fgets(line, sizeof(line)-1, stdin) != NULL)
+ {
+ command_parser(line);
+ printf("> ");fflush(stdout);
+ }
+ }
+
+ if (libfd > 0)
+ if (FD_ISSET (libfd, &readfds))
+ pollfunc();
+ }
+}
+
+static void
+print_enum_list(int mixnum, int ctl)
+{
+ oss_mixer_enuminfo desc;
+ int i;
+
+ if (ossmix_get_enuminfo(mixnum, ctl, &desc)<0)
+ {
+ fprintf(stderr, "ossmix_get_enuminfo() failed\n");
+ return;
+ }
+
+ for (i=0;i<desc.nvalues;i++)
+ {
+ if (i>0)printf(" | ");
+ printf("%s", desc.strings+desc.strindex[i]);
+ }
+
+ printf("\n");
+}
+
+static void
+print_description(int mixnum, int ctl)
+{
+ oss_mixer_enuminfo desc;
+
+ if (ossmix_get_description(mixnum, ctl, &desc)<0)
+ {
+ fprintf(stderr, "ossmix_get_description() failed\n");
+ return;
+ }
+
+ printf("%s\n", desc.strings);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int err, i;
+ char *host=NULL;
+ int port=7777;
+ int nmixers=0;
+ extern int mixlib_trace;
+ int interactive=0;
+
+ //mixlib_trace=1;
+
+ while ((i = getopt(argc, argv, "ip:h:")) != EOF)
+ switch (i)
+ {
+ case 'i':
+ interactive=1;
+ break;
+ case 'p':
+ port = atoi(optarg);
+ break;
+
+ case 'h':
+ host=optarg;
+ break;
+ }
+
+ if ((err=ossmix_init())<0)
+ {
+ fprintf(stderr, "ossmix_init() failed, err=%d\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if ((err=ossmix_connect(host, port))<0)
+ {
+ fprintf(stderr, "ossmix_connect() failed, err=%d\n", err);
+ exit(EXIT_FAILURE);
+ }
+
+ if ((nmixers=ossmix_get_nmixers())<0)
+ {
+ fprintf(stderr, "ossmix_get_nmixers() failed, err=%d\n", nmixers);
+ exit(EXIT_FAILURE);
+ }
+
+ printf("Number of mixers=%d\n", nmixers);
+
+ for (i=0;i<nmixers;i++)
+ {
+ oss_mixerinfo mi;
+ int n, ctl;
+
+ if (ossmix_get_mixerinfo(i, &mi)<0)
+ {
+ fprintf(stderr, "ossmix_get_mixerinfo(%d) failed\n", i);
+ exit(EXIT_FAILURE);
+ }
+
+ printf("Mixer %2d: %s\n", i, mi.name);
+
+ if (ossmix_open_mixer(i)<0)
+ {
+ fprintf(stderr, "ossmix_open_mixer(%d) failed\n", i);
+ exit(EXIT_FAILURE);
+ }
+
+ if ((n=ossmix_get_nrext(i))<0)
+ {
+ fprintf(stderr, "ossmix_get_nrext(%d) failed, err=\n", i, n);
+ exit(EXIT_FAILURE);
+ }
+
+ printf("Mixer has %d nodes\n", n);
+
+ for (ctl=0;ctl<n;ctl++)
+ {
+ oss_mixext node;
+ int value=0;
+
+ if (ossmix_get_nodeinfo(i, ctl, &node)<0)
+ {
+ fprintf(stderr, "ossmix_get_nodeinfo(%d, %d) failed\n",
+ i, ctl);
+ exit(EXIT_FAILURE);
+
+ }
+
+ if (node.type != MIXT_DEVROOT && node.type != MIXT_GROUP && node.type != MIXT_MARKER)
+ if ((value=ossmix_get_value(i, ctl, node.timestamp))<0)
+ {
+ fprintf(stderr, "ossmix_get_value(%d, %d, %d) failed, err=%d\n",
+ i, ctl, node.timestamp, value);
+ }
+
+ printf("%3d: %s = 0x%08x\n", ctl, node.extname, value);
+
+ if (node.type == MIXT_ENUM)
+ print_enum_list(i, ctl);
+
+ if (node.flags & MIXF_DESCR)
+ print_description(i, ctl);
+
+ }
+
+ if (!interactive)
+ ossmix_close_mixer(i);
+ }
+
+ if (interactive)
+ interactive_mode();
+
+printf("Disconnecting\n");
+ ossmix_disconnect();
+
+ exit(EXIT_SUCCESS);
+}
diff --git a/tutorials/sndkit/morse/Makefile b/tutorials/sndkit/morse/Makefile
new file mode 100644
index 0000000..79d36f5
--- /dev/null
+++ b/tutorials/sndkit/morse/Makefile
@@ -0,0 +1,27 @@
+INSTALLDIR=/usr/local/bin
+TARGETS=morse morse2 morse3 testgen loadcw
+CFLAGS=-I../../../include
+LDLAGS=
+
+all: $(TARGETS)
+
+morse2.o morse3.o morse.o testgen.o: charlist.h
+
+morse: morse.o
+ $(CC) $(LDFLAGS) -o morse morse.o -lm
+
+morse2: morse2.o
+ $(CC) $(LDFLAGS) -o morse2 morse2.o -lm
+
+morse3: morse3.o
+ $(CC) $(LDFLAGS) -o morse3 morse3.o -lm
+
+testgen: testgen.o
+ $(CC) $(LDFLAGS) -o testgen testgen.o -lm
+
+install: $(TARGETS)
+ cp -f $(TARGETS) randomcw Vvcw $(INSTALLDIR)
+ chmod 755 $(INSTALLDIR)/randomcw
+
+clean:
+ rm -f *.o $(TARGETS) core core.* x y z *~
diff --git a/tutorials/sndkit/morse/Vvcw b/tutorials/sndkit/morse/Vvcw
new file mode 100644
index 0000000..d7c8e57
--- /dev/null
+++ b/tutorials/sndkit/morse/Vvcw
@@ -0,0 +1,1801 @@
+#H001
+FSIEL DHNRS NEINN O=IDI
+EIIIE U0ZHS VSE™N BXN9O
+ISEHS N5PAN AMTUP JTGAC
+AEHGE IXWUM KSYQU ISEIT
+SUIVŽ WOUDQ I5™ES
+#H002
+PDSTH ANQ™L TIINT A3BEA
+SUXIU OIDGE NIIŽB EDUNF
+SCIES VEVWQ 2SSRA ITDET
+ETMID HEUA2 YEQND 8A9BA
+ITDJN EUSZ KAFBS
+#H003
+ERPT7 EŽH8F ESOGE AZUAT
+ILN™™ ANSES FAQC? BATTI
+ZUTWI QTHAP BVIII BISXR
+AVT2N NMESS /EDGE ENYKD
+UEHAT LMJSI SIHMR
+#H004
+JTDE2 TTNSY IANOV ZXUIU
+DIP1R FEEH AAQHH N8NMD
+BIAND IWEES GRKII TUUI
+ILEEN R/MJA HUEIA TDNAI
+™TCSS UAEAE SŽ1QS
+#H005
+SDXI9 ANSTC NHSIV MYENJ
+EX82I ™IAQI NEAVZ TNLSA
+TCELH UDNEO ŽKTI™ AMLNI
+SFAEE NEWTH S1GTM PDBII
+MA5ŽE MSHHJ ETIBR
+#H006
+USŽEI SEGIA NBNŽR IQL™R
+DAAET OFMCE MEZIT LPLSN
+SY699 DANDB HIARA HIIVE
+QETO3 TNEAS A6ATI AJXRE
+™REDT DAKSC YWIII
+#H007
+ZSRNI HEŽSE SLTFE LE3DA
+NXCSO INVVJ E3HWS KTEWM
+IYNUI BTUUS 7ZAKR TPINS
+=PEEE A™RII ™NFRA RTIDE
+QDATG AMTL/ WQNTS
+#H008
+IHUAS KIOZV ?NOTT TNXYF
+J6I™U TFBS™ T0AAH EAYUE
+DE0US ENTAE EMPEI ŽGNNI
+ITANT NHSBC QURSL AŽEMX
+ŽIRTR SSSWM EIRE5
+#H009
+FHKCZ O™SES BSIE0 SIEE8
+PAŽUM EUMNA 2IHOD WVSUE
+IEIIQ TNAVP TETXM LISNN
+IDNRT INETR NIGND B2AVF
+XTTAN SKND0 EHUYJ
+#H010
+STPEI EVAIT HSUEA ODWEI
+SSMNŽ YZH?J C™RTO FIBHE
+I6EHT ™ANNW ŽERAH AEXT1
+N3LIQ NIISK =AYSN RTTSJ
+DEHIV SPTND EGSWH
+#H011
+IWRES ALJAE EVWXE SIQHB
+TSDN2 ASSNY ENRTQ EIRTD
+MAEDN UIKRA ZYUCF IGS™T
+SXETI ŽDOCI A0WIE PAIRN
+IDTNI M/EAB 6N9ZD
+#H012
+EPQUR RL2SE HS=VK ETFŽR
+SSTNI HW™OH SBAVA XNQT6
+TIDNN RWECS SAONT I™ESN
+EIUNU SKIIP YETEA 6ETIE
+Q1TJT ZRTDA ™SGDM
+#H013
+KISLO TENAS ANITD W?HEZ
+1E=PR NQ™SF ŽHFBE ŽTLJ™
+CXTMU ARSON ETIIQ SIIAD
+?IHTS 5VPUE HYAMA DSEEM
+ATEAI CCEAN IGTAD
+#H014
+ANSQI 3GMHE EADXE TEEA™
+ESTYI KUUIC THNI6 ŽWLCD
+IAD0V KTETR STZSH FINEH
+WNH3S I™OAP SXVIA BEMHA
+EATDS I6SA ŽJZTM
+#H015
+ENELT AHMUQ AUSNT EPMUN
+LAU3Q ?TMTŽ 3KNUŽ NEHT6
+TNMMD SSCWA NTJEI ™TVYE
+ZXACB EIGFO LAANI ŽEHII
+ISTER EŽTSO IGN7J
+#H016
+VIESW I7AAE UINNS EYILI
+NSAAU NED2F SSEIK G™7FH
+IAETS M?NTZ UHAI9 OEGML
+CQQIL MRIIU IRNEN SHRAO
+DBZ™J NTEPE TXOTŽ
+#H017
+DH™U6 EOTN3 A=ELK UIMTI
+ZDIEI LANF SQAHT NADGK
+ISSVS ANVNŽ EIHCD SXEPM
+NTET™ WTNTE UAGAI N=BTP
+STJED ™G?UE EYIRC
+#H018
+YAŽŽB TSVW= NDFOV KTBJE
+AUQ/R TIATI R9PSN E™3HI
+CGRST EXTZ8 ENNIA TMFSS
+TETNI DEECM SMNLH MERCT
+EMAJC NMIAE ATSJS
+#H019
+WXLID TISEŽ SCTUE SCHAA
+RABHR AAH0U FAJII AEOWD
+S™NMH I1NYQ XŽATI PTTVT
+KEAEI ISSEK MIDN9 M1HKN
+ZAI4C ENTEL GAEER
+#H020
+UQNZS /IIHN AN™TŽ KFYMI
+IZIT 6OENL RII2E ERNTT
+NAMWI EUSE TJEES XAC?2
+VINEU MSTDD MPQGS DCNSI
+BTARU ESDAJ RRAES
+#H021
+KAEA= AQXUE DNTTE NAIAO
+HDG3L IIGE/ TŽRAT ZECSO
+ZBI™G HADY ERE=S 7INIW
+AWIEI ASSDF NUTVD VEPUY
+EMNTW SNIUT AJHIJ
+#H022
+TZQT™ DXETJ ESIBA RHTUD
+HIVŽK YSUXD NŽEIE IK2W?
+AJSIK GSITO EAIUR ISFES
+ASM4D EE2JP IA6GD NNSEC
+LFPRI TSNEX SNDAT
+#H023
+V™SUA XLWAE T4NES FHRNI
+SQSYI SPATS ŽGUNY TKMTU
+LEOOM NQTS™ ESSSB IEFS3
+EHTIC JITBH N5GIT MZEMA
+AEDXI CRD1E T3RNE
+#H024
+YCVSW Q™IEM SJEKN DEIII
+BEEIT AIIEM GESJE TPA=I
+SZTES NONFM RRD9S TTNUJ
+ISHV4 OINQX S2NL4 ISATN
+DSŽEN UGUAY HFMDŽ
+#H025
+KHSAA IYZNA LE5IE UARC1
+N™UTS NHE/U ATD1G OTEMT
+ETIGI INEES QF=EQ DUIEP
+DXSHC BBJAT M™SHN IŽWST
+SIVNN ETFNN IUXK
+#H026
+WAHSU FMOIM TYEBV S48SE
+AENNN IAMEO EJCWN S™EPA
+7HTYM DKQOT YDGTH SŽITA
+HEIAT II8I EWMAU ANDLE
+VZRTX BI6AA TEVIS
+#H027
+OPIRM NLENM THUTG THEIN
+XYSIS V™NEA NLEI= NKEI™
+BAYRQ TRFSI SI6S7 NSHOM
+OBZ22 IEDMU EEWSW NSJAI
+RINTE ŽEŽTO DWNTC
+#H028
+H7TNM SN=ZI NTYKJ EOXDX
+AIG™I MPWAI AEDBM KFE=H
+AVŽWT ENUED NNESM QYEPX
+TT8NO LI6D™ SIUPD IESNE
+ACHRF NTNTT IEAST
+#H029
+EFHSH ZŽIUM EEUII LADST
+M9I8N EIXAN N78GT UTLSB
+DEWSM ISTPN ™MRN™ VEPCD
+NEKAT AJSLE FIKGT TEODR
+TSYAE TNAJ9 QKINS
+#H030
+HSPEA TSOHS G5WŽF ME™PT
+IKUPA LŽZ1= R3NDV QTRMM
+I™BBI NGEEK HIATE NNOJD
+EXGRC MASTE MTS=I EISYU
+TEASS TTAES JAISA
+#H031
+EEICS IEPUK HIAM9 ANSEH
+ŽMLAS JXETT HE8ZA NSAI2
+TIRKD IHQND D™NRS BWGTY
+SFDSO JTIAI INFEP IJEAR
+FRA?K ?IWSS EEVIB
+#H032
+STKVB FEGL UFRHC GS?CI
+NSZK7 TSG0T EEEŽI UTAIR
+MMTN™ ISFSI HMTE8 ZISIS
+HDPSN SSRDT UYTEA NES=E
+TTEOD XAAQE FJWUA
+#H033
+E8TXT IIZMW TLSWR PITSK
+XSTIF ASMNG MTHJE OTND5
+IJTAE USITB A=NŽU ARJLS
+EEAHE YEUAA SAQLS ETTKV
+HXDS8 ™RŽHE E2NCP
+#H034
+ANNTY FDRŽD 5LETI EVESB
+UIAIJ ?ARTA EOYSŽ SGZEA
+TAMEI IEA™R /DAAR HP?DE
+IWTNE PWIVR NPW4A CFAXN
+MITIM EJHSK IQIS
+#H035
+SEO™D DAIGM 3EITN SIQ=E
+EUSDL NV™NN SHYUT ACEKE
+SIGEA UAE™D INNEP AEIFU
+XTBDZ HS8TR RŽJ2Ž AIIAT
+DTNIR FNDDI ŽT8W
+#H036
+EMHDZ FDQN5 5IBEK SSSEP
+Y0RTS ANNIX WDIGA AQCNV
+LHHS™ DE™IJ ETREI SHOAU
+SAEER PUUEA XTAOC T7IIH
+TŽTYS T0TST EKSNI
+#H037
+ŽIE™Y BHNGE LSLNU GŽISU
+SSQAS NCW8M AETIA E4XTI
+TUAOJ NZR2T E™IAT TUUFG
+EETF2 ASESS KADCO TRRN9
+HSVYD IDTPR NEITE
+#H038
+DDBEJ E™H8R TADA5 DSIHI
+ZUPAS ŽODTI EAVTS YTSIM
+GICFE NIREZ QNAJI NESAJ
+ECKTR TTVŽ3 DTSLA EAXT™
+E4NA5 ENMWŽ SYIXR
+#H039
+SOI6S KDTP AKWNI IXDM™
+NN™YB TFL/V IHOCE HIZQS
+TNDTL UE6ET JIUAI ERTNN
+H4REG EUAAT EAMAE ?IŽSA
+ZTŽES MEPJN NTDNL
+#H040
+NPRXN V™ASR OAGJI IASWE
+EME2K ZEGDS MD9ZI EAUAI
+OAESN EZEIA TINSH QICT3
+TRET7 WJBUY NŽSTA AEITH
+S3DGL TQOIU DRTNF
+#H041
+MAPTE RTŽŽT MLTNE IYKMS
+IMZE™ HNAIS TAEXE 6QBNI
+QSTEU MGEZ? HEWCD NŽARS
+AANV4 AGT0V UEFTR IPNI
+DN5JI TMACS SDIEO
+#H042
+ŽTMTN SNUR8 DA?SE T™TUJ
+SEFNA MESMT UETWA OEVTZ
+VTYAF NMFHN ILHPE TFCTQ
+TU0TC A/RVN NBRTX EENAO
+NK2ZE NTEND CTGIM
+#H043
+S/SUI TSEN0 INGEP SŽFFS
+ITNDP HN8CT TOEEL EAŽS3
+NSEST NHUTH RAKNB XTXBQ
+HPNAW TTNEU DII™Z RSTET
+VYRFR PHLHE MIE8J
+#H044
+DDBKN 7IXSE RTMAT INNCS
+LENTT ONAHO PERKR EA™WM
+ŽEA™U FA0ZJ JUIŽM SNE1E
+5CHTE NITTS GVRTM XNDTD
+NAFNI 8IETI ANYQE
+#H045
+NTJWF DETT? NEUNP TLRSS
+INOIN USNF7 TQEMJ VMNOA
+EIUI™ T8WYI IAAXU EENZE
+HLVTG KS8AM RTNBS RDHS™
+EŽABI RA7EC EIPTG
+#H046
+WIKME ZRUI 5DIIT EREXO
+RACPE INVOL EDIC™ S?ITN
+1RNET =DEXS BIYQA NSESG
+ITNAU SANHS HUQFA 5ŽTTS
+ŽILSI AODEN SJ™DE
+#H047
+P™EEŽ NBEEA MYOUS RK™OE
+SI1VZ YTUNI SR=TM G™ELN
+DSEIS TASMA UAPA= AAHTS
+DI4QN IIEFT TGMWI ZI8RT
+ESXGR SHJSC SKTEI
+#H048
+DKSUJ SF4IT NAESI SACEZ
+MCZQU HHUBS PTVE7 NOSNE
+?TE™M ?USAI UXNIW BYLTE
+O™SKN ŽBTLI TUAGR 7IŽUR
+SEINT ESYIT IREES
+#H049
+NUWEA ASTND ™S™LA NVHTM
+SENME T8ANI BOZOT HESE
+EHSIE MINNR 5EEDM TXATI
+DKJ/T CK9NT YTS6U B™PFU
+UEVS TNLNI ŽGTQT
+#H050
+TMSSE E9IGA BL?NŽ ITH?Y
+EOTJN ™ASZI GEKST NNIAB
+PRAM1 MJHAS DAGTR QEETS
+GTRRA EUEAI EXNVT NCIIF
+HKX™H UTEŽA IMSW6
+#H051
+UTCS™ NNEKN WFERY SADKE
+TEANZ JLAUB RTAE9 BHGII
+VRSET IDNXU SIED SPŽAD
+SUIUF QEEUI NJJII I9NTU
+AE1H8 OANIW 6TINM
+#H052
+TEKIT GŽWWN EAUMC NPMUR
+Ž=ENV TSIII S™MSM ISCN3
+7BTHI SHDEP NLACL TATZN
+DEQKJ AEUVE BIRNX TSEAD
+RDA0F OTEIS 9ESNY
+#H053
+CNS/A UTW9A ŽZIEI IHOTE
+MINIR SY7IX ESNYJ QPKI™
+ATNAK REZEV UHISG RKFAA
+YERTA EHUDA Ž/DIO ŽALST
+NEENT BISAI EBR8D
+#H054
+FECKA REIŽK OIENE QU1ET
+J2DIR 1US5N GBJEV TUZSN
+GIEAP TDSNT NSZSY SEAHI
+HEAXL E™STU ŽULSS 8TSIA
+DŽIWM VITTN SR™RI
+#H055
+NTAHE FAMST SJSIA X™ETI
+TEEM/ TSSB9 6METW AIIKN
+OCAGV TCLAZ ANQM™ AE™ŽN
+EDEAZ HEWŽR SAT0E 5SIDP
+DDDIG TYHLU UIRAK
+#H056
+QAFRE ADN™N MNEII ESSWL
+DQNTI NIYEI THUES TNNBI
+AIQSK ERTFX OUNŽZ YTMUM
+AI8EP NHI/M PEE4V GSHFC
+SAE9T NZ/TX ISBJU
+#H057
+SITNM T9IML IIEGS RBIXW
+FPMSC DUETI VTSSS XJIIS
+EAENS RGEDD Z1NOY NEJQ?
+IRSK3 AGNET NETNF FTENH
+SHAH™ U™Ž8E IKBAD
+#H058
+AŽNBS LTHIW UDOAE SDH9I
+IVCQQ XIAII XRETN SKARS
+ARTEF SS1EE IPEE™ FS=VT
+EWIUI KR1MG SSEZM FIANA
+SH4YJ MSJEI NDIOT
+#H059
+SNEEP AMNDU KEER4 EWH8T
+TNYBR ADETA E=ETD IATEN
+MNIDT IZVSA ASMFS LSIYM
+BŽIPI ONQNE RVV™G OA6™M
+MNJTC II0AX IMIZZ
+#H060
+NNENN IIT/N EST9Ž DIUIZ
+TEGWY NLDAE RNIUE 3NARM
+TISKS UNXSB IMRIR YO™NK
+X8AEE TETD A4QEE HPFTL
+CMVHA BTOI™ SJKSA
+#H061
+DBEHT GCATI DIAZI UD4IS
+?RFK1 TNHNY HAKNS NNTMN
+ENJLU 0TE™E EE1ŽA ITOSU
+EEXIF EXMOP SDSSR TITPA
+ASSWI TSREZ ODBVQ
+#H062
+SAWIY =SŽ™Z TT4AE AECI™
+I9EPE XEINN ENTA/ HSNIE
+DTAEN 5KHNA ISICE VJASU
+MKMSV DIITM RBLDG UTQUQ
+OTNKC NRAMW MFEŽJ
+#H063
+C5ANI MRDNM XJPGI WOTAE
+HYESC T0NBP ISUTT F™QAI
+ŽAERH NUTAI RTD™L ETEOI
+VEŽUK NSG8A NEAN2 EITNI
+AOENE IC6US ZFMDU
+#H064
+AEMIT MWMXK DENNG GRI1™
+ERGAT ?SEAŽ OZFER MUMEI
+SVTII SRIP JANIT TNILS
+JITRQ 35WAE SEFUA PJBEI
+NNNN? HAYIC NŽUEA
+#H065
+RZISI NANSN /IPAH ETSMN
+OTENR TXUDV CHT/W S™TVK
+ATEJ5 NRTFD AGZAQ IVLEE
+MSAEE 3PP™N XEXSH TDUED
+IBQIA EOŽSI II2YR
+#H066
+EASTL NIETT NSQTB E6ZF5
+GEST AHEMŽ EXQHK IRTN2
+DJAII CXSDS AQMAŽ MFTNX
+SIEAY DTFOI DEBPR 8UVSE
+IT™MW ANEFU NTD3A
+#H067
+CTNHN KBTOG NSEBL HIENE
+ZDNNI IZARN EŽRTI GSMPD
+TKD03 IENUT EUHMB AMW=2
+MJCAA ISFIC TZQEE T2EOY
+NMTVS EIHXS NI™AN
+#H068
+TWMKŽ FTAIN ZHERD ATTA™
+UMADO TSEBJ GASNE PYSTU
+NQEOJ AT4Q= AEAHE ENLDE
+MNSTF TTTMP CNSUI H1I0K
+BVNŽR TAEES XI5LM
+#H069
+STTJD 9WBTI HZKS5 1HEPE
+TDSNN LFUZP EOATT NEAAE
+DTARG SŽYIU IDIHR RZTNE
+XISHX FS™EH NE™SI ŽENE5
+INQ3™ CIMAQ NTVAA
+#H070
+HAEPI AE5KM E41RT TNHSH
+IAJXJ SONEA NGETN ŽCDIN
+NVSSA NTETF MQ™RL SZJNI
+TIA2S IRYHE UIUCI EFEIB
+EADST IB™0R PXHWM
+#H071
+ATAUE ŽAQST IAYU MGSXH
+MA/5 NZJEA IRH1O ™TTHF
+THNTT EGUEA IJIAD TKSAN
+ITESM U2EEM APENT ACBTA
+DEVYS W9LRT LAERB
+#H072
+ADINT EYJUN ANSTQ EUTUQ
+ZFNEH TEPIA NJ™HN G/SAI
+EEB4I AETMR LUXNN TBKOI
+FSETN DMHSD MIHEO TSEFT
+NAC1P IR0VŽ ?TWPN
+#H073
+QTTMG IXTEF TEOEE VAZPS
+LNEAN HD™BE UFTNH SJC5N
+SNNRI ™=NDX UYIJN YMUIA
+USSIE IET9N HSŽAT ITEDR
+OMKA5 AT5OF QENWC
+#H074
+KAS5S LYUSN MŽR8O EST/V
+XVTIE ISTEN KXG™U ITAHA
+™8WSB IKAEU SUUL/ LZHAA
+CDQII IEEFZ IEUED TPMJ
+ETSNT IUJSN RENIS
+#H075
+SNSAI AHROC EE™ZK ™SQTN
+ZTNHI ™IUAA 1TERI FJTBA
+DDYID ZŽMIX I™ASR GVSBI
+4NEEA P5NWB DESQN EII6A
+MI5DK EMIYA LEMAE
+#H076
+XRSRM IRT7B EIEUY KAIFI
+?FGAZ AEAIC EWEOR A™TM=
+ATNTI SNMW JEHDE V™YNL
+NSENT ŽSDG2 INDHA T1WMI
+NVTEQ THNSN EIVAP
+#H077
+BESEM ATEUE HWTEI CTH™J
+ITSUS AZMTL GKNIH SXUED
+ANISX ŽVFR™ WRAP4 N?PHT
+NRESS PBTDN IQSEE ETAO™
+YSTNT XR1IN 85AIQ
+#H078
+ATTQR JKKSX NRIDG OTWDE
+NIT™E AEMZI RTESI VNDP
+SHIN3 ES5IL HSESO EXFAS
+ENENT SMNTR YLWNT RSI7E
+NAB8Q IHYU1 PUCŽI
+#H079
+RHNEK LUYFC XUCWE ITSEA
+ABMIS TAZNY LAIJE VSAEI
+™IRNM KDEAT SDEGS S8SZ7
+EATET TIIO =RIPS JUR7K
+DN4QN ŽMFTI IENVD
+#H080
+AEPQT MIDEŽ NADNM TQ1EX
+ŽETMT NENKU LEERR ?T™AJ
+I?PAI ETIJO S™ALF WVFZE
+DŽANN AT4DE BTNCN SKGAS
+STATU Y6IMS MHIIU
+#H081
+Q5A8E NALRI TTMRI NIORD
+PQOZE IIBOJ KE9ŽE SHEHF
+AINAS SNTI7 XSTAD JEIEC
+RTNDT K™SRZ ITVEN WGTBM
+SEEAA AAH4A ™DYU™
+#H082
+IEEMS HNB3R IZTAR VSADN
+LCNXR TX/SK ŽSUIY DFISG
+3TNVŽ EYETU INSDI QAEWE
+NGS™P NUECN MIEHO X?JEE
+TJ5AI UOSIT TIPND
+#H083
+MAEI RIEAA SRS2E RQIVL
+INEWE KQ8MS AA9ES ISSSH
+UDETY EEJ=Ž QIIDU NSAHU
+TZ™NR VRAIT NIOBP TUMWL
+TECTS NFKGG TXIS?
+#H084
+ETINR TŽFSI TCVFA P4OXŽ
+TADEE AEUSE HNSZD NA™BA
+GMASA YETIT 9NSEE WMRIR
+FDDDQ 7UUNT KTIMI 3LLQG
+ICECV EA0AT NJAAU
+#H085
+MENNK HRSSS EEOEJ NXIWN
+IQJET IUBNS UNFMT ILP4I
+EIHZI DQHST TKST9 SEDST
+NNNUI VEBRN MTA™ 4ŽTJX
+3MGJA 4NECE FYIHA
+#H086
+D5ARN ARNAE SEJZU TRSS4
+VIYAE UINRN 9TBNP USOTI
+SDWN™ DTNCT YN4EW IJMNH
+ICIEŽ EVIT™ EQXET TGLXY
+YEESS KFMAB 5HMST
+#H087
+ZSHWE ŽIJUE ENMIN QDNIE
+YNNTT NEADM UHNWI MSZEI
+NSYTC 9IDGC NA/XE LAMVR
+ESOE4 MXRI™ S5HII PKATA
+SQFBK INFST VMET0
+#H088
+3TEVN EAIOZ IMANI PTMTA
+ARMBK IO5EI STG7M U2XAT
+EODIE ™MNAQ TUSSL MNUTA
+PNEDX JXTSH ENYN™ JCNRE
+LE7VI NEŽZT SWIMF
+#H089
+LTSNL R1ANN ŽPIEE CHQ™H
+KEHNS TVHŽN IEAJS T/TDS
+AJEDT SXAEU T9BEZ NKAIU
+OYBHY EHTCN OTGUT 4UHAI
+TNII= ANIXE NMFWE
+#H090
+2AJDE AXHTN SATHX ZQTUB
+ZRTR™ YNSSI NEREE ZŽTFH
+ŽNOTI LVPUE TASNT MTVZC
+DE7ŽA A0FKE A58IM RTNCE
+INRIH SHWEI NAGEI
+#H091
+IG8UH JEXIF OAAAX NARKH
+QIDOE ESSSL O4REC ENICM
+JM8BW DCMIE ZIWNE EAITI
+TSDNS TK/ŽM AHPSX AYVIT
+™ETTT ENQAA TDU5A
+#H092
+Ž/RP ASNSI DIIEN XFVEM
+TMANK ™SSAR LTEIE SEISQ
+IKTIH MTEUZ IQEBW VYISC
+SN4AE DHWYT STUGO E6EAT
+T94SX NNJ™A JUUMD
+#H093
+HNLIV TEITJ RHUES EQA0M
+XNEZN ETHTI USILA APZUC
+XFNUN ASIWW TR=KE AAAMG
+NVIIH AB™EN PEQTY NSPIA
+ŽD3EM ?COTE MIT4R
+#H094
+LNVWS AIDI4 QTAJI EMCEE
+MTM™I AEP9F DM7E HNUSI
+ARSDZ J5ZEA NMPVQ STRSS
+GNINY NKM2O BŽSRN TOTTM
+STEWT ETTES IXŽAE
+#H095
+A7KVS RTAVK MDŽSE MSUHH
+NE7TE ENNNA NEH?I NJF™™
+TTESN OIEAA A™VIY PCMUŽ
+IDOSK 84UTI TSIET IDNSQ
+PGIZI UEDLW XEBOI
+#H096
+TWESA TAVLH IR5AI AHXEU
+9TESN 0EFSI =NIEH HUSFN
+DSINN TRXUS NOYDK ™SQIR
+T6EIY ECELR GTNNS VSSYM
+BHZPŽ IEPXI KIUEJ
+#H097
+GNU=S HJETT EXWTV FSPAO
+RDFAŽ NNCUT VSUTK TINUW
+IQEBE IRIBN SN9EN ZFRIL
+SSSM0 KAEUT SU0EE EE0NN
+AIT™S TIDXI HYWHM
+#H098
+GPTZN LTNQ3 TTSDE RO9IS
+SBEIF VB™EV AAEHR JTNJA
+NWXEE FDEIS THHAT ?CMAI
+YIEUP 6AVNE SQHKI NŽIIA
+DIMMA ZUN9S TAXER
+#H099
+UHSTG JHSEL AB9CS SEIXR
+FUESZ IVIIZ TASEE N9YCH
+V2NMD T0OIA FCDTN ADESE
+OIEHI UEALI IIEŽR QIHDN
+FSN/P NAI™S TRSWK
+#H100
+KFPNN UTSZA MIRQS IŽTSX
+YA/EE NJWSD TGITL KIBAN
+ETIMO ŽAECE AMKSW NEVNX
+E™SMR 7IZM/ D3AER HLND0
+MBOBM AATTU EIIEI
+#H101
+EVQ=D ™NNJS CTUAA HQENV
+RSTUŽ A8AIE ANXNT ZŽNHE
+EHTET PKAIS EMDBV N4TUA
+EWZDT ACIEG E5IM YXRPT
+4IOTJ FHSLU ISTIS
+#H102
+CIEAC AHVSE MRFOQ EZTLA
+ISSWG ND5NA ZY6/R EQJIZ
+PEQAB IIIAŽ ZDSB/ EXAED
+TE™SK VUNNM TUSE4 SIMNY
+TTRWT UFIHE IDSIA
+#H103
+H™DKO OTJXD IEEHN FVNTL
+AWSHW DNIMG ESSIA ENXAU
+TSXN5 IUŽAP 0ETIP ASEB8
+SSEKR TEAEX HUQPT YUPNS
+ICSII IRZ32 RIETM
+#H104
+YEIEC CTMOY 7VIEU FIHCR
+ŽSABN SI™RD GHDSA AISZP
+EEEEA ARIH7 NQAFN IEQII
+WLNSE GTSSE NITD6 TULNP
+IKXRD IA=WU N9JAK
+#H105
+UA9PE WIGSZ VESEP JFI5P
+AQNRT EANNU HTSNX RBGIS
+=SIDE AHMIT AOOC9 GSLNT
+RAZEŽ ATELE ?UKHT SYIIE
+YTIQA MTD™N RHEST
+#H106
+SHSE? YGAEU EIAŽE ZTADW
+29LAQ ISIT0 WNBDI MAMUR
+HNHSW U™KKN ATAET NEFEI
+OHUXL CSVHN IIAIA NTJRF
+EP0II CWEIA CMETN
+#H107
+OITWI SSPQA ICSTA MNNR2
+HUSXŽ IN9JS CES0N NHKDZ
+TEPUE EEEGN ITKAN IREES
+WESDL MMONT YIYVD HŽDBI
+IURSF SSIT™ T=GE7
+#H108
+F4™RG V4EH= WIASS DETJN
+EENCQ ARINX NŽQKP GTIJI
+TEFZN RAEI1 RTXEI JBYGT
+TT3UI APOLS ADDSE ETNTA
+EDHDU ASSMB MSAAF
+#H109
+™HEAI TIAHL ROEEE SISI6
+THKEI ŽATD/ ASHEI NESLK
+NSJAS DAGZI BTIVJ IS=AS
+PYRZA FA™6O WCEAW QUXTM
+NMERZ D4RTT EJMFW
+#H110
+H™ENT N3IOI FPU5S NEAYS
+TIR4S ™SSXI TTUNL DZMTE
+NBILA TESSE SRUNK 8HPOJ
+8ETEA MEUVE GETQN FIGT
+AHDDC TSCYŽ WNRIW
+#H111
+IGNTA IENUS AFIMH SIUEN
+VHDEY ™RCI3 7HPEK MS™II
+ZUNTN CVCIH OTAEA JX?N5
+DLLZS DRAZQ IŽAIE WLENY
+AAIES SDEIE TBOT8
+#H112
+SCLUI IMREI ZNETS SPIPD
+AJJEH NVA9S AEOES MKTZG
+DŽNRW 16NMS BREM™ ATXAR
+ETLOY TISSH IES0S DETIO
+YRATB IE5TQ SFTAM
+#H113
+QHSAT ENETU HSTTE BHUAT
+T™TK UHFEV PTSJA LIQNZ
+S=AAT UMD/F WEOII OCSIL
+JSHXI ZRNT3 SAIBV SMEGE
+EUŽEN ST/GS ARY3E
+#H114
+7OVBS IPANO STTAT ZTZ?7
+NDEME YN8™J GERUM N6BAI
+UWUIE QHXTM TNBUT UVST™
+EICMN LNKEM ESDIU IAEAC
+EŽVFX A™AET AIIAA
+#H115
+GEXIŽ ŽENNC N™EIU QMESA
+TI?J? FTTIA DATNN A5NTR
+K1ENS EIHMI ™PINE OSEIH
+UMTCS NAEMV 2AQRC SAIYE
+PULKW HVGTB RZTAM
+#H116
+HXTIB TTNNR 8ACTM G0HDB
+ORTAF EDEEN VTFTC ESLEI
+BOT2I EQT3D NHHJE NAFUM
+USZWP PSR?T ISATK EXSIN
+YETAS ŽSNM™ DSSKE
+#H117
+NOTEE SZ7EA UUNTO AŽGHH
+EAIDC STVEH E7KNE IGDNL
+NEXJO MQTIF N?QHS EPNSN
+YACT™ TR4DT ARLWB IMNSJ
+VT6EI TXNT™ UIIRN
+#H118
+GXAEU AJEYT TO2ME QRVFP
+EEWTA BETC8 CTJSI ZUHNY
+UEIBF KIUH SSNH3 ITXIA
+AEAIS ALNIU HUTUI NŽA™H
+SS=RR 3NDIE AEISV
+#H119
+IMCEB RŽSUK ERHR INNBF
+61ITT FTTVE NAAES YAASL
+E1WBU DDU/X TAOGT LPŽWT
+NITEE 3IMAU ZATHE NTQTJ
+S™NHN SASEJ UEAJI
+#H120
+ICNQS ESPZK TEEAI EYTKF
+EDIN= HT6U7 DJNŽT OF™UT
+BAVVT ESOGA UASNA JSGIR
+ZSEAŽ IGSWA MMH9L MT™XE
+NNT3R EAHTT EMRIN
+#H121
+SOHTX DAYNU TDSJ0 SNBTE
+KNTNX MEKID MS4TQ IEKIS
+ESVTR T™LSA AAZNA RQUS6
+FWŽLE ILA3G VEAŽM IS9RI
+ITIME MNMEE CHYEP
+#H122
+PSTBE SVITN N6Ž2A TDDUO
+ITITW OZY2I NTRSQ XKISY
+SLENL RVHAA HETHH 2AUJU
+RFASG NTEX™ EMENT EVIPS
+TDE/A CSEWQ ELMSI
+#H123
+ETZCE 2EPWY SSMVN QDMES
+5MOIS DSI3R NUUSI CUNFH
+EŽQAB TTMNN ITSNJ ILIUI
+9™AIU IWIII XNXAE ASHEA
+FA1EX ŽYGSB UEIKE
+#H124
+™LOTM BETED MWMPA TNNYD
+EESSH HRIYA NE0TM INAFQ
+UTOSI GŽP3S E0VET HNDRE
+S=T4S ZIŽEC XKDTA NITMT
+ATHIN JAEQK KSNAG
+#H125
+EBNNJ UNAS1 FI/TX RRUQY
+=EMEN IAR™I VOECA STI=N
+IMZEE TUAIP EOSGH TTATI
+NTSYN XETTD HSWIS PAKLR
+RLVAA EDDKS 9BEŽJ
+#H126
+VNQ8R K™R9I IEUCS NASER
+FVN6E ELRTE ŽUJKI WHNIS
+TZJYE IDBSI NXGSE 1IENA
+MIHQA TILXE NNMFA INNOU
+AATSU T9IUG PHGET
+#H127
+UIRS? EXIYY VŽKMG /AYHS
+AE5NI HHIEL ŽCIII TEIAM
+SWSF MIIEP OSSIB SNFDE
+HEMEC RZEJI NSKIU HTNIQ
+I8N5 NAEUN M™NVN
+#H128
+NWU?A OEOSJ 3EPKE ŽTKXT
+MLIAS QIUUO HCMIA FHN7I
+EYAER IVIEŽ JUSKX TARAT
+U7DII TPN5D NANNN MTAEB
+ME™ES EAGCS TITJZ
+#H129
+NMSEQ SSDST IQ™SM AAT=W
+U8ZEE BHVEZ CAMDU IYMOE
+NPSTE HFNGS TTITH ™SWIA
+NTTN= PSC=N EIGIU LTIŽD
+EKEXA ICRKE ŽAJ4R
+#H130
+AUTSM ASAŽZ FRNLI IBNCP
+IICEG OJNV VEESD ISKQT
+KETET JIISH 2GXIN SEUMT
+N™?HY RS3ER 56SAI EWRIT
+SEEŽ™ NHZRS DVINH
+#H131
+G?MUR CEŽRG DAADH SIJET
+TETVZ STAO7 ENST8 NATAA
+LDKŽE EYIBX ESSR9 QTFUS
+TIAC™ WNODH RAIEZ SKTRX
+ANMN0 BEEIT IIPAM
+#H132
+I™SOY NM™ST TUTXA 5NIPK
+EREDS NT1/N HAENT UHLJ
+TEEZD TRIY TEHDI DAFGD
+EK1VN RAIQN SMMTA NEEHS
+NŽBIG IWN9E UINNC
+#H133
+EAALT ANMDA ENUAE EIŽUE
+IRYQO JCPNŽ BNASE ZTKHS
+SBXMT FWI5N TCQ™O ŽSRDW
+NGS7D JITRN E3UIE A0VID
+TETTE HITA0 NRNIH
+#H134
+RAAYA MJDNO EBNKC NLQMT
+ETITS NKEXH TESUS WXGPP
+MIŽRF TŽHNE 7FAŽZ AUH8P
+HA0DE N4SWA ESIQI IIE=E
+SI™IR UTDVT IAEAT
+#H135
+6XMN ATHHT BIIBI EVREA
+OSSCR SFRNT JHNRE LDVTA
+WEZPI HKSHA HX8ZŽ ETTNE
+TNEAI SUIGM TKAŽN X™ETH
+YR4TN NTESE 0?FSQ
+#H136
+RRQ4I TECXJ EWJM™ TUDMS
+FJŽRE I8CAD IAANE E1STT
+ESRIA NSIEQ EIETS DSTAT
+ILGEK SAI6B CNAAN DSZAM
+OFPBH ?GTYT RDAPV
+#H137
+CPEEY ETAEU FHDUN PTIGT
+?HSK™ EHQN2 UDINH UNSJI
+ZNEWA A1QTI MPDAE NTNEY
+BSIUS LTEXT ETISZ S4VNT
+AŽO7K IRMPA WIWAS
+#H138
+SMYŽI EUQLR SSPJA EUINT
+EAVHO BNIIR YETEA EENIC
+™NTAD PZNS9 NIDOI /GSTE
+Ž0EIB XMEVT MSUNF AXSFH
+WK5VM AINTU RMAI3
+#H139
+AAAIZ NNGVE HXERU K™ICN
+ŽHNIA TEIUI TWDEC SFAAG
+NFT0E UT3ST MAEIJ AHHSZ
+ELDBN IDIDX THUNC Q4MSO
+N™TYG E7IX5 EESPK
+#H140
+ISWSI INHAH ME6TO BHXAT
+VEANT TEM/D TMNRT AENSE
+HXJST ŽIWNT YFHO2 N™4IJ
+SOLIE QETTN KGEAR H0NYA
+ZEECM NFDIZ USUBP
+#H141
+EDOHG R?FIN RVDAI SE9TH
+QESES D2ISV NEMAY 7ITOJ
+ILNSN SAZUU NAMEE ™DKTE
+CT=DI DSPAS OFEAQ TIETX
+NFIT™ TRSBS ŽPVWT
+#H142
+XRINE IN2UQ OAEŽP WEEAE
+QGIN? SHSAK CAURL IUEYA
+THTHN IIAI2 NHGTV XIYIS
+E/EJI VTWNA E5SMM DWUTA
+FZTMZ CP™HI ENBSS
+#H143
+EISTI PMO6H NTAAT GEXNS
+MTEEB F?J=N SQLNI NQŽE
+GNDES ™R1AI KNUAE SUNWH
+YAHIC NTGZI SA™IM XDSFI
+TTLVU EŽD7E TIHUE
+#H144
+INRN0 IKIAR XTŽSH TZNSI
+EWA1S NDEE ARTIG YH8TM
+AIIEQ NMANE CMENI F4APH
+MTSEC RKMIA SEOVI FESFE
+TAI1D RL™JP GUSGB
+#H145
+EXZLJ SNNŽL FŽMTI MDL9H
+4SLUR 7IIHS HRSOI K™E7A
+CENTI EISNE LNUME IPABE
+ATGFS S=EAI QENUR WNUEP
+CQISQ TTAIA TYSRV
+#H146
+EENTY LTIZF EP6KR IACUT
+HDWDE J=A3G QNDEC ESFBN
+HXA3A NIIHH EOVDQ MTBN5
+SI™EM UŽNNA NITI™ FSMIG
+CNPSI ŽENIA PEINR
+#H147
+TPERF STWER OFA3Q SODRI
+TIPUI AV?ST EHIIF NQRNH
+SMEEH ANEYE JNETA ATM5S
+SEAŽA ITBI9 NGXI™ SLCGU
+M1ŽKH CQZET ISNRS
+#H148
+EBYEC IPMXR AIK8E EFEVI
+2ASNE HU5ŽE AGIHH WTSSY
+SHQTE M3XSO ZKNUF ENHSW
+NLRDE NAIIA PTMIA IJTTŽ
+T8BNI N™UUS ŽJIAT
+#H149
+HTMNN N8SDS SKZI SDE3X
+NEEIM AAUKI LTŽTM JOIEH
+DENIS OGIEA YSSŽA HMI™E
+IQBFA TEVLE 5S9SQ HISOD
+JPCRL T3ITW ATUAE
+#H150
+NUH9H M™TQV LBPLI HŽSNS
+JIEZO AEMAF TE0MT BEXT
+PIYNR TIEEN DES6N QHSŽI
+ATHCS ETSDA Z5AII WT7HE
+M™SAN NISGK UNRTE
+#H151
+SGIHU WRFEM ŽJDWN NA?UB
+=1E9T ATIAX DDAZN ES™TJ
+B™IMV RENSN QIIEO LGNAV
+KAYTE NSIPE IEUEA KGAAH
+CTWTD 1ITTT DDNEN
+#H152
+ZVMNS SYI2M FTOA? I2NNY
+IEHNE EAUGT PUEAN EMBRT
+PS™RV /SRES OTZEI UVWŽR
+R5QJA FIKXS SLYTV OTIEH
+TNCAA ETUNE SDITT
+#H153
+MASDP NTRIT 0HAŽA EIFL9
+TSENG ZDNDN 0UULS AIRUN
+TVNQB IYDEG LH0WE EITNV
+KUNFS GIN™X SEWTS CGTRE
+TSEJE T8NTO UEUIN
+#H154
+NTQI/ SNTŽI SISWN RUEQD
+ESEEY OXNI™ JKNZE MBXEI
+HE=TA XNEIL 5NHUW IEMDW
+CFTLN SRNPH IDE4G CHNNH
+TNXBV ST7IA ™AUŽT
+#H155
+MSBLV =HEFR SIEI2 ESSKE
+IGNZU IEDŽH HIIOS SNZEI
+IHERA KAEUJ I™OSE NN=RN
+H4GJS IIQBC CWDAT HXAAT
+JEINY ICNA2 TUPGI
+#H156
+ACPAQ TSIHT C4KLN STNSE
+™RTVE DMFKA AET2G ERQU™
+YBIED ZWXAN 6IAEM MHURI
+™PVET NUSIT TXDIN S4INR
+JTŽAQ OSEEA A/TVE
+#H157
+TATSN RUITN THTZM EETIL
+2AEZE QOISN SDMRP N3KDN
+JTUEC CGNAT LI™XV MTEIW
+S=NNA TENBS GQYR2 LSELH
+ASEOH FERTQ ŽKI/H
+#H158
+QSVRE ™IIM8 JSSCN UHSWH
+HTAYS EISFM INZGI IIETI
+EMEXU TBCEK KEIKN OANDE
+UFCSU DMLSI ACNPI EIEDŽ
+UA1IA VT0SN 0LA=S
+#H159
+ICF7H MREEZ IAŽAS NIN7Q
+R6EIU JTIW SIASI SET™J
+ETSNT NN8IA ™DBED LTRT
+XTESI NVY=D KDSNE XSMEP
+SGIH ERAAU NNOUX
+#H160
+THDTI DJISA AHAZU NUDEK
+DNSEA I5ŽOE METIF ATISE
+OSPME NNJX/ NGI™E DVF8P
+™SVLE 6ISSR 2ZCBW AIQHT
+ATSTN REYCI TZCEU
+#H161
+/JEKN LYTTX QCRMT IETDN
+ISDP? TIAMN ™OAMD ZSŽE?
+NEFCU AIEUH SIECR 5SENG
+KXRAA IZPTI TARWQ XQDŽ5
+ANINI DEEBN ATEAV
+#H162
+ZTADŽ AJKDE RUFE™ FNSTZ
+Ž1TNT TEEWT P?GNE ZKETH
+HIUNT ALJNM DSNON EULIN
+VE1XE B5RNC SITUR TQ=YI
+SRTNM NOTEA SIAYN
+#H163
+INPMR YFAS™ HIERW QKTWE
+HETES ŽAAUS T™THH VDAHL
+E9SEM IENNA I/BET OS7EE
+AAK™W LU9M ATINR TGWTA
+I=NID JZSNC NIXGI
+#H164
+ZVEIU JINAI T8TVE LHAUN
+UNNED TQMZ0 O7HIU LSMSE
+NSEEW AEAIH ™ŽHSI FSRRA
+CIPSŽ INGYE VTIE DD7KO
+GTTTX 9SEAT BSADN
+#H165
+WNHAS W3PTA TXLUC ETNDE
+ITŽF8 NS5EE ITNKN AIEMA
+MQTZT DESŽI TXRAD VJ2YI
+RJUAY BI™BN EOEMA ELTET
+SFSM2 AMSND TZGRO
+#H166
+ŽNHTA 3IGES ASN3B JTRAO
+DDTNE E™XSO J4SAE HNNIS
+YQIOD TUGLŽ N1NMC NIQE/
+SKVIR TEDHU EKFBP UEZER
+VTAVT EIRIS AWNTT
+#H167
+OUELO RNESŽ EŽTE6 PRSGH
+MN6CT ESBAE RSFAI TTICI
+TJISF YDMVS XK/NE ITHSA
+ZIIQB WLYS™ HTENM MJSNN
+SY6TE U7EMD VNSVT
+#H168
+XN=VS RESES ESDHI NW8A™
+DLE1I HIEGF RAESU ITDSI
+EYSQ? AMŽID IPNNS JRMNT
+FTIEG HOSIA ZMWOT I™AT™
+T?UŽK CBEWS KSTEN
+#H169
+NILTK BISZD ?2ŽPQ EAEHT
+AIYUE ARURG IFKIM ŽESD™
+IASDE SSMET LIETA XNBNO
+NSSHC NSUR9 VA8UE IZAAZ
+IN0EJ DDTWE KTZTT
+#H170
+QIAFO XLTHH 1MNTA SEEN2
+K6NUA TITET ETNAA IGMBŽ
+JTSKT SEVIS AL6XS MTJE™
+T™TQU CYSZE A™NAM FRTYE
+EPEU6 DNWHU DISRS
+#H171
+8/JXI NE6TC ZUTAŽ SETDT
+DOMNK ŽŽTN2 GJFTE PTNEH
+AŽNAI MNLIŽ SLAEA HJBVA
+YAEF™ EHNDR A1RGW MIUNT
+IUIRS NTAEE QTAEI
+#H172
+ODDTW H™YNF =SŽEG UZLA1
+DTEVT MEDS= JAEES SNTET
+PUVAT TAEEH SXC1T KINMB
+VUTMI N2D™T TASTQ AEYLG
+ETNŽT USRAK NOAIA
+#H173
+JDNU? HZNIV MNKOE C™BES
+ZNFUZ USNIE SDCT7 NIIYQ
+RT=EO TSIDE TEITS TITAG
+NNMNU ASEML AXŽIH NMKVV
+EWEE0 IASNP ZM0TD
+#H174
+HI/KT EŽRIA SEATC TWXTA
+ANPNE SNIEI GSWAE THNSH
+™ASLI TEMDY AF0E TAZHE
+ZRBI5 XEDHR EUUMV CQHON
+STTTJ HWPO0 A/TSA
+#H175
+ENOMQ ELTTU HŽ=EI T=ETR
+YOUSA NE8JI TPTRT GNDBX
+EEKET AIMRV S/G™X NIINW
+AVSJH NF3MP NTDNI IASNE
+EOUKZ AYINV CARUN
+#H176
+ASAIY VTTI9 OHILE ZTSEE
+NŽ8RD WXESM ™TM™I VM1SA
+UYNER NNGTA EL9HN JANPS
+MUTNM GAUSB EFTCT T?KKL
+HSSWT HADTE ETQIE
+#H177
+SANOZ DC™RŽ HTIIE ESIXE
+HJŽAN NFYH CNITR XNI5L
+MANEI IDQKY 5NUES NAPNB
+MWI2E EATEW XGTIY SB3VS
+MNRU3 EICTE RTIHA
+#H178
+RIFS= BU™IM ATRHT IHTDS
+NSAIR AXVQT EEM90 BMEL2
+SPŽCM NOEZU SGEEA KZOAU
+TNL9T ETAAJ OEYZS TAAUI
+EUTGW TTNEI LUAAA
+#H179
+3RCRZ DUEEA NBZII ŽAPK6
+S3EET NE/™N UCHI/ EEILB
+ARLNT IQ™ST SCSBT VNNIN
+IDSTA IDNSE XIWUU OBIJU
+ISAWM SGEFY JLDED
+#H180
+HNSIM T=EH NM™MA THVŽF
+ETCHS GLAJT GENNE EE=IX
+GABS0 TSŽON HSSTI ANEID
+NTQUI WTWMR ETRS2 BZTON
+KAIPC WYSTD E/EDR
+#H181
+XNISU TRTFI SPŽHE IAYAV
+K9MLA NMCED GEZF™ EŽMYV
+UUIEN NB/HT NTSIE OA4NI
+IEYEA QWNUX IIJRA ES2ST
+TXTAN SSMDR T0HEK
+#H182
+EFXN0 8NERB TPWIS ATOAE
+NCEIM UZHTA UMTAE CVRSQ
+WNTAG FSRNŽ TIEAK ™ALHA
+GMSEG TTHXS T?HTY TNMIE
+JNE3D ?TEWŽ IUSRZ
+#H183
+9EXEA D/TEM HTTST ™SBY7
+PETLU VIEEM HNRIŽ NAGJT
+ONA3 7UMAS FSSNV CZETK
+ENQID SNINI GMNIW ESEZT
+YMKHS UIATT WŽDN™
+#H184
+TZET TSTAY IIESP NOEDL
+JRE9T ANQUM IETSŽ AUJRO
+SCHSY N™ISA 5TPDE BDEDE
+FGCAU ™VTAM 5ETKX 5STWD
+IININ EDNNY CNH=S
+#H185
+MVM9H T™JII TG98T RQMWT
+LIIAU SRETN TLT9Ž UZNEF
+ATMSH 9HSRN ADAMT NIEQK
+SSS™O ICEU PYBRE ENAAN
+ESTAT UUXSM EEAUE
+#H186
+IND9 RIŽCB EŽTPD IEJHH
+TAFVN E4ESA SXSAO S1THT
+TF4RY VTGŽN JE=SI NIASK
+MUAWS IQIG™ HSEWE URSEI
+AISLH MTADZ YEINE
+#H187
+IATGS AXJIM DOOED ISTRT
+SQK4G PYACR 5OTTA NRIRT
+VYBEE ZXTSG DLUAE DTENM
+MPN™8 4ŽWAY SATTI NINME
+H™EET 7NASF EE™HN
+#H188
+2YSEN ASXIA N™EE GIAPA
+TE8SH EESOT BNTNR MTVST
+LAWHU EECYT ZATQH TJIIW
+3URKT I?FOU OUENA INEŽ™
+RHHM2 ADAMN TMURT
+#H189
+QIZII WJR2P TCURS BAŽSŽ
+8KSHR ŽOWSI HYTE™ FSXMU
+TXVEI TMETD JSRNE E6ERL
+SŽIUT ES5RN IKSN™ TETTN
+GENNF NSSMC 8SEAI
+#H190
+ATACD BIZNK IE™YB XNIJ?
+MAASS AIWEA ™VHŽE ARSHE
+SDTLK ENIEH MANEH EHTIE
+UGSSR LNTQ9 OFIRI IA62W
+EHIA9 NUUIT TPQF
+#H191
+FRLHI ŽMTTR AMS7D TTQBS
+FS7NM 2NEPT ŽAX6N SHNIS
+MAEPW TMYIZ CSKTN ORKIS
+JNETE URIPR QGEYŽ WEEEE
+NA™SH A3VII EIZIA
+#H192
+VENNY EWCIB 7HUII TISWN
+MHOWD ZIIHN TNITS FEUAB
+ASEDE AMKMT ISQSE ŽTXAH
+LARRŽ TDPOT NNNMA B™J0E
+SXAEE TY5H7 EGPA1
+#H193
+MTEEY N0EBE IRSDN AEIE2
+AUSCA JUQCN HODEE RTKLI
+SENSE MTM8H FNS™H NIATT
+NZ5HO VWBRA 0IHIA ITIAK
+SGCSS TTRŽO PXTPC
+#H194
+OIZRB SNIAW PSCAI AHENI
+SIXDI 0AEHM FDVRT HQNEN
+EANIŽ AHHED UAVTL YGSEE
+UKITU RP?PS AGE™E IJA2H
+?TAE3 IKTNZ WIZBT
+#H195
+TJXRI SUDNA NWE=F SRHBD
+0IBMI ™YAAW ENQXH L/ATS
+HZTSE QUCIG FTIKT SNEET
+MENET IUŽEN ŽST9D P5HIS
+SZSWA HRNVE IECON
+#H196
+ABEIS HTSKX OLB3O SMSHŽ
+ITASA NNIBF IGYUT UMINT
+N™T=D VCZNI TEAAE JNEAH
+RHJT1 HSAES EHIIS ™APEF
+QEXR? BEOUI 2WTUE
+#H197
+CMNIŽ ATBSS UDZSZ 8RKAC
+JENAM EDIDE ASNEC 0TZUL
+TH0G™ YVTFA EEENN TESO8
+TSTML USRAI 5KITZ TWPID
+EEPHD QTTAH NXTAN
+#H198
+GTŽEN RCETR IINMT WOAWD
+7SFNA 0SK1T EIAIP EH9TC
+AEMIN EDNNB ŽYVJO SPIIA
+RBLEE EHHTX TESUH =AS™Ž
+ASZQH AMNKI IRTSM
+#H199
+UVIV2 EE?IY FNRKT STDER
+SAŽJT ED/DK EHDIE BTZIL
+DE0IS NNPMI ETANT ANP8D
+ARPUS ONSM™ ZGCME GITFS
+MWNII QŽTES AAAXF
+#H200
+YNRXX UIEIG TUTSQ E0EBŽ
+AVCRF A?NTT PNSOH FWDD™
+?CMMS IANEW UG/EE IJDSA
+TIDOS NITIT TSKSM EFSSR
+ZEDT L6MEA VASET
+#H201
+I?GAN ZKBRI ENIUE ™GHST
+VŽQIR DXTED YXLTI 9VNEF
+SAEME ADIDW YAAT6 OIVKT
+ANSIE 9IEŽS WDIDP MNHAJ
+YNUAR EETSN TCNS2
+#H202
+EJTND PNARU BEZTN Y™ECW
+X2DTT NIREC FENŽE SHN5L
+SENIU RHE/M ATQTP NAIUX
+NAMSC DNIIQ 5UDEK ŽA0IC
+UEIVG NFINT OTAQT
+#H203
+SJUQ= TIVIV NAERE AANE
+A2ŽSY HES8M CU™AR DIEHD
+LHATQ IEENI AIUII WEXKR
+EPABJ MIAIW SZGEN FIUFA
+WBIS3 9OUNN TAFII
+#H204
+ETNOY AEMQS NF9NN ICTSM
+ISPT™ ZEIKL N1ASE TEMST
+IHDTL BIGUM WFAW/ IEESE
+IXJŽT RA8SN TINHL VLE1H
+AMDTD ™WE™V DNRNN
+#H205
+5TTBN VLMCE G0MSN MSZED
+AEUOE IISGS PSREF URHNA
+NNUOR 8TXTA TEWE SILGE
+S3UEA Z™RKH 3SENP JTITN
+RAIII ITZMZ ŽYIQS
+#H206
+AEBIN GSXHC NŽLAO STROT
+DMIBP SDPV7 ENNTI WEAT™
+NGTTA SANE/ YETRQ IENIA
+NRI/I ?EPDR UIZEI PFUŽ4
+DYAEF KEQAH MDTJN
+#H207
+SAHUW ™QEIE AITSD 4AIŽE
+GMWSE YTMBI UN5EZ SRFAK
+DHLER TNISS RTSAI SZVTI
+A0TOI =XAF? OECYD ODMSE
+JAIRE EDP™S ZCISI
+#H208
+TIIKU SHNDT E8NNE E7EDT
+6NPER SSU8S PSMPZ ARTOE
+SSUEU SKTNA LTGTL ISEMB
+IUTDQ ŽTXPU OVEND NYTT0
+TAOJN XVEHF ™IWAC
+#H209
+IINL™ HNNKM SEMHD ZHP4F
+WDEIS AIAIP TQO8V UX?TT
+IRDT= CSDEN ASMIE AT™ŽT
+ŽNV™P DSASE EAEMA NACUS
+ITAWI EYEVB I1EGJ
+#H210
+ŽZ™TT WSJ1T RSLSS NOETA
+CIEYS HSSIM NSA5U TEVKS
+TIHQT NEHIZ ETDUE ENI9T
+RMAŽJ UEYAE ISMGI EXPZN
+AHBCB /H?IL NSFHV
+#H211
+MESRD TS6JK TTGPL CTIUI
+ENSSN SN0DR SYMON J/WRE
+TEAIA MTQTN OVHIT SDNHE
+IWFRZ TA8SH IENEE ONBEN
+ŽKMWN T9IXN ™QMCE
+#H212
+TBNI2 IHLNN QNPNU IIIKN
+IM™TR QDIEE YCECO TA3HU
+NŽSSD IINVA IŽ5IE DXTCE
+AJEFI ZU5E2 WXMSS AAZŽR
+XQNET GDMHE AENAB
+#H213
+TNTHS DUZET IN2TJ SNH3S
+AEQXT FEIRR NYMII RGGOE
+J™IIS WITN9 DEPTH LNSŽE
+E4ABT JMENV NNUVA OZCES
+NKNTK AIRE0 UPMSR
+#H214
+HTYNB OQHET AUULT E™T7I
+ISNI= JDTAU ENIPW MSFEK
+ŽEAGU PAUUL ZKIHT ™YO3T
+ELVIT XCAON I?MAE TANNA
+FAIER 3AAEŽ MSUAE
+#H215
+™ENPT TTQME GRUSL ANR3T
+KWESO IGEAC NNEAQ ŽJXHI
+9UTWA STNI5 ESVNW IHEEM
+JNDRZ ŽSENA FBRIE HSRIY
+MWIHI ZSNAP 37TTO
+#H216
+FLDSE NJCTM OHTEI CSSSE
+IHEIU B?MAW FNIYI AWEN2
+SAISG PHRVT TBSTT TT™ZŽ
+DUSES WRR0D GA3ET ETEME
+IKQD DDANS NAX8P
+#H217
+SRIEW UEFEP YWERR ŽTTTS
+IŽZ7E BARTM STINC HINHS
+TTESC AOGSG IAIAU NKRBH
+TETRN QA™MX D8EFS ENS2D
+EŽKVS XJLS0 R2ITS
+#H218
+IDEIT NILHI QRESH EA5J
+N2VNR 7EYAQ ICEPI T5IHC
+ZMAFX SDPKX AUNS™ UTTMT
+ENIGA SIMBE A5CES WXHŽM
+NEDAA OIJTT NNXOE
+#H219
+DSE8S ID™ET ZVNTP AUSLZ
+WMTUT NQ™NE ERCUN IINIH
+TEEBW A9TSY CMUTA SSDI
+E™R5K SPISI FEEEK NIJI=
+AIDŽS A6OMS SCUXG
+#H220
+7INIL AUEN E8ŽCI SDSFR
+NEIUE NTNQE SIAJV RKUIA
+OKYNS ER3QR S4UVW EISBS
+NJHXA TBFTE SDGE APHMI
+MES1T I™TRI MNTTZ
+#H221
+ARAEW NQTSI N9UZI AQN™N
+KŽP3U N™STŽ XDRTV TUA™T
+TUNKK =D6JM ELIRT OETME
+MNSAS WIAIO TAE8N EBACT
+YIIFG EEENE AUUHX
+#H222
+DAUMB MZ/NS VRINA PTAFI
+XSŽN1 RZLTQ EAHEE DWESK
+AWAGS HSSMZ TYTAY RT?ZN
+JC4DO UNTET HISII I™INI
+2EE™T EELAE KUTAŽ
+#H223
+LGIEZ ANIMR EVNNS NUD/Y
+A=LSE IBETA I4EDN PMŽNL
+CAISE GDUEJ OITEN ESLZE
+YXHST ŽNINA KWT0T HDSTI
+MIUCI RX4LF SHMQ™
+#H224
+TXJAN IVSŽA 1NDSD DRMIA
+DESRW TSID1 ES2ET BIREE
+MNSTP WEKNG TASNS 7LTZI
+INSRH OPIS0 FTZXT UCEEA
+AFYEG DDICQ UEKI™
+#H225
+DH™IK 7ESDH AEMPU T™IEB
+ITYHT TTMEO XSTEM EADNV
+NSNHE ITSNC N6LNF WA?TX
+QEŽJU SAMSG TEMNI E3UOŽ
+KNS0K RTTIO NSZCF
+#H226
+ESPRC TUT™I KTAUS ™EŽGN
+ANDAI IZJ3D TILUE PVBQH
+8UHEI EASSS INRII TNEIY
+™?ESZ M5FTU WAIEV EUBSR
+EOTSA PXFP7 SNRCA
+#H227
+T™UHN ETELE G=VŽR ZP0FS
+RASPS SUTIU 8NAMT GAZQA
+AENNI W3VIT YU2MŽ TETIH
+TRVDN ZSNUS KEJXI TCBZD
+INSOI EEDEI NMENF
+#H228
+ŽEAOA EAAEI IMKT8 NNPTB
+XVDYF IDCWU IEJNT OSART
+EEUOA KSN3™ UEFDI FNTEU
+AAZEN L?HQN NCFEN INMYU
+™NII8 UFIGT TTMH6
+#H229
+EELIH AEON KWLTC I4UTE
+NARAZ TFXET RŽJ6Y AEVES
+™NIDA NDIMA SES5N TSPTH
+WMIHS REHTT OIIŽH TYABX
+GEN?O KDA™U ?ISQA
+#H230
+LWFSL DHZHK ARQNP SVIIE
+OŽ™JN TSHPD =IEU= UAEDA
+EDSED /II0A CEEIŽ TIIIM
+VESY SEAAE ATGTA XIRMT
+LDICA 9NBSH SASTP
+#H231
+HIIIG GR2E= MB?AD HJHID
+WNZTI XCSŽS NPA™I WNNEN
+TIYEE FEOMK EATRS TXHAN
+LIFAJ DT4/A ETENS EASWE
+IQRVO ZUA™H CTHAT
+#H232
+ESEKP NBSIE TEUXT AJTFI
+NMZZN SDANE HA/UA YERM™
+ŽQIIA IE=NS CD2OF OTENT
+IHWVL NEUI6 RTNNI CRAE/
+KGMNP TQTAP IYRWR
+#H233
+DNJNR IPEIA N=BD™ EEVS8
+GIAAA VNTRL IUEKM RDTSX
+ISCAI CYEA0 TQDWA MŽWHT
+3SETF DTZBS STBXE NTEOL
+ANTET RZIA= DHME
+#H234
+BII7D S1FSW E0ŽAE TVIF
+KLENA MHADH AGIHN EAXMC
+SNDNY TKAMN NIIHO EMIAA
+IUSIE K1ENE ™DPJA QETUB
+UGAV0 ITSZE TRMTI
+#H235
+EEYKS HNXT GNMNF REESE
+MAZD/ ITSTH ATRI= NUTW
+UIIBN ATASI NEGLL TIEI2
+SMHJW AQDTH VC™T= OAJSA
+EMEQE ANT9S ŽDPHM
+#H236
+EWIA AIPS™ UISHA EITMR
+ŽNSIS RJIZE ASRTI HQEAK
+1IST 5LTVE HANEU IEFNO
+™BENN AMRI GLUDS EIF5I
+MEVNT YS=U? XRQCA
+#H237
+DI=VN E3RTO UIATI SIAEE
+6MŽIR A0HZP EE™LT NEZNQ
+UIACT ETAYD AH8IE MFTTO
+XSMWA BSHDS GJESD UAŽNR
+KALT WSVSS IGOET
+#H238
+YOIKS ZFOR6 IED5N HEIII
+IUTEX EIADN ITNTR UNTST
+ANETS RUTSM EGCIV JŽ™KW
+AE2EL HUSS9 BTXQA N?QAH
+VPEDN SCXEJ DN™NN
+#H239
+HABOH JSSPU ŽNMIQ ISRET
+A2K™S IEFIE FMHTE NNIAJ
+HIIGY 6STVI X94ID NNHTA
+SWRO EEHSN EU?NZ IKECL
+EBQXN TSTSM ESRWI
+#H240
+3NDEW ATAEO QUTPV BIHAN
+?EDNT EWNS8 DNE™G IDKMU
+EICSI VRŽTH NIRCH TE™SE
+HNIJ GEDYX IQTNS MSRN=
+LINFT RTZ7E ATŽAA
+#H241
+PNLŽI RUMA3 INGIN EMNSN
+EAAMD TKECM ZRC?T SNSJI
+0IVAO UGKŽA OTTZ™ IHIRN
+ATWWH IIAA? TSEEX MFEEY
+RRIIO /ESQV AEEBM
+#H242
+EKSCI ZB?TY RSTST XIEAE
+UOTUM HLITB ES3S HWSON
+AIDAN SNEJJ ILEIS TIU4V
+ŽHDT1 AFGRM REAAI WMS™P
+CIGIE 3NESN YQSRE
+#H243
+HOM™A EIZSB PENE™ OETIA
+TWEMA BWTEJ QNUNR KSIIA
+2ORTE ŽRSZE TANNT 5JZNX
+TV4PN I=UCF GTSLH ESDIU
+TRIUO NNUN2 ERSTY
+#H244
+UHXGI T™ATŽ AJ5GL T?1EI
+RXPTN NTHJS PIRSD SSNQN
+EZDAC NEWEH IIDAD IBZQE
+EVRNI RYTDA ŽEOTT SIKEN
+GNFMN INE7S EM1SL
+#H245
+GN21C YIMWS NATRU BDŽNG
+VTWAE TRUGP JK=FR LE™IA
+JAŽIH AASII ERS3E AMŽXA
+TANTU TMISE TE™TT EQMSN
+SIEEO APZTE 3UDŽA
+#H246
+NKIEN YPEPI GTTRT XDTNR
+ŽIED5 PS3EH ISMRE VTUJJ
+AHABA ATASI LVCWN MFMAU
+OEIBE DES™R YRTQ0 LSIIN
+OAESN 5HE9A OSAIZ
+#H247
+EINA3 TKDHZ ™FTMD QYXBT
+LSEBA ™IDSE LPNGN RESIP
+JSIMF A3AOH W3RSN MAID3
+WAVSS EATJE 7INFI FMIEA
+ITTCE EEDŽO UNVUT
+#H248
+™=HME TYYMA ?ITAD ?MPDT
+JQATE EHNH8 TMSDN EIEIS
+NACIE LŽERI SY3FS GVSNT
+PKRAV BATAT ENIGZ IXOGE
+UDTFF SFSCT NAUWE
+#H249
+EDSAE INR=™ IWRA5 EEEST
+CEGIJ C?AJH MSQHO LBP1Z
+ITUUE PTTSA INEE9 RHSHN
+WASNŽ OOAYS VQMUN TINHR
+IFIXE NTWIS TWKST
+#H250
+XTJD KSSAG 8SHIS ECOTS
+SHMTI MU™ZE ITTY2 VARET
+K™EHA HS3NI /ATAC BRMTE
+FATSN WNIXM ŽAYEN ETIQF
+MENTP EELUD NUL=D
+#H251
+TBGRL TTZYI KHR8Ž VAUID
+SQA=R NATNH CIJRK RFNIA
+BISAE EŽUIA ES9TP ESEEH
+INOIE WSXND 3ATE™ MYE7E
+STU™I NISCA MŽGNZ
+#H252
+CNS2N EJSLS EUNFA IDSI1
+OIESL NRIIM TDIL RT7ME
+ETMMT ABSXW NŽHQP TABII
+T™EYS SEA9T LNSIH AU3HE
+QKMEG JTEMN SZGDV
+#H253
+TEWTF VSDLI ŽYIME KQAMS
+RYZSS SGET1 XARKI DMB9E
+SQPAW TE6UR MCSTE NTTES
+SIOED ™TMMH AN7TD IFSEA
+XWEAA NJLAI ST1TC
+#H254
+RQHSS PEVTA 3COL9 WMZUT
+TSSS8 AZERT 0AETN UTSGF
+™DTDS ISIFN BEKKJ ŽITEU
+MSTRY UETFJ ANEIA IAS/N
+HRETP ESIXM OUAEO
+#H255
+IALIX ASŽIV ETAF™ EXA6N
+NNCEF JTETU SABUT IIPSM
+SSGUR EVITA TYDES DANTR
+INRTŽ UMUŽA ™HSNH OETEU
+KEV11 ZIEUO Q7WN0
+#H256
+THUE™ SIEEA D/ROD BITNT
+IEVEŽ PNJST QFSLI UE/EM
+MSTAX SDTPN IEATC TMDSW
+NNHZE KNA?Q ATGB™ YS9YA
+INIA6 FISME RLCRZ
+#H257
+MUSEH TN6AE I7TDU HQMEH
+KISGU TWI8E RBDN ASLIN
+?SETE HNTI AE1UA NIJNK
+JNSSR AWVNN ETFEM J™XET
+ASZCT POIŽL MYMII
+#H258
+DUGAA RNUUP NŽHAE ™TCEM
+XEEIT DTIPE IAFTI LAVTŽ
+AUWIN EYOSS NJBR6 RSQRE
+WIAQS SA/=9 KAUIN ESJIH
+LDLII ™ZEP5 EAWNI
+#H259
+PISD6 VFINE L/E8G REALS
+RGIAR UIAXJ =AITE IHCEE
+IKSSB 0ISWN MCUNR AUAER
+ZOTVS XNAMD DHQT TITAM
+EIEŽT OGTEA SZYN™
+#H260
+NWAC™ TZGVP /IA1T TIXIA
+TAVES SFSET ESNH1 JDBHS
+XNEOP IHKUR EIŽDU DQDTM
+ASEHF TIINQ E6MH2 QNSSE
+MTELS ASENW WŽYII
+#H261
+TNQ0S GNRSB HFY1E T=GUI
+SDRTG LJSEE ASBIA ZIDTP
+FEENK TNITT TBDER ™ŽOHM
+HAHSA 1SCEN 0ZTME ANTGA
+EPMTA RWDEV NAXTT
+#H262
+NLRCE GTEQT T5?BD SEWL7
+OTAPA M/NES ADGKU ETAAS
+ZTTAM ASDAA FTT™M VYIT2
+EHXEN MABEN JEKZŽ ICDIT
+HAEK MTSTA T™JMR
+#H263
+CDIVT IWYMW NTFZU BR™ET
+S1ENM TETRE /6AAN NHSLP
+STGDA EFHTJ QDIYI XYIIU
+EUGUE EIATA SENCZ ™HKIN
+ONESA NN4DA AŽ™I8
+#H264
+BMTNE VTUQ8 LQCYI XCESU
+NMVEN ITA/A XGIAA EIFNA
+IRAEJ UPN4G TTAE™ ARESA
+SZT9I ODKET TWHŽE TIMNI
+HHAED N1RAY KROGA
+#H265
+JEKSS ESTIN MQTRH IYTIŽ
+5EB5I NYFE™ MEEAI IN/TM
+NWIIN 8SSRT ROE™Ž AŽUCN
+BPOIX ŽNMIU HDGN4 TLRDN
+NTEAV SJZDC NEEYS
+#H266
+QTBAM RSNCE HMS™V KTASE
+ANCX TMHEA NISDU EA0LZ
+OEENP EETTP ISFSX TI/MR
+SIIY5 ETWIJ EXVSF 8IIGM
+NISU3 USMTN ŽUMBŽ
+#H267
+ŽEIAE OEAUW PFINN OETWN
+8G™CA MJHTM AJNIY RNJXT
+TEIŽB J7EDV NTIEN NZEAP
+THTLS INREQ VN1KS D5DSS
+ŽRDTS ITTHN ENRM3
+#H268
+LTHAO XSHS= DX1PN HLRDI
+GENET FNAQ™ IINB™ SVS8S
+WSYCZ TETMS NFIUS ™JUGT
+AAUTŽ CSRTT KEIEV UE8EH
+XEITS RRISE SIE3N
+#H269
+IIMHM I3GLM ZTHT5 KŽEAI
+XNUNH ™ITAT NEQNA BŽETN
+JNOMN ESADR NP8XS RIIHL
+HWŽZA EGECW AHTTD IFVN0
+NSAI ETUEE ESAY0
+#H270
+™IRRU TVSNN NTSZL AIGE
+MJDHI NOYQN BEBDN PSHAL
+TNEAD KSEUN YŽ3WG ETIUE
+U1FDS ATSNI ISCW™ 1SXTU
+AEETO M5?IT ETIGE
+#H271
+UYA/F PNXMT SDORB SREVA
+SW™WT COQSK KTRML IRI4X
+STŽEJ SSNAT TICTD TESEH
+MS™ŽE U/MIZ ESSEN HTMAS
+0GSŽT S8TEH TKETE
+#H272
+A/YT2 5US™D RPQZU TN1NT
+GEXMN OFIAC GEEPO 0TDEI
+AZTTT DRSAB KTTNS EAMAA
+GATSE JDEER ANAAL SDWEU
+UIIET HQCŽN RV™IT
+#H273
+HPATE NMCRT TEOEQ UANTV
+AEVTE ML4AR NII7Z NINSD
+AŽESI IESRF HLEIE CMQ5I
+TA™KN DJNIN YUIMN W7O™X
+GHM9I SLSNB QEFON
+#H274
+NHNHU FHEIK XORIA A9AI0
+9EMAT ETEIM HEKAA WCDTD
+RHPAB QEGE JDUIN ISSTE
+SAITA HD?TG ELHTS AATAA
+HRN™ YVŽIE M1ZAI
+#H275
+ENEIK JPASS QUFNT KI8DS
+ET=AT IILMH YIESR S2ZDA
+TEXEI AIONN AIHBM N9RTB
+TSNEW QXCET KGVD™ AZYUN
+URTEE ANADI Ž4XDX
+#H276
+OITEX W4DQL RJTEE AYZAS
+MCTEI TNNAM N9I™H EGTAI
+ŽGFYA HSJRN SUU6O EGPSN
+ES9TS GVYAB EDMKT INHNR
+ENŽTA IITSD QET6D
+#H277
+WBJNE DHMII KESIR BT1DN
+ELART SSAZF YEVSE IICNN
+LNTNA IVXEI IDSTŽ E1AMH
+QVSAN E?™OS STEUT ?VSYP
+HISFQ IGMI5 DDEM
+#H278
+TWMK9 NIVT™ EEXRN CBRUK
+EQISD ŽMIQ5 AAJNA FAPTN
+STITY JTEMU OEEIP ŽE?SR
+AM?IO HNUTH QLETA NENDS
+IHSEN CSATG 6IFZA
+#H279
+ERIRS IITSL UUSIH X4KI0
+AAEQŽ GNRWY PISME N™IEE
+TMVYS DIŽET CNSTN SHŽEZ
+O5BIE A/AA TDLNO AUTDM
+M3AJS ALCPF TREEI
+#H280
+ALMQO TDOLI TEŽBS ENSAT
+CNT=I UWQ8S SIXDD TEIAF
+EAAM™ PSHYQ MTPVE EKMSE
+HZTTN JE4AB RSHIU SGEIG
+TNUNA =9OT™ ETADS
+#H281
+KAAAA HESYG NNTHY XSSUS
+UET9J ™SVIT TCROI SUIY2
+LTVLQ TDISF VRWI ITTPT
+AFU?I RSADR NEESE /EBUŽ
+ENWNE SERG/ AMEZT
+#H282
+ŽOHMA SIGNI EHSST ZTLWU
+4NMTN EMGOF NTU/O TSYWA
+RALSH IUIIB SEBUM KIIE=
+NOEHX NHI™A EI7IX CPENE
+DVHSS QEKJI CES0N
+#H283
+TINFQ RTHEU SEAIE FITML
+R8DLS SPGKE ITTSA EEASF
+SIEAY DS0TI NŽJDC PSMNA
+B™XED MAAIZ NTIZ2 AVZSP
+EEWNO R1BII RMHU2
+#H284
+NSŽ2U ESIDM HPJ=A LIMKI
+HSANI AGRSK ILNTZ TCXIE
+RY=IT EWNVE OINOE NHMLE
+XBQ™E SN0IH NILFI LITR
+EAATE U8BRN RUNES
+#H285
+FARLI SES™6 ITNKQ CSTUY
+S6WEI ™TVII IED2N JSAGW
+MQNSX EPNIQ HNVSA SSCEO
+HE9ŽN ŽMIWU DTTRS IHAEH
+AZADM IM8TE ECTEB
+#H286
+ZU9Q9 EITET EGDSF AG6AM
+SŽSBO E9CAD REJDF TISSN
+EMDIA OTIIS NNN3Q QYLKN
+RTEŽH FARIP ITGTR VGENE
+HTNSW NXEIT ™NHNR
+#H287
+ANEEI ZQUHI HA7JA YNIUU
+UNIBT DT2MS FS5CA ATNNX
+EMRQJ SŽXEI EDUPT S8UHS
+IOXTW ELGNI ESAII KVAŽI
+T™EI1 EZSLH APNEV
+#H288
+IGUGM ETTNE 8PXAK HDRIE
+CTDAI ONNNS SIAWS AE?YP
+EIEEB ATWIV ŽA5IN MEIYZ
+SRHPI MMHEA TXNLH 8T™IA
+1ESRA AQJSJ VHOFT
+#H289
+LSLH5 DSBYD IP™RW TA™AX
+EGI1I 7DQUZ IUBTY ENBUT
+ESIJE ANTTN AEU1E TNINU
+EIGS MCIVN OIŽHN 1NSNI
+FAGAV EENDD MKAEI
+#H290
+IBLTR WESSN AHTEU XESAZ
+WN6IN RHSV/ IJTMI UJDEE
+™TTNI THUŽN AEUOE UTCAC
+TQAEI I?NŽS I2FŽS KSFTN
+AYEGV PEHJ GDA5M
+#H291
+AŽZGY =™BNA MNDTA PPEAR
+MSLDF ET?TC UOI4Q ANSGE
+IENXI AETTA 8IIHR BTDWJ
+EUTEN UPPGN SSNJE KEUWN
+TVZEY UNI/T IHDAT
+#H292
+MP25E STITU TAEAB MVEM0
+IIAUI CKXAP OSYSG UTNI
+SMIQS NZLMD ONENB IE3EP
+TENSF NJWNŽ IHDZK GETES
+MHAR0 ISTU™ NINDE
+#H293
+JBUTŽ SNHWI IENHT EUIE/
+AINEI N2SLN RROTW EPZNL
+MRDAE DOCEK ŽDZN/ TSAS™
+MEUF8 GXSET TSJHY TTKII
+IZ™QN AAVTP 5ENHN
+#H294
+ITWEI E5HCK EVSPI EDIEO
+NEPNU TNŽAŽ WSIAA NBRFX
+HX1AA LNMUE EN=0Y HTDJU
+DNASS GHMMI ™IRZE IIIIQ
+OTAWE BNNSA GYUT3
+#H295
+IFIET WS™SS PRTXN 22SDN
+MLŽIE YSEOE EZAEI WSBMJ
+MU0MV IIQ5N CHZO NNAKM
+ISUOA SSLIA ENHPR UTIAE
+RIEUE ITCGI 4SRTL
+#H296
+EEUIA WSMAK HNEDT E=CRV
+EGXNS RRAAS CSOUS YIEZM
+HLNQ2 TNVIA OT5EF ŽI™DE
+JSNŽN IHBQD TIZ0O TOFET
+IPSIM ARIEA 7IDAT
+#H297
+AWGEU RETNI PITWA JCTBA
+IŽTY NIMDT UXSRN UTDNE
+H™NZN IPEDQ YVJSS ATENX
+HFDFE HAEEN AT5AO MISNK
+E?T6V IZTEL AP24U
+#H298
+SFEMI WOINA MUJAŽ GS20T
+AIXEŽ TQMPH CEMIA AMSII
+2NAEI VISEC KAN7T IAI8Y
+ASRUE LZGRE OVBEM ™TKUI
+KNHRD IXTEE NNLDS
+#H299
+DI9L? TONEH HTAI5 IEQSN
+VEEJM ZLEEI ITARS WCFS2
+NQZHE EXGPC GARUT DYTTS
+TAFHI NBIAU IŽCUA NSADS
+™KEN2 INEC™ THNMA
+#H300
+NIATU IIZDH KAMSN 2UUEX
+M™QOO DAVKF //QNH EITHP
+IXCIA KJTNS FRETE EWEGI
+IBISF TETNN AW?NN UYDEA
+LRLTD NNEAŽ IYS2E
+ø% \ No newline at end of file
diff --git a/tutorials/sndkit/morse/charlist.h b/tutorials/sndkit/morse/charlist.h
new file mode 100644
index 0000000..053aaae
--- /dev/null
+++ b/tutorials/sndkit/morse/charlist.h
@@ -0,0 +1,96 @@
+#ifndef CHARLIST_H
+#define CHARLIST_H
+
+#define INT_LETTERS 1
+
+static const char Chars[] =
+#ifdef INT_LETTERS
+ "abcdefghijklmnopqrstuvwxyz0123456789?.,:åäöü£)/=";
+#else
+ "abcdefghijklmnopqrstuvwxyz0123456789?.,:)/=";
+#endif
+
+static const char *Codes[] =
+ { ".-", "-...", "-.-.", "-..", ".", "..-.", "--.",
+ "....", "..", ".---", "-.-", ".-..", "--", "-.", "---",
+ ".--.", "--.-", ".-.", "...", "-", "..-", "...-",
+ ".--", "-..-", "-.--", "--..", /* A..Z */
+
+ "-----", ".----", "..---", "...--", "....-",
+ ".....", "-....", "--...", "---..", "----.", /* 0..9 */
+
+ "..--..", ".-.-.-", "--..--", "---...", /* ?|.,: */
+
+#ifdef INT_LETTERS
+ ".--.-", ".-.-", "---.", "..--", /* International letters */
+#endif
+ "........", /* Error */
+ "-.--.-", /* () */
+ "-..-.", /* / */
+ "-...-" /* = */
+};
+
+static int
+parse_charlist (char *ch)
+{
+ int prev = 0, i, j;
+
+ if (strcmp (ch, "-1") == 0)
+ return parse_charlist ("aeost");
+ if (strcmp (ch, "-2") == 0)
+ return parse_charlist ("hilnr");
+ if (strcmp (ch, "-3") == 0)
+ return parse_charlist ("cfgu");
+ if (strcmp (ch, "-4") == 0)
+ return parse_charlist ("dkmp");
+ if (strcmp (ch, "-5") == 0)
+ return parse_charlist ("bqvy");
+ if (strcmp (ch, "-6") == 0)
+ return parse_charlist ("jvxz");
+#ifdef INT_LETTERS
+ if (strcmp (ch, "-7") == 0)
+ return parse_charlist ("äöå");
+#endif
+ if (strcmp (ch, "-8") == 0)
+ return parse_charlist ("50146");
+ if (strcmp (ch, "-9") == 0)
+ return parse_charlist ("27389");
+ if (strcmp (ch, "-10") == 0)
+ return parse_charlist ("/=?");
+ if (strcmp (ch, "-11") == 0)
+ return parse_charlist (").,-");
+#ifdef INT_LETTERS
+ if (strcmp (ch, "-a") == 0) /* Finnish CWH module */
+ return parse_charlist ("a-zåäö0-9/=?");
+ if (strcmp (ch, "-d") == 0) /* Some very difficult characters */
+ return parse_charlist ("1j/l4bh569ö");
+#endif
+
+ for (i = 0; i < strlen (ch); i++)
+ if (ch[i] == '-')
+ {
+ prev = -prev;
+ }
+ else
+ {
+ if (prev > 0)
+ {
+ for (j = prev; j <= ch[i]; j++)
+ {
+ randomlist[nrandom++] = j;
+ randomlist[nrandom] = 0;
+ }
+ }
+ else
+ {
+ randomlist[nrandom++] = ch[i];
+ randomlist[nrandom] = 0;
+ }
+
+ prev = -((unsigned char) ch[i] + 1);
+ }
+
+ return 0;
+}
+
+#endif
diff --git a/tutorials/sndkit/morse/loadcw.c b/tutorials/sndkit/morse/loadcw.c
new file mode 100644
index 0000000..d628a44
--- /dev/null
+++ b/tutorials/sndkit/morse/loadcw.c
@@ -0,0 +1,163 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#define TESTFILE "Vvcw"
+
+static int ngroups = 0;
+
+static void
+printchar (unsigned char c)
+{
+ switch (c)
+ {
+ case ' ':
+ ngroups++;
+ if (ngroups && !(ngroups % 6))
+ printf (" \n");
+ else
+ printf (" ");
+ break;
+
+ case 0x99:
+ printf ("ö");
+ break;
+ case 0x8e:
+ printf ("ä");
+ break;
+ case 0x8f:
+ printf ("å");
+ break;
+ default:
+ if (c <= 'Z' && c >= 'A')
+ c = c + 32;
+ printf ("%c", c);
+ }
+}
+
+static int
+showtest (char *tname)
+{
+ FILE *f;
+ unsigned char line[1024], *p;
+ int on = 0;
+
+ if ((f = fopen (TESTFILE, "r")) == NULL)
+ {
+ perror (TESTFILE);
+ return -1;
+ }
+
+ while (fgets (line, sizeof (line), f))
+ {
+ p = &line[strlen (line) - 1];
+ while (p > line)
+ {
+ if (*p == '\n' || *p == '\r')
+ *p = 0;
+ p--;
+ }
+
+ if (*line == '#')
+ {
+ if (on)
+ {
+ fclose (f);
+ printf ("\n");
+ return 0;
+ }
+
+ if (strcmp (line + 1, tname) == 0)
+ on = 1;
+ }
+ else if (on)
+ {
+ p = line;
+
+ while (*p)
+ {
+ switch (*p)
+ {
+ default:
+ printchar (*p);
+ }
+ p++;
+ }
+ }
+
+ }
+ fclose (f);
+
+ return 1;
+}
+
+static char *
+randomtest (void)
+{
+ FILE *f;
+ unsigned char line[1024], *p;
+ static char name[10] = "????";
+ int n = 0, x;
+ char *tests[1000];
+
+ if ((f = fopen (TESTFILE, "r")) == NULL)
+ {
+ perror (TESTFILE);
+ return NULL;
+ }
+
+ while (fgets (line, sizeof (line), f))
+ {
+ if (strlen (line) < 1)
+ continue;
+ p = &line[strlen (line) - 1];
+ while (p > line)
+ {
+ if (*p == '\n' || *p == '\r')
+ *p = 0;
+ p--;
+ }
+ if (strlen (line) < 1)
+ continue;
+
+ if (*line == '#')
+ {
+ char *tmp;
+ tmp = malloc (strlen (line + 1) + 1);
+ strcpy (tmp, line + 1);
+
+ if (n >= 1000)
+ {
+ fprintf (stderr, "Too many test texts\n");
+ exit (-1);
+ }
+
+ tests[n++] = tmp;
+ }
+ }
+ fclose (f);
+
+ srandom (time (0));
+
+ x = random () % n; /* Select one of the tests randomly */
+ strcpy (name, tests[x]);
+
+ return name;
+}
+
+int
+main (int argc, char *argv[])
+{
+ char *thistest;
+
+ if (argc > 1)
+ exit (showtest (argv[1]));
+
+ thistest = randomtest ();
+ if (thistest == NULL)
+ exit (-1);
+ fprintf (stderr, "Selected test text is '%s'\n", thistest);
+ fflush (stderr);
+ exit (showtest (thistest));
+}
diff --git a/tutorials/sndkit/morse/morse.c b/tutorials/sndkit/morse/morse.c
new file mode 100644
index 0000000..6077873
--- /dev/null
+++ b/tutorials/sndkit/morse/morse.c
@@ -0,0 +1,316 @@
+/*
+ * Purpose: Simple audio programming example that plays morse code.
+ * Copyright (C) 4Front Technologies, 2002-2004. Released under GPLv2/CDDL.
+ *
+ * Description:
+ * This program reads stdin and plays the input to an audio device using morse
+ * code. The stdin input is supposed to be originated from a file. This
+ * program is not capable to play live keyboard input.
+ *
+ * This is a great OSS programming example because it shows how simple
+ * audio programming can be with OSS.
+ *
+ * You can use this program as a template. Just replace the
+ * while loop of the main routine by your own code.
+ *
+ * The {!nlink morse2.c} and {!nlink morse3.c} programs are more complex
+ * versions of the same program. They demonstrate how the {!nlink select}
+ * system call can be used for serving the audio device in parallel
+ * while handling terminal input.
+ *
+ * This program was tuned to be used when practising for the
+ * finnish morse code test for radio amateurs. It supports the
+ * scandinavian version of the morse code alphabet used in this test.
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/soundcard.h>
+#include <math.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <sys/types.h>
+
+#define BUFSZ (16*1024)
+#define SRATE 48000
+#define ATTACK 100
+#define CHARDELAY 3
+
+int charspeed = 25;
+
+int dotsize, charsize;
+int audiofd = -1;
+
+char randomlist[256];
+int nrandom;
+
+static int ncodes;
+
+double a, step;
+
+/*
+ * The genpulse() routine converts generates a single dot, dash or
+ * a pause between the symbols. this is done by generating sine wave
+ * (using cos()). It's pretty slow but works for us.
+ */
+#include "charlist.h"
+
+static int
+genpulse (short *buf, int w, int state)
+{
+ int i, l;
+ a = 0.0;
+
+ l = w * dotsize;
+
+ for (i = 0; i < ATTACK; i++)
+ {
+ double tmp = 0x7fff * cos (a * M_PI / 180.0);
+ tmp *= (double) (i) / (double) ATTACK;
+
+ *buf++ = (int) tmp *state;
+
+ a += step;
+ if (a > 360.0)
+ a -= 360.0;
+ }
+
+ for (i = ATTACK; i < l - ATTACK; i++)
+ {
+ double tmp = 0x7fff * cos (a * M_PI / 180.0);
+
+ *buf++ = (int) tmp *state;
+
+ a += step;
+ if (a > 360.0)
+ a -= 360.0;
+ }
+
+ for (i = l - ATTACK; i < l; i++)
+ {
+ double tmp = 0x7fff * cos (a * M_PI / 180.0);
+
+ tmp *= (double) (l - i) / (double) ATTACK;
+
+ *buf++ = (int) tmp *state;
+
+ a += step;
+ if (a > 360.0)
+ a -= 360.0;
+ }
+
+ return l;
+}
+
+/*
+ * The genmorse() routine converts an ASCII character to the
+ * equivivalent audio morse code signal.
+ */
+static int
+genmorse (short *buf, char c)
+{
+ int l = 0, i;
+ const char *s;
+
+ //printf("%c", c);
+ //fflush(stdout);
+
+ if (c == ' ')
+ return genpulse (buf, 4, 0);
+
+ for (i = 0; i < ncodes; i++)
+ if (Chars[i] == c)
+ {
+ s = Codes[i];
+
+ while (*s)
+ {
+ if (*s++ == '.')
+ l += genpulse (&buf[l], 1, 1);
+ else
+ l += genpulse (&buf[l], 3, 1);
+
+ l += genpulse (&buf[l], 1, 0);
+ }
+
+ l += genpulse (&buf[l], CHARDELAY, 0);
+ return l;
+ }
+
+ return 0;
+}
+
+/*
+ * The playchar() routine handles some special characters that are not
+ * included in the international morse aplhabet. Characters are then played by
+ * calling the genmorse() routine.
+ */
+
+static void
+playchar (char c)
+{
+ short buf[16 * BUFSZ];
+ int l;
+
+ l = 0;
+
+ if (c <= 'Z' && c >= 'A')
+ c += 32;
+
+ switch (c)
+ {
+ case '\n':
+ return;
+ break;
+ case ' ':
+ case '\t':
+ l = genmorse (buf, ' ');
+ break;
+
+ case '\r':
+ break;
+
+ case 'Å':
+ l = genmorse (buf, 'å');
+ break;
+ case 'Ä':
+ l = genmorse (buf, 'ä');
+ break;
+ case 'Ö':
+ l = genmorse (buf, 'ö');
+ break;
+ case 'Ü':
+ l = genmorse (buf, 'ü');
+ break;
+
+ default:
+ l = genmorse (buf, c);
+ }
+
+ write (audiofd, buf, 2 * l);
+ if (2 * l < charsize)
+ {
+ char tmp[4 * 1024 * 1024];
+ l = charsize - 2 * l;
+ memset (tmp, 0, l);
+ write (audiofd, tmp, l);
+ }
+}
+
+int
+main (int argc, char *argv[])
+{
+ char *devname = "/dev/dsp";
+ short buf[16 * BUFSZ];
+ char line[1024];
+ int i, parm;
+ int l, speed, charspeed, wpm = 8;
+
+/*
+ * Charcter rate (CPS/WPS) handling.
+ */
+
+ if (argc > 1)
+ {
+ wpm = atoi (argv[1]);
+ if (wpm == 0)
+ wpm = 12;
+ }
+
+ if (argc > 2)
+ charspeed = atoi (argv[2]);
+
+ speed = wpm;
+ charsize = 60 * SRATE * 2 / charspeed;
+
+ printf ("Words per minute %d. Characters per minute %d\n", wpm, wpm * 5);
+ printf ("Charrate %d chars/min -> (%d samples)\n", charspeed, charsize);
+ dotsize = SRATE / speed;
+
+ ncodes = strlen (Chars);
+
+/*
+ * Open the audio device and set up the parameters.
+ */
+
+ if ((audiofd = open (devname, O_WRONLY, 0)) == -1)
+ {
+ perror (devname);
+ exit (-1);
+ }
+
+ parm = AFMT_S16_LE;
+ if (ioctl (audiofd, SNDCTL_DSP_SETFMT, &parm) == -1)
+ {
+ perror ("SETFMT");
+ close (audiofd);
+ exit (-1);
+ }
+
+ if (parm != AFMT_S16_LE)
+ {
+ printf
+ ("Error: 32/24 bit sample format is not supported by the device\n");
+ printf ("%08x/%08x\n", parm, AFMT_S16_LE);
+ close (audiofd);
+ exit (-1);
+ }
+
+ parm = SRATE;
+ if (ioctl (audiofd, SNDCTL_DSP_SPEED, &parm) == -1)
+ {
+ perror ("SPEED");
+ close (audiofd);
+ exit (-1);
+ }
+
+ if (parm != SRATE)
+ {
+ printf
+ ("Error: %d Hz sampling rate is not supported by the device (%d)\n",
+ SRATE, parm);
+ close (audiofd);
+ exit (-1);
+ }
+
+/*
+ * The setup phase is complete. After this moment we can forget that we are
+ * working on a device. The remainder of this program behaves just like
+ * it's writing to any (disk) file.
+ */
+ a = 0.0;
+
+ step = 360.0 * 600.0 / parm;
+
+ l = 0;
+ l += genpulse (&buf[l], 1, 0);
+ write (audiofd, buf, l * 2);
+
+ /* Some initial delay */
+ memset (buf, 0, 4096);
+ for (l = 0; l < 30; l++)
+ write (audiofd, buf, 4096);
+
+ while (fgets (line, sizeof (line), stdin) != NULL)
+ {
+
+ if (*line == '#')
+ continue;
+
+ for (i = 0; i < strlen (line); i++)
+ {
+ playchar (line[i]);
+ }
+ }
+
+ /* Some final delay */
+ memset (buf, 0, 4096);
+ for (l = 0; l < 20; l++)
+ write (audiofd, buf, 4096);
+
+ close (audiofd);
+
+ exit (0);
+}
diff --git a/tutorials/sndkit/morse/morse2.c b/tutorials/sndkit/morse/morse2.c
new file mode 100644
index 0000000..63e5453
--- /dev/null
+++ b/tutorials/sndkit/morse/morse2.c
@@ -0,0 +1,578 @@
+/*
+ * Purpose: Another morse code program that uses select
+ * Copyright (C) 4Front Technologies, 2002-2004. Released under GPLv2/CDDL.
+ *
+ * Description:
+ * This program is a significantly more complicated version of
+ * {!nlink morse.c}. It uses the {!nlink select} system call to be able to
+ * handle keyboard input and audio output at the same time. It shows how
+ * to prevent output underruns by writing silent samples to the audio device
+ * when there is no "payload" signal to play.
+ *
+ * What the program does is playing randomly selected morse symbols
+ * (based on the command line options). It then waits until the
+ * users hits a key. If the input character matches the morse output then
+ * the program continues by playing another morse character. If there was an
+ * error then the next character is repeated. The Esc key is used to stop
+ * the program.
+ *
+ * Parts on this program are common with {!nlink morse.c} and commented only
+ * there.
+ *
+ * The {!nlink morse3.c} program is a slightly different version of this one.
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/soundcard.h>
+#include <math.h>
+#include <termios.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <sys/types.h>
+
+#define BUFSZ (16*1024)
+#define SRATE 48000
+#define ATTACK 100
+#define CHARDELAY 3
+
+char randomlist[65536];
+int nrandom = 0;
+int done = 0;
+int chars_to_play = 100;
+
+int dotsize;
+int audiofd = -1;
+static int ncodes;
+static int playc;
+static int terminal_fd = 0;
+static struct termios ti, saved_ti;
+static int totalchars = 0, totalerrors = 0, errors = 0;
+static time_t t0;
+
+double a, step;
+#include "charlist.h"
+
+static void
+terminate (int sig)
+{
+ time_t t;
+
+ t = time (0) - t0;
+
+ if (terminal_fd == -1)
+ return;
+
+ if (tcsetattr (terminal_fd, TCSAFLUSH, &saved_ti) == -1)
+ {
+ perror ("tcgetattr");
+ exit (-1);
+ }
+
+ printf ("\n\nTotal characters: %d\n", totalchars);
+ printf ("Errors: %d\n", totalerrors);
+ printf ("Elapsed time: %d seconds\n", t);
+ if (errors == 0) totalchars--;
+ if (totalchars <= totalerrors) totalchars = 0;
+ else
+ {
+ totalchars -= totalerrors;
+ totalchars += totalchars / 5;
+ }
+
+ printf ("Characters per minute: %g\n",
+ t > 0?(float) totalchars / (float) t * 60.0:0);
+ exit (sig);
+}
+
+static int
+genpulse (short *buf, int w, int state)
+{
+ int i, l;
+ a = 0.0;
+
+ l = w * dotsize;
+
+ for (i = 0; i < ATTACK; i++)
+ {
+ double tmp = 0x7fff * cos (a * M_PI / 180.0);
+ tmp *= (double) (i) / (double) ATTACK;
+
+ *buf++ = (int) tmp *state;
+
+ a += step;
+ if (a > 360.0)
+ a -= 360.0;
+ }
+
+ for (i = ATTACK; i < l - ATTACK; i++)
+ {
+ double tmp = 0x7fff * cos (a * M_PI / 180.0);
+
+ *buf++ = (int) tmp *state;
+
+ a += step;
+ if (a > 360.0)
+ a -= 360.0;
+ }
+
+ for (i = l - ATTACK; i < l; i++)
+ {
+ double tmp = 0x7fff * cos (a * M_PI / 180.0);
+
+ tmp *= (double) (l - i) / (double) ATTACK;
+
+ *buf++ = (int) tmp *state;
+
+ a += step;
+ if (a > 360.0)
+ a -= 360.0;
+ }
+
+ return l;
+}
+
+static void
+playerror (void)
+{
+ short buffer[65536], *buf = buffer;
+ int i, l;
+ a = 0.0;
+
+ l = dotsize;
+
+ for (i = 0; i < ATTACK; i++)
+ {
+ double tmp = 0x7fff * cos (a * M_PI / 180.0);
+ tmp *= (double) (i) / (double) ATTACK;
+
+ *buf++ = (int) tmp;
+
+ a += step * 2.0;
+ if (a > 360.0)
+ a -= 360.0;
+ }
+
+ for (i = ATTACK; i < l - ATTACK; i++)
+ {
+ double tmp = 0x7fff * cos (a * M_PI / 180.0);
+
+ *buf++ = (int) tmp;
+
+ a += step * 2.0;
+ if (a > 360.0)
+ a -= 360.0;
+ }
+
+ for (i = l - ATTACK; i < l; i++)
+ {
+ double tmp = 0x7fff * cos (a * M_PI / 180.0);
+
+ tmp *= (double) (l - i) / (double) ATTACK;
+
+ *buf++ = (int) tmp;
+
+ a += step * 2.0;
+ if (a > 360.0)
+ a -= 360.0;
+ }
+
+ write (audiofd, buffer, 2 * l);
+}
+
+static int
+genmorse (short *buf, char c)
+{
+ int l = 0, i;
+ const char *s;
+
+ if (c == ' ')
+ return genpulse (buf, 4, 0);
+
+ for (i = 0; i < ncodes; i++)
+ if (Chars[i] == c)
+ {
+ s = Codes[i];
+
+ while (*s)
+ {
+ if (*s++ == '.')
+ l += genpulse (&buf[l], 1, 1);
+ else
+ l += genpulse (&buf[l], 3, 1);
+
+ l += genpulse (&buf[l], 1, 0);
+ }
+
+ l += genpulse (&buf[l], CHARDELAY, 0);
+ return l;
+ }
+
+ printf ("<?>");
+ fflush (stdout);
+ return 0;
+}
+
+static void
+playchar (char c)
+{
+ short buf[16 * BUFSZ];
+ int l;
+
+ l = 0;
+
+ if (c <= 'Z' && c >= 'A')
+ c += 32;
+
+ switch (c)
+ {
+ case ' ':
+ case '\n':
+ case '\t':
+ l = genmorse (buf, ' ');
+ break;
+
+ case '\r':
+ break;
+
+ case 'Å':
+ l = genmorse (buf, 'å');
+ break;
+ case 'Ä':
+ l = genmorse (buf, 'ä');
+ break;
+ case 'Ö':
+ l = genmorse (buf, 'ö');
+ break;
+ case 'Ü':
+ l = genmorse (buf, 'ü');
+ break;
+
+ default:
+ l = genmorse (buf, c);
+ }
+
+/*
+ * Next write whatever we have in the buffer. Note that this write will
+ * block but that time is at most the time required to play one morse
+ * code character. There is no need to avoid blocking because it doesn't cause
+ * any annoying delays.
+ *
+ * This is often the case with audio application. While many programmers think
+ * blocking is evil it's actually programmer's best friend.
+ */
+ if (write (audiofd, buf, 2 * l) != 2 * l)
+ {
+ perror ("write audio");
+ terminate (15);
+ }
+}
+
+/*
+ * The randomplay() routine picks a character from the list of characters
+ * selected for practice.
+ */
+static void
+randomplay (void)
+{
+ int old = playc;
+ if (totalchars == chars_to_play)
+ {
+ done = 1;
+ return;
+ }
+
+// while (playc==old)
+ {
+ int i, x, tmp;
+
+ x = random () % nrandom;
+ playc = randomlist[x];
+#if 1
+ if (x != nrandom - 1)
+ {
+ tmp = randomlist[x];
+ for (i = x; i < nrandom - 1; i++)
+ randomlist[i] = randomlist[i + 1];
+ randomlist[nrandom - 1] = tmp;
+ }
+#endif
+ }
+
+ playchar (playc);
+ totalchars++;
+}
+
+static int
+findcode (char c)
+{
+ int i;
+
+ for (i = 0; i < ncodes; i++)
+ if (Chars[i] == c)
+ return i;
+
+ return 0;
+}
+
+int
+main (int argc, char *argv[])
+{
+ char *devname = "/dev/dsp";
+ short buf[32 * BUFSZ];
+ char line[1024];
+ int i, parm;
+ int l, speed, wpm = 12;
+
+ fd_set readfds, writefds;
+
+ if (argc > 1)
+ devname = argv[1];
+
+ if (argc > 2)
+ {
+ wpm = atoi (argv[2]);
+ if (wpm == 0)
+ wpm = 12;
+ }
+
+ ncodes = strlen (Chars);
+
+ srandom (time (0));
+
+ if (argc > 3)
+ {
+ for (i = 3; i < argc; i++)
+ parse_charlist (argv[i]);
+ }
+ else
+ {
+ strcpy (randomlist, Chars);
+ nrandom = strlen (randomlist);
+ }
+
+ if (nrandom < 2)
+ {
+ printf ("Bad character list\n");
+ exit (-1);
+ }
+
+ randomlist[nrandom] = 0;
+
+ printf ("Practicing codes: %s\n", randomlist);
+ for (i = 0; i <= nrandom; i += 4)
+ {
+ int j, k;
+ char line[256], tmp[20];
+ memset (line, ' ', 80), line[78] = 0;
+
+ for (j = 0; j < 4; j++)
+ if (i + j <= nrandom)
+ {
+ int ix;
+
+ ix = findcode (randomlist[i + j]);
+
+ sprintf (tmp, "%c %s", randomlist[i + j], Codes[ix]);
+ for (k = 0; k < strlen (tmp); k++)
+ line[j * 20 + k] = tmp[k];
+ }
+
+ printf ("%s\n", line);
+ }
+
+ speed = wpm;
+
+ printf ("Words per minute %d. Characters per minute %d\n", wpm, wpm * 5);
+
+ dotsize = SRATE / speed;
+
+ if ((audiofd = open (devname, O_WRONLY, 0)) == -1)
+ {
+ perror (devname);
+ exit (-1);
+ }
+
+ parm = 0x0003000a;
+ ioctl (audiofd, SNDCTL_DSP_SETFRAGMENT, &parm);
+
+ parm = AFMT_S16_LE;
+ if (ioctl (audiofd, SNDCTL_DSP_SETFMT, &parm) == -1)
+ {
+ perror ("SETFMT");
+ close (audiofd);
+ exit (-1);
+ }
+
+ if (parm != AFMT_S16_LE)
+ {
+ printf
+ ("Error: 32/24 bit sample format is not supported by the device\n");
+ printf ("%08x/%08x\n", parm, AFMT_S16_LE);
+ close (audiofd);
+ exit (-1);
+ }
+
+ parm = SRATE;
+ if (ioctl (audiofd, SNDCTL_DSP_SPEED, &parm) == -1)
+ {
+ perror ("SPEED");
+ close (audiofd);
+ exit (-1);
+ }
+
+ if (parm != SRATE)
+ {
+ printf
+ ("Error: %d Hz sampling rate is not supported by the device (%d)\n",
+ SRATE, parm);
+ close (audiofd);
+ exit (-1);
+ }
+
+ if (tcgetattr (terminal_fd, &saved_ti) == -1)
+ {
+ perror ("tcgetattr");
+ exit (-1);
+ }
+
+ signal (SIGINT, terminate);
+/*
+ * Set up the terminal (stdin) for single character input.
+ */
+
+ if (tcgetattr (terminal_fd, &ti) == -1)
+ {
+ perror ("tcgetattr");
+ exit (-1);
+ }
+
+
+ ti.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
+ ti.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
+ ti.c_cflag &= ~(CSIZE | PARENB);
+ ti.c_cflag |= CS8;
+ ti.c_oflag &= ~(OPOST);
+
+ ti.c_cc[VMIN] = 1;
+ ti.c_cc[VTIME] = 1;
+
+ if (tcsetattr (terminal_fd, TCSAFLUSH, &ti) == -1)
+ {
+ perror ("tcgetattr");
+ exit (-1);
+ }
+
+ a = 0.0;
+
+/*
+ * Play some "extra" audio data to delay the startup. This just gives
+ * some extra time to the user to prepare before we start.
+ */
+
+ step = 360.0 * 600.0 / parm;
+
+ l = 0;
+ l += genpulse (&buf[l], 1, 0);
+ write (audiofd, buf, l * 2);
+ memset (buf, 0, 4096);
+ for (i = 0; i < 2; i++)
+ write (audiofd, buf, 4096);
+
+/*
+ * The actual playback starts here
+ */
+
+ randomplay ();
+
+ t0 = time (0);
+ while (!done)
+ {
+ int n;
+
+/*
+ * Set up select for output events on the audio device and input events on
+ * the keyboard.
+ */
+ FD_ZERO (&readfds);
+ FD_ZERO (&writefds);
+
+ FD_SET (audiofd, &writefds);
+ FD_SET (0, &readfds);
+
+/*
+ * Call select with no timeouts
+ */
+ if ((n = select (audiofd + 1, &readfds, &writefds, NULL, NULL)) == -1)
+ {
+ perror ("select");
+ exit (-1);
+ }
+
+ if (n == 0)
+ continue;
+
+ if (FD_ISSET (0, &readfds)) /* 0 means stdin */
+ {
+/*
+ * Handling of keyboard input. Check if the answer was right.
+ */
+ if (read (0, line, 1) == 1)
+ {
+ if (*line == 27) /* ESC */
+ terminate (SIGINT);
+ if ((unsigned char) *line != (unsigned char) playc)
+ {
+ int x;
+
+ totalerrors++;
+ chars_to_play += 4;
+ randomlist[nrandom++] = playc;
+ randomlist[nrandom++] = playc;
+ for (x = 0; x < nrandom; x++)
+ if (randomlist[x] == *line)
+ {
+ randomlist[nrandom++] = *line;
+ break;
+ }
+ playerror ();
+ if (++errors > 3)
+ {
+ printf ("It is '%c' not '%c'\r\n", playc, *line);
+ fflush (stdout);
+ }
+ playchar (playc);
+ }
+ else
+ {
+ errors = 0;
+ randomplay ();
+ }
+ }
+ }
+
+ if (FD_ISSET (audiofd, &writefds))
+ {
+/*
+ * The audio device is ready to accept more data. Keep the device happy by
+ * writing some silent samples to it.
+ *
+ * Note that the real "playload" signal will be played by the playchar()
+ * routine.
+ */
+ memset (buf, 0, 1024);
+ write (audiofd, buf, 1024);
+ }
+ }
+
+/*
+ * Everything done. Restore the teminal settings and exit.
+ */
+ terminate (15);
+
+ close (audiofd);
+
+ exit (0);
+}
diff --git a/tutorials/sndkit/morse/morse3.c b/tutorials/sndkit/morse/morse3.c
new file mode 100644
index 0000000..22eb9c2
--- /dev/null
+++ b/tutorials/sndkit/morse/morse3.c
@@ -0,0 +1,572 @@
+/*
+ * Purpose: Yet another morse code program that uses select
+ * Copyright (C) 4Front Technologies, 2002-2004. Released under GPLv2/CDDL.
+ *
+ * Description:
+ * This program is a significantly more complicated version of
+ * {!nlink morse.c}. It uses the {!nlink select} system call to be able to
+ * handle keyboard input and audio output at the same time. It shows how
+ * to prevent output underruns by writing silent samples to the audio device
+ * when there is no "payload" signal to play.
+ *
+ * What the program does is playing randomly selected morse symbols
+ * (based on the command line options). At the same time it reads the keyboard
+ * and checks if the user typed the right character. This program
+ * doesn't stop to wait for the input. This simulates the real morse code
+ * exam but ufortunately this dosesn't work in practice.
+ *
+ * Parts on this program are common with {!nlink morse.c} and commented only
+ * there.
+ *
+ * The {!nlink morse2.c} program is a slightly different version of this one.
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <math.h>
+#include <termios.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <soundcard.h>
+
+#define BUFSZ (16*1024)
+#define SRATE 48000
+#define ATTACK 100
+#define CHARDELAY 3
+
+char randomlist[256];
+int nrandom = 0;
+int done = 0;
+int charspeed, charsize;
+
+int dotsize;
+int audiofd = -1;
+static int ncodes;
+static int playc;
+static int terminal_fd = 0;
+static struct termios ti, saved_ti;
+static int totalchars = 0;
+static time_t t0;
+
+double a, step;
+#include "charlist.h"
+
+int nplayed = 0, ntyped = 0;
+char played_chars[4096], typed_chars[4096];
+
+static void
+check_results (void)
+{
+ int i, j, n;
+ int errors = 0;
+
+ n = nplayed;
+
+ for (j = 0; j < n; j += 50)
+ {
+ printf ("Answer: ");
+ for (i = j; i < n && i < j + 50; i++)
+ {
+ if (i != j && !(i % 5))
+ printf (" ");
+ printf ("%c", typed_chars[i]);
+ }
+ printf ("\n");
+
+ printf ("Correct:");
+ for (i = j; i < n && i < j + 50; i++)
+ {
+ if (i != j && !(i % 5))
+ printf (" ");
+ if (typed_chars[i] != played_chars[i])
+ {
+ printf ("%c", played_chars[i]);
+ errors++;
+ }
+ else
+ printf (" ");
+ }
+ printf ("\n");
+ }
+ printf ("\n");
+
+ printf ("Errors: %d\n", errors);
+}
+
+static void
+terminate (int sig)
+{
+ time_t t;
+
+ t = time (0) - t0;
+
+ if (terminal_fd == -1)
+ return;
+
+ if (tcsetattr (terminal_fd, TCSAFLUSH, &saved_ti) == -1)
+ {
+ perror ("tcgetattr");
+ exit (-1);
+ }
+
+ printf ("\n\nTotal characters: %d\n", totalchars);
+ printf ("Elapsed time: %d seconds\n", t);
+
+ printf ("Characters per minute: %g\n",
+ t > 0?(float) totalchars / (float) t * 60.0:0);
+ printf ("\n");
+ check_results ();
+ exit (sig);
+}
+
+static int
+genpulse (short *buf, int w, int state)
+{
+ int i, l;
+ a = 0.0;
+
+ l = w * dotsize;
+
+ for (i = 0; i < ATTACK; i++)
+ {
+ double tmp = 0x7fff * cos (a * M_PI / 180.0);
+ tmp *= (double) (i) / (double) ATTACK;
+
+ *buf++ = (int) tmp *state;
+
+ a += step;
+ if (a > 360.0)
+ a -= 360.0;
+ }
+
+ for (i = ATTACK; i < l - ATTACK; i++)
+ {
+ double tmp = 0x7fff * cos (a * M_PI / 180.0);
+
+ *buf++ = (int) tmp *state;
+
+ a += step;
+ if (a > 360.0)
+ a -= 360.0;
+ }
+
+ for (i = l - ATTACK; i < l; i++)
+ {
+ double tmp = 0x7fff * cos (a * M_PI / 180.0);
+
+ tmp *= (double) (l - i) / (double) ATTACK;
+
+ *buf++ = (int) tmp *state;
+
+ a += step;
+ if (a > 360.0)
+ a -= 360.0;
+ }
+
+ return l;
+}
+
+static void
+playerror (void)
+{
+ short buffer[65536], *buf = buffer;
+ int i, l;
+ a = 0.0;
+
+ l = dotsize;
+
+ for (i = 0; i < ATTACK; i++)
+ {
+ double tmp = 0x7fff * cos (a * M_PI / 180.0);
+ tmp *= (double) (i) / (double) ATTACK;
+
+ *buf++ = (int) tmp;
+
+ a += step * 2.0;
+ if (a > 360.0)
+ a -= 360.0;
+ }
+
+ for (i = ATTACK; i < l - ATTACK; i++)
+ {
+ double tmp = 0x7fff * cos (a * M_PI / 180.0);
+
+ *buf++ = (int) tmp;
+
+ a += step * 2.0;
+ if (a > 360.0)
+ a -= 360.0;
+ }
+
+ for (i = l - ATTACK; i < l; i++)
+ {
+ double tmp = 0x7fff * cos (a * M_PI / 180.0);
+
+ tmp *= (double) (l - i) / (double) ATTACK;
+
+ *buf++ = (int) tmp;
+
+ a += step * 2.0;
+ if (a > 360.0)
+ a -= 360.0;
+ }
+
+ write (audiofd, buffer, 2 * l);
+}
+
+static int
+genmorse (short *buf, char c)
+{
+ int l = 0, i;
+ const char *s;
+
+ if (c == ' ')
+ return genpulse (buf, 4, 0);
+
+ for (i = 0; i < ncodes; i++)
+ if (Chars[i] == c)
+ {
+ s = Codes[i];
+
+ while (*s)
+ {
+ if (*s++ == '.')
+ l += genpulse (&buf[l], 1, 1);
+ else
+ l += genpulse (&buf[l], 3, 1);
+
+ l += genpulse (&buf[l], 1, 0);
+ }
+
+ l += genpulse (&buf[l], CHARDELAY, 0);
+ return l;
+ }
+
+ printf ("<?>");
+ fflush (stdout);
+ return 0;
+}
+
+static void
+playchar (char c)
+{
+ short buf[16 * BUFSZ];
+ int l;
+
+ l = 0;
+
+ if (c <= 'Z' && c >= 'A')
+ c += 32;
+
+ switch (c)
+ {
+ case ' ':
+ case '\n':
+ case '\t':
+ l = genmorse (buf, ' ');
+ break;
+
+ case '\r':
+ break;
+
+ case 'Å':
+ l = genmorse (buf, 'å');
+ break;
+ case 'Ä':
+ l = genmorse (buf, 'ä');
+ break;
+ case 'Ö':
+ l = genmorse (buf, 'ö');
+ break;
+ case 'Ü':
+ l = genmorse (buf, 'ü');
+ break;
+
+ default:
+ l = genmorse (buf, c);
+ }
+
+ write (audiofd, buf, 2 * l);
+ if (2 * l < charsize)
+ {
+ char tmp[512 * 1024];
+ l = charsize - 2 * l;
+ memset (tmp, 0, l);
+ write (audiofd, tmp, l);
+ }
+}
+
+static void
+randomplay (void)
+{
+ int old = playc;
+ if (totalchars == 100)
+ {
+ return;
+ }
+
+// while (playc==old)
+ {
+ playc = random () % nrandom;
+ playc = randomlist[playc];
+ }
+
+ playchar (playc);
+ played_chars[nplayed++] = playc;
+ totalchars++;
+}
+
+static int
+findcode (char c)
+{
+ int i;
+
+ for (i = 0; i < ncodes; i++)
+ if (Chars[i] == c)
+ return i;
+
+ return 0;
+}
+
+static void
+editor (char c)
+{
+ int i;
+
+ if (c == 27) /* ESC */
+ terminate (SIGINT);
+
+ if (c == 8) /* BS */
+ {
+ if (ntyped > 0)
+ ntyped--;
+ return;
+ }
+
+ if (c == ' ') /* Sync */
+ {
+ for (i = ntyped; i < nplayed; i++)
+ typed_chars[i] = '_'; /* Empty placeholder */
+ ntyped = nplayed;
+ ioctl (audiofd, SNDCTL_DSP_HALT, 0);
+ return;
+ }
+ typed_chars[ntyped++] = c;
+
+ if (ntyped >= 100)
+ done = 1;
+}
+
+int
+main (int argc, char *argv[])
+{
+ char *devname = "/dev/dsp";
+ short buf[16 * BUFSZ];
+ char line[1024];
+ int i, parm;
+ int l, speed, wpm = 12;
+
+ fd_set readfds, writefds;
+
+ if (argc > 1)
+ devname = argv[1];
+
+ if (argc > 2)
+ {
+ wpm = atoi (argv[2]);
+ if (wpm == 0)
+ wpm = 12;
+ }
+
+ ncodes = strlen (Chars);
+
+ srandom (time (0));
+
+ if (argc > 3)
+ {
+ for (i = 3; i < argc; i++)
+ parse_charlist (argv[i]);
+ }
+ else
+ {
+ strcpy (randomlist, Chars);
+ nrandom = strlen (randomlist);
+ }
+
+ if (nrandom < 2)
+ {
+ printf ("Bad character list\n");
+ exit (-1);
+ }
+
+ randomlist[nrandom] = 0;
+
+ memset (typed_chars, ' ', sizeof (typed_chars));
+
+ printf ("Practicing codes: %s\n", randomlist);
+ for (i = 0; i <= nrandom; i += 4)
+ {
+ int j, k;
+ char line[256], tmp[20];
+ memset (line, ' ', 80), line[78] = 0;
+
+ for (j = 0; j < 4; j++)
+ if (i + j <= nrandom)
+ {
+ int ix;
+
+ ix = findcode (randomlist[i + j]);
+
+ sprintf (tmp, "%c %s", randomlist[i + j], Codes[ix]);
+ for (k = 0; k < strlen (tmp); k++)
+ line[j * 20 + k] = tmp[k];
+ }
+
+ printf ("%s\n", line);
+ }
+
+ speed = wpm;
+
+ printf ("Words per minute %d. Characters per minute %d\n", wpm, wpm * 5);
+
+ dotsize = SRATE / speed;
+
+ if ((audiofd = open (devname, O_WRONLY, 0)) == -1)
+ {
+ perror (devname);
+ exit (-1);
+ }
+
+ parm = 0x0004000a;
+ ioctl (audiofd, SNDCTL_DSP_SETFRAGMENT, &parm);
+
+ parm = AFMT_S16_LE;
+ if (ioctl (audiofd, SNDCTL_DSP_SETFMT, &parm) == -1)
+ {
+ perror ("SETFMT");
+ close (audiofd);
+ exit (-1);
+ }
+
+ if (parm != AFMT_S16_LE)
+ {
+ printf
+ ("Error: 32/24 bit sample format is not supported by the device\n");
+ printf ("%08x/%08x\n", parm, AFMT_S16_LE);
+ close (audiofd);
+ exit (-1);
+ }
+
+ parm = SRATE;
+ if (ioctl (audiofd, SNDCTL_DSP_SPEED, &parm) == -1)
+ {
+ perror ("SPEED");
+ close (audiofd);
+ exit (-1);
+ }
+
+ if (parm != SRATE)
+ {
+ printf
+ ("Error: %d Hz sampling rate is not supported by the device (%d)\n",
+ SRATE, parm);
+ close (audiofd);
+ exit (-1);
+ }
+
+ if (tcgetattr (terminal_fd, &saved_ti) == -1)
+ {
+ perror ("tcgetattr");
+ exit (-1);
+ }
+
+ signal (SIGINT, terminate);
+/*
+ * Line setup
+ */
+
+ if (tcgetattr (terminal_fd, &ti) == -1)
+ {
+ perror ("tcgetattr");
+ exit (-1);
+ }
+
+
+ ti.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
+ ti.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
+ ti.c_cflag &= ~(CSIZE | PARENB);
+ ti.c_cflag |= CS8;
+ ti.c_oflag &= ~(OPOST);
+
+ ti.c_cc[VMIN] = 1;
+ ti.c_cc[VTIME] = 1;
+
+ if (tcsetattr (terminal_fd, TCSAFLUSH, &ti) == -1)
+ {
+ perror ("tcgetattr");
+ exit (-1);
+ }
+
+ a = 0.0;
+
+ step = 360.0 * 600.0 / parm;
+
+ l = 0;
+ l += genpulse (&buf[l], 1, 0);
+ write (audiofd, buf, l * 2);
+ memset (buf, 0, 4096);
+ for (i = 0; i < 2; i++)
+ write (audiofd, buf, 4096);
+
+ charspeed = speed * 5;
+ if (charspeed > 25)
+ charspeed = 25;
+ charsize = 60 * SRATE * 2 / charspeed;
+ printf ("Charrate %d chars/min -> (%d samples)\n", charspeed, charsize);
+
+ printf ("\r\n");
+ randomplay ();
+
+ t0 = time (0);
+ while (!done)
+ {
+ int n;
+
+ FD_ZERO (&readfds);
+ FD_ZERO (&writefds);
+
+ FD_SET (audiofd, &writefds);
+ FD_SET (0, &readfds);
+
+ if ((n = select (audiofd + 1, &readfds, &writefds, NULL, NULL)) == -1)
+ {
+ perror ("select");
+ exit (-1);
+ }
+
+ if (n == 0)
+ continue;
+
+ if (FD_ISSET (0, &readfds))
+ {
+ if (read (0, line, 1) == 1)
+ editor (*line);
+ }
+
+ if (FD_ISSET (audiofd, &writefds))
+ {
+ if (!(nplayed % 5))
+ playchar (' ');
+ randomplay ();
+ }
+ }
+
+ terminate (15);
+
+ close (audiofd);
+
+ exit (0);
+}
diff --git a/tutorials/sndkit/morse/randomcw b/tutorials/sndkit/morse/randomcw
new file mode 100644
index 0000000..ed36b75
--- /dev/null
+++ b/tutorials/sndkit/morse/randomcw
@@ -0,0 +1,2 @@
+#!/bin/sh
+loadcw|tee x|morse $*;tr 'a-zåäö' 'A-ZÅÄÖ' < x
diff --git a/tutorials/sndkit/morse/testgen.c b/tutorials/sndkit/morse/testgen.c
new file mode 100644
index 0000000..b74df4b
--- /dev/null
+++ b/tutorials/sndkit/morse/testgen.c
@@ -0,0 +1,65 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <math.h>
+
+int ncodes;
+
+char randomlist[256];
+int nrandom = 0;
+
+#include "charlist.h"
+
+int
+main (int argc, char *argv[])
+{
+ int ngroups = 0, maxgroups = 20;
+ ncodes = strlen (Chars);
+
+ srandom (time (0));
+
+ if (argc > 1)
+ {
+ parse_charlist (argv[1]);
+ }
+ else
+ {
+ strcpy (randomlist, Chars);
+ nrandom = strlen (randomlist);
+ }
+
+ if (argc > 2)
+ maxgroups = atoi (argv[2]);
+
+ if (nrandom < 2)
+ {
+ printf ("Bad character list\n");
+ exit (-1);
+ }
+
+ while (1)
+ {
+ int i, c;
+
+ if (ngroups && !(ngroups % 5))
+ printf ("\n");
+
+ if (ngroups++ >= maxgroups)
+ {
+ printf ("\n");
+ exit (0);
+ }
+
+
+ for (i = 0; i < 5; i++)
+ {
+ c = random () % nrandom;
+
+ printf ("%c", randomlist[c]);
+ }
+
+ printf (" ");
+ fflush (stdout);
+ }
+}
diff --git a/tutorials/sndkit/ossmplay/Makefile b/tutorials/sndkit/ossmplay/Makefile
new file mode 100644
index 0000000..375df17
--- /dev/null
+++ b/tutorials/sndkit/ossmplay/Makefile
@@ -0,0 +1,31 @@
+CC = cc
+CFLAGS = -g
+LIBOBJS = mlib.o
+INSTALLDIR=/usr/local/bin
+INSTALLLIB=/usr/lib
+.PREFIXES: .o .c
+
+# Uncomment the following lines if you want to use OSSlib.
+# Look at the OSSlib subdirectory for more info about OSSlib.
+# NOTE! OSS version 3.8-beta2 or later is required for OSSlib.
+OSSDEFINES=-DOSSLIB
+OSSLIBS=-lOSSlib
+
+all : ossmplay
+
+install: ossmplay
+ cp ossmplay $(INSTALLDIR)/ossmplay
+
+ossmplay : main.o $(LIBOBJS)
+ $(CC) $(CFLAGS) -o ossmplay main.o $(LIBOBJS) -L$(INSTALLLIB) $(OSSLIBS)
+
+main.o: mlib.h main.c
+ $(CC) $(CFLAGS) $(OSSDEFINES) -c main.c
+
+mlib.o: mlib.h
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
+clean:
+ rm -f *.o ossmplay core
diff --git a/tutorials/sndkit/ossmplay/mlib.c b/tutorials/sndkit/ossmplay/mlib.c
new file mode 100644
index 0000000..517d39f
--- /dev/null
+++ b/tutorials/sndkit/ossmplay/mlib.c
@@ -0,0 +1,903 @@
+#define DEB(stmt) /* stmt */ /* For debug printing */
+#define DEB2(stmt) /* stmt */ /* For debug printing */
+#define DEB3(stmt) /* stmt */ /* For debug printing */
+#define DEB_ALWAYS(stmt) /* stmt */ /* Interesting things */
+
+#define _INCLUDE_POSIX_SOURCE 1
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/errno.h>
+
+extern int errno;
+
+#define USE_SIMPLE_MACROS
+#include "../../../include/soundcard.h"
+
+#include "mlib.h"
+
+#define _seqbuf eventptr
+#define _seqbufptr 4
+#define _SEQ_ADVBUF(len) *optr = *optr + 1;
+
+#define STORE(cmd) \
+ if (track->events) \
+ { \
+ unsigned char *eventptr = &(track->events[*optr*12]); \
+ *(int *)&eventptr[0] = track->current_time; \
+ cmd; \
+ } \
+ else \
+ { \
+ *optr = *optr + 1; \
+ }
+
+#define META_EVENT(type, buf, len) \
+ { \
+ eventptr[_seqbufptr + 0] = EV_PRIVATE_META; \
+ eventptr[_seqbufptr + 1] = (type); \
+ *(short*)&eventptr[_seqbufptr + 2] = (short)len; \
+ *(char**)&eventptr[_seqbufptr + 4] = malloc(len); \
+ memcpy((char**)&eventptr[_seqbufptr + 4], &(buf), len); \
+ *optr = *optr + 1; \
+ }
+
+#define MAX_EVENT_SIZE 4095
+
+static int midi_msglen[8] = {
+ 2, /* 8x = Note off */
+ 2, /* 9x = Note on */
+ 2, /* Ax = Polyphonic key pressure (After-touch) */
+ 2, /* Bx = Control change */
+ 1, /* Cx = Program change */
+ 1, /* Dx = Channel pressure (After-touch) */
+ 2, /* Ex = Pitch wheel change */
+ 0 /* Fx = system messages */
+};
+
+static char mlib_errstring[256] = "No errors so far\n";
+
+static char *
+mlib_seterr (char *msg)
+{
+ strncpy (mlib_errstring, msg, sizeof (mlib_errstring));
+ return NULL;
+}
+
+static char *
+mlib_syserr (char *name)
+{
+ sprintf (mlib_errstring, "%s: %s", name, strerror (errno));
+ return NULL;
+}
+
+int
+mlib_chkdesc (mlib_desc * desc)
+{
+ if (desc == NULL)
+ {
+ mlib_seterr ("NULL mlib descriptor specified");
+ return 0;
+ }
+
+ if (desc->magic != 0x121234)
+ {
+ mlib_seterr ("Invalid MAGIC in the mlib descriptor");
+ return 0;
+ }
+
+
+ if (desc->fd < 0)
+ {
+ mlib_seterr ("Invalid fd in the mlib descriptor");
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+mlib_loadbuf (mlib_desc * desc)
+{
+ desc->bufp = 0;
+
+ if ((desc->bufcnt = read (desc->fd, desc->buf, sizeof (desc->buf))) == -1)
+ {
+ mlib_syserr (desc->path);
+ return 0;
+ }
+
+ if (desc->bufcnt == 0)
+ {
+ mlib_seterr ("End of MIDI file");
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+mlib_seek (mlib_desc * desc, long pos)
+{
+
+ if (lseek (desc->fd, pos, 0) == -1)
+ {
+ mlib_syserr (desc->path);
+ return 0;
+ }
+
+ desc->bufcnt = 0;
+ desc->bufp = 1;
+
+ return 1;
+}
+
+static int
+mlib_rdstr (mlib_desc * desc, char *target, int nchars)
+{
+ int i;
+
+ if (!mlib_chkdesc (desc))
+ return 0;
+
+ for (i = 0; i < nchars; i++)
+ {
+ if (desc->bufp >= desc->bufcnt)
+ if (!mlib_loadbuf (desc))
+ return 0;
+
+ target[i] = desc->buf[desc->bufp];
+ desc->bufp++;
+ }
+
+ target[nchars] = 0; /* End of string */
+
+ return nchars;
+}
+
+static int
+mlib_rdvarlen (mlib_desc * desc, int *target)
+{
+ int tmp, i;
+
+ *target = 0;
+
+ for (i = 0; i < 6; i++) /* Read at most 6 bytes */
+ {
+ if (desc->bufp >= desc->bufcnt)
+ if (!mlib_loadbuf (desc))
+ return 0;
+
+ tmp = desc->buf[desc->bufp];
+ desc->bufp++;
+
+ *target <<= 7;
+ *target |= tmp & 0x7f; /* Extract 7 bits */
+ if ((tmp & 0x80) == 0)
+ return i + 1; /* Last byte */
+ }
+
+ mlib_seterr ("Unterminated variable length value");
+
+ return 0;
+}
+
+static int
+mlib_rdint16 (mlib_desc * desc, int *target)
+{
+ int tmp, i;
+
+ *target = 0;
+
+ for (i = 0; i < 2; i++)
+ {
+ if (desc->bufp >= desc->bufcnt)
+ if (!mlib_loadbuf (desc))
+ return 0;
+
+ tmp = desc->buf[desc->bufp];
+ desc->bufp++;
+
+ *target <<= 8;
+ *target |= tmp & 0xff;
+ }
+
+ return 2;
+}
+
+static int
+mlib_rdbyte (mlib_desc * desc, unsigned char *target)
+{
+ if (desc->bufp >= desc->bufcnt)
+ if (!mlib_loadbuf (desc))
+ return 0;
+
+ *target = (char) (desc->buf[desc->bufp]);
+ desc->bufp++;
+ return 1;
+}
+
+static int
+mlib_rdint32 (mlib_desc * desc, int *target)
+{
+ int tmp, i;
+
+ *target = 0;
+
+ for (i = 0; i < 4; i++)
+ {
+ if (desc->bufp >= desc->bufcnt)
+ if (!mlib_loadbuf (desc))
+ return 0;
+
+ tmp = desc->buf[desc->bufp];
+ desc->bufp++;
+
+ *target <<= 8;
+ *target += tmp & 0xff;
+ }
+
+ return 4;
+}
+
+static int
+mlib_read_mthd (mlib_desc * desc)
+{
+ char sig[5];
+ unsigned char hi, lo;
+ int len;
+
+ if (!mlib_chkdesc (desc))
+ return 0;
+
+ if (!mlib_rdstr (desc, sig, 4))
+ return 0;
+
+ if (strcmp (sig, "MThd"))
+ {
+ mlib_seterr ("Invalid header signature (!= MThd)");
+ return 0;
+ }
+
+ if (mlib_rdint32 (desc, &len))
+
+ if (mlib_rdint16 (desc, &desc->hdr.MThd_fmt))
+ DEB2 (printf ("Header: Format %d\n", desc->hdr.MThd_fmt));
+
+ if (mlib_rdint16 (desc, &desc->hdr.MThd_ntrk))
+ DEB2 (printf ("Header: ntrks %d\n", desc->hdr.MThd_ntrk));
+
+ if (mlib_rdbyte (desc, &hi))
+ if (mlib_rdbyte (desc, &lo))
+
+ if (hi & 0x80) /* Negative */
+ {
+ DEB2 (printf ("SMPTE timing: format = %d, resolution = %d\n",
+ (char) hi, lo));
+ desc->hdr.time_mode = TIME_SMPTE;
+ desc->hdr.SMPTE_format = (char) hi;
+ desc->hdr.SMPTE_resolution = lo;
+ }
+ else
+ {
+ desc->hdr.time_mode = TIME_MIDI;
+ desc->hdr.division = (hi << 8) + lo;
+ DEB2 (printf ("Midi timing: timebase = %d ppqn\n",
+ desc->hdr.division));
+ }
+
+ desc->curr_trk = -1;
+ desc->trk_offs = 0;
+ desc->next_trk_offs = 4 + 4 + len;;
+
+ return mlib_seek (desc, desc->trk_offs); /* Point to the first track */
+}
+
+static int
+mlib_store_chn_voice_msg (mlib_desc * desc, mlib_track * track, int *optr,
+ unsigned char *evbuf, int len)
+{
+
+ unsigned char chn, pgm, note, vel;
+
+ chn = evbuf[0] & 0x0f; /* Midi channel */
+
+ if (track->init_chn == -1)
+ track->init_chn = chn;
+ else if (chn != track->init_chn)
+ track->flags |= TRK_MULTICHN;
+
+ track->chnmask |= (1 << chn); /* Update channel mask */
+
+ switch (evbuf[0] & 0xf0)
+ {
+ case 0x90: /* Note on */
+ vel = evbuf[2];
+ note = evbuf[1];
+
+ if (vel != 0)
+ { /* Velocity given -> true note on event */
+ track->flags |= TRK_NOTES;
+
+ if (note > track->max_note)
+ track->max_note = note;
+
+ if (note < track->min_note)
+ track->min_note = note;
+
+ if (track->noteon_time == -1)
+ track->noteon_time = track->current_time;
+
+ if (vel != 64)
+ track->flags |= TRK_VEL_NOTEON;
+
+ STORE (SEQ_START_NOTE (0, chn, note, vel));
+ if (chn == 9) /* Drum channel */
+ track->drum_map[note] = note;
+ break; /* NOTE! The break is here, not later */
+ }
+ /* Note on with zero G -> note off with vel=64. */
+ /* Fall to the next case */
+ evbuf[2] = 64; /* Velocity for the note off handler */
+
+ case 0x80: /* Note off */
+ vel = evbuf[2];
+ note = evbuf[1];
+
+ if (vel != 64)
+ track->flags |= TRK_VEL_NOTEOFF;
+ STORE (SEQ_STOP_NOTE (0, chn, note, vel));
+ break;
+
+ case 0xA0: /* Polyphonic key pressure/Aftertouch */
+ track->flags |= TRK_POLY_AFTERTOUCH | TRK_AFTERTOUCH;
+ STORE (SEQ_KEY_PRESSURE (0, chn, note, vel));
+ break;
+
+ case 0xB0: /* Control change */
+ {
+ unsigned short value = evbuf[2]; /* Incorrect */
+ unsigned char ctl = evbuf[1];
+
+ track->flags |= TRK_CONTROLS;
+ STORE (SEQ_CONTROL (0, chn, ctl, value));
+ }
+ break;
+
+ case 0xC0: /* Program change */
+ pgm = evbuf[1];
+ if (track->init_pgm == -1)
+ track->init_pgm = pgm;
+ else if (pgm != track->init_pgm)
+ track->flags |= TRK_MULTIPGM;
+
+ track->pgm_map[pgm] = pgm;
+ STORE (SEQ_SET_PATCH (0, chn, pgm));
+ break;
+
+ case 0xD0: /* Channel pressure/Aftertouch */
+ track->flags |= TRK_AFTERTOUCH;
+ STORE (SEQ_CHN_PRESSURE (0, chn, evbuf[1]));
+ break;
+
+ case 0xE0: /* Pitch bend change */
+ track->flags |= TRK_BENDER;
+ STORE (SEQ_BENDER
+ (0, chn, (evbuf[1] & 0x7f) + ((evbuf[2] & 0x7f) << 7)));
+ break;
+
+ default:
+ mlib_seterr ("Internal error: Unexpected midi event");
+ fprintf (stderr, "Internal error: Unexpected midi event %02x\n",
+ evbuf[0]);
+
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+mlib_store_meta_event (mlib_desc * desc, mlib_track * track, int *optr,
+ unsigned char *evbuf, int len)
+{
+ int type = evbuf[0];
+ int i;
+
+/*
+ * The event is stored at the end of this function. If there is no
+ * need to store the event, the case entry should return (return 1).
+ */
+
+ switch (type)
+ {
+ case 0x00: /* Extension events ?????????????? */
+ /* Not supported yet */
+ break;
+
+ case 0x01: /* Descriptive text */
+ case 0x02: /* Copyright notice */
+ case 0x03: /* Sequence/track name */
+ case 0x04: /* Instrument name */
+ case 0x05: /* Lyric */
+ case 0x06: /* Marker */
+ case 0x07: /* Cue point */
+#if 0
+ for (i = 0; i < len - 1; i++)
+ printf ("%c", evbuf[1 + i]);
+ printf ("\n");
+#endif
+ break;
+
+ case 0x21: /* What is this */
+ break;
+
+/* Here is a big hole in the known meta event space */
+
+ case 0x2f: /* End of track */
+ break;
+
+/* Here is a big hole in the known meta event space */
+
+ case 0x51: /* Set tempo (usec per MIDI quarter-note) */
+ {
+ int tempo_bpm = 120;
+ unsigned int usecs_per_qn;
+
+ usecs_per_qn = (evbuf[1] << 16) | (evbuf[2] << 8) | evbuf[3];
+ tempo_bpm = (60000000 + (usecs_per_qn / 2)) / usecs_per_qn;
+
+ STORE (SEQ_SET_TEMPO (tempo_bpm));
+ return 1;
+ }
+ break;
+
+ case 0x54: /* SMPTE offset */
+ break;
+
+ case 0x58: /* Time signature */
+ {
+ unsigned sig;
+
+ sig = evbuf[1] << 24;
+ sig |= evbuf[2] << 16;
+ sig |= evbuf[3] << 8;
+ sig |= evbuf[4];
+
+ if (!desc->timesig)
+ desc->timesig = sig;
+ STORE (SEQ_TIME_SIGNATURE (sig));
+ }
+ break;
+
+ case 0x59: /* Key signature */
+ break;
+
+ case 0x7f: /* Vendor specific meta event */
+ if (evbuf[1] == 0 && evbuf[2] == 0 && evbuf[3] == 0x41)
+ { /* Microsoft ?? */
+ if (len == 4)
+ DEB_ALWAYS (printf ("GM file flag \n"));
+
+/* Don't forget to add more code here */
+ }
+ else
+ {
+ DEB_ALWAYS (printf ("Private meta event:\n"));
+ for (i = 0; i < len; i++)
+ DEB_ALWAYS (printf ("%02x ", evbuf[i]));
+ DEB_ALWAYS (printf ("\n"));
+ for (i = 0; i < len; i++)
+ DEB_ALWAYS (printf ("%c", (evbuf[i] >= ' ' && evbuf[i] < 127) ?
+ evbuf[i] : '.'));
+ DEB_ALWAYS (printf ("\n"));
+ }
+ break;
+
+ default:
+ DEB_ALWAYS (printf ("Meta event: 0x%02x???\n", type));
+ for (i = 0; i < len; i++)
+ DEB_ALWAYS (printf ("%02x ", evbuf[i]));
+ DEB_ALWAYS (printf ("\n"));
+ for (i = 0; i < len; i++)
+ DEB_ALWAYS (printf ("%c", (evbuf[i] >= ' ' && evbuf[i] < 127) ?
+ evbuf[i] : '.'));
+ DEB_ALWAYS (printf ("\n"));
+ }
+
+ STORE (META_EVENT (type, evbuf, len));
+
+ return 1;
+}
+
+static int
+mlib_input_event (mlib_desc * desc, int *iptr, int *optr, mlib_track * track)
+{
+ int time, len, event_len, i, p;
+ unsigned char b, status, type;
+
+ unsigned char buf[MAX_EVENT_SIZE + 1];
+
+ if (!(len = mlib_rdvarlen (desc, &time)))
+ return 0;
+ *iptr += len;
+
+ track->current_time += time;
+ if (track->end_time < track->current_time)
+ track->end_time = track->current_time;
+
+ if (!(len = mlib_rdbyte (desc, &type)))
+ return 0;
+ *iptr += len;
+
+ DEB (printf ("EVENT: time = %d type = 0x%02x: ", time, type));
+
+ p = 0;
+
+ switch (type)
+ {
+ case 0xff: /* Meta event */
+ if (!(len = mlib_rdbyte (desc, &type)))
+ return 0;
+ *iptr += len;
+
+ buf[p++] = type;
+
+ if (!(len = mlib_rdvarlen (desc, &event_len)))
+ return 0;
+ *iptr += len;
+
+ DEB (printf ("meta(type=%02x, len=%d): ", type, event_len));
+
+ if ((event_len + 1) > MAX_EVENT_SIZE)
+ {
+ mlib_seterr ("Too long meta event in file");
+ *iptr += event_len;
+ return 0; /* Fatal error */
+ }
+
+ for (i = 0; i < event_len; i++)
+ {
+ if (!(len = mlib_rdbyte (desc, &b)))
+ return 0;
+ *iptr += len;
+
+ buf[p++] = b;
+ DEB (printf ("%02x ", b));
+ }
+
+/*
+ * End of track test
+ */
+
+ if (type == 0x2f)
+ {
+ *iptr = 0x7fffffff;
+ return 1;
+ }
+ if (!mlib_store_meta_event (desc, track, optr, buf, p))
+ return 0;
+ break;
+
+ case 0xf0: /* Sysex, variation 1 */
+ if (!(len = mlib_rdvarlen (desc, &event_len)))
+ return 0;
+ *iptr += len;
+ DEB (printf ("sysex1 (%d): f0 ", event_len));
+ p = 0;
+ buf[p++] = 0xf0;
+ for (i = 0; i < event_len; i++)
+ {
+ if (!(len = mlib_rdbyte (desc, &b)))
+ return 0;
+ *iptr += len;
+ buf[p++] = b;
+
+ DEB (printf ("%02x ", b));
+ }
+ {
+ int l;
+
+ i = 0;
+ while (i < p)
+ {
+ int xx;
+ l = p - i;
+ if (l > 6)
+ l = 6;
+ STORE (SEQ_SYSEX (0, &buf[i], l));
+ i += l;
+ }
+ }
+ break;
+
+ case 0xf7: /* Sysex, variation 2 */
+ if (!(len = mlib_rdvarlen (desc, &event_len)))
+ return 0;
+ *iptr += len;
+ DEB (printf ("sysex2 (%d): ", event_len));
+ for (i = 0; i < event_len; i++)
+ {
+ if (!(len = mlib_rdbyte (desc, &b)))
+ return 0;
+ buf[p++] = b;
+ *iptr += len;
+
+ DEB (printf ("%02x ", b));
+ }
+ {
+ int l;
+
+ i = 0;
+ while (i < p)
+ {
+ l = p - i;
+ if (l > 6)
+ l = 6;
+ STORE (SEQ_SYSEX (0, &buf[i], l));
+ i += l;
+ }
+ }
+ break;
+
+ default:
+
+ if ((type & 0xf0) == 0xf0) /* Sys message */
+ {
+ DEB (printf ("system message "));
+ DEB (printf ("\n"));
+ mlib_seterr ("Unsupported midi file event");
+ return 0;
+ }
+ else if (type < 0x80)
+ {
+ buf[p++] = status = desc->prev_status;
+ DEB (printf ("Midi message (RST): %02x %02x ", status, type));
+ buf[p++] = type;
+
+ event_len = midi_msglen[((status >> 4) & 0xf) - 8] - 1;
+ for (i = 0; i < event_len; i++)
+ {
+ if (!(len = mlib_rdbyte (desc, &b)))
+ return 0;
+ *iptr += len;
+ buf[p++] = b;
+ DEB (printf ("%02x ", b));
+ }
+ if (!mlib_store_chn_voice_msg (desc, track, optr, buf, p))
+ return 0;
+ }
+ else
+ {
+ buf[p++] = status = desc->prev_status = type;
+ DEB (printf ("Midi message: %02x ", status));
+
+ event_len = midi_msglen[((status >> 4) & 0xf) - 8];
+ for (i = 0; i < event_len; i++)
+ {
+ if (!(len = mlib_rdbyte (desc, &b)))
+ return 0;
+ *iptr += len;
+ buf[p++] = b;
+ DEB (printf ("%02x ", b));
+ }
+ if (!mlib_store_chn_voice_msg (desc, track, optr, buf, p))
+ return 0;
+ }
+ }
+
+ DEB (printf ("\n"));
+
+ return 1;
+}
+
+static int
+mlib_load_pass (mlib_desc * desc, mlib_track * track, int *iptr, int *optr,
+ int len)
+{
+ int i;
+
+ for (i = 0; i < 128; i++)
+ {
+ track->pgm_map[i] = -1;
+ track->drum_map[i] = -1;
+ }
+
+ track->len = 0;
+ track->flags = 0;
+ track->init_chn = -1;
+ track->init_pgm = -1;
+ track->port = -1;
+ track->chn = -1;
+ track->chnmask = 0;
+ track->pgm = -1;
+ track->current_time = 0;
+ track->noteon_time = -1;
+ track->end_time = 0;
+ track->min_note = 300;
+ track->max_note = -1;
+
+ *iptr = *optr = 0;
+ desc->prev_status = 0;
+
+ while (*iptr < len)
+ if (!mlib_input_event (desc, iptr, optr, track))
+ {
+ return 0;
+ }
+
+ return 1;
+}
+
+mlib_track *
+mlib_loadtrack (mlib_desc * desc, int *end_detected)
+{
+ mlib_track *track;
+ char sig[5];
+ int dummy, len, iptr, optr, pass1events;
+
+ *end_detected = 0;
+
+ if (!mlib_chkdesc (desc))
+ return NULL;
+
+ if ((track = (mlib_track *) malloc (sizeof (*track))) == NULL)
+ return (mlib_track *) mlib_seterr ("Can't malloc track descriptor");
+
+ desc->curr_trk++;
+
+ if (desc->curr_trk >= desc->hdr.MThd_ntrk)
+ {
+ *end_detected = 1;
+ return (mlib_track *) mlib_seterr ("No more tracks in the midi file");
+ }
+
+ desc->trk_offs = desc->next_trk_offs;
+
+ if (!mlib_seek (desc, desc->trk_offs))
+ {
+ free ((char *) track);
+ return NULL;
+ }
+
+ if (!mlib_rdstr (desc, sig, 4))
+ {
+ free ((char *) track);
+ return NULL;
+ }
+
+ if (strcmp (sig, "MTrk"))
+ {
+ free ((char *) track);
+ return (mlib_track *) mlib_seterr ("Invalid track signature (!= MTrk)");
+ }
+
+ if (!mlib_rdint32 (desc, &len))
+ {
+ free ((char *) track);
+ return 0;
+ }
+
+ desc->next_trk_offs = desc->trk_offs + 4 + 4 + len;
+
+ track->events = NULL;
+
+ if (!mlib_load_pass (desc, track, &iptr, &optr, len))
+ {
+ free ((char *) track);
+ return NULL;
+ }
+
+ pass1events = optr;
+
+/*
+ * Pass 2 actually store the messages
+ */
+
+ dummy = pass1events;
+ if (dummy < 10)
+ dummy = 10;
+ track->events = malloc (12 * dummy);
+ if (pass1events == 0)
+ return track; /* Empty track */
+
+ if (!mlib_seek (desc, desc->trk_offs + 4)) /* Past the MTrk */
+ {
+ free (track->events);
+ free ((char *) track);
+ return NULL;
+ }
+
+ if (!mlib_rdint32 (desc, &dummy))
+ {
+ free (track->events);
+ free ((char *) track);
+ return 0;
+ }
+
+ if (!mlib_load_pass (desc, track, &iptr, &optr, len))
+ {
+ free (track->events);
+ free ((char *) track);
+ return NULL;
+ }
+
+ track->len = optr;
+
+ if (optr != pass1events)
+ fprintf (stderr,
+ "Warning: Events counts of pass 1 and 2 differ (%d/%d)\n",
+ pass1events, optr);
+
+ return track;
+}
+
+void
+mlib_deltrack (mlib_track * track)
+{
+ if (track == NULL)
+ return;
+
+ if (track->events != NULL)
+ free (track->events);
+
+ free ((char *) track);
+}
+
+mlib_desc *
+mlib_open (char *path)
+{
+ mlib_desc *desc;
+ int i;
+
+ desc = (mlib_desc *) malloc (sizeof (*desc));
+ if (desc == NULL)
+ return (mlib_desc *) mlib_seterr ("Malloc failed");
+
+ if ((desc->fd = open (path, O_RDONLY, 0)) == -1)
+ {
+ free ((char *) desc);
+ return (mlib_desc *) mlib_syserr (path);
+ }
+
+ strncpy (desc->path, path, sizeof (desc->path));
+
+ desc->bufcnt = 0;
+ desc->bufp = 1;
+ desc->curr_trk = 0;
+ desc->trk_offs = 0;
+ desc->next_trk_offs = 0;
+ desc->magic = 0x121234;
+ desc->control_track = NULL;
+ desc->timesig = 0;
+ for (i = 0; i < MAX_TRACK; i++)
+ desc->tracks[i] = NULL;
+
+ if (!mlib_read_mthd (desc))
+ {
+ close (desc->fd);
+ free ((char *) desc);
+ return NULL;
+ }
+
+ return desc;
+}
+
+void
+mlib_close (mlib_desc * desc)
+{
+ if (mlib_chkdesc (desc))
+ {
+ close (desc->fd);
+ desc->fd = -1;
+ free (desc);
+ }
+}
+
+char *
+mlib_errmsg (void)
+{
+ return &mlib_errstring[0];
+}
diff --git a/tutorials/sndkit/ossmplay/mlib.h b/tutorials/sndkit/ossmplay/mlib.h
new file mode 100644
index 0000000..5f7997e
--- /dev/null
+++ b/tutorials/sndkit/ossmplay/mlib.h
@@ -0,0 +1,87 @@
+/*
+ * Event types 0 to 127 are available for private use
+ * by applications
+ */
+
+#define EV_PRIVATE_META 0
+
+#define MAX_TRACK 256
+
+struct midi_hdr
+{
+ int MThd_fmt;
+ int MThd_ntrk; /* Num of tracks */
+
+ int time_mode;
+#define TIME_MIDI 1
+ int division;
+#define TIME_SMPTE 2
+ int SMPTE_format;
+ int SMPTE_resolution;
+};
+
+struct mlib_track
+{
+ int len;
+ unsigned char *events;
+
+/*
+ * The flags are set when loading the track. Let's hope they are
+ * updated also when the track gets changed.
+ */
+ unsigned long flags;
+#define TRK_MULTICHN 0x00000001 /* More than one channel */
+#define TRK_MULTIPGM 0x00000002 /* More than one program */
+#define TRK_VEL_NOTEON 0x00000004 /* Events with on vel. <> 64 */
+#define TRK_AFTERTOUCH 0x00000008 /* Aftertouch events */
+#define TRK_POLY_AFTERTOUCH 0x00000010 /* Polyph. aftertouch events */
+#define TRK_VEL_NOTEOFF 0x00000020 /* Events with off vel. <> 64 */
+#define TRK_CONTROLS 0x00000040 /* Controller events */
+#define TRK_BENDER 0x00000080 /* Bender events */
+#define TRK_NOTES 0x00000100 /* At least one note on */
+ int init_chn; /* First chn referenced by the track */
+ int init_pgm; /* First pgm referenced by the track */
+ int chn; /* chn assigned to the track */
+ int chnmask; /* channel bitmap */
+ int port; /* port assigned to the track */
+ int pgm; /* pgm assigned to the track */
+ int current_time;
+ int noteon_time; /* Time of the first noteon */
+ int end_time;
+ int min_note, max_note; /* Scale info */
+ short pgm_map[128]; /* MIDI pgm mapping table */
+ short drum_map[128]; /* MIDI drum pgm mapping table */
+};
+typedef struct mlib_track mlib_track;
+
+struct mlib_desc
+{
+ int magic; /* 0x121234 */
+ int fd;
+ char path[1024];
+ struct midi_hdr hdr;
+
+ int curr_trk;
+ int trk_offs;
+ int next_trk_offs;
+
+ unsigned char buf[1024];
+ int bufcnt, bufp;
+
+ unsigned int timesig;
+
+ unsigned char prev_status; /* For running status */
+
+ mlib_track *control_track;
+
+ mlib_track *tracks[MAX_TRACK];
+};
+
+typedef struct mlib_desc mlib_desc;
+
+int mlib_chkdesc (mlib_desc * desc);
+mlib_track *mlib_loadtrack (mlib_desc * desc, int *end_detected);
+void mlib_deltrack (mlib_track * track);
+mlib_desc *mlib_open (char *path);
+void mlib_close (mlib_desc * desc);
+char *mlib_errmsg (void);
diff --git a/tutorials/sndkit/ossmplay/ossmplay.c b/tutorials/sndkit/ossmplay/ossmplay.c
new file mode 100644
index 0000000..0325922
--- /dev/null
+++ b/tutorials/sndkit/ossmplay/ossmplay.c
@@ -0,0 +1,233 @@
+#define DEB(stmt) /* stmt */ /* For debug printing */
+#define DEB2(stmt) /* stmt */ /* For debug printing */
+#define DEB3(stmt) stmt /* For debug printing */
+#define DEB_ALWAYS(stmt) stmt /* Interesting things */
+
+#define _INCLUDE_POSIX_SOURCE 1
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <sys/errno.h>
+#include "../../../include/soundcard.h"
+
+extern int errno;
+
+#include "mlib.h"
+
+SEQ_DEFINEBUF (1024);
+
+int seqfd;
+mlib_track *tracks[1024];
+int ntrks = 0;
+int dev = 0;
+
+void
+player ()
+{
+ int i, track;
+ int prev_time = 0;
+
+ int ptrs[1024] = { 0 };
+ unsigned char *ptr;
+ unsigned char *event;
+ int time, n = 0;
+
+#if 0
+ for (track = 0; track < ntrks; track++)
+ for (i = 0; i < 128; i++)
+ {
+ if (tracks[track]->pgm_map[i] != -1)
+ {
+ n++;
+ SEQ_LOAD_GMINSTR (dev, i);
+ }
+ tracks[track]->pgm_map[i] = i;
+
+ if (n == 0) /* No program changes. Assume pgm# 0 */
+ SEQ_LOAD_GMINSTR (dev, 0);
+
+ if (tracks[track]->drum_map[i] != -1)
+ SEQ_LOAD_GMDRUM (dev, i);
+ tracks[track]->drum_map[i] = i;
+ }
+
+ if (n == 0) /* No program changes detected */
+ SEQ_LOAD_GMINSTR (dev, 0); /* Acoustic piano */
+
+#endif
+
+ SEQ_START_TIMER ();
+ while (1)
+ {
+ int best = -1, best_time = 0x7fffffff;
+
+ for (i = 0; i < ntrks; i++)
+ if (ptrs[i] < tracks[i]->len)
+ {
+ ptr = &(tracks[i]->events[ptrs[i] * 12]);
+ event = &ptr[4];
+ time = *(int *) ptr;
+
+ if (time < best_time)
+ {
+ best = i;
+ best_time = time;
+ }
+ }
+
+ if (best == -1)
+ return;
+
+ ptr = &(tracks[best]->events[ptrs[best] * 12]);
+ event = &ptr[4];
+ time = *(int *) ptr;
+ ptrs[best]++;
+
+ if (event[0] < 128)
+ {
+ }
+ else
+ {
+ int j;
+
+ if (time > prev_time)
+ {
+ SEQ_WAIT_TIME (time);
+ prev_time = time;
+ }
+
+ if (event[0] == EV_SYSEX)
+ {
+ event[1] = dev;
+ }
+
+ if ((event[0] & 0xf0) == 0x90)
+ {
+ event[1] = dev;
+
+ if (event[0] == EV_CHN_COMMON && event[2] == MIDI_PGM_CHANGE)
+ {
+ event[4] = tracks[best]->pgm_map[event[4]];
+ }
+ }
+
+ _SEQ_NEEDBUF (8);
+ memcpy (&_seqbuf[_seqbufptr], event, 8);
+ _SEQ_ADVBUF (8);
+
+ }
+ }
+}
+
+int
+main (int argc, char *argv[])
+{
+ mlib_desc *mdesc;
+ int was_last;
+ int tmp, argp = 1;
+ oss_longname_t song_name;
+ char *p, *s, *devname="/dev/midi00";;
+ extern void OSS_set_timebase(int tb);
+
+ if (argc < 2)
+ {
+ fprintf (stderr, "Usage: %s midifile\n", argv[0]);
+ exit (-1);
+ }
+
+ if (argc==3)
+ {
+ devname=argv[1];
+ argp++;
+ }
+
+
+ if ((seqfd = open (devname, O_WRONLY, 0)) == -1)
+ {
+ perror (devname);
+ exit (-1);
+ }
+ dev=0;
+
+#ifdef OSSLIB
+ OSS_init (seqfd, 1024);
+#endif
+
+ if ((mdesc = mlib_open (argv[argp])) == NULL)
+ {
+ fprintf (stderr, "Can't open MIDI file %s: %s\n",
+ argv[argp], mlib_errmsg ());
+ exit (-1);
+ }
+ ioctl(seqfd, SNDCTL_SETSONG, argv[argp]);
+
+/*
+ * Extract the file name part of the argument
+ */
+
+ p=s=argv[argp];
+ while (*s)
+ {
+ if (*s=='/')
+ p=s+1;
+ s++;
+ }
+
+ memset(song_name, 0, sizeof(song_name));
+ strcpy(song_name, p);
+
+#if 1
+ tmp=MIDI_MODE_TIMED;
+ if (ioctl(seqfd, SNDCTL_MIDI_SETMODE, &tmp)==-1)
+ {
+ perror("SNDCTL_MIDI_SETMODE");
+ exit(-1);
+ }
+#endif
+
+ tmp = mdesc->hdr.division;
+printf("Timebase %d\n", tmp);
+ OSS_set_timebase(tmp);
+ if (ioctl(seqfd, SNDCTL_MIDI_TIMEBASE, &tmp)==-1)
+ {
+ perror("SNDCTL_MIDI_TIMEBASE");
+ exit(-1);
+ }
+
+ ntrks = 0;
+
+ while ((tracks[ntrks] = mlib_loadtrack (mdesc, &was_last)) != NULL)
+ {
+ int i;
+
+ DEB2 (printf ("Loaded track %03d: len = %d events, flags = %08x\n",
+ mdesc->curr_trk, tracks[ntrks]->len,
+ tracks[ntrks]->flags));
+ ntrks++;
+ }
+
+ if (!was_last)
+ {
+ fprintf (stderr, "%s: %s\n", argv[argp], mlib_errmsg ());
+ exit (-1);
+ }
+
+ tmp = (int) mdesc->timesig;
+ printf("Timesig %08x\n", tmp);
+
+/*
+ * Set the current song name (OSS 4.0 feature).
+ */
+ ioctl(seqfd, SNDCTL_SETSONG, song_name);
+ player ();
+ SEQ_DELTA_TIME (mdesc->hdr.division * 8);
+ SEQ_PGM_CHANGE(0, 0, 0);
+ SEQ_DUMPBUF ();
+
+ mlib_close (mdesc);
+ close (seqfd);
+ exit (0);
+}
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);
+}
diff --git a/tutorials/sndkit/sblive/Makefile b/tutorials/sndkit/sblive/Makefile
new file mode 100644
index 0000000..85efde5
--- /dev/null
+++ b/tutorials/sndkit/sblive/Makefile
@@ -0,0 +1,53 @@
+CC=cc -O
+SRCDIR=../../..
+INCLUDE=-I$(SRCDIR)/include
+LDFLAGS=
+INSTALLDIR=/usr/local/bin
+INSTALLLIB=/usr/lib/oss
+CFLAGS=$(INCLUDE) -DUSERLAND
+
+all: asm10k ld10k emu10k.bin1 emu10k.bin2 emu10k1_dsp.h emu10k2_dsp.h
+
+install: ld10k emu10k.bin1 emu10k.bin2
+ strip asm10k
+ rm -f $(INSTALLDIR)/ld10k $(INSTALLDIR)/emu10k.bin1 $(INSTALLDIR)/emu10k.bin2
+ cp ld10k $(INSTALLDIR)
+ cp emu10k.bin1 $(INSTALLLIB)
+ cp emu10k.bin2 $(INSTALLLIB)
+ cp *.map /tmp
+
+load: ld10k emu10k.bin1
+ cp -f *.map /tmp
+ ./ld10k emu10k.bin1 -a
+
+aload: ld10k emu10k.bin2
+ cp -f *.map /tmp
+ ./ld10k emu10k.bin2 -a
+
+testload: ld10k test.bin2
+ ./ld10k test.bin2 -a
+
+ld10k: ld10k.c
+ $(CC) $(CFLAGS) $(INCLUDE) -o ld10k ld10k.c
+ strip ld10k
+
+asm10k: asm10k.c
+ $(CC) $(CFLAGS) $(INCLUDE) -o asm10k asm10k.c
+
+emu10k.bin1: emu10k.dsp asm10k equalizer.mac emu10k1.mac
+ ./emuasm1 emu10k.dsp
+
+emu10k.bin2: emu10k.dsp asm10k equalizer.mac emu10k2.mac
+ ./emuasm2 emu10k.dsp
+
+test.bin2: test.dsp asm10k equalizer.mac emu10k2.mac
+ ./emuasm2 test.dsp
+
+emu10k1_dsp.h: emu10k.bin1 mkheader
+ ./mkheader emu10k1_dsp < emu10k.bin1 > emu10k1_dsp.h
+
+emu10k2_dsp.h: emu10k.bin2 mkheader
+ ./mkheader emu10k2_dsp < emu10k.bin2 > emu10k2_dsp.h
+
+clean:
+ rm -f *.o asm10k ld10k writegpr *.bin *.bin1 *.bin2 core x y z testgen *.map mkheader *_dsp.h
diff --git a/tutorials/sndkit/sblive/README b/tutorials/sndkit/sblive/README
new file mode 100644
index 0000000..51bbf97
--- /dev/null
+++ b/tutorials/sndkit/sblive/README
@@ -0,0 +1,12 @@
+This directory contains undocumented and unsupported utilities to compile
+DSP code for SB Live! and Audigy/2/4. The sblive driver has used precompiled
+embedded versions of this code (emu10k1_dsp.h and emu10k2_dsp.h) so these tools
+have not been tested for years. However it should still be possible to modify,
+compile and load the code to the card.
+
+Undocumented and unsupported really means that. If you want to use these tools
+then you have to obtain the knowledge about the Live!/audigy DSP architecture
+yourself. Also you need to figure out how these tools work.
+
+Note! The Makefile required to compile this stuff is included in the "source"
+directory of OSS. However this file may not appear in the "build" tree.
diff --git a/tutorials/sndkit/sblive/asm10k.c b/tutorials/sndkit/sblive/asm10k.c
new file mode 100644
index 0000000..b160228
--- /dev/null
+++ b/tutorials/sndkit/sblive/asm10k.c
@@ -0,0 +1,1206 @@
+/*
+ * Assembler for Emu10k1
+ * Copyright (C) 4Front Technologies 2001. All rights reserved
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include <soundcard.h>
+#ifdef USERLAND
+#define oss_native_word unsigned long
+#define oss_mutex_t unsigned long
+#define oss_device_t unsigned long
+#define ac97_devc unsigned long
+#define oss_midi_inputbyte_t char
+#define uart401_devc unsigned long
+typedef int oss_mutex;
+typedef void *oss_dma_handle_t;
+#else
+#include "../../../kernel/framework/include/os.h"
+#endif /* USERLAND */
+#include "../../../kernel/drv/oss_sblive/sblive.h"
+
+#define MAX_NAME 64
+#define MAX_SYMBOLS 1024
+
+int is_audigy = 0;
+
+int gpr_base = 0x100;
+int input_base = 0x10;
+int output_base = 0x20;
+
+static char line[4096] = "", *lineptr = line;
+static char lastline[4096];
+
+typedef struct
+{
+ char name[MAX_NAME];
+ int type;
+#define SY_DUMMY 0
+#define SY_GPR 1
+#define SY_INPUT 2
+#define SY_OUTPUT 3
+#define SY_CONST 4
+#define SY_FX 5
+#define SY_ACCUM 6
+#define SY_PARM 7
+ int arg;
+}
+sym_t;
+
+typedef struct
+{
+ char *name;
+ int opcode;
+}
+instruction_t;
+
+static instruction_t instructions[] = {
+ {"MACS", 0x0},
+ {"MACS1", 0x1},
+ {"MACW", 0x2},
+ {"MACW1", 0x3},
+ {"MACINTS", 0x4},
+ {"MACIINTW", 0x5},
+ {"SUM", 0x6},
+ {"ACC3", 0x6},
+ {"MACMV", 0x7},
+ {"ANDXOR", 0x8},
+ {"TSTNEG", 0x9},
+ {"LIMIT", 0xa},
+ {"LIMIT1", 0xb},
+ {"LOG", 0xc},
+ {"EXP", 0xd},
+ {"INTERP", 0xe},
+ {"SKIP", 0xf},
+ {NULL, 0}
+};
+
+static sym_t symtab[MAX_SYMBOLS];
+static int nsyms = 0;
+static int group_created = 0;
+
+int lineno = 0, errors = 0;
+static emu10k1_file fle;
+static int pc;
+
+static int ngpr = 0;
+
+static char *
+agetline (char *buf, int len)
+{
+ char *s;
+
+ if (*lineptr == 0)
+ {
+ lineptr = line;
+
+ if (fgets (line, sizeof (line), stdin) == NULL)
+ return NULL;
+
+ if (*line != '#')
+ lineno++;
+
+ strcpy(lastline, line);
+ }
+
+ s = buf;
+
+ while (*lineptr && *lineptr != ';')
+ {
+ *s++ = *lineptr++;
+ }
+
+ *s = 0;
+
+ if (*lineptr == ';')
+ *lineptr++ = 0;
+
+/* printf("%s\n", buf); */
+ return buf;
+}
+
+static void
+error (char *msg)
+{
+ fprintf (stderr, "%s\n", lastline);
+ fprintf (stderr, "Error '%s' on line %d\n", msg, lineno);
+ errors++;
+}
+
+static sym_t *
+find_symbol (char *name)
+{
+ int i;
+
+ for (i = 0; i < nsyms; i++)
+ if (strcmp (symtab[i].name, name) == 0)
+ {
+ return &symtab[i];
+ }
+
+ return NULL;
+}
+
+static void
+add_symbol (char *name, int type, int arg)
+{
+ sym_t *sym;
+
+#if 0
+ /* This is not needed any more */
+ if (is_audigy)
+ {
+ if (type == SY_CONST)
+ arg += 0x80;
+ }
+#endif
+
+ /* printf("'%s' = %d/%x\n", name, type, arg); */
+
+ if (nsyms >= MAX_SYMBOLS)
+ {
+ error ("Symbol table full");
+ exit (-1);
+ }
+
+ if (find_symbol (name) != NULL)
+ {
+ error ("Dublicate symbol");
+ return;
+ }
+
+ if (strlen (name) >= MAX_NAME)
+ {
+ error ("Too long symbol name");
+ exit (-1);
+ }
+
+ sym = &symtab[nsyms++];
+
+ strcpy (sym->name, name);
+ sym->type = type;
+ sym->arg = arg;
+}
+
+static void
+compile_gpr (char *parms)
+{
+ char *p = parms;
+
+ while (*p && *p != ' ')
+ p++;
+ if (*p == ' ')
+ {
+ error ("Too many parameters");
+ return;
+ }
+
+ if (ngpr >= MAX_GPR)
+ error ("Too many GPR variables");
+
+ add_symbol (parms, SY_GPR, gpr_base + ngpr++);
+}
+
+static void
+declare_const (unsigned int gpr, char *value)
+{
+ int n, intv;
+ float v;
+
+ n = fle.consts.nconst;
+
+ if (n >= MAX_CONST_PARMS)
+ {
+ error ("Too many constant parameters");
+ return;
+ }
+
+ if (*value == 'I')
+ {
+ if (sscanf (&value[1], "%g", &v) != 1)
+ {
+ error ("Bad floating point value");
+ return;
+ }
+ intv = (int) v;
+ /* printf("%s/%g -> %08x -> %g\n", value, v, intv, (float)intv); */
+ }
+ else if (*value == '0' && value[1] == 'x')
+ {
+ if (sscanf (&value[2], "%x", &intv) != 1)
+ {
+ error ("Bad hexadecimal value");
+ return;
+ }
+ /* printf("%s/%g -> %08x -> %g\n", value, v, intv, (float)intv); */
+ }
+ else
+ {
+ if (sscanf (value, "%g", &v) != 1)
+ {
+ error ("Bad floating point value");
+ return;
+ }
+ intv = (int) (v * 0x7fffffff);
+ /* printf("%s/%g -> %08x -> %g\n", value, v, intv, (float)intv / (float)0x7fffffff); */
+ }
+
+ fle.consts.consts[n].gpr = gpr;
+ fle.consts.consts[n].value = intv;
+ fle.consts.nconst = n + 1;
+}
+
+static void
+compile_const (char *parms)
+{
+ char *p = parms;
+ char *value;
+
+ while (*p && *p != ' ')
+ p++;
+
+ if (*p == 0)
+ {
+ error ("Too few parameters");
+ return;
+ }
+
+ *p++ = 0;
+ while (*p == ' ')
+ p++;
+
+ if (*p == 0)
+ {
+ error ("Too few parameters");
+ return;
+ }
+
+ value = p;
+
+ while (*p && *p != ' ')
+ p++;
+
+ if (*p == ' ')
+ {
+ error ("Too many parameters");
+ return;
+ }
+
+ if (ngpr >= MAX_GPR)
+ error ("Too many GPR variables");
+
+ declare_const (ngpr, value);
+
+ add_symbol (parms, SY_GPR, gpr_base + ngpr++);
+}
+
+static void
+define_parm (char *parms, char *name, int num, char *typ)
+{
+ int n;
+
+/* printf("Parameter '%s/%s' = GPR %d (%d), typ=%s\n", parms, name, ngpr, num, typ); */
+
+ n = fle.parms.ngpr;
+
+ if (n >= MAX_GPR_PARMS)
+ {
+ error ("Too many GPR parameters");
+ return;
+ }
+
+ if (strcmp (typ, "group") == 0)
+ {
+ strcpy (fle.parms.gpr[n].name, parms);
+ fle.parms.gpr[n].num = 0;
+ fle.parms.gpr[n].type = MIXT_GROUP;
+ fle.parms.gpr[n].def = 0;
+ fle.parms.ngpr = n + 1;
+ group_created = 1;
+ return;
+ }
+
+#if 0
+ if (!group_created)
+ {
+ strcpy (fle.parms.gpr[n].name, "EFX");
+ fle.parms.gpr[n].num = 0;
+ fle.parms.gpr[n].type = MIXT_GROUP;
+ fle.parms.gpr[n].def = 0;
+ fle.parms.ngpr = n + 1;
+ group_created = 1;
+ }
+#endif
+
+ if (strcmp (typ, "stereo") == 0)
+ {
+ char tmp[128];
+
+ strcpy (fle.parms.gpr[n].name, name);
+ fle.parms.gpr[n].num = ngpr;
+ fle.parms.gpr[n].type = MIXT_STEREOSLIDER;
+ fle.parms.gpr[n].def = num | (num << 8);
+ fle.parms.ngpr = n + 1;
+
+ sprintf (tmp, "%s_L", parms);
+ add_symbol (tmp, SY_PARM, gpr_base + ngpr++);
+ sprintf (tmp, "%s_R", parms);
+ add_symbol (tmp, SY_PARM, gpr_base + ngpr++);
+ return;
+ }
+
+ if (strcmp (typ, "stereopeak") == 0)
+ {
+ char tmp[128];
+
+ strcpy (fle.parms.gpr[n].name, name);
+ fle.parms.gpr[n].num = ngpr;
+ fle.parms.gpr[n].type = MIXT_STEREOPEAK;
+ fle.parms.gpr[n].def = num | (num << 8);
+ fle.parms.ngpr = n + 1;
+
+ sprintf (tmp, "%s_L", parms);
+ add_symbol (tmp, SY_PARM, gpr_base + ngpr++);
+ sprintf (tmp, "%s_R", parms);
+ add_symbol (tmp, SY_PARM, gpr_base + ngpr++);
+ return;
+ }
+
+ if (strcmp (typ, "onoff") == 0)
+ {
+ char tmp[128];
+
+ strcpy (fle.parms.gpr[n].name, name);
+ fle.parms.gpr[n].num = ngpr;
+ fle.parms.gpr[n].type = MIXT_ONOFF;
+ fle.parms.gpr[n].def = num;
+ fle.parms.ngpr = n + 1;
+
+ sprintf (tmp, "%s_ON", parms);
+ add_symbol (tmp, SY_PARM, gpr_base + ngpr++);
+ sprintf (tmp, "%s_OFF", parms);
+ add_symbol (tmp, SY_PARM, gpr_base + ngpr++);
+ return;
+ }
+
+ if (strcmp (typ, "eq1") == 0)
+ {
+ char tmp[128];
+
+ strcpy (fle.parms.gpr[n].name, name);
+ fle.parms.gpr[n].num = ngpr;
+ fle.parms.gpr[n].type = EMU_MIXT_EQ1;
+ fle.parms.gpr[n].def = num;
+ fle.parms.ngpr = n + 1;
+
+/* printf("EQ1, GPR=%d\n", ngpr); */
+ sprintf (tmp, "%s_CST_0", parms);
+ add_symbol (tmp, SY_PARM, gpr_base + ngpr++);
+ sprintf (tmp, "%s_CST_1", parms);
+ add_symbol (tmp, SY_PARM, gpr_base + ngpr++);
+ sprintf (tmp, "%s_CST_2", parms);
+ add_symbol (tmp, SY_PARM, gpr_base + ngpr++);
+ sprintf (tmp, "%s_CST_3", parms);
+ add_symbol (tmp, SY_PARM, gpr_base + ngpr++);
+ sprintf (tmp, "%s_CST_4", parms);
+ add_symbol (tmp, SY_PARM, gpr_base + ngpr++);
+ return;
+ }
+
+ if (strcmp (typ, "eq2") == 0)
+ {
+ char tmp[128];
+
+ strcpy (fle.parms.gpr[n].name, name);
+ fle.parms.gpr[n].num = ngpr;
+ fle.parms.gpr[n].type = EMU_MIXT_EQ2;
+ fle.parms.gpr[n].def = num;
+ fle.parms.ngpr = n + 1;
+
+/* printf("EQ2, GPR=%d\n", ngpr); */
+ sprintf (tmp, "%s_CST_0", parms);
+ add_symbol (tmp, SY_PARM, gpr_base + ngpr++);
+ sprintf (tmp, "%s_CST_1", parms);
+ add_symbol (tmp, SY_PARM, gpr_base + ngpr++);
+ sprintf (tmp, "%s_CST_2", parms);
+ add_symbol (tmp, SY_PARM, gpr_base + ngpr++);
+ sprintf (tmp, "%s_CST_3", parms);
+ add_symbol (tmp, SY_PARM, gpr_base + ngpr++);
+ sprintf (tmp, "%s_CST_4", parms);
+ add_symbol (tmp, SY_PARM, gpr_base + ngpr++);
+ return;
+ }
+
+ if (strcmp (typ, "eq3") == 0)
+ {
+ char tmp[128];
+
+ strcpy (fle.parms.gpr[n].name, name);
+ fle.parms.gpr[n].num = ngpr;
+ fle.parms.gpr[n].type = EMU_MIXT_EQ3;
+ fle.parms.gpr[n].def = num;
+ fle.parms.ngpr = n + 1;
+/* printf("EQ3, GPR=%d\n", ngpr); */
+
+ sprintf (tmp, "%s_CST_0", parms);
+ add_symbol (tmp, SY_PARM, gpr_base + ngpr++);
+ sprintf (tmp, "%s_CST_1", parms);
+ add_symbol (tmp, SY_PARM, gpr_base + ngpr++);
+ sprintf (tmp, "%s_CST_2", parms);
+ add_symbol (tmp, SY_PARM, gpr_base + ngpr++);
+ sprintf (tmp, "%s_CST_3", parms);
+ add_symbol (tmp, SY_PARM, gpr_base + ngpr++);
+ sprintf (tmp, "%s_CST_4", parms);
+ add_symbol (tmp, SY_PARM, gpr_base + ngpr++);
+ return;
+ }
+
+ if (strcmp (typ, "eq4") == 0)
+ {
+ char tmp[128];
+
+ strcpy (fle.parms.gpr[n].name, name);
+ fle.parms.gpr[n].num = ngpr;
+ fle.parms.gpr[n].type = EMU_MIXT_EQ4;
+ fle.parms.gpr[n].def = num;
+ fle.parms.ngpr = n + 1;
+/* printf("EQ4, GPR=%d\n", ngpr); */
+
+ sprintf (tmp, "%s_CST_0", parms);
+ add_symbol (tmp, SY_PARM, gpr_base + ngpr++);
+ sprintf (tmp, "%s_CST_1", parms);
+ add_symbol (tmp, SY_PARM, gpr_base + ngpr++);
+ sprintf (tmp, "%s_CST_2", parms);
+ add_symbol (tmp, SY_PARM, gpr_base + ngpr++);
+ sprintf (tmp, "%s_CST_3", parms);
+ add_symbol (tmp, SY_PARM, gpr_base + ngpr++);
+ sprintf (tmp, "%s_CST_4", parms);
+ add_symbol (tmp, SY_PARM, gpr_base + ngpr++);
+ return;
+ }
+
+ strcpy (fle.parms.gpr[n].name, name);
+ fle.parms.gpr[n].num = ngpr;
+ fle.parms.gpr[n].type = MIXT_SLIDER;
+ fle.parms.gpr[n].def = num;
+ fle.parms.ngpr = n + 1;
+ add_symbol (parms, SY_PARM, gpr_base + ngpr++);
+}
+
+static void
+compile_parm (char *parms)
+{
+ char *p = parms, *def, *typ, *name;
+ int n;
+ int num;
+
+ while (*p && *p != ' ')
+ p++;
+ if (*p != ' ')
+ {
+ error ("Too few parameters");
+ return;
+ }
+
+ *p++ = 0;
+ while (*p && *p == ' ')
+ p++;
+
+ if (*p == 0)
+ {
+ error ("Too few parameters");
+ return;
+ }
+
+ def = p;
+ while (*p && *p != ' ')
+ p++;
+
+ if (*p != 0)
+ {
+ *p++ = 0;
+
+ while (*p == ' ' || *p == '\t')
+ p++;
+ }
+
+ if (*p == 0)
+ typ = "";
+ else
+ {
+ typ = p;
+
+ while (*p && *p != ' ' && *p != '\t')
+ p++;
+ if (*p != 0)
+ *p++ = 0;
+ }
+
+ while (*p == ' ' || *p == '\t')
+ p++;
+ name = p;
+ while (*p && *p != ' ' && *p != '\t')
+ p++;
+ *p = 0;
+
+ if (*name == 0)
+ name = parms;
+
+ if (sscanf (def, "%d", &num) != 1)
+ {
+ error ("Bad integer value");
+ return;
+ }
+
+ define_parm (parms, name, num, typ);
+}
+
+static void
+compile_input (char *parms)
+{
+ char *p = parms, *s;
+ int num;
+
+ while (*p && *p != ' ')
+ p++;
+ if (*p != ' ')
+ {
+ error ("Too few parameters");
+ return;
+ }
+
+ *p++ = 0;
+ while (*p && *p == ' ')
+ p++;
+
+ if (*p == 0)
+ {
+ error ("Too few parameters");
+ return;
+ }
+
+ s = p;
+ while (*p && *p != ' ')
+ p++;
+ if (*p == ' ')
+ {
+ error ("Too many parameters");
+ return;
+ }
+
+ if (sscanf (s, "%d", &num) != 1)
+ {
+ error ("Bad integer value");
+ return;
+ }
+
+ add_symbol (parms, SY_INPUT, input_base + num);
+}
+
+static void
+compile_send (char *parms)
+{
+ char *p = parms, *s;
+ int num;
+
+ while (*p && *p != ' ')
+ p++;
+ if (*p != ' ')
+ {
+ error ("Too few parameters");
+ return;
+ }
+
+ *p++ = 0;
+ while (*p && *p == ' ')
+ p++;
+
+ if (*p == 0)
+ {
+ error ("Too few parameters");
+ return;
+ }
+
+ s = p;
+ while (*p && *p != ' ')
+ p++;
+ if (*p == ' ')
+ {
+ error ("Too many parameters");
+ return;
+ }
+
+ if (sscanf (s, "%d", &num) != 1)
+ {
+ error ("Bad integer value");
+ return;
+ }
+
+ add_symbol (parms, SY_FX, num);
+}
+
+static void
+compile_output (char *parms)
+{
+ char *p = parms, *s;
+ int num;
+
+ while (*p && *p != ' ')
+ p++;
+ if (*p != ' ')
+ {
+ error ("Too few parameters");
+ return;
+ }
+
+ *p++ = 0;
+ while (*p && *p == ' ')
+ p++;
+
+ if (*p == 0)
+ {
+ error ("Too few parameters");
+ return;
+ }
+
+ s = p;
+ while (*p && *p != ' ')
+ p++;
+ if (*p == ' ')
+ {
+ error ("Too many parameters");
+ return;
+ }
+
+ if (sscanf (s, "%d", &num) != 1)
+ {
+ error ("Bad integer value");
+ return;
+ }
+
+ add_symbol (parms, SY_OUTPUT, output_base + num);
+}
+
+static void
+compile_directive (char *line)
+{
+ char *p = line;
+
+ while (*p && *p != ' ')
+ p++;
+ if (*p != ' ')
+ {
+ error ("Missing parameter");
+ return;
+ }
+
+ *p++ = 0;
+
+ while (*p == ' ')
+ p++;
+
+ if (strcmp (line, ".gpr") == 0)
+ {
+ compile_gpr (p);
+ return;
+ }
+
+ if (strcmp (line, ".const") == 0)
+ {
+ compile_const (p);
+ return;
+ }
+
+ if (strcmp (line, ".parm") == 0)
+ {
+ compile_parm (p);
+ return;
+ }
+
+ if (strcmp (line, ".input") == 0)
+ {
+ compile_input (p);
+ return;
+ }
+
+ if (strcmp (line, ".send") == 0)
+ {
+ compile_send (p);
+ return;
+ }
+
+ if (strcmp (line, ".output") == 0)
+ {
+ compile_output (p);
+ return;
+ }
+
+ error ("Unknown directive");
+}
+
+static void
+compile_asm (char *iline)
+{
+ char line[4096], *s, *p;
+ char *parms[4];
+ sym_t *symbols[4];
+#define EMIT(o, r, a, x, y) \
+ fle.code[pc*2] = ((x) << 10) | (y); \
+ fle.code[pc*2+1] = ((o) << 20) | ((r) << 10) | a;pc++
+#define EMIT_AUDIGY(o, r, a, x, y) \
+ fle.code[pc*2] = ((x) << 12) | (y); \
+ fle.code[pc*2+1] = ((o) << 24) | ((r) << 12) | a;pc++
+
+ int i, n = 0, nerr = 0;
+ int ninputs = 0;
+
+ s = iline;
+ p = line;
+
+ /* Remove all spaces and the trailing ')' */
+ while (*s)
+ {
+ if (*s != ' ' && *s != ')' && *s != '#')
+ *p++ = *s;
+ s++;
+ }
+
+ *p = 0;
+
+ /* Replace ',' and '(' characters by spaces */
+ p = line;
+ while (*p)
+ {
+ if (*p == ',' || *p == '(')
+ *p = ' ';
+ p++;
+ }
+
+ p = line;
+ while (*p && *p != ' ')
+ p++;
+ if (*p != ' ')
+ {
+ error ("Too few parameters");
+ return;
+ }
+
+ *p++ = 0;
+
+ while (*p && n < 4)
+ {
+ parms[n++] = p;
+
+ while (*p && *p != ' ')
+ p++;
+ if (*p == ' ')
+ *p++ = 0;
+ }
+
+ if (*p != 0)
+ {
+ error ("Too many parameters");
+ return;
+ }
+
+ if (n != 4)
+ {
+ error ("Too few parameters");
+ return;
+ }
+
+
+ for (i = 0; i < 4; i++)
+ {
+ if ((symbols[i] = find_symbol (parms[i])) == NULL)
+ {
+ fprintf (stderr, "%s\n", parms[i]);
+ nerr++;
+ error ("Undefined symbol");
+ continue;
+ }
+
+ if (symbols[i]->type == SY_INPUT)
+ ninputs++;
+
+ if (symbols[i]->type == SY_ACCUM && i != 1)
+ error ("Bad usage of 'accum' operand.");
+ }
+
+ if (nerr > 0)
+ return;
+
+ if (ninputs > 1)
+ {
+ error
+ ("Attempt to access more than one input GPRs by the same instruction");
+ }
+
+ for (i = 0; i < strlen (line); i++)
+ if (line[i] >= 'a' && line[i] <= 'z')
+ line[i] -= 32; /* Upshift */
+
+ for (i = 0; instructions[i].name != NULL; i++)
+ if (strcmp (line, instructions[i].name) == 0)
+ {
+#if 0
+ printf ("Instruction %x: ", instructions[i].opcode);
+ printf ("%03x ", symbols[0]->arg);
+ printf ("%03x ", symbols[1]->arg);
+ printf ("%03x ", symbols[2]->arg);
+ printf ("%03x ", symbols[3]->arg);
+ printf ("\n");
+#endif
+
+ if (is_audigy)
+ {
+ EMIT_AUDIGY (instructions[i].opcode,
+ symbols[0]->arg,
+ symbols[1]->arg, symbols[2]->arg, symbols[3]->arg);
+ }
+ else
+ {
+ EMIT (instructions[i].opcode,
+ symbols[0]->arg,
+ symbols[1]->arg, symbols[2]->arg, symbols[3]->arg);
+ }
+
+ return;
+ }
+
+ fprintf (stderr, "%s\n", line);
+ error ("Unrecognized instruction");
+}
+
+static void
+init_compiler (void)
+{
+ char tmp[100];
+ int i;
+
+ memset (&fle, 0, sizeof (fle));
+/*
+ * Initialize few predefined GPR parameter registers. These definitions
+ * have to be in sync with the GPR_* macros in <sblive.h>.
+ */
+
+/*
+;.parm AC97VOL 0 mono AC97
+;.parm AC97MONVU 0 stereopeak -
+;.parm PCM 100 stereo
+;.parm MAIN 100 stereo VOL
+;.parm VU 0 stereopeak -
+*/
+ add_symbol ("NULL", SY_DUMMY, gpr_base + ngpr++);
+ add_symbol ("NULL_", SY_DUMMY, gpr_base + ngpr++);
+ define_parm ("PCM", "PCM", 100, "stereo");
+ define_parm ("MAIN", "VOL", 100, "stereo");
+
+ pc = 0;
+
+ if (is_audigy)
+ {
+ /* Initialize the code array with NOPs (AUDIGY) */
+ for (i = 0; i < 512; i++)
+ {
+ fle.code[i * 2 + 0] = (0xc0 << 12) | 0xc0;
+ fle.code[i * 2 + 1] = (0x06 << 24) | (0xc0 << 12) | 0xc0;
+ }
+
+ for (i = 0; i < 32; i++)
+ {
+ sprintf (tmp, "fx%d", i);
+ add_symbol (tmp, SY_FX, i);
+ }
+ }
+ else
+ {
+ /* Initialize the code array with NOPs (LIVE) */
+ for (i = 0; i < 512; i++)
+ {
+ fle.code[i * 2 + 0] = 0x10040;
+ fle.code[i * 2 + 1] = 0x610040;
+ }
+
+ for (i = 0; i < 16; i++)
+ {
+ sprintf (tmp, "fx%d", i);
+ add_symbol (tmp, SY_FX, i);
+ }
+ }
+
+/*
+ * Constants
+ */
+
+ if (is_audigy)
+ {
+ /* Audigy symbols */
+ add_symbol ("0", SY_CONST, 0x0c0);
+ add_symbol ("1", SY_CONST, 0x0c1);
+ add_symbol ("2", SY_CONST, 0x0c2);
+ add_symbol ("3", SY_CONST, 0x0c3);
+ add_symbol ("4", SY_CONST, 0x0c4);
+ add_symbol ("8", SY_CONST, 0x0c5);
+ add_symbol ("16", SY_CONST, 0x0c6);
+ add_symbol ("32", SY_CONST, 0x0c7);
+ add_symbol ("256", SY_CONST, 0x0c8);
+ add_symbol ("65536", SY_CONST, 0x0c9);
+
+ add_symbol ("2048", SY_CONST, 0x0ca);
+ add_symbol ("0x800", SY_CONST, 0x0ca);
+
+ add_symbol ("2^28", SY_CONST, 0x0cb);
+ add_symbol ("0x10000000", SY_CONST, 0x0cb);
+
+ add_symbol ("2^29", SY_CONST, 0x0cc);
+ add_symbol ("0x20000000", SY_CONST, 0x0cc);
+
+ add_symbol ("2^30", SY_CONST, 0x0cd);
+ add_symbol ("0x40000000", SY_CONST, 0x0cd);
+
+ add_symbol ("2^31", SY_CONST, 0x0ce);
+ add_symbol ("0x80000000", SY_CONST, 0x0ce);
+
+ add_symbol ("0x7fffffff", SY_CONST, 0x0cf);
+
+ add_symbol ("0xffffffff", SY_CONST, 0x0d0);
+ add_symbol ("-1", SY_CONST, 0x0d0);
+
+ add_symbol ("0xfffffffe", SY_CONST, 0x0d1);
+ add_symbol ("-2", SY_CONST, 0x0d1);
+
+ add_symbol ("0xc0000000", SY_CONST, 0x0d2);
+
+ add_symbol ("0x4f1bbcdc", SY_CONST, 0x0d3);
+
+ add_symbol ("0x5a7ef9db", SY_CONST, 0x0d4);
+
+ add_symbol ("0x100000", SY_CONST, 0x0d5);
+
+ add_symbol ("accum", SY_ACCUM, 0x0d6);
+ add_symbol ("CCR", SY_CONST, 0x0d7);
+
+ add_symbol ("noise_L", SY_CONST, 0x0d8);
+ add_symbol ("noise_R", SY_CONST, 0x0d9);
+ add_symbol ("IRQREQ", SY_CONST, 0x0da);
+ }
+ else
+ {
+ /* SB Live symbols */
+ add_symbol ("0", SY_CONST, 0x040);
+ add_symbol ("1", SY_CONST, 0x041);
+ add_symbol ("2", SY_CONST, 0x042);
+ add_symbol ("3", SY_CONST, 0x043);
+ add_symbol ("4", SY_CONST, 0x044);
+ add_symbol ("8", SY_CONST, 0x045);
+ add_symbol ("16", SY_CONST, 0x046);
+ add_symbol ("32", SY_CONST, 0x047);
+ add_symbol ("256", SY_CONST, 0x048);
+ add_symbol ("65536", SY_CONST, 0x049);
+
+ add_symbol ("2^23", SY_CONST, 0x04a);
+ add_symbol ("0x80000", SY_CONST, 0x04a);
+
+ add_symbol ("2^28", SY_CONST, 0x04b);
+ add_symbol ("0x10000000", SY_CONST, 0x04b);
+
+ add_symbol ("2^29", SY_CONST, 0x04c);
+ add_symbol ("0x20000000", SY_CONST, 0x04c);
+
+ add_symbol ("2^30", SY_CONST, 0x04d);
+ add_symbol ("0x40000000", SY_CONST, 0x04d);
+
+ add_symbol ("2^31", SY_CONST, 0x04e);
+ add_symbol ("0x80000000", SY_CONST, 0x04e);
+
+ add_symbol ("0x7fffffff", SY_CONST, 0x04f);
+
+ add_symbol ("0xffffffff", SY_CONST, 0x050);
+ add_symbol ("-1", SY_CONST, 0x050);
+
+ add_symbol ("0xfffffffe", SY_CONST, 0x051);
+ add_symbol ("-2", SY_CONST, 0x051);
+
+ add_symbol ("accum", SY_ACCUM, 0x056);
+ add_symbol ("CCR", SY_CONST, 0x057);
+
+ add_symbol ("noise_L", SY_CONST, 0x058);
+ add_symbol ("noise_R", SY_CONST, 0x059);
+ add_symbol ("IRQREQ", SY_CONST, 0x05a);
+ }
+}
+
+static void
+produce_map (char *name)
+{
+ char fname[1024];
+ int i;
+ FILE *f;
+
+ sprintf (fname, "%s.map", name);
+
+ if ((f = fopen (fname, "w")) == NULL)
+ {
+ perror (fname);
+ return;
+ }
+
+ fprintf (f, "%d\n", fle.size);
+
+ for (i = 0; i < nsyms; i++)
+ {
+ fprintf (f, "%04x %x %s\n", symtab[i].arg, symtab[i].type,
+ symtab[i].name);
+ }
+
+ fclose (f);
+}
+
+static void
+produce_output (char *fname)
+{
+ int fd;
+
+ if ((fd = creat (fname, 0644)) == -1)
+ {
+ perror (fname);
+ exit (-1);
+ }
+
+ if (is_audigy)
+ {
+ fle.magic = EMU10K2_MAGIC;
+ fle.feature_mask = SB_AUDIGY;
+ }
+ else
+ {
+ fle.magic = EMU10K1_MAGIC;
+ fle.feature_mask = SB_LIVE;
+ }
+ fle.size = pc;
+ fprintf (stderr, "%d instructions out of 512 produced\n", pc);
+
+ if (write (fd, &fle, sizeof (fle)) != sizeof (fle))
+ {
+ perror (fname);
+ exit (-1);
+ }
+
+ close (fd);
+}
+
+int
+main (int argc, char *argv[])
+{
+ char iline[4096], line[4096], *p, *s, *outfile;
+
+ if (argc < 2)
+ {
+ fprintf (stderr, "No output\n");
+ exit (-1);
+ }
+
+ outfile = argv[1];
+
+ if (argc > 2)
+ if (strcmp (argv[2], "-2") == 0)
+ {
+ is_audigy = 1;
+ gpr_base = 0x400;
+ input_base = 0x40;
+ output_base = 0x60;
+ printf ("Compiling for SB Audigy\n");
+ }
+ else
+ printf ("Compiling for SB Live family\n");
+
+ init_compiler ();
+
+ while (agetline (iline, sizeof (iline) - 1) != NULL)
+ {
+ if (*iline == '#')
+ {
+ if (iline[1] == ' ')
+ if (sscanf (&iline[1], "%d", &lineno) == 1)
+ lineno--;
+ continue;
+ }
+/*
+ * Fix for HP-UX cpp. Strip all '#' characters from input.
+ */
+
+ s = iline;
+ p = line;
+
+ while (*s)
+ {
+ if (*s != '#')
+ *p++ = *s;
+ s++;
+ }
+ *p = 0;
+
+ p = line;
+
+ while (*p && *p != '\n' && *p != '/')
+ p++;
+ *p = 0;
+
+ if (*line == 0) /* Empty line */
+ continue;
+
+ p = &line[strlen (line) - 1];
+
+ while (p > line && (*p == ' ' || *p == '\t'))
+ p--;
+ p[1] = 0;
+
+ p = line;
+ while (*p)
+ {
+ if (*p == '\t')
+ *p = ' ';
+ p++;
+ }
+
+ p = line;
+
+ while (*p && (*p == ' ' || *p == '\t'))
+ p++;
+
+ if (*p == 0) /* Empty line */
+ continue;
+
+ if (*p == '.')
+ compile_directive (p);
+ else
+ compile_asm (p);
+ }
+
+ if (lineno < 1)
+ {
+ fprintf (stderr, "Empty input\n");
+ errors++;
+ }
+
+ if (errors == 0)
+ {
+ produce_output (outfile);
+ produce_map (outfile);
+ }
+
+ if (errors > 0)
+ {
+ fprintf (stderr, "%d errors - compile failed\n", errors);
+ exit (-1);
+ }
+
+ fprintf (stderr, "No errors detected - Output written to %s\n", outfile);
+ exit (0);
+}
diff --git a/tutorials/sndkit/sblive/emu10k.dsp b/tutorials/sndkit/sblive/emu10k.dsp
new file mode 100644
index 0000000..5bb6022
--- /dev/null
+++ b/tutorials/sndkit/sblive/emu10k.dsp
@@ -0,0 +1,378 @@
+//
+
+// DSP code for SB Live (C) 4Front Technologies 2001. All rights reserved.
+
+#define HAVE_EQ
+#define HAVE_SURR_EQ
+
+#define COPY(a,b) ACC3(a, b, 0, 0)
+
+// Temporary variables
+
+.gpr TMP_L
+.gpr TMP_R
+.gpr PCMTMP_L
+.gpr PCMTMP_R
+.gpr S_SUM_L
+.gpr S_SUM_R
+.gpr AUX_L
+.gpr AUX_R
+.gpr MIC_L
+.gpr MIC_R
+.gpr VUTMP
+.gpr VUTMP2
+.gpr CENTER_TMP
+.gpr LFE_TMP
+.gpr SURR_TMP_R
+.gpr SURR_TMP_L
+#ifdef AUDIGY
+.gpr SPDIF_TMP_L
+.gpr SPDIF_TMP_R
+#endif
+
+// Some parameters that are currently predefined by the assembler
+//.parm PCM 100 stereo
+//.parm MAIN 100 stereo VOL
+
+#ifdef HAVE_EQ
+#include "equalizer.mac"
+#endif
+
+// Monitor volumes and peak meters
+
+#ifdef HAVE_EQ
+.parm EQUALIZER 0 group
+ INCLUDE_EQPARMS
+#endif
+
+.parm FRONT 0 group
+.parm SPDINVOL 100 mono SPDIF
+.parm SPDINMONVU 0 stereopeak -
+.parm CDSPDINVOL 100 mono DIGCD
+.parm CDSPDINMONVU 0 stereopeak -
+.parm AC97VOL 0 mono AC97
+.parm AC97MONVU 0 stereopeak -
+.parm FRONTPCM 100 mono PCM
+.parm FRONTPCMVU 0 stereopeak -
+.parm FRONTAUX 100 mono AUX
+.parm FRONTAUXVU 0 stereopeak -
+#ifdef AUDIGY
+.parm FRONTMIC 100 mono MIC
+.parm FRONTMICVU 0 stereopeak -
+#endif
+.parm FRONTVOL 100 stereo VOL
+.parm FRONTVU 0 stereopeak -
+
+.parm SURR 0 group
+.parm R_SPDINVOL 0 mono SPDIF
+.parm R_SPDINMONVU 0 stereopeak -
+.parm R_CDSPDINVOL 0 mono DIGCD
+.parm R_CDSPDINMONVU 0 stereopeak -
+.parm R_AC97VOL 0 mono AC97
+.parm R_AC97MONVU 0 stereopeak -
+.parm SURRPCM 100 mono PCM
+.parm SURRPCMVU 0 stereopeak -
+.parm SURRAUX 0 mono AUX
+.parm SURRAUXVU 0 stereopeak -
+#ifdef AUDIGY
+.parm SURRMIC 100 mono MIC
+.parm SURRMICVU 0 stereopeak -
+#endif
+.parm SURRVOL 100 stereo VOL
+.parm SURRVU 0 stereopeak -
+
+// Recording volumes and VU meters
+.parm RECORD 0 group
+.parm SPDIFREC 100 mono SPDIF
+.parm SPDIFVU 0 stereopeak -
+.parm CDSPDIFREC 100 mono DIGCD
+.parm CDVU 0 stereopeak -
+.parm AC97REC 100 mono AC97
+.parm AC97VU 0 stereopeak -
+.parm LOOP 0 mono PCM
+.parm LOOPVU 0 stereopeak -
+.parm RECAUX 100 mono AUX
+.parm RECAUXVU 0 stereopeak -
+#ifdef AUDIGY
+.parm RECMIC 0 mono MIC
+.parm RECMICVU 0 stereopeak -
+#endif
+.parm RECVOL 100 stereo VOL
+.parm RECVU 0 stereopeak -
+
+// Dummy placeholders for the main VU meter.
+.gpr VU_L
+.gpr VU_R
+
+#ifdef AUDIGY
+#include "emu10k2.mac"
+#else
+#include "emu10k1.mac"
+#endif
+
+#ifdef AUDIGY
+.parm _PASSTHROUGH 0 onoff;
+#endif
+
+#ifdef HAVE_EQ
+ INCLUDE_EQVARS
+ INCLUDE_DELAYS(L)
+ INCLUDE_DELAYS(R)
+# ifdef HAVE_SURR_EQ
+ INCLUDE_DELAYS(SL)
+ INCLUDE_DELAYS(SR)
+# endif
+#endif
+
+#include "vu.mac"
+
+#ifdef AUDIGY
+// Take wavetable engine front sends (0,1)
+ MACINTS(TMP_L, 0, SEND_L, 1)
+ MACINTS(TMP_R, 0, SEND_R, 1)
+ MACS(PCMTMP_L, 0, TMP_L, PCM_L)
+ MACS(PCMTMP_R, 0, TMP_R, PCM_R)
+
+// Take raw S/PDIF from the dedicated sends
+ COPY(SPDIF_TMP_L, SPDIF_L)
+ COPY(SPDIF_TMP_R, SPDIF_R)
+
+// Do the same for surround and rear speaker outputs (sum them together)
+ SUM(S_SUM_L, SEND_SL, SEND_RL, 0)
+ SUM(S_SUM_R, SEND_SR, SEND_RR, 0)
+ MACINTS(CENTER_TMP, 0, SEND_C, 1)
+ MACINTS(LFE_TMP, 0, SEND_W, 1)
+ MACS(S_SUM_L, 0, S_SUM_L, PCM_L)
+ MACS(S_SUM_R, 0, S_SUM_R, PCM_R)
+#else
+// Take wavetable engine front sends (0,1) and multiply them by 4 to boost volume
+ MACINTS(TMP_L, 0, SEND_L, 4)
+ MACINTS(TMP_R, 0, SEND_R, 4)
+ MACS(PCMTMP_L, 0, TMP_L, PCM_L)
+ MACS(PCMTMP_R, 0, TMP_R, PCM_R)
+
+// Do the same for rear outputs (2,3)
+ MACINTS(S_SUM_L, 0, SEND_SL, 4)
+ MACINTS(S_SUM_R, 0, SEND_SR, 4)
+ MACS(S_SUM_L, 0, S_SUM_L, PCM_L)
+ MACS(S_SUM_R, 0, S_SUM_R, PCM_R)
+#endif
+
+//
+// Auxiliary input monitors (front channel)
+//
+
+ MACS(AUX_L, 0, PCMTMP_L, FRONTPCM)
+ MACS(AUX_R, 0, PCMTMP_R, FRONTPCM)
+ VU(FRONTPCMVU_L, AUX_L)
+ VU(FRONTPCMVU_R, AUX_R)
+ MACS(AUX_L, AUX_L, CDSPDIFIN_L, CDSPDINVOL)
+ MACS(AUX_R, AUX_R, CDSPDIFIN_R, CDSPDINVOL)
+ // CD S/PDIF input monitor VU
+ VUSCALE(CDSPDINMONVU_L, CDSPDIFIN_L, CDSPDINVOL)
+ VUSCALE(CDSPDINMONVU_R, CDSPDIFIN_R, CDSPDINVOL)
+
+ MACS(AUX_L, AUX_L, AC97IN_L, AC97VOL)
+ MACS(AUX_R, AUX_R, AC97IN_R, AC97VOL)
+ // AC97 monitor VU
+ VUSCALE(AC97MONVU_L, AC97IN_L, AC97VOL)
+ VUSCALE(AC97MONVU_R, AC97IN_R, AC97VOL)
+
+ MACS(AUX_L, AUX_L, GPSPDIFIN_L, SPDINVOL)
+ MACS(AUX_R, AUX_R, GPSPDIFIN_R, SPDINVOL)
+ // S/PDIF input monitor VU
+ VUSCALE(SPDINMONVU_L, GPSPDIFIN_L, SPDINVOL)
+ VUSCALE(SPDINMONVU_R, GPSPDIFIN_R, SPDINVOL)
+ // Boost S/PDIF input level by summing it twice
+ MACS(AUX_L, AUX_L, GPSPDIFIN_L, SPDINVOL)
+ MACS(AUX_R, AUX_R, GPSPDIFIN_R, SPDINVOL)
+
+ // Auxiliary inputs (Live Drive)
+ MACS(AUX_L, AUX_L, AUXIN_L, FRONTAUX)
+ MACS(AUX_R, AUX_R, AUXIN_R, FRONTAUX)
+ VUSCALE(FRONTAUXVU_L, AUXIN_L, FRONTAUX)
+ VUSCALE(FRONTAUXVU_R, AUXIN_R, FRONTAUX)
+
+// Apply main volume to the aux input front volume
+ MACS(AUX_L, 0, AUX_L, MAIN_L)
+ MACS(AUX_R, 0, AUX_R, MAIN_R)
+
+#ifdef AUDIGY
+ // Livedrive mic inputs
+ MACS(MIC_L, MIC_L, MICIN_L, FRONTMIC)
+ MACS(MIC_R, MIC_R, MICIN_R, FRONTMIC)
+ VUSCALE(FRONTMICVU_L, MICIN_L, FRONTMIC)
+ VUSCALE(FRONTMICVU_R, MICIN_R, FRONTMIC)
+
+// Apply main volume to the aux input front volume
+ MACS(MIC_L, 0, AUX_L, MAIN_L)
+ MACS(MIC_R, 0, AUX_R, MAIN_R)
+#endif
+
+#ifdef HAVE_EQ
+ EQUALIZER(L, L, AUX_L)
+ EQUALIZER(R, R, AUX_R)
+#endif
+
+// Secondary sends before applying front volume
+#ifdef AUDIGY
+ MACINTS(SPDOUT1_L, 0, AUX_L, _PASSTHROUGH_OFF)
+ MACINTS(SPDOUT1_R, 0, AUX_R, _PASSTHROUGH_OFF)
+
+// S/PDIF passthrough
+.const 0x1008 0x1008
+.const 0xffff0000 0xffff0000
+.gpr TMP0
+.gpr TMP1
+.gpr TMP2
+
+// Left channel
+ COPY(TMP2, SPDIF_TMP_L)
+ SKIP(CCR, CCR, 0x1008, 1)
+ ACC3(TMP2, 0, 65536, TMP2)
+ ANDXOR(TMP2, TMP2, 0xffff0000, 0)
+ MACINTS(TMP0, 0, TMP2, _PASSTHROUGH_ON)
+ ANDXOR(TMP1, _PASSTHROUGH_ON, 1, 1)
+ MACINTS(TMP1, 0, AUX_L, TMP1)
+ ACC3(SPDOUT1_L, TMP0, TMP1, 0)
+
+// Right channel
+ COPY(TMP2, SPDIF_TMP_R)
+ SKIP(CCR, CCR, 0x1008, 1)
+ ACC3(TMP2, 0, 65536, TMP2)
+ ANDXOR(TMP2, TMP2, 0xffff0000, 0)
+ MACINTS(TMP0, 0, TMP2, _PASSTHROUGH_ON)
+ ANDXOR(TMP1, _PASSTHROUGH_ON, 1, 1)
+ MACINTS(TMP1, 0, AUX_R, TMP1)
+ ACC3(SPDOUT1_R, TMP0, TMP1, 0)
+#else
+ COPY(SPDOUT1_L, AUX_L)
+ COPY(SPDOUT1_R, AUX_R)
+#endif
+
+// Update main peak meters
+ VU(VU_L, AUX_L)
+ VU(VU_R, AUX_R)
+
+// Mix the PCM sends with monitor inputs in and write to front output
+ MACS(FRONT_L, 0, AUX_L, FRONTVOL_L)
+ MACS(FRONT_R, 0, AUX_R, FRONTVOL_R)
+
+ MACS(HEADPH_L, 0, AUX_L, FRONTVOL_L)
+ MACS(HEADPH_R, 0, AUX_R, FRONTVOL_R)
+ VUSCALE(FRONTVU_L, AUX_L, FRONTVOL_L)
+ VUSCALE(FRONTVU_R, AUX_R, FRONTVOL_R)
+
+//
+// Auxiliary input monitors (rear channel)
+//
+
+ MACS(S_SUM_L, 0, S_SUM_L, SURRPCM)
+ MACS(S_SUM_R, 0, S_SUM_R, SURRPCM)
+ VU(SURRPCMVU_L, S_SUM_L)
+ VU(SURRPCMVU_R, S_SUM_R)
+ MACS(S_SUM_L, S_SUM_L, CDSPDIFIN_L, R_CDSPDINVOL)
+ MACS(S_SUM_R, S_SUM_R, CDSPDIFIN_R, R_CDSPDINVOL)
+ // CD S/PDIF input monitor VU
+ VUSCALE(R_CDSPDINMONVU_L, CDSPDIFIN_L, R_CDSPDINVOL)
+ VUSCALE(R_CDSPDINMONVU_R, CDSPDIFIN_R, R_CDSPDINVOL)
+
+ MACS(S_SUM_L, S_SUM_L, AC97IN_L, R_AC97VOL)
+ MACS(S_SUM_R, S_SUM_R, AC97IN_R, R_AC97VOL)
+ // AC97 monitor VU
+ VUSCALE(R_AC97MONVU_L, AC97IN_L, R_AC97VOL)
+ VUSCALE(R_AC97MONVU_R, AC97IN_R, R_AC97VOL)
+
+ MACS(S_SUM_L, S_SUM_L, GPSPDIFIN_L, R_SPDINVOL)
+ MACS(S_SUM_R, S_SUM_R, GPSPDIFIN_R, R_SPDINVOL)
+ // S/PDIF input monitor VU
+ VUSCALE(R_SPDINMONVU_L, GPSPDIFIN_L, R_SPDINVOL)
+ VUSCALE(R_SPDINMONVU_R, GPSPDIFIN_R, R_SPDINVOL)
+
+ MACS(S_SUM_L, S_SUM_L, AUXIN_L, SURRAUX)
+ MACS(S_SUM_R, S_SUM_R, AUXIN_R, SURRAUX)
+ VUSCALE(SURRAUXVU_L, AUXIN_L, SURRAUX)
+ VUSCALE(SURRAUXVU_R, AUXIN_R, SURRAUX)
+#ifdef AUDIGY
+ MACS(S_SUM_L, S_SUM_L, MICIN_L, SURRMIC)
+ MACS(S_SUM_R, S_SUM_R, MICIN_R, SURRMIC)
+ VUSCALE(SURRMICVU_L, MICIN_L, SURRMIC)
+ VUSCALE(SURRMICVU_R, MICIN_R, SURRMIC)
+#endif
+#if 0
+ // Boost S/PDIF input level by summing it twice
+ MACS(S_SUM_L, S_SUM_L, GPSPDIFIN_L, R_SPDINVOL)
+ MACS(S_SUM_R, S_SUM_R, GPSPDIFIN_R, R_SPDINVOL)
+#endif
+
+ MACS(TMP_L, 0, S_SUM_L, MAIN_L)
+ MACS(TMP_R, 0, S_SUM_R, MAIN_R)
+#if defined(HAVE_EQ) && defined(HAVE_SURR_EQ)
+ EQUALIZER(L, SL, TMP_L)
+ EQUALIZER(R, SR, TMP_R)
+#endif
+ MACS(SURR_L, 0, TMP_L, SURRVOL_L)
+ MACS(SURR_R, 0, TMP_R, SURRVOL_R)
+
+#ifdef AUDIGY
+ MACS(DSURR_L, 0, TMP_L, SURRVOL_L)
+ MACS(DSURR_R, 0, TMP_R, SURRVOL_R)
+#else
+ MACS(AC97SURR_L, 0, TMP_L, SURRVOL_L)
+ MACS(AC97SURR_R, 0, TMP_R, SURRVOL_R)
+ MACINTS(CENTER_TMP, 0, SEND_C, 1)
+ MACINTS(LFE_TMP, 0, SEND_W, 1)
+
+#endif
+ VUSCALE(SURRVU_L, TMP_L, SURRVOL_L)
+ VUSCALE(SURRVU_R, TMP_R, SURRVOL_R)
+
+//
+// Misc outputs
+//
+
+ SUM(DCENTER, CENTER_TMP, 0, 0)
+ SUM(DLFE, LFE_TMP, 0, 0)
+ SUM(ACENTER, CENTER_TMP, 0, 0)
+ SUM(ALFE, LFE_TMP, 0, 0)
+
+//
+// Recording sources and VU meters
+//
+
+ MACS(TMP_L, 0, AC97IN_L, AC97REC)
+ MACS(TMP_R, 0, AC97IN_R, AC97REC)
+ VUSCALE(AC97VU_L, AC97IN_L, AC97REC)
+ VUSCALE(AC97VU_R, AC97IN_R, AC97REC)
+
+ MACS(TMP_L, TMP_L, GPSPDIFIN_L, SPDIFREC)
+ MACS(TMP_R, TMP_R, GPSPDIFIN_R, SPDIFREC)
+ VUSCALE(SPDIFVU_L, GPSPDIFIN_L, SPDIFREC)
+ VUSCALE(SPDIFVU_R, GPSPDIFIN_R, SPDIFREC)
+
+ MACS(TMP_L, TMP_L, CDSPDIFIN_L, CDSPDIFREC)
+ MACS(TMP_R, TMP_R, CDSPDIFIN_R, CDSPDIFREC)
+ VUSCALE(CDVU_L, CDSPDIFIN_L, CDSPDIFREC)
+ VUSCALE(CDVU_R, CDSPDIFIN_R, CDSPDIFREC)
+
+ MACS(TMP_L, TMP_L, PCMTMP_L, LOOP)
+ MACS(TMP_R, TMP_R, PCMTMP_R, LOOP)
+ VUSCALE(LOOPVU_L, PCMTMP_L, LOOP)
+ VUSCALE(LOOPVU_R, PCMTMP_R, LOOP)
+
+ MACS(TMP_L, TMP_L, AUXIN_L, RECAUX)
+ MACS(TMP_R, TMP_R, AUXIN_R, RECAUX)
+ VUSCALE(RECAUXVU_L, AUXIN_L, RECAUX)
+ VUSCALE(RECAUXVU_R, AUXIN_R, RECAUX)
+
+#ifdef AUDIGY
+ MACS(TMP_L, TMP_L, MICIN_L, RECMIC)
+ MACS(TMP_R, TMP_R, MICIN_R, RECMIC)
+ VUSCALE(RECMICVU_L, MICIN_L, RECMIC)
+ VUSCALE(RECMICVU_R, MICIN_R, RECMIC)
+#endif
+
+ MACS(ADC_L, 0, TMP_L, RECVOL_L)
+ MACS(ADC_R, 0, TMP_R, RECVOL_R)
+ VUSCALE(RECVU_L, TMP_L, RECVOL_L)
+ VUSCALE(RECVU_R, TMP_R, RECVOL_R)
diff --git a/tutorials/sndkit/sblive/emu10k1.mac b/tutorials/sndkit/sblive/emu10k1.mac
new file mode 100644
index 0000000..157bcad
--- /dev/null
+++ b/tutorials/sndkit/sblive/emu10k1.mac
@@ -0,0 +1,51 @@
+// Constants for EMU 10k1 (SB Live)
+// Sends
+
+.send SEND_L 0
+.send SEND_R 1
+.send SEND_SL 2
+.send SEND_SR 3
+.send SEND_C 4
+.send SEND_W 5
+.send SEND_RL 6
+.send SEND_RR 7
+
+// NOTE! 5 and 6 reserved for S/PDIF (AC3) passthrough with Audigy
+
+// Inputs
+
+.input AC97IN_L 0
+.input AC97IN_R 1
+.input CDSPDIFIN_L 2
+.input CDSPDIFIN_R 3
+.input ZVIN_L 4
+.input ZVIN_R 5
+.input GPSPDIFIN_L 6 // TOSLink
+.input GPSPDIFIN_R 7
+.input MICIN_L 8 // LiveDrive (Line/Mic In 1)
+.input MICIN_R 9
+.input CXSPDIFIN_L 10 // LiveDrive (Coax S/PDIF input)
+.input CXSPDIFIN_R 11
+.input AUXIN_L 12 // LiveDrive (Line/Mic 2)
+.input AUXIN_R 13
+
+// Outputs
+
+.output FRONT_L 0
+.output FRONT_R 1
+.output SPDOUT1_L 2
+.output SPDOUT1_R 3
+.output DCENTER 4 // Digital Center channel
+.output DLFE 5 // Digital LFE
+.output HEADPH_L 6 // LiveDrive headphone out
+.output HEADPH_R 7
+.output SURR_L 8 // Rear output
+.output SURR_R 9
+.output ADC_L 10 // Send to the ADC recording channel
+.output ADC_R 11
+.output MICREC 12 // Send to the microphone recording buffer
+.output AC97SURR_L 13 // AC97 Surround L
+.output AC97SURR_R 14 // AC97 Surround R
+
+.output ACENTER 17 // Analog center channel
+.output ALFE 18 // Analog LFE output
diff --git a/tutorials/sndkit/sblive/emu10k2.mac b/tutorials/sndkit/sblive/emu10k2.mac
new file mode 100644
index 0000000..851f9c2
--- /dev/null
+++ b/tutorials/sndkit/sblive/emu10k2.mac
@@ -0,0 +1,71 @@
+// Constants for EMU 10k2 (SB Audigy)
+// Sends
+
+.send SEND_L 0
+.send SEND_R 1
+.send SEND_SL 2
+.send SEND_SR 3
+.send SEND_C 4
+.send SEND_W 5
+.send SEND_RL 6
+.send SEND_RR 7
+
+// Raw S/PDIF output stream
+
+.send SPDIF_L 20
+.send SPDIF_R 21
+
+// Inputs
+
+.input AC97IN_L 0
+.input AC97IN_R 1
+.input CDSPDIFIN_L 2
+.input CDSPDIFIN_R 3
+.input GPSPDIFIN_L 4 // TOSLink
+.input GPSPDIFIN_R 5
+.input CXSPDIFIN_L 6 // LiveDrive (Coax S/PDIF input)
+.input CXSPDIFIN_R 7
+.input MICIN_L 8 // LiveDrive (Line/Mic In 1)
+.input MICIN_R 9
+.input ANACD_L 10 // Analog CD input?
+.input ANACD_R 11
+.input AUXIN_L 12 // LiveDrive (Line/Mic 2)
+.input AUXIN_R 13
+
+// Outputs
+/*
+audigy's Mic In is not connected to AC97;
+known Audigy output map:
+[digital/analog]:
+0x60, 0x68 - front left
+0x61, 0x69 - front right
+0x66, 0x6e - rear left
+0x67, 0x6f - rear right
+0x62, 0x6a - center
+0x63, 0x6b - subwoofer
+
+0x7c,0x7d - in Creative DSP code this is the same as Digital Front; can be
+LiveDrive SPDIF out
+*/
+
+.output SPDOUT1_L 0
+.output SPDOUT1_R 1
+.output DCENTER 2 // Digital Center channel
+.output DLFE 3 // Digital LFE
+.output HEADPH_L 4 // LiveDrive headphone out
+.output HEADPH_R 5
+.output DSURR_L 6 // Surround output
+.output DSURR_R 7
+.output FRONT_L 8
+.output FRONT_R 9
+.output ACENTER 10 // Analog center channel
+.output ALFE 11 // Analog LFE output
+.output SURR_L 14 // Surround output
+.output SURR_R 15
+.output AC97_L 16 // Send to the AC97 front channel
+.output AC97_R 17
+.output ADC_L 22 // Send to the ADC recording channel
+.output ADC_R 23
+.output MICREC_L 24 // Send to the microphone recording buffer
+.output MICREC_R 25 // ??????? (maybe not in use at all)
+
diff --git a/tutorials/sndkit/sblive/emuasm1 b/tutorials/sndkit/sblive/emuasm1
new file mode 100755
index 0000000..26629df
--- /dev/null
+++ b/tutorials/sndkit/sblive/emuasm1
@@ -0,0 +1,3 @@
+#!/bin/sh
+PATH=/opt/langtools/lbin:/usr/local/lib/gcc-lib/sparc-sun-solaris2.6/2.8.1:$PATH
+cpp < $1|./asm10k `basename $1 .dsp`.bin1 -1
diff --git a/tutorials/sndkit/sblive/emuasm2 b/tutorials/sndkit/sblive/emuasm2
new file mode 100755
index 0000000..554c8c7
--- /dev/null
+++ b/tutorials/sndkit/sblive/emuasm2
@@ -0,0 +1,3 @@
+#!/bin/sh
+PATH=/opt/langtools/lbin:/usr/local/lib/gcc-lib/sparc-sun-solaris2.6/2.8.1:$PATH
+cpp -DAUDIGY < $1|./asm10k `basename $1 .dsp`.bin2 -2
diff --git a/tutorials/sndkit/sblive/equalizer.mac b/tutorials/sndkit/sblive/equalizer.mac
new file mode 100644
index 0000000..f9498ac
--- /dev/null
+++ b/tutorials/sndkit/sblive/equalizer.mac
@@ -0,0 +1,117 @@
+//
+// Equalizer macros.
+//
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+
+#if 0
+// Constant 50% (128) gain tables
+#define INCLUDE_EQPARMS \
+.const EQb1_CST_0 0x10000000;\
+.const EQb1_CST_1 0xE042DE20;\
+.const EQb1_CST_2 0x1FBD21E0;\
+.const EQb1_CST_3 0x0FBD8640;\
+.const EQb1_CST_4 0xF04279C0;\
+.const EQb2_CST_0 0x10000000;\
+.const EQb2_CST_1 0xE2E5DDC0;\
+.const EQb2_CST_2 0x1D1A2240;\
+.const EQb2_CST_3 0x0D48B550;\
+.const EQb2_CST_4 0xF2B74AB0;\
+.const EQb3_CST_0 0x10000000;\
+.const EQb3_CST_1 0xEC7021C0;\
+.const EQb3_CST_2 0x138FDE40;\
+.const EQb3_CST_3 0x069696A0;\
+.const EQb3_CST_4 0xF9696960;\
+.const EQb4_CST_0 0x10000000;\
+.const EQb4_CST_1 0x094B0F90;\
+.const EQb4_CST_2 0xF6B4F070;\
+.const EQb4_CST_3 0x02961F24;\
+.const EQb4_CST_4 0xFD69E0DC;\
+.parm BYPASS 0 onoff ;
+#else
+#define INCLUDE_EQPARMS \
+.parm EQ_GAIN 100 mono PRESCALE;\
+.parm EQb1 128 eq1 LO;\
+.parm EQb2 128 eq2 MID;\
+.parm EQb3 128 eq3 HI;\
+.parm EQb4 128 eq4 XHI;\
+.parm BYPASS 0 onoff ;
+#endif
+
+#define INCLUDE_EQVARS \
+.gpr eq_save;\
+.gpr eq_value;\
+.const 0x1000 0x1000;\
+.const 44 I44;\
+.const EQ_ADJUST I8;\
+.const EQ_AFTERSCALE I32;\
+.const EQ_PRESCALE 0.03125 /* 1/32 */
+
+#define INCLUDE_DELAYS(PREFIX) \
+.gpr PREFIX##b1_x0;\
+.gpr PREFIX##b1_x1;\
+.gpr PREFIX##b1_x2;\
+.gpr PREFIX##b1_y0;\
+.gpr PREFIX##b1_y1;\
+.gpr PREFIX##b1_y2;\
+.gpr PREFIX##b2_x0;\
+.gpr PREFIX##b2_x1;\
+.gpr PREFIX##b2_x2;\
+.gpr PREFIX##b2_y0;\
+.gpr PREFIX##b2_y1;\
+.gpr PREFIX##b2_y2;\
+.gpr PREFIX##b3_x0;\
+.gpr PREFIX##b3_x1;\
+.gpr PREFIX##b3_x2;\
+.gpr PREFIX##b3_y0;\
+.gpr PREFIX##b3_y1;\
+.gpr PREFIX##b3_y2;\
+.gpr PREFIX##b4_x0;\
+.gpr PREFIX##b4_x1;\
+.gpr PREFIX##b4_x2;\
+.gpr PREFIX##b4_y0;\
+.gpr PREFIX##b4_y1;\
+.gpr PREFIX##b4_y2
+
+#define MOVE(a,b) ACC3(b, a, 0, 0)
+
+#define PASS_BAND(N, BAND)\
+\
+ MOVE(N##_x1,N##_x2);\
+ MOVE(N##_x0,N##_x1);\
+ MOVE(eq_value, N##_x0);\
+\
+ MOVE(N##_y1,N##_y2);\
+ MOVE(N##_y0,N##_y1);\
+ MOVE(0,N##_y0);\
+\
+ MACS(N##_y0, 0, N##_x0,EQ##BAND##_CST_0);\
+ MACS(N##_y0, N##_y0, N##_x1,EQ##BAND##_CST_1);\
+ MACS(N##_y0, N##_y0, N##_y1,EQ##BAND##_CST_2);\
+ MACS(N##_y0, N##_y0, N##_x2,EQ##BAND##_CST_3);\
+ MACS(N##_y0, N##_y0, N##_y2,EQ##BAND##_CST_4);\
+\
+ MACINTS(N##_y0, 0, N##_y0, EQ_ADJUST);\
+ ACC3(eq_value, N##_y0, 0, 0)
+
+#define EQUALIZER(SUFFIX, PREFIX, INOUT) \
+ ACC3(eq_value, INOUT, 0, 0); \
+ ACC3(eq_save, INOUT, 0, 0); \
+ MACS(eq_value, 0, eq_value, EQ_GAIN);\
+ MACS(eq_value, 0, eq_value, EQ_PRESCALE);\
+ PASS_BAND(PREFIX##b4, b4); \
+ PASS_BAND(PREFIX##b3, b3); \
+ PASS_BAND(PREFIX##b2, b2); \
+ PASS_BAND(PREFIX##b1, b1); \
+ MACINTS(eq_value, 44, eq_value, EQ_AFTERSCALE);\
+ MACINTS(INOUT, 0, eq_value, BYPASS_OFF);\
+ MACINTS(INOUT, INOUT, eq_save, BYPASS_ON)
diff --git a/tutorials/sndkit/sblive/ld10k.c b/tutorials/sndkit/sblive/ld10k.c
new file mode 100644
index 0000000..d01dc2c
--- /dev/null
+++ b/tutorials/sndkit/sblive/ld10k.c
@@ -0,0 +1,220 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/ioctl.h>
+
+#include <soundcard.h>
+#ifdef USERLAND
+#define oss_native_word unsigned long
+#define oss_mutex_t unsigned long
+#define oss_device_t unsigned long
+#define ac97_devc unsigned long
+#define oss_midi_inputbyte_t char
+#define uart401_devc unsigned long
+typedef int oss_mutex;
+typedef void *oss_dma_handle_t;
+#else
+#include "../../../kernel/framework/include/os.h"
+#endif /* USERLAND */
+#include "../../../kernel/drv/oss_sblive/sblive.h"
+
+int work_done = 0;
+int ossfd;
+emu10k1_file fle;
+
+static int
+find_last_device (void)
+{
+ oss_sysinfo info;
+ int mixerfd;
+
+ if ((mixerfd = open ("/dev/mixer0", O_RDWR, 0)) == -1)
+ {
+ perror ("/dev/mixer0");
+ return 0;
+ }
+
+ if (ioctl (mixerfd, SNDCTL_SYSINFO, &info) == -1)
+ {
+ perror ("SNDCTL_SYSINFO");
+ exit (-1);
+ }
+
+ close (mixerfd);
+ return info.numaudios - 1;
+}
+
+static void
+touch_all_mixers (int ossfd)
+{
+ int i, n;
+
+ for (i = 0; i < 16; i++)
+ {
+ n = i;
+ if (ioctl (ossfd, SNDCTL_MIX_NREXT, &n) == -1)
+ return;
+ }
+}
+
+static void
+do_download (void)
+{
+ int card_type;
+
+ if (ioctl (ossfd, SBLIVE_GETCHIPTYPE, &card_type) == -1)
+ {
+ perror ("???");
+ return;
+ }
+
+ if (card_type != fle.feature_mask)
+ {
+ fprintf (stderr, "Device/file incompatibility (%x/%x)\n", card_type,
+ fle.feature_mask);
+ return;
+ }
+
+ if (ioctl (ossfd, SBLIVE_WRITECODE1, fle.code) == -1)
+ {
+ perror ("code1");
+ return;
+ }
+
+ if (ioctl (ossfd, SBLIVE_WRITECODE2, fle.code + 512) == -1)
+ {
+ perror ("code2");
+ return;
+ }
+
+ if (fle.parms.ngpr > 0)
+ {
+ if (ioctl (ossfd, SBLIVE_WRITEPARMS, &fle.parms) == -1)
+ {
+ perror ("parms");
+ return;
+ }
+
+ touch_all_mixers (ossfd);
+ }
+
+ if (fle.consts.nconst > 0)
+ {
+ if (ioctl (ossfd, SBLIVE_WRITECONST, &fle.consts) == -1)
+ {
+ perror ("consts");
+ return;
+ }
+
+ }
+ work_done = 1;
+}
+
+static void
+do_all_devices (void)
+{
+ int i, mixerfd;
+ oss_sysinfo info;
+ char dspname[100];
+
+ if ((mixerfd = open ("/dev/mixer0", O_RDWR, 0)) == -1)
+ {
+ perror ("/dev/mixer");
+ exit (-1);
+ }
+
+ if (ioctl (mixerfd, SNDCTL_SYSINFO, &info) == -1)
+ {
+ perror ("SNDCTL_SYSINFO");
+ exit (-1);
+ }
+
+ for (i = 0; i < info.numaudios; i++)
+ {
+ oss_audioinfo ainfo;
+ int acc;
+
+ ainfo.dev = i;
+ if (ioctl (mixerfd, SNDCTL_AUDIOINFO, &ainfo) == -1)
+ {
+ perror ("SNDCTL_AUDIOINFO");
+ exit (-1);
+ }
+
+ if (ainfo.magic == fle.magic)
+ {
+
+ sprintf (dspname, "/dev/dsp%d", i);
+ if ((ossfd = open (dspname, O_RDWR, 0)) == -1)
+ {
+ perror (dspname);
+ exit (-1);
+ }
+
+ do_download ();
+ close (ossfd);
+ }
+ }
+ close (mixerfd);
+}
+
+int
+main (int argc, char *argv[])
+{
+ int fd, dspnum = 0;
+ char *dspname = "/dev/dsp", namebuf[32];
+
+ if (argc < 2)
+ {
+ fprintf (stderr, "Usage: %s dspcodefile\n", argv[0]);
+ exit (-1);
+ }
+
+ if ((fd = open (argv[1], O_RDONLY, 0)) == -1)
+ {
+ perror (argv[1]);
+ exit (-1);
+ }
+
+ if (argc > 2)
+ dspname = argv[2];
+
+ if (read (fd, &fle, sizeof (fle)) != sizeof (fle))
+ {
+ fprintf (stderr, "Short file\n");
+ exit (-1);
+ }
+
+ if (fle.magic != EMU10K1_MAGIC && fle.magic != EMU10K2_MAGIC)
+ {
+ fprintf (stderr, "Bad microcode file %s\n", argv[1]);
+ exit (-1);
+ }
+
+ if (strcmp (dspname, "-a") == 0)
+ {
+ do_all_devices ();
+ exit (0);
+ }
+
+ if (strcmp (dspname, "-l") == 0)
+ { /* Last /dev/dsp# file requested */
+ dspnum = find_last_device ();
+ sprintf (namebuf, "/dev/dsp%d", dspnum);
+ dspname = namebuf;
+ }
+
+ if ((ossfd = open (dspname, O_RDWR, 0)) == -1)
+ {
+ perror (dspname);
+ exit (-1);
+ }
+
+ do_download ();
+
+ if (!work_done)
+ exit (-1);
+ exit (0);
+}
diff --git a/tutorials/sndkit/sblive/mkheader.c b/tutorials/sndkit/sblive/mkheader.c
new file mode 100644
index 0000000..4081a56
--- /dev/null
+++ b/tutorials/sndkit/sblive/mkheader.c
@@ -0,0 +1,37 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+int
+main (int argc, char *argv[])
+{
+ unsigned char buffer[1024];
+ int i, l, n = 0;
+
+ if (argc != 2)
+ {
+ fprintf (stderr, "Bad usage\n");
+ exit (-1);
+ }
+
+ printf ("static unsigned char %s[] =\n", argv[1]);
+ printf ("{\n");
+
+ while ((l = read (0, buffer, sizeof (buffer))) > 0)
+ {
+ for (i = 0; i < l; i++)
+ {
+ if (n > 0)
+ printf (", ");
+
+ if (n && (n % 16) == 0)
+ printf ("\n");
+ printf ("0x%02x", buffer[i]);
+ n++;
+ }
+ }
+
+ printf ("\n};\n");
+
+ exit (0);
+}
diff --git a/tutorials/sndkit/sblive/null.dsp b/tutorials/sndkit/sblive/null.dsp
new file mode 100644
index 0000000..ccba794
--- /dev/null
+++ b/tutorials/sndkit/sblive/null.dsp
@@ -0,0 +1,23 @@
+#include "emu10k2.mac"
+
+.gpr AUX_L
+.gpr AUX_R
+.gpr SPDIF_TMP_L
+.gpr SPDIF_TMP_R
+.parm _PASSTHROUGH 0 onoff;
+
+ACC3(AUX_L, SEND_L, 0, 0)
+ACC3(FRONT_L, AUX_L, 0, 0)
+
+ACC3(AUX_R, SEND_L, 0, 0)
+ACC3(FRONT_R, AUX_L, 0, 0)
+
+ACC3(SURR_R, 0, 0, 0)
+ACC3(SURR_L, 0, 0, 0)
+ACC3(SPDIF_TMP_L, SPDIF_L, 0, 0)
+ACC3(SPDIF_TMP_R, SPDIF_R, 0, 0)
+
+ MACINTS(SPDOUT1_L, 0, AUX_L, _PASSTHROUGH_OFF)
+ MACINTS(SPDOUT1_R, 0, AUX_R, _PASSTHROUGH_OFF)
+ MACINTS(SPDOUT1_L, SPDOUT1_L, SPDIF_TMP_L, _PASSTHROUGH_ON)
+ MACINTS(SPDOUT1_R, SPDOUT1_R, SPDIF_TMP_R, _PASSTHROUGH_ON)
diff --git a/tutorials/sndkit/sblive/testgen.c b/tutorials/sndkit/sblive/testgen.c
new file mode 100644
index 0000000..e41d2b7
--- /dev/null
+++ b/tutorials/sndkit/sblive/testgen.c
@@ -0,0 +1,92 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#define N 64
+
+static void
+gen_inputtest (void)
+{
+ int i;
+
+ printf ("// Input test program\n");
+ printf ("#include \"vu.mac\"\n");
+ printf (".gpr VUTMP\n");
+ printf (".gpr TMP\n");
+
+ for (i = 0; i < N; i += 2)
+ {
+ printf (".input I%d_L %d\n", i, i);
+ printf (".input I%d_R %d\n", i, i + 1);
+ }
+
+ for (i = 0; i < N; i += 2)
+ {
+ if (!(i % 16))
+ printf (".parm G%x 0 group\n", i);
+ printf (".parm IVU%d 0 stereopeak\n", i);
+ }
+
+ for (i = 0; i < N; i += 2)
+ {
+ printf ("ACC3(TMP, I%d_L, 0, 0)\n", i);
+ printf ("VU(IVU%d_L, TMP)\n", i);
+ printf ("ACC3(TMP, I%d_R, 0, 0)\n", i);
+ printf ("VU(IVU%d_R, TMP)\n", i);
+ }
+
+}
+
+static void
+gen_outputtest (void)
+{
+ int i;
+
+ printf ("// Output test program\n");
+ printf ("#include \"vu.mac\"\n");
+ printf (".parm VU 0 stereopeak\n");
+ printf (".gpr VUTMP\n");
+ printf (".gpr TMP\n");
+ printf (".gpr TMP_L\n");
+ printf (".gpr TMP_R\n");
+ printf (".send TEST_L 0\n");
+ printf (".send TEST_R 1\n");
+
+ for (i = 0; i < N; i += 2)
+ {
+ printf (".output O%d_L %d\n", i, i);
+ printf (".output O%d_R %d\n", i, i + 1);
+ }
+
+ for (i = 0; i < N; i += 2)
+ {
+ if (!(i % 16))
+ printf (".parm G%x 0 group\n", i);
+ printf (".parm OUT%d 0 stereo\n", i);
+ }
+
+ printf ("ACC3(TMP_L, TEST_L, 0, 0)\n");
+ printf ("ACC3(TMP_R, TEST_R, 0, 0)\n");
+ printf ("VU(VU_L, TMP_L)\n");
+ printf ("VU(VU_R, TMP_R)\n");
+
+ for (i = 0; i < N; i += 2)
+ {
+ printf ("MACS(O%d_L, 0, TMP_L, OUT%d_L)\n", i, i);
+ printf ("MACS(O%d_R, 0, TMP_R, OUT%d_R)\n", i, i);
+ }
+
+}
+
+int
+main (int argc, char *argv[])
+{
+ if (argc < 2)
+ exit (-1);
+
+ if (strcmp (argv[1], "in") == 0)
+ gen_inputtest ();
+
+ if (strcmp (argv[1], "out") == 0)
+ gen_outputtest ();
+ exit (0);
+}
diff --git a/tutorials/sndkit/sblive/vu.mac b/tutorials/sndkit/sblive/vu.mac
new file mode 100644
index 0000000..c563d98
--- /dev/null
+++ b/tutorials/sndkit/sblive/vu.mac
@@ -0,0 +1,27 @@
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+// VU meter computation macros
+//
+// Simple VU
+#define VU(vuvar, input) \
+ TSTNEG(VUTMP, input, input, 0);\
+ LIMIT(vuvar, vuvar, vuvar, VUTMP)
+/* VU meter with volume scaling */
+#define VUSCALE(vuvar, input, scale) \
+ MACS(VUTMP2, 0, input, scale) ;\
+ TSTNEG(VUTMP, VUTMP2, VUTMP2, 0);\
+ LIMIT(vuvar, vuvar, vuvar, VUTMP)
+/* VU meter with volume scaling and summing with a 'base' signal */
+#define VUSCALESUM(vuvar, basevar, input, scale) \
+ MACS(VUTMP2, basevar, input, scale) ;\
+ TSTNEG(VUTMP, VUTMP2, VUTMP2, 0);\
+ LIMIT(vuvar, vuvar, vuvar, VUTMP)
diff --git a/tutorials/sndkit/sblive/writegpr.c b/tutorials/sndkit/sblive/writegpr.c
new file mode 100644
index 0000000..c70e191
--- /dev/null
+++ b/tutorials/sndkit/sblive/writegpr.c
@@ -0,0 +1,48 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#define oss_native_ulong unsigned long
+#define sound_os_info unsigned long
+#define ac97_devc unsigned long
+typedef int oss_mutex;
+#include <soundcard.h>
+#include <sblive.h>
+
+int
+main (int argc, char *argv[])
+{
+ int ossfd, reg, value;
+ char *dspname = "/dev/dsp";
+ sblive_reg rg;
+
+ if (argc < 3)
+ {
+ fprintf (stderr, "Usage: %s GPR hexvalue\n", argv[0]);
+ exit (-1);
+ }
+
+ if (sscanf (argv[1], "%d", &reg) != 1)
+ exit (-1);
+
+ if (sscanf (argv[2], "%x", &value) != 1)
+ exit (-1);
+
+ if ((ossfd = open (dspname, O_WRONLY, 0)) == -1)
+ {
+ perror (dspname);
+ exit (-1);
+ }
+
+ rg.reg = GPR0 + reg;
+ rg.chn = 0;
+ rg.value = value;
+
+ if (ioctl (ossfd, SBLIVE_WRITEREG, &rg) == -1)
+ perror (dspname);
+
+
+ exit (0);
+}
diff --git a/tutorials/sndkit/softsynth/Makefile b/tutorials/sndkit/softsynth/Makefile
new file mode 100644
index 0000000..b522436
--- /dev/null
+++ b/tutorials/sndkit/softsynth/Makefile
@@ -0,0 +1,23 @@
+include /etc/oss.conf
+
+BINDIR=/usr/local/bin
+
+OSSINCLUDES=-I$(OSSLIBDIR)/include/sys -I$(OSSLIBDIR)/include -L/usr/lib/oss/lib
+CFLAGS=-lm -O6
+
+all: softsynth softsynth_gtk
+
+softsynth: softsynth.c
+ $(CC) $(CFLAGS) $(OSSINCLUDES) softsynth.c -o softsynth -lOSSlib
+
+softsynth_gtk1: softsynth_gtk.c
+ -$(CC) $(CFLAGS) `gtk-config --cflags --libs` $(OSSINCLUDES) softsynth_gtk.c -o softsynth_gtk -lOSSlib
+
+softsynth_gtk: softsynth_gtk.c
+ -$(CC) $(CFLAGS) `pkg-config gtk+-2.0 --cflags --libs` $(OSSINCLUDES) softsynth_gtk.c -o softsynth_gtk -lOSSlib
+
+install: all
+ -cp softsynth softsynth_gtk $(BINDIR)
+
+clean:
+ rm -f softsynth softsynth_gtk *.o x y z *~ core core.*
diff --git a/tutorials/sndkit/softsynth/README b/tutorials/sndkit/softsynth/README
new file mode 100644
index 0000000..1bfe1a8
--- /dev/null
+++ b/tutorials/sndkit/softsynth/README
@@ -0,0 +1,16 @@
+What is this?
+=============
+
+This directory contains a very simple software MIDI organ which is
+based on playback of sine waves at various frequencies and amplitudes. Please
+see the softsynth.c file for better explanation. This is probably the simpliest
+software synthesizer application one can write.
+
+The softsynth.c file contains the original version of this program
+(no GUI). It's compatible also with any of the earlier OSS versions if you
+use an OSSlib version that contains the MIDI parser library (shipped with
+OSS 3.99.1j and later).
+
+The softsynth_gtk.c contains another version of the original program. It
+has GTK+ GUI. In addition it uses several services that are only
+available in OSS 4.0 and later.
diff --git a/tutorials/sndkit/softsynth/softsynth.c b/tutorials/sndkit/softsynth/softsynth.c
new file mode 100644
index 0000000..12e77eb
--- /dev/null
+++ b/tutorials/sndkit/softsynth/softsynth.c
@@ -0,0 +1,651 @@
+/*
+ * Purpose: A simple software MIDI synthesizer program.
+ * Copyright (C) 4Front Technologies, 2002-2004. Released in public domain.
+ *
+ * Description:
+ * This is a pretty simple program that demonstrates how to do MIDI input at
+ * the same time with audio playback (using select). It also demonstrates how
+ * to use the MIDI loopback devices of OSS 4.0 (and later).
+ * Please note that this program is nothing but a programming example. It's
+ * "output quality" equals to $10 (or cheaper) toy organs. However it's very
+ * amazing how great some songs (MIDI files) sound even 90% of the MIDI
+ * information is simply ignored.
+ *
+ * What this program actually does is that it listen's to the MIDI input port
+ * and interprets the incoming MIDI stream (using the midiparser routines
+ * included in the OSSlib library).
+ *
+ * For simplicity reasons this program does nothing else but plays simple
+ * sine waves at the right note frequencies. Percussive sounds (MIDI
+ * channel 10) are simply ignored because playing them as sine waves doesn't
+ * make any sense. All MIDI controllers, pitch bend as well as all the other
+ * MIDI features are ignored too. However the all notes off control change
+ * message is handled because otherwise hanging notes will be left if the
+ * client (player) application gets killed abnormally.
+ *
+ * There is simple fixed envelope handling (actually just attack and decay)
+ * and primitive handling of note on velocity. These features appeared to be
+ * necessary because otherwise nobody can listen the output.
+ *
+ * This program is not too usefull as a synthesizer. It's not intended to be
+ * any super "modular synthesizer". However it demonstrates how simple it is
+ * to implement any kind of software MIDI synthesizer using the OSS API.
+ * You don't need to know how to use some 450 audio related calls or 300
+ * MIDI/sequencer related calls. As you will see practically everything will
+ * be handled automagically by OSS. So you can spend all your time on
+ * writing the application itself. This program was written, and debugged
+ * in less than 5 hours from scratch (including MIDI input, audio output
+ * and the actual synthesis). In fact it took longer time to write these
+ * comments than the application itself.
+ *
+ * The major benefit of this super simple design is that it cannot fail.
+ * Provided that you don't try to set the buffer size to a too small value
+ * the application logic is fully nuke proof. It will work unmodified with
+ * every sound card in the world (past, current and future).
+ *
+ * The MIDI parser code was taken from some earlier work but we have included
+ * if in the OSSlib library for you (under LGPL). Please feel free to use it
+ * in your own OSS MIDI applications.
+ *
+ * To use this program you will need to install the "4Front MIDI loopback"
+ * driver using the "Add new card/device" function of soundconf.
+ * Then start this program in background (the audio and MIDI device names
+ * can be given as command line arguments. For example
+ *
+ * softsynth /dev/dsp /dev/midi01
+ *
+ * You can find out the loopback MIDI device number by looking for the
+ * "MIDI loopback server side" devices using the {!xlink ossinfo} -m
+ * command. Btw, nothing prevents using any "real" physical MIDI port as the
+ * input.
+ *
+ * When the synthesizer server is running you can play any MIDI file using
+ * some OSS based MIDI sequencer/player such as {!xlink ossmplay}.
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <math.h>
+#include <sys/select.h>
+#include <sys/soundcard.h>
+#include <midiparser.h>
+midiparser_common_t *parser = NULL;
+
+int audio_fd;
+int midi_fd;
+int sample_rate = 48000;
+
+/*
+ * The open_audio_device routine opens the audio device and initializes it
+ * for the required mode. This code was borrowed directly from the
+ * {!nlink singen.c} sample program. However since the buffer size
+ * is inportant with this kind of application we have added a call that
+ * sets the fragment and buffer sizes.
+ */
+
+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 audio buffering policy so that reasonably small latencies
+ * can be obtained.
+ *
+ * 4 fragments of 256 samples (512 bytes) might be good. 256 samples
+ * will give timing granularity of 256/sample_rate seconds (5.33 msec)
+ * which is fairly adequate. The effect of the granularity (fragment size) in
+ * this type of applications is timing jitter (or choking). Every event that
+ * occurs withing the 5.33 msec period (fragment time) will get executed
+ * in the beginning of the next period. If the fragment size is decreased
+ * then the granularity will decrease too. However this will cause slight
+ * increase in the CPU consumption of the application.
+ *
+ * The total buffer size (number_of_fragments*fragment_time) will define the
+ * maximum latency between the event (note on/off) and the actual change in the
+ * audio output. The average latency will be something like
+ * (number_of_fragments-0.5)*fragment_time). The theoretical average latency
+ * caused by this application is (4-0.5)*5.33 msec = ~19 msec).
+ *
+ * In musical terms 5.33 msec granularity equals to 1/750 note at 60 bpm
+ * and 19 msecs equals to 1/214. This should be pretty adequate.
+ *
+ * The latency can be decreased by limiting the number of fragments and/or the
+ * fragment size. However the after the buffer size drops close to the
+ * capabilities of the system (delays caused by the other applications) the
+ * audio output will start breaking. This can cured only by tuning the
+ * hardware and the software environments (tuning some kernel parameters and
+ * by killing all the other applications). However this is in no way an OSS
+ * issue.
+ *
+ * With these parameters it was possible to compile Linux kernel in another
+ * terminal window without any hiccup (fairly entry level 2.4 GHz P4 system
+ * running Linux 2.6.x).
+ */
+ tmp = 0x00040009;
+ if (ioctl (fd, SNDCTL_DSP_SETFRAGMENT, &tmp) == -1)
+ {
+ perror ("SNDCTL_DSP_SETFRAGMENT");
+ }
+
+/*
+ * 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 rate 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;
+}
+
+static int
+open_midi_device (char *name, int mode)
+{
+ int tmp, fd;
+
+/*
+ * This is pretty much all we nbeed.
+ */
+
+ if ((fd = open (name, mode, 0)) == -1)
+ {
+ perror (name);
+ exit (-1);
+ }
+
+ return fd;
+}
+
+#define MAX_VOICES 256
+
+typedef struct
+{
+ int active; /* ON/OFF */
+ int chn, note, velocity; /* MIDI note parameters */
+
+ float phase, step; /* Sine frequency generator */
+
+ float volume; /* Note volume */
+
+ float envelope, envelopestep; /* Envelope generator */
+ int envelopedir; /* 0=fixed level, 1=attack, -1=decay */
+} voice_t;
+
+static voice_t voices[MAX_VOICES] = { 0 };
+
+static int
+note_to_freq (int note_num)
+{
+
+/*
+ * This routine converts a midi note to a frequency (multiplied by 1000)
+ * Notice! This routine was copied from the OSS sequencer code.
+ */
+
+ int note, octave, note_freq;
+ static int notes[] = {
+ 261632, 277189, 293671, 311132, 329632, 349232,
+ 369998, 391998, 415306, 440000, 466162, 493880
+ };
+
+#define BASE_OCTAVE 5
+
+ octave = note_num / 12;
+ note = note_num % 12;
+
+ note_freq = notes[note];
+
+ if (octave < BASE_OCTAVE)
+ note_freq >>= (BASE_OCTAVE - octave);
+ else if (octave > BASE_OCTAVE)
+ note_freq <<= (octave - BASE_OCTAVE);
+
+ return note_freq;
+}
+
+/*
+ * The note_on() routine initializes a voice with the right
+ * frequency, volume and envelope parameters.
+ */
+
+static void
+note_on (int ch, int note, int velocity)
+{
+ int i;
+
+ for (i = 0; i < MAX_VOICES; i++)
+ if (!voices[i].active)
+ {
+ voice_t *v = &voices[i];
+ int freq;
+ float step;
+
+/*
+ * Record the MIDI note on message parameters (just in case)
+ */
+
+ v->chn = ch;
+ v->note = note;
+ v->velocity = velocity;
+
+/*
+ * Convert the note number to the actual frequency (multiplied by 1000).
+ * Then compute the step to be added to the phase angle to get the right
+ * frequency.
+ */
+
+ freq = note_to_freq (note);
+ step = 1000.0 * (float) sample_rate / (float) freq; /* Samples/cycle */
+ v->step = 2.0 * M_PI / step;
+ if (v->step > M_PI) /* Nyqvist was here */
+ return;
+ v->phase = 0.0;
+
+/*
+ * Compute the note volume based on the velocity. Use linear scale which
+ * maps velocity=0 to the 25% volume level. Proper synthesizers will use more
+ * advanced methods (such as logarithmic scales) but this is good for our
+ * purposes.
+ */
+ v->volume = 0.25 + ((float) velocity / 127.0) * 0.75;
+
+/*
+ * Initialize the envelope engine to start from zero level and to add
+ * some fixed amount to the envelope level after each sample.
+ */
+ v->envelope = 0.0;
+ v->envelopedir = 1;
+ v->envelopestep = 0.01;
+
+/*
+ * Fire the voice. However nothing will happen before the next audio
+ * period (fragment) gets computed. This means that all the voices started
+ * during the ending period will be rounded to start at the same moment.
+ */
+ v->active = 1;
+ break;
+ }
+}
+
+/*
+ * The note_off() routine finds all the voices that have matching channel and
+ * note numbers. Then it starts the envelope decay phase (10 times slower
+ * than the attack phase.
+ */
+
+static void
+note_off (int ch, int note, int velocity)
+{
+ int i;
+
+ for (i = 0; i < MAX_VOICES; i++)
+ if (voices[i].active && voices[i].chn == ch)
+ if (voices[i].note = note)
+ {
+ voice_t *v = &voices[i];
+ v->envelopedir = -1;
+ v->envelopestep = -0.001;
+ }
+}
+
+/*
+ * all_notes_off() is a version of note_off() that checks only the channel
+ * number. Used for the All Notes Off MIDI controller (123).
+ */
+
+static void
+all_notes_off (int ch)
+{
+ int i;
+
+ for (i = 0; i < MAX_VOICES; i++)
+ if (voices[i].active && voices[i].chn == ch)
+ {
+ voice_t *v = &voices[i];
+ v->envelopedir = -1;
+ v->envelopestep = -0.01;
+ }
+}
+
+/*
+ * Compute voice computes few samples (nloops) and sums them to the
+ * buffer (that contains the sum of all previously computed voices).
+ *
+ * In real world applications it may be necessary to convert this routine to
+ * use floating point buffers (-1.0 to 1.0 range) and do the conversion
+ * to fixed point only in the final output stage. Another change you may
+ * want to do is using multiple output buffers (for stereo or multiple
+ * channels) instead of the current mono scheme.
+ *
+ * For clarity reasons we have not done that.
+ */
+
+static void
+compute_voice (voice_t * v, short *buf, int nloops)
+{
+ int i;
+
+ for (i = 0; i < nloops; i++)
+ {
+ float val;
+
+/*
+ * First compute the sine wave (-1.0 to 1.0) and scale it to the right
+ * level. Finally sum the sample with the earlier voices in the buffer.
+ */
+ val = sin (v->phase) * 1024.0 * v->envelope * v->volume;
+ buf[i] += (short) val;
+
+/*
+ * Increase the phase angle for the next sample.
+ */
+ v->phase += v->step;
+
+/*
+ * Handle envelope attack or decay
+ */
+ switch (v->envelopedir)
+ {
+ case 1:
+ v->envelope += v->envelopestep;
+ if (v->envelope >= 1.0) /* Full level ? */
+ {
+ v->envelope = 1.0;
+ v->envelopestep = 0.0;
+ v->envelopedir = 0;
+ }
+ break;
+
+ case -1:
+ v->envelope += v->envelopestep;
+ if (v->envelope <= 0.0) /* Decay done */
+ {
+ v->envelope = 0.0;
+ v->envelopestep = 0.0;
+ v->envelopedir = 0;
+ v->active = 0; /* Shut up */
+ }
+ break;
+ }
+ }
+}
+
+/*
+ * The midi_callback() function is called by the midi parser library when
+ * a complete MIDI message is seen in the input. The MIDI message number
+ * (lowest 4 bits usually set to zero), the channel (0-15), as well as the
+ * remaining bytes will be passed in the parameters.
+ *
+ * The MIDI parser library will handle oddities (like running status
+ * or use of note on with velocity of 0 as note off) so the application
+ * doesn't need to care about such nasty things.
+ *
+ * Note that the MIDI percussion channel 10 (9 as passed in the ch parameter)
+ * will be ignored. All other MIDI messages other than note on, note off
+ * and the "all notes off" controller are simply ignored.
+ *
+ * Macros like MIDI_NOTEON and MIDI_NOTEOFF are defined in soundcard.h.
+ */
+
+static void
+midi_callback (void *context, int category, unsigned char msg,
+ unsigned char ch, unsigned char *parms, int len)
+{
+ switch (msg)
+ {
+ case MIDI_NOTEON:
+ if (ch != 9) /* Avoid percussions */
+ note_on (ch, parms[0], parms[1]);
+ break;
+
+ case MIDI_NOTEOFF:
+ if (ch != 9) /* Avoid percussions */
+ note_off (ch, parms[0], parms[1]);
+ break;
+
+ case MIDI_CTL_CHANGE:
+ if (parms[0] == 123)
+ all_notes_off (ch);
+ break;
+
+ }
+}
+
+/*
+ * The handle_midi_input() routine reads all the MIDI input bytes
+ * that have been received by OSS since the last read. Note that
+ * this read will not block.
+ *
+ * Finally the received buffer is sent to the midi parser library which in turn
+ * calls midi_callback (see above) to handle the actual events.
+ */
+
+static void
+handle_midi_input (void)
+{
+ unsigned char buffer[256];
+ int l, i;
+
+ if ((l = read (midi_fd, buffer, sizeof (buffer))) == -1)
+ {
+ perror ("MIDI read");
+ exit (-1);
+ }
+
+ if (l > 0)
+ midiparser_input_buf (parser, buffer, l);
+}
+
+/*
+ * handle_audio_output() computes a new block of audio and writes it to the
+ * audio device. As you see there is no checking for blocking or available
+ * buffer space because it's simply not necessary with OSS 4.0 any more.
+ * If there is any blocking then the time below our "tolerances".
+ */
+
+static void
+handle_audio_output (void)
+{
+/*
+ * Ideally the buffer size equals to the fragment size (in samples).
+ * Using different sizes is not a big mistake but the granularity is
+ * defined by the buffer size or the fragment size (depending on which
+ * one is larger),
+ */
+ short buf[256];
+ int i;
+
+ memset (buf, 0, sizeof (buf));
+
+ /* Loop all the active voices */
+ for (i = 0; i < MAX_VOICES; i++)
+ if (voices[i].active)
+ compute_voice (&voices[i], buf, sizeof (buf) / sizeof (*buf));
+
+ if (write (audio_fd, buf, sizeof (buf)) == -1)
+ {
+ perror ("Audio write");
+ exit (-1);
+ }
+}
+
+int
+main (int argc, char *argv[])
+{
+ fd_set readfds, writefds;
+/*
+ * 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 *audiodev_name;
+ char *mididev_name;
+
+/*
+ * 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 != 3)
+ {
+ fprintf (stderr, "Usage: %s audio_device midi_device\n", argv[0]);
+ exit (-1);
+ }
+
+ audiodev_name = argv[1];
+ mididev_name = argv[2];
+
+/*
+ * 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.
+ */
+ audio_fd = open_audio_device (audiodev_name, O_WRONLY);
+
+/*
+ * Open the MIDI device for read access (only).
+ */
+
+ midi_fd = open_midi_device (mididev_name, O_RDONLY);
+
+/*
+ * Init the MIDI input parser (from OSSlib)
+ */
+
+ if ((parser = midiparser_create (midi_callback, NULL)) == NULL)
+ {
+ fprintf (stderr, "Creating a MIDI parser failed\n");
+ exit (-1);
+ }
+
+/*
+ * Then the select loop. This program uses select instead of poll. However
+ * you can use select if you like (it should not matter).
+ *
+ * The logic is very simple. Wait for MIDI input and audio output events.
+ * If there is any MIDI input then handle it (by modifying the voices[]
+ * array.
+ *
+ * When there is space to write more audio data then we simply compute one
+ * block of output and write it to the device.
+ */
+
+ while (1) /* Infinite loop */
+ {
+ int i, n;
+
+ FD_ZERO (&readfds);
+ FD_ZERO (&writefds);
+
+ FD_SET (audio_fd, &writefds);
+ FD_SET (midi_fd, &readfds);
+
+ if ((n = select (midi_fd + 1, &readfds, &writefds, NULL, NULL)) == -1)
+ {
+ perror ("select");
+ exit (-1);
+ }
+
+ if (n > 0)
+ {
+ if (FD_ISSET (midi_fd, &readfds))
+ handle_midi_input ();
+ if (FD_ISSET (audio_fd, &writefds))
+ handle_audio_output ();
+ }
+ }
+
+/*
+ * You may wonder what do we do between the songs. The answer is nothing.
+ * The note off messages (or the all notes off controller) takes care of
+ * shutting up the voices. When there are no voices playing the application
+ * will just output silent audio (until it's killed). So there is no need to
+ * know if a song has ended.
+ *
+ * However the MIDI loopback devices will retgurn a MIDI stop (0xfc) message
+ * when the client side is closed and a MIDI start (0xfa) message when some
+ * application starts playing. The server side application (synth) can
+ * use these events for it's purposes.
+ */
+
+/*
+ * That's all folks!
+ *
+ * This is pretty much all of it. This program can be easily improced by
+ * using some more advanced synthesis algorithm (wave table, sample playback,
+ * physical modelling or whatever else) and by interpreting all the MIDI
+ * messages. You can also add a nice GUI. You have complete freedom to
+ * modify this program and distribute it as your own work (under GPL, BSD
+ * proprietary or whatever license you can imagine) but only AS LONG AS YOU
+ * DON*T DO ANY STUPID CHANGES THAT BREAK THE RELIABILITY AND ROBUSTNESS.
+ *
+ * The point is that regardless of what you do there is no need to touch the
+ * audio/MIDI device related parts. They are already "state of the art".
+ * So you can spend all your time to work on the "payload" code. What you
+ * can do is changing the compute_voice() and midi_callback() routines and
+ * everything called by them.
+ */
+
+ exit (0);
+}
diff --git a/tutorials/sndkit/softsynth/softsynth_gtk.c b/tutorials/sndkit/softsynth/softsynth_gtk.c
new file mode 100644
index 0000000..7f9d6ac
--- /dev/null
+++ b/tutorials/sndkit/softsynth/softsynth_gtk.c
@@ -0,0 +1,869 @@
+/*
+ * Purpose: A simple software MIDI synthesizer program with GTK GUI.
+ * Copyright (C) 4Front Technologies, 2002-2004. Released in public domain.
+ *
+ * Description:
+ *
+ * This is an expanded version of the {!nlink softsynth.c} program
+ * (please take a look at the original program first). Also this program
+ * is more or less just an programming example that demontstrates how to
+ * implement "real-time" GUI controls in the simpliest possible way. While
+ * you can use this program as a program template, you may equally well start
+ * from the original one.
+ *
+ * Like the original there is not much to learn about synthesis in this program
+ * the only design goal has been to demonstrate some additional OSS MIDI
+ * features and to work as a test bed for them.
+ *
+ * The primary thing that has been done is replacing the original select() loop
+ * with a GTK (actually GDK) compatible mechanism (gdk_add_input and
+ * gtk_main). This is done in the main program.
+ *
+ * There are few other added features that should be mentioned:
+ *
+ * 1) The "Enhance switch" that makes the sound slightly "different" by
+ * adding some harmonics of the original note frequency. The sound is just
+ * different (not necessarily better). This together with the "Mute" button
+ * demonstrates how to do real-time adjustment of the settings.
+ * 2) The "mute" button was added just to be able to check if the latencies
+ * are audible or not.
+ * 3) The new SNDCTL_SETNAME call is used to change the description of the
+ * MIDI device. In this way the user of the client program can select the
+ * right device much easier.
+ * 4) The SNDCTL_GETSONG call is used to obtain the song name given
+ * by the client (ossmplay does this using SNDCTL_SETSONG).
+ * 5) Some code has been added to handle the MIDI timer start (0xfa) and
+ * stop (0xfc) messages. WIth loopback devices these messages can be
+ * used to find out when the client closed/opened the device. However if
+ * the server starts in the middle of a song there will be no start message.
+ *
+ * To use this program you will need to install the "4Front MIDI loopback"
+ * driver using the "Add new card/device" function of soundconf.
+ * Then start this program in background (the audio and MIDI device names
+ * can be given as command line arguments. For example
+ *
+ * softsynth /dev/dsp /dev/midi01
+ *
+ * You can find out the loopback MIDI device number by looking for the
+ * "MIDI loopback server side" devices using the {!xlink ossinfo} -m
+ * command. Btw, nothing prevents using any "real" physical MIDI port as the
+ * input.
+ *
+ * When the synthesizer server is running you can play any MIDI file using
+ * some OSS based MIDI sequencer/player such as {!xlink ossmplay}.
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <math.h>
+#include <sys/select.h>
+#include <sys/soundcard.h>
+#include <midiparser.h>
+midiparser_common_t *parser = NULL;
+
+int audio_fd;
+int midi_fd;
+int sample_rate = 48000;
+int new_song = 1;
+
+int enhanced_mode = 0;
+float main_vol = 1.0;
+
+/*
+ * The open_audio_device routine opens the audio device and initializes it
+ * for the required mode. This code was borrowed directly from the
+ * {!nlink singen.c} sample program. However since the buffer size
+ * is inportant with this kind of application we have added a call that
+ * sets the fragment and buffer sizes.
+ */
+
+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 audio buffering policy so that reasonably small latencies
+ * can be obtained.
+ *
+ * 4 fragments of 256 samples (512 bytes) might be good. 256 samples
+ * will give timing granularity of 256/sample_rate seconds (5.33 msec)
+ * which is fairly adequate. The effect of the granularity (fragment size) in
+ * this type of applications is timing jitter (or choking). Every event that
+ * occurs withing the 5.33 msec period (fragment time) will get executed
+ * in the beginning of the next period. If the fragment size is decreased
+ * then the granularity will decrease too. However this will cause slight
+ * increase in the CPU consumption of the application.
+ *
+ * The total buffer size (number_of_fragments*fragment_time) will define the
+ * maximum latency between the event (note on/off) and the actual change in the
+ * audio output. The average latency will be something like
+ * (number_of_fragments-0.5)*fragment_time). The theoretical average latency
+ * caused by this application is (4-0.5)*5.33 msec = ~19 msec).
+ *
+ * In musical terms 5.33 msec granularity equals to 1/750 note at 60 bpm
+ * and 19 msecs equals to 1/214. This should be pretty adequate.
+ *
+ * The latency can be decreased by limiting the number of fragments and/or the
+ * fragment size. However the after the buffer size drops close to the
+ * capabilities of the system (delays caused by the other applications) the
+ * audio output will start breaking. This can cured only by tuning the
+ * hardware and the software environments (tuning some kernel parameters and
+ * by killing all the other applications). However this is in no way an OSS
+ * issue.
+ *
+ * With these parameters it was possible to compile Linux kernel in another
+ * terminal window without any hiccup (fairly entry level 2.4 GHz P4 system
+ * running Linux 2.6.x).
+ */
+ tmp = 0x00040009;
+ if (ioctl (fd, SNDCTL_DSP_SETFRAGMENT, &tmp) == -1)
+ {
+ perror ("SNDCTL_DSP_SETFRAGMENT");
+ }
+
+/*
+ * 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 rate 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;
+}
+
+static int
+open_midi_device (char *name, int mode)
+{
+ int tmp, fd;
+
+/*
+ * This is pretty much all we nbeed.
+ */
+
+ if ((fd = open (name, mode, 0)) == -1)
+ {
+ perror (name);
+ exit (-1);
+ }
+
+ return fd;
+}
+
+/*
+ ************** Some GTK+ related routines **********************
+ */
+#include <gtk/gtk.h>
+
+GtkWidget *main_window, *song_name, *time_code;
+
+static void
+close_all (GtkWidget * window, gpointer data)
+{
+ gtk_main_quit ();
+ exit (-1);
+}
+
+/*
+ * toggle_enhance() gets called when the "Enhance" button is hit. It just
+ * changes the state of the enhanced_mode flag which is used by the
+ * note_on() routine. This setting affects only the notes to be started
+ * after the change. It doesn't affect any of the currently playing voices.
+ */
+
+static void
+toggle_enhance (GtkWidget * window, gpointer data)
+{
+ enhanced_mode = !enhanced_mode; /* ON <-> OFF */
+}
+
+/*
+ * toggle_mute() handles the "Mute" button. The change will take effect
+ * at the moment when the next audio block to be computed starts playing
+ * on the device. So this button can be used to check how long the total
+ * latencies are (including any delays caused by device level FIFOs).
+ */
+
+static void
+toggle_mute (GtkWidget * window, gpointer data)
+{
+ if (main_vol > 0)
+ main_vol = 0.0;
+ else
+ main_vol = 1.0;
+}
+
+static void
+update_song_name (void)
+{
+ oss_longname_t name;
+ char tmp[256];
+
+/*
+ * Get the song name from the client and update the label. Notice that
+ * SNDCTL_GETSONG will return error (EINVAL) if the device doesn't
+ * support song names. This is not an error. It simple means that no
+ * song information is available. The song name may also be an empty
+ * string if the client has not registered any song name. Also this is
+ * perfectly normal.
+ *
+ * The difference between EINVAL and an empty string (if it matters) is that
+ * EINVAL means that the device will not return this info later (the
+ * application may stop polling for it).
+ */
+
+ if (ioctl (midi_fd, SNDCTL_GETSONG, name) != -1)
+ {
+ if (*name == 0)
+ strcpy (name, "Unknown song");
+ sprintf (tmp, "Song: %s", name);
+ gtk_label_set (GTK_LABEL (song_name), tmp);
+
+ /* Forward the song name to the audio device too */
+ ioctl (audio_fd, SNDCTL_SETSONG, name);
+ }
+
+ new_song = 0;
+}
+
+/*
+ * The create_user_interface() routine is pretty much a standard
+ * GUI initialization for any GTK based application. Not important
+ * for the logic of this program. We just create some buttons and one
+ * label and assign the callbacks to handle them.
+ */
+
+static void
+create_user_interface (void)
+{
+ GtkWidget *button, *vbox;
+ main_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ gtk_signal_connect (GTK_OBJECT (main_window),
+ "destroy", GTK_SIGNAL_FUNC (close_all), NULL);
+
+ vbox = gtk_vbox_new (FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (main_window), vbox);
+
+ song_name = gtk_label_new ("Song: Unknown song");
+ gtk_box_pack_start (GTK_BOX (vbox), song_name, FALSE, FALSE, 0);
+ gtk_widget_show (song_name);
+
+ time_code = gtk_label_new ("Song: Unknown song");
+ gtk_box_pack_start (GTK_BOX (vbox), time_code, FALSE, FALSE, 0);
+ gtk_widget_show (time_code);
+
+ update_song_name ();
+
+ button = gtk_check_button_new_with_label ("Mute");
+ gtk_signal_connect (GTK_OBJECT (button),
+ "clicked", GTK_SIGNAL_FUNC (toggle_mute), NULL);
+ gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+
+ button = gtk_check_button_new_with_label ("Enhance");
+ gtk_signal_connect (GTK_OBJECT (button),
+ "clicked", GTK_SIGNAL_FUNC (toggle_enhance), NULL);
+ gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+
+ button = gtk_button_new_with_label ("Exit");
+ gtk_signal_connect (GTK_OBJECT (button),
+ "clicked", GTK_SIGNAL_FUNC (close_all), NULL);
+ gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+
+ gtk_widget_show (vbox);
+ gtk_widget_show (main_window);
+}
+
+/*
+ ************** The actual synthesis engine *********************
+ */
+
+#define MAX_VOICES 256
+
+typedef struct
+{
+ int active; /* ON/OFF */
+ int chn, note, velocity; /* MIDI note parameters */
+
+ float phase, step; /* Sine frequency generator */
+
+ float volume; /* Note volume */
+
+ float envelope, envelopestep; /* Envelope generator */
+ int envelopedir; /* 0=fixed level, 1=attack, -1=decay */
+} voice_t;
+
+static voice_t voices[MAX_VOICES] = { 0 };
+
+static int
+note_to_freq (int note_num)
+{
+
+/*
+ * This routine converts a midi note to a frequency (multiplied by 1000)
+ * Notice! This routine was copied from the OSS sequencer code.
+ */
+
+ int note, octave, note_freq;
+ static int notes[] = {
+ 261632, 277189, 293671, 311132, 329632, 349232,
+ 369998, 391998, 415306, 440000, 466162, 493880
+ };
+
+#define BASE_OCTAVE 5
+
+ octave = note_num / 12;
+ note = note_num % 12;
+
+ note_freq = notes[note];
+
+ if (octave < BASE_OCTAVE)
+ note_freq >>= (BASE_OCTAVE - octave);
+ else if (octave > BASE_OCTAVE)
+ note_freq <<= (octave - BASE_OCTAVE);
+
+ return note_freq;
+}
+
+/*
+ * fork_voice() creates another instance of a voice (just like the
+ * fork system call does). It's possible to change the pitch and volume
+ * by setting the freq_ratio and vol_scale parameters to values below 1.0.
+ */
+static void
+fork_voice (voice_t * orig, float freq_ratio, float vol_scale)
+{
+ int i;
+
+ for (i = 0; i < MAX_VOICES; i++)
+ if (!voices[i].active)
+ {
+ voice_t *v = &voices[i];
+
+ memcpy (v, orig, sizeof (voice_t));
+ v->step /= freq_ratio;
+ v->volume *= vol_scale;
+ return;
+ }
+}
+
+/*
+ * The note_on() routine initializes a voice with the right
+ * frequency, volume and envelope parameters.
+ */
+
+static void
+note_on (int ch, int note, int velocity)
+{
+ int i;
+
+ for (i = 0; i < MAX_VOICES; i++)
+ if (!voices[i].active)
+ {
+ voice_t *v = &voices[i];
+ int freq;
+ float step;
+
+ /*
+ * Record the MIDI note on message parameters (just in case)
+ */
+
+ v->chn = ch;
+ v->note = note;
+ v->velocity = velocity;
+
+ /*
+ * Convert the note number to the actual frequency (multiplied by 1000).
+ * Then compute the step to be added to the phase angle to get the right
+ * frequency.
+ */
+
+ freq = note_to_freq (note);
+ step = 1000.0 * (float) sample_rate / (float) freq; /* Samples/cycle */
+ v->step = 2.0 * M_PI / step;
+ if (v->step > M_PI) /* Nyqvist was here */
+ return;
+ v->phase = 0.0;
+
+ /*
+ * Compute the note volume based on the velocity. Use linear scale which
+ * maps velocity=0 to the 25% volume level. Proper synthesizers will use more
+ * advanced methods (such as logarithmic scales) but this is good for our
+ * purposes.
+ */
+ v->volume = 0.25 + ((float) velocity / 127.0) * 0.75;
+
+ /*
+ * Initialize the envelope engine to start from zero level and to add
+ * some fixed amount to the envelope level after each sample.
+ */
+ v->envelope = 0.0;
+ v->envelopedir = 1;
+ v->envelopestep = 0.01;
+
+ /*
+ * Fire the voice. However nothing will happen before the next audio
+ * period (fragment) gets computed. This means that all the voices started
+ * during the ending period will be rounded to start at the same moment.
+ */
+ v->active = 1;
+
+ if (enhanced_mode)
+ {
+/*
+ * Stupid test that adds some harmonic frequencies. This makes the output
+ * to sound bolder. This algorithm is called additive synthesis. However
+ * this program is not the best possible one for learning that technique.
+ */
+ fork_voice (v, 1.001, 0.9); /* Add some beating */
+ fork_voice (v, 2.0, 0.1);
+ fork_voice (v, 3.0, 0.2);
+ fork_voice (v, 4.0, 0.02);
+ fork_voice (v, 6.0, 0.01);
+ fork_voice (v, 8.0, 0.01);
+ }
+ break;
+ }
+}
+
+/*
+ * The note_off() routine finds all the voices that have matching channel and
+ * note numbers. Then it starts the envelope decay phase (10 times slower
+ * than the attack phase.
+ */
+
+static void
+note_off (int ch, int note, int velocity)
+{
+ int i;
+
+ for (i = 0; i < MAX_VOICES; i++)
+ if (voices[i].active && voices[i].chn == ch)
+ if (voices[i].note = note)
+ {
+ voice_t *v = &voices[i];
+ v->envelopedir = -1;
+ v->envelopestep = -0.001;
+ }
+}
+
+/*
+ * all_notes_off() is a version of note_off() that checks only the channel
+ * number. Used for the All Notes Off MIDI controller (123).
+ */
+
+static void
+all_notes_off (int ch)
+{
+ int i;
+
+ for (i = 0; i < MAX_VOICES; i++)
+ if (voices[i].active && voices[i].chn == ch)
+ {
+ voice_t *v = &voices[i];
+ v->envelopedir = -1;
+ v->envelopestep = -0.01;
+ }
+}
+
+/*
+ * all_voices_off() mutes all voices immediately.
+ */
+
+static void
+all_voices_off (int ch)
+{
+ int i;
+
+ for (i = 0; i < MAX_VOICES; i++)
+ if (voices[i].active && voices[i].chn == ch)
+ {
+ voice_t *v = &voices[i];
+
+ v->active = 0;
+ }
+}
+
+/*
+ * Compute voice computes few samples (nloops) and sums them to the
+ * buffer (that contains the sum of all previously computed voices).
+ *
+ * In real world applications it may be necessary to convert this routine to
+ * use floating point buffers (-1.0 to 1.0 range) and do the conversion
+ * to fixed point only in the final output stage. Another change you may
+ * want to do is using multiple output buffers (for stereo or multiple
+ * channels) instead of the current mono scheme.
+ *
+ * For clarity reasons we have not done that.
+ */
+
+static void
+compute_voice (voice_t * v, short *buf, int nloops)
+{
+ int i;
+
+ for (i = 0; i < nloops; i++)
+ {
+ float val;
+
+/*
+ * First compute the sine wave (-1.0 to 1.0) and scale it to the right
+ * level. Finally sum the sample with the earlier voices in the buffer.
+ */
+ val = sin (v->phase) * 1024.0 * v->envelope * v->volume * main_vol;
+ buf[i] += (short) val;
+
+/*
+ * Increase the phase angle for the next sample.
+ */
+ v->phase += v->step;
+
+/*
+ * Handle envelope attack or decay
+ */
+ switch (v->envelopedir)
+ {
+ case 1:
+ v->envelope += v->envelopestep;
+ if (v->envelope >= 1.0) /* Full level ? */
+ {
+ v->envelope = 1.0;
+ v->envelopestep = 0.0;
+ v->envelopedir = 0;
+ }
+ break;
+
+ case -1:
+ v->envelope += v->envelopestep;
+ if (v->envelope <= 0.0) /* Decay done */
+ {
+ v->envelope = 0.0;
+ v->envelopestep = 0.0;
+ v->envelopedir = 0;
+ v->active = 0; /* Shut up */
+ }
+ break;
+ }
+ }
+}
+
+/*
+ *********** Handling of OSS MIDI input and audio output ***********
+ */
+
+/*
+ * The midi_callback() function is called by the midi parser library when
+ * a complete MIDI message is seen in the input. The MIDI message number
+ * (lowest 4 bits usually set to zero), the channel (0-15), as well as the
+ * remaining bytes will be passed in the parameters.
+ *
+ * The MIDI parser library will handle oddities (like running status
+ * or use of note on with velocity of 0 as note off) so the application
+ * doesn't need to care about such nasty things.
+ *
+ * Note that the MIDI percussion channel 10 (9 as passed in the ch parameter)
+ * will be ignored. All other MIDI messages other than note on, note off
+ * and the "all notes off" controller are simply ignored.
+ *
+ * Macros like MIDI_NOTEON and MIDI_NOTEOFF are defined in soundcard.h.
+ */
+
+static void
+midi_callback (void *context, int category, unsigned char msg,
+ unsigned char ch, unsigned char *parms, int len)
+{
+ switch (msg)
+ {
+ case MIDI_NOTEON:
+ if (new_song)
+ update_song_name ();
+
+ if (ch != 9) /* Avoid percussions */
+ note_on (ch, parms[0], parms[1]);
+ break;
+
+ case MIDI_NOTEOFF:
+ if (ch != 9) /* Avoid percussions */
+ note_off (ch, parms[0], parms[1]);
+ break;
+
+ case MIDI_CTL_CHANGE:
+ if (parms[0] == 120)
+ all_voices_off (ch);
+ if (parms[0] == 123)
+ all_notes_off (ch);
+ break;
+
+ case 0xfa: /* Start */
+ /*
+ * Note that the start message arrives at the moment when the
+ * client side of the loopback device is opened. At that moment
+ * the client has not updated the song name so we should
+ * not try to read it immediately. Instead we have to do it
+ * (for example) at the moment the first note is started.
+ */
+ new_song = 1;
+ break;
+
+ case 0xfc: /* Stop */
+ /*
+ * The stop message arrives after the client side of the
+ * loopback device has been closed. We will just re-draw
+ * the song name (to clear the display field on the screen).
+ */
+ update_song_name ();
+ break;
+
+ }
+}
+
+/*
+ * The handle_midi_input() routine reads all the MIDI input bytes
+ * that have been received by OSS since the last read. Note that
+ * this read will not block.
+ *
+ * Finally the received buffer is sent to the midi parser library which in turn
+ * calls midi_callback (see above) to handle the actual events.
+ */
+
+static void
+handle_midi_input (gpointer data, gint source, GdkInputCondition cond)
+{
+ unsigned char buffer[256];
+ int l, i;
+
+ if ((l = read (midi_fd, buffer, sizeof (buffer))) == -1)
+ {
+ perror ("MIDI read");
+ exit (-1);
+ }
+
+ if (l > 0)
+ midiparser_input_buf (parser, buffer, l);
+}
+
+/*
+ * handle_audio_output() computes a new block of audio and writes it to the
+ * audio device. As you see there is no checking for blocking or available
+ * buffer space because it's simply not necessary with OSS 4.0 any more.
+ * If there is any blocking then the time below our "tolerances".
+ */
+
+static void
+handle_audio_output (gpointer data, gint source, GdkInputCondition cond)
+{
+/*
+ * Ideally the buffer size equals to the fragment size (in samples).
+ * Using different sizes is not a big mistake but the granularity is
+ * defined by the buffer size or the fragment size (depending on which
+ * one is larger),
+ */
+ short buf[256];
+ int i;
+
+ memset (buf, 0, sizeof (buf));
+
+ /* Loop all the active voices */
+ for (i = 0; i < MAX_VOICES; i++)
+ if (voices[i].active)
+ compute_voice (&voices[i], buf, sizeof (buf) / sizeof (*buf));
+
+ if (write (audio_fd, buf, sizeof (buf)) == -1)
+ {
+ perror ("Audio write");
+ exit (-1);
+ }
+}
+
+/*
+ * The mtc_callback() routine updates the SMTPE/MTC time display on the
+ * screen. The quarter frames (qframes) field is not shown.
+ */
+
+static void
+mtc_callback (void *context, oss_mtc_data_t * mtc)
+{
+ char tmp[64];
+
+ if (mtc->qframes != 0)
+ return;
+
+ sprintf (tmp, "%02d:%02d:%02d.%02d\n",
+ mtc->hours, mtc->minutes, mtc->seconds, mtc->frames);
+ gtk_label_set (GTK_LABEL (time_code), tmp);
+}
+
+/*
+ * Finally the main program
+ */
+
+int
+main (int argc, char *argv[])
+{
+ fd_set readfds, writefds;
+/*
+ * 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 *audiodev_name;
+ char *mididev_name;
+
+ int tmp;
+
+/*
+ * 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 != 3)
+ {
+ fprintf (stderr, "Usage: %s audio_device midi_device\n", argv[0]);
+ exit (-1);
+ }
+
+ audiodev_name = argv[1];
+ mididev_name = argv[2];
+
+/*
+ * 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.
+ */
+ audio_fd = open_audio_device (audiodev_name, O_WRONLY);
+
+/*
+ * Open the MIDI device for read access (only).
+ */
+
+ midi_fd = open_midi_device (mididev_name, O_RDONLY);
+
+/*
+ * Request input of MTC time (25 FPS). This is just for fun.
+ */
+ tmp = 25;
+ ioctl (midi_fd, SNDCTL_MIDI_MTCINPUT, &tmp);
+
+/*
+ * Report the server name to the client side. This name will be reported
+ * by applications that check the device names. It will also be shown
+ * in /dev/sndstat
+ *
+ * SNDCTL_SETNAME is a new ioctl call in OSS 4.0. It doesn't make any
+ * sense to do error checking with it.
+ */
+
+ ioctl (midi_fd, SNDCTL_SETNAME, "OSS user land synth demo");
+
+/*
+ * Init the MIDI input parser (from OSSlib)
+ */
+
+ if ((parser = midiparser_create (midi_callback, NULL)) == NULL)
+ {
+ fprintf (stderr, "Creating a MIDI parser failed\n");
+ exit (-1);
+ }
+
+/*
+ * Register the MTC timecode handler
+ */
+ midiparser_mtc_callback (parser, mtc_callback);
+
+/*
+ * Standard GTK+ initialization.
+ */
+
+ gtk_init (&argc, &argv);
+
+ create_user_interface ();
+
+ gdk_input_add (audio_fd, GDK_INPUT_WRITE, handle_audio_output, NULL);
+ gdk_input_add (midi_fd, GDK_INPUT_READ, handle_midi_input, NULL);
+
+ gtk_main ();
+
+/*
+ * That's all folks!
+ *
+ * This is pretty much all of it. This program can be easily improced by
+ * using some more advanced synthesis algorithm (wave table, sample playback,
+ * physical modelling or whatever else) and by interpreting all the MIDI
+ * messages. You can also add a nice GUI. You have complete freedom to
+ * modify this program and distribute it as your own work (under GPL, BSD
+ * proprietary or whatever license you can imagine) but only AS LONG AS YOU
+ * DON*T DO ANY STUPID CHANGES THAT BREAK THE RELIABILITY AND ROBUSTNESS.
+ *
+ * The point is that regardless of what you do there is no need to touch the
+ * audio/MIDI device related parts. They are already "state of the art".
+ * So you can spend all your time to work on the "payload" code. What you
+ * can do is changing the compute_voice() and midi_callback() routines and
+ * everything called by them.
+ */
+
+ exit (0);
+}
diff --git a/tutorials/sndkit/tests/Makefile b/tutorials/sndkit/tests/Makefile
new file mode 100644
index 0000000..e3fb129
--- /dev/null
+++ b/tutorials/sndkit/tests/Makefile
@@ -0,0 +1,11 @@
+TARGETS=iosync multich16 multich32 musicin seltest2 spdif_in_debug sweepdown sweepup synctest ioctl_test
+
+CFLAGS=-I../../../include
+
+all: ${TARGETS}
+
+sweepup:
+ $(CC) ${CFLAGS} $(LDFLAGS) -lm -o sweepup sweepup.c
+
+clean:
+ rm -f ${TARGETS} *.o x y z *~
diff --git a/tutorials/sndkit/tests/README.1ST b/tutorials/sndkit/tests/README.1ST
new file mode 100644
index 0000000..8a69f0f
--- /dev/null
+++ b/tutorials/sndkit/tests/README.1ST
@@ -0,0 +1,11 @@
+Important warning
+=================
+
+Many prongrams in this directory are in no way sample programs. Most of them
+intentionally misuse the OSS API to force the drivers to fail if there are
+any bugs in them. Using this kind of stupid methods in ordinary applications
+is asking for troubles.
+
+However some of the programs demonstrate how to use certain new features
+of OSS. In such cases the program name is mentioned somewhere else in the
+OSS documentation.
diff --git a/tutorials/sndkit/tests/ioctl_test.c b/tutorials/sndkit/tests/ioctl_test.c
new file mode 100644
index 0000000..ea0e4b9
--- /dev/null
+++ b/tutorials/sndkit/tests/ioctl_test.c
@@ -0,0 +1,624 @@
+/*
+ * Purpose: This program has been used to verify that some of the ioctl calls work
+ *
+ * Description:
+ * This program can be used to debug some of the most common OSS audio ioctl calls (use the -m option to select):
+ *
+ * Output tests:
+ * -m0 : SNDCTL_DSP_GETOSPTR - The output buffer pointer
+ * -m1 : SNDCTL_DSP_GETODELAY - The output delay
+ * -m2 : SNDCTL_DSP_GETOSPACE - Space available in the output buffer
+ *
+ * Input tests:
+ * -m20 : SNDCTL_DSP_GETISPTR - The input buffer pointer
+ * -m21 : SNDCTL_DSP_GETISPACE - Data available in the input buffer
+ *
+ * CAUTION!
+ *
+ * This program is not an automated test that tells if the device/driver is working correctly or not.
+ * It simply displays the value returned by the ioctl call and gives some visual indication of the
+ * result. You will see a '*' walking left or right on some kind of bar display.
+ *
+ * Tho use this program you should have very detailed knowledge about the internals of OSS. Otherwise
+ * the results will not make any sense to you.
+ *
+ * NOTE!
+ *
+ * Output of this program is supposed to be redirected to a disk file that is to be examined after the
+ * test is finished. Output to a terminal will delay the program too much which gives wrong results.
+ *
+ * How it works:
+ *
+ * There are different tests in this program. All they write audio data to the audio device (no sound will be produced).
+ * After each write call the program will display the information specific to that test. The times displayed are derived
+ * from the number of bytes written to the device so far. It is important to understand that they are not real time. As
+ * long as the device buffer is not completely filled the time difference between two subsequent writes will be zero
+ * (unless the -D option is used).
+ *
+ * Device setup:
+ *
+ * Use the following command line options to select the setup parameters for the device:
+ *
+ * -s NNN Selects the sampling rate (in Hz or kHz)
+ * -c NNN Selects the number of channels (1, 2, ..., N)
+ * -b NNN Selects the number of bits (8, 16 or 32)
+ *
+ * -f NNN Selects the fragment size (fs = 2**NNN)
+ * -n NNN Selects the number of fragments (2 to N)
+ * -d NNN Selects the output device to use (/dev/dsp by default)
+ *
+ * -r Disables automatic format conversions performed by OSS
+ * -B Open the physical device directly (bypassing virtual mixer (vmix))
+ *
+ * -w NNN Selects the number of bytes written during each loop. By default this is
+ * equal to the fragment size.
+ * -D NNN Delay NNN milliseconds after each write/read (before displaying the test output).
+ * This emulates the processing done by an application. Using too long
+ * delay times will cause buffer underruns. Use delays that are shorter than the fragment time
+ * reported by the program. Note that granularity of the system timer is about 10 ms in typical
+ * systems (HZ=100). This means that the delay time will always be rounded up to the nearest
+ * system timer tick aftere the requested time. Using -D1 may result in up to 10-20 milliseconds of
+ * delay.
+ */
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <string.h>
+#include <soundcard.h>
+#ifdef _AIX
+#include <sys/select.h>
+#endif
+
+typedef void (*measure_f_t) (int fd);
+
+measure_f_t measure_f = NULL, run_test = NULL;
+
+char *name = "Unknown";
+
+char *dspdev = "/dev/dsp";
+int mode = 0;
+int fd = -1;
+int speed = 48000;
+int bits = 16;
+int fmt = AFMT_S16_LE;
+int channels = 2;
+unsigned char silence = 0;
+int fragsize = 0; /* Use default */
+int fragcount = 0x7fff; /* Unlimited */
+int write_size = 0;
+int write_byte = 0;
+int raw_mode = 0;
+int loop_delay = 0;
+
+long long prev_time = 0;
+
+int data_rate, buffer_size;
+
+static long long
+get_usecs(void)
+{
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+
+ return (long long)tv.tv_sec * 1000000LL + (long long)tv.tv_usec;
+}
+
+static void
+player (int fd)
+{
+ char *buffer;
+
+ if ((buffer = malloc (write_size)) == NULL)
+ {
+ fprintf (stderr, "Out of memory\n");
+ exit (EXIT_FAILURE);
+ }
+
+ memset (buffer, silence, write_size);
+
+ while (1)
+ {
+ long long t, d;
+
+ t = get_usecs ();
+
+ d = t - prev_time;
+ prev_time = t;
+
+ printf("d=%2lld.%03lldms ", d / 1000LL, d % 1000LL);
+
+ if (write (fd, buffer, write_size) != write_size)
+ {
+ perror ("write");
+ exit (EXIT_FAILURE);
+ }
+
+ if (loop_delay > 0)
+ usleep (loop_delay * 1000);
+
+ measure_f (fd);
+
+ write_byte += write_size;
+ }
+}
+
+static void
+recorder (int fd)
+{
+ char *buffer;
+
+ if ((buffer = malloc (write_size)) == NULL)
+ {
+ fprintf (stderr, "Out of memory\n");
+ exit (EXIT_FAILURE);
+ }
+
+ while (1)
+ {
+ long long t, d;
+
+ t = get_usecs ();
+
+ d = t - prev_time;
+ prev_time = t;
+
+ printf("d=%2lld.%03lldms ", d / 1000LL, d % 1000LL);
+
+ if (read (fd, buffer, write_size) != write_size)
+ {
+ perror ("read");
+ exit (EXIT_FAILURE);
+ }
+
+ if (loop_delay > 0)
+ usleep (loop_delay * 1000);
+
+ measure_f (fd);
+
+ write_byte += write_size;
+ }
+}
+
+static void
+print_spacing (int i)
+{
+ if ((i % 10) == 0)
+ {
+ printf ("%d", (i / 10) % 10);
+ return;
+ }
+
+ if ((i % 5) == 0)
+ {
+ printf(",");
+ return;
+ }
+
+ printf (".");
+}
+
+static void
+error_check (int fd)
+{
+ audio_errinfo err;
+
+ if (ioctl (fd, SNDCTL_DSP_GETERROR, &err) == -1)
+ return;
+
+ if (err.play_underruns > 0)
+ printf (" %d underruns\n", err.play_underruns);
+
+ if (err.rec_overruns > 0)
+ printf (" %d overruns\n", err.rec_overruns);
+}
+
+static void
+measure_getoptr (int fd)
+{
+ count_info ci;
+ int i, n;
+
+ if (ioctl (fd, SNDCTL_DSP_GETOPTR, &ci) == -1)
+ {
+ perror ("SNDCTL_DSP_GETOPTR");
+ exit (EXIT_FAILURE);
+ }
+
+ n = (100 * ci.ptr + buffer_size / 2) / buffer_size;
+
+ printf ("b=%8d, t=%8d ms, p=%6d : ", write_byte,
+ (1000 * write_byte + data_rate / 2) / data_rate, ci.ptr);
+
+ for (i = 0; i < n; i++)
+ print_spacing (i);
+ printf ("*");
+
+ if (n < 100)
+ {
+ for (i = n + 1; i < 100; i++)
+ print_spacing (i);
+ printf ("%%");
+ }
+
+ error_check (fd);
+ printf ("\n");
+ fflush (stdout);
+}
+
+static void
+measure_getiptr (int fd)
+{
+ count_info ci;
+ int i, n;
+
+ if (ioctl (fd, SNDCTL_DSP_GETIPTR, &ci) == -1)
+ {
+ perror ("SNDCTL_DSP_GETIPTR");
+ exit (EXIT_FAILURE);
+ }
+
+ n = (100 * ci.ptr + buffer_size / 2) / buffer_size;
+
+ printf ("b=%8d, t=%8d ms, p=%6d : ", write_byte,
+ (1000 * write_byte + data_rate / 2) / data_rate, ci.ptr);
+
+ for (i = 0; i < n; i++)
+ print_spacing (i);
+ printf ("*");
+
+ if (n < 100)
+ {
+ for (i = n + 1; i < 100; i++)
+ print_spacing (i);
+ printf ("%%");
+ }
+
+ error_check (fd);
+ printf ("\n");
+ fflush (stdout);
+}
+
+static void
+measure_getospace (int fd)
+{
+ audio_buf_info bi;
+ int i, n;
+
+ if (ioctl (fd, SNDCTL_DSP_GETOSPACE, &bi) == -1)
+ {
+ perror ("SNDCTL_DSP_GETOSPACE");
+ exit (EXIT_FAILURE);
+ }
+
+ n = (100 * bi.bytes + buffer_size / 2) / buffer_size;
+
+ printf ("b=%8d, t=%8d ms, p=%6d : ", write_byte,
+ (1000 * write_byte + data_rate / 2) / data_rate, bi.bytes);
+
+ for (i = 0; i < n; i++)
+ print_spacing (i);
+ printf ("*");
+
+ if (n < 100)
+ {
+ for (i = n + 1; i < 100; i++)
+ print_spacing (i);
+ printf ("%%");
+ }
+
+ error_check (fd);
+ printf ("\n");
+ fflush (stdout);
+}
+
+static void
+measure_getispace (int fd)
+{
+ audio_buf_info bi;
+ int i, n;
+
+ if (ioctl (fd, SNDCTL_DSP_GETISPACE, &bi) == -1)
+ {
+ perror ("SNDCTL_DSP_GETISPACE");
+ exit (EXIT_FAILURE);
+ }
+
+ n = (100 * bi.bytes + buffer_size / 2) / buffer_size;
+
+ printf ("b=%8d, t=%8d ms, p=%6d : ", write_byte,
+ (1000 * write_byte + data_rate / 2) / data_rate, bi.bytes);
+
+ for (i = 0; i < n; i++)
+ print_spacing (i);
+ printf ("*");
+
+ if (n < 100)
+ {
+ for (i = n + 1; i < 100; i++)
+ print_spacing (i);
+ printf ("%%");
+ }
+
+ error_check (fd);
+ printf ("\n");
+ fflush (stdout);
+}
+
+static void
+measure_getodelay (int fd)
+{
+ int d, i, n;
+
+ if (ioctl (fd, SNDCTL_DSP_GETODELAY, &d) == -1)
+ {
+ perror ("SNDCTL_DSP_GETODELAY");
+ exit (EXIT_FAILURE);
+ }
+
+ n = (100 * d + buffer_size / 2) / buffer_size;
+
+ printf ("b=%8d, t=%8d ms, d=%6d : ", write_byte,
+ (1000 * write_byte + data_rate / 2) / data_rate, d);
+
+ for (i = 0; i < n; i++)
+ print_spacing (i);
+ printf ("*");
+
+ if (n < 100)
+ {
+ for (i = n + 1; i < 100; i++)
+ print_spacing (i);
+ printf ("%%");
+ }
+
+ error_check (fd);
+ printf ("\n");
+ fflush (stdout);
+}
+
+int
+main (int argc, char *argv[])
+{
+ int i;
+ int tmp;
+ int is_input = 0;
+ audio_buf_info bi;
+
+ int open_mode = 0;
+
+ measure_f = measure_getospace;
+
+ while ((i = getopt (argc, argv, "rBd:m:s:c:b:f:n:w:D:")) != EOF)
+ switch (i)
+ {
+ case 'd':
+ dspdev = optarg;
+ break;
+
+ case 'm':
+ mode = atoi (optarg);
+ break;
+
+ case 's':
+ speed = atoi (optarg);
+ if (speed < 200)
+ speed *= 1000;
+ break;
+
+ case 'c':
+ channels = atoi (optarg);
+ break;
+
+ case 'b':
+ bits = atoi (optarg);
+ break;
+
+ case 'f':
+ fragsize = atoi (optarg);
+ break;
+
+ case 'n':
+ fragcount = atoi (optarg);
+ break;
+
+ case 'w':
+ write_size = atoi (optarg);
+ break;
+
+ case 'D':
+ loop_delay = atoi (optarg);
+ break;
+
+ case 'r':
+ raw_mode = 1;
+ break;
+
+ case 'B':
+ open_mode |= O_EXCL;
+ break;
+ }
+
+ switch (bits)
+ {
+ case 8:
+ bits = AFMT_U8;
+ silence = 0x80;
+ break;
+ case 16:
+ bits = AFMT_S16_NE;
+ break;
+ case 32:
+ bits = AFMT_S32_LE;
+ break;
+ default:
+ fprintf (stderr, "Bad numer of bits %d\n", bits);
+ exit (EXIT_FAILURE);
+ }
+
+ switch (mode)
+ {
+ case 0:
+ measure_f = measure_getoptr;
+ name = "Getoptr";
+ run_test = player;
+ open_mode |= O_WRONLY;
+ break;
+
+ case 1:
+ measure_f = measure_getodelay;
+ name = "Getodelay";
+ run_test = player;
+ open_mode |= O_WRONLY;
+ break;
+
+ case 2:
+ measure_f = measure_getospace;
+ name = "Getospace";
+ run_test = player;
+ open_mode |= O_WRONLY;
+ break;
+
+ case 20:
+ measure_f = measure_getiptr;
+ name = "Getiptr";
+ run_test = recorder;
+ open_mode |= O_RDONLY;
+ is_input = 1;
+ break;
+
+ case 21:
+ measure_f = measure_getispace;
+ name = "Getispace";
+ run_test = recorder;
+ open_mode |= O_RDONLY;
+ is_input = 1;
+ break;
+
+ default:
+ fprintf (stderr, "Bad mode -m %d\n", mode);
+ exit (EXIT_FAILURE);
+ }
+
+ if ((fd = open (dspdev, open_mode, 0)) == -1)
+ {
+ perror (dspdev);
+ exit (EXIT_FAILURE);
+ }
+
+ if (raw_mode)
+ {
+ tmp = 0;
+ ioctl (fd, SNDCTL_DSP_COOKEDMODE, &tmp); /* Ignore errors */
+ }
+
+ if (fragsize != 0)
+ {
+ fragsize = (fragsize & 0xffff) | ((fragcount & 0x7fff) << 16);
+ ioctl (fd, SNDCTL_DSP_SETFRAGMENT, &fragsize); /* Ignore errors */
+ }
+
+ tmp = fmt;
+ if (ioctl (fd, SNDCTL_DSP_SETFMT, &tmp) == -1)
+ {
+ perror ("SNDCTL_DSP_SETFMT");
+ exit (EXIT_FAILURE);
+ }
+
+ if (tmp != fmt)
+ {
+ fprintf (stderr,
+ "Failed to select the requested sample format (%x, %x)\n", fmt,
+ tmp);
+ exit (EXIT_FAILURE);
+ }
+
+ tmp = channels;
+ if (ioctl (fd, SNDCTL_DSP_CHANNELS, &tmp) == -1)
+ {
+ perror ("SNDCTL_DSP_CHANNELS");
+ exit (EXIT_FAILURE);
+ }
+
+ if (tmp != channels)
+ {
+ fprintf (stderr, "Failed to select the requested #channels (%d, %d)\n",
+ channels, tmp);
+ exit (EXIT_FAILURE);
+ }
+
+ tmp = speed;
+ if (ioctl (fd, SNDCTL_DSP_SPEED, &tmp) == -1)
+ {
+ perror ("SNDCTL_DSP_SPEED");
+ exit (EXIT_FAILURE);
+ }
+
+ if (tmp != speed)
+ {
+ fprintf (stderr, "Failed to select the requested rate (%d, %d)\n",
+ speed, tmp);
+ exit (EXIT_FAILURE);
+ }
+
+ if (is_input)
+ {
+ if (ioctl (fd, SNDCTL_DSP_GETISPACE, &bi) == -1)
+ {
+ perror ("SNDCTL_DSP_GETISPACE");
+ exit (EXIT_FAILURE);
+ }
+ }
+ else
+ {
+ if (ioctl (fd, SNDCTL_DSP_GETOSPACE, &bi) == -1)
+ {
+ perror ("SNDCTL_DSP_GETOSPACE");
+ exit (EXIT_FAILURE);
+ }
+ }
+
+ buffer_size = bi.fragsize * bi.fragstotal;
+
+ data_rate = speed * channels * (bits / 8);
+
+ printf ("fragsize %d, nfrags %d, total buffer %d (bytes)\n", bi.fragsize,
+ bi.fragstotal, buffer_size);
+
+ if (write_size == 0)
+ write_size = bi.fragsize;
+
+ printf ("Sample rate rate %d Hz, channels %d, bits %d\n", speed, channels,
+ bits);
+ printf ("Data rate %d bytes / second\n", data_rate);
+ printf ("Fragment time %d ms\n",
+ (1000 * bi.fragsize + data_rate / 2) / data_rate);
+ printf ("Buffer time %d ms\n",
+ (1000 * buffer_size + data_rate / 2) / data_rate);
+ printf ("Write size %d bytes, write time %d ms\n", write_size,
+ (1000 * write_size + data_rate / 2) / data_rate);
+
+ printf ("\n");
+ printf ("*** Starting test %d (%s)\n", mode, name);
+ printf ("\n");
+
+ prev_time = get_usecs();
+
+ run_test (fd);
+ exit (0);
+}
diff --git a/tutorials/sndkit/tests/iosync.c b/tutorials/sndkit/tests/iosync.c
new file mode 100644
index 0000000..fc9aa52
--- /dev/null
+++ b/tutorials/sndkit/tests/iosync.c
@@ -0,0 +1,232 @@
+/*
+ * Purpose: Measuring the hardware level latencies.
+ * Copyright (C) 4Front Technologies, 2002-2004. Released under GPLv2/CDDL.
+ *
+ * Description:
+ * This simple program was once used to measure internal latencies of some
+ * sound cards. It was written in great hurry so don't use it as a program
+ * template. Error checking is completely inadequate.
+ *
+ * This program requires that the output of the sound card is connected
+ * to the line in of the same card.
+ *
+ * The program will play a short spike and then measure how many "silent "
+ * samples there are in the input before the spike is seen. You can
+ * get the delay in seconds by dividing this sample count by the
+ * sample rate.
+ *
+ * This is the total delay value that includes both the output and input
+ * delays. There is no way to estimate how large part of it is caused by
+ * input or output.
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <soundcard.h>
+#ifdef _AIX
+#include <sys/select.h>
+#endif
+
+int frag_size = 0;
+static int pos = 0;
+
+static int
+open_device (char *name, int mode)
+{
+ int tmp, fd;
+
+ int frag = 0x0020000b; /* 32 fragments of 2^11=2048 bytes */
+
+ if ((fd = open (name, mode, 0)) == -1)
+ {
+ perror (name);
+ exit (-1);
+ }
+
+/*
+ * WARNING!!!!!!!!!!!
+ *
+ * The following code makes ioctl calls without verifying that the
+ * result was OK. Don't do this at home.
+ */
+ if (ioctl (fd, SNDCTL_DSP_SETFRAGMENT, &frag) == -1)
+ perror ("SNDCTL_DSP_SETFRAGMENT");
+
+ tmp = 1;
+ if (ioctl (fd, SNDCTL_DSP_CHANNELS, &tmp) == -1)
+ perror ("SNDCTL_DSP_CHANNELS");
+
+ tmp = AFMT_S16_NE;
+ if (ioctl (fd, SNDCTL_DSP_SETFMT, &tmp) == -1)
+ perror ("SNDCTL_DSP_SETFMT");
+
+ tmp = 48000;
+ if (ioctl (fd, SNDCTL_DSP_SPEED, &tmp) == -1) /* And #channels & #bits if required */
+ perror ("SNDCTL_DSP_SPEED");
+
+ if (ioctl (fd, SNDCTL_DSP_GETBLKSIZE, &frag_size) == -1)
+ perror ("SNDCTL_DSP_GETBLKSIZE");
+
+ tmp = 0;
+ if (ioctl (fd, SNDCTL_DSP_SETTRIGGER, &tmp) == -1)
+ perror ("SNDCTL_DSP_SETTRIGGER");
+/*
+ * WARNING!!!!!!!!!!!
+ *
+ * The above code makes ioctl calls without verifying that the
+ * result was OK. Don't do this at home.
+ */
+
+ return fd;
+}
+
+static void
+gen_spike (short *buf)
+{
+ int i, p;
+
+ for (i = 0; i < 30; i++)
+ {
+ p = (i / 4) % 3;
+ buf[i] = (p - 1) * 8 * 1024;
+ }
+}
+
+static void
+check_buf (short *buf, int l)
+{
+ int i;
+
+ for (i = 0; i < l; i++)
+ {
+ int v;
+
+ v = buf[i];
+ if (v < 0)
+ v = -v;
+ if (v > 4096)
+ printf ("%d = %d\n", pos, v);
+ pos++;
+ }
+}
+
+int
+main (int argc, char *argv[])
+{
+ int fd_in, fd_out;
+ char *name_in = "/dev/dsp", *name_out = NULL;
+
+ int tmp;
+ int have_data = 0;
+
+ char buf[128 * 1024];
+
+ int n, l;
+
+ fd_set reads, writes;
+
+ if (argc > 1)
+ name_in = argv[1];
+ if (argc > 2)
+ name_out = argv[2];
+
+ if (name_out != NULL)
+ {
+ fd_in = open_device (name_in, O_RDONLY);
+ fd_out = open_device (name_out, O_WRONLY);
+ }
+ else
+ {
+ fd_in = fd_out == open_device (name_in, O_RDWR);
+ }
+
+ memset (buf, 0, frag_size);
+ gen_spike ((short *) buf);
+ write (fd_out, buf, frag_size);
+
+ if (fd_out != fd_in)
+ {
+ tmp = PCM_ENABLE_INPUT;
+ ioctl (fd_in, SNDCTL_DSP_SETTRIGGER, &tmp);
+ tmp = PCM_ENABLE_OUTPUT;
+ ioctl (fd_out, SNDCTL_DSP_SETTRIGGER, &tmp);
+ }
+ else
+ {
+ tmp = PCM_ENABLE_INPUT | PCM_ENABLE_OUTPUT;
+ ioctl (fd_in, SNDCTL_DSP_SETTRIGGER, &tmp);
+ }
+
+ while (1)
+ {
+ struct timeval time;
+
+ FD_ZERO (&reads);
+ FD_ZERO (&writes);
+
+ FD_SET (fd_out, &writes);
+ FD_SET (fd_in, &reads);
+
+ time.tv_sec = 0;
+ time.tv_usec = 100000;
+ if ((l = select (fd_out + 1, &reads, &writes, NULL, &time)) == -1)
+ {
+ perror ("select");
+ exit (-1);
+ }
+ if (l == 0)
+ printf ("Timeout ");
+
+ if (FD_ISSET (fd_in, &reads))
+ {
+ struct audio_buf_info info;
+
+ if (ioctl (fd_in, SNDCTL_DSP_GETISPACE, &info) == -1)
+ {
+ perror ("select");
+ exit (-1);
+ }
+
+ n = info.bytes;
+ if (n <= 0)
+ {
+ printf ("Error: NREAD=%d\n", n);
+ exit (-1);
+ }
+
+ l = read (fd_in, buf, n);
+ if (l > 0)
+ have_data = 1;
+ else
+ perror ("read");
+ check_buf ((short *) buf, l / 2);
+ }
+
+ if (FD_ISSET (fd_out, &writes))
+ {
+ int i;
+
+ struct audio_buf_info info;
+
+ if (ioctl (fd_out, SNDCTL_DSP_GETOSPACE, &info) == -1)
+ {
+ perror ("select");
+ exit (-1);
+ }
+
+ n = info.bytes;
+
+ /* printf("Write %d", l); */
+ l = n;
+ memset (buf, 0, l);
+ if (write (fd_out, buf, l) == l);
+ have_data = 0;
+ }
+ }
+
+ exit (0);
+}
diff --git a/tutorials/sndkit/tests/multich16.c b/tutorials/sndkit/tests/multich16.c
new file mode 100644
index 0000000..b575a02
--- /dev/null
+++ b/tutorials/sndkit/tests/multich16.c
@@ -0,0 +1,126 @@
+/*
+ * Multi channel audio test.
+ *
+ * This program is intended to test playback of 16 bit samples using 4 or more
+ * channels at 48000 Hz. The program plays sine wave pulses sequentially on
+ * channels 0 to N-1.
+ *
+ * Arguments:
+ *
+ * 1: Number of channelts (default is 8).
+ * 2: Audio device (/dev/dsp by default).
+ * 3-N: Options
+ * -b Bypass virtual mixer
+ * -r Raw mode (disables automatic sample rate/format conversions)
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <soundcard.h>
+
+static int sinedata[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
+};
+
+int
+main (int argc, char *argv[])
+{
+ char *dev = "/dev/dsp";
+ int fd, l, i, n = 0, ch, p = 0, phase = 0, arg, channels, srate, thisch = 0;
+ int tick = 0;
+ int nch = 8;
+ int bypass_vmix=0;
+ int disable_format_conversions=0;
+
+ short buf[1024];
+
+ if (argc > 1)
+ if (sscanf (argv[1], "%d", &nch) != 1)
+ nch = 2;
+
+ if (argc > 2)
+ dev = argv[2];
+
+ for (i=3;i<argc;i++)
+ if (argv[i][0]=='-')
+ switch (argv[i][1])
+ {
+ case 'b': /* Bypass virtual mixer */
+ bypass_vmix = O_EXCL;
+ break;
+
+ case 'r': /* Use raw mode (disable automatic format conversions) */
+ disable_format_conversions=1;
+ break;
+ }
+
+ if ((fd = open (dev, O_WRONLY|bypass_vmix, 0)) == -1)
+ {
+ perror (dev);
+ exit (-1);
+ }
+
+ if (disable_format_conversions)
+ {
+ arg=0;
+ ioctl(fd, SNDCTL_DSP_COOKEDMODE, &arg);
+ }
+
+ arg = nch;
+ if (ioctl (fd, SNDCTL_DSP_CHANNELS, &arg) == -1)
+ perror ("SNDCTL_DSP_CHANNELS");
+ channels = arg;
+ fprintf (stderr, "Channels %d\n", arg);
+
+ arg = AFMT_S16_NE;
+ if (ioctl (fd, SNDCTL_DSP_SETFMT, &arg) == -1)
+ perror ("SNDCTL_DSP_SETFMT");
+ fprintf (stderr, "Format %x\n", arg);
+
+ arg = 48000;
+ if (ioctl (fd, SNDCTL_DSP_SPEED, &arg) == -1)
+ perror ("SNDCTL_DSP_SPEED");
+ printf ("Using sampling rate %d\n", arg);
+ srate = arg;
+
+ while (1)
+ {
+ for (ch = 0; ch < channels; ch++)
+ {
+ if (ch == thisch)
+ {
+ buf[p] = sinedata[phase];
+ phase = (phase + 1 + (ch / 2)) % 48;
+ if (phase == 0 && tick > 10 * channels)
+ {
+ thisch = (thisch + 1) % channels;
+ tick = 0;
+ }
+ }
+ else
+ buf[p] = 0;
+
+ p++;
+
+ if (p >= sizeof (buf) / 2)
+ {
+ if (write (fd, buf, p * 2) != p * 2)
+ perror ("write");
+ p = 0;
+ tick++;
+
+ }
+ }
+ n++;
+ }
+
+ exit (0);
+}
diff --git a/tutorials/sndkit/tests/multich32.c b/tutorials/sndkit/tests/multich32.c
new file mode 100644
index 0000000..ab39297
--- /dev/null
+++ b/tutorials/sndkit/tests/multich32.c
@@ -0,0 +1,126 @@
+/*
+ * Multi channel audio test.
+ *
+ * This program is intended to test playback of 16 bit samples using 4 or more
+ * channels at 48000 Hz. The program plays sine wave pulses sequentially on
+ * channels 0 to N-1.
+ *
+ * Arguments:
+ *
+ * 1: Number of channelts (default is 8).
+ * 2: Audio device (/dev/dsp by default).
+ * 3-N: Options
+ * -b Bypass virtual mixer
+ * -r Raw mode (disables automatic sample rate/format conversions)
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <soundcard.h>
+
+static int sinedata[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
+};
+
+int
+main (int argc, char *argv[])
+{
+ char *dev = "/dev/dsp";
+ int fd, l, i, n = 0, ch, p = 0, phase = 0, arg, channels, srate, thisch = 0;
+ int tick = 0;
+ int nch = 8;
+ int bypass_vmix=0;
+ int disable_format_conversions=0;
+
+ int buf[32*1024];
+
+ if (argc > 1)
+ if (sscanf (argv[1], "%d", &nch) != 1)
+ nch = 2;
+
+ if (argc > 2)
+ dev = argv[2];
+
+ for (i=3;i<argc;i++)
+ if (argv[i][0]=='-')
+ switch (argv[i][1])
+ {
+ case 'b': /* Bypass virtual mixer */
+ bypass_vmix = O_EXCL;
+ break;
+
+ case 'r': /* Use raw mode (disable automatic format conversions) */
+ disable_format_conversions=1;
+ break;
+ }
+
+ if ((fd = open (dev, O_WRONLY|bypass_vmix, 0)) == -1)
+ {
+ perror (dev);
+ exit (-1);
+ }
+
+ if (disable_format_conversions)
+ {
+ arg=0;
+ ioctl(fd, SNDCTL_DSP_COOKEDMODE, &arg);
+ }
+
+ arg = nch;
+ if (ioctl (fd, SNDCTL_DSP_CHANNELS, &arg) == -1)
+ perror ("SNDCTL_DSP_CHANNELS");
+ channels = arg;
+ fprintf (stderr, "Channels %d\n", arg);
+
+ arg = AFMT_S32_NE;
+ if (ioctl (fd, SNDCTL_DSP_SETFMT, &arg) == -1)
+ perror ("SNDCTL_DSP_SETFMT");
+ fprintf (stderr, "Format %x\n", arg);
+
+ arg = 48000;
+ if (ioctl (fd, SNDCTL_DSP_SPEED, &arg) == -1)
+ perror ("SNDCTL_DSP_SPEED");
+ printf ("Using sampling rate %d\n", arg);
+ srate = arg;
+
+ while (1)
+ {
+ for (ch = 0; ch < channels; ch++)
+ {
+ if (ch == thisch)
+ {
+ buf[p] = sinedata[phase]<<16;
+ phase = (phase + 1 + (ch / 2)) % 48;
+ if (phase == 0 && tick > 10 * channels)
+ {
+ thisch = (thisch + 1) % channels;
+ tick = 0;
+ }
+ }
+ else
+ buf[p] = 0;
+
+ p++;
+
+ if (p >= sizeof (buf) / 4)
+ {
+ if (write (fd, buf, p * 4) != p * 4)
+ perror ("write");
+ p = 0;
+ tick++;
+
+ }
+ }
+ n++;
+ }
+
+ exit (0);
+}
diff --git a/tutorials/sndkit/tests/musicin.c b/tutorials/sndkit/tests/musicin.c
new file mode 100644
index 0000000..651e27a
--- /dev/null
+++ b/tutorials/sndkit/tests/musicin.c
@@ -0,0 +1,222 @@
+/*
+ * Purpose: A program that demonstrates MIDI input with /dev/music (obsolete)
+ * Copyright (C) 4Front Technologies, 2002-2004. Released under GPLv2/CDDL.
+ *
+ * Description:
+ * This program was supposed to be a sample program for doing MIDI input
+ * with the /dev/music interface. However it has not much use since the
+ * /dev/music interface is now obsoleted.
+ *
+ * The /dev/midi interface is recommended in the new applications. Please
+ * see the "{!link MIDI}" section of the OSS Developer's manual.
+ */
+#include <soundcard.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+void
+decode_event (unsigned char *ev)
+{
+ int i;
+
+ for (i = 0; i < 8; i++)
+ {
+ printf ("%02x ", ev[i]);
+ }
+
+ printf (": ");
+
+ switch (ev[0])
+ {
+ case EV_CHN_VOICE:
+ printf ("EV_CHN_VOICE(%d)\t| ", ev[1]);
+ switch (ev[2])
+ {
+ case MIDI_NOTEON:
+ printf ("Note On ch%d note%d vel%d", ev[3], ev[4], ev[5]);
+ break;
+
+ case MIDI_NOTEOFF:
+ printf ("Note Off ch%d note%d vel%d", ev[3], ev[4], ev[5]);
+ break;
+
+ case MIDI_KEY_PRESSURE:
+ printf ("KPressure ch%d note%d vel%d", ev[3], ev[4], ev[5]);
+ break;
+
+ default:
+ printf ("*** Unknown ***");
+ }
+ break;
+
+ case EV_CHN_COMMON:
+ printf ("EV_CHN_COMMON\tdev%d\t| ", ev[1]);
+ switch (ev[2])
+ {
+ case MIDI_CHN_PRESSURE:
+ printf ("MIDI_CHN_PRESSURE ch%d %d", ev[3], ev[4]);
+ break;
+ case MIDI_PGM_CHANGE:
+ printf ("MIDI_PGM_CHANGE ch%d %d", ev[3], ev[4]);
+ break;
+ case MIDI_CTL_CHANGE:
+ printf ("MIDI_CTL_CHANGE ch%d %d,%d", ev[3], ev[4],
+ *(short *) &ev[6]);
+ break;
+ case MIDI_PITCH_BEND:
+ printf ("MIDI_CTL_CHANGE ch%d %d", ev[3], *(short *) &ev[6]);
+ break;
+ default:
+ printf ("*** Unknown ***");
+ }
+ break;
+
+ case EV_SYSEX:
+ printf ("EV_SYSEX\tdev%d: ", ev[1]);
+ for (i = 2; i < 8; i++)
+ printf ("%02x ", ev[i]);
+ break;
+
+ case EV_TIMING:
+ printf ("EV_TIMING\t\t| ");
+ switch (ev[1])
+ {
+ case TMR_START:
+ printf ("TMR_START\t");
+ break;
+ case TMR_STOP:
+ printf ("TMR_STOP\t");
+ break;
+ case TMR_CONTINUE:
+ printf ("TMR_CONTINUE\t");
+ break;
+ case TMR_WAIT_ABS:
+ printf ("TMR_WAIT_ABS\t%10u", *(unsigned int *) &ev[4]);
+ break;
+ case TMR_WAIT_REL:
+ printf ("TMR_WAIT_REL\t%10u", *(unsigned int *) &ev[4]);
+ break;
+ case TMR_ECHO:
+ printf ("TMR_ECHO\t%10u", *(unsigned int *) &ev[4]);
+ break;
+ case TMR_TEMPO:
+ printf ("TMR_TEMPO\t%10u", *(unsigned int *) &ev[4]);
+ break;
+ case TMR_SPP:
+ printf ("TMR_SPP\t%10u", *(unsigned int *) &ev[4]);
+ break;
+ case TMR_TIMESIG:
+ printf ("TMR_TIMESIG\t%10u", *(unsigned int *) &ev[4]);
+ break;
+ }
+ break;
+
+ case EV_SEQ_LOCAL:
+ printf ("EV_SEQ_LOCAL\t*** Should not happen ***");
+ break;
+
+ case EV_SYSTEM:
+ printf ("EV_SYSTEM(%d)\t\t| ", ev[1]);
+ switch (ev[2])
+ {
+ case 0xf0:
+ printf ("SysEx *** Should not happen ***");
+ break;
+
+ case 0xf1:
+ printf ("MTC Qframe %02x", ev[3]);
+ break;
+
+ case 0xf2:
+ printf ("Songpos ptr %02x,%02x", ev[3], ev[4]);
+ break;
+
+ case 0xf3:
+ printf ("Song select %02x", ev[3]);
+ break;
+
+ case 0xf4:
+ case 0xf5:
+ printf ("*** Undefined ***");
+ break;
+
+ case 0xf6:
+ printf ("Tune request");
+ break;
+
+ case 0xf7:
+ printf ("EOX");
+ break;
+ case 0xf8:
+ printf ("Timing clock");
+ break;
+ case 0xf9:
+ printf ("** Undefined ***");
+ break;
+ case 0xfa:
+ printf ("Start");
+ break;
+ case 0xfb:
+ printf ("Continue");
+ break;
+ case 0xfc:
+ printf ("Stop");
+ break;
+ case 0xfd:
+ printf ("** Undefined ***");
+ break;
+ case 0xfe:
+ printf ("Active sensing");
+ break;
+ case 0xff:
+ printf ("SYSTEM RESET");
+ break;
+ }
+ break;
+
+ default:
+ printf ("*** Unknown event type ***");
+ }
+ printf ("\n");
+}
+
+int
+main (int argc, int *argv[])
+{
+ int fd, l, i;
+ unsigned char buf[4096];
+
+ if ((fd = open ("/dev/music", O_RDONLY, 0)) == -1)
+ {
+ perror ("/dev/music");
+ exit (-1);
+ }
+
+ if (ioctl (fd, SNDCTL_SEQ_ACTSENSE_ENABLE, 0) == -1)
+ {
+ perror ("/dev/music ACTSENSE_ENABLE");
+ exit (-1);
+ }
+
+ if (ioctl (fd, SNDCTL_SEQ_TIMING_ENABLE, 0) == -1)
+ {
+ perror ("/dev/music TIMING_ENABLE");
+ exit (-1);
+ }
+
+ if (ioctl (fd, SNDCTL_SEQ_RT_ENABLE, 0) == -1)
+ {
+ perror ("/dev/music RT_ENABLE");
+ exit (-1);
+ }
+
+ while ((l = read (fd, buf, sizeof (buf))) != -1)
+ {
+ for (i = 0; i < l; i += 8)
+ {
+ decode_event (&buf[i]);
+ }
+ }
+}
diff --git a/tutorials/sndkit/tests/seltest2.c b/tutorials/sndkit/tests/seltest2.c
new file mode 100644
index 0000000..d4e3ab8
--- /dev/null
+++ b/tutorials/sndkit/tests/seltest2.c
@@ -0,0 +1,113 @@
+/*
+ Purpose: This program has been used to verify that the select() call works
+ * Copyright (C) 4Front Technologies, 2002-2004. Released under GPLv2/CDDL.
+ *
+ * Description:
+ * This program opens an audio device and then just
+ * copies input to output. Select is used for flow control.
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <soundcard.h>
+#ifdef _AIX
+#include <sys/select.h>
+#endif
+
+int
+main (int agrc, char *argv[])
+{
+ int fd;
+
+ int tmp;
+
+ char buf[128 * 1024];
+
+ int have_data = 0;
+ int n, l;
+
+ int frag = 0x00200008; /* 32 fragments of 2^8=256 bytes */
+
+ fd_set reads, writes;
+
+ close (0);
+
+ if ((fd = open ("/dev/dsp", O_RDWR, 0)) == -1)
+ {
+ perror ("/dev/dsp open");
+ exit (-1);
+ }
+
+ ioctl (fd, SNDCTL_DSP_SETFRAGMENT, &frag);
+
+/*
+ * Set just the sampling tahe. Use the default format. We do not do any
+ * error checking (maybe not so good idea) because we don't care what
+ * the sampling rate really is.
+ */
+ tmp = 48000;
+ ioctl (fd, SNDCTL_DSP_SPEED, &tmp);
+
+ while (1)
+ {
+ struct timeval time;
+
+ FD_ZERO (&reads);
+ FD_ZERO (&writes);
+
+ if (have_data)
+ FD_SET (fd, &writes);
+ else
+ 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))
+ {
+ struct audio_buf_info info;
+
+ if (ioctl (fd, SNDCTL_DSP_GETISPACE, &info) == -1)
+ {
+ perror ("select");
+ exit (-1);
+ }
+
+ n = info.bytes;
+
+ l = read (fd, buf, n);
+ if (l > 0)
+ have_data = 1;
+ }
+
+ if (FD_ISSET (fd, &writes))
+ {
+ int i;
+
+ struct audio_buf_info info;
+
+ if (ioctl (fd, SNDCTL_DSP_GETOSPACE, &info) == -1)
+ {
+ perror ("select");
+ exit (-1);
+ }
+
+ n = info.bytes;
+
+ printf ("Write %d\n", l);
+ write (fd, buf, l);
+ printf ("OK");
+ have_data = 0;
+ }
+ }
+
+ exit (0);
+}
diff --git a/tutorials/sndkit/tests/singen.inc b/tutorials/sndkit/tests/singen.inc
new file mode 100644
index 0000000..8e2af93
--- /dev/null
+++ b/tutorials/sndkit/tests/singen.inc
@@ -0,0 +1,38 @@
+#if 0
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+{
+ static unsigned int p[10] = { 0 };
+ int *pp;
+ int i;
+
+ 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 < dmap->grc3state[ch].outsz; i++)
+ {
+ if (0 * ctr)
+ {
+ *((unsigned int *) (p2) + i * channels + ch) =
+ sinebuf[p[ch] % 48] * 64 * 0;
+ p[ch]++;
+ }
+ }
+}
+#endif
diff --git a/tutorials/sndkit/tests/spdif_in_debug.c b/tutorials/sndkit/tests/spdif_in_debug.c
new file mode 100644
index 0000000..20963eb
--- /dev/null
+++ b/tutorials/sndkit/tests/spdif_in_debug.c
@@ -0,0 +1,303 @@
+/*
+ * Purpose: A program that prints the S/PDIF receiver status.
+ * Copyright (C) 4Front Technologies, 2002-2004. Released under GPLv2/CDDL.
+ *
+ * Description:
+ * This program demonstrates use of the {!nlink SNDCTL_DSP_READCTL}
+ * call. It's actually a low cost digital (S/PDIF) input analyzer.
+ *
+ * {!notice This program will work just with a bunch of sound cards because
+ * most devices are not able to return this information. AT this moment the
+ * only card that is verified is M Audio Audiophile 2496. It's possible that
+ * some other M Audio Delta models work too. It's almost certain that
+ * "ordinary" sound cards will never have a digital receiver chip capable to
+ * return this information.}
+ *
+ * Please read the "{!link spdif_control}" section of the OSS Developer's
+ * manual for more info.
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <time.h>
+#include <soundcard.h>
+
+#undef SHOW_DATA
+#define SHOW_STATUS
+
+#define RATE 48000
+#define CHANNELS 2
+#define BUFSZ (CHANNELS*RATE)
+
+#ifdef SHOW_STATUS
+static int
+xbits (unsigned char b, int first, int last)
+{
+ int v, i;
+
+ v = 0;
+
+ for (i = first; i <= last; i++)
+ {
+ v <<= 1;
+ if (b & (1 << i))
+ v |= 1;
+ }
+
+ return v;
+}
+
+static void
+decode_pro_mode (unsigned char *bits)
+{
+ printf ("Professional mode (PRO=1)\n");
+}
+
+static void
+decode_consumer_mode (unsigned char *bits)
+{
+ int tmp, tmp2, tmp3;
+
+ printf ("Consumer mode (PRO=0)\n");
+
+ printf ("Byte 00=%02x: ", bits[0]);
+ if (bits[0] & 0x02)
+ printf ("Data (not audio) ");
+ else
+ printf ("Audio (not data) ");
+ if (bits[0] & 0x04)
+ printf ("Copy permitted ");
+ else
+ printf ("Copy inhibited ");
+
+ tmp = xbits (bits[0], 3, 5);
+ if (bits[0] & 0x02)
+ printf ("Non-audio=0x%x ", tmp);
+ else
+ printf ("Pre-emph=0x%x ", tmp);
+
+ tmp = xbits (bits[0], 6, 7);
+ printf ("Mode=0x%x ", tmp);
+ printf ("\n");
+
+ printf ("Byte 01=%02x: ", bits[1]);
+ tmp = xbits (bits[1], 0, 2);
+ tmp2 = xbits (bits[1], 3, 6);
+ tmp3 = xbits (bits[1], 7, 7);
+
+ printf ("Category code = %x:%x, L=%d ", tmp, tmp2, tmp3);
+ printf ("\n");
+
+ printf ("Byte 02=%02x: ", bits[2]);
+ tmp = xbits (bits[2], 0, 3);
+ tmp2 = xbits (bits[2], 4, 7);
+
+ printf ("Source number=0x%x Channel number=0x%x ", tmp, tmp2);
+ printf ("\n");
+
+ printf ("Byte 03=%02x: ", bits[3]);
+ tmp = xbits (bits[3], 0, 3);
+ tmp2 = xbits (bits[3], 4, 5);
+
+ printf ("Sample rate=0x%x Clock accuracy=0x%x ", tmp, tmp2);
+ printf ("\n");
+
+}
+#endif
+
+int
+main (int argc, char *argv[])
+{
+ char *devname = "/dev/dsp";
+ unsigned short buf[BUFSZ], expected = 0;
+ int fd, parm, l, i;
+ int bcount = 0;
+
+ if (argc > 1)
+ devname = argv[1];
+
+ if ((fd = open (devname, O_RDONLY, 0)) == -1)
+ {
+ perror (devname);
+ exit (-1);
+ }
+
+ parm = AFMT_S16_NE;
+ if (ioctl (fd, SNDCTL_DSP_SETFMT, &parm) == -1)
+ {
+ perror ("SETFMT");
+ close (fd);
+ exit (-1);
+ }
+
+ if (parm != AFMT_S16_NE)
+ {
+ printf
+ ("Error: 16 bit sample format is not supported by the device\n");
+ printf ("%08x/%08x\n", parm, AFMT_S16_LE);
+ close (fd);
+ exit (-1);
+ }
+
+ parm = CHANNELS;
+ if (ioctl (fd, SNDCTL_DSP_CHANNELS, &parm) == -1)
+ {
+ perror ("CHANNELS");
+ close (fd);
+ exit (-1);
+ }
+
+ parm = RATE;
+ if (ioctl (fd, SNDCTL_DSP_SPEED, &parm) == -1)
+ {
+ perror ("SPEED");
+ close (fd);
+ exit (-1);
+ }
+
+ if (parm != RATE)
+ {
+ printf
+ ("Warning: %d Hz sampling rate is not supported by the device. Will use %d)\n",
+ RATE, parm);
+ }
+
+#ifdef SHOW_DATA
+ while ((l = read (fd, buf, sizeof (buf))) > 0)
+#else
+ while (1)
+#endif
+ {
+#ifdef SHOW_STATUS
+ time_t t;
+ oss_digital_control c;
+
+ c.valid = VAL_CBITIN | VAL_ISTATUS;
+
+ if (ioctl (fd, SNDCTL_DSP_READCTL, &c) == -1)
+ {
+ perror ("SNDCTL_DSP_READCTL");
+ exit (-1);
+ }
+
+ time (&t);
+ printf ("\n%s\n", ctime (&t));
+
+ if (c.valid & VAL_ISTATUS)
+ {
+ switch (c.in_locked)
+ {
+ case LOCK_NOT_INDICATED:
+ printf ("Receiver locked: Status unknown\n");
+ break;
+ case LOCK_UNLOCKED:
+ printf ("receiver locked: *** NOT LOCKED ***\n");
+ break;
+ case LOCK_LOCKED:
+ printf ("receiver locked: Locked OK\n");
+ break;
+ }
+
+ switch (c.in_quality)
+ {
+ case IN_QUAL_NOT_INDICATED:
+ printf ("Signal quality: Unknown\n");
+ break;
+ case IN_QUAL_POOR:
+ printf ("Signal quality: *** POOR ***\n");
+ break;
+ case IN_QUAL_GOOD:
+ printf ("Signal quality: Good\n");
+ break;
+ }
+
+ switch (c.in_vbit)
+ {
+ case VBIT_NOT_INDICATED:
+ printf ("V-bit: Unknown\n");
+ break;
+ case VBIT_ON:
+ printf ("V-bit: On (not valid audio)\n");
+ break;
+ case VBIT_OFF:
+ printf ("V-bit: Off (valid audio signal)\n");
+ break;
+ }
+
+ switch (c.in_data)
+ {
+ case IND_UNKNOWN:
+ printf ("Audio/data: Unknown\n");
+ break;
+ case IND_AUDIO:
+ printf ("Audio/data: Audio\n");
+ break;
+ case IND_DATA:
+ printf ("Audio/data: Data\n");
+ break;
+ }
+
+ printf ("Errors: ");
+ if (c.in_errors & INERR_CRC)
+ printf ("CRC ");
+ if (c.in_errors & INERR_QCODE_CRC)
+ printf ("QCODE_CRC ");
+ if (c.in_errors & INERR_PARITY)
+ printf ("PARITY ");
+ if (c.in_errors & INERR_BIPHASE)
+ printf ("BIPHASE ");
+ printf ("\n");
+ }
+ else
+ printf ("No input status information available\n");
+
+ if (c.valid & VAL_CBITIN && c.in_locked != LOCK_UNLOCKED)
+ {
+ printf ("\n");
+ printf ("Control bits: ");
+ for (i = 0; i < 24; i++)
+ printf ("%02x ", c.cbitin[i]);
+ printf ("\n");
+
+ if (c.cbitin[0] & 0x01)
+ decode_pro_mode (c.cbitin);
+ else
+ decode_consumer_mode (c.cbitin);
+ }
+ else
+ printf ("No incoming control bit information available\n");
+#endif
+
+#ifdef SHOW_DATA
+# ifdef SHOW_STATUS
+ if (c.in_locked != LOCK_UNLOCKED)
+# endif
+ {
+ for (i = 0; i < l / 2; i++)
+ {
+ if (buf[i] == expected)
+ {
+ printf ("%04x\n", buf[i]);
+ }
+ else
+ {
+ printf ("Error %04x != %04x (%4x), c=%d/%x\n", buf[i],
+ expected, buf[i] ^ expected, bcount, bcount);
+ }
+ expected = buf[i] + 1;
+ bcount++;
+ }
+ }
+
+#else
+ sleep (1);
+#endif
+ }
+
+ perror (devname);
+
+ exit (-1);
+}
diff --git a/tutorials/sndkit/tests/sweepdown.c b/tutorials/sndkit/tests/sweepdown.c
new file mode 100644
index 0000000..8006053
--- /dev/null
+++ b/tutorials/sndkit/tests/sweepdown.c
@@ -0,0 +1,126 @@
+/*
+ * Purpose: Plays a funny synthetic engine stop sound
+ * Copyright (C) 4Front Technologies, 2002-2004. Released under GPLv2/CDDL.
+ *
+ * Description:
+ * This program was supposed to do a frequency sweep down from sample_rate/2
+ * to 0 Hz. However due to some arithmetic problem the result was much more
+ * interesting.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <soundcard.h>
+
+char *dspname = "/dev/dsp";
+
+int fd;
+
+#define SIN_STEPS 48
+static short 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
+};
+
+#define F_SCALE (1000000)
+
+int phase = 0, freq = F_SCALE / 2;
+
+static void
+sweeper (int speed, int channels, int fragsize)
+{
+ short buf[4096], *p = buf;
+ int c, i, n, v, x;
+
+ fragsize /= 2 * channels;
+ speed /= 100;
+
+ for (i = 0; i < fragsize; i++)
+ {
+ x = (phase + F_SCALE / 2) / F_SCALE;
+ if (x < 0) x = 0;
+ v = sinebuf[x % SIN_STEPS] / 4;
+
+ phase = phase + freq * 480 / speed;
+
+ if (freq > 1000)
+ freq -= 3 * 480 / speed;
+ else
+ freq -= 1;
+
+ for (c = 0; c < channels; c++)
+ *p++ = v;
+
+ if (freq <= 0)
+ break;
+ }
+
+ write (fd, buf, i * 2 * channels);
+}
+
+int
+main (int argc, char *argv[])
+{
+ int bits, channels, speed, tmp;
+ int i, l;
+
+ if (argc == 2)
+ dspname = argv[1];
+
+
+ if ((fd = open (dspname, O_WRONLY, 0)) == -1)
+ {
+ perror (dspname);
+ exit (-1);
+ }
+
+ speed = 96000;
+ channels = 2;
+ bits = AFMT_S16_NE;
+
+ if (ioctl (fd, SNDCTL_DSP_CHANNELS, &channels) == -1)
+ {
+ perror ("SNDCTL_DSP_CHANNELS");
+ exit (-1);
+ }
+
+ if (ioctl (fd, SNDCTL_DSP_SETFMT, &bits) == -1)
+ {
+ perror ("SNDCTL_DSP_SETFMT");
+ exit (-1);
+ }
+
+ if (bits != AFMT_S16_NE)
+ {
+ fprintf (stderr,
+ "Device %s doesn't support 16 bit (native endian) format\n",
+ dspname);
+ exit (-1);
+ }
+
+ if (ioctl (fd, SNDCTL_DSP_SPEED, &speed) == -1)
+ {
+ perror ("SNDCTL_DSP_SPEED");
+ exit (-1);
+ }
+
+ if (ioctl (fd, SNDCTL_DSP_GETBLKSIZE, &tmp) == -1)
+ {
+ perror ("SNDCTL_DSP_GETBLKSIZE");
+ exit (-1);
+ }
+
+ printf ("Outputting sweep at %d Hz, %d channels, 16 bits\n", speed,
+ channels);
+ printf ("Fragment size is %d\n", tmp);
+
+ while (freq > 0)
+ sweeper (speed, channels, tmp);
+
+ exit (0);
+}
diff --git a/tutorials/sndkit/tests/sweepup.c b/tutorials/sndkit/tests/sweepup.c
new file mode 100644
index 0000000..de4c421
--- /dev/null
+++ b/tutorials/sndkit/tests/sweepup.c
@@ -0,0 +1,122 @@
+/*
+ * Purpose: A simple program that plays frequency sweep from 10 Hz to fs/2.
+ * Copyright (C) 4Front Technologies, 2002-2004. Released under GPLv2/CDDL.
+ *
+ * Description:
+ * This program plays a frequency seep up starting from 10 Hz and
+ * goes up to sample_rate/2.
+ */
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <soundcard.h>
+
+char *dspname = "/dev/dsp";
+
+int fd;
+
+int
+main (int argc, char *argv[])
+{
+ int bits, channels, speed, tmp;
+ int i, l, n;
+#define BUFFSIZE (64*1024)
+ short buf[BUFFSIZE];
+ int p, t;
+ double freq = 10.0 /* Hz */ , phase = 0.0;
+ int delay = 0;
+
+ if (argc > 1)
+ dspname = argv[1];
+
+ if (argc > 2)
+ delay = atoi (argv[2]);
+
+ if ((fd = open (dspname, O_WRONLY, 0)) == -1)
+ {
+ perror (dspname);
+ exit (-1);
+ }
+
+ speed = 48000;
+ channels = 2;
+ bits = AFMT_S16_NE;
+
+ if (ioctl (fd, SNDCTL_DSP_CHANNELS, &channels) == -1)
+ {
+ perror ("SNDCTL_DSP_CHANNELS");
+ exit (-1);
+ }
+
+ if (ioctl (fd, SNDCTL_DSP_SETFMT, &bits) == -1)
+ {
+ perror ("SNDCTL_DSP_SETFMT");
+ exit (-1);
+ }
+
+ if (bits != AFMT_S16_NE)
+ {
+ fprintf (stderr,
+ "Device %s doesn't support 16 bit (native endian) format\n",
+ dspname);
+ exit (-1);
+ }
+
+ if (ioctl (fd, SNDCTL_DSP_SPEED, &speed) == -1)
+ {
+ perror ("SNDCTL_DSP_SPEED");
+ exit (-1);
+ }
+
+ fprintf (stderr, "Outputting sweep at %d Hz, %d channels, 16 bits\n", speed,
+ channels);
+
+ n = 0;
+ p = 0;
+
+ while (1) /* Infinite loop */
+ {
+ int v;
+ double step;
+
+ v = sin (phase) * 16483.0;
+
+ for (i = 0; i < channels; i++)
+ {
+ buf[p++] = v;
+
+ if (p >= BUFFSIZE)
+ {
+// write (1, buf, p * 2);
+
+ if (write (fd, buf, p * 2) != p * 2)
+ {
+ perror ("write");
+ exit (-1);
+ }
+ if (delay > 0)
+ usleep (delay * 1000);
+ fprintf (stderr, "\r%d ", (int) (freq));
+ fflush (stderr);
+ p = 0;
+ }
+ }
+
+ step = 2.0 * M_PI * freq / (double) speed;
+
+ phase += step;
+
+ if (freq < (double) speed / 2.1)
+ freq *= 1.000002;
+ else
+ break;
+ }
+
+ fprintf (stderr, "\n");
+ if (p > 0)
+ write (fd, buf, p * 2);
+
+ exit (0);
+}
diff --git a/tutorials/sndkit/tests/synctest.c b/tutorials/sndkit/tests/synctest.c
new file mode 100644
index 0000000..d2ed77b
--- /dev/null
+++ b/tutorials/sndkit/tests/synctest.c
@@ -0,0 +1,96 @@
+/*
+ * Purpose: A program that demonstrates use of syncronization groups.
+ * Copyright (C) 4Front Technologies, 2002-2004. Released under GPLv2/CDDL.
+ *
+ * Description:
+ * This program opens three audio devices (hard coded in the program)
+ * and creates a syncronization group using {!nlink SNDCTL_DSP_SYNCGROUP}.
+ *
+ * Next it starts all the devices joined in the group simultaneously
+ * by calling {!nlink SNDCTL_DSP_SYNCSTART}. Finally it will keep copying
+ * audio input from the 3rd device to the other two ones.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <soundcard.h>
+
+#define MAX_DEV 10
+
+int
+main (int argc, char *argv[])
+{
+ int i, id, fd[MAX_DEV], ndevs = 0;
+ char buf[32768] = { 0 };
+
+ oss_syncgroup group;
+
+ group.id = 0;
+ group.mode = PCM_ENABLE_OUTPUT;
+
+/*
+ * Open the devices listed on command line
+ */
+ if (argc < 2)
+ exit (-1);
+
+ for (i = 1; i < argc; i++)
+ {
+ if ((fd[ndevs] = open (argv[i], O_WRONLY, 0)) == -1)
+ {
+ perror (argv[i]);
+ exit (-1);
+ }
+
+ if (ioctl (fd[ndevs], SNDCTL_DSP_SYNCGROUP, &group) == -1)
+ {
+ perror ("SNDCTL_DSP_SYNCGROUP");
+ exit (-1);
+ }
+/*
+ * Note! It is very important to write some data to all output devices
+ * between calling SNDCTL_DSP_SYNCGROUP and SNDCTL_DSP_SYNCSTART. Otherwise
+ * playback will not start properly. However do not write more data than
+ * there is room in device's DMA buffer. Recommended amount of prteload data
+ * is one full fragment.
+ *
+ * In applications that record audio, process it and then play back it's
+ * necessary to write two fragments of silence to the output device(s) before
+ * starting the group. Otherwise output device(s) will run out of data before
+ * the first read from the input device returns.
+ */
+
+ if (write (fd[ndevs], buf, sizeof (buf)) != sizeof (buf))
+ {
+ perror ("write");
+ exit (-1);
+ }
+
+ ndevs++;
+ }
+
+ printf ("Sync group %x created with %d devices\n", group.id, ndevs);
+
+
+ id = group.id;
+
+ if (ioctl (fd[0], SNDCTL_DSP_SYNCSTART, &id) == -1)
+ {
+ perror ("SNDCTL_DSP_SYNCSTART");
+ exit (-1);
+ }
+
+ while (1)
+ {
+ for (i = 0; i < ndevs; i++)
+ if (write (fd[i], buf, sizeof (buf)) != sizeof (buf))
+ {
+ perror ("write2");
+ exit (-1);
+ }
+ }
+
+ exit (0);
+}
diff --git a/tutorials/sndkit/userdev_demo/Makefile b/tutorials/sndkit/userdev_demo/Makefile
new file mode 100644
index 0000000..530adf9
--- /dev/null
+++ b/tutorials/sndkit/userdev_demo/Makefile
@@ -0,0 +1,6 @@
+CFLAGS=-I/usr/lib/oss/include/sys -I/usr/include/oss -g
+
+all: udserver
+
+clean:
+ rm -f *.o udserver
diff --git a/tutorials/sndkit/userdev_demo/udserver.c b/tutorials/sndkit/userdev_demo/udserver.c
new file mode 100644
index 0000000..eafe8c9
--- /dev/null
+++ b/tutorials/sndkit/userdev_demo/udserver.c
@@ -0,0 +1,285 @@
+/*
+ * Sample server program that uses the oss_userdev driver.
+ *
+ * Copyright (C) 4Front Technologies. Released under the BSD license.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/stat.h>
+
+/*
+ * OSS specific includes. Use correct -I setting when compiling. Typically
+ * -I/usr/lib/oss/include/sys or -I/usr/include/sys
+ */
+#include <soundcard.h>
+#include <oss_userdev_exports.h>
+
+#define SERVER_DEVNAME "/dev/oss/oss_userdev0/server"
+
+int server_fd = -1; /* File descriptor for the server side device. */
+
+static void
+terminator(int sig)
+{
+ wait();
+ exit(0);
+}
+
+static void
+create_mixer_interface(int fd)
+{
+ userdev_mixctl_t ctl;
+ userdev_mixgroup_t grp;
+ userdev_mixvalues_t rec;
+ int group;
+
+/*
+ * Create a slider on the top level
+ */
+
+ memset(&ctl, 0, sizeof(ctl));
+ strcpy(ctl.name, "volumitaz");
+ ctl.parent = 0; /* Device root group */
+ ctl.type = MIXT_STEREOSLIDER16;
+ ctl.flags = MIXF_READABLE|MIXF_WRITEABLE;
+ ctl.index = 0; /* Use position 0 of the value array */
+ ctl.maxvalue = 100;
+ ctl.offset = 0;
+ ctl.rgbcolor = OSS_RGB_RED;
+
+ if (ioctl(fd, USERDEV_CREATE_MIXCTL, &ctl)==-1)
+ {
+ perror("USERDEV_CREATE_MIXCTL");
+ return;
+ }
+
+/*
+ * Create a mixer group under the device root
+ */
+
+ memset(&grp, 0, sizeof(grp));
+
+ strcat(grp.name, "private");
+ grp.parent = 0;
+
+ if (ioctl(fd, USERDEV_CREATE_MIXGROUP, &grp)==-1)
+ {
+ perror("USERDEV_CREATE_MIXGROUP");
+ return;
+ }
+
+ group = grp.num;
+
+/*
+ * Create an enumerated control under the "private" group that was created above
+ */
+ memset(&ctl, 0, sizeof(ctl));
+ strcpy(ctl.name, "mode");
+ ctl.parent = group; /* See above */
+ ctl.type = MIXT_ENUM;
+ ctl.flags = MIXF_READABLE|MIXF_WRITEABLE;
+ ctl.index = 1; /* Use position 1 of the value array */
+ ctl.maxvalue = 4;
+
+ memset (&ctl.enum_present, 0xff, sizeof(ctl.enum_present)); /* Mark all choices active */
+
+ strcpy(ctl.enum_choises, "stall crawl cruise warp");
+
+ if (ioctl(fd, USERDEV_CREATE_MIXCTL, &ctl)==-1)
+ {
+ perror("USERDEV_CREATE_MIXCTL");
+ return;
+ }
+
+/*
+ * Finally set the initial values for all the controls
+ */
+ memset(&rec, 0, sizeof(rec)); /* Set all to zeroes */
+
+ rec.values[0] = 100 | (100<<16); // volumitaz = 100:100
+ rec.values[1] = 2; // private.mode = "cruise"
+
+/*
+ * Post the initial settings
+ */
+ if (ioctl(fd, USERDEV_SET_MIXERS, &rec)==-1)
+ {
+ perror("USERDEV_SET_MIXERS");
+ }
+}
+
+static void
+poll_mixer(int fd)
+{
+ static int prev_count=0;
+ int count;
+
+ if (ioctl(fd, USERDEV_GET_MIX_CHANGECOUNT, &count)==-1)
+ {
+ perror("USERDEV_GET_MIX_CHANGECOUNT");
+ return;
+ }
+
+ if (count > prev_count) /* Something has changed */
+ {
+ userdev_mixvalues_t rec;
+ int i;
+
+ if (ioctl(fd, USERDEV_GET_MIXERS, &rec)==-1)
+ {
+ perror("USERDEV_GET_MIXERS");
+ return;
+ }
+
+ printf("Mixer change %d\n", count);
+
+ /*
+ * Print only the controls that were allocated in
+ * create_mixer_interface()
+ */
+ for (i=0;i<2;i++)
+ printf("%2d: %08x\n", i, rec.values[i]);
+ fflush(stdout);
+ }
+
+ prev_count = count;
+}
+
+int
+main(int argc, char *argv[])
+{
+ userdev_create_t crea = {0};
+ char cmd[512];
+
+/*
+ * Sample rate/format we would like to use on server side. The client side
+ * can use whatever they want since OSS will automatically do the required
+ * conversions.
+ */
+ int rate = 48000;
+ int fmt = AFMT_S16_LE;
+ int channels = 2;
+
+
+
+ int tmp;
+ int fragsize=0;
+
+ if (argc != 2)
+ {
+ fprintf(stderr, "Usage: %s <command>\n", argv[0]);
+ exit(-1);
+ }
+
+/*
+ * Open the server side device. A new userdev instance (client&server)
+ * will automatically get created when the device is opened. However
+ * creation of the client side will be delayed until USERDEV_CREATE_INSTANCE
+ * gets called.
+ */
+
+ if ((server_fd = open(SERVER_DEVNAME, O_RDWR, 0))==-1)
+ {
+ perror(SERVER_DEVNAME);
+ exit(-1);
+ }
+
+/*
+ * Create the client side device.
+ */
+ sprintf(crea.name, "udserver device for %s", getenv("USER"));
+ crea.flags = USERDEV_F_VMIX_ATTACH | USERDEV_F_VMIX_PRIVATENODE; /* Doesn't work at this moment */
+ crea.match_method = UD_MATCH_UID;
+ crea.match_key = geteuid();
+ crea.poll_interval = 10; /* In milliseconds */
+
+ if (ioctl(server_fd, USERDEV_CREATE_INSTANCE, &crea)==-1)
+ {
+ perror("USERDEV_CREATE_INSTANCE");
+ exit(-1);
+ }
+
+/*
+ * Set up the master side parameters such as sampling rate and sample format.
+ * The server application can select whatever format is best for its
+ * purposes. The client side can select different rate/format if necessary.
+ */
+
+ tmp=0;
+ ioctl(server_fd, SNDCTL_DSP_COOKEDMODE, &tmp); /* Turn off conversions */
+
+ if (ioctl(server_fd, SNDCTL_DSP_SETFMT, &fmt)==-1)
+ perror("SNDCTL_DSP_SETFMT");
+
+ if (ioctl(server_fd, SNDCTL_DSP_CHANNELS, &channels)==-1)
+ perror("SNDCTL_DSP_CHANNELS");
+
+ if (ioctl(server_fd, SNDCTL_DSP_SPEED, &rate)==-1)
+ perror("SNDCTL_DSP_SPEED");
+
+ if (ioctl(server_fd, SNDCTL_DSP_GETBLKSIZE, &fragsize)==-1)
+ fragsize = 1024;
+
+ create_mixer_interface(server_fd);
+
+ if (fork())
+ {
+ /*
+ * Server side code. In this case we have just a simple echo loop
+ * that writes back everything everything received from the client side.
+ */
+ int l;
+ int poll_count=0;
+
+ char *buffer;
+ signal(SIGCHLD, terminator);
+
+ buffer = malloc (fragsize);
+ memset(buffer, 0, fragsize);
+
+ write(server_fd, buffer, fragsize);
+ write(server_fd, buffer, fragsize);
+
+ while ((l=read(server_fd, buffer, fragsize))>0)
+ {
+ if (write(server_fd, buffer, l)!=l)
+ {
+ perror("write");
+ exit(-1);
+ }
+
+ if (poll_count++ > 10)
+ {
+ poll_mixer(server_fd);
+ poll_count = 0;
+ }
+ }
+
+ exit(0);
+ }
+
+/*
+ * Client side code. Simply execute the command that was given in
+ * argv[1]. However replace all %s's by the client side device node name.
+ */
+
+ if (setenv("OSS_AUDIODEV", crea.devnode, 1) == -1)
+ perror("setenv OSS_AUDIODEV");
+
+ if (setenv("OSS_MIXERDEV", crea.devnode, 1) == -1)
+ perror("setenv OSS_MIXERDEV");
+
+ setenv("PS1", "udserver> ", 1);
+
+ sprintf(cmd, argv[1], crea.devnode, crea.devnode, crea.devnode);
+ printf("Running '%s'\n", cmd);
+
+ system(cmd);
+ exit(0);
+}