4064 lines
105 KiB
C
4064 lines
105 KiB
C
/*
|
|
** ST3PLAY v0.75a - 5th of October 2016 - http://16-bits.org
|
|
** =========================================================
|
|
** This is the foobar2000 version, with added code by kode54
|
|
**
|
|
** Changelog from v0.71:
|
|
** - Initial pans should be 128 since we simulate GUS mode.
|
|
** Pans are still changed if custom pans follow.
|
|
**
|
|
** Changelog from v0.70:
|
|
** - Any pan commands except Lxy/Yxy/XA4 should disable channel surround
|
|
**
|
|
** Changelog from v0.60:
|
|
** - Added S2x (non-ST3, set middle-C finetune)
|
|
** - Added S6x (non-ST3, tick delay)
|
|
** - Added S9E/S9F (non-ST3, play sample backwards/forwards)
|
|
** - Fixed a bug in setspd() in Amiga limit mode
|
|
** - Proper tracker handling for non-ST3 effects
|
|
** - Panbrello (Yxy) didn't set the panning at all
|
|
** - Decodes ADPCM samples at load time instead of play time
|
|
** - Mxx (set cannel volume) didn't work correctly
|
|
**
|
|
** C port of Scream Tracker 3's replayer, by 8bitbubsy (Olav Sørensen)
|
|
** using the original asm source codes by PSI (Sami Tammilehto) of Future Crew
|
|
**
|
|
** This is by no means a piece of beautiful code, nor is it meant to be...
|
|
** It's just an accurate Scream Tracker 3 replayer port for people to enjoy.
|
|
**
|
|
** Non-ST3 additions from other trackers (only handled for non ST3 S3Ms):
|
|
**
|
|
** - Mixing:
|
|
** * 16-bit sample support
|
|
** * Stereo sample support
|
|
** * 2^32 sample length support
|
|
** * Middle-C speeds beyond 65535
|
|
** * Process the last 16 channels as PCM (if not AdLib)
|
|
** * Process 8 octaves instead of 7
|
|
** * Compile-time optional volume ramping
|
|
** * The ability to play samples backwards
|
|
**
|
|
** - Effects:
|
|
** * Command S2x (set middle-C finetune)
|
|
** * Command S5x (panbrello type)
|
|
** * Command S6x (tick delay)
|
|
** * Command S9x (sound control - only S90/S91/S9E/S9F)
|
|
** * Command Mxx (set channel volume)
|
|
** * Command Nxy (channel volume slide)
|
|
** * Command Pxy (panning slide)
|
|
** * Command Txx<0x20 (tempo slide)
|
|
** * Command Wxy (global volume slide)
|
|
** * Command Xxx (7+1-bit pan) + XA4 for 'surround'
|
|
** * Command Yxy (panbrello)
|
|
** * Volume Command Pxx (set 4+1-bit panning)
|
|
**
|
|
** - Variables:
|
|
** * Pan changed from 4-bit (0..15) to 8+1-bit (0..256)
|
|
** * Memory variables for the new N/P/T/W/Y effects
|
|
** * Panbrello counter
|
|
** * Panbrello type
|
|
** * Channel volume multiplier
|
|
** * Channel surround flag
|
|
**
|
|
** - Other changes:
|
|
** * Added tracker identification to make sure Scream Tracker 3.xx
|
|
** modules are still played exactly like they should. :-)
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
#include <limits.h>
|
|
#include <math.h>
|
|
#include <assert.h>
|
|
|
|
#if defined(_MSC_VER) && !defined(inline)
|
|
#define inline __forceinline
|
|
#endif
|
|
|
|
#include "dbopl.h"
|
|
#include "resampler.h"
|
|
#include "st3play.h"
|
|
|
|
#define USE_VOL_RAMP
|
|
|
|
// TRACKER ID
|
|
enum
|
|
{
|
|
SCREAM_TRACKER = 1,
|
|
IMAGO_ORPHEUS = 2,
|
|
IMPULSE_TRACKER = 3,
|
|
SCHISM_TRACKER = 4,
|
|
OPENMPT = 5,
|
|
BEROTRACKER = 6,
|
|
// there is also CREAMTRACKER (7), but let's ignore that weird thing
|
|
};
|
|
|
|
|
|
// STRUCTS
|
|
typedef struct chn
|
|
{
|
|
int8_t aorgvol;
|
|
int8_t avol;
|
|
uint8_t channelnum;
|
|
uint8_t achannelused;
|
|
uint8_t aglis;
|
|
uint8_t atremor;
|
|
uint8_t atreon;
|
|
uint8_t atrigcnt;
|
|
uint8_t anotecutcnt;
|
|
uint8_t anotedelaycnt;
|
|
uint8_t avibtretype;
|
|
uint8_t note;
|
|
uint8_t ins;
|
|
uint8_t vol;
|
|
uint8_t cmd;
|
|
uint8_t info;
|
|
uint8_t lastins;
|
|
uint8_t lastnote;
|
|
uint8_t alastnfo;
|
|
uint8_t alasteff;
|
|
uint8_t alasteff1;
|
|
int16_t apanpos;
|
|
int16_t avibcnt;
|
|
uint16_t astartoffset;
|
|
uint16_t astartoffset00;
|
|
int32_t ac2spd;
|
|
int32_t asldspd;
|
|
int32_t aorgspd;
|
|
int32_t aspd;
|
|
|
|
// NON-ST3 variables
|
|
int8_t chanvol;
|
|
uint8_t surround;
|
|
uint8_t apantype;
|
|
uint8_t nxymem;
|
|
uint8_t pxymem;
|
|
uint8_t txxmem;
|
|
uint8_t wxymem;
|
|
uint8_t yxymem;
|
|
int16_t apancnt;
|
|
} chn_t;
|
|
|
|
typedef struct
|
|
{
|
|
const int8_t *sampleData;
|
|
int8_t loopEnabled;
|
|
int8_t sixteenBit;
|
|
int8_t stereo;
|
|
int8_t mixing;
|
|
int8_t interpolating;
|
|
int8_t playBackwards;
|
|
int32_t sampleLength;
|
|
int32_t sampleLoopEnd;
|
|
int32_t samplePosition;
|
|
int32_t sampleLoopLength;
|
|
|
|
float incRate;
|
|
float volume;
|
|
float panningL;
|
|
float panningR;
|
|
float orgPanR;
|
|
|
|
#ifdef USE_VOL_RAMP
|
|
float targetVol;
|
|
float targetPanL;
|
|
float targetPanR;
|
|
float volDelta;
|
|
float panDeltaL;
|
|
float panDeltaR;
|
|
float fader;
|
|
float faderDest;
|
|
float faderDelta;
|
|
#endif
|
|
} VOICE;
|
|
|
|
// VARIABLES / STATE
|
|
typedef struct
|
|
{
|
|
int8_t tickdelay;
|
|
int8_t volslidetype;
|
|
int8_t patterndelay;
|
|
int8_t patloopcount;
|
|
uint8_t breakpat;
|
|
uint8_t startrow;
|
|
uint8_t musiccount;
|
|
int16_t np_ord;
|
|
int16_t np_row;
|
|
int16_t np_pat;
|
|
int16_t np_patoff;
|
|
int16_t patloopstart;
|
|
int16_t jumptorow;
|
|
uint16_t patternadd;
|
|
uint16_t instrumentadd;
|
|
uint16_t patmusicrand;
|
|
int32_t aspdmax;
|
|
int32_t aspdmin;
|
|
uint32_t np_patseg;
|
|
chn_t chn[32];
|
|
|
|
uint8_t mixingVolume;
|
|
int32_t soundBufferSize;
|
|
uint32_t outputFreq;
|
|
|
|
#ifdef USE_VOL_RAMP
|
|
VOICE voice[32 * 2];
|
|
void *resampler[64 * 2];
|
|
#else
|
|
VOICE voice[32];
|
|
void *resampler[64];
|
|
#endif
|
|
|
|
void *fmChip;
|
|
void *fmResampler;
|
|
|
|
const uint8_t *fmPatchTable[9];
|
|
uint8_t fmLastB0[9];
|
|
|
|
int8_t **adpcmSamples;
|
|
|
|
float f_outputFreq;
|
|
float f_masterVolume;
|
|
|
|
#ifdef USE_VOL_RAMP
|
|
float f_samplesPerFrame;
|
|
float f_samplesPerFrameSharp;
|
|
#endif
|
|
|
|
int8_t samplingInterpolation;
|
|
#ifdef USE_VOL_RAMP
|
|
int8_t rampStyle;
|
|
#endif
|
|
float *masterBufferL;
|
|
float *masterBufferR;
|
|
int32_t samplesLeft;
|
|
int8_t isMixing;
|
|
uint32_t samplesPerFrame;
|
|
|
|
// GLOBAL VARIABLES
|
|
int8_t ModuleLoaded;
|
|
int8_t MusicPaused;
|
|
int8_t Playing;
|
|
|
|
uint8_t *mseg;
|
|
int8_t lastachannelused;
|
|
int8_t tracker;
|
|
int8_t oldstvib;
|
|
int8_t fastvolslide;
|
|
int8_t amigalimits;
|
|
uint8_t musicmax;
|
|
uint8_t numChannels;
|
|
int16_t x_np_row;
|
|
int16_t x_np_ord;
|
|
int16_t x_np_pat;
|
|
int16_t tempo;
|
|
int16_t globalvol;
|
|
int8_t stereomode;
|
|
uint8_t mastervol;
|
|
uint32_t mseg_len;
|
|
|
|
uint8_t muted[4];
|
|
|
|
uint32_t loopCount;
|
|
uint8_t playedOrder[8192];
|
|
} PLAYER;
|
|
|
|
typedef void (*effect_routine)(PLAYER *, chn_t *ch);
|
|
|
|
|
|
enum { _soundBufferSize = 512 };
|
|
|
|
|
|
// TABLES
|
|
|
|
static const int16_t xfinetune_amiga[16] =
|
|
{
|
|
7895, 7941, 7985, 8046, 8107, 8169, 8232, 8280,
|
|
8363, 8413, 8463, 8529, 8581, 8651, 8723, 8757
|
|
};
|
|
|
|
static const int8_t retrigvoladd[32] =
|
|
{
|
|
0, -1, -2, -4, -8,-16, 0, 0,
|
|
0, 1, 2, 4, 8, 16, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 10, 8,
|
|
0, 0, 0, 0, 0, 0, 24, 32
|
|
};
|
|
|
|
static const uint16_t notespd[12] =
|
|
{
|
|
1712 * 16, 1616 * 16, 1524 * 16,
|
|
1440 * 16, 1356 * 16, 1280 * 16,
|
|
1208 * 16, 1140 * 16, 1076 * 16,
|
|
1016 * 16, 960 * 16, 907 * 16
|
|
};
|
|
|
|
static const int16_t vibsin[64] =
|
|
{
|
|
0x00, 0x18, 0x31, 0x4A, 0x61, 0x78, 0x8D, 0xA1,
|
|
0xB4, 0xC5, 0xD4, 0xE0, 0xEB, 0xF4, 0xFA, 0xFD,
|
|
0xFF, 0xFD, 0xFA, 0xF4, 0xEB, 0xE0, 0xD4, 0xC5,
|
|
0xB4, 0xA1, 0x8D, 0x78, 0x61, 0x4A, 0x31, 0x18,
|
|
0x00,-0x18,-0x31,-0x4A,-0x61,-0x78,-0x8D,-0xA1,
|
|
-0xB4,-0xC5,-0xD4,-0xE0,-0xEB,-0xF4,-0xFA,-0xFD,
|
|
-0xFF,-0xFD,-0xFA,-0xF4,-0xEB,-0xE0,-0xD4,-0xC5,
|
|
-0xB4,-0xA1,-0x8D,-0x78,-0x61,-0x4A,-0x31,-0x18
|
|
};
|
|
|
|
static const uint8_t vibsqu[64] =
|
|
{
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
|
};
|
|
|
|
static const int16_t vibramp[64] =
|
|
{
|
|
0, -248,-240,-232,-224,-216,-208,-200,
|
|
-192, -184,-176,-168,-160,-152,-144,-136,
|
|
-128, -120,-112,-104, -96, -88, -80, -72,
|
|
-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, 136, 144, 152, 160, 168, 176, 184,
|
|
192, 200, 208, 216, 224, 232, 240, 248
|
|
};
|
|
|
|
static const char Adlib_PortBases[9] = {0, 1, 2, 8, 9, 10, 16, 17, 18};
|
|
|
|
// FUNCTION DECLARATIONS
|
|
|
|
|
|
static void setSamplesPerFrame(PLAYER *, uint32_t val);
|
|
static void setSamplingInterpolation(PLAYER *, int8_t value);
|
|
#ifdef USE_VOL_RAMP
|
|
static void setRampStyle(PLAYER *, int8_t value);
|
|
#endif
|
|
static void setStereoMode(PLAYER *, int8_t value);
|
|
static void setMasterVolume(PLAYER *, uint8_t value);
|
|
static void voiceSetSource(PLAYER *, uint8_t voiceNumber, const int8_t *sampleData,
|
|
int32_t sampleLength, int32_t sampleLoopLength, int32_t sampleLoopEnd,
|
|
int8_t loopEnabled, int8_t sixteenbit, int8_t stereo);
|
|
static void voiceSetSamplePosition(PLAYER *, uint8_t voiceNumber, uint16_t value);
|
|
static void voiceSetVolume(PLAYER *, uint8_t voiceNumber, float volume, uint8_t note_on);
|
|
static void voiceSetSurround(PLAYER *, uint8_t voiceNumber, int8_t surround);
|
|
static void voiceSetPanning(PLAYER *, uint8_t voiceNumber, uint16_t pan);
|
|
static void voiceSetSamplingFrequency(PLAYER *, uint8_t voiceNumber, float samplingFrequency);
|
|
static void voiceSetPlayBackwards(PLAYER *, uint8_t voiceNumber, int8_t playBackwards);
|
|
static void voiceSetReadPosToEnd(PLAYER *, uint8_t voiceNumber);
|
|
|
|
static void FreeSong(PLAYER *);
|
|
|
|
static void s_ret(PLAYER *, chn_t *ch);
|
|
static void s_setgliss(PLAYER *, chn_t *ch);
|
|
static void s_setfinetune(PLAYER *, chn_t *ch); // NON-ST3
|
|
static void s_setvibwave(PLAYER *, chn_t *ch);
|
|
static void s_settrewave(PLAYER *, chn_t *ch);
|
|
static void s_setpanwave(PLAYER *, chn_t *ch); // NON-ST3
|
|
static void s_tickdelay(PLAYER *, chn_t *ch); // NON-ST3
|
|
static void s_setpanpos(PLAYER *, chn_t *ch);
|
|
static void s_sndcntrl(PLAYER *, chn_t *ch); // NON-ST3
|
|
static void s_patloop(PLAYER *, chn_t *ch);
|
|
static void s_notecut(PLAYER *, chn_t *ch);
|
|
static void s_notecutb(PLAYER *, chn_t *ch);
|
|
static void s_notedelay(PLAYER *, chn_t *ch);
|
|
static void s_notedelayb(PLAYER *, chn_t *ch);
|
|
static void s_patterdelay(PLAYER *, chn_t *ch);
|
|
static void s_setspeed(PLAYER *, chn_t *ch);
|
|
static void s_jmpto(PLAYER *, chn_t *ch);
|
|
static void s_break(PLAYER *, chn_t *ch);
|
|
static void s_volslide(PLAYER *, chn_t *ch);
|
|
static void s_slidedown(PLAYER *, chn_t *ch);
|
|
static void s_slideup(PLAYER *, chn_t *ch);
|
|
static void s_toneslide(PLAYER *, chn_t *ch);
|
|
static void s_vibrato(PLAYER *, chn_t *ch);
|
|
static void s_tremor(PLAYER *, chn_t *ch);
|
|
static void s_arp(PLAYER *, chn_t *ch);
|
|
static void s_chanvol(PLAYER *, chn_t *ch); // NON-ST3
|
|
static void s_chanvolslide(PLAYER *, chn_t *ch); // NON-ST3
|
|
static void s_vibvol(PLAYER *, chn_t *ch);
|
|
static void s_tonevol(PLAYER *, chn_t *ch);
|
|
static void s_panslide(PLAYER *, chn_t *ch);
|
|
static void s_retrig(PLAYER *, chn_t *ch);
|
|
static void s_tremolo(PLAYER *, chn_t *ch);
|
|
static void s_scommand1(PLAYER *, chn_t *ch);
|
|
static void s_scommand2(PLAYER *, chn_t *ch);
|
|
static void s_settempo(PLAYER *, chn_t *ch);
|
|
static void s_finevibrato(PLAYER *, chn_t *ch);
|
|
static void s_setgvol(PLAYER *, chn_t *ch);
|
|
static void s_globvolslide(PLAYER *, chn_t *ch); // NON-ST3
|
|
static void s_setpan(PLAYER *, chn_t *ch); // NON-ST3
|
|
static void s_panbrello(PLAYER *, chn_t *ch); // NON-ST3
|
|
|
|
static effect_routine ssoncejmp[16] =
|
|
{
|
|
s_ret,
|
|
s_setgliss,
|
|
s_setfinetune, // NON-ST3
|
|
s_setvibwave,
|
|
s_settrewave,
|
|
s_setpanwave, // NON-ST3
|
|
s_tickdelay, // NON-ST3
|
|
s_ret,
|
|
s_setpanpos,
|
|
s_sndcntrl, // NON-ST3
|
|
s_ret,
|
|
s_patloop,
|
|
s_notecut,
|
|
s_notedelay,
|
|
s_patterdelay,
|
|
s_ret
|
|
};
|
|
|
|
static effect_routine ssotherjmp[16] =
|
|
{
|
|
s_ret,
|
|
s_ret,
|
|
s_ret,
|
|
s_ret,
|
|
s_ret,
|
|
s_ret,
|
|
s_ret,
|
|
s_ret,
|
|
s_ret,
|
|
s_ret,
|
|
s_ret,
|
|
s_ret,
|
|
s_notecutb,
|
|
s_notedelayb,
|
|
s_ret,
|
|
s_ret
|
|
};
|
|
|
|
static effect_routine soncejmp[27] =
|
|
{
|
|
s_ret,
|
|
s_setspeed,
|
|
s_jmpto,
|
|
s_break,
|
|
s_volslide,
|
|
s_slidedown,
|
|
s_slideup,
|
|
s_ret,
|
|
s_ret,
|
|
s_tremor,
|
|
s_arp,
|
|
s_ret,
|
|
s_ret,
|
|
s_chanvol, // NON-ST3
|
|
s_chanvolslide, // NON-ST3
|
|
s_ret,
|
|
s_panslide, // NON-ST3
|
|
s_retrig,
|
|
s_ret,
|
|
s_scommand1,
|
|
s_settempo,
|
|
s_ret,
|
|
s_ret,
|
|
s_globvolslide, // NON-ST3
|
|
s_setpan, // NON-ST3
|
|
s_panbrello, // NON-ST3
|
|
s_ret
|
|
};
|
|
|
|
static effect_routine sotherjmp[27] =
|
|
{
|
|
s_ret,
|
|
s_ret,
|
|
s_ret,
|
|
s_ret,
|
|
s_volslide,
|
|
s_slidedown,
|
|
s_slideup,
|
|
s_toneslide,
|
|
s_vibrato,
|
|
s_tremor,
|
|
s_arp,
|
|
s_vibvol,
|
|
s_tonevol,
|
|
s_ret,
|
|
s_chanvolslide, // NON-ST3
|
|
s_ret,
|
|
s_panslide, // NON-ST3
|
|
s_retrig,
|
|
s_tremolo,
|
|
s_scommand2,
|
|
s_settempo, // NON-ST3 (for tempo slides)
|
|
s_finevibrato,
|
|
s_setgvol,
|
|
s_globvolslide, // NON-ST3
|
|
s_ret,
|
|
s_panbrello, // NON-ST3
|
|
s_ret
|
|
};
|
|
|
|
|
|
// CODE START
|
|
void * st3play_Alloc(uint32_t outputFreq, int8_t interpolation, int8_t ramp_style)
|
|
{
|
|
int32_t i;
|
|
PLAYER *p;
|
|
|
|
p = (PLAYER *)(calloc(sizeof (PLAYER), 1));
|
|
if (p == NULL)
|
|
return (0);
|
|
|
|
resampler_init();
|
|
|
|
#ifdef USE_VOL_RAMP
|
|
for (i = 0; i < (64 * 2); ++i)
|
|
#else
|
|
for (i = 0; i < 64; ++i)
|
|
#endif
|
|
{
|
|
p->resampler[i] = resampler_create();
|
|
if (p->resampler[i] == NULL)
|
|
{
|
|
st3play_Free(p);
|
|
return (0);
|
|
}
|
|
|
|
resampler_set_quality(p->resampler[i], interpolation);
|
|
}
|
|
|
|
p->soundBufferSize = _soundBufferSize;
|
|
p->masterBufferL = (float *)(malloc(p->soundBufferSize * sizeof (float)));
|
|
p->masterBufferR = (float *)(malloc(p->soundBufferSize * sizeof (float)));
|
|
|
|
if ((p->masterBufferL == NULL) || (p->masterBufferR == NULL))
|
|
{
|
|
st3play_Free(p);
|
|
return (0);
|
|
}
|
|
|
|
p->stereomode = 1;
|
|
p->numChannels = 32;
|
|
p->globalvol = 64;
|
|
p->mastervol = 48;
|
|
|
|
p->outputFreq = outputFreq;
|
|
p->f_outputFreq = (float)(outputFreq);
|
|
|
|
setSamplingInterpolation(p, interpolation);
|
|
#ifdef USE_VOL_RAMP
|
|
setRampStyle(p, ramp_style);
|
|
#endif
|
|
setSamplesPerFrame(p, ((outputFreq * 5) / 2 / 125));
|
|
|
|
return (p);
|
|
}
|
|
|
|
static int st3play_AdlibInit(PLAYER *p)
|
|
{
|
|
p->fmChip = malloc(Chip_GetSize());
|
|
if (p->fmChip == NULL)
|
|
return (-1);
|
|
|
|
Chip_Init(p->fmChip);
|
|
Chip_Setup(p->fmChip, 3579545 * 4, 49716);
|
|
Chip_WriteReg(p->fmChip, 0x01, 0x20); // enable wave select, but rather pointless with dbopl
|
|
|
|
p->fmResampler = resampler_create();
|
|
if (p->fmResampler == NULL)
|
|
return (-1);
|
|
|
|
resampler_set_quality(p->fmResampler, RESAMPLER_QUALITY_MAX);
|
|
resampler_set_rate(p->fmResampler, 49716.0f / p->f_outputFreq);
|
|
|
|
return (0);
|
|
}
|
|
|
|
void st3play_Free(void *_p)
|
|
{
|
|
int32_t i;
|
|
PLAYER *p;
|
|
|
|
p = (PLAYER *)(_p);
|
|
FreeSong(p);
|
|
|
|
if (p->fmResampler)
|
|
resampler_delete(p->fmResampler);
|
|
|
|
if (p->fmChip)
|
|
free(p->fmChip);
|
|
|
|
#ifdef USE_VOL_RAMP
|
|
for (i = 0; i < (64 * 2); ++i)
|
|
#else
|
|
for (i = 0; i < 64; ++i)
|
|
#endif
|
|
{
|
|
if (p->resampler[i])
|
|
resampler_delete(p->resampler[i]);
|
|
}
|
|
|
|
if (p->masterBufferL) free(p->masterBufferL);
|
|
if (p->masterBufferR) free(p->masterBufferR);
|
|
|
|
free(p);
|
|
}
|
|
|
|
static inline uint16_t get_le16(const void *_p)
|
|
{
|
|
const uint8_t *p;
|
|
|
|
p = (const uint8_t *)(_p);
|
|
return (p[0] | (p[1] << 8));
|
|
}
|
|
|
|
static inline void set_le16(void *_p, uint16_t v)
|
|
{
|
|
uint8_t *p;
|
|
|
|
p = (uint8_t *)(_p);
|
|
p[0] = (uint8_t)(v);
|
|
p[1] = v >> 8;
|
|
}
|
|
|
|
static inline uint32_t get_le32(const void *_p)
|
|
{
|
|
const uint8_t *p;
|
|
|
|
p = (const uint8_t *)(_p);
|
|
return (p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24));
|
|
}
|
|
|
|
static inline void getlastnfo(PLAYER *p, chn_t *ch)
|
|
{
|
|
(void)(p);
|
|
|
|
if (!ch->info)
|
|
ch->info = ch->alastnfo;
|
|
}
|
|
|
|
static void setspeed(PLAYER *p, uint8_t val)
|
|
{
|
|
if (val)
|
|
p->musicmax = val;
|
|
}
|
|
|
|
static void settempo(PLAYER *p, uint16_t val)
|
|
{
|
|
if (val > 32)
|
|
{
|
|
p->tempo = val;
|
|
setSamplesPerFrame(p, ((p->outputFreq * 5) / 2) / p->tempo);
|
|
}
|
|
}
|
|
|
|
static void st3play_AdlibHertzTouch(PLAYER *p, uint8_t ch, int32_t Hertz, uint8_t keyoff)
|
|
{
|
|
int32_t Oct;
|
|
|
|
for (Oct = 0; Hertz >= 512; Oct++)
|
|
Hertz /= 2;
|
|
|
|
Chip_WriteReg(p->fmChip, 0xA0 + ch, Hertz & 0xFF);
|
|
|
|
p->fmLastB0[ch] = (keyoff ? 0 : 0x20) | ((Hertz >> 8) & 3) | ((Oct & 7) << 2);
|
|
Chip_WriteReg(p->fmChip, 0xB0 + ch, p->fmLastB0[ch]);
|
|
}
|
|
|
|
static inline void setspd(PLAYER *p, uint8_t ch)
|
|
{
|
|
uint8_t adlibChannel;
|
|
int32_t tmpspd;
|
|
|
|
adlibChannel = (p->mseg[0x40 + ch] & 0x7F) - 16;
|
|
p->chn[ch].achannelused |= 0x80;
|
|
|
|
tmpspd = p->chn[ch].aspd;
|
|
|
|
if (p->amigalimits)
|
|
{
|
|
if (p->chn[ch].aorgspd > p->aspdmax) p->chn[ch].aorgspd = p->aspdmax;
|
|
if (p->chn[ch].aorgspd < p->aspdmin) p->chn[ch].aorgspd = p->aspdmin;
|
|
|
|
if (p->chn[ch].aspd > p->aspdmax)
|
|
p->chn[ch].aspd = p->aspdmax;
|
|
}
|
|
|
|
if ((p->tracker == SCREAM_TRACKER) || p->amigalimits)
|
|
{
|
|
if (tmpspd > p->aspdmax)
|
|
tmpspd = p->aspdmax;
|
|
}
|
|
else
|
|
{
|
|
// *ABSOLUTE* max!
|
|
if (tmpspd > 14317056)
|
|
tmpspd = 14317056;
|
|
}
|
|
|
|
if (tmpspd == 0)
|
|
{
|
|
// cut channel
|
|
voiceSetSamplingFrequency(p, ch, 0);
|
|
return;
|
|
}
|
|
|
|
if (tmpspd < p->aspdmin)
|
|
{
|
|
tmpspd = p->aspdmin;
|
|
|
|
if (p->amigalimits && (p->chn[ch].aspd < p->aspdmin))
|
|
p->chn[ch].aspd = p->aspdmin;
|
|
}
|
|
|
|
// ST3 actually uses 14317056 (3.579264MHz * 4) instead of 14317456 (8363*1712)
|
|
if (tmpspd > 0)
|
|
voiceSetSamplingFrequency(p, ch, 14317056 / tmpspd);
|
|
|
|
if (adlibChannel < 9)
|
|
st3play_AdlibHertzTouch(p, adlibChannel, 14317056.0f / (float)(tmpspd), 0);
|
|
}
|
|
|
|
static void st3play_AdlibTouch(PLAYER *p, uint8_t ch, const uint8_t * patch, uint8_t vol)
|
|
{
|
|
int32_t Operator;
|
|
|
|
if (!patch)
|
|
{
|
|
patch = p->fmPatchTable[ch];
|
|
if (!patch)
|
|
return;
|
|
}
|
|
|
|
p->fmPatchTable[ch] = patch;
|
|
Operator = Adlib_PortBases[ch];
|
|
|
|
Chip_WriteReg(p->fmChip, 0x40 + Operator, (patch[2] & 0xC0) | (((int32_t)(patch[2] & 63) - 63) * vol + 63 * 64 - 32) / 64);
|
|
Chip_WriteReg(p->fmChip, 0x43 + Operator, (patch[3] & 0xC0) | (((int32_t)(patch[3] & 63) - 63) * vol + 63 * 64 - 32) / 64);
|
|
}
|
|
|
|
static inline void setvol(PLAYER *p, uint8_t ch, uint8_t offset, uint8_t note_on)
|
|
{
|
|
uint8_t adlibChannel;
|
|
|
|
adlibChannel = (p->mseg[0x40 + ch] & 0x7F) - 16;
|
|
p->chn[ch].achannelused |= 0x80;
|
|
|
|
#ifdef USE_VOL_RAMP
|
|
voiceSetVolume(p, ch + offset, ((float)(p->chn[ch].avol) / 63.0f) * ((float)(p->chn[ch].chanvol) / 64.0f) * ((float)(p->globalvol) / 64.0f), note_on);
|
|
#else
|
|
voiceSetVolume(p, ch, ((float)(p->chn[ch].avol) / 63.0f) * ((float)(p->chn[ch].chanvol) / 64.0f) * ((float)(p->globalvol) / 64.0f), note_on);
|
|
#endif
|
|
|
|
if (adlibChannel < 9)
|
|
st3play_AdlibTouch(p, adlibChannel, NULL, p->chn[ch].avol);
|
|
}
|
|
|
|
static inline void setpan(PLAYER *p, uint8_t ch)
|
|
{
|
|
voiceSetPanning(p, ch, p->chn[ch].apanpos);
|
|
}
|
|
|
|
static inline int16_t stnote2herz(PLAYER *p, uint8_t note)
|
|
{
|
|
uint8_t tmpnote;
|
|
uint8_t tmpocta;
|
|
|
|
if (note == 254) return (0);
|
|
|
|
tmpnote = note & 0x0F;
|
|
tmpocta = note >> 4;
|
|
|
|
// ST3 doesn't do this, but do it for safety
|
|
if (tmpnote > 11) tmpnote = 11;
|
|
|
|
// Limit octaves to 8 in ST3 mode
|
|
if ((p->tracker == SCREAM_TRACKER) && (tmpocta > 7))
|
|
tmpocta = 7;
|
|
|
|
return (notespd[tmpnote] >> tmpocta);
|
|
}
|
|
|
|
static inline int32_t scalec2spd(PLAYER *p, uint8_t ch, int32_t spd)
|
|
{
|
|
spd *= 8363;
|
|
|
|
if (p->tracker == SCREAM_TRACKER)
|
|
{
|
|
if ((spd / 65536) > p->chn[ch].ac2spd)
|
|
return (32767);
|
|
}
|
|
|
|
if (p->chn[ch].ac2spd)
|
|
spd /= p->chn[ch].ac2spd;
|
|
|
|
if (p->tracker == SCREAM_TRACKER)
|
|
{
|
|
if (spd > 32767)
|
|
return (32767);
|
|
}
|
|
|
|
return (spd);
|
|
}
|
|
|
|
// for Gxx with semitones slide flag
|
|
static inline int32_t roundspd(PLAYER *p, uint8_t ch, int32_t spd)
|
|
{
|
|
int8_t octa;
|
|
int8_t lastnote;
|
|
int8_t newnote;
|
|
int32_t note;
|
|
int32_t lastspd;
|
|
int32_t newspd;
|
|
|
|
newspd = spd * p->chn[ch].ac2spd;
|
|
|
|
if (p->tracker == SCREAM_TRACKER)
|
|
{
|
|
if ((newspd / 65536) >= 8363)
|
|
return (spd);
|
|
}
|
|
|
|
newspd /= 8363;
|
|
|
|
// find octave
|
|
octa = 0;
|
|
lastspd = ((1712 * 8) + (907 * 16)) / 2;;
|
|
while (newspd < lastspd)
|
|
{
|
|
octa++;
|
|
lastspd /= 2;
|
|
}
|
|
|
|
// find note
|
|
lastnote = 0;
|
|
newnote = 0;
|
|
|
|
if (p->tracker == SCREAM_TRACKER)
|
|
lastspd = 32767;
|
|
else
|
|
lastspd = 32767 * 2;
|
|
|
|
while (newnote < 11)
|
|
{
|
|
note = (notespd[newnote] >> octa) - newspd;
|
|
if (note < 0) note *= -1; // abs()
|
|
|
|
if (note < lastspd)
|
|
{
|
|
lastspd = note;
|
|
lastnote = newnote;
|
|
}
|
|
|
|
newnote++;
|
|
}
|
|
|
|
// get new speed from new note
|
|
newspd = (stnote2herz(p, (octa << 4) | (lastnote & 0x0F))) * 8363;
|
|
|
|
if (p->tracker == SCREAM_TRACKER)
|
|
{
|
|
if ((newspd / 65536) >= p->chn[ch].ac2spd)
|
|
return (spd);
|
|
}
|
|
|
|
if (p->chn[ch].ac2spd)
|
|
newspd /= p->chn[ch].ac2spd;
|
|
|
|
return (newspd);
|
|
}
|
|
|
|
static int16_t neworder(PLAYER *p)
|
|
{
|
|
newOrderSkip:
|
|
p->np_ord++;
|
|
|
|
if ((p->mseg[0x60 + (p->np_ord - 1)] == 255) || (p->np_ord > get_le16(&p->mseg[0x20]))) // end
|
|
p->np_ord = 1;
|
|
|
|
if (p->mseg[0x60 + (p->np_ord - 1)] == 254) // skip
|
|
goto newOrderSkip; // avoid recursive calling
|
|
|
|
p->np_pat = (int16_t)(p->mseg[0x60 + (p->np_ord - 1)]);
|
|
p->np_patoff = -1; // force reseek
|
|
p->np_row = p->startrow;
|
|
p->startrow = 0;
|
|
p->patmusicrand = 0;
|
|
p->patloopstart = -1;
|
|
p->jumptorow = -1;
|
|
|
|
return (p->np_row);
|
|
}
|
|
|
|
// updates np_patseg and np_patoff
|
|
static inline void seekpat(PLAYER *p)
|
|
{
|
|
uint8_t dat;
|
|
int16_t i;
|
|
int16_t j;
|
|
|
|
if (p->np_patoff == -1) // seek must be done
|
|
{
|
|
p->np_patseg = get_le16(&p->mseg[p->patternadd + (p->np_pat * 2)]) * 16;
|
|
if (p->np_patseg)
|
|
{
|
|
j = 2; // skip packed pat len flag
|
|
|
|
// inc np_patoff on patbreak
|
|
if (p->np_row)
|
|
{
|
|
i = p->np_row;
|
|
while (i)
|
|
{
|
|
dat = p->mseg[p->np_patseg + j++];
|
|
if (!dat)
|
|
{
|
|
i--;
|
|
}
|
|
else
|
|
{
|
|
// skip ch data
|
|
if (dat & 0x20) j += 2;
|
|
if (dat & 0x40) j += 1;
|
|
if (dat & 0x80) j += 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
p->np_patoff = j;
|
|
}
|
|
}
|
|
}
|
|
|
|
static inline uint8_t getnote(PLAYER *p)
|
|
{
|
|
uint8_t dat;
|
|
uint8_t ch;
|
|
int16_t i;
|
|
|
|
if (!p->np_patseg || (p->np_patseg >= p->mseg_len) || (p->np_pat >= get_le16(&p->mseg[0x24])))
|
|
return (255);
|
|
|
|
i = p->np_patoff;
|
|
for (;;)
|
|
{
|
|
dat = p->mseg[p->np_patseg + i++];
|
|
if (!dat) // end of row
|
|
{
|
|
p->np_patoff = i;
|
|
return (255);
|
|
}
|
|
|
|
if (!(p->mseg[0x40 + (dat & 0x1F)] & 0x80))
|
|
{
|
|
ch = dat & 0x1F; // channel to trigger
|
|
break;
|
|
}
|
|
|
|
// channel is off, skip data
|
|
if (dat & 0x20) i += 2;
|
|
if (dat & 0x40) i += 1;
|
|
if (dat & 0x80) i += 2;
|
|
}
|
|
|
|
if (dat & 0x20)
|
|
{
|
|
p->chn[ch].note = p->mseg[p->np_patseg + i++];
|
|
p->chn[ch].ins = p->mseg[p->np_patseg + i++];
|
|
|
|
if (p->chn[ch].note != 255) p->chn[ch].lastnote = p->chn[ch].note;
|
|
if (p->chn[ch].ins) p->chn[ch].lastins = p->chn[ch].ins;
|
|
}
|
|
|
|
if (dat & 0x40)
|
|
p->chn[ch].vol = p->mseg[p->np_patseg + i++];
|
|
|
|
if (dat & 0x80)
|
|
{
|
|
p->chn[ch].cmd = p->mseg[p->np_patseg + i++];
|
|
p->chn[ch].info = p->mseg[p->np_patseg + i++];
|
|
}
|
|
|
|
p->np_patoff = i;
|
|
return (ch);
|
|
}
|
|
|
|
static void st3play_AdlibPatch(PLAYER *p, uint8_t ch, const uint8_t *patch)
|
|
{
|
|
int32_t Operator;
|
|
|
|
Operator = Adlib_PortBases[ch];
|
|
p->fmPatchTable[ch] = patch;
|
|
|
|
Chip_WriteReg(p->fmChip, 0x20 + Operator, patch[0]);
|
|
Chip_WriteReg(p->fmChip, 0x60 + Operator, patch[4]);
|
|
Chip_WriteReg(p->fmChip, 0x80 + Operator, patch[6]);
|
|
Chip_WriteReg(p->fmChip, 0xE0 + Operator, patch[8] & 3);
|
|
Chip_WriteReg(p->fmChip, 0x23 + Operator, patch[1]);
|
|
Chip_WriteReg(p->fmChip, 0x63 + Operator, patch[5]);
|
|
Chip_WriteReg(p->fmChip, 0x83 + Operator, patch[7]);
|
|
Chip_WriteReg(p->fmChip, 0xE3 + Operator, patch[9] & 3);
|
|
Chip_WriteReg(p->fmChip, 0xC0 + ch, patch[10]);
|
|
}
|
|
|
|
static void st3play_AdlibNoteOff(PLAYER *p, uint8_t ch)
|
|
{
|
|
Chip_WriteReg(p->fmChip, 0xB0 + ch, p->fmLastB0[ch] & ~0x20);
|
|
}
|
|
|
|
static inline void doamiga(PLAYER *p, uint8_t ch)
|
|
{
|
|
uint8_t *insdat;
|
|
int8_t loop;
|
|
int8_t shift;
|
|
uint8_t adlibChannel;
|
|
uint32_t insoffs;
|
|
uint32_t inslen;
|
|
uint32_t insrepbeg;
|
|
uint32_t insrepend;
|
|
|
|
#ifdef USE_VOL_RAMP
|
|
uint8_t volassigned;
|
|
#endif
|
|
|
|
volassigned = 0;
|
|
adlibChannel = (p->mseg[0x40 + ch] & 0x7F) - 16;
|
|
|
|
if ((p->fmChip == NULL) || (p->fmResampler == NULL)) // safety check
|
|
adlibChannel = 255;
|
|
|
|
if (p->chn[ch].ins)
|
|
{
|
|
p->chn[ch].lastins = p->chn[ch].ins;
|
|
p->chn[ch].astartoffset = 0;
|
|
|
|
if (p->chn[ch].ins <= get_le16(&p->mseg[0x22])) // added for safety reasons
|
|
{
|
|
insdat = &p->mseg[get_le16(&p->mseg[p->instrumentadd + ((p->chn[ch].ins - 1) * 2)]) * 16];
|
|
if (insdat[0])
|
|
{
|
|
if ((insdat[0] == 1) && (adlibChannel >= 9))
|
|
{
|
|
p->chn[ch].ac2spd = get_le32(&insdat[0x20]);
|
|
|
|
if ((p->tracker == OPENMPT) || (p->tracker == BEROTRACKER))
|
|
{
|
|
if ((p->chn[ch].cmd == ('S' - 64)) && ((p->chn[ch].info & 0xF0) == 0x20))
|
|
p->chn[ch].ac2spd = xfinetune_amiga[p->chn[ch].info & 0x0F];
|
|
}
|
|
|
|
p->chn[ch].avol = (int8_t)(insdat[0x1C]);
|
|
|
|
if (p->chn[ch].avol < 0) p->chn[ch].avol = 0;
|
|
else if (p->chn[ch].avol > 63) p->chn[ch].avol = 63;
|
|
|
|
p->chn[ch].aorgvol = p->chn[ch].avol;
|
|
|
|
insoffs = ((insdat[0x0D] << 16) | (insdat[0x0F] << 8) | insdat[0x0E]) * 16;
|
|
|
|
if (insoffs > p->mseg_len)
|
|
insoffs = p->mseg_len;
|
|
|
|
inslen = get_le32(&insdat[0x10]);
|
|
|
|
shift = 0;
|
|
if (insdat[0x1F] & 2) shift++;
|
|
if (insdat[0x1F] & 4) shift++;
|
|
|
|
if (insoffs + (inslen << shift) > p->mseg_len)
|
|
inslen = (p->mseg_len - insoffs) >> shift;
|
|
|
|
insrepbeg = get_le32(&insdat[0x14]);
|
|
insrepend = get_le32(&insdat[0x18]);
|
|
|
|
if (insrepbeg > inslen) insrepbeg = inslen;
|
|
if (insrepend > inslen) insrepend = inslen;
|
|
|
|
loop = 0;
|
|
if ((insdat[0x1F] & 1) && inslen && (insrepend > insrepbeg))
|
|
loop = 1;
|
|
|
|
#ifdef USE_VOL_RAMP
|
|
if (p->rampStyle > 0 && (p->chn[ch].cmd != 7) && p->voice[ch].mixing)
|
|
{
|
|
memcpy(p->voice + 32 + (int32_t)(ch), p->voice + (int32_t)(ch), sizeof (VOICE));
|
|
p->voice[ch + 32].faderDest = 0.0f;
|
|
p->voice[ch + 32].faderDelta = (p->voice[ch + 32].faderDest - p->voice[ch + 32].fader) * p->f_samplesPerFrameSharp;
|
|
|
|
setvol(p, ch, 32, 0);
|
|
|
|
resampler_dup_inplace(p->resampler[ch + 32], p->resampler[ch]);
|
|
resampler_dup_inplace(p->resampler[ch + 32 + 64], p->resampler[ch + 64]);
|
|
resampler_clear(p->resampler[ch]);
|
|
resampler_clear(p->resampler[ch + 64]);
|
|
|
|
if (p->chn[ch].vol != 255)
|
|
{
|
|
if (p->chn[ch].vol <= 64)
|
|
{
|
|
p->chn[ch].avol = p->chn[ch].vol;
|
|
p->chn[ch].aorgvol = p->chn[ch].vol;
|
|
}
|
|
else if ((p->chn[ch].vol >= 128) && (p->chn[ch].vol <= 192))// NON-ST3
|
|
{
|
|
p->chn[ch].surround = 0;
|
|
voiceSetSurround(p, ch, 0);
|
|
|
|
p->chn[ch].apanpos = (p->chn[ch].vol - 128) * 4;
|
|
setpan(p, ch);
|
|
}
|
|
}
|
|
|
|
setvol(p, ch, 0, 1);
|
|
volassigned = 1;
|
|
}
|
|
else
|
|
#endif
|
|
setvol(p, ch, 0, 0);
|
|
|
|
// This specific phase differs from what sound card driver you use in ST3...
|
|
// GUS: Don't set new voice sample. SB: Set new voice sample.
|
|
// Let's use the GUS model here.
|
|
if ((p->chn[ch].cmd != ('G' - 64)) && (p->chn[ch].cmd != ('L' - 64)))
|
|
{
|
|
if (insdat[0x1E] == 4)
|
|
voiceSetSource(p, ch, p->adpcmSamples[p->chn[ch].ins - 1], inslen,
|
|
insrepend - insrepbeg, insrepend, loop, 0, 0);
|
|
else
|
|
voiceSetSource(p, ch, (const int8_t *)(&p->mseg[insoffs]), inslen,
|
|
insrepend - insrepbeg, insrepend, loop,
|
|
insdat[0x1F] & 4, insdat[0x1F] & 2);
|
|
}
|
|
|
|
#ifdef USE_VOL_RAMP
|
|
if (p->rampStyle > 0)
|
|
{
|
|
p->voice[ch].fader = 0.0f;
|
|
p->voice[ch].faderDest = 1.0f;
|
|
p->voice[ch].faderDelta = (p->voice[ch].faderDest - p->voice[ch].fader) * p->f_samplesPerFrameSharp;
|
|
}
|
|
#endif
|
|
}
|
|
else if (insdat[0] == 2 && adlibChannel < 9)
|
|
{
|
|
p->chn[ch].ac2spd = (8363 * 164) / 249;
|
|
p->chn[ch].avol = (int8_t)(insdat[0x1C]);
|
|
p->chn[ch].aorgvol = p->chn[ch].avol;
|
|
|
|
st3play_AdlibPatch(p, adlibChannel, &insdat[0x10]);
|
|
voiceSetSource(p, ch, NULL, 0, 0, 0, 0, 0, 0);
|
|
voiceSetSamplePosition(p, ch, 0);
|
|
}
|
|
else
|
|
{
|
|
p->chn[ch].lastins = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// continue only if we have an active instrument on this channel
|
|
if (!p->chn[ch].lastins) return;
|
|
|
|
if (p->chn[ch].cmd == ('O' - 64))
|
|
{
|
|
if (!p->chn[ch].info)
|
|
{
|
|
p->chn[ch].astartoffset = p->chn[ch].astartoffset00;
|
|
}
|
|
else
|
|
{
|
|
p->chn[ch].astartoffset = 256 * p->chn[ch].info;
|
|
p->chn[ch].astartoffset00 = p->chn[ch].astartoffset;
|
|
}
|
|
}
|
|
|
|
if (p->chn[ch].note != 255)
|
|
{
|
|
if (p->chn[ch].note == 254)
|
|
{
|
|
p->chn[ch].aspd = 0;
|
|
p->chn[ch].avol = 0;
|
|
p->chn[ch].asldspd = 65535;
|
|
|
|
if (adlibChannel >= 9)
|
|
{
|
|
setspd(p, ch);
|
|
setvol(p, ch, 0, 0);
|
|
}
|
|
|
|
// shutdown channel
|
|
voiceSetSource(p, ch, NULL, 0, 0, 0, 0, 0, 0);
|
|
voiceSetSamplePosition(p, ch, 0);
|
|
|
|
if (adlibChannel < 9)
|
|
st3play_AdlibNoteOff(p, adlibChannel);
|
|
}
|
|
else
|
|
{
|
|
p->chn[ch].lastnote = p->chn[ch].note;
|
|
|
|
if (adlibChannel >= 9)
|
|
{
|
|
if ((p->chn[ch].cmd != ('G' - 64)) && (p->chn[ch].cmd != ('L' - 64)))
|
|
voiceSetSamplePosition(p, ch, p->chn[ch].astartoffset);
|
|
|
|
if ((p->tracker == OPENMPT) || (p->tracker == BEROTRACKER))
|
|
{
|
|
voiceSetPlayBackwards(p, ch, 0);
|
|
if ((p->chn[ch].cmd == ('S' - 64)) && (p->chn[ch].info == 0x9F))
|
|
voiceSetReadPosToEnd(p, ch);
|
|
}
|
|
}
|
|
|
|
if (!p->chn[ch].aorgspd || ((p->chn[ch].cmd != ('G' - 64)) && (p->chn[ch].cmd != ('L' - 64))))
|
|
{
|
|
p->chn[ch].aspd = scalec2spd(p, ch, stnote2herz(p, p->chn[ch].note));
|
|
p->chn[ch].aorgspd = p->chn[ch].aspd;
|
|
p->chn[ch].avibcnt = 0;
|
|
p->chn[ch].apancnt = 0;
|
|
|
|
setspd(p, ch);
|
|
}
|
|
|
|
p->chn[ch].asldspd = scalec2spd(p, ch, stnote2herz(p, p->chn[ch].note));
|
|
|
|
if (adlibChannel < 9)
|
|
{
|
|
st3play_AdlibNoteOff(p, adlibChannel);
|
|
setspd(p, ch);
|
|
st3play_AdlibTouch(p, adlibChannel, NULL, p->chn[ch].avol);
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef USE_VOL_RAMP
|
|
if (p->chn[ch].vol != 255 && (p->rampStyle < 1 || !volassigned))
|
|
#else
|
|
if (p->chn[ch].vol != 255)
|
|
#endif
|
|
{
|
|
if (p->chn[ch].vol <= 64)
|
|
{
|
|
p->chn[ch].avol = p->chn[ch].vol;
|
|
p->chn[ch].aorgvol = p->chn[ch].vol;
|
|
|
|
setvol(p, ch, 0, 0);
|
|
|
|
return;
|
|
}
|
|
|
|
// NON-ST3, but let's handle it no matter what tracker
|
|
if ((p->chn[ch].vol >= 128) && (p->chn[ch].vol <= 192))
|
|
{
|
|
p->chn[ch].surround = 0;
|
|
voiceSetSurround(p, ch, 0);
|
|
|
|
p->chn[ch].apanpos = (p->chn[ch].vol - 128) * 4;
|
|
setpan(p, ch);
|
|
}
|
|
}
|
|
}
|
|
|
|
static inline void donewnote(PLAYER *p, uint8_t ch, int8_t notedelayflag)
|
|
{
|
|
if (notedelayflag)
|
|
{
|
|
p->chn[ch].achannelused = 0x81;
|
|
}
|
|
else
|
|
{
|
|
if (ch > p->lastachannelused)
|
|
{
|
|
p->lastachannelused = ch + 1;
|
|
|
|
// sanity fix
|
|
if (p->lastachannelused > 31) p->lastachannelused = 31;
|
|
}
|
|
|
|
p->chn[ch].achannelused = 0x01;
|
|
|
|
if (p->chn[ch].cmd == ('S' - 64))
|
|
{
|
|
if ((p->chn[ch].info & 0xF0) == 0xD0)
|
|
return;
|
|
}
|
|
}
|
|
|
|
doamiga(p, ch);
|
|
}
|
|
|
|
static inline void donotes(PLAYER *p)
|
|
{
|
|
uint8_t i;
|
|
uint8_t ch;
|
|
|
|
for (i = 0; i < 32; ++i)
|
|
{
|
|
p->chn[i].note = 255;
|
|
p->chn[i].vol = 255;
|
|
p->chn[i].ins = 0;
|
|
p->chn[i].cmd = 0;
|
|
p->chn[i].info = 0;
|
|
}
|
|
|
|
seekpat(p);
|
|
|
|
for (;;)
|
|
{
|
|
ch = getnote(p);
|
|
if (ch == 255) break; // end of row/channels
|
|
|
|
if ((p->mseg[0x40 + ch] & 0x7F) <= (15 + 9))
|
|
donewnote(p, ch, 0);
|
|
}
|
|
}
|
|
|
|
// tick 0 commands
|
|
static inline void docmd1(PLAYER *p)
|
|
{
|
|
uint8_t i;
|
|
|
|
for (i = 0; i < (p->lastachannelused + 1); ++i)
|
|
{
|
|
if (p->chn[i].achannelused)
|
|
{
|
|
if (p->chn[i].info)
|
|
p->chn[i].alastnfo = p->chn[i].info;
|
|
|
|
if (p->chn[i].cmd)
|
|
{
|
|
p->chn[i].achannelused |= 0x80;
|
|
|
|
if (p->chn[i].cmd == ('D' - 64))
|
|
{
|
|
// fix retrig if Dxy
|
|
p->chn[i].atrigcnt = 0;
|
|
|
|
// fix speed if tone port noncomplete
|
|
if (p->chn[i].aspd != p->chn[i].aorgspd)
|
|
{
|
|
p->chn[i].aspd = p->chn[i].aorgspd;
|
|
setspd(p, i);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (p->chn[i].cmd != ('I' - 64))
|
|
{
|
|
p->chn[i].atremor = 0;
|
|
p->chn[i].atreon = 1;
|
|
}
|
|
|
|
if ((p->chn[i].cmd != ('H' - 64)) &&
|
|
(p->chn[i].cmd != ('U' - 64)) &&
|
|
(p->chn[i].cmd != ('K' - 64)) &&
|
|
(p->chn[i].cmd != ('R' - 64)))
|
|
{
|
|
p->chn[i].avibcnt |= 0x80;
|
|
}
|
|
|
|
// NON-ST3
|
|
if ((p->tracker != SCREAM_TRACKER) && (p->tracker != IMAGO_ORPHEUS))
|
|
{
|
|
if (p->chn[i].cmd != ('Y' - 64))
|
|
p->chn[i].apancnt |= 0x80;
|
|
}
|
|
}
|
|
|
|
if (p->chn[i].cmd < 27)
|
|
{
|
|
p->volslidetype = 0;
|
|
soncejmp[p->chn[i].cmd](p, &p->chn[i]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// NON-ST3
|
|
if (p->tracker != SCREAM_TRACKER)
|
|
{
|
|
// recalc pans
|
|
setpan(p, i);
|
|
voiceSetSurround(p, i, p->chn[i].surround);
|
|
}
|
|
|
|
// fix retrig if no command
|
|
p->chn[i].atrigcnt = 0;
|
|
|
|
// fix speed if tone port noncomplete
|
|
if (p->chn[i].aspd != p->chn[i].aorgspd)
|
|
{
|
|
p->chn[i].aspd = p->chn[i].aorgspd;
|
|
setspd(p, i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// tick >0 commands
|
|
static inline void docmd2(PLAYER *p)
|
|
{
|
|
uint8_t i;
|
|
|
|
for (i = 0; i < (p->lastachannelused + 1); ++i)
|
|
{
|
|
if (p->chn[i].achannelused)
|
|
{
|
|
if (p->chn[i].cmd)
|
|
{
|
|
p->chn[i].achannelused |= 0x80;
|
|
|
|
if (p->chn[i].cmd < 27)
|
|
{
|
|
p->volslidetype = 0;
|
|
sotherjmp[p->chn[i].cmd](p, &p->chn[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void dorow(PLAYER *p) // periodically called from mixer
|
|
{
|
|
int8_t offset;
|
|
int8_t bit;
|
|
|
|
p->patmusicrand = (((p->patmusicrand * 0xCDEF) >> 16) + 0x1727) & 0x0000FFFF;
|
|
|
|
if (!p->musiccount)
|
|
{
|
|
if (p->patterndelay)
|
|
{
|
|
p->np_row--;
|
|
docmd1(p);
|
|
p->patterndelay--;
|
|
}
|
|
else
|
|
{
|
|
donotes(p);
|
|
docmd1(p);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
docmd2(p);
|
|
}
|
|
|
|
p->musiccount++;
|
|
if (p->musiccount >= (p->musicmax + p->tickdelay))
|
|
{
|
|
p->tickdelay = 0;
|
|
p->np_row++;
|
|
|
|
if (p->jumptorow != -1)
|
|
{
|
|
p->np_row = p->jumptorow;
|
|
p->jumptorow = -1;
|
|
}
|
|
|
|
// np_row = 0..63, 64 = get new pat
|
|
if ((p->np_row >= 64) || (!p->patloopcount && p->breakpat))
|
|
{
|
|
if (p->breakpat == 255)
|
|
{
|
|
p->breakpat = 0;
|
|
p->Playing = 0;
|
|
|
|
return;
|
|
}
|
|
|
|
p->breakpat = 0;
|
|
p->np_row = neworder(p); // if breakpat, np_row = break row
|
|
}
|
|
|
|
// x_ used for info retrieval
|
|
p->x_np_ord = p->np_ord;
|
|
p->x_np_row = p->np_row;
|
|
p->x_np_pat = p->np_pat;
|
|
|
|
if (p->np_row == 0)
|
|
{
|
|
offset = (p->np_ord - 1) / 8;
|
|
bit = 1 << ((p->np_ord - 1) % 8);
|
|
|
|
if (p->playedOrder[offset] & bit)
|
|
{
|
|
p->loopCount++;
|
|
memset(p->playedOrder, 0, sizeof (p->playedOrder));
|
|
}
|
|
|
|
p->playedOrder[offset] |= bit;
|
|
}
|
|
|
|
p->musiccount = 0;
|
|
}
|
|
}
|
|
|
|
static inline int8_t get_adpcm_sample(const int8_t *sampleDictionary, const uint8_t *sampleData, int32_t samplePosition, int8_t *lastDelta)
|
|
{
|
|
uint8_t byte;
|
|
|
|
byte = sampleData[samplePosition / 2];
|
|
byte = (samplePosition & 1) ? (byte >> 4) : (byte & 0x0F);
|
|
|
|
return (*(lastDelta) += sampleDictionary[byte]);
|
|
}
|
|
|
|
static inline void decode_adpcm(const uint8_t *sampleData, int8_t *decodedSampleData, int32_t sampleLength)
|
|
{
|
|
int32_t i;
|
|
int8_t lastDelta;
|
|
int8_t sample;
|
|
const int8_t *sampleDictionary;
|
|
|
|
sampleDictionary = (const int8_t *)(sampleData);
|
|
sampleData += 16;
|
|
|
|
lastDelta = 0;
|
|
for (i = 0; i < sampleLength; ++i)
|
|
{
|
|
sample = get_adpcm_sample(sampleDictionary, sampleData, i, &lastDelta);
|
|
|
|
*decodedSampleData++ = sample;
|
|
}
|
|
}
|
|
|
|
static void loadheaderparms(PLAYER *p)
|
|
{
|
|
uint8_t *insdat;
|
|
uint16_t insnum;
|
|
uint32_t i;
|
|
uint32_t j;
|
|
uint32_t inslen;
|
|
uint32_t insoff;
|
|
|
|
// set to init defaults first
|
|
p->oldstvib = 0;
|
|
setspeed(p, 6);
|
|
settempo(p, 125);
|
|
p->aspdmin = 64;
|
|
p->aspdmax = 32767;
|
|
p->globalvol = 64;
|
|
p->amigalimits = 0;
|
|
p->fastvolslide = 0;
|
|
setStereoMode(p, 0);
|
|
setMasterVolume(p, 48);
|
|
|
|
p->tracker = p->mseg[0x29] >> 4;
|
|
|
|
if (p->mseg[0x33])
|
|
{
|
|
if (p->mseg[0x33] & 0x80)
|
|
setStereoMode(p, 1);
|
|
|
|
if (p->mseg[0x33] & 0x7F)
|
|
{
|
|
if ((p->mseg[0x33] & 0x7F) < 16)
|
|
setMasterVolume(p, 16);
|
|
else
|
|
setMasterVolume(p, p->mseg[0x33] & 0x7F);
|
|
}
|
|
}
|
|
|
|
if (p->mseg[0x32])
|
|
settempo(p, p->mseg[0x32]);
|
|
|
|
if (p->mseg[0x31] != 255)
|
|
setspeed(p, p->mseg[0x31]);
|
|
|
|
if (p->mseg[0x30] != 255)
|
|
{
|
|
p->globalvol = p->mseg[0x30];
|
|
if (p->globalvol > 64)
|
|
p->globalvol = 64;
|
|
}
|
|
|
|
if (p->mseg[0x26] != 255)
|
|
{
|
|
p->amigalimits = p->mseg[0x26] & 0x10;
|
|
p->fastvolslide = p->mseg[0x26] & 0x40;
|
|
|
|
if (p->amigalimits)
|
|
{
|
|
p->aspdmax = 1712 * 2;
|
|
p->aspdmin = 907 / 2;
|
|
}
|
|
}
|
|
|
|
// force fastvolslide if ST3.00
|
|
if (get_le16(&p->mseg[0x28]) == 0x1300)
|
|
p->fastvolslide = 1;
|
|
|
|
p->oldstvib = p->mseg[0x26] & 0x01;
|
|
|
|
if (*((uint16_t *)(&p->mseg[0x2A])))
|
|
{
|
|
// we have unsigned samples, convert to signed
|
|
|
|
insnum = get_le16(&p->mseg[0x22]);
|
|
for (i = 0; i < insnum; ++i)
|
|
{
|
|
insdat = &p->mseg[get_le16(&p->mseg[p->instrumentadd + (i * 2)]) * 16];
|
|
insoff = ((insdat[0x0D] << 16) | (insdat[0x0F] << 8) | insdat[0x0E]) * 16;
|
|
|
|
if (insoff && (insdat[0] == 1)) // PCM
|
|
{
|
|
if (insoff > p->mseg_len)
|
|
insoff = p->mseg_len;
|
|
|
|
inslen = get_le32(&insdat[0x10]);
|
|
|
|
if (insdat[0x1E] == 4) // modplug packed
|
|
{
|
|
if (p->adpcmSamples == NULL)
|
|
p->adpcmSamples = (int8_t **)(calloc(sizeof(int8_t *), insnum));
|
|
|
|
if (p->adpcmSamples)
|
|
{
|
|
p->adpcmSamples[i] = (int8_t *)(calloc(1, inslen));
|
|
if (p->adpcmSamples[i])
|
|
{
|
|
if ((insoff + (16 + (inslen + 1) / 2)) > p->mseg_len)
|
|
inslen = ((p->mseg_len - insoff) - 16) * 2;
|
|
|
|
if (inslen >= 1)
|
|
decode_adpcm(&p->mseg[insoff], p->adpcmSamples[i], inslen);
|
|
}
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
if (insdat[0x1F] & 2) inslen *= 2; // stereo
|
|
|
|
if (insdat[0x1F] & 4)
|
|
{
|
|
// 16-bit
|
|
if ((insoff + (inslen * 2)) > p->mseg_len)
|
|
inslen = (p->mseg_len - insoff) / 2;
|
|
|
|
for (j = 0; j < inslen; ++j)
|
|
set_le16(&p->mseg[insoff + (j * 2)], get_le16(&p->mseg[insoff + (j * 2)]) - 0x8000);
|
|
}
|
|
else
|
|
{
|
|
// 8-bit
|
|
if ((insoff + inslen) > p->mseg_len)
|
|
inslen = p->mseg_len - insoff;
|
|
|
|
for (j = 0; j < inslen; ++j)
|
|
p->mseg[insoff + j] = p->mseg[insoff + j] - 0x80;
|
|
}
|
|
}
|
|
else if (insdat[0] == 2) // AdLib melodic
|
|
{
|
|
if (!p->fmChip && st3play_AdlibInit(p) < 0)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void st3play_PlaySong(void *_p, int16_t startOrder)
|
|
{
|
|
uint8_t i;
|
|
int8_t offset;
|
|
int8_t bit;
|
|
uint8_t dat;
|
|
int16_t pan;
|
|
PLAYER *p;
|
|
|
|
p = (PLAYER *)(_p);
|
|
if (!p->ModuleLoaded) return;
|
|
|
|
memset(p->voice, 0, sizeof (p->voice));
|
|
|
|
loadheaderparms(p);
|
|
|
|
p->np_ord = startOrder;
|
|
neworder(p);
|
|
p->x_np_ord = p->np_ord;
|
|
|
|
// setup pans
|
|
for (i = 0; i < 32; ++i)
|
|
{
|
|
pan = 128;
|
|
if (p->mseg[0x35] == 0xFC) // non-default pannings follow
|
|
{
|
|
dat = p->mseg[(p->patternadd + (get_le16(&p->mseg[0x24]) * 2)) + i];
|
|
if (dat & 0x20)
|
|
pan = (dat & 0x0F) * 16;
|
|
}
|
|
|
|
p->chn[i].apanpos = p->stereomode ? pan : 128;
|
|
voiceSetPanning(p, i, pan);
|
|
}
|
|
|
|
p->Playing = 1;
|
|
setSamplesPerFrame(p, ((p->outputFreq * 5) / 2 / p->tempo));
|
|
p->isMixing = 1;
|
|
|
|
p->loopCount = 0;
|
|
memset(p->playedOrder, 0, sizeof (p->playedOrder));
|
|
|
|
offset = startOrder / 8;
|
|
bit = 1 << (startOrder % 8);
|
|
|
|
p->playedOrder[offset] = bit;
|
|
}
|
|
|
|
int8_t st3play_LoadModule(void *_p, const uint8_t *module, size_t size)
|
|
{
|
|
char SCRM[4];
|
|
uint16_t i;
|
|
PLAYER *p;
|
|
|
|
p = (PLAYER *)(_p);
|
|
if (p->ModuleLoaded)
|
|
FreeSong(p);
|
|
|
|
p->ModuleLoaded = 0;
|
|
|
|
if (size < 0x30)
|
|
{
|
|
return (0);
|
|
}
|
|
|
|
memcpy(SCRM, module + 0x2C, 4);
|
|
if (memcmp(SCRM, "SCRM", 4))
|
|
{
|
|
return (0);
|
|
}
|
|
|
|
if (p->mseg)
|
|
{
|
|
free(p->mseg);
|
|
p->mseg = NULL;
|
|
}
|
|
|
|
p->mseg = (uint8_t *)(malloc(size));
|
|
if (p->mseg == NULL)
|
|
{
|
|
return (0);
|
|
}
|
|
|
|
memcpy(p->mseg, module, size);
|
|
|
|
p->mseg_len = (uint32_t)(size);
|
|
|
|
p->instrumentadd = 0x60 + p->mseg[0x20];
|
|
p->patternadd = p->instrumentadd + (p->mseg[0x22] * 2);
|
|
p->tickdelay = 0;
|
|
p->musiccount = 0;
|
|
p->patterndelay = 0;
|
|
p->patloopstart = 0;
|
|
p->patloopcount = 0;
|
|
p->np_row = 0;
|
|
p->np_pat = 0;
|
|
p->x_np_row = 0;
|
|
p->x_np_pat = 0;
|
|
p->x_np_ord = 0;
|
|
p->startrow = 0;
|
|
p->np_patseg = 0;
|
|
p->np_patoff = 0;
|
|
p->breakpat = 0;
|
|
p->patmusicrand = 0;
|
|
p->volslidetype = 0;
|
|
p->jumptorow = 0;
|
|
|
|
// zero all channel vars
|
|
memset(p->chn, 0, sizeof (p->chn));
|
|
|
|
p->numChannels = 0;
|
|
for (i = 0; i < 32; ++i)
|
|
{
|
|
if (!(p->mseg[0x40 + i] & 0x80))
|
|
p->numChannels++;
|
|
|
|
p->chn[i].channelnum = (int8_t)(i);
|
|
p->chn[i].achannelused = 0x80;
|
|
p->chn[i].chanvol = 0x40;
|
|
}
|
|
|
|
p->lastachannelused = 1;
|
|
|
|
// count *real* amounts of orders
|
|
i = get_le16(&p->mseg[0x20]);
|
|
while (i)
|
|
{
|
|
if (p->mseg[0x60 + (i - 1)] != 255)
|
|
break;
|
|
|
|
i--;
|
|
}
|
|
set_le16(&p->mseg[0x20], i);
|
|
|
|
p->ModuleLoaded = 1;
|
|
|
|
return (1);
|
|
}
|
|
|
|
|
|
// EFFECTS
|
|
|
|
// non-used effects
|
|
static void s_ret(PLAYER *p, chn_t *ch) { (void)(p); (void)(ch); }
|
|
// ----------------
|
|
|
|
static void s_setgliss(PLAYER *p, chn_t *ch)
|
|
{
|
|
(void)(p);
|
|
ch->aglis = ch->info & 0x0F;
|
|
}
|
|
|
|
static void s_setfinetune(PLAYER *p, chn_t *ch)
|
|
{
|
|
// this function does nothing in ST3 and many other trackers
|
|
if ((p->tracker == OPENMPT) || (p->tracker == BEROTRACKER))
|
|
ch->ac2spd = xfinetune_amiga[ch->info & 0x0F];
|
|
}
|
|
|
|
static void s_setvibwave(PLAYER *p, chn_t *ch)
|
|
{
|
|
(void)(p);
|
|
ch->avibtretype = (ch->avibtretype & 0xF0) | ((ch->info << 1) & 0x0F);
|
|
}
|
|
|
|
static void s_settrewave(PLAYER *p, chn_t *ch)
|
|
{
|
|
(void)(p);
|
|
ch->avibtretype = ((ch->info << 5) & 0xF0) | (ch->avibtretype & 0x0F);
|
|
}
|
|
|
|
static void s_setpanwave(PLAYER *p, chn_t *ch) // NON-ST3
|
|
{
|
|
if ((p->tracker != SCREAM_TRACKER) && (p->tracker != IMAGO_ORPHEUS))
|
|
ch->apantype = ch->info & 0x0F;
|
|
}
|
|
|
|
static void s_tickdelay(PLAYER *p, chn_t *ch) // NON-ST3
|
|
{
|
|
if ( (p->tracker == OPENMPT)
|
|
|| (p->tracker == BEROTRACKER)
|
|
|| (p->tracker == IMPULSE_TRACKER)
|
|
|| (p->tracker == SCHISM_TRACKER)
|
|
)
|
|
{
|
|
p->tickdelay += (ch->info & 0x0F);
|
|
}
|
|
}
|
|
|
|
static void s_setpanpos(PLAYER *p, chn_t *ch)
|
|
{
|
|
ch->surround = 0;
|
|
voiceSetSurround(p, ch->channelnum, 0);
|
|
|
|
ch->apanpos = (ch->info & 0x0F) * 16;
|
|
setpan(p, ch->channelnum);
|
|
}
|
|
|
|
static void s_sndcntrl(PLAYER *p, chn_t *ch) // NON-ST3
|
|
{
|
|
if ((ch->info & 0x0F) == 0x00)
|
|
{
|
|
if ((p->tracker != SCREAM_TRACKER) && (p->tracker != IMAGO_ORPHEUS))
|
|
{
|
|
ch->surround = 0;
|
|
voiceSetSurround(p, ch->channelnum, 0);
|
|
}
|
|
}
|
|
else if ((ch->info & 0x0F) == 0x01)
|
|
{
|
|
if ((p->tracker != SCREAM_TRACKER) && (p->tracker != IMAGO_ORPHEUS))
|
|
{
|
|
ch->surround = 1;
|
|
voiceSetSurround(p, ch->channelnum, 1);
|
|
}
|
|
}
|
|
else if ((ch->info & 0x0F) == 0x0E)
|
|
{
|
|
if ((p->tracker == OPENMPT) || (p->tracker == BEROTRACKER))
|
|
voiceSetPlayBackwards(p, ch->channelnum, 0);
|
|
}
|
|
else if ((ch->info & 0x0F) == 0x0F)
|
|
{
|
|
if ((p->tracker == OPENMPT) || (p->tracker == BEROTRACKER))
|
|
voiceSetPlayBackwards(p, ch->channelnum, 1);
|
|
}
|
|
}
|
|
|
|
static void s_patloop(PLAYER *p, chn_t *ch)
|
|
{
|
|
int8_t offset;
|
|
int8_t bit;
|
|
|
|
if (!(ch->info & 0x0F))
|
|
{
|
|
p->patloopstart = p->np_row;
|
|
return;
|
|
}
|
|
|
|
if (!p->patloopcount)
|
|
{
|
|
p->patloopcount = (ch->info & 0x0F) + 1;
|
|
|
|
if (p->patloopstart == -1)
|
|
p->patloopstart = 0; // default loopstart
|
|
}
|
|
|
|
if (p->patloopcount > 1)
|
|
{
|
|
p->patloopcount--;
|
|
p->jumptorow = p->patloopstart;
|
|
p->np_patoff = -1; // force reseek
|
|
|
|
if (p->patloopstart == 0)
|
|
{
|
|
offset = (p->np_ord - 1) / 8;
|
|
bit = 1 << ((p->np_ord - 1) % 8);
|
|
|
|
p->playedOrder[offset] &= ~bit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
p->patloopcount = 0;
|
|
p->patloopstart = p->np_row + 1;
|
|
}
|
|
}
|
|
|
|
static void s_notecut(PLAYER *p, chn_t *ch)
|
|
{
|
|
(void)(p);
|
|
ch->anotecutcnt = ch->info & 0x0F;
|
|
}
|
|
|
|
static void s_notecutb(PLAYER *p, chn_t *ch)
|
|
{
|
|
uint8_t adlibChannel;
|
|
|
|
if (ch->anotecutcnt)
|
|
{
|
|
ch->anotecutcnt--;
|
|
if (!ch->anotecutcnt)
|
|
{
|
|
adlibChannel = (p->mseg[0x40 + ch->channelnum] & 0x7F) - 16;
|
|
|
|
voiceSetSamplingFrequency(p, ch->channelnum, 0); // cut note
|
|
|
|
if (adlibChannel < 9)
|
|
st3play_AdlibNoteOff(p, adlibChannel);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void s_notedelay(PLAYER *p, chn_t *ch)
|
|
{
|
|
(void)(p);
|
|
ch->anotedelaycnt = ch->info & 0x0F;
|
|
}
|
|
|
|
static void s_notedelayb(PLAYER *p, chn_t *ch)
|
|
{
|
|
if (ch->anotedelaycnt)
|
|
{
|
|
ch->anotedelaycnt--;
|
|
if (!ch->anotedelaycnt)
|
|
donewnote(p, ch->channelnum, 1); // 1 = notedelay end
|
|
}
|
|
}
|
|
|
|
static void s_patterdelay(PLAYER *p, chn_t *ch)
|
|
{
|
|
if (p->patterndelay == 0)
|
|
p->patterndelay = ch->info & 0x0F;
|
|
}
|
|
|
|
static void s_setspeed(PLAYER *p, chn_t *ch)
|
|
{
|
|
setspeed(p, ch->info);
|
|
}
|
|
|
|
static void s_jmpto(PLAYER *p, chn_t *ch)
|
|
{
|
|
if (ch->info != 0xFF)
|
|
{
|
|
p->breakpat = 1;
|
|
p->np_ord = ch->info;
|
|
}
|
|
else
|
|
{
|
|
p->breakpat = 255;
|
|
}
|
|
}
|
|
|
|
static void s_break(PLAYER *p, chn_t *ch)
|
|
{
|
|
p->startrow = ((ch->info >> 4) * 10) + (ch->info & 0x0F);
|
|
p->breakpat = 1;
|
|
}
|
|
|
|
static void s_volslide(PLAYER *p, chn_t *ch)
|
|
{
|
|
uint8_t infohi;
|
|
uint8_t infolo;
|
|
|
|
getlastnfo(p, ch);
|
|
|
|
infohi = ch->info >> 4;
|
|
infolo = ch->info & 0x0F;
|
|
|
|
if (infolo == 0x0F)
|
|
{
|
|
if (!infohi)
|
|
ch->avol -= infolo;
|
|
else if (!p->musiccount)
|
|
ch->avol += infohi;
|
|
}
|
|
else if (infohi == 0x0F)
|
|
{
|
|
if (!infolo)
|
|
ch->avol += infohi;
|
|
else if (!p->musiccount)
|
|
ch->avol -= infolo;
|
|
}
|
|
else if (p->fastvolslide || p->musiccount)
|
|
{
|
|
if (!infolo)
|
|
ch->avol += infohi;
|
|
else
|
|
ch->avol -= infolo;
|
|
}
|
|
else
|
|
{
|
|
return; // illegal slide
|
|
}
|
|
|
|
if (ch->avol < 0) ch->avol = 0;
|
|
else if (ch->avol > 63) ch->avol = 63;
|
|
|
|
setvol(p, ch->channelnum, 0, 0);
|
|
|
|
if (p->volslidetype == 1) s_vibrato(p, ch);
|
|
else if (p->volslidetype == 2) s_toneslide(p, ch);
|
|
}
|
|
|
|
static void s_slidedown(PLAYER *p, chn_t *ch)
|
|
{
|
|
if (ch->aorgspd)
|
|
{
|
|
getlastnfo(p, ch);
|
|
|
|
if (p->musiccount)
|
|
{
|
|
if (ch->info >= 0xE0) return; // no fine slides here
|
|
|
|
ch->aspd += (ch->info * 4);
|
|
if (ch->aspd > 32767) ch->aspd = 32767;
|
|
}
|
|
else
|
|
{
|
|
if (ch->info <= 0xE0) return; // only fine slides here
|
|
|
|
if (ch->info <= 0xF0)
|
|
{
|
|
ch->aspd += (ch->info & 0x0F);
|
|
if (ch->aspd > 32767) ch->aspd = 32767;
|
|
}
|
|
else
|
|
{
|
|
ch->aspd += ((ch->info & 0x0F) * 4);
|
|
if (ch->aspd > 32767) ch->aspd = 32767;
|
|
}
|
|
}
|
|
|
|
ch->aorgspd = ch->aspd;
|
|
setspd(p, ch->channelnum);
|
|
}
|
|
}
|
|
|
|
static void s_slideup(PLAYER *p, chn_t *ch)
|
|
{
|
|
if (ch->aorgspd)
|
|
{
|
|
getlastnfo(p, ch);
|
|
|
|
if (p->musiccount)
|
|
{
|
|
if (ch->info >= 0xE0) return; // no fine slides here
|
|
|
|
ch->aspd -= (ch->info * 4);
|
|
if (ch->aspd < 0) ch->aspd = 0;
|
|
}
|
|
else
|
|
{
|
|
if (ch->info <= 0xE0) return; // only fine slides here
|
|
|
|
if (ch->info <= 0xF0)
|
|
{
|
|
ch->aspd -= (ch->info & 0x0F);
|
|
if (ch->aspd < 0) ch->aspd = 0;
|
|
}
|
|
else
|
|
{
|
|
ch->aspd -= ((ch->info & 0x0F) * 4);
|
|
if (ch->aspd < 0) ch->aspd = 0;
|
|
}
|
|
}
|
|
|
|
ch->aorgspd = ch->aspd;
|
|
setspd(p, ch->channelnum);
|
|
}
|
|
}
|
|
|
|
static void s_toneslide(PLAYER *p, chn_t *ch)
|
|
{
|
|
if (p->volslidetype == 2) // we came from a Lxy (toneslide+volslide)
|
|
{
|
|
ch->info = ch->alasteff1;
|
|
}
|
|
else
|
|
{
|
|
if (!ch->aorgspd)
|
|
{
|
|
if (!ch->asldspd)
|
|
return;
|
|
|
|
ch->aorgspd = ch->asldspd;
|
|
ch->aspd = ch->asldspd;
|
|
}
|
|
|
|
if (!ch->info)
|
|
ch->info = ch->alasteff1;
|
|
else
|
|
ch->alasteff1 = ch->info;
|
|
}
|
|
|
|
if (ch->aorgspd != ch->asldspd)
|
|
{
|
|
if (ch->aorgspd < ch->asldspd)
|
|
{
|
|
ch->aorgspd += (ch->info * 4);
|
|
if (ch->aorgspd > ch->asldspd)
|
|
ch->aorgspd = ch->asldspd;
|
|
}
|
|
else
|
|
{
|
|
ch->aorgspd -= (ch->info * 4);
|
|
if (ch->aorgspd < ch->asldspd)
|
|
ch->aorgspd = ch->asldspd;
|
|
}
|
|
|
|
if (ch->aglis)
|
|
ch->aspd = roundspd(p, ch->channelnum, ch->aorgspd);
|
|
else
|
|
ch->aspd = ch->aorgspd;
|
|
|
|
setspd(p, ch->channelnum);
|
|
}
|
|
}
|
|
|
|
static void s_vibrato(PLAYER *p, chn_t *ch)
|
|
{
|
|
int8_t type;
|
|
int16_t cnt;
|
|
int32_t dat;
|
|
|
|
if (p->volslidetype == 1) // we came from a Kxy (vibrato+volslide)
|
|
{
|
|
ch->info = ch->alasteff;
|
|
}
|
|
else
|
|
{
|
|
if (!ch->info)
|
|
ch->info = ch->alasteff;
|
|
|
|
if (!(ch->info & 0xF0))
|
|
ch->info = (ch->alasteff & 0xF0) | (ch->info & 0x0F);
|
|
|
|
ch->alasteff = ch->info;
|
|
}
|
|
|
|
if (ch->aorgspd)
|
|
{
|
|
cnt = ch->avibcnt;
|
|
type = (ch->avibtretype & 0x0E) >> 1;
|
|
|
|
// sine
|
|
if ((type == 0) || (type == 4))
|
|
{
|
|
if (type == 4)
|
|
{
|
|
cnt &= 0x7F;
|
|
}
|
|
else
|
|
{
|
|
if (cnt & 0x80) cnt = 0;
|
|
}
|
|
|
|
dat = vibsin[cnt / 2];
|
|
}
|
|
|
|
// ramp
|
|
else if ((type == 1) || (type == 5))
|
|
{
|
|
if (type == 5)
|
|
{
|
|
cnt &= 0x7F;
|
|
}
|
|
else
|
|
{
|
|
if (cnt & 0x80) cnt = 0;
|
|
}
|
|
|
|
dat = vibramp[cnt / 2];
|
|
}
|
|
|
|
// square
|
|
else if ((type == 2) || (type == 6))
|
|
{
|
|
if (type == 6)
|
|
{
|
|
cnt &= 0x7F;
|
|
}
|
|
else
|
|
{
|
|
if (cnt & 0x80) cnt = 0;
|
|
}
|
|
|
|
dat = vibsqu[cnt / 2];
|
|
}
|
|
|
|
// random
|
|
else if ((type == 3) || (type == 7))
|
|
{
|
|
if (type == 7)
|
|
{
|
|
cnt &= 0x7F;
|
|
}
|
|
else
|
|
{
|
|
if (cnt & 0x80) cnt = 0;
|
|
}
|
|
|
|
dat = vibsin[cnt / 2];
|
|
cnt += (p->patmusicrand & 0x1E);
|
|
}
|
|
|
|
// warning C4701: potentially uninitialized
|
|
else
|
|
{
|
|
dat = 0;
|
|
}
|
|
|
|
if (p->oldstvib)
|
|
dat = ((dat * (ch->info & 0x0F)) >> 4) + ch->aorgspd;
|
|
else
|
|
dat = ((dat * (ch->info & 0x0F)) >> 5) + ch->aorgspd;
|
|
|
|
ch->aspd = dat;
|
|
setspd(p, ch->channelnum);
|
|
|
|
ch->avibcnt = (cnt + ((ch->info >> 4) * 2)) & 0x7E;
|
|
}
|
|
}
|
|
|
|
static void s_tremor(PLAYER *p, chn_t *ch)
|
|
{
|
|
getlastnfo(p, ch);
|
|
|
|
if (ch->atremor)
|
|
{
|
|
ch->atremor--;
|
|
return;
|
|
}
|
|
|
|
if (ch->atreon)
|
|
{
|
|
ch->atreon = 0;
|
|
|
|
ch->avol = 0;
|
|
setvol(p, ch->channelnum, 0, 0);
|
|
|
|
ch->atremor = ch->info & 0x0F;
|
|
}
|
|
else
|
|
{
|
|
ch->atreon = 1;
|
|
|
|
ch->avol = ch->aorgvol;
|
|
setvol(p, ch->channelnum, 0, 0);
|
|
|
|
ch->atremor = ch->info >> 4;
|
|
}
|
|
}
|
|
|
|
static void s_arp(PLAYER *p, chn_t *ch)
|
|
{
|
|
int8_t note;
|
|
int8_t octa;
|
|
int8_t noteadd;
|
|
uint8_t tick;
|
|
|
|
getlastnfo(p, ch);
|
|
|
|
tick = p->musiccount % 3;
|
|
|
|
if (tick == 1) noteadd = ch->info >> 4;
|
|
else if (tick == 2) noteadd = ch->info & 0x0F;
|
|
else noteadd = 0;
|
|
|
|
// check for octave overflow
|
|
octa = ch->lastnote & 0xF0;
|
|
note = (ch->lastnote & 0x0F) + noteadd;
|
|
|
|
while (note >= 12)
|
|
{
|
|
note -= 12;
|
|
octa += 16;
|
|
}
|
|
|
|
ch->aspd = scalec2spd(p, ch->channelnum, stnote2herz(p, octa | note));
|
|
setspd(p, ch->channelnum);
|
|
}
|
|
|
|
static void s_chanvol(PLAYER *p, chn_t *ch) // NON-ST3
|
|
{
|
|
if ((p->tracker != SCREAM_TRACKER) && (p->tracker != IMAGO_ORPHEUS))
|
|
{
|
|
if (ch->info <= 0x40)
|
|
ch->chanvol = ch->info;
|
|
|
|
setvol(p, ch->channelnum, 0, 0);
|
|
}
|
|
}
|
|
|
|
static void s_chanvolslide(PLAYER *p, chn_t *ch) // NON-ST3
|
|
{
|
|
uint8_t infohi;
|
|
uint8_t infolo;
|
|
|
|
if ((p->tracker != SCREAM_TRACKER) && (p->tracker != IMAGO_ORPHEUS))
|
|
{
|
|
if (ch->info)
|
|
ch->nxymem = ch->info;
|
|
else
|
|
ch->info = ch->nxymem;
|
|
|
|
infohi = ch->nxymem >> 4;
|
|
infolo = ch->nxymem & 0x0F;
|
|
|
|
if (infolo == 0x0F)
|
|
{
|
|
if (!infohi)
|
|
ch->chanvol -= infolo;
|
|
else if (!p->musiccount)
|
|
ch->chanvol += infohi;
|
|
}
|
|
else if (infohi == 0x0F)
|
|
{
|
|
if (!infolo)
|
|
ch->chanvol += infohi;
|
|
else if (!p->musiccount)
|
|
ch->chanvol -= infolo;
|
|
}
|
|
else if (p->musiccount) // don't rely on fastvolslide flag here
|
|
{
|
|
if (!infolo)
|
|
ch->chanvol += infohi;
|
|
else
|
|
ch->chanvol -= infolo;
|
|
}
|
|
else
|
|
{
|
|
return; // illegal slide
|
|
}
|
|
|
|
if (ch->chanvol < 0) ch->chanvol = 0;
|
|
else if (ch->chanvol > 64) ch->chanvol = 64;
|
|
|
|
setvol(p, ch->channelnum, 0, 0);
|
|
}
|
|
}
|
|
|
|
static void s_vibvol(PLAYER *p, chn_t *ch)
|
|
{
|
|
p->volslidetype = 1;
|
|
s_volslide(p, ch);
|
|
}
|
|
|
|
static void s_tonevol(PLAYER *p, chn_t *ch)
|
|
{
|
|
p->volslidetype = 2;
|
|
s_volslide(p, ch);
|
|
}
|
|
|
|
static void s_panslide(PLAYER *p, chn_t *ch) // NON-ST3
|
|
{
|
|
uint8_t infohi;
|
|
uint8_t infolo;
|
|
|
|
if ((p->tracker != SCREAM_TRACKER) && (p->tracker != IMAGO_ORPHEUS))
|
|
{
|
|
if (ch->info)
|
|
ch->pxymem = ch->info;
|
|
else
|
|
ch->info = ch->pxymem;
|
|
|
|
infohi = ch->pxymem >> 4;
|
|
infolo = ch->pxymem & 0x0F;
|
|
|
|
if (infolo == 0x0F)
|
|
{
|
|
if (!infohi)
|
|
ch->apanpos += (infolo * 4);
|
|
else if (!p->musiccount)
|
|
ch->apanpos -= (infohi * 4);
|
|
}
|
|
else if (infohi == 0x0F)
|
|
{
|
|
if (!infolo)
|
|
ch->apanpos -= (infohi * 4);
|
|
else if (!p->musiccount)
|
|
ch->apanpos += (infolo * 4);
|
|
}
|
|
else if (p->musiccount) // don't rely on fastvolslide flag here
|
|
{
|
|
if (!infolo)
|
|
ch->apanpos -= (infohi * 4);
|
|
else
|
|
ch->apanpos += (infolo * 4);
|
|
}
|
|
else
|
|
{
|
|
return; // illegal slide
|
|
}
|
|
|
|
if (ch->apanpos < 0) ch->apanpos = 0;
|
|
else if (ch->apanpos > 256) ch->apanpos = 256;
|
|
|
|
setpan(p, ch->channelnum);
|
|
}
|
|
}
|
|
|
|
static void s_retrig(PLAYER *p, chn_t *ch)
|
|
{
|
|
uint8_t infohi;
|
|
|
|
getlastnfo(p, ch);
|
|
infohi = ch->info >> 4;
|
|
|
|
if (!(ch->info & 0x0F) || (ch->atrigcnt < (ch->info & 0x0F)))
|
|
{
|
|
ch->atrigcnt++;
|
|
return;
|
|
}
|
|
|
|
ch->atrigcnt = 0;
|
|
|
|
voiceSetPlayBackwards(p, ch->channelnum, 0);
|
|
voiceSetSamplePosition(p, ch->channelnum, 0);
|
|
|
|
if (!retrigvoladd[16 + infohi])
|
|
ch->avol += retrigvoladd[infohi];
|
|
else
|
|
ch->avol = (int8_t)((ch->avol * retrigvoladd[16 + infohi]) / 16);
|
|
|
|
if (ch->avol > 63) ch->avol = 63;
|
|
else if (ch->avol < 0) ch->avol = 0;
|
|
|
|
setvol(p, ch->channelnum, 0, 0);
|
|
|
|
ch->atrigcnt++; // probably a mistake? Keep it anyways.
|
|
}
|
|
|
|
static void s_tremolo(PLAYER *p, chn_t *ch)
|
|
{
|
|
int8_t type;
|
|
int16_t cnt;
|
|
int16_t dat;
|
|
|
|
getlastnfo(p, ch);
|
|
|
|
if (!(ch->info & 0xF0))
|
|
ch->info = (ch->alastnfo & 0xF0) | (ch->info & 0x0F);
|
|
|
|
ch->alastnfo = ch->info;
|
|
|
|
if (ch->aorgvol)
|
|
{
|
|
cnt = ch->avibcnt;
|
|
type = ch->avibtretype >> 5;
|
|
|
|
// sine
|
|
if ((type == 0) || (type == 4))
|
|
{
|
|
if (type == 4)
|
|
{
|
|
cnt &= 0x7F;
|
|
}
|
|
else
|
|
{
|
|
if (cnt & 0x80) cnt = 0;
|
|
}
|
|
|
|
dat = vibsin[cnt / 2];
|
|
}
|
|
|
|
// ramp
|
|
else if ((type == 1) || (type == 5))
|
|
{
|
|
if (type == 5)
|
|
{
|
|
cnt &= 0x7F;
|
|
}
|
|
else
|
|
{
|
|
if (cnt & 0x80) cnt = 0;
|
|
}
|
|
|
|
dat = vibramp[cnt / 2];
|
|
}
|
|
|
|
// square
|
|
else if ((type == 2) || (type == 6))
|
|
{
|
|
if (type == 6)
|
|
{
|
|
cnt &= 0x7F;
|
|
}
|
|
else
|
|
{
|
|
if (cnt & 0x80) cnt = 0;
|
|
}
|
|
|
|
dat = vibsqu[cnt / 2];
|
|
}
|
|
|
|
// random
|
|
else if ((type == 3) || (type == 7))
|
|
{
|
|
if (type == 7)
|
|
{
|
|
cnt &= 0x7F;
|
|
}
|
|
else
|
|
{
|
|
if (cnt & 0x80) cnt = 0;
|
|
}
|
|
|
|
dat = vibsin[cnt / 2];
|
|
cnt += (p->patmusicrand & 0x1E);
|
|
}
|
|
|
|
// warning C4701: potentially uninitialized
|
|
else
|
|
{
|
|
dat = 0;
|
|
}
|
|
|
|
dat = ((dat * (ch->info & 0x0F)) >> 7) + ch->aorgvol;
|
|
|
|
if (dat > 63) dat = 63;
|
|
else if (dat < 0) dat = 0;
|
|
|
|
ch->avol = (int8_t)(dat);
|
|
setvol(p, ch->channelnum, 0, 0);
|
|
|
|
ch->avibcnt = (cnt + ((ch->info >> 4) * 2)) & 0x7E;
|
|
}
|
|
}
|
|
|
|
static void s_scommand1(PLAYER *p, chn_t *ch)
|
|
{
|
|
getlastnfo(p, ch);
|
|
ssoncejmp[ch->info >> 4](p, ch);
|
|
}
|
|
|
|
static void s_scommand2(PLAYER *p, chn_t *ch)
|
|
{
|
|
getlastnfo(p, ch);
|
|
ssotherjmp[ch->info >> 4](p, ch);
|
|
}
|
|
|
|
static void s_settempo(PLAYER *p, chn_t *ch)
|
|
{
|
|
if (!p->musiccount && (ch->info >= 0x20))
|
|
p->tempo = ch->info;
|
|
|
|
// NON-ST3 tempo slide
|
|
if ((p->tracker != SCREAM_TRACKER) && (p->tracker != IMAGO_ORPHEUS))
|
|
{
|
|
if (!p->musiccount)
|
|
{
|
|
if (!ch->info)
|
|
ch->info = ch->txxmem;
|
|
else
|
|
ch->txxmem = ch->info;
|
|
}
|
|
else if (p->musiccount)
|
|
{
|
|
if (ch->info <= 0x0F)
|
|
{
|
|
p->tempo -= ch->info;
|
|
if (p->tempo < 32)
|
|
p->tempo = 32;
|
|
}
|
|
else if (ch->info <= 0x1F)
|
|
{
|
|
p->tempo += (ch->info - 0x10);
|
|
if (p->tempo > 255)
|
|
p->tempo = 255;
|
|
}
|
|
}
|
|
}
|
|
// ------------------
|
|
|
|
settempo(p, p->tempo);
|
|
}
|
|
|
|
static void s_finevibrato(PLAYER *p, chn_t *ch)
|
|
{
|
|
int8_t type;
|
|
int16_t cnt;
|
|
int32_t dat;
|
|
|
|
if (!ch->info)
|
|
ch->info = ch->alasteff;
|
|
|
|
if (!(ch->info & 0xF0))
|
|
ch->info = (ch->alasteff & 0xF0) | (ch->info & 0x0F);
|
|
|
|
ch->alasteff = ch->info;
|
|
|
|
if (ch->aorgspd)
|
|
{
|
|
cnt = ch->avibcnt;
|
|
type = (ch->avibtretype & 0x0E) >> 1;
|
|
|
|
// sine
|
|
if ((type == 0) || (type == 4))
|
|
{
|
|
if (type == 4)
|
|
{
|
|
cnt &= 0x7F;
|
|
}
|
|
else
|
|
{
|
|
if (cnt & 0x80) cnt = 0;
|
|
}
|
|
|
|
dat = vibsin[cnt / 2];
|
|
}
|
|
|
|
// ramp
|
|
else if ((type == 1) || (type == 5))
|
|
{
|
|
if (type == 5)
|
|
{
|
|
cnt &= 0x7F;
|
|
}
|
|
else
|
|
{
|
|
if (cnt & 0x80) cnt = 0;
|
|
}
|
|
|
|
dat = vibramp[cnt / 2];
|
|
}
|
|
|
|
// square
|
|
else if ((type == 2) || (type == 6))
|
|
{
|
|
if (type == 6)
|
|
{
|
|
cnt &= 0x7F;
|
|
}
|
|
else
|
|
{
|
|
if (cnt & 0x80) cnt = 0;
|
|
}
|
|
|
|
dat = vibsqu[cnt / 2];
|
|
}
|
|
|
|
// random
|
|
else if ((type == 3) || (type == 7))
|
|
{
|
|
if (type == 7)
|
|
{
|
|
cnt &= 0x7F;
|
|
}
|
|
else
|
|
{
|
|
if (cnt & 0x80) cnt = 0;
|
|
}
|
|
|
|
dat = vibsin[cnt / 2];
|
|
cnt += (p->patmusicrand & 0x1E);
|
|
}
|
|
|
|
// warning C4701: potentially uninitialized
|
|
else
|
|
{
|
|
dat = 0;
|
|
}
|
|
|
|
if (p->oldstvib)
|
|
dat = ((dat * (ch->info & 0x0F)) >> 6) + ch->aorgspd;
|
|
else
|
|
dat = ((dat * (ch->info & 0x0F)) >> 7) + ch->aorgspd;
|
|
|
|
ch->aspd = dat;
|
|
setspd(p, ch->channelnum);
|
|
|
|
ch->avibcnt = (cnt + ((ch->info >> 4) * 2)) & 0x7E;
|
|
}
|
|
}
|
|
|
|
static void s_setgvol(PLAYER *p, chn_t *ch)
|
|
{
|
|
if (ch->info <= 64)
|
|
p->globalvol = ch->info;
|
|
}
|
|
|
|
static void s_globvolslide(PLAYER *p, chn_t *ch) // NON-ST3
|
|
{
|
|
uint8_t i;
|
|
uint8_t infohi;
|
|
uint8_t infolo;
|
|
|
|
if ((p->tracker != SCREAM_TRACKER) && (p->tracker != IMAGO_ORPHEUS))
|
|
{
|
|
if (ch->info)
|
|
ch->wxymem = ch->info;
|
|
else
|
|
ch->info = ch->wxymem;
|
|
|
|
infohi = ch->wxymem >> 4;
|
|
infolo = ch->wxymem & 0x0F;
|
|
|
|
if (infolo == 0x0F)
|
|
{
|
|
if (!infohi)
|
|
p->globalvol -= infolo;
|
|
else if (!p->musiccount)
|
|
p->globalvol += infohi;
|
|
}
|
|
else if (infohi == 0x0F)
|
|
{
|
|
if (!infolo)
|
|
p->globalvol += infohi;
|
|
else if (!p->musiccount)
|
|
p->globalvol -= infolo;
|
|
}
|
|
else if (p->musiccount) // don't rely on fastvolslide flag here
|
|
{
|
|
if (!infolo)
|
|
p->globalvol += infohi;
|
|
else
|
|
p->globalvol -= infolo;
|
|
}
|
|
else
|
|
{
|
|
return; // illegal slide
|
|
}
|
|
|
|
if (p->globalvol < 0) p->globalvol = 0;
|
|
else if (p->globalvol > 64) p->globalvol = 64;
|
|
|
|
// update all channels now
|
|
for (i = 0; i < (p->lastachannelused + 1); ++i)
|
|
setvol(p, i, 0, 0);
|
|
}
|
|
}
|
|
|
|
static void s_setpan(PLAYER *p, chn_t *ch) // NON-ST3
|
|
{
|
|
// This one should work even in MONO mode
|
|
// for newer trackers that exports as ST3
|
|
|
|
if (ch->info <= 0x80)
|
|
{
|
|
ch->surround = 0;
|
|
voiceSetSurround(p, ch->channelnum, 0);
|
|
|
|
ch->apanpos = ch->info * 2;
|
|
setpan(p, ch->channelnum);
|
|
}
|
|
else if (ch->info == 0xA4) // surround
|
|
{
|
|
if ((p->tracker != SCREAM_TRACKER) && (p->tracker != IMAGO_ORPHEUS))
|
|
{
|
|
ch->surround = 1;
|
|
voiceSetSurround(p, ch->channelnum, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void s_panbrello(PLAYER *p, chn_t *ch) // NON-ST3
|
|
{
|
|
int8_t type;
|
|
int16_t cnt;
|
|
int16_t dat;
|
|
|
|
if ((p->tracker != SCREAM_TRACKER) && (p->tracker != IMAGO_ORPHEUS))
|
|
{
|
|
if (!p->musiccount)
|
|
{
|
|
if (!ch->info)
|
|
ch->info = ch->alasteff;
|
|
else
|
|
ch->yxymem = ch->info;
|
|
|
|
if (!(ch->info & 0xF0))
|
|
ch->info = (ch->yxymem & 0xF0) | (ch->info & 0x0F);
|
|
|
|
if (!(ch->info & 0x0F))
|
|
ch->info = (ch->info & 0xF0) | (ch->yxymem & 0x0F);
|
|
}
|
|
|
|
cnt = ch->apancnt;
|
|
type = ch->apantype;
|
|
|
|
// sine
|
|
if ((type == 0) || (type == 4))
|
|
{
|
|
if (type == 4)
|
|
{
|
|
cnt &= 0x7F;
|
|
}
|
|
else
|
|
{
|
|
if (cnt & 0x80) cnt = 0;
|
|
}
|
|
|
|
dat = vibsin[cnt / 2];
|
|
}
|
|
|
|
// ramp
|
|
else if ((type == 1) || (type == 5))
|
|
{
|
|
if (type == 5)
|
|
{
|
|
cnt &= 0x7F;
|
|
}
|
|
else
|
|
{
|
|
if (cnt & 0x80) cnt = 0;
|
|
}
|
|
|
|
dat = vibramp[cnt / 2];
|
|
}
|
|
|
|
// square
|
|
else if ((type == 2) || (type == 6))
|
|
{
|
|
if (type == 6)
|
|
{
|
|
cnt &= 0x7F;
|
|
}
|
|
else
|
|
{
|
|
if (cnt & 0x80) cnt = 0;
|
|
}
|
|
|
|
dat = vibsqu[cnt / 2];
|
|
}
|
|
|
|
// random
|
|
else if ((type == 3) || (type == 7))
|
|
{
|
|
if (type == 7)
|
|
{
|
|
cnt &= 0x7F;
|
|
}
|
|
else
|
|
{
|
|
if (cnt & 0x80) cnt = 0;
|
|
}
|
|
|
|
dat = vibsin[cnt / 2];
|
|
cnt += (p->patmusicrand & 0x1E);
|
|
}
|
|
|
|
// warning C4701: potentially uninitialized
|
|
else
|
|
{
|
|
dat = 0;
|
|
}
|
|
|
|
dat = ((dat * (ch->info & 0x0F)) >> 4) + ch->apanpos;
|
|
|
|
if (dat < 0) dat = 0;
|
|
else if (dat > 256) dat = 256;
|
|
|
|
voiceSetPanning(p, ch->channelnum, dat);
|
|
|
|
ch->apancnt = (cnt + ((ch->info >> 6) * 2)) & 0x7E;
|
|
}
|
|
}
|
|
|
|
void setSamplesPerFrame(PLAYER *p, uint32_t val)
|
|
{
|
|
p->samplesPerFrame = val;
|
|
|
|
#ifdef USE_VOL_RAMP
|
|
p->f_samplesPerFrame = 1.0f / ((float)(val) / 4.0f);
|
|
p->f_samplesPerFrameSharp = 1.0f / (p->f_outputFreq / 1000.0f); // 1ms
|
|
#endif
|
|
}
|
|
|
|
void setSamplingInterpolation(PLAYER *p, int8_t value)
|
|
{
|
|
p->samplingInterpolation = value;
|
|
}
|
|
|
|
#ifdef USE_VOL_RAMP
|
|
void setRampStyle(PLAYER *p, int8_t value)
|
|
{
|
|
p->rampStyle = value;
|
|
}
|
|
#endif
|
|
|
|
void setStereoMode(PLAYER *p, int8_t value)
|
|
{
|
|
p->stereomode = value;
|
|
}
|
|
|
|
void setMasterVolume(PLAYER *p, uint8_t value)
|
|
{
|
|
p->mastervol = value;
|
|
p->f_masterVolume = (float)(value) / 127.0f;
|
|
}
|
|
|
|
void voiceSetSource(PLAYER *p, uint8_t voiceNumber, const int8_t *sampleData,
|
|
int32_t sampleLength, int32_t sampleLoopLength, int32_t sampleLoopEnd,
|
|
int8_t loopEnabled, int8_t sixteenbit, int8_t stereo)
|
|
{
|
|
p->voice[voiceNumber].sampleData = sampleData;
|
|
p->voice[voiceNumber].sampleLength = sampleLength;
|
|
p->voice[voiceNumber].sampleLoopEnd = sampleLoopEnd;
|
|
p->voice[voiceNumber].sampleLoopLength = sampleLoopLength;
|
|
p->voice[voiceNumber].loopEnabled = loopEnabled;
|
|
|
|
p->voice[voiceNumber].sixteenBit = sixteenbit;
|
|
p->voice[voiceNumber].stereo = stereo;
|
|
|
|
p->voice[voiceNumber].mixing = 1;
|
|
p->voice[voiceNumber].interpolating = 1;
|
|
|
|
if (p->voice[voiceNumber].samplePosition >= p->voice[voiceNumber].sampleLength)
|
|
p->voice[voiceNumber].samplePosition = 0;
|
|
}
|
|
|
|
void voiceSetSamplePosition(PLAYER *p, uint8_t voiceNumber, uint16_t value)
|
|
{
|
|
p->voice[voiceNumber].samplePosition = value;
|
|
p->voice[voiceNumber].mixing = 1;
|
|
p->voice[voiceNumber].interpolating = 1;
|
|
|
|
if (p->voice[voiceNumber].loopEnabled)
|
|
{
|
|
while (p->voice[voiceNumber].samplePosition >= p->voice[voiceNumber].sampleLoopEnd)
|
|
p->voice[voiceNumber].samplePosition -= p->voice[voiceNumber].sampleLoopLength;
|
|
}
|
|
else if (p->voice[voiceNumber].samplePosition >= p->voice[voiceNumber].sampleLength)
|
|
{
|
|
p->voice[voiceNumber].mixing = 0;
|
|
p->voice[voiceNumber].interpolating = 0;
|
|
p->voice[voiceNumber].samplePosition = 0;
|
|
}
|
|
}
|
|
|
|
void voiceSetVolume(PLAYER *p, uint8_t voiceNumber, float volume, uint8_t note_on)
|
|
{
|
|
float rampRate;
|
|
|
|
#ifdef USE_VOL_RAMP
|
|
rampRate = p->f_samplesPerFrame;
|
|
if (p->rampStyle > 1 && !note_on)
|
|
{
|
|
p->voice[voiceNumber].targetVol = volume;
|
|
p->voice[voiceNumber].volDelta = (p->voice[voiceNumber].targetVol - p->voice[voiceNumber].volume) * rampRate;
|
|
}
|
|
else
|
|
{
|
|
p->voice[voiceNumber].volume = volume;
|
|
p->voice[voiceNumber].targetVol = volume;
|
|
p->voice[voiceNumber].volDelta = 0;
|
|
}
|
|
#else
|
|
p->voice[voiceNumber].volume = volume;
|
|
#endif
|
|
}
|
|
|
|
void voiceSetSurround(PLAYER *p, uint8_t voiceNumber, int8_t surround)
|
|
{
|
|
float rampRate;
|
|
|
|
if (surround)
|
|
{
|
|
p->chn[voiceNumber].apanpos = 128;
|
|
setpan(p, voiceNumber);
|
|
}
|
|
|
|
#ifdef USE_VOL_RAMP
|
|
rampRate = p->f_samplesPerFrameSharp;
|
|
if (p->rampStyle > 1)
|
|
{
|
|
if (surround)
|
|
p->voice[voiceNumber].targetPanR = -p->voice[voiceNumber].orgPanR;
|
|
else
|
|
p->voice[voiceNumber].targetPanR = p->voice[voiceNumber].orgPanR;
|
|
|
|
p->voice[voiceNumber].panDeltaR = (p->voice[voiceNumber].targetPanR - p->voice[voiceNumber].panningR) * rampRate;
|
|
}
|
|
else
|
|
{
|
|
if (surround)
|
|
p->voice[voiceNumber].panningR = -p->voice[voiceNumber].orgPanR;
|
|
else
|
|
p->voice[voiceNumber].panningR = p->voice[voiceNumber].orgPanR;
|
|
|
|
p->voice[voiceNumber].targetPanR = p->voice[voiceNumber].panningR;
|
|
p->voice[voiceNumber].panDeltaR = 0;
|
|
}
|
|
#else
|
|
if (surround)
|
|
p->voice[voiceNumber].panningR = -p->voice[voiceNumber].orgPanR;
|
|
else
|
|
p->voice[voiceNumber].panningR = p->voice[voiceNumber].orgPanR;
|
|
#endif
|
|
}
|
|
|
|
void voiceSetPanning(PLAYER *p, uint8_t voiceNumber, uint16_t pan)
|
|
{
|
|
float pf;
|
|
float rampRate;
|
|
|
|
pf = (float)(pan) / 256.0f;
|
|
|
|
#ifdef USE_VOL_RAMP
|
|
rampRate = p->f_samplesPerFrameSharp;
|
|
if (p->rampStyle > 1)
|
|
{
|
|
p->voice[voiceNumber].targetPanL = 1.0f - pf;
|
|
p->voice[voiceNumber].targetPanR = pf;
|
|
p->voice[voiceNumber].panDeltaL = (p->voice[voiceNumber].targetPanL - p->voice[voiceNumber].panningL) * rampRate;
|
|
p->voice[voiceNumber].panDeltaR = (p->voice[voiceNumber].targetPanR - p->voice[voiceNumber].panningR) * rampRate;
|
|
}
|
|
else
|
|
{
|
|
p->voice[voiceNumber].panningL = 1.0f - pf;
|
|
p->voice[voiceNumber].targetPanL = 1.0f - pf;
|
|
p->voice[voiceNumber].panningR = pf;
|
|
p->voice[voiceNumber].targetPanR = pf;
|
|
p->voice[voiceNumber].panDeltaL = 0;
|
|
p->voice[voiceNumber].panDeltaR = 0;
|
|
}
|
|
#else
|
|
p->voice[voiceNumber].panningL = 1.0f - pf;
|
|
p->voice[voiceNumber].panningR = pf;
|
|
#endif
|
|
p->voice[voiceNumber].orgPanR = pf;
|
|
}
|
|
|
|
void voiceSetSamplingFrequency(PLAYER *p, uint8_t voiceNumber, float samplingFrequency)
|
|
{
|
|
p->voice[voiceNumber].incRate = samplingFrequency / p->f_outputFreq;
|
|
}
|
|
|
|
void voiceSetPlayBackwards(PLAYER *p, uint8_t voiceNumber, int8_t playBackwards)
|
|
{
|
|
p->voice[voiceNumber].playBackwards = playBackwards;
|
|
}
|
|
|
|
void voiceSetReadPosToEnd(PLAYER *p, uint8_t voiceNumber)
|
|
{
|
|
p->voice[voiceNumber].samplePosition = p->voice[voiceNumber].sampleLength - 1;
|
|
}
|
|
|
|
static inline void mix8b(PLAYER *p, uint8_t ch, uint32_t samples)
|
|
{
|
|
const int8_t *sampleData;
|
|
int8_t loopEnabled;
|
|
int32_t sampleLength;
|
|
int32_t sampleLoopEnd;
|
|
int32_t sampleLoopLength;
|
|
int32_t sampleLoopBegin;
|
|
int32_t samplePosition;
|
|
int32_t interpolating;
|
|
#ifdef USE_VOL_RAMP
|
|
int32_t rampStyle;
|
|
#endif
|
|
uint32_t j;
|
|
float volume;
|
|
float sample;
|
|
float panningL;
|
|
float panningR;
|
|
void *resampler;
|
|
VOICE *v;
|
|
|
|
v = &p->voice[ch];
|
|
|
|
#ifdef USE_VOL_RAMP
|
|
rampStyle = p->rampStyle;
|
|
#endif
|
|
|
|
sampleLength = v->sampleLength;
|
|
sampleLoopLength = v->sampleLoopLength;
|
|
sampleLoopEnd = v->sampleLoopEnd;
|
|
sampleLoopBegin = sampleLoopEnd - sampleLoopLength;
|
|
loopEnabled = v->loopEnabled;
|
|
volume = v->volume;
|
|
panningL = v->panningL;
|
|
panningR = v->panningR;
|
|
interpolating = v->interpolating;
|
|
sampleData = v->sampleData;
|
|
resampler = p->resampler[ch];
|
|
|
|
resampler_set_rate(resampler, v->incRate);
|
|
|
|
for (j = 0; (j < samples) && sampleData; ++j)
|
|
{
|
|
samplePosition = v->samplePosition;
|
|
|
|
while (interpolating > 0 && (resampler_get_free_count(resampler) || !resampler_get_sample_count(resampler)))
|
|
{
|
|
resampler_write_sample_fixed(resampler, sampleData[samplePosition], 8);
|
|
|
|
if (v->playBackwards)
|
|
{
|
|
--samplePosition;
|
|
|
|
if (loopEnabled)
|
|
{
|
|
if (samplePosition < sampleLoopBegin)
|
|
samplePosition = sampleLoopEnd - (sampleLoopBegin - samplePosition);
|
|
}
|
|
else
|
|
{
|
|
if (samplePosition < 0)
|
|
{
|
|
samplePosition = 0;
|
|
interpolating = -resampler_get_padding_size();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
++samplePosition;
|
|
|
|
if (loopEnabled)
|
|
{
|
|
if (samplePosition >= sampleLoopEnd)
|
|
samplePosition = sampleLoopBegin + (samplePosition - sampleLoopEnd);
|
|
}
|
|
else
|
|
{
|
|
if (samplePosition >= sampleLength)
|
|
interpolating = -resampler_get_padding_size();
|
|
}
|
|
}
|
|
}
|
|
|
|
while (interpolating < 0 && (resampler_get_free_count(resampler) || !resampler_get_sample_count(resampler)))
|
|
{
|
|
resampler_write_sample_fixed(resampler, 0, 8);
|
|
++interpolating;
|
|
}
|
|
|
|
v->samplePosition = samplePosition;
|
|
v->interpolating = (int8_t)(interpolating);
|
|
|
|
if (!resampler_get_sample_count(resampler))
|
|
{
|
|
resampler_clear(resampler);
|
|
|
|
v->mixing = 0;
|
|
break;
|
|
}
|
|
|
|
sample = resampler_get_sample_float(resampler);
|
|
resampler_remove_sample(resampler, 1);
|
|
|
|
#ifdef USE_VOL_RAMP
|
|
if (rampStyle > 0)
|
|
{
|
|
v->fader += v->faderDelta;
|
|
|
|
if ((v->faderDelta > 0.0f) && (v->fader > v->faderDest))
|
|
{
|
|
v->fader = v->faderDest;
|
|
}
|
|
else if ((v->faderDelta < 0.0f) && (v->fader < v->faderDest))
|
|
{
|
|
v->fader = v->faderDest;
|
|
resampler_clear(resampler);
|
|
|
|
v->mixing = 0;
|
|
sampleData = 0;
|
|
}
|
|
|
|
sample *= v->fader;
|
|
}
|
|
#endif
|
|
|
|
sample *= volume;
|
|
|
|
p->masterBufferL[j] += (sample * panningL);
|
|
p->masterBufferR[j] += (sample * panningR);
|
|
|
|
#ifdef USE_VOL_RAMP
|
|
if (rampStyle > 1)
|
|
{
|
|
volume += v->volDelta;
|
|
panningL += v->panDeltaL;
|
|
panningR += v->panDeltaR;
|
|
|
|
if ((v->volDelta > 0.0f) && (volume > v->targetVol))
|
|
volume = v->targetVol;
|
|
else if ((v->volDelta < 0.0f) && (volume < v->targetVol))
|
|
volume = v->targetVol;
|
|
|
|
if ((v->panDeltaL > 0.0f) && (panningL > v->targetPanL))
|
|
panningL = v->targetPanL;
|
|
else if ((v->panDeltaL < 0.0f) && (panningL < v->targetPanL))
|
|
panningL = v->targetPanL;
|
|
|
|
if ((v->panDeltaR > 0.0f) && (panningR > v->targetPanR))
|
|
panningR = v->targetPanR;
|
|
else if ((v->panDeltaR < 0.0f) && (panningR < v->targetPanR))
|
|
panningR = v->targetPanR;
|
|
}
|
|
#endif
|
|
}
|
|
#ifdef USE_VOL_RAMP
|
|
v->volume = volume;
|
|
v->panningL = panningL;
|
|
v->panningR = panningR;
|
|
#endif
|
|
}
|
|
|
|
static inline void mix8bstereo(PLAYER *p, uint8_t ch, uint32_t samples)
|
|
{
|
|
const int8_t *sampleData;
|
|
int8_t loopEnabled;
|
|
int32_t sampleLength;
|
|
int32_t sampleLoopEnd;
|
|
int32_t sampleLoopLength;
|
|
int32_t sampleLoopBegin;
|
|
int32_t samplePosition;
|
|
int32_t interpolating;
|
|
#ifdef USE_VOL_RAMP
|
|
int32_t rampStyle;
|
|
#endif
|
|
uint32_t j;
|
|
float volume;
|
|
float sampleL;
|
|
float sampleR;
|
|
float panningL;
|
|
float panningR;
|
|
void *resampler[2];
|
|
VOICE *v;
|
|
|
|
v = &p->voice[ch];
|
|
#ifdef USE_VOL_RAMP
|
|
rampStyle = p->rampStyle;
|
|
#endif
|
|
|
|
sampleLength = v->sampleLength;
|
|
sampleLoopLength = v->sampleLoopLength;
|
|
sampleLoopEnd = v->sampleLoopEnd;
|
|
sampleLoopBegin = sampleLoopEnd - sampleLoopLength;
|
|
loopEnabled = v->loopEnabled;
|
|
volume = v->volume;
|
|
panningL = v->panningL;
|
|
panningR = v->panningR;
|
|
interpolating = v->interpolating;
|
|
sampleData = v->sampleData;
|
|
|
|
resampler[0] = p->resampler[ch];
|
|
#ifdef USE_VOL_RAMP
|
|
resampler[1] = p->resampler[ch + 64];
|
|
#else
|
|
resampler[1] = p->resampler[ch + 32];
|
|
#endif
|
|
|
|
resampler_set_rate(resampler[0], v->incRate );
|
|
resampler_set_rate(resampler[1], v->incRate );
|
|
|
|
for (j = 0; (j < samples) && sampleData; ++j)
|
|
{
|
|
samplePosition = v->samplePosition;
|
|
|
|
while (interpolating > 0 && (resampler_get_free_count(resampler[0]) ||
|
|
(!resampler_get_sample_count(resampler[0]) &&
|
|
!resampler_get_sample_count(resampler[1]))))
|
|
{
|
|
resampler_write_sample_fixed(resampler[0], sampleData[samplePosition], 8);
|
|
resampler_write_sample_fixed(resampler[1], sampleData[sampleLength + samplePosition], 8);
|
|
|
|
if (v->playBackwards)
|
|
{
|
|
--samplePosition;
|
|
|
|
if (loopEnabled)
|
|
{
|
|
if (samplePosition < sampleLoopBegin)
|
|
samplePosition = sampleLoopEnd - (sampleLoopBegin - samplePosition);
|
|
}
|
|
else
|
|
{
|
|
if (samplePosition < 0)
|
|
{
|
|
samplePosition = 0;
|
|
interpolating = -resampler_get_padding_size();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
++samplePosition;
|
|
|
|
if (loopEnabled)
|
|
{
|
|
if (samplePosition >= sampleLoopEnd)
|
|
samplePosition = sampleLoopBegin + (samplePosition - sampleLoopEnd);
|
|
}
|
|
else
|
|
{
|
|
if (samplePosition >= sampleLength)
|
|
interpolating = -resampler_get_padding_size();
|
|
}
|
|
}
|
|
}
|
|
|
|
while (interpolating < 0 && (resampler_get_free_count(resampler[0]) ||
|
|
(!resampler_get_sample_count(resampler[0]) &&
|
|
!resampler_get_sample_count(resampler[1]))))
|
|
{
|
|
resampler_write_sample_fixed(resampler[0], 0, 8);
|
|
resampler_write_sample_fixed(resampler[1], 0, 8);
|
|
++interpolating;
|
|
}
|
|
|
|
v->samplePosition = samplePosition;
|
|
v->interpolating = (int8_t)(interpolating);
|
|
|
|
if (!resampler_get_sample_count(resampler[0]))
|
|
{
|
|
resampler_clear(resampler[0]);
|
|
resampler_clear(resampler[1]);
|
|
|
|
v->mixing = 0;
|
|
break;
|
|
}
|
|
|
|
sampleL = resampler_get_sample_float(resampler[0]);
|
|
sampleR = resampler_get_sample_float(resampler[1]);
|
|
resampler_remove_sample(resampler[0], 1);
|
|
resampler_remove_sample(resampler[1], 1);
|
|
|
|
#ifdef USE_VOL_RAMP
|
|
if (rampStyle > 0)
|
|
{
|
|
v->fader += v->faderDelta;
|
|
|
|
if ((v->faderDelta > 0.0f) && (v->fader > v->faderDest))
|
|
{
|
|
v->fader = v->faderDest;
|
|
}
|
|
else if ((v->faderDelta < 0.0f) && (v->fader < v->faderDest))
|
|
{
|
|
v->fader = v->faderDest;
|
|
resampler_clear(resampler);
|
|
|
|
v->mixing = 0;
|
|
sampleData = 0;
|
|
}
|
|
|
|
sampleL *= v->fader;
|
|
sampleR *= v->fader;
|
|
}
|
|
#endif
|
|
|
|
sampleL *= volume;
|
|
sampleR *= volume;
|
|
|
|
p->masterBufferL[j] += (sampleL * panningL);
|
|
p->masterBufferR[j] += (sampleR * panningR);
|
|
|
|
#ifdef USE_VOL_RAMP
|
|
if (rampStyle > 1)
|
|
{
|
|
volume += v->volDelta;
|
|
panningL += v->panDeltaL;
|
|
panningR += v->panDeltaR;
|
|
|
|
if ((v->volDelta > 0.0f) && (volume > v->targetVol))
|
|
volume = v->targetVol;
|
|
else if ((v->volDelta < 0.0f) && (volume < v->targetVol))
|
|
volume = v->targetVol;
|
|
|
|
if ((v->panDeltaL > 0.0f) && (panningL > v->targetPanL))
|
|
panningL = v->targetPanL;
|
|
else if ((v->panDeltaL < 0.0f) && (panningL < v->targetPanL))
|
|
panningL = v->targetPanL;
|
|
|
|
if ((v->panDeltaR > 0.0f) && (panningR > v->targetPanR))
|
|
panningR = v->targetPanR;
|
|
else if ((v->panDeltaR < 0.0f) && (panningR < v->targetPanR))
|
|
panningR = v->targetPanR;
|
|
}
|
|
#endif
|
|
}
|
|
#ifdef USE_VOL_RAMP
|
|
v->volume = volume;
|
|
v->panningL = panningL;
|
|
v->panningR = panningR;
|
|
#endif
|
|
}
|
|
|
|
static inline void mix16b(PLAYER *p, uint8_t ch, uint32_t samples)
|
|
{
|
|
const int16_t *sampleData;
|
|
int8_t loopEnabled;
|
|
int32_t sampleLength;
|
|
int32_t sampleLoopEnd;
|
|
int32_t sampleLoopLength;
|
|
int32_t sampleLoopBegin;
|
|
int32_t samplePosition;
|
|
int32_t interpolating;
|
|
#ifdef USE_VOL_RAMP
|
|
int32_t rampStyle;
|
|
#endif
|
|
uint32_t j;
|
|
float volume;
|
|
float sample;
|
|
float panningL;
|
|
float panningR;
|
|
void *resampler;
|
|
VOICE *v;
|
|
|
|
v = &p->voice[ch];
|
|
#ifdef USE_VOL_RAMP
|
|
rampStyle = p->rampStyle;
|
|
#endif
|
|
|
|
sampleLength = v->sampleLength;
|
|
sampleLoopLength = v->sampleLoopLength;
|
|
sampleLoopEnd = v->sampleLoopEnd;
|
|
sampleLoopBegin = sampleLoopEnd - sampleLoopLength;
|
|
loopEnabled = v->loopEnabled;
|
|
volume = v->volume;
|
|
panningL = v->panningL;
|
|
panningR = v->panningR;
|
|
interpolating = v->interpolating;
|
|
sampleData = (const int16_t *)(v->sampleData);
|
|
resampler = p->resampler[ch];
|
|
|
|
resampler_set_rate(resampler, v->incRate);
|
|
|
|
for (j = 0; (j < samples) && sampleData; ++j)
|
|
{
|
|
samplePosition = v->samplePosition;
|
|
|
|
while (interpolating > 0 && (resampler_get_free_count(resampler) || !resampler_get_sample_count(resampler)))
|
|
{
|
|
resampler_write_sample_fixed(resampler, (int16_t)(get_le16(&sampleData[samplePosition])), 16);
|
|
|
|
if (v->playBackwards)
|
|
{
|
|
--samplePosition;
|
|
|
|
if (loopEnabled)
|
|
{
|
|
if (samplePosition < sampleLoopBegin)
|
|
samplePosition = sampleLoopEnd - (sampleLoopBegin - samplePosition);
|
|
}
|
|
else
|
|
{
|
|
if (samplePosition < 0)
|
|
{
|
|
samplePosition = 0;
|
|
interpolating = -resampler_get_padding_size();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
++samplePosition;
|
|
|
|
if (loopEnabled)
|
|
{
|
|
if (samplePosition >= sampleLoopEnd)
|
|
samplePosition = sampleLoopBegin + (samplePosition - sampleLoopEnd);
|
|
}
|
|
else
|
|
{
|
|
if (samplePosition >= sampleLength)
|
|
interpolating = -resampler_get_padding_size();
|
|
}
|
|
}
|
|
}
|
|
|
|
while (interpolating < 0 && (resampler_get_free_count(resampler) || !resampler_get_sample_count(resampler)))
|
|
{
|
|
resampler_write_sample_fixed(resampler, 0, 16);
|
|
++interpolating;
|
|
}
|
|
|
|
v->samplePosition = samplePosition;
|
|
v->interpolating = (int8_t)(interpolating);
|
|
|
|
if (!resampler_get_sample_count(resampler))
|
|
{
|
|
resampler_clear(resampler);
|
|
|
|
v->mixing = 0;
|
|
break;
|
|
}
|
|
|
|
sample = resampler_get_sample_float(resampler);
|
|
resampler_remove_sample(resampler, 1);
|
|
|
|
#ifdef USE_VOL_RAMP
|
|
if (rampStyle > 0)
|
|
{
|
|
v->fader += v->faderDelta;
|
|
|
|
if ((v->faderDelta > 0.0f) && (v->fader > v->faderDest))
|
|
{
|
|
v->fader = v->faderDest;
|
|
}
|
|
else if ((v->faderDelta < 0.0f) && (v->fader < v->faderDest))
|
|
{
|
|
v->fader = v->faderDest;
|
|
resampler_clear(resampler);
|
|
|
|
v->mixing = 0;
|
|
sampleData = 0;
|
|
}
|
|
|
|
sample *= v->fader;
|
|
}
|
|
#endif
|
|
|
|
sample *= volume;
|
|
|
|
p->masterBufferL[j] += (sample * panningL);
|
|
p->masterBufferR[j] += (sample * panningR);
|
|
|
|
#ifdef USE_VOL_RAMP
|
|
if (rampStyle > 1)
|
|
{
|
|
volume += v->volDelta;
|
|
panningL += v->panDeltaL;
|
|
panningR += v->panDeltaR;
|
|
|
|
if ((v->volDelta > 0.0f) && (volume > v->targetVol))
|
|
volume = v->targetVol;
|
|
else if ((v->volDelta < 0.0f) && (volume < v->targetVol))
|
|
volume = v->targetVol;
|
|
|
|
if ((v->panDeltaL > 0.0f) && (panningL > v->targetPanL))
|
|
panningL = v->targetPanL;
|
|
else if ((v->panDeltaL < 0.0f) && (panningL < v->targetPanL))
|
|
panningL = v->targetPanL;
|
|
|
|
if ((v->panDeltaR > 0.0f) && (panningR > v->targetPanR))
|
|
panningR = v->targetPanR;
|
|
else if ((v->panDeltaR < 0.0f) && (panningR < v->targetPanR))
|
|
panningR = v->targetPanR;
|
|
}
|
|
#endif
|
|
}
|
|
#ifdef USE_VOL_RAMP
|
|
v->volume = volume;
|
|
v->panningL = panningL;
|
|
v->panningR = panningR;
|
|
#endif
|
|
}
|
|
|
|
static inline void mix16bstereo(PLAYER *p, uint8_t ch, uint32_t samples)
|
|
{
|
|
const int16_t *sampleData;
|
|
int8_t loopEnabled;
|
|
int32_t sampleLength;
|
|
int32_t sampleLoopEnd;
|
|
int32_t sampleLoopLength;
|
|
int32_t sampleLoopBegin;
|
|
int32_t samplePosition;
|
|
int32_t interpolating;
|
|
#ifdef USE_VOL_RAMP
|
|
int32_t rampStyle;
|
|
#endif
|
|
uint32_t j;
|
|
float volume;
|
|
float sampleL;
|
|
float sampleR;
|
|
float panningL;
|
|
float panningR;
|
|
void *resampler[2];
|
|
VOICE *v;
|
|
|
|
v = &p->voice[ch];
|
|
#ifdef USE_VOL_RAMP
|
|
rampStyle = p->rampStyle;
|
|
#endif
|
|
|
|
sampleLength = v->sampleLength;
|
|
sampleLoopLength = v->sampleLoopLength;
|
|
sampleLoopEnd = v->sampleLoopEnd;
|
|
sampleLoopBegin = sampleLoopEnd - sampleLoopLength;
|
|
loopEnabled = v->loopEnabled;
|
|
volume = v->volume;
|
|
panningL = v->panningL;
|
|
panningR = v->panningR;
|
|
interpolating = v->interpolating;
|
|
sampleData = (const int16_t *)(v->sampleData);
|
|
|
|
resampler[0] = p->resampler[ch];
|
|
#ifdef USE_VOL_RAMP
|
|
resampler[1] = p->resampler[ch+64];
|
|
#else
|
|
resampler[1] = p->resampler[ch+32];
|
|
#endif
|
|
|
|
resampler_set_rate(resampler[0], v->incRate);
|
|
resampler_set_rate(resampler[1], v->incRate);
|
|
|
|
for (j = 0; (j < samples) && sampleData; ++j)
|
|
{
|
|
samplePosition = v->samplePosition;
|
|
|
|
while (interpolating > 0 && (resampler_get_free_count(resampler[0]) ||
|
|
(!resampler_get_sample_count(resampler[0]) &&
|
|
!resampler_get_sample_count(resampler[1]))))
|
|
{
|
|
resampler_write_sample_fixed(resampler[0], (int16_t)(get_le16(&sampleData[samplePosition])), 16);
|
|
resampler_write_sample_fixed(resampler[1], (int16_t)(get_le16(&sampleData[sampleLength + samplePosition])), 16);
|
|
|
|
if (v->playBackwards)
|
|
{
|
|
--samplePosition;
|
|
|
|
if (loopEnabled)
|
|
{
|
|
if (samplePosition < sampleLoopBegin)
|
|
samplePosition = sampleLoopEnd - (sampleLoopBegin - samplePosition);
|
|
}
|
|
else
|
|
{
|
|
if (samplePosition < 0)
|
|
{
|
|
samplePosition = 0;
|
|
interpolating = -resampler_get_padding_size();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
++samplePosition;
|
|
|
|
if (loopEnabled)
|
|
{
|
|
if (samplePosition >= sampleLoopEnd)
|
|
samplePosition = sampleLoopBegin + (samplePosition - sampleLoopEnd);
|
|
}
|
|
else
|
|
{
|
|
if (samplePosition >= sampleLength)
|
|
interpolating = -resampler_get_padding_size();
|
|
}
|
|
}
|
|
}
|
|
|
|
while (interpolating < 0 && (resampler_get_free_count(resampler[0]) ||
|
|
(!resampler_get_sample_count(resampler[0]) &&
|
|
!resampler_get_sample_count(resampler[1]))))
|
|
{
|
|
resampler_write_sample_fixed(resampler[0], 0, 16);
|
|
resampler_write_sample_fixed(resampler[1], 0, 16);
|
|
++interpolating;
|
|
}
|
|
|
|
v->samplePosition = samplePosition;
|
|
v->interpolating = (int8_t)(interpolating);
|
|
|
|
if (!resampler_get_sample_count(resampler[0]))
|
|
{
|
|
resampler_clear(resampler[0]);
|
|
resampler_clear(resampler[1]);
|
|
|
|
v->mixing = 0;
|
|
break;
|
|
}
|
|
|
|
sampleL = resampler_get_sample_float(resampler[0]);
|
|
sampleR = resampler_get_sample_float(resampler[1]);
|
|
resampler_remove_sample(resampler[0], 1);
|
|
resampler_remove_sample(resampler[1], 1);
|
|
|
|
#ifdef USE_VOL_RAMP
|
|
if (rampStyle > 0)
|
|
{
|
|
v->fader += v->faderDelta;
|
|
|
|
if ((v->faderDelta > 0.0f) && (v->fader > v->faderDest))
|
|
{
|
|
v->fader = v->faderDest;
|
|
}
|
|
else if ((v->faderDelta < 0.0f) && (v->fader < v->faderDest))
|
|
{
|
|
v->fader = v->faderDest;
|
|
resampler_clear(resampler);
|
|
|
|
v->mixing = 0;
|
|
sampleData = 0;
|
|
}
|
|
|
|
sampleL *= v->fader;
|
|
sampleR *= v->fader;
|
|
}
|
|
#endif
|
|
|
|
sampleL *= volume;
|
|
sampleR *= volume;
|
|
|
|
p->masterBufferL[j] += (sampleL * panningL);
|
|
p->masterBufferR[j] += (sampleR * panningR);
|
|
|
|
#ifdef USE_VOL_RAMP
|
|
if (rampStyle > 1)
|
|
{
|
|
volume += v->volDelta;
|
|
panningL += v->panDeltaL;
|
|
panningR += v->panDeltaR;
|
|
|
|
if ((v->volDelta > 0.0f) && (volume > v->targetVol))
|
|
volume = v->targetVol;
|
|
else if ((v->volDelta < 0.0f) && (volume < v->targetVol))
|
|
volume = v->targetVol;
|
|
|
|
if ((v->panDeltaL > 0.0f) && (panningL > v->targetPanL))
|
|
panningL = v->targetPanL;
|
|
else if ((v->panDeltaL < 0.0f) && (panningL < v->targetPanL))
|
|
panningL = v->targetPanL;
|
|
|
|
if ((v->panDeltaR > 0.0f) && (panningR > v->targetPanR))
|
|
panningR = v->targetPanR;
|
|
else if ((v->panDeltaR < 0.0f) && (panningR < v->targetPanR))
|
|
panningR = v->targetPanR;
|
|
}
|
|
#endif
|
|
}
|
|
#ifdef USE_VOL_RAMP
|
|
v->volume = volume;
|
|
v->panningL = panningL;
|
|
v->panningR = panningR;
|
|
#endif
|
|
}
|
|
|
|
void mixChannel(PLAYER *p, uint8_t i, uint32_t sampleBlockLength)
|
|
{
|
|
if (p->voice[i].incRate && p->voice[i].mixing)
|
|
{
|
|
if (p->voice[i].stereo)
|
|
{
|
|
if (p->voice[i].sixteenBit)
|
|
mix16bstereo(p, i, sampleBlockLength);
|
|
else
|
|
mix8bstereo(p, i, sampleBlockLength);
|
|
}
|
|
else
|
|
{
|
|
if (p->voice[i].sixteenBit)
|
|
mix16b(p, i, sampleBlockLength);
|
|
else
|
|
mix8b(p, i, sampleBlockLength);
|
|
}
|
|
}
|
|
}
|
|
|
|
void mixSampleBlock(PLAYER *p, float *outputStream, uint32_t sampleBlockLength)
|
|
{
|
|
float *streamPointer;
|
|
uint8_t i;
|
|
uint32_t j;
|
|
float outL;
|
|
float outR;
|
|
|
|
#ifdef USE_VOL_RAMP
|
|
int32_t rampStyle = p->rampStyle;
|
|
#endif
|
|
|
|
streamPointer = outputStream;
|
|
|
|
memset(p->masterBufferL, 0, sampleBlockLength * sizeof (float));
|
|
memset(p->masterBufferR, 0, sampleBlockLength * sizeof (float));
|
|
|
|
for (i = 0; i < 32; ++i)
|
|
{
|
|
if (p->muted[i / 8] & (1 << (i % 8)))
|
|
continue;
|
|
|
|
mixChannel(p, i, sampleBlockLength);
|
|
|
|
#ifdef USE_VOL_RAMP
|
|
if (rampStyle > 0)
|
|
mixChannel(p, i + 32, sampleBlockLength);
|
|
#endif
|
|
}
|
|
|
|
for (j = 0; j < sampleBlockLength; ++j)
|
|
{
|
|
outL = p->masterBufferL[j] * p->f_masterVolume;
|
|
outR = p->masterBufferR[j] * p->f_masterVolume;
|
|
|
|
*streamPointer++ = outL;
|
|
*streamPointer++ = outR;
|
|
}
|
|
}
|
|
|
|
static void st3play_AdlibMix(PLAYER *p, float *buffer, int32_t count)
|
|
{
|
|
int32_t tempbuffer[256];
|
|
int32_t inbuffer_free;
|
|
int32_t outbuffer_avail;
|
|
int32_t i;
|
|
float sample;
|
|
|
|
while (count)
|
|
{
|
|
inbuffer_free = resampler_get_free_count(p->fmResampler);
|
|
if (inbuffer_free > 256)
|
|
inbuffer_free = 256;
|
|
|
|
if (inbuffer_free)
|
|
{
|
|
Chip_GenerateBlock_Mono(p->fmChip, inbuffer_free, tempbuffer);
|
|
|
|
for (i = 0; i < inbuffer_free; ++i)
|
|
resampler_write_sample_fixed( p->fmResampler, (int32_t)(tempbuffer[i]), 16);
|
|
}
|
|
|
|
outbuffer_avail = resampler_get_sample_count(p->fmResampler);
|
|
if (outbuffer_avail > count)
|
|
outbuffer_avail = count;
|
|
|
|
for (i = 0; i < outbuffer_avail; ++i)
|
|
{
|
|
sample = resampler_get_sample_float(p->fmResampler);
|
|
resampler_remove_sample(p->fmResampler, 1);
|
|
|
|
buffer[(i * 2) + 0] += sample;
|
|
buffer[(i * 2) + 1] += sample;
|
|
}
|
|
|
|
buffer += (outbuffer_avail * 2);
|
|
count -= outbuffer_avail;
|
|
}
|
|
}
|
|
|
|
void st3play_RenderFloat(void *_p, float *buffer, int32_t count)
|
|
{
|
|
PLAYER *p;
|
|
int32_t samplesTodo;
|
|
float *outputStream;
|
|
|
|
p = (PLAYER *)(_p);
|
|
if (p->isMixing)
|
|
{
|
|
outputStream = buffer;
|
|
|
|
while (count)
|
|
{
|
|
if (p->samplesLeft)
|
|
{
|
|
samplesTodo = (count < p->samplesLeft) ? count : p->samplesLeft;
|
|
samplesTodo = (samplesTodo < p->soundBufferSize) ? samplesTodo : p->soundBufferSize;
|
|
|
|
if (outputStream)
|
|
{
|
|
mixSampleBlock(p, outputStream, samplesTodo);
|
|
|
|
if (p->fmChip && p->fmResampler)
|
|
st3play_AdlibMix(p, outputStream, samplesTodo);
|
|
|
|
outputStream += (samplesTodo * 2);
|
|
}
|
|
|
|
p->samplesLeft -= samplesTodo;
|
|
count -= samplesTodo;
|
|
}
|
|
else
|
|
{
|
|
if (p->Playing)
|
|
dorow(p);
|
|
|
|
p->samplesLeft = p->samplesPerFrame;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void st3play_RenderFixed32(void *_p, int32_t *buffer, int32_t count, int8_t depth)
|
|
{
|
|
int32_t i;
|
|
float *fbuffer;
|
|
float scale;
|
|
float sample;
|
|
|
|
assert(sizeof (int32_t) == sizeof (float));
|
|
|
|
fbuffer = (float *)(buffer);
|
|
st3play_RenderFloat(_p, fbuffer, count);
|
|
|
|
if (buffer)
|
|
{
|
|
scale = (float)(1 << (depth - 1));
|
|
for (i = 0; i < (count * 2); ++i)
|
|
{
|
|
sample = fbuffer[i] * scale;
|
|
|
|
if (sample > INT_MAX) sample = INT_MAX;
|
|
else if (sample < INT_MIN) sample = INT_MIN;
|
|
|
|
buffer[i] = (int32_t)(sample);
|
|
}
|
|
}
|
|
}
|
|
|
|
void st3play_RenderFixed16(void *_p, int16_t *buffer, int32_t count, int8_t depth)
|
|
{
|
|
int32_t i;
|
|
int32_t SamplesTodo;
|
|
float scale;
|
|
float sample;
|
|
float fbuffer[1024];
|
|
|
|
if (buffer == NULL)
|
|
{
|
|
st3play_RenderFloat(_p, 0, count);
|
|
}
|
|
else
|
|
{
|
|
scale = (float)(1 << (depth - 1));
|
|
|
|
while (count)
|
|
{
|
|
SamplesTodo = (count < 512) ? count : 512;
|
|
|
|
st3play_RenderFloat(_p, fbuffer, SamplesTodo);
|
|
for (i = 0; i < (SamplesTodo * 2); ++i)
|
|
{
|
|
sample = fbuffer[i] * scale;
|
|
|
|
if (sample > 32767) sample = 32767;
|
|
else if (sample < -32768) sample = -32768;
|
|
|
|
buffer[i] = (int16_t)(sample);
|
|
}
|
|
|
|
buffer += (SamplesTodo * 2);
|
|
count -= SamplesTodo;
|
|
}
|
|
}
|
|
}
|
|
|
|
void FreeSong(PLAYER *p)
|
|
{
|
|
uint16_t i;
|
|
|
|
p->Playing = 0;
|
|
|
|
if (p->adpcmSamples != NULL)
|
|
{
|
|
for (i = 0; i < get_le16(&p->mseg[0x22]); ++i)
|
|
{
|
|
if (p->adpcmSamples[i] != NULL)
|
|
free(p->adpcmSamples[i]);
|
|
}
|
|
|
|
free(p->adpcmSamples);
|
|
p->adpcmSamples = NULL;
|
|
}
|
|
|
|
memset(p->voice, 0, sizeof (p->voice));
|
|
|
|
if (p->mseg)
|
|
{
|
|
free(p->mseg);
|
|
p->mseg = NULL;
|
|
}
|
|
|
|
p->ModuleLoaded = 0;
|
|
}
|
|
|
|
void st3play_Mute(void *_p, int8_t channel, int8_t mute)
|
|
{
|
|
PLAYER *p;
|
|
int8_t offset;
|
|
int8_t bit;
|
|
uint8_t adlibChannel;
|
|
|
|
if (channel > 31)
|
|
return;
|
|
|
|
p = (PLAYER *)(_p);
|
|
|
|
adlibChannel = (p->mseg[0x40 + channel] & 0x7F) - 16;
|
|
|
|
offset = channel / 8;
|
|
bit = 1 << (channel % 8);
|
|
|
|
if (mute)
|
|
p->muted[offset] |= bit;
|
|
else
|
|
p->muted[offset] &= ~bit;
|
|
|
|
if (adlibChannel < 9)
|
|
Chip_Mute(p->fmChip, adlibChannel, mute);
|
|
}
|
|
|
|
int32_t st3play_GetLoopCount(void *_p)
|
|
{
|
|
PLAYER *p = (PLAYER *)(_p);
|
|
return (p->loopCount);
|
|
}
|
|
|
|
void st3play_GetInfo(void *_p, st3_info *info)
|
|
{
|
|
int32_t i;
|
|
int32_t channels_playing;
|
|
PLAYER *p;
|
|
|
|
p = (PLAYER *)(_p);
|
|
|
|
info->order = p->x_np_ord - 1;
|
|
info->pattern = p->x_np_pat;
|
|
info->row = p->x_np_row;
|
|
info->speed = p->musicmax;
|
|
info->tempo = p->tempo;
|
|
|
|
channels_playing = 0;
|
|
|
|
if (p->isMixing)
|
|
{
|
|
for (i = 0; i < 32; ++i)
|
|
{
|
|
if (p->voice[i].mixing)
|
|
++channels_playing;
|
|
}
|
|
|
|
if (p->fmChip)
|
|
channels_playing += Chip_GetActiveChannels(p->fmChip);
|
|
}
|
|
|
|
info->channels_playing = (int8_t)(channels_playing);
|
|
}
|
|
|
|
// EOF
|