492 lines
12 KiB
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;
|
|
}
|