Category : Music and Digitized Voice
Archive   : USTR.ZIP
Filename : STR.C
/* */
/* str.c - plays sound/noisetracker files on a SparcStation */
/* */
/* Authors : Liam Corner - [email protected] */
/* Marc Espie - [email protected] */
/* Version : 1.20 - 3 November 1991 */
/* */
/* Usage : str32
/* [f|z]cat filename | str32 */
/* */
/***********************************************************************/
#include
#include
/**********************************************************/
/* 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 ( 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( 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( 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( 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 [
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)); /* print name of sample */
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);
}
Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!
This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.
But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/