xmpnx/libxmp/src/loaders/flt_load.c

492 lines
12 KiB
C

/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "loader.h"
#include "mod.h"
#include "../period.h"
static int flt_test(HIO_HANDLE *, char *, const int);
static int flt_load(struct module_data *, HIO_HANDLE *, const int);
const struct format_loader libxmp_loader_flt = {
"Startrekker",
flt_test,
flt_load
};
static int flt_test(HIO_HANDLE * f, char *t, const int start)
{
char buf[4];
hio_seek(f, start + 1080, SEEK_SET);
if (hio_read(buf, 1, 4, f) < 4)
return -1;
/* Also RASP? */
if (memcmp(buf, "FLT", 3) && memcmp(buf, "EXO", 3))
return -1;
if (buf[3] != '4' && buf[3] != '8' && buf[3] != 'M')
return -1;
hio_seek(f, start + 0, SEEK_SET);
libxmp_read_title(f, t, 20);
return 0;
}
/* Waveforms from the Startrekker 1.2 AM synth replayer code */
static const int8 am_waveform[3][32] = {
{ 0, 25, 49, 71, 90, 106, 117, 125, /* Sine */
127, 125, 117, 106, 90, 71, 49, 25,
0, -25, -49, -71, -90, -106, -117, -125,
-127, -125, -117, -106, -90, -71, -49, -25
},
{ -128, -120, -112, -104, -96, -88, -80, -72, /* Ramp */
-64, -56, -48, -40, -32, -24, -16, -8,
0, 8, 16, 24, 32, 40, 48, 56,
64, 72, 80, 88, 96, 104, 112, 120
},
{ -128, -128, -128, -128, -128, -128, -128, -128, /* Square */
-128, -128, -128, -128, -128, -128, -128, -128,
127, 127, 127, 127, 127, 127, 127, 127,
127, 127, 127, 127, 127, 127, 127, 127
}
};
struct am_instrument {
int16 l0; /* start amplitude */
int16 a1l; /* attack level */
int16 a1s; /* attack speed */
int16 a2l; /* secondary attack level */
int16 a2s; /* secondary attack speed */
int16 sl; /* sustain level */
int16 ds; /* decay speed */
int16 st; /* sustain time */
int16 rs; /* release speed */
int16 wf; /* waveform */
int16 p_fall; /* ? */
int16 v_amp; /* vibrato amplitude */
int16 v_spd; /* vibrato speed */
int16 fq; /* base frequency */
};
static int is_am_instrument(HIO_HANDLE *nt, int i)
{
char buf[2];
int16 wf;
hio_seek(nt, 144 + i * 120, SEEK_SET);
hio_read(buf, 1, 2, nt);
if (memcmp(buf, "AM", 2))
return 0;
hio_seek(nt, 24, SEEK_CUR);
wf = hio_read16b(nt);
if (hio_error(nt) || wf < 0 || wf > 3)
return 0;
return 1;
}
static int read_am_instrument(struct module_data *m, HIO_HANDLE *nt, int i)
{
struct xmp_module *mod = &m->mod;
struct xmp_instrument *xxi = &mod->xxi[i];
struct xmp_sample *xxs = &mod->xxs[i];
struct xmp_envelope *vol_env = &xxi->aei;
struct xmp_envelope *freq_env = &xxi->fei;
struct am_instrument am;
char *wave;
int a, b;
int8 am_noise[1024];
hio_seek(nt, 144 + i * 120 + 2 + 4, SEEK_SET);
am.l0 = hio_read16b(nt);
am.a1l = hio_read16b(nt);
am.a1s = hio_read16b(nt);
am.a2l = hio_read16b(nt);
am.a2s = hio_read16b(nt);
am.sl = hio_read16b(nt);
am.ds = hio_read16b(nt);
am.st = hio_read16b(nt);
hio_read16b(nt);
am.rs = hio_read16b(nt);
am.wf = hio_read16b(nt);
am.p_fall = -(int16) hio_read16b(nt);
am.v_amp = hio_read16b(nt);
am.v_spd = hio_read16b(nt);
am.fq = hio_read16b(nt);
if (hio_error(nt)) {
return -1;
}
#if 0
printf
("L0=%d A1L=%d A1S=%d A2L=%d A2S=%d SL=%d DS=%d ST=%d RS=%d WF=%d\n",
am.l0, am.a1l, am.a1s, am.a2l, am.a2s, am.sl, am.ds, am.st, am.rs,
am.wf);
#endif
if (am.wf < 3) {
xxs->len = 32;
xxs->lps = 0;
xxs->lpe = 32;
wave = (char *)&am_waveform[am.wf][0];
} else {
int j;
xxs->len = 1024;
xxs->lps = 0;
xxs->lpe = 1024;
for (j = 0; j < 1024; j++)
am_noise[j] = rand() % 256;
wave = (char *)&am_noise[0];
}
xxs->flg = XMP_SAMPLE_LOOP;
xxi->sub[0].vol = 0x40; /* prelude.mod has 0 in instrument */
xxi->nsm = 1;
xxi->sub[0].xpo = -12 * am.fq;
xxi->sub[0].vwf = 0;
xxi->sub[0].vde = am.v_amp << 2;
xxi->sub[0].vra = am.v_spd;
/*
* AM synth envelope parameters based on the Startrekker 1.2 docs
*
* L0 Start amplitude for the envelope
* A1L Attack level
* A1S The speed that the amplitude changes to the attack level, $1
* is slow and $40 is fast.
* A2L Secondary attack level, for those who likes envelopes...
* A2S Secondary attack speed.
* DS The speed that the amplitude decays down to the:
* SL Sustain level. There is remains for the time set by the
* ST Sustain time.
* RS Release speed. The speed that the amplitude falls from ST to 0.
*/
if (am.a1s == 0)
am.a1s = 1;
if (am.a2s == 0)
am.a2s = 1;
if (am.ds == 0)
am.ds = 1;
if (am.rs == 0)
am.rs = 1;
vol_env->npt = 6;
vol_env->flg = XMP_ENVELOPE_ON;
vol_env->data[0] = 0;
vol_env->data[1] = am.l0 / 4;
/*
* Startrekker increments/decrements the envelope by the stage speed
* until it reaches the next stage level.
*
* ^
* |
* 100 +.........o
* | /:
* A2L +.......o : x = 256 * (A2L - A1L) / (256 - A1L)
* | /: :
* | / : :
* A1L +....o..:.:
* | : : :
* | :x : :
* +----+--+-+----->
* | |
* |256/|
* A2S
*/
if (am.a1l > am.l0) {
a = am.a1l - am.l0;
b = 256 - am.l0;
} else {
a = am.l0 - am.a1l;
b = am.l0;
}
if (b == 0)
b = 1;
vol_env->data[2] = vol_env->data[0] + (256 * a) / (am.a1s * b);
vol_env->data[3] = am.a1l / 4;
if (am.a2l > am.a1l) {
a = am.a2l - am.a1l;
b = 256 - am.a1l;
} else {
a = am.a1l - am.a2l;
b = am.a1l;
}
if (b == 0)
b = 1;
vol_env->data[4] = vol_env->data[2] + (256 * a) / (am.a2s * b);
vol_env->data[5] = am.a2l / 4;
if (am.sl > am.a2l) {
a = am.sl - am.a2l;
b = 256 - am.a2l;
} else {
a = am.a2l - am.sl;
b = am.a2l;
}
if (b == 0)
b = 1;
vol_env->data[6] = vol_env->data[4] + (256 * a) / (am.ds * b);
vol_env->data[7] = am.sl / 4;
vol_env->data[8] = vol_env->data[6] + am.st;
vol_env->data[9] = am.sl / 4;
vol_env->data[10] = vol_env->data[8] + (256 / am.rs);
vol_env->data[11] = 0;
/*
* Implement P.FALL using pitch envelope
*/
if (am.p_fall) {
freq_env->npt = 2;
freq_env->flg = XMP_ENVELOPE_ON;
freq_env->data[0] = 0;
freq_env->data[1] = 0;
freq_env->data[2] = 1024 / abs(am.p_fall);
freq_env->data[3] = 10 * (am.p_fall < 0 ? -256 : 256);
}
if (libxmp_load_sample(m, NULL, SAMPLE_FLAG_NOLOAD, xxs, wave))
return -1;
return 0;
}
static int flt_load(struct module_data *m, HIO_HANDLE * f, const int start)
{
struct xmp_module *mod = &m->mod;
int i, j;
struct xmp_event *event;
struct mod_header mh;
uint8 mod_event[4];
const char *tracker;
char filename[1024];
char buf[16];
HIO_HANDLE *nt;
int am_synth;
LOAD_INIT();
/* See if we have the synth parameters file */
am_synth = 0;
snprintf(filename, 1024, "%s%s.NT", m->dirname, m->basename);
if ((nt = hio_open(filename, "rb")) == NULL) {
snprintf(filename, 1024, "%s%s.nt", m->dirname, m->basename);
if ((nt = hio_open(filename, "rb")) == NULL) {
snprintf(filename, 1024, "%s%s.AS", m->dirname,
m->basename);
if ((nt = hio_open(filename, "rb")) == NULL) {
snprintf(filename, 1024, "%s%s.as", m->dirname,
m->basename);
nt = hio_open(filename, "rb");
}
}
}
tracker = "Startrekker";
if (nt) {
if (hio_read(buf, 1, 16, nt) != 16) {
goto err;
}
if (memcmp(buf, "ST1.2 ModuleINFO", 16) == 0) {
am_synth = 1;
tracker = "Startrekker 1.2";
} else if (memcmp(buf, "ST1.3 ModuleINFO", 16) == 0) {
am_synth = 1;
tracker = "Startrekker 1.3";
} else if (memcmp(buf, "AudioSculpture10", 16) == 0) {
am_synth = 1;
tracker = "AudioSculpture 1.0";
}
}
hio_read(mh.name, 20, 1, f);
for (i = 0; i < 31; i++) {
hio_read(mh.ins[i].name, 22, 1, f);
mh.ins[i].size = hio_read16b(f);
mh.ins[i].finetune = hio_read8(f);
mh.ins[i].volume = hio_read8(f);
mh.ins[i].loop_start = hio_read16b(f);
mh.ins[i].loop_size = hio_read16b(f);
}
mh.len = hio_read8(f);
mh.restart = hio_read8(f);
hio_read(mh.order, 128, 1, f);
hio_read(mh.magic, 4, 1, f);
if (mh.magic[3] == '4') {
mod->chn = 4;
} else {
mod->chn = 8;
}
mod->ins = 31;
mod->smp = mod->ins;
mod->len = mh.len;
mod->rst = mh.restart;
memcpy(mod->xxo, mh.order, 128);
for (i = 0; i < 128; i++) {
if (mod->chn > 4)
mod->xxo[i] >>= 1;
if (mod->xxo[i] > mod->pat)
mod->pat = mod->xxo[i];
}
mod->pat++;
mod->trk = mod->chn * mod->pat;
strncpy(mod->name, (char *)mh.name, 20);
libxmp_set_type(m, "%s %4.4s", tracker, mh.magic);
MODULE_INFO();
if (libxmp_init_instrument(m) < 0)
goto err;
for (i = 0; i < mod->ins; i++) {
struct xmp_instrument *xxi = &mod->xxi[i];
struct xmp_sample *xxs = &mod->xxs[i];
struct xmp_subinstrument *sub;
if (libxmp_alloc_subinstrument(mod, i, 1) < 0)
goto err;
sub = &xxi->sub[0];
xxs->len = 2 * mh.ins[i].size;
xxs->lps = 2 * mh.ins[i].loop_start;
xxs->lpe = xxs->lps + 2 * mh.ins[i].loop_size;
xxs->flg = mh.ins[i].loop_size > 1 ? XMP_SAMPLE_LOOP : 0;
sub->fin = (int8) (mh.ins[i].finetune << 4);
sub->vol = mh.ins[i].volume;
sub->pan = 0x80;
sub->sid = i;
xxi->rls = 0xfff;
if (xxs->len > 0)
xxi->nsm = 1;
libxmp_instrument_name(mod, i, mh.ins[i].name, 22);
}
if (libxmp_init_pattern(mod) < 0)
goto err;
/* Load and convert patterns */
D_(D_INFO "Stored patterns: %d", mod->pat);
/* "The format you are looking for is FLT8, and the ONLY two
* differences are: It says FLT8 instead of FLT4 or M.K., AND, the
* patterns are PAIRED. I thought this was the easiest 8 track
* format possible, since it can be loaded in a normal 4 channel
* tracker if you should want to rip sounds or patterns. So, in a
* 8 track FLT8 module, patterns 00 and 01 is "really" pattern 00.
* Patterns 02 and 03 together is "really" pattern 01. Thats it.
* Oh well, I didnt have the time to implement all effect commands
* either, so some FLT8 modules would play back badly (I think
* especially the portamento command uses a different "scale" than
* the normal portamento command, that would be hard to patch).
*/
for (i = 0; i < mod->pat; i++) {
if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0)
goto err;
for (j = 0; j < (64 * 4); j++) {
event = &EVENT(i, j % 4, j / 4);
if (hio_read(mod_event, 1, 4, f) < 4) {
D_(D_CRIT "read error at pat %d", i);
goto err;
}
libxmp_decode_noisetracker_event(event, mod_event);
}
if (mod->chn > 4) {
for (j = 0; j < (64 * 4); j++) {
event = &EVENT(i, (j % 4) + 4, j / 4);
if (hio_read(mod_event, 1, 4, f) < 4) {
D_(D_CRIT "read error at pat %d", i);
goto err;
}
libxmp_decode_noisetracker_event(event, mod_event);
/* no macros */
if (event->fxt == 0x0e)
event->fxt = event->fxp = 0;
}
}
}
/* no such limit for synth instruments
* mod->flg |= XXM_FLG_MODRNG;
*/
/* Load samples */
D_(D_INFO "Stored samples: %d", mod->smp);
for (i = 0; i < mod->smp; i++) {
if (mod->xxs[i].len == 0) {
if (am_synth && is_am_instrument(nt, i)) {
if (read_am_instrument(m, nt, i) < 0) {
D_(D_CRIT "Missing nt file");
goto err;
}
}
continue;
}
if (libxmp_load_sample(m, f, SAMPLE_FLAG_FULLREP, &mod->xxs[i], NULL) <
0) {
goto err;
}
}
if (nt) {
hio_close(nt);
}
return 0;
err:
if (nt) {
hio_close(nt);
}
return -1;
}