/***********************************************************************/ /* */ /* str.c - plays sound/noisetracker files on a SparcStation */ /* modified for thomson 6bit D/A */ /* corrected core dump on */ /* Authors : Liam Corner - zenith@dcs.warwick.ac.uk */ /* Marc Espie - espie@dmi.ens.fr */ /* Version : 1.20 - 3 November 1991 */ /* */ /* */ /***********************************************************************/ #include #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 "./out.raw" #define MIN(A,B) ((A)<(B) ? (A) : (B)) #define MAX(A,B) ((A)>(B) ? (A) : (B)) typedef struct { /***********************************/ signed 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; /***********************************************/ #define MX_CHUNKS_IN_SONG 80000 #define MX_CHUNKS_NB 80000 int song_chunks_index=0; int song_chunks[MX_CHUNKS_IN_SONG]; int nb_chunk=0; char chunks[MX_CHUNKS_NB][VSYNC]; int cur_chunk_index=0; char cur_chunk[VSYNC*10]; int cmp_chunks(char *a, char *b, int size,int mask) { int i; int diff; // 1st pass ok if equal with mask for (i=0;i32) return -1; } return 0; } void insert_cur_chunk(void) { int i; for (i=0;i=MX_CHUNKS_NB) { fprintf(stderr,"Argh I'm dead... %d %d\n",song_chunks_index,i); exit(-1); } } } void rebuild_from_chunks(char * fname) { FILE *f; int i,j; f=fopen(fname,"w"); for (i=0;i2) { fprintf(stderr,"Usage: %s []\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].samp=0; /* GFE : added this to avoid nasty core dumps :) */ 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_slicur_chunk_indexde */ 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; } } // of for channel // clip if (byte>127) byte=127; if (byte<-128) byte=-128; // make unsigned byte+=128; byte /= 4; // fputc(byte, audio); /* and play the note */ cur_chunk[cur_chunk_index]=byte; cur_chunk_index++; } // of for bytes insert_cur_chunk(); cur_chunk_index=0; /* 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; } // of for notes for (loop = 0; loop < 4; loop++) { ch[loop].samp=0; /* GFE : added this to avoid nasty core dumps :) */ ch[loop].pointer = 0; ch[loop].step = 0; ch[loop].volume = 0; ch[loop].pitch = 0; } } // of for pattern // fclose(audio); rebuild_from_chunks("out2.raw"); build_outfiles("MOD"); printf("\n"); fprintf(stdout,"%d chunks %d chunks in song\n",nb_chunk,song_chunks_index); return (0); }