Merge branch 'master' of https://github.com/tildearrow/furnace into nmk112
This commit is contained in:
commit
3922770e8f
166 changed files with 4829 additions and 2054 deletions
|
|
@ -24,33 +24,58 @@
|
|||
static inline int bsr(unsigned short v) {
|
||||
unsigned long idx;
|
||||
if (_BitScanReverse(&idx,(unsigned long)v)) {
|
||||
return idx;
|
||||
return idx+1;
|
||||
}
|
||||
else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static inline int bsr32(unsigned int v) {
|
||||
unsigned long idx;
|
||||
if (_BitScanReverse(&idx,(unsigned long)v)) {
|
||||
return idx+1;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
#elif defined( __GNUC__ )
|
||||
|
||||
static inline int bsr(unsigned short v)
|
||||
{
|
||||
static inline int bsr(unsigned short v) {
|
||||
if (v) {
|
||||
return 32 - __builtin_clz(v);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
else{
|
||||
}
|
||||
|
||||
static inline int bsr32(unsigned int v) {
|
||||
if (v) {
|
||||
return 32 - __builtin_clz(v);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline int bsr(unsigned short v)
|
||||
{
|
||||
static inline int bsr(unsigned short v) {
|
||||
unsigned short mask = 0x8000;
|
||||
for (int i = 15; i >= 0; --i) {
|
||||
for (int i = 16; i >= 0; --i) {
|
||||
if (v&mask)
|
||||
return (int)i;
|
||||
return i;
|
||||
mask>>=1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int bsr32(unsigned int v) {
|
||||
unsigned int mask = 0x80000000;
|
||||
for (int i = 32; i >= 0; --i) {
|
||||
if (v&mask)
|
||||
return i;
|
||||
mask>>=1;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
#ifndef _CHIP_UTILS_H
|
||||
#define _CHIP_UTILS_H
|
||||
|
||||
#include "defines.h"
|
||||
#include "macroInt.h"
|
||||
|
||||
// custom clock limits
|
||||
|
|
@ -29,7 +30,7 @@
|
|||
// common shared channel struct
|
||||
template<typename T> struct SharedChannel {
|
||||
int freq, baseFreq, baseNoteOverride, pitch, pitch2, arpOff;
|
||||
int ins, note;
|
||||
int ins, note, sampleNote, sampleNoteDelta;
|
||||
bool active, insChanged, freqChanged, fixedArp, keyOn, keyOff, portaPause, inPorta;
|
||||
T vol, outVol;
|
||||
DivMacroInt std;
|
||||
|
|
@ -71,6 +72,8 @@ template<typename T> struct SharedChannel {
|
|||
arpOff(0),
|
||||
ins(-1),
|
||||
note(0),
|
||||
sampleNote(DIV_NOTE_NULL),
|
||||
sampleNoteDelta(0),
|
||||
active(false),
|
||||
insChanged(true),
|
||||
freqChanged(false),
|
||||
|
|
|
|||
|
|
@ -36,5 +36,6 @@
|
|||
|
||||
// dispatch
|
||||
#define DIV_MAX_OUTPUTS 16
|
||||
#define DIV_NOTE_NULL 0x7fffffff
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -25,11 +25,10 @@
|
|||
#include "../pch.h"
|
||||
#include "config.h"
|
||||
#include "chipUtils.h"
|
||||
#include "defines.h"
|
||||
|
||||
#define ONE_SEMITONE 2200
|
||||
|
||||
#define DIV_NOTE_NULL 0x7fffffff
|
||||
|
||||
#define addWrite(a,v) regWrites.push_back(DivRegWrite(a,v));
|
||||
|
||||
// HOW TO ADD A NEW COMMAND:
|
||||
|
|
@ -250,6 +249,9 @@ enum DivDispatchCmds {
|
|||
|
||||
DIV_CMD_MACRO_RESTART, // (which)
|
||||
|
||||
DIV_CMD_POWERNOISE_COUNTER_LOAD, // (which, val)
|
||||
DIV_CMD_POWERNOISE_IO_WRITE, // (port, value)
|
||||
|
||||
DIV_CMD_MAX
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@
|
|||
#include "platform/c140.h"
|
||||
#include "platform/pcmdac.h"
|
||||
#include "platform/esfm.h"
|
||||
#include "platform/powernoise.h"
|
||||
#include "platform/dummy.h"
|
||||
#include "../ta-log.h"
|
||||
#include "song.h"
|
||||
|
|
@ -648,6 +649,9 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
|
|||
case DIV_SYSTEM_ESFM:
|
||||
dispatch=new DivPlatformESFM;
|
||||
break;
|
||||
case DIV_SYSTEM_POWERNOISE:
|
||||
dispatch=new DivPlatformPowerNoise;
|
||||
break;
|
||||
case DIV_SYSTEM_DUMMY:
|
||||
dispatch=new DivPlatformDummy;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -3412,6 +3412,10 @@ void DivEngine::setMidiCallback(std::function<int(const TAMidiMessage&)> what) {
|
|||
midiCallback=what;
|
||||
}
|
||||
|
||||
void DivEngine::setMidiDebug(bool enable) {
|
||||
midiDebug=enable;
|
||||
}
|
||||
|
||||
bool DivEngine::sendMidiMessage(TAMidiMessage& msg) {
|
||||
if (output==NULL) {
|
||||
logW("output is NULL!");
|
||||
|
|
@ -3749,12 +3753,16 @@ bool DivEngine::preInit(bool noSafeMode) {
|
|||
initConfDir();
|
||||
logD("config path: %s",configPath.c_str());
|
||||
|
||||
// TODO: re-enable with a better approach
|
||||
// see issue #1581
|
||||
/*
|
||||
if (!noSafeMode) {
|
||||
String safeModePath=configPath+DIR_SEPARATOR_STR+"safemode";
|
||||
if (touchFile(safeModePath.c_str())==-EEXIST) {
|
||||
wantSafe=true;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
String logPath=configPath+DIR_SEPARATOR_STR+"furnace.log";
|
||||
startLogFile(logPath.c_str());
|
||||
|
|
@ -3778,8 +3786,12 @@ bool DivEngine::preInit(bool noSafeMode) {
|
|||
}
|
||||
|
||||
void DivEngine::everythingOK() {
|
||||
// TODO: re-enable with a better approach
|
||||
// see issue #1581
|
||||
/*
|
||||
String safeModePath=configPath+DIR_SEPARATOR_STR+"safemode";
|
||||
deleteFile(safeModePath.c_str());
|
||||
*/
|
||||
}
|
||||
|
||||
bool DivEngine::init() {
|
||||
|
|
|
|||
|
|
@ -52,10 +52,10 @@ class DivWorkPool;
|
|||
#define EXTERN_BUSY_BEGIN_SOFT e->softLocked=true; e->isBusy.lock();
|
||||
#define EXTERN_BUSY_END e->isBusy.unlock(); e->softLocked=false;
|
||||
|
||||
#define DIV_UNSTABLE
|
||||
//#define DIV_UNSTABLE
|
||||
|
||||
#define DIV_VERSION "dev191"
|
||||
#define DIV_ENGINE_VERSION 191
|
||||
#define DIV_VERSION "0.6.1"
|
||||
#define DIV_ENGINE_VERSION 192
|
||||
// for imports
|
||||
#define DIV_VERSION_MOD 0xff01
|
||||
#define DIV_VERSION_FC 0xff02
|
||||
|
|
@ -174,7 +174,7 @@ struct DivChannelState {
|
|||
|
||||
struct DivNoteEvent {
|
||||
signed char channel;
|
||||
unsigned char ins;
|
||||
short ins;
|
||||
signed char note, volume;
|
||||
bool on, nop, insChange, fromMIDI;
|
||||
DivNoteEvent(int c, int i, int n, int v, bool o, bool ic=false, bool fm=false):
|
||||
|
|
@ -497,6 +497,7 @@ class DivEngine {
|
|||
short effectSlotMap[4096];
|
||||
int midiBaseChan;
|
||||
bool midiPoly;
|
||||
bool midiDebug;
|
||||
size_t midiAgeCounter;
|
||||
|
||||
blip_buffer_t* samp_bb;
|
||||
|
|
@ -1208,6 +1209,9 @@ class DivEngine {
|
|||
// send MIDI message
|
||||
bool sendMidiMessage(TAMidiMessage& msg);
|
||||
|
||||
// enable MIDI debug
|
||||
void setMidiDebug(bool enable);
|
||||
|
||||
// perform secure/sync operation
|
||||
void synchronized(const std::function<void()>& what);
|
||||
|
||||
|
|
@ -1329,6 +1333,7 @@ class DivEngine {
|
|||
cmdStreamInt(NULL),
|
||||
midiBaseChan(0),
|
||||
midiPoly(true),
|
||||
midiDebug(false),
|
||||
midiAgeCounter(0),
|
||||
samp_bb(NULL),
|
||||
samp_bbInLen(0),
|
||||
|
|
|
|||
|
|
@ -253,6 +253,10 @@ bool DivInstrumentESFM::Operator::operator==(const DivInstrumentESFM::Operator&
|
|||
);
|
||||
}
|
||||
|
||||
bool DivInstrumentPowerNoise::operator==(const DivInstrumentPowerNoise& other) {
|
||||
return _C(octave);
|
||||
}
|
||||
|
||||
#undef _C
|
||||
|
||||
#define FEATURE_BEGIN(x) \
|
||||
|
|
@ -782,6 +786,14 @@ void DivInstrument::writeFeatureEF(SafeWriter* w) {
|
|||
FEATURE_END;
|
||||
}
|
||||
|
||||
void DivInstrument::writeFeaturePN(SafeWriter* w) {
|
||||
FEATURE_BEGIN("PN");
|
||||
|
||||
w->writeC(powernoise.octave);
|
||||
|
||||
FEATURE_END;
|
||||
}
|
||||
|
||||
void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song, bool insName) {
|
||||
size_t blockStartSeek=0;
|
||||
size_t blockEndSeek=0;
|
||||
|
|
@ -826,6 +838,7 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song, bo
|
|||
bool featureX1=false;
|
||||
bool featureNE=false;
|
||||
bool featureEF=false;
|
||||
bool featurePN=false;
|
||||
|
||||
bool checkForWL=false;
|
||||
|
||||
|
|
@ -1043,6 +1056,12 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song, bo
|
|||
featureFM=true;
|
||||
featureEF=true;
|
||||
break;
|
||||
case DIV_INS_POWERNOISE:
|
||||
featurePN=true;
|
||||
break;
|
||||
case DIV_INS_POWERNOISE_SLOPE:
|
||||
featurePN=true;
|
||||
break;
|
||||
case DIV_INS_MAX:
|
||||
break;
|
||||
case DIV_INS_NULL:
|
||||
|
|
@ -1093,6 +1112,9 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song, bo
|
|||
if (esfm!=defaultIns.esfm) {
|
||||
featureEF=true;
|
||||
}
|
||||
if (powernoise!=defaultIns.powernoise) {
|
||||
featurePN=true;
|
||||
}
|
||||
}
|
||||
|
||||
// check ins name
|
||||
|
|
@ -1238,6 +1260,9 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song, bo
|
|||
if (featureEF) {
|
||||
writeFeatureEF(w);
|
||||
}
|
||||
if (featurePN) {
|
||||
writeFeaturePN(w);
|
||||
}
|
||||
|
||||
if (fui && (featureSL || featureWL)) {
|
||||
w->write("EN",2);
|
||||
|
|
@ -2673,6 +2698,14 @@ void DivInstrument::readFeatureEF(SafeReader& reader, short version) {
|
|||
READ_FEAT_END;
|
||||
}
|
||||
|
||||
void DivInstrument::readFeaturePN(SafeReader& reader, short version) {
|
||||
READ_FEAT_BEGIN;
|
||||
|
||||
powernoise.octave=reader.readC();
|
||||
|
||||
READ_FEAT_END;
|
||||
}
|
||||
|
||||
DivDataErrors DivInstrument::readInsDataNew(SafeReader& reader, short version, bool fui, DivSong* song) {
|
||||
unsigned char featCode[2];
|
||||
bool volIsCutoff=false;
|
||||
|
|
@ -2743,6 +2776,8 @@ DivDataErrors DivInstrument::readInsDataNew(SafeReader& reader, short version, b
|
|||
readFeatureNE(reader,version);
|
||||
} else if (memcmp(featCode,"EF",2)==0) { // ESFM
|
||||
readFeatureEF(reader,version);
|
||||
} else if (memcmp(featCode,"PN",2)==0) { // PowerNoise
|
||||
readFeaturePN(reader,version);
|
||||
} else {
|
||||
if (song==NULL && (memcmp(featCode,"SL",2)==0 || (memcmp(featCode,"WL",2)==0))) {
|
||||
// nothing
|
||||
|
|
|
|||
|
|
@ -86,6 +86,8 @@ enum DivInstrumentType: unsigned short {
|
|||
DIV_INS_C140=53,
|
||||
DIV_INS_C219=54,
|
||||
DIV_INS_ESFM=55,
|
||||
DIV_INS_POWERNOISE=56,
|
||||
DIV_INS_POWERNOISE_SLOPE=57,
|
||||
DIV_INS_MAX,
|
||||
DIV_INS_NULL
|
||||
};
|
||||
|
|
@ -822,6 +824,17 @@ struct DivInstrumentESFM {
|
|||
}
|
||||
};
|
||||
|
||||
struct DivInstrumentPowerNoise {
|
||||
unsigned char octave;
|
||||
|
||||
bool operator==(const DivInstrumentPowerNoise& other);
|
||||
bool operator!=(const DivInstrumentPowerNoise& other) {
|
||||
return !(*this==other);
|
||||
}
|
||||
DivInstrumentPowerNoise():
|
||||
octave(0) {}
|
||||
};
|
||||
|
||||
struct DivInstrument {
|
||||
String name;
|
||||
DivInstrumentType type;
|
||||
|
|
@ -839,6 +852,7 @@ struct DivInstrument {
|
|||
DivInstrumentES5506 es5506;
|
||||
DivInstrumentSNES snes;
|
||||
DivInstrumentESFM esfm;
|
||||
DivInstrumentPowerNoise powernoise;
|
||||
|
||||
/**
|
||||
* these are internal functions.
|
||||
|
|
@ -864,6 +878,7 @@ struct DivInstrument {
|
|||
void writeFeatureX1(SafeWriter* w);
|
||||
void writeFeatureNE(SafeWriter* w);
|
||||
void writeFeatureEF(SafeWriter* w);
|
||||
void writeFeaturePN(SafeWriter* w);
|
||||
|
||||
void readFeatureNA(SafeReader& reader, short version);
|
||||
void readFeatureFM(SafeReader& reader, short version);
|
||||
|
|
@ -885,6 +900,7 @@ struct DivInstrument {
|
|||
void readFeatureX1(SafeReader& reader, short version);
|
||||
void readFeatureNE(SafeReader& reader, short version);
|
||||
void readFeatureEF(SafeReader& reader, short version);
|
||||
void readFeaturePN(SafeReader& reader, short version);
|
||||
|
||||
DivDataErrors readInsDataOld(SafeReader& reader, short version);
|
||||
DivDataErrors readInsDataNew(SafeReader& reader, short version, bool fui, DivSong* song);
|
||||
|
|
|
|||
|
|
@ -370,20 +370,10 @@ void DivPlatformAmiga::tick(bool sysTick) {
|
|||
chan[i].outVol=((chan[i].vol%65)*MIN(64,chan[i].std.vol.val))>>6;
|
||||
chan[i].writeVol=true;
|
||||
}
|
||||
double off=1.0;
|
||||
if (!chan[i].useWave && chan[i].sample>=0 && chan[i].sample<parent->song.sampleLen) {
|
||||
DivSample* s=parent->getSample(chan[i].sample);
|
||||
if (s->centerRate<1) {
|
||||
off=1.0;
|
||||
} else {
|
||||
off=8363.0/(double)s->centerRate;
|
||||
}
|
||||
}
|
||||
if (NEW_ARP_STRAT) {
|
||||
chan[i].handleArp();
|
||||
} else if (chan[i].std.arp.had) {
|
||||
// TODO: why the off mult? this may be a bug!
|
||||
chan[i].baseFreq=round(off*NOTE_PERIODIC_NOROUND(parent->calcArp(chan[i].note,chan[i].std.arp.val)));
|
||||
chan[i].baseFreq=round(NOTE_PERIODIC_NOROUND(parent->calcArp(chan[i].note,chan[i].std.arp.val)));
|
||||
chan[i].freqChanged=true;
|
||||
}
|
||||
if (chan[i].useWave && chan[i].std.wave.had) {
|
||||
|
|
@ -577,10 +567,14 @@ int DivPlatformAmiga::dispatch(DivCommand c) {
|
|||
chan[c.chan].updateWave=true;
|
||||
}
|
||||
}
|
||||
chan[c.chan].sampleNote=DIV_NOTE_NULL;
|
||||
chan[c.chan].sampleNoteDelta=0;
|
||||
} else {
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].sample=ins->amiga.getSample(c.value);
|
||||
chan[c.chan].sampleNote=c.value;
|
||||
c.value=ins->amiga.getFreq(c.value);
|
||||
chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote;
|
||||
}
|
||||
chan[c.chan].useWave=false;
|
||||
}
|
||||
|
|
@ -657,9 +651,7 @@ int DivPlatformAmiga::dispatch(DivCommand c) {
|
|||
chan[c.chan].updateWave=true;
|
||||
break;
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
|
||||
chan[c.chan].sample=ins->amiga.getSample(c.value2);
|
||||
int destFreq=round(NOTE_PERIODIC_NOROUND(c.value2));
|
||||
int destFreq=round(NOTE_PERIODIC_NOROUND(c.value2+chan[c.chan].sampleNoteDelta));
|
||||
bool return2=false;
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
chan[c.chan].baseFreq+=c.value;
|
||||
|
|
@ -682,7 +674,7 @@ int DivPlatformAmiga::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_LEGATO: {
|
||||
chan[c.chan].baseFreq=round(NOTE_PERIODIC_NOROUND(c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0))));
|
||||
chan[c.chan].baseFreq=round(NOTE_PERIODIC_NOROUND(c.value+chan[c.chan].sampleNoteDelta+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0))));
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -405,7 +405,12 @@ int DivPlatformAY8910::dispatch(DivCommand c) {
|
|||
if (!parent->song.disableSampleMacro && (ins->type==DIV_INS_AMIGA || ins->amiga.useSample)) {
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].dac.sample=ins->amiga.getSample(c.value);
|
||||
chan[c.chan].sampleNote=c.value;
|
||||
c.value=ins->amiga.getFreq(c.value);
|
||||
chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote;
|
||||
} else if (chan[c.chan].sampleNote!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].dac.sample=ins->amiga.getSample(chan[c.chan].sampleNote);
|
||||
c.value=ins->amiga.getFreq(chan[c.chan].sampleNote);
|
||||
}
|
||||
if (chan[c.chan].dac.sample<0 || chan[c.chan].dac.sample>=parent->song.sampleLen) {
|
||||
chan[c.chan].dac.sample=-1;
|
||||
|
|
@ -457,6 +462,8 @@ int DivPlatformAY8910::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].sampleNote=DIV_NOTE_NULL;
|
||||
chan[c.chan].sampleNoteDelta=0;
|
||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
|
|
@ -526,7 +533,7 @@ int DivPlatformAY8910::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
int destFreq=NOTE_PERIODIC(c.value2);
|
||||
int destFreq=NOTE_PERIODIC(c.value2+chan[c.chan].sampleNoteDelta);
|
||||
bool return2=false;
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
chan[c.chan].baseFreq+=c.value;
|
||||
|
|
@ -549,7 +556,7 @@ int DivPlatformAY8910::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_LEGATO: {
|
||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
|
||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+chan[c.chan].sampleNoteDelta);
|
||||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -406,7 +406,12 @@ int DivPlatformAY8930::dispatch(DivCommand c) {
|
|||
if (ins->type==DIV_INS_AMIGA || ins->amiga.useSample) {
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].dac.sample=ins->amiga.getSample(c.value);
|
||||
chan[c.chan].sampleNote=c.value;
|
||||
c.value=ins->amiga.getFreq(c.value);
|
||||
chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote;
|
||||
} else if (chan[c.chan].sampleNote!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].dac.sample=ins->amiga.getSample(chan[c.chan].sampleNote);
|
||||
c.value=ins->amiga.getFreq(chan[c.chan].sampleNote);
|
||||
}
|
||||
if (chan[c.chan].dac.sample<0 || chan[c.chan].dac.sample>=parent->song.sampleLen) {
|
||||
chan[c.chan].dac.sample=-1;
|
||||
|
|
@ -458,6 +463,8 @@ int DivPlatformAY8930::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].sampleNote=DIV_NOTE_NULL;
|
||||
chan[c.chan].sampleNoteDelta=0;
|
||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
|
|
@ -520,7 +527,7 @@ int DivPlatformAY8930::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
int destFreq=NOTE_PERIODIC(c.value2);
|
||||
int destFreq=NOTE_PERIODIC(c.value2+chan[c.chan].sampleNoteDelta);
|
||||
bool return2=false;
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
chan[c.chan].baseFreq+=c.value;
|
||||
|
|
@ -543,7 +550,7 @@ int DivPlatformAY8930::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_LEGATO: {
|
||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
|
||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+chan[c.chan].sampleNoteDelta);
|
||||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -320,7 +320,9 @@ int DivPlatformC140::dispatch(DivCommand c) {
|
|||
chan[c.chan].macroPanMul=ins->type==DIV_INS_AMIGA?127:255;
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].sample=ins->amiga.getSample(c.value);
|
||||
chan[c.chan].sampleNote=c.value;
|
||||
c.value=ins->amiga.getFreq(c.value);
|
||||
chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote;
|
||||
}
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);
|
||||
|
|
@ -393,7 +395,7 @@ int DivPlatformC140::dispatch(DivCommand c) {
|
|||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
int destFreq=NOTE_FREQUENCY(c.value2);
|
||||
int destFreq=NOTE_FREQUENCY(c.value2+chan[c.chan].sampleNoteDelta);
|
||||
bool return2=false;
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
chan[c.chan].baseFreq+=c.value;
|
||||
|
|
@ -416,7 +418,7 @@ int DivPlatformC140::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_LEGATO: {
|
||||
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val-12):(0)));
|
||||
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value+chan[c.chan].sampleNoteDelta+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val-12):(0)));
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -719,27 +719,51 @@ void DivPlatformES5506::tick(bool sysTick) {
|
|||
}
|
||||
}
|
||||
|
||||
// man this code
|
||||
// part of the reason why it's so messy is because the chip is
|
||||
// overly complex and because when this pull request was made,
|
||||
// Furnace still was in an early state with no support for sample
|
||||
// maps or whatever...
|
||||
// one day I'll come back and clean this up
|
||||
int DivPlatformES5506::dispatch(DivCommand c) {
|
||||
switch (c.cmd) {
|
||||
case DIV_CMD_NOTE_ON: {
|
||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_ES5506);
|
||||
bool sampleValid=false;
|
||||
if (((ins->amiga.useNoteMap) && (c.value>=0 && c.value<120)) ||
|
||||
((!ins->amiga.useNoteMap) && (ins->amiga.initSample>=0 && ins->amiga.initSample<parent->song.sampleLen))) {
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
int sample=ins->amiga.getSample(c.value);
|
||||
chan[c.chan].sampleNote=c.value;
|
||||
if (sample>=0 && sample<parent->song.sampleLen) {
|
||||
sampleValid=true;
|
||||
chan[c.chan].volMacroMax=ins->type==DIV_INS_AMIGA?64:0xfff;
|
||||
chan[c.chan].panMacroMax=ins->type==DIV_INS_AMIGA?127:0xfff;
|
||||
chan[c.chan].pcm.next=ins->amiga.useNoteMap?c.value:sample;
|
||||
c.value=ins->amiga.getFreq(c.value);
|
||||
chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote;
|
||||
chan[c.chan].pcm.note=c.value;
|
||||
chan[c.chan].filter=ins->es5506.filter;
|
||||
chan[c.chan].envelope=ins->es5506.envelope;
|
||||
} else {
|
||||
chan[c.chan].sampleNoteDelta=0;
|
||||
}
|
||||
} else {
|
||||
int sample=ins->amiga.getSample(chan[c.chan].sampleNote);
|
||||
if (sample>=0 && sample<parent->song.sampleLen) {
|
||||
sampleValid=true;
|
||||
chan[c.chan].volMacroMax=ins->type==DIV_INS_AMIGA?64:0xfff;
|
||||
chan[c.chan].panMacroMax=ins->type==DIV_INS_AMIGA?127:0xfff;
|
||||
chan[c.chan].pcm.next=ins->amiga.useNoteMap?chan[c.chan].sampleNote:sample;
|
||||
c.value=ins->amiga.getFreq(chan[c.chan].sampleNote);
|
||||
chan[c.chan].pcm.note=c.value;
|
||||
chan[c.chan].filter=ins->es5506.filter;
|
||||
chan[c.chan].envelope=ins->es5506.envelope;
|
||||
} else {
|
||||
chan[c.chan].sampleNoteDelta=0;
|
||||
}
|
||||
}
|
||||
if (!sampleValid) {
|
||||
chan[c.chan].pcm.index=chan[c.chan].pcm.next=-1;
|
||||
chan[c.chan].sampleNoteDelta=0;
|
||||
chan[c.chan].filter=DivInstrumentES5506::Filter();
|
||||
chan[c.chan].envelope=DivInstrumentES5506::Envelope();
|
||||
}
|
||||
|
|
@ -962,7 +986,7 @@ int DivPlatformES5506::dispatch(DivCommand c) {
|
|||
break;
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
int nextFreq=chan[c.chan].baseFreq;
|
||||
const int destFreq=NOTE_ES5506(c.chan,c.value2);
|
||||
const int destFreq=NOTE_ES5506(c.chan,c.value2+chan[c.chan].sampleNoteDelta);
|
||||
bool return2=false;
|
||||
if (destFreq>nextFreq) {
|
||||
nextFreq+=c.value;
|
||||
|
|
@ -987,7 +1011,7 @@ int DivPlatformES5506::dispatch(DivCommand c) {
|
|||
}
|
||||
case DIV_CMD_LEGATO: {
|
||||
chan[c.chan].note=c.value;
|
||||
chan[c.chan].nextNote=chan[c.chan].note+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val-12):(0));
|
||||
chan[c.chan].nextNote=chan[c.chan].note+chan[c.chan].sampleNoteDelta+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val-12):(0));
|
||||
chan[c.chan].noteChanged.note=1;
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -207,7 +207,9 @@ int DivPlatformGA20::dispatch(DivCommand c) {
|
|||
chan[c.chan].macroVolMul=ins->type==DIV_INS_AMIGA?64:255;
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].sample=ins->amiga.getSample(c.value);
|
||||
chan[c.chan].sampleNote=c.value;
|
||||
c.value=ins->amiga.getFreq(c.value);
|
||||
chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote;
|
||||
}
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
|
||||
|
|
@ -263,7 +265,7 @@ int DivPlatformGA20::dispatch(DivCommand c) {
|
|||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
const int destFreq=NOTE_PERIODIC(c.value2);
|
||||
const int destFreq=NOTE_PERIODIC(c.value2+chan[c.chan].sampleNoteDelta);
|
||||
bool return2=false;
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
chan[c.chan].baseFreq+=c.value;
|
||||
|
|
@ -286,7 +288,7 @@ int DivPlatformGA20::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_LEGATO: {
|
||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val-12):(0)));
|
||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+chan[c.chan].sampleNoteDelta+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val-12):(0)));
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -704,8 +704,12 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
|
|||
} else if (chan[c.chan].furnaceDac) {
|
||||
chan[c.chan].dacMode=0;
|
||||
rWrite(0x2b,0<<7);
|
||||
chan[c.chan].sampleNote=DIV_NOTE_NULL;
|
||||
chan[c.chan].sampleNoteDelta=0;
|
||||
} else if (!chan[c.chan].dacMode) {
|
||||
rWrite(0x2b,0<<7);
|
||||
chan[c.chan].sampleNote=DIV_NOTE_NULL;
|
||||
chan[c.chan].sampleNoteDelta=0;
|
||||
}
|
||||
}
|
||||
if (c.chan>=5 && chan[c.chan].dacMode) {
|
||||
|
|
@ -713,7 +717,12 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
|
|||
if (ins->type==DIV_INS_AMIGA) { // Furnace mode
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].dacSample=ins->amiga.getSample(c.value);
|
||||
chan[c.chan].sampleNote=c.value;
|
||||
c.value=ins->amiga.getFreq(c.value);
|
||||
chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote;
|
||||
} else if (chan[c.chan].sampleNote!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].dacSample=ins->amiga.getSample(chan[c.chan].sampleNote);
|
||||
c.value=ins->amiga.getFreq(chan[c.chan].sampleNote);
|
||||
}
|
||||
if (chan[c.chan].dacSample<0 || chan[c.chan].dacSample>=parent->song.sampleLen) {
|
||||
chan[c.chan].dacSample=-1;
|
||||
|
|
@ -752,6 +761,8 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
|
|||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].note=c.value;
|
||||
}
|
||||
chan[c.chan].sampleNote=DIV_NOTE_NULL;
|
||||
chan[c.chan].sampleNoteDelta=0;
|
||||
chan[c.chan].dacSample=12*chan[c.chan].sampleBank+chan[c.chan].note%12;
|
||||
if (chan[c.chan].dacSample>=parent->song.sampleLen) {
|
||||
chan[c.chan].dacSample=-1;
|
||||
|
|
@ -863,7 +874,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
|
|||
}
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
if (parent->song.linearPitch==2) {
|
||||
int destFreq=NOTE_FREQUENCY(c.value2);
|
||||
int destFreq=NOTE_FREQUENCY(c.value2+chan[c.chan].sampleNoteDelta);
|
||||
bool return2=false;
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
chan[c.chan].baseFreq+=c.value;
|
||||
|
|
@ -909,7 +920,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
if (c.chan>=5 && chan[c.chan].furnaceDac && chan[c.chan].dacMode) {
|
||||
int destFreq=parent->calcBaseFreq(1,1,c.value2,false);
|
||||
int destFreq=parent->calcBaseFreq(1,1,c.value2+chan[c.chan].sampleNoteDelta,false);
|
||||
bool return2=false;
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
chan[c.chan].baseFreq+=c.value*16;
|
||||
|
|
@ -963,7 +974,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
|
|||
if (c.chan==csmChan) {
|
||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
|
||||
} else if (c.chan>=5 && chan[c.chan].furnaceDac && chan[c.chan].dacMode) {
|
||||
chan[c.chan].baseFreq=parent->calcBaseFreq(1,1,c.value,false);
|
||||
chan[c.chan].baseFreq=parent->calcBaseFreq(1,1,c.value+chan[c.chan].sampleNoteDelta,false);
|
||||
} else {
|
||||
if (chan[c.chan].insChanged) {
|
||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM);
|
||||
|
|
|
|||
|
|
@ -281,7 +281,9 @@ int DivPlatformK007232::dispatch(DivCommand c) {
|
|||
chan[c.chan].macroVolMul=ins->type==DIV_INS_AMIGA?64:15;
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].sample=ins->amiga.getSample(c.value);
|
||||
chan[c.chan].sampleNote=c.value;
|
||||
c.value=ins->amiga.getFreq(c.value);
|
||||
chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote;
|
||||
}
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
|
||||
|
|
@ -347,7 +349,7 @@ int DivPlatformK007232::dispatch(DivCommand c) {
|
|||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
const int destFreq=NOTE_PERIODIC(c.value2);
|
||||
const int destFreq=NOTE_PERIODIC(c.value2+chan[c.chan].sampleNoteDelta);
|
||||
bool return2=false;
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
chan[c.chan].baseFreq+=c.value;
|
||||
|
|
@ -370,7 +372,7 @@ int DivPlatformK007232::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_LEGATO: {
|
||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val-12):(0)));
|
||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+chan[c.chan].sampleNoteDelta+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val-12):(0)));
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -230,7 +230,9 @@ int DivPlatformK053260::dispatch(DivCommand c) {
|
|||
chan[c.chan].macroVolMul=ins->type==DIV_INS_AMIGA?64:127;
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].sample=ins->amiga.getSample(c.value);
|
||||
chan[c.chan].sampleNote=c.value;
|
||||
c.value=ins->amiga.getFreq(c.value);
|
||||
chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote;
|
||||
}
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
|
||||
|
|
@ -291,7 +293,7 @@ int DivPlatformK053260::dispatch(DivCommand c) {
|
|||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
int destFreq=NOTE_PERIODIC(c.value2);
|
||||
int destFreq=NOTE_PERIODIC(c.value2+chan[c.chan].sampleNoteDelta);
|
||||
bool return2=false;
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
chan[c.chan].baseFreq+=c.value;
|
||||
|
|
@ -314,7 +316,7 @@ int DivPlatformK053260::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_LEGATO: {
|
||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0)));
|
||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+chan[c.chan].sampleNoteDelta+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0)));
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -228,19 +228,28 @@ int DivPlatformLynx::dispatch(DivCommand c) {
|
|||
WRITE_CONTROL(c.chan,0x18);
|
||||
WRITE_BACKUP(c.chan,0);
|
||||
} else {
|
||||
chan[c.chan].sampleNote=DIV_NOTE_NULL;
|
||||
chan[c.chan].sampleNoteDelta=0;
|
||||
WRITE_FEEDBACK(c.chan,chan[c.chan].duty.feedback);
|
||||
WRITE_CONTROL(c.chan,(chan[c.chan].fd.clockDivider|0x18|chan[c.chan].duty.int_feedback7));
|
||||
WRITE_BACKUP(c.chan,chan[c.chan].fd.backup);
|
||||
}
|
||||
}
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
if (chan[c.chan].pcm) {
|
||||
if (chan[c.chan].pcm) {
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].sample=ins->amiga.getSample(c.value);
|
||||
chan[c.chan].sampleNote=c.value;
|
||||
c.value=ins->amiga.getFreq(c.value);
|
||||
chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote;
|
||||
chan[c.chan].sampleBaseFreq=NOTE_FREQUENCY(c.value);
|
||||
chan[c.chan].sampleAccum=0;
|
||||
chan[c.chan].samplePos=0;
|
||||
} else if (chan[c.chan].sampleNote!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].sample=ins->amiga.getSample(chan[c.chan].sampleNote);
|
||||
c.value=ins->amiga.getFreq(chan[c.chan].sampleNote);
|
||||
}
|
||||
chan[c.chan].sampleAccum=0;
|
||||
chan[c.chan].samplePos=0;
|
||||
}
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
|
|
@ -301,7 +310,7 @@ int DivPlatformLynx::dispatch(DivCommand c) {
|
|||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
int destFreq=NOTE_PERIODIC(c.value2);
|
||||
int destFreq=NOTE_PERIODIC(c.value2+chan[c.chan].sampleNoteDelta);
|
||||
bool return2=false;
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
chan[c.chan].baseFreq+=c.value;
|
||||
|
|
@ -327,7 +336,7 @@ int DivPlatformLynx::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_LEGATO: {
|
||||
int whatAMess=c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0));
|
||||
int whatAMess=c.value+chan[c.chan].sampleNoteDelta+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0));
|
||||
chan[c.chan].baseFreq=NOTE_PERIODIC(whatAMess);
|
||||
if (chan[c.chan].pcm) {
|
||||
chan[c.chan].sampleBaseFreq=NOTE_FREQUENCY(whatAMess);
|
||||
|
|
|
|||
|
|
@ -178,7 +178,12 @@ int DivPlatformMMC5::dispatch(DivCommand c) {
|
|||
if (ins->type==DIV_INS_AMIGA) {
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
dacSample=ins->amiga.getSample(c.value);
|
||||
chan[c.chan].sampleNote=c.value;
|
||||
c.value=ins->amiga.getFreq(c.value);
|
||||
chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote;
|
||||
} else if (chan[c.chan].sampleNote!=DIV_NOTE_NULL) {
|
||||
dacSample=ins->amiga.getSample(chan[c.chan].sampleNote);
|
||||
c.value=ins->amiga.getFreq(chan[c.chan].sampleNote);
|
||||
}
|
||||
if (dacSample<0 || dacSample>=parent->song.sampleLen) {
|
||||
dacSample=-1;
|
||||
|
|
@ -189,8 +194,8 @@ int DivPlatformMMC5::dispatch(DivCommand c) {
|
|||
}
|
||||
dacPos=0;
|
||||
dacPeriod=0;
|
||||
chan[c.chan].baseFreq=parent->calcBaseFreq(1,1,c.value,false);
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].baseFreq=parent->calcBaseFreq(1,1,c.value,false);
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
}
|
||||
|
|
@ -270,7 +275,7 @@ int DivPlatformMMC5::dispatch(DivCommand c) {
|
|||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
int destFreq=(c.chan==2)?(parent->calcBaseFreq(1,1,c.value2,false)):(NOTE_PERIODIC(c.value2));
|
||||
int destFreq=(c.chan==2)?(parent->calcBaseFreq(1,1,c.value2+chan[c.chan].sampleNoteDelta,false)):(NOTE_PERIODIC(c.value2));
|
||||
bool return2=false;
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
chan[c.chan].baseFreq+=c.value;
|
||||
|
|
@ -304,7 +309,7 @@ int DivPlatformMMC5::dispatch(DivCommand c) {
|
|||
break;
|
||||
case DIV_CMD_LEGATO:
|
||||
if (c.chan==2) {
|
||||
chan[c.chan].baseFreq=parent->calcBaseFreq(1,1,c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0)),false);
|
||||
chan[c.chan].baseFreq=parent->calcBaseFreq(1,1,c.value+chan[c.chan].sampleNoteDelta+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0)),false);
|
||||
} else {
|
||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0)));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -407,7 +407,14 @@ int DivPlatformNES::dispatch(DivCommand c) {
|
|||
if (c.value!=DIV_NOTE_NULL) {
|
||||
dacSample=ins->amiga.getSample(c.value);
|
||||
if (ins->type==DIV_INS_AMIGA) {
|
||||
chan[c.chan].sampleNote=c.value;
|
||||
c.value=ins->amiga.getFreq(c.value);
|
||||
chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote;
|
||||
}
|
||||
} else if (chan[c.chan].sampleNote!=DIV_NOTE_NULL) {
|
||||
dacSample=ins->amiga.getSample(chan[c.chan].sampleNote);
|
||||
if (ins->type==DIV_INS_AMIGA) {
|
||||
c.value=ins->amiga.getFreq(chan[c.chan].sampleNote);
|
||||
}
|
||||
}
|
||||
if (dacSample<0 || dacSample>=parent->song.sampleLen) {
|
||||
|
|
@ -535,7 +542,7 @@ int DivPlatformNES::dispatch(DivCommand c) {
|
|||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
int destFreq=(c.chan==4)?(parent->calcBaseFreq(1,1,c.value2,false)):(NOTE_PERIODIC(c.value2));
|
||||
int destFreq=(c.chan==4)?(parent->calcBaseFreq(1,1,c.value2+chan[c.chan].sampleNoteDelta,false)):(NOTE_PERIODIC(c.value2));
|
||||
bool return2=false;
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
chan[c.chan].baseFreq+=c.value;
|
||||
|
|
@ -636,7 +643,7 @@ int DivPlatformNES::dispatch(DivCommand c) {
|
|||
case DIV_CMD_LEGATO:
|
||||
if (c.chan==3) break;
|
||||
if (c.chan==4) {
|
||||
chan[c.chan].baseFreq=parent->calcBaseFreq(1,1,c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0)),false);
|
||||
chan[c.chan].baseFreq=parent->calcBaseFreq(1,1,c.value+chan[c.chan].sampleNoteDelta+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0)),false);
|
||||
} else {
|
||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0)));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -515,6 +515,18 @@ static const int cycleMapDrums[18]={
|
|||
0, 1, 2, 3, 4, 5, 3, 4, 5,
|
||||
};
|
||||
|
||||
static const int cycleMap3[36]={
|
||||
14, 12, 13, 14, 0, 2, 4, 0, 2,
|
||||
4, 1, 3, 5, 1, 3, 5, 15, 16,
|
||||
17, 15, 16, 17, 6, 8, 10, 6, 8, 10, 7, 9, 11, 7, 9, 11, 12, 13
|
||||
};
|
||||
|
||||
static const int cycleMap3Drums[36]={
|
||||
14, 12, 13, 14, 0, 2, 4, 0, 2,
|
||||
4, 1, 3, 5, 1, 3, 5, 15, 19,
|
||||
17, 15, 16, 18, 6, 8, 10, 6, 8, 10, 7, 9, 11, 7, 9, 11, 12, 13
|
||||
};
|
||||
|
||||
void DivPlatformOPL::acquire_nukedLLE2(short** buf, size_t len) {
|
||||
int chOut[11];
|
||||
thread_local ymfm::ymfm_output<2> aOut;
|
||||
|
|
@ -662,10 +674,11 @@ void DivPlatformOPL::acquire_nukedLLE2(short** buf, size_t len) {
|
|||
|
||||
void DivPlatformOPL::acquire_nukedLLE3(short** buf, size_t len) {
|
||||
int chOut[20];
|
||||
int ch=0;
|
||||
|
||||
for (size_t h=0; h<len; h++) {
|
||||
//int curCycle=0;
|
||||
//unsigned char subCycle=0;
|
||||
int curCycle=0;
|
||||
unsigned char subCycle=0;
|
||||
|
||||
for (int i=0; i<20; i++) {
|
||||
chOut[i]=0;
|
||||
|
|
@ -721,10 +734,24 @@ void DivPlatformOPL::acquire_nukedLLE3(short** buf, size_t len) {
|
|||
if (--delay<0) waitingBusy=false;
|
||||
}
|
||||
|
||||
/*if (!(++subCycle&3)) {
|
||||
// TODO: chan osc
|
||||
if (!(++subCycle&1)) {
|
||||
if (properDrums) {
|
||||
ch=cycleMap3Drums[curCycle];
|
||||
} else {
|
||||
ch=cycleMap3[curCycle];
|
||||
}
|
||||
|
||||
if (ch<12 && !(ch&1) && chan[ch&(~1)].state.alg>0) ch|=1;
|
||||
|
||||
if (ch>=12 || (ch&1) || !chan[ch&(~1)].fourOp) {
|
||||
if (fm_lle3.op_value_debug&0x1000) {
|
||||
chOut[ch]+=(fm_lle3.op_value_debug|0xfffff000)<<1;
|
||||
} else {
|
||||
chOut[ch]+=(fm_lle3.op_value_debug)<<1;
|
||||
}
|
||||
}
|
||||
curCycle++;
|
||||
}*/
|
||||
}
|
||||
|
||||
if (fm_lle3.o_sy && !lastSY) {
|
||||
dacVal>>=1;
|
||||
|
|
@ -746,11 +773,11 @@ void DivPlatformOPL::acquire_nukedLLE3(short** buf, size_t len) {
|
|||
}
|
||||
|
||||
for (int i=0; i<20; i++) {
|
||||
if (i>=15 && properDrums) {
|
||||
/*if (i>=15 && properDrums) {
|
||||
chOut[i]<<=1;
|
||||
} else {
|
||||
chOut[i]<<=2;
|
||||
}
|
||||
}*/
|
||||
if (chOut[i]<-32768) chOut[i]=-32768;
|
||||
if (chOut[i]>32767) chOut[i]=32767;
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=chOut[i];
|
||||
|
|
|
|||
|
|
@ -278,6 +278,8 @@ int DivPlatformPCE::dispatch(DivCommand c) {
|
|||
chan[c.chan].pcm=true;
|
||||
} else if (chan[c.chan].furnaceDac) {
|
||||
chan[c.chan].pcm=false;
|
||||
chan[c.chan].sampleNote=DIV_NOTE_NULL;
|
||||
chan[c.chan].sampleNoteDelta=0;
|
||||
}
|
||||
if (chan[c.chan].pcm) {
|
||||
if (ins->type==DIV_INS_AMIGA || ins->amiga.useSample) {
|
||||
|
|
@ -285,7 +287,12 @@ int DivPlatformPCE::dispatch(DivCommand c) {
|
|||
if (skipRegisterWrites) break;
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].dacSample=ins->amiga.getSample(c.value);
|
||||
chan[c.chan].sampleNote=c.value;
|
||||
c.value=ins->amiga.getFreq(c.value);
|
||||
chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote;
|
||||
} else if (chan[c.chan].sampleNote!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].dacSample=ins->amiga.getSample(chan[c.chan].sampleNote);
|
||||
c.value=ins->amiga.getFreq(chan[c.chan].sampleNote);
|
||||
}
|
||||
if (chan[c.chan].dacSample<0 || chan[c.chan].dacSample>=parent->song.sampleLen) {
|
||||
chan[c.chan].dacSample=-1;
|
||||
|
|
@ -312,6 +319,8 @@ int DivPlatformPCE::dispatch(DivCommand c) {
|
|||
//chan[c.chan].keyOn=true;
|
||||
} else {
|
||||
chan[c.chan].furnaceDac=false;
|
||||
chan[c.chan].sampleNote=DIV_NOTE_NULL;
|
||||
chan[c.chan].sampleNoteDelta=0;
|
||||
if (skipRegisterWrites) break;
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].note=c.value;
|
||||
|
|
@ -334,6 +343,8 @@ int DivPlatformPCE::dispatch(DivCommand c) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
chan[c.chan].sampleNote=DIV_NOTE_NULL;
|
||||
chan[c.chan].sampleNoteDelta=0;
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
|
||||
chan[c.chan].freqChanged=true;
|
||||
|
|
@ -413,7 +424,7 @@ int DivPlatformPCE::dispatch(DivCommand c) {
|
|||
updateLFO=true;
|
||||
break;
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
int destFreq=NOTE_PERIODIC(c.value2);
|
||||
int destFreq=NOTE_PERIODIC(c.value2+chan[c.chan].sampleNoteDelta);
|
||||
bool return2=false;
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
chan[c.chan].baseFreq+=c.value;
|
||||
|
|
@ -454,7 +465,7 @@ int DivPlatformPCE::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_LEGATO:
|
||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0)));
|
||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+chan[c.chan].sampleNoteDelta+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0)));
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -314,6 +314,8 @@ int DivPlatformPCMDAC::dispatch(DivCommand c) {
|
|||
case DIV_CMD_NOTE_ON: {
|
||||
DivInstrument* ins=parent->getIns(chan[0].ins,DIV_INS_AMIGA);
|
||||
if (ins->amiga.useWave) {
|
||||
chan[c.chan].sampleNote=DIV_NOTE_NULL;
|
||||
chan[c.chan].sampleNoteDelta=0;
|
||||
chan[0].useWave=true;
|
||||
chan[0].audLen=ins->amiga.waveLen+1;
|
||||
if (chan[0].insChanged) {
|
||||
|
|
@ -326,7 +328,12 @@ int DivPlatformPCMDAC::dispatch(DivCommand c) {
|
|||
} else {
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[0].sample=ins->amiga.getSample(c.value);
|
||||
chan[c.chan].sampleNote=c.value;
|
||||
c.value=ins->amiga.getFreq(c.value);
|
||||
chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote;
|
||||
} else if (chan[c.chan].sampleNote!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].sample=ins->amiga.getSample(chan[c.chan].sampleNote);
|
||||
c.value=ins->amiga.getFreq(chan[c.chan].sampleNote);
|
||||
}
|
||||
chan[0].useWave=false;
|
||||
}
|
||||
|
|
@ -335,6 +342,8 @@ int DivPlatformPCMDAC::dispatch(DivCommand c) {
|
|||
}
|
||||
if (chan[0].useWave || chan[0].sample<0 || chan[0].sample>=parent->song.sampleLen) {
|
||||
chan[0].sample=-1;
|
||||
chan[c.chan].sampleNote=DIV_NOTE_NULL;
|
||||
chan[c.chan].sampleNoteDelta=0;
|
||||
}
|
||||
if (chan[0].setPos) {
|
||||
chan[0].setPos=false;
|
||||
|
|
@ -402,9 +411,7 @@ int DivPlatformPCMDAC::dispatch(DivCommand c) {
|
|||
chan[0].ws.changeWave1(chan[0].wave);
|
||||
break;
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
DivInstrument* ins=parent->getIns(chan[0].ins,DIV_INS_AMIGA);
|
||||
chan[0].sample=ins->amiga.getSample(c.value2);
|
||||
int destFreq=round(NOTE_FREQUENCY(c.value2));
|
||||
int destFreq=round(NOTE_FREQUENCY(c.value2+chan[c.chan].sampleNoteDelta));
|
||||
bool return2=false;
|
||||
if (destFreq>chan[0].baseFreq) {
|
||||
chan[0].baseFreq+=c.value;
|
||||
|
|
@ -427,7 +434,7 @@ int DivPlatformPCMDAC::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_LEGATO: {
|
||||
chan[0].baseFreq=round(NOTE_FREQUENCY(c.value+((HACKY_LEGATO_MESS)?(chan[0].std.arp.val):(0))));
|
||||
chan[0].baseFreq=round(NOTE_FREQUENCY(c.value+chan[c.chan].sampleNoteDelta+((HACKY_LEGATO_MESS)?(chan[0].std.arp.val):(0))));
|
||||
chan[0].freqChanged=true;
|
||||
chan[0].note=c.value;
|
||||
break;
|
||||
|
|
|
|||
541
src/engine/platform/powernoise.cpp
Normal file
541
src/engine/platform/powernoise.cpp
Normal file
|
|
@ -0,0 +1,541 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2024 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "powernoise.h"
|
||||
#include "../engine.h"
|
||||
#include "../../ta-log.h"
|
||||
#include "furIcons.h"
|
||||
#include <math.h>
|
||||
#include "../bsr.h"
|
||||
|
||||
#define rWrite(a,v) if (!skipRegisterWrites) {regPool[a]=(v); pwrnoise_write(&pn,(unsigned char)(a),(unsigned char)(v)); if (dumpWrites) {addWrite(a,v);}}
|
||||
#define chWrite(c,a,v) rWrite((c<<3)|((a)+1),(v))
|
||||
#define noiseCtl(enable,am,tapB) (((enable)?0x80:0x00)|((am)?0x02:0x00)|((tapB)?0x01:0x00))
|
||||
#define slopeCtl(enable,rst,a,b) (((enable)?0x80:0x00)| \
|
||||
(rst?0x40:0x00)| \
|
||||
(a.clip?0x20:0x00)| \
|
||||
(b.clip?0x10:0x00)| \
|
||||
(a.reset?0x08:0x00)| \
|
||||
(b.reset?0x04:0x00)| \
|
||||
(a.dir?0x02:0x00)| \
|
||||
(b.dir?0x01:0x00))
|
||||
#define volPan(v,p) (((v*(p>>4)/15)<<4)|((v*(p&0xf)/15)&0xf))
|
||||
// TODO: optimize!
|
||||
#define mapAmp(a) ((((a)*65535/63-32768)*(pn.flags&0x7)/7)>>1)
|
||||
|
||||
const char* regCheatSheetPowerNoise[]={
|
||||
"ACTL", "00",
|
||||
"N1CTL", "01",
|
||||
"N1FL", "02",
|
||||
"N1FH", "03",
|
||||
"N1SRL", "04",
|
||||
"N1SRH", "05",
|
||||
"N1TAP", "06",
|
||||
"N1V", "07",
|
||||
"IOA", "08",
|
||||
"N2CTL", "09",
|
||||
"N2FL", "0A",
|
||||
"N2FH", "0B",
|
||||
"N2SRL", "0C",
|
||||
"N2SRH", "0D",
|
||||
"N2TAP", "0E",
|
||||
"N2V", "0F",
|
||||
"IOB", "10",
|
||||
"N3CTL", "11",
|
||||
"N3FL", "12",
|
||||
"N3FH", "13",
|
||||
"N3SRL", "14",
|
||||
"N3SRH", "15",
|
||||
"N3TAP", "16",
|
||||
"N3V", "17",
|
||||
"SLACC", "18",
|
||||
"SLCTL", "19",
|
||||
"SLFL", "1A",
|
||||
"SLFH", "1B",
|
||||
"SLPA", "1C",
|
||||
"SLPB", "1D",
|
||||
"SLPO", "1E",
|
||||
"SLV", "1F",
|
||||
NULL
|
||||
};
|
||||
|
||||
const char** DivPlatformPowerNoise::getRegisterSheet() {
|
||||
return regCheatSheetPowerNoise;
|
||||
}
|
||||
|
||||
void DivPlatformPowerNoise::acquire(short** buf, size_t len) {
|
||||
short left, right;
|
||||
|
||||
for (size_t h=0; h<len; h++) {
|
||||
pwrnoise_step(&pn,32,&left,&right);
|
||||
|
||||
oscBuf[0]->data[oscBuf[0]->needle++]=mapAmp((pn.n1.out_latch&0xf)+(pn.n1.out_latch>>4));
|
||||
oscBuf[1]->data[oscBuf[1]->needle++]=mapAmp((pn.n2.out_latch&0xf)+(pn.n2.out_latch>>4));
|
||||
oscBuf[2]->data[oscBuf[2]->needle++]=mapAmp((pn.n3.out_latch&0xf)+(pn.n3.out_latch>>4));
|
||||
oscBuf[3]->data[oscBuf[3]->needle++]=mapAmp((pn.s.out_latch&0xf)+(pn.s.out_latch>>4));
|
||||
|
||||
buf[0][h]=left;
|
||||
buf[1][h]=right;
|
||||
}
|
||||
}
|
||||
|
||||
/* macros:
|
||||
* EX1 - control (0-63)
|
||||
* EX2 - portion A length (0-255) - slope only
|
||||
* EX3 - portion B length (0-255) - slope only
|
||||
* EX4 - tap A location (0-15) - noise only
|
||||
* EX5 - tap B location (0-15) - noise only
|
||||
* EX6 - portion A offset (0-15) - slope only
|
||||
* EX7 - portion B offset (0-15) - slope only
|
||||
* EX8 - load LFSR (0-65535) - noise only
|
||||
*/
|
||||
|
||||
void DivPlatformPowerNoise::tick(bool sysTick) {
|
||||
for (int i=0; i<4; i++) {
|
||||
int CHIP_DIVIDER=2;
|
||||
if (i==3) CHIP_DIVIDER=128;
|
||||
chan[i].std.next();
|
||||
|
||||
if (chan[i].std.ex1.had) {
|
||||
int val=chan[i].std.ex1.val;
|
||||
|
||||
if (chan[i].slope) {
|
||||
chan[i].slopeA.clip=(val&0x20);
|
||||
chan[i].slopeB.clip=(val&0x10);
|
||||
chan[i].slopeA.reset=(val&0x08);
|
||||
chan[i].slopeB.reset=(val&0x04);
|
||||
chan[i].slopeA.dir=(val&0x02);
|
||||
chan[i].slopeB.dir=(val&0x01);
|
||||
chWrite(i,0x00,slopeCtl(chan[i].active,false,chan[i].slopeA,chan[i].slopeB));
|
||||
} else {
|
||||
chan[i].am=(val&0x02);
|
||||
chan[i].tapBEnable=(val&0x01);
|
||||
chWrite(i,0x00,noiseCtl(chan[i].active, chan[i].am, chan[i].tapBEnable));
|
||||
}
|
||||
}
|
||||
if (chan[i].slope) {
|
||||
if (chan[i].std.ex2.had) {
|
||||
chan[i].slopeA.len=chan[i].std.ex2.val;
|
||||
chWrite(i,0x03,chan[i].std.ex2.val);
|
||||
}
|
||||
if (chan[i].std.ex3.had) {
|
||||
chan[i].slopeB.len=chan[i].std.ex3.val;
|
||||
chWrite(i,0x04,chan[i].std.ex3.val);
|
||||
}
|
||||
if (chan[i].std.ex6.had) {
|
||||
chan[i].slopeA.offset=chan[i].std.ex6.val;
|
||||
}
|
||||
if (chan[i].std.ex7.had) {
|
||||
chan[i].slopeB.offset=chan[i].std.ex7.val;
|
||||
}
|
||||
if (chan[i].std.ex6.had || chan[i].std.ex7.had) {
|
||||
chWrite(i,0x05,(chan[i].slopeA.offset<<4)|chan[i].slopeB.offset);
|
||||
}
|
||||
} else {
|
||||
if (chan[i].std.ex4.had) {
|
||||
chan[i].tapA=chan[i].std.ex4.val;
|
||||
}
|
||||
if (chan[i].std.ex5.had) {
|
||||
chan[i].tapB=chan[i].std.ex5.val;
|
||||
}
|
||||
if (chan[i].std.ex4.had || chan[i].std.ex5.had) {
|
||||
chWrite(i,0x05,(chan[i].tapA<<4)|chan[i].tapB);
|
||||
}
|
||||
if (chan[i].std.ex8.had) {
|
||||
if (chan[i].initLFSR!=(chan[i].std.ex8.val&0xffff)) {
|
||||
chan[i].initLFSR=chan[i].std.ex8.val&0xffff;
|
||||
chWrite(i,0x03,chan[i].std.ex8.val&0xff);
|
||||
chWrite(i,0x04,chan[i].std.ex8.val>>8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (chan[i].std.vol.had) {
|
||||
chan[i].outVol=VOL_SCALE_LINEAR(chan[i].vol&15,MIN(15,chan[i].std.vol.val),15);
|
||||
if (chan[i].outVol<0) chan[i].outVol=0;
|
||||
}
|
||||
if (NEW_ARP_STRAT) {
|
||||
chan[i].handleArp();
|
||||
} else if (chan[i].std.arp.had) {
|
||||
if (!chan[i].inPorta) {
|
||||
chan[i].baseFreq=NOTE_PERIODIC(parent->calcArp(chan[i].note,chan[i].std.arp.val));
|
||||
}
|
||||
chan[i].freqChanged=true;
|
||||
}
|
||||
if (chan[i].std.panL.had) {
|
||||
chan[i].pan&=0x0f;
|
||||
chan[i].pan|=(chan[i].std.panL.val&15)<<4;
|
||||
}
|
||||
if (chan[i].std.panR.had) {
|
||||
chan[i].pan&=0xf0;
|
||||
chan[i].pan|=chan[i].std.panR.val&15;
|
||||
}
|
||||
|
||||
if (chan[i].std.vol.had || chan[i].std.panL.had || chan[i].std.panR.had) {
|
||||
chWrite(i,0x06,isMuted[i]?0:volPan(chan[i].outVol,chan[i].pan));
|
||||
}
|
||||
if (chan[i].std.pitch.had) {
|
||||
if (chan[i].std.pitch.mode) {
|
||||
chan[i].pitch2+=chan[i].std.pitch.val;
|
||||
CLAMP_VAR(chan[i].pitch2,-32768,32767);
|
||||
} else {
|
||||
chan[i].pitch2=chan[i].std.pitch.val;
|
||||
}
|
||||
chan[i].freqChanged=true;
|
||||
}
|
||||
if (chan[i].std.phaseReset.had && chan[i].std.phaseReset.val==1) {
|
||||
if (chan[i].slope) {
|
||||
chWrite(i,0x00,slopeCtl(chan[i].active,true,chan[i].slopeA,chan[i].slopeB));
|
||||
chan[i].keyOn=true;
|
||||
} else {
|
||||
chWrite(i,0x03,chan[i].initLFSR&0xff);
|
||||
chWrite(i,0x04,chan[i].initLFSR>>8);
|
||||
}
|
||||
}
|
||||
|
||||
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER);
|
||||
chan[i].freq>>=chan[i].octaveOff;
|
||||
|
||||
if (chan[i].freq<0) chan[i].freq=0;
|
||||
if (chan[i].freq>0x7ffffff) chan[i].freq=0x7ffffff;
|
||||
int bsr32Val=bsr32(chan[i].freq);
|
||||
chan[i].octave=MAX(bsr32Val-12,0);
|
||||
if (chan[i].octave>15) chan[i].octave=15;
|
||||
chan[i].fNum=0x1000-(chan[i].freq>>chan[i].octave);
|
||||
if (chan[i].fNum<0) chan[i].fNum=0;
|
||||
if (chan[i].fNum>4095) chan[i].fNum=4095;
|
||||
|
||||
chWrite(i,0x01,chan[i].fNum&0xff);
|
||||
chWrite(i,0x02,(chan[i].fNum>>8)|(chan[i].octave<<4));
|
||||
|
||||
if (chan[i].keyOn) {
|
||||
if (chan[i].slope) {
|
||||
chWrite(i,0x00,slopeCtl(true,false,chan[i].slopeA,chan[i].slopeB));
|
||||
} else {
|
||||
chWrite(i,0x00,noiseCtl(true,chan[i].am,chan[i].tapBEnable));
|
||||
}
|
||||
}
|
||||
if (chan[i].keyOff) {
|
||||
if (chan[i].slope) {
|
||||
chWrite(i,0x00,slopeCtl(false,false,chan[i].slopeA,chan[i].slopeB));
|
||||
} else {
|
||||
chWrite(i,0x00,noiseCtl(false,chan[i].am,chan[i].tapBEnable));
|
||||
}
|
||||
}
|
||||
|
||||
if (chan[i].keyOn) chan[i].keyOn=false;
|
||||
if (chan[i].keyOff) chan[i].keyOff=false;
|
||||
chan[i].freqChanged=false;
|
||||
}
|
||||
|
||||
if (chan[i].slope) {
|
||||
unsigned char counter=pn.s.accum;
|
||||
regPool[0x18]=counter;
|
||||
} else {
|
||||
unsigned short lfsr;
|
||||
if (i==0) {
|
||||
lfsr=pn.n1.lfsr;
|
||||
} else if (i==1) {
|
||||
lfsr=pn.n2.lfsr;
|
||||
} else {
|
||||
lfsr=pn.n3.lfsr;
|
||||
}
|
||||
regPool[(i<<3)+0x4]=lfsr&0xff;
|
||||
regPool[(i<<3)+0x5]=lfsr>>8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int DivPlatformPowerNoise::dispatch(DivCommand c) {
|
||||
int CHIP_DIVIDER=2;
|
||||
if (c.chan==3) CHIP_DIVIDER=128;
|
||||
|
||||
switch (c.cmd) {
|
||||
case DIV_CMD_NOTE_ON: {
|
||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_POWERNOISE);
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
}
|
||||
if (chan[c.chan].insChanged) {
|
||||
chan[c.chan].octaveOff=ins->powernoise.octave;
|
||||
}
|
||||
chan[c.chan].active=true;
|
||||
chan[c.chan].macroInit(ins);
|
||||
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
|
||||
chan[c.chan].outVol=chan[c.chan].vol;
|
||||
}
|
||||
chan[c.chan].keyOn=true;
|
||||
chan[c.chan].insChanged=false;
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_NOTE_OFF:
|
||||
chan[c.chan].active=false;
|
||||
chan[c.chan].keyOff=true;
|
||||
chan[c.chan].macroInit(NULL);
|
||||
break;
|
||||
case DIV_CMD_NOTE_OFF_ENV:
|
||||
case DIV_CMD_ENV_RELEASE:
|
||||
chan[c.chan].std.release();
|
||||
break;
|
||||
case DIV_CMD_INSTRUMENT:
|
||||
if (chan[c.chan].ins!=c.value || c.value2==1) {
|
||||
chan[c.chan].ins=c.value;
|
||||
chan[c.chan].insChanged=true;
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_VOLUME:
|
||||
if (chan[c.chan].vol!=c.value) {
|
||||
chan[c.chan].vol=c.value;
|
||||
if (!chan[c.chan].std.vol.has) {
|
||||
chan[c.chan].outVol=c.value;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_GET_VOLUME:
|
||||
if (chan[c.chan].std.vol.has) {
|
||||
return chan[c.chan].vol;
|
||||
}
|
||||
return chan[c.chan].outVol;
|
||||
break;
|
||||
case DIV_CMD_PITCH:
|
||||
chan[c.chan].pitch=c.value;
|
||||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
int destFreq=NOTE_PERIODIC(c.value2);
|
||||
|
||||
bool return2=false;
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
chan[c.chan].baseFreq+=c.value;
|
||||
if (chan[c.chan].baseFreq>0x7ffffff) {
|
||||
chan[c.chan].baseFreq=0x7ffffff;
|
||||
}
|
||||
if (chan[c.chan].baseFreq>=destFreq) {
|
||||
chan[c.chan].baseFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
} else {
|
||||
chan[c.chan].baseFreq-=c.value;
|
||||
if (chan[c.chan].baseFreq<0) {
|
||||
chan[c.chan].baseFreq=0;
|
||||
}
|
||||
if (chan[c.chan].baseFreq<=destFreq) {
|
||||
chan[c.chan].baseFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
}
|
||||
chan[c.chan].freqChanged=true;
|
||||
if (return2) {
|
||||
chan[c.chan].inPorta=false;
|
||||
return 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_PANNING:
|
||||
chan[c.chan].pan=(c.value&0xf0)|(c.value2>>4);
|
||||
break;
|
||||
case DIV_CMD_LEGATO: {
|
||||
int whatAMess=c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0));
|
||||
|
||||
chan[c.chan].baseFreq=NOTE_PERIODIC(whatAMess);
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_PRE_PORTA: {
|
||||
if (chan[c.chan].active && c.value2) {
|
||||
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_POWERNOISE));
|
||||
}
|
||||
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) {
|
||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
|
||||
}
|
||||
chan[c.chan].inPorta=c.value;
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_GET_VOLMAX:
|
||||
return 15;
|
||||
break;
|
||||
case DIV_CMD_POWERNOISE_COUNTER_LOAD: {
|
||||
if (chan[c.chan].slope && c.value==0) {
|
||||
rWrite(0x18,c.value2&0x7f);
|
||||
} else if (!chan[c.chan].slope) {
|
||||
chWrite(c.chan,0x03+c.value,c.value2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_POWERNOISE_IO_WRITE:
|
||||
rWrite(0x08+(c.value<<3),c.value2);
|
||||
break;
|
||||
case DIV_CMD_MACRO_OFF:
|
||||
chan[c.chan].std.mask(c.value,true);
|
||||
break;
|
||||
case DIV_CMD_MACRO_ON:
|
||||
chan[c.chan].std.mask(c.value,false);
|
||||
break;
|
||||
case DIV_CMD_MACRO_RESTART:
|
||||
chan[c.chan].std.restart(c.value);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void DivPlatformPowerNoise::muteChannel(int ch, bool mute) {
|
||||
isMuted[ch]=mute;
|
||||
chWrite(ch,0x06,isMuted[ch]?0:volPan(chan[ch].outVol,chan[ch].pan));
|
||||
}
|
||||
|
||||
void DivPlatformPowerNoise::forceIns() {
|
||||
for (int i=0; i<4; i++) {
|
||||
chan[i].insChanged=true;
|
||||
chan[i].freqChanged=true;
|
||||
if (i<3) {
|
||||
chWrite(i,0x03,chan[i].initLFSR&0xff);
|
||||
chWrite(i,0x04,chan[i].initLFSR>>8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void* DivPlatformPowerNoise::getChanState(int ch) {
|
||||
return &chan[ch];
|
||||
}
|
||||
|
||||
DivMacroInt* DivPlatformPowerNoise::getChanMacroInt(int ch) {
|
||||
return &chan[ch].std;
|
||||
}
|
||||
|
||||
unsigned short DivPlatformPowerNoise::getPan(int ch) {
|
||||
return ((chan[ch].pan&0xf0)<<4)|(chan[ch].pan&15);
|
||||
}
|
||||
|
||||
DivChannelModeHints DivPlatformPowerNoise::getModeHints(int ch) {
|
||||
DivChannelModeHints ret;
|
||||
ret.count=0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool DivPlatformPowerNoise::getDCOffRequired() {
|
||||
return true;
|
||||
}
|
||||
|
||||
DivDispatchOscBuffer* DivPlatformPowerNoise::getOscBuffer(int ch) {
|
||||
return oscBuf[ch];
|
||||
}
|
||||
|
||||
unsigned char* DivPlatformPowerNoise::getRegisterPool() {
|
||||
return regPool;
|
||||
}
|
||||
|
||||
int DivPlatformPowerNoise::getRegisterPoolSize() {
|
||||
return 32;
|
||||
}
|
||||
|
||||
void DivPlatformPowerNoise::reset() {
|
||||
memset(regPool,0,32);
|
||||
for (int i=0; i<4; i++) {
|
||||
chan[i]=DivPlatformPowerNoise::Channel();
|
||||
chan[i].std.setEngine(parent);
|
||||
chan[i].slope=(i==3);
|
||||
}
|
||||
|
||||
pwrnoise_reset(&pn);
|
||||
|
||||
// set master volume to full
|
||||
rWrite(0,0x87);
|
||||
// set per-channel panning
|
||||
for (int i=0; i<4; i++) {
|
||||
chWrite(i,0x06,volPan(chan[i].outVol,chan[i].pan));
|
||||
}
|
||||
// set default params so we have sound
|
||||
// noise
|
||||
for (int i=0; i<3; i++) {
|
||||
chWrite(i,0x03,chan[i].initLFSR&0xff);
|
||||
chWrite(i,0x04,chan[i].initLFSR>>8);
|
||||
chWrite(i,0x05,(chan[i].tapA<<4)|chan[i].tapB);
|
||||
}
|
||||
// slope
|
||||
chWrite(3,0x03,chan[3].slopeA.len);
|
||||
chWrite(3,0x04,chan[3].slopeB.len);
|
||||
chWrite(3,0x05,(chan[3].slopeA.offset<<4)|chan[3].slopeB.offset);
|
||||
|
||||
addWrite(0xffffffff,0);
|
||||
}
|
||||
|
||||
int DivPlatformPowerNoise::getOutputCount() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
bool DivPlatformPowerNoise::keyOffAffectsArp(int ch) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void DivPlatformPowerNoise::notifyWaveChange(int wave) {
|
||||
}
|
||||
|
||||
void DivPlatformPowerNoise::notifyInsDeletion(void* ins) {
|
||||
for (int i=0; i<4; i++) {
|
||||
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformPowerNoise::setFlags(const DivConfig& flags) {
|
||||
chipClock=16000000;
|
||||
|
||||
CHECK_CUSTOM_CLOCK;
|
||||
rate=chipClock/32;
|
||||
|
||||
for (int i=0; i<4; i++) {
|
||||
oscBuf[i]->rate=rate;
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformPowerNoise::poke(unsigned int addr, unsigned short val) {
|
||||
rWrite(addr,val);
|
||||
}
|
||||
|
||||
void DivPlatformPowerNoise::poke(std::vector<DivRegWrite>& wlist) {
|
||||
for (DivRegWrite& i: wlist) rWrite(i.addr,i.val);
|
||||
}
|
||||
|
||||
int DivPlatformPowerNoise::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) {
|
||||
parent=p;
|
||||
dumpWrites=false;
|
||||
skipRegisterWrites=false;
|
||||
|
||||
for (int i=0; i<4; i++) {
|
||||
oscBuf[i]=new DivDispatchOscBuffer;
|
||||
isMuted[i]=false;
|
||||
}
|
||||
setFlags(flags);
|
||||
reset();
|
||||
return 4;
|
||||
}
|
||||
|
||||
void DivPlatformPowerNoise::quit() {
|
||||
for (int i=0; i<4; i++) {
|
||||
delete oscBuf[i];
|
||||
}
|
||||
}
|
||||
|
||||
DivPlatformPowerNoise::~DivPlatformPowerNoise() {
|
||||
}
|
||||
108
src/engine/platform/powernoise.h
Normal file
108
src/engine/platform/powernoise.h
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2024 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef _POWERNOISE_H
|
||||
#define _POWERNOISE_H
|
||||
|
||||
#include "../dispatch.h"
|
||||
#include "../../../extern/pwrnoise/pwrnoise.h"
|
||||
|
||||
class DivPlatformPowerNoise: public DivDispatch {
|
||||
struct SlopePortion {
|
||||
unsigned char len, offset;
|
||||
bool clip, reset, dir;
|
||||
|
||||
SlopePortion(unsigned char l, unsigned char o, bool c, bool r, bool d):
|
||||
len(l),
|
||||
offset(o),
|
||||
clip(c),
|
||||
reset(r),
|
||||
dir(d) {}
|
||||
|
||||
SlopePortion():
|
||||
len(0),
|
||||
offset(0),
|
||||
clip(false),
|
||||
reset(false),
|
||||
dir(false) {}
|
||||
};
|
||||
|
||||
struct Channel: public SharedChannel<signed char> {
|
||||
int fNum;
|
||||
unsigned short initLFSR;
|
||||
unsigned char octave, pan, tapA, tapB, octaveOff;
|
||||
bool slope, am, tapBEnable, keyOn, keyOff;
|
||||
SlopePortion slopeA, slopeB;
|
||||
|
||||
Channel():
|
||||
SharedChannel<signed char>(15),
|
||||
fNum(0),
|
||||
initLFSR(0x5555),
|
||||
octave(0),
|
||||
pan(255),
|
||||
tapA(1),
|
||||
tapB(2),
|
||||
octaveOff(0),
|
||||
slope(false),
|
||||
am(false),
|
||||
tapBEnable(false),
|
||||
keyOn(false),
|
||||
keyOff(false),
|
||||
slopeA(255,1,false,false,false),
|
||||
slopeB() {}
|
||||
};
|
||||
|
||||
Channel chan[4];
|
||||
DivDispatchOscBuffer* oscBuf[4];
|
||||
bool isMuted[4];
|
||||
unsigned char regPool[32];
|
||||
|
||||
power_noise_t pn;
|
||||
|
||||
friend void putDispatchChip(void*,int);
|
||||
friend void putDispatchChan(void*,int,int);
|
||||
public:
|
||||
void acquire(short** buf, size_t len);
|
||||
int dispatch(DivCommand c);
|
||||
void* getChanState(int chan);
|
||||
DivMacroInt* getChanMacroInt(int ch);
|
||||
unsigned short getPan(int chan);
|
||||
DivChannelModeHints getModeHints(int chan);
|
||||
bool getDCOffRequired();
|
||||
DivDispatchOscBuffer* getOscBuffer(int chan);
|
||||
unsigned char* getRegisterPool();
|
||||
int getRegisterPoolSize();
|
||||
void reset();
|
||||
void forceIns();
|
||||
void tick(bool sysTick=true);
|
||||
void muteChannel(int ch, bool mute);
|
||||
int getOutputCount();
|
||||
bool keyOffAffectsArp(int ch);
|
||||
void setFlags(const DivConfig& flags);
|
||||
void notifyWaveChange(int wave);
|
||||
void notifyInsDeletion(void* ins);
|
||||
void poke(unsigned int addr, unsigned short val);
|
||||
void poke(std::vector<DivRegWrite>& wlist);
|
||||
const char** getRegisterSheet();
|
||||
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
|
||||
void quit();
|
||||
~DivPlatformPowerNoise();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -451,7 +451,9 @@ int DivPlatformQSound::dispatch(DivCommand c) {
|
|||
chan[c.chan].isNewQSound=(ins->type==DIV_INS_QSOUND);
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].sample=ins->amiga.getSample(c.value);
|
||||
chan[c.chan].sampleNote=c.value;
|
||||
c.value=ins->amiga.getFreq(c.value);
|
||||
chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote;
|
||||
}
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].baseFreq=QS_NOTE_FREQUENCY(c.value);
|
||||
|
|
@ -543,7 +545,7 @@ int DivPlatformQSound::dispatch(DivCommand c) {
|
|||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
int destFreq=QS_NOTE_FREQUENCY(c.value2);
|
||||
int destFreq=QS_NOTE_FREQUENCY(c.value2+chan[c.chan].sampleNoteDelta);
|
||||
bool return2=false;
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
chan[c.chan].baseFreq+=c.value;
|
||||
|
|
@ -566,7 +568,7 @@ int DivPlatformQSound::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_LEGATO: {
|
||||
chan[c.chan].baseFreq=QS_NOTE_FREQUENCY(c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val-12):(0)));
|
||||
chan[c.chan].baseFreq=QS_NOTE_FREQUENCY(c.value+chan[c.chan].sampleNoteDelta+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val-12):(0)));
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -184,7 +184,9 @@ int DivPlatformRF5C68::dispatch(DivCommand c) {
|
|||
chan[c.chan].macroVolMul=ins->type==DIV_INS_AMIGA?64:255;
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].sample=ins->amiga.getSample(c.value);
|
||||
chan[c.chan].sampleNote=c.value;
|
||||
c.value=ins->amiga.getFreq(c.value);
|
||||
chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote;
|
||||
}
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);
|
||||
|
|
@ -243,7 +245,7 @@ int DivPlatformRF5C68::dispatch(DivCommand c) {
|
|||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
int destFreq=NOTE_FREQUENCY(c.value2);
|
||||
int destFreq=NOTE_FREQUENCY(c.value2+chan[c.chan].sampleNoteDelta);
|
||||
bool return2=false;
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
chan[c.chan].baseFreq+=c.value;
|
||||
|
|
@ -266,7 +268,7 @@ int DivPlatformRF5C68::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_LEGATO: {
|
||||
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val-12):(0)));
|
||||
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value+chan[c.chan].sampleNoteDelta+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val-12):(0)));
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -189,7 +189,9 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
|
|||
chan[c.chan].isNewSegaPCM=(ins->type==DIV_INS_SEGAPCM);
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].pcm.sample=ins->amiga.getSample(c.value);
|
||||
chan[c.chan].sampleNote=c.value;
|
||||
c.value=ins->amiga.getFreq(c.value);
|
||||
chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote;
|
||||
}
|
||||
if (chan[c.chan].pcm.sample<0 || chan[c.chan].pcm.sample>=parent->song.sampleLen) {
|
||||
chan[c.chan].pcm.sample=-1;
|
||||
|
|
@ -297,7 +299,7 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
int destFreq=(c.value2<<7);
|
||||
int destFreq=((c.value2+chan[c.chan].sampleNoteDelta)<<7);
|
||||
int newFreq;
|
||||
int mul=(oldSlides || parent->song.linearPitch!=2)?8:1;
|
||||
bool return2=false;
|
||||
|
|
@ -323,7 +325,7 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_LEGATO: {
|
||||
chan[c.chan].baseFreq=(c.value<<7);
|
||||
chan[c.chan].baseFreq=((c.value+chan[c.chan].sampleNoteDelta)<<7);
|
||||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -333,6 +333,8 @@ int DivPlatformSNES::dispatch(DivCommand c) {
|
|||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_SNES);
|
||||
if (ins->amiga.useWave) {
|
||||
chan[c.chan].useWave=true;
|
||||
chan[c.chan].sampleNote=DIV_NOTE_NULL;
|
||||
chan[c.chan].sampleNoteDelta=0;
|
||||
chan[c.chan].wtLen=ins->amiga.waveLen+1;
|
||||
if (chan[c.chan].insChanged) {
|
||||
if (chan[c.chan].wave<0) {
|
||||
|
|
@ -345,7 +347,9 @@ int DivPlatformSNES::dispatch(DivCommand c) {
|
|||
} else {
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].sample=ins->amiga.getSample(c.value);
|
||||
chan[c.chan].sampleNote=c.value;
|
||||
c.value=ins->amiga.getFreq(c.value);
|
||||
chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote;
|
||||
}
|
||||
chan[c.chan].useWave=false;
|
||||
}
|
||||
|
|
@ -424,7 +428,7 @@ int DivPlatformSNES::dispatch(DivCommand c) {
|
|||
chan[c.chan].ws.changeWave1(chan[c.chan].wave);
|
||||
break;
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
int destFreq=round(NOTE_FREQUENCY(c.value2));
|
||||
int destFreq=round(NOTE_FREQUENCY(c.value2+chan[c.chan].sampleNoteDelta));
|
||||
bool return2=false;
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
chan[c.chan].baseFreq+=c.value;
|
||||
|
|
@ -447,7 +451,7 @@ int DivPlatformSNES::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_LEGATO: {
|
||||
chan[c.chan].baseFreq=round(NOTE_FREQUENCY(c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0))));
|
||||
chan[c.chan].baseFreq=round(NOTE_FREQUENCY(c.value+chan[c.chan].sampleNoteDelta+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0))));
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -459,7 +459,7 @@ protected:
|
|||
uint8_t m_clock_prescale; // prescale factor (2/3/6)
|
||||
uint8_t m_irq_mask; // mask of which bits signal IRQs
|
||||
uint8_t m_irq_state; // current IRQ state
|
||||
uint8_t m_timer_running[2]; // current timer running state
|
||||
uint8_t m_timer_running[4]; // current timer running state
|
||||
uint8_t m_total_clocks; // low 8 bits of the total number of clocks processed
|
||||
uint32_t m_active_channels; // mask of active channels (computed by prepare)
|
||||
uint32_t m_modified_channels; // mask of channels that have been modified
|
||||
|
|
|
|||
|
|
@ -1198,7 +1198,7 @@ fm_engine_base<RegisterType>::fm_engine_base(ymfm_interface &intf) :
|
|||
m_clock_prescale(RegisterType::DEFAULT_PRESCALE),
|
||||
m_irq_mask(STATUS_TIMERA | STATUS_TIMERB),
|
||||
m_irq_state(0),
|
||||
m_timer_running{0,0},
|
||||
m_timer_running{0,0,0,0},
|
||||
m_total_clocks(0),
|
||||
m_active_channels(ALL_CHANNELS),
|
||||
m_modified_channels(ALL_CHANNELS),
|
||||
|
|
|
|||
|
|
@ -282,8 +282,13 @@ int DivPlatformSoundUnit::dispatch(DivCommand c) {
|
|||
if (chan[c.chan].pcm) {
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].sample=ins->amiga.getSample(c.value);
|
||||
chan[c.chan].sampleNote=c.value;
|
||||
c.value=ins->amiga.getFreq(c.value);
|
||||
chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote;
|
||||
}
|
||||
} else {
|
||||
chan[c.chan].sampleNote=DIV_NOTE_NULL;
|
||||
chan[c.chan].sampleNoteDelta=0;
|
||||
}
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].baseFreq=NOTE_SU(c.chan,c.value);
|
||||
|
|
@ -450,7 +455,7 @@ int DivPlatformSoundUnit::dispatch(DivCommand c) {
|
|||
}
|
||||
break;
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
int destFreq=NOTE_SU(c.chan,c.value2);
|
||||
int destFreq=NOTE_SU(c.chan,c.value2+chan[c.chan].sampleNoteDelta);
|
||||
bool return2=false;
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
chan[c.chan].baseFreq+=c.value*((parent->song.linearPitch==2)?1:(1+(chan[c.chan].baseFreq>>9)));
|
||||
|
|
@ -482,7 +487,7 @@ int DivPlatformSoundUnit::dispatch(DivCommand c) {
|
|||
chan[c.chan].keyOn=true;
|
||||
break;
|
||||
case DIV_CMD_LEGATO:
|
||||
chan[c.chan].baseFreq=NOTE_SU(c.chan,c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0)));
|
||||
chan[c.chan].baseFreq=NOTE_SU(c.chan,c.value+chan[c.chan].sampleNoteDelta+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0)));
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -257,6 +257,8 @@ int DivPlatformSwan::dispatch(DivCommand c) {
|
|||
pcm=true;
|
||||
} else if (furnaceDac) {
|
||||
pcm=false;
|
||||
chan[c.chan].sampleNote=DIV_NOTE_NULL;
|
||||
chan[c.chan].sampleNoteDelta=0;
|
||||
}
|
||||
if (pcm) {
|
||||
if (skipRegisterWrites) break;
|
||||
|
|
@ -265,7 +267,12 @@ int DivPlatformSwan::dispatch(DivCommand c) {
|
|||
if (ins->type==DIV_INS_AMIGA || ins->amiga.useSample) {
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
dacSample=ins->amiga.getSample(c.value);
|
||||
chan[c.chan].sampleNote=c.value;
|
||||
c.value=ins->amiga.getFreq(c.value);
|
||||
chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote;
|
||||
} else if (chan[c.chan].sampleNote!=DIV_NOTE_NULL) {
|
||||
dacSample=ins->amiga.getSample(chan[c.chan].sampleNote);
|
||||
c.value=ins->amiga.getFreq(chan[c.chan].sampleNote);
|
||||
}
|
||||
if (dacSample<0 || dacSample>=parent->song.sampleLen) {
|
||||
dacSample=-1;
|
||||
|
|
@ -332,6 +339,8 @@ int DivPlatformSwan::dispatch(DivCommand c) {
|
|||
dacSample=-1;
|
||||
if (dumpWrites) postWrite(0xffff0002,0);
|
||||
pcm=false;
|
||||
chan[c.chan].sampleNote=DIV_NOTE_NULL;
|
||||
chan[c.chan].sampleNoteDelta=0;
|
||||
}
|
||||
chan[c.chan].active=false;
|
||||
chan[c.chan].keyOff=true;
|
||||
|
|
@ -383,7 +392,7 @@ int DivPlatformSwan::dispatch(DivCommand c) {
|
|||
}
|
||||
break;
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
int destFreq=NOTE_PERIODIC(c.value2);
|
||||
int destFreq=NOTE_PERIODIC(c.value2+chan[c.chan].sampleNoteDelta);
|
||||
bool return2=false;
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
chan[c.chan].baseFreq+=c.value;
|
||||
|
|
@ -412,7 +421,13 @@ int DivPlatformSwan::dispatch(DivCommand c) {
|
|||
}
|
||||
break;
|
||||
case DIV_CMD_SAMPLE_MODE:
|
||||
if (c.chan==1) pcm=c.value;
|
||||
if (c.chan==1) {
|
||||
pcm=c.value;
|
||||
if (!pcm) {
|
||||
chan[c.chan].sampleNote=DIV_NOTE_NULL;
|
||||
chan[c.chan].sampleNoteDelta=0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_SAMPLE_BANK:
|
||||
sampleBank=c.value;
|
||||
|
|
@ -426,7 +441,7 @@ int DivPlatformSwan::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_LEGATO:
|
||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0)));
|
||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+chan[c.chan].sampleNoteDelta+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0)));
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -299,10 +299,15 @@ int DivPlatformVERA::dispatch(DivCommand c) {
|
|||
if (c.chan<16) {
|
||||
rWriteLo(c.chan,2,chan[c.chan].vol);
|
||||
} else {
|
||||
DivInstrument* ins=parent->getIns(chan[16].ins,DIV_INS_VERA);
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
DivInstrument* ins=parent->getIns(chan[16].ins,DIV_INS_VERA);
|
||||
chan[16].pcm.sample=ins->amiga.getSample(c.value);
|
||||
chan[16].sampleNote=c.value;
|
||||
c.value=ins->amiga.getFreq(c.value);
|
||||
chan[16].sampleNoteDelta=c.value-chan[c.chan].sampleNote;
|
||||
} else if (chan[c.chan].sampleNote!=DIV_NOTE_NULL) {
|
||||
chan[16].pcm.sample=ins->amiga.getSample(chan[c.chan].sampleNote);
|
||||
c.value=ins->amiga.getFreq(chan[c.chan].sampleNote);
|
||||
}
|
||||
if (chan[16].pcm.sample<0 || chan[16].pcm.sample>=parent->song.sampleLen) {
|
||||
chan[16].pcm.sample=-1;
|
||||
|
|
@ -371,7 +376,7 @@ int DivPlatformVERA::dispatch(DivCommand c) {
|
|||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
int destFreq=calcNoteFreq(c.chan,c.value2);
|
||||
int destFreq=calcNoteFreq(c.chan,c.value2+chan[c.chan].sampleNoteDelta);
|
||||
bool return2=false;
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
chan[c.chan].baseFreq+=c.value;
|
||||
|
|
@ -394,7 +399,7 @@ int DivPlatformVERA::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_LEGATO:
|
||||
chan[c.chan].baseFreq=calcNoteFreq(c.chan,c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0)));
|
||||
chan[c.chan].baseFreq=calcNoteFreq(c.chan,c.value+chan[c.chan].sampleNoteDelta+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0)));
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -236,13 +236,20 @@ int DivPlatformVRC6::dispatch(DivCommand c) {
|
|||
chan[c.chan].pcm=true;
|
||||
} else if (chan[c.chan].furnaceDac) {
|
||||
chan[c.chan].pcm=false;
|
||||
chan[c.chan].sampleNote=DIV_NOTE_NULL;
|
||||
chan[c.chan].sampleNoteDelta=0;
|
||||
}
|
||||
if (chan[c.chan].pcm) {
|
||||
if (skipRegisterWrites) break;
|
||||
if (ins->type==DIV_INS_AMIGA || ins->amiga.useSample) {
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].dacSample=ins->amiga.getSample(c.value);
|
||||
chan[c.chan].sampleNote=c.value;
|
||||
c.value=ins->amiga.getFreq(c.value);
|
||||
chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote;
|
||||
} else if (chan[c.chan].sampleNote!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].dacSample=ins->amiga.getSample(chan[c.chan].sampleNote);
|
||||
c.value=ins->amiga.getFreq(chan[c.chan].sampleNote);
|
||||
}
|
||||
if (chan[c.chan].dacSample<0 || chan[c.chan].dacSample>=parent->song.sampleLen) {
|
||||
chan[c.chan].dacSample=-1;
|
||||
|
|
@ -270,6 +277,8 @@ int DivPlatformVRC6::dispatch(DivCommand c) {
|
|||
//chan[c.chan].keyOn=true;
|
||||
chan[c.chan].furnaceDac=true;
|
||||
} else {
|
||||
chan[c.chan].sampleNote=DIV_NOTE_NULL;
|
||||
chan[c.chan].sampleNoteDelta=0;
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].note=c.value;
|
||||
}
|
||||
|
|
@ -314,6 +323,8 @@ int DivPlatformVRC6::dispatch(DivCommand c) {
|
|||
chan[c.chan].dacSample=-1;
|
||||
if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0);
|
||||
chan[c.chan].pcm=false;
|
||||
chan[c.chan].sampleNote=DIV_NOTE_NULL;
|
||||
chan[c.chan].sampleNoteDelta=0;
|
||||
chan[c.chan].active=false;
|
||||
chan[c.chan].keyOff=true;
|
||||
chan[c.chan].macroInit(NULL);
|
||||
|
|
@ -352,7 +363,7 @@ int DivPlatformVRC6::dispatch(DivCommand c) {
|
|||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
int destFreq=NOTE_PERIODIC(c.value2);
|
||||
int destFreq=NOTE_PERIODIC(c.value2+chan[c.chan].sampleNoteDelta);
|
||||
bool return2=false;
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
chan[c.chan].baseFreq+=c.value;
|
||||
|
|
@ -382,6 +393,10 @@ int DivPlatformVRC6::dispatch(DivCommand c) {
|
|||
case DIV_CMD_SAMPLE_MODE:
|
||||
if (c.chan!=2) { // pulse
|
||||
chan[c.chan].pcm=c.value;
|
||||
if (!chan[c.chan].pcm) {
|
||||
chan[c.chan].sampleNote=DIV_NOTE_NULL;
|
||||
chan[c.chan].sampleNoteDelta=0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_SAMPLE_BANK:
|
||||
|
|
@ -391,7 +406,7 @@ int DivPlatformVRC6::dispatch(DivCommand c) {
|
|||
}
|
||||
break;
|
||||
case DIV_CMD_LEGATO:
|
||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0)));
|
||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+chan[c.chan].sampleNoteDelta+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0)));
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -536,6 +536,8 @@ int DivPlatformX1_010::dispatch(DivCommand c) {
|
|||
chan[c.chan].furnacePCM=true;
|
||||
} else {
|
||||
chan[c.chan].furnacePCM=false;
|
||||
chan[c.chan].sampleNote=DIV_NOTE_NULL;
|
||||
chan[c.chan].sampleNoteDelta=0;
|
||||
}
|
||||
if (skipRegisterWrites) break;
|
||||
if (chan[c.chan].furnacePCM) {
|
||||
|
|
@ -543,7 +545,9 @@ int DivPlatformX1_010::dispatch(DivCommand c) {
|
|||
chan[c.chan].macroInit(ins);
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].sample=ins->amiga.getSample(c.value);
|
||||
chan[c.chan].sampleNote=c.value;
|
||||
c.value=ins->amiga.getFreq(c.value);
|
||||
chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote;
|
||||
}
|
||||
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
|
||||
DivSample* s=parent->getSample(chan[c.chan].sample);
|
||||
|
|
@ -608,6 +612,8 @@ int DivPlatformX1_010::dispatch(DivCommand c) {
|
|||
}
|
||||
} else if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].note=c.value;
|
||||
chan[c.chan].sampleNote=DIV_NOTE_NULL;
|
||||
chan[c.chan].sampleNoteDelta=0;
|
||||
chan[c.chan].baseFreq=NoteX1_010(c.chan,chan[c.chan].note);
|
||||
chan[c.chan].fixedFreq=0;
|
||||
chan[c.chan].freqChanged=true;
|
||||
|
|
@ -684,7 +690,7 @@ int DivPlatformX1_010::dispatch(DivCommand c) {
|
|||
}
|
||||
break;
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
int destFreq=NoteX1_010(c.chan,c.value2);
|
||||
int destFreq=NoteX1_010(c.chan,c.value2+chan[c.chan].sampleNoteDelta);
|
||||
bool return2=false;
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
chan[c.chan].baseFreq+=c.value;
|
||||
|
|
@ -732,7 +738,7 @@ int DivPlatformX1_010::dispatch(DivCommand c) {
|
|||
}
|
||||
case DIV_CMD_LEGATO:
|
||||
chan[c.chan].note=c.value;
|
||||
chan[c.chan].baseFreq=NoteX1_010(c.chan,chan[c.chan].note+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0)));
|
||||
chan[c.chan].baseFreq=NoteX1_010(c.chan,chan[c.chan].note+chan[c.chan].sampleNoteDelta+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0)));
|
||||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
case DIV_CMD_PRE_PORTA:
|
||||
|
|
|
|||
|
|
@ -919,7 +919,9 @@ int DivPlatformYM2608::dispatch(DivCommand c) {
|
|||
}
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].sample=ins->amiga.getSample(c.value);
|
||||
chan[c.chan].sampleNote=c.value;
|
||||
c.value=ins->amiga.getFreq(c.value);
|
||||
chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote;
|
||||
}
|
||||
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
|
||||
DivSample* s=parent->getSample(chan[c.chan].sample);
|
||||
|
|
@ -1109,7 +1111,7 @@ int DivPlatformYM2608::dispatch(DivCommand c) {
|
|||
}
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
if (c.chan>5 || parent->song.linearPitch==2) { // PSG, ADPCM-B
|
||||
int destFreq=NOTE_OPNB(c.chan,c.value2);
|
||||
int destFreq=NOTE_OPNB(c.chan,c.value2+chan[c.chan].sampleNoteDelta);
|
||||
bool return2=false;
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
chan[c.chan].baseFreq+=c.value;
|
||||
|
|
@ -1150,7 +1152,7 @@ int DivPlatformYM2608::dispatch(DivCommand c) {
|
|||
chan[c.chan].insChanged=false;
|
||||
}
|
||||
}
|
||||
chan[c.chan].baseFreq=NOTE_OPNB(c.chan,c.value);
|
||||
chan[c.chan].baseFreq=NOTE_OPNB(c.chan,c.value+chan[c.chan].sampleNoteDelta);
|
||||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -850,7 +850,9 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
|
|||
}
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].sample=ins->amiga.getSample(c.value);
|
||||
chan[c.chan].sampleNote=c.value;
|
||||
c.value=ins->amiga.getFreq(c.value);
|
||||
chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote;
|
||||
}
|
||||
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
|
||||
DivSample* s=parent->getSample(chan[c.chan].sample);
|
||||
|
|
@ -1081,7 +1083,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
|
|||
}
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
if (c.chan>=psgChanOffs || parent->song.linearPitch==2) { // PSG, ADPCM-B
|
||||
int destFreq=NOTE_OPNB(c.chan,c.value2);
|
||||
int destFreq=NOTE_OPNB(c.chan,c.value2+chan[c.chan].sampleNoteDelta);
|
||||
bool return2=false;
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
chan[c.chan].baseFreq+=c.value;
|
||||
|
|
@ -1122,7 +1124,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
|
|||
chan[c.chan].insChanged=false;
|
||||
}
|
||||
}
|
||||
chan[c.chan].baseFreq=NOTE_OPNB(c.chan,c.value);
|
||||
chan[c.chan].baseFreq=NOTE_OPNB(c.chan,c.value+chan[c.chan].sampleNoteDelta);
|
||||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -917,7 +917,9 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
|
|||
}
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].sample=ins->amiga.getSample(c.value);
|
||||
chan[c.chan].sampleNote=c.value;
|
||||
c.value=ins->amiga.getFreq(c.value);
|
||||
chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote;
|
||||
}
|
||||
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
|
||||
DivSample* s=parent->getSample(chan[c.chan].sample);
|
||||
|
|
@ -1148,7 +1150,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
|
|||
}
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
if (c.chan>=psgChanOffs || parent->song.linearPitch==2) { // PSG, ADPCM-B
|
||||
int destFreq=NOTE_OPNB(c.chan,c.value2);
|
||||
int destFreq=NOTE_OPNB(c.chan,c.value2+chan[c.chan].sampleNoteDelta);
|
||||
bool return2=false;
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
chan[c.chan].baseFreq+=c.value;
|
||||
|
|
@ -1189,7 +1191,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
|
|||
chan[c.chan].insChanged=false;
|
||||
}
|
||||
}
|
||||
chan[c.chan].baseFreq=NOTE_OPNB(c.chan,c.value);
|
||||
chan[c.chan].baseFreq=NOTE_OPNB(c.chan,c.value+chan[c.chan].sampleNoteDelta);
|
||||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -213,7 +213,9 @@ int DivPlatformYMZ280B::dispatch(DivCommand c) {
|
|||
chan[c.chan].macroVolMul=ins->type==DIV_INS_AMIGA?64:255;
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].sample=ins->amiga.getSample(c.value);
|
||||
chan[c.chan].sampleNote=c.value;
|
||||
c.value=ins->amiga.getFreq(c.value);
|
||||
chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote;
|
||||
}
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);
|
||||
|
|
@ -272,7 +274,7 @@ int DivPlatformYMZ280B::dispatch(DivCommand c) {
|
|||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
int destFreq=NOTE_FREQUENCY(c.value2);
|
||||
int destFreq=NOTE_FREQUENCY(c.value2+chan[c.chan].sampleNoteDelta);
|
||||
bool return2=false;
|
||||
int multiplier=(parent->song.linearPitch==2)?1:256;
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
|
|
@ -296,7 +298,7 @@ int DivPlatformYMZ280B::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_LEGATO: {
|
||||
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val-12):(0)));
|
||||
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value+chan[c.chan].sampleNoteDelta+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val-12):(0)));
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -30,13 +30,14 @@ const char** DivPlatformZXBeeperQuadTone::getRegisterSheet() {
|
|||
}
|
||||
|
||||
void DivPlatformZXBeeperQuadTone::acquire(short** buf, size_t len) {
|
||||
bool o=false;
|
||||
for (size_t h=0; h<len; h++) {
|
||||
if (curSample>=0 && curSample<parent->song.sampleLen && !isMuted[4]) {
|
||||
bool sampleActive=false;
|
||||
if (curSample>=0 && curSample<parent->song.sampleLen) {
|
||||
sampleActive=!isMuted[4];
|
||||
while (curSamplePeriod>=chan[4].freq) {
|
||||
DivSample* s=parent->getSample(curSample);
|
||||
if (s->samples>0) {
|
||||
if (!isMuted[4]) o=(s->data8[curSamplePos++]>0);
|
||||
chan[4].out=(s->data8[curSamplePos++]>0);
|
||||
if (curSamplePos>=s->samples) curSample=-1;
|
||||
// (theoretical) 32KiB limit
|
||||
if (curSamplePos>=32768*8) curSample=-1;
|
||||
|
|
@ -46,46 +47,47 @@ void DivPlatformZXBeeperQuadTone::acquire(short** buf, size_t len) {
|
|||
curSamplePeriod-=chan[4].freq;
|
||||
}
|
||||
curSamplePeriod+=40;
|
||||
if ((outputClock&3)==0) {
|
||||
}
|
||||
if (sampleActive) {
|
||||
buf[0][h]=chan[4].out?32767:0;
|
||||
if (outputClock==0) {
|
||||
oscBuf[0]->data[oscBuf[0]->needle++]=0;
|
||||
oscBuf[1]->data[oscBuf[1]->needle++]=0;
|
||||
oscBuf[2]->data[oscBuf[2]->needle++]=0;
|
||||
oscBuf[3]->data[oscBuf[3]->needle++]=0;
|
||||
oscBuf[4]->data[oscBuf[4]->needle++]=o?32767:0;
|
||||
}
|
||||
oscBuf[4]->data[oscBuf[4]->needle++]=buf[0][h];
|
||||
} else {
|
||||
int ch=outputClock/2;
|
||||
int b=ch*4;
|
||||
bool o=false;
|
||||
if ((outputClock&1)==0) {
|
||||
short oscOut;
|
||||
chan[ch].sPosition+=(regPool[1+b]<<8)|regPool[0+b];
|
||||
chan[ch].out=regPool[3+b]+((((chan[ch].sPosition>>8)&0xff)<regPool[2+b])?1:0);
|
||||
if (isMuted[ch]) chan[ch].out=0;
|
||||
}
|
||||
if ((outputClock&3)==0) {
|
||||
oscBuf[4]->data[oscBuf[4]->needle++]=0;
|
||||
}
|
||||
o=chan[ch].out&0x10;
|
||||
oscBuf[ch]->data[oscBuf[ch]->needle++]=o?32767:0;
|
||||
chan[ch].out<<=1;
|
||||
|
||||
// if muted, ztill run sample
|
||||
if (curSample>=0 && curSample<parent->song.sampleLen && isMuted[4]) {
|
||||
while (curSamplePeriod>=chan[4].freq) {
|
||||
DivSample* s=parent->getSample(curSample);
|
||||
if (s->samples>0) {
|
||||
if (curSamplePos>=s->samples) curSample=-1;
|
||||
// (theoretical) 32KiB limit
|
||||
if (curSamplePos>=32768*8) curSample=-1;
|
||||
} else {
|
||||
curSample=-1;
|
||||
}
|
||||
curSamplePeriod-=chan[4].freq;
|
||||
if (isMuted[ch] || ((chan[ch].out&0x18)==0)) {
|
||||
oscOut=0;
|
||||
} else if ((chan[ch].out&0x18)==0x18) {
|
||||
oscOut=32767;
|
||||
} else {
|
||||
oscOut=16383;
|
||||
}
|
||||
curSamplePeriod+=40;
|
||||
oscBuf[ch]->data[oscBuf[ch]->needle++]=oscOut;
|
||||
}
|
||||
if (!isMuted[ch]) o=chan[ch].out&0x10;
|
||||
if (noHiss) {
|
||||
deHisser[outputClock]=o;
|
||||
buf[0][h]=-1;
|
||||
for (int i=0; i<8; i++) {
|
||||
buf[0][h]+=deHisser[i]?4096:0;
|
||||
}
|
||||
} else {
|
||||
buf[0][h]=o?32767:0;
|
||||
}
|
||||
chan[ch].out<<=1;
|
||||
oscBuf[4]->data[oscBuf[4]->needle++]=0;
|
||||
}
|
||||
outputClock=(outputClock+1)&7;
|
||||
buf[0][h]=o?32767:0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -157,6 +159,7 @@ void DivPlatformZXBeeperQuadTone::tick(bool sysTick) {
|
|||
chan[4].freq=parent->calcFreq(chan[4].baseFreq,chan[4].pitch,chan[4].fixedArp?chan[4].baseNoteOverride:chan[4].arpOff,chan[4].fixedArp,true,2,chan[4].pitch2,chipClock,off);
|
||||
if (chan[4].freq>258) chan[4].freq=258;
|
||||
if (chan[4].freq<3) chan[4].freq=3;
|
||||
rWrite(16,(chan[4].freq-2)&255);
|
||||
chan[4].freq*=13;
|
||||
}
|
||||
if (chan[4].keyOn) chan[4].keyOn=false;
|
||||
|
|
@ -187,13 +190,21 @@ int DivPlatformZXBeeperQuadTone::dispatch(DivCommand c) {
|
|||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
curSample=ins->amiga.getSample(c.value);
|
||||
chan[c.chan].sampleNote=c.value;
|
||||
c.value=ins->amiga.getFreq(c.value);
|
||||
chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote;
|
||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
// TODO support offset commands
|
||||
curSamplePos=0;
|
||||
curSamplePeriod=0;
|
||||
} else if (chan[c.chan].sampleNote!=DIV_NOTE_NULL) {
|
||||
curSample=ins->amiga.getSample(chan[c.chan].sampleNote);
|
||||
c.value=ins->amiga.getFreq(chan[c.chan].sampleNote);
|
||||
// TODO support offset commands
|
||||
curSamplePos=0;
|
||||
curSamplePeriod=0;
|
||||
}
|
||||
chan[c.chan].active=true;
|
||||
chan[c.chan].keyOn=true;
|
||||
|
|
@ -241,7 +252,7 @@ int DivPlatformZXBeeperQuadTone::dispatch(DivCommand c) {
|
|||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
int destFreq=NOTE_FREQUENCY(c.value2);
|
||||
int destFreq=NOTE_FREQUENCY(c.value2+chan[c.chan].sampleNoteDelta);
|
||||
bool return2=false;
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
chan[c.chan].baseFreq+=c.value;
|
||||
|
|
@ -268,7 +279,7 @@ int DivPlatformZXBeeperQuadTone::dispatch(DivCommand c) {
|
|||
if (c.chan<4) rWrite(2+c.chan*4,chan[c.chan].duty^0xff);
|
||||
break;
|
||||
case DIV_CMD_LEGATO:
|
||||
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0)));
|
||||
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value+chan[c.chan].sampleNoteDelta+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0)));
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
break;
|
||||
|
|
@ -332,11 +343,12 @@ unsigned char* DivPlatformZXBeeperQuadTone::getRegisterPool() {
|
|||
}
|
||||
|
||||
int DivPlatformZXBeeperQuadTone::getRegisterPoolSize() {
|
||||
return 16;
|
||||
return 17;
|
||||
}
|
||||
|
||||
void DivPlatformZXBeeperQuadTone::reset() {
|
||||
memset(regPool,0,16);
|
||||
memset(regPool,0,17);
|
||||
memset(deHisser,0,8);
|
||||
for (int i=0; i<5; i++) {
|
||||
chan[i]=DivPlatformZXBeeperQuadTone::Channel();
|
||||
chan[i].std.setEngine(parent);
|
||||
|
|
@ -345,7 +357,6 @@ void DivPlatformZXBeeperQuadTone::reset() {
|
|||
cycles=0;
|
||||
curChan=0;
|
||||
sOffTimer=0;
|
||||
ulaOut=0;
|
||||
curSample=-1;
|
||||
curSamplePos=0;
|
||||
curSamplePeriod=0;
|
||||
|
|
@ -373,9 +384,11 @@ void DivPlatformZXBeeperQuadTone::setFlags(const DivConfig& flags) {
|
|||
}
|
||||
CHECK_CUSTOM_CLOCK;
|
||||
rate=chipClock/40;
|
||||
noHiss=flags.getBool("noHiss",false);
|
||||
for (int i=0; i<4; i++) {
|
||||
oscBuf[i]->rate=rate/4;
|
||||
oscBuf[i]->rate=rate/8;
|
||||
}
|
||||
oscBuf[4]->rate=rate;
|
||||
}
|
||||
|
||||
void DivPlatformZXBeeperQuadTone::poke(unsigned int addr, unsigned short val) {
|
||||
|
|
|
|||
|
|
@ -36,12 +36,13 @@ class DivPlatformZXBeeperQuadTone: public DivDispatch {
|
|||
Channel chan[5];
|
||||
DivDispatchOscBuffer* oscBuf[5];
|
||||
bool isMuted[5];
|
||||
unsigned char ulaOut;
|
||||
bool noHiss;
|
||||
bool deHisser[8];
|
||||
|
||||
int cycles, curChan, sOffTimer, delay, curSample, curSamplePeriod;
|
||||
unsigned int curSamplePos;
|
||||
unsigned int outputClock;
|
||||
unsigned char regPool[16];
|
||||
unsigned char regPool[17];
|
||||
friend void putDispatchChip(void*,int);
|
||||
friend void putDispatchChan(void*,int,int);
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -247,6 +247,9 @@ const char* cmdName[]={
|
|||
"ESFM_MODIN",
|
||||
"ESFM_ENV_DELAY",
|
||||
|
||||
"POWERNOISE_COUNTER_LOAD",
|
||||
"POWERNOISE_IO_WRITE",
|
||||
|
||||
"MACRO_RESTART",
|
||||
};
|
||||
|
||||
|
|
@ -1838,13 +1841,20 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
|
|||
// process MIDI events (TODO: everything)
|
||||
if (output) if (output->midiIn) while (!output->midiIn->queue.empty()) {
|
||||
TAMidiMessage& msg=output->midiIn->queue.front();
|
||||
if (midiDebug) {
|
||||
if (msg.type==TA_MIDI_SYSEX) {
|
||||
logD("MIDI debug: %.2X SysEx",msg.type);
|
||||
} else {
|
||||
logD("MIDI debug: %.2X %.2X %.2X",msg.type,msg.data[0],msg.data[1]);
|
||||
}
|
||||
}
|
||||
int ins=-1;
|
||||
if ((ins=midiCallback(msg))!=-2) {
|
||||
int chan=msg.type&15;
|
||||
switch (msg.type&0xf0) {
|
||||
case TA_MIDI_NOTE_OFF: {
|
||||
if (chan<0 || chan>=chans) break;
|
||||
if (midiIsDirect) {
|
||||
if (chan<0 || chan>=chans) break;
|
||||
pendingNotes.push_back(DivNoteEvent(chan,-1,-1,-1,false,false,true));
|
||||
} else {
|
||||
autoNoteOff(msg.type&15,msg.data[0]-12,msg.data[1]);
|
||||
|
|
@ -1857,15 +1867,16 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
|
|||
break;
|
||||
}
|
||||
case TA_MIDI_NOTE_ON: {
|
||||
if (chan<0 || chan>=chans) break;
|
||||
if (msg.data[1]==0) {
|
||||
if (midiIsDirect) {
|
||||
if (chan<0 || chan>=chans) break;
|
||||
pendingNotes.push_back(DivNoteEvent(chan,-1,-1,-1,false,false,true));
|
||||
} else {
|
||||
autoNoteOff(msg.type&15,msg.data[0]-12,msg.data[1]);
|
||||
}
|
||||
} else {
|
||||
if (midiIsDirect) {
|
||||
if (chan<0 || chan>=chans) break;
|
||||
pendingNotes.push_back(DivNoteEvent(chan,ins,msg.data[0]-12,msg.data[1],true,false,true));
|
||||
} else {
|
||||
autoNoteOn(msg.type&15,ins,msg.data[0]-12,msg.data[1]);
|
||||
|
|
@ -1880,6 +1891,8 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
|
|||
break;
|
||||
}
|
||||
}
|
||||
} else if (midiDebug) {
|
||||
logD("callback wants ignore");
|
||||
}
|
||||
//logD("%.2x",msg.type);
|
||||
output->midiIn->queue.pop();
|
||||
|
|
|
|||
|
|
@ -132,7 +132,8 @@ enum DivSystem {
|
|||
DIV_SYSTEM_TED,
|
||||
DIV_SYSTEM_C140,
|
||||
DIV_SYSTEM_C219,
|
||||
DIV_SYSTEM_ESFM
|
||||
DIV_SYSTEM_ESFM,
|
||||
DIV_SYSTEM_POWERNOISE,
|
||||
};
|
||||
|
||||
enum DivEffectType: unsigned short {
|
||||
|
|
|
|||
|
|
@ -536,7 +536,7 @@ void DivEngine::registerSystems() {
|
|||
|
||||
EffectHandlerMap fmOPNAPostEffectHandlerMap(fmOPNPostEffectHandlerMap);
|
||||
fmOPNAPostEffectHandlerMap.insert({
|
||||
{0x31, {DIV_CMD_ADPCMA_GLOBAL_VOLUME, "1Fxx: Set ADPCM-A global volume (0 to 3F)"}},
|
||||
{0x1f, {DIV_CMD_ADPCMA_GLOBAL_VOLUME, "1Fxx: Set ADPCM-A global volume (0 to 3F)"}},
|
||||
});
|
||||
|
||||
EffectHandlerMap fmOPLLPostEffectHandlerMap={
|
||||
|
|
@ -1984,6 +1984,23 @@ void DivEngine::registerSystems() {
|
|||
},
|
||||
fmESFMPostEffectHandlerMap
|
||||
);
|
||||
|
||||
sysDefs[DIV_SYSTEM_POWERNOISE]=new DivSysDef(
|
||||
"PowerNoise", NULL, 0xd4, 0, 4, false, false, 0, false, 0, 0, 0,
|
||||
"a fantasy sound chip designed by jvsTSX and The Beesh-Spweesh!\nused in the Hexheld fantasy console.",
|
||||
{"Noise 1", "Noise 2", "Noise 3", "Slope"},
|
||||
{"N1", "N2", "N3", "SL"},
|
||||
{DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_WAVE},
|
||||
{DIV_INS_POWERNOISE, DIV_INS_POWERNOISE, DIV_INS_POWERNOISE, DIV_INS_POWERNOISE_SLOPE},
|
||||
{},
|
||||
{
|
||||
{0x20, {DIV_CMD_POWERNOISE_COUNTER_LOAD, "20xx: Load low byte of noise channel LFSR (00 to FF) or slope channel accumulator (00 to 7F)", constVal<0>, effectVal}},
|
||||
{0x21, {DIV_CMD_POWERNOISE_COUNTER_LOAD, "21xx: Load high byte of noise channel LFSR (00 to FF)", constVal<1>, effectVal}},
|
||||
{0x22, {DIV_CMD_POWERNOISE_IO_WRITE, "22xx: Write to I/O port A", constVal<0>, effectVal}},
|
||||
{0x23, {DIV_CMD_POWERNOISE_IO_WRITE, "23xx: Write to I/O port B", constVal<1>, effectVal}},
|
||||
},
|
||||
{}
|
||||
);
|
||||
|
||||
sysDefs[DIV_SYSTEM_DUMMY]=new DivSysDef(
|
||||
"Dummy System", NULL, 0xfd, 0, 8, false, true, 0, false, 0, 0, 0,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue