home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Audio Version 4.94
/
audioversion4.94knowledgemediaresourcelibraryoctober1994.iso
/
amiga
/
utils
/
exp_iv
/
load.c
< prev
next >
Wrap
C/C++ Source or Header
|
1991-05-16
|
14KB
|
572 lines
/* 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;
}