home *** CD-ROM | disk | FTP | other *** search
- /* load.c
-
- /* loads a soundtracker binary file
- * into memory, using a more convenient format
- */
-
- /* $Author: Espie $
- * $Date: 91/05/16 15:05:24 $
- * $Revision: 1.34 $
- * $Log: load.c,v $
- * Revision 1.34 91/05/16 15:05:24 Espie
- * *** empty log message ***
- *
- * Revision 1.33 91/05/12 19:56:39 Espie
- * Shortened event structure.
- *
- * Revision 1.32 91/05/12 16:00:39 Espie
- * switched back to a char *.
- *
- * Revision 1.31 91/05/07 12:13:36 Espie
- * *** empty log message ***
- *
- * Revision 1.30 91/05/06 23:39:31 Espie
- * Now tries to load a WBArg.
- * Changes for accomodating new finetune and external find_note.
- *
- * Revision 1.29 91/05/06 15:15:06 Espie
- * Some more error checking, still not enough probably.
- *
- * Revision 1.28 91/05/05 19:06:41 Espie
- * *** empty log message ***
- *
- * Revision 1.27 91/05/05 03:59:37 Espie
- * Corrected other problems. Added some measure of error recovery
- *
- *
- * Revision 1.26 91/05/04 23:22:22 Espie
- * Corrected a very small bug in the handling of dummy samples.
- *
- * Revision 1.25 91/05/02 23:31:14 Espie
- * Now uses standard amigados filehandles... Might come in handy.
- *
- * Revision 1.24 91/05/02 11:20:48 Espie
- * Can now unload() empty songs...
- *
- * Revision 1.23 91/05/02 01:30:57 Espie
- * Correction of some small bugs, added a dummy sample for the player.
- *
- * Revision 1.22 91/04/30 16:52:09 Espie
- * Suppressed every IO.
- * Added channel tracks, should handle finetuned instruments.
- *
- * Revision 1.21 91/04/30 01:48:58 Espie
- * Corrected a BIG and stupid bug, there was two separate cleanups where
- * there should have been one, so that cleaning up an old module only happened
- * on exit of the program...
- *
- * Revision 1.20 91/04/30 00:35:15 Espie
- * Stable version III.
- *
- * Revision 1.19 91/04/30 00:23:34 Espie
- * New error checking, does still exit if not enough memory.
- *
- * Revision 1.18 91/04/29 23:53:56 Espie
- * Added unload_song(). Need a decent error recovery strategy right now.
- *
- * Revision 1.17 91/04/29 02:22:06 Espie
- * checkabort() added for user interruption,
- * since there are no longer critical sections.
- *
- * Revision 1.16 91/04/28 22:53:37 Espie
- * More leeway in the period computation.
- *
- * Revision 1.15 91/04/27 20:49:00 Espie
- * Small bug in find_note (order was reversed)
- *
- * Revision 1.14 91/04/27 16:45:56 Espie
- * Rounding errors for note period.
- *
- * Revision 1.13 91/04/24 15:27:19 Espie
- * Minor changes ???
- *
- * Revision 1.12 91/04/23 21:30:32 Espie
- * Revised so that one song can be automatically cleaned out.
- *
- * Revision 1.11 91/04/21 20:06:07 Espie
- * The loader now precomputes notes.
- * It does not yet precompiles the effects (next step ?).
- *
- * Revision 1.10 91/04/21 12:11:43 Espie
- * Stable version, known as bunch II.
- * Also features ``right'' log description.
- *
- * Revision 1.9 91/04/21 11:14:34
- * Bug in st files: repeat start is sometimes double what it should be.
- *
- * Revision 1.8 91/04/20 18:14:24
- *
- * Revision 1.7 91/04/19 13:22:20
- *
- * Revision 1.6 91/04/19 02:20:07
- * loader without error checking for last sample.
- * added dummy sample for simplified check.
- *
- * Revision 1.5 91/04/18 02:25:41
- * bunch I.
- *
- * Revision 1.4 91/04/17 18:50:42
- * This is now a working module. Only the block translation is not yet debugged.
- *
- * Revision 1.3 91/04/14 22:02:32
- * Playing version. This one knows how to play sample!
- *
- * Revision 1.2 91/04/14 18:06:52
- * This version is able to load most soundtrackers files without apparent mistakes.
- * I still have got to figure out how the audio hardware works.
- * So this doesn't play any tune.
- * Apparently no bug as long as I don't try anything with the hardware.
- *
- * Revision 1.1 91/04/11 18:39:23
- * Initial revision.
- * Not yet debugged or anything.
- * No interface for other modules.
- *
- */
-
- #include <exec/types.h>
- #include <exec/memory.h>
- #include <string.h>
- #include <stddef.h>
- #include <dos/dos.h>
- #include <workbench/startup.h>
- #include <proto/dos.h>
- #include <proto/exec.h>
- #include <custom/cleanup.h>
- #include "song.h"
- #include "proto.h"
- #include "periods.h"
-
- #include <proto/exec.h>
- #include <stdlib.h>
-
- /* definitions for the binary st file format
- */
-
- #define MAX_BLOCKS 128
- #define SAMPLE_NAME 22
- #define SONG_NAME 20
- #define OLD_SAMPLES 15
- #define NEW_SAMPLES 31
-
- struct binary_sample_info
- {
- char sample_name[SAMPLE_NAME];
- UWORD length;
- UBYTE finetune;
- UBYTE volume;
- UWORD rp_start;
- UWORD rp_length;
- };
-
- struct binary_song_info
- {
- UBYTE length;
- UBYTE thing;
- UBYTE block_number[MAX_BLOCKS];
- };
-
- struct binary_event
- {
- UBYTE detail[4];
- };
-
- struct interleaved
- {
- struct binary_event track[NUMBER_TRACKS];
- };
-
- struct binary_block
- {
- struct interleaved data[BLOCK_LENGTH];
- };
-
-
- /* a song is laid out like this:
- char song_name[SONG_NAME];
- struct sample_info samples[OLD_SAMPLES or NEW_SAMPLES];
- struct song_info song;
- (char sig[4] == "M.K." for new soundtrackers, with NEW_SAMPLES instruments)
- struct block blocks[number of blocks];
- UWORD sample0[length sample0];
- UWORD sample1[length sample1];
- ...
- UWORD samplen[length samplen]; where n=OLD_SAMPLES or NEW_SAMPLES.
- */
-
- struct noisetracker_header
- {
- char song_name[SONG_NAME];
- struct binary_sample_info sample_info[NEW_SAMPLES];
- struct binary_song_info song_info;
- ULONG sig;
- };
-
- struct old_st_header
- {
- char song_name[SONG_NAME];
- struct binary_sample_info sample_info[OLD_SAMPLES];
- struct binary_song_info song_info;
- /* no sig */
- };
-
- struct other_stuff
- {
- ULONG sig;
- };
-
- #define MK_SIG(a, b, c, d) ((a<<24) | (b<< 16) | (c<<8) | d)
-
- union header
- {
- struct noisetracker_header nt;
- struct old_st_header st;
- struct other_stuff other;
- } header;
-
-
-
-
- struct sample_info dummy =
- {
- "dummy sample",
- 0, 0, 0, 0, 0, 0, 0
- };
-
- int last_error;
-
-
- /* cstring(buffer, maxlength)
- * converts a soundtracker string into a decent cstring
- */
-
- char *cstring(CLEAN clear, char buffer[], int maxlength)
- {
- char *st;
- int i;
- for (i = 0; i < maxlength; i++)
- if (buffer[i] == 0)
- break;
- st = malloc(i+1);
- if (st)
- ToCleanL(clear, free, st);
- else
- return NULL;
- st[i] = 0;
- return strncpy(st, buffer, i);
- }
-
-
-
- struct sample_info *extract_sample_info(CLEAN clear, struct binary_sample_info *i)
- {
- struct sample_info *new;
- check_abort();
- if (i->length <= 1)
- return &dummy;
- new = malloc(sizeof(struct sample_info));
- if (new)
- ToCleanL(clear, free, new);
- else
- return NULL;
- new->length = i->length;
- new->finetune = normalize_finetune(i->finetune);
- new->volume = i->volume;
- if (i->rp_start + i->rp_length - 1 > i->length)
- {
- i->rp_start >>=1;
- }
- new->rp_offset = i->rp_start;
- new->rp_length = i->rp_length;
- new->name = cstring(clear, i->sample_name, SAMPLE_NAME);
- if (new->finetune < 0)
- {
- last_error = NOT_A_MOD;
- return NULL;
- }
- else
- return new;
- }
-
- int find_max(UBYTE block_number[], int max_index)
- {
- int current, i;
- for (current = -1, i = 0; i < max_index; i++)
- if (block_number[i] > current)
- current = block_number[i];
- /* a negative return indicates a problem */
- if (current > 127)
- return (-1);
- return current;
- }
-
- struct block **map_blocks(CLEAN clear, UBYTE block_number[], int length, struct block array[])
- {
- int i;
- struct block **pters;
- pters = malloc(length * sizeof(struct block *));
- if (pters)
- ToCleanL(clear, free, pters);
- else
- return NULL;
- for (i = 0; i < length; i++)
- pters[i] = array+block_number[i];
- return pters;
- }
-
- struct block *allocate_blocks(CLEAN clear, int number)
- {
- struct block *new;
- new = malloc(number * sizeof(struct block));
- if (new)
- ToCleanL(clear, free, new);
- else
- return NULL;
- return new;
- }
-
- struct song_info *extract_song_info(CLEAN clear, struct binary_song_info *b)
- {
- struct song_info *new;
- new = malloc(sizeof(struct song_info));
- if (new)
- ToCleanL(clear, free, new);
- else
- return NULL;
- new->length = b->length;
- new->thing = b->thing;
- new->total = find_max(b->block_number, 128)+1;
- if (new->total < 0)
- return NULL;
- new->physical = allocate_blocks(clear, new->total);
- if (!new->physical)
- return NULL;
- new->pblocks = map_blocks(clear, b->block_number, new->length,
- new->physical);
- if (!new->pblocks)
- return NULL;
- return new;
- }
-
-
- /* we will leave that alone for the time being */
-
- struct sample_info *instr_channel[NUMBER_TRACKS];
- struct sample_info **sample_array;
- int current_max;
-
- void parse_event(struct binary_event *b, struct event *e, int tn)
- {
- UWORD period;
- e->sample_number = (b->detail[0]&~15) | (b->detail[2]>>4);
- e->sample_number &= 31;
- if (e->sample_number)
- {
- instr_channel[tn] = sample_array[e->sample_number];
- if (e->sample_number > current_max)
- current_max = e->sample_number;
- }
- period = ((b->detail[0]&15)<<8) | b->detail[1];
- e->note = find_note(period, instr_channel[tn]->finetune);
- if (e->note > FINE_PERIOD)
- last_error = NOTE_PROBLEM;
- e->effect = b->detail[2]&15;
- e->parameters = b->detail[3];
- }
-
- void parse_block(struct binary_block *b, struct block *d)
- {
- int i, j;
- for (i = 0; i < BLOCK_LENGTH; i++)
- for (j = 0; j < NUMBER_TRACKS; j++)
- parse_event(&b->data[i].track[j], &d->e[j][i], j);
- }
-
- int read_blocks(CLEAN clear, BPTR f, int total, struct block *first)
- {
- struct binary_block b;
- int i;
- current_max = 0;
- for (i = 0; i < NUMBER_TRACKS; i++)
- instr_channel[i] = sample_array[0];
- for (i = 0; i < total; i++)
- {
- if (Read(f, &b, sizeof(b)) != sizeof(b))
- return 0;
- check_abort();
- parse_block(&b, first+i);
- }
- return current_max;
- }
-
- BOOL allocate_samples(CLEAN clear, struct song *s, int max_sn)
- {
- int i;
- int length;
- UWORD *p, *empty;
- /* so we don't have to multiply everything by 2
- */
-
- /* compute the total length
- */
- for (length = 1, i = 1 ; i <= max_sn; i++)
- {
- if (s->samples[i] != &dummy)
- length += s->samples[i]->length;
- }
-
- /* allocate corresponding chip memory
- */
- p = AllocMem(2 * length, MEMF_CHIP|MEMF_CLEAR);
- if (p)
- ToClean2L(clear, FreeMem, p, 2*length);
- else
- {
- last_error = OUT_OF_CHIP;
- return NULL;
- }
- empty = p++;
- dummy.start = empty;
- dummy.length = 1;
- dummy.rp_start = empty;
- dummy.rp_length = 1;
- /* compute addresses information
- */
- for (i = 1; i <= max_sn; i++)
- if (s->samples[i] != &dummy)
- {
- s->samples[i]->start = p;
- if (s->samples[i]->rp_length == 1)
- s->samples[i]->rp_start = empty;
- else
- s->samples[i]->rp_start = p + s->samples[i]->rp_offset;
- p+= s->samples[i]->length;
- }
- }
-
- BOOL read_sample(CLEAN clear, BPTR f, struct binary_sample_info *b,
- struct sample_info *s)
- {
- check_abort();
- if (Read(f, s->start, b->length*2) != b->length*2
- && b->length > 1)
- {
- last_error = MISSING_SAMPLE;
- return TRUE;
- }
- else
- return TRUE;
- }
-
-
- struct song *read_song(BPTR f)
- {
- struct song *s;
- int i, sn, max_sn;
- CLEAN clear;
- last_error = OUT_OF_MEMORY;
- clear = AllocClean(NIL);
- if (!clear)
- mayPanic("Couldn't allocate cleanup");
- s = malloc(sizeof(struct song));
- if (s)
- {
- s->clear = clear;
- ToCleanL(clear, free, s);
- }
- else
- {
- last_error = OUT_OF_MEMORY;
- return NULL;
- }
- if (Read(f, &header, sizeof(union header)) != sizeof(union header))
- {
- last_error = NOT_A_MOD;
- return unload_song(s);
- }
- /* fast recognize other formats */
- if (header.other.sig == MK_SIG('M','M','D','0')
- || header.other.sig == MK_SIG('S','M','O','D')
- || header.other.sig == MK_SIG('F','C','1','4'))
- {
- last_error = UNSUPPORTED;
- return unload_song(s);
- }
- if (header.nt.sig == MK_SIG('M','.','K','.'))
- {
- sn = NEW_SAMPLES;
- s->info = extract_song_info(clear, &header.nt.song_info);
- }
- else
- {
- sn = OLD_SAMPLES;
- s->info = extract_song_info(clear, &header.st.song_info);
- Seek(f, sizeof(struct old_st_header), OFFSET_BEGINNING);
- }
- if (!s->info)
- return unload_song(s);
-
- s->title = cstring(clear, header.nt.song_name, SONG_NAME);
- if (!s->title)
- return unload_song(s);
-
- /* so that we don't allocate memory for dummy samples */
- dummy.length = 0;
- for (i = 0; i < NUMBER_SAMPLES; i++)
- s->samples[i] = &dummy;
- for (i = 0; i < sn; i++)
- {
- s->samples[i+1] = extract_sample_info(clear,
- &header.nt.sample_info[i]);
- if (!s->samples[i+1])
- return unload_song(s);
- }
- sample_array = s->samples;
- max_sn = read_blocks(clear, f, s->info->total, s->info->physical);
- if (!max_sn)
- return unload_song(s);
- if (!allocate_samples(clear, s, max_sn))
- return unload_song(s);
- for (i = 0; i < max_sn; i++)
- if (!read_sample(clear, f, &header.nt.sample_info[i], s->samples[i+1]))
- return unload_song(s);
- return s;
- }
-
- int load_error(void)
- {
- return last_error;
- }
-
- struct song *unload_song(struct song *s)
- {
- if (s)
- CleanUp(s->clear);
- return NULL;
- }
-
- struct song *load_song(char *arg)
- {
- BPTR f;
- CLEAN closefile;
- struct song *s;
- static char message[200];
- closefile = AllocClean(NIL);
- if (!arg)
- return NULL;
- /* check the precise length !!! */
- sprintf(message, "Loading %s...", arg);
- temporary_title(message);
- ToClean0L(closefile, restore_title);
- f = Open(arg, MODE_OLDFILE);
- if (f)
- {
- ToCleanL(closefile, Close, f);
- s = read_song(f);
- }
- else
- s = NULL;
- CleanUp(closefile);
- return s;
- }
-