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