diff options
author | Igor Pashev <pashev.igor@gmail.com> | 2013-05-03 21:08:42 +0400 |
---|---|---|
committer | Igor Pashev <pashev.igor@gmail.com> | 2013-05-03 21:08:42 +0400 |
commit | 1058def8e7827e56ce4a70afb4aeacb5dc44148f (patch) | |
tree | 4495d23e7b54ab5700e3839081e797c1eafe0db9 /tutorials | |
download | oss4-upstream/4.2-build2006.tar.gz |
Imported Upstream version 4.2-build2006upstream/4.2-build2006upstream
Diffstat (limited to 'tutorials')
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", ®) != 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); +} |