diff options
Diffstat (limited to 'tutorials/sndkit/dsp/str')
-rw-r--r-- | tutorials/sndkit/dsp/str/Makefile | 34 | ||||
-rw-r--r-- | tutorials/sndkit/dsp/str/Readme | 142 | ||||
-rw-r--r-- | tutorials/sndkit/dsp/str/str.c | 295 | ||||
-rw-r--r-- | tutorials/sndkit/dsp/str/str.h | 74 | ||||
-rw-r--r-- | tutorials/sndkit/dsp/str/strplay.c | 575 |
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"); +} |