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

* 'master' of https://github.com/tildearrow/furnace: (53 commits)
  prepare for better backward writing
  VGM export: fix oops
  GUI: drag-and-drop ins/wave/sample loading
  GUI: add "set loop" to sample editor
  MSM6295: VGM export!
  oops
  MSM6295: add rate select effect (20xx)
  update meteor shower
  MSVC is better than GCC right?
  update to-do list
  door into summer
  GUI: implement input for touch events
  GUI: update SDL hints
  fix Termux build
  add another demo song
  add demos/ecolove.fur
  update to-do list
  update demos/README.md
  add new demo songs
  dev99 - major Fractal system change
  ...

# Conflicts:
#	src/engine/dispatch.h
#	src/engine/platform/genesis.cpp
#	src/engine/playback.cpp
#	src/engine/song.h
#	src/engine/vgmOps.cpp
This commit is contained in:
cam900 2022-05-29 13:47:39 +09:00
commit 32152fd89b
72 changed files with 967 additions and 304 deletions

View file

@ -59,6 +59,7 @@ enum DivDispatchCmds {
DIV_CMD_SAMPLE_FREQ, // (frequency)
DIV_CMD_SAMPLE_BANK, // (bank)
DIV_CMD_SAMPLE_POS, // (pos)
DIV_CMD_SAMPLE_DIR, // (direction)
DIV_CMD_SAMPLE_TRANSWAVE_SLICE_MODE, // (enabled)
DIV_CMD_SAMPLE_TRANSWAVE_SLICE_POS, // (slice)
@ -236,6 +237,8 @@ struct DivRegWrite {
* - data is the sample rate
* - 0xffffxx02: stop sample playback
* - xx is the instance ID
* - 0xffffxx03: set sample playback direction
* - x is the instance ID
* - 0xffffffff: reset
*/
unsigned int addr;

View file

@ -185,10 +185,22 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
case DIV_SYSTEM_YM2612:
dispatch=new DivPlatformGenesis;
((DivPlatformGenesis*)dispatch)->setYMFM(eng->getConfInt("ym2612Core",0));
((DivPlatformGenesis*)dispatch)->setSoftPCM(false);
break;
case DIV_SYSTEM_YM2612_EXT:
dispatch=new DivPlatformGenesisExt;
((DivPlatformGenesisExt*)dispatch)->setYMFM(eng->getConfInt("ym2612Core",0));
((DivPlatformGenesisExt*)dispatch)->setSoftPCM(false);
break;
case DIV_SYSTEM_YM2612_FRAC:
dispatch=new DivPlatformGenesis;
((DivPlatformGenesis*)dispatch)->setYMFM(eng->getConfInt("ym2612Core",0));
((DivPlatformGenesis*)dispatch)->setSoftPCM(true);
break;
case DIV_SYSTEM_YM2612_FRAC_EXT:
dispatch=new DivPlatformGenesisExt;
((DivPlatformGenesisExt*)dispatch)->setYMFM(eng->getConfInt("ym2612Core",0));
((DivPlatformGenesisExt*)dispatch)->setSoftPCM(true);
break;
case DIV_SYSTEM_SMS:
dispatch=new DivPlatformSMS;

View file

@ -697,7 +697,7 @@ void DivEngine::initSongWithDesc(const int* description) {
song.systemFlags[index]=description[i+3];
index++;
chanCount+=getChannelCount(song.system[index]);
if (chanCount>=63) break;
if (chanCount>=DIV_MAX_CHANS) break;
if (index>=32) break;
}
song.systemLen=index;
@ -887,9 +887,8 @@ bool DivEngine::addSystem(DivSystem which) {
lastError="max number of systems is 32";
return false;
}
// this was DIV_MAX_CHANS but I am setting it to 63 for now due to an ImGui limitation
if (chans+getChannelCount(which)>63) {
lastError="max number of total channels is 63";
if (chans+getChannelCount(which)>DIV_MAX_CHANS) {
lastError=fmt::sprintf("max number of total channels is %d",DIV_MAX_CHANS);
return false;
}
quitDispatch();
@ -1773,7 +1772,7 @@ int DivEngine::addWave() {
return waveCount;
}
bool DivEngine::addWaveFromFile(const char* path) {
bool DivEngine::addWaveFromFile(const char* path, bool addRaw) {
if (song.wave.size()>=256) {
lastError="too many wavetables!";
return false;
@ -1869,8 +1868,27 @@ bool DivEngine::addWaveFromFile(const char* path) {
}
} else {
// read as binary
logI("reading binary...");
if (addRaw) {
logI("reading binary...");
len=reader.size();
if (len>256) len=256;
reader.seek(0,SEEK_SET);
for (int i=0; i<len; i++) {
wave->data[i]=(unsigned char)reader.readC();
if (wave->max<wave->data[i]) wave->max=wave->data[i];
}
wave->len=len;
} else {
delete wave;
delete[] buf;
return false;
}
}
} catch (EndOfFileException& e) {
// read as binary
if (addRaw) {
len=reader.size();
logI("reading binary for being too small...");
if (len>256) len=256;
reader.seek(0,SEEK_SET);
for (int i=0; i<len; i++) {
@ -1878,18 +1896,11 @@ bool DivEngine::addWaveFromFile(const char* path) {
if (wave->max<wave->data[i]) wave->max=wave->data[i];
}
wave->len=len;
} else {
delete wave;
delete[] buf;
return false;
}
} catch (EndOfFileException& e) {
// read as binary
len=reader.size();
logI("reading binary for being too small...");
if (len>256) len=256;
reader.seek(0,SEEK_SET);
for (int i=0; i<len; i++) {
wave->data[i]=(unsigned char)reader.readC();
if (wave->max<wave->data[i]) wave->max=wave->data[i];
}
wave->len=len;
}
}
} catch (EndOfFileException& e) {

View file

@ -45,8 +45,8 @@
#define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock();
#define BUSY_END isBusy.unlock(); softLocked=false;
#define DIV_VERSION "dev97"
#define DIV_ENGINE_VERSION 97
#define DIV_VERSION "dev99"
#define DIV_ENGINE_VERSION 99
// for imports
#define DIV_VERSION_MOD 0xff01
@ -377,7 +377,7 @@ class DivEngine {
void processRow(int i, bool afterDelay);
void nextOrder();
void nextRow();
void performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write, int streamOff, double* loopTimer, double* loopFreq, int* loopSample, bool isSecond);
void performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write, int streamOff, double* loopTimer, double* loopFreq, int* loopSample, bool* sampleDir, bool isSecond);
// returns true if end of song.
bool nextTick(bool noAccum=false, bool inhibitLowLat=false);
bool perSystemEffect(int ch, unsigned char effect, unsigned char effectVal);
@ -676,7 +676,7 @@ class DivEngine {
int addWave();
// add wavetable from file
bool addWaveFromFile(const char* path);
bool addWaveFromFile(const char* path, bool loadRaw=true);
// delete wavetable
void delWave(int index);

View file

@ -167,6 +167,10 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
ds.fbPortaPause=true;
ds.snDutyReset=true;
ds.oldOctaveBoundary=false;
ds.noOPN2Vol=true;
ds.newVolumeScaling=false;
ds.volMacroLinger=false;
ds.brokenOutVol=true; // ???
// 1.1 compat flags
if (ds.version>24) {
@ -1031,6 +1035,14 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
if (ds.version<97) {
ds.oldOctaveBoundary=true;
}
if (ds.version<97) { // actually should be 98 but yky uses this feature ahead of time
ds.noOPN2Vol=true;
}
if (ds.version<99) {
ds.newVolumeScaling=false;
ds.volMacroLinger=false;
ds.brokenOutVol=true;
}
ds.isDMF=false;
reader.readS(); // reserved
@ -1413,7 +1425,21 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
} else {
reader.readC();
}
for (int i=0; i<13; i++) {
if (ds.version>=98) {
ds.noOPN2Vol=reader.readC();
} else {
reader.readC();
}
if (ds.version>=99) {
ds.newVolumeScaling=reader.readC();
ds.volMacroLinger=reader.readC();
ds.brokenOutVol=reader.readC();
} else {
reader.readC();
reader.readC();
reader.readC();
}
for (int i=0; i<9; i++) {
reader.readC();
}
}
@ -2894,7 +2920,11 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
w->writeC(song.pitchMacroIsLinear);
w->writeC(song.pitchSlideSpeed);
w->writeC(song.oldOctaveBoundary);
for (int i=0; i<13; i++) {
w->writeC(song.noOPN2Vol);
w->writeC(song.newVolumeScaling);
w->writeC(song.volMacroLinger);
w->writeC(song.brokenOutVol);
for (int i=0; i<9; i++) {
w->writeC(0);
}

View file

@ -125,51 +125,109 @@ const char* DivPlatformGenesis::getEffectName(unsigned char effect) {
case 0x5f:
return "5Fxx: Set decay 2 of operator 4 (0 to 1F)";
break;
case 0xdf:
return "DFxx: Set sample playback direction (0: normal; 1: reverse)";
break;
}
return NULL;
}
void DivPlatformGenesis::processDAC() {
if (softPCM) {
softPCMTimer+=chipClock/576;
if (softPCMTimer>rate) {
softPCMTimer-=rate;
int sample=0;
for (int i=5; i<7; i++) {
if (chan[i].dacSample!=-1) {
DivSample* s=parent->getSample(chan[i].dacSample);
if (!isMuted[i] && s->samples>0) {
if (parent->song.noOPN2Vol) {
sample+=s->data8[chan[i].getDacDirection()?(s->samples-chan[i].dacPos-1):chan[i].dacPos];
} else {
sample+=(s->data8[chan[i].getDacDirection()?(s->samples-chan[i].dacPos-1):chan[i].dacPos]*dacVolTable[chan[i].outVol])>>7;
}
}
chan[i].dacPeriod+=chan[i].dacRate;
if (chan[i].dacPeriod>=(chipClock/576)) {
if (s->samples>0) {
while (chan[i].dacPeriod>=(chipClock/576)) {
chan[i].dacPos++;
if (((s->loopMode!=DIV_SAMPLE_LOOPMODE_ONESHOT) && chan[i].dacPos>=s->loopEnd) || (chan[i].dacPos>=s->samples)) {
if (s->isLoopable() && !chan[i].getDacDirection()) {
chan[i].dacPos=s->loopStart;
} else {
chan[i].dacSample=-1;
chan[i].dacPeriod=0;
break;
}
}
chan[i].dacPeriod-=(chipClock/576);
}
} else {
chan[i].dacSample=-1;
}
}
}
}
//sample>>=1;
if (sample<-128) sample=-128;
if (sample>127) sample=127;
urgentWrite(0x2a,(unsigned char)sample+0x80);
}
} else {
if (!chan[5].dacReady) {
chan[5].dacDelay+=32000;
if (chan[5].dacDelay>=rate) {
chan[5].dacDelay-=rate;
chan[5].dacReady=true;
}
}
if (chan[5].dacMode && chan[5].dacSample!=-1) {
chan[5].dacPeriod+=chan[5].dacRate;
if (chan[5].dacPeriod>=rate) {
DivSample* s=parent->getSample(chan[5].dacSample);
if (s->samples>0) {
if (!isMuted[5]) {
if (chan[5].dacReady && writes.size()<16) {
int sample;
if (parent->song.noOPN2Vol) {
sample=s->data8[chan[5].getDacDirection()?(s->samples-chan[5].dacPos-1):chan[5].dacPos];
} else {
sample=(s->data8[chan[5].getDacDirection()?(s->samples-chan[5].dacPos-1):chan[5].dacPos]*dacVolTable[chan[5].outVol])>>7;
}
urgentWrite(0x2a,(unsigned char)sample+0x80);
chan[5].dacReady=false;
}
}
chan[5].dacPos++;
if (((s->loopMode!=DIV_SAMPLE_LOOPMODE_ONESHOT) && chan[5].dacPos>=s->loopEnd) || (chan[5].dacPos>=s->samples)) {
if (s->isLoopable() && !chan[5].getDacDirection()) {
chan[5].dacPos=s->loopStart;
} else {
chan[5].dacSample=-1;
if (parent->song.brokenDACMode) {
rWrite(0x2b,0);
}
}
}
while (chan[5].dacPeriod>=rate) chan[5].dacPeriod-=rate;
} else {
chan[5].dacSample=-1;
}
}
}
}
}
void DivPlatformGenesis::acquire_nuked(short* bufL, short* bufR, size_t start, size_t len) {
static short o[2];
static int os[2];
for (size_t h=start; h<start+len; h++) {
if (!dacReady) {
dacDelay+=32000;
if (dacDelay>=rate) {
dacDelay-=rate;
dacReady=true;
}
}
if (dacMode && dacSample!=-1) {
dacPeriod+=dacRate;
if (dacPeriod>=rate) {
DivSample* s=parent->getSample(dacSample);
if (s->samples>0) {
if (!isMuted[5]) {
if (dacReady && writes.size()<16) {
urgentWrite(0x2a,(unsigned char)s->data8[dacPos]+0x80);
dacReady=false;
}
}
dacPos++;
if (((s->loopMode!=DIV_SAMPLE_LOOPMODE_ONESHOT) && dacPos>=s->loopEnd) || (dacPos>=s->samples)) {
if (s->isLoopable()) {
dacPos=s->loopStart;
} else {
dacSample=-1;
if (parent->song.brokenDACMode) {
rWrite(0x2b,0);
}
}
}
while (dacPeriod>=rate) dacPeriod-=rate;
} else {
dacSample=-1;
}
}
}
processDAC();
os[0]=0; os[1]=0;
for (int i=0; i<6; i++) {
if (!writes.empty() && --delay<0) {
@ -215,41 +273,7 @@ void DivPlatformGenesis::acquire_ymfm(short* bufL, short* bufR, size_t start, si
ymfm::ym2612::fm_engine* fme=fm_ymfm->debug_engine();
for (size_t h=start; h<start+len; h++) {
if (!dacReady) {
dacDelay+=32000;
if (dacDelay>=rate) {
dacDelay-=rate;
dacReady=true;
}
}
if (dacMode && dacSample!=-1) {
dacPeriod+=dacRate;
if (dacPeriod>=rate) {
DivSample* s=parent->getSample(dacSample);
if (s->samples>0) {
if (!isMuted[5]) {
if (dacReady && writes.size()<16) {
urgentWrite(0x2a,(unsigned char)s->data8[dacPos]+0x80);
dacReady=false;
}
}
dacPos++;
if (((s->loopMode!=DIV_SAMPLE_LOOPMODE_ONESHOT) && dacPos>=s->loopEnd) || (dacPos>=s->samples)) {
if (s->isLoopable()) {
dacPos=s->loopStart;
} else {
dacSample=-1;
if (parent->song.brokenDACMode) {
rWrite(0x2b,0);
}
}
}
while (dacPeriod>=rate) dacPeriod-=rate;
} else {
dacSample=-1;
}
}
}
processDAC();
os[0]=0; os[1]=0;
if (!writes.empty()) {
@ -474,7 +498,7 @@ void DivPlatformGenesis::tick(bool sysTick) {
}
for (int i=0; i<6; i++) {
for (int i=0; i<8; i++) {
if (i==2 && extMode) continue;
if (chan[i].freqChanged) {
if (parent->song.linearPitch==2) {
@ -493,12 +517,14 @@ void DivPlatformGenesis::tick(bool sysTick) {
chan[i].freq=(block<<11)|fNum;
}
if (chan[i].freq>0x3fff) chan[i].freq=0x3fff;
immWrite(chanOffs[i]+ADDR_FREQH,chan[i].freq>>8);
immWrite(chanOffs[i]+ADDR_FREQ,chan[i].freq&0xff);
if (chan[i].furnaceDac && dacMode) {
if (i<6) {
immWrite(chanOffs[i]+ADDR_FREQH,chan[i].freq>>8);
immWrite(chanOffs[i]+ADDR_FREQ,chan[i].freq&0xff);
}
if (chan[i].furnaceDac && chan[i].dacMode) {
double off=1.0;
if (dacSample>=0 && dacSample<parent->song.sampleLen) {
DivSample* s=parent->getSample(dacSample);
if (chan[i].dacSample>=0 && chan[i].dacSample<parent->song.sampleLen) {
DivSample* s=parent->getSample(chan[i].dacSample);
if (s->centerRate<1) {
off=1.0;
} else {
@ -506,14 +532,14 @@ void DivPlatformGenesis::tick(bool sysTick) {
}
}
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].pitch2,1,1);
dacRate=chan[i].freq*off;
if (dacRate<1) dacRate=1;
if (dumpWrites) addWrite(0xffff0001,dacRate);
chan[i].dacRate=chan[i].freq*off;
if (chan[i].dacRate<1) chan[i].dacRate=1;
if (dumpWrites) addWrite(0xffff0001,chan[i].dacRate);
}
chan[i].freqChanged=false;
}
if (chan[i].keyOn) {
immWrite(0x28,0xf0|konOffs[i]);
if (i<6) immWrite(0x28,0xf0|konOffs[i]);
chan[i].keyOn=false;
}
}
@ -521,6 +547,7 @@ void DivPlatformGenesis::tick(bool sysTick) {
void DivPlatformGenesis::muteChannel(int ch, bool mute) {
isMuted[ch]=mute;
if (ch>5) return;
for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[ch]|opOffs[j];
DivInstrumentFM::Operator& op=chan[ch].state.op[j];
@ -541,29 +568,33 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM);
if (c.chan==5) {
if (c.chan>=5) {
if (ins->type==DIV_INS_AMIGA) {
dacMode=1;
chan[c.chan].dacMode=1;
rWrite(0x2b,1<<7);
} else if (chan[c.chan].furnaceDac) {
dacMode=0;
chan[c.chan].dacMode=0;
rWrite(0x2b,0<<7);
}
}
if (c.chan==5 && dacMode) {
if (c.chan>=5 && chan[c.chan].dacMode) {
if (skipRegisterWrites) break;
if (ins->type==DIV_INS_AMIGA) { // Furnace mode
dacSample=ins->amiga.getSample(c.value);
if (dacSample<0 || dacSample>=parent->song.sampleLen) {
dacSample=-1;
chan[c.chan].dacSample=ins->amiga.getSample(c.value);
if (chan[c.chan].dacSample<0 || chan[c.chan].dacSample>=parent->song.sampleLen) {
chan[c.chan].dacSample=-1;
if (dumpWrites) addWrite(0xffff0002,0);
break;
} else {
chan[c.chan].dacReversed=ins->amiga.getReversed(c.value);
rWrite(0x2b,1<<7);
if (dumpWrites) addWrite(0xffff0000,dacSample);
if (dumpWrites) {
addWrite(0xffff0000,chan[c.chan].dacSample);
addWrite(0xffff0003,chan[c.chan].getDacDirection());
}
}
dacPos=0;
dacPeriod=0;
chan[c.chan].dacPos=0;
chan[c.chan].dacPeriod=0;
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].baseFreq=parent->calcBaseFreq(1,1,c.value,false);
chan[c.chan].freqChanged=true;
@ -573,23 +604,25 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].note=c.value;
}
dacSample=12*sampleBank+chan[c.chan].note%12;
if (dacSample>=parent->song.sampleLen) {
dacSample=-1;
chan[c.chan].dacSample=12*chan[c.chan].sampleBank+chan[c.chan].note%12;
if (chan[c.chan].dacSample>=parent->song.sampleLen) {
chan[c.chan].dacSample=-1;
if (dumpWrites) addWrite(0xffff0002,0);
break;
} else {
rWrite(0x2b,1<<7);
if (dumpWrites) addWrite(0xffff0000,dacSample);
if (dumpWrites) addWrite(0xffff0000,chan[c.chan].dacSample);
}
dacPos=0;
dacPeriod=0;
dacRate=MAX(1,parent->getSample(dacSample)->rate);
if (dumpWrites) addWrite(0xffff0001,parent->getSample(dacSample)->rate);
chan[c.chan].dacPos=0;
chan[c.chan].dacPeriod=0;
chan[c.chan].dacRate=MAX(1,parent->getSample(chan[c.chan].dacSample)->rate);
if (dumpWrites) addWrite(0xffff0001,parent->getSample(chan[c.chan].dacSample)->rate);
chan[c.chan].furnaceDac=false;
chan[c.chan].dacReversed=false;
}
break;
}
if (c.chan>=6) break;
if (chan[c.chan].insChanged) {
chan[c.chan].state=ins->fm;
@ -642,12 +675,12 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break;
}
case DIV_CMD_NOTE_OFF:
if (c.chan==5) {
dacSample=-1;
if (c.chan>=5) {
chan[c.chan].dacSample=-1;
if (dumpWrites) addWrite(0xffff0002,0);
if (parent->song.brokenDACMode) {
rWrite(0x2b,0);
if (dacMode) break;
if (chan[c.chan].dacMode) break;
}
}
chan[c.chan].keyOff=true;
@ -655,8 +688,8 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
chan[c.chan].active=false;
break;
case DIV_CMD_NOTE_OFF_ENV:
if (c.chan==5) {
dacSample=-1;
if (c.chan>=5) {
chan[c.chan].dacSample=-1;
if (dumpWrites) addWrite(0xffff0002,0);
}
chan[c.chan].keyOff=true;
@ -672,6 +705,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
if (!chan[c.chan].std.vol.has) {
chan[c.chan].outVol=c.value;
}
if (c.chan>=6) break;
for (int i=0; i<4; i++) {
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
@ -698,6 +732,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
chan[c.chan].ins=c.value;
break;
case DIV_CMD_PANNING: {
if (c.chan>5) c.chan=5;
if (c.value==0 && c.value2==0) {
chan[c.chan].pan=3;
} else {
@ -735,7 +770,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
}
break;
}
if (c.chan==5 && chan[c.chan].furnaceDac && dacMode) {
if (c.chan>=5 && chan[c.chan].furnaceDac && chan[c.chan].dacMode) {
int destFreq=parent->calcBaseFreq(1,1,c.value2,false);
bool return2=false;
if (destFreq>chan[c.chan].baseFreq) {
@ -762,18 +797,26 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break;
}
case DIV_CMD_SAMPLE_MODE: {
dacMode=c.value;
if (c.chan<5) c.chan=5;
chan[c.chan].dacMode=c.value;
rWrite(0x2b,c.value<<7);
break;
}
case DIV_CMD_SAMPLE_BANK:
sampleBank=c.value;
if (sampleBank>(parent->song.sample.size()/12)) {
sampleBank=parent->song.sample.size()/12;
if (c.chan<5) c.chan=5;
chan[c.chan].sampleBank=c.value;
if (chan[c.chan].sampleBank>(parent->song.sample.size()/12)) {
chan[c.chan].sampleBank=parent->song.sample.size()/12;
}
break;
case DIV_CMD_SAMPLE_DIR: {
if (c.chan<5) c.chan=5;
chan[c.chan].dacDirection=c.value;
if (dumpWrites) addWrite(0xffff0003,chan[c.chan].dacDirection);
break;
}
case DIV_CMD_LEGATO: {
if (c.chan==5 && chan[c.chan].furnaceDac && dacMode) {
if (c.chan>=5 && chan[c.chan].furnaceDac && chan[c.chan].dacMode) {
chan[c.chan].baseFreq=parent->calcBaseFreq(1,1,c.value,false);
} else {
chan[c.chan].baseFreq=NOTE_FNUM_BLOCK(c.value,11);
@ -783,16 +826,19 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_LFO: {
if (c.chan>=6) break;
lfoValue=(c.value&7)|((c.value>>4)<<3);
rWrite(0x22,lfoValue);
break;
}
case DIV_CMD_FM_FB: {
if (c.chan>=6) break;
chan[c.chan].state.fb=c.value&7;
rWrite(chanOffs[c.chan]+ADDR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3));
break;
}
case DIV_CMD_FM_MULT: {
if (c.chan>=6) break;
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
op.mult=c.value2&15;
@ -800,6 +846,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_TL: {
if (c.chan>=6) break;
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
op.tl=c.value2;
@ -815,6 +862,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_AR: {
if (c.chan>=6) break;
if (c.value<0) {
for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
@ -831,6 +879,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_RS: {
if (c.chan>=6) break;
if (c.value<0) {
for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
@ -847,6 +896,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_AM: {
if (c.chan>=6) break;
if (c.value<0) {
for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
@ -863,6 +913,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_DR: {
if (c.chan>=6) break;
if (c.value<0) {
for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
@ -879,6 +930,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_SL: {
if (c.chan>=6) break;
if (c.value<0) {
for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
@ -895,6 +947,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_RR: {
if (c.chan>=6) break;
if (c.value<0) {
for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
@ -911,6 +964,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_D2R: {
if (c.chan>=6) break;
if (c.value<0) {
for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
@ -927,6 +981,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_DT: {
if (c.chan>=6) break;
if (c.value<0) {
for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
@ -943,6 +998,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_SSG: {
if (c.chan>=6) break;
if (c.value<0) {
for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
@ -959,6 +1015,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_HARD_RESET:
if (c.chan>=6) break;
chan[c.chan].hardReset=c.value;
break;
case DIV_ALWAYS_SET_VOLUME:
@ -1007,7 +1064,7 @@ void DivPlatformGenesis::forceIns() {
chan[i].freqChanged=true;
}
}
if (dacMode) {
if (chan[5].dacMode) {
rWrite(0x2b,0x80);
}
immWrite(0x22,lfoValue);
@ -1057,18 +1114,18 @@ void DivPlatformGenesis::reset() {
}
lastBusy=60;
dacMode=0;
dacPeriod=0;
dacPos=0;
dacRate=0;
dacDelay=0;
dacReady=true;
dacSample=-1;
sampleBank=0;
lfoValue=8;
softPCMTimer=0;
extMode=false;
if (softPCM) {
chan[5].dacMode=true;
chan[6].dacMode=true;
}
// normal sample direction
if (dumpWrites) addWrite(0xffff0003,0);
// LFO
immWrite(0x22,lfoValue);
@ -1088,7 +1145,7 @@ bool DivPlatformGenesis::keyOffAffectsPorta(int ch) {
}
void DivPlatformGenesis::notifyInsChange(int ins) {
for (int i=0; i<6; i++) {
for (int i=0; i<10; i++) {
if (chan[i].ins==ins) {
chan[i].insChanged=true;
}
@ -1114,6 +1171,10 @@ void DivPlatformGenesis::setYMFM(bool use) {
useYMFM=use;
}
void DivPlatformGenesis::setSoftPCM(bool value) {
softPCM=value;
}
void DivPlatformGenesis::setFlags(unsigned int flags) {
switch (flags) {
case 1: chipClock=COLOR_PAL*12.0/7.0; break;
@ -1152,6 +1213,11 @@ int DivPlatformGenesis::init(DivEngine* p, int channels, int sugRate, unsigned i
fm_ymfm=NULL;
setFlags(flags);
for (int i=0; i<128; i++) {
dacVolTable[127-i]=128*pow(10.0f,(float)(-i)*0.75f/20.0f);
}
dacVolTable[0]=0;
reset();
return 10;
}

View file

@ -41,10 +41,24 @@ class DivPlatformGenesis: public DivDispatch {
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, furnaceDac, inPorta, hardReset;
int vol, outVol;
unsigned char pan;
bool dacMode;
int dacPeriod;
int dacRate;
unsigned int dacPos;
int dacSample;
int dacDelay;
bool dacReady;
bool dacDirection;
bool dacReversed;
unsigned char sampleBank;
void macroInit(DivInstrument* which) {
std.init(which);
pitch2=0;
}
bool getDacDirection() {
return dacReversed^dacDirection;
}
Channel():
freqH(0),
freqL(0),
@ -65,7 +79,18 @@ class DivPlatformGenesis: public DivDispatch {
inPorta(false),
hardReset(false),
vol(0),
pan(3) {}
outVol(0),
pan(3),
dacMode(false),
dacPeriod(0),
dacRate(0),
dacPos(0),
dacSample(-1),
dacDelay(0),
dacReady(true),
dacDirection(false),
dacReversed(false),
sampleBank(0) {}
};
Channel chan[10];
DivDispatchOscBuffer* oscBuf[10];
@ -86,24 +111,21 @@ class DivPlatformGenesis: public DivDispatch {
DivYM2612Interface iface;
unsigned char regPool[512];
bool dacMode;
int dacPeriod;
int dacRate;
unsigned int dacPos;
int dacSample;
int dacDelay;
bool dacReady;
unsigned char sampleBank;
unsigned char lfoValue;
bool extMode, useYMFM;
int softPCMTimer;
bool extMode, softPCM, useYMFM;
bool ladder;
short oldWrites[512];
short pendingWrites[512];
unsigned char dacVolTable[128];
friend void putDispatchChan(void*,int,int);
inline void processDAC();
void acquire_nuked(short* bufL, short* bufR, size_t start, size_t len);
void acquire_ymfm(short* bufL, short* bufR, size_t start, size_t len);
@ -126,6 +148,7 @@ class DivPlatformGenesis: public DivDispatch {
void setFlags(unsigned int flags);
void notifyInsChange(int ins);
void notifyInsDeletion(void* ins);
void setSoftPCM(bool value);
int getPortaFloor(int ch);
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);

View file

@ -156,16 +156,16 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
case DIV_CMD_SAMPLE_MODE: {
// not ignored actually!
if (!parent->song.ignoreDACModeOutsideIntendedChannel) {
dacMode=c.value;
chan[5].dacMode=c.value;
rWrite(0x2b,c.value<<7);
}
break;
}
case DIV_CMD_SAMPLE_BANK:
if (!parent->song.ignoreDACModeOutsideIntendedChannel) {
sampleBank=c.value;
if (sampleBank>(parent->song.sample.size()/12)) {
sampleBank=parent->song.sample.size()/12;
chan[5].sampleBank=c.value;
if (chan[5].sampleBank>(parent->song.sample.size()/12)) {
chan[5].sampleBank=parent->song.sample.size()/12;
}
}
break;
@ -484,7 +484,7 @@ void DivPlatformGenesisExt::forceIns() {
chan[i].freqChanged=true;
}
}
if (dacMode) {
if (chan[5].dacMode) {
rWrite(0x2b,0x80);
}
immWrite(0x22,lfoValue);

View file

@ -25,6 +25,7 @@
#define WRITE_VOLUME(ch,v) rWrite(0x20+(ch<<3),(v))
#define WRITE_FEEDBACK(ch,v) rWrite(0x21+(ch<<3),(v))
#define WRITE_OUTPUT(ch,v) rWrite(0x22+(ch<<3),(v))
#define WRITE_LFSR(ch,v) rWrite(0x23+(ch<<3),(v))
#define WRITE_BACKUP(ch,v) rWrite(0x24+(ch<<3),(v))
#define WRITE_CONTROL(ch,v) rWrite(0x25+(ch<<3),(v))
@ -151,13 +152,18 @@ void DivPlatformLynx::acquire(short* bufL, short* bufR, size_t start, size_t len
DivSample* s=parent->getSample(chan[i].sample);
if (s!=NULL) {
if (isMuted[i]) {
WRITE_VOLUME(i,0);
WRITE_OUTPUT(i,0);
chan[i].samplePos++;
} else {
WRITE_VOLUME(i,(s->data8[chan[i].samplePos++]*chan[i].outVol)>>7);
WRITE_OUTPUT(i,(s->data8[chan[i].samplePos++]*chan[i].outVol)>>7);
}
if (chan[i].samplePos>=(int)s->samples) {
chan[i].sample=-1;
if (s->loopStart>=0 && s->loopStart<(int)s->samples) {
chan[i].samplePos=s->loopStart;
} else {
chan[i].sample=-1;
}
}
}
}
@ -176,8 +182,8 @@ void DivPlatformLynx::tick(bool sysTick) {
chan[i].outVol=((chan[i].vol&127)*MIN(64,chan[i].std.vol.val))>>6;
} else {
chan[i].outVol=((chan[i].vol&127)*MIN(127,chan[i].std.vol.val))>>7;
WRITE_VOLUME(i,(isMuted[i]?0:(chan[i].outVol&127)));
}
WRITE_VOLUME(i,(isMuted[i]?0:(chan[i].outVol&127)));
}
if (chan[i].std.arp.had) {
if (!chan[i].inPorta) {
@ -244,11 +250,6 @@ void DivPlatformLynx::tick(bool sysTick) {
}
}
chan[i].sampleFreq=off*parent->calcFreq(chan[i].sampleBaseFreq,chan[i].pitch,false,2,chan[i].pitch2,1,1);
WRITE_FEEDBACK(i,0);
WRITE_LFSR(i,0);
WRITE_OTHER(i,0);
WRITE_CONTROL(i,0x18);
WRITE_BACKUP(i,2);
} else {
if (chan[i].lfsr >= 0) {
WRITE_LFSR(i, (chan[i].lfsr&0xff));
@ -300,7 +301,8 @@ int DivPlatformLynx::dispatch(DivCommand c) {
}
case DIV_CMD_NOTE_OFF:
chan[c.chan].active=false;
WRITE_VOLUME(c.chan, 0);
WRITE_VOLUME(c.chan,0);
WRITE_CONTROL(c.chan,0);
chan[c.chan].macroInit(NULL);
if (chan[c.chan].pcm) {
chan[c.chan].pcm=false;

View file

@ -23,13 +23,18 @@
#include <string.h>
#include <math.h>
#define rWrite(v) if (!skipRegisterWrites) {writes.emplace(0,v); if (dumpWrites) {addWrite(0,v);} }
#define rWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} }
const char** DivPlatformMSM6295::getRegisterSheet() {
return NULL;
}
const char* DivPlatformMSM6295::getEffectName(unsigned char effect) {
switch (effect) {
case 0x20:
return "20xx: Set chip output rate (0: clock/132; 1: clock/165)";
break;
}
return NULL;
}
@ -42,7 +47,28 @@ void DivPlatformMSM6295::acquire(short* bufL, short* bufR, size_t start, size_t
if (delay<=0) {
if (!writes.empty()) {
QueuedWrite& w=writes.front();
msm->command_w(w.val);
switch (w.addr) {
case 0: // command
msm->command_w(w.val);
break;
case 8: // chip clock select (VGM)
case 9:
case 10:
case 11:
break;
case 12: // rate select
msm->ss_w(!w.val);
break;
case 14: // enable bankswitch
break;
case 15: // set bank base
break;
case 16: // switch bank
case 17:
case 18:
case 19:
break;
}
writes.pop();
delay=32;
}
@ -92,9 +118,9 @@ int DivPlatformMSM6295::dispatch(DivCommand c) {
}
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
rWrite((8<<c.chan)); // turn off
rWrite(0x80|chan[c.chan].sample); // set phrase
rWrite((16<<c.chan)|(8-chan[c.chan].outVol)); // turn on
rWrite(0,(8<<c.chan)); // turn off
rWrite(0,0x80|chan[c.chan].sample); // set phrase
rWrite(0,(16<<c.chan)|(8-chan[c.chan].outVol)); // turn on
} else {
break;
}
@ -107,9 +133,9 @@ int DivPlatformMSM6295::dispatch(DivCommand c) {
}
//DivSample* s=parent->getSample(12*sampleBank+c.value%12);
chan[c.chan].sample=12*sampleBank+c.value%12;
rWrite((8<<c.chan)); // turn off
rWrite(0x80|chan[c.chan].sample); // set phrase
rWrite((16<<c.chan)|(8-chan[c.chan].outVol)); // turn on
rWrite(0,(8<<c.chan)); // turn off
rWrite(0,0x80|chan[c.chan].sample); // set phrase
rWrite(0,(16<<c.chan)|(8-chan[c.chan].outVol)); // turn on
}
break;
}
@ -117,14 +143,14 @@ int DivPlatformMSM6295::dispatch(DivCommand c) {
chan[c.chan].keyOff=true;
chan[c.chan].keyOn=false;
chan[c.chan].active=false;
rWrite((8<<c.chan)); // turn off
rWrite(0,(8<<c.chan)); // turn off
chan[c.chan].macroInit(NULL);
break;
case DIV_CMD_NOTE_OFF_ENV:
chan[c.chan].keyOff=true;
chan[c.chan].keyOn=false;
chan[c.chan].active=false;
rWrite((8<<c.chan)); // turn off
rWrite(0,(8<<c.chan)); // turn off
chan[c.chan].std.release();
break;
case DIV_CMD_ENV_RELEASE:
@ -153,6 +179,10 @@ int DivPlatformMSM6295::dispatch(DivCommand c) {
case DIV_CMD_NOTE_PORTA: {
return 2;
}
case DIV_CMD_SAMPLE_FREQ:
rateSel=c.value;
rWrite(12,!rateSel);
break;
case DIV_CMD_SAMPLE_BANK:
sampleBank=c.value;
if (sampleBank>(parent->song.sample.size()/12)) {
@ -190,6 +220,7 @@ void DivPlatformMSM6295::forceIns() {
for (int i=0; i<4; i++) {
chan[i].insChanged=true;
}
rWrite(12,!rateSel);
}
void* DivPlatformMSM6295::getChanState(int ch) {
@ -219,6 +250,7 @@ void DivPlatformMSM6295::poke(std::vector<DivRegWrite>& wlist) {
void DivPlatformMSM6295::reset() {
while (!writes.empty()) writes.pop();
msm->reset();
msm->ss_w(false);
if (dumpWrites) {
addWrite(0xffffffff,0);
}
@ -232,6 +264,7 @@ void DivPlatformMSM6295::reset() {
}
sampleBank=0;
rateSel=false;
delay=0;
}
@ -240,6 +273,10 @@ bool DivPlatformMSM6295::keyOffAffectsArp(int ch) {
return false;
}
float DivPlatformMSM6295::getPostAmp() {
return 3.0f;
}
void DivPlatformMSM6295::notifyInsChange(int ins) {
for (int i=0; i<4; i++) {
if (chan[i].ins==ins) {
@ -302,12 +339,21 @@ void DivPlatformMSM6295::renderSamples() {
}
void DivPlatformMSM6295::setFlags(unsigned int flags) {
if (flags&1) {
chipClock=8448000;
} else {
chipClock=8000000;
switch (flags) {
case 0:
chipClock=4000000/4;
break;
case 1:
chipClock=4224000/4;
break;
case 2:
chipClock=4000000;
break;
case 3:
chipClock=4224000;
break;
}
rate=chipClock/((flags&2)?6:24);
rate=chipClock/3;
for (int i=0; i<4; i++) {
isMuted[i]=false;
oscBuf[i]->rate=rate/22;

View file

@ -101,6 +101,7 @@ class DivPlatformMSM6295: public DivDispatch {
int delay, updateOsc;
bool extMode;
bool rateSel;
short oldWrites[512];
short pendingWrites[512];
@ -119,6 +120,7 @@ class DivPlatformMSM6295: public DivDispatch {
void tick(bool sysTick=true);
void muteChannel(int ch, bool mute);
bool keyOffAffectsArp(int ch);
float getPostAmp();
void notifyInsChange(int ins);
void notifyInsDeletion(void* ins);
void poke(unsigned int addr, unsigned short val);

View file

@ -735,6 +735,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
DivSample* s=parent->getSample(chan[c.chan].sample);
immWrite(8,0);
immWrite(7,0x01); // reset
immWrite(9,(s->offB>>2)&0xff);
immWrite(10,(s->offB>>10)&0xff);
int end=s->offB+s->lengthB-1;
@ -770,6 +771,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
}
DivSample* s=parent->getSample(12*sampleBank+c.value%12);
immWrite(8,0);
immWrite(7,0x01); // reset
immWrite(9,(s->offB>>2)&0xff);
immWrite(10,(s->offB>>10)&0xff);
int end=s->offB+s->lengthB-1;

View file

@ -42,6 +42,7 @@ const char* DivPlatformSMS::getEffectName(unsigned char effect) {
}
void DivPlatformSMS::acquire_nuked(short* bufL, short* bufR, size_t start, size_t len) {
int o=0;
for (size_t h=start; h<start+len; h++) {
if (!writes.empty()) {
unsigned char w=writes.front();
@ -64,7 +65,10 @@ void DivPlatformSMS::acquire_nuked(short* bufL, short* bufR, size_t start, size_
YMPSG_Clock(&sn_nuked);
YMPSG_Clock(&sn_nuked);
YMPSG_Clock(&sn_nuked);
bufL[h]=YMPSG_GetOutput(&sn_nuked)*8192.0;
o=YMPSG_GetOutput(&sn_nuked);
if (o<-32768) o=-32768;
if (o>32767) o=32767;
bufL[h]=o;
/*
for (int i=0; i<4; i++) {
if (isMuted[i]) {

View file

@ -63,6 +63,7 @@ const char* cmdName[]={
"SAMPLE_FREQ",
"SAMPLE_BANK",
"SAMPLE_POS",
"SAMPLE_DIR",
"SAMPLE_TRANSWAVE_SLICE_MODE", // (enabled)
"SAMPLE_TRANSWAVE_SLICE_POS", // (slice)
@ -570,6 +571,9 @@ void DivEngine::processRow(int i, bool afterDelay) {
clockDrift=0;
subticks=0;
break;
case 0xdf: // set sample direction
dispatchCmd(DivCommand(DIV_CMD_SAMPLE_DIR,i,effectVal));
break;
case 0xe0: // arp speed
if (effectVal>0) {
curSubSong->arpLen=effectVal;

View file

@ -111,6 +111,9 @@ enum DivSystem {
DIV_SYSTEM_NAMCO,
DIV_SYSTEM_NAMCO_15XX,
DIV_SYSTEM_NAMCO_CUS30,
DIV_SYSTEM_YM2612_FRAC,
DIV_SYSTEM_YM2612_FRAC_EXT,
DIV_SYSTEM_RESERVED_8,
DIV_SYSTEM_DUMMY,
DIV_SYSTEM_MAX // boundary for max system number
};
@ -395,6 +398,10 @@ struct DivSong {
bool snDutyReset;
bool pitchMacroIsLinear;
bool oldOctaveBoundary;
bool noOPN2Vol;
bool newVolumeScaling;
bool volMacroLinger;
bool brokenOutVol;
std::vector<DivInstrument*> ins;
std::vector<DivWavetable*> wave;
@ -488,7 +495,11 @@ struct DivSong {
fbPortaPause(false),
snDutyReset(false),
pitchMacroIsLinear(true),
oldOctaveBoundary(false) {
oldOctaveBoundary(false),
noOPN2Vol(false),
newVolumeScaling(true),
volMacroLinger(true),
brokenOutVol(false) {
for (int i=0; i<32; i++) {
system[i]=DIV_SYSTEM_NULL;
systemVol[i]=64;

View file

@ -2027,12 +2027,23 @@ void DivEngine::registerSystems() {
);
sysDefs[DIV_SYSTEM_MSM6295]=new DivSysDef(
"OKI MSM6295", NULL, 0xaa, 0, 4, false, true, 0, false,
"OKI MSM6295", NULL, 0xaa, 0, 4, false, true, 0x161, false,
"an ADPCM sound chip manufactured by OKI and used in many arcade boards.",
{"Channel 1", "Channel 2", "Channel 3", "Channel 4"},
{"CH1", "CH2", "CH3", "CH4"},
{DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM},
{DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA}
{DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA},
{},
[this](int ch, unsigned char effect, unsigned char effectVal) -> bool {
switch (effect) {
case 0x20: // select rate
dispatchCmd(DivCommand(DIV_CMD_SAMPLE_FREQ,ch,effectVal));
break;
default:
return false;
}
return true;
}
);
sysDefs[DIV_SYSTEM_MSM6258]=new DivSysDef(
@ -2100,6 +2111,40 @@ void DivEngine::registerSystems() {
namcoEffectHandler
);
// replace with an 8-channel chip in a future
sysDefs[DIV_SYSTEM_RESERVED_8]=new DivSysDef(
"Reserved", NULL, 0xbc, 0, 8, false, true, 0, false,
"this was YM2612_FRAC, but due to changes this ID is reserved.",
{"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8"},
{"CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8"},
{DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE},
{DIV_INS_STD, DIV_INS_STD, DIV_INS_STD, DIV_INS_STD, DIV_INS_STD, DIV_INS_STD, DIV_INS_STD, DIV_INS_STD}
);
sysDefs[DIV_SYSTEM_YM2612_FRAC]=new DivSysDef(
"Yamaha YM2612 (OPN2) with DualPCM", NULL, 0xbe, 0, 7, true, false, 0, false,
"this chip is mostly known for being in the Sega Genesis (but it also was on the FM Towns computer).\nthis system uses software mixing to provide two sample channels.",
{"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6/PCM 1", "PCM 2"},
{"F1", "F2", "F3", "F4", "F5", "P1", "P2"},
{DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PCM, DIV_CH_PCM},
{DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AMIGA},
{DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_AMIGA, DIV_INS_NULL},
opn2EffectHandler,
fmPostEffectHandler
);
sysDefs[DIV_SYSTEM_YM2612_FRAC_EXT]=new DivSysDef(
"Yamaha YM2612 (OPN2) Extended Channel 3 with DualPCM and CSM", NULL, 0xbd, 0, 11, true, false, 0, false,
"this chip is mostly known for being in the Sega Genesis (but it also was on the FM Towns computer).\nthis system uses software mixing to provide two sample channels.\nthis one is in Extended Channel mode, which turns the second FM channel into four operators with independent notes/frequencies.",
{"FM 1", "FM 2", "FM 3 OP1", "FM 3 OP2", "FM 3 OP3", "FM 3 OP4", "FM 4", "FM 5", "FM 6/PCM 1", "PCM 2", "CSM Timer"},
{"F1", "F2", "O1", "O2", "O3", "O4", "F4", "F5", "P1", "P2", "CSM"},
{DIV_CH_FM, DIV_CH_FM, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_NOISE},
{DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AMIGA, DIV_INS_FM},
{DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_AMIGA, DIV_INS_NULL, DIV_INS_NULL},
opn2EffectHandler,
fmPostEffectHandler
);
sysDefs[DIV_SYSTEM_DUMMY]=new DivSysDef(
"Dummy System", NULL, 0xfd, 0, 8, false, true, 0, false,
"this is a system designed for testing purposes.",

View file

@ -24,7 +24,7 @@
constexpr int MASTER_CLOCK_PREC=(sizeof(void*)==8)?8:0;
void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write, int streamOff, double* loopTimer, double* loopFreq, int* loopSample, bool isSecond) {
void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write, int streamOff, double* loopTimer, double* loopFreq, int* loopSample, bool* sampleDir, bool isSecond) {
unsigned char baseAddr1=isSecond?0xa0:0x50;
unsigned char baseAddr2=isSecond?0x80:0;
unsigned short baseAddr2S=isSecond?0x8000:0;
@ -420,22 +420,22 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
case DIV_SYSTEM_Y8950_DRUMS:
// disable envelope
for (int i=0; i<6; i++) {
w->writeC(0x0b|baseAddr1);
w->writeC(0x0c|baseAddr1);
w->writeC(0x80+i);
w->writeC(0x0f);
w->writeC(0x0b|baseAddr1);
w->writeC(0x0c|baseAddr1);
w->writeC(0x88+i);
w->writeC(0x0f);
w->writeC(0x0b|baseAddr1);
w->writeC(0x0c|baseAddr1);
w->writeC(0x90+i);
w->writeC(0x0f);
}
// key off + freq reset
for (int i=0; i<9; i++) {
w->writeC(0x0b|baseAddr1);
w->writeC(0x0c|baseAddr1);
w->writeC(0xa0+i);
w->writeC(0);
w->writeC(0x0b|baseAddr1);
w->writeC(0x0c|baseAddr1);
w->writeC(0xb0+i);
w->writeC(0);
}
@ -522,6 +522,15 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
w->writeC(rf5c68Addr);
w->writeC(8);
w->writeC(0xff);
break;
case DIV_SYSTEM_MSM6295:
w->writeC(0xb8); // disable all channels
w->writeC(baseAddr2|0);
w->writeC(0x78);
w->writeC(0xb8); // select rate
w->writeC(baseAddr2|12);
w->writeC(1);
break;
default:
break;
}
@ -536,8 +545,8 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
w->writeC(0x95);
w->writeC(streamID);
w->writeS(write.val); // sample number
w->writeC((sample->loopStart==0)); // flags
if (sample->loopStart>0) {
w->writeC((sample->loopStart==0)|(sampleDir[streamID]?0x10:0)); // flags
if (sample->loopStart>0 && !sampleDir[streamID]) {
loopTimer[streamID]=(double)sample->loopEnd;
loopSample[streamID]=write.val;
}
@ -554,6 +563,9 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
w->writeC(streamID);
loopSample[streamID]=-1;
break;
case 3: // set sample direction
sampleDir[streamID]=write.val;
break;
}
return;
}
@ -818,6 +830,11 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
w->writeC(write.addr&0xff);
w->writeC(write.val);
break;
case DIV_SYSTEM_MSM6295:
w->writeC(0xb8);
w->writeC(baseAddr2|(write.addr&0x7f));
w->writeC(write.val);
break;
default:
logW("write not handled!");
break;
@ -923,11 +940,13 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) {
double loopTimer[DIV_MAX_CHANS];
double loopFreq[DIV_MAX_CHANS];
int loopSample[DIV_MAX_CHANS];
bool sampleDir[DIV_MAX_CHANS];
for (int i=0; i<DIV_MAX_CHANS; i++) {
loopTimer[i]=0;
loopFreq[i]=0;
loopSample[i]=-1;
sampleDir[i]=false;
}
bool writeDACSamples=false;
@ -942,6 +961,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) {
DivDispatch* writeES5506[2]={NULL,NULL};
DivDispatch* writeZ280[2]={NULL,NULL};
DivDispatch* writeRF5C68[2]={NULL,NULL};
DivDispatch* writeMSM6295[2]={NULL,NULL};
for (int i=0; i<song.systemLen; i++) {
willExport[i]=false;
@ -1348,6 +1368,19 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) {
writeRF5C68[0]=disCont[i].dispatch;
}
break;
case DIV_SYSTEM_MSM6295:
if (!hasOKIM6295) {
hasOKIM6295=disCont[i].dispatch->chipClock;
willExport[i]=true;
writeMSM6295[0]=disCont[i].dispatch;
} else if (!(hasOKIM6295&0x40000000)) {
isSecond[i]=true;
willExport[i]=true;
writeMSM6295[1]=disCont[i].dispatch;
hasOKIM6295|=0x40000000;
howManyChips++;
}
break;
default:
break;
}
@ -1694,6 +1727,15 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) {
w->writeI(0);
w->write(writeRF5C68[i]->getSampleMem(),writeRF5C68[i]->getSampleMemUsage());
}
if (writeMSM6295[i]!=NULL && writeMSM6295[i]->getSampleMemUsage()>0) {
w->writeC(0x67);
w->writeC(0x66);
w->writeC(0x8b);
w->writeI((writeMSM6295[i]->getSampleMemUsage()+8)|(i*0x80000000));
w->writeI(writeMSM6295[i]->getSampleMemCapacity());
w->writeI(0);
w->write(writeMSM6295[i]->getSampleMem(),writeMSM6295[i]->getSampleMemUsage());
}
}
// TODO
@ -1829,7 +1871,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) {
for (int i=0; i<song.systemLen; i++) {
std::vector<DivRegWrite>& writes=disCont[i].dispatch->getRegisterWrites();
for (DivRegWrite& j: writes) {
performVGMWrite(w,song.system[i],j,streamIDs[i],loopTimer,loopFreq,loopSample,isSecond[i]);
performVGMWrite(w,song.system[i],j,streamIDs[i],loopTimer,loopFreq,loopSample,sampleDir,isSecond[i]);
writeCount++;
}
writes.clear();

View file

@ -186,7 +186,7 @@ bool DivWaveSynth::tick(bool skipSubDiv) {
break;
case DIV_WS_PHASE_MOD:
for (int i=0; i<=state.speed; i++) {
int mod=(wave2[pos]*(state.param2-stage)*width)/512;
int mod=(wave2[pos]*(state.param2-stage)*width)/(64*(height+1));
output[pos]=wave1[(pos+mod)%width];
if (++pos>=width) {
pos=0;