Merge branch 'master' of https://github.com/tildearrow/furnace into k053260
This commit is contained in:
commit
ac8db58cbf
127 changed files with 2967 additions and 1338 deletions
|
|
@ -236,6 +236,8 @@ enum DivDispatchCmds {
|
|||
|
||||
DIV_CMD_NES_LINEAR_LENGTH,
|
||||
|
||||
DIV_CMD_EXTERNAL, // (value)
|
||||
|
||||
DIV_ALWAYS_SET_VOLUME, // () -> alwaysSetVol
|
||||
|
||||
DIV_CMD_MAX
|
||||
|
|
|
|||
|
|
@ -173,6 +173,7 @@ void DivDispatchContainer::fillBuf(size_t runtotal, size_t offset, size_t size)
|
|||
if (bbIn[i]==NULL) continue;
|
||||
if (bb[i]==NULL) continue;
|
||||
for (size_t j=0; j<runtotal; j++) {
|
||||
if (bbIn[i][j]==temp[i]) continue;
|
||||
temp[i]=bbIn[i][j];
|
||||
blip_add_delta_fast(bb[i],j,temp[i]-prevSample[i]);
|
||||
prevSample[i]=temp[i];
|
||||
|
|
@ -183,6 +184,7 @@ void DivDispatchContainer::fillBuf(size_t runtotal, size_t offset, size_t size)
|
|||
if (bbIn[i]==NULL) continue;
|
||||
if (bb[i]==NULL) continue;
|
||||
for (size_t j=0; j<runtotal; j++) {
|
||||
if (bbIn[i][j]==temp[i]) continue;
|
||||
temp[i]=bbIn[i][j];
|
||||
blip_add_delta(bb[i],j,temp[i]-prevSample[i]);
|
||||
prevSample[i]=temp[i];
|
||||
|
|
@ -274,12 +276,12 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
|
|||
break;
|
||||
case DIV_SYSTEM_C64_6581:
|
||||
dispatch=new DivPlatformC64;
|
||||
((DivPlatformC64*)dispatch)->setFP(eng->getConfInt("c64Core",1)==1);
|
||||
((DivPlatformC64*)dispatch)->setCore(eng->getConfInt("c64Core",0));
|
||||
((DivPlatformC64*)dispatch)->setChipModel(true);
|
||||
break;
|
||||
case DIV_SYSTEM_C64_8580:
|
||||
dispatch=new DivPlatformC64;
|
||||
((DivPlatformC64*)dispatch)->setFP(eng->getConfInt("c64Core",1)==1);
|
||||
((DivPlatformC64*)dispatch)->setCore(eng->getConfInt("c64Core",0));
|
||||
((DivPlatformC64*)dispatch)->setChipModel(false);
|
||||
break;
|
||||
case DIV_SYSTEM_YM2151:
|
||||
|
|
|
|||
|
|
@ -1678,6 +1678,51 @@ int DivEngine::addSubSong() {
|
|||
return song.subsong.size()-1;
|
||||
}
|
||||
|
||||
int DivEngine::duplicateSubSong(int index) {
|
||||
if (song.subsong.size()>=127) return -1;
|
||||
BUSY_BEGIN;
|
||||
saveLock.lock();
|
||||
DivSubSong* theCopy=new DivSubSong;
|
||||
DivSubSong* theOrig=song.subsong[index];
|
||||
|
||||
theCopy->name=theOrig->name;
|
||||
theCopy->notes=theOrig->notes;
|
||||
theCopy->hilightA=theOrig->hilightA;
|
||||
theCopy->hilightB=theOrig->hilightB;
|
||||
theCopy->timeBase=theOrig->timeBase;
|
||||
theCopy->arpLen=theOrig->arpLen;
|
||||
theCopy->speeds=theOrig->speeds;
|
||||
theCopy->virtualTempoN=theOrig->virtualTempoN;
|
||||
theCopy->virtualTempoD=theOrig->virtualTempoD;
|
||||
theCopy->hz=theOrig->hz;
|
||||
theCopy->patLen=theOrig->patLen;
|
||||
theCopy->ordersLen=theOrig->ordersLen;
|
||||
theCopy->orders=theOrig->orders;
|
||||
|
||||
memcpy(theCopy->chanShow,theOrig->chanShow,DIV_MAX_CHANS*sizeof(bool));
|
||||
memcpy(theCopy->chanCollapse,theOrig->chanCollapse,DIV_MAX_CHANS);
|
||||
|
||||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||
theCopy->chanName[i]=theOrig->chanName[i];
|
||||
theCopy->chanShortName[i]=theOrig->chanShortName[i];
|
||||
|
||||
theCopy->pat[i].effectCols=theOrig->pat[i].effectCols;
|
||||
|
||||
for (int j=0; j<DIV_MAX_PATTERNS; j++) {
|
||||
if (theOrig->pat[i].data[j]==NULL) continue;
|
||||
DivPattern* origPat=theOrig->pat[i].getPattern(j,false);
|
||||
DivPattern* copyPat=theCopy->pat[i].getPattern(j,true);
|
||||
origPat->copyOn(copyPat);
|
||||
}
|
||||
}
|
||||
|
||||
song.subsong.push_back(theCopy);
|
||||
|
||||
saveLock.unlock();
|
||||
BUSY_END;
|
||||
return song.subsong.size()-1;
|
||||
}
|
||||
|
||||
bool DivEngine::removeSubSong(int index) {
|
||||
if (song.subsong.size()<=1) return false;
|
||||
stop();
|
||||
|
|
|
|||
|
|
@ -54,8 +54,8 @@
|
|||
#define EXTERN_BUSY_BEGIN_SOFT e->softLocked=true; e->isBusy.lock();
|
||||
#define EXTERN_BUSY_END e->isBusy.unlock(); e->softLocked=false;
|
||||
|
||||
#define DIV_VERSION "dev159"
|
||||
#define DIV_ENGINE_VERSION 159
|
||||
#define DIV_VERSION "dev160"
|
||||
#define DIV_ENGINE_VERSION 160
|
||||
// for imports
|
||||
#define DIV_VERSION_MOD 0xff01
|
||||
#define DIV_VERSION_FC 0xff02
|
||||
|
|
@ -1052,6 +1052,9 @@ class DivEngine {
|
|||
// add subsong
|
||||
int addSubSong();
|
||||
|
||||
// duplicate subsong
|
||||
int duplicateSubSong(int index);
|
||||
|
||||
// remove subsong
|
||||
bool removeSubSong(int index);
|
||||
|
||||
|
|
|
|||
|
|
@ -1047,6 +1047,11 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
|||
ds.systemFlags[0].set("dpcmMode",false);
|
||||
}
|
||||
|
||||
// C64 no key priority
|
||||
if (ds.system[0]==DIV_SYSTEM_C64_8580 || ds.system[0]==DIV_SYSTEM_C64_6581) {
|
||||
ds.systemFlags[0].set("keyPriority",false);
|
||||
}
|
||||
|
||||
ds.systemName=getSongSystemLegacyName(ds,!getConfInt("noMultiSystem",0));
|
||||
|
||||
if (active) quitDispatch();
|
||||
|
|
@ -2927,6 +2932,15 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
}
|
||||
}
|
||||
|
||||
// C64 key priority compat
|
||||
if (ds.version<160) {
|
||||
for (int i=0; i<ds.systemLen; i++) {
|
||||
if (ds.system[i]==DIV_SYSTEM_C64_8580 || ds.system[i]==DIV_SYSTEM_C64_6581) {
|
||||
ds.systemFlags[i].set("keyPriority",false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (active) quitDispatch();
|
||||
BUSY_BEGIN_SOFT;
|
||||
saveLock.lock();
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
#include "../engine.h"
|
||||
#include "sound/c64_fp/siddefs-fp.h"
|
||||
#include <math.h>
|
||||
#include "../../ta-log.h"
|
||||
|
||||
#define rWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} }
|
||||
|
||||
|
|
@ -64,34 +65,45 @@ const char** DivPlatformC64::getRegisterSheet() {
|
|||
}
|
||||
|
||||
void DivPlatformC64::acquire(short** buf, size_t len) {
|
||||
int dcOff=isFP?0:sid.get_dc(0);
|
||||
int dcOff=(sidCore)?0:sid->get_dc(0);
|
||||
for (size_t i=0; i<len; i++) {
|
||||
if (!writes.empty()) {
|
||||
QueuedWrite w=writes.front();
|
||||
if (isFP) {
|
||||
sid_fp.write(w.addr,w.val);
|
||||
if (sidCore==2) {
|
||||
dSID_write(sid_d,w.addr,w.val);
|
||||
} else if (sidCore==1) {
|
||||
sid_fp->write(w.addr,w.val);
|
||||
} else {
|
||||
sid.write(w.addr,w.val);
|
||||
};
|
||||
sid->write(w.addr,w.val);
|
||||
}
|
||||
regPool[w.addr&0x1f]=w.val;
|
||||
writes.pop();
|
||||
}
|
||||
if (isFP) {
|
||||
sid_fp.clock(4,&buf[0][i]);
|
||||
if (sidCore==2) {
|
||||
double o=dSID_render(sid_d);
|
||||
buf[0][i]=32767*CLAMP(o,-1.0,1.0);
|
||||
if (++writeOscBuf>=4) {
|
||||
writeOscBuf=0;
|
||||
oscBuf[0]->data[oscBuf[0]->needle++]=(sid_fp.lastChanOut[0]-dcOff)>>5;
|
||||
oscBuf[1]->data[oscBuf[1]->needle++]=(sid_fp.lastChanOut[1]-dcOff)>>5;
|
||||
oscBuf[2]->data[oscBuf[2]->needle++]=(sid_fp.lastChanOut[2]-dcOff)>>5;
|
||||
oscBuf[0]->data[oscBuf[0]->needle++]=sid_d->lastOut[0];
|
||||
oscBuf[1]->data[oscBuf[1]->needle++]=sid_d->lastOut[1];
|
||||
oscBuf[2]->data[oscBuf[2]->needle++]=sid_d->lastOut[2];
|
||||
}
|
||||
} else if (sidCore==1) {
|
||||
sid_fp->clock(4,&buf[0][i]);
|
||||
if (++writeOscBuf>=4) {
|
||||
writeOscBuf=0;
|
||||
oscBuf[0]->data[oscBuf[0]->needle++]=(sid_fp->lastChanOut[0]-dcOff)>>5;
|
||||
oscBuf[1]->data[oscBuf[1]->needle++]=(sid_fp->lastChanOut[1]-dcOff)>>5;
|
||||
oscBuf[2]->data[oscBuf[2]->needle++]=(sid_fp->lastChanOut[2]-dcOff)>>5;
|
||||
}
|
||||
} else {
|
||||
sid.clock();
|
||||
buf[0][i]=sid.output();
|
||||
sid->clock();
|
||||
buf[0][i]=sid->output();
|
||||
if (++writeOscBuf>=16) {
|
||||
writeOscBuf=0;
|
||||
oscBuf[0]->data[oscBuf[0]->needle++]=(sid.last_chan_out[0]-dcOff)>>5;
|
||||
oscBuf[1]->data[oscBuf[1]->needle++]=(sid.last_chan_out[1]-dcOff)>>5;
|
||||
oscBuf[2]->data[oscBuf[2]->needle++]=(sid.last_chan_out[2]-dcOff)>>5;
|
||||
oscBuf[0]->data[oscBuf[0]->needle++]=(sid->last_chan_out[0]-dcOff)>>5;
|
||||
oscBuf[1]->data[oscBuf[1]->needle++]=(sid->last_chan_out[1]-dcOff)>>5;
|
||||
oscBuf[2]->data[oscBuf[2]->needle++]=(sid->last_chan_out[2]-dcOff)>>5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -106,7 +118,9 @@ void DivPlatformC64::updateFilter() {
|
|||
|
||||
void DivPlatformC64::tick(bool sysTick) {
|
||||
bool willUpdateFilter=false;
|
||||
for (int i=0; i<3; i++) {
|
||||
for (int _i=0; _i<3; _i++) {
|
||||
int i=chanOrder[_i];
|
||||
|
||||
chan[i].std.next();
|
||||
if (chan[i].std.vol.had) {
|
||||
DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_C64);
|
||||
|
|
@ -179,8 +193,8 @@ void DivPlatformC64::tick(bool sysTick) {
|
|||
if (--chan[i].testWhen<1) {
|
||||
if (!chan[i].resetMask && !chan[i].inPorta) {
|
||||
DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_C64);
|
||||
rWrite(i*7+5,0);
|
||||
rWrite(i*7+6,0);
|
||||
rWrite(i*7+5,testAD);
|
||||
rWrite(i*7+6,testSR);
|
||||
rWrite(i*7+4,(chan[i].wave<<4)|(ins->c64.noTest?0:8)|(chan[i].test<<3)|(chan[i].ring<<2)|(chan[i].sync<<1));
|
||||
}
|
||||
}
|
||||
|
|
@ -212,6 +226,7 @@ void DivPlatformC64::tick(bool sysTick) {
|
|||
}
|
||||
|
||||
int DivPlatformC64::dispatch(DivCommand c) {
|
||||
if (c.chan>2) return 0;
|
||||
switch (c.cmd) {
|
||||
case DIV_CMD_NOTE_ON: {
|
||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_C64);
|
||||
|
|
@ -249,6 +264,16 @@ int DivPlatformC64::dispatch(DivCommand c) {
|
|||
if (chan[c.chan].insChanged) {
|
||||
chan[c.chan].insChanged=false;
|
||||
}
|
||||
if (keyPriority) {
|
||||
if (chanOrder[1]==c.chan) {
|
||||
chanOrder[1]=chanOrder[2];
|
||||
chanOrder[2]=c.chan;
|
||||
} else if (chanOrder[0]==c.chan) {
|
||||
chanOrder[0]=chanOrder[1];
|
||||
chanOrder[1]=chanOrder[2];
|
||||
chanOrder[2]=c.chan;
|
||||
}
|
||||
}
|
||||
chan[c.chan].macroInit(ins);
|
||||
break;
|
||||
}
|
||||
|
|
@ -352,7 +377,7 @@ int DivPlatformC64::dispatch(DivCommand c) {
|
|||
break;
|
||||
case DIV_CMD_C64_CUTOFF:
|
||||
if (c.value>100) c.value=100;
|
||||
filtCut=c.value*2047/100;
|
||||
filtCut=(c.value+2)*2047/102;
|
||||
updateFilter();
|
||||
break;
|
||||
case DIV_CMD_C64_FINE_CUTOFF:
|
||||
|
|
@ -438,10 +463,17 @@ int DivPlatformC64::dispatch(DivCommand c) {
|
|||
|
||||
void DivPlatformC64::muteChannel(int ch, bool mute) {
|
||||
isMuted[ch]=mute;
|
||||
if (isFP) {
|
||||
sid_fp.mute(ch,mute);
|
||||
if (sidCore==2) {
|
||||
dSID_setMuteMask(
|
||||
sid_d,
|
||||
(isMuted[0]?0:1)|
|
||||
(isMuted[1]?0:2)|
|
||||
(isMuted[2]?0:4)
|
||||
);
|
||||
} else if (sidCore==1) {
|
||||
sid_fp->mute(ch,mute);
|
||||
} else {
|
||||
sid.set_is_muted(ch,mute);
|
||||
sid->set_is_muted(ch,mute);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -500,7 +532,7 @@ bool DivPlatformC64::getWantPreNote() {
|
|||
}
|
||||
|
||||
float DivPlatformC64::getPostAmp() {
|
||||
return isFP?3.0f:1.0f;
|
||||
return (sidCore==1)?3.0f:1.0f;
|
||||
}
|
||||
|
||||
void DivPlatformC64::reset() {
|
||||
|
|
@ -510,10 +542,20 @@ void DivPlatformC64::reset() {
|
|||
chan[i].std.setEngine(parent);
|
||||
}
|
||||
|
||||
if (isFP) {
|
||||
sid_fp.reset();
|
||||
if (sidCore==2) {
|
||||
dSID_init(sid_d,chipClock,rate,sidIs6581?6581:8580,needInitTables);
|
||||
dSID_setMuteMask(
|
||||
sid_d,
|
||||
(isMuted[0]?0:1)|
|
||||
(isMuted[1]?0:2)|
|
||||
(isMuted[2]?0:4)
|
||||
);
|
||||
needInitTables=false;
|
||||
} else if (sidCore==1) {
|
||||
sid_fp->reset();
|
||||
sid_fp->clockSilent(16000);
|
||||
} else {
|
||||
sid.reset();
|
||||
sid->reset();
|
||||
}
|
||||
memset(regPool,0,32);
|
||||
|
||||
|
|
@ -524,6 +566,10 @@ void DivPlatformC64::reset() {
|
|||
filtCut=2047;
|
||||
resetTime=1;
|
||||
vol=15;
|
||||
|
||||
chanOrder[0]=0;
|
||||
chanOrder[1]=1;
|
||||
chanOrder[2]=2;
|
||||
}
|
||||
|
||||
void DivPlatformC64::poke(unsigned int addr, unsigned short val) {
|
||||
|
|
@ -535,23 +581,11 @@ void DivPlatformC64::poke(std::vector<DivRegWrite>& wlist) {
|
|||
}
|
||||
|
||||
void DivPlatformC64::setChipModel(bool is6581) {
|
||||
if (is6581) {
|
||||
if (isFP) {
|
||||
sid_fp.setChipModel(reSIDfp::MOS6581);
|
||||
} else {
|
||||
sid.set_chip_model(MOS6581);
|
||||
}
|
||||
} else {
|
||||
if (isFP) {
|
||||
sid_fp.setChipModel(reSIDfp::MOS8580);
|
||||
} else {
|
||||
sid.set_chip_model(MOS8580);
|
||||
}
|
||||
}
|
||||
sidIs6581=is6581;
|
||||
}
|
||||
|
||||
void DivPlatformC64::setFP(bool fp) {
|
||||
isFP=fp;
|
||||
void DivPlatformC64::setCore(unsigned char which) {
|
||||
sidCore=which;
|
||||
}
|
||||
|
||||
void DivPlatformC64::setFlags(const DivConfig& flags) {
|
||||
|
|
@ -572,21 +606,58 @@ void DivPlatformC64::setFlags(const DivConfig& flags) {
|
|||
for (int i=0; i<3; i++) {
|
||||
oscBuf[i]->rate=rate/16;
|
||||
}
|
||||
if (isFP) {
|
||||
if (sidCore>0) {
|
||||
rate/=4;
|
||||
sid_fp.setSamplingParameters(chipClock,reSIDfp::DECIMATE,rate,0);
|
||||
if (sidCore==1) sid_fp->setSamplingParameters(chipClock,reSIDfp::DECIMATE,rate,0);
|
||||
}
|
||||
keyPriority=flags.getBool("keyPriority",true);
|
||||
testAD=((flags.getInt("testAttack",0)&15)<<4)|(flags.getInt("testDecay",0)&15);
|
||||
testSR=((flags.getInt("testSustain",0)&15)<<4)|(flags.getInt("testRelease",0)&15);
|
||||
}
|
||||
|
||||
int DivPlatformC64::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) {
|
||||
parent=p;
|
||||
dumpWrites=false;
|
||||
skipRegisterWrites=false;
|
||||
needInitTables=true;
|
||||
writeOscBuf=0;
|
||||
for (int i=0; i<3; i++) {
|
||||
isMuted[i]=false;
|
||||
oscBuf[i]=new DivDispatchOscBuffer;
|
||||
}
|
||||
|
||||
if (sidCore==2) {
|
||||
sid=NULL;
|
||||
sid_fp=NULL;
|
||||
sid_d=new struct SID_chip;
|
||||
} else if (sidCore==1) {
|
||||
sid=NULL;
|
||||
sid_fp=new reSIDfp::SID;
|
||||
sid_d=NULL;
|
||||
} else {
|
||||
sid=new SID;
|
||||
sid_fp=NULL;
|
||||
sid_d=NULL;
|
||||
}
|
||||
|
||||
if (sidIs6581) {
|
||||
if (sidCore==2) {
|
||||
// do nothing
|
||||
} else if (sidCore==1) {
|
||||
sid_fp->setChipModel(reSIDfp::MOS6581);
|
||||
} else {
|
||||
sid->set_chip_model(MOS6581);
|
||||
}
|
||||
} else {
|
||||
if (sidCore==2) {
|
||||
// do nothing
|
||||
} else if (sidCore==1) {
|
||||
sid_fp->setChipModel(reSIDfp::MOS8580);
|
||||
} else {
|
||||
sid->set_chip_model(MOS8580);
|
||||
}
|
||||
}
|
||||
|
||||
setFlags(flags);
|
||||
|
||||
reset();
|
||||
|
|
@ -598,6 +669,9 @@ void DivPlatformC64::quit() {
|
|||
for (int i=0; i<3; i++) {
|
||||
delete oscBuf[i];
|
||||
}
|
||||
if (sid!=NULL) delete sid;
|
||||
if (sid_fp!=NULL) delete sid_fp;
|
||||
if (sid_d!=NULL) delete sid_d;
|
||||
}
|
||||
|
||||
DivPlatformC64::~DivPlatformC64() {
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
#include <queue>
|
||||
#include "sound/c64/sid.h"
|
||||
#include "sound/c64_fp/SID.h"
|
||||
#include "sound/c64_d/dsid.h"
|
||||
|
||||
class DivPlatformC64: public DivDispatch {
|
||||
struct Channel: public SharedChannel<signed char> {
|
||||
|
|
@ -64,11 +65,16 @@ class DivPlatformC64: public DivDispatch {
|
|||
|
||||
unsigned char filtControl, filtRes, vol;
|
||||
unsigned char writeOscBuf;
|
||||
unsigned char sidCore;
|
||||
int filtCut, resetTime;
|
||||
bool isFP;
|
||||
|
||||
SID sid;
|
||||
reSIDfp::SID sid_fp;
|
||||
bool keyPriority, sidIs6581, needInitTables;
|
||||
unsigned char chanOrder[3];
|
||||
unsigned char testAD, testSR;
|
||||
|
||||
SID* sid;
|
||||
reSIDfp::SID* sid_fp;
|
||||
struct SID_chip* sid_d;
|
||||
unsigned char regPool[32];
|
||||
|
||||
friend void putDispatchChip(void*,int);
|
||||
|
|
@ -101,7 +107,7 @@ class DivPlatformC64: public DivDispatch {
|
|||
const char** getRegisterSheet();
|
||||
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
|
||||
void setChipModel(bool is6581);
|
||||
void setFP(bool fp);
|
||||
void setCore(unsigned char which);
|
||||
void quit();
|
||||
~DivPlatformC64();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include "gb.h"
|
||||
#include "../engine.h"
|
||||
#include "../../ta-log.h"
|
||||
#include <math.h>
|
||||
|
||||
#define rWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); regPool[(a)&0x7f]=v; if (dumpWrites) {addWrite(a,v);} }
|
||||
|
|
@ -80,6 +81,7 @@ void DivPlatformGB::acquire(short** buf, size_t len) {
|
|||
}
|
||||
|
||||
void DivPlatformGB::updateWave() {
|
||||
logV("WAVE UPDATE");
|
||||
rWrite(0x1a,0);
|
||||
for (int i=0; i<16; i++) {
|
||||
int nibble1=ws.output[((i<<1)+antiClickWavePos)&31];
|
||||
|
|
@ -299,6 +301,7 @@ void DivPlatformGB::tick(bool sysTick) {
|
|||
}
|
||||
if (chan[i].keyOn) {
|
||||
if (i==2) { // wave
|
||||
rWrite(16+i*5,0x00);
|
||||
rWrite(16+i*5,0x80);
|
||||
rWrite(16+i*5+2,gbVolMap[chan[i].outVol]);
|
||||
} else {
|
||||
|
|
@ -664,9 +667,7 @@ void DivPlatformGB::setFlags(const DivConfig& flags) {
|
|||
}
|
||||
invertWave=flags.getBool("invertWave",true);
|
||||
enoughAlready=flags.getBool("enoughAlready",false);
|
||||
}
|
||||
|
||||
int DivPlatformGB::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) {
|
||||
chipClock=4194304;
|
||||
CHECK_CUSTOM_CLOCK;
|
||||
rate=chipClock/16;
|
||||
|
|
@ -675,6 +676,9 @@ int DivPlatformGB::init(DivEngine* p, int channels, int sugRate, const DivConfig
|
|||
oscBuf[i]=new DivDispatchOscBuffer;
|
||||
oscBuf[i]->rate=rate;
|
||||
}
|
||||
}
|
||||
|
||||
int DivPlatformGB::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) {
|
||||
parent=p;
|
||||
dumpWrites=false;
|
||||
skipRegisterWrites=false;
|
||||
|
|
|
|||
|
|
@ -207,7 +207,7 @@ void DivPlatformN163::tick(bool sysTick) {
|
|||
}
|
||||
}
|
||||
if (chan[i].std.wave.had) {
|
||||
if (chan[i].wave!=chan[i].std.wave.val) {
|
||||
if (chan[i].wave!=chan[i].std.wave.val || chan[i].ws.activeChanged()) {
|
||||
chan[i].wave=chan[i].std.wave.val;
|
||||
chan[i].ws.changeWave1(chan[i].wave);
|
||||
if (chan[i].waveMode&0x2) {
|
||||
|
|
|
|||
|
|
@ -423,7 +423,7 @@ const void* DivPlatformSegaPCM::getSampleMem(int index) {
|
|||
}
|
||||
|
||||
size_t DivPlatformSegaPCM::getSampleMemCapacity(int index) {
|
||||
return index == 0 ? 16777216 : 0;
|
||||
return index == 0 ? 2097152 : 0;
|
||||
}
|
||||
|
||||
size_t DivPlatformSegaPCM::getSampleMemUsage(int index) {
|
||||
|
|
@ -465,7 +465,7 @@ void DivPlatformSegaPCM::reset() {
|
|||
void DivPlatformSegaPCM::renderSamples(int sysID) {
|
||||
size_t memPos=0;
|
||||
|
||||
memset(sampleMem,0,16777216);
|
||||
memset(sampleMem,0,2097152);
|
||||
memset(sampleLoaded,0,256*sizeof(bool));
|
||||
memset(sampleOffSegaPCM,0,256*sizeof(unsigned int));
|
||||
memset(sampleEndSegaPCM,0,256);
|
||||
|
|
@ -482,7 +482,7 @@ void DivPlatformSegaPCM::renderSamples(int sysID) {
|
|||
}
|
||||
logV("- sample %d will be at %x with length %x",i,memPos,alignedSize);
|
||||
sampleLoaded[i]=true;
|
||||
if (memPos>=16777216) break;
|
||||
if (memPos>=2097152) break;
|
||||
sampleOffSegaPCM[i]=memPos;
|
||||
for (unsigned int j=0; j<alignedSize; j++) {
|
||||
if (j>=sample->samples) {
|
||||
|
|
@ -491,10 +491,10 @@ void DivPlatformSegaPCM::renderSamples(int sysID) {
|
|||
sampleMem[memPos++]=((unsigned char)sample->data8[j]+0x80);
|
||||
}
|
||||
sampleEndSegaPCM[i]=((memPos+0xff)>>8)-1;
|
||||
if (memPos>=16777216) break;
|
||||
if (memPos>=2097152) break;
|
||||
}
|
||||
logV(" and it ends in %d",sampleEndSegaPCM[i]);
|
||||
if (memPos>=16777216) break;
|
||||
if (memPos>=2097152) break;
|
||||
}
|
||||
sampleMemLen=memPos;
|
||||
}
|
||||
|
|
@ -522,10 +522,10 @@ int DivPlatformSegaPCM::init(DivEngine* p, int channels, int sugRate, const DivC
|
|||
isMuted[i]=false;
|
||||
oscBuf[i]=new DivDispatchOscBuffer;
|
||||
}
|
||||
sampleMem=new unsigned char[16777216];
|
||||
sampleMem=new unsigned char[2097152];
|
||||
pcm.set_bank(segapcm_device::BANK_12M|segapcm_device::BANK_MASKF8);
|
||||
pcm.set_read([this](unsigned int addr) -> unsigned char {
|
||||
return sampleMem[addr&0xffffff];
|
||||
return sampleMem[addr&0x1fffff];
|
||||
});
|
||||
setFlags(flags);
|
||||
reset();
|
||||
|
|
|
|||
19
src/engine/platform/sound/c64_d/LICENSE
Normal file
19
src/engine/platform/sound/c64_d/LICENSE
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
Copyright (c) 2021 DefleMask Team
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
11
src/engine/platform/sound/c64_d/README.md
Normal file
11
src/engine/platform/sound/c64_d/README.md
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
dSID
|
||||
===
|
||||
|
||||
This is the SID core used in DefleMask.
|
||||
|
||||
The project started as a very careful port from jsSID, comparing the wave
|
||||
output from both, ensuring they were exactly the same.
|
||||
|
||||
## License
|
||||
|
||||
MIT License
|
||||
367
src/engine/platform/sound/c64_d/dsid.c
Normal file
367
src/engine/platform/sound/c64_d/dsid.c
Normal file
|
|
@ -0,0 +1,367 @@
|
|||
#include "dsid.h"
|
||||
#include <stdio.h>
|
||||
#include <math.h> // INFINITY
|
||||
#include <stdlib.h>
|
||||
#include <string.h> // memset, memcpy
|
||||
|
||||
#define SID_OUT_SCALE (0x10000 * 3 * 16)
|
||||
|
||||
// CONTROL
|
||||
#define GAT 0x01
|
||||
#define SYN 0x02
|
||||
#define RNG 0x04
|
||||
#define TST 0x08
|
||||
#define TRI 0x10
|
||||
#define SAW 0x20
|
||||
#define PUL 0x40
|
||||
#define NOI 0x80
|
||||
|
||||
#define _HZ 0x10
|
||||
#define DECSUS 0x40
|
||||
#define ATK 0x80
|
||||
|
||||
// filter mode (high)
|
||||
#define LP 0x10
|
||||
#define BP 0x20
|
||||
#define HP 0x40
|
||||
#define OFF3 0x80
|
||||
|
||||
#define waveforms_add_sample(_id,_s) \
|
||||
sid->lastOut[_id]=(_s);
|
||||
|
||||
const int Aexp[256] = {
|
||||
1, 30, 30, 30, 30, 30, 30, 16, 16, 16, 16, 16, 16, 16, 16, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
|
||||
8, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
4, 4, 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
|
||||
|
||||
double cmbWF(int chn, int *wfa, int index, int differ6581, struct SID_globals *g) {
|
||||
if (differ6581 && g->model == 6581)
|
||||
index &= 0x7FF;
|
||||
|
||||
return wfa[index];
|
||||
}
|
||||
|
||||
void cCmbWF(int *wfa, double bitmul, double bstr, double trh) {
|
||||
for (int i = 0; i < 4096; i++) {
|
||||
wfa[i] = 0;
|
||||
for (int j = 0; j < 12; j++) {
|
||||
double blvl = 0;
|
||||
for (int k = 0; k < 12; k++) {
|
||||
blvl += (bitmul / pow(bstr, abs(k - j))) * (((i >> k) & 1) - 0.5);
|
||||
}
|
||||
wfa[i] += (blvl >= trh) ? pow(2, j) : 0;
|
||||
}
|
||||
wfa[i] *= 12;
|
||||
}
|
||||
}
|
||||
|
||||
void dSID_init(struct SID_chip* sid, double clockRate, double samplingRate, int model, unsigned char init_wf) {
|
||||
if (model == 6581) {
|
||||
sid->g.model = 6581;
|
||||
} else {
|
||||
sid->g.model = 8580;
|
||||
}
|
||||
|
||||
memset(sid->M,0,MemLen);
|
||||
memset(sid->SIDct, 0, sizeof(sid->SIDct));
|
||||
for (int i = 0; i < 3; i++) {
|
||||
for (int j = 0; j < 3; j++) {
|
||||
sid->SIDct[i].ch[j].Ast = _HZ;
|
||||
sid->SIDct[i].ch[j].nLFSR = 0x7FFFF8;
|
||||
sid->SIDct[i].ch[j].prevwfout = 0;
|
||||
}
|
||||
sid->SIDct[i].ch[0].FSW = 1;
|
||||
sid->SIDct[i].ch[1].FSW = 2;
|
||||
sid->SIDct[i].ch[2].FSW = 4;
|
||||
}
|
||||
|
||||
sid->g.ctfr = -2.0 * 3.14 * (12500.0 / 256.0) / samplingRate,
|
||||
sid->g.ctf_ratio_6581 = -2.0 * 3.14 * (samplingRate / 44100.0) * (20000.0 / 256.0) / samplingRate;
|
||||
sid->g.ckr = clockRate / samplingRate;
|
||||
|
||||
const double bAprd[16] = {9, 32 * 1, 63 * 1, 95 * 1, 149 * 1, 220 * 1,
|
||||
267 * 1, 313 * 1, 392 * 1, 977 * 1, 1954 * 1, 3126 * 1,
|
||||
3907 * 1, 11720 * 1, 19532 * 1, 31251 * 1};
|
||||
const int bAstp[16] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
|
||||
memcpy(&sid->g.Aprd, &bAprd, sizeof(bAprd));
|
||||
memcpy(&sid->g.Astp, &bAstp, sizeof(bAstp));
|
||||
if (init_wf) {
|
||||
cCmbWF(sid->g.trsaw, 0.8, 2.4, 0.64);
|
||||
cCmbWF(sid->g.pusaw, 1.4, 1.9, 0.68);
|
||||
cCmbWF(sid->g.Pulsetrsaw, 0.8, 2.5, 0.64);
|
||||
|
||||
for (int i = 0; i < 2048; i++) {
|
||||
double ctf = (double) i / 8.0 + 0.2;
|
||||
if (model == 8580) {
|
||||
ctf = 1 - exp(ctf * sid->g.ctfr);
|
||||
} else {
|
||||
if (ctf < 24) {
|
||||
ctf = 2.0 * sin(771.78 / samplingRate);
|
||||
} else {
|
||||
ctf = (44100.0 / samplingRate) - 1.263 * (44100.0 / samplingRate) * exp(ctf * sid->g.ctf_ratio_6581);
|
||||
}
|
||||
}
|
||||
sid->g.ctf_table[i] = ctf;
|
||||
}
|
||||
}
|
||||
|
||||
double prd0 = sid->g.ckr > 9 ? sid->g.ckr : 9;
|
||||
sid->g.Aprd[0] = prd0;
|
||||
sid->g.Astp[0] = ceil(prd0 / 9);
|
||||
}
|
||||
|
||||
double dSID_render(struct SID_chip* sid) {
|
||||
double flin = 0, output = 0;
|
||||
double wfout = 0;
|
||||
for (int chn = 0; chn < 3; chn++) {
|
||||
struct SIDVOICE *voic = &((struct SIDMEM *) (sid->M))->v[chn];
|
||||
double pgt = (sid->SIDct->ch[chn].Ast & GAT);
|
||||
uint8_t ctrl = voic->control;
|
||||
uint8_t wf = ctrl & 0xF0;
|
||||
uint8_t test = ctrl & TST;
|
||||
uint8_t SR = voic->susres;
|
||||
double tmp = 0;
|
||||
if (pgt != (ctrl & GAT)) {
|
||||
if (pgt) {
|
||||
sid->SIDct->ch[chn].Ast &= 0xFF - (GAT | ATK | DECSUS);
|
||||
} else {
|
||||
sid->SIDct->ch[chn].Ast = (GAT | ATK | DECSUS);
|
||||
if ((SR & 0xF) > (sid->SIDct->ch[chn].pSR & 0xF))
|
||||
tmp = 1;
|
||||
}
|
||||
}
|
||||
sid->SIDct->ch[chn].pSR = SR;
|
||||
sid->SIDct->ch[chn].rcnt += sid->g.ckr;
|
||||
if (sid->SIDct->ch[chn].rcnt >= 0x8000)
|
||||
sid->SIDct->ch[chn].rcnt -= 0x8000;
|
||||
|
||||
static double step;
|
||||
double prd;
|
||||
|
||||
if (sid->SIDct->ch[chn].Ast & ATK) {
|
||||
step = voic->attack;
|
||||
prd = sid->g.Aprd[(int) step];
|
||||
} else if (sid->SIDct->ch[chn].Ast & DECSUS) {
|
||||
step = voic->decay;
|
||||
prd = sid->g.Aprd[(int) step];
|
||||
} else {
|
||||
step = SR & 0xF;
|
||||
prd = sid->g.Aprd[(int) step];
|
||||
}
|
||||
step = sid->g.Astp[(int) step];
|
||||
if (sid->SIDct->ch[chn].rcnt >= prd && sid->SIDct->ch[chn].rcnt < prd + sid->g.ckr &&
|
||||
tmp == 0) {
|
||||
sid->SIDct->ch[chn].rcnt -= prd;
|
||||
if ((sid->SIDct->ch[chn].Ast & ATK) ||
|
||||
++sid->SIDct->ch[chn].expcnt == Aexp[(int) sid->SIDct->ch[chn].envcnt]) {
|
||||
if (!(sid->SIDct->ch[chn].Ast & _HZ)) {
|
||||
if (sid->SIDct->ch[chn].Ast & ATK) {
|
||||
sid->SIDct->ch[chn].envcnt += step;
|
||||
if (sid->SIDct->ch[chn].envcnt >= 0xFF) {
|
||||
sid->SIDct->ch[chn].envcnt = 0xFF;
|
||||
sid->SIDct->ch[chn].Ast &= 0xFF - ATK;
|
||||
}
|
||||
} else if (!(sid->SIDct->ch[chn].Ast & DECSUS) ||
|
||||
sid->SIDct->ch[chn].envcnt > (SR >> 4) + (SR & 0xF0)) {
|
||||
sid->SIDct->ch[chn].envcnt -= step;
|
||||
if (sid->SIDct->ch[chn].envcnt <= 0 &&
|
||||
sid->SIDct->ch[chn].envcnt + step != 0) {
|
||||
sid->SIDct->ch[chn].envcnt = 0;
|
||||
sid->SIDct->ch[chn].Ast |= _HZ;
|
||||
}
|
||||
}
|
||||
}
|
||||
sid->SIDct->ch[chn].expcnt = 0;
|
||||
} else {
|
||||
}
|
||||
}
|
||||
sid->SIDct->ch[chn].envcnt = (int) sid->SIDct->ch[chn].envcnt & 0xFF;
|
||||
double aAdd = (voic->freq_low + voic->freq_high * 256) * sid->g.ckr;
|
||||
if (test || ((ctrl & SYN) && sid->SIDct->sMSBrise)) {
|
||||
sid->SIDct->ch[chn].pacc = 0;
|
||||
} else {
|
||||
sid->SIDct->ch[chn].pacc += aAdd;
|
||||
if (sid->SIDct->ch[chn].pacc > 0xFFFFFF)
|
||||
sid->SIDct->ch[chn].pacc -= 0x1000000;
|
||||
}
|
||||
double MSB = (int) sid->SIDct->ch[chn].pacc & 0x800000;
|
||||
sid->SIDct->sMSBrise = (MSB > ((int) sid->SIDct->ch[chn].pracc & 0x800000)) ? 1 : 0;
|
||||
|
||||
if (wf & NOI) {
|
||||
tmp = sid->SIDct->ch[chn].nLFSR;
|
||||
if ((((int) sid->SIDct->ch[chn].pacc & 0x100000) !=
|
||||
((int) sid->SIDct->ch[chn].pracc & 0x100000)) ||
|
||||
aAdd >= 0x100000) {
|
||||
step = ((int) tmp & 0x400000) ^ (((int) tmp & 0x20000) << 5);
|
||||
tmp = (((int) tmp << 1) + (step > 0 || test)) & 0x7FFFFF;
|
||||
sid->SIDct->ch[chn].nLFSR = tmp;
|
||||
}
|
||||
wfout = (wf & 0x70) ? 0
|
||||
: (((int) tmp & 0x100000) >> 5) + (((int) tmp & 0x40000) >> 4) +
|
||||
(((int) tmp & 0x4000) >> 1) + (((int) tmp & 0x800) << 1) +
|
||||
(((int) tmp & 0x200) << 2) + (((int) tmp & 0x20) << 5) +
|
||||
(((int) tmp & 0x04) << 7) + (((int) tmp & 0x01) << 8);
|
||||
} else if (wf & PUL) {
|
||||
double pw = (voic->pw_low + (voic->pw_high) * 256) * 16;
|
||||
tmp = (int) aAdd >> 9;
|
||||
if (0 < pw && pw < tmp)
|
||||
pw = tmp;
|
||||
tmp = (int) tmp ^ 0xFFFF;
|
||||
if (pw > tmp)
|
||||
pw = tmp;
|
||||
tmp = (int) sid->SIDct->ch[chn].pacc >> 8;
|
||||
if (wf == PUL) {
|
||||
int lel = ((int) aAdd >> 16);
|
||||
if (lel > 0) {
|
||||
step = 256.0 / (double) lel;
|
||||
} else {
|
||||
step = INFINITY;
|
||||
}
|
||||
if (test)
|
||||
wfout = 0xFFFF;
|
||||
else if (tmp < pw) {
|
||||
double lim = (0xFFFF - pw) * step;
|
||||
if (lim > 0xFFFF)
|
||||
lim = 0xFFFF;
|
||||
wfout = lim - (pw - tmp) * step;
|
||||
if (wfout < 0)
|
||||
wfout = 0;
|
||||
} else {
|
||||
double lim = pw * step;
|
||||
if (lim > 0xFFFF)
|
||||
lim = 0xFFFF;
|
||||
wfout = (0xFFFF - tmp) * step - lim;
|
||||
if (wfout >= 0)
|
||||
wfout = 0xFFFF;
|
||||
wfout = (int) wfout & 0xFFFF;
|
||||
}
|
||||
} else {
|
||||
wfout = (tmp >= pw || test) ? 0xFFFF : 0;
|
||||
if (wf & TRI) {
|
||||
if (wf & SAW) {
|
||||
wfout =
|
||||
(wfout) ? cmbWF(chn, sid->g.Pulsetrsaw, (int) tmp >> 4, 1, &sid->g) : 0;
|
||||
} else {
|
||||
tmp = (int) sid->SIDct->ch[chn].pacc ^ (ctrl & RNG ? sid->SIDct->sMSB : 0);
|
||||
wfout =
|
||||
(wfout)
|
||||
? cmbWF(chn, sid->g.pusaw,
|
||||
((int) tmp ^ ((int) tmp & 0x800000 ? 0xFFFFFF : 0)) >> 11,
|
||||
0, &sid->g)
|
||||
: 0;
|
||||
}
|
||||
} else if (wf & SAW)
|
||||
wfout = (wfout) ? cmbWF(chn, sid->g.pusaw, (int) tmp >> 4, 1, &sid->g) : 0;
|
||||
}
|
||||
} else if (wf & SAW) {
|
||||
wfout = (int) sid->SIDct->ch[chn].pacc >> 8;
|
||||
if (wf & TRI)
|
||||
wfout = cmbWF(chn, sid->g.trsaw, (int) wfout >> 4, 1, &sid->g);
|
||||
else {
|
||||
step = aAdd / 0x1200000;
|
||||
wfout += wfout * step;
|
||||
if (wfout > 0xFFFF)
|
||||
wfout = 0xFFFF - (wfout - 0x10000) / step;
|
||||
}
|
||||
} else if (wf & TRI) {
|
||||
tmp = (int) sid->SIDct->ch[chn].pacc ^ (ctrl & RNG ? sid->SIDct->sMSB : 0);
|
||||
wfout = ((int) tmp ^ ((int) tmp & 0x800000 ? 0xFFFFFF : 0)) >> 7;
|
||||
}
|
||||
if (wf)
|
||||
sid->SIDct->ch[chn].prevwfout = wfout;
|
||||
else {
|
||||
wfout = sid->SIDct->ch[chn].prevwfout;
|
||||
}
|
||||
sid->SIDct->ch[chn].pracc = sid->SIDct->ch[chn].pacc;
|
||||
sid->SIDct->sMSB = MSB;
|
||||
// double preflin = flin;
|
||||
if ((sid->mute_mask & (1 << chn))) {
|
||||
if (sid->M[0x17] & sid->SIDct->ch[chn].FSW) {
|
||||
double chnout = (wfout - 0x8000) * (sid->SIDct->ch[chn].envcnt / 256);
|
||||
flin += chnout;
|
||||
|
||||
// fake filter for solo waveform ahead
|
||||
// mostly copypasted from below
|
||||
double fakeflin = chnout;
|
||||
double fakeflout = 0;
|
||||
static double fakeplp[3] = {0};
|
||||
static double fakepbp[3] = {0};
|
||||
double ctf = sid->g.ctf_table[((sid->M[0x15]&7)|(sid->M[0x16]<<3))&0x7ff];
|
||||
double reso;
|
||||
if (sid->g.model == 8580) {
|
||||
reso = pow(2, ((double) (4 - (double) (sid->M[0x17] >> 4)) / 8));
|
||||
} else {
|
||||
reso = (sid->M[0x17] > 0x5F) ? 8.0 / (double) (sid->M[0x17] >> 4) : 1.41;
|
||||
}
|
||||
double tmp = fakeflin + fakepbp[chn] * reso + fakeplp[chn];
|
||||
if (sid->M[0x18] & HP)
|
||||
fakeflout -= tmp;
|
||||
tmp = fakepbp[chn] - tmp * ctf;
|
||||
fakepbp[chn] = tmp;
|
||||
if (sid->M[0x18] & BP)
|
||||
fakeflout -= tmp;
|
||||
tmp = fakeplp[chn] + tmp * ctf;
|
||||
fakeplp[chn] = tmp;
|
||||
if (sid->M[0x18] & LP)
|
||||
fakeflout += tmp;
|
||||
|
||||
double wf_out = (fakeflout / SID_OUT_SCALE) * (sid->M[0x18] & 0xF) * 65535;
|
||||
waveforms_add_sample(chn, wf_out);
|
||||
} else if ((chn % 3) != 2 || !(sid->M[0x18] & OFF3)) {
|
||||
double chnout = (wfout - 0x8000) * (sid->SIDct->ch[chn].envcnt / 256);
|
||||
output += chnout;
|
||||
|
||||
double wf_out = (chnout / SID_OUT_SCALE) * (sid->M[0x18] & 0xF) * 65535;
|
||||
waveforms_add_sample(chn, wf_out);
|
||||
}
|
||||
} else {
|
||||
waveforms_add_sample(chn, 0);
|
||||
}
|
||||
}
|
||||
int M1 = 0;
|
||||
if (M1 & 3)
|
||||
sid->M[0x1B] = (int) wfout >> 8;
|
||||
sid->M[0x1C] = sid->SIDct->ch[2].envcnt;
|
||||
|
||||
double ctf = sid->g.ctf_table[((sid->M[0x15]&7)|(sid->M[0x16]<<3))&0x7ff];
|
||||
double reso;
|
||||
if (sid->g.model == 8580) {
|
||||
reso = pow(2, ((double) (4 - (double) (sid->M[0x17] >> 4)) / 8));
|
||||
} else {
|
||||
reso = (sid->M[0x17] > 0x5F) ? 8.0 / (double) (sid->M[0x17] >> 4) : 1.41;
|
||||
}
|
||||
|
||||
double tmp = flin + sid->SIDct->pbp * reso + sid->SIDct->plp;
|
||||
if (sid->M[0x18] & HP)
|
||||
output -= tmp;
|
||||
tmp = sid->SIDct->pbp - tmp * ctf;
|
||||
sid->SIDct->pbp = tmp;
|
||||
if (sid->M[0x18] & BP)
|
||||
output -= tmp;
|
||||
tmp = sid->SIDct->plp + tmp * ctf;
|
||||
sid->SIDct->plp = tmp;
|
||||
if (sid->M[0x18] & LP)
|
||||
output += tmp;
|
||||
return (output / SID_OUT_SCALE) * (sid->M[0x18] & 0xF);
|
||||
}
|
||||
|
||||
void dSID_setMuteMask(struct SID_chip* sid, int mute_mask) {
|
||||
sid->mute_mask = mute_mask;
|
||||
}
|
||||
|
||||
float dSID_getVolume(struct SID_chip* sid, int channel) {
|
||||
if ((sid->M[0x18] & 0xF) == 0)
|
||||
return 0;
|
||||
return sid->SIDct[0].ch[channel].envcnt / 256.0f;
|
||||
}
|
||||
|
||||
void dSID_write(struct SID_chip* sid, unsigned char addr, unsigned char val) {
|
||||
sid->M[addr&0x1f]=val;
|
||||
}
|
||||
97
src/engine/platform/sound/c64_d/dsid.h
Normal file
97
src/engine/platform/sound/c64_d/dsid.h
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
#ifndef DSID_H
|
||||
#define DSID_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct SID_ctx_chan {
|
||||
double rcnt;
|
||||
double envcnt;
|
||||
double expcnt;
|
||||
double pacc;
|
||||
double pracc;
|
||||
int FSW;
|
||||
int nLFSR;
|
||||
double prevwfout;
|
||||
uint8_t pSR;
|
||||
int Ast;
|
||||
};
|
||||
|
||||
struct SID_ctx {
|
||||
int sMSBrise;
|
||||
int sMSB;
|
||||
double plp;
|
||||
double pbp;
|
||||
struct SID_ctx_chan ch[3];
|
||||
};
|
||||
|
||||
struct SIDVOICE {
|
||||
uint8_t freq_low;
|
||||
uint8_t freq_high;
|
||||
uint8_t pw_low;
|
||||
uint8_t pw_high : 4;
|
||||
uint8_t UNUSED : 4;
|
||||
uint8_t control;
|
||||
uint8_t decay : 4;
|
||||
uint8_t attack : 4;
|
||||
uint8_t susres;
|
||||
// uint8_t release : 4;
|
||||
// uint8_t sustain : 4;
|
||||
};
|
||||
|
||||
struct SIDMEM {
|
||||
struct SIDVOICE v[3];
|
||||
uint8_t UNUSED : 4;
|
||||
uint8_t cutoff_low : 4;
|
||||
uint8_t cutoff_high;
|
||||
uint8_t reso_rt : 4;
|
||||
uint8_t reso : 4;
|
||||
uint8_t volume : 4;
|
||||
uint8_t filter_mode : 4;
|
||||
uint8_t paddlex;
|
||||
uint8_t paddley;
|
||||
uint8_t osc3;
|
||||
uint8_t env3;
|
||||
};
|
||||
|
||||
struct SID_globals {
|
||||
double ckr;
|
||||
double ctfr;
|
||||
double ctf_ratio_6581;
|
||||
|
||||
double ctf_table[2048];
|
||||
|
||||
int trsaw[4096];
|
||||
int pusaw[4096];
|
||||
int Pulsetrsaw[4096];
|
||||
|
||||
double Aprd[16];
|
||||
int Astp[16];
|
||||
int model;
|
||||
};
|
||||
|
||||
#define MemLen 65536
|
||||
|
||||
struct SID_chip {
|
||||
struct SID_globals g;
|
||||
struct SID_ctx SIDct[3];
|
||||
uint8_t M[MemLen];
|
||||
int16_t lastOut[3];
|
||||
int mute_mask;
|
||||
};
|
||||
|
||||
double dSID_render(struct SID_chip* sid);
|
||||
void dSID_init(struct SID_chip* sid, double clockRate, double samplingRate, int model, unsigned char init_wf);
|
||||
float dSID_getVolume(struct SID_chip* sid, int channel);
|
||||
void dSID_setMuteMask(struct SID_chip* sid, int mute_mask);
|
||||
|
||||
void dSID_write(struct SID_chip* sid, unsigned char addr, unsigned char val);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -61,7 +61,6 @@ const unsigned int EnvelopeGenerator::adsrtable[16] =
|
|||
|
||||
void EnvelopeGenerator::reset()
|
||||
{
|
||||
// counter is not changed on reset
|
||||
envelope_pipeline = 0;
|
||||
|
||||
state_pipeline = 0;
|
||||
|
|
@ -73,7 +72,7 @@ void EnvelopeGenerator::reset()
|
|||
|
||||
gate = false;
|
||||
|
||||
resetLfsr = true;
|
||||
resetLfsr = false;
|
||||
|
||||
exponential_counter = 0;
|
||||
exponential_counter_period = 1;
|
||||
|
|
@ -81,7 +80,11 @@ void EnvelopeGenerator::reset()
|
|||
|
||||
state = RELEASE;
|
||||
counter_enabled = true;
|
||||
rate = adsrtable[release];
|
||||
rate = 0;
|
||||
|
||||
envelope_counter = 0;
|
||||
env3 = 0;
|
||||
lfsr = 0x7fff;
|
||||
}
|
||||
|
||||
void EnvelopeGenerator::writeCONTROL_REG(unsigned char control)
|
||||
|
|
|
|||
|
|
@ -146,7 +146,7 @@ public:
|
|||
counter_enabled(true),
|
||||
gate(false),
|
||||
resetLfsr(false),
|
||||
envelope_counter(0xaa),
|
||||
envelope_counter(0),
|
||||
attack(0),
|
||||
decay(0),
|
||||
sustain(0),
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include "vera.h"
|
||||
#include "../engine.h"
|
||||
#include "../../ta-log.h"
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
|
|
@ -32,10 +33,11 @@ extern "C" {
|
|||
#define rWrite(c,a,d) {regPool[(c)*4+(a)]=(d); psg_writereg(psg,((c)*4+(a)),(d));if (dumpWrites) {addWrite(((c)*4+(a)),(d));}}
|
||||
#define rWriteLo(c,a,d) rWrite(c,a,(regPool[(c)*4+(a)]&(~0x3f))|((d)&0x3f))
|
||||
#define rWriteHi(c,a,d) rWrite(c,a,(regPool[(c)*4+(a)]&(~0xc0))|(((d)<<6)&0xc0))
|
||||
#define rWritePCMCtrl(d) {regPool[64]=(d); pcm_write_ctrl(pcm,d);}
|
||||
#define rWritePCMRate(d) {regPool[65]=(d); pcm_write_rate(pcm,d);}
|
||||
#define rWritePCMCtrl(d) {regPool[64]=(d); pcm_write_ctrl(pcm,d);if (dumpWrites) addWrite(64,(d));}
|
||||
#define rWritePCMRate(d) {regPool[65]=(d); pcm_write_rate(pcm,d);if (dumpWrites) addWrite(65,(d));}
|
||||
#define rWritePCMData(d) {regPool[66]=(d); pcm_write_fifo(pcm,d);}
|
||||
#define rWritePCMVol(d) rWritePCMCtrl((regPool[64]&(~0x8f))|((d)&15))
|
||||
#define rWriteZSMSync(d) {if (dumpWrites) addWrite(68,(d));}
|
||||
|
||||
const char* regCheatSheetVERA[]={
|
||||
"CHxFreq", "00+x*4",
|
||||
|
|
@ -45,6 +47,7 @@ const char* regCheatSheetVERA[]={
|
|||
"AUDIO_CTRL", "40",
|
||||
"AUDIO_RATE", "41",
|
||||
"AUDIO_DATA", "42",
|
||||
"ZSM_SYNC", "44",
|
||||
|
||||
NULL
|
||||
};
|
||||
|
|
@ -226,6 +229,49 @@ void DivPlatformVERA::tick(bool sysTick) {
|
|||
rWritePCMRate(chan[16].freq&0xff);
|
||||
chan[16].freqChanged=false;
|
||||
}
|
||||
|
||||
if (dumpWrites) {
|
||||
DivSample* s=parent->getSample(chan[16].pcm.sample);
|
||||
if (s->samples>0) {
|
||||
while (true) {
|
||||
short tmp_l=0;
|
||||
short tmp_r=0;
|
||||
if (!isMuted[16]) {
|
||||
if (chan[16].pcm.depth16) {
|
||||
tmp_l=s->data16[chan[16].pcm.pos];
|
||||
tmp_r=tmp_l;
|
||||
} else {
|
||||
tmp_l=s->data8[chan[16].pcm.pos];
|
||||
tmp_r=tmp_l;
|
||||
}
|
||||
if (!(chan[16].pan&1)) tmp_l=0;
|
||||
if (!(chan[16].pan&2)) tmp_r=0;
|
||||
}
|
||||
if (chan[16].pcm.depth16) {
|
||||
addWrite(66,tmp_l&0xff);
|
||||
addWrite(66,(tmp_l>>8)&0xff);
|
||||
addWrite(66,tmp_r&0xff);
|
||||
addWrite(66,(tmp_r>>8)&0xff);
|
||||
} else {
|
||||
addWrite(66,tmp_l&0xff);
|
||||
addWrite(66,tmp_r&0xff);
|
||||
}
|
||||
chan[16].pcm.pos++;
|
||||
if (s->isLoopable() && chan[16].pcm.pos>=(unsigned int)s->loopEnd) {
|
||||
//chan[16].pcm.pos=s->loopStart;
|
||||
logI("VERA PCM export: treating looped sample as non-looped");
|
||||
chan[16].pcm.sample=-1;
|
||||
break;
|
||||
}
|
||||
if (chan[16].pcm.pos>=s->samples) {
|
||||
chan[16].pcm.sample=-1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
chan[16].pcm.sample=-1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int DivPlatformVERA::dispatch(DivCommand c) {
|
||||
|
|
@ -370,6 +416,9 @@ int DivPlatformVERA::dispatch(DivCommand c) {
|
|||
case DIV_CMD_MACRO_ON:
|
||||
chan[c.chan].std.mask(c.value,false);
|
||||
break;
|
||||
case DIV_CMD_EXTERNAL:
|
||||
rWriteZSMSync(c.value);
|
||||
break;
|
||||
case DIV_ALWAYS_SET_VOLUME:
|
||||
return 0;
|
||||
break;
|
||||
|
|
@ -441,6 +490,15 @@ void DivPlatformVERA::poke(std::vector<DivRegWrite>& wlist) {
|
|||
for (auto &i: wlist) poke(i.addr,i.val);
|
||||
}
|
||||
|
||||
void DivPlatformVERA::setFlags(const DivConfig& flags) {
|
||||
chipClock=25000000;
|
||||
CHECK_CUSTOM_CLOCK;
|
||||
rate=chipClock/512;
|
||||
for (int i=0; i<17; i++) {
|
||||
oscBuf[i]->rate=rate;
|
||||
}
|
||||
}
|
||||
|
||||
int DivPlatformVERA::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) {
|
||||
for (int i=0; i<17; i++) {
|
||||
isMuted[i]=false;
|
||||
|
|
@ -451,12 +509,7 @@ int DivPlatformVERA::init(DivEngine* p, int channels, int sugRate, const DivConf
|
|||
pcm=new struct VERA_PCM;
|
||||
dumpWrites=false;
|
||||
skipRegisterWrites=false;
|
||||
chipClock=25000000;
|
||||
CHECK_CUSTOM_CLOCK;
|
||||
rate=chipClock/512;
|
||||
for (int i=0; i<17; i++) {
|
||||
oscBuf[i]->rate=rate;
|
||||
}
|
||||
setFlags(flags);
|
||||
reset();
|
||||
return 17;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ class DivPlatformVERA: public DivDispatch {
|
|||
Channel chan[17];
|
||||
DivDispatchOscBuffer* oscBuf[17];
|
||||
bool isMuted[17];
|
||||
unsigned char regPool[67];
|
||||
unsigned char regPool[69];
|
||||
struct VERA_PSG* psg;
|
||||
struct VERA_PCM* pcm;
|
||||
|
||||
|
|
@ -70,6 +70,7 @@ class DivPlatformVERA: public DivDispatch {
|
|||
void reset();
|
||||
void tick(bool sysTick=true);
|
||||
void muteChannel(int ch, bool mute);
|
||||
void setFlags(const DivConfig& flags);
|
||||
void notifyInsDeletion(void* ins);
|
||||
float getPostAmp();
|
||||
int getOutputCount();
|
||||
|
|
|
|||
|
|
@ -236,6 +236,8 @@ const char* cmdName[]={
|
|||
|
||||
"NES_LINEAR_LENGTH",
|
||||
|
||||
"EXTERNAL",
|
||||
|
||||
"ALWAYS_SET_VOLUME"
|
||||
};
|
||||
|
||||
|
|
@ -913,6 +915,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
|
|||
//printf("\x1b[1;36m%d: extern command %d\x1b[m\n",i,effectVal);
|
||||
extValue=effectVal;
|
||||
extValuePresent=true;
|
||||
dispatchCmd(DivCommand(DIV_CMD_EXTERNAL,effectVal));
|
||||
break;
|
||||
case 0xef: // global pitch
|
||||
globalPitch+=(signed char)(effectVal-0x80);
|
||||
|
|
@ -1980,14 +1983,10 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
|
|||
} else {
|
||||
// 3. run MIDI clock
|
||||
int midiTotal=MIN(cycles,runLeftG);
|
||||
for (int i=0; i<midiTotal; i++) {
|
||||
runMidiClock();
|
||||
}
|
||||
runMidiClock(midiTotal);
|
||||
|
||||
// 4. run MIDI timecode
|
||||
for (int i=0; i<midiTotal; i++) {
|
||||
runMidiTime();
|
||||
}
|
||||
runMidiTime(midiTotal);
|
||||
|
||||
// 5. tick the clock and fill buffers as needed
|
||||
if (cycles<runLeftG) {
|
||||
|
|
|
|||
|
|
@ -392,7 +392,7 @@ bool DivSample::save(const char* path) {
|
|||
if (length16<1) return false;
|
||||
|
||||
si.channels=1;
|
||||
si.samplerate=rate;
|
||||
si.samplerate=centerRate;
|
||||
switch (depth) {
|
||||
case DIV_SAMPLE_DEPTH_8BIT: // 8-bit
|
||||
si.format=SF_FORMAT_PCM_U8|SF_FORMAT_WAV;
|
||||
|
|
@ -412,7 +412,8 @@ bool DivSample::save(const char* path) {
|
|||
SF_INSTRUMENT inst;
|
||||
memset(&inst, 0, sizeof(inst));
|
||||
inst.gain = 1;
|
||||
short pitch = (0x3c * 100) + 50 - (log2((double)centerRate/rate) * 12.0 * 100.0);
|
||||
// TODO: fix
|
||||
short pitch = (0x3c * 100) + 50 - (log2((double)centerRate/8363.0) * 12.0 * 100.0);
|
||||
inst.basenote = pitch / 100;
|
||||
inst.detune = 50 - (pitch % 100);
|
||||
inst.velocity_hi = 0x7f;
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ bool DivWaveSynth::activeChanged() {
|
|||
activeChangedB=false;
|
||||
return true;
|
||||
}
|
||||
if (first) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -53,9 +53,17 @@ void DivZSM::init(unsigned int rate) {
|
|||
tickRate=rate;
|
||||
loopOffset=-1;
|
||||
numWrites=0;
|
||||
ticks=0;
|
||||
// Initialize YM/PSG states
|
||||
memset(&ymState,-1,sizeof(ymState));
|
||||
memset(&psgState,-1,sizeof(psgState));
|
||||
ticks=0;
|
||||
// Initialize PCM states
|
||||
pcmRateCache=-1;
|
||||
pcmCtrlRVCache=-1;
|
||||
pcmCtrlDCCache=-1;
|
||||
// Channel masks
|
||||
ymMask=0;
|
||||
psgMask=0;
|
||||
}
|
||||
|
||||
int DivZSM::getoffset() {
|
||||
|
|
@ -108,9 +116,17 @@ void DivZSM::writeYM(unsigned char a, unsigned char v) {
|
|||
|
||||
void DivZSM::writePSG(unsigned char a, unsigned char v) {
|
||||
// TODO: suppress writes to PSG voice that is not audible (volume=0)
|
||||
if (a>=64) {
|
||||
logD ("ZSM: ignoring VERA PSG write a=%02x v=%02x",a,v);
|
||||
// ^ Let's leave these alone, ZSMKit has a feature that can benefit
|
||||
// from silent channels.
|
||||
if (a>=69) {
|
||||
logD("ZSM: ignoring VERA PSG write a=%02x v=%02x",a,v);
|
||||
return;
|
||||
} else if (a==68) {
|
||||
// Sync event
|
||||
numWrites++;
|
||||
return syncCache.push_back(v);
|
||||
} else if (a>=64) {
|
||||
return writePCM(a-64,v);
|
||||
}
|
||||
if (psgState[psg_PREV][a]==v) {
|
||||
if (psgState[psg_NEW][a]!=v) {
|
||||
|
|
@ -131,7 +147,26 @@ void DivZSM::writePSG(unsigned char a, unsigned char v) {
|
|||
}
|
||||
|
||||
void DivZSM::writePCM(unsigned char a, unsigned char v) {
|
||||
// ZSM standard for PCM playback has not been established yet.
|
||||
if (a==0) { // PCM Ctrl
|
||||
// cache the depth and channels but don't write it to the
|
||||
// register queue
|
||||
pcmCtrlDCCache=v&0x30;
|
||||
// save only the reset bit and volume (if it isn't a dupe)
|
||||
if (pcmCtrlRVCache!=(v&0x8f)) {
|
||||
pcmMeta.push_back(DivRegWrite(a,(v&0x8f)));
|
||||
pcmCtrlRVCache=v&0x8f;
|
||||
numWrites++;
|
||||
}
|
||||
} else if (a==1) { // PCM Rate
|
||||
if (pcmRateCache!=v) {
|
||||
pcmMeta.push_back(DivRegWrite(a,v));
|
||||
pcmRateCache=v;
|
||||
numWrites++;
|
||||
}
|
||||
} else if (a==2) { // PCM data
|
||||
pcmCache.push_back(v);
|
||||
numWrites++;
|
||||
}
|
||||
}
|
||||
|
||||
void DivZSM::tick(int numticks) {
|
||||
|
|
@ -151,6 +186,9 @@ void DivZSM::setLoopPoint() {
|
|||
w->seek(loopOffset,SEEK_SET);
|
||||
// reset the PSG shadow and write cache
|
||||
memset(&psgState,-1,sizeof(psgState));
|
||||
// reset the PCM caches that would inhibit dupes
|
||||
pcmRateCache=-1;
|
||||
pcmCtrlRVCache=-1;
|
||||
// reset the YM shadow....
|
||||
memset(&ymState[ym_PREV],-1,sizeof(ymState[ym_PREV]));
|
||||
// ... and cache (except for unused channels)
|
||||
|
|
@ -170,16 +208,62 @@ SafeWriter* DivZSM::finish() {
|
|||
tick(0); // flush any pending writes / ticks
|
||||
flushTicks(); // flush ticks in case there were no writes pending
|
||||
w->writeC(ZSM_EOF);
|
||||
if (pcmInsts.size()>256) {
|
||||
logE("ZSM: more than the maximum number of PCM instruments exist. Skipping PCM export entirely.");
|
||||
pcmData.clear();
|
||||
pcmInsts.clear();
|
||||
} else if (pcmData.size()) { // if exists, write PCM instruments and blob to the end of file
|
||||
int pcmOff=w->tell();
|
||||
w->writeC('P');
|
||||
w->writeC('C');
|
||||
w->writeC('M');
|
||||
w->writeC((unsigned char)pcmInsts.size()-1);
|
||||
int i=0;
|
||||
for (S_pcmInst& inst: pcmInsts) {
|
||||
// write out the instruments
|
||||
// PCM playback location follows:
|
||||
// <instrument number>
|
||||
// <geometry (depth and channel)>
|
||||
// <l m h> of PCM data offset
|
||||
// <l m h> of length
|
||||
w->writeC((unsigned char)i&0xff);
|
||||
w->writeC((unsigned char)inst.geometry&0x30);
|
||||
w->writeC((unsigned char)inst.offset&0xff);
|
||||
w->writeC((unsigned char)(inst.offset>>8)&0xff);
|
||||
w->writeC((unsigned char)(inst.offset>>16)&0xff);
|
||||
w->writeC((unsigned char)inst.length&0xff);
|
||||
w->writeC((unsigned char)(inst.length>>8)&0xff);
|
||||
w->writeC((unsigned char)(inst.length>>16)&0xff);
|
||||
// Feature mask: Lxxxxxxx
|
||||
// L = Loop enabled
|
||||
w->writeC(0);
|
||||
// Loop point (not yet implemented)
|
||||
w->writeC(0);
|
||||
w->writeS(0);
|
||||
// Reserved for future use
|
||||
w->writeS(0);
|
||||
w->writeS(0);
|
||||
i++;
|
||||
}
|
||||
for (unsigned char& c: pcmData) {
|
||||
w->writeC(c);
|
||||
}
|
||||
pcmData.clear();
|
||||
// update PCM offset in file
|
||||
w->seek(0x06,SEEK_SET);
|
||||
w->writeC((unsigned char)pcmOff&0xff);
|
||||
w->writeC((unsigned char)(pcmOff>>8)&0xff);
|
||||
w->writeC((unsigned char)(pcmOff>>16)&0xff);
|
||||
}
|
||||
// update channel use masks.
|
||||
w->seek(0x09,SEEK_SET);
|
||||
w->writeC((unsigned char)(ymMask&0xff));
|
||||
w->writeS((short)(psgMask&0xffff));
|
||||
// todo: put PCM offset/data writes here once defined in ZSM standard.
|
||||
return w;
|
||||
}
|
||||
|
||||
void DivZSM::flushWrites() {
|
||||
logD("ZSM: flushWrites.... numwrites=%d ticks=%d ymwrites=%d",numWrites,ticks,ymwrites.size());
|
||||
logD("ZSM: flushWrites.... numwrites=%d ticks=%d ymwrites=%d pcmMeta=%d pcmCache=%d pcmData=%d syncCache=%d",numWrites,ticks,ymwrites.size(),pcmMeta.size(),pcmCache.size(),pcmData.size(),syncCache.size());
|
||||
if (numWrites==0) return;
|
||||
flushTicks(); // only flush ticks if there are writes pending.
|
||||
for (unsigned char i=0; i<64; i++) {
|
||||
|
|
@ -204,6 +288,107 @@ void DivZSM::flushWrites() {
|
|||
w->writeC(write.val);
|
||||
}
|
||||
ymwrites.clear();
|
||||
unsigned int pcmInst=0;
|
||||
int pcmOff=0;
|
||||
int pcmLen=0;
|
||||
int extCmd0Len=pcmMeta.size()*2;
|
||||
if (pcmCache.size()) {
|
||||
// collapse stereo data to mono if both channels are fully identical
|
||||
// which cuts PCM data size in half for center-panned PCM events
|
||||
if (pcmCtrlDCCache&0x10) { // stereo bit is on
|
||||
unsigned int e;
|
||||
if (pcmCtrlDCCache&0x20) { // 16-bit
|
||||
// for 16-bit PCM data, the size must be a multiple of 4
|
||||
if (pcmCache.size()%4==0) {
|
||||
// check for identical L+R channels
|
||||
for (e=0; e<pcmCache.size(); e+=4) {
|
||||
if (pcmCache[e]!=pcmCache[e+2] || pcmCache[e+1]!=pcmCache[e+3]) break;
|
||||
}
|
||||
if (e==pcmCache.size()) { // did not find a mismatch
|
||||
// collapse the data to mono 16-bit
|
||||
for (e=0; e<pcmCache.size()>>1; e+=2) {
|
||||
pcmCache[e]=pcmCache[e<<1];
|
||||
pcmCache[e+1]=pcmCache[(e<<1)+1];
|
||||
}
|
||||
pcmCache.resize(pcmCache.size()>>1);
|
||||
pcmCtrlDCCache&=(unsigned char)~0x10; // clear stereo bit
|
||||
}
|
||||
}
|
||||
} else { // 8-bit
|
||||
// for 8-bit PCM data, the size must be a multiple of 2
|
||||
if (pcmCache.size()%2==0) {
|
||||
// check for identical L+R channels
|
||||
for (e=0; e<pcmCache.size(); e+=2) {
|
||||
if (pcmCache[e]!=pcmCache[e+1]) break;
|
||||
}
|
||||
if (e==pcmCache.size()) { // did not find a mismatch
|
||||
// collapse the data to mono 8-bit
|
||||
for (e=0; e<pcmCache.size()>>1; e++) {
|
||||
pcmCache[e]=pcmCache[e<<1];
|
||||
}
|
||||
pcmCache.resize(pcmCache.size()>>1);
|
||||
pcmCtrlDCCache&=(unsigned char)~0x10; // clear stereo bit
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// check to see if the most recent received blob matches any of the previous data
|
||||
// and reuse it if there is a match, otherwise append the cache to the rest of
|
||||
// the PCM data
|
||||
std::vector<unsigned char>::iterator it;
|
||||
it=std::search(pcmData.begin(),pcmData.end(),pcmCache.begin(),pcmCache.end());
|
||||
pcmOff=std::distance(pcmData.begin(),it);
|
||||
pcmLen=pcmCache.size();
|
||||
logD("ZSM: pcmOff: %d pcmLen: %d",pcmOff,pcmLen);
|
||||
if (it==pcmData.end()) {
|
||||
pcmData.insert(pcmData.end(),pcmCache.begin(),pcmCache.end());
|
||||
}
|
||||
pcmCache.clear();
|
||||
extCmd0Len+=2;
|
||||
// search for a matching PCM instrument definition
|
||||
for (S_pcmInst& inst: pcmInsts) {
|
||||
if (inst.offset==pcmOff && inst.length==pcmLen && inst.geometry==pcmCtrlDCCache)
|
||||
break;
|
||||
pcmInst++;
|
||||
}
|
||||
if (pcmInst==pcmInsts.size()) {
|
||||
S_pcmInst inst;
|
||||
inst.geometry=pcmCtrlDCCache;
|
||||
inst.offset=pcmOff;
|
||||
inst.length=pcmLen;
|
||||
pcmInsts.push_back(inst);
|
||||
}
|
||||
}
|
||||
if (extCmd0Len>63) { // this would be bad, but will almost certainly never happen
|
||||
logE("ZSM: extCmd 0 exceeded maximum length of 63: %d",extCmd0Len);
|
||||
extCmd0Len=0;
|
||||
pcmMeta.clear();
|
||||
}
|
||||
if (extCmd0Len) { // we have some PCM events to write
|
||||
w->writeC(ZSM_EXT);
|
||||
w->writeC(ZSM_EXT_PCM|(unsigned char)extCmd0Len);
|
||||
for (DivRegWrite& write: pcmMeta) {
|
||||
w->writeC(write.addr);
|
||||
w->writeC(write.val);
|
||||
}
|
||||
pcmMeta.clear();
|
||||
if (pcmLen) {
|
||||
w->writeC(0x02); // 0x02 = Instrument trigger
|
||||
w->writeC((unsigned char)pcmInst&0xff);
|
||||
}
|
||||
}
|
||||
n=0;
|
||||
while (n<(long)syncCache.size()) { // we have one or more sync events to write
|
||||
int writes=syncCache.size()-n;
|
||||
w->writeC(ZSM_EXT);
|
||||
if (writes>ZSM_SYNC_MAX_WRITES) writes=ZSM_SYNC_MAX_WRITES;
|
||||
w->writeC(ZSM_EXT_SYNC|(writes<<1));
|
||||
for (; writes>0; writes--) {
|
||||
w->writeC(0x00); // 0x00 = Arbitrary sync message
|
||||
w->writeC(syncCache[n++]);
|
||||
}
|
||||
}
|
||||
syncCache.clear();
|
||||
numWrites=0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,24 +30,42 @@
|
|||
#define ZSM_YM_CMD 0x40
|
||||
#define ZSM_DELAY_CMD 0x80
|
||||
#define ZSM_YM_MAX_WRITES 63
|
||||
#define ZSM_SYNC_MAX_WRITES 31
|
||||
#define ZSM_DELAY_MAX 127
|
||||
#define ZSM_EOF ZSM_DELAY_CMD
|
||||
|
||||
#define ZSM_EXT ZSM_YM_CMD
|
||||
#define ZSM_EXT_PCM 0x00
|
||||
#define ZSM_EXT_CHIP 0x40
|
||||
#define ZSM_EXT_SYNC 0x80
|
||||
#define ZSM_EXT_CUSTOM 0xC0
|
||||
|
||||
enum YM_STATE { ym_PREV, ym_NEW, ym_STATES };
|
||||
enum PSG_STATE { psg_PREV, psg_NEW, psg_STATES };
|
||||
|
||||
class DivZSM {
|
||||
private:
|
||||
struct S_pcmInst {
|
||||
int geometry, offset, length;
|
||||
};
|
||||
SafeWriter* w;
|
||||
int ymState[ym_STATES][256];
|
||||
int psgState[psg_STATES][64];
|
||||
int pcmRateCache;
|
||||
int pcmCtrlRVCache;
|
||||
int pcmCtrlDCCache;
|
||||
std::vector<DivRegWrite> ymwrites;
|
||||
std::vector<DivRegWrite> pcmMeta;
|
||||
std::vector<unsigned char> pcmData;
|
||||
std::vector<unsigned char> pcmCache;
|
||||
std::vector<S_pcmInst> pcmInsts;
|
||||
std::vector<unsigned char> syncCache;
|
||||
int loopOffset;
|
||||
int numWrites;
|
||||
int ticks;
|
||||
int tickRate;
|
||||
int ymMask = 0;
|
||||
int psgMask = 0;
|
||||
int ymMask;
|
||||
int psgMask;
|
||||
public:
|
||||
DivZSM();
|
||||
~DivZSM();
|
||||
|
|
|
|||
|
|
@ -150,7 +150,10 @@ SafeWriter* DivEngine::saveZSM(unsigned int zsmrate, bool loop) {
|
|||
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==VERA) zsm.writePSG(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);
|
||||
}
|
||||
}
|
||||
writes.clear();
|
||||
}
|
||||
|
|
@ -160,7 +163,7 @@ SafeWriter* DivEngine::saveZSM(unsigned int zsmrate, bool loop) {
|
|||
fracWait+=cycles&MASTER_CLOCK_MASK;
|
||||
totalWait+=fracWait>>MASTER_CLOCK_PREC;
|
||||
fracWait&=MASTER_CLOCK_MASK;
|
||||
if (totalWait>0) {
|
||||
if (totalWait>0 && !done) {
|
||||
zsm.tick(totalWait);
|
||||
//tickCount+=totalWait;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue