home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 January
/
usenetsourcesnewsgroupsinfomagicjanuary1994.iso
/
sources
/
sun
/
volume3
/
sparctracker
/
str.c
< prev
Wrap
C/C++ Source or Header
|
1992-03-02
|
18KB
|
504 lines
/***********************************************************************/
/* */
/* 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);
}