home *** CD-ROM | disk | FTP | other *** search
- /***********************************************************************/
- /* */
- /* str.c - plays sound/noisetracker files on a SparcStation */
- /* */
- /* Authors : Liam Corner - zenith@dcs.warwick.ac.uk */
- /* Marc Espie - espie@dmi.ens.fr */
- /* Version : 1.20 - 3 November 1991 */
- /* */
- /* Usage : str32 <filename> */
- /* [f|z]cat filename | str32 */
- /* */
- /***********************************************************************/
-
-
- #include <stdio.h>
- #include <malloc.h>
-
-
- /**********************************************************/
- /* uS is the number of uSeconds that a byte is played for */
- /* Sparc plays at 8000 bytes/sec => 1 byte = 125 uSec */
- /* VSYNC is the number of bytes played in 1/50 sec */
- /* ie 0.02/(uS * 10**-6) */
- /**********************************************************/
- #define uS 125
- #define VSYNC 160
- #define AUDIO "/dev/audio"
-
- #define MIN(A,B) ((A)<(B) ? (A) : (B))
- #define MAX(A,B) ((A)>(B) ? (A) : (B))
-
-
- typedef struct { /***********************************/
- char *info; /* Sample */
- int length; /* Length of sample */
- float volume; /* Fractional volume 0-1 (min-max) */
- int rep_start; /* Byte offset of repeat start */
- int rep_end; /* Byte offset of repeat end */
- } Voice; /***********************************/
-
-
- typedef struct { /**************************/
- char sample [64][4]; /* Sample number */
- char effect [64][4]; /* Effect number */
- unsigned char params [64][4]; /* Effect parameters */
- int period [64][4]; /* Period (pitch) of note */
- } Pattern; /**************************/
-
-
- typedef struct { /***********************************************/
- char samp; /* Sample number of current note */
- int pitch; /* Current channel pitch (index to step_table) */
- int slide; /* Step size of pitch slide (if any) */
- int doslide;
- unsigned int pointer; /* Current sample position */
- unsigned int step; /* Sample offset increment (gives pitch) */
- float volume; /* Fractional volume of current note */
- float volslide;
- int doslidevol;
- int doporta;
- int pitchgoal;
- int portarate;
- } Channel; /***********************************************/
-
-
- /*****************************************************************************/
- /* Skips the next 'n' input bytes - because fseek won't work on stdin */
- /*****************************************************************************/
- void byteskip (fp, bytes)
- FILE *fp;
- int bytes;
- {
- int loop;
-
- for (loop = 0; loop < bytes; loop++)
- getc(fp);
- }
-
-
- /************************************************************************/
- /* For routine 'cvt' only */
- /************************************************************************/
- /* Copyright 1989 by Rich Gopstein and Harris Corporation */
- /************************************************************************/
-
- unsigned int cvt(ch)
- int ch;
- {
- int mask;
-
- if (ch < 0)
- {
- ch = -ch;
- mask = 0x7f;
- }
- else
- mask = 0xff;
-
- if (ch < 32)
- {
- ch = 0xF0 | 15 - (ch / 2);
- }
- else if (ch < 96)
- {
- ch = 0xE0 | 15 - (ch - 32) / 4;
- }
- else if (ch < 224)
- {
- ch = 0xD0 | 15 - (ch - 96) / 8;
- }
- else if (ch < 480)
- {
- ch = 0xC0 | 15 - (ch - 224) / 16;
- }
- else if (ch < 992)
- {
- ch = 0xB0 | 15 - (ch - 480) / 32;
- }
- else if (ch < 2016)
- {
- ch = 0xA0 | 15 - (ch - 992) / 64;
- }
- else if (ch < 4064)
- {
- ch = 0x90 | 15 - (ch - 2016) / 128;
- }
- else if (ch < 8160)
- {
- ch = 0x80 | 15 - (ch - 4064) / 256;
- }
- else
- {
- ch = 0x80;
- }
- return (mask & ch);
- }
-
-
- char *getstring(f, len)
- FILE *f;
- int len;
- {
- static char s[150];
- int i;
-
- for (i = 0; i < len; i++)
- s[i] = fgetc(f);
- s[len] = '\0';
- return s;
- }
-
- #define OLD 0
- #define NEW 1
-
- int main (argc, argv)
- int argc;
- char **argv;
- {
- FILE *fp, *audio;
- int loop;
- int notes, note, channel, vsync;
- int pat, pat_num;
- int byte, bytes;
- int step_table[1024];
- int speed=6; /* Default speed is 6 */
- int end_pattern=0;
- char songlength;
- char tune[128];
- char num_patterns=0;
- unsigned char ulaw;
- float dummy1, dummy2;
- Voice voices[32];
- Pattern patterns[64];
- Channel ch[4];
- int nvoices;
- int effect;
-
- int type; /* module type: old or new */
- char *command; /* the actual command name used */
-
- command = argv[0];
- if (strcmp(argv[0], "str32") == 0)
- type = NEW;
- else if (strcmp(argv[0], "str15") == 0)
- type = OLD;
- else
- {
- fprintf(stderr,
- "Error: command should be named either str15 or str32\n");
- exit(1);
- }
-
- if (type == OLD)
- nvoices = 15;
- else
- nvoices = 31;
-
- if (argc>2)
- {
- fprintf(stderr,"Usage: %s [<filename>]\n", command);
- exit(1);
- }
-
- /***********************************************************************/
- /* Creates a table of the byte_step << 16 for a given pitch */
- /* The step and pointer are stored << 16 to get accuracy without floats*/
- /* eg to get double pitch only play every other byte */
- /* so step of 0x10000 is normal pitch, 0x8000 is half, */
- /* 0x20000 is double. Pointer is >> 16 when accessed, */
- /* so 0x10000 is 1st byte, 0x20000 2nd etc */
- /* I have no idea where the other numbers are from, I copied them from */
- /* a SoundTracker player for the Acorn Archimedes */
- /* */
- /* Actually, these other numbers are highly dependent on the amiga hw. */
- /***********************************************************************/
- step_table[0] = 0;
- for (loop = 1; loop < 1024; loop++)
- {
- dummy1 = 3575872 / loop;
- dummy2 = (dummy1 / (1000000 /uS) ) * 60000;
- step_table[loop] = (int)dummy2;
- }
-
- if (argc < 2)
- fp = stdin;
- else
- fp = fopen(argv[1], "r");
- if (fp == NULL)
- {
- fprintf(stderr, "%s: unable to open tune file %s\n",
- command, argv[1]);
- exit(1);
- }
-
- /* read song name */
- printf("Module : %s\n\n", getstring(fp, 20));
-
- /* Reads in the sample-information tables */
- for (loop = 1; loop <= nvoices; loop++)
- {
- printf("%6d : %s\n", loop, getstring(fp, 22));
- voices[loop].length = ( (getc(fp) << 8) | getc(fp) ) * 2;
- getc(fp);
- voices[loop].volume = getc(fp);
- voices[loop].volume = MIN(voices[loop].volume, 64);
- voices[loop].volume /= 64; /* Volume is a fraction */
- voices[loop].rep_start = ( (getc(fp) << 8) | getc(fp) ) * 2;
- voices[loop].rep_end = ( (getc(fp) << 8) | getc(fp) ) * 2;
- if (voices[loop].rep_end <= 4)
- voices[loop].rep_end = 0;
- else
- {
- /* If there is a repeat then end=start+length, but must be */
- /* less than the sample length. Not sure if this is 100% */
- /* correct, but it seems to work OK :-) */
- if (voices[loop].rep_end + voices[loop].rep_start - 1
- > voices[loop].length)
- voices[loop].rep_start >>= 1;
- voices[loop].rep_end += voices[loop].rep_start;
- voices[loop].rep_end = MIN(voices[loop].rep_end,
- voices[loop].length);
- }
- }
- voices[0].length = 0;
-
- songlength = getc(fp);
- byteskip(fp, 1);
-
- /* Reads in the tune */
- for (loop = 0; loop < 128; loop++)
- {
- tune[loop] = getc(fp);
- if (tune[loop] > num_patterns)
- num_patterns = tune[loop];
- }
- num_patterns++;
-
- /* skip over sig (usually M.K.) */
- if (type == NEW)
- byteskip(fp,4);
-
- /* Reads in the patterns */
- for (pat_num = 0; pat_num < num_patterns; pat_num++)
- {
- /* 64 notes per pattern */
- for (notes = 0; notes < 64; notes++)
- {
- /* 4 channels per note */
- for (channel = 0; channel < 4; channel++)
- {
- note = (getc(fp) << 24) | (getc(fp) << 16) |
- (getc(fp) << 8) | getc(fp);
- (patterns[pat_num]).effect[notes][channel] =
- (note & 0xF00) >> 8;
- (patterns[pat_num]).params[notes][channel] = note & 0xFF;
- (patterns[pat_num]).sample[notes][channel] =
- ( (note & 0xF000) >> 12) | ( (note >> 24) & 0x10);
- (patterns[pat_num]).period[notes][channel] =
- MIN( (note & 0xFFF0000) >> 16, 1023);
- }
- }
- }
-
- /* Stores the samples voices as an array of char */
- for (loop = 1; loop <= nvoices; loop++)
- {
- voices[loop].info = malloc(voices[loop].length);
- if (voices[loop].info == NULL)
- {
- fprintf(stderr, "%s: unable to allocate memory\n, command");
- exit(1);
- }
- fread(voices[loop].info, 1, voices[loop].length, fp);
- }
-
- audio = fopen(AUDIO, "w");
- if (audio == NULL)
- {
- fprintf(stderr, "%s: unable to access %s\n", command, AUDIO);
- exit(1);
- }
-
- for (loop = 0; loop < 4; loop++)
- {
- ch[loop].pointer = 0;
- ch[loop].step = 0;
- ch[loop].volume = 0;
- ch[loop].pitch = 0;
- }
-
- printf("\nPosition (%d):", songlength);
- fflush(stdout);
-
- for (pat_num = 0; pat_num < songlength; pat_num++)
- {
- printf("\r\t\t%3d", pat_num);
- fflush(stdout);
- pat = tune[pat_num];
- end_pattern = 0;
- for (notes = 0; notes < 64; notes++)
- {
- for (channel = 0; channel < 4; channel++)
- {
- int samp, pitch, cmd, para;
-
- 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 (samp)
- {
- ch[channel].samp = samp;
- /* load new instrument */
- ch[channel].volume = voices[ch[channel].samp].volume;
- }
- /* If sample number=0 and no new period */
- /* continue last note */
- if (pitch && cmd != 3)
- {
- ch[channel].pointer = 0;
- ch[channel].step = step_table[pitch];
- ch[channel].pitch = pitch;
- }
- ch[channel].doslide = 0;
- ch[channel].doslidevol = 0;
- ch[channel].doporta = 0;
- switch(cmd) /* Do effects */
- {
- case 0xF :
- speed = para;
- break;
- case 0xD :
- end_pattern = 1;
- break;
- case 0xC :
- ch[channel].volume= MIN(para, 64);
- ch[channel].volume /= 64;
- break;
- /* volume_slide */
- case 0xB :
- pat_num = (para & 0xF) + (10 * (para >> 4));
- break;
- case 0xA :
- ch[channel].doslidevol = 1;
- if (para)
- {
- if (para & 15)
- ch[channel].volslide = - para / 64;
- else
- ch[channel].volslide = (para >> 4)/64;
- }
- break;
- case 3 :
- ch[channel].doporta = 1;
- if (para)
- ch[channel].portarate = para;
- if (pitch)
- ch[channel].pitchgoal = pitch;
- break;
- case 2 :
- ch[channel].doslide = 1;
- if (para)
- ch[channel].slide = para;
- break;
- case 1 :
- ch[channel].doslide = 1;
- if (para)
- ch[channel].slide = -para;
- break;
- case 0 :
- break;
- default :
- /* printf(" [%d][%d] ", cmd, para); */
- break;
- }
- }
- /* 1 vsync = 0.02 sec */
- for (vsync = 0; vsync < speed; vsync++)
- {
- /* 160*125uSec = 0.02 */
- for (bytes = 0; bytes < VSYNC; bytes++)
- {
- byte = 0;
- for (channel = 0; channel < 4; channel++)
- {
- if (ch[channel].samp == 0)
- continue;
- /* If at end of sample jump to rep_start position */
- if (voices[ch[channel].samp].rep_end)
- {
- if ((ch[channel].pointer >> 16) >=
- voices[ch[channel].samp].rep_end)
- ch[channel].pointer +=
- (voices[ch[channel].samp].rep_start -
- voices[ch[channel].samp].length)<< 16;
- }
- else
- if ((ch[channel].pointer >> 16) >=
- voices[ch[channel].samp].length)
- continue;
- /* byte = sum of (sample byte * volume) for each */
- /* of 4 channels which mixes the sounds */
- if (ch[channel].pointer >> 16 <
- voices[ch[channel].samp].length)
- {
- byte += (int) ( (voices[ch[channel].samp]
- .info[ch[channel].pointer >> 16])
- * (ch[channel].volume));
- ch[channel].pointer += ch[channel].step;
- }
- }
- /* Divide by 4 to get the correct volume */
- byte /= 4;
- ulaw = (unsigned char) cvt(byte * 16);/* Convert byte */
- fputc(ulaw, audio); /* and play the note */
- }
- /* Do end of vsync */
- if (vsync == 0)
- continue;
- for (channel = 0; channel < 4; channel++)
- {
- if (ch[channel].doslide) /* effects */
- {
- ch[channel].pitch += ch[channel].slide;
- ch[channel].pitch = MIN(ch[channel].pitch, 1023);
- ch[channel].pitch = MAX(ch[channel].pitch, 113);
- ch[channel].step = step_table[ch[channel].pitch];
- }
- if (ch[channel].doslidevol)
- {
- ch[channel].volume += ch[channel].volslide;
- if (ch[channel].volume < 0.0)
- ch[channel].volume = 0.0;
- else if (ch[channel].volume >= 1.0)
- ch[channel].volume = 1.0;
- }
- if (ch[channel].doporta)
- {
- if (ch[channel].pitch < ch[channel].pitchgoal)
- {
- ch[channel].pitch += ch[channel].portarate;
- if (ch[channel].pitch > ch[channel].pitchgoal)
- ch[channel].pitch = ch[channel].pitchgoal;
- }
- else if (ch[channel].pitch > ch[channel].pitchgoal)
- {
- ch[channel].pitch -= ch[channel].portarate;
- if (ch[channel].pitch < ch[channel].pitchgoal)
- ch[channel].pitch = ch[channel].pitchgoal;
- }
- }
- }
- }
- if (end_pattern == 1)
- break;
- }
- }
-
- fclose(audio);
- printf("\n");
- return (0);
- }
-
-