mirror of
https://github.com/ZDoom/raze-gles.git
synced 2024-12-27 04:00:42 +00:00
d715918e45
git-svn-id: https://svn.eduke32.com/eduke32@6158 1a8010ca-5511-0410-912e-c29ae57300e0
1069 lines
26 KiB
C
1069 lines
26 KiB
C
/* Extended Module Player
|
||
* Copyright (C) 1996-2016 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 "common.h"
|
||
#include "player.h"
|
||
#include "effects.h"
|
||
#include "period.h"
|
||
#include "virtual.h"
|
||
#include "mixer.h"
|
||
#ifndef LIBXMP_CORE_PLAYER
|
||
#include "extras.h"
|
||
#endif
|
||
|
||
#define NOT_IMPLEMENTED
|
||
#define HAS_QUIRK(x) (m->quirk & (x))
|
||
|
||
#define SET_LFO_NOTZERO(lfo, depth, rate) do { \
|
||
if ((depth) != 0) libxmp_lfo_set_depth(lfo, depth); \
|
||
if ((rate) != 0) libxmp_lfo_set_rate(lfo, rate); \
|
||
} while (0)
|
||
|
||
#define EFFECT_MEMORY__(p, m) do { \
|
||
if ((p) == 0) { (p) = (m); } else { (m) = (p); } \
|
||
} while (0)
|
||
|
||
/* ST3 effect memory is not a bug, but it's a weird implementation and it's
|
||
* unlikely to be supported in anything other than ST3 (or OpenMPT).
|
||
*/
|
||
#define EFFECT_MEMORY(p, m) do { \
|
||
if (HAS_QUIRK(QUIRK_ST3BUGS)) { \
|
||
EFFECT_MEMORY__((p), xc->vol.memory); \
|
||
} else { \
|
||
EFFECT_MEMORY__((p), (m)); \
|
||
} \
|
||
} while (0)
|
||
|
||
#define EFFECT_MEMORY_SETONLY(p, m) do { \
|
||
EFFECT_MEMORY__((p), (m)); \
|
||
if (HAS_QUIRK(QUIRK_ST3BUGS)) { \
|
||
if ((p) != 0) { xc->vol.memory = (p); } \
|
||
} \
|
||
} while (0)
|
||
|
||
#define EFFECT_MEMORY_S3M(p) do { \
|
||
if (HAS_QUIRK(QUIRK_ST3BUGS)) { \
|
||
EFFECT_MEMORY__((p), xc->vol.memory); \
|
||
} \
|
||
} while (0)
|
||
|
||
|
||
static void do_toneporta(struct context_data *ctx,
|
||
struct channel_data *xc, int note)
|
||
{
|
||
struct module_data *m = &ctx->m;
|
||
struct xmp_instrument *instrument = &m->mod.xxi[xc->ins];
|
||
int mapped = instrument->map[xc->key].ins;
|
||
struct xmp_subinstrument *sub;
|
||
|
||
if (mapped >= instrument->nsm) {
|
||
mapped = 0;
|
||
}
|
||
|
||
sub = &instrument->sub[mapped];
|
||
|
||
if (note >= 1 && note <= 0x80 && (uint32)xc->ins < m->mod.ins) {
|
||
note--;
|
||
xc->porta.target = libxmp_note_to_period(ctx, note + sub->xpo +
|
||
instrument->map[xc->key_porta].xpo, xc->finetune,
|
||
xc->per_adj);
|
||
}
|
||
xc->porta.dir = xc->period < xc->porta.target ? 1 : -1;
|
||
}
|
||
|
||
void libxmp_process_fx(struct context_data *ctx, struct channel_data *xc, int chn,
|
||
struct xmp_event *e, int fnum)
|
||
{
|
||
struct player_data *p = &ctx->p;
|
||
struct module_data *m = &ctx->m;
|
||
struct xmp_module *mod = &m->mod;
|
||
struct flow_control *f = &p->flow;
|
||
uint8 note, fxp, fxt;
|
||
int h, l;
|
||
|
||
/* key_porta is IT only */
|
||
if (m->read_event_type != READ_EVENT_IT) {
|
||
xc->key_porta = xc->key;
|
||
}
|
||
|
||
note = e->note;
|
||
if (fnum == 0) {
|
||
fxt = e->fxt;
|
||
fxp = e->fxp;
|
||
} else {
|
||
fxt = e->f2t;
|
||
fxp = e->f2p;
|
||
}
|
||
|
||
switch (fxt) {
|
||
case FX_ARPEGGIO:
|
||
fx_arpeggio:
|
||
if (!HAS_QUIRK(QUIRK_ARPMEM) || fxp != 0) {
|
||
xc->arpeggio.val[0] = 0;
|
||
xc->arpeggio.val[1] = MSN(fxp);
|
||
xc->arpeggio.val[2] = LSN(fxp);
|
||
xc->arpeggio.size = 3;
|
||
}
|
||
break;
|
||
case FX_S3M_ARPEGGIO:
|
||
EFFECT_MEMORY(fxp, xc->arpeggio.memory);
|
||
goto fx_arpeggio;
|
||
|
||
#ifndef LIBXMP_CORE_PLAYER
|
||
case FX_OKT_ARP3:
|
||
if (fxp != 0) {
|
||
xc->arpeggio.val[0] = -MSN(fxp);
|
||
xc->arpeggio.val[1] = 0;
|
||
xc->arpeggio.val[2] = LSN(fxp);
|
||
xc->arpeggio.size = 3;
|
||
}
|
||
break;
|
||
case FX_OKT_ARP4:
|
||
if (fxp != 0) {
|
||
xc->arpeggio.val[0] = 0;
|
||
xc->arpeggio.val[1] = LSN(fxp);
|
||
xc->arpeggio.val[2] = 0;
|
||
xc->arpeggio.val[3] = -MSN(fxp);
|
||
xc->arpeggio.size = 4;
|
||
}
|
||
break;
|
||
case FX_OKT_ARP5:
|
||
if (fxp != 0) {
|
||
xc->arpeggio.val[0] = LSN(fxp);
|
||
xc->arpeggio.val[1] = LSN(fxp);
|
||
xc->arpeggio.val[2] = 0;
|
||
xc->arpeggio.size = 3;
|
||
}
|
||
break;
|
||
#endif
|
||
case FX_PORTA_UP: /* Portamento up */
|
||
EFFECT_MEMORY(fxp, xc->freq.memory);
|
||
|
||
if (HAS_QUIRK(QUIRK_FINEFX)
|
||
&& (fnum == 0 || !HAS_QUIRK(QUIRK_ITVPOR))) {
|
||
switch (MSN(fxp)) {
|
||
case 0xf:
|
||
fxp &= 0x0f;
|
||
goto fx_f_porta_up;
|
||
case 0xe:
|
||
fxp &= 0x0f;
|
||
fxp |= 0x10;
|
||
goto fx_xf_porta;
|
||
}
|
||
}
|
||
|
||
SET(PITCHBEND);
|
||
|
||
if (fxp != 0) {
|
||
xc->freq.slide = -fxp;
|
||
if (HAS_QUIRK(QUIRK_UNISLD))
|
||
xc->porta.memory = fxp;
|
||
} else if (xc->freq.slide > 0) {
|
||
xc->freq.slide *= -1;
|
||
}
|
||
break;
|
||
case FX_PORTA_DN: /* Portamento down */
|
||
EFFECT_MEMORY(fxp, xc->freq.memory);
|
||
|
||
if (HAS_QUIRK(QUIRK_FINEFX)
|
||
&& (fnum == 0 || !HAS_QUIRK(QUIRK_ITVPOR))) {
|
||
switch (MSN(fxp)) {
|
||
case 0xf:
|
||
fxp &= 0x0f;
|
||
goto fx_f_porta_dn;
|
||
case 0xe:
|
||
fxp &= 0x0f;
|
||
fxp |= 0x20;
|
||
goto fx_xf_porta;
|
||
}
|
||
}
|
||
|
||
SET(PITCHBEND);
|
||
|
||
if (fxp != 0) {
|
||
xc->freq.slide = fxp;
|
||
if (HAS_QUIRK(QUIRK_UNISLD))
|
||
xc->porta.memory = fxp;
|
||
} else if (xc->freq.slide < 0) {
|
||
xc->freq.slide *= -1;
|
||
}
|
||
break;
|
||
case FX_TONEPORTA: /* Tone portamento */
|
||
EFFECT_MEMORY_SETONLY(fxp, xc->porta.memory);
|
||
|
||
if (fxp != 0) {
|
||
if (HAS_QUIRK(QUIRK_UNISLD)) /* IT compatible Gxx off */
|
||
xc->freq.memory = fxp;
|
||
xc->porta.slide = fxp;
|
||
}
|
||
|
||
if (HAS_QUIRK(QUIRK_IGSTPOR)) {
|
||
if (note == 0 && xc->porta.dir == 0)
|
||
break;
|
||
}
|
||
|
||
if (!IS_VALID_INSTRUMENT(xc->ins))
|
||
break;
|
||
|
||
do_toneporta(ctx, xc, note);
|
||
|
||
SET(TONEPORTA);
|
||
break;
|
||
|
||
case FX_VIBRATO: /* Vibrato */
|
||
EFFECT_MEMORY_SETONLY(fxp, xc->vibrato.memory);
|
||
|
||
SET(VIBRATO);
|
||
SET_LFO_NOTZERO(&xc->vibrato.lfo, LSN(fxp) << 2, MSN(fxp));
|
||
break;
|
||
case FX_FINE_VIBRATO: /* Fine vibrato */
|
||
EFFECT_MEMORY_SETONLY(fxp, xc->vibrato.memory);
|
||
|
||
SET(VIBRATO);
|
||
SET_LFO_NOTZERO(&xc->vibrato.lfo, LSN(fxp), MSN(fxp));
|
||
break;
|
||
|
||
case FX_TONE_VSLIDE: /* Toneporta + vol slide */
|
||
if (!IS_VALID_INSTRUMENT(xc->ins))
|
||
break;
|
||
do_toneporta(ctx, xc, note);
|
||
SET(TONEPORTA);
|
||
goto fx_volslide;
|
||
case FX_VIBRA_VSLIDE: /* Vibrato + vol slide */
|
||
SET(VIBRATO);
|
||
goto fx_volslide;
|
||
|
||
case FX_TREMOLO: /* Tremolo */
|
||
EFFECT_MEMORY(fxp, xc->tremolo.memory);
|
||
SET(TREMOLO);
|
||
SET_LFO_NOTZERO(&xc->tremolo.lfo, LSN(fxp), MSN(fxp));
|
||
break;
|
||
|
||
case FX_SETPAN: /* Set pan */
|
||
if (HAS_QUIRK(QUIRK_PROTRACK)) {
|
||
break;
|
||
}
|
||
fx_setpan:
|
||
/* From OpenMPT PanOff.xm:
|
||
* "Another chapter of weird FT2 bugs: Note-Off + Note Delay
|
||
* + Volume Column Panning = Panning effect is ignored."
|
||
*/
|
||
if (!HAS_QUIRK(QUIRK_FT2BUGS) /* If not FT2 */
|
||
|| fnum == 0 /* or not vol column */
|
||
|| e->note != XMP_KEY_OFF /* or not keyoff */
|
||
|| e->fxt != FX_EXTENDED /* or not delay */
|
||
|| MSN(e->fxp) != EX_DELAY) {
|
||
xc->pan.val = fxp;
|
||
xc->pan.surround = 0;
|
||
}
|
||
xc->rpv = 0; /* storlek_20: set pan overrides random pan */
|
||
xc->pan.surround = 0;
|
||
break;
|
||
case FX_OFFSET: /* Set sample offset */
|
||
EFFECT_MEMORY(fxp, xc->offset.memory);
|
||
SET(OFFSET);
|
||
if (note) {
|
||
xc->offset.val &= xc->offset.val & ~0xffff;
|
||
xc->offset.val |= fxp << 8;
|
||
xc->offset.val2 = fxp << 8;
|
||
}
|
||
if (e->ins) {
|
||
xc->offset.val2 = fxp << 8;
|
||
}
|
||
break;
|
||
case FX_VOLSLIDE: /* Volume slide */
|
||
fx_volslide:
|
||
/* S3M file volume slide note:
|
||
* DFy Fine volume down by y (...) If y is 0, the command will
|
||
* be treated as a volume slide up with a value of f (15).
|
||
* If a DFF command is specified, the volume will be slid
|
||
* up.
|
||
*/
|
||
if (HAS_QUIRK(QUIRK_FINEFX)) {
|
||
h = MSN(fxp);
|
||
l = LSN(fxp);
|
||
if (l == 0xf && h != 0) {
|
||
xc->vol.memory = fxp;
|
||
fxp >>= 4;
|
||
goto fx_f_vslide_up;
|
||
} else if (h == 0xf && l != 0) {
|
||
xc->vol.memory = fxp;
|
||
fxp &= 0x0f;
|
||
goto fx_f_vslide_dn;
|
||
}
|
||
}
|
||
|
||
/* recover memory */
|
||
if (fxp == 0x00) {
|
||
if ((fxp = xc->vol.memory) != 0)
|
||
goto fx_volslide;
|
||
}
|
||
|
||
SET(VOL_SLIDE);
|
||
|
||
/* Skaven's 2nd reality (S3M) has volslide parameter D7 => pri
|
||
* down. Other trackers only compute volumes if the other
|
||
* parameter is 0, Fall from sky.xm has 2C => do nothing.
|
||
* Also don't assign xc->vol.memory if fxp is 0, see Guild
|
||
* of Sounds.xm
|
||
*/
|
||
if (fxp) {
|
||
xc->vol.memory = fxp;
|
||
h = MSN(fxp);
|
||
l = LSN(fxp);
|
||
if (fxp) {
|
||
if (HAS_QUIRK(QUIRK_VOLPDN)) {
|
||
xc->vol.slide = l ? -l : h;
|
||
} else {
|
||
xc->vol.slide = h ? h : -l;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Mirko reports that a S3M with D0F effects created with ST321
|
||
* should process volume slides in all frames like ST300. I
|
||
* suspect ST3/IT could be handling D0F effects like this.
|
||
*/
|
||
if (HAS_QUIRK(QUIRK_FINEFX)) {
|
||
if (MSN(xc->vol.memory) == 0xf
|
||
|| LSN(xc->vol.memory) == 0xf) {
|
||
SET(FINE_VOLS);
|
||
xc->vol.fslide = xc->vol.slide;
|
||
}
|
||
}
|
||
break;
|
||
case FX_VOLSLIDE_2: /* Secondary volume slide */
|
||
SET(VOL_SLIDE_2);
|
||
if (fxp) {
|
||
h = MSN(fxp);
|
||
l = LSN(fxp);
|
||
xc->vol.slide2 = h ? h : -l;
|
||
}
|
||
break;
|
||
case FX_JUMP: /* Order jump */
|
||
p->flow.pbreak = 1;
|
||
p->flow.jump = fxp;
|
||
/* effect B resets effect D in lower channels */
|
||
p->flow.jumpline = 0;
|
||
break;
|
||
case FX_VOLSET: /* Volume set */
|
||
SET(NEW_VOL);
|
||
xc->volume = fxp;
|
||
if (xc->split) {
|
||
p->xc_data[xc->pair].volume = xc->volume;
|
||
}
|
||
break;
|
||
case FX_BREAK: /* Pattern break */
|
||
p->flow.pbreak = 1;
|
||
p->flow.jumpline = 10 * MSN(fxp) + LSN(fxp);
|
||
break;
|
||
case FX_EXTENDED: /* Extended effect */
|
||
EFFECT_MEMORY_S3M(fxp);
|
||
fxt = fxp >> 4;
|
||
fxp &= 0x0f;
|
||
switch (fxt) {
|
||
case EX_FILTER: /* Amiga led filter */
|
||
if (IS_AMIGA_MOD()) {
|
||
p->filter = !(fxp & 1);
|
||
}
|
||
break;
|
||
case EX_F_PORTA_UP: /* Fine portamento up */
|
||
EFFECT_MEMORY(fxp, xc->fine_porta.up_memory);
|
||
goto fx_f_porta_up;
|
||
case EX_F_PORTA_DN: /* Fine portamento down */
|
||
EFFECT_MEMORY(fxp, xc->fine_porta.down_memory);
|
||
goto fx_f_porta_dn;
|
||
case EX_GLISS: /* Glissando toggle */
|
||
if (fxp) {
|
||
SET_NOTE(NOTE_GLISSANDO);
|
||
} else {
|
||
RESET_NOTE(NOTE_GLISSANDO);
|
||
}
|
||
break;
|
||
case EX_VIBRATO_WF: /* Set vibrato waveform */
|
||
fxp &= 3;
|
||
libxmp_lfo_set_waveform(&xc->vibrato.lfo, fxp);
|
||
break;
|
||
case EX_FINETUNE: /* Set finetune */
|
||
if (!HAS_QUIRK(QUIRK_FT2BUGS) || note > 0) {
|
||
xc->finetune = (int8)(fxp << 4);
|
||
}
|
||
break;
|
||
case EX_PATTERN_LOOP: /* Loop pattern */
|
||
if (fxp == 0) {
|
||
/* mark start of loop */
|
||
f->loop[chn].start = p->row;
|
||
if (HAS_QUIRK(QUIRK_FT2BUGS))
|
||
p->flow.jumpline = p->row;
|
||
} else {
|
||
/* end of loop */
|
||
if (f->loop[chn].count) {
|
||
if (--f->loop[chn].count) {
|
||
/* **** H:FIXME **** */
|
||
f->loop_chn = ++chn;
|
||
} else {
|
||
if (HAS_QUIRK(QUIRK_S3MLOOP))
|
||
f->loop[chn].start =
|
||
p->row + 1;
|
||
}
|
||
} else {
|
||
f->loop[chn].count = fxp;
|
||
f->loop_chn = ++chn;
|
||
}
|
||
}
|
||
break;
|
||
case EX_TREMOLO_WF: /* Set tremolo waveform */
|
||
libxmp_lfo_set_waveform(&xc->tremolo.lfo, fxp & 3);
|
||
break;
|
||
case EX_SETPAN:
|
||
fxp <<= 4;
|
||
goto fx_setpan;
|
||
case EX_RETRIG: /* Retrig note */
|
||
SET(RETRIG);
|
||
xc->retrig.val = fxp;
|
||
xc->retrig.count = LSN(xc->retrig.val) + 1;
|
||
xc->retrig.type = 0;
|
||
break;
|
||
case EX_F_VSLIDE_UP: /* Fine volume slide up */
|
||
EFFECT_MEMORY(fxp, xc->fine_vol.up_memory);
|
||
goto fx_f_vslide_up;
|
||
case EX_F_VSLIDE_DN: /* Fine volume slide down */
|
||
EFFECT_MEMORY(fxp, xc->fine_vol.down_memory);
|
||
goto fx_f_vslide_dn;
|
||
case EX_CUT: /* Cut note */
|
||
SET(RETRIG);
|
||
SET_NOTE(NOTE_CUT); /* for IT cut-carry */
|
||
xc->retrig.val = fxp + 1;
|
||
xc->retrig.count = xc->retrig.val;
|
||
xc->retrig.type = 0x10;
|
||
break;
|
||
case EX_DELAY: /* Note delay */
|
||
/* computed at frame loop */
|
||
break;
|
||
case EX_PATT_DELAY: /* Pattern delay */
|
||
goto fx_patt_delay;
|
||
case EX_INVLOOP: /* Invert loop / funk repeat */
|
||
xc->invloop.speed = fxp;
|
||
break;
|
||
}
|
||
break;
|
||
case FX_SPEED: /* Set speed */
|
||
if (HAS_QUIRK(QUIRK_NOBPM) || p->flags & XMP_FLAGS_VBLANK) {
|
||
goto fx_s3m_speed;
|
||
}
|
||
|
||
/* speedup.xm needs BPM = 20 */
|
||
if (fxp < 0x20) {
|
||
goto fx_s3m_speed;
|
||
} else {
|
||
goto fx_s3m_bpm;
|
||
}
|
||
break;
|
||
|
||
case FX_FINETUNE:
|
||
xc->finetune = (int16) (fxp - 0x80);
|
||
break;
|
||
|
||
case FX_F_VSLIDE_UP: /* Fine volume slide up */
|
||
EFFECT_MEMORY(fxp, xc->fine_vol.up_memory);
|
||
fx_f_vslide_up:
|
||
SET(FINE_VOLS);
|
||
xc->vol.fslide = fxp;
|
||
break;
|
||
case FX_F_VSLIDE_DN: /* Fine volume slide down */
|
||
EFFECT_MEMORY(fxp, xc->fine_vol.up_memory);
|
||
fx_f_vslide_dn:
|
||
SET(FINE_VOLS);
|
||
xc->vol.fslide = -fxp;
|
||
break;
|
||
|
||
case FX_F_PORTA_UP: /* Fine portamento up */
|
||
fx_f_porta_up:
|
||
if (fxp) {
|
||
SET(FINE_BEND);
|
||
xc->freq.fslide = -fxp;
|
||
}
|
||
break;
|
||
case FX_F_PORTA_DN: /* Fine portamento down */
|
||
fx_f_porta_dn:
|
||
if (fxp) {
|
||
SET(FINE_BEND);
|
||
xc->freq.fslide = fxp;
|
||
}
|
||
break;
|
||
case FX_PATT_DELAY:
|
||
fx_patt_delay:
|
||
if (m->read_event_type != READ_EVENT_ST3 || !p->flow.delay) {
|
||
p->flow.delay = fxp;
|
||
}
|
||
break;
|
||
|
||
case FX_S3M_SPEED: /* Set S3M speed */
|
||
EFFECT_MEMORY_S3M(fxp);
|
||
fx_s3m_speed:
|
||
if (fxp) {
|
||
p->speed = fxp;
|
||
#ifndef LIBXMP_CORE_PLAYER
|
||
p->st26_speed = 0;
|
||
#endif
|
||
}
|
||
break;
|
||
case FX_S3M_BPM: /* Set S3M BPM */
|
||
fx_s3m_bpm: {
|
||
/* Lower time factor in MED allows lower BPM values */
|
||
int min_bpm = (int)(0.5 + m->time_factor * XMP_MIN_BPM / 10);
|
||
if (fxp < min_bpm)
|
||
fxp = min_bpm;
|
||
p->bpm = fxp;
|
||
p->frame_time = m->time_factor * m->rrate / p->bpm;
|
||
break;
|
||
}
|
||
|
||
#ifndef LIBXMP_CORE_DISABLE_IT
|
||
case FX_IT_BPM: /* Set IT BPM */
|
||
if (MSN(fxp) == 0) { /* T0x - Tempo slide down by x */
|
||
SET(TEMPO_SLIDE);
|
||
xc->tempo.slide = -LSN(fxp);
|
||
} else if (MSN(fxp) == 1) { /* T1x - Tempo slide up by x */
|
||
SET(TEMPO_SLIDE);
|
||
xc->tempo.slide = LSN(fxp);
|
||
} else {
|
||
if (fxp < XMP_MIN_BPM)
|
||
fxp = XMP_MIN_BPM;
|
||
p->bpm = fxp;
|
||
}
|
||
p->frame_time = m->time_factor * m->rrate / p->bpm;
|
||
break;
|
||
case FX_IT_ROWDELAY:
|
||
if (!f->rowdelay_set) {
|
||
f->rowdelay = fxp;
|
||
f->rowdelay_set = 3;
|
||
}
|
||
break;
|
||
|
||
/* From the OpenMPT VolColMemory.it test case:
|
||
* "Volume column commands a, b, c and d (volume slide) share one
|
||
* effect memory, but it should not be shared with Dxy in the effect
|
||
* column.
|
||
*/
|
||
case FX_VSLIDE_UP_2: /* Fine volume slide up */
|
||
EFFECT_MEMORY(fxp, xc->vol.memory2);
|
||
SET(VOL_SLIDE_2);
|
||
xc->vol.slide2 = fxp;
|
||
break;
|
||
case FX_VSLIDE_DN_2: /* Fine volume slide down */
|
||
EFFECT_MEMORY(fxp, xc->vol.memory2);
|
||
SET(VOL_SLIDE_2);
|
||
xc->vol.slide2 = -fxp;
|
||
break;
|
||
case FX_F_VSLIDE_UP_2: /* Fine volume slide up */
|
||
EFFECT_MEMORY(fxp, xc->vol.memory2);
|
||
SET(FINE_VOLS_2);
|
||
xc->vol.fslide2 = fxp;
|
||
break;
|
||
case FX_F_VSLIDE_DN_2: /* Fine volume slide down */
|
||
EFFECT_MEMORY(fxp, xc->vol.memory2);
|
||
SET(FINE_VOLS_2);
|
||
xc->vol.fslide2 = -fxp;
|
||
break;
|
||
case FX_IT_BREAK: /* Pattern break with hex parameter */
|
||
p->flow.pbreak = 1;
|
||
p->flow.jumpline = fxp;
|
||
break;
|
||
|
||
#endif
|
||
|
||
case FX_GLOBALVOL: /* Set global volume */
|
||
if (fxp > m->gvolbase) {
|
||
p->gvol = m->gvolbase;
|
||
} else {
|
||
p->gvol = fxp;
|
||
}
|
||
break;
|
||
case FX_GVOL_SLIDE: /* Global volume slide */
|
||
fx_gvolslide:
|
||
if (fxp) {
|
||
SET(GVOL_SLIDE);
|
||
xc->gvol.memory = fxp;
|
||
h = MSN(fxp);
|
||
l = LSN(fxp);
|
||
|
||
if (HAS_QUIRK(QUIRK_FINEFX)) {
|
||
if (l == 0xf && h != 0) {
|
||
xc->gvol.slide = 0;
|
||
xc->gvol.fslide = h;
|
||
} else if (h == 0xf && l != 0) {
|
||
xc->gvol.slide = 0;
|
||
xc->gvol.fslide = -l;
|
||
} else {
|
||
xc->gvol.slide = h ? h : -l;
|
||
xc->gvol.fslide = 0;
|
||
}
|
||
} else {
|
||
xc->gvol.slide = h ? h : -l;
|
||
xc->gvol.fslide = 0;
|
||
}
|
||
} else {
|
||
if ((fxp = xc->gvol.memory) != 0) {
|
||
goto fx_gvolslide;
|
||
}
|
||
}
|
||
break;
|
||
case FX_KEYOFF: /* Key off */
|
||
xc->keyoff = fxp + 1;
|
||
break;
|
||
case FX_ENVPOS: /* Set envelope position */
|
||
/* From OpenMPT SetEnvPos.xm:
|
||
* "When using the Lxx effect, Fasttracker 2 only sets the
|
||
* panning envelope position if the volume envelope’s sustain
|
||
* flag is set.
|
||
*/
|
||
if (HAS_QUIRK(QUIRK_FT2BUGS)) {
|
||
struct xmp_instrument *instrument;
|
||
instrument = libxmp_get_instrument(ctx, xc->ins);
|
||
if (instrument != NULL) {
|
||
if (instrument->aei.flg & XMP_ENVELOPE_SUS) {
|
||
xc->p_idx = fxp;
|
||
}
|
||
}
|
||
} else {
|
||
xc->p_idx = fxp;
|
||
}
|
||
xc->v_idx = fxp;
|
||
xc->f_idx = fxp;
|
||
break;
|
||
case FX_PANSLIDE: /* Pan slide (XM) */
|
||
EFFECT_MEMORY(fxp, xc->pan.memory);
|
||
SET(PAN_SLIDE);
|
||
xc->pan.slide = LSN(fxp) - MSN(fxp);
|
||
break;
|
||
case FX_PANSL_NOMEM: /* Pan slide (XM volume column) */
|
||
SET(PAN_SLIDE);
|
||
xc->pan.slide = LSN(fxp) - MSN(fxp);
|
||
break;
|
||
|
||
#ifndef LIBXMP_CORE_DISABLE_IT
|
||
case FX_IT_PANSLIDE: /* Pan slide w/ fine pan (IT) */
|
||
SET(PAN_SLIDE);
|
||
if (fxp) {
|
||
if (MSN(fxp) == 0xf) {
|
||
xc->pan.slide = 0;
|
||
xc->pan.fslide = LSN(fxp);
|
||
} else if (LSN(fxp) == 0xf) {
|
||
xc->pan.slide = 0;
|
||
xc->pan.fslide = -MSN(fxp);
|
||
} else {
|
||
SET(PAN_SLIDE);
|
||
xc->pan.slide = LSN(fxp) - MSN(fxp);
|
||
xc->pan.fslide = 0;
|
||
}
|
||
}
|
||
break;
|
||
#endif
|
||
|
||
case FX_MULTI_RETRIG: /* Multi retrig */
|
||
EFFECT_MEMORY_S3M(fxp);
|
||
if (fxp) {
|
||
xc->retrig.val = fxp;
|
||
}
|
||
if (note) {
|
||
xc->retrig.count = LSN(xc->retrig.val) + 1;
|
||
xc->retrig.type = MSN(xc->retrig.val);
|
||
}
|
||
SET(RETRIG);
|
||
break;
|
||
case FX_TREMOR: /* Tremor */
|
||
EFFECT_MEMORY(fxp, xc->tremor.memory);
|
||
xc->tremor.up = MSN(fxp);
|
||
xc->tremor.down = LSN(fxp);
|
||
if (IS_PLAYER_MODE_FT2()) {
|
||
xc->tremor.count |= 0x80;
|
||
} else {
|
||
if (xc->tremor.up == 0) {
|
||
xc->tremor.up++;
|
||
}
|
||
if (xc->tremor.down == 0) {
|
||
xc->tremor.down++;
|
||
}
|
||
}
|
||
SET(TREMOR);
|
||
break;
|
||
case FX_XF_PORTA: /* Extra fine portamento */
|
||
fx_xf_porta:
|
||
SET(FINE_BEND);
|
||
switch (MSN(fxp)) {
|
||
case 1:
|
||
xc->freq.fslide = -0.25 * LSN(fxp);
|
||
break;
|
||
case 2:
|
||
xc->freq.fslide = 0.25 * LSN(fxp);
|
||
break;
|
||
}
|
||
break;
|
||
case FX_SURROUND:
|
||
xc->pan.surround = fxp;
|
||
break;
|
||
|
||
#ifndef LIBXMP_CORE_DISABLE_IT
|
||
case FX_TRK_VOL: /* Track volume setting */
|
||
if (fxp <= m->volbase) {
|
||
xc->mastervol = fxp;
|
||
}
|
||
break;
|
||
case FX_TRK_VSLIDE: /* Track volume slide */
|
||
if (fxp == 0) {
|
||
if ((fxp = xc->trackvol.memory) == 0)
|
||
break;
|
||
}
|
||
|
||
if (HAS_QUIRK(QUIRK_FINEFX)) {
|
||
h = MSN(fxp);
|
||
l = LSN(fxp);
|
||
if (h == 0xf && l != 0) {
|
||
xc->trackvol.memory = fxp;
|
||
fxp &= 0x0f;
|
||
goto fx_trk_fvslide;
|
||
} else if (l == 0xf && h != 0) {
|
||
xc->trackvol.memory = fxp;
|
||
fxp &= 0xf0;
|
||
goto fx_trk_fvslide;
|
||
}
|
||
}
|
||
|
||
SET(TRK_VSLIDE);
|
||
if (fxp) {
|
||
h = MSN(fxp);
|
||
l = LSN(fxp);
|
||
|
||
xc->trackvol.memory = fxp;
|
||
if (HAS_QUIRK(QUIRK_VOLPDN)) {
|
||
xc->trackvol.slide = l ? -l : h;
|
||
} else {
|
||
xc->trackvol.slide = h ? h : -l;
|
||
}
|
||
}
|
||
|
||
break;
|
||
case FX_TRK_FVSLIDE: /* Track fine volume slide */
|
||
fx_trk_fvslide:
|
||
SET(TRK_FVSLIDE);
|
||
if (fxp) {
|
||
xc->trackvol.fslide = MSN(fxp) - LSN(fxp);
|
||
}
|
||
break;
|
||
|
||
case FX_IT_INSTFUNC:
|
||
switch (fxp) {
|
||
case 0: /* Past note cut */
|
||
libxmp_virt_pastnote(ctx, chn, VIRT_ACTION_CUT);
|
||
break;
|
||
case 1: /* Past note off */
|
||
libxmp_virt_pastnote(ctx, chn, VIRT_ACTION_OFF);
|
||
break;
|
||
case 2: /* Past note fade */
|
||
libxmp_virt_pastnote(ctx, chn, VIRT_ACTION_FADE);
|
||
break;
|
||
case 3: /* Set NNA to note cut */
|
||
libxmp_virt_setnna(ctx, chn, XMP_INST_NNA_CUT);
|
||
break;
|
||
case 4: /* Set NNA to continue */
|
||
libxmp_virt_setnna(ctx, chn, XMP_INST_NNA_CONT);
|
||
break;
|
||
case 5: /* Set NNA to note off */
|
||
libxmp_virt_setnna(ctx, chn, XMP_INST_NNA_OFF);
|
||
break;
|
||
case 6: /* Set NNA to note fade */
|
||
libxmp_virt_setnna(ctx, chn, XMP_INST_NNA_FADE);
|
||
break;
|
||
case 7: /* Turn off volume envelope */
|
||
SET_PER(VENV_PAUSE);
|
||
break;
|
||
case 8: /* Turn on volume envelope */
|
||
RESET_PER(VENV_PAUSE);
|
||
break;
|
||
case 9: /* Turn off pan envelope */
|
||
SET_PER(PENV_PAUSE);
|
||
break;
|
||
case 0xa: /* Turn on pan envelope */
|
||
RESET_PER(PENV_PAUSE);
|
||
break;
|
||
case 0xb: /* Turn off pitch envelope */
|
||
SET_PER(FENV_PAUSE);
|
||
break;
|
||
case 0xc: /* Turn on pitch envelope */
|
||
RESET_PER(FENV_PAUSE);
|
||
break;
|
||
}
|
||
break;
|
||
case FX_FLT_CUTOFF:
|
||
if (fxp < 0xfe || xc->filter.resonance > 0) {
|
||
xc->filter.cutoff = fxp;
|
||
}
|
||
break;
|
||
case FX_FLT_RESN:
|
||
xc->filter.resonance = fxp;
|
||
break;
|
||
case FX_PANBRELLO: /* Panbrello */
|
||
SET(PANBRELLO);
|
||
SET_LFO_NOTZERO(&xc->panbrello.lfo, LSN(fxp) << 4, MSN(fxp));
|
||
break;
|
||
case FX_PANBRELLO_WF: /* Panbrello waveform */
|
||
libxmp_lfo_set_waveform(&xc->panbrello.lfo, fxp & 3);
|
||
break;
|
||
case FX_HIOFFSET: /* High offset */
|
||
xc->offset.val &= 0xffff;
|
||
xc->offset.val |= fxp << 16;
|
||
break;
|
||
#endif
|
||
|
||
#ifndef LIBXMP_CORE_PLAYER
|
||
|
||
/* SFX effects */
|
||
case FX_VOL_ADD:
|
||
if (!IS_VALID_INSTRUMENT(xc->ins)) {
|
||
break;
|
||
}
|
||
SET(NEW_VOL);
|
||
xc->volume = m->mod.xxi[xc->ins].sub[0].vol + fxp;
|
||
if (xc->volume > m->volbase) {
|
||
xc->volume = m->volbase;
|
||
}
|
||
break;
|
||
case FX_VOL_SUB:
|
||
if (!IS_VALID_INSTRUMENT(xc->ins)) {
|
||
break;
|
||
}
|
||
SET(NEW_VOL);
|
||
xc->volume = m->mod.xxi[xc->ins].sub[0].vol - fxp;
|
||
if (xc->volume < 0) {
|
||
xc->volume =0;
|
||
}
|
||
break;
|
||
case FX_PITCH_ADD:
|
||
SET_PER(TONEPORTA);
|
||
xc->porta.target = libxmp_note_to_period(ctx, note - 1, xc->finetune, 0)
|
||
+ fxp;
|
||
xc->porta.slide = 2;
|
||
xc->porta.dir = 1;
|
||
break;
|
||
case FX_PITCH_SUB:
|
||
SET_PER(TONEPORTA);
|
||
xc->porta.target = libxmp_note_to_period(ctx, note - 1, xc->finetune, 0)
|
||
- fxp;
|
||
xc->porta.slide = 2;
|
||
xc->porta.dir = -1;
|
||
break;
|
||
|
||
/* Saga Musix says:
|
||
*
|
||
* "When both nibbles of an Fxx command are set, SoundTracker 2.6
|
||
* applies the both values alternatingly, first the high nibble,
|
||
* then the low nibble on the next row, then the high nibble again...
|
||
* If only the high nibble is set, it should act like if only the low
|
||
* nibble is set (i.e. F30 is the same as F03).
|
||
*/
|
||
case FX_ICE_SPEED:
|
||
if (fxp) {
|
||
if (LSN(fxp)) {
|
||
p->st26_speed = (MSN(fxp) << 8) | LSN(fxp);
|
||
} else {
|
||
p->st26_speed = MSN(fxp);
|
||
}
|
||
}
|
||
break;
|
||
|
||
case FX_VOLSLIDE_UP: /* Vol slide with uint8 arg */
|
||
if (HAS_QUIRK(QUIRK_FINEFX)) {
|
||
h = MSN(fxp);
|
||
l = LSN(fxp);
|
||
if (h == 0xf && l != 0) {
|
||
fxp &= 0x0f;
|
||
goto fx_f_vslide_up;
|
||
}
|
||
}
|
||
|
||
if (fxp)
|
||
xc->vol.slide = fxp;
|
||
SET(VOL_SLIDE);
|
||
break;
|
||
case FX_VOLSLIDE_DN: /* Vol slide with uint8 arg */
|
||
if (HAS_QUIRK(QUIRK_FINEFX)) {
|
||
h = MSN(fxp);
|
||
l = LSN(fxp);
|
||
if (h == 0xf && l != 0) {
|
||
fxp &= 0x0f;
|
||
goto fx_f_vslide_dn;
|
||
}
|
||
}
|
||
|
||
if (fxp)
|
||
xc->vol.slide = -fxp;
|
||
SET(VOL_SLIDE);
|
||
break;
|
||
case FX_F_VSLIDE: /* Fine volume slide */
|
||
SET(FINE_VOLS);
|
||
if (fxp) {
|
||
h = MSN(fxp);
|
||
l = LSN(fxp);
|
||
xc->vol.fslide = h ? h : -l;
|
||
}
|
||
break;
|
||
case FX_NSLIDE_DN:
|
||
case FX_NSLIDE_UP:
|
||
case FX_NSLIDE_R_DN:
|
||
case FX_NSLIDE_R_UP:
|
||
if (fxp != 0) {
|
||
if (fxt == FX_NSLIDE_R_DN || fxt == FX_NSLIDE_R_UP) {
|
||
xc->retrig.val = MSN(fxp);
|
||
xc->retrig.count = MSN(fxp) + 1;
|
||
xc->retrig.type = 0;
|
||
}
|
||
|
||
if (fxt == FX_NSLIDE_UP || fxt == FX_NSLIDE_R_UP)
|
||
xc->noteslide.slide = LSN(fxp);
|
||
else
|
||
xc->noteslide.slide = -LSN(fxp);
|
||
|
||
xc->noteslide.count = xc->noteslide.speed = MSN(fxp);
|
||
}
|
||
if (fxt == FX_NSLIDE_R_DN || fxt == FX_NSLIDE_R_UP)
|
||
SET(RETRIG);
|
||
SET(NOTE_SLIDE);
|
||
break;
|
||
case FX_NSLIDE2_DN:
|
||
SET(NOTE_SLIDE);
|
||
xc->noteslide.slide = -fxp;
|
||
xc->noteslide.count = xc->noteslide.speed = 1;
|
||
break;
|
||
case FX_NSLIDE2_UP:
|
||
SET(NOTE_SLIDE);
|
||
xc->noteslide.slide = fxp;
|
||
xc->noteslide.count = xc->noteslide.speed = 1;
|
||
break;
|
||
case FX_F_NSLIDE_DN:
|
||
SET(FINE_NSLIDE);
|
||
xc->noteslide.fslide = -fxp;
|
||
break;
|
||
case FX_F_NSLIDE_UP:
|
||
SET(FINE_NSLIDE);
|
||
xc->noteslide.fslide = fxp;
|
||
break;
|
||
|
||
case FX_PER_VIBRATO: /* Persistent vibrato */
|
||
if (LSN(fxp) != 0) {
|
||
SET_PER(VIBRATO);
|
||
} else {
|
||
RESET_PER(VIBRATO);
|
||
}
|
||
SET_LFO_NOTZERO(&xc->vibrato.lfo, LSN(fxp) << 2, MSN(fxp));
|
||
break;
|
||
case FX_PER_PORTA_UP: /* Persistent portamento up */
|
||
SET_PER(PITCHBEND);
|
||
xc->freq.slide = -fxp;
|
||
if ((xc->freq.memory = fxp) == 0)
|
||
RESET_PER(PITCHBEND);
|
||
break;
|
||
case FX_PER_PORTA_DN: /* Persistent portamento down */
|
||
SET_PER(PITCHBEND);
|
||
xc->freq.slide = fxp;
|
||
if ((xc->freq.memory = fxp) == 0)
|
||
RESET_PER(PITCHBEND);
|
||
break;
|
||
case FX_PER_TPORTA: /* Persistent tone portamento */
|
||
if (!IS_VALID_INSTRUMENT(xc->ins))
|
||
break;
|
||
SET_PER(TONEPORTA);
|
||
do_toneporta(ctx, xc, note);
|
||
xc->porta.slide = fxp;
|
||
if (fxp == 0)
|
||
RESET_PER(TONEPORTA);
|
||
break;
|
||
case FX_PER_VSLD_UP: /* Persistent volslide up */
|
||
SET_PER(VOL_SLIDE);
|
||
xc->vol.slide = fxp;
|
||
if (fxp == 0)
|
||
RESET_PER(VOL_SLIDE);
|
||
break;
|
||
case FX_PER_VSLD_DN: /* Persistent volslide down */
|
||
SET_PER(VOL_SLIDE);
|
||
xc->vol.slide = -fxp;
|
||
if (fxp == 0)
|
||
RESET_PER(VOL_SLIDE);
|
||
break;
|
||
case FX_VIBRATO2: /* Deep vibrato (2x) */
|
||
SET(VIBRATO);
|
||
SET_LFO_NOTZERO(&xc->vibrato.lfo, LSN(fxp) << 3, MSN(fxp));
|
||
break;
|
||
case FX_SPEED_CP: /* Set speed and ... */
|
||
if (fxp) {
|
||
p->speed = fxp;
|
||
p->st26_speed = 0;
|
||
}
|
||
/* fall through */
|
||
case FX_PER_CANCEL: /* Cancel persistent effects */
|
||
xc->per_flags = 0;
|
||
break;
|
||
|
||
/* 669 effects */
|
||
|
||
case FX_669_PORTA_UP: /* 669 portamento up */
|
||
SET_PER(PITCHBEND);
|
||
xc->freq.slide = 80 * fxp;
|
||
if ((xc->freq.memory = fxp) == 0)
|
||
RESET_PER(PITCHBEND);
|
||
break;
|
||
case FX_669_PORTA_DN: /* 669 portamento down */
|
||
SET_PER(PITCHBEND);
|
||
xc->freq.slide = -80 * fxp;
|
||
if ((xc->freq.memory = fxp) == 0)
|
||
RESET_PER(PITCHBEND);
|
||
break;
|
||
case FX_669_TPORTA: /* 669 tone portamento */
|
||
if (!IS_VALID_INSTRUMENT(xc->ins))
|
||
break;
|
||
SET_PER(TONEPORTA);
|
||
do_toneporta(ctx, xc, note);
|
||
xc->porta.slide = 40 * fxp;
|
||
if (fxp == 0)
|
||
RESET_PER(TONEPORTA);
|
||
break;
|
||
case FX_669_FINETUNE: /* 669 finetune */
|
||
xc->finetune = 80 * (int8)fxp;
|
||
break;
|
||
case FX_669_VIBRATO: /* 669 vibrato */
|
||
if (LSN(fxp) != 0) {
|
||
libxmp_lfo_set_waveform(&xc->vibrato.lfo, 669);
|
||
SET_PER(VIBRATO);
|
||
} else {
|
||
RESET_PER(VIBRATO);
|
||
}
|
||
SET_LFO_NOTZERO(&xc->vibrato.lfo, 669, 1);
|
||
break;
|
||
#endif
|
||
|
||
default:
|
||
#ifndef LIBXMP_CORE_PLAYER
|
||
libxmp_extras_process_fx(ctx, xc, chn, note, fxt, fxp, fnum);
|
||
#endif
|
||
break;
|
||
}
|
||
}
|