audio.c 666 1155 310 14222 5160354463 5445 /* audio.c */ /* $Author: espie $ * $Id: audio.c,v 2.8 1991/12/03 21:24:53 espie Exp $ * $Revision: 2.8 $ * $Log: audio.c,v $ * Revision 2.8 1991/12/03 21:24:53 espie * Added comments. * * Revision 2.7 1991/12/03 20:43:46 espie * Added possibility to get back to MONO for the sgi. * * Revision 2.6 1991/12/03 18:07:38 espie * Added stereo capabilities to the indigo version. * * Revision 2.5 1991/12/03 13:23:10 espie * Minor bug: a SAMPLE_FAULT is a minor error, * we should first check that there was no other * error before setting it. * * Revision 2.4 1991/11/19 16:07:19 espie * Added comments, moved minor stuff around. * * Revision 2.3 1991/11/18 14:10:30 espie * New resample function coming from the player. * * Revision 2.2 1991/11/18 01:12:31 espie * Added more notes. * * Revision 2.1 1991/11/17 23:07:58 espie * Just computes some frequency-related parameters. * * */ #include #include #include "extern.h" #include "machine.h" #include "song.h" #include "channel.h" static char *id = "$Id: audio.c,v 2.8 1991/12/03 21:24:53 espie Exp $"; /* creates a table for converting ``amiga'' pitch * to a step rate at a given resampling frequency. * For accuracy, we don't use floating point, but * instead fixed point ( << ACCURACY). */ #define ACCURACY 16 #define AMIGA_CLOCKFREQ 3575872 int step_table[MAX_PITCH]; /* holds the increment for finding the next sampled * byte at a given pitch (see resample() ). */ void create_step_table(oversample, output_fr) int oversample; /* we sample oversample i for each byte output */ int output_fr; /* output frequency */ { double note_fr; /* note frequency (in Hz) */ double step; int pitch; /* amiga pitch */ step_table[0] = 0; for (pitch = 1; pitch < MAX_PITCH; pitch++) { note_fr = AMIGA_CLOCKFREQ / pitch; /* int_to_fix(1) is the normalizing factor */ step = note_fr / output_fr * int_to_fix(1) / oversample; step_table[pitch] = (int)step; } } /* the musical notes correspond to some specific pitch. * It's useful to be able to find them back, at least for * arpeggii. */ int pitch_table[NUMBER_NOTES]; void create_notes_table() { double base, pitch; int i; base = AMIGA_CLOCKFREQ/440; for (i = 0; i < NUMBER_NOTES; i++) { pitch = base / pow(2.0, i/12.0); pitch_table[i] = pitch; } } void init_tables(oversample, frequency) int oversample, frequency; { create_step_table(oversample, frequency); create_notes_table(); } #define C fix_to_int(ch->pointer) /* The playing mechanism itself. * According to the current channel automata, * we resample the instruments in real time to * generate output. */ void resample(chan, oversample, number) struct channel *chan; int oversample; int number; { int i; /* sample counter */ int channel; /* channel counter */ int sampling; /* oversample counter */ SAMPLE sample; /* sample from the channel */ int byte[NUMBER_TRACKS]; /* recombinations of the various data */ struct channel *ch; /* check the existence of samples */ for (channel = 0; channel < NUMBER_TRACKS; channel++) if (!chan[channel].samp->start) { if (!error) error = SAMPLE_FAULT; chan[channel].mode = DO_NOTHING; } /* do the resampling, i.e., actually play sounds */ for (i = 0; i < number; i++) { for (channel = 0; channel < NUMBER_TRACKS; channel++) { byte[channel] = 0; for (sampling = 0; sampling < oversample; sampling++) { ch = chan + channel; switch(ch->mode) { case DO_NOTHING: break; case PLAY: /* small liability: the sample may have * changed, and we may be out of range. * However, this routine is time-critical, * so we don't check for this very rare case. */ sample = ch->samp->start[C]; byte[channel] += sample * ch->volume; ch->pointer += ch->step; if (C >= ch->samp->length) { /* is there a replay ? */ if (ch->samp->rp_start) { ch->mode = REPLAY; ch->pointer -= int_to_fix(ch->samp->length); } else ch->mode = DO_NOTHING; } break; case REPLAY: /* small liability: the sample may have * changed, and we may be out of range. * However, this routine is time-critical, * so we don't check for this very rare case. */ sample = ch->samp->rp_start[C]; byte[channel] += sample * ch->volume; ch->pointer += ch->step; if (C >= ch->samp->rp_length) ch->pointer -= int_to_fix(ch->samp->rp_length); break; } } } output_samples((byte[0]+byte[3])/oversample, (byte[1]+byte[2])/oversample); } flush_buffer(); } /* setting up a given note */ void reset_note(ch, note, pitch) struct channel *ch; int note; { ch->pointer = 0; ch->mode = PLAY; ch->pitch = pitch; ch->step = step_table[pitch]; ch->note = note; ch->viboffset = 0; } /* changing the current pitch (value * may be temporary, and not stored * in channel pitch, for instance vibratos. */ void set_current_pitch(ch, pitch) struct channel *ch; int pitch; { ch->step = step_table[pitch]; } } #define C fix_to_int(ch->pointer) /* The playing mechanism itself. * According to the current channel automata, * we resample the instruments in real time to * generate output. */ void resample(chan, oversample, number) struct channel *chan; int oversample; int number; { int i; /* sample counter */ int channel; /* channel counterautomaton.c 666 1155 310 7556 5160355414 6344 /* automaton.c */ /* $Author: espie $ * $Id: automaton.c,v 2.5 1991/11/20 20:46:35 espie Exp $ * $Revision: 2.5 $ * $Log: automaton.c,v $ * Revision 2.5 1991/11/20 20:46:35 espie * Minor correction. * * Revision 2.4 1991/11/19 16:07:19 espie * Added comments, moved minor stuff around. * * Revision 2.3 1991/11/18 01:23:30 espie * Added two level of fault tolerancy. * * Revision 2.2 1991/11/18 01:12:31 espie * Minor changes. * * Revision 2.1 1991/11/17 23:07:58 espie * Coming from str32. * * */ static char *id = "$Id: automaton.c,v 2.5 1991/11/20 20:46:35 espie Exp $"; #include #include #include #include "extern.h" #include "machine.h" #include "song.h" #include "channel.h" /* updates the pattern to play in the automaton. * Checks that the pattern actually exists. */ void set_pattern(a) struct automaton *a; { int p; if (a->pattern_num >= a->info->length) { error = UNRECOVERABLE; return; } /* there is a level of indirection in the format, * i.e., patterns can be repeated. */ p = a->info->patnumber[a->pattern_num]; if (p >= a->info->maxpat) { error = UNRECOVERABLE; return; } a->pattern = a->info->pblocks + p; } /* initialize all the fields of the automaton necessary * to play a given song. */ void init_automaton(a, song) struct automaton *a; struct song *song; { a->info = song->info; a->pattern_num = 0; /* first pattern */ a->note_num = 0; /* first note in pattern */ a->counter = 0; /* counter for the effect tempo */ a->speed = NORMAL_SPEED;/* this is the default effect tempo */ a->finespeed = NORMAL_FINESPEED; /* this is the fine speed (100%) */ a->do_stuff = DO_NOTHING; /* some effects affect the automaton, * we keep them here. */ error = NONE; /* Maybe we should not reset errors at * this point. */ set_pattern(a); } /* Gets to the next pattern, and displays stuff */ void advance_pattern(a) struct automaton *a; { #ifdef SHOW_SEQ printf("\n"); #endif printf("%3d", a->pattern_num); fflush(stdout); #ifdef SHOW_SEQ printf("\n"); #endif if (++a->pattern_num >= a->info->length) { a->pattern_num = 0; error = ENDED; } set_pattern(a); a->note_num = 0; } /* process all the stuff which we need to advance in the song, * including set_speed, set_skip and set_fastskip. */ void next_tick(a) struct automaton *a; { if (a->do_stuff & SET_SPEED && a->new_speed) { /* there are three classes of speed changes: * 0 does nothing. * <32 is the effect speed (resets the fine speed). * >=32 changes the finespeed, so this is 32% to 255% */ if (a->new_speed >= 32) { a->finespeed = a->new_speed - 32; printf("(%d)", a->new_speed); fflush(stdout); } else { a->speed = a->new_speed; a->finespeed = 100; } } if (++a->counter >= a->speed) { a->counter = 0; #ifdef SHOW_SEQ printf("\n"); #endif if (a->do_stuff & SET_FASTSKIP) { a->pattern_num = a->new_pattern; set_pattern(a); a->note_num = 0; } else if (a->do_stuff & SET_SKIP) { advance_pattern(a); a->note_num = a->new_note; } else { if (++a->note_num >= BLOCK_LENGTH) advance_pattern(a); } a->do_stuff = DO_NOTHING; } } { if (!error) error = SAMPLE_FAULT; chan[channel].mode = DO_NOTHING; } commands.c 666 1155 310 21552 5160354466 6154 /* commands.c */ /* $Author: espie $ * $Id: commands.c,v 2.4 1991/12/03 13:23:10 espie Exp $ * $Revision: 2.4 $ * $Log: commands.c,v $ * Revision 2.4 1991/12/03 13:23:10 espie * Defensive programming: check the range of each note * for arpeggio setup. * * Revision 2.3 1991/11/19 16:07:19 espie * Added comments, moved minor stuff around. * * Revision 2.2 1991/11/18 01:12:31 espie * Minor changes. * * Revision 2.1 1991/11/17 23:07:58 espie * Used some constants. * * Revision 2.0 1991/11/17 21:42:08 espie * Structured part of the code, especially replay ``automaton'' * and setting up of effects. * * Revision 1.9 1991/11/17 17:09:53 espie * Added missing prototypes. * * Revision 1.8 1991/11/16 15:42:43 espie * tabs. * * Revision 1.7 1991/11/08 14:25:55 espie * Dynamic oversample and frequency. * * Revision 1.6 1991/11/07 21:40:16 espie * Added arpeggio. * * Revision 1.5 1991/11/07 20:12:34 espie * Minor problem with version id. * * Revision 1.4 1991/11/07 20:11:10 espie * Added embedded version id. * * Revision 1.3 1991/11/07 20:05:53 espie * Fixed up vibrato depth. * Added vibslide and portaslide. * * Revision 1.2 1991/11/07 15:27:02 espie * Added command 9. * * Revision 1.1 1991/11/06 09:46:06 espie * Initial revision * * */ #include #include "extern.h" #include "channel.h" #include "machine.h" #include "song.h" static char *id = "$Id: commands.c,v 2.4 1991/12/03 13:23:10 espie Exp $"; /* sine table for the vibrato effect (could be much more precise) */ int vibrato_table[32] = { 0, 25, 49, 71, 90, 106, 117, 125, 127, 125, 117, 106, 90, 71, 49, 25, 0, -25, -49, -71, -90,-106,-117,-125,-127,-125, -117,-106, -90, -71, -49, -25}; /*** * * * setting up effects/doing effects. * The set_xxx gets called while parsing the effect, * the do_xxx gets called each tick, and update the * sound parameters while playing it. * * ***/ void do_nothing(ch) struct channel *ch; { } void set_nothing(a, ch) struct automaton *a; struct channel *ch; { } /* slide pitch (up or down) */ void do_slide(ch) struct channel *ch; { ch->pitch += ch->slide; ch->pitch = MIN(ch->pitch, MAX_PITCH); ch->pitch = MAX(ch->pitch, MIN_PITCH); set_current_pitch(ch, ch->pitch); } void set_upslide(a, ch) struct automaton *a; struct channel *ch; { ch->adjust = do_slide; if (a->para) ch->slide = a->para; } void set_downslide(a, ch) struct automaton *a; struct channel *ch; { ch->adjust = do_slide; if (a->para) ch->slide = -a->para; } /* modulating the pitch with vibrato */ void do_vibrato(ch) struct channel *ch; { int offset; /* this is a literal transcription of the protracker * code. I should rescale the vibrato table at some point */ ch->viboffset += ch->vibrate; ch->viboffset %= 64; offset = (vibrato_table[ch->viboffset >> 1] * ch->vibdepth)/64; /* temporary update of only the step value, * note that we do not change the saved pitch. */ set_current_pitch(ch, ch->pitch + offset); } void set_vibrato(a, ch) struct automaton *a; struct channel *ch; { ch->adjust = do_vibrato; if (a->para) { ch->vibrate = HI(a->para); ch->vibdepth = LOW(a->para); } } /* arpeggio looks a bit like chords: we alternate between two * or three notes very fast. * Issue: we are able to re-generate real chords. Would that be * better ? To try. */ void do_arpeggio(ch) struct channel *ch; { if (++ch->arpindex >= MAX_ARP) ch->arpindex =0; set_current_pitch(ch, ch->arp[ch->arpindex]); } void set_arpeggio(a, ch) struct automaton *a; struct channel *ch; { /* normal play is arpeggio with 0/0 */ if (!a->para) return; /* arpeggio can be installed relative to the * previous note, so we have to check that there * actually is a current(previous) note */ if (ch->note == NO_NOTE) { fprintf(stderr, "No note present for arpeggio"); error = FAULT; } else { int note; ch->arp[0] = pitch_table[ch->note]; note = ch->note + HI(a->para); if (note < NUMBER_NOTES) ch->arp[1] = pitch_table[note]; else { fprintf(stderr, "Arpeggio note out of range"); error = FAULT; } note = ch->note + LOW(a->para); if (note < NUMBER_NOTES) ch->arp[2] = pitch_table[note]; else { fprintf(stderr, "Arpeggio note out of range"); error = FAULT; } ch->arpindex = 0; ch->adjust = do_arpeggio; } } /* volume slide. Mostly used to simulate waveform control. * (attack/decay/sustain). */ void do_slidevol(ch) struct channel *ch; { ch->volume += ch->volumerate; ch->volume = MIN(ch->volume, MAX_VOLUME); ch->volume = MAX(ch->volume, MIN_VOLUME); } /* note that volumeslide does not have a ``take default'' * behavior. If para is 0, this is truly a 0 volumeslide. * Issue: is the test really necessary ? Can't we do * a HI(para) - LOW(para). */ void parse_slidevol(ch, para) struct channel *ch; int para; { if (LOW(para)) ch->volumerate = -LOW(para); else ch->volumerate = HI(para); } void set_slidevol(a, ch) struct automaton *a; struct channel *ch; { ch->adjust = do_slidevol; parse_slidevol(ch, a->para); } /* portamento: gets from a given pitch to another. * We can simplify the routine by cutting it in * a pitch up and pitch down part while setting up * the effect. */ void do_portamento(ch) struct channel *ch; { if (ch->pitch < ch->pitchgoal) { ch->pitch += ch->pitchrate; ch->pitch = MIN(ch->pitch, ch->pitchgoal); } else if (ch->pitch > ch->pitchgoal) { ch->pitch -= ch->pitchrate; ch->pitch = MAX(ch->pitch, ch->pitchgoal); } set_current_pitch(ch, ch->pitch); } /* if para and pitch are 0, this is obviously a continuation * of the previous portamento. */ void set_portamento(a, ch) struct automaton *a; struct channel *ch; { ch->adjust = do_portamento; if (a->para) ch->pitchrate = a->para; if (a->pitch) ch->pitchgoal = a->pitch; } /* * combined commands. */ void do_portaslide(ch) struct channel *ch; { do_portamento(ch); do_slidevol(ch); } void set_portaslide(a, ch) struct automaton *a; struct channel *ch; { ch->adjust = do_portaslide; parse_slidevol(ch, a->para); } void do_vibratoslide(ch) struct channel *ch; { do_vibrato(ch); do_slidevol(ch); } void set_vibratoslide(a, ch) struct automaton *a; struct channel *ch; { ch->adjust = do_vibratoslide; parse_slidevol(ch, a->para); } /*** * * effects that just need a setup part * ***/ /* IMPORTANT: because of the special nature of * the player, we can't process each effect independently, * we have to merge effects from the four channel before * doing anything about it. For instance, there can be * several speed changein the same note, * only the last one takes effect. */ void set_speed(a, ch) struct automaton *a; struct channel *ch; { a->new_speed = a->para; a->do_stuff |= SET_SPEED; } void set_skip(a, ch) struct automaton *a; struct channel *ch; { /* yep, this is BCD. */ a->new_note = HI(a->para) * 10 + LOW(a->para); a->do_stuff |= SET_SKIP; } void set_fastskip(a, ch) struct automaton *a; struct channel *ch; { a->new_pattern = a->para; a->do_stuff |= SET_FASTSKIP; } /* immediate effect: starts the sample somewhere * off the start. */ void set_offset(a, ch) struct automaton *a; struct channel *ch; { ch->pointer = int_to_fix(a->para * 256); } /* change the volume of the current channel. * Is effective until there is a new set_volume, * slide_volume, or an instrument is reloaded * explicitly by giving its number. Obviously, if * you load an instrument and do a set_volume in the * same note, the set_volume will take precedence. */ void set_volume(a, ch) struct automaton *a; struct channel *ch; { ch->volume = a->para; } /* Initialize the whole effect table */ void init_effects(table) void (*table[])(); { table[0] = set_arpeggio; table[15] = set_speed; table[13] = set_skip; table[11] = set_fastskip; table[12] = set_volume; table[10] = set_slidevol; table[9] = set_offset; table[3] = set_portamento; table[5] = set_portaslide; table[2] = set_upslide; table[1] = set_downslide; table[4] = set_vibrato; table[6] = set_vibratoslide; table[14] = set_nothing; table[7] = set_nothing; table[8] = set_nothing; } * (attack/decay/sustain). */ void do_slidevol(ch) struct channel *ch; { ch->volume += ch->volumerate; ch->volume = MIN(ch->volume, MAX_player.c 666 1155 310 13607 5160354470 5644 /* player.c */ /* $Author: espie $ * $Id: player.c,v 2.10 1991/12/03 23:03:39 espie Exp $ * $Revision: 2.10 $ * $Log: player.c,v $ * Revision 2.10 1991/12/03 23:03:39 espie * Added transpose feature. * * Revision 2.9 1991/12/03 21:24:53 espie * Reverted to previous behaviour because of intromusic6.b. * * Revision 2.8 1991/12/03 20:43:46 espie * Added possibility to get back to MONO for the sgi. * * Revision 2.7 1991/12/03 18:07:38 espie * Added stereo capabilities to the indigo version. * * Revision 2.6 1991/12/03 17:12:33 espie * Minor changes ?? * * Revision 2.5 1991/11/19 16:07:19 espie * Added comments, moved minor stuff around. * * Revision 2.4 1991/11/18 14:10:30 espie * Moved resample part and empty sample test to audio. * * Revision 2.3 1991/11/18 01:23:30 espie * Added two level of fault tolerancy. * * Revision 2.2 1991/11/18 01:12:31 espie * Added some control on the number of replays, * and better error recovery. * * Revision 2.1 1991/11/17 23:07:58 espie * Coming from str32. * */ static char *id = "$Id: player.c,v 2.10 1991/12/03 23:03:39 espie Exp $"; #ifdef SHOW_SEQ char *num[] = { " 0", " 1", " 2", " 3", " 4", " 5", " 6", " 7", " 8", " 9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39"}; #endif #include #include "extern.h" #include "machine.h" #include "song.h" #include "channel.h" #include "pref.h" /* init_channel(ch, dummy): * setup channel, with initially * a dummy sample ready to play, * and no note. */ void init_channel(ch, dummy) struct channel *ch; struct sample_info *dummy; { ch->samp = dummy; ch->mode = DO_NOTHING; ch->pointer = 0; ch->step = 0; ch->volume = 0; ch->pitch = 0; ch->note = NO_NOTE; /* we don't setup arpeggio values. */ ch->viboffset = 0; ch->vibdepth = 0; ch->slide = 0; ch->pitchgoal = 0; ch->pitchrate = 0; ch->volumerate = 0; ch->vibrate = 0; ch->adjust = do_nothing; } int VSYNC; /* base number of sample to output */ void (*eval[NUMBER_EFFECTS])(); /* the effect table */ int oversample; /* oversample value */ int frequency; /* output frequency */ int channel; /* channel loop counter */ struct channel chan[NUMBER_TRACKS]; /* every channel */ int countdown; /* keep playing the tune or not */ struct song_info *info; struct sample_info **voices; struct automaton a; void init_player(o, f) int o, f; { oversample = o; frequency = f; init_tables(oversample, frequency); init_effects(eval); } void setup_effect(ch, a, e) struct channel *ch; struct automaton *a; struct event *e; { int samp, cmd; /* retrieves all the parameters */ samp = e[a->note_num].sample_number; a->note = e[a->note_num].note; if (a->note != NO_NOTE) a->pitch = pitch_table[a->note]; else a->pitch = e[a->note_num].pitch; cmd = e[a->note_num].effect; a->para = e[a->note_num].parameters; if (a->pitch >= MAX_PITCH) { fprintf(stderr, "Pitch out of bounds %d\n", a->pitch); a->pitch = 0; error = FAULT; } #ifdef SHOW_SEQ if (samp == 0 && a->pitch == 0 && cmd == 0) printf("----------- "); else { printf("%s %s ", num[samp], a->pitch ? "N" : "-"); if (cmd == 0 && a->para == 0) printf("------ "); else printf("%s %3d ", num[cmd], a->para); } fflush(stdout); #endif /* load new instrument */ if (samp) { /* note that we can change sample in the middle * of a note. This is a *feature*, not a bug (see * intromusic6.b) */ ch->samp = voices[samp]; ch->volume = voices[samp]->volume; } /* check for a new note: cmd 3 (portamento) * is the special case where we do not restart * the note. */ if (a->pitch && cmd != 3) reset_note(ch, a->note, a->pitch); ch->adjust = do_nothing; /* do effects */ (eval[cmd])(a, ch); } void play_song(song, pref) struct song *song; struct pref *pref; { init_automaton(&a, song); VSYNC = frequency * 100 / pref->speed; /* a repeats of 0 is infinite replays */ if (pref->repeats) countdown = pref->repeats; else countdown = 1; info = song->info; voices = song->samples; for (channel = 0; channel < NUMBER_TRACKS; channel++) init_channel(chan + channel, voices[0]); printf("(%d):\n", info->length); while(countdown) { for (channel = 0; channel < NUMBER_TRACKS; channel++) if (a.counter == 0) /* setup effects */ setup_effect(chan + channel, &a, a.pattern->e[channel]); else /* do the effects */ (chan[channel].adjust)(chan + channel); /* advance player for the next tick */ next_tick(&a); /* actually output samples */ resample(chan, oversample, VSYNC / a.finespeed); switch(error) { case NONE: break; case ENDED: if (pref->repeats) countdown--; break; case SAMPLE_FAULT: if (!pref->tolerate) countdown = 0; break; case FAULT: if (pref->tolerate < 2) countdown = 0; break; case NEXT_SONG: case UNRECOVERABLE: countdown = 0; break; default: break; } error = NONE; } printf("\n"); } SET_SPEED; } void set_skip(a, ch) struct automaton *a; struct channel *ch; { /* yep, this is BCD. */ read.c 666 1155 310 27456 5160354500 5264 /* read.c */ /* $Author: espie $ * $Id: read.c,v 2.7 1991/12/03 23:03:39 espie Exp $ * $Revision: 2.7 $ * $Log: read.c,v $ * Revision 2.7 1991/12/03 23:03:39 espie * Added transpose feature. * * Revision 2.6 1991/12/03 21:24:53 espie * Feature fix: length 1 sample should be empty. * * Revision 2.5 1991/12/03 17:10:11 espie * Corrected repeat length problems concerning badly formed files, * added signature checking for new tracker files. * * Revision 2.4 1991/11/19 16:07:19 espie * Added comments, moved minor stuff around. * * Revision 2.3 1991/11/18 14:10:30 espie * Corrected small problem with repeat being too short. * * Revision 2.2 1991/11/18 01:10:45 espie * Minor corrections. * * Revision 2.1 1991/11/17 23:07:58 espie * Coded error types. More amiga specific stuff. * * Revision 2.0 1991/11/17 21:42:08 espie * New version. * * Revision 1.17 1991/11/17 16:30:48 espie * Forgot to return a song_info from new_song_info * * Revision 1.16 1991/11/16 15:50:34 espie * Tabs. * * Revision 1.15 1991/11/16 15:42:43 espie * Rationnalized error recovery. * There was a bug: you could try to deallocate * stuff in no-noland. Also, strings never got * to be freed. * * Revision 1.14 1991/11/15 20:57:34 espie * Centralized error control to error_song. * * Revision 1.13 1991/11/15 18:22:10 espie * Added a new test on length, aborts most modules now. * Maybe should say it as well. * * Revision 1.12 1991/11/10 16:26:14 espie * Nasty bug regarding evaluation order in getulong. * Bitten on the sparc. * * Revision 1.11 1991/11/09 17:47:33 espie * Added checkpoints for early return if file too short. * * Revision 1.10 1991/11/08 13:35:57 espie * Added memory recovery and error control. * * Revision 1.9 1991/11/08 12:37:37 espie * Bug in checkfp: should return an int * because characters are signed on some machines, * and we want to use it as un unsigned value * for computing short values. * * Revision 1.8 1991/11/07 23:29:09 espie * Suppressed ! warning for bad note. * * Revision 1.7 1991/11/07 21:40:16 espie * Added note support. * * Revision 1.6 1991/11/07 20:12:34 espie * Minor problem with version id. * * Revision 1.5 1991/11/07 20:11:10 espie * Added embedded version id. * * Revision 1.4 1991/11/07 15:27:02 espie * Added some error control, essentially checkgetc. * * Revision 1.3 1991/11/05 22:49:03 espie * Modified the format of the dump slightly for * a better readability. * * Revision 1.2 1991/11/04 20:27:05 espie * Corrected length and rep_length/rep_offset * which are given in words and should be converted to * bytes. * * Revision 1.1 1991/11/04 13:23:59 espie * Initial revision * * */ #include #include #include #include "extern.h" #include "machine.h" #include "song.h" #include "channel.h" static char *id = "$Id: read.c,v 2.7 1991/12/03 23:03:39 espie Exp $"; static int transpose; int find_note(pitch) int pitch; { int a, b, i; if (pitch == 0) return -1; a = 0; b = NUMBER_NOTES-1; while(b-a > 1) { i = (a+b)/2; if (pitch_table[i] == pitch) return i + transpose; if (pitch_table[i] > pitch) a = i; else b = i; } if (pitch_table[a] - FUZZ <= pitch) return a + transpose; if (pitch_table[b] + FUZZ >= pitch) return b + transpose; return NO_NOTE; } /* c = checkgetc(f): * gets a character from file f. * Aborts program if file f is finished */ int checkgetc(f) FILE *f; { int c; if ((c = fgetc(f)) == EOF) error = FILE_TOO_SHORT; return c; } /* s = getstring(f, len): * gets a soundtracker string from file f. * I.e, it is a fixed length string terminated * by a 0 if too short */ #define MAX_LEN 50 char *getstring(f, len) FILE *f; int len; { static char s[MAX_LEN]; char *new; int i; for (i = 0; i < len; i++) s[MIN(i, MAX_LEN - 1)] = checkgetc(f); s[MIN(len, MAX_LEN - 1)] = '\0'; new = malloc(strlen(s)+1); return strcpy(new, s); } /* byteskip(f, len) * same as fseek, xcpt it works on stdin */ void byteskip(f, len) FILE *f; int len; { int i; for (i = 0; i < len; i++) checkgetc(f); } /* v = getushort(f) * reads an unsigned short from f */ int getushort(f) FILE *f; { int i; i = checkgetc(f) << 8; return i | checkgetc(f); } void fill_sample_info(info, f) struct sample_info *info; FILE *f; { info->name = getstring(f, 22); info->length = getushort(f); info->finetune = checkgetc(f); info->volume = checkgetc(f); info->volume = MIN(info->volume, MAX_VOLUME); info->rp_offset = getushort(f); info->rp_length = getushort(f); /* the next check is for old modules for which * the sample data types are a bit confused, so * that what we were expecting to be #words is #bytes. */ /* not sure I understand the -1 myself, though it's * necessary to play kawai-k1 correctly */ if (info->rp_length + info->rp_offset - 1 > info->length) info->rp_offset /= 2; if (info->rp_length + info->rp_offset > info->length) info->rp_length = info->length - info->rp_offset; info->length *= 2; info->rp_offset *= 2; info->rp_length *= 2; /* in all logic, a 2-sized sample could exist, * but this is not the case, and even so, some * trackers output empty instruments as being 2-sized. */ if (info->length <= 2) return; info->start = (SAMPLE *)calloc(info->length, 1); if (info->rp_length > 2) info->rp_start = info->start + info->rp_offset; else info->rp_start = NULL; if (info->length > MAX_SAMPLE_LENGTH) error = CORRUPT_FILE; } void fill_song_info(info, f) struct song_info *info; FILE *f; { int i; int p; info->length = checkgetc(f); checkgetc(f); info->maxpat = -1; for (i = 0; i < NUMBER_PATTERNS; i++) { p = getc(f); if (p >= NUMBER_PATTERNS) p = 0; if (p > info->maxpat) info->maxpat = p; info->patnumber[i] = p; } info->maxpat++; if (info->maxpat == 0 || info->length == 0) error = CORRUPT_FILE; } void fill_event(e, f) struct event *e; FILE *f; { int a, b, c, d; a = checkgetc(f); b = checkgetc(f); c = checkgetc(f); d = checkgetc(f); e->sample_number = a & 0x10 | (c >> 4); e->effect = c & 0xf; e->parameters = d; e->pitch = ( (a & 15) << 8 ) | b; e->note = find_note(e->pitch); } void fill_pattern(pattern, f) struct block *pattern; FILE *f; { int i, j; for (i = 0; i < BLOCK_LENGTH; i++) for (j = 0; j < NUMBER_TRACKS; j++) fill_event(&(pattern->e[j][i]), f); } void read_sample(info, f) struct sample_info *info; FILE *f; { int i; if (info->start) { fread(info->start, 1, info->length, f); } } /*** * * new_XXX: allocates a new structure for a song. * clears each and every field as appropriate. * ***/ struct song *new_song() { struct song *new; int i; new = (struct song *)malloc(sizeof(struct song)); new->title = NULL; new->info = NULL; for (i = 0; i < NUMBER_SAMPLES; i++) new->samples[i] = NULL; return new; } struct sample_info *new_sample_info() { struct sample_info *new; new = (struct sample_info *)malloc(sizeof(struct sample_info)); new->name = NULL; new->length = NULL; new->start = NULL; new->rp_start = NULL; return new; } struct song_info *new_song_info() { struct song_info *new; new = (struct song_info *)malloc(sizeof(struct song_info)); new->length = 0; new->maxpat = -1; new->pblocks = NULL; return new; } /* release_song(song): gives back all memory * occupied by song. Assume that each structure * has been correctly allocated by a call to the * corresponding new_XXX function. */ void release_song(song) struct song *song; { int i; for (i = 0; i < 31; i++) { if (song->samples[i]) { if (song->samples[i]->start) free(song->samples[i]->start); if (song->samples[i]->name) free(song->samples[i]->name); free(song->samples[i]); } } if (song->info) { if (song->info->pblocks) free(song->info->pblocks); free(song->info); } if (song->title) free(song->title); free(song); } /* error_song(song): what we should return * if there was an error. Actually, is mostly * useful for its side effects. */ struct song *error_song(song) struct song *song; { release_song(song); fprintf(stderr, "Error while reading file\n"); return NULL; } /* bad_sig(f): read the signature on file f * and returns !0 if it is not a known sig. */ int bad_sig(f) FILE *f; { char a, b, c, d; a = fgetc(f); b = fgetc(f); c = fgetc(f); d = fgetc(f); if (a == 'M' && b == '.' && c == 'K' && d == '.') return 0; if (a == 'M' && b == '&' && c == 'K' && d == '!') return 0; if (a == 'F' && b == 'L' && c == 'T' && d == '4') return 0; return 1; } /* s = read_song(f, type): tries to read a song s * of type NEW/OLD in file f. Might fail, i.e., * returns NULL if file is not a mod file of the * correct type. */ struct song *read_song(f, type, tr) FILE *f; int type; int tr; { struct song *song; int i; int ninstr; error = NONE; transpose = tr; if (type == NEW || type == NEW_NO_CHECK) ninstr = 31; else ninstr = 15; song = new_song(); song->title = getstring(f, 20); if (error != NONE) return error_song(song); for (i = 0; i <= 31; i++) song->samples[i] = new_sample_info(); for (i = 1; i <= ninstr; i++) { fill_sample_info(song->samples[i], f); if (error != NONE) return error_song(song); } song->info = new_song_info(); fill_song_info(song->info, f); if (error != NONE) return error_song(song); if (type == NEW && bad_sig(f)) return error_song(song); if (type == NEW_NO_CHECK) byteskip(f, 4); song->info->pblocks = (struct block *) malloc(sizeof(struct block) * song->info->maxpat); for (i = 0; i < song->info->maxpat; i++) { fill_pattern(song->info->pblocks + i, f); if (error != NONE) return error_song(song); } for (i = 1; i <= ninstr; i++) read_sample(song->samples[i], f); if (error != NONE) return error_song(song); return song; } /*** * * dump_block/dump_song: * shows most of the readable info * concerning a module on the screen. * ***/ void dump_block(b) struct block *b; { int i, j; for (i = 0; i < BLOCK_LENGTH; i++) { for (j = 0; j < NUMBER_TRACKS; j++) { printf("%8d%5d%2d%4d", b->e[j][i].sample_number, b->e[j][i].pitch, b->e[j][i].effect, b->e[j][i].parameters); } printf("\n"); } } void dump_song(song) struct song *song; { int i; printf("%s\n", song->title); for (i = 1; i <= 31; i++) { if (song->samples[i]->start) { printf("\t%-30s", song->samples[i]->name); printf("%5d", song->samples[i]->length); if (song->samples[i]->rp_length > 2) printf("(%5d %5d)\n", song->samples[i]->rp_offset, song->samples[i]->rp_length); else printf("\n"); } } } ->length = 0; new->maxpat = -1; new->pblocks = NULL; return new; } /* release_song(song): gives back all memory * occupied by song. Assume that each structure * has been correctly allocated sgi_audio.c 666 1155 310 6254 5160354502 6267 /* sgi_audio.c */ /* $Author: espie $ * $Id: sgi_audio.c,v 2.5 1991/12/04 08:28:53 espie Exp $ * $Revision: 2.5 $ * $Log: sgi_audio.c,v $ * Revision 2.5 1991/12/04 08:28:53 espie * Separated mix/stereo stuff. * * Revision 2.4 1991/12/03 21:24:53 espie * Checked buffer size. * * Revision 2.3 1991/12/03 20:43:46 espie * Added possibility to get back to MONO for the sgi. * * Revision 2.2 1991/12/03 18:07:38 espie * Added stereo capabilities to the indigo version. * * Revision 2.1 1991/11/18 01:10:45 espie * Minor corrections. * * Revision 2.0 1991/11/17 21:42:08 espie * New version. * * Revision 1.9 1991/11/17 17:09:53 espie * Added missing prototypes. * * Revision 1.8 1991/11/16 15:42:43 espie * Can't remember what. * * Revision 1.7 1991/11/08 14:25:55 espie * Ask the frequency to the audio device. * * Revision 1.6 1991/11/07 20:12:34 espie * Minor problem with version id. * * Revision 1.5 1991/11/07 20:11:10 espie * Added embedded version id. * * Revision 1.4 1991/11/07 15:27:02 espie * Corrected bug: when closing audio, * we know wait for the samples queue to be empty. * * Revision 1.3 1991/11/05 22:49:03 espie * Added a #define. We can now be non commited as * far as the sampling rate is concerned. * * Revision 1.2 1991/11/04 13:23:59 espie * Use HZ macro. * * Revision 1.1 1991/11/03 22:46:33 espie * Initial revision * * */ #include #include #include #include "machine.h" #include "extern.h" extern int sginap(long ticks); static char *id = "$Id: sgi_audio.c,v 2.5 1991/12/04 08:28:53 espie Exp $"; signed short *buffer; int index; ALport audio; ALconfig config; long chpars[] = {AL_OUTPUT_RATE, 0}; static int stereo; /* are we playing stereo or not ? */ /* 256th of primary/secondary source for that side. */ static int primary, secondary; void set_mix(percent) int percent; { percent *= 256; percent /= 100; primary = percent; secondary = 512 - percent; } int open_audio(frequency) int frequency; { int number; chpars[1] = frequency; if (frequency == 0) ALgetparams(AL_DEFAULT_DEVICE, chpars, 2); else ALsetparams(AL_DEFAULT_DEVICE, chpars, 2); config = ALnewconfig(); if (stereo = !getenv("MONO")) { ALsetchannels(config, AL_STEREO); number = 2; set_mix(30); } else { ALsetchannels(config, AL_MONO); number = 1; } ALsetwidth(config, AL_SAMPLE_16); audio = ALopenport("soundtracker mono", "w", config); index = 0; buffer = malloc(sizeof(signed short) * number * chpars[1]); return chpars[1]; } void output_samples(int left, int right) { if (stereo) { buffer[index++] = (left * primary + right * secondary)/256; buffer[index++] = (right * primary + left * secondary)/256; } else buffer[index++] = left + right; } void flush_buffer(void) { ALwritesamps(audio, buffer, index); index = 0; } void close_audio(void) { while(ALgetfilled(audio) != 0) sginap(1); ALcloseport(audio); ALfreeconfig(config); } j][i].effect, b->e[j][i].parameters); } printf("\n"); } } void dump_song(song) struct song *song; { int i; printf("%s\n", song->title); for (i = 1; i <= 31; i++) { if (song->samples[i]->start) { printf("\t%-30s", song->samples[i]->nasparc_audio.c 666 1155 310 6024 5160354505 6613 /* sparc_audio.h */ /* $Author: espie $ * $Id: sparc_audio.c,v 2.5 1991/12/04 14:04:15 espie Exp $ * $Revision: 2.5 $ * $Log: sparc_audio.c,v $ * Revision 2.5 1991/12/04 14:04:15 espie * Minor fix. * * Revision 2.4 1991/12/04 08:28:53 espie * Separated mix/stereo stuff. * * Revision 2.3 1991/12/03 20:43:46 espie * Added possibility to get back to MONO for the sgi. * * Revision 2.2 1991/12/03 18:07:38 espie * Added stereo capabilities to the indigo version. * * Revision 2.1 1991/11/18 01:10:45 espie * Minor corrections. * * Revision 2.0 1991/11/17 21:42:08 espie * New version. * * Revision 1.6 1991/11/16 15:42:43 espie * Can't remember what, must be tabs. * * Revision 1.5 1991/11/08 14:25:55 espie * We now have to return frequency. * * Revision 1.4 1991/11/07 20:12:34 espie * Minor problem with version id. * * Revision 1.3 1991/11/07 20:11:10 espie * Added embedded version id. * * Revision 1.2 1991/11/04 13:23:59 espie * I don't know... * * Revision 1.1 1991/11/03 22:46:57 espie * Initial revision * * */ #include #include "extern.h" #include "machine.h" static char *id = "$Id: sparc_audio.c,v 2.5 1991/12/04 14:04:15 espie Exp $"; FILE *audio; int open_audio(frequency) int frequency; { audio = fopen("/dev/audio", "w"); if (audio == NULL) { fprintf(stderr, "Error: could not open audio\n"); exit(10); } return 8000; } void set_mix(percent) int percent; { /* void operation */ } int sign(x) unsigned char x; { return x; } /************************************************************************/ /* 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); } void output_samples(left, right) int left, right; { fputc(cvt((left + right)/16), audio); } void flush_buffer() { } void close_audio() { fclose(audio); } 0; } void close_audio(void) { while(ALgetfilled(audio) != 0) sginap(1); ALcloseport(audio); ALfreeconfig(config); } j][i].effect, b->e[j][i].parameters); } printf("\n"); } } void dump_song(song) struct song *song; { int i; printf("%s\n", song->title); for (i = 1; i <= 31; i++) { if (song->samples[i]->start) { printf("\t%-30s", song->samples[i]->nastr32.c 666 1155 310 26356 5160354506 5332 /* str32.c */ /* plays sound/noisetracker files on Sparc, silicon graphics. * Authors : Liam Corner - zenith@dcs.warwick.ac.uk * Marc Espie - espie@dmi.ens.fr * * Usage : tracker * this version plays compressed files and simple zoo archives * as well. */ /* $Author: espie $ * $Id: str32.c,v 2.9 1991/12/04 08:28:53 espie Exp $ * $Revision: 2.9 $ * $Log: str32.c,v $ * Revision 2.9 1991/12/04 08:28:53 espie * Separated mix/stereo stuff. * * Revision 2.8 1991/12/03 23:03:39 espie * Added transpose feature. * * Revision 2.7 1991/12/03 20:43:46 espie * Added possibility to get back to MONO for the sgi. * * Revision 2.6 1991/12/03 18:07:38 espie * Added stereo capabilities to the indigo version. * * Revision 2.5 1991/12/03 17:10:11 espie * Added recovery and reread for automatic recognition * of old/new tracker files. * * Revision 2.4 1991/11/20 20:46:35 espie * Minor correction. * * Revision 2.3 1991/11/18 01:23:30 espie * Added two level of fault tolerancy. * * Revision 2.2 1991/11/18 01:10:45 espie * Added more rational options. * * Revision 2.1 1991/11/17 23:07:58 espie * Moved almost everything to audio and automaton. * * Revision 2.0 1991/11/17 21:42:08 espie * Structured part of the code, especially replay ``automaton'' * and setting up of effects. * * Revision 1.26 1991/11/17 17:09:53 espie * Added missing prototypes. * * Revision 1.25 1991/11/17 16:30:48 espie * Corrected cosmetic details, mostly. * * Revision 1.24 1991/11/16 16:57:01 espie * Forgot that read_song only returned NULL on read problem. * Should test that before printing Ok. * * Revision 1.23 1991/11/16 16:54:19 espie * Some more info while loading files. * Added FAULT env variable, FAULT resistant playing, * for playing modules which are not quite correct. * * Revision 1.22 1991/11/16 15:42:43 espie * tabs. * * Revision 1.21 1991/11/15 20:57:34 espie * Minor cleanup, mostly comments. * * Revision 1.20 1991/11/15 18:22:10 espie * Serious bug: dochangespeed was not reset all the time. * Check all these parameters, they MUST be reset for * each new song. * * Revision 1.19 1991/11/09 20:01:56 espie * Fixed a stupid bug: when env variable LOOPING was * undefined, we got a segv on strcmp. * Now we just test for its existence, since this is * about all we want... * * Revision 1.18 1991/11/09 17:47:33 espie * Bug correction: when doing arpeggio, there might not * be a new note, so we have to save the old note value * and do the arppeggio on that note. * * Revision 1.17 1991/11/08 14:47:52 espie * Completely added control with OVERSAMPLE and FREQUENCY. * * Revision 1.15 1991/11/08 13:35:57 espie * Added control flow. * * Revision 1.14 1991/11/08 12:37:37 espie * Added pipe decompression, so that now you can do * str32 file.Z directly. * stdin may go away. * * Looping is now controlled by an environment variable. * * Revision 1.13 1991/11/07 21:40:16 espie * Added arpeggio. * * Revision 1.12 1991/11/07 20:12:34 espie * Minor problem with version id. * * Revision 1.11 1991/11/07 20:09:06 espie * Added embedded version id. * * Revision 1.10 1991/11/07 20:05:53 espie * Added vibslide and portaslide. * * Revision 1.9 1991/11/07 15:27:02 espie * Added speed command. * Added signal control. * Error checking: there shouldn't be that many * segv signals any more. * * Revision 1.8 1991/11/06 09:46:06 espie * Moved every command to commands.c. * * Revision 1.7 1991/11/06 00:03:11 espie * Added effect #9, change offset in sample. * It seems to work quite correctly (example: sampled annie). * * Revision 1.6 1991/11/05 22:49:03 espie * Added some debug code for showing the full * sequence for a file. * Corrected the bug in volume slide: there is * no default value, i.e., if it is 0, it is 0, * as stupid as it may seem. * Added vibrato. * Added fastskip/corrected skip. * * Revision 1.5 1991/11/05 11:14:59 espie * Modified control flow of the player till * it looks like something reasonable (i.e., * the structure is more natural and reflects * the way stuff is played actually...) * * Added some #define for a slightly higher level * of abstraction ( :-) ). * * Revision 1.4 1991/11/04 20:27:05 espie * Do not restart the sound when we change instruments * on the fly. A bit strange, but it works that way. * * Revision 1.3 1991/11/04 13:23:59 espie * Modified main to use new data structures. * The sound player is MUCH cleaner, it uses now * a 3-state automaton for each voice. * * Revision 1.2 1991/11/04 08:01:20 espie * Corrected ruckus with data type of sample. * * Revision 1.1 1991/11/03 22:47:05 espie * Initial revision * * */ static char *id = "$Id: str32.c,v 2.9 1991/12/04 08:28:53 espie Exp $"; #include #include #include #include /* this should be in getopt.h, but sparc doesn't know it... */ extern int getopt(); extern char *optarg; extern int optind, opterr; #include "extern.h" #include "machine.h" #include "song.h" #include "pref.h" /* global variable to catch various types of errors * and achieve the desired flow of control */ int error; /* signal handlers */ void goodbye(sig) int sig; { printf("\nSignal %d\n", sig); exit(10); } void nextsong(sig) int sig; { printf("\nSignal %d\n", sig); signal(sig, nextsong); error = NEXT_SONG; } /* v = read_env(name, default): reads the value v * in the environment, supplies a defaults. */ int read_env(name, def) char *name; int def; { char *var; int value; var = getenv(name); if (!var) return def; if (sscanf(var, "%d", &value) == 1) return value; else return def; } /*** * * Handling compressed modules. * ***/ int check_Z(s) char *s; { int i; i = strlen(s); if (i < 2) return 0; return s[i-1] == 'Z' && s[i-2] == '.'; } int check_zoo(s) char *s; { int i; i = strlen(s); if (i < 4) return 0; return s[i-1] == 'o' && s[i-2] == 'o' && s[i-3] == 'z' && s[i-4] == '.'; } /* small hack for transposing songs on the fly */ static int transpose; struct song *do_read_song(s, type) char *s; int type; { struct song *song; FILE *fp; printf("(%s)...", s); fflush(stdout); if (check_Z(s)) { char pipe[200]; sprintf(pipe, "zcat %s", s); fp = popen(pipe, "r"); if (fp == NULL) { fprintf(stderr, "Unable to open tune file %s\n", s); return NULL; } song = read_song(fp, type, transpose); pclose(fp); } else if (check_zoo(s)) { char pipe[200]; sprintf(pipe, "zoo xpq %s", s); fp = popen(pipe, "r"); if (fp == NULL) { fprintf(stderr, "Unable to open tune file %s\n", s); return NULL; } song = read_song(fp, type, transpose); pclose(fp); } else { fp = fopen(s, "r"); if (fp == NULL) { fprintf(stderr, "Unable to open tune file %s\n", s); return NULL; } song = read_song(fp, type, transpose); fclose(fp); } if (song) printf("Ok!\n"); return song; } int optvalue(def) int def; { int d; if (sscanf(optarg, "%d", &d) == 1) return d; else { optind--; return def; } } int main (argc, argv) int argc; char **argv; { int frequency; int oversample; struct pref pref; struct song *song; int index; int c; int default_type; signal(2, nextsong); signal(3, goodbye); frequency = read_env("FREQUENCY", 0); oversample = read_env("OVERSAMPLE", 1); transpose = read_env("TRANSPOSE", 0); frequency = open_audio(frequency); init_player(oversample, frequency); /* check the command name for default reading type */ if (strcmp(argv[0], "otracker") == 0) default_type = OLD; else if (strcmp(argv[0], "ntracker") == 0) default_type = NEW; else default_type = BOTH; pref.type = default_type; pref.repeats = 1; pref.speed = 50; pref.tolerate = 1; for (optind = 1; optind < argc; optind++) { while ((c = getopt(argc, argv, "onhbr:s:f:m:" )) != -1) switch(c) { case 'o': /* old tracker type */ pref.type = OLD; break; case 'n': /* new tracker type */ pref.type = NEW; break; case 'b': /* both tracker types */ pref.type = BOTH; break; case 'r': /* number of repeats */ pref.repeats = optvalue(0); break; case 's': /* speed */ pref.speed = optvalue(50); break; case 'f': /* level of fault */ pref.tolerate = optvalue(2); break; case 'm': /* % of channel mix. * 0->full stereo, 100->mono */ set_mix(optvalue(30)); break; case 'h': /* template */ fprintf(stderr, "%s options:\n", argv[0]); fprintf(stderr, "\to : old tracker format"); if (default_type == OLD) fprintf(stderr, " (default)"); fprintf(stderr, "\n\tn : new tracker format"); if (default_type == NEW) fprintf(stderr, " (default)"); fprintf(stderr, "\n\tb : try both formats"); if (default_type == BOTH) fprintf(stderr, " (default)"); fprintf(stderr, "\n\tr n: number of repeats (0 is forever, "); fprintf(stderr, "1 is the default)\n"); fprintf(stderr, "\ts n: speed (default 50, "); fprintf(stderr, "some songs want 60)\n"); fprintf(stderr, "\tf : tolerate faults\n"); fprintf(stderr, "\tf 0: abort song if problem (default)\n"); fprintf(stderr, "\tm p: percent of mix (0 is spatial"); fprintf(stderr, "100 is mono)\n"); exit(2); } if (optind < argc) { switch(pref.type) { case BOTH: song = do_read_song(argv[optind], NEW); if (!song) song = do_read_song(argv[optind], OLD); break; case OLD: song = do_read_song(argv[optind], pref.type); break; /* this is explicitly flagged as a new module, * so we don't need to look for a signature. */ case NEW: song = do_read_song(argv[optind], NEW_NO_CHECK); break; } if (song == NULL) continue; dump_song(song); play_song(song, &pref); release_song(song); } } close_audio(); return 0; } = read_song(fp, type, transpose); fclose(fp); } if (song) printf("Ok!\n"); return song; } int optvalue(def) int def; { int d; if (sscanf(optarg, "%d", &d) == 1) return d; else { song.h 666 1155 310 3033 5160354503 5270 /* song.h */ /* internal data structures for the soundtracker player routine.... */ /* $Author: espie $ * $Date: 1991/11/17 23:07:58 $ * $Revision: 2.1 $ * $Log: song.h,v $ * Revision 2.1 1991/11/17 23:07:58 espie * Added some symbolic constants. * * Revision 2.0 1991/11/17 21:42:08 espie * New version. * * Revision 1.1 1991/11/04 13:23:59 espie * Initial revision * * */ #define NUMBER_SAMPLES 32 #define BLOCK_LENGTH 64 #define NUMBER_TRACKS 4 #define NUMBER_PATTERNS 128 #define NUMBER_EFFECTS 16 #define MIN_PITCH 113 #define MAX_PITCH 1023 #define MIN_VOLUME 0 #define MAX_VOLUME 64 /* the fuzz in note pitch */ #define FUZZ 2 /* we refuse to allocate more than 500000 bytes for one sample */ #define MAX_SAMPLE_LENGTH 500000 struct sample_info { char *name; int length, rp_offset, rp_length; int volume; int finetune; SAMPLE *start, *rp_start; }; /* the actual parameters may be split in two halves occasionnally */ #define LOW(para) ((para) & 15) #define HI(para) ((para) >> 4) struct event { unsigned char sample_number; unsigned char effect; unsigned char parameters; unsigned char note; int pitch; }; struct block { struct event e[NUMBER_TRACKS][BLOCK_LENGTH]; }; struct song_info { int length; int maxpat; char patnumber[NUMBER_PATTERNS]; struct block *pblocks; }; struct song { char *title; /* sample 0 is always a dummy sample */ struct sample_info *samples[NUMBER_SAMPLES]; struct song_info *info; }; level of fault */ pref.tolerate = optvalue(2); break; case 'm': /* % of channel mix. * 0->full stereo, 100->mono */ set_mix(optvalue(30)); break; case 'h': /* template */ fprintf(stderr, "%s options:\n", argv[0]); fprintf(stderr, "\to : old tracker format"); if (default_type == OLD) fprintf(stdersgi.h 666 1155 310 1473 5160354501 5110 /* sgi.h */ /* $Author: espie $ * $Id: sgi.h,v 2.1 1991/11/18 01:10:45 espie Exp $ * $Revision: 2.1 $ * $Log: sgi.h,v $ * Revision 2.1 1991/11/18 01:10:45 espie * Minor corrections. * * Revision 2.0 1991/11/17 21:42:08 espie * New version. * * Revision 1.5 1991/11/08 14:25:55 espie * Static speed parameters VSYNC and nS are now dynamic. * * Revision 1.4 1991/11/04 20:27:05 espie * Moved to 48000Hz, again. * * Revision 1.3 1991/11/04 13:23:59 espie * Just changed sampling frequency to allow for debugging. * * Revision 1.2 1991/11/04 08:01:20 espie * Corrected ruckus with data type of sample. * * Revision 1.1 1991/11/03 22:46:05 espie * Initial revision * * */ /* this is the precise type for storing the samples: * should be signed char. */ typedef signed char SAMPLE; ffset, rp_length; int volume; int finetune; SAMPLE *start, *rp_start; }; /* the actual parameters may be split in two halves occasionnally */ #define LOW(para) ((para) & 15) #define Hsparc.h 666 1155 310 2125 5160354504 5434 /* sparc.h */ /* $Author: espie $ * $Id: sparc.h,v 2.0 1991/11/17 21:42:08 espie Exp $ * $Revision: 2.0 $ * $Log: sparc.h,v $ * Revision 2.0 1991/11/17 21:42:08 espie * New version. * * Revision 1.3 1991/11/08 14:25:55 espie * We now have to return frequency. * * Revision 1.2 1991/11/04 08:01:20 espie * Corrected ruckus with data type of sample. * * Revision 1.1 1991/11/03 22:46:23 espie * Initial revision * * */ /************************************************************/ /* nS is the number of nSeconds that a sample is played for */ /* the sparc plays at 8000 bytes/sec => */ /* 1 byte = 125000 nSec */ /* VSYNC is the number of bytes played in 1/50 sec */ /* i.e., 8000/50 */ /************************************************************/ #define HZ 8000 /* at some point, we should decide for an oversample rate. * 2 seems like a good idea. */ /* this is the type for storing the samples. * Warning: char must be signed. */ typedef char SAMPLE; char effect; unsigned char parameters; unsigned char note; int pitch; }; struct block { struct event e[NUMBER_TRACKS][BLOCK_LENGTH]; }; struct song_info { int length; int maxpat; char patnumber[NUMBER_PATTERNS]; struct block *pblocks; }; struct song { char *title; /* sample 0 is always a dummy sample */ struct sample_info *samples[NUMBER_SAMPLES]; struextern.h 666 1155 310 12542 5160354467 5665 /* extern.h */ /* $Author: espie $ * $Id: extern.h,v 2.9 1991/12/04 08:28:53 espie Exp $ * $Revision: 2.9 $ * $Log: extern.h,v $ * Revision 2.9 1991/12/04 08:28:53 espie * Separated mix/stereo stuff. * * Revision 2.8 1991/12/03 20:43:46 espie * Added possibility to get back to MONO for the sgi. * * Revision 2.7 1991/12/03 18:07:38 espie * Added stereo capabilities to the indigo version. * * Revision 2.6 1991/12/03 17:10:11 espie * Added some new song types to automatize the choice process. * * Revision 2.5 1991/11/19 16:07:19 espie * Added comments, moved minor stuff around. * * Revision 2.4 1991/11/18 14:10:30 espie * Moved resampling to audio, added prototype. * * Revision 2.3 1991/11/18 01:23:30 espie * Added SAMPLE_FAULT, for trying to play * a note without a sample (not really an error). * * Revision 2.2 1991/11/18 01:10:45 espie * Added some prototypes. * * Revision 2.1 1991/11/17 23:07:58 espie * Moved prototypes around, added new functions. * * Revision 2.0 1991/11/17 21:42:08 espie * Added prototypes for new functions. * Removed prototypes for some commands. * * Revision 1.7 1991/11/08 14:25:55 espie * Modified audio prototype so that you can change * frequency. * * Revision 1.6 1991/11/08 13:35:57 espie * Added prototype for release_song. * * Revision 1.5 1991/11/07 21:40:16 espie * Added arpeggio effect. * * Revision 1.4 1991/11/07 20:05:53 espie * Added entries for new effects. * * Revision 1.3 1991/11/06 09:46:06 espie * Added entries for commands.c. * * Revision 1.2 1991/11/04 13:23:59 espie * Moved some definitions from str32. * Added debug. * * Revision 1.1 1991/11/03 22:45:26 espie * Initial revision * */ /* str32.c */ /* error types. Everything is centralized, * and we check in some places (see read, player and str32) * that there was no error. Additionnally signal traps work * that way too. */ /* normal state */ #define NONE 0 /* read error */ #define FILE_TOO_SHORT 1 #define CORRUPT_FILE 2 /* trap error: goto next song right now */ #define NEXT_SONG 3 /* run time problem */ #define FAULT 4 /* the song has ended */ #define ENDED 5 /* unrecoverable problem: typically, trying to * jump to nowhere land. */ #define UNRECOVERABLE 6 /* Missing sample. Very common error, not too serious. */ #define SAMPLE_FAULT 7 extern int error; /* xxx_audio.c */ /* real_freq = open_audio(ask_freq): * try to open audio with a sampling rate of ask_freq. * We get the real frequency back. If we ask for 0, we * get the ``preferred'' frequency. */ extern int open_audio(); /* close_audio(): * returns the audio to the system control, doing necessary * cleanup */ extern void close_audio(); /* set_mix(percent): set mix channels level. * 0: spatial stereo. 100: mono. */ extern void set_mix(); /* output_samples(l, r): outputs a pair of stereo samples. * Samples are 15 bits signed. */ extern void output_samples(); /* flush_buffer(): call from time to time, because buffering * is done by the program to get better (?) performance. */ extern void flush_buffer(); /* automaton.c */ /* init_automaton(a, song): * put the automaton a in the right state to play song. */ extern void init_automaton(); /* next_tick(a): * set up everything for the next tick. */ extern void next_tick(); /* read.c */ /* s = read_song(f, type): * tries to read f as a song of type NEW/OLD. * returns NULL (and an error) if it doesn't work. * Returns a dynamic song structure if successful. */ extern struct song *read_song(); /* dump_song(s): * displays some information pertinent to the given * song s. */ extern void dump_song(); /* release_song(s): * release all the memory song occupies. */ extern void release_song(); /* commands.c */ /* init_effects(): sets up all data for the effects */ extern void init_effects(); /* do_nothing: this is the default behavior for an effect. */ extern void do_nothing(); /* audio.c */ /* init_tables(oversample, frequency): * precomputes the step_table and the pitch_table * according to the desired oversample and frequency. * This is static, you can call it again whenever you want. */ extern void init_tables(); /* pitch of each and every note */ extern int pitch_table[]; /* resample(chan, oversample, number): * send number samples out computed according * to the current state of chan[0:NUMBER_CHANNELS], * and oversample. */ extern void resample(); /* reset_note(ch, note, pitch): * set channel ch to play note at pitch pitch */ extern void reset_note(); /* set_current_pitch(ch, pitch): * set channel ch to play at pitch pitch */ extern void set_current_pitch(); /* player.c */ /* init_player(oversample, frequency): * sets up the player for a given oversample and * output frequency. */ extern void init_player(); /* play_song(song, pref): * plays the song according to the current pref. */ extern void play_song(); #define ACCURACY 16 #define fix_to_int(x) ((x) >> ACCURACY) #define int_to_fix(x) ((x) << ACCURACY) /* some definitions for being able to read song.h */ #define OLD 0 #define NEW 1 /* special new type: for when we try to read it as both types. */ #define BOTH 2 /* special type: does not check the signature */ #define NEW_NO_CHECK 3 #define NUMBER_NOTES 120 #define MIN(A,B) ((A)<(B) ? (A) : (B)) #define MAX(A,B) ((A)>(B) ? (A) : (B)) #define D fprintf(stderr, "%d\n", __LINE__); song; } int optvalue(def) int def; { int d; if (sscanf(optarg, "%d", &d) == 1) return d; else { channel.h 666 1155 310 6764 5160354465 5757 /* channel.h */ /* $Author: espie $ * $Id: channel.h,v 2.3 1991/12/03 23:03:39 espie Exp $ * $Revision: 2.3 $ * $Log: channel.h,v $ * Revision 2.3 1991/12/03 23:03:39 espie * Added transpose feature. * * Revision 2.2 1991/11/19 16:07:19 espie * Added comments, moved minor stuff around. * * Revision 2.1 1991/11/17 23:07:58 espie * Added some comments. * * Revision 2.0 1991/11/17 21:42:08 espie * Structured part of the code, especially replay ``automaton'' * and setting up of effects. * * Revision 1.5 1991/11/16 16:54:19 espie * Added comments to each and every field. * * Revision 1.4 1991/11/16 15:42:43 espie * tabs. * * Revision 1.3 1991/11/09 17:47:33 espie * Bug correction: when doing arpeggio, there might not * be a new note, so we have to save the old note value * and do the arppeggio on that note. * * Revision 1.2 1991/11/07 21:40:16 espie * Added fields for arpeggio. * * Revision 1.1 1991/11/06 09:46:06 espie * Initial revision * * */ /* DO_NOTHING is also used for the automaton */ #define DO_NOTHING 0 #define PLAY 1 #define REPLAY 2 #define MAX_ARP 3 /* there is no note in each channel initially. * This is defensive programming, because some * commands rely on the previous note. Checking * that there was no previous note is a way to * detect faulty modules. */ #define NO_NOTE 255 struct channel { struct sample_info *samp; int mode; /* automaton state for the sound generatio */ unsigned int pointer; /* current sample position (fixed pos) */ unsigned int step; /* sample position increment (gives pitch) */ int volume; /* current volume of the sample (0-64) */ int pitch; /* current pitch of the sample */ int note; /* we have to save the note cause */ /* we can do an arpeggio without a new note */ int arp[MAX_ARP]; /* the three pitch values for an arpeggio */ int arpindex; /* an index to know which note the arpeggio is doing */ int viboffset; /* current offset for vibrato (if any) */ int vibdepth; /* depth of vibrato (if any) */ int slide; /* step size of pitch slide */ int pitchgoal; /* pitch to slide to */ int pitchrate; /* step rate for portamento */ int volumerate; /* step rate for volume slide */ int vibrate; /* step rate for vibrato */ void (*adjust)(); /* current command to adjust parameters */ }; /* DO_NOTHING was already declared for the channel #define DO_NOTHING 0 */ #define SET_SPEED 1 #define SET_SKIP 2 #define SET_FASTSKIP 4 #define NORMAL_SPEED 6 #define NORMAL_FINESPEED 100 struct automaton { int pattern_num; /* the pattern in the song */ int note_num; /* the note in the pattern */ struct block *pattern; /* the physical pattern */ struct song_info *info; /* we need the song_info */ int counter; /* the fine position inside the effect */ int speed; /* the `speed', number of effect repeats */ int finespeed; /* the finespeed, base is 100 */ int do_stuff; /* keeping some stuff to do */ /* ... and parameters for it: */ int new_speed, new_note, new_pattern, new_fastspeed; int pitch, note, para; /* some extra parameters effects need */ }; ng able to rpref.h 666 1155 310 1031 5160354477 5264 /* pref.h */ /* $Author: espie $ * $Id: pref.h,v 2.2 1991/12/03 20:43:46 espie Exp $ * $Rev$ * $Log: pref.h,v $ * Revision 2.2 1991/12/03 20:43:46 espie * Added possibility to get back to MONO for the sgi. * * Revision 2.1 1991/12/03 18:07:38 espie * Added stereo capabilities to the indigo version. * * Revision 2.0 1991/11/18 09:02:09 espie * *** empty log message *** * * Revision 1.1 1991/11/18 09:00:58 espie * Initial revision * * */ struct pref { int type, speed, tolerate, repeats; }; 991/11/16 16:54:19 espie * Added comments to each and every field. * * Revision 1.4 1991/11/16 15:42:43 espie * tabs. * * Revision 1.3 1991/11/09 17:47:33 espie * Bug correction: when doing arpeggio, there might not * be a new note, so we have to save the old note value * and do the arppeggio on that note. * * Revision 1.2 1991/11/07 21:40:16 espie * Added fields for arpeggio. * * Revision 1.1 1991/11/06 09:46:06 espie * Initial revision * * */ makefile.sgi 644 1155 310 5177 5117160171 6437 # Makefile for sgi # # $Author: espie $ # $Id: makefile.sgi,v 2.5 1991/12/04 14:04:15 espie Exp espie $ # $Revision: 2.5 $ # $Log: makefile.sgi,v $ # Revision 2.5 1991/12/04 14:04:15 espie # Added doc file. # # Revision 2.4 1991/12/03 17:19:29 espie # Added ntracker. # # Revision 2.3 1991/11/19 16:07:19 espie # Added comments, moved minor stuff around. # # Revision 2.2 1991/11/18 01:10:45 espie # Updated, added install entry. # # Revision 2.1 1991/11/17 23:07:58 espie # Added new files. # # Revision 2.0 1991/11/17 21:42:08 espie # Completely corrected buggy tabs. # # Revision 1.11 1991/11/17 16:30:48 espie # Stupid bugs: have to keep the tabs to keep the makefile working. # # Revision 1.10 1991/11/16 15:50:34 espie # Tabs. # # Revision 1.9 1991/11/16 15:42:43 espie # Minor modifications in make clean. # # Revision 1.8 1991/11/09 17:49:14 espie # Added optimization a la sgi. # # Revision 1.7 1991/11/07 21:40:16 espie # Added some macros. # # Revision 1.5 1991/11/04 13:23:59 espie # Added some dependencies. # Entry for read.c. # # Revision 1.4 1991/11/04 08:01:20 espie # Added rcp to clipper # # Revision 1.3 1991/11/03 22:58:41 espie # Fixed name mistake. # # Revision 1.2 1991/11/03 22:55:23 espie # Added all, clean and export entries. # # Revision 1.1 1991/11/03 22:45:39 espie # Initial revision # # INSTALL_DIR = $(HOME)/bin O = .u MAIN_OPTS = -O3 COPTS = $(MAIN_OPTS) -j MACHINE = sgi OBJECTS = str32$O $(MACHINE)_audio$O read$O commands$O \ audio$O automaton$O player$O all: tracker otracker ntracker install: all cp tracker $(INSTALL_DIR) -rm -f ${INSTALL_DIR}/otracker ln ${INSTALL_DIR}/tracker ${INSTALL_DIR}/otracker -rm -f ${INSTALL_DIR}/ntracker ln ${INSTALL_DIR}/tracker ${INSTALL_DIR}/ntracker tracker: ${OBJECTS} cc -o tracker ${MAIN_OPTS} ${OBJECTS} -laudio -lm strip tracker otracker: tracker -rm -f otracker ln tracker otracker ntracker: tracker -rm -f ntracker ln tracker ntracker str32$O: str32.c machine.h song.h cc ${COPTS} str32.c $(MACHINE)_audio$O: $(MACHINE)_audio.c machine.h cc ${COPTS} $(MACHINE)_audio.c audio$O: audio.c cc ${COPTS} audio.c automaton$O: automaton.c machine.h song.h channel.h cc ${COPTS} automaton.c player$O: player.c machine.h song.h channel.h cc ${COPTS} player.c read$O: read.c machine.h song.h cc ${COPTS} read.c commands$O: commands.c channel.h machine.h song.h cc ${COPTS} commands.c machine.h: $(MACHINE).h cp $(MACHINE).h machine.h clean: -rm *.u *.o machine.h tracker otracker core str.tar str.tar.Z export: tar cvf str.tar *.c song.h sgi.h sparc.h extern.h channel.h \ pref.h makefile.* tracker.doc compress str.tar rcp str.tar.Z clipper: upid bugs: have to keep the tabs to keep the makefile working. # # Revision 1.10 1991/11/16 15:50:34 espie # Tabs. # # Revision 1.9 1991/11/16 15:42:43 espie # Minor modifications in make clean. # # Revision 1.8 1991/11/09 17:49:14 espie # Added optimization a la sgi. # # Revision 1.7 1991/11/07 21:40:16 espie # Added some macros. # # Revision 1.5 1991/11/04 13:23:59 espie # makefile.sparc 644 1155 310 5205 5117160173 6757 # Makefile for sparc # # $Author: espie $ # $Id: makefile.sparc,v 2.4 1991/12/04 14:04:15 espie Exp espie $ # $Revision: 2.4 $ # $Log: makefile.sparc,v $ # Revision 2.4 1991/12/04 14:04:15 espie # Added doc file. # # Revision 2.3 1991/12/03 17:19:29 espie # Added ntracker. # # Revision 2.2 1991/11/19 16:07:19 espie # Added comments, moved minor stuff around. # # Revision 2.2 1991/11/18 01:10:45 espie # Updated, added install entry. # # Revision 2.1 1991/11/17 23:07:58 espie # Added new files. # # Revision 2.0 1991/11/17 21:42:08 espie # Completely corrected buggy tabs. # # Revision 1.11 1991/11/17 16:30:48 espie # Stupid bugs: have to keep the tabs to keep the makefile working. # # Revision 1.10 1991/11/16 15:50:34 espie # Tabs. # # Revision 1.9 1991/11/16 15:42:43 espie # Minor modifications in make clean. # # Revision 1.8 1991/11/09 17:49:14 espie # Added optimization a la sgi. # # Revision 1.7 1991/11/07 21:40:16 espie # Added some macros. # # Revision 1.5 1991/11/04 13:23:59 espie # Added some dependencies. # Entry for read.c. # # Revision 1.4 1991/11/04 08:01:20 espie # Added rcp to clipper # # Revision 1.3 1991/11/03 22:58:41 espie # Fixed name mistake. # # Revision 1.2 1991/11/03 22:55:23 espie # Added all, clean and export entries. # # Revision 1.1 1991/11/03 22:45:39 espie # Initial revision # # INSTALL_DIR = $(HOME)/bin/sparc O = .o MAIN_OPTS = -O4 COPTS = $(MAIN_OPTS) -c MACHINE = sparc OBJECTS = str32$O $(MACHINE)_audio$O read$O commands$O \ audio$O automaton$O player$O all: tracker otracker ntracker install: all cp tracker $(INSTALL_DIR) -rm -f ${INSTALL_DIR}/otracker ln ${INSTALL_DIR}/tracker ${INSTALL_DIR}/otracker -rm -f ${INSTALL_DIR}/ntracker ln ${INSTALL_DIR}/tracker ${INSTALL_DIR}/ntracker tracker: ${OBJECTS} cc -o tracker ${MAIN_OPTS} ${OBJECTS} -lm strip tracker otracker: tracker -rm -f otracker ln tracker otracker ntracker: tracker -rm -f ntracker ln tracker ntracker str32$O: str32.c machine.h song.h cc ${COPTS} str32.c $(MACHINE)_audio$O: $(MACHINE)_audio.c machine.h cc ${COPTS} $(MACHINE)_audio.c audio$O: audio.c cc ${COPTS} audio.c automaton$O: automaton.c machine.h song.h channel.h cc ${COPTS} automaton.c player$O: player.c machine.h song.h channel.h cc ${COPTS} player.c read$O: read.c machine.h song.h cc ${COPTS} read.c commands$O: commands.c channel.h machine.h song.h cc ${COPTS} commands.c machine.h: $(MACHINE).h cp $(MACHINE).h machine.h clean: -rm *.u *.o machine.h tracker otracker core str.tar str.tar.Z export: tar cvf str.tar *.c song.h sgi.h sparc.h extern.h channel.h \ pref.h makefile.* tracker.doc compress str.tar rcp str.tar.Z clipper: : makefile.sparc,v $ # Revision 2.4 1991/12/04 14:04:15 espie # Added doc file. # # Revision 2.3 1991/12/03 17:19:29 espie # Added ntracker. # # Revision 2.2 1991/11/19 16:07:19 espie # Added comments, moved minor stuff around. # # Revision 2.2 1991/11/18 01:10:45 espie # Updated, added install entry. # # Revision 2.1 1991/11/17 23:07:58 espie # Added new files.tracker.doc 644 1155 144 11717 5160653563 6334 This is the version 2.1 of tracker. It includes a minor bug-fix to the speed-changing routine. So far, one module has been heard to be affected by it. tracker is a soundtracker player for sparc or silicon graphics machines. To build it, just use the appropriate makefile. tracker is a public domain program, it is not guaranteed to do anything at all, either useful or useless. Do with it as you will, but use it at your own risk. HOWEVER, IF YOU INTEND TO PORT IT TO ANOTHER MACHINE, I WOULD VERY MUCH APPRECIATE THAT YOU CHECK UP WITH ME FIRST. There just might already be a port in progress, or I might have a newer version. Even so, I'd like to be able to maintain one source instead of a bazillion of version. My e-mail is espie@ens.fr, not liable to change for some time. ``Soundtracker'' is a family of music composition programs that exists on the amiga. The resulting data files (modules) have been appearing on ftp sites for some time now. For a machine with sufficient horsepower and some audio capability, it is possible to emulate the amiga audio hardware in real time, and play those modules. After that, you're only limited by the machine's capabilities. The sparc is a bit poor (8Khz sampling), in contrast with the indigo, which gives an almost perfect rendition of most modules. This release of tracker supports most amiga soundtracker file formats, and plays most of the existing effects, so that about 98% of the modules are output correctly. There is no man page (write it yourself), but a short description of the options can be obtained with tracker -h. Here is some supplementary information. Environment variables: OVERSAMPLE can be used to control the acuracy of the reproduction. (The number of samples used to output one audio word). The higher, the better, but the more CPU it will use. The default value (1) is quite good at high frequencies, but not so for, for instance a poor sparc station's 8000 Hz. You can try, say, 2 or 3. After that, there won't be any noticeable improvement, and anyway, the program won't be fast enough to keep up with the output rate. FREQUENCY can be used to set the audio output at a specific frequency (if the hardware supports it). The hardware will decide which frequency to actually use, according to other external parameters. MONO on the sgi can be used to force mono output, which uses less cpu power. TRANSPOSE is the number of halftones to transpose each note (>0 is higher). Useful for low frequency sparcs which can't play some tunes accurately, or when you get bored... Most of the switches are here for compatibility reasons. As there are billions and billions.. wait, wrong series. As there are lots and lots of soundtrackers clones out there, they are not *quite* compatible with one another. Mainly, there was an old format and a newer format. You can force one of these formats by either renaming your command to ntracker or otracker, or you can use the -n and -o switch to try reading a file as a new tracker file, or an old tracker file. The default is to try first the new tracker format, then revert to the old tracker format (switch -b for both). There is also a speed problems. Most trackers use some timing which is dependent upon the powerline frequency... 60Hz in the states, 50Hz in Europe. Most modules have been composed in Europe, so the default is 50Hz, but you can set that speed to 60Hz with -s60. (Incidentally, you can try and speed up a module to amazing speeds like -s200, just for fun). The -f switch is not really there to be used, except if there is a module you really can't play, then try -f2. The -r switch is obvious (I hope). The -m value is a mix value. In real-world stereo, you hear each side on the other side, at least a bit. With headphones, this effect disappears, unless you mix a bit of each side on the other side. 0 is spatial stereo (not for headphones), 100 is mono. A reasonable value is 30. The perceived change tends to be logarithmic, interesting values would be 30, 70, 85, 90, 92, 95... Finally, the program itself is reasonably smart, you can use it as a module jukebox by giving a tracker * -like command line, then skip from one module to the next by sending a signal 2 (usually ^C) and aborting altogether with a signal 3 (usually ^\). It recognizes also modules packed with either compress or zoo... Adding your own compression method should be trivial. It is also fairly difficult to crash, because tracker modules are notorious problem files, with lots of formats problems. The original idea of the tracker was by Liam Corner, back when it was called str32, but there is precious little left of his original code (if any of it). I must admit, I would never have had the idea (nor the courage) to start that project all by myself. You can do what you want with the source code, though I would suggest that Liam and myself be cited if you add anything to it. Send bug reports to espie@ens.fr, encouragements and nice things to espie@ens.fr and zenith@dcs.warwick.ac.uk. -- Marc Espie, Paris, march the 15th 1992. any noticeable improvement, and anyway, the program won't be fast enough to keep up with the output rate. FREQUENCY can be used to set the audio output at a specific frequency (if the hardware supports it). The hardware will decide which frequency to actually use, according to other external parameters. MONO on the sgi can be used to force mono output, which uses less cpu power. TRANSPOSE is the number of halftones to transpose each note (>0 is higher). Useful for low frequency sparcs which can't play some tunes accurately, or when you get bored... Most of the switches are here for compatibility reasons. As there are billions and billions.. wait, wrong series. As there are lots and lots of soundtrackers clones out there, they are not *quite* compatible with one another. Mainly, there was an old format and a newer format. You can force one of these formats by either renaming your command to ntracker or otracker, or you can use the -n and -o switch to try reading a file as a new tracker file, or an old tracker file. The default is to try first the new t