ftp.nice.ch/pub/next/unix/audio/str.1.0.s.tar.gz#/str.c

This is str.c in view mode; [Download] [Up]

/* The following was written long ago on a NeXT Cube. I found it in
   one of my old archives and since quite a lot poeple asked for it...
   
   Here it is: a modplayer for black AND white

   Caveats: It is a hack, based on a very old version of 
            sparctracker and is completely unsupported.
	    The quality is not very good and on slow machines
	    it does not work at all. Untested on a Gecko *GRIN*

            Mark Espie & Liam Corner don't know anything about
            this hack, so don't blame them if it does not work

	    As always you are working at your own risk.
        
   Tested on: NeXT Cube 25 MHz 16 MB, 486DX2 EISA 32MB both NS 3.1

   Compile:   cc str.c -O -o str32; ln -s str32 str15

   No copyrights from my side apply, ask Mark Espie if you want to
   use it in your commercial apps 8-). In case someone want to
   improve it I strongly recommend to get a newer version of the
   tracker code from the archives.

   "It was long ago and it was far away 
	and it was so much better than it is today"

  	-Meatloaf, Paradise by the dashboard light
   
   Frank Siegert u288869@uebung3.rz.fh-reutlingen.de (until 7. March 94)
*/

/*****************************************************************/
/*                                             	                 */
/* str.c - plays sound/noisetracker files on a NeXT              */
/*	   "A Q&D hack is better than nothing!"                  */
/* 	   NeXT code by Frank Siegert 1992	                 */
/* 	      							 */
/*                                                               */
/* all other code from the sound/noisetracker for a SparcStation */
/* by: 	      Liam Corner - zenith@dcs.warwick.ac.uk             */
/*            Marc Espie - espie@dmi.ens.fr                      */
/* Version  : 1.00 - 6 April 1992                                */
/*                                                               */
/* Usage    : str32 <filename>                                   */
/*            [f|z]cat filename | str32                          */
/*                                                               */
/*****************************************************************/
 
 
#include <stdio.h>
#include <stdlib.h>
#include <sound/sound.h>
#include <sys/resource.h>


/**********************************************************/
/* Use HALFFREQ on slow machines, quality goes down but   */
/* the tracker has more time to calculate the samples     */

/* #define HALFFREQ */
 
/**********************************************************/
/* uS is the number of uSeconds that a byte is played for */
/* Sparc plays at 8000 bytes/sec  =>  1 byte = 125 uSec   */
/* we play at 22050/2 kHz, so we have to change this value */
/* to 22055/sec *2 => approx. 90 uSec */

/* VSYNC is the number of bytes played in 1/50 sec        */
/* ie 0.02/(uS * 10**-6)                                  */
/**********************************************************/

#ifdef HALFFREQ
#define uS 100
#define VSYNC 218			/* 11025 Hz */
#else
#define uS 50
#define VSYNC 435			/* 22050 Hz */
#endif

/* defined for the NeXT */
#define MAXSOUND 4			/* number of independent sound frames */
#define MAXSAMPLE 32*1024		/* size of a frame  32kByte*/
#define MARKEDFREE 0			/* marker for unused frame */
#define MARKEDBUSY 1			/* marker for used frame */

SNDSoundStruct *blubber[MAXSOUND];	/* array of sound frames */
char 	*fillSound;			/* current fill point */
int	fillCount;			/* fill frame counter */
int	actualSample;			/* frame to be filled up */
int	played;				/* to keep track of running sounds in queue */
int	lastFreed;			/* last sample freed */
int	DEBUG;				/* Debugging on/off */

/* End NeXT defines */

#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);
    }
    
    
