Merge branch 'master' into SID3
This commit is contained in:
commit
47f36f99d9
188 changed files with 5790 additions and 546 deletions
|
|
@ -364,6 +364,7 @@ bool DivCSPlayer::tick() {
|
|||
}
|
||||
|
||||
if (sendVolume || chan[i].volSpeed!=0) {
|
||||
int preSpeedVol=chan[i].volume;
|
||||
chan[i].volume+=chan[i].volSpeed;
|
||||
if (chan[i].volSpeedTarget!=-1) {
|
||||
bool atTarget=false;
|
||||
|
|
@ -377,7 +378,11 @@ bool DivCSPlayer::tick() {
|
|||
}
|
||||
|
||||
if (atTarget) {
|
||||
chan[i].volume=chan[i].volSpeedTarget;
|
||||
if (chan[i].volSpeed>0) {
|
||||
chan[i].volume=MAX(preSpeedVol,chan[i].volSpeedTarget);
|
||||
} else if (chan[i].volSpeed<0) {
|
||||
chan[i].volume=MIN(preSpeedVol,chan[i].volSpeedTarget);
|
||||
}
|
||||
chan[i].volSpeed=0;
|
||||
chan[i].volSpeedTarget=-1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -267,6 +267,22 @@ enum DivDispatchCmds {
|
|||
DIV_CMD_FDS_MOD_AUTO,
|
||||
|
||||
DIV_CMD_FM_OPMASK, // (mask)
|
||||
|
||||
DIV_CMD_MULTIPCM_MIX_FM, // (value)
|
||||
DIV_CMD_MULTIPCM_MIX_PCM, // (value)
|
||||
DIV_CMD_MULTIPCM_LFO, // (value)
|
||||
DIV_CMD_MULTIPCM_VIB, // (value)
|
||||
DIV_CMD_MULTIPCM_AM, // (value)
|
||||
DIV_CMD_MULTIPCM_AR, // (value)
|
||||
DIV_CMD_MULTIPCM_D1R, // (value)
|
||||
DIV_CMD_MULTIPCM_DL, // (value)
|
||||
DIV_CMD_MULTIPCM_D2R, // (value)
|
||||
DIV_CMD_MULTIPCM_RC, // (value)
|
||||
DIV_CMD_MULTIPCM_RR, // (value)
|
||||
DIV_CMD_MULTIPCM_DAMP, // (value)
|
||||
DIV_CMD_MULTIPCM_PSEUDO_REVERB, // (value)
|
||||
DIV_CMD_MULTIPCM_LFO_RESET, // (value)
|
||||
DIV_CMD_MULTIPCM_LEVEL_DIRECT, // (value)
|
||||
|
||||
DIV_CMD_SID3_SPECIAL_WAVE,
|
||||
DIV_CMD_SID3_RING_MOD_SRC,
|
||||
|
|
|
|||
|
|
@ -76,6 +76,8 @@
|
|||
#include "platform/vb.h"
|
||||
#include "platform/k007232.h"
|
||||
#include "platform/ga20.h"
|
||||
#include "platform/supervision.h"
|
||||
#include "platform/upd1771c.h"
|
||||
#include "platform/sm8521.h"
|
||||
#include "platform/pv1000.h"
|
||||
#include "platform/k053260.h"
|
||||
|
|
@ -685,6 +687,12 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
|
|||
case DIV_SYSTEM_GA20:
|
||||
dispatch=new DivPlatformGA20;
|
||||
break;
|
||||
case DIV_SYSTEM_SUPERVISION:
|
||||
dispatch=new DivPlatformSupervision;
|
||||
break;
|
||||
case DIV_SYSTEM_UPD1771C:
|
||||
dispatch=new DivPlatformUPD1771c;
|
||||
break;
|
||||
case DIV_SYSTEM_SM8521:
|
||||
dispatch=new DivPlatformSM8521;
|
||||
if (isRender) {
|
||||
|
|
@ -764,6 +772,24 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
|
|||
case DIV_SYSTEM_SID3:
|
||||
dispatch=new DivPlatformSID3;
|
||||
break;
|
||||
case DIV_SYSTEM_OPL4:
|
||||
dispatch=new DivPlatformOPL;
|
||||
((DivPlatformOPL*)dispatch)->setOPLType(4,false);
|
||||
if (isRender) {
|
||||
((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl4CoreRender",0));
|
||||
} else {
|
||||
((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl4Core",0));
|
||||
}
|
||||
break;
|
||||
case DIV_SYSTEM_OPL4_DRUMS:
|
||||
dispatch=new DivPlatformOPL;
|
||||
((DivPlatformOPL*)dispatch)->setOPLType(4,true);
|
||||
if (isRender) {
|
||||
((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl4CoreRender",0));
|
||||
} else {
|
||||
((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl4Core",0));
|
||||
}
|
||||
break;
|
||||
case DIV_SYSTEM_DUMMY:
|
||||
dispatch=new DivPlatformDummy;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -520,6 +520,15 @@ void DivEngine::initSongWithDesc(const char* description, bool inBase64, bool ol
|
|||
if (song.subsong[0]->hz<1.0) song.subsong[0]->hz=1.0;
|
||||
if (song.subsong[0]->hz>999.0) song.subsong[0]->hz=999.0;
|
||||
|
||||
curChanMask=c.getIntList("chanMask",{});
|
||||
for (unsigned char i:curChanMask) {
|
||||
int j=i-1;
|
||||
if (j<0) j=0;
|
||||
if (j>DIV_MAX_CHANS) j=DIV_MAX_CHANS-1;
|
||||
curSubSong->chanShow[j]=false;
|
||||
curSubSong->chanShowChanOsc[j]=false;
|
||||
}
|
||||
|
||||
song.author=getConfString("defaultAuthorName","");
|
||||
}
|
||||
|
||||
|
|
@ -754,6 +763,13 @@ int DivEngine::addSubSong() {
|
|||
BUSY_BEGIN;
|
||||
saveLock.lock();
|
||||
song.subsong.push_back(new DivSubSong);
|
||||
for (unsigned char i:curChanMask) {
|
||||
int j=i-1;
|
||||
if (j<0) j=0;
|
||||
if (j>DIV_MAX_CHANS) j=DIV_MAX_CHANS-1;
|
||||
song.subsong.back()->chanShow[j]=false;
|
||||
song.subsong.back()->chanShowChanOsc[j]=false;
|
||||
}
|
||||
saveLock.unlock();
|
||||
BUSY_END;
|
||||
return song.subsong.size()-1;
|
||||
|
|
|
|||
|
|
@ -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 "0.6.6"
|
||||
#define DIV_ENGINE_VERSION 218
|
||||
#define DIV_VERSION "dev222"
|
||||
#define DIV_ENGINE_VERSION 222
|
||||
// for imports
|
||||
#define DIV_VERSION_MOD 0xff01
|
||||
#define DIV_VERSION_FC 0xff02
|
||||
|
|
@ -518,6 +518,7 @@ class DivEngine {
|
|||
std::vector<DivCommand> cmdStream;
|
||||
std::vector<DivInstrumentType> possibleInsTypes;
|
||||
std::vector<DivEffectContainer> effectInst;
|
||||
std::vector<int> curChanMask;
|
||||
static DivSysDef* sysDefs[DIV_MAX_CHIP_DEFS];
|
||||
static DivSystem sysFileMapFur[DIV_MAX_CHIP_DEFS];
|
||||
static DivSystem sysFileMapDMF[DIV_MAX_CHIP_DEFS];
|
||||
|
|
|
|||
|
|
@ -503,12 +503,6 @@ void DivExportTiuna::run() {
|
|||
running=false;
|
||||
return;
|
||||
}
|
||||
SafeWriter dbg;
|
||||
dbg.init();
|
||||
dbg.writeText(fmt::format("renderedCmds size={}\n",renderedCmds.size()));
|
||||
for (const auto& i: confirmedMatches) {
|
||||
dbg.writeText(fmt::format("pos={},end={},id={}\n",i.pos,i.endPos,i.id,i.size));
|
||||
}
|
||||
|
||||
// write commands
|
||||
int totalSize=0;
|
||||
|
|
|
|||
|
|
@ -674,7 +674,10 @@ void DivExportZSM::run() {
|
|||
if (writes.size()>0)
|
||||
logD("zsmOps: Writing %d messages to chip %d",writes.size(),i);
|
||||
for (DivRegWrite& write: writes) {
|
||||
if (i==YM) zsm.writeYM(write.addr&0xff,write.val);
|
||||
if (i==YM) {
|
||||
if (done && write.addr==0x08 && (write.val&0x78)>0) continue; // don't process keydown on lookahead
|
||||
zsm.writeYM(write.addr&0xff,write.val);
|
||||
}
|
||||
if (i==VERA) {
|
||||
if (done && write.addr>=64) continue; // don't process any PCM or sync events on the loop lookahead
|
||||
zsm.writePSG(write.addr&0xff,write.val);
|
||||
|
|
|
|||
|
|
@ -2102,6 +2102,16 @@ bool DivEngine::loadFur(unsigned char* file, size_t len, int variantID) {
|
|||
}
|
||||
}
|
||||
|
||||
// SNES no anti-click
|
||||
if (ds.version<220) {
|
||||
for (int i=0; i<ds.systemLen; i++) {
|
||||
if (ds.system[i]==DIV_SYSTEM_SNES) {
|
||||
ds.systemFlags[i].set("antiClick",false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (active) quitDispatch();
|
||||
BUSY_BEGIN_SOFT;
|
||||
saveLock.lock();
|
||||
|
|
|
|||
|
|
@ -175,7 +175,11 @@ bool DivInstrumentMultiPCM::operator==(const DivInstrumentMultiPCM& other) {
|
|||
_C(rc) &&
|
||||
_C(lfo) &&
|
||||
_C(vib) &&
|
||||
_C(am)
|
||||
_C(am) &&
|
||||
_C(damp) &&
|
||||
_C(pseudoReverb) &&
|
||||
_C(lfoReset) &&
|
||||
_C(levelDirect)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -938,6 +942,14 @@ void DivInstrument::writeFeatureMP(SafeWriter* w) {
|
|||
w->writeC(multipcm.vib);
|
||||
w->writeC(multipcm.am);
|
||||
|
||||
unsigned char next=(
|
||||
(multipcm.damp?1:0)&
|
||||
(multipcm.pseudoReverb?2:0)&
|
||||
(multipcm.lfoReset?4:0)&
|
||||
(multipcm.levelDirect?8:0)
|
||||
);
|
||||
w->writeC(next);
|
||||
|
||||
FEATURE_END;
|
||||
}
|
||||
|
||||
|
|
@ -1386,6 +1398,12 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song, bo
|
|||
featureS2=true;
|
||||
featureS3=true;
|
||||
break;
|
||||
case DIV_INS_SUPERVISION:
|
||||
featureSM=true;
|
||||
if (amiga.useSample) featureSL=true;
|
||||
break;
|
||||
case DIV_INS_UPD1771C:
|
||||
break;
|
||||
case DIV_INS_MAX:
|
||||
break;
|
||||
case DIV_INS_NULL:
|
||||
|
|
@ -2337,6 +2355,14 @@ void DivInstrument::readFeatureMP(SafeReader& reader, short version) {
|
|||
multipcm.vib=reader.readC();
|
||||
multipcm.am=reader.readC();
|
||||
|
||||
if (version>=221) {
|
||||
unsigned char next=reader.readC();
|
||||
multipcm.damp=next&1;
|
||||
multipcm.pseudoReverb=next&2;
|
||||
multipcm.lfoReset=next&4;
|
||||
multipcm.levelDirect=next&8;
|
||||
}
|
||||
|
||||
READ_FEAT_END;
|
||||
}
|
||||
|
||||
|
|
@ -3676,4 +3702,4 @@ DivInstrument& DivInstrument::operator=( const DivInstrument& ins ) {
|
|||
*(DivInstrumentPOD*)this=ins;
|
||||
name=ins.name;
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,6 +96,8 @@ enum DivInstrumentType: unsigned short {
|
|||
DIV_INS_GBA_MINMOD=61,
|
||||
DIV_INS_BIFURCATOR=62,
|
||||
DIV_INS_SID2=63, // coincidence!
|
||||
DIV_INS_SUPERVISION=64,
|
||||
DIV_INS_UPD1771C=65,
|
||||
DIV_INS_SID3=66,
|
||||
DIV_INS_MAX,
|
||||
DIV_INS_NULL
|
||||
|
|
@ -612,6 +614,7 @@ struct DivInstrumentFDS {
|
|||
struct DivInstrumentMultiPCM {
|
||||
unsigned char ar, d1r, dl, d2r, rr, rc;
|
||||
unsigned char lfo, vib, am;
|
||||
bool damp, pseudoReverb, lfoReset, levelDirect;
|
||||
|
||||
bool operator==(const DivInstrumentMultiPCM& other);
|
||||
bool operator!=(const DivInstrumentMultiPCM& other) {
|
||||
|
|
@ -620,7 +623,11 @@ struct DivInstrumentMultiPCM {
|
|||
|
||||
DivInstrumentMultiPCM():
|
||||
ar(15), d1r(15), dl(0), d2r(0), rr(15), rc(15),
|
||||
lfo(0), vib(0), am(0) {
|
||||
lfo(0), vib(0), am(0),
|
||||
damp(false),
|
||||
pseudoReverb(false),
|
||||
lfoReset(false),
|
||||
levelDirect(true) {
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -762,7 +762,20 @@ int DivPlatformArcade::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_FM_OPMASK:
|
||||
chan[c.chan].opMask=c.value&15;
|
||||
switch (c.value>>4) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
chan[c.chan].opMask&=~(1<<((c.value>>4)-1));
|
||||
if (c.value&15) {
|
||||
chan[c.chan].opMask|=(1<<((c.value>>4)-1));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
chan[c.chan].opMask=c.value&15;
|
||||
break;
|
||||
}
|
||||
if (chan[c.chan].active) {
|
||||
chan[c.chan].opMaskChanged=true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -113,14 +113,15 @@ const unsigned char dacLogTableAY[256]={
|
|||
15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15
|
||||
};
|
||||
|
||||
void DivPlatformAY8910::runDAC() {
|
||||
void DivPlatformAY8910::runDAC(int runRate) {
|
||||
if (runRate==0) runRate=dacRate;
|
||||
for (int i=0; i<3; i++) {
|
||||
if (chan[i].active && (chan[i].curPSGMode.val&8) && chan[i].dac.sample!=-1) {
|
||||
chan[i].dac.period+=chan[i].dac.rate;
|
||||
bool end=false;
|
||||
bool changed=false;
|
||||
int prevOut=chan[i].dac.out;
|
||||
while (chan[i].dac.period>dacRate && !end) {
|
||||
while (chan[i].dac.period>runRate && !end) {
|
||||
DivSample* s=parent->getSample(chan[i].dac.sample);
|
||||
if (s->samples<=0 || chan[i].dac.pos<0 || chan[i].dac.pos>=(int)s->samples) {
|
||||
chan[i].dac.sample=-1;
|
||||
|
|
@ -143,7 +144,7 @@ void DivPlatformAY8910::runDAC() {
|
|||
end=true;
|
||||
break;
|
||||
}
|
||||
chan[i].dac.period-=dacRate;
|
||||
chan[i].dac.period-=runRate;
|
||||
}
|
||||
if (changed && !end) {
|
||||
if (!isMuted[i]) {
|
||||
|
|
@ -154,13 +155,15 @@ void DivPlatformAY8910::runDAC() {
|
|||
}
|
||||
}
|
||||
|
||||
void DivPlatformAY8910::runTFX() {
|
||||
void DivPlatformAY8910::runTFX(int runRate) {
|
||||
/*
|
||||
developer's note: if you are checking for intellivision
|
||||
make sure to add "&& selCore"
|
||||
because for some reason, the register remap doesn't work
|
||||
when the user uses AtomicSSG core
|
||||
*/
|
||||
float counterRatio=1.0;
|
||||
if (runRate!=0) counterRatio=(double)rate/(double)runRate;
|
||||
int timerPeriod, output;
|
||||
for (int i=0; i<3; i++) {
|
||||
if (chan[i].active && (chan[i].curPSGMode.val&16) && !(chan[i].curPSGMode.val&8) && chan[i].tfx.mode!=-1) {
|
||||
|
|
@ -182,9 +185,9 @@ void DivPlatformAY8910::runTFX() {
|
|||
continue;
|
||||
}
|
||||
}
|
||||
chan[i].tfx.counter += 1;
|
||||
chan[i].tfx.counter += counterRatio;
|
||||
if (chan[i].tfx.counter >= chan[i].tfx.period && chan[i].tfx.mode == 0) {
|
||||
chan[i].tfx.counter = 0;
|
||||
chan[i].tfx.counter -= chan[i].tfx.period;
|
||||
chan[i].tfx.out ^= 1;
|
||||
output = ((chan[i].tfx.out) ? chan[i].outVol : (chan[i].tfx.lowBound-(15-chan[i].outVol)));
|
||||
// TODO: fix this stupid crackling noise that happens
|
||||
|
|
@ -201,7 +204,7 @@ void DivPlatformAY8910::runTFX() {
|
|||
}
|
||||
}
|
||||
if (chan[i].tfx.counter >= chan[i].tfx.period && chan[i].tfx.mode == 1) {
|
||||
chan[i].tfx.counter = 0;
|
||||
chan[i].tfx.counter -= chan[i].tfx.period;
|
||||
if (!isMuted[i]) {
|
||||
if (intellivision && selCore) {
|
||||
immWrite(0xa, ayEnvMode);
|
||||
|
|
@ -211,7 +214,7 @@ void DivPlatformAY8910::runTFX() {
|
|||
}
|
||||
}
|
||||
if (chan[i].tfx.counter >= chan[i].tfx.period && chan[i].tfx.mode == 2) {
|
||||
chan[i].tfx.counter = 0;
|
||||
chan[i].tfx.counter -= chan[i].tfx.period;
|
||||
}
|
||||
}
|
||||
if (chan[i].tfx.num > 0) {
|
||||
|
|
@ -327,12 +330,9 @@ void DivPlatformAY8910::acquire(short** buf, size_t len) {
|
|||
|
||||
void DivPlatformAY8910::fillStream(std::vector<DivDelayedWrite>& stream, int sRate, size_t len) {
|
||||
writes.clear();
|
||||
int rate=(int)(chipClock/sRate);
|
||||
for (size_t i=0; i<len; i++) {
|
||||
for (int h=0; h<rate; h++) {
|
||||
runDAC();
|
||||
runTFX();
|
||||
}
|
||||
runDAC(sRate);
|
||||
runTFX(sRate);
|
||||
while (!writes.empty()) {
|
||||
QueuedWrite& w=writes.front();
|
||||
stream.push_back(DivDelayedWrite(i,w.addr,w.val));
|
||||
|
|
|
|||
|
|
@ -78,7 +78,9 @@ class DivPlatformAY8910: public DivDispatch {
|
|||
} dac;
|
||||
|
||||
struct TFX {
|
||||
int period, counter, offset, den, num, mode, lowBound, out;
|
||||
int period;
|
||||
float counter;
|
||||
int offset, den, num, mode, lowBound, out;
|
||||
TFX():
|
||||
period(0),
|
||||
counter(0),
|
||||
|
|
@ -156,8 +158,8 @@ class DivPlatformAY8910: public DivDispatch {
|
|||
friend void putDispatchChan(void*,int,int);
|
||||
|
||||
public:
|
||||
void runDAC();
|
||||
void runTFX();
|
||||
void runDAC(int runRate=0);
|
||||
void runTFX(int runRate=0);
|
||||
void setExtClockDiv(unsigned int eclk=COLOR_NTSC, unsigned char ediv=8);
|
||||
void acquire(short** buf, size_t len);
|
||||
void fillStream(std::vector<DivDelayedWrite>& stream, int sRate, size_t len);
|
||||
|
|
|
|||
|
|
@ -1465,7 +1465,20 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
|
|||
}
|
||||
case DIV_CMD_FM_OPMASK:
|
||||
if (c.chan>=psgChanOffs) break;
|
||||
chan[c.chan].opMask=c.value&15;
|
||||
switch (c.value>>4) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
chan[c.chan].opMask&=~(1<<((c.value>>4)-1));
|
||||
if (c.value&15) {
|
||||
chan[c.chan].opMask|=(1<<((c.value>>4)-1));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
chan[c.chan].opMask=c.value&15;
|
||||
break;
|
||||
}
|
||||
if (chan[c.chan].active) {
|
||||
chan[c.chan].opMaskChanged=true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -175,6 +175,13 @@ void DivPlatformNES::acquire_NSFPlayE(short** buf, size_t len) {
|
|||
int out2[2];
|
||||
for (size_t i=0; i<len; i++) {
|
||||
doPCM;
|
||||
|
||||
if (!writes.empty()) {
|
||||
QueuedWrite w=writes.front();
|
||||
doWrite(w.addr,w.val);
|
||||
regPool[w.addr&0x1f]=w.val;
|
||||
writes.pop();
|
||||
}
|
||||
|
||||
e1_NP->Tick(8);
|
||||
e2_NP->TickFrameSequence(8);
|
||||
|
|
@ -436,7 +443,7 @@ void DivPlatformNES::tick(bool sysTick) {
|
|||
// https://www.youtube.com/watch?v=vB4P8x2Am6Y
|
||||
|
||||
if (lsamp->loopEnd>lsamp->loopStart && goingToLoop) {
|
||||
int loopStartAddr=(sampleOffDPCM[dacSample]+lsamp->loopStart)>>3;
|
||||
int loopStartAddr=sampleOffDPCM[dacSample]+(lsamp->loopStart>>3);
|
||||
int loopLen=(lsamp->loopEnd-lsamp->loopStart)>>3;
|
||||
|
||||
rWrite(0x4012,(loopStartAddr>>6)&0xff);
|
||||
|
|
@ -492,14 +499,14 @@ int DivPlatformNES::dispatch(DivCommand c) {
|
|||
}
|
||||
}
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
dacSample=ins->amiga.getSample(c.value);
|
||||
dacSample=(int)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);
|
||||
dacSample=(int)ins->amiga.getSample(chan[c.chan].sampleNote);
|
||||
if (ins->type==DIV_INS_AMIGA) {
|
||||
c.value=ins->amiga.getFreq(chan[c.chan].sampleNote);
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -29,24 +29,41 @@ extern "C" {
|
|||
}
|
||||
#include "sound/ymfm/ymfm_adpcm.h"
|
||||
#include "sound/ymfm/ymfm_opl.h"
|
||||
#include "sound/ymfm/ymfm_pcm.h"
|
||||
#include "sound/ymf278b/ymf278.h"
|
||||
|
||||
class DivOPLAInterface: public ymfm::ymfm_interface {
|
||||
public:
|
||||
unsigned char* adpcmBMem;
|
||||
unsigned char* pcmMem;
|
||||
int sampleBank;
|
||||
uint8_t ymfm_external_read(ymfm::access_class type, uint32_t address);
|
||||
void ymfm_external_write(ymfm::access_class type, uint32_t address, uint8_t data);
|
||||
DivOPLAInterface(): adpcmBMem(NULL), sampleBank(0) {}
|
||||
DivOPLAInterface(): adpcmBMem(NULL), pcmMem(NULL), sampleBank(0) {}
|
||||
};
|
||||
|
||||
class DivYMF278MemoryInterface: public MemoryInterface {
|
||||
public:
|
||||
unsigned char* memory;
|
||||
DivYMF278MemoryInterface(unsigned size_) : memory(NULL), size(size_) {};
|
||||
byte operator[](unsigned address) const override;
|
||||
unsigned getSize() const override { return size; };
|
||||
void write(unsigned address, byte value) override {};
|
||||
void clear(byte value) override {};
|
||||
private:
|
||||
unsigned size;
|
||||
};
|
||||
|
||||
class DivPlatformOPL: public DivDispatch {
|
||||
protected:
|
||||
struct Channel: public SharedChannel<int> {
|
||||
DivInstrumentFM state;
|
||||
unsigned char freqH, freqL;
|
||||
unsigned int freqH, freqL;
|
||||
int sample, fixedFreq;
|
||||
bool furnacePCM, fourOp, hardReset;
|
||||
unsigned char pan;
|
||||
bool furnacePCM, fourOp, hardReset, writeCtrl;
|
||||
bool levelDirect, damp, pseudoReverb, lfoReset, ch;
|
||||
int lfo, vib, am, ar, d1r, d2r, dl, rc, rr;
|
||||
int pan;
|
||||
int macroVolMul;
|
||||
Channel():
|
||||
SharedChannel<int>(0),
|
||||
|
|
@ -57,14 +74,29 @@ class DivPlatformOPL: public DivDispatch {
|
|||
furnacePCM(false),
|
||||
fourOp(false),
|
||||
hardReset(false),
|
||||
writeCtrl(false),
|
||||
levelDirect(true),
|
||||
damp(false),
|
||||
pseudoReverb(false),
|
||||
lfoReset(false),
|
||||
ch(false),
|
||||
lfo(0),
|
||||
vib(0),
|
||||
am(0),
|
||||
ar(15),
|
||||
d1r(15),
|
||||
d2r(0),
|
||||
dl(0),
|
||||
rc(15),
|
||||
rr(15),
|
||||
pan(3),
|
||||
macroVolMul(64) {
|
||||
state.ops=2;
|
||||
}
|
||||
};
|
||||
Channel chan[20];
|
||||
DivDispatchOscBuffer* oscBuf[20];
|
||||
bool isMuted[20];
|
||||
Channel chan[44];
|
||||
DivDispatchOscBuffer* oscBuf[44];
|
||||
bool isMuted[44];
|
||||
struct QueuedWrite {
|
||||
unsigned short addr;
|
||||
unsigned char val;
|
||||
|
|
@ -72,7 +104,7 @@ class DivPlatformOPL: public DivDispatch {
|
|||
QueuedWrite(): addr(0), val(0), addrOrVal(false) {}
|
||||
QueuedWrite(unsigned short a, unsigned char v): addr(a), val(v), addrOrVal(false) {}
|
||||
};
|
||||
FixedQueue<QueuedWrite,2048> writes;
|
||||
FixedQueue<QueuedWrite,4096> writes;
|
||||
|
||||
unsigned int dacVal;
|
||||
unsigned int dacVal2;
|
||||
|
|
@ -86,8 +118,12 @@ class DivPlatformOPL: public DivDispatch {
|
|||
|
||||
unsigned char* adpcmBMem;
|
||||
size_t adpcmBMemLen;
|
||||
unsigned char* pcmMem;
|
||||
size_t pcmMemLen;
|
||||
DivOPLAInterface iface;
|
||||
DivYMF278MemoryInterface pcmMemory;
|
||||
unsigned int sampleOffB[256];
|
||||
unsigned int sampleOffPCM[256];
|
||||
bool sampleLoaded[256];
|
||||
|
||||
ymfm::adpcm_b_engine* adpcmB;
|
||||
|
|
@ -97,12 +133,13 @@ class DivPlatformOPL: public DivDispatch {
|
|||
const unsigned short* chanMap;
|
||||
const unsigned char* outChanMap;
|
||||
int chipFreqBase, chipRateBase;
|
||||
int delay, chipType, oplType, chans, melodicChans, totalChans, adpcmChan, sampleBank, totalOutputs;
|
||||
int delay, chipType, oplType, chans, melodicChans, totalChans, adpcmChan=-1, pcmChanOffs=-1, sampleBank, totalOutputs, ramSize;
|
||||
int fmMixL=7, fmMixR=7, pcmMixL=7, pcmMixR=7;
|
||||
unsigned char lastBusy;
|
||||
unsigned char drumState;
|
||||
unsigned char drumVol[5];
|
||||
|
||||
unsigned char regPool[512];
|
||||
unsigned char regPool[768];
|
||||
|
||||
bool properDrums, properDrumsSys, dam, dvb;
|
||||
|
||||
|
|
@ -115,15 +152,17 @@ class DivPlatformOPL: public DivDispatch {
|
|||
|
||||
bool update4OpMask, pretendYMU, downsample, compatPan;
|
||||
|
||||
short oldWrites[512];
|
||||
short pendingWrites[512];
|
||||
short oldWrites[768];
|
||||
short pendingWrites[768];
|
||||
|
||||
// chips
|
||||
opl3_chip fm;
|
||||
YMF278 pcm;
|
||||
ymfm::ym3526* fm_ymfm1;
|
||||
ymfm::ym3812* fm_ymfm2;
|
||||
ymfm::y8950* fm_ymfm8950;
|
||||
ymfm::ymf262* fm_ymfm3;
|
||||
ymfm::ymf278b* fm_ymfm4;
|
||||
fmopl2_t fm_lle2;
|
||||
fmopl3_t fm_lle3;
|
||||
|
||||
|
|
@ -141,6 +180,7 @@ class DivPlatformOPL: public DivDispatch {
|
|||
void acquire_nukedLLE3(short** buf, size_t len);
|
||||
void acquire_nuked(short** buf, size_t len);
|
||||
void acquire_ymfm3(short** buf, size_t len);
|
||||
void acquire_ymfm4(short** buf, size_t len);
|
||||
void acquire_ymfm8950(short** buf, size_t len);
|
||||
void acquire_ymfm2(short** buf, size_t len);
|
||||
void acquire_ymfm1(short** buf, size_t len);
|
||||
|
|
@ -182,6 +222,9 @@ class DivPlatformOPL: public DivDispatch {
|
|||
void renderSamples(int chipID);
|
||||
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
|
||||
void quit();
|
||||
DivPlatformOPL():
|
||||
pcmMemory(0x400000),
|
||||
pcm(pcmMemory) {}
|
||||
~DivPlatformOPL();
|
||||
};
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -28,6 +28,11 @@ uint8_t DivOPLAInterface::ymfm_external_read(ymfm::access_class type, uint32_t a
|
|||
return 0;
|
||||
}
|
||||
return adpcmBMem[address&0x3ffff];
|
||||
case ymfm::ACCESS_PCM:
|
||||
if (pcmMem==NULL) {
|
||||
return 0;
|
||||
}
|
||||
return pcmMem[address&0x3fffff];
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -36,3 +41,10 @@ uint8_t DivOPLAInterface::ymfm_external_read(ymfm::access_class type, uint32_t a
|
|||
|
||||
void DivOPLAInterface::ymfm_external_write(ymfm::access_class type, uint32_t address, uint8_t data) {
|
||||
}
|
||||
|
||||
byte DivYMF278MemoryInterface::operator[](unsigned address) const {
|
||||
if (memory && address<size) {
|
||||
return memory[address];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -27,6 +27,8 @@
|
|||
|
||||
#define rWrite(a,v) if (!skipRegisterWrites) {writes.push(QueuedWrite(a,v)); if (dumpWrites) {addWrite(a,v);} }
|
||||
#define chWrite(c,a,v) {rWrite((a)+(c)*16,v)}
|
||||
#define rWriteDelay(a,v,d) if (!skipRegisterWrites) {writes.push(QueuedWrite(a,v,d)); if (dumpWrites) {addWrite(a,v);} }
|
||||
#define chWriteDelay(c,a,v,d) {rWrite((a)+(c)*16,v,d)}
|
||||
#define sampleTableAddr(c) (sampleTableBase+(c)*4)
|
||||
#define waveTableAddr(c) (sampleTableBase+8*4+(c)*9*16)
|
||||
|
||||
|
|
@ -77,7 +79,7 @@ void DivPlatformSNES::acquire(short** buf, size_t len) {
|
|||
dsp.write(w.addr,w.val);
|
||||
regPool[w.addr&0x7f]=w.val;
|
||||
writes.pop();
|
||||
delay=(w.addr==0x5c)?8:1;
|
||||
delay=w.delay;
|
||||
}
|
||||
}
|
||||
dsp.set_output(out,1);
|
||||
|
|
@ -253,7 +255,18 @@ void DivPlatformSNES::tick(bool sysTick) {
|
|||
}
|
||||
}
|
||||
if (koff!=0) {
|
||||
rWrite(0x5c,koff);
|
||||
// TODO: improve
|
||||
if (antiClick) {
|
||||
for (int i=0; i<8; i++) {
|
||||
if (koff&(1<<i)) {
|
||||
chWrite(i,5,0);
|
||||
chWrite(i,7,0x9f);
|
||||
chan[i].shallWriteEnv=true;
|
||||
}
|
||||
}
|
||||
rWriteDelay(0x7e,0,64);
|
||||
}
|
||||
rWriteDelay(0x5c,koff,8);
|
||||
}
|
||||
if (writeControl) {
|
||||
unsigned char control=(noiseFreq&0x1f)|(echoOn?0:0x20);
|
||||
|
|
@ -314,10 +327,7 @@ void DivPlatformSNES::tick(bool sysTick) {
|
|||
}
|
||||
}
|
||||
if (koff!=0) {
|
||||
rWrite(0x5c,0);
|
||||
}
|
||||
if (kon!=0) {
|
||||
rWrite(0x4c,kon);
|
||||
rWriteDelay(0x5c,0,8);
|
||||
}
|
||||
for (int i=0; i<8; i++) {
|
||||
if (chan[i].shallWriteVol) {
|
||||
|
|
@ -325,6 +335,9 @@ void DivPlatformSNES::tick(bool sysTick) {
|
|||
chan[i].shallWriteVol=false;
|
||||
}
|
||||
}
|
||||
if (kon!=0) {
|
||||
rWrite(0x4c,kon);
|
||||
}
|
||||
}
|
||||
|
||||
int DivPlatformSNES::dispatch(DivCommand c) {
|
||||
|
|
@ -1026,6 +1039,7 @@ void DivPlatformSNES::setFlags(const DivConfig& flags) {
|
|||
initEchoMask=flags.getInt("echoMask",0);
|
||||
|
||||
interpolationOff=flags.getBool("interpolationOff",false);
|
||||
antiClick=flags.getBool("antiClick",true);
|
||||
}
|
||||
|
||||
int DivPlatformSNES::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) {
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ class DivPlatformSNES: public DivDispatch {
|
|||
bool writeDryVol;
|
||||
bool echoOn;
|
||||
bool interpolationOff;
|
||||
bool antiClick;
|
||||
|
||||
bool initEchoOn;
|
||||
signed char initEchoVolL;
|
||||
|
|
@ -82,8 +83,10 @@ class DivPlatformSNES: public DivDispatch {
|
|||
struct QueuedWrite {
|
||||
unsigned char addr;
|
||||
unsigned char val;
|
||||
QueuedWrite(): addr(0), val(0) {}
|
||||
QueuedWrite(unsigned char a, unsigned char v): addr(a), val(v) {}
|
||||
unsigned char delay;
|
||||
unsigned char padding;
|
||||
QueuedWrite(): addr(0), val(0), delay(0), padding(0) {}
|
||||
QueuedWrite(unsigned char a, unsigned char v, unsigned char d=0): addr(a), val(v), delay(d), padding(0) {}
|
||||
};
|
||||
FixedQueue<QueuedWrite,256> writes;
|
||||
|
||||
|
|
|
|||
|
|
@ -146,7 +146,8 @@ void SoundUnit::NextSample(short* l, short* r) {
|
|||
}
|
||||
}
|
||||
}
|
||||
fns[i]=ns[i]*chan[i].vol*((chan[i].flags0&8)?4:2);
|
||||
fns[i]=ns[i]*chan[i].vol;
|
||||
if (!(chan[i].flags0&8)) fns[i]>>=1;
|
||||
if ((chan[i].flags0&0xe0)!=0) {
|
||||
int ff=chan[i].cutoff;
|
||||
nslow[i]=nslow[i]+(((ff)*nsband[i])>>16);
|
||||
|
|
@ -259,12 +260,12 @@ void SoundUnit::NextSample(short* l, short* r) {
|
|||
|
||||
// mix
|
||||
if (dsOut) {
|
||||
tnsL=nsL[dsChannel]<<1;
|
||||
tnsR=nsR[dsChannel]<<1;
|
||||
tnsL=nsL[dsChannel]<<3;
|
||||
tnsR=nsR[dsChannel]<<3;
|
||||
dsChannel=(dsChannel+1)&7;
|
||||
} else {
|
||||
tnsL=(nsL[0]+nsL[1]+nsL[2]+nsL[3]+nsL[4]+nsL[5]+nsL[6]+nsL[7])>>2;
|
||||
tnsR=(nsR[0]+nsR[1]+nsR[2]+nsR[3]+nsR[4]+nsR[5]+nsR[6]+nsR[7])>>2;
|
||||
tnsL=(nsL[0]+nsL[1]+nsL[2]+nsL[3]+nsL[4]+nsL[5]+nsL[6]+nsL[7]);
|
||||
tnsR=(nsR[0]+nsR[1]+nsR[2]+nsR[3]+nsR[4]+nsR[5]+nsR[6]+nsR[7]);
|
||||
|
||||
IL1=minval(32767,maxval(-32767,tnsL))>>8;
|
||||
IL2=minval(32767,maxval(-32767,tnsR))>>8;
|
||||
|
|
|
|||
|
|
@ -33,12 +33,12 @@ class SoundUnit {
|
|||
int rcycle[8];
|
||||
unsigned int lfsr[8];
|
||||
signed char ns[8];
|
||||
int fns[8];
|
||||
int nsL[8];
|
||||
int nsR[8];
|
||||
int nslow[8];
|
||||
int nshigh[8];
|
||||
int nsband[8];
|
||||
short fns[8];
|
||||
short nsL[8];
|
||||
short nsR[8];
|
||||
short nslow[8];
|
||||
short nshigh[8];
|
||||
short nsband[8];
|
||||
int tnsL, tnsR;
|
||||
unsigned char ilBufPeriod;
|
||||
unsigned short ilBufPos;
|
||||
|
|
|
|||
282
src/engine/platform/sound/supervision.c
Normal file
282
src/engine/platform/sound/supervision.c
Normal file
|
|
@ -0,0 +1,282 @@
|
|||
// THIS IS A MODIFIED VERSION OF POTATOR'S SOUND EMULATION CORE
|
||||
// MODIFIED BY AART1256 IN 2024
|
||||
|
||||
#include "supervision.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#define SV_SAMPLE_RATE ((svision->UNSCALED_CLOCK)/64)
|
||||
#define SV_DEC_TICK ((SV_SAMPLE_RATE)/60)
|
||||
|
||||
void supervision_sound_set_clock(struct svision_t *svision, uint32 clock) {
|
||||
svision->UNSCALED_CLOCK = clock;
|
||||
}
|
||||
|
||||
void supervision_memorymap_registers_write(struct svision_t *svision, uint32 Addr, uint8 Value)
|
||||
{
|
||||
switch (Addr & 0x1fff) {
|
||||
case 0x10: case 0x11: case 0x12: case 0x13:
|
||||
case 0x14: case 0x15: case 0x16: case 0x17:
|
||||
supervision_sound_wave_write(svision, ((Addr & 0x4) >> 2), Addr & 3, Value);
|
||||
break;
|
||||
case 0x18:
|
||||
case 0x19:
|
||||
case 0x1a:
|
||||
case 0x1b:
|
||||
case 0x1c:
|
||||
supervision_sound_dma_write(svision, Addr & 0x07, Value);
|
||||
break;
|
||||
case 0x28:
|
||||
case 0x29:
|
||||
case 0x2a:
|
||||
supervision_sound_noise_write(svision, Addr & 0x07, Value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void supervision_set_mute_mask(struct svision_t *svision, uint8 mask) {
|
||||
svision->ch_mask = mask;
|
||||
}
|
||||
|
||||
void supervision_sound_set_flags(struct svision_t *svision, uint8 flags_set)
|
||||
{
|
||||
svision->flags = flags_set;
|
||||
}
|
||||
|
||||
void supervision_sound_reset(struct svision_t *svision)
|
||||
{
|
||||
memset(svision->m_channel, 0, sizeof(svision->m_channel));
|
||||
memset(&svision->m_noise, 0, sizeof(svision->m_noise) );
|
||||
memset(&svision->m_dma, 0, sizeof(svision->m_dma) );
|
||||
|
||||
memset(svision->ch, 0, sizeof(svision->ch) );
|
||||
svision->decrement_tick = 0;
|
||||
svision->ch_mask = 15;
|
||||
}
|
||||
|
||||
void supervision_sound_stream_update(struct svision_t *svision, uint8 *stream, uint32 len)
|
||||
{
|
||||
size_t i, j;
|
||||
SVISION_CHANNEL *channel;
|
||||
uint8 s = 0;
|
||||
uint8 *left = stream + 0;
|
||||
uint8 *right = stream + 1;
|
||||
uint8 *chout = stream + 2;
|
||||
|
||||
for (i = 0; i < len >> 1; i++, left += 2, right += 2) {
|
||||
*left = *right = 0;
|
||||
for (channel = svision->m_channel, j = 0; j < 2; j++, channel++) {
|
||||
chout[j] = 0;
|
||||
if (svision->ch[j].size != 0) {
|
||||
if (svision->ch[j].on || channel->count != 0) {
|
||||
BOOL on = 0;
|
||||
switch (svision->ch[j].waveform) {
|
||||
case 0: // 12.5%
|
||||
on = svision->ch[j].pos < (28 * svision->ch[j].size) >> 5;
|
||||
break;
|
||||
case 1: // 25%
|
||||
on = svision->ch[j].pos < (24 * svision->ch[j].size) >> 5;
|
||||
break;
|
||||
case 2: // 50%
|
||||
on = svision->ch[j].pos < svision->ch[j].size / 2;
|
||||
break;
|
||||
case 3: // 75%
|
||||
on = svision->ch[j].pos < svision->ch[j].size / 4;
|
||||
// MESS/MAME: <= (9 * svision->ch[j].size) >> 5;
|
||||
break;
|
||||
}
|
||||
s = on ? (svision->ch[j].volume)<<2 : 0;
|
||||
s = ((svision->ch_mask>>(3-j))&1)?s:0;
|
||||
if (svision->flags&1) {
|
||||
if (j == 0)
|
||||
*right += s;
|
||||
else
|
||||
*left += s;
|
||||
} else {
|
||||
*left += s;
|
||||
*right += s;
|
||||
}
|
||||
chout[j] = s;
|
||||
}
|
||||
svision->ch[j].pos++;
|
||||
if (svision->ch[j].pos >= svision->ch[j].size) {
|
||||
svision->ch[j].pos = 0;
|
||||
// Transition from off to on
|
||||
if (channel->on) {
|
||||
memcpy(&svision->ch[j], channel, sizeof(svision->ch[j]));
|
||||
channel->on = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (svision->m_noise.on && (svision->m_noise.play || svision->m_noise.count != 0)) {
|
||||
s = (svision->m_noise.value * svision->m_noise.volume) << 2;
|
||||
s = svision->ch_mask&1?s:0;
|
||||
chout[3] = 0;
|
||||
if (svision->m_noise.left) {
|
||||
*left += s;
|
||||
chout[3] = s;
|
||||
}
|
||||
if (svision->m_noise.right) {
|
||||
*right += s;
|
||||
chout[3] = s;
|
||||
}
|
||||
svision->m_noise.pos += svision->m_noise.step;
|
||||
while (svision->m_noise.pos >= 1.0) { // if/while difference - Pacific Battle
|
||||
// LFSR: x^2 + x + 1
|
||||
uint16 feedback;
|
||||
svision->m_noise.value = svision->m_noise.state & 1;
|
||||
feedback = ((svision->m_noise.state >> 1) ^ svision->m_noise.state) & 0x0001;
|
||||
feedback <<= svision->m_noise.type;
|
||||
svision->m_noise.state = (svision->m_noise.state >> 1) | feedback;
|
||||
svision->m_noise.pos -= 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
chout[2] = 0;
|
||||
if (svision->m_dma.on) {
|
||||
uint8 sample;
|
||||
uint16 addr = svision->m_dma.start + (uint16)svision->m_dma.pos / 2;
|
||||
if (addr >= 0x8000 && addr < 0xc000) {
|
||||
sample = svision->supervision_dma_mem[(addr & 0x3fff) | svision->m_dma.ca14to16];
|
||||
}
|
||||
if (((uint16)svision->m_dma.pos) & 1)
|
||||
s = (sample & 0xf);
|
||||
else
|
||||
s = (sample & 0xf0) >> 4;
|
||||
s <<= 2;
|
||||
s = ((svision->ch_mask>>1)&1)?s:0;
|
||||
chout[2] = 0;
|
||||
if (svision->m_dma.left) {
|
||||
*left += s;
|
||||
chout[2] = s;
|
||||
}
|
||||
if (svision->m_dma.right) {
|
||||
*right += s;
|
||||
chout[2] = s;
|
||||
}
|
||||
svision->m_dma.pos += svision->m_dma.step;
|
||||
if (svision->m_dma.pos >= svision->m_dma.size) {
|
||||
svision->m_dma.on = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (svision->decrement_tick > SV_DEC_TICK) {
|
||||
svision->decrement_tick = 0;
|
||||
supervision_sound_decrement(svision);
|
||||
}
|
||||
svision->decrement_tick++;
|
||||
}
|
||||
}
|
||||
|
||||
void supervision_sound_decrement(struct svision_t *svision)
|
||||
{
|
||||
if (svision->m_channel[0].count > 0)
|
||||
svision->m_channel[0].count--;
|
||||
if (svision->m_channel[1].count > 0)
|
||||
svision->m_channel[1].count--;
|
||||
if (svision->m_noise.count > 0)
|
||||
svision->m_noise.count--;
|
||||
}
|
||||
|
||||
void supervision_sound_wave_write(struct svision_t *svision, int which, int offset, uint8 data)
|
||||
{
|
||||
SVISION_CHANNEL *channel = &svision->m_channel[which];
|
||||
|
||||
channel->reg[offset] = data;
|
||||
switch (offset) {
|
||||
case 0:
|
||||
case 1: {
|
||||
uint16 size;
|
||||
size = channel->reg[0] | ((channel->reg[1] & 7) << 8);
|
||||
// if size == 0 then channel->size == 0
|
||||
if (size)
|
||||
channel->size = (uint16)(((real)SV_SAMPLE_RATE) * ((real)((size + 1) << 5)) / ((real)svision->UNSCALED_CLOCK));
|
||||
else
|
||||
channel->size = 0;
|
||||
channel->pos = 0;
|
||||
// Popo Team
|
||||
if (channel->count != 0 || svision->ch[which].size == 0 || channel->size == 0) {
|
||||
svision->ch[which].size = channel->size;
|
||||
if (channel->count == 0)
|
||||
svision->ch[which].pos = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
channel->on = data & 0x40;
|
||||
channel->waveform = (data & 0x30) >> 4;
|
||||
channel->volume = data & 0x0f;
|
||||
if (!channel->on || svision->ch[which].size == 0 || channel->size == 0) {
|
||||
uint16 pos = svision->ch[which].pos;
|
||||
memcpy(&svision->ch[which], channel, sizeof(svision->ch[which]));
|
||||
if (channel->count != 0) // Journey to the West
|
||||
svision->ch[which].pos = pos;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
channel->count = data + 1;
|
||||
svision->ch[which].size = channel->size; // Sonny Xpress!
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void supervision_sound_dma_write(struct svision_t *svision, int offset, uint8 data)
|
||||
{
|
||||
svision->m_dma.reg[offset] = data;
|
||||
switch (offset) {
|
||||
case 0:
|
||||
case 1:
|
||||
svision->m_dma.start = (svision->m_dma.reg[0] | (svision->m_dma.reg[1] << 8));
|
||||
break;
|
||||
case 2:
|
||||
svision->m_dma.size = (data ? data : 0x100) * 32; // Number of 4-bit samples
|
||||
break;
|
||||
case 3:
|
||||
// Test games: Classic Casino, SSSnake
|
||||
svision->m_dma.step = ((real)svision->UNSCALED_CLOCK) / ((real)SV_SAMPLE_RATE * (256 << (data & 3)));
|
||||
// MESS/MAME. Wrong
|
||||
//svision->m_dma.step = svision->UNSCALED_CLOCK / (256.0 * SV_SAMPLE_RATE * (1 + (data & 3)));
|
||||
svision->m_dma.right = data & 4;
|
||||
svision->m_dma.left = data & 8;
|
||||
svision->m_dma.ca14to16 = ((data & 0x70) >> 4) << 14;
|
||||
break;
|
||||
case 4:
|
||||
svision->m_dma.on = data & 0x80;
|
||||
if (svision->m_dma.on) {
|
||||
svision->m_dma.pos = 0.0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void supervision_sound_noise_write(struct svision_t *svision, int offset, uint8 data)
|
||||
{
|
||||
svision->m_noise.reg[offset] = data;
|
||||
switch (offset) {
|
||||
case 0: {
|
||||
uint32 divisor = 8 << (data >> 4);
|
||||
if (divisor)
|
||||
svision->m_noise.step = ((real)svision->UNSCALED_CLOCK) / ((real)SV_SAMPLE_RATE * divisor);
|
||||
else
|
||||
svision->m_noise.step = 0;
|
||||
|
||||
svision->m_noise.step = ((real)svision->UNSCALED_CLOCK) / ((real)SV_SAMPLE_RATE * divisor);
|
||||
svision->m_noise.volume = data & 0xf;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
svision->m_noise.count = data + 1;
|
||||
break;
|
||||
case 2:
|
||||
svision->m_noise.type = (data & 1) ? 14 : 6;
|
||||
svision->m_noise.play = data & 2;
|
||||
svision->m_noise.right = data & 4;
|
||||
svision->m_noise.left = data & 8;
|
||||
svision->m_noise.on = data & 0x10; /* honey bee start */
|
||||
svision->m_noise.state = 1;
|
||||
break;
|
||||
}
|
||||
svision->m_noise.pos = 0.0;
|
||||
}
|
||||
76
src/engine/platform/sound/supervision.h
Normal file
76
src/engine/platform/sound/supervision.h
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
#ifndef __SUPERVISION_SOUND_H__
|
||||
#define __SUPERVISION_SOUND_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef uint8_t uint8;
|
||||
typedef uint16_t uint16;
|
||||
typedef uint32_t uint32;
|
||||
typedef float real;
|
||||
typedef uint8_t BOOL;
|
||||
|
||||
|
||||
typedef struct {
|
||||
uint8 reg[4];
|
||||
int on;
|
||||
uint8 waveform, volume;
|
||||
uint16 pos, size;
|
||||
uint16 count;
|
||||
} SVISION_CHANNEL;
|
||||
|
||||
typedef struct {
|
||||
uint8 reg[3];
|
||||
int on, right, left, play;
|
||||
uint8 type; // 6 - 7-Bit, 14 - 15-Bit
|
||||
uint16 state;
|
||||
uint8 value, volume;
|
||||
uint16 count;
|
||||
real pos, step;
|
||||
} SVISION_NOISE;
|
||||
|
||||
typedef struct {
|
||||
uint8 reg[5];
|
||||
int on, right, left;
|
||||
uint32 ca14to16;
|
||||
uint16 start;
|
||||
uint16 size;
|
||||
real pos, step;
|
||||
} SVISION_DMA;
|
||||
|
||||
struct svision_t {
|
||||
SVISION_CHANNEL m_channel[2];
|
||||
// For clear sound (no grating), sync with m_channel
|
||||
SVISION_CHANNEL ch[2];
|
||||
SVISION_NOISE m_noise;
|
||||
SVISION_DMA m_dma;
|
||||
uint8 supervision_dma_mem[65536];
|
||||
uint32 decrement_tick;
|
||||
uint32 UNSCALED_CLOCK;
|
||||
uint8 ch_mask, flags;
|
||||
};
|
||||
|
||||
void supervision_sound_reset(struct svision_t *svision);
|
||||
void supervision_sound_set_clock(struct svision_t *svision, uint32 clock);
|
||||
void supervision_sound_stream_update(struct svision_t *svision, uint8 *stream, uint32 len);
|
||||
void supervision_sound_decrement(struct svision_t *svision);
|
||||
void supervision_sound_wave_write(struct svision_t *svision, int which, int offset, uint8 data);
|
||||
void supervision_sound_dma_write(struct svision_t *svision,int offset, uint8 data);
|
||||
void supervision_sound_noise_write(struct svision_t *svision, int offset, uint8 data);
|
||||
void supervision_sound_noise_write(struct svision_t *svision, int offset, uint8 data);
|
||||
void supervision_memorymap_registers_write(struct svision_t *svision, uint32 Addr, uint8 Value);
|
||||
// 12SN
|
||||
void supervision_set_mute_mask(struct svision_t *svision, uint8 mask);
|
||||
void supervision_sound_set_flags(struct svision_t *svision, uint8 flags_set);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
300
src/engine/platform/sound/upd1771c.c
Normal file
300
src/engine/platform/sound/upd1771c.c
Normal file
|
|
@ -0,0 +1,300 @@
|
|||
// SOME CODE IS TAKEN FROM MAME'S EMULATION OF THE UPD1771C
|
||||
// MADE BY AART1256 IN 2024
|
||||
|
||||
#include "upd1771c.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
Each of the 8 waveforms have been extracted from the uPD1771c-017 internal
|
||||
ROM, from offset 0x1fd (start of first waveform) to offset 0x2fc (end of
|
||||
last waveform).
|
||||
(note: given test mode dumping offset non-clarity it may be 0x200-0x2ff)
|
||||
The waveforms are stored in an 8-bit sign-magnitude format, so if in ROM the
|
||||
upper bit is 0x80, invert the lower 7 bits to get the 2's complement result
|
||||
seen here.
|
||||
Note that only the last 4 waveforms appear to have been intended for use as
|
||||
waveforms; the first four look as if they're playing back a piece of code as
|
||||
wave data.
|
||||
*/
|
||||
const signed char WAVEFORMS[8][32]={
|
||||
{ 0, 0,-123,-123, -61, -23, 125, 107, 94, 83,-128,-128,-128, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0,-128,-128,-128, 0, 0, 0, 0, 0, 0},
|
||||
{ 37, 16, 32, -21, 32, 52, 4, 4, 33, 18, 60, 56, 0, 8, 5, 16, 65, 19, 69, 16, -2, 19, 37, 16, 97, 19, 0, 87, 127, -3, 1, 2},
|
||||
{ 0, 8, 1, 52, 4, 0, 0, 77, 81,-109, 47, 97, -83,-109, 38, 97, 0, 52, 4, 0, 1, 4, 1, 22, 2, -46, 33, 97, 0, 8, -85, -99},
|
||||
{ 47, 97, 40, 97, -3, 25, 64, 17, 0, 52, 12, 5, 12, 5, 12, 5, 12, 5, 12, 5, 8, 4,-114, 19, 0, 52,-122, 21, 2, 5, 0, 8},
|
||||
{ -52, -96,-118,-128,-111, -74, -37, -5, 31, 62, 89, 112, 127, 125, 115, 93, 57, 23, 0, -16, -8, 15, 37, 54, 65, 70, 62, 54, 43, 31, 19, 0},
|
||||
{ -81,-128, -61, 13, 65, 93, 127, 47, 41, 44, 52, 55, 56, 58, 58, 34, 0, 68, 76, 72, 61, 108, 55, 29, 32, 39, 43, 49, 50, 51, 51, 0},
|
||||
{ -21, -45, -67, -88,-105,-114,-122,-128,-123,-116,-103, -87, -70, -53, -28, -9, 22, 46, 67, 86, 102, 114, 123, 125, 127, 117, 104, 91, 72, 51, 28, 0},
|
||||
{ -78,-118,-128,-102, -54, -3, 40, 65, 84, 88, 84, 80, 82, 88, 94, 103, 110, 119, 122, 125, 122, 122, 121, 123, 125, 126, 127, 127, 125, 118, 82, 0}
|
||||
};
|
||||
|
||||
|
||||
#define NOISE_SIZE 255
|
||||
|
||||
|
||||
/*
|
||||
const unsigned char noise_tbl[]=
|
||||
{
|
||||
0x1c,0x86,0x8a,0x8f,0x98,0xa1,0xad,0xbe,0xd9,0x8a,0x66,0x4d,0x40,0x33,0x2b,0x23,
|
||||
0x1e,0x8a,0x90,0x97,0xa4,0xae,0xb8,0xd6,0xec,0xe9,0x69,0x4a,0x3e,0x34,0x2d,0x27,
|
||||
0x24,0x24,0x89,0x8e,0x93,0x9c,0xa5,0xb0,0xc1,0xdd,0x40,0x36,0x30,0x29,0x27,0x24,
|
||||
0x8b,0x90,0x96,0x9e,0xa7,0xb3,0xc4,0xe1,0x25,0x21,0x8a,0x8f,0x93,0x9d,0xa5,0xb2,
|
||||
0xc2,0xdd,0xdd,0x98,0xa2,0xaf,0xbf,0xd8,0xfd,0x65,0x4a,0x3c,0x31,0x2b,0x24,0x22,
|
||||
0x1e,0x87,0x8c,0x91,0x9a,0xa3,0xaf,0xc0,0xdb,0xbe,0xd9,0x8c,0x66,0x4d,0x40,0x34,
|
||||
0x2c,0x24,0x1f,0x88,0x90,0x9a,0xa4,0xb2,0xc2,0xda,0xff,0x67,0x4d,0x3d,0x34,0x2d,
|
||||
0x26,0x24,0x20,0x89,0x8e,0x93,0x9c,0xa5,0xb1,0xc2,0xde,0xc1,0xda,0xff,0x67,0x4d,
|
||||
0x3d,0x33,0x2d,0x26,0x24,0x20,0x89,0x8e,0x93,0x9c,0xa5,0xb1,0xc2,0xdd,0xa3,0xb0,
|
||||
0xc0,0xd9,0xfe,0x66,0x4b,0x3c,0x32,0x2b,0x24,0x23,0x1e,0x88,0x8d,0x92,0x9b,0xa4,
|
||||
0xb0,0xc1,0xdc,0xad,0xbe,0xda,0x22,0x20,0x1c,0x85,0x8a,0x8f,0x98,0xa1,0xad,0xbe,
|
||||
0xda,0x20,0x1b,0x85,0x8d,0x97,0xa1,0xaf,0xbf,0xd8,0xfd,0x64,0x49,0x3a,0x30,0x2a,
|
||||
0x23,0x21,0x1d,0x86,0x8b,0x91,0x9a,0xa2,0xae,0xc0,0xdb,0x33,0x2b,0x24,0x1f,0x88,
|
||||
0x90,0x9a,0xa4,0xb2,0xc2,0xda,0xff,0x67,0x4c,0x3e,0x33,0x2d,0x25,0x24,0x1f,0x89,
|
||||
0x8e,0x93,0x9c,0xa5,0xb1,0xc2,0xde,0x85,0x8e,0x98,0xa2,0xb0,0xc0,0xd9,0xfe,0x64,
|
||||
0x4b,0x3b,0x31,0x2a,0x23,0x22,0x1e,0x88,0x8c,0x91,0x9b,0xa3,0xaf,0xc1,0xdc,0xdc
|
||||
};
|
||||
*/
|
||||
|
||||
const unsigned char noise_tbl[8][256] = {
|
||||
{
|
||||
0x84, 0x87, 0x8c, 0x93, 0x9a, 0xa4, 0xb2, 0xc8, 0x8a, 0x6c, 0x58, 0x4e, 0x43, 0x3d, 0x37, 0x33,
|
||||
0x86, 0x8d, 0x95, 0x9d, 0xa8, 0xb5, 0xc9, 0xe7, 0x6d, 0x58, 0x4b, 0x43, 0x3e, 0x38, 0x37, 0x34,
|
||||
0x87, 0x8b, 0x8f, 0x96, 0x9d, 0xa7, 0xb5, 0xcb, 0x4d, 0x45, 0x40, 0x3a, 0x39, 0x35, 0x89, 0x8d,
|
||||
0x91, 0x98, 0x9f, 0xa9, 0xb7, 0xcd, 0x37, 0x34, 0x87, 0x8c, 0x90, 0x97, 0x9e, 0xa8, 0xb5, 0xcb,
|
||||
0xca, 0x93, 0x9b, 0xa6, 0xb3, 0xc7, 0xe5, 0x6b, 0x56, 0x49, 0x41, 0x3c, 0x36, 0x35, 0x32, 0x86,
|
||||
0x89, 0x8e, 0x95, 0x9c, 0xa6, 0xb4, 0xc9, 0xb2, 0xc8, 0x8a, 0x6c, 0x58, 0x4d, 0x43, 0x3d, 0x37,
|
||||
0x33, 0x86, 0x8d, 0x95, 0x9d, 0xa8, 0xb5, 0xc9, 0xe7, 0x6d, 0x58, 0x4b, 0x43, 0x3e, 0x38, 0x37,
|
||||
0x34, 0x87, 0x8b, 0x8f, 0x97, 0x9e, 0xa8, 0xb5, 0xcb, 0xb5, 0xc9, 0xe6, 0x6d, 0x58, 0x4b, 0x43,
|
||||
0x3d, 0x38, 0x37, 0x33, 0x87, 0x8b, 0x8f, 0x96, 0x9e, 0xa7, 0xb5, 0xcb, 0x9c, 0xa7, 0xb3, 0xc8,
|
||||
0xe6, 0x6c, 0x56, 0x4a, 0x42, 0x3d, 0x37, 0x36, 0x33, 0x86, 0x8a, 0x8e, 0x95, 0x9c, 0xa6, 0xb4,
|
||||
0xca, 0xa4, 0xb2, 0xc8, 0x35, 0x33, 0x30, 0x84, 0x88, 0x8c, 0x93, 0x9a, 0xa5, 0xb2, 0xc8, 0x34,
|
||||
0x30, 0x84, 0x8a, 0x93, 0x9a, 0xa5, 0xb2, 0xc6, 0xe4, 0x6a, 0x55, 0x49, 0x41, 0x3b, 0x36, 0x34,
|
||||
0x31, 0x85, 0x89, 0x8d, 0x94, 0x9b, 0xa5, 0xb3, 0xc9, 0x43, 0x3d, 0x37, 0x33, 0x86, 0x8d, 0x95,
|
||||
0x9d, 0xa8, 0xb4, 0xc9, 0xe7, 0x6d, 0x57, 0x4b, 0x43, 0x3e, 0x38, 0x37, 0x33, 0x87, 0x8b, 0x8f,
|
||||
0x96, 0x9d, 0xa7, 0xb5, 0xcb, 0x85, 0x8b, 0x93, 0x9b, 0xa6, 0xb3, 0xc7, 0xe5, 0x6b, 0x56, 0x4a,
|
||||
0x41, 0x3c, 0x36, 0x35, 0x32, 0x86, 0x89, 0x8e, 0x95, 0x9c, 0xa6, 0xb4, 0xca, 0x30
|
||||
},
|
||||
|
||||
{
|
||||
0x95, 0x95, 0x98, 0x9c, 0x9e, 0xa3, 0xaa, 0xb4, 0xb3, 0x98, 0x9c, 0xa1, 0xa7, 0xb1, 0xc0, 0x57,
|
||||
0x4f, 0x49, 0x48, 0x44, 0x42, 0x40, 0x40, 0x94, 0x95, 0x96, 0x9a, 0x9d, 0xa2, 0xa9, 0xb3, 0xa8,
|
||||
0xb1, 0x66, 0x59, 0x50, 0x4c, 0x4a, 0x46, 0x44, 0x41, 0x94, 0x97, 0x9b, 0x9e, 0xa4, 0xaa, 0xb3,
|
||||
0xc2, 0x59, 0x52, 0x4c, 0x4a, 0x46, 0x45, 0x43, 0x43, 0x96, 0x97, 0x99, 0x9d, 0x9f, 0xa5, 0xab,
|
||||
0xb5, 0xaa, 0xb4, 0xc2, 0x5a, 0x52, 0x4c, 0x4b, 0x47, 0x45, 0x43, 0x43, 0x96, 0x97, 0x99, 0x9d,
|
||||
0xa0, 0xa5, 0xab, 0xb5, 0x9d, 0xa3, 0xa9, 0xb3, 0xc2, 0x59, 0x51, 0x4b, 0x4a, 0x46, 0x44, 0x42,
|
||||
0x42, 0x96, 0x97, 0x98, 0x9c, 0x9f, 0xa4, 0xab, 0xb4, 0xa2, 0xa9, 0xb3, 0x41, 0x40, 0x40, 0x93,
|
||||
0x94, 0x96, 0x9a, 0x9d, 0xa2, 0xa8, 0xb2, 0x41, 0x3e, 0x92, 0x94, 0x99, 0x9b, 0xa2, 0xa7, 0xb1,
|
||||
0xc0, 0x57, 0x4f, 0x4a, 0x48, 0x44, 0x42, 0x40, 0x40, 0x94, 0x95, 0x97, 0x9a, 0x9d, 0xa2, 0xa9,
|
||||
0xb3, 0x48, 0x45, 0x43, 0x41, 0x94, 0x97, 0x9b, 0x9d, 0xa3, 0xa9, 0xb3, 0xc1, 0x59, 0x51, 0x4b,
|
||||
0x4a, 0x45, 0x44, 0x42, 0x42, 0x95, 0x97, 0x98, 0x9c, 0x9f, 0xa4, 0xab, 0xb4, 0x92, 0x95, 0x99,
|
||||
0x9c, 0xa2, 0xa8, 0xb1, 0xc0, 0x57, 0x4f, 0x4a, 0x48, 0x44, 0x42, 0x41, 0x41, 0x94, 0x95, 0x97,
|
||||
0x9b, 0x9e, 0xa3, 0xa9, 0xb3, 0x3e, 0x92, 0x93, 0x95, 0x98, 0x9b, 0xa0, 0xa7, 0xb1, 0x65, 0x58,
|
||||
0x50, 0x4b, 0x49, 0x45, 0x43, 0x41, 0x94, 0x97, 0x9b, 0x9e, 0xa4, 0xa9, 0xb3, 0xc2, 0x59, 0x51,
|
||||
0x4b, 0x4a, 0x46, 0x45, 0x42, 0x42, 0x96, 0x96, 0x99, 0x9c, 0x9f, 0xa4, 0xab, 0xb5, 0x4d, 0x4b,
|
||||
0x47, 0x45, 0x43, 0x43, 0x97, 0x97, 0x9a, 0x9d, 0xa0, 0xa5, 0xac, 0xb6, 0x41, 0x42
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
0x93, 0x93, 0x93, 0x94, 0x95, 0x99, 0x9c, 0xa1, 0xa8, 0x4e, 0x48, 0x45, 0x43, 0x44, 0x42, 0x42,
|
||||
0x95, 0x95, 0x95, 0x96, 0x97, 0x98, 0x9b, 0x9e, 0xa6, 0x43, 0x41, 0x41, 0x95, 0x94, 0x94, 0x95,
|
||||
0x97, 0x9b, 0x9d, 0xa3, 0xa9, 0x4f, 0x4a, 0x46, 0x45, 0x45, 0x43, 0x43, 0x97, 0x96, 0x96, 0x97,
|
||||
0x99, 0x9a, 0x9c, 0x9f, 0xa7, 0x94, 0x93, 0x94, 0x96, 0x9a, 0x9d, 0xa2, 0xa9, 0x4f, 0x49, 0x45,
|
||||
0x44, 0x45, 0x43, 0x43, 0x96, 0x96, 0x96, 0x96, 0x98, 0x99, 0x9c, 0x9f, 0xa7, 0x93, 0x93, 0x93,
|
||||
0x93, 0x95, 0x96, 0x99, 0x9c, 0xa4, 0x50, 0x4b, 0x49, 0x45, 0x44, 0x42, 0x42, 0x95, 0x95, 0x95,
|
||||
0x96, 0x97, 0x9b, 0x9e, 0xa3, 0xaa, 0x50, 0x4b, 0x46, 0x46, 0x46, 0x44, 0x44, 0x97, 0x97, 0x97,
|
||||
0x97, 0x99, 0x9a, 0x9d, 0x9f, 0xa8, 0x47, 0x46, 0x46, 0x44, 0x44, 0x98, 0x98, 0x97, 0x98, 0x9a,
|
||||
0x9b, 0x9d, 0xa0, 0xa8, 0x42, 0x96, 0x95, 0x95, 0x96, 0x98, 0x98, 0x9b, 0x9e, 0xa6, 0xa6, 0x93,
|
||||
0x95, 0x99, 0x9c, 0xa1, 0xa7, 0x4e, 0x48, 0x44, 0x43, 0x44, 0x41, 0x42, 0x95, 0x95, 0x95, 0x95,
|
||||
0x97, 0x98, 0x9b, 0x9e, 0xa6, 0x9d, 0xa5, 0x52, 0x4c, 0x4b, 0x46, 0x45, 0x42, 0x43, 0x97, 0x96,
|
||||
0x96, 0x97, 0x98, 0x9c, 0x9f, 0xa4, 0xab, 0x51, 0x4b, 0x48, 0x46, 0x47, 0x44, 0x45, 0x98, 0x98,
|
||||
0x98, 0x98, 0x9a, 0x9b, 0x9e, 0xa0, 0xa8, 0x9f, 0xa4, 0xab, 0x51, 0x4c, 0x48, 0x47, 0x47, 0x45,
|
||||
0x45, 0x99, 0x98, 0x98, 0x99, 0x9a, 0x9b, 0x9e, 0xa1, 0xa9, 0x98, 0x9d, 0x9f, 0xa4, 0xab, 0x51,
|
||||
0x4b, 0x47, 0x47, 0x47, 0x45, 0x45, 0x98, 0x98, 0x98, 0x98, 0x9a, 0x9b, 0x9e, 0xa1, 0xa8, 0x9c,
|
||||
0x9f, 0xa7, 0x42, 0x42, 0x95, 0x95, 0x95, 0x96, 0x97, 0x98, 0x9b, 0x9e, 0xa6, 0x3f
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
0x94, 0x93, 0x92, 0x92, 0x93, 0x95, 0x96, 0x99, 0x9c, 0x43, 0x43, 0x43, 0x41, 0x41, 0x42, 0x95,
|
||||
0x95, 0x95, 0x94, 0x94, 0x94, 0x95, 0x97, 0x97, 0x9a, 0x40, 0x40, 0x41, 0x95, 0x95, 0x94, 0x95,
|
||||
0x95, 0x97, 0x97, 0x9a, 0x9e, 0x45, 0x44, 0x44, 0x42, 0x42, 0x43, 0x97, 0x96, 0x96, 0x96, 0x96,
|
||||
0x95, 0x96, 0x98, 0x99, 0x9c, 0x94, 0x94, 0x94, 0x94, 0x96, 0x97, 0x9a, 0x9d, 0x44, 0x43, 0x44,
|
||||
0x42, 0x42, 0x43, 0x96, 0x96, 0x96, 0x95, 0x95, 0x95, 0x96, 0x98, 0x98, 0x9b, 0x93, 0x93, 0x93,
|
||||
0x92, 0x92, 0x93, 0x95, 0x95, 0x98, 0x49, 0x44, 0x43, 0x44, 0x42, 0x42, 0x43, 0x96, 0x96, 0x96,
|
||||
0x95, 0x96, 0x98, 0x99, 0x9b, 0x9f, 0x46, 0x45, 0x45, 0x43, 0x44, 0x44, 0x98, 0x98, 0x97, 0x97,
|
||||
0x97, 0x97, 0x97, 0x99, 0x9a, 0x9d, 0x45, 0x43, 0x43, 0x44, 0x98, 0x98, 0x98, 0x97, 0x97, 0x97,
|
||||
0x97, 0x99, 0x9a, 0x9d, 0x95, 0x95, 0x94, 0x94, 0x94, 0x94, 0x94, 0x96, 0x97, 0x9a, 0x9a, 0x92,
|
||||
0x93, 0x94, 0x95, 0x98, 0x9c, 0x43, 0x42, 0x43, 0x40, 0x40, 0x41, 0x95, 0x95, 0x94, 0x94, 0x94,
|
||||
0x93, 0x94, 0x96, 0x97, 0x9a, 0x96, 0x99, 0x4a, 0x45, 0x44, 0x44, 0x42, 0x42, 0x43, 0x97, 0x96,
|
||||
0x96, 0x96, 0x97, 0x98, 0x99, 0x9c, 0xa0, 0x47, 0x46, 0x46, 0x44, 0x44, 0x45, 0x98, 0x98, 0x98,
|
||||
0x98, 0x97, 0x97, 0x98, 0x99, 0x9a, 0x9d, 0x9a, 0x9c, 0xa0, 0x47, 0x46, 0x46, 0x44, 0x44, 0x45,
|
||||
0x99, 0x98, 0x98, 0x98, 0x97, 0x97, 0x98, 0x9a, 0x9a, 0x9e, 0x97, 0x98, 0x99, 0x9c, 0xa0, 0x47,
|
||||
0x46, 0x46, 0x44, 0x44, 0x45, 0x98, 0x98, 0x98, 0x98, 0x98, 0x97, 0x98, 0x9a, 0x9a, 0x9d, 0x98,
|
||||
0x99, 0x9c, 0x42, 0x95, 0x95, 0x95, 0x94, 0x94, 0x94, 0x95, 0x96, 0x97, 0x9a, 0x40
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
0xe6, 0x1c, 0x87, 0xcb, 0x42, 0x9b, 0x87, 0x35, 0xd8, 0x1c, 0xa3, 0x58, 0x84, 0xa4, 0x28, 0x71,
|
||||
0xd9, 0x3e, 0x82, 0xbb, 0x28, 0xd2, 0x80, 0x63, 0xe0, 0x38, 0x71, 0xcb, 0x22, 0x81, 0x9b, 0x83,
|
||||
0xa4, 0x28, 0x71, 0xd8, 0x3e, 0x81, 0xbb, 0x28, 0xd2, 0x80, 0x63, 0xe0, 0x37, 0x71, 0xcb, 0x22,
|
||||
0x81, 0x9b, 0xa3, 0x57, 0x83, 0xa3, 0x28, 0x70, 0xd8, 0x3e, 0x81, 0xba, 0x28, 0xd1, 0x80, 0x63,
|
||||
0xe0, 0x37, 0x70, 0xcb, 0x22, 0x81, 0x9b, 0x23, 0x82, 0x9b, 0x28, 0xd2, 0x80, 0x63, 0xe1, 0x38,
|
||||
0x71, 0xcc, 0x23, 0x81, 0x9b, 0x9c, 0x88, 0x36, 0xd9, 0x1d, 0xa4, 0x58, 0x84, 0xa5, 0x28, 0x72,
|
||||
0xd9, 0x3f, 0x82, 0xbb, 0x29, 0xd2, 0x81, 0x64, 0xe1, 0x38, 0x72, 0xcc, 0x23, 0x82, 0x9b, 0xcc,
|
||||
0x43, 0x9c, 0x88, 0x36, 0xd8, 0x1d, 0xa4, 0x58, 0x84, 0xa5, 0x29, 0x71, 0xd9, 0x3f, 0x82, 0xbc,
|
||||
0x29, 0xd2, 0x81, 0x64, 0xe1, 0x39, 0x72, 0xcc, 0x23, 0x82, 0x9c, 0x37, 0xd9, 0x1d, 0xa4, 0x59,
|
||||
0x85, 0xa6, 0x29, 0x72, 0xda, 0x40, 0x83, 0xbc, 0x2a, 0xd3, 0x82, 0x64, 0xe2, 0x39, 0x72, 0xcc,
|
||||
0x24, 0x82, 0x9c, 0x82, 0x64, 0xe1, 0x3a, 0x72, 0xcc, 0x24, 0x82, 0x9c, 0x40, 0xe8, 0x1e, 0x89,
|
||||
0xcd, 0x44, 0x9d, 0x89, 0x37, 0xd9, 0x1e, 0xa5, 0x59, 0x85, 0xa6, 0x2a, 0x73, 0xda, 0x40, 0x84,
|
||||
0xbd, 0x2a, 0xd3, 0x82, 0x65, 0xe2, 0x39, 0x73, 0xcd, 0x24, 0x83, 0x9c, 0x40, 0x83, 0xbd, 0x2a,
|
||||
0xd3, 0x82, 0x65, 0xe2, 0x39, 0x73, 0xcd, 0x24, 0x83, 0x9c, 0xd2, 0x81, 0x63, 0xe0, 0x39, 0x71,
|
||||
0xcc, 0x23, 0x82, 0x9b, 0x9b, 0x1d, 0xa4, 0x59, 0x84, 0xa5, 0x29, 0x72, 0xd9, 0x3f, 0x83, 0xbc,
|
||||
0x29, 0xd3, 0x81, 0x64, 0xe1, 0x39, 0x72, 0xcc, 0x24, 0x82, 0x9c, 0x82, 0x9c, 0x3f
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
0xe7, 0x89, 0x1b, 0x8b, 0xe6, 0x89, 0x1b, 0x8b, 0xe6, 0x89, 0x1b, 0x8a, 0xe6, 0x88, 0x1b, 0x8a,
|
||||
0xe6, 0x88, 0x1a, 0x8a, 0x1c, 0x8b, 0xe7, 0x89, 0x1b, 0x8b, 0xe6, 0x89, 0x1b, 0x8a, 0xe6, 0x89,
|
||||
0x1b, 0x8a, 0xe6, 0x89, 0x1b, 0x8a, 0xe6, 0x89, 0x1a, 0x8a, 0xe6, 0x88, 0x1a, 0x89, 0xe5, 0x88,
|
||||
0x1a, 0x89, 0xe5, 0x88, 0x1a, 0xe5, 0x88, 0x1a, 0x89, 0xe5, 0x88, 0x1a, 0x89, 0xe4, 0x87, 0x19,
|
||||
0x89, 0xe4, 0x87, 0x19, 0x88, 0xe4, 0x87, 0x19, 0x1a, 0x8a, 0xe5, 0x88, 0x1a, 0x89, 0xe5, 0x88,
|
||||
0x1a, 0x89, 0xe5, 0x88, 0x1a, 0x89, 0xe4, 0x87, 0x19, 0x89, 0xe4, 0x87, 0x19, 0xe4, 0x87, 0x19,
|
||||
0xe4, 0x87, 0x1a, 0x88, 0xe4, 0x87, 0x19, 0x88, 0xe4, 0x87, 0x19, 0x88, 0x1a, 0x89, 0xe5, 0x88,
|
||||
0x1a, 0x89, 0xe5, 0x88, 0x1a, 0x89, 0xe5, 0x87, 0x1a, 0x89, 0xe4, 0x88, 0x19, 0x89, 0xe4, 0x87,
|
||||
0x19, 0x88, 0xe4, 0x87, 0x19, 0x88, 0xe4, 0x87, 0x19, 0x88, 0xe4, 0x87, 0x19, 0x88, 0xe4, 0x87,
|
||||
0x19, 0x88, 0xe3, 0x86, 0x19, 0x88, 0xe3, 0x86, 0x18, 0x87, 0xe3, 0x86, 0x18, 0x88, 0xe3, 0x86,
|
||||
0x18, 0x87, 0xe3, 0x86, 0x18, 0x87, 0xe3, 0x86, 0x18, 0x87, 0xe3, 0x86, 0x18, 0x87, 0xe3, 0x86,
|
||||
0x18, 0x87, 0xe2, 0x85, 0x18, 0x87, 0xe3, 0x85, 0x17, 0x19, 0x88, 0xe3, 0x86, 0x19, 0x88, 0xe4,
|
||||
0x86, 0x19, 0x88, 0xe3, 0x86, 0x18, 0x88, 0xe3, 0x86, 0x18, 0x87, 0xe3, 0x86, 0x18, 0x87, 0xe3,
|
||||
0x86, 0x18, 0x87, 0xe3, 0x85, 0x18, 0x87, 0xe3, 0x86, 0x18, 0x87, 0xe3, 0x86, 0x18, 0x87, 0xe3,
|
||||
0x85, 0x18, 0x87, 0x19, 0x88, 0xe3, 0x86, 0x19, 0x88, 0xe3, 0x86, 0x18, 0x87, 0xe3, 0x86, 0x18,
|
||||
0x87, 0x1a, 0x89, 0xe4, 0x87, 0x19, 0x88, 0xe4, 0x87, 0x19, 0x1b, 0x89, 0x1c, 0x8b
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
0xa1, 0xe6, 0xcf, 0x79, 0x58, 0x4a, 0x78, 0x7a, 0x53, 0x7b, 0xc2, 0xc3, 0x82, 0x36, 0x48, 0x84,
|
||||
0xcb, 0xa6, 0x67, 0x2e, 0x49, 0x7c, 0xaa, 0x93, 0x87, 0x73, 0x62, 0x6c, 0xa4, 0xaa, 0x71, 0xa7,
|
||||
0x67, 0x2f, 0x4a, 0x7c, 0xab, 0x94, 0x87, 0x74, 0x62, 0x6d, 0xa4, 0xab, 0x72, 0x7b, 0xaa, 0x93,
|
||||
0x87, 0x72, 0x61, 0x6c, 0xa4, 0xaa, 0x71, 0x72, 0x7b, 0xc2, 0xc3, 0x81, 0x36, 0x48, 0x84, 0xcb,
|
||||
0xa6, 0x67, 0x2e, 0x49, 0x7b, 0xaa, 0x93, 0x87, 0x72, 0x61, 0x6c, 0xa4, 0xaa, 0x71, 0xaa, 0x71,
|
||||
0x59, 0xa0, 0xe5, 0xce, 0x78, 0x57, 0x49, 0x77, 0x79, 0x52, 0x7a, 0xc1, 0xc2, 0x81, 0x35, 0x47,
|
||||
0x83, 0xca, 0xa6, 0x66, 0x2d, 0x48, 0x7b, 0xa9, 0x92, 0x86, 0x72, 0x61, 0x6b, 0xa3, 0xa9, 0x70,
|
||||
0x82, 0x36, 0x49, 0x84, 0xcc, 0xa7, 0x67, 0x2e, 0x4a, 0x7c, 0xab, 0x94, 0x87, 0x73, 0x62, 0x6c,
|
||||
0xa4, 0xab, 0x72, 0xc2, 0xc3, 0x82, 0x36, 0x48, 0x84, 0xcb, 0xa6, 0x67, 0x2e, 0x4a, 0x7c, 0xaa,
|
||||
0x93, 0x87, 0x73, 0x62, 0x6c, 0xa4, 0xaa, 0x71, 0xa3, 0xaa, 0x70, 0x49, 0x7b, 0xa9, 0x92, 0x86,
|
||||
0x72, 0x61, 0x6b, 0xa3, 0xaa, 0x70, 0x4b, 0x78, 0x7a, 0x53, 0x7c, 0xc2, 0xc3, 0x82, 0x36, 0x49,
|
||||
0x84, 0xcc, 0xa6, 0x67, 0x2f, 0x4a, 0x7c, 0xaa, 0x93, 0x87, 0x73, 0x62, 0x6c, 0xa4, 0xab, 0x72,
|
||||
0x7b, 0x5a, 0x4c, 0x7a, 0x7c, 0x55, 0x7e, 0xc4, 0xc5, 0x83, 0x38, 0x4a, 0x85, 0xcd, 0xa8, 0x69,
|
||||
0x30, 0x4b, 0x7d, 0xac, 0x95, 0x88, 0x74, 0x63, 0x6e, 0xa5, 0xac, 0x73, 0x7c, 0x55, 0x7d, 0xc4,
|
||||
0xc5, 0x83, 0x37, 0x4a, 0x85, 0xcd, 0xa8, 0x68, 0x30, 0x4b, 0x7d, 0xac, 0x95, 0x88, 0x74, 0x63,
|
||||
0x6e, 0xa6, 0xac, 0x73, 0xab, 0x94, 0x87, 0x73, 0x62, 0x6d, 0xa5, 0xab, 0x72, 0x5a
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
0xbf, 0xdc, 0xd4, 0xaa, 0x82, 0x81, 0x35, 0x34, 0x4d, 0x75, 0x95, 0xcb, 0xde, 0xc9, 0x91, 0x72,
|
||||
0x4a, 0x30, 0x6b, 0x57, 0x69, 0x7d, 0x65, 0x3f, 0x33, 0x4f, 0x69, 0x81, 0xc1, 0xde, 0xd6, 0xac,
|
||||
0x83, 0x82, 0x37, 0x36, 0x4f, 0x77, 0x96, 0xcd, 0xe0, 0xca, 0x93, 0x73, 0x4c, 0x31, 0x6d, 0x58,
|
||||
0x6a, 0x4f, 0x68, 0x80, 0xc0, 0xdd, 0xd5, 0xab, 0x82, 0x82, 0x36, 0x35, 0x4e, 0x76, 0x95, 0xcc,
|
||||
0xdf, 0xca, 0x92, 0x73, 0x4b, 0x31, 0x6c, 0x57, 0x6a, 0xe0, 0xca, 0x92, 0x73, 0x4c, 0x31, 0x6c,
|
||||
0x58, 0x6a, 0xad, 0xd5, 0xdb, 0xbc, 0x7b, 0x63, 0x3c, 0x31, 0x4e, 0x66, 0x7e, 0xbe, 0xdb, 0xd3,
|
||||
0xaa, 0x81, 0x80, 0x34, 0x33, 0x4c, 0x75, 0x94, 0xcb, 0xde, 0xc8, 0x91, 0x71, 0x4a, 0x2f, 0x6b,
|
||||
0x56, 0x68, 0x35, 0x4e, 0x76, 0x95, 0xcc, 0xdf, 0xca, 0x92, 0x72, 0x4b, 0x30, 0x6c, 0x57, 0x6a,
|
||||
0xcc, 0xdf, 0xca, 0x91, 0x72, 0x4b, 0x30, 0x6c, 0x57, 0x69, 0x69, 0x7e, 0xbe, 0xdb, 0xd3, 0xa9,
|
||||
0x81, 0x80, 0x34, 0x33, 0x4c, 0x74, 0x94, 0xcb, 0xdd, 0xc8, 0x90, 0x71, 0x4a, 0x2f, 0x6b, 0x55,
|
||||
0x68, 0x56, 0x69, 0xac, 0xd4, 0xd9, 0xbb, 0x7a, 0x62, 0x3c, 0x30, 0x4c, 0x65, 0x7d, 0xbd, 0xda,
|
||||
0xd2, 0xa8, 0x80, 0x7f, 0x34, 0x33, 0x4c, 0x74, 0x94, 0xca, 0xdd, 0xc8, 0x90, 0x71, 0x49, 0x2e,
|
||||
0x6a, 0x55, 0x67, 0xd3, 0xa9, 0x81, 0x80, 0x34, 0x33, 0x4c, 0x74, 0x94, 0xca, 0xde, 0xc8, 0x91,
|
||||
0x71, 0x4a, 0x2f, 0x6a, 0x56, 0x68, 0xbd, 0xda, 0xd2, 0xa8, 0x7f, 0x7e, 0x33, 0x32, 0x4b, 0x73,
|
||||
0x92, 0xc9, 0xdc, 0xc7, 0x8f, 0x70, 0x49, 0x2e, 0x69, 0x54, 0x67, 0x6a, 0x56, 0x68, 0x93, 0xca,
|
||||
0xdd, 0xc8, 0x90, 0x70, 0x49, 0x2e, 0x6a, 0x55, 0x68, 0x3d, 0x31, 0x4e, 0x67, 0x7f
|
||||
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
void upd1771c_reset(struct upd1771c_t *scv) {
|
||||
memset(scv->upd1771c_packets,0,16);
|
||||
scv->upd1771c_mode = 0;
|
||||
scv->upd1771c_pos = 0;
|
||||
scv->upd1771c_posc = 0;
|
||||
scv->upd1771c_wave = 0;
|
||||
scv->upd1771c_vol = 0;
|
||||
scv->upd1771c_period = 0;
|
||||
scv->upd1771c_off = 0;
|
||||
scv->upd1771c_npos = 0;
|
||||
//scv->upd1771c_repsamp = 0;
|
||||
}
|
||||
|
||||
void upd1771c_write_packet(struct upd1771c_t *scv, uint8_t ind, uint8_t val) {
|
||||
scv->upd1771c_packets[ind&15] = val;
|
||||
switch (scv->upd1771c_packets[0]) {
|
||||
case 1:
|
||||
if (ind == 3) {
|
||||
scv->upd1771c_mode = 1;
|
||||
scv->upd1771c_wave = (scv->upd1771c_packets[1]&0xe0)>>5;
|
||||
scv->upd1771c_off = 0; //?
|
||||
scv->upd1771c_period = scv->upd1771c_packets[2];
|
||||
scv->upd1771c_vol = scv->upd1771c_packets[3]&0x1f;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (ind == 3) {
|
||||
scv->upd1771c_mode = 2;
|
||||
scv->upd1771c_wave = (scv->upd1771c_packets[1]&0xe0)>>5;
|
||||
scv->upd1771c_off = scv->upd1771c_packets[1]&0x1f;
|
||||
scv->upd1771c_period = scv->upd1771c_packets[2]<0x20?0x20:scv->upd1771c_packets[2];
|
||||
scv->upd1771c_vol = scv->upd1771c_packets[3]&0x1f;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
case 0:
|
||||
scv->upd1771c_mode = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void upd1771c_sound_set_clock(struct upd1771c_t *scv, unsigned int clock, unsigned int divi) {
|
||||
scv->upd1771c_repsamp = divi;
|
||||
}
|
||||
|
||||
int16_t upd1771c_sound_stream_update(struct upd1771c_t *scv) {
|
||||
int16_t s = 0;
|
||||
for (int i = 0; i < scv->upd1771c_repsamp; i++) {
|
||||
s = 0;
|
||||
switch (scv->upd1771c_mode) {
|
||||
case 2:
|
||||
s = ((int16_t)WAVEFORMS[scv->upd1771c_wave][scv->upd1771c_posc])*scv->upd1771c_vol;
|
||||
scv->upd1771c_pos++;
|
||||
if (scv->upd1771c_pos >= scv->upd1771c_period) {
|
||||
scv->upd1771c_pos=0;
|
||||
scv->upd1771c_posc++;
|
||||
if (scv->upd1771c_posc == 32)
|
||||
scv->upd1771c_posc = scv->upd1771c_off;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
scv->upd1771c_pos++;
|
||||
if (scv->upd1771c_pos >= ((((uint32_t)scv->upd1771c_period) + 1)*128)) {
|
||||
scv->upd1771c_pos=0;
|
||||
scv->upd1771c_posc++;
|
||||
if (scv->upd1771c_posc == NOISE_SIZE)
|
||||
scv->upd1771c_posc = 0;
|
||||
}
|
||||
uint16_t p = scv->upd1771c_posc;
|
||||
p = p>=254?253:p;
|
||||
s = ((int16_t)(noise_tbl[scv->upd1771c_wave][p])-127)*scv->upd1771c_vol;
|
||||
// inaccurate noise mixing :/
|
||||
// s |= (scv->upd1771c_npos&128)?127*scv->upd1771c_vol:0;
|
||||
break;
|
||||
case 0:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
33
src/engine/platform/sound/upd1771c.h
Normal file
33
src/engine/platform/sound/upd1771c.h
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
#ifndef __UPD1771C_SOUND_H__
|
||||
#define __UPD1771C_SOUND_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct upd1771c_t {
|
||||
uint8_t upd1771c_packets[16];
|
||||
uint8_t upd1771c_mode;
|
||||
uint32_t upd1771c_pos;
|
||||
uint8_t upd1771c_off;
|
||||
uint8_t upd1771c_posc;
|
||||
uint8_t upd1771c_wave;
|
||||
uint8_t upd1771c_vol;
|
||||
uint8_t upd1771c_period;
|
||||
uint8_t upd1771c_npos;
|
||||
int upd1771c_repsamp;
|
||||
};
|
||||
|
||||
void upd1771c_reset(struct upd1771c_t *scv);
|
||||
void upd1771c_write_packet(struct upd1771c_t *scv, uint8_t ind, uint8_t val);
|
||||
int16_t upd1771c_sound_stream_update(struct upd1771c_t *scv);
|
||||
void upd1771c_sound_set_clock(struct upd1771c_t *scv, unsigned int clock, unsigned int divi);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
339
src/engine/platform/sound/ymf278b/License.txt
Normal file
339
src/engine/platform/sound/ymf278b/License.txt
Normal file
|
|
@ -0,0 +1,339 @@
|
|||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
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.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
1094
src/engine/platform/sound/ymf278b/ymf278.cpp
Normal file
1094
src/engine/platform/sound/ymf278b/ymf278.cpp
Normal file
File diff suppressed because it is too large
Load diff
155
src/engine/platform/sound/ymf278b/ymf278.h
Normal file
155
src/engine/platform/sound/ymf278b/ymf278.h
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
#ifndef YMF278_HH
|
||||
#define YMF278_HH
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
|
||||
#define UNREACHABLE while (1) assert(false)
|
||||
|
||||
using byte = uint8_t;
|
||||
|
||||
template<typename T>
|
||||
const T& YMF_clamp(const T& value, const T& min, const T& max) {
|
||||
return std::min(std::max(value, min), max);
|
||||
}
|
||||
|
||||
class MemoryInterface {
|
||||
public:
|
||||
virtual byte operator[](unsigned address) const = 0;
|
||||
virtual unsigned getSize() const = 0;
|
||||
virtual void write(unsigned address, byte value) = 0;
|
||||
virtual void clear(byte value) = 0;
|
||||
virtual void setMemoryType(bool memoryType) {};
|
||||
};
|
||||
|
||||
class YMF278Base
|
||||
{
|
||||
public:
|
||||
YMF278Base(MemoryInterface& memory, int channelCount, int clockDivider, double clockFrequency);
|
||||
~YMF278Base();
|
||||
int getChannelCount();
|
||||
int getClockDivider();
|
||||
double getClockFrequency();
|
||||
void setClockFrequency(double clockFrequency);
|
||||
double getSampleRate();
|
||||
virtual void reset();
|
||||
|
||||
void generate(short& fleft, short& fright, short& rleft, short& rright, short* channelBufs = nullptr);
|
||||
|
||||
class Slot final {
|
||||
public:
|
||||
Slot();
|
||||
void reset();
|
||||
int compute_rate(int val) const;
|
||||
int compute_decay_rate(int val) const;
|
||||
unsigned decay_rate(int num, int sample_rate);
|
||||
void envelope_next(int sample_rate);
|
||||
int16_t compute_vib() const;
|
||||
uint16_t compute_am() const;
|
||||
|
||||
template<typename Archive>
|
||||
void serialize(Archive& ar, unsigned version);
|
||||
|
||||
uint32_t startaddr;
|
||||
uint16_t loopaddr;
|
||||
uint16_t endaddr; // Note: stored in 2s complement (0x0000 = 0, 0x0001 = -65536, 0xffff = -1)
|
||||
uint32_t step; // fixed-point frequency step
|
||||
// invariant: step == calcStep(OCT, FN)
|
||||
uint32_t stepptr; // fixed-point pointer into the sample
|
||||
uint16_t pos;
|
||||
|
||||
int16_t env_vol;
|
||||
|
||||
uint32_t lfo_cnt;
|
||||
|
||||
int16_t DL;
|
||||
uint16_t wave; // wavetable number
|
||||
uint16_t FN; // f-number TODO store 'FN | 1024'?
|
||||
int8_t OCT; // octave [-8..+7]
|
||||
bool PRVB; // pseudo-reverb
|
||||
uint8_t TLdest; // destination total level
|
||||
uint8_t TL; // total level (goes towards TLdest)
|
||||
uint8_t pan; // panpot 0..15
|
||||
bool ch; // channel select
|
||||
bool keyon; // slot keyed on
|
||||
bool DAMP;
|
||||
uint8_t lfo; // LFO speed 0..7
|
||||
uint8_t vib; // vibrato 0..7
|
||||
uint8_t AM; // AM level 0..7
|
||||
uint8_t AR; // 0..15
|
||||
uint8_t D1R; // 0..15
|
||||
uint8_t D2R; // 0..15
|
||||
uint8_t RC; // rate correction 0..15
|
||||
uint8_t RR; // 0..15
|
||||
|
||||
uint8_t bits; // width of the samples
|
||||
|
||||
uint8_t state; // envelope generator state
|
||||
bool lfo_active;
|
||||
};
|
||||
|
||||
protected:
|
||||
void keyOnHelper(Slot& slot);
|
||||
|
||||
MemoryInterface& memory;
|
||||
std::vector<Slot> slots;
|
||||
|
||||
private:
|
||||
int16_t getSample(Slot& slot, uint16_t pos) const;
|
||||
static uint16_t nextPos(Slot& slot, uint16_t pos, uint16_t increment);
|
||||
void advance();
|
||||
bool anyActive();
|
||||
|
||||
/** Global envelope generator counter. */
|
||||
unsigned eg_cnt;
|
||||
|
||||
unsigned channelCount, clockDivider;
|
||||
double clockFrequency;
|
||||
};
|
||||
|
||||
class YMF278 final : public YMF278Base {
|
||||
public:
|
||||
YMF278(MemoryInterface& memory);
|
||||
void reset() override;
|
||||
void writeReg(byte reg, byte data);
|
||||
byte readReg(byte reg);
|
||||
byte peekReg(byte reg) const;
|
||||
|
||||
void generateMix(short fmL, short fmR, short& bufFL, short& bufFR, short& bufRL, short& bufRR, short* channelBufs = nullptr) {
|
||||
generate(bufFL, bufFR, bufRL, bufRR, channelBufs);
|
||||
bufFL = std::min(std::max((pcmMixL * bufFL + fmMixL * fmL) >> 4, -0x8000), 0x7fff);
|
||||
bufFR = std::min(std::max((pcmMixR * bufFR + fmMixR * fmR) >> 4, -0x8000), 0x7fff);;
|
||||
}
|
||||
|
||||
private:
|
||||
int fmMixL, fmMixR, pcmMixL, pcmMixR;
|
||||
int memAdr;
|
||||
byte regs[256];
|
||||
};
|
||||
|
||||
class YMW258 final : public YMF278Base {
|
||||
public:
|
||||
YMW258(MemoryInterface& memory);
|
||||
void writeReg(byte channel, byte reg, byte data);
|
||||
};
|
||||
|
||||
class MemoryMoonSound : MemoryInterface {
|
||||
public:
|
||||
MemoryMoonSound(MemoryInterface& rom, MemoryInterface& ram);
|
||||
byte operator[](unsigned address) const override;
|
||||
unsigned getSize() const override;
|
||||
void write(unsigned address, byte value) override;
|
||||
void clear(byte value) override;
|
||||
void setMemoryType(bool memoryType) override;
|
||||
|
||||
private:
|
||||
unsigned getRamAddress(unsigned addr) const;
|
||||
|
||||
MemoryInterface& rom;
|
||||
MemoryInterface& ram;
|
||||
|
||||
bool memoryType;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -304,9 +304,9 @@ public:
|
|||
|
||||
// simple getters for debugging
|
||||
fm_operator<RegisterType> *debug_operator(uint32_t index) const { return m_op[index]; }
|
||||
int32_t debug_output(uint32_t index) const { return m_output[index]; }
|
||||
int32_t debug_special1() const { return m_special1; }
|
||||
int32_t debug_special2() const { return m_special2; }
|
||||
int32_t debug_output(uint32_t index) const { return m_output[index]; }
|
||||
int32_t debug_special1() const { return m_special1; }
|
||||
int32_t debug_special2() const { return m_special2; }
|
||||
|
||||
private:
|
||||
// helper to add values to the outputs based on channel enables
|
||||
|
|
@ -320,21 +320,21 @@ private:
|
|||
constexpr int out3_index = 3 % RegisterType::OUTPUTS;
|
||||
|
||||
if (RegisterType::OUTPUTS == 1 || m_regs.ch_output_0(choffs)) {
|
||||
m_output[out0_index]=value;
|
||||
m_output[out0_index]=value;
|
||||
output.data[out0_index] += value;
|
||||
}
|
||||
}
|
||||
if (RegisterType::OUTPUTS >= 2 && m_regs.ch_output_1(choffs)) {
|
||||
m_output[out1_index]=value;
|
||||
m_output[out1_index]=value;
|
||||
output.data[out1_index] += value;
|
||||
}
|
||||
}
|
||||
if (RegisterType::OUTPUTS >= 3 && m_regs.ch_output_2(choffs)) {
|
||||
m_output[out2_index]=value;
|
||||
m_output[out2_index]=value;
|
||||
output.data[out2_index] += value;
|
||||
}
|
||||
}
|
||||
if (RegisterType::OUTPUTS >= 4 && m_regs.ch_output_3(choffs)) {
|
||||
m_output[out3_index]=value;
|
||||
m_output[out3_index]=value;
|
||||
output.data[out3_index] += value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// internal state
|
||||
|
|
@ -344,9 +344,9 @@ private:
|
|||
fm_operator<RegisterType> *m_op[4]; // up to 4 operators
|
||||
RegisterType &m_regs; // direct reference to registers
|
||||
fm_engine_base<RegisterType> &m_owner; // reference to the owning engine
|
||||
mutable int32_t m_output[4];
|
||||
mutable int32_t m_special1;
|
||||
mutable int32_t m_special2;
|
||||
mutable int32_t m_output[4];
|
||||
mutable int32_t m_special1;
|
||||
mutable int32_t m_special2;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -529,7 +529,7 @@ public:
|
|||
// generate samples of sound
|
||||
void generate(output_data *output, uint32_t numsamples = 1);
|
||||
|
||||
fm_engine* debug_fm_engine() { return &m_fm; }
|
||||
fm_engine* debug_fm_engine() { return &m_fm; }
|
||||
protected:
|
||||
// internal state
|
||||
uint8_t m_address; // address register
|
||||
|
|
@ -577,8 +577,8 @@ public:
|
|||
// generate samples of sound
|
||||
void generate(output_data *output, uint32_t numsamples = 1);
|
||||
|
||||
fm_engine* debug_fm_engine() { return &m_fm; }
|
||||
adpcm_b_engine* debug_adpcm_b_engine() { return &m_adpcm_b; }
|
||||
fm_engine* debug_fm_engine() { return &m_fm; }
|
||||
adpcm_b_engine* debug_adpcm_b_engine() { return &m_adpcm_b; }
|
||||
|
||||
protected:
|
||||
// internal state
|
||||
|
|
@ -628,7 +628,7 @@ public:
|
|||
// generate samples of sound
|
||||
void generate(output_data *output, uint32_t numsamples = 1);
|
||||
|
||||
fm_engine* debug_fm_engine() { return &m_fm; }
|
||||
fm_engine* debug_fm_engine() { return &m_fm; }
|
||||
|
||||
protected:
|
||||
// internal state
|
||||
|
|
@ -677,7 +677,7 @@ public:
|
|||
// generate samples of sound
|
||||
void generate(output_data *output, uint32_t numsamples = 1);
|
||||
|
||||
fm_engine* debug_fm_engine() { return &m_fm; }
|
||||
fm_engine* debug_fm_engine() { return &m_fm; }
|
||||
|
||||
protected:
|
||||
// internal state
|
||||
|
|
@ -791,6 +791,8 @@ public:
|
|||
// generate samples of sound
|
||||
void generate(output_data *output, uint32_t numsamples = 1);
|
||||
|
||||
fm_engine* debug_fm_engine() { return &m_fm; }
|
||||
pcm_engine* debug_pcm_engine() { return &m_pcm; }
|
||||
protected:
|
||||
// internal state
|
||||
uint16_t m_address; // address register
|
||||
|
|
|
|||
|
|
@ -309,6 +309,7 @@ void pcm_channel::clock(uint32_t env_counter)
|
|||
|
||||
void pcm_channel::output(output_data &output) const
|
||||
{
|
||||
m_output[0] = m_output[1] = m_output[2] = m_output[3] = 0;
|
||||
// early out if the envelope is effectively off
|
||||
uint32_t envelope = m_env_attenuation;
|
||||
if (envelope > EG_QUIET)
|
||||
|
|
@ -340,6 +341,8 @@ void pcm_channel::output(output_data &output) const
|
|||
uint32_t outnum = m_regs.ch_output_channel(m_choffs) * 2;
|
||||
output.data[outnum + 0] += (lvol * sample) >> 15;
|
||||
output.data[outnum + 1] += (rvol * sample) >> 15;
|
||||
m_output[outnum + 0] = output.data[outnum + 0];
|
||||
m_output[outnum + 1] = output.data[outnum + 1];
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -267,6 +267,8 @@ public:
|
|||
// load a new wavetable entry
|
||||
void load_wavetable();
|
||||
|
||||
int32_t debug_output(uint32_t index) const { return m_output[index]; }
|
||||
|
||||
private:
|
||||
// internal helpers
|
||||
void start_attack();
|
||||
|
|
@ -291,6 +293,7 @@ private:
|
|||
pcm_cache m_cache; // cached data
|
||||
pcm_registers &m_regs; // reference to registers
|
||||
pcm_engine &m_owner; // reference to our owner
|
||||
mutable int32_t m_output[4];
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -331,6 +334,8 @@ public:
|
|||
// return a reference to our registers
|
||||
pcm_registers ®s() { return m_regs; }
|
||||
|
||||
// simple getters for debugging
|
||||
pcm_channel *debug_channel(uint32_t index) const { return m_channel[index].get(); }
|
||||
private:
|
||||
// internal state
|
||||
ymfm_interface &m_intf; // reference to the interface
|
||||
|
|
|
|||
599
src/engine/platform/supervision.cpp
Normal file
599
src/engine/platform/supervision.cpp
Normal file
|
|
@ -0,0 +1,599 @@
|
|||
/**
|
||||
* 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 "supervision.h"
|
||||
#include "../engine.h"
|
||||
#include "../../ta-log.h"
|
||||
#include "furIcons.h"
|
||||
#include <math.h>
|
||||
|
||||
//#define rWrite(a,v) pendingWrites[a]=v;
|
||||
#define rWrite(a,v) if (!skipRegisterWrites) {writes.push(QueuedWrite(a,v)); if (dumpWrites) {addWrite(a,v);} }
|
||||
|
||||
#define CHIP_DIVIDER 32
|
||||
|
||||
const char* regCheatSheetSupervision[]={
|
||||
"Freq0L", "10",
|
||||
"Freq0H", "11",
|
||||
"Control0", "12",
|
||||
"Len0", "13",
|
||||
|
||||
"Freq1L", "14",
|
||||
"Freq1H", "15",
|
||||
"Control1", "16",
|
||||
"Len1", "17",
|
||||
|
||||
"DMAstartL", "18",
|
||||
"DMAstartH", "19",
|
||||
"DMAlen", "1a",
|
||||
"DMAstep", "1b",
|
||||
"DMAon", "1c",
|
||||
|
||||
"NoisDiv", "28",
|
||||
"NoisLen", "29",
|
||||
"NoisCtrl", "2a",
|
||||
|
||||
NULL
|
||||
};
|
||||
|
||||
const char** DivPlatformSupervision::getRegisterSheet() {
|
||||
return regCheatSheetSupervision;
|
||||
}
|
||||
|
||||
void DivPlatformSupervision::acquire(short** buf, size_t len) {
|
||||
int mask_bits=0;
|
||||
for (int i=0; i<4; i++)
|
||||
mask_bits |= isMuted[i]?0:8>>i;
|
||||
supervision_set_mute_mask(&svision,mask_bits);
|
||||
|
||||
for (size_t h=0; h<len; h++) {
|
||||
while (!writes.empty()) {
|
||||
QueuedWrite w=writes.front();
|
||||
supervision_memorymap_registers_write(&svision,w.addr|0x2000,w.val);
|
||||
regPool[w.addr&0x3f]=w.val;
|
||||
writes.pop();
|
||||
}
|
||||
|
||||
unsigned char s[6];
|
||||
supervision_sound_stream_update(&svision,s,2);
|
||||
tempL[0]=(((int)s[0])-128)*256;
|
||||
tempR[0]=(((int)s[1])-128)*256;
|
||||
|
||||
for (int i=0; i<4; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP((((int)s[2+i])-128)*256,-32768,32767);
|
||||
}
|
||||
|
||||
tempL[0]=(tempL[0]>>1)+(tempL[0]>>2);
|
||||
tempR[0]=(tempR[0]>>1)+(tempR[0]>>2);
|
||||
|
||||
if (tempL[0]<-32768) tempL[0]=-32768;
|
||||
if (tempL[0]>32767) tempL[0]=32767;
|
||||
if (tempR[0]<-32768) tempR[0]=-32768;
|
||||
if (tempR[0]>32767) tempR[0]=32767;
|
||||
|
||||
//printf("tempL: %d tempR: %d\n",tempL,tempR);
|
||||
buf[0][h]=tempL[0];
|
||||
buf[1][h]=tempR[0];
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformSupervision::tick(bool sysTick) {
|
||||
for (int i=0; i<4; i++) {
|
||||
|
||||
chan[i].std.next();
|
||||
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 (NEW_ARP_STRAT) {
|
||||
chan[i].handleArp();
|
||||
} else if (chan[i].std.arp.had) {
|
||||
if (!chan[i].inPorta) {
|
||||
int f=parent->calcArp(chan[i].note,chan[i].std.arp.val);
|
||||
if (i==2 || i==3) {
|
||||
chan[i].baseFreq=f;
|
||||
//if (chan[i].baseFreq>255) chan[i].baseFreq=255;
|
||||
if (chan[i].baseFreq<0) chan[i].baseFreq=0;
|
||||
} else {
|
||||
chan[i].baseFreq=NOTE_PERIODIC(f);
|
||||
}
|
||||
}
|
||||
chan[i].freqChanged=true;
|
||||
}
|
||||
if (chan[i].std.duty.had) {
|
||||
chan[i].duty=chan[i].std.duty.val;
|
||||
}
|
||||
if (chan[i].std.panL.had) {
|
||||
chan[i].pan=chan[i].std.panL.val&3;
|
||||
}
|
||||
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].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
||||
//DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_PCE);
|
||||
if (i<2) {
|
||||
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<<1,CHIP_DIVIDER);
|
||||
if (chan[i].freq<1) chan[i].freq=1;
|
||||
if (chan[i].freq>2047) chan[i].freq=2047;
|
||||
if (chan[i].freqChanged || chan[i].initWrite) {
|
||||
rWrite(0x10|(i<<2),chan[i].freq&0xff);
|
||||
rWrite(0x11|(i<<2),(chan[i].freq>>8)&0x7);
|
||||
}
|
||||
chan[i].initWrite=false;
|
||||
} else if (i==3) {
|
||||
int ntPos=chan[i].baseFreq;
|
||||
if (NEW_ARP_STRAT) {
|
||||
if (chan[i].fixedArp) {
|
||||
ntPos=chan[i].baseNoteOverride;
|
||||
} else {
|
||||
ntPos+=chan[i].arpOff;
|
||||
}
|
||||
}
|
||||
ntPos+=chan[i].pitch2;
|
||||
chan[i].freq=15-(ntPos&15);
|
||||
unsigned char r=(chan[i].freq<<4)|(chan[i].outVol&0xf);
|
||||
rWrite(0x28,r);
|
||||
noiseReg[0]=r;
|
||||
rWrite(0x29,0xc8);
|
||||
r=((chan[i].duty&1)^dutySwap)|(0x02|0x10)|(chan[i].pan<<2);
|
||||
rWrite(0x2A,r);
|
||||
}
|
||||
if (chan[i].keyOn && i==2) {
|
||||
if (chan[i].pcm) {
|
||||
int ntPos=chan[i].sampleNote;
|
||||
ntPos+=chan[i].pitch2;
|
||||
chan[i].freq=3-(ntPos&3);
|
||||
int sNum=chan[i].sample;
|
||||
DivSample* sample=parent->getSample(sNum);
|
||||
if (sample!=NULL && sNum>=0 && sNum<parent->song.sampleLen) {
|
||||
unsigned int off=MIN(sampleOff[sNum]+chan[i].hasOffset,sampleOff[sNum]+sampleLen[sNum]);
|
||||
unsigned int len=MAX((((int)sampleLen[sNum])-((int)chan[i].hasOffset)),0);
|
||||
if (len) {
|
||||
rWrite(0x18,off&0xff);
|
||||
rWrite(0x19,(off>>8&0x3f)|0x80);
|
||||
rWrite(0x1A,MIN(MAX(len>>4,0),255));
|
||||
rWrite(0x1B,chan[i].freq|((chan[i].pan&3)<<2)|((off>>14&7)<<4));
|
||||
rWrite(0x1C,0x80);
|
||||
}
|
||||
sampleOffset=chan[i].hasOffset;
|
||||
chan[i].hasOffset=0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (chan[i].keyOff && i==2) {
|
||||
if (chan[i].pcm) {
|
||||
int ntPos=chan[i].sampleNote;
|
||||
ntPos+=chan[i].pitch2;
|
||||
chan[i].freq=3-(ntPos&3);
|
||||
int sNum=chan[i].sample;
|
||||
DivSample* sample=parent->getSample(sNum);
|
||||
if (sample!=NULL && sNum>=0 && sNum<parent->song.sampleLen) {
|
||||
rWrite(0x1C,0x00);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (chan[i].keyOn) chan[i].kon=true;
|
||||
if (chan[i].keyOff) chan[i].kon=false;
|
||||
if (chan[i].keyOn) chan[i].keyOn=false;
|
||||
if (chan[i].keyOff) chan[i].keyOff=false;
|
||||
chan[i].freqChanged=false;
|
||||
}
|
||||
|
||||
if (chan[i].kon) {
|
||||
if (i<2) {
|
||||
rWrite(0x12|(i<<2),(chan[i].outVol&0xf)|((chan[i].duty&3)<<4));
|
||||
rWrite(0x13|(i<<2),0xc8);
|
||||
} else if (i == 3) {
|
||||
int ntPos=chan[i].baseFreq;
|
||||
if (NEW_ARP_STRAT) {
|
||||
if (chan[i].fixedArp) {
|
||||
ntPos=chan[i].baseNoteOverride;
|
||||
} else {
|
||||
ntPos+=chan[i].arpOff;
|
||||
}
|
||||
}
|
||||
ntPos+=chan[i].pitch2;
|
||||
chan[i].freq=15-(ntPos&15);
|
||||
unsigned char r=(chan[i].freq<<4)|(chan[i].outVol&0xf);
|
||||
if (noiseReg[0] != r) rWrite(0x28,r);
|
||||
noiseReg[0]=r;
|
||||
rWrite(0x29,0xc8);
|
||||
r=((chan[i].duty&1)^dutySwap)|(0x02|0x10)|(chan[i].pan<<2);
|
||||
if (noiseReg[2] != r) rWrite(0x2A,r);
|
||||
noiseReg[2]=r;
|
||||
} else if (i==2) {
|
||||
if (chan[i].pcm) {
|
||||
int ntPos=chan[i].sampleNote;
|
||||
ntPos+=chan[i].pitch2;
|
||||
chan[i].freq=3-(ntPos&3);
|
||||
int sNum=chan[i].sample;
|
||||
DivSample* sample=parent->getSample(sNum);
|
||||
if (sample!=NULL && sNum>=0 && sNum<parent->song.sampleLen) {
|
||||
unsigned int off=MIN(sampleOff[sNum]+sampleOffset,sampleOff[sNum]+sampleLen[sNum]);
|
||||
unsigned int len=MAX((((int)sampleLen[sNum])-((int)sampleOffset)),0);
|
||||
if (len) {
|
||||
rWrite(0x1A,MIN(MAX(len>>4,0),255));
|
||||
if (chan[i].outVol==0) {
|
||||
rWrite(0x1B,chan[i].freq|((off>>14&7)<<4));
|
||||
} else {
|
||||
rWrite(0x1B,chan[i].freq|((chan[i].pan&3)<<2)|((off>>14&7)<<4));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (i < 2) {
|
||||
rWrite(0x12|(i<<2),0);
|
||||
rWrite(0x13|(i<<2),0xc8);
|
||||
} else if (i == 3) {
|
||||
rWrite(0x29,0);
|
||||
unsigned char r=0;
|
||||
if (noiseReg[2] != r) rWrite(0x2A,r);
|
||||
noiseReg[2]=r;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
int DivPlatformSupervision::dispatch(DivCommand c) {
|
||||
switch (c.cmd) {
|
||||
case DIV_CMD_NOTE_ON: {
|
||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_SUPERVISION);
|
||||
if (ins->type==DIV_INS_AMIGA || ins->amiga.useSample) {
|
||||
chan[c.chan].pcm=true;
|
||||
} else {
|
||||
chan[c.chan].pcm=false;
|
||||
}
|
||||
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;
|
||||
if (c.chan==2) {
|
||||
c.value=ins->amiga.getFreq(c.value);
|
||||
chan[c.chan].sampleNote=c.value;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
chan[c.chan].sampleNote=DIV_NOTE_NULL;
|
||||
chan[c.chan].sampleNoteDelta=0;
|
||||
}
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].baseFreq=c.chan==3?c.value:NOTE_PERIODIC(c.value);
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
}
|
||||
chan[c.chan].active=true;
|
||||
chan[c.chan].keyOn=true;
|
||||
//chwrite(c.chan,0x04,0x80|chan[c.chan].vol);
|
||||
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].insChanged=false;
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_NOTE_OFF:
|
||||
if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0);
|
||||
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;
|
||||
if (chan[c.chan].active) {
|
||||
//chwrite(c.chan,0x04,0x80|chan[c.chan].outVol);
|
||||
}
|
||||
}
|
||||
}
|
||||
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+chan[c.chan].sampleNoteDelta);
|
||||
bool return2=false;
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
chan[c.chan].baseFreq+=c.value;
|
||||
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<=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_STD_NOISE_MODE:
|
||||
chan[c.chan].duty=c.value;
|
||||
break;
|
||||
case DIV_CMD_SAMPLE_POS:
|
||||
chan[c.chan].hasOffset=c.value;
|
||||
chan[c.chan].keyOn=true;
|
||||
break;
|
||||
case DIV_CMD_PANNING: {
|
||||
chan[c.chan].pan=0;
|
||||
if (c.value&0xf0) chan[c.chan].pan|=2;
|
||||
if (c.value2>>4) chan[c.chan].pan|=1;
|
||||
if (chan[c.chan].pan==0) chan[c.chan].pan=3;
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_LEGATO:
|
||||
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;
|
||||
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_SUPERVISION));
|
||||
}
|
||||
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(chan[c.chan].note);
|
||||
chan[c.chan].inPorta=c.value;
|
||||
break;
|
||||
case DIV_CMD_GET_VOLMAX:
|
||||
return 15;
|
||||
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 DivPlatformSupervision::muteChannel(int ch, bool mute) {
|
||||
isMuted[ch]=mute;
|
||||
}
|
||||
|
||||
void DivPlatformSupervision::forceIns() {
|
||||
for (int i=0; i<4; i++) {
|
||||
chan[i].insChanged=true;
|
||||
chan[i].freqChanged=true;
|
||||
//chwrite(i,0x05,isMuted[i]?0:chan[i].pan);
|
||||
}
|
||||
}
|
||||
|
||||
void* DivPlatformSupervision::getChanState(int ch) {
|
||||
return &chan[ch];
|
||||
}
|
||||
|
||||
DivMacroInt* DivPlatformSupervision::getChanMacroInt(int ch) {
|
||||
return &chan[ch].std;
|
||||
}
|
||||
|
||||
DivDispatchOscBuffer* DivPlatformSupervision::getOscBuffer(int ch) {
|
||||
return oscBuf[ch];
|
||||
}
|
||||
|
||||
unsigned char* DivPlatformSupervision::getRegisterPool() {
|
||||
return regPool;
|
||||
}
|
||||
|
||||
int DivPlatformSupervision::getRegisterPoolSize() {
|
||||
return 64;
|
||||
}
|
||||
|
||||
void DivPlatformSupervision::reset() {
|
||||
writes.clear();
|
||||
memset(regPool,0,64);
|
||||
for (int i=0; i<4; i++) {
|
||||
chan[i]=DivPlatformSupervision::Channel();
|
||||
chan[i].std.setEngine(parent);
|
||||
}
|
||||
if (dumpWrites) {
|
||||
addWrite(0xffffffff,0);
|
||||
}
|
||||
supervision_sound_reset(&svision);
|
||||
memset(tempL,0,32*sizeof(int));
|
||||
memset(tempR,0,32*sizeof(int));
|
||||
memset(noiseReg,0,3*sizeof(unsigned char));
|
||||
noiseReg[2]=0xff;
|
||||
sampleOffset=0;
|
||||
}
|
||||
|
||||
int DivPlatformSupervision::getOutputCount() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
bool DivPlatformSupervision::keyOffAffectsArp(int ch) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void DivPlatformSupervision::notifyInsDeletion(void* ins) {
|
||||
for (int i=0; i<4; i++) {
|
||||
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformSupervision::setFlags(const DivConfig& flags) {
|
||||
if (flags.getInt("swapDuty",true)) {
|
||||
dutySwap=1;
|
||||
} else {
|
||||
dutySwap=0;
|
||||
}
|
||||
otherFlags=0;
|
||||
if (flags.getInt("sqStereo",false)) {
|
||||
otherFlags |= 1;
|
||||
}
|
||||
chipClock=4000000;
|
||||
CHECK_CUSTOM_CLOCK;
|
||||
rate=chipClock/64;
|
||||
for (int i=0; i<4; i++) {
|
||||
oscBuf[i]->rate=rate;
|
||||
}
|
||||
supervision_sound_set_clock(&svision,(unsigned int)chipClock);
|
||||
supervision_sound_set_flags(&svision,(unsigned int)otherFlags);
|
||||
}
|
||||
|
||||
void DivPlatformSupervision::poke(unsigned int addr, unsigned short val) {
|
||||
rWrite(addr,val);
|
||||
}
|
||||
|
||||
void DivPlatformSupervision::poke(std::vector<DivRegWrite>& wlist) {
|
||||
for (DivRegWrite& i: wlist) rWrite(i.addr,i.val);
|
||||
}
|
||||
|
||||
const void* DivPlatformSupervision::getSampleMem(int index) {
|
||||
return index==0?sampleMem:NULL;
|
||||
}
|
||||
|
||||
size_t DivPlatformSupervision::getSampleMemCapacity(int index) {
|
||||
return index==0?65536:0;
|
||||
}
|
||||
|
||||
size_t DivPlatformSupervision::getSampleMemUsage(int index) {
|
||||
return index==0?sampleMemLen:0;
|
||||
}
|
||||
|
||||
bool DivPlatformSupervision::isSampleLoaded(int index, int sample) {
|
||||
if (index!=0) return false;
|
||||
if (sample<0 || sample>255) return false;
|
||||
return sampleLoaded[sample];
|
||||
}
|
||||
|
||||
const DivMemoryComposition* DivPlatformSupervision::getMemCompo(int index) {
|
||||
if (index!=0) return NULL;
|
||||
return &memCompo;
|
||||
}
|
||||
|
||||
void DivPlatformSupervision::renderSamples(int sysID) {
|
||||
memset(sampleMem,0,getSampleMemCapacity(0));
|
||||
memset(sampleLoaded,0,256*sizeof(bool));
|
||||
|
||||
memCompo=DivMemoryComposition();
|
||||
memCompo.name="Sample Memory";
|
||||
|
||||
size_t memPos=0;
|
||||
for (int i=0; i<parent->song.sampleLen; i++) {
|
||||
DivSample* s=parent->song.sample[i];
|
||||
if (!s->renderOn[0][sysID]) {
|
||||
sampleOff[i]=0;
|
||||
continue;
|
||||
}
|
||||
unsigned int paddedLen=((s->length8>>1)+31)&(~0x1f);
|
||||
logV("%d padded length: %d",i,paddedLen);
|
||||
if (paddedLen>=4096) {
|
||||
paddedLen=4096;
|
||||
}
|
||||
if ((memPos&(~0x3fff))!=((memPos+paddedLen)&(~0x3fff))) {
|
||||
memPos=(memPos+0x3fff)&(~0x3fff);
|
||||
}
|
||||
if (memPos>=getSampleMemCapacity(0)) {
|
||||
logW("out of memory for sample %d!",i);
|
||||
break;
|
||||
}
|
||||
if (memPos+paddedLen>=getSampleMemCapacity(0)) {
|
||||
sampleLen[i]=(getSampleMemCapacity(0)-memPos)>>1;
|
||||
for (size_t i=0; i<(getSampleMemCapacity(0)-memPos)>>1; i++) {
|
||||
sampleMem[memPos+i]=(((s->data8[i*2+0]+128)>>4)<<4&0xf0)|(((s->data8[i*2+1]+128)>>4)&0xf);
|
||||
}
|
||||
logW("out of memory for sample %d!",i);
|
||||
} else {
|
||||
size_t len=MIN((s->length8>>1),paddedLen);
|
||||
sampleLen[i]=(unsigned int)(len);
|
||||
for (size_t i=0; i<len; i++) {
|
||||
sampleMem[memPos+i]=(((s->data8[i*2+0]+128)>>4)<<4&0xf0)|(((s->data8[i*2+1]+128)>>4)&0xf);
|
||||
}
|
||||
sampleLoaded[i]=true;
|
||||
}
|
||||
sampleOff[i]=memPos;
|
||||
memCompo.entries.push_back(DivMemoryEntry(DIV_MEMORY_SAMPLE,"Sample",i,memPos,memPos+paddedLen));
|
||||
memPos+=paddedLen;
|
||||
}
|
||||
sampleMemLen=memPos;
|
||||
|
||||
memCompo.capacity=65536;
|
||||
memCompo.used=sampleMemLen;
|
||||
}
|
||||
|
||||
bool DivPlatformSupervision::getDCOffRequired() {
|
||||
return true;
|
||||
}
|
||||
|
||||
int DivPlatformSupervision::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) {
|
||||
parent=p;
|
||||
dumpWrites=false;
|
||||
skipRegisterWrites=false;
|
||||
for (int i=0; i<4; i++) {
|
||||
isMuted[i]=false;
|
||||
oscBuf[i]=new DivDispatchOscBuffer;
|
||||
}
|
||||
sampleMem=svision.supervision_dma_mem;
|
||||
dutySwap=0;
|
||||
otherFlags=0;
|
||||
sampleOffset=0;
|
||||
sampleMemLen=0;
|
||||
memset(sampleMem,0,65536);
|
||||
svision.ch_mask=15;
|
||||
svision.flags=1;
|
||||
setFlags(flags);
|
||||
reset();
|
||||
return 4;
|
||||
}
|
||||
|
||||
void DivPlatformSupervision::quit() {
|
||||
for (int i=0; i<4; i++) {
|
||||
delete oscBuf[i];
|
||||
}
|
||||
}
|
||||
|
||||
DivPlatformSupervision::~DivPlatformSupervision() {
|
||||
}
|
||||
104
src/engine/platform/supervision.h
Normal file
104
src/engine/platform/supervision.h
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
/**
|
||||
* 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 _SUPERVISION_H
|
||||
#define _SUPERVISION_H
|
||||
|
||||
#include "../dispatch.h"
|
||||
#include "../../fixedQueue.h"
|
||||
#include "sound/supervision.h"
|
||||
|
||||
class DivPlatformSupervision: public DivDispatch {
|
||||
struct Channel: public SharedChannel<signed char> {
|
||||
unsigned int duty, len, pan, pcm; // pcm is channel 3 ONLY
|
||||
int sample, hasOffset; // again, for channel 3 ONLY
|
||||
bool setPos, kon, initWrite;
|
||||
Channel():
|
||||
SharedChannel<signed char>(63),
|
||||
duty(0),
|
||||
len(0x1f),
|
||||
pan(3),
|
||||
pcm(false),
|
||||
hasOffset(0),
|
||||
setPos(false),
|
||||
kon(false),
|
||||
initWrite(true) {}
|
||||
};
|
||||
Channel chan[4];
|
||||
DivDispatchOscBuffer* oscBuf[4];
|
||||
bool isMuted[4];
|
||||
struct QueuedWrite {
|
||||
unsigned char addr;
|
||||
unsigned char val;
|
||||
QueuedWrite(): addr(0), val(9) {}
|
||||
QueuedWrite(unsigned char a, unsigned char v): addr(a), val(v) {}
|
||||
};
|
||||
FixedQueue<QueuedWrite,512> writes;
|
||||
|
||||
int curChan;
|
||||
int tempL[32];
|
||||
int tempR[32];
|
||||
int coreQuality;
|
||||
unsigned char regPool[64];
|
||||
unsigned int sampleOff[256];
|
||||
unsigned int sampleLen[256];
|
||||
bool sampleLoaded[256];
|
||||
DivMemoryComposition memCompo;
|
||||
unsigned char* sampleMem;
|
||||
size_t sampleMemLen;
|
||||
unsigned char dutySwap;
|
||||
unsigned char otherFlags;
|
||||
unsigned int sampleOffset;
|
||||
unsigned char noiseReg[3];
|
||||
struct svision_t svision;
|
||||
|
||||
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);
|
||||
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 notifyInsDeletion(void* ins);
|
||||
void poke(unsigned int addr, unsigned short val);
|
||||
void poke(std::vector<DivRegWrite>& wlist);
|
||||
const char** getRegisterSheet();
|
||||
const void* getSampleMem(int index);
|
||||
size_t getSampleMemCapacity(int index);
|
||||
size_t getSampleMemUsage(int index);
|
||||
bool isSampleLoaded(int index, int sample);
|
||||
const DivMemoryComposition* getMemCompo(int index);
|
||||
void renderSamples(int chipID);
|
||||
bool getDCOffRequired();
|
||||
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
|
||||
void quit();
|
||||
~DivPlatformSupervision();
|
||||
};
|
||||
|
||||
#endif
|
||||
366
src/engine/platform/upd1771c.cpp
Normal file
366
src/engine/platform/upd1771c.cpp
Normal file
|
|
@ -0,0 +1,366 @@
|
|||
/**
|
||||
* 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 "upd1771c.h"
|
||||
#include "../engine.h"
|
||||
#include "../../ta-log.h"
|
||||
#include "furIcons.h"
|
||||
#include <math.h>
|
||||
|
||||
//#define rWrite(a,v) pendingWrites[a]=v;
|
||||
#define rWrite(a,v) if (!skipRegisterWrites) {writes.push(QueuedWrite(a,v)); if (dumpWrites) {addWrite(a,v);} }
|
||||
|
||||
#define CHIP_DIVIDER 64
|
||||
|
||||
const char* regCheatSheetUPD1771c[]={
|
||||
NULL
|
||||
};
|
||||
|
||||
const char** DivPlatformUPD1771c::getRegisterSheet() {
|
||||
return regCheatSheetUPD1771c;
|
||||
}
|
||||
|
||||
void DivPlatformUPD1771c::acquire(short** buf, size_t len) {
|
||||
for (size_t h=0; h<len; h++) {
|
||||
while (!writes.empty()) {
|
||||
QueuedWrite w=writes.front();
|
||||
upd1771c_write_packet(&scv,w.addr&15,w.val);
|
||||
regPool[w.addr&0xf]=w.val;
|
||||
writes.pop();
|
||||
}
|
||||
|
||||
short s=upd1771c_sound_stream_update(&scv)<<3;
|
||||
if (isMuted[0]) s=0;
|
||||
oscBuf[0]->data[oscBuf[0]->needle++]=s;
|
||||
buf[0][h]=s;
|
||||
buf[1][h]=s;
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformUPD1771c::tick(bool sysTick) {
|
||||
for (int i=0; i<1; i++) {
|
||||
|
||||
chan[i].std.next();
|
||||
if (chan[i].std.vol.had) {
|
||||
chan[i].outVol=VOL_SCALE_LINEAR(chan[i].vol&31,MIN(31,chan[i].std.vol.val),31);
|
||||
}
|
||||
if (NEW_ARP_STRAT) {
|
||||
chan[i].handleArp();
|
||||
} else if (chan[i].std.arp.had) {
|
||||
if (!chan[i].inPorta) {
|
||||
int f=parent->calcArp(chan[i].note,chan[i].std.arp.val);
|
||||
chan[i].baseFreq=NOTE_PERIODIC(f);
|
||||
}
|
||||
chan[i].freqChanged=true;
|
||||
}
|
||||
if (chan[i].std.wave.had) {
|
||||
chan[i].wave=chan[i].std.wave.val&7;
|
||||
}
|
||||
if (chan[i].std.duty.had) {
|
||||
chan[i].duty=chan[i].std.duty.val&1;
|
||||
}
|
||||
if (chan[i].std.ex1.had) {
|
||||
chan[i].pos=chan[i].std.ex1.val&31;
|
||||
}
|
||||
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].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
||||
//DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_PCE);
|
||||
if (i==0) {
|
||||
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);
|
||||
}
|
||||
if (chan[i].freqChanged || initWrite[i] || chan[i].keyOn) {
|
||||
if (chan[i].duty == 0) {
|
||||
rWrite(0,2);
|
||||
rWrite(1,(chan[i].wave<<5)|chan[i].pos);
|
||||
float p = ((float)chan[i].freq)/((float)(31-chan[i].pos))*31.0;
|
||||
rWrite(2,MIN(MAX((int)p,0),255));
|
||||
rWrite(3,chan[i].outVol);
|
||||
} else if (chan[i].duty == 1) {
|
||||
rWrite(0,1);
|
||||
rWrite(1,(chan[i].wave<<5));
|
||||
rWrite(2,MIN(MAX(chan[i].freq>>7,0),255));
|
||||
rWrite(3,chan[i].outVol);
|
||||
} else {
|
||||
rWrite(0,0);
|
||||
}
|
||||
initWrite[i]=0;
|
||||
}
|
||||
if (chan[i].keyOff) {
|
||||
rWrite(0,0);
|
||||
}
|
||||
if (chan[i].keyOn) kon[i]=1;
|
||||
if (chan[i].keyOff) kon[i]=0;
|
||||
if (chan[i].keyOn) chan[i].keyOn=false;
|
||||
if (chan[i].keyOff) chan[i].keyOff=false;
|
||||
chan[i].freqChanged=false;
|
||||
}
|
||||
|
||||
if (kon[i]) {
|
||||
if (i==0) {
|
||||
if (chan[i].duty == 0) {
|
||||
rWrite(0,2);
|
||||
rWrite(1,(chan[i].wave<<5)|chan[i].pos);
|
||||
// TODO: improve
|
||||
float p = ((float)chan[i].freq)/((float)(32-chan[i].pos))*32.0;
|
||||
rWrite(2,MIN(MAX((int)p,0),255));
|
||||
rWrite(3,chan[i].outVol);
|
||||
} else if (chan[i].duty == 1) {
|
||||
rWrite(0,1);
|
||||
rWrite(1,(chan[i].wave<<5));
|
||||
rWrite(2,MIN(MAX(chan[i].freq>>7,0),255));
|
||||
rWrite(3,chan[i].outVol);
|
||||
} else {
|
||||
rWrite(0,0);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (i == 0) {
|
||||
rWrite(0,0);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
int DivPlatformUPD1771c::dispatch(DivCommand c) {
|
||||
switch (c.cmd) {
|
||||
case DIV_CMD_NOTE_ON: {
|
||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_UPD1771C);
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].baseFreq=c.chan==3?c.value:NOTE_PERIODIC(c.value);
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
}
|
||||
chan[c.chan].active=true;
|
||||
chan[c.chan].keyOn=true;
|
||||
//chwrite(c.chan,0x04,0x80|chan[c.chan].vol);
|
||||
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].insChanged=false;
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_NOTE_OFF:
|
||||
if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0);
|
||||
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;
|
||||
if (chan[c.chan].active) {
|
||||
//chwrite(c.chan,0x04,0x80|chan[c.chan].outVol);
|
||||
}
|
||||
}
|
||||
}
|
||||
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>=destFreq) {
|
||||
chan[c.chan].baseFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
} else {
|
||||
chan[c.chan].baseFreq-=c.value;
|
||||
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_STD_NOISE_MODE:
|
||||
chan[c.chan].wave=c.value&7;
|
||||
chan[c.chan].duty=c.value>>4&1;
|
||||
break;
|
||||
case DIV_CMD_N163_WAVE_POSITION:
|
||||
chan[c.chan].pos=c.value;
|
||||
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].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_UPD1771C));
|
||||
}
|
||||
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(chan[c.chan].note);
|
||||
chan[c.chan].inPorta=c.value;
|
||||
break;
|
||||
case DIV_CMD_GET_VOLMAX:
|
||||
return 31;
|
||||
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 DivPlatformUPD1771c::muteChannel(int ch, bool mute) {
|
||||
isMuted[ch]=mute;
|
||||
}
|
||||
|
||||
void DivPlatformUPD1771c::forceIns() {
|
||||
for (int i=0; i<1; i++) {
|
||||
chan[i].insChanged=true;
|
||||
chan[i].freqChanged=true;
|
||||
//chwrite(i,0x05,isMuted[i]?0:chan[i].pan);
|
||||
}
|
||||
}
|
||||
|
||||
void* DivPlatformUPD1771c::getChanState(int ch) {
|
||||
return &chan[ch];
|
||||
}
|
||||
|
||||
DivMacroInt* DivPlatformUPD1771c::getChanMacroInt(int ch) {
|
||||
return &chan[ch].std;
|
||||
}
|
||||
|
||||
DivDispatchOscBuffer* DivPlatformUPD1771c::getOscBuffer(int ch) {
|
||||
return oscBuf[ch];
|
||||
}
|
||||
|
||||
unsigned char* DivPlatformUPD1771c::getRegisterPool() {
|
||||
return regPool;
|
||||
}
|
||||
|
||||
int DivPlatformUPD1771c::getRegisterPoolSize() {
|
||||
return 16;
|
||||
}
|
||||
|
||||
void DivPlatformUPD1771c::reset() {
|
||||
writes.clear();
|
||||
memset(regPool,0,16);
|
||||
for (int i=0; i<1; i++) {
|
||||
chan[i]=DivPlatformUPD1771c::Channel();
|
||||
chan[i].std.setEngine(parent);
|
||||
}
|
||||
if (dumpWrites) {
|
||||
addWrite(0xffffffff,0);
|
||||
}
|
||||
upd1771c_reset(&scv);
|
||||
memset(tempL,0,32*sizeof(int));
|
||||
memset(tempR,0,32*sizeof(int));
|
||||
memset(kon,0,1*sizeof(unsigned char));
|
||||
memset(initWrite,1,1*sizeof(unsigned char));
|
||||
}
|
||||
|
||||
int DivPlatformUPD1771c::getOutputCount() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
bool DivPlatformUPD1771c::keyOffAffectsArp(int ch) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void DivPlatformUPD1771c::notifyInsDeletion(void* ins) {
|
||||
for (int i=0; i<1; i++) {
|
||||
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformUPD1771c::setFlags(const DivConfig& flags) {
|
||||
chipClock=6000000;
|
||||
CHECK_CUSTOM_CLOCK;
|
||||
rate=chipClock/32;
|
||||
for (int i=0; i<1; i++) {
|
||||
oscBuf[i]->rate=rate;
|
||||
}
|
||||
upd1771c_sound_set_clock(&scv,(unsigned int)chipClock,8);
|
||||
}
|
||||
|
||||
void DivPlatformUPD1771c::poke(unsigned int addr, unsigned short val) {
|
||||
rWrite(addr,val);
|
||||
}
|
||||
|
||||
void DivPlatformUPD1771c::poke(std::vector<DivRegWrite>& wlist) {
|
||||
for (DivRegWrite& i: wlist) rWrite(i.addr,i.val);
|
||||
}
|
||||
|
||||
int DivPlatformUPD1771c::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) {
|
||||
parent=p;
|
||||
dumpWrites=false;
|
||||
skipRegisterWrites=false;
|
||||
for (int i=0; i<1; i++) {
|
||||
isMuted[i]=false;
|
||||
oscBuf[i]=new DivDispatchOscBuffer;
|
||||
}
|
||||
setFlags(flags);
|
||||
reset();
|
||||
return 1;
|
||||
}
|
||||
|
||||
void DivPlatformUPD1771c::quit() {
|
||||
for (int i=0; i<1; i++) {
|
||||
delete oscBuf[i];
|
||||
}
|
||||
}
|
||||
|
||||
DivPlatformUPD1771c::~DivPlatformUPD1771c() {
|
||||
}
|
||||
84
src/engine/platform/upd1771c.h
Normal file
84
src/engine/platform/upd1771c.h
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
/**
|
||||
* 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 _UPD1771C_H
|
||||
#define _UPD1771C_H
|
||||
|
||||
#include "../dispatch.h"
|
||||
#include "../../fixedQueue.h"
|
||||
#include "sound/upd1771c.h"
|
||||
|
||||
class DivPlatformUPD1771c: public DivDispatch {
|
||||
struct Channel: public SharedChannel<signed char> {
|
||||
unsigned int wave;
|
||||
int pos, duty;
|
||||
Channel():
|
||||
SharedChannel<signed char>(15),
|
||||
wave(0),
|
||||
pos(0),
|
||||
duty(0) {}
|
||||
};
|
||||
Channel chan[1];
|
||||
DivDispatchOscBuffer* oscBuf[1];
|
||||
bool isMuted[4];
|
||||
struct QueuedWrite {
|
||||
unsigned char addr;
|
||||
unsigned char val;
|
||||
QueuedWrite(): addr(0), val(9) {}
|
||||
QueuedWrite(unsigned char a, unsigned char v): addr(a), val(v) {}
|
||||
};
|
||||
FixedQueue<QueuedWrite,512> writes;
|
||||
|
||||
int curChan;
|
||||
int tempL[32];
|
||||
int tempR[32];
|
||||
int coreQuality;
|
||||
unsigned char regPool[16];
|
||||
unsigned char kon[1];
|
||||
unsigned char initWrite[1];
|
||||
struct upd1771c_t scv;
|
||||
|
||||
|
||||
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);
|
||||
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 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();
|
||||
~DivPlatformUPD1771c();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -173,6 +173,7 @@ void DivPlatformYM2203::acquire_combo(short** buf, size_t len) {
|
|||
for (size_t h=0; h<len; h++) {
|
||||
// AY -> OPN
|
||||
ay->runDAC();
|
||||
ay->runTFX(rate);
|
||||
ay->flushWrites();
|
||||
for (DivRegWrite& i: ay->getRegisterWrites()) {
|
||||
if (i.addr>15) continue;
|
||||
|
|
@ -255,6 +256,7 @@ void DivPlatformYM2203::acquire_ymfm(short** buf, size_t len) {
|
|||
for (size_t h=0; h<len; h++) {
|
||||
// AY -> OPN
|
||||
ay->runDAC();
|
||||
ay->runTFX(rate);
|
||||
ay->flushWrites();
|
||||
for (DivRegWrite& i: ay->getRegisterWrites()) {
|
||||
if (i.addr>15) continue;
|
||||
|
|
@ -311,6 +313,16 @@ void DivPlatformYM2203::acquire_lle(short** buf, size_t len) {
|
|||
fmOut[i]=0;
|
||||
}
|
||||
|
||||
// AY -> OPN
|
||||
ay->runDAC();
|
||||
ay->runTFX(rate);
|
||||
ay->flushWrites();
|
||||
for (DivRegWrite& i: ay->getRegisterWrites()) {
|
||||
if (i.addr>15) continue;
|
||||
immWrite(i.addr&15,i.val);
|
||||
}
|
||||
ay->getRegisterWrites().clear();
|
||||
|
||||
while (true) {
|
||||
bool canWeWrite=fm_lle.prescaler_latch[1]&1;
|
||||
|
||||
|
|
@ -444,6 +456,10 @@ void DivPlatformYM2203::acquire_lle(short** buf, size_t len) {
|
|||
}
|
||||
}
|
||||
|
||||
void DivPlatformYM2203::fillStream(std::vector<DivDelayedWrite>& stream, int sRate, size_t len) {
|
||||
ay->fillStream(stream,sRate,len);
|
||||
}
|
||||
|
||||
void DivPlatformYM2203::tick(bool sysTick) {
|
||||
// PSG
|
||||
ay->tick(sysTick);
|
||||
|
|
@ -1006,7 +1022,20 @@ int DivPlatformYM2203::dispatch(DivCommand c) {
|
|||
}
|
||||
case DIV_CMD_FM_OPMASK:
|
||||
if (c.chan>=psgChanOffs) break;
|
||||
chan[c.chan].opMask=c.value&15;
|
||||
switch (c.value>>4) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
chan[c.chan].opMask&=~(1<<((c.value>>4)-1));
|
||||
if (c.value&15) {
|
||||
chan[c.chan].opMask|=(1<<((c.value>>4)-1));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
chan[c.chan].opMask=c.value&15;
|
||||
break;
|
||||
}
|
||||
if (chan[c.chan].active) {
|
||||
chan[c.chan].opMaskChanged=true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@ class DivPlatformYM2203: public DivPlatformOPN {
|
|||
|
||||
public:
|
||||
void acquire(short** buf, size_t len);
|
||||
void fillStream(std::vector<DivDelayedWrite>& stream, int sRate, size_t len);
|
||||
int dispatch(DivCommand c);
|
||||
void* getChanState(int chan);
|
||||
DivMacroInt* getChanMacroInt(int ch);
|
||||
|
|
|
|||
|
|
@ -325,6 +325,7 @@ void DivPlatformYM2608::acquire_combo(short** buf, size_t len) {
|
|||
for (size_t h=0; h<len; h++) {
|
||||
// AY -> OPN
|
||||
ay->runDAC();
|
||||
ay->runTFX(rate);
|
||||
ay->flushWrites();
|
||||
for (DivRegWrite& i: ay->getRegisterWrites()) {
|
||||
if (i.addr>15) continue;
|
||||
|
|
@ -440,6 +441,7 @@ void DivPlatformYM2608::acquire_ymfm(short** buf, size_t len) {
|
|||
for (size_t h=0; h<len; h++) {
|
||||
// AY -> OPN
|
||||
ay->runDAC();
|
||||
ay->runTFX(rate);
|
||||
ay->flushWrites();
|
||||
for (DivRegWrite& i: ay->getRegisterWrites()) {
|
||||
if (i.addr>15) continue;
|
||||
|
|
@ -680,6 +682,10 @@ void DivPlatformYM2608::acquire_lle(short** buf, size_t len) {
|
|||
}
|
||||
}
|
||||
|
||||
void DivPlatformYM2608::fillStream(std::vector<DivDelayedWrite>& stream, int sRate, size_t len) {
|
||||
ay->fillStream(stream,sRate,len);
|
||||
}
|
||||
|
||||
void DivPlatformYM2608::tick(bool sysTick) {
|
||||
// FM
|
||||
for (int i=0; i<6; i++) {
|
||||
|
|
@ -1539,7 +1545,20 @@ int DivPlatformYM2608::dispatch(DivCommand c) {
|
|||
}
|
||||
case DIV_CMD_FM_OPMASK:
|
||||
if (c.chan>=psgChanOffs) break;
|
||||
chan[c.chan].opMask=c.value&15;
|
||||
switch (c.value>>4) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
chan[c.chan].opMask&=~(1<<((c.value>>4)-1));
|
||||
if (c.value&15) {
|
||||
chan[c.chan].opMask|=(1<<((c.value>>4)-1));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
chan[c.chan].opMask=c.value&15;
|
||||
break;
|
||||
}
|
||||
if (chan[c.chan].active) {
|
||||
chan[c.chan].opMaskChanged=true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -93,6 +93,7 @@ class DivPlatformYM2608: public DivPlatformOPN {
|
|||
|
||||
public:
|
||||
void acquire(short** buf, size_t len);
|
||||
void fillStream(std::vector<DivDelayedWrite>& stream, int sRate, size_t len);
|
||||
int dispatch(DivCommand c);
|
||||
void* getChanState(int chan);
|
||||
DivMacroInt* getChanMacroInt(int ch);
|
||||
|
|
|
|||
|
|
@ -260,6 +260,7 @@ void DivPlatformYM2610::acquire_combo(short** buf, size_t len) {
|
|||
for (size_t h=0; h<len; h++) {
|
||||
// AY -> OPN
|
||||
ay->runDAC();
|
||||
ay->runTFX(rate);
|
||||
ay->flushWrites();
|
||||
for (DivRegWrite& i: ay->getRegisterWrites()) {
|
||||
if (i.addr>15) continue;
|
||||
|
|
@ -373,6 +374,7 @@ void DivPlatformYM2610::acquire_ymfm(short** buf, size_t len) {
|
|||
for (size_t h=0; h<len; h++) {
|
||||
// AY -> OPN
|
||||
ay->runDAC();
|
||||
ay->runTFX(rate);
|
||||
ay->flushWrites();
|
||||
for (DivRegWrite& i: ay->getRegisterWrites()) {
|
||||
if (i.addr>15) continue;
|
||||
|
|
@ -1509,7 +1511,20 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
|
|||
}
|
||||
case DIV_CMD_FM_OPMASK:
|
||||
if (c.chan>=psgChanOffs) break;
|
||||
chan[c.chan].opMask=c.value&15;
|
||||
switch (c.value>>4) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
chan[c.chan].opMask&=~(1<<((c.value>>4)-1));
|
||||
if (c.value&15) {
|
||||
chan[c.chan].opMask|=(1<<((c.value>>4)-1));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
chan[c.chan].opMask=c.value&15;
|
||||
break;
|
||||
}
|
||||
if (chan[c.chan].active) {
|
||||
chan[c.chan].opMaskChanged=true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -324,6 +324,7 @@ void DivPlatformYM2610B::acquire_combo(short** buf, size_t len) {
|
|||
for (size_t h=0; h<len; h++) {
|
||||
// AY -> OPN
|
||||
ay->runDAC();
|
||||
ay->runTFX(rate);
|
||||
ay->flushWrites();
|
||||
for (DivRegWrite& i: ay->getRegisterWrites()) {
|
||||
if (i.addr>15) continue;
|
||||
|
|
@ -439,6 +440,7 @@ void DivPlatformYM2610B::acquire_ymfm(short** buf, size_t len) {
|
|||
for (size_t h=0; h<len; h++) {
|
||||
// AY -> OPN
|
||||
ay->runDAC();
|
||||
ay->runTFX(rate);
|
||||
ay->flushWrites();
|
||||
for (DivRegWrite& i: ay->getRegisterWrites()) {
|
||||
if (i.addr>15) continue;
|
||||
|
|
@ -1578,7 +1580,20 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
|
|||
}
|
||||
case DIV_CMD_FM_OPMASK:
|
||||
if (c.chan>=psgChanOffs) break;
|
||||
chan[c.chan].opMask=c.value&15;
|
||||
switch (c.value>>4) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
chan[c.chan].opMask&=~(1<<((c.value>>4)-1));
|
||||
if (c.value&15) {
|
||||
chan[c.chan].opMask|=(1<<((c.value>>4)-1));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
chan[c.chan].opMask=c.value&15;
|
||||
break;
|
||||
}
|
||||
if (chan[c.chan].active) {
|
||||
chan[c.chan].opMaskChanged=true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -108,6 +108,10 @@ class DivPlatformYM2610Base: public DivPlatformOPN {
|
|||
}
|
||||
|
||||
public:
|
||||
void fillStream(std::vector<DivDelayedWrite>& stream, int sRate, size_t len) {
|
||||
ay->fillStream(stream,sRate,len);
|
||||
}
|
||||
|
||||
void reset() {
|
||||
writeADPCMAOff=0;
|
||||
writeADPCMAOn=0;
|
||||
|
|
|
|||
|
|
@ -268,6 +268,22 @@ const char* cmdName[]={
|
|||
|
||||
"FM_OPMASK",
|
||||
|
||||
"MULTIPCM_MIX_FM",
|
||||
"MULTIPCM_MIX_PCM",
|
||||
"MULTIPCM_LFO",
|
||||
"MULTIPCM_VIB",
|
||||
"MULTIPCM_AM",
|
||||
"MULTIPCM_AR",
|
||||
"MULTIPCM_D1R",
|
||||
"MULTIPCM_DL",
|
||||
"MULTIPCM_D2R",
|
||||
"MULTIPCM_RR",
|
||||
"MULTIPCM_RC",
|
||||
"MULTIPCM_DAMP",
|
||||
"MULTIPCM_PSEUDO_REVERB",
|
||||
"MULTIPCM_LFO_RESET",
|
||||
"MULTIPCM_LEVEL_DIRECT",
|
||||
|
||||
"SID3_SPECIAL_WAVE",
|
||||
"SID3_RING_MOD_SRC",
|
||||
"SID3_HARD_SYNC_SRC",
|
||||
|
|
@ -289,7 +305,7 @@ const char* cmdName[]={
|
|||
"SID3_NOISE_PHASE_RESET",
|
||||
"SID3_ENVELOPE_RESET",
|
||||
"SID3_CUTOFF_SCALING",
|
||||
"SID3_RESONANCE_SCALING",
|
||||
"SID3_RESONANCE_SCALING"
|
||||
};
|
||||
|
||||
static_assert((sizeof(cmdName)/sizeof(void*))==DIV_CMD_MAX,"update cmdName!");
|
||||
|
|
@ -559,7 +575,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
|
|||
bool comparison=(song.delayBehavior==1)?(effectVal<=nextSpeed):(effectVal<(nextSpeed*(curSubSong->timeBase+1)));
|
||||
if (song.delayBehavior==2) comparison=true;
|
||||
if (comparison) {
|
||||
chan[i].rowDelay=effectVal+1;
|
||||
chan[i].rowDelay=effectVal;
|
||||
chan[i].delayOrder=whatOrder;
|
||||
chan[i].delayRow=whatRow;
|
||||
if (effectVal==nextSpeed) {
|
||||
|
|
@ -1603,6 +1619,19 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
|
|||
if (--subticks<=0) {
|
||||
subticks=tickMult;
|
||||
|
||||
// apply delayed rows before potentially advancing to a new row, which would overwrite the
|
||||
// delayed row's state before it has a chance to do anything. a typical example would be
|
||||
// a delay scheduling a note-on to be simultaneous with the next row, and the next row also
|
||||
// containing a delayed note. if we don't apply the delayed row first,
|
||||
for (int i=0; i<chans; i++) {
|
||||
// delay effects
|
||||
if (chan[i].rowDelay>0) {
|
||||
if (--chan[i].rowDelay==0) {
|
||||
processRow(i,true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (stepPlay!=1) {
|
||||
tempoAccum+=(skipping && virtualTempoN<virtualTempoD)?virtualTempoD:virtualTempoN;
|
||||
while (tempoAccum>=virtualTempoD) {
|
||||
|
|
@ -1633,15 +1662,9 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
|
|||
// under no circumstances shall the accumulator become this large
|
||||
if (tempoAccum>1023) tempoAccum=1023;
|
||||
}
|
||||
|
||||
// process stuff
|
||||
if (!shallStop) for (int i=0; i<chans; i++) {
|
||||
// delay effects
|
||||
if (chan[i].rowDelay>0) {
|
||||
if (--chan[i].rowDelay==0) {
|
||||
processRow(i,true);
|
||||
}
|
||||
}
|
||||
|
||||
// retrigger
|
||||
if (chan[i].retrigSpeed) {
|
||||
if (--chan[i].retrigTick<0) {
|
||||
|
|
@ -1655,6 +1678,7 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
|
|||
if (!song.noSlidesOnFirstTick || !firstTick) {
|
||||
if (chan[i].volSpeed!=0) {
|
||||
chan[i].volume=(chan[i].volume&0xff)|(dispatchCmd(DivCommand(DIV_CMD_GET_VOLUME,i))<<8);
|
||||
int preSpeedVol=chan[i].volume;
|
||||
chan[i].volume+=chan[i].volSpeed;
|
||||
if (chan[i].volSpeedTarget!=-1) {
|
||||
bool atTarget=false;
|
||||
|
|
@ -1668,7 +1692,11 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
|
|||
}
|
||||
|
||||
if (atTarget) {
|
||||
chan[i].volume=chan[i].volSpeedTarget;
|
||||
if (chan[i].volSpeed>0) {
|
||||
chan[i].volume=MAX(preSpeedVol,chan[i].volSpeedTarget);
|
||||
} else if (chan[i].volSpeed<0) {
|
||||
chan[i].volume=MIN(preSpeedVol,chan[i].volSpeedTarget);
|
||||
}
|
||||
chan[i].volSpeed=0;
|
||||
chan[i].volSpeedTarget=-1;
|
||||
dispatchCmd(DivCommand(DIV_CMD_HINT_VOLUME,i,chan[i].volume>>8));
|
||||
|
|
|
|||
|
|
@ -285,6 +285,9 @@ int DivSample::getSampleOffset(int offset, int length, DivSampleDepth depth) {
|
|||
case DIV_SAMPLE_DEPTH_IMA_ADPCM:
|
||||
off=(offset+1)/2;
|
||||
break;
|
||||
case DIV_SAMPLE_DEPTH_12BIT:
|
||||
off=((offset*3)+1)/2;
|
||||
break;
|
||||
case DIV_SAMPLE_DEPTH_16BIT:
|
||||
off=offset*2;
|
||||
break;
|
||||
|
|
@ -348,6 +351,10 @@ int DivSample::getSampleOffset(int offset, int length, DivSampleDepth depth) {
|
|||
off=(offset+1)/2;
|
||||
len=(length+1)/2;
|
||||
break;
|
||||
case DIV_SAMPLE_DEPTH_12BIT:
|
||||
off=((offset*3)+1)/2;
|
||||
len=((length*3)+1)/2;
|
||||
break;
|
||||
case DIV_SAMPLE_DEPTH_16BIT:
|
||||
off=offset*2;
|
||||
len=length*2;
|
||||
|
|
@ -409,6 +416,9 @@ int DivSample::getEndPosition(DivSampleDepth depth) {
|
|||
case DIV_SAMPLE_DEPTH_IMA_ADPCM:
|
||||
off=lengthIMA;
|
||||
break;
|
||||
case DIV_SAMPLE_DEPTH_12BIT:
|
||||
off=length12;
|
||||
break;
|
||||
case DIV_SAMPLE_DEPTH_16BIT:
|
||||
off=length16;
|
||||
break;
|
||||
|
|
@ -606,6 +616,12 @@ bool DivSample::initInternal(DivSampleDepth d, int count) {
|
|||
dataIMA=new unsigned char[lengthIMA];
|
||||
memset(dataIMA,0,lengthIMA);
|
||||
break;
|
||||
case DIV_SAMPLE_DEPTH_12BIT: // 12-bit PCM (MultiPCM)
|
||||
if (data12!=NULL) delete[] data12;
|
||||
length12=((count*3)+1)/2;
|
||||
data12=new unsigned char[length12];
|
||||
memset(data12,0,length12);
|
||||
break;
|
||||
case DIV_SAMPLE_DEPTH_16BIT: // 16-bit
|
||||
if (data16!=NULL) delete[] data16;
|
||||
length16=count*2;
|
||||
|
|
@ -1293,6 +1309,14 @@ void DivSample::render(unsigned int formatMask) {
|
|||
case DIV_SAMPLE_DEPTH_IMA_ADPCM: // IMA ADPCM
|
||||
if (adpcm_decode_block(data16,dataIMA,lengthIMA,samples)==0) logE("oh crap!");
|
||||
break;
|
||||
case DIV_SAMPLE_DEPTH_12BIT: // 12-bit PCM (MultiPCM)
|
||||
for (unsigned int i=0, j=0; i<samples; i+=2, j+=3) {
|
||||
data16[i+0]=(data12[j+0]<<8)|(data12[j+1]&0xf0);
|
||||
if (i+1<samples) {
|
||||
data16[i+1]=(data12[j+2]<<8)|((data12[j+1]<<4)&0xf0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
|
@ -1482,6 +1506,16 @@ void DivSample::render(unsigned int formatMask) {
|
|||
adpcm_free_context(codec);
|
||||
}
|
||||
}
|
||||
if (NOT_IN_FORMAT(DIV_SAMPLE_DEPTH_12BIT)) { // 12-bit PCM (MultiPCM)
|
||||
if (!initInternal(DIV_SAMPLE_DEPTH_12BIT,samples)) return;
|
||||
for (unsigned int i=0, j=0; i<samples; i+=2, j+=3) {
|
||||
data12[j+0]=data16[i+0]>>8;
|
||||
data12[j+1]=((data16[i+0]>>4)&0xf)|(i+1<samples?(data16[i+1]>>4)&0xf:0);
|
||||
if (i+1<samples) {
|
||||
data12[j+2]=data16[i+1]>>8;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void* DivSample::getCurBuf() {
|
||||
|
|
@ -1512,6 +1546,8 @@ void* DivSample::getCurBuf() {
|
|||
return dataC219;
|
||||
case DIV_SAMPLE_DEPTH_IMA_ADPCM:
|
||||
return dataIMA;
|
||||
case DIV_SAMPLE_DEPTH_12BIT:
|
||||
return data12;
|
||||
case DIV_SAMPLE_DEPTH_16BIT:
|
||||
return data16;
|
||||
default:
|
||||
|
|
@ -1548,6 +1584,8 @@ unsigned int DivSample::getCurBufLen() {
|
|||
return lengthC219;
|
||||
case DIV_SAMPLE_DEPTH_IMA_ADPCM:
|
||||
return lengthIMA;
|
||||
case DIV_SAMPLE_DEPTH_12BIT:
|
||||
return length12;
|
||||
case DIV_SAMPLE_DEPTH_16BIT:
|
||||
return length16;
|
||||
default:
|
||||
|
|
@ -1662,4 +1700,5 @@ DivSample::~DivSample() {
|
|||
if (dataMuLaw) delete[] dataMuLaw;
|
||||
if (dataC219) delete[] dataC219;
|
||||
if (dataIMA) delete[] dataIMA;
|
||||
if (data12) delete[] data12;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ enum DivSampleDepth: unsigned char {
|
|||
DIV_SAMPLE_DEPTH_MULAW=11,
|
||||
DIV_SAMPLE_DEPTH_C219=12,
|
||||
DIV_SAMPLE_DEPTH_IMA_ADPCM=13,
|
||||
DIV_SAMPLE_DEPTH_12BIT=14,
|
||||
DIV_SAMPLE_DEPTH_16BIT=16,
|
||||
DIV_SAMPLE_DEPTH_MAX // boundary for sample depth
|
||||
};
|
||||
|
|
@ -118,6 +119,7 @@ struct DivSample {
|
|||
// - 11: 8-bit µ-law PCM
|
||||
// - 12: C219 "µ-law" PCM
|
||||
// - 13: IMA ADPCM
|
||||
// - 14: 12-bit PCM (MultiPCM)
|
||||
// - 16: 16-bit PCM
|
||||
DivSampleDepth depth;
|
||||
bool loop, brrEmphasis, brrNoFilter, dither;
|
||||
|
|
@ -144,8 +146,9 @@ struct DivSample {
|
|||
unsigned char* dataMuLaw; // 11
|
||||
unsigned char* dataC219; // 12
|
||||
unsigned char* dataIMA; // 13
|
||||
unsigned char* data12; // 14
|
||||
|
||||
unsigned int length8, length16, length1, lengthDPCM, lengthZ, lengthQSoundA, lengthA, lengthB, lengthK, lengthBRR, lengthVOX, lengthMuLaw, lengthC219, lengthIMA;
|
||||
unsigned int length8, length16, length1, lengthDPCM, lengthZ, lengthQSoundA, lengthA, lengthB, lengthK, lengthBRR, lengthVOX, lengthMuLaw, lengthC219, lengthIMA, length12;
|
||||
|
||||
unsigned int samples;
|
||||
|
||||
|
|
@ -356,6 +359,7 @@ struct DivSample {
|
|||
dataMuLaw(NULL),
|
||||
dataC219(NULL),
|
||||
dataIMA(NULL),
|
||||
data12(NULL),
|
||||
length8(0),
|
||||
length16(0),
|
||||
length1(0),
|
||||
|
|
@ -370,6 +374,7 @@ struct DivSample {
|
|||
lengthMuLaw(0),
|
||||
lengthC219(0),
|
||||
lengthIMA(0),
|
||||
length12(0),
|
||||
samples(0) {
|
||||
for (int i=0; i<DIV_MAX_CHIPS; i++) {
|
||||
for (int j=0; j<DIV_MAX_SAMPLE_TYPE; j++) {
|
||||
|
|
|
|||
|
|
@ -141,6 +141,8 @@ enum DivSystem {
|
|||
DIV_SYSTEM_5E01,
|
||||
DIV_SYSTEM_BIFURCATOR,
|
||||
DIV_SYSTEM_SID2,
|
||||
DIV_SYSTEM_SUPERVISION,
|
||||
DIV_SYSTEM_UPD1771C,
|
||||
DIV_SYSTEM_SID3,
|
||||
|
||||
DIV_SYSTEM_MAX
|
||||
|
|
|
|||
|
|
@ -602,6 +602,25 @@ void DivEngine::registerSystems() {
|
|||
{0x5b, {DIV_CMD_FM_KSR, _("5Bxy: Set whether key will scale envelope (x: operator from 1 to 4 (0 for all ops); y: enabled)"), effectOpVal<4>, effectValAnd<1>}},
|
||||
};
|
||||
|
||||
EffectHandlerMap fmOPL4PostEffectHandlerMap(fmOPLPostEffectHandlerMap);
|
||||
fmOPL4PostEffectHandlerMap.insert({
|
||||
{0x1e, {DIV_CMD_MULTIPCM_MIX_FM, _("1Exy: FM global level (x: left, y: right; 0 to 7)"), effectVal}},
|
||||
{0x1f, {DIV_CMD_MULTIPCM_MIX_PCM, _("1Fxy: PCM global level (x: left, y: right; 0 to 7)"), effectVal}},
|
||||
{0x20, {DIV_CMD_MULTIPCM_LFO, _("20xx: PCM LFO Rate (0 to 7)"), effectValAnd<7>}},
|
||||
{0x21, {DIV_CMD_MULTIPCM_VIB, _("21xx: PCM LFO PM Depth (0 to 7)"), effectValAnd<7>}},
|
||||
{0x22, {DIV_CMD_MULTIPCM_AM, _("22xx: PCM LFO AM Depth (0 to 7)"), effectValAnd<7>}},
|
||||
{0x23, {DIV_CMD_MULTIPCM_AR, _("23xx: PCM Attack Rate (0 to 15)"), effectValAnd<15>}},
|
||||
{0x24, {DIV_CMD_MULTIPCM_D1R, _("24xx: PCM Decay 1 Rate (0 to 15)"), effectValAnd<15>}},
|
||||
{0x25, {DIV_CMD_MULTIPCM_DL, _("25xx: PCM Decay Level (0 to 15)"), effectValAnd<15>}},
|
||||
{0x26, {DIV_CMD_MULTIPCM_D2R, _("26xx: PCM Decay 2 Rate (0 to 15)"), effectValAnd<15>}},
|
||||
{0x27, {DIV_CMD_MULTIPCM_RR, _("27xx: PCM Release Rate (0 to 15)"), effectValAnd<15>}},
|
||||
{0x28, {DIV_CMD_MULTIPCM_RC, _("28xx: PCM Rate Correction (0 to 15)"), effectValAnd<15>}},
|
||||
{0x2c, {DIV_CMD_MULTIPCM_DAMP, _("2Cxx: PCM Damp"), effectValAnd<1>}},
|
||||
{0x2d, {DIV_CMD_MULTIPCM_PSEUDO_REVERB, _("2Dxx: PCM Pseudo Reverb"), effectValAnd<1>}},
|
||||
{0x2e, {DIV_CMD_MULTIPCM_LFO_RESET, _("2Exx: PCM LFO Reset"), effectValAnd<1>}},
|
||||
{0x2f, {DIV_CMD_MULTIPCM_LEVEL_DIRECT, _("2Fxx: PCM Level Direct"), effectValAnd<1>}},
|
||||
});
|
||||
|
||||
EffectHandlerMap c64PostEffectHandlerMap={
|
||||
{0x10, {DIV_CMD_WAVE, _("10xx: Set waveform (bit 0: triangle; bit 1: saw; bit 2: pulse; bit 3: noise)")}},
|
||||
{0x11, {DIV_CMD_C64_CUTOFF, _("11xx: Set coarse cutoff (not recommended; use 4xxx instead)")}},
|
||||
|
|
@ -1709,24 +1728,28 @@ void DivEngine::registerSystems() {
|
|||
);
|
||||
|
||||
// to Grauw: feel free to change this to 24 during development of OPL4's PCM part.
|
||||
// TODO: add 12-bit and 16-bit big-endian sample formats
|
||||
sysDefs[DIV_SYSTEM_OPL4]=new DivSysDef(
|
||||
_("Yamaha YMF278B (OPL4)"), NULL, 0xae, 0, 42, true, true, 0, false, (1U<<DIV_SAMPLE_DEPTH_8BIT)|(1U<<DIV_SAMPLE_DEPTH_16BIT), 0, 0,
|
||||
_("Yamaha YMF278B (OPL4)"), NULL, 0xae, 0, 42, true, true, 0x151, false, (1U<<DIV_SAMPLE_DEPTH_8BIT)|(1U<<DIV_SAMPLE_DEPTH_12BIT)|(1U<<DIV_SAMPLE_DEPTH_16BIT), 0, 0,
|
||||
_("like OPL3, but this time it also has a 24-channel version of MultiPCM."),
|
||||
{_("4OP 1"), _("FM 2"), _("4OP 3"), _("FM 4"), _("4OP 5"), _("FM 6"), _("4OP 7"), _("FM 8"), _("4OP 9"), _("FM 10"), _("4OP 11"), _("FM 12"), _("FM 13"), _("FM 14"), _("FM 15"), _("FM 16"), _("FM 17"), _("FM 18"), _("PCM 1"), _("PCM 2"), _("PCM 3"), _("PCM 4"), _("PCM 5"), _("PCM 6"), _("PCM 7"), _("PCM 8"), _("PCM 9"), _("PCM 10"), _("PCM 11"), _("PCM 12"), _("PCM 13"), _("PCM 14"), _("PCM 15"), _("PCM 16"), _("PCM 17"), _("PCM 18"), _("PCM 19"), _("PCM 20"), _("PCM 21"), _("PCM 22"), _("PCM 23"), _("PCM 24")},
|
||||
{"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "F13", "F14", "F15", "F16", "F17", "F18", "P1", "P2", "P3", "P4", "P5", "P6", "P7", "P8", "P8", "P10", "P11", "P12", "P13", "P14", "P15", "P16", "P17", "P18", "P19", "P20", "P21", "P22", "P23", "P24"},
|
||||
{"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "F13", "F14", "F15", "F16", "F17", "F18", "P1", "P2", "P3", "P4", "P5", "P6", "P7", "P8", "P9", "P10", "P11", "P12", "P13", "P14", "P15", "P16", "P17", "P18", "P19", "P20", "P21", "P22", "P23", "P24"},
|
||||
{DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM},
|
||||
{DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM}
|
||||
{DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM},
|
||||
{DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA},
|
||||
fmEffectHandlerMap,
|
||||
fmOPL4PostEffectHandlerMap
|
||||
);
|
||||
|
||||
// TODO: same here
|
||||
sysDefs[DIV_SYSTEM_OPL4_DRUMS]=new DivSysDef(
|
||||
_("Yamaha YMF278B (OPL4) with drums"), NULL, 0xaf, 0, 44, true, true, 0, false, (1U<<DIV_SAMPLE_DEPTH_8BIT)|(1U<<DIV_SAMPLE_DEPTH_16BIT), 0, 0,
|
||||
_("Yamaha YMF278B (OPL4) with drums"), NULL, 0xaf, 0, 44, true, true, 0x151, false, (1U<<DIV_SAMPLE_DEPTH_8BIT)|(1U<<DIV_SAMPLE_DEPTH_12BIT)|(1U<<DIV_SAMPLE_DEPTH_16BIT), 0, 0,
|
||||
_("the OPL4 but with drums mode turned on."),
|
||||
{_("4OP 1"), _("FM 2"), _("4OP 3"), _("FM 4"), _("4OP 5"), _("FM 6"), _("4OP 7"), _("FM 8"), _("4OP 9"), _("FM 10"), _("4OP 11"), _("FM 12"), _("FM 13"), _("FM 14"), _("FM 15"), _("Kick/FM 16"), _("Snare"), _("Tom"), _("Top"), _("HiHat"), _("PCM 1"), _("PCM 2"), _("PCM 3"), _("PCM 4"), _("PCM 5"), _("PCM 6"), _("PCM 7"), _("PCM 8"), _("PCM 9"), _("PCM 10"), _("PCM 11"), _("PCM 12"), _("PCM 13"), _("PCM 14"), _("PCM 15"), _("PCM 16"), _("PCM 17"), _("PCM 18"), _("PCM 19"), _("PCM 20"), _("PCM 21"), _("PCM 22"), _("PCM 23"), _("PCM 24")},
|
||||
{"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "F13", "F14", "F15", "BD", "SD", "TM", "TP", "HH", "P1", "P2", "P3", "P4", "P5", "P6", "P7", "P8", "P8", "P10", "P11", "P12", "P13", "P14", "P15", "P16", "P17", "P18", "P19", "P20", "P21", "P22", "P23", "P24"},
|
||||
{"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "F13", "F14", "F15", "BD", "SD", "TM", "TP", "HH", "P1", "P2", "P3", "P4", "P5", "P6", "P7", "P8", "P9", "P10", "P11", "P12", "P13", "P14", "P15", "P16", "P17", "P18", "P19", "P20", "P21", "P22", "P23", "P24"},
|
||||
{DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM},
|
||||
{DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM}
|
||||
{DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM},
|
||||
{DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA},
|
||||
fmOPLDrumsEffectHandlerMap,
|
||||
fmOPL4PostEffectHandlerMap
|
||||
);
|
||||
|
||||
EffectHandlerMap es5506PreEffectHandlerMap={
|
||||
|
|
@ -2015,6 +2038,33 @@ void DivEngine::registerSystems() {
|
|||
{DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA}
|
||||
);
|
||||
|
||||
sysDefs[DIV_SYSTEM_SUPERVISION]=new DivSysDef(
|
||||
_("Watara Supervision"), NULL, 0xe3, 0, 4, false, true, 0, false, 1U<<DIV_SAMPLE_DEPTH_8BIT, 0, 0,
|
||||
_("a handheld that was trying to compete with the Game Boy, but it never succeded."),
|
||||
{_("Pulse 1"), _("Pulse 2"), _("PCM"), _("Noise")},
|
||||
{"S1", "S2", "PCM", "NO"},
|
||||
{DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM, DIV_CH_NOISE},
|
||||
{DIV_INS_SUPERVISION, DIV_INS_SUPERVISION, DIV_INS_SUPERVISION, DIV_INS_SUPERVISION},
|
||||
{DIV_INS_SUPERVISION, DIV_INS_SUPERVISION, DIV_INS_AMIGA, DIV_INS_SUPERVISION},
|
||||
{
|
||||
{0x12, {DIV_CMD_STD_NOISE_MODE, _("12xx: Set duty cycle/noise mode (pulse: 0 to 3; noise: 0 or 1)")}},
|
||||
}
|
||||
);
|
||||
|
||||
sysDefs[DIV_SYSTEM_UPD1771C]=new DivSysDef(
|
||||
_("NEC μPD1771C"), NULL, 0xe4, 0, 1, false, true, 0, false, 0, 0, 0,
|
||||
_("this was an SoC with some funky wavetable/noise hardware"),
|
||||
{_("Wave/Noise")},
|
||||
{"W"},
|
||||
{DIV_CH_NOISE},
|
||||
{DIV_INS_UPD1771C},
|
||||
{},
|
||||
{
|
||||
{0x10, {DIV_CMD_STD_NOISE_MODE, _("10xx: Set duty/waveform (bit 0-3: waveform; bit 4: mode)")}},
|
||||
{0x12, {DIV_CMD_N163_WAVE_POSITION, _("12xx: Set waveform position (0-31)")}},
|
||||
}
|
||||
);
|
||||
|
||||
sysDefs[DIV_SYSTEM_SM8521]=new DivSysDef(
|
||||
_("Sharp SM8521"), NULL, 0xc8, 0, 3, false, true, 0, false, 0, 32, 16,
|
||||
_("a SoC with wavetable sound hardware."),
|
||||
|
|
|
|||
|
|
@ -617,6 +617,96 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
|
|||
w->writeC(0);
|
||||
}
|
||||
break;
|
||||
case DIV_SYSTEM_OPL4:
|
||||
case DIV_SYSTEM_OPL4_DRUMS:
|
||||
// disable envelope
|
||||
for (int i=0; i<6; i++) {
|
||||
w->writeC(0xd0);
|
||||
w->writeC(0x00|baseAddr2);
|
||||
w->writeC(0x80+i);
|
||||
w->writeC(0x0f);
|
||||
w->writeC(0xd0);
|
||||
w->writeC(0x00|baseAddr2);
|
||||
w->writeC(0x88+i);
|
||||
w->writeC(0x0f);
|
||||
w->writeC(0xd0);
|
||||
w->writeC(0x00|baseAddr2);
|
||||
w->writeC(0x90+i);
|
||||
w->writeC(0x0f);
|
||||
w->writeC(0xd0);
|
||||
w->writeC(0x01|baseAddr2);
|
||||
w->writeC(0x80+i);
|
||||
w->writeC(0x0f);
|
||||
w->writeC(0xd0);
|
||||
w->writeC(0x01|baseAddr2);
|
||||
w->writeC(0x88+i);
|
||||
w->writeC(0x0f);
|
||||
w->writeC(0xd0);
|
||||
w->writeC(0x01|baseAddr2);
|
||||
w->writeC(0x90+i);
|
||||
w->writeC(0x0f);
|
||||
}
|
||||
for (int i=0; i<24; i++) {
|
||||
w->writeC(0xd0);
|
||||
w->writeC(0x02|baseAddr2);
|
||||
w->writeC(0x80+i);
|
||||
w->writeC(0x00);
|
||||
w->writeC(0xd0);
|
||||
w->writeC(0x02|baseAddr2);
|
||||
w->writeC(0x98+i);
|
||||
w->writeC(0x00);
|
||||
w->writeC(0xd0);
|
||||
w->writeC(0x02|baseAddr2);
|
||||
w->writeC(0xb0+i);
|
||||
w->writeC(0x00);
|
||||
w->writeC(0xd0);
|
||||
w->writeC(0x02|baseAddr2);
|
||||
w->writeC(0xc8+i);
|
||||
w->writeC(0x00);
|
||||
w->writeC(0xd0);
|
||||
w->writeC(0x02|baseAddr2);
|
||||
w->writeC(0xe0+i);
|
||||
w->writeC(0x00);
|
||||
}
|
||||
// key off + freq reset
|
||||
for (int i=0; i<9; i++) {
|
||||
w->writeC(0xd0);
|
||||
w->writeC(0x00|baseAddr2);
|
||||
w->writeC(0xa0+i);
|
||||
w->writeC(0);
|
||||
w->writeC(0xd0);
|
||||
w->writeC(0x00|baseAddr2);
|
||||
w->writeC(0xb0+i);
|
||||
w->writeC(0);
|
||||
w->writeC(0xd0);
|
||||
w->writeC(0x01|baseAddr2);
|
||||
w->writeC(0xa0+i);
|
||||
w->writeC(0);
|
||||
w->writeC(0xd0);
|
||||
w->writeC(0x01|baseAddr2);
|
||||
w->writeC(0xb0+i);
|
||||
w->writeC(0);
|
||||
}
|
||||
for (int i=0; i<24; i++) {
|
||||
w->writeC(0xd0);
|
||||
w->writeC(0x02|baseAddr2);
|
||||
w->writeC(0x20+i);
|
||||
w->writeC(0);
|
||||
w->writeC(0xd0);
|
||||
w->writeC(0x02|baseAddr2);
|
||||
w->writeC(0x38+i);
|
||||
w->writeC(0);
|
||||
w->writeC(0xd0);
|
||||
w->writeC(0x02|baseAddr2);
|
||||
w->writeC(0x68+i);
|
||||
w->writeC(8);
|
||||
}
|
||||
// reset 4-op
|
||||
w->writeC(0xd0);
|
||||
w->writeC(0x01|baseAddr2);
|
||||
w->writeC(0x04);
|
||||
w->writeC(0x00);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
@ -1096,6 +1186,13 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
|
|||
w->writeS_BE(baseAddr2S|(write.addr&0x1ff));
|
||||
w->writeC(write.val&0xff);
|
||||
break;
|
||||
case DIV_SYSTEM_OPL4:
|
||||
case DIV_SYSTEM_OPL4_DRUMS:
|
||||
w->writeC(0xd0);
|
||||
w->writeC(((write.addr>>8)&0x7f)|baseAddr2);
|
||||
w->writeC(write.addr&0xff);
|
||||
w->writeC(write.val);
|
||||
break;
|
||||
default:
|
||||
logW("write not handled!");
|
||||
break;
|
||||
|
|
@ -1276,6 +1373,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
|||
DivDispatch* writeC140[2]={NULL,NULL};
|
||||
DivDispatch* writeC219[2]={NULL,NULL};
|
||||
DivDispatch* writeNES[2]={NULL,NULL};
|
||||
DivDispatch* writePCM_OPL4[2]={NULL,NULL};
|
||||
|
||||
int writeNESIndex[2]={0,0};
|
||||
|
||||
|
|
@ -1874,6 +1972,22 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
|||
howManyChips++;
|
||||
}
|
||||
break;
|
||||
case DIV_SYSTEM_OPL4:
|
||||
case DIV_SYSTEM_OPL4_DRUMS:
|
||||
if (!hasOPL4) {
|
||||
hasOPL4=disCont[i].dispatch->chipClock;
|
||||
CHIP_VOL(13,1.0);
|
||||
willExport[i]=true;
|
||||
writePCM_OPL4[0]=disCont[i].dispatch;
|
||||
} else if (!(hasOPL4&0x40000000)) {
|
||||
isSecond[i]=true;
|
||||
CHIP_VOL_SECOND(13,1.0);
|
||||
willExport[i]=true;
|
||||
writePCM_OPL4[1]=disCont[i].dispatch;
|
||||
hasOPL4|=0x40000000;
|
||||
howManyChips++;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
@ -2209,6 +2323,16 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
|||
w->write(sampleMem,sampleMemLen);
|
||||
delete[] sampleMem;
|
||||
}
|
||||
// PCM (OPL4)
|
||||
if (writePCM_OPL4[i]!=NULL && writePCM_OPL4[i]->getSampleMemUsage(0)>0) {
|
||||
w->writeC(0x67);
|
||||
w->writeC(0x66);
|
||||
w->writeC(0x84);
|
||||
w->writeI((writePCM_OPL4[i]->getSampleMemUsage(0)+8)|(i*0x80000000));
|
||||
w->writeI(writePCM_OPL4[i]->getSampleMemCapacity(0));
|
||||
w->writeI(0);
|
||||
w->write(writePCM_OPL4[i]->getSampleMem(0),writePCM_OPL4[i]->getSampleMemUsage(0));
|
||||
}
|
||||
}
|
||||
|
||||
for (int i=0; i<2; i++) {
|
||||
|
|
|
|||
|
|
@ -300,6 +300,7 @@ const char* aboutLine[]={
|
|||
_N("PowerNoise emulator by scratchminer"),
|
||||
_N("ep128emu by Istvan Varga"),
|
||||
_N("NDS sound emulator by cam900"),
|
||||
_N("openMSX YMF278 emulator (modified version) by the openMSX developers"),
|
||||
_N("SID2 emulator by LTVA (modification of reSID emulator)"),
|
||||
_N("SID3 emulator by LTVA"),
|
||||
"",
|
||||
|
|
|
|||
|
|
@ -69,6 +69,9 @@ void FurnaceGUI::startSelection(int xCoarse, int xFine, int y, bool fullRow) {
|
|||
selStart.y=y;
|
||||
selEnd.y=y;
|
||||
} else {
|
||||
if (xCoarse!=cursor.xCoarse || y!=cursor.y) {
|
||||
makeCursorUndo();
|
||||
}
|
||||
cursor.xCoarse=xCoarse;
|
||||
cursor.xFine=xFine;
|
||||
cursor.y=y;
|
||||
|
|
@ -208,6 +211,9 @@ void FurnaceGUI::finishSelection() {
|
|||
}
|
||||
|
||||
void FurnaceGUI::moveCursor(int x, int y, bool select) {
|
||||
if (y>=editStepCoarse || y<=-editStepCoarse || x<=-5 || x>=5 ) {
|
||||
makeCursorUndo();
|
||||
}
|
||||
if (!select) {
|
||||
finishSelection();
|
||||
}
|
||||
|
|
@ -326,6 +332,7 @@ void FurnaceGUI::moveCursor(int x, int y, bool select) {
|
|||
}
|
||||
|
||||
void FurnaceGUI::moveCursorPrevChannel(bool overflow) {
|
||||
makeCursorUndo();
|
||||
finishSelection();
|
||||
curNibble=false;
|
||||
|
||||
|
|
@ -354,6 +361,7 @@ void FurnaceGUI::moveCursorPrevChannel(bool overflow) {
|
|||
}
|
||||
|
||||
void FurnaceGUI::moveCursorNextChannel(bool overflow) {
|
||||
makeCursorUndo();
|
||||
finishSelection();
|
||||
curNibble=false;
|
||||
|
||||
|
|
@ -382,6 +390,7 @@ void FurnaceGUI::moveCursorNextChannel(bool overflow) {
|
|||
}
|
||||
|
||||
void FurnaceGUI::moveCursorTop(bool select) {
|
||||
makeCursorUndo();
|
||||
if (!select) {
|
||||
finishSelection();
|
||||
}
|
||||
|
|
@ -403,6 +412,7 @@ void FurnaceGUI::moveCursorTop(bool select) {
|
|||
}
|
||||
|
||||
void FurnaceGUI::moveCursorBottom(bool select) {
|
||||
makeCursorUndo();
|
||||
if (!select) {
|
||||
finishSelection();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@
|
|||
#include "../engine/platform/pcmdac.h"
|
||||
#include "../engine/platform/k007232.h"
|
||||
#include "../engine/platform/ga20.h"
|
||||
#include "../engine/platform/supervision.h"
|
||||
#include "../engine/platform/sm8521.h"
|
||||
#include "../engine/platform/pv1000.h"
|
||||
#include "../engine/platform/k053260.h"
|
||||
|
|
|
|||
|
|
@ -731,6 +731,25 @@ void FurnaceGUI::drawDebug() {
|
|||
ImGui::Text("result: %.0f%%",realVol*100.0f);
|
||||
ImGui::TreePop();
|
||||
}
|
||||
if (ImGui::TreeNode("Cursor Undo Debug")) {
|
||||
auto DrawSpot=[&](const CursorJumpPoint& spot) {
|
||||
ImGui::Text("[%d:%d] <%d:%d, %d>", spot.subSong, spot.order, spot.point.xCoarse, spot.point.xFine, spot.point.y);
|
||||
};
|
||||
if (ImGui::BeginChild("##CursorUndoDebugChild", ImVec2(0, 300), true)) {
|
||||
if (ImGui::BeginTable("##CursorUndoDebug", 2, ImGuiTableFlags_Borders|ImGuiTableFlags_SizingStretchSame)) {
|
||||
for (size_t row=0; row<MAX(cursorUndoHist.size(),cursorRedoHist.size()); ++row) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
if (row<cursorUndoHist.size()) DrawSpot(cursorUndoHist[cursorUndoHist.size()-row-1]);
|
||||
ImGui::TableNextColumn();
|
||||
if (row<cursorRedoHist.size()) DrawSpot(cursorRedoHist[cursorRedoHist.size()-row-1]);
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
ImGui::TreePop();
|
||||
}
|
||||
if (ImGui::TreeNode("User Interface")) {
|
||||
if (ImGui::Button("Inspect")) {
|
||||
inspectorOpen=!inspectorOpen;
|
||||
|
|
|
|||
|
|
@ -680,10 +680,15 @@ void FurnaceGUI::doAction(int what) {
|
|||
latchTarget=0;
|
||||
latchNibble=false;
|
||||
break;
|
||||
case GUI_ACTION_PAT_ABSORB_INSTRUMENT: {
|
||||
case GUI_ACTION_PAT_ABSORB_INSTRUMENT:
|
||||
doAbsorbInstrument();
|
||||
break;
|
||||
}
|
||||
case GUI_ACTION_PAT_CURSOR_UNDO:
|
||||
doCursorUndo();
|
||||
break;
|
||||
case GUI_ACTION_PAT_CURSOR_REDO:
|
||||
doCursorRedo();
|
||||
break;
|
||||
|
||||
case GUI_ACTION_INS_LIST_ADD:
|
||||
if (settings.insTypeMenu) {
|
||||
|
|
|
|||
|
|
@ -678,6 +678,7 @@ void FurnaceGUI::doPasteFurnace(PasteMode mode, int arg, bool readClipboard, Str
|
|||
|
||||
if (readClipboard) {
|
||||
if (settings.cursorPastePos) {
|
||||
makeCursorUndo();
|
||||
cursor.y=j;
|
||||
if (cursor.y>=e->curSubSong->patLen) cursor.y=e->curSubSong->patLen-1;
|
||||
selStart=cursor;
|
||||
|
|
@ -1220,6 +1221,7 @@ void FurnaceGUI::doPasteMPT(PasteMode mode, int arg, bool readClipboard, String
|
|||
|
||||
if (readClipboard) {
|
||||
if (settings.cursorPastePos) {
|
||||
makeCursorUndo();
|
||||
cursor.y=j;
|
||||
if (cursor.y>=e->curSubSong->patLen) cursor.y=e->curSubSong->patLen-1;
|
||||
selStart=cursor;
|
||||
|
|
@ -1846,8 +1848,11 @@ void FurnaceGUI::doAbsorbInstrument() {
|
|||
}
|
||||
|
||||
// absorb most recent octave (i.e. set curOctave such that the "main row" (QWERTY) of
|
||||
// notes will result in an octave number equal to the previous note).
|
||||
if (!foundOctave && pat->data[i][0] != 0) {
|
||||
// notes will result in an octave number equal to the previous note). make sure to
|
||||
// skip "special note values" like OFF/REL/=== and "none", since there won't be valid
|
||||
// octave values
|
||||
unsigned char note=pat->data[i][0];
|
||||
if (!foundOctave && note!=0 && note!=100 && note!=101 && note!=102) {
|
||||
foundOctave=true;
|
||||
|
||||
// decode octave data (was signed cast to unsigned char)
|
||||
|
|
@ -2058,3 +2063,52 @@ void FurnaceGUI::doRedo() {
|
|||
|
||||
redoHist.pop_back();
|
||||
}
|
||||
|
||||
CursorJumpPoint FurnaceGUI::getCurrentCursorJumpPoint() {
|
||||
return CursorJumpPoint(cursor, curOrder, e->getCurrentSubSong());
|
||||
}
|
||||
|
||||
void FurnaceGUI::applyCursorJumpPoint(const CursorJumpPoint& spot) {
|
||||
cursor=spot.point;
|
||||
curOrder=MIN(e->curSubSong->ordersLen-1, spot.order);
|
||||
e->setOrder(curOrder);
|
||||
e->changeSongP(spot.subSong);
|
||||
if (!settings.cursorMoveNoScroll) {
|
||||
updateScroll(cursor.y);
|
||||
}
|
||||
}
|
||||
|
||||
void FurnaceGUI::makeCursorUndo() {
|
||||
CursorJumpPoint spot = getCurrentCursorJumpPoint();
|
||||
if (!cursorUndoHist.empty() && spot == cursorUndoHist.back()) return;
|
||||
|
||||
if (cursorUndoHist.size()>=settings.maxUndoSteps) cursorUndoHist.pop_front();
|
||||
cursorUndoHist.push_back(spot);
|
||||
|
||||
// redo history no longer relevant, we've changed timeline
|
||||
cursorRedoHist.clear();
|
||||
}
|
||||
|
||||
void FurnaceGUI::doCursorUndo() {
|
||||
if (cursorUndoHist.empty()) return;
|
||||
|
||||
// allow returning to current spot
|
||||
if (cursorRedoHist.size()>=settings.maxUndoSteps) cursorRedoHist.pop_front();
|
||||
cursorRedoHist.push_back(getCurrentCursorJumpPoint());
|
||||
|
||||
// apply spot
|
||||
applyCursorJumpPoint(cursorUndoHist.back());
|
||||
cursorUndoHist.pop_back();
|
||||
}
|
||||
|
||||
void FurnaceGUI::doCursorRedo() {
|
||||
if (cursorRedoHist.empty()) return;
|
||||
|
||||
// allow returning to current spot
|
||||
if (cursorUndoHist.size()>=settings.maxUndoSteps) cursorUndoHist.pop_front();
|
||||
cursorUndoHist.push_back(getCurrentCursorJumpPoint());
|
||||
|
||||
// apply spot
|
||||
applyCursorJumpPoint(cursorRedoHist.back());
|
||||
cursorRedoHist.pop_back();
|
||||
}
|
||||
|
|
@ -560,6 +560,7 @@ void FurnaceGUI::drawFindReplace() {
|
|||
if (ImGui::TableNextColumn()) {
|
||||
snprintf(tempID,1024,ICON_FA_CHEVRON_RIGHT "##_FR%d",index);
|
||||
if (ImGui::Selectable(tempID)) {
|
||||
makeCursorUndo();
|
||||
e->changeSongP(i.subsong);
|
||||
if (e->isPlaying()) {
|
||||
followPattern=false;
|
||||
|
|
|
|||
|
|
@ -388,6 +388,7 @@ void FurnaceGUI::decodeMMLStr(String& source, int* macro, unsigned char& macroLe
|
|||
setBit30=false;
|
||||
macroLen++;
|
||||
buf=0;
|
||||
MARK_MODIFIED;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -424,6 +425,21 @@ void FurnaceGUI::decodeMMLStr(String& source, int* macro, unsigned char& macroLe
|
|||
} \
|
||||
}
|
||||
|
||||
bool FurnaceGUI::isCtrlWheelModifierHeld() const {
|
||||
switch (settings.ctrlWheelModifier) {
|
||||
case 0:
|
||||
return ImGui::IsKeyDown(ImGuiMod_Ctrl) || ImGui::IsKeyDown(ImGuiMod_Super);
|
||||
case 1:
|
||||
return ImGui::IsKeyDown(ImGuiMod_Ctrl);
|
||||
case 2:
|
||||
return ImGui::IsKeyDown(ImGuiMod_Super);
|
||||
case 3:
|
||||
return ImGui::IsKeyDown(ImGuiMod_Alt);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool FurnaceGUI::CWSliderScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags) {
|
||||
flags^=ImGuiSliderFlags_AlwaysClamp;
|
||||
if (ImGui::SliderScalar(label,data_type,p_data,p_min,p_max,format,flags)) {
|
||||
|
|
@ -993,11 +1009,6 @@ Pos=339,177\n\
|
|||
Size=601,400\n\
|
||||
Collapsed=0\n\
|
||||
\n\
|
||||
[Window][Rendering...]\n\
|
||||
Pos=585,342\n\
|
||||
Size=600,100\n\
|
||||
Collapsed=0\n\
|
||||
\n\
|
||||
[Window][Export VGM##FileDialog]\n\
|
||||
Pos=340,177\n\
|
||||
Size=600,400\n\
|
||||
|
|
@ -1216,6 +1227,7 @@ void FurnaceGUI::play(int row) {
|
|||
memset(chanOscBright,0,DIV_MAX_CHANS*sizeof(float));
|
||||
e->walkSong(loopOrder,loopRow,loopEnd);
|
||||
memset(lastIns,-1,sizeof(int)*DIV_MAX_CHANS);
|
||||
if (followPattern) makeCursorUndo();
|
||||
if (!followPattern) e->setOrder(curOrder);
|
||||
if (row>0) {
|
||||
if (!e->playToRow(row)) {
|
||||
|
|
@ -5859,7 +5871,8 @@ bool FurnaceGUI::loop() {
|
|||
MEASURE_BEGIN(popup);
|
||||
|
||||
centerNextWindow(_("Rendering..."),canvasW,canvasH);
|
||||
if (ImGui::BeginPopupModal(_("Rendering..."),NULL,ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoMove)) {
|
||||
// ImGui::SetNextWindowSize(ImVec2(0.0f,0.0f));
|
||||
if (ImGui::BeginPopupModal(_("Rendering..."),NULL,ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoSavedSettings)) {
|
||||
// WHAT the HELL?!
|
||||
WAKE_UP;
|
||||
if (audioExportOptions.mode!=DIV_EXPORT_MODE_MANY_CHAN) {
|
||||
|
|
@ -5875,35 +5888,32 @@ bool FurnaceGUI::loop() {
|
|||
int curFile=0;
|
||||
int* curFileLambda=&curFile;
|
||||
if (e->isExporting()) {
|
||||
e->lockEngine([this, progressLambda, curPosInRowsLambda, curFileLambda,
|
||||
loopsLeftLambda, totalLoopsLambda] () {
|
||||
int curRow=0; int curOrder=0;
|
||||
e->getCurSongPos(curRow, curOrder); *curFileLambda=0;
|
||||
e->getCurFileIndex(*curFileLambda);
|
||||
*curPosInRowsLambda=curRow; for (int i=0; i<curOrder;
|
||||
i++) {
|
||||
*curPosInRowsLambda+=songOrdersLengths[i];}
|
||||
|
||||
if (!songHasSongEndCommand) {
|
||||
e->getLoopsLeft(*loopsLeftLambda); e->getTotalLoops(*totalLoopsLambda); if ((*totalLoopsLambda)!=(*loopsLeftLambda)) //we are going 2nd, 3rd, etc. time through the song
|
||||
{
|
||||
*curPosInRowsLambda-=(songLength-songLoopedSectionLength); //a hack so progress bar does not jump?
|
||||
}
|
||||
if (e->getIsFadingOut()) //we are in fadeout??? why it works like that bruh
|
||||
{
|
||||
// LIVE WITH IT damn it
|
||||
*curPosInRowsLambda-=(songLength-songLoopedSectionLength); //a hack so progress bar does not jump?
|
||||
}
|
||||
}
|
||||
// this horrible indentation courtesy of `indent`
|
||||
*progressLambda=(float) ((*curPosInRowsLambda) + ((*totalLoopsLambda)- (*loopsLeftLambda)) * songLength + lengthOfOneFile * (*curFileLambda)) / (float) totalLength;});
|
||||
e->lockEngine(
|
||||
[this, progressLambda, curPosInRowsLambda, curFileLambda, loopsLeftLambda, totalLoopsLambda] () {
|
||||
int curRow=0; int curOrder=0;
|
||||
e->getCurSongPos(curRow, curOrder);
|
||||
*curFileLambda=0;
|
||||
e->getCurFileIndex(*curFileLambda);
|
||||
*curPosInRowsLambda=curRow;
|
||||
for (int i=0; i<curOrder; i++) *curPosInRowsLambda+=songOrdersLengths[i];
|
||||
if (!songHasSongEndCommand) {
|
||||
e->getLoopsLeft(*loopsLeftLambda);
|
||||
e->getTotalLoops(*totalLoopsLambda);
|
||||
if ((*totalLoopsLambda)!=(*loopsLeftLambda)) { // we are going 2nd, 3rd, etc. time through the song
|
||||
*curPosInRowsLambda-=(songLength-songLoopedSectionLength); // a hack so progress bar does not jump?
|
||||
}
|
||||
if (e->getIsFadingOut()) { // we are in fadeout??? why it works like that bruh
|
||||
// LIVE WITH IT damn it
|
||||
*curPosInRowsLambda-=(songLength-songLoopedSectionLength); // a hack so progress bar does not jump?
|
||||
}
|
||||
}
|
||||
*progressLambda=(float)((*curPosInRowsLambda)+((*totalLoopsLambda)-(*loopsLeftLambda))*songLength+lengthOfOneFile*(*curFileLambda))/(float)totalLength;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
ImGui::Text(_("Row %d of %d"),curPosInRows+((totalLoops)-(loopsLeft))*songLength,lengthOfOneFile);
|
||||
|
||||
if (audioExportOptions.mode==DIV_EXPORT_MODE_MANY_CHAN) {
|
||||
ImGui::Text(_("Channel %d of %d"),curFile+1,totalFiles);
|
||||
}
|
||||
if (audioExportOptions.mode==DIV_EXPORT_MODE_MANY_CHAN) ImGui::Text(_("Channel %d of %d"),curFile+1,totalFiles);
|
||||
|
||||
ImGui::ProgressBar(curProgress,ImVec2(320.0f*dpiScale,0),fmt::sprintf("%.2f%%",curProgress*100.0f).c_str());
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@
|
|||
#define FURNACE_APP_ID "org.tildearrow.furnace"
|
||||
|
||||
#define rightClickable if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) ImGui::SetKeyboardFocusHere(-1);
|
||||
#define ctrlWheeling ((ImGui::IsKeyDown(ImGuiKey_LeftCtrl) || ImGui::IsKeyDown(ImGuiKey_RightCtrl)) && wheelY!=0)
|
||||
#define ctrlWheeling (isCtrlWheelModifierHeld() && wheelY!=0)
|
||||
|
||||
#define handleUnimportant if (settings.insFocusesPattern && patternOpen) {nextWindow=GUI_WINDOW_PATTERN;}
|
||||
#define unimportant(x) if (x) {handleUnimportant}
|
||||
|
|
@ -354,6 +354,8 @@ enum FurnaceGUIColors {
|
|||
GUI_COLOR_INSTR_GBA_MINMOD,
|
||||
GUI_COLOR_INSTR_BIFURCATOR,
|
||||
GUI_COLOR_INSTR_SID2,
|
||||
GUI_COLOR_INSTR_SUPERVISION,
|
||||
GUI_COLOR_INSTR_UPD1771C,
|
||||
GUI_COLOR_INSTR_SID3,
|
||||
GUI_COLOR_INSTR_UNKNOWN,
|
||||
|
||||
|
|
@ -819,6 +821,8 @@ enum FurnaceGUIActions {
|
|||
GUI_ACTION_PAT_SCROLL_MODE,
|
||||
GUI_ACTION_PAT_CLEAR_LATCH,
|
||||
GUI_ACTION_PAT_ABSORB_INSTRUMENT,
|
||||
GUI_ACTION_PAT_CURSOR_UNDO,
|
||||
GUI_ACTION_PAT_CURSOR_REDO,
|
||||
GUI_ACTION_PAT_MAX,
|
||||
|
||||
GUI_ACTION_INS_LIST_MIN,
|
||||
|
|
@ -1104,6 +1108,22 @@ struct UndoStep {
|
|||
newPatLen(0) {}
|
||||
};
|
||||
|
||||
struct CursorJumpPoint {
|
||||
SelectionPoint point;
|
||||
int order;
|
||||
int subSong;
|
||||
CursorJumpPoint(const SelectionPoint& p, int o, int ss):
|
||||
point(p), order(o), subSong(ss) {}
|
||||
CursorJumpPoint():
|
||||
point(), order(0), subSong(0) {}
|
||||
bool operator== (const CursorJumpPoint& spot) {
|
||||
return point.xCoarse==spot.point.xCoarse && point.xFine==spot.point.xFine && point.y==spot.point.y && order==spot.order && subSong==spot.subSong;
|
||||
}
|
||||
bool operator!= (const CursorJumpPoint& spot) {
|
||||
return !(*this == spot);
|
||||
}
|
||||
};
|
||||
|
||||
// -1 = any
|
||||
struct MIDIBind {
|
||||
int type, channel, data1, data2;
|
||||
|
|
@ -1753,6 +1773,7 @@ class FurnaceGUI {
|
|||
int opnbCore;
|
||||
int opl2Core;
|
||||
int opl3Core;
|
||||
int opl4Core;
|
||||
int esfmCore;
|
||||
int opllCore;
|
||||
int ayCore;
|
||||
|
|
@ -1779,6 +1800,7 @@ class FurnaceGUI {
|
|||
int opnbCoreRender;
|
||||
int opl2CoreRender;
|
||||
int opl3CoreRender;
|
||||
int opl4CoreRender;
|
||||
int esfmCoreRender;
|
||||
int opllCoreRender;
|
||||
int ayCoreRender;
|
||||
|
|
@ -1805,6 +1827,7 @@ class FurnaceGUI {
|
|||
int patRowsBase;
|
||||
int orderRowsBase;
|
||||
int soloAction;
|
||||
int ctrlWheelModifier;
|
||||
int pullDeleteBehavior;
|
||||
int wrapHorizontal;
|
||||
int wrapVertical;
|
||||
|
|
@ -2013,6 +2036,7 @@ class FurnaceGUI {
|
|||
opnbCore(1),
|
||||
opl2Core(0),
|
||||
opl3Core(0),
|
||||
opl4Core(0),
|
||||
esfmCore(0),
|
||||
opllCore(0),
|
||||
ayCore(0),
|
||||
|
|
@ -2039,6 +2063,7 @@ class FurnaceGUI {
|
|||
opnbCoreRender(1),
|
||||
opl2CoreRender(0),
|
||||
opl3CoreRender(0),
|
||||
opl4CoreRender(0),
|
||||
esfmCoreRender(0),
|
||||
opllCoreRender(0),
|
||||
ayCoreRender(0),
|
||||
|
|
@ -2064,6 +2089,7 @@ class FurnaceGUI {
|
|||
patRowsBase(0),
|
||||
orderRowsBase(1),
|
||||
soloAction(0),
|
||||
ctrlWheelModifier(0),
|
||||
pullDeleteBehavior(1),
|
||||
wrapHorizontal(0),
|
||||
wrapVertical(0),
|
||||
|
|
@ -2501,6 +2527,8 @@ class FurnaceGUI {
|
|||
std::map<unsigned short,DivPattern*> oldPatMap;
|
||||
FixedQueue<UndoStep,256> undoHist;
|
||||
FixedQueue<UndoStep,256> redoHist;
|
||||
FixedQueue<CursorJumpPoint,256> cursorUndoHist;
|
||||
FixedQueue<CursorJumpPoint,256> cursorRedoHist;
|
||||
|
||||
// sample editor specific
|
||||
double sampleZoom;
|
||||
|
|
@ -2741,6 +2769,7 @@ class FurnaceGUI {
|
|||
static bool LocalizedComboGetter(void* data, int idx, const char** out_text);
|
||||
|
||||
// these ones offer ctrl-wheel fine value changes.
|
||||
bool isCtrlWheelModifierHeld() const;
|
||||
bool CWSliderScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format=NULL, ImGuiSliderFlags flags=0);
|
||||
bool CWVSliderScalar(const char* label, const ImVec2& size, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format=NULL, ImGuiSliderFlags flags=0);
|
||||
bool CWSliderInt(const char* label, int* v, int v_min, int v_max, const char* format="%d", ImGuiSliderFlags flags=0);
|
||||
|
|
@ -2941,6 +2970,12 @@ class FurnaceGUI {
|
|||
|
||||
void doGenerateWave();
|
||||
|
||||
CursorJumpPoint getCurrentCursorJumpPoint();
|
||||
void applyCursorJumpPoint(const CursorJumpPoint& spot);
|
||||
void makeCursorUndo();
|
||||
void doCursorUndo();
|
||||
void doCursorRedo();
|
||||
|
||||
void doUndoSample();
|
||||
void doRedoSample();
|
||||
|
||||
|
|
|
|||
|
|
@ -184,6 +184,8 @@ const char* insTypes[DIV_INS_MAX+1][3]={
|
|||
{"GBA MinMod",ICON_FA_VOLUME_UP,ICON_FUR_INS_GBA_MINMOD},
|
||||
{"Bifurcator",ICON_FA_LINE_CHART,ICON_FUR_INS_BIFURCATOR},
|
||||
{"SID2",ICON_FA_KEYBOARD_O,ICON_FUR_INS_SID2},
|
||||
{"Watara Supervision",ICON_FA_GAMEPAD,ICON_FUR_INS_SUPERVISION},
|
||||
{"NEC μPD1771C",ICON_FA_BAR_CHART,ICON_FUR_INS_UPD1771C},
|
||||
{"SID3",ICON_FA_KEYBOARD_O,ICON_FUR_INS_SID3},
|
||||
{NULL,ICON_FA_QUESTION,ICON_FA_QUESTION}
|
||||
};
|
||||
|
|
@ -209,7 +211,7 @@ const char* sampleDepths[DIV_SAMPLE_DEPTH_MAX]={
|
|||
"8-bit µ-law PCM",
|
||||
"C219 PCM",
|
||||
"IMA ADPCM",
|
||||
NULL,
|
||||
"12-bit PCM",
|
||||
NULL,
|
||||
"16-bit PCM"
|
||||
};
|
||||
|
|
@ -689,6 +691,8 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={
|
|||
D("PAT_SCROLL_MODE", _N("Change mobile scroll mode"), 0),
|
||||
D("PAT_CLEAR_LATCH", _N("Clear note input latch"), 0),
|
||||
D("PAT_ABSORB_INSTRUMENT", _N("Absorb instrument/octave from status at cursor"), 0),
|
||||
D("PAT_CURSOR_UNDO", _N("Return cursor to previous jump point"), 0),
|
||||
D("PAT_CURSOR_REDO", _N("Reverse recent cursor undo"), 0),
|
||||
D("PAT_MAX", "", NOT_AN_ACTION),
|
||||
|
||||
D("INS_LIST_MIN", _N("---Instrument list"), NOT_AN_ACTION),
|
||||
|
|
@ -1017,6 +1021,8 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={
|
|||
D(GUI_COLOR_INSTR_GBA_MINMOD,"",ImVec4(0.5f,0.45f,0.7f,1.0f)),
|
||||
D(GUI_COLOR_INSTR_BIFURCATOR,"",ImVec4(0.8925f,0.8925f,0.8925f,1.0f)),
|
||||
D(GUI_COLOR_INSTR_SID2,"",ImVec4(0.6f,0.75f,1.0f,1.0f)),
|
||||
D(GUI_COLOR_INSTR_SUPERVISION,"",ImVec4(0.52f,1.0f,0.6f,1.0f)),
|
||||
D(GUI_COLOR_INSTR_UPD1771C,"",ImVec4(0.94f,0.52f,0.6f,1.0f)),
|
||||
D(GUI_COLOR_INSTR_SID3,"",ImVec4(0.6f,0.75f,0.6f,1.0f)),
|
||||
D(GUI_COLOR_INSTR_UNKNOWN,"",ImVec4(0.3f,0.3f,0.3f,1.0f)),
|
||||
|
||||
|
|
@ -1266,6 +1272,10 @@ const int availableSystems[]={
|
|||
DIV_SYSTEM_5E01,
|
||||
DIV_SYSTEM_BIFURCATOR,
|
||||
DIV_SYSTEM_SID2,
|
||||
DIV_SYSTEM_OPL4,
|
||||
DIV_SYSTEM_OPL4_DRUMS,
|
||||
DIV_SYSTEM_SUPERVISION,
|
||||
DIV_SYSTEM_UPD1771C,
|
||||
DIV_SYSTEM_SID3,
|
||||
0 // don't remove this last one!
|
||||
};
|
||||
|
|
@ -1302,6 +1312,8 @@ const int chipsFM[]={
|
|||
DIV_SYSTEM_OPL3_DRUMS,
|
||||
DIV_SYSTEM_OPZ,
|
||||
DIV_SYSTEM_ESFM,
|
||||
DIV_SYSTEM_OPL4,
|
||||
DIV_SYSTEM_OPL4_DRUMS,
|
||||
0 // don't remove this last one!
|
||||
};
|
||||
|
||||
|
|
@ -1362,6 +1374,8 @@ const int chipsSpecial[]={
|
|||
DIV_SYSTEM_5E01,
|
||||
DIV_SYSTEM_BIFURCATOR,
|
||||
DIV_SYSTEM_SID2,
|
||||
DIV_SYSTEM_SUPERVISION,
|
||||
DIV_SYSTEM_UPD1771C,
|
||||
DIV_SYSTEM_SID3,
|
||||
0 // don't remove this last one!
|
||||
};
|
||||
|
|
@ -1388,6 +1402,8 @@ const int chipsSample[]={
|
|||
DIV_SYSTEM_NDS,
|
||||
DIV_SYSTEM_GBA_DMA,
|
||||
DIV_SYSTEM_GBA_MINMOD,
|
||||
DIV_SYSTEM_OPL4,
|
||||
DIV_SYSTEM_OPL4_DRUMS,
|
||||
0 // don't remove this last one!
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -2746,48 +2746,68 @@ void FurnaceGUI::drawMacros(std::vector<FurnaceGUIMacroDesc>& macros, FurnaceGUI
|
|||
drawMacroEdit(m,totalFit,availableWidth,index);
|
||||
|
||||
if (m.macro->open&1) {
|
||||
if ((m.macro->open&6)==0) {
|
||||
ImGui::Text(_("Length"));
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(120.0f*dpiScale);
|
||||
int macroLen=m.macro->len;
|
||||
if (ImGui::InputScalar("##IMacroLen",ImGuiDataType_U8,¯oLen,&_ONE,&_THREE)) { MARK_MODIFIED
|
||||
if (macroLen<0) macroLen=0;
|
||||
if (macroLen>255) macroLen=255;
|
||||
m.macro->len=macroLen;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
}
|
||||
ImGui::Text(_("StepLen"));
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(120.0f*dpiScale);
|
||||
if (ImGui::InputScalar("##IMacroSpeed",ImGuiDataType_U8,&m.macro->speed,&_ONE,&_THREE)) {
|
||||
if (m.macro->speed<1) m.macro->speed=1;
|
||||
MARK_MODIFIED;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::Text(_("Delay"));
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(120.0f*dpiScale);
|
||||
if (ImGui::InputScalar("##IMacroDelay",ImGuiDataType_U8,&m.macro->delay,&_ONE,&_THREE)) {
|
||||
MARK_MODIFIED;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
{
|
||||
FurnaceGUIMacroDesc& i=m;
|
||||
BUTTON_TO_SET_MODE(ImGui::Button);
|
||||
if ((i.macro->open&6)==0) {
|
||||
bool showLen=((m.macro->open&6)==0);
|
||||
int colCount=showLen ? 4 : 3;
|
||||
float availX=ImGui::GetContentRegionAvail().x;
|
||||
|
||||
// fairly arbitrary scaling logic
|
||||
bool shortLabels=(availX<600.0f*dpiScale);
|
||||
float scalarItemWidth=MIN((availX-90.0f*dpiScale)/colCount, 120.0f*dpiScale);
|
||||
if (ImGui::BeginTable("##MacroMetaData",colCount)) {
|
||||
if (showLen) ImGui::TableSetupColumn("len",ImGuiTableColumnFlags_WidthStretch,0.0);
|
||||
ImGui::TableSetupColumn("stepLen",ImGuiTableColumnFlags_WidthStretch,0.0);
|
||||
ImGui::TableSetupColumn("delay",ImGuiTableColumnFlags_WidthStretch,0.0);
|
||||
ImGui::TableSetupColumn("buttons",ImGuiTableColumnFlags_WidthFixed,0.0);
|
||||
|
||||
ImGui::TableNextRow();
|
||||
if (showLen) {
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text(shortLabels ? _("Len##macroEditLengthShortLabel") : _("Length"));
|
||||
if (shortLabels && ImGui::IsItemHovered()) ImGui::SetTooltip("%s", _("Length"));
|
||||
ImGui::SameLine();
|
||||
BUTTON_TO_SET_RELEASE(ImGui::Button);
|
||||
ImGui::SetNextItemWidth(scalarItemWidth);
|
||||
int macroLen=m.macro->len;
|
||||
if (ImGui::InputScalar("##IMacroLen",ImGuiDataType_U8,¯oLen,&_ONE,&_THREE)) { MARK_MODIFIED
|
||||
if (macroLen<0) macroLen=0;
|
||||
if (macroLen>255) macroLen=255;
|
||||
m.macro->len=macroLen;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (m.modeName!=NULL) {
|
||||
bool modeVal=m.macro->mode;
|
||||
String modeName=fmt::sprintf("%s##IMacroMode",m.modeName);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text(shortLabels ? _("SLen##macroEditStepLenShortLabel") : _("StepLen"));
|
||||
if (shortLabels && ImGui::IsItemHovered()) ImGui::SetTooltip("%s", _("StepLen"));
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Checkbox(modeName.c_str(),&modeVal)) {
|
||||
m.macro->mode=modeVal;
|
||||
ImGui::SetNextItemWidth(scalarItemWidth);
|
||||
if (ImGui::InputScalar("##IMacroSpeed",ImGuiDataType_U8,&m.macro->speed,&_ONE,&_THREE)) {
|
||||
if (m.macro->speed<1) m.macro->speed=1;
|
||||
MARK_MODIFIED;
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text(shortLabels ? _("Del##macroEditDelayShortLabel") : _("Delay"));
|
||||
if (shortLabels && ImGui::IsItemHovered()) ImGui::SetTooltip("%s", _("Delay"));
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(scalarItemWidth);
|
||||
if (ImGui::InputScalar("##IMacroDelay",ImGuiDataType_U8,&m.macro->delay,&_ONE,&_THREE)) {
|
||||
MARK_MODIFIED;
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
{
|
||||
FurnaceGUIMacroDesc& i=m;
|
||||
BUTTON_TO_SET_MODE(ImGui::Button);
|
||||
if ((i.macro->open&6)==0) {
|
||||
ImGui::SameLine();
|
||||
BUTTON_TO_SET_RELEASE(ImGui::Button);
|
||||
}
|
||||
}
|
||||
if (m.modeName!=NULL) {
|
||||
bool modeVal=m.macro->mode;
|
||||
String modeName=fmt::sprintf("%s##IMacroMode",m.modeName);
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Checkbox(modeName.c_str(),&modeVal)) {
|
||||
m.macro->mode=modeVal;
|
||||
}
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
} else {
|
||||
ImGui::Text(_("The heck? No, this isn't even working correctly..."));
|
||||
|
|
@ -3289,6 +3309,7 @@ void FurnaceGUI::insTabSample(DivInstrument* ins) {
|
|||
ins->type==DIV_INS_VRC6 ||
|
||||
ins->type==DIV_INS_SU ||
|
||||
ins->type==DIV_INS_NDS ||
|
||||
ins->type==DIV_INS_SUPERVISION ||
|
||||
ins->type==DIV_INS_SID3) {
|
||||
P(ImGui::Checkbox(_("Use sample"),&ins->amiga.useSample));
|
||||
if (ins->type==DIV_INS_X1_010) {
|
||||
|
|
@ -4120,8 +4141,8 @@ void FurnaceGUI::insTabFM(DivInstrument* ins) {
|
|||
|
||||
opllPreview.alg=ins->fm.alg;
|
||||
opllPreview.fb=patch->fb;
|
||||
opllPreview.fms=patch->dm;
|
||||
opllPreview.ams=patch->dc;
|
||||
opllPreview.fms=patch->dc;
|
||||
opllPreview.ams=patch->dm;
|
||||
|
||||
opllPreview.op[0].tl=patch->tl;
|
||||
opllPreview.op[1].tl=ins->fm.op[1].tl;
|
||||
|
|
@ -4131,6 +4152,7 @@ void FurnaceGUI::insTabFM(DivInstrument* ins) {
|
|||
opllPreview.op[i].vib=patch->vib[i];
|
||||
opllPreview.op[i].ssgEnv=patch->et[i]?8:0;
|
||||
opllPreview.op[i].ksr=patch->ksr[i];
|
||||
opllPreview.op[i].ksl=patch->ksl[i];
|
||||
opllPreview.op[i].mult=patch->multi[i];
|
||||
opllPreview.op[i].ar=patch->ar[i];
|
||||
opllPreview.op[i].dr=patch->dr[i];
|
||||
|
|
@ -4650,7 +4672,7 @@ void FurnaceGUI::insTabFM(DivInstrument* ins) {
|
|||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
P(CWSliderScalar("##WS",ImGuiDataType_U8,&op.ws,&_ZERO,&_SEVEN,(ins->type==DIV_INS_OPZ)?opzWaveforms[op.ws&7]:(settings.oplStandardWaveNames?oplWaveformsStandard[op.ws&7]:oplWaveforms[op.ws&7]))); rightClickable
|
||||
if ((ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS) && ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip(_("OPL2/3 only (last 4 waveforms are OPL3 only)"));
|
||||
ImGui::SetTooltip(_("OPL2/3/4 only (last 4 waveforms are OPL3/4 only)"));
|
||||
}
|
||||
if (ins->type==DIV_INS_ESFM && fixedOn) {
|
||||
if (ImGui::Checkbox(FM_SHORT_NAME(FM_VIB),&vibOn)) { PARAMETER
|
||||
|
|
@ -5014,7 +5036,7 @@ void FurnaceGUI::insTabFM(DivInstrument* ins) {
|
|||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
P(CWSliderScalar("##WS",ImGuiDataType_U8,&op.ws,&_ZERO,&_SEVEN,(ins->type==DIV_INS_OPZ)?opzWaveforms[op.ws&7]:(settings.oplStandardWaveNames?oplWaveformsStandard[op.ws&7]:oplWaveforms[op.ws&7]))); rightClickable
|
||||
if ((ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS) && ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip(_("OPL2/3 only (last 4 waveforms are OPL3 only)"));
|
||||
ImGui::SetTooltip(_("OPL2/3/4 only (last 4 waveforms are OPL3/4 only)"));
|
||||
}
|
||||
|
||||
// params
|
||||
|
|
@ -5064,7 +5086,7 @@ void FurnaceGUI::insTabFM(DivInstrument* ins) {
|
|||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
P(CWSliderScalar("##WS",ImGuiDataType_U8,&op.ws,&_ZERO,&_SEVEN,(ins->type==DIV_INS_OPZ)?opzWaveforms[op.ws&7]:(settings.oplStandardWaveNames?oplWaveformsStandard[op.ws&7]:oplWaveforms[op.ws&7]))); rightClickable
|
||||
if ((ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS) && ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip(_("OPL2/3 only (last 4 waveforms are OPL3 only)"));
|
||||
ImGui::SetTooltip(_("OPL2/3/4 only (last 4 waveforms are OPL3/4 only)"));
|
||||
}
|
||||
|
||||
// params
|
||||
|
|
@ -5772,7 +5794,7 @@ void FurnaceGUI::insTabFM(DivInstrument* ins) {
|
|||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
P(CWSliderScalar("##WS",ImGuiDataType_U8,&op.ws,&_ZERO,&_SEVEN,(ins->type==DIV_INS_OPZ)?opzWaveforms[op.ws&7]:(settings.oplStandardWaveNames?oplWaveformsStandard[op.ws&7]:oplWaveforms[op.ws&7]))); rightClickable
|
||||
if ((ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS) && ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip(_("OPL2/3 only (last 4 waveforms are OPL3 only)"));
|
||||
ImGui::SetTooltip(_("OPL2/3/4 only (last 4 waveforms are OPL3/4 only)"));
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s",FM_NAME(FM_WS));
|
||||
|
|
@ -7475,7 +7497,8 @@ void FurnaceGUI::drawInsEdit() {
|
|||
ins->type==DIV_INS_C219 ||
|
||||
ins->type==DIV_INS_NDS ||
|
||||
ins->type==DIV_INS_GBA_DMA ||
|
||||
ins->type==DIV_INS_GBA_MINMOD) {
|
||||
ins->type==DIV_INS_GBA_MINMOD ||
|
||||
ins->type==DIV_INS_SUPERVISION) {
|
||||
insTabSample(ins);
|
||||
}
|
||||
if (ins->type==DIV_INS_N163) if (ImGui::BeginTabItem("Namco 163")) {
|
||||
|
|
@ -7789,6 +7812,19 @@ void FurnaceGUI::drawInsEdit() {
|
|||
P(CWSliderScalar(_("AM Depth"),ImGuiDataType_U8,&ins->multipcm.am,&_ZERO,&_SEVEN)); rightClickable
|
||||
ImGui::EndTable();
|
||||
}
|
||||
P(ImGui::Checkbox(_("Damp"),&ins->multipcm.damp));
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip(_("Only for OPL4 PCM."));
|
||||
}
|
||||
P(ImGui::Checkbox(_("Pseudo Reverb"),&ins->multipcm.pseudoReverb));
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip(_("Only for OPL4 PCM."));
|
||||
}
|
||||
P(ImGui::Checkbox(_("LFO Reset"),&ins->multipcm.lfoReset));
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip(_("Only for OPL4 PCM."));
|
||||
}
|
||||
P(ImGui::Checkbox(_("Level Direct"),&ins->multipcm.levelDirect));
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
}
|
||||
|
|
@ -8225,6 +8261,9 @@ void FurnaceGUI::drawInsEdit() {
|
|||
macroList.push_back(FurnaceGUIMacroDesc(_("Panning"),&ins->std.panLMacro,-7,7,45,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL));
|
||||
macroList.push_back(FurnaceGUIMacroDesc(_("Pitch"),&ins->std.pitchMacro,-2048,2047,160,uiColors[GUI_COLOR_MACRO_PITCH],true,macroRelativeMode));
|
||||
macroList.push_back(FurnaceGUIMacroDesc(_("Phase Reset"),&ins->std.phaseResetMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true));
|
||||
macroList.push_back(FurnaceGUIMacroDesc(_("LFO Speed"),&ins->std.ex1Macro,0,7,160,uiColors[GUI_COLOR_MACRO_OTHER]));
|
||||
macroList.push_back(FurnaceGUIMacroDesc(_("LFO Vib Depth"),&ins->std.fmsMacro,0,7,160,uiColors[GUI_COLOR_MACRO_PITCH]));
|
||||
macroList.push_back(FurnaceGUIMacroDesc(_("LFO AM Depth"),&ins->std.amsMacro,0,7,160,uiColors[GUI_COLOR_MACRO_VOLUME]));
|
||||
break;
|
||||
case DIV_INS_SNES:
|
||||
macroList.push_back(FurnaceGUIMacroDesc(_("Volume"),&ins->std.volMacro,0,127,160,uiColors[GUI_COLOR_MACRO_VOLUME]));
|
||||
|
|
@ -8376,6 +8415,13 @@ void FurnaceGUI::drawInsEdit() {
|
|||
macroList.push_back(FurnaceGUIMacroDesc(_("Pulse Width"),&ins->std.dutyMacro,0,255,160,uiColors[GUI_COLOR_MACRO_OTHER]));
|
||||
macroList.push_back(FurnaceGUIMacroDesc(_("Pitch"),&ins->std.pitchMacro,-2048,2047,160,uiColors[GUI_COLOR_MACRO_PITCH],true,macroRelativeMode));
|
||||
break;
|
||||
case DIV_INS_SUPERVISION:
|
||||
macroList.push_back(FurnaceGUIMacroDesc(_("Volume"),&ins->std.volMacro,0,15,160,uiColors[GUI_COLOR_MACRO_VOLUME]));
|
||||
macroList.push_back(FurnaceGUIMacroDesc(_("Arpeggio"),&ins->std.arpMacro,-120,120,160,uiColors[GUI_COLOR_MACRO_PITCH],true,NULL,macroHoverNote,false,NULL,true,ins->std.arpMacro.val));
|
||||
macroList.push_back(FurnaceGUIMacroDesc(_("Duty/Noise"),&ins->std.dutyMacro,0,3,160,uiColors[GUI_COLOR_MACRO_NOISE]));
|
||||
macroList.push_back(FurnaceGUIMacroDesc(_("Noise/PCM Pan"),&ins->std.panLMacro,0,2,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,panBits));
|
||||
macroList.push_back(FurnaceGUIMacroDesc(_("Pitch"),&ins->std.pitchMacro,-2048,2047,160,uiColors[GUI_COLOR_MACRO_PITCH],true,macroRelativeMode));
|
||||
break;
|
||||
case DIV_INS_SM8521:
|
||||
macroList.push_back(FurnaceGUIMacroDesc(_("Volume"),&ins->std.volMacro,0,31,160,uiColors[GUI_COLOR_MACRO_VOLUME]));
|
||||
macroList.push_back(FurnaceGUIMacroDesc(_("Arpeggio"),&ins->std.arpMacro,-120,120,160,uiColors[GUI_COLOR_MACRO_PITCH],true,NULL,macroHoverNote,false,NULL,true,ins->std.arpMacro.val));
|
||||
|
|
@ -8518,8 +8564,17 @@ void FurnaceGUI::drawInsEdit() {
|
|||
macroList.push_back(FurnaceGUIMacroDesc(_("Noise Mode"),&ins->std.fmsMacro,0,3,64,uiColors[GUI_COLOR_MACRO_NOISE]));
|
||||
macroList.push_back(FurnaceGUIMacroDesc(_("Wave Mix"),&ins->std.amsMacro,0,3,64,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,macroSID2WaveMixMode));
|
||||
break;
|
||||
case DIV_INS_SID3: break;
|
||||
|
||||
case DIV_INS_UPD1771C:
|
||||
macroList.push_back(FurnaceGUIMacroDesc(_("Volume"),&ins->std.volMacro,0,31,160,uiColors[GUI_COLOR_MACRO_VOLUME]));
|
||||
macroList.push_back(FurnaceGUIMacroDesc(_("Arpeggio"),&ins->std.arpMacro,-120,120,160,uiColors[GUI_COLOR_MACRO_PITCH],true,NULL,macroHoverNote,false,NULL,true,ins->std.arpMacro.val));
|
||||
macroList.push_back(FurnaceGUIMacroDesc(_("Waveform"),&ins->std.waveMacro,0,7,160,uiColors[GUI_COLOR_MACRO_WAVE],false,NULL,NULL,false,NULL));
|
||||
macroList.push_back(FurnaceGUIMacroDesc(_("Wave Pos"),&ins->std.ex1Macro,0,31,160,uiColors[GUI_COLOR_MACRO_OTHER]));
|
||||
macroList.push_back(FurnaceGUIMacroDesc(_("Duty/Mode"),&ins->std.dutyMacro,0,1,160,uiColors[GUI_COLOR_MACRO_NOISE]));
|
||||
macroList.push_back(FurnaceGUIMacroDesc(_("Pitch"),&ins->std.pitchMacro,-2048,2047,160,uiColors[GUI_COLOR_MACRO_PITCH],true,macroRelativeMode));
|
||||
break;
|
||||
case DIV_INS_SID3:
|
||||
// TODO: put stuff here to kill that goto.
|
||||
break;
|
||||
case DIV_INS_MAX:
|
||||
case DIV_INS_NULL:
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -320,6 +320,11 @@ void FurnaceGUI::initSystemPresets() {
|
|||
CH(DIV_SYSTEM_NDS, 1.0f, 0, "")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Watara Supervision", {
|
||||
CH(DIV_SYSTEM_SUPERVISION, 1.0f, 0, "")
|
||||
}
|
||||
);
|
||||
CATEGORY_END;
|
||||
|
||||
CATEGORY_BEGIN(_("Computers"),_("let's get to work on chiptune today."));
|
||||
|
|
@ -519,6 +524,18 @@ void FurnaceGUI::initSystemPresets() {
|
|||
) // variable rate, Mono DAC
|
||||
}
|
||||
);
|
||||
SUB_ENTRY(
|
||||
"MSX + MoonSound", {
|
||||
CH(DIV_SYSTEM_AY8910, 1.0f, 0, "chipType=1"),
|
||||
CH(DIV_SYSTEM_OPL4, 1.0f, 0, "")
|
||||
}
|
||||
);
|
||||
SUB_ENTRY(
|
||||
"MSX + MoonSound (drums mode)", {
|
||||
CH(DIV_SYSTEM_AY8910, 1.0f, 0, "chipType=1"),
|
||||
CH(DIV_SYSTEM_OPL4_DRUMS, 1.0f, 0, "")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
_("NEC PC-6001"), {
|
||||
CH(DIV_SYSTEM_AY8910, 1.0f, 0, "customClock=3993600")
|
||||
|
|
@ -1319,6 +1336,11 @@ void FurnaceGUI::initSystemPresets() {
|
|||
)
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Epoch Super Cassette Vision", {
|
||||
CH(DIV_SYSTEM_UPD1771C, 1.0f, 0, "")
|
||||
}
|
||||
);
|
||||
CATEGORY_END;
|
||||
|
||||
CATEGORY_BEGIN(_("Arcade systems"),_("INSERT COIN"));
|
||||
|
|
@ -1895,6 +1917,30 @@ void FurnaceGUI::initSystemPresets() {
|
|||
}
|
||||
);
|
||||
|
||||
ENTRY(
|
||||
_("Psikyo"), {}
|
||||
);
|
||||
SUB_ENTRY(
|
||||
_("Psikyo 68EC020 hardware with OPL4"), {
|
||||
CH(DIV_SYSTEM_OPL4, 1.0f, 0, "")
|
||||
}
|
||||
);
|
||||
SUB_ENTRY(
|
||||
_("Psikyo 68EC020 hardware with OPL4 (drums mode)"), {
|
||||
CH(DIV_SYSTEM_OPL4_DRUMS, 1.0f, 0, "")
|
||||
}
|
||||
);
|
||||
SUB_ENTRY(
|
||||
_("Psikyo SH-2 hardware"), {
|
||||
CH(DIV_SYSTEM_OPL4, 1.0f, 0, "clockSel=1")
|
||||
}
|
||||
);
|
||||
SUB_ENTRY(
|
||||
_("Psikyo SH-2 hardware (drums mode)"), {
|
||||
CH(DIV_SYSTEM_OPL4_DRUMS, 1.0f, 0, "clockSel=1")
|
||||
}
|
||||
);
|
||||
|
||||
ENTRY(
|
||||
_("Sega"), {}
|
||||
);
|
||||
|
|
@ -2719,6 +2765,16 @@ void FurnaceGUI::initSystemPresets() {
|
|||
CH(DIV_SYSTEM_ESFM, 1.0f, 0, "")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Yamaha YMF278B (OPL4)", {
|
||||
CH(DIV_SYSTEM_OPL4, 1.0f, 0, "")
|
||||
}
|
||||
);
|
||||
SUB_ENTRY(
|
||||
"Yamaha YMF278B (drums mode)", {
|
||||
CH(DIV_SYSTEM_OPL4_DRUMS, 1.0f, 0, "")
|
||||
}
|
||||
);
|
||||
if (settings.hiddenSystems) {
|
||||
ENTRY(
|
||||
_("Yamaha YMU759 (MA-2)"), {
|
||||
|
|
@ -2930,6 +2986,16 @@ void FurnaceGUI::initSystemPresets() {
|
|||
CH(DIV_SYSTEM_NDS, 1.0f, 0, "")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Yamaha YMF278B (OPL4)", {
|
||||
CH(DIV_SYSTEM_OPL4, 1.0f, 0, "")
|
||||
}
|
||||
);
|
||||
SUB_ENTRY(
|
||||
"Yamaha YMF278B (drums mode)", {
|
||||
CH(DIV_SYSTEM_OPL4_DRUMS, 1.0f, 0, "")
|
||||
}
|
||||
);
|
||||
CATEGORY_END;
|
||||
|
||||
CATEGORY_BEGIN(_("Wavetable"),_("chips which use user-specified waveforms to generate sound."));
|
||||
|
|
@ -3125,6 +3191,16 @@ void FurnaceGUI::initSystemPresets() {
|
|||
CH(DIV_SYSTEM_SID3, 1.0f, 0, "")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
_("Watara Supervision"), {
|
||||
CH(DIV_SYSTEM_SUPERVISION, 1.0f, 0, "")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
_("NEC μPD1771C"), {
|
||||
CH(DIV_SYSTEM_UPD1771C, 1.0f, 0, "")
|
||||
}
|
||||
);
|
||||
CATEGORY_END;
|
||||
|
||||
CATEGORY_BEGIN(_("DefleMask-compatible"),_("these configurations are compatible with DefleMask.\nselect this if you need to save as .dmf or work with that program."));
|
||||
|
|
|
|||
|
|
@ -413,6 +413,25 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
SAMPLE_WARN(warnLength,_("GBA DMA: sample length will be padded to multiple of 16"));
|
||||
}
|
||||
break;
|
||||
case DIV_SYSTEM_OPL4:
|
||||
case DIV_SYSTEM_OPL4_DRUMS:
|
||||
if (sample->samples>65535) {
|
||||
SAMPLE_WARN(warnLength,_("OPL4: maximum sample length is 65535"));
|
||||
}
|
||||
break;
|
||||
case DIV_SYSTEM_SUPERVISION:
|
||||
if (sample->loop) {
|
||||
if (sample->loopStart!=0 || sample->loopEnd!=(int)(sample->samples)) {
|
||||
SAMPLE_WARN(warnLoopPos,_("Supervision: loop point ignored on sample channel"));
|
||||
}
|
||||
}
|
||||
if (sample->samples&31) {
|
||||
SAMPLE_WARN(warnLength,_("Supervision: sample length will be padded to multiple of 32"));
|
||||
}
|
||||
if (sample->samples>8192) {
|
||||
SAMPLE_WARN(warnLength,_("Supervision: maximum sample length is 8192"));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -189,6 +189,11 @@ const char* opl3Cores[]={
|
|||
"YMF262-LLE"
|
||||
};
|
||||
|
||||
const char* opl4Cores[]={
|
||||
"Nuked-OPL3 (FM) + openMSX (PCM)",
|
||||
"ymfm"
|
||||
};
|
||||
|
||||
const char* esfmCores[]={
|
||||
"ESFMu",
|
||||
_N("ESFMu (fast)")
|
||||
|
|
@ -2058,6 +2063,17 @@ void FurnaceGUI::drawSettings() {
|
|||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
if (ImGui::Combo("##OPL3CoreRender",&settings.opl3CoreRender,opl3Cores,3)) settingsChanged=true;
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::Text("OPL4");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
if (ImGui::Combo("##OPL4Core",&settings.opl4Core,opl4Cores,2)) settingsChanged=true;
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
if (ImGui::Combo("##OPL4CoreRender",&settings.opl4CoreRender,opl4Cores,2)) settingsChanged=true;
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::AlignTextToFramePadding();
|
||||
|
|
@ -2137,7 +2153,6 @@ void FurnaceGUI::drawSettings() {
|
|||
ImGui::SameLine();
|
||||
if (ImGui::Combo("##PCSOutMethod",&settings.pcSpeakerOutMethod,LocalizedComboGetter,pcspkrOutMethods,5)) settingsChanged=true;
|
||||
|
||||
/*
|
||||
ImGui::Separator();
|
||||
ImGui::Text(_("Sample ROMs:"));
|
||||
|
||||
|
|
@ -2150,6 +2165,7 @@ void FurnaceGUI::drawSettings() {
|
|||
openFileDialog(GUI_FILE_YRW801_ROM_OPEN);
|
||||
}
|
||||
|
||||
/*
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::Text(_("MultiPCM TG100 path"));
|
||||
ImGui::SameLine();
|
||||
|
|
@ -2428,6 +2444,8 @@ void FurnaceGUI::drawSettings() {
|
|||
UI_KEYBIND_CONFIG(GUI_ACTION_PAT_LATCH);
|
||||
UI_KEYBIND_CONFIG(GUI_ACTION_PAT_CLEAR_LATCH);
|
||||
UI_KEYBIND_CONFIG(GUI_ACTION_PAT_ABSORB_INSTRUMENT);
|
||||
UI_KEYBIND_CONFIG(GUI_ACTION_PAT_CURSOR_UNDO);
|
||||
UI_KEYBIND_CONFIG(GUI_ACTION_PAT_CURSOR_REDO);
|
||||
|
||||
KEYBIND_CONFIG_END;
|
||||
ImGui::TreePop();
|
||||
|
|
@ -2720,6 +2738,27 @@ void FurnaceGUI::drawSettings() {
|
|||
}
|
||||
ImGui::Unindent();
|
||||
|
||||
ImGui::Text(_("Modifier for alternate wheel-scrolling (vertical/zoom/slider-input):"));
|
||||
ImGui::Indent();
|
||||
if (ImGui::RadioButton(_("Ctrl or Meta/Cmd##cwm1"),settings.ctrlWheelModifier==0)) {
|
||||
settings.ctrlWheelModifier=0;
|
||||
settingsChanged=true;
|
||||
}
|
||||
if (ImGui::RadioButton(_("Ctrl##cwm2"),settings.ctrlWheelModifier==1)) {
|
||||
settings.ctrlWheelModifier=1;
|
||||
settingsChanged=true;
|
||||
}
|
||||
if (ImGui::RadioButton(_("Meta/Cmd##cwm3"),settings.ctrlWheelModifier==2)) {
|
||||
settings.ctrlWheelModifier=2;
|
||||
settingsChanged=true;
|
||||
}
|
||||
// technically this key is called Option on mac, but we call it Alt in getKeyName(s)
|
||||
if (ImGui::RadioButton(_("Alt##cwm4"),settings.ctrlWheelModifier==3)) {
|
||||
settings.ctrlWheelModifier=3;
|
||||
settingsChanged=true;
|
||||
}
|
||||
ImGui::Unindent();
|
||||
|
||||
bool doubleClickColumnB=settings.doubleClickColumn;
|
||||
if (ImGui::Checkbox(_("Double click selects entire column"),&doubleClickColumnB)) {
|
||||
settings.doubleClickColumn=doubleClickColumnB;
|
||||
|
|
@ -4126,6 +4165,8 @@ void FurnaceGUI::drawSettings() {
|
|||
UI_COLOR_CONFIG(GUI_COLOR_INSTR_GBA_MINMOD,_("GBA MinMod"));
|
||||
UI_COLOR_CONFIG(GUI_COLOR_INSTR_BIFURCATOR,_("Bifurcator"));
|
||||
UI_COLOR_CONFIG(GUI_COLOR_INSTR_SID2,_("SID2"));
|
||||
UI_COLOR_CONFIG(GUI_COLOR_INSTR_SUPERVISION,_("Supervision"));
|
||||
UI_COLOR_CONFIG(GUI_COLOR_INSTR_UPD1771C,_("μPD1771C"));
|
||||
UI_COLOR_CONFIG(GUI_COLOR_INSTR_SID3,_("SID3"));
|
||||
UI_COLOR_CONFIG(GUI_COLOR_INSTR_UNKNOWN,_("Other/Unknown"));
|
||||
ImGui::TreePop();
|
||||
|
|
@ -4820,6 +4861,7 @@ void FurnaceGUI::readConfig(DivConfig& conf, FurnaceGUISettingGroups groups) {
|
|||
|
||||
if (groups&GUI_SETTINGS_BEHAVIOR) {
|
||||
settings.soloAction=conf.getInt("soloAction",0);
|
||||
settings.ctrlWheelModifier=conf.getInt("ctrlWheelModifier",0);
|
||||
settings.pullDeleteBehavior=conf.getInt("pullDeleteBehavior",1);
|
||||
settings.wrapHorizontal=conf.getInt("wrapHorizontal",0);
|
||||
settings.wrapVertical=conf.getInt("wrapVertical",0);
|
||||
|
|
@ -4999,6 +5041,7 @@ void FurnaceGUI::readConfig(DivConfig& conf, FurnaceGUISettingGroups groups) {
|
|||
settings.opnbCore=conf.getInt("opnbCore",1);
|
||||
settings.opl2Core=conf.getInt("opl2Core",0);
|
||||
settings.opl3Core=conf.getInt("opl3Core",0);
|
||||
settings.opl4Core=conf.getInt("opl4Core",0);
|
||||
settings.esfmCore=conf.getInt("esfmCore",0);
|
||||
settings.opllCore=conf.getInt("opllCore",0);
|
||||
settings.ayCore=conf.getInt("ayCore",0);
|
||||
|
|
@ -5027,6 +5070,7 @@ void FurnaceGUI::readConfig(DivConfig& conf, FurnaceGUISettingGroups groups) {
|
|||
settings.opnbCoreRender=conf.getInt("opnbCoreRender",1);
|
||||
settings.opl2CoreRender=conf.getInt("opl2CoreRender",0);
|
||||
settings.opl3CoreRender=conf.getInt("opl3CoreRender",0);
|
||||
settings.opl4CoreRender=conf.getInt("opl4CoreRender",0);
|
||||
settings.esfmCoreRender=conf.getInt("esfmCoreRender",0);
|
||||
settings.opllCoreRender=conf.getInt("opllCoreRender",0);
|
||||
settings.ayCoreRender=conf.getInt("ayCoreRender",0);
|
||||
|
|
@ -5072,6 +5116,7 @@ void FurnaceGUI::readConfig(DivConfig& conf, FurnaceGUISettingGroups groups) {
|
|||
clampSetting(settings.opnbCore,0,2);
|
||||
clampSetting(settings.opl2Core,0,2);
|
||||
clampSetting(settings.opl3Core,0,2);
|
||||
clampSetting(settings.opl4Core,0,1);
|
||||
clampSetting(settings.esfmCore,0,1);
|
||||
clampSetting(settings.opllCore,0,1);
|
||||
clampSetting(settings.ayCore,0,1);
|
||||
|
|
@ -5098,6 +5143,7 @@ void FurnaceGUI::readConfig(DivConfig& conf, FurnaceGUISettingGroups groups) {
|
|||
clampSetting(settings.opnbCoreRender,0,2);
|
||||
clampSetting(settings.opl2CoreRender,0,2);
|
||||
clampSetting(settings.opl3CoreRender,0,2);
|
||||
clampSetting(settings.opl4CoreRender,0,1);
|
||||
clampSetting(settings.esfmCoreRender,0,1);
|
||||
clampSetting(settings.opllCoreRender,0,1);
|
||||
clampSetting(settings.ayCoreRender,0,1);
|
||||
|
|
@ -5118,6 +5164,7 @@ void FurnaceGUI::readConfig(DivConfig& conf, FurnaceGUISettingGroups groups) {
|
|||
clampSetting(settings.patRowsBase,0,1);
|
||||
clampSetting(settings.orderRowsBase,0,1);
|
||||
clampSetting(settings.soloAction,0,2);
|
||||
clampSetting(settings.ctrlWheelModifier,0,3);
|
||||
clampSetting(settings.pullDeleteBehavior,0,1);
|
||||
clampSetting(settings.wrapHorizontal,0,2);
|
||||
clampSetting(settings.wrapVertical,0,3);
|
||||
|
|
@ -5405,6 +5452,7 @@ void FurnaceGUI::writeConfig(DivConfig& conf, FurnaceGUISettingGroups groups) {
|
|||
// behavior
|
||||
if (groups&GUI_SETTINGS_BEHAVIOR) {
|
||||
conf.set("soloAction",settings.soloAction);
|
||||
conf.set("ctrlWheelModifier",settings.ctrlWheelModifier);
|
||||
conf.set("pullDeleteBehavior",settings.pullDeleteBehavior);
|
||||
conf.set("wrapHorizontal",settings.wrapHorizontal);
|
||||
conf.set("wrapVertical",settings.wrapVertical);
|
||||
|
|
@ -5588,6 +5636,7 @@ void FurnaceGUI::writeConfig(DivConfig& conf, FurnaceGUISettingGroups groups) {
|
|||
conf.set("opnbCore",settings.opnbCore);
|
||||
conf.set("opl2Core",settings.opl2Core);
|
||||
conf.set("opl3Core",settings.opl3Core);
|
||||
conf.set("opl4Core",settings.opl4Core);
|
||||
conf.set("esfmCore",settings.esfmCore);
|
||||
conf.set("opllCore",settings.opllCore);
|
||||
conf.set("ayCore",settings.ayCore);
|
||||
|
|
@ -5616,6 +5665,7 @@ void FurnaceGUI::writeConfig(DivConfig& conf, FurnaceGUISettingGroups groups) {
|
|||
conf.set("opnbCoreRender",settings.opnbCoreRender);
|
||||
conf.set("opl2CoreRender",settings.opl2CoreRender);
|
||||
conf.set("opl3CoreRender",settings.opl3CoreRender);
|
||||
conf.set("opl4CoreRender",settings.opl4CoreRender);
|
||||
conf.set("esfmCoreRender",settings.esfmCoreRender);
|
||||
conf.set("opllCoreRender",settings.opllCoreRender);
|
||||
conf.set("ayCoreRender",settings.ayCoreRender);
|
||||
|
|
@ -5679,6 +5729,7 @@ void FurnaceGUI::commitSettings() {
|
|||
settings.opnbCore!=e->getConfInt("opnbCore",1) ||
|
||||
settings.opl2Core!=e->getConfInt("opl2Core",0) ||
|
||||
settings.opl3Core!=e->getConfInt("opl3Core",0) ||
|
||||
settings.opl4Core!=e->getConfInt("opl4Core",0) ||
|
||||
settings.esfmCore!=e->getConfInt("esfmCore",0) ||
|
||||
settings.opllCore!=e->getConfInt("opllCore",0) ||
|
||||
settings.ayCore!=e->getConfInt("ayCore",0) ||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ void FurnaceGUI::drawSubSongs(bool asChild) {
|
|||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Selectable(id,i==e->getCurrentSubSong())) {
|
||||
makeCursorUndo();
|
||||
e->changeSongP(i);
|
||||
updateScroll(0);
|
||||
oldRow=0;
|
||||
|
|
@ -72,6 +73,7 @@ void FurnaceGUI::drawSubSongs(bool asChild) {
|
|||
if (!e->addSubSong()) {
|
||||
showError(_("too many subsongs!"));
|
||||
} else {
|
||||
makeCursorUndo();
|
||||
e->changeSongP(e->song.subsong.size()-1);
|
||||
updateScroll(0);
|
||||
oldRow=0;
|
||||
|
|
@ -92,6 +94,7 @@ void FurnaceGUI::drawSubSongs(bool asChild) {
|
|||
if (!e->duplicateSubSong(e->getCurrentSubSong())) {
|
||||
showError(_("too many subsongs!"));
|
||||
} else {
|
||||
makeCursorUndo();
|
||||
e->changeSongP(e->song.subsong.size()-1);
|
||||
updateScroll(0);
|
||||
oldRow=0;
|
||||
|
|
|
|||
|
|
@ -1970,6 +1970,7 @@ bool FurnaceGUI::drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& fl
|
|||
echoFilter[7]=flags.getInt("echoFilter7",0);
|
||||
|
||||
bool interpolationOff=flags.getBool("interpolationOff",false);
|
||||
bool antiClick=flags.getBool("antiClick",true);
|
||||
|
||||
ImGui::Text(_("Volume scale:"));
|
||||
if (CWSliderInt(_("Left##VolScaleL"),&vsL,0,127)) {
|
||||
|
|
@ -2090,6 +2091,10 @@ bool FurnaceGUI::drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& fl
|
|||
altered=true;
|
||||
}
|
||||
|
||||
if (ImGui::Checkbox(_("Anti-click"),&antiClick)) {
|
||||
altered=true;
|
||||
}
|
||||
|
||||
if (altered) {
|
||||
e->lockSave([&]() {
|
||||
flags.set("volScaleL",127-vsL);
|
||||
|
|
@ -2109,6 +2114,7 @@ bool FurnaceGUI::drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& fl
|
|||
flags.set("echoFilter7",echoFilter[7]);
|
||||
flags.set("echoMask",echoMask);
|
||||
flags.set("interpolationOff",interpolationOff);
|
||||
flags.set("antiClick",antiClick);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -2324,6 +2330,29 @@ bool FurnaceGUI::drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& fl
|
|||
}
|
||||
break;
|
||||
}
|
||||
case DIV_SYSTEM_SUPERVISION: {
|
||||
bool swapDuty=flags.getInt("swapDuty",true);
|
||||
|
||||
if (ImGui::Checkbox(_("Swap noise duty cycles"),&swapDuty)) {
|
||||
altered=true;
|
||||
}
|
||||
|
||||
bool sqStereo=flags.getInt("sqStereo",false);
|
||||
|
||||
if (ImGui::Checkbox(_("Stereo pulse waves"),&sqStereo)) {
|
||||
altered=true;
|
||||
}
|
||||
|
||||
if (altered) {
|
||||
e->lockSave([&]() {
|
||||
flags.set("swapDuty",(int)swapDuty);
|
||||
});
|
||||
e->lockSave([&]() {
|
||||
flags.set("sqStereo",(int)sqStereo);
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_SYSTEM_SM8521:/* {
|
||||
bool noAntiClick=flags.getBool("noAntiClick",false);
|
||||
|
||||
|
|
@ -2514,6 +2543,67 @@ bool FurnaceGUI::drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& fl
|
|||
}
|
||||
break;
|
||||
}
|
||||
case DIV_SYSTEM_OPL4:
|
||||
case DIV_SYSTEM_OPL4_DRUMS: {
|
||||
int clockSel=flags.getInt("clockSel",0);
|
||||
int ramSize=flags.getInt("ramSize",0);
|
||||
|
||||
ImGui::Text(_("Clock rate:"));
|
||||
ImGui::Indent();
|
||||
if (ImGui::RadioButton(_("33.8688MHz"),clockSel==0)) {
|
||||
clockSel=0;
|
||||
altered=true;
|
||||
}
|
||||
if (ImGui::RadioButton(_("28.64MHz (NTSC)"),clockSel==1)) {
|
||||
clockSel=1;
|
||||
altered=true;
|
||||
}
|
||||
if (ImGui::RadioButton(_("28.38MHz (PAL)"),clockSel==2)) {
|
||||
clockSel=2;
|
||||
altered=true;
|
||||
}
|
||||
ImGui::Unindent();
|
||||
|
||||
ImGui::Text(_("RAM size:"));
|
||||
ImGui::Indent();
|
||||
if (ImGui::RadioButton(_("4MB"),ramSize==0)) {
|
||||
ramSize=0;
|
||||
altered=true;
|
||||
}
|
||||
if (ImGui::RadioButton(_("2MB"),ramSize==1)) {
|
||||
ramSize=1;
|
||||
altered=true;
|
||||
}
|
||||
if (ImGui::RadioButton(_("1MB"),ramSize==2)) {
|
||||
ramSize=2;
|
||||
altered=true;
|
||||
}
|
||||
if (ImGui::RadioButton(_("640KB"),ramSize==3)) {
|
||||
ramSize=3;
|
||||
altered=true;
|
||||
}
|
||||
if (ImGui::RadioButton(_("512KB"),ramSize==4)) {
|
||||
ramSize=4;
|
||||
altered=true;
|
||||
}
|
||||
if (ImGui::RadioButton(_("256KB"),ramSize==5)) {
|
||||
ramSize=5;
|
||||
altered=true;
|
||||
}
|
||||
if (ImGui::RadioButton(_("128KB"),ramSize==6)) {
|
||||
ramSize=6;
|
||||
altered=true;
|
||||
}
|
||||
ImGui::Unindent();
|
||||
|
||||
if (altered) {
|
||||
e->lockSave([&]() {
|
||||
flags.set("clockSel",clockSel);
|
||||
flags.set("ramSize",ramSize);
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_SYSTEM_SWAN:
|
||||
case DIV_SYSTEM_BUBSYS_WSG:
|
||||
case DIV_SYSTEM_PET:
|
||||
|
|
@ -2522,6 +2612,7 @@ bool FurnaceGUI::drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& fl
|
|||
case DIV_SYSTEM_C219:
|
||||
case DIV_SYSTEM_BIFURCATOR:
|
||||
case DIV_SYSTEM_POWERNOISE:
|
||||
case DIV_SYSTEM_UPD1771C:
|
||||
break;
|
||||
case DIV_SYSTEM_YMU759:
|
||||
case DIV_SYSTEM_ESFM:
|
||||
|
|
|
|||
|
|
@ -279,6 +279,12 @@ const char* FurnaceGUI::getSystemPartNumber(DivSystem sys, DivConfig& flags) {
|
|||
break;
|
||||
case DIV_SYSTEM_ESFM:
|
||||
return "ES1xxx";
|
||||
case DIV_SYSTEM_SUPERVISION:
|
||||
return "Watara Supervision";
|
||||
break;
|
||||
case DIV_SYSTEM_UPD1771C:
|
||||
return "μPD1771C";
|
||||
break;
|
||||
default:
|
||||
return FurnaceGUI::getSystemName(sys);
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -475,7 +475,8 @@ void FurnaceGUI::drawUserPresets() {
|
|||
ImGui::SetTooltip(_(
|
||||
"insert additional settings in `option=value` format.\n"
|
||||
"available options:\n"
|
||||
"- tickRate"
|
||||
"- tickRate \n"
|
||||
"- chanMask \n"
|
||||
));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -948,6 +948,7 @@ void FurnaceGUI::drawWaveEdit() {
|
|||
wave->len=waveGenScaleX;
|
||||
MARK_MODIFIED;
|
||||
});
|
||||
e->notifyWaveChange(curWave);
|
||||
}
|
||||
|
||||
ImGui::TableNextRow();
|
||||
|
|
@ -966,6 +967,7 @@ void FurnaceGUI::drawWaveEdit() {
|
|||
wave->max=waveGenScaleY-1;
|
||||
MARK_MODIFIED;
|
||||
});
|
||||
e->notifyWaveChange(curWave);
|
||||
}
|
||||
|
||||
ImGui::TableNextRow();
|
||||
|
|
@ -988,6 +990,7 @@ void FurnaceGUI::drawWaveEdit() {
|
|||
}
|
||||
MARK_MODIFIED;
|
||||
});
|
||||
e->notifyWaveChange(curWave);
|
||||
}
|
||||
|
||||
ImGui::TableNextRow();
|
||||
|
|
@ -1005,6 +1008,7 @@ void FurnaceGUI::drawWaveEdit() {
|
|||
}
|
||||
MARK_MODIFIED;
|
||||
});
|
||||
e->notifyWaveChange(curWave);
|
||||
}
|
||||
|
||||
ImGui::TableNextRow();
|
||||
|
|
@ -1031,6 +1035,7 @@ void FurnaceGUI::drawWaveEdit() {
|
|||
}
|
||||
MARK_MODIFIED;
|
||||
});
|
||||
e->notifyWaveChange(curWave);
|
||||
}
|
||||
|
||||
ImGui::TableNextRow();
|
||||
|
|
@ -1050,6 +1055,7 @@ void FurnaceGUI::drawWaveEdit() {
|
|||
}
|
||||
MARK_MODIFIED;
|
||||
});
|
||||
e->notifyWaveChange(curWave);
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
|
|
@ -1093,6 +1099,7 @@ void FurnaceGUI::drawWaveEdit() {
|
|||
}
|
||||
MARK_MODIFIED;
|
||||
});
|
||||
e->notifyWaveChange(curWave);
|
||||
}
|
||||
if (ImGui::Button(_("Invert"),buttonSizeHalf)) {
|
||||
e->lockEngine([this,wave]() {
|
||||
|
|
@ -1101,6 +1108,7 @@ void FurnaceGUI::drawWaveEdit() {
|
|||
}
|
||||
MARK_MODIFIED;
|
||||
});
|
||||
e->notifyWaveChange(curWave);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(_("Reverse"),buttonSizeHalf)) {
|
||||
|
|
@ -1113,6 +1121,7 @@ void FurnaceGUI::drawWaveEdit() {
|
|||
}
|
||||
MARK_MODIFIED;
|
||||
});
|
||||
e->notifyWaveChange(curWave);
|
||||
}
|
||||
|
||||
if (ImGui::Button(_("Half"),buttonSizeHalf)) {
|
||||
|
|
@ -1122,6 +1131,7 @@ void FurnaceGUI::drawWaveEdit() {
|
|||
for (int i=0; i<wave->len; i++) {
|
||||
wave->data[i]=origData[i>>1];
|
||||
}
|
||||
e->notifyWaveChange(curWave);
|
||||
MARK_MODIFIED;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
|
|
@ -1132,6 +1142,7 @@ void FurnaceGUI::drawWaveEdit() {
|
|||
for (int i=0; i<wave->len; i++) {
|
||||
wave->data[i]=origData[(i*2)%wave->len];
|
||||
}
|
||||
e->notifyWaveChange(curWave);
|
||||
MARK_MODIFIED;
|
||||
}
|
||||
|
||||
|
|
@ -1146,6 +1157,7 @@ void FurnaceGUI::drawWaveEdit() {
|
|||
}
|
||||
MARK_MODIFIED;
|
||||
});
|
||||
e->notifyWaveChange(curWave);
|
||||
}
|
||||
if (ImGui::Button(_("Randomize"),buttonSize)) {
|
||||
if (wave->max>0) e->lockEngine([this,wave]() {
|
||||
|
|
@ -1154,6 +1166,7 @@ void FurnaceGUI::drawWaveEdit() {
|
|||
}
|
||||
MARK_MODIFIED;
|
||||
});
|
||||
e->notifyWaveChange(curWave);
|
||||
}
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// not auto-generated. update every time you change icons.ttf!
|
||||
|
||||
#define ICON_MIN_FUR 0xe0f0
|
||||
#define ICON_MAX_FUR 0xe161
|
||||
#define ICON_MAX_FUR 0xe164
|
||||
|
||||
// test
|
||||
#define ICON_FUR_TEST0 u8"\ue0f0"
|
||||
|
|
@ -75,7 +75,9 @@
|
|||
#define ICON_FUR_INS_GBA_MINMOD u8"\ue15f"
|
||||
#define ICON_FUR_INS_BIFURCATOR u8"\ue160"
|
||||
#define ICON_FUR_INS_SID2 u8"\ue161"
|
||||
#define ICON_FUR_INS_SID3 u8"\ue162"
|
||||
#define ICON_FUR_INS_SUPERVISION u8"\ue162"
|
||||
#define ICON_FUR_INS_UPD1771C u8"\ue163"
|
||||
#define ICON_FUR_INS_SID3 u8"\ue164"
|
||||
|
||||
// sample editor
|
||||
#define ICON_FUR_SAMPLE_APPLY_SILENCE u8"\ue136"
|
||||
|
|
|
|||
|
|
@ -341,6 +341,7 @@ TAParamResult pVersion(String) {
|
|||
printf("- NDS sound emulator by cam900 (zlib license)\n");
|
||||
printf("- SID2 emulator by LTVA (GPLv2, modification of reSID emulator)\n");
|
||||
printf("- SID3 emulator by LTVA (MIT)\n");
|
||||
printf("- openMSX YMF278 emulator (modified version) by the openMSX developers (GPLv2)\n");
|
||||
return TA_PARAM_QUIT;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue