Merge branch 'master' of https://github.com/tildearrow/furnace into nmk112

This commit is contained in:
cam900 2024-02-03 15:04:43 +09:00
commit 3922770e8f
166 changed files with 4829 additions and 2054 deletions

View file

@ -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;
}

View file

@ -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),

View file

@ -36,5 +36,6 @@
// dispatch
#define DIV_MAX_OUTPUTS 16
#define DIV_NOTE_NULL 0x7fffffff
#endif

View file

@ -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
};

View file

@ -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;

View file

@ -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() {

View file

@ -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),

View file

@ -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

View file

@ -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);

View file

@ -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;

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;

View file

@ -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;
}

View file

@ -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;

View file

@ -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);

View file

@ -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;

View file

@ -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;

View file

@ -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);

View file

@ -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)));
}

View file

@ -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)));
}

View file

@ -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];

View file

@ -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;

View file

@ -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;

View 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() {
}

View 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

View file

@ -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;

View file

@ -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;

View file

@ -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;
}

View file

@ -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;

View file

@ -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

View file

@ -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),

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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:

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;

View file

@ -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) {

View file

@ -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:

View file

@ -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();

View file

@ -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 {

View file

@ -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,