/*****************************************************************************/
/* soundEnd is called by the soundplayer when a sound finished playing       */    
/*****************************************************************************/
int soundEnd(SNDSoundStruct *s, int tag, int err)
{
	lastFreed=tag;
	s->info[0]=MARKEDFREE;
	if (DEBUG) printf("Released %d\n",tag);
	played--;
	return (0);
}
/*****************************************************************************/
/* doSound is called to put a note in the current frame and play this frame  */
/* must be called with the parameter All==TRUE, if the current frame should  */
/* be played at the end							     */    
/*****************************************************************************/
/* play is called by nextSample to put the current frame to the sound queue  */
/*****************************************************************************/
 void play(SNDSoundStruct *theSound, int SoundTag)
 {
 	    int err;
	    theSound ->info[0]=MARKEDBUSY;
	    played++;
 	    err = SNDStartPlaying(theSound,SoundTag,5,0,0,(SNDNotificationFun)soundEnd);
	    if (err) {
	    	fprintf(stderr,"Error: Unable to call SNDStartPlaying\n");
		exit(1);
	    }
	   return;
}
/*****************************************************************************/
/* nextSample plays the current Sample and tries to get a unused one,        */
/* it waits until a frame is avaiable 					     */
/*****************************************************************************/
int nextSample()
 {

	play(blubber[actualSample],actualSample);	/* play current */

 	actualSample++;					/* next frame */
	if (actualSample>=MAXSOUND) {			/* reached end of frames */
		actualSample=0;				/* back to the beginning */
	}
 	while (blubber[actualSample]->info[0]==MARKEDBUSY) ;
	/* running around and wait for free */

	if (DEBUG) printf("Got %d\n", actualSample);	
 	return 0;
}
 
 
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;
    register 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 */
    int	lastbyte=0;
    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");
        type = NEW;
        }
 
    if (type == OLD)
        nvoices = 15;
    else
        nvoices = 31;
 
    if (argc==3) {
    	if (strcmp(argv[2],"DEBUG")==0) {
		DEBUG=TRUE;
	}
    }
 
    if (argc>3)
        {
        fprintf(stderr,"Usage: %s [<filename>] [DEBUG]\n", command);
        exit(1);
        }
 
 /***********************************************************************/
 /* create some Sound structures in mem big enougth to hold a           */
 /* a significant amount of sound data                                  */

    for (loop=0;loop<MAXSOUND;loop++) {
 	blubber[loop]=(SNDSoundStruct*) malloc(sizeof(SNDSoundStruct)+MAXSAMPLE+16);  /* must be more than MAXSAMPLE */
	blubber[loop]->magic=SND_MAGIC;
	blubber[loop]->dataLocation=sizeof(SNDSoundStruct);
	blubber[loop]->dataSize=0;
	blubber[loop]->dataFormat=SND_FORMAT_LINEAR_8;    	/* 8 bit linear */
	blubber[loop]->samplingRate=SND_RATE_LOW;             	/* 22 kHz */
	blubber[loop]->channelCount=1;	
	blubber[loop]->info[0]=MARKEDFREE;
    }

    fillCount=0;
    actualSample=0;
    played=0;
    lastFreed=0;

    fillSound=(char *)((int)blubber[actualSample]+sizeof(SNDSoundStruct)); 	/* start of data area */

/***********************************************************************/
/* 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;

    task_priority(task_self(), 18, TRUE); 			/* set our priority to high */
    	
    SNDSetFilter(1);						/* low pass filter ON */
    
    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);
        }
  
    for (loop = 0; loop < 4; loop++)
        {
        ch[loop].pointer = 0;
        ch[loop].step = 0;
        ch[loop].volume = 0;
        ch[loop].pitch = 0;
        }
 
     for (pat_num = 0; pat_num < songlength; pat_num++)
        {
         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  :

                    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++)
                      {
		      register char R_sample;
		      register unsigned int R_pointer;
		      
                        if ((R_sample=ch[channel].samp) == 0)
                            continue;
                            /* If at end of sample jump to rep_start position */
 			R_pointer=ch[channel].pointer;
                        if (voices[R_sample].rep_end)
                            {
                            if ((R_pointer >> 16) >=
                                voices[R_sample].rep_end)
                                R_pointer  +=
                                    (voices[R_sample].rep_start -
                                    voices[R_sample].length)<< 16;
			ch[channel].pointer =R_pointer;
                            }
                        else
                            if ((R_pointer >> 16) >=
                                voices[R_sample].length)
                                continue;
  
                        /* byte = sum of (sample byte * volume) for each */
                        /* of 4 channels which mixes the sounds          */
    
                        if (R_pointer>> 16 <
                            voices[R_sample].length)
                            {
                            byte += (int) ( (voices[R_sample].info[R_pointer >> 16])
                                * (ch[channel].volume));
                            ch[channel].pointer =R_pointer+ ch[channel].step;
                            }
                        }

                        /* Divide by 8 to get the correct volume */
                    
		    byte /= 8;
		
#ifdef HALFFREQ
  		   *fillSound++ = ((byte<<2)+lastbyte)/3;	/* write to sndbuffer */   
		   fillCount++;		   
#endif    
  		   *fillSound++ = byte;			/* write to sndbuffer */   
		   fillCount++;

		   lastbyte=byte;			/* for evaluation of mean value in case 11 kHz is emulated
							   by 22 kHz */

		   if (fillCount>MAXSAMPLE)	/* is frame filled up? */
		   {
		    	blubber[actualSample]->dataSize=fillCount-1;
			nextSample();
			fillCount=0;
			fillSound=(char*)((int)blubber[actualSample]+sizeof(SNDSoundStruct));                 
			/* start of data area */
                    }
  		
                    /* 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;
            }
        }
    }
    
    if (fillCount>0) 
    {
   	blubber[actualSample]->dataSize=fillCount-1;    /* play last pending frame */
   	nextSample();
    }

    while (played) 
    {   				/* wait for end of sndplay */
    	sleep(1);      		
    }

 /***********************************************************************/
 /* free the Sound structures    	                                */

    for (loop=0;loop<MAXSOUND;loop++) free(blubber[loop]);

    if (DEBUG) printf("\ndone\n");
    exit(0);
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.