1
1
Fork 0
mirror of https://github.com/QB64-Phoenix-Edition/QB64pe.git synced 2024-09-20 03:14:45 +00:00
QB64-PE/internal/c/parts/audio/extras/libxmp-lite/effects.c

1073 lines
26 KiB
C
Raw Normal View History

/* 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 "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;
}
goto fx_s3m_bpm;
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) {
SET(TEMPO_SLIDE);
if (LSN(fxp)) /* T0x - Tempo slide down by x */
xc->tempo.slide = -LSN(fxp);
/* T00 - Repeat previous slide */
} 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 = ROWDELAY_ON | ROWDELAY_FIRST_FRAME;
}
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 */
if (!f->loop_chn)
{
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 envelopes 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;
xc->retrig.type = MSN(xc->retrig.val);
}
if (note) {
xc->retrig.count = LSN(xc->retrig.val) + 1;
}
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;
}
}