summaryrefslogtreecommitdiff
path: root/tutorials/sndkit/dsp/str
diff options
context:
space:
mode:
Diffstat (limited to 'tutorials/sndkit/dsp/str')
-rw-r--r--tutorials/sndkit/dsp/str/Makefile34
-rw-r--r--tutorials/sndkit/dsp/str/Readme142
-rw-r--r--tutorials/sndkit/dsp/str/str.c295
-rw-r--r--tutorials/sndkit/dsp/str/str.h74
-rw-r--r--tutorials/sndkit/dsp/str/strplay.c575
5 files changed, 1120 insertions, 0 deletions
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");
+}