Merge branch 'master' of https://github.com/tildearrow/furnace into es5506_alt
* 'master' of https://github.com/tildearrow/furnace: (58 commits) SMS: early Nuked-PSG modding SMS: add modified Nuked-PSG core build release and don't strip strip MinGW builds FDS: a bit more FDS: set a post-amp value FDS: fix NSFplay core low pass filter precision fix .dmf saving fix compilation on GCC 12 Fix multithreading on CI Lynx: why did I not commit this Lynx: more sample improvements Lynx: sample improvements Lynx: add sample support! GUI: fix wavetable list oversight WaveSynth: fix phase modulation - again GUI: fix possible wave editor crash WaveSynth: fix phase modulation Lynx: add phase reset macro fix another fucking IGFD crash bug ... # Conflicts: # src/gui/insEdit.cpp # src/gui/presets.cpp
This commit is contained in:
commit
cbf20c6320
116 changed files with 8748 additions and 857 deletions
|
|
@ -207,7 +207,7 @@ void DivPlatformAmiga::tick(bool sysTick) {
|
|||
if (chan[i].std.pitch.had) {
|
||||
if (chan[i].std.pitch.mode) {
|
||||
chan[i].pitch2+=chan[i].std.pitch.val;
|
||||
CLAMP_VAR(chan[i].pitch2,-2048,2048);
|
||||
CLAMP_VAR(chan[i].pitch2,-32768,32767);
|
||||
} else {
|
||||
chan[i].pitch2=chan[i].std.pitch.val;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -337,7 +337,7 @@ void DivPlatformArcade::tick(bool sysTick) {
|
|||
if (chan[i].std.pitch.had) {
|
||||
if (chan[i].std.pitch.mode) {
|
||||
chan[i].pitch2+=chan[i].std.pitch.val;
|
||||
CLAMP_VAR(chan[i].pitch2,-2048,2048);
|
||||
CLAMP_VAR(chan[i].pitch2,-32768,32767);
|
||||
} else {
|
||||
chan[i].pitch2=chan[i].std.pitch.val;
|
||||
}
|
||||
|
|
@ -345,7 +345,7 @@ void DivPlatformArcade::tick(bool sysTick) {
|
|||
}
|
||||
|
||||
if (chan[i].std.phaseReset.had) {
|
||||
if (chan[i].std.phaseReset.val==1) {
|
||||
if (chan[i].std.phaseReset.val==1 && chan[i].active) {
|
||||
chan[i].keyOn=true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -224,7 +224,7 @@ void DivPlatformAY8910::tick(bool sysTick) {
|
|||
if (chan[i].std.pitch.had) {
|
||||
if (chan[i].std.pitch.mode) {
|
||||
chan[i].pitch2+=chan[i].std.pitch.val;
|
||||
CLAMP_VAR(chan[i].pitch2,-2048,2048);
|
||||
CLAMP_VAR(chan[i].pitch2,-32768,32767);
|
||||
} else {
|
||||
chan[i].pitch2=chan[i].std.pitch.val;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -242,7 +242,7 @@ void DivPlatformAY8930::tick(bool sysTick) {
|
|||
if (chan[i].std.pitch.had) {
|
||||
if (chan[i].std.pitch.mode) {
|
||||
chan[i].pitch2+=chan[i].std.pitch.val;
|
||||
CLAMP_VAR(chan[i].pitch2,-2048,2048);
|
||||
CLAMP_VAR(chan[i].pitch2,-32768,32767);
|
||||
} else {
|
||||
chan[i].pitch2=chan[i].std.pitch.val;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@ void DivPlatformBubSysWSG::tick(bool sysTick) {
|
|||
if (chan[i].std.pitch.had) {
|
||||
if (chan[i].std.pitch.mode) {
|
||||
chan[i].pitch2+=chan[i].std.pitch.val;
|
||||
CLAMP_VAR(chan[i].pitch2,-2048,2048);
|
||||
CLAMP_VAR(chan[i].pitch2,-32768,32767);
|
||||
} else {
|
||||
chan[i].pitch2=chan[i].std.pitch.val;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -191,7 +191,7 @@ void DivPlatformC64::tick(bool sysTick) {
|
|||
if (chan[i].std.pitch.had) {
|
||||
if (chan[i].std.pitch.mode) {
|
||||
chan[i].pitch2+=chan[i].std.pitch.val;
|
||||
CLAMP_VAR(chan[i].pitch2,-2048,2048);
|
||||
CLAMP_VAR(chan[i].pitch2,-32768,32767);
|
||||
} else {
|
||||
chan[i].pitch2=chan[i].std.pitch.val;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -284,7 +284,7 @@ void DivPlatformES5506::e_pin(bool state)
|
|||
}
|
||||
if (chan[ch].transwaveIRQ) {
|
||||
if ((chan[ch].cr&0x37)==0x34) { // IRQE = 1, BLE = 1, LPE = 0, LEI = 1
|
||||
DivInstrument* ins=parent->getIns(chan[i].ins);
|
||||
DivInstrument* ins=parent->getIns(chan[ch].ins);
|
||||
if (!ins->amiga.useNoteMap && ins->amiga.transWave.enable) {
|
||||
const int next=chan[ch].pcm.next;
|
||||
if (next>=0 && next<ins->amiga.transWaveMap.size()) {
|
||||
|
|
@ -316,8 +316,7 @@ void DivPlatformES5506::e_pin(bool state)
|
|||
loopEnd=(double)transWaveInd.loopEnd;
|
||||
if (ins->amiga.transWave.sliceEnable) { // sliced loop position?
|
||||
chan[ch].transWave.updateSize(s->samples,loopStart,loopEnd);
|
||||
chan[ch].transWave.slice=transWaveInd.slice;
|
||||
chan[ch].transWave.slicePos(chan[ch].transWave.slice);
|
||||
chan[ch].transWave.slicePos(double(chan[ch].transWave.slice)/4095.0);
|
||||
loopStart=transWaveInd.sliceStart;
|
||||
loopEnd=transWaveInd.sliceEnd;
|
||||
}
|
||||
|
|
@ -371,10 +370,10 @@ void DivPlatformES5506::e_pin(bool state)
|
|||
}
|
||||
chan[ch].transwaveIRQ=false;
|
||||
}
|
||||
if (chan[ch].isTransWave) {
|
||||
if (chan[ch].isTranswave) {
|
||||
pageReadMask(0x00|ch,0x5f,0x00,&chan[ch].cr);
|
||||
chan[ch].transwaveIRQ=true;
|
||||
chan[ch].isTransWave=false;
|
||||
chan[ch].isTranswave=false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -640,7 +639,7 @@ void DivPlatformES5506::tick(bool sysTick) {
|
|||
chan[i].volChanged.changed=0;
|
||||
}
|
||||
if (chan[i].pcmChanged.changed) {
|
||||
if (!chan[i].isTransWave) {
|
||||
if (!chan[i].isTranswave) {
|
||||
if (chan[i].pcmChanged.transwaveInd && (!ins->amiga.useNoteMap && ins->amiga.transWave.enable)) {
|
||||
const int next=chan[i].pcm.next;
|
||||
if (next>=0 && next<ins->amiga.transWaveMap.size()) {
|
||||
|
|
@ -667,8 +666,7 @@ void DivPlatformES5506::tick(bool sysTick) {
|
|||
loopEnd=(double)transWaveInd.loopEnd;
|
||||
if (ins->amiga.transWave.sliceEnable) { // sliced loop position?
|
||||
chan[i].transWave.updateSize(s->samples,loopStart,loopEnd);
|
||||
chan[i].transWave.slice=transWaveInd.slice;
|
||||
chan[i].transWave.slicePos(transWaveInd.slice);
|
||||
chan[i].transWave.slicePos(double(chan[i].transWave.slice)/4095.0);
|
||||
loopStart=transWaveInd.sliceStart;
|
||||
loopEnd=transWaveInd.sliceEnd;
|
||||
}
|
||||
|
|
@ -688,7 +686,7 @@ void DivPlatformES5506::tick(bool sysTick) {
|
|||
}
|
||||
chan[i].pcmChanged.transwaveInd=0;
|
||||
}
|
||||
if ((!chan[i].pcmChanged.transwaveInd) && (!chan[i].isTransWave)) {
|
||||
if ((!chan[i].pcmChanged.transwaveInd) && (!chan[i].isTranswave)) {
|
||||
if (chan[i].pcmChanged.index) {
|
||||
const int next=chan[i].pcm.next;
|
||||
bool sampleVaild=false;
|
||||
|
|
@ -737,8 +735,7 @@ void DivPlatformES5506::tick(bool sysTick) {
|
|||
loopEnd=(double)transWaveInd.loopEnd;
|
||||
if (ins->amiga.transWave.sliceEnable) { // sliced loop position?
|
||||
chan[i].transWave.updateSize(s->samples,loopStart,loopEnd);
|
||||
chan[i].transWave.slice=transWaveInd.slice;
|
||||
chan[i].transWave.slicePos(transWaveInd.slice);
|
||||
chan[i].transWave.slicePos(double(chan[i].transWave.slice)/4095.0);
|
||||
loopStart=transWaveInd.sliceStart;
|
||||
loopEnd=transWaveInd.sliceEnd;
|
||||
}
|
||||
|
|
@ -790,7 +787,7 @@ void DivPlatformES5506::tick(bool sysTick) {
|
|||
double loopEnd=(double)s->loopEnd;
|
||||
if (ins->amiga.transWave.sliceEnable) { // sliced loop position?
|
||||
chan[i].transWave.updateSize(s->samples,loopStart,loopEnd);
|
||||
chan[i].transWave.slicePos(chan[i].transWave.slice);
|
||||
chan[i].transWave.slicePos(double(chan[i].transWave.slice)/4095.0);
|
||||
loopStart=chan[i].transWave.sliceStart;
|
||||
loopEnd=chan[i].transWave.sliceEnd;
|
||||
}
|
||||
|
|
@ -798,8 +795,8 @@ void DivPlatformES5506::tick(bool sysTick) {
|
|||
const unsigned int nextLoopStart=(start+(unsigned int)(loopStart*2048.0))&0xfffff800;
|
||||
const unsigned int nextLoopEnd=(start+(unsigned int)((loopEnd-1.0)*2048.0))&0xffffff80;
|
||||
if ((chan[i].pcm.loopStart!=nextLoopStart) || (chan[i].pcm.loopEnd!=nextLoopEnd)) {
|
||||
chan[i].pcm.loopStart=(start+(unsigned int)(loopStart*2048.0))&0xfffff800;
|
||||
chan[i].pcm.loopEnd=(start+(unsigned int)((loopEnd-1.0)*2048.0))&0xffffff80;
|
||||
chan[i].pcm.loopStart=nextLoopStart;
|
||||
chan[i].pcm.loopEnd=nextLoopEnd;
|
||||
chan[i].pcmChanged.position=1;
|
||||
}
|
||||
}
|
||||
|
|
@ -1002,11 +999,11 @@ void DivPlatformES5506::tick(bool sysTick) {
|
|||
int DivPlatformES5506::dispatch(DivCommand c) {
|
||||
switch (c.cmd) {
|
||||
case DIV_CMD_NOTE_ON: {
|
||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
|
||||
bool sampleVaild=false;
|
||||
if (((ins->amiga.useNoteMap && !ins->amiga.transWave.enable) && (c.value>=0 && c.value<120)) ||
|
||||
((!ins->amiga.useNoteMap && ins->amiga.transWave.enable) && (ins->amiga.transWave.ind>=0 && ins->amiga.transWave.ind<ins->amiga.transWaveMap.size())) ||
|
||||
((!ins->amiga.useNoteMap && !ins->amiga.transWave.enable) && (ins->amiga.initSample>=0 && ins->amiga.initSample<parent->song.sampleLen))) {
|
||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
|
||||
DivInstrumentAmiga::NoteMap& noteMapind=ins->amiga.noteMap[c.value];
|
||||
DivInstrumentAmiga::TransWaveMap& transWaveInd=ins->amiga.transWaveMap[ins->amiga.transWave.ind];
|
||||
int sample=ins->amiga.initSample;
|
||||
|
|
@ -1043,7 +1040,7 @@ int DivPlatformES5506::dispatch(DivCommand c) {
|
|||
if (ins->amiga.transWave.enable) {
|
||||
if (transWaveInd.loopMode!=DIV_SAMPLE_LOOPMODE_ONESHOT) {
|
||||
loopMode=transWaveInd.loopMode;
|
||||
} else if ((chan[i].pcm.loopMode==DIV_SAMPLE_LOOPMODE_ONESHOT) || (!s->isLoopable())) { // default
|
||||
} else if ((chan[c.chan].pcm.loopMode==DIV_SAMPLE_LOOPMODE_ONESHOT) || (!s->isLoopable())) { // default
|
||||
loopMode=DIV_SAMPLE_LOOPMODE_PINGPONG;
|
||||
}
|
||||
// get loop position
|
||||
|
|
@ -1051,8 +1048,8 @@ int DivPlatformES5506::dispatch(DivCommand c) {
|
|||
loopEnd=(double)transWaveInd.loopEnd;
|
||||
if (ins->amiga.transWave.sliceEnable) { // sliced loop position?
|
||||
chan[c.chan].transWave.updateSize(s->samples,loopStart,loopEnd);
|
||||
chan[c.chan].transWave.slice=transWaveInd.slice;
|
||||
chan[c.chan].transWave.slicePos(transWaveInd.slice);
|
||||
chan[c.chan].transWave.slice=ins->amiga.transWave.slice;
|
||||
chan[c.chan].transWave.slicePos(double(ins->amiga.transWave.slice)/4095.0);
|
||||
loopStart=transWaveInd.sliceStart;
|
||||
loopEnd=transWaveInd.sliceEnd;
|
||||
}
|
||||
|
|
@ -1173,6 +1170,7 @@ int DivPlatformES5506::dispatch(DivCommand c) {
|
|||
case DIV_CMD_WAVE:
|
||||
if (!chan[c.chan].useWave) {
|
||||
if (chan[c.chan].active) {
|
||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
|
||||
if (((ins->amiga.useNoteMap && !ins->amiga.transWave.enable) && (c.value>=0 && c.value<120)) ||
|
||||
((!ins->amiga.useNoteMap && ins->amiga.transWave.enable) && (c.value>=0 && c.value<ins->amiga.transWaveMap.size())) ||
|
||||
((!ins->amiga.useNoteMap && !ins->amiga.transWave.enable) && (c.value>=0 && c.value<parent->song.sampleLen))) {
|
||||
|
|
@ -1246,7 +1244,7 @@ int DivPlatformES5506::dispatch(DivCommand c) {
|
|||
if (chan[c.chan].active) {
|
||||
if (chan[c.chan].pcm.pause!=(bool)(c.value&1)) {
|
||||
chan[c.chan].pcm.pause=c.value&1;
|
||||
pageWriteMask(0x00|i,0x5f,0x00,chan[c.chan].pcm.pause?0x0002:0x0000,0x0002);
|
||||
pageWriteMask(0x00|c.chan,0x5f,0x00,chan[c.chan].pcm.pause?0x0002:0x0000,0x0002);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
@ -1325,7 +1323,7 @@ void DivPlatformES5506::forceIns() {
|
|||
chan[i].volChanged.changed=0xff;
|
||||
chan[i].filterChanged.changed=0xff;
|
||||
chan[i].envChanged.changed=0xff;
|
||||
chan[i].sample=-1;
|
||||
chan[i].pcmChanged.changed=0xff;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ class DivPlatformES5506: public DivDispatch, public es550x_intf {
|
|||
} pcm;
|
||||
int freq, baseFreq, nextFreq, pitch, pitch2, note, nextNote, prevNote, ins, wave;
|
||||
unsigned int volMacroMax, panMacroMax;
|
||||
bool active, insChanged, freqChanged, pcmChanged, keyOn, keyOff, inPorta, useWave, isReverseLoop, isTranswave, transwaveIRQ;
|
||||
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, useWave, isReverseLoop, isTranswave, transwaveIRQ;
|
||||
unsigned int cr;
|
||||
|
||||
struct NoteChanged { // Note changed flags
|
||||
|
|
@ -181,7 +181,6 @@ class DivPlatformES5506: public DivDispatch, public es550x_intf {
|
|||
active(false),
|
||||
insChanged(true),
|
||||
freqChanged(false),
|
||||
pcmChanged(false),
|
||||
keyOn(false),
|
||||
keyOff(false),
|
||||
inPorta(false),
|
||||
|
|
|
|||
|
|
@ -185,7 +185,7 @@ void DivPlatformFDS::tick(bool sysTick) {
|
|||
if (chan[i].std.pitch.had) {
|
||||
if (chan[i].std.pitch.mode) {
|
||||
chan[i].pitch2+=chan[i].std.pitch.val;
|
||||
CLAMP_VAR(chan[i].pitch2,-2048,2048);
|
||||
CLAMP_VAR(chan[i].pitch2,-32768,32767);
|
||||
} else {
|
||||
chan[i].pitch2=chan[i].std.pitch.val;
|
||||
}
|
||||
|
|
@ -498,6 +498,10 @@ void DivPlatformFDS::notifyInsDeletion(void* ins) {
|
|||
}
|
||||
}
|
||||
|
||||
float DivPlatformFDS::getPostAmp() {
|
||||
return useNP?2.0f:1.0f;
|
||||
}
|
||||
|
||||
void DivPlatformFDS::poke(unsigned int addr, unsigned short val) {
|
||||
rWrite(addr,val);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -99,6 +99,7 @@ class DivPlatformFDS: public DivDispatch {
|
|||
void setNSFPlay(bool use);
|
||||
void setFlags(unsigned int flags);
|
||||
void notifyInsDeletion(void* ins);
|
||||
float getPostAmp();
|
||||
void poke(unsigned int addr, unsigned short val);
|
||||
void poke(std::vector<DivRegWrite>& wlist);
|
||||
const char** getRegisterSheet();
|
||||
|
|
|
|||
|
|
@ -32,4 +32,65 @@
|
|||
#define ADDR_FB_ALG 0xb0
|
||||
#define ADDR_LRAF 0xb4
|
||||
|
||||
#define PLEASE_HELP_ME(_targetChan) \
|
||||
int boundaryBottom=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,0,false); \
|
||||
int boundaryTop=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,12,false); \
|
||||
int destFreq=NOTE_FNUM_BLOCK(c.value2,11); \
|
||||
int newFreq; \
|
||||
bool return2=false; \
|
||||
if (_targetChan.portaPause) { \
|
||||
if (parent->song.oldOctaveBoundary) { \
|
||||
if ((_targetChan.portaPauseFreq&0xf800)>(_targetChan.baseFreq&0xf800)) { \
|
||||
_targetChan.baseFreq=((_targetChan.baseFreq&0x7ff)>>1)|(_targetChan.portaPauseFreq&0xf800); \
|
||||
} else { \
|
||||
_targetChan.baseFreq=((_targetChan.baseFreq&0x7ff)<<1)|(_targetChan.portaPauseFreq&0xf800); \
|
||||
} \
|
||||
c.value*=2; \
|
||||
} else { \
|
||||
_targetChan.baseFreq=_targetChan.portaPauseFreq; \
|
||||
} \
|
||||
} \
|
||||
if (destFreq>_targetChan.baseFreq) { \
|
||||
newFreq=_targetChan.baseFreq+c.value; \
|
||||
if (newFreq>=destFreq) { \
|
||||
newFreq=destFreq; \
|
||||
return2=true; \
|
||||
} \
|
||||
} else { \
|
||||
newFreq=_targetChan.baseFreq-c.value; \
|
||||
if (newFreq<=destFreq) { \
|
||||
newFreq=destFreq; \
|
||||
return2=true; \
|
||||
} \
|
||||
} \
|
||||
/* check for octave boundary */ \
|
||||
/* what the heck! */ \
|
||||
if (!_targetChan.portaPause) { \
|
||||
if ((newFreq&0x7ff)>boundaryTop && (newFreq&0xf800)<0x3800) { \
|
||||
if (parent->song.fbPortaPause) { \
|
||||
_targetChan.portaPauseFreq=(boundaryBottom)|((newFreq+0x800)&0xf800); \
|
||||
_targetChan.portaPause=true; \
|
||||
break; \
|
||||
} else { \
|
||||
newFreq=((newFreq&0x7ff)>>1)|((newFreq+0x800)&0xf800); \
|
||||
} \
|
||||
} \
|
||||
if ((newFreq&0x7ff)<boundaryBottom && (newFreq&0xf800)>0) { \
|
||||
if (parent->song.fbPortaPause) { \
|
||||
_targetChan.portaPauseFreq=newFreq=(boundaryTop-1)|((newFreq-0x800)&0xf800); \
|
||||
_targetChan.portaPause=true; \
|
||||
break; \
|
||||
} else { \
|
||||
newFreq=((newFreq&0x7ff)<<1)|((newFreq-0x800)&0xf800); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
_targetChan.portaPause=false; \
|
||||
_targetChan.freqChanged=true; \
|
||||
_targetChan.baseFreq=newFreq; \
|
||||
if (return2) { \
|
||||
_targetChan.inPorta=false; \
|
||||
return 2; \
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -204,7 +204,7 @@ void DivPlatformGB::tick(bool sysTick) {
|
|||
if (chan[i].std.pitch.had) {
|
||||
if (chan[i].std.pitch.mode) {
|
||||
chan[i].pitch2+=chan[i].std.pitch.val;
|
||||
CLAMP_VAR(chan[i].pitch2,-2048,2048);
|
||||
CLAMP_VAR(chan[i].pitch2,-32768,32767);
|
||||
} else {
|
||||
chan[i].pitch2=chan[i].std.pitch.val;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -339,7 +339,7 @@ void DivPlatformGenesis::tick(bool sysTick) {
|
|||
if (chan[i].std.pitch.had) {
|
||||
if (chan[i].std.pitch.mode) {
|
||||
chan[i].pitch2+=chan[i].std.pitch.val;
|
||||
CLAMP_VAR(chan[i].pitch2,-2048,2048);
|
||||
CLAMP_VAR(chan[i].pitch2,-32768,32767);
|
||||
} else {
|
||||
chan[i].pitch2=chan[i].std.pitch.val;
|
||||
}
|
||||
|
|
@ -347,7 +347,7 @@ void DivPlatformGenesis::tick(bool sysTick) {
|
|||
}
|
||||
|
||||
if (chan[i].std.phaseReset.had) {
|
||||
if (chan[i].std.phaseReset.val==1) {
|
||||
if (chan[i].std.phaseReset.val==1 && chan[i].active) {
|
||||
chan[i].keyOn=true;
|
||||
}
|
||||
}
|
||||
|
|
@ -758,48 +758,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
int boundaryBottom=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,0,false);
|
||||
int boundaryTop=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,12,false);
|
||||
int destFreq=NOTE_FNUM_BLOCK(c.value2,11);
|
||||
int newFreq;
|
||||
bool return2=false;
|
||||
if (chan[c.chan].portaPause) {
|
||||
chan[c.chan].baseFreq=chan[c.chan].portaPauseFreq;
|
||||
}
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
newFreq=chan[c.chan].baseFreq+c.value;
|
||||
if (newFreq>=destFreq) {
|
||||
newFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
} else {
|
||||
newFreq=chan[c.chan].baseFreq-c.value;
|
||||
if (newFreq<=destFreq) {
|
||||
newFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
}
|
||||
// check for octave boundary
|
||||
// what the heck!
|
||||
if (!chan[c.chan].portaPause) {
|
||||
if ((newFreq&0x7ff)>boundaryTop && (newFreq&0xf800)<0x3800) {
|
||||
chan[c.chan].portaPauseFreq=(boundaryBottom)|((newFreq+0x800)&0xf800);
|
||||
chan[c.chan].portaPause=true;
|
||||
break;
|
||||
}
|
||||
if ((newFreq&0x7ff)<boundaryBottom && (newFreq&0xf800)>0) {
|
||||
chan[c.chan].portaPauseFreq=newFreq=(boundaryTop-1)|((newFreq-0x800)&0xf800);
|
||||
chan[c.chan].portaPause=true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
chan[c.chan].portaPause=false;
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].baseFreq=newFreq;
|
||||
if (return2) {
|
||||
chan[c.chan].inPorta=false;
|
||||
return 2;
|
||||
}
|
||||
PLEASE_HELP_ME(chan[c.chan]);
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_SAMPLE_MODE: {
|
||||
|
|
|
|||
|
|
@ -150,52 +150,7 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
int boundaryBottom=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,0,false);
|
||||
int boundaryTop=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,12,false);
|
||||
int destFreq=NOTE_FNUM_BLOCK(c.value2,11);
|
||||
int newFreq;
|
||||
bool return2=false;
|
||||
if (opChan[ch].portaPause) {
|
||||
opChan[ch].baseFreq=opChan[ch].portaPauseFreq;
|
||||
}
|
||||
if (destFreq>opChan[ch].baseFreq) {
|
||||
newFreq=opChan[ch].baseFreq+c.value;
|
||||
if (newFreq>=destFreq) {
|
||||
newFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
} else {
|
||||
newFreq=opChan[ch].baseFreq-c.value;
|
||||
if (newFreq<=destFreq) {
|
||||
newFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
}
|
||||
// what the heck!
|
||||
if (!opChan[ch].portaPause) {
|
||||
if ((newFreq&0x7ff)>boundaryTop && (newFreq&0xf800)<0x3800) {
|
||||
if (parent->song.fbPortaPause) {
|
||||
opChan[ch].portaPauseFreq=(boundaryBottom)|((newFreq+0x800)&0xf800);
|
||||
opChan[ch].portaPause=true;
|
||||
break;
|
||||
} else {
|
||||
newFreq=(newFreq>>1)|((newFreq+0x800)&0xf800);
|
||||
}
|
||||
}
|
||||
if ((newFreq&0x7ff)<boundaryBottom && (newFreq&0xf800)>0) {
|
||||
if (parent->song.fbPortaPause) {
|
||||
opChan[ch].portaPauseFreq=newFreq=(boundaryTop-1)|((newFreq-0x800)&0xf800);
|
||||
opChan[ch].portaPause=true;
|
||||
break;
|
||||
} else {
|
||||
newFreq=(newFreq<<1)|((newFreq-0x800)&0xf800);
|
||||
}
|
||||
}
|
||||
}
|
||||
opChan[ch].portaPause=false;
|
||||
opChan[ch].freqChanged=true;
|
||||
opChan[ch].baseFreq=newFreq;
|
||||
if (return2) return 2;
|
||||
PLEASE_HELP_ME(opChan[ch]);
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_SAMPLE_MODE: {
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ class DivPlatformGenesisExt: public DivPlatformGenesis {
|
|||
unsigned char freqH, freqL;
|
||||
int freq, baseFreq, pitch, pitch2, portaPauseFreq, ins;
|
||||
signed char konCycles;
|
||||
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause;
|
||||
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta;
|
||||
int vol;
|
||||
unsigned char pan;
|
||||
OpChannel():
|
||||
|
|
@ -45,6 +45,7 @@ class DivPlatformGenesisExt: public DivPlatformGenesis {
|
|||
keyOn(false),
|
||||
keyOff(false),
|
||||
portaPause(false),
|
||||
inPorta(false),
|
||||
vol(0),
|
||||
pan(3) {}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -142,23 +142,52 @@ const char* DivPlatformLynx::getEffectName(unsigned char effect) {
|
|||
}
|
||||
|
||||
void DivPlatformLynx::acquire(short* bufL, short* bufR, size_t start, size_t len) {
|
||||
mikey->sampleAudio( bufL + start, bufR + start, len, oscBuf );
|
||||
for (size_t h=start; h<start+len; h++) {
|
||||
for (int i=0; i<4; i++) {
|
||||
if (chan[i].pcm && chan[i].sample>=0 && chan[i].sample<parent->song.sampleLen) {
|
||||
chan[i].sampleAccum-=chan[i].sampleFreq;
|
||||
if (chan[i].sampleAccum<0) {
|
||||
chan[i].sampleAccum+=rate;
|
||||
DivSample* s=parent->getSample(chan[i].sample);
|
||||
if (s!=NULL) {
|
||||
if (isMuted[i]) {
|
||||
WRITE_VOLUME(i,0);
|
||||
chan[i].samplePos++;
|
||||
} else {
|
||||
WRITE_VOLUME(i,(s->data8[chan[i].samplePos++]*chan[i].outVol)>>7);
|
||||
}
|
||||
if (chan[i].samplePos>=(int)s->samples) {
|
||||
chan[i].sample=-1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mikey->sampleAudio( bufL + h, bufR + h, 1, oscBuf );
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformLynx::tick(bool sysTick) {
|
||||
for (int i=0; i<4; i++) {
|
||||
chan[i].std.next();
|
||||
if (chan[i].std.vol.had) {
|
||||
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)));
|
||||
if (chan[i].pcm) {
|
||||
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)));
|
||||
}
|
||||
}
|
||||
if (chan[i].std.arp.had) {
|
||||
if (!chan[i].inPorta) {
|
||||
if (chan[i].std.arp.mode) {
|
||||
chan[i].baseFreq=NOTE_PERIODIC(chan[i].std.arp.val);
|
||||
if (chan[i].pcm) chan[i].sampleBaseFreq=parent->calcBaseFreq(1.0,1.0,chan[i].std.arp.val,false);
|
||||
chan[i].actualNote=chan[i].std.arp.val;
|
||||
} else {
|
||||
chan[i].baseFreq=NOTE_PERIODIC(chan[i].note+chan[i].std.arp.val);
|
||||
if (chan[i].pcm) chan[i].sampleBaseFreq=parent->calcBaseFreq(1.0,1.0,chan[i].note+chan[i].std.arp.val,false);
|
||||
chan[i].actualNote=chan[i].note+chan[i].std.arp.val;
|
||||
}
|
||||
chan[i].freqChanged=true;
|
||||
|
|
@ -166,6 +195,7 @@ void DivPlatformLynx::tick(bool sysTick) {
|
|||
} else {
|
||||
if (chan[i].std.arp.mode && chan[i].std.arp.finished) {
|
||||
chan[i].baseFreq=NOTE_PERIODIC(chan[i].note);
|
||||
if (chan[i].pcm) chan[i].sampleBaseFreq=parent->calcBaseFreq(1.0,1.0,chan[i].note,false);
|
||||
chan[i].actualNote=chan[i].note;
|
||||
chan[i].freqChanged=true;
|
||||
}
|
||||
|
|
@ -188,40 +218,75 @@ void DivPlatformLynx::tick(bool sysTick) {
|
|||
if (chan[i].std.pitch.had) {
|
||||
if (chan[i].std.pitch.mode) {
|
||||
chan[i].pitch2+=chan[i].std.pitch.val;
|
||||
CLAMP_VAR(chan[i].pitch2,-2048,2048);
|
||||
CLAMP_VAR(chan[i].pitch2,-32768,32767);
|
||||
} else {
|
||||
chan[i].pitch2=chan[i].std.pitch.val;
|
||||
}
|
||||
chan[i].freqChanged=true;
|
||||
}
|
||||
|
||||
if (chan[i].std.phaseReset.had) {
|
||||
if (chan[i].std.phaseReset.val==1) {
|
||||
WRITE_LFSR(i, 0);
|
||||
WRITE_OTHER(i, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (chan[i].freqChanged) {
|
||||
if (chan[i].lfsr >= 0) {
|
||||
WRITE_LFSR(i, (chan[i].lfsr&0xff));
|
||||
WRITE_OTHER(i, ((chan[i].lfsr&0xf00)>>4));
|
||||
chan[i].lfsr=-1;
|
||||
if (chan[i].pcm) {
|
||||
double off=1.0;
|
||||
if (chan[i].sample>=0 && chan[i].sample<parent->song.sampleLen) {
|
||||
DivSample* s=parent->getSample(chan[i].sample);
|
||||
if (s->centerRate<1) {
|
||||
off=1.0;
|
||||
} else {
|
||||
off=(double)s->centerRate/8363.0;
|
||||
}
|
||||
}
|
||||
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));
|
||||
WRITE_OTHER(i, ((chan[i].lfsr&0xf00)>>4));
|
||||
chan[i].lfsr=-1;
|
||||
}
|
||||
chan[i].fd=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER);
|
||||
if (chan[i].std.duty.had) {
|
||||
chan[i].duty=chan[i].std.duty.val;
|
||||
WRITE_FEEDBACK(i, chan[i].duty.feedback);
|
||||
}
|
||||
WRITE_CONTROL(i, (chan[i].fd.clockDivider|0x18|chan[i].duty.int_feedback7));
|
||||
WRITE_BACKUP( i, chan[i].fd.backup );
|
||||
}
|
||||
chan[i].fd=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER);
|
||||
if (chan[i].std.duty.had) {
|
||||
chan[i].duty=chan[i].std.duty.val;
|
||||
WRITE_FEEDBACK(i, chan[i].duty.feedback);
|
||||
}
|
||||
WRITE_CONTROL(i, (chan[i].fd.clockDivider|0x18|chan[i].duty.int_feedback7));
|
||||
WRITE_BACKUP( i, chan[i].fd.backup );
|
||||
chan[i].freqChanged=false;
|
||||
} else if (chan[i].std.duty.had) {
|
||||
chan[i].duty = chan[i].std.duty.val;
|
||||
WRITE_FEEDBACK(i, chan[i].duty.feedback);
|
||||
WRITE_CONTROL(i, (chan[i].fd.clockDivider|0x18|chan[i].duty.int_feedback7));
|
||||
if (!chan[i].pcm) {
|
||||
WRITE_FEEDBACK(i, chan[i].duty.feedback);
|
||||
WRITE_CONTROL(i, (chan[i].fd.clockDivider|0x18|chan[i].duty.int_feedback7));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int DivPlatformLynx::dispatch(DivCommand c) {
|
||||
switch (c.cmd) {
|
||||
case DIV_CMD_NOTE_ON:
|
||||
case DIV_CMD_NOTE_ON: {
|
||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_MIKEY);
|
||||
chan[c.chan].pcm=(ins->type==DIV_INS_AMIGA);
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
|
||||
if (chan[c.chan].pcm) {
|
||||
chan[c.chan].sampleBaseFreq=parent->calcBaseFreq(1.0,1.0,c.value,false);
|
||||
chan[c.chan].sample=ins->amiga.getSample(c.value);
|
||||
chan[c.chan].sampleAccum=0;
|
||||
chan[c.chan].samplePos=0;
|
||||
}
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
chan[c.chan].actualNote=c.value;
|
||||
|
|
@ -232,10 +297,14 @@ int DivPlatformLynx::dispatch(DivCommand c) {
|
|||
WRITE_VOLUME(c.chan,(isMuted[c.chan]?0:(chan[c.chan].vol&127)));
|
||||
chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_MIKEY));
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_NOTE_OFF:
|
||||
chan[c.chan].active=false;
|
||||
WRITE_VOLUME(c.chan, 0);
|
||||
chan[c.chan].macroInit(NULL);
|
||||
if (chan[c.chan].pcm) {
|
||||
chan[c.chan].pcm=false;
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_LYNX_LFSR_LOAD:
|
||||
chan[c.chan].freqChanged=true;
|
||||
|
|
@ -255,7 +324,7 @@ int DivPlatformLynx::dispatch(DivCommand c) {
|
|||
if (!chan[c.chan].std.vol.has) {
|
||||
chan[c.chan].outVol=c.value;
|
||||
}
|
||||
if (chan[c.chan].active) WRITE_VOLUME(c.chan,(isMuted[c.chan]?0:(chan[c.chan].vol&127)));
|
||||
if (chan[c.chan].active && !chan[c.chan].pcm) WRITE_VOLUME(c.chan,(isMuted[c.chan]?0:(chan[c.chan].vol&127)));
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_PANNING:
|
||||
|
|
@ -289,18 +358,26 @@ int DivPlatformLynx::dispatch(DivCommand c) {
|
|||
}
|
||||
}
|
||||
chan[c.chan].freqChanged=true;
|
||||
if (chan[c.chan].pcm && parent->song.linearPitch==2) {
|
||||
chan[c.chan].sampleBaseFreq=chan[c.chan].baseFreq;
|
||||
}
|
||||
if (return2) {
|
||||
chan[c.chan].inPorta=false;
|
||||
return 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_LEGATO:
|
||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+((chan[c.chan].std.arp.will && !chan[c.chan].std.arp.mode)?(chan[c.chan].std.arp.val):(0)));
|
||||
case DIV_CMD_LEGATO: {
|
||||
int whatAMess=c.value+((chan[c.chan].std.arp.will && !chan[c.chan].std.arp.mode)?(chan[c.chan].std.arp.val):(0));
|
||||
chan[c.chan].baseFreq=NOTE_PERIODIC(whatAMess);
|
||||
if (chan[c.chan].pcm) {
|
||||
chan[c.chan].sampleBaseFreq=parent->calcBaseFreq(1.0,1.0,whatAMess,false);
|
||||
}
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
chan[c.chan].actualNote=c.value;
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_PRE_PORTA:
|
||||
if (chan[c.chan].active && c.value2) {
|
||||
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_MIKEY));
|
||||
|
|
|
|||
|
|
@ -44,9 +44,9 @@ class DivPlatformLynx: public DivDispatch {
|
|||
DivMacroInt std;
|
||||
MikeyFreqDiv fd;
|
||||
MikeyDuty duty;
|
||||
int baseFreq, pitch, pitch2, note, actualNote, lfsr, ins;
|
||||
int baseFreq, pitch, pitch2, note, actualNote, lfsr, ins, sample, samplePos, sampleAccum, sampleBaseFreq, sampleFreq;
|
||||
unsigned char pan;
|
||||
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta;
|
||||
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, pcm;
|
||||
signed char vol, outVol;
|
||||
void macroInit(DivInstrument* which) {
|
||||
std.init(which);
|
||||
|
|
@ -63,6 +63,11 @@ class DivPlatformLynx: public DivDispatch {
|
|||
actualNote(0),
|
||||
lfsr(-1),
|
||||
ins(-1),
|
||||
sample(-1),
|
||||
samplePos(0),
|
||||
sampleAccum(0),
|
||||
sampleBaseFreq(0),
|
||||
sampleFreq(0),
|
||||
pan(0xff),
|
||||
active(false),
|
||||
insChanged(true),
|
||||
|
|
@ -70,6 +75,7 @@ class DivPlatformLynx: public DivDispatch {
|
|||
keyOn(false),
|
||||
keyOff(false),
|
||||
inPorta(false),
|
||||
pcm(false),
|
||||
vol(127),
|
||||
outVol(127) {}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@ void DivPlatformMMC5::tick(bool sysTick) {
|
|||
if (chan[i].std.pitch.had) {
|
||||
if (chan[i].std.pitch.mode) {
|
||||
chan[i].pitch2+=chan[i].std.pitch.val;
|
||||
CLAMP_VAR(chan[i].pitch2,-2048,2048);
|
||||
CLAMP_VAR(chan[i].pitch2,-32768,32767);
|
||||
} else {
|
||||
chan[i].pitch2=chan[i].std.pitch.val;
|
||||
}
|
||||
|
|
|
|||
349
src/engine/platform/msm6258.cpp
Normal file
349
src/engine/platform/msm6258.cpp
Normal file
|
|
@ -0,0 +1,349 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "msm6258.h"
|
||||
#include "../engine.h"
|
||||
#include "../../ta-log.h"
|
||||
#include "sound/oki/okim6258.h"
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#define rWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} }
|
||||
|
||||
const char** DivPlatformMSM6258::getRegisterSheet() {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char* DivPlatformMSM6258::getEffectName(unsigned char effect) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void DivPlatformMSM6258::acquire(short* bufL, short* bufR, size_t start, size_t len) {
|
||||
for (size_t h=start; h<start+len; h++) {
|
||||
short* outs[2]={
|
||||
&bufL[h],
|
||||
NULL
|
||||
};
|
||||
if (!writes.empty()) {
|
||||
QueuedWrite& w=writes.front();
|
||||
switch (w.addr) {
|
||||
case 0:
|
||||
msm->ctrl_w(w.val);
|
||||
break;
|
||||
}
|
||||
writes.pop();
|
||||
}
|
||||
|
||||
if (sample>=0 && sample<parent->song.sampleLen) {
|
||||
DivSample* s=parent->getSample(sample);
|
||||
unsigned char nextData=(s->dataVOX[samplePos]>>4)|(s->dataVOX[samplePos]<<4);
|
||||
if (msm->data_w(nextData)) {
|
||||
samplePos++;
|
||||
if (samplePos>=(int)s->lengthVOX) {
|
||||
sample=-1;
|
||||
samplePos=0;
|
||||
msm->ctrl_w(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
msm->sound_stream_update(outs,1);
|
||||
if (isMuted[0]) {
|
||||
bufL[h]=0;
|
||||
}
|
||||
|
||||
/*if (++updateOsc>=22) {
|
||||
updateOsc=0;
|
||||
// TODO: per-channel osc
|
||||
for (int i=0; i<1; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=msm->m_voice[i].m_muted?0:(msm->m_voice[i].m_out<<6);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformMSM6258::tick(bool sysTick) {
|
||||
// nothing
|
||||
}
|
||||
|
||||
int DivPlatformMSM6258::dispatch(DivCommand c) {
|
||||
switch (c.cmd) {
|
||||
case DIV_CMD_NOTE_ON: {
|
||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM);
|
||||
if (ins->type==DIV_INS_AMIGA) {
|
||||
chan[c.chan].furnacePCM=true;
|
||||
} else {
|
||||
chan[c.chan].furnacePCM=false;
|
||||
}
|
||||
if (skipRegisterWrites) break;
|
||||
if (chan[c.chan].furnacePCM) {
|
||||
chan[c.chan].macroInit(ins);
|
||||
if (!chan[c.chan].std.vol.will) {
|
||||
chan[c.chan].outVol=chan[c.chan].vol;
|
||||
}
|
||||
sample=ins->amiga.getSample(c.value);
|
||||
if (sample>=0 && sample<parent->song.sampleLen) {
|
||||
//DivSample* s=parent->getSample(chan[c.chan].sample);
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].note=c.value;
|
||||
chan[c.chan].freqChanged=true;
|
||||
}
|
||||
chan[c.chan].active=true;
|
||||
chan[c.chan].keyOn=true;
|
||||
rWrite(0,1);
|
||||
rWrite(0,2);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
chan[c.chan].sample=-1;
|
||||
chan[c.chan].macroInit(NULL);
|
||||
chan[c.chan].outVol=chan[c.chan].vol;
|
||||
if ((12*sampleBank+c.value%12)>=parent->song.sampleLen) {
|
||||
break;
|
||||
}
|
||||
//DivSample* s=parent->getSample(12*sampleBank+c.value%12);
|
||||
sample=12*sampleBank+c.value%12;
|
||||
samplePos=0;
|
||||
msm->ctrl_w(1);
|
||||
msm->ctrl_w(2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_NOTE_OFF:
|
||||
chan[c.chan].keyOff=true;
|
||||
chan[c.chan].keyOn=false;
|
||||
chan[c.chan].active=false;
|
||||
rWrite(0,1); // turn off
|
||||
sample=-1;
|
||||
samplePos=0;
|
||||
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(0,1); // turn off
|
||||
sample=-1;
|
||||
samplePos=0;
|
||||
chan[c.chan].std.release();
|
||||
break;
|
||||
case DIV_CMD_ENV_RELEASE:
|
||||
chan[c.chan].std.release();
|
||||
break;
|
||||
case DIV_CMD_VOLUME: {
|
||||
chan[c.chan].vol=c.value;
|
||||
if (!chan[c.chan].std.vol.has) {
|
||||
chan[c.chan].outVol=c.value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_GET_VOLUME: {
|
||||
return chan[c.chan].vol;
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_INSTRUMENT:
|
||||
if (chan[c.chan].ins!=c.value || c.value2==1) {
|
||||
chan[c.chan].insChanged=true;
|
||||
}
|
||||
chan[c.chan].ins=c.value;
|
||||
break;
|
||||
case DIV_CMD_PITCH: {
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
return 2;
|
||||
}
|
||||
case DIV_CMD_SAMPLE_BANK:
|
||||
sampleBank=c.value;
|
||||
if (sampleBank>(parent->song.sample.size()/12)) {
|
||||
sampleBank=parent->song.sample.size()/12;
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_LEGATO: {
|
||||
break;
|
||||
}
|
||||
case DIV_ALWAYS_SET_VOLUME:
|
||||
return 0;
|
||||
break;
|
||||
case DIV_CMD_GET_VOLMAX:
|
||||
return 8;
|
||||
break;
|
||||
case DIV_CMD_PRE_PORTA:
|
||||
break;
|
||||
case DIV_CMD_PRE_NOTE:
|
||||
break;
|
||||
default:
|
||||
//printf("WARNING: unimplemented command %d\n",c.cmd);
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void DivPlatformMSM6258::muteChannel(int ch, bool mute) {
|
||||
isMuted[ch]=mute;
|
||||
}
|
||||
|
||||
void DivPlatformMSM6258::forceIns() {
|
||||
while (!writes.empty()) writes.pop();
|
||||
for (int i=0; i<1; i++) {
|
||||
chan[i].insChanged=true;
|
||||
}
|
||||
}
|
||||
|
||||
void* DivPlatformMSM6258::getChanState(int ch) {
|
||||
return &chan[ch];
|
||||
}
|
||||
|
||||
DivDispatchOscBuffer* DivPlatformMSM6258::getOscBuffer(int ch) {
|
||||
return oscBuf[ch];
|
||||
}
|
||||
|
||||
unsigned char* DivPlatformMSM6258::getRegisterPool() {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int DivPlatformMSM6258::getRegisterPoolSize() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DivPlatformMSM6258::poke(unsigned int addr, unsigned short val) {
|
||||
//immWrite(addr,val);
|
||||
}
|
||||
|
||||
void DivPlatformMSM6258::poke(std::vector<DivRegWrite>& wlist) {
|
||||
//for (DivRegWrite& i: wlist) immWrite(i.addr,i.val);
|
||||
}
|
||||
|
||||
void DivPlatformMSM6258::reset() {
|
||||
while (!writes.empty()) writes.pop();
|
||||
msm->device_reset();
|
||||
if (dumpWrites) {
|
||||
addWrite(0xffffffff,0);
|
||||
}
|
||||
for (int i=0; i<1; i++) {
|
||||
chan[i]=DivPlatformMSM6258::Channel();
|
||||
chan[i].std.setEngine(parent);
|
||||
}
|
||||
for (int i=0; i<1; i++) {
|
||||
chan[i].vol=8;
|
||||
chan[i].outVol=8;
|
||||
}
|
||||
|
||||
sampleBank=0;
|
||||
sample=-1;
|
||||
samplePos=0;
|
||||
|
||||
delay=0;
|
||||
}
|
||||
|
||||
bool DivPlatformMSM6258::keyOffAffectsArp(int ch) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void DivPlatformMSM6258::notifyInsChange(int ins) {
|
||||
for (int i=0; i<1; i++) {
|
||||
if (chan[i].ins==ins) {
|
||||
chan[i].insChanged=true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformMSM6258::notifyInsDeletion(void* ins) {
|
||||
}
|
||||
|
||||
const void* DivPlatformMSM6258::getSampleMem(int index) {
|
||||
return index == 0 ? adpcmMem : NULL;
|
||||
}
|
||||
|
||||
size_t DivPlatformMSM6258::getSampleMemCapacity(int index) {
|
||||
return index == 0 ? 262144 : 0;
|
||||
}
|
||||
|
||||
size_t DivPlatformMSM6258::getSampleMemUsage(int index) {
|
||||
return index == 0 ? adpcmMemLen : 0;
|
||||
}
|
||||
|
||||
void DivPlatformMSM6258::renderSamples() {
|
||||
memset(adpcmMem,0,getSampleMemCapacity(0));
|
||||
|
||||
// sample data
|
||||
size_t memPos=0;
|
||||
int sampleCount=parent->song.sampleLen;
|
||||
if (sampleCount>128) sampleCount=128;
|
||||
for (int i=0; i<sampleCount; i++) {
|
||||
DivSample* s=parent->song.sample[i];
|
||||
int paddedLen=s->lengthVOX;
|
||||
if (memPos>=getSampleMemCapacity(0)) {
|
||||
logW("out of ADPCM memory for sample %d!",i);
|
||||
break;
|
||||
}
|
||||
if (memPos+paddedLen>=getSampleMemCapacity(0)) {
|
||||
memcpy(adpcmMem+memPos,s->dataVOX,getSampleMemCapacity(0)-memPos);
|
||||
logW("out of ADPCM memory for sample %d!",i);
|
||||
} else {
|
||||
memcpy(adpcmMem+memPos,s->dataVOX,paddedLen);
|
||||
}
|
||||
s->offVOX=memPos;
|
||||
memPos+=paddedLen;
|
||||
}
|
||||
adpcmMemLen=memPos+256;
|
||||
}
|
||||
|
||||
void DivPlatformMSM6258::setFlags(unsigned int flags) {
|
||||
if (flags&1) {
|
||||
chipClock=4096000;
|
||||
} else {
|
||||
chipClock=4000000;
|
||||
}
|
||||
rate=chipClock/256;
|
||||
for (int i=0; i<1; i++) {
|
||||
isMuted[i]=false;
|
||||
oscBuf[i]->rate=rate/256;
|
||||
}
|
||||
}
|
||||
|
||||
int DivPlatformMSM6258::init(DivEngine* p, int channels, int sugRate, unsigned int flags) {
|
||||
parent=p;
|
||||
adpcmMem=new unsigned char[getSampleMemCapacity(0)];
|
||||
adpcmMemLen=0;
|
||||
dumpWrites=false;
|
||||
skipRegisterWrites=false;
|
||||
updateOsc=0;
|
||||
for (int i=0; i<1; i++) {
|
||||
isMuted[i]=false;
|
||||
oscBuf[i]=new DivDispatchOscBuffer;
|
||||
}
|
||||
msm=new okim6258_device(4000000);
|
||||
msm->device_start();
|
||||
setFlags(flags);
|
||||
reset();
|
||||
return 4;
|
||||
}
|
||||
|
||||
void DivPlatformMSM6258::quit() {
|
||||
for (int i=0; i<1; i++) {
|
||||
delete oscBuf[i];
|
||||
}
|
||||
delete msm;
|
||||
delete[] adpcmMem;
|
||||
}
|
||||
|
||||
DivPlatformMSM6258::~DivPlatformMSM6258() {
|
||||
}
|
||||
129
src/engine/platform/msm6258.h
Normal file
129
src/engine/platform/msm6258.h
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef _MSM6258_H
|
||||
#define _MSM6258_H
|
||||
#include "../dispatch.h"
|
||||
#include "../macroInt.h"
|
||||
#include <queue>
|
||||
#include "sound/oki/okim6258.h"
|
||||
|
||||
class DivPlatformMSM6258: public DivDispatch {
|
||||
protected:
|
||||
const unsigned short chanOffs[6]={
|
||||
0x00, 0x01, 0x02, 0x100, 0x101, 0x102
|
||||
};
|
||||
|
||||
struct Channel {
|
||||
unsigned char freqH, freqL;
|
||||
int freq, baseFreq, pitch, pitch2, portaPauseFreq, note, ins;
|
||||
unsigned char psgMode, autoEnvNum, autoEnvDen;
|
||||
signed char konCycles;
|
||||
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta, furnacePCM, hardReset;
|
||||
int vol, outVol;
|
||||
int sample;
|
||||
unsigned char pan;
|
||||
DivMacroInt std;
|
||||
void macroInit(DivInstrument* which) {
|
||||
std.init(which);
|
||||
pitch2=0;
|
||||
}
|
||||
Channel():
|
||||
freqH(0),
|
||||
freqL(0),
|
||||
freq(0),
|
||||
baseFreq(0),
|
||||
pitch(0),
|
||||
pitch2(0),
|
||||
portaPauseFreq(0),
|
||||
note(0),
|
||||
ins(-1),
|
||||
psgMode(1),
|
||||
autoEnvNum(0),
|
||||
autoEnvDen(0),
|
||||
active(false),
|
||||
insChanged(true),
|
||||
freqChanged(false),
|
||||
keyOn(false),
|
||||
keyOff(false),
|
||||
portaPause(false),
|
||||
inPorta(false),
|
||||
furnacePCM(false),
|
||||
hardReset(false),
|
||||
vol(0),
|
||||
outVol(15),
|
||||
sample(-1),
|
||||
pan(3) {}
|
||||
};
|
||||
Channel chan[1];
|
||||
DivDispatchOscBuffer* oscBuf[1];
|
||||
bool isMuted[1];
|
||||
struct QueuedWrite {
|
||||
unsigned short addr;
|
||||
unsigned char val;
|
||||
bool addrOrVal;
|
||||
QueuedWrite(unsigned short a, unsigned char v): addr(a), val(v), addrOrVal(false) {}
|
||||
};
|
||||
std::queue<QueuedWrite> writes;
|
||||
okim6258_device* msm;
|
||||
unsigned char regPool[512];
|
||||
unsigned char lastBusy;
|
||||
|
||||
unsigned char* adpcmMem;
|
||||
size_t adpcmMemLen;
|
||||
unsigned char sampleBank;
|
||||
|
||||
int delay, updateOsc, sample, samplePos;
|
||||
|
||||
bool extMode;
|
||||
|
||||
short oldWrites[512];
|
||||
short pendingWrites[512];
|
||||
|
||||
friend void putDispatchChan(void*,int,int);
|
||||
|
||||
public:
|
||||
void acquire(short* bufL, short* bufR, size_t start, size_t len);
|
||||
int dispatch(DivCommand c);
|
||||
void* getChanState(int chan);
|
||||
DivDispatchOscBuffer* getOscBuffer(int chan);
|
||||
unsigned char* getRegisterPool();
|
||||
int getRegisterPoolSize();
|
||||
void reset();
|
||||
void forceIns();
|
||||
void tick(bool sysTick=true);
|
||||
void muteChannel(int ch, bool mute);
|
||||
bool keyOffAffectsArp(int ch);
|
||||
void notifyInsChange(int ins);
|
||||
void notifyInsDeletion(void* ins);
|
||||
void poke(unsigned int addr, unsigned short val);
|
||||
void poke(std::vector<DivRegWrite>& wlist);
|
||||
void setFlags(unsigned int flags);
|
||||
const char** getRegisterSheet();
|
||||
const char* getEffectName(unsigned char effect);
|
||||
const void* getSampleMem(int index);
|
||||
size_t getSampleMemCapacity(int index);
|
||||
size_t getSampleMemUsage(int index);
|
||||
void renderSamples();
|
||||
|
||||
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
|
||||
void quit();
|
||||
~DivPlatformMSM6258();
|
||||
};
|
||||
#endif
|
||||
345
src/engine/platform/msm6295.cpp
Normal file
345
src/engine/platform/msm6295.cpp
Normal file
|
|
@ -0,0 +1,345 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "msm6295.h"
|
||||
#include "../engine.h"
|
||||
#include "../../ta-log.h"
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#define rWrite(v) if (!skipRegisterWrites) {writes.emplace(0,v); if (dumpWrites) {addWrite(0,v);} }
|
||||
|
||||
const char** DivPlatformMSM6295::getRegisterSheet() {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char* DivPlatformMSM6295::getEffectName(unsigned char effect) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
u8 DivMSM6295Interface::read_byte(u32 address) {
|
||||
return adpcmMem[address&0xffff];
|
||||
}
|
||||
|
||||
void DivPlatformMSM6295::acquire(short* bufL, short* bufR, size_t start, size_t len) {
|
||||
for (size_t h=start; h<start+len; h++) {
|
||||
if (delay<=0) {
|
||||
if (!writes.empty()) {
|
||||
QueuedWrite& w=writes.front();
|
||||
msm->command_w(w.val);
|
||||
writes.pop();
|
||||
delay=32;
|
||||
}
|
||||
} else {
|
||||
delay--;
|
||||
}
|
||||
|
||||
msm->tick();
|
||||
|
||||
bufL[h]=msm->out()<<4;
|
||||
|
||||
if (++updateOsc>=22) {
|
||||
updateOsc=0;
|
||||
// TODO: per-channel osc
|
||||
for (int i=0; i<4; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=msm->m_voice[i].m_muted?0:(msm->m_voice[i].m_out<<6);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformMSM6295::tick(bool sysTick) {
|
||||
// nothing
|
||||
}
|
||||
|
||||
int DivPlatformMSM6295::dispatch(DivCommand c) {
|
||||
switch (c.cmd) {
|
||||
case DIV_CMD_NOTE_ON: {
|
||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM);
|
||||
if (ins->type==DIV_INS_AMIGA) {
|
||||
chan[c.chan].furnacePCM=true;
|
||||
} else {
|
||||
chan[c.chan].furnacePCM=false;
|
||||
}
|
||||
if (skipRegisterWrites) break;
|
||||
if (chan[c.chan].furnacePCM) {
|
||||
chan[c.chan].macroInit(ins);
|
||||
if (!chan[c.chan].std.vol.will) {
|
||||
chan[c.chan].outVol=chan[c.chan].vol;
|
||||
}
|
||||
chan[c.chan].sample=ins->amiga.getSample(c.value);
|
||||
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
|
||||
//DivSample* s=parent->getSample(chan[c.chan].sample);
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].note=c.value;
|
||||
chan[c.chan].freqChanged=true;
|
||||
}
|
||||
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
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
chan[c.chan].sample=-1;
|
||||
chan[c.chan].macroInit(NULL);
|
||||
chan[c.chan].outVol=chan[c.chan].vol;
|
||||
if ((12*sampleBank+c.value%12)>=parent->song.sampleLen) {
|
||||
break;
|
||||
}
|
||||
//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
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_NOTE_OFF:
|
||||
chan[c.chan].keyOff=true;
|
||||
chan[c.chan].keyOn=false;
|
||||
chan[c.chan].active=false;
|
||||
rWrite((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
|
||||
chan[c.chan].std.release();
|
||||
break;
|
||||
case DIV_CMD_ENV_RELEASE:
|
||||
chan[c.chan].std.release();
|
||||
break;
|
||||
case DIV_CMD_VOLUME: {
|
||||
chan[c.chan].vol=c.value;
|
||||
if (!chan[c.chan].std.vol.has) {
|
||||
chan[c.chan].outVol=c.value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_GET_VOLUME: {
|
||||
return chan[c.chan].vol;
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_INSTRUMENT:
|
||||
if (chan[c.chan].ins!=c.value || c.value2==1) {
|
||||
chan[c.chan].insChanged=true;
|
||||
}
|
||||
chan[c.chan].ins=c.value;
|
||||
break;
|
||||
case DIV_CMD_PITCH: {
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
return 2;
|
||||
}
|
||||
case DIV_CMD_SAMPLE_BANK:
|
||||
sampleBank=c.value;
|
||||
if (sampleBank>(parent->song.sample.size()/12)) {
|
||||
sampleBank=parent->song.sample.size()/12;
|
||||
}
|
||||
iface.sampleBank=sampleBank;
|
||||
break;
|
||||
case DIV_CMD_LEGATO: {
|
||||
break;
|
||||
}
|
||||
case DIV_ALWAYS_SET_VOLUME:
|
||||
return 0;
|
||||
break;
|
||||
case DIV_CMD_GET_VOLMAX:
|
||||
return 8;
|
||||
break;
|
||||
case DIV_CMD_PRE_PORTA:
|
||||
break;
|
||||
case DIV_CMD_PRE_NOTE:
|
||||
break;
|
||||
default:
|
||||
//printf("WARNING: unimplemented command %d\n",c.cmd);
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void DivPlatformMSM6295::muteChannel(int ch, bool mute) {
|
||||
isMuted[ch]=mute;
|
||||
msm->m_voice[ch].m_muted=mute;
|
||||
}
|
||||
|
||||
void DivPlatformMSM6295::forceIns() {
|
||||
while (!writes.empty()) writes.pop();
|
||||
for (int i=0; i<4; i++) {
|
||||
chan[i].insChanged=true;
|
||||
}
|
||||
}
|
||||
|
||||
void* DivPlatformMSM6295::getChanState(int ch) {
|
||||
return &chan[ch];
|
||||
}
|
||||
|
||||
DivDispatchOscBuffer* DivPlatformMSM6295::getOscBuffer(int ch) {
|
||||
return oscBuf[ch];
|
||||
}
|
||||
|
||||
unsigned char* DivPlatformMSM6295::getRegisterPool() {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int DivPlatformMSM6295::getRegisterPoolSize() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DivPlatformMSM6295::poke(unsigned int addr, unsigned short val) {
|
||||
//immWrite(addr,val);
|
||||
}
|
||||
|
||||
void DivPlatformMSM6295::poke(std::vector<DivRegWrite>& wlist) {
|
||||
//for (DivRegWrite& i: wlist) immWrite(i.addr,i.val);
|
||||
}
|
||||
|
||||
void DivPlatformMSM6295::reset() {
|
||||
while (!writes.empty()) writes.pop();
|
||||
msm->reset();
|
||||
if (dumpWrites) {
|
||||
addWrite(0xffffffff,0);
|
||||
}
|
||||
for (int i=0; i<4; i++) {
|
||||
chan[i]=DivPlatformMSM6295::Channel();
|
||||
chan[i].std.setEngine(parent);
|
||||
}
|
||||
for (int i=0; i<4; i++) {
|
||||
chan[i].vol=8;
|
||||
chan[i].outVol=8;
|
||||
}
|
||||
|
||||
sampleBank=0;
|
||||
|
||||
delay=0;
|
||||
}
|
||||
|
||||
bool DivPlatformMSM6295::keyOffAffectsArp(int ch) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void DivPlatformMSM6295::notifyInsChange(int ins) {
|
||||
for (int i=0; i<4; i++) {
|
||||
if (chan[i].ins==ins) {
|
||||
chan[i].insChanged=true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformMSM6295::notifyInsDeletion(void* ins) {
|
||||
}
|
||||
|
||||
const void* DivPlatformMSM6295::getSampleMem(int index) {
|
||||
return index == 0 ? adpcmMem : NULL;
|
||||
}
|
||||
|
||||
size_t DivPlatformMSM6295::getSampleMemCapacity(int index) {
|
||||
return index == 0 ? 262144 : 0;
|
||||
}
|
||||
|
||||
size_t DivPlatformMSM6295::getSampleMemUsage(int index) {
|
||||
return index == 0 ? adpcmMemLen : 0;
|
||||
}
|
||||
|
||||
void DivPlatformMSM6295::renderSamples() {
|
||||
memset(adpcmMem,0,getSampleMemCapacity(0));
|
||||
|
||||
// sample data
|
||||
size_t memPos=128*8;
|
||||
int sampleCount=parent->song.sampleLen;
|
||||
if (sampleCount>128) sampleCount=128;
|
||||
for (int i=0; i<sampleCount; i++) {
|
||||
DivSample* s=parent->song.sample[i];
|
||||
int paddedLen=s->lengthVOX;
|
||||
if (memPos>=getSampleMemCapacity(0)) {
|
||||
logW("out of ADPCM memory for sample %d!",i);
|
||||
break;
|
||||
}
|
||||
if (memPos+paddedLen>=getSampleMemCapacity(0)) {
|
||||
memcpy(adpcmMem+memPos,s->dataVOX,getSampleMemCapacity(0)-memPos);
|
||||
logW("out of ADPCM memory for sample %d!",i);
|
||||
} else {
|
||||
memcpy(adpcmMem+memPos,s->dataVOX,paddedLen);
|
||||
}
|
||||
s->offVOX=memPos;
|
||||
memPos+=paddedLen;
|
||||
}
|
||||
adpcmMemLen=memPos+256;
|
||||
|
||||
// phrase book
|
||||
for (int i=0; i<sampleCount; i++) {
|
||||
DivSample* s=parent->song.sample[i];
|
||||
int endPos=s->offVOX+s->lengthVOX;
|
||||
adpcmMem[i*8]=(s->offVOX>>16)&0xff;
|
||||
adpcmMem[1+i*8]=(s->offVOX>>8)&0xff;
|
||||
adpcmMem[2+i*8]=(s->offVOX)&0xff;
|
||||
adpcmMem[3+i*8]=(endPos>>16)&0xff;
|
||||
adpcmMem[4+i*8]=(endPos>>8)&0xff;
|
||||
adpcmMem[5+i*8]=(endPos)&0xff;
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformMSM6295::setFlags(unsigned int flags) {
|
||||
if (flags&1) {
|
||||
chipClock=8448000;
|
||||
} else {
|
||||
chipClock=8000000;
|
||||
}
|
||||
rate=chipClock/((flags&2)?6:24);
|
||||
for (int i=0; i<4; i++) {
|
||||
isMuted[i]=false;
|
||||
oscBuf[i]->rate=rate/22;
|
||||
}
|
||||
}
|
||||
|
||||
int DivPlatformMSM6295::init(DivEngine* p, int channels, int sugRate, unsigned int flags) {
|
||||
parent=p;
|
||||
adpcmMem=new unsigned char[getSampleMemCapacity(0)];
|
||||
adpcmMemLen=0;
|
||||
iface.adpcmMem=adpcmMem;
|
||||
iface.sampleBank=0;
|
||||
dumpWrites=false;
|
||||
skipRegisterWrites=false;
|
||||
updateOsc=0;
|
||||
for (int i=0; i<4; i++) {
|
||||
isMuted[i]=false;
|
||||
oscBuf[i]=new DivDispatchOscBuffer;
|
||||
}
|
||||
msm=new msm6295_core(iface);
|
||||
setFlags(flags);
|
||||
reset();
|
||||
return 4;
|
||||
}
|
||||
|
||||
void DivPlatformMSM6295::quit() {
|
||||
for (int i=0; i<4; i++) {
|
||||
delete oscBuf[i];
|
||||
}
|
||||
delete msm;
|
||||
delete[] adpcmMem;
|
||||
}
|
||||
|
||||
DivPlatformMSM6295::~DivPlatformMSM6295() {
|
||||
}
|
||||
138
src/engine/platform/msm6295.h
Normal file
138
src/engine/platform/msm6295.h
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef _MSM6295_H
|
||||
#define _MSM6295_H
|
||||
#include "../dispatch.h"
|
||||
#include "../macroInt.h"
|
||||
#include <queue>
|
||||
#include "sound/oki/msm6295.hpp"
|
||||
|
||||
class DivMSM6295Interface: public vgsound_emu_mem_intf {
|
||||
public:
|
||||
unsigned char* adpcmMem;
|
||||
int sampleBank;
|
||||
u8 read_byte(u32 address);
|
||||
DivMSM6295Interface(): adpcmMem(NULL), sampleBank(0) {}
|
||||
};
|
||||
|
||||
class DivPlatformMSM6295: public DivDispatch {
|
||||
protected:
|
||||
const unsigned short chanOffs[6]={
|
||||
0x00, 0x01, 0x02, 0x100, 0x101, 0x102
|
||||
};
|
||||
|
||||
struct Channel {
|
||||
unsigned char freqH, freqL;
|
||||
int freq, baseFreq, pitch, pitch2, portaPauseFreq, note, ins;
|
||||
unsigned char psgMode, autoEnvNum, autoEnvDen;
|
||||
signed char konCycles;
|
||||
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta, furnacePCM, hardReset;
|
||||
int vol, outVol;
|
||||
int sample;
|
||||
unsigned char pan;
|
||||
DivMacroInt std;
|
||||
void macroInit(DivInstrument* which) {
|
||||
std.init(which);
|
||||
pitch2=0;
|
||||
}
|
||||
Channel():
|
||||
freqH(0),
|
||||
freqL(0),
|
||||
freq(0),
|
||||
baseFreq(0),
|
||||
pitch(0),
|
||||
pitch2(0),
|
||||
portaPauseFreq(0),
|
||||
note(0),
|
||||
ins(-1),
|
||||
psgMode(1),
|
||||
autoEnvNum(0),
|
||||
autoEnvDen(0),
|
||||
active(false),
|
||||
insChanged(true),
|
||||
freqChanged(false),
|
||||
keyOn(false),
|
||||
keyOff(false),
|
||||
portaPause(false),
|
||||
inPorta(false),
|
||||
furnacePCM(false),
|
||||
hardReset(false),
|
||||
vol(0),
|
||||
outVol(15),
|
||||
sample(-1),
|
||||
pan(3) {}
|
||||
};
|
||||
Channel chan[4];
|
||||
DivDispatchOscBuffer* oscBuf[4];
|
||||
bool isMuted[4];
|
||||
struct QueuedWrite {
|
||||
unsigned short addr;
|
||||
unsigned char val;
|
||||
bool addrOrVal;
|
||||
QueuedWrite(unsigned short a, unsigned char v): addr(a), val(v), addrOrVal(false) {}
|
||||
};
|
||||
std::queue<QueuedWrite> writes;
|
||||
msm6295_core* msm;
|
||||
unsigned char regPool[512];
|
||||
unsigned char lastBusy;
|
||||
|
||||
unsigned char* adpcmMem;
|
||||
size_t adpcmMemLen;
|
||||
DivMSM6295Interface iface;
|
||||
unsigned char sampleBank;
|
||||
|
||||
int delay, updateOsc;
|
||||
|
||||
bool extMode;
|
||||
|
||||
short oldWrites[512];
|
||||
short pendingWrites[512];
|
||||
|
||||
friend void putDispatchChan(void*,int,int);
|
||||
|
||||
public:
|
||||
void acquire(short* bufL, short* bufR, size_t start, size_t len);
|
||||
int dispatch(DivCommand c);
|
||||
void* getChanState(int chan);
|
||||
DivDispatchOscBuffer* getOscBuffer(int chan);
|
||||
unsigned char* getRegisterPool();
|
||||
int getRegisterPoolSize();
|
||||
void reset();
|
||||
void forceIns();
|
||||
void tick(bool sysTick=true);
|
||||
void muteChannel(int ch, bool mute);
|
||||
bool keyOffAffectsArp(int ch);
|
||||
void notifyInsChange(int ins);
|
||||
void notifyInsDeletion(void* ins);
|
||||
void poke(unsigned int addr, unsigned short val);
|
||||
void poke(std::vector<DivRegWrite>& wlist);
|
||||
void setFlags(unsigned int flags);
|
||||
const char** getRegisterSheet();
|
||||
const char* getEffectName(unsigned char effect);
|
||||
const void* getSampleMem(int index);
|
||||
size_t getSampleMemCapacity(int index);
|
||||
size_t getSampleMemUsage(int index);
|
||||
void renderSamples();
|
||||
|
||||
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
|
||||
void quit();
|
||||
~DivPlatformMSM6295();
|
||||
};
|
||||
#endif
|
||||
|
|
@ -268,7 +268,7 @@ void DivPlatformN163::tick(bool sysTick) {
|
|||
if (chan[i].std.pitch.had) {
|
||||
if (chan[i].std.pitch.mode) {
|
||||
chan[i].pitch2+=chan[i].std.pitch.val;
|
||||
CLAMP_VAR(chan[i].pitch2,-2048,2048);
|
||||
CLAMP_VAR(chan[i].pitch2,-32768,32767);
|
||||
} else {
|
||||
chan[i].pitch2=chan[i].std.pitch.val;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -244,7 +244,7 @@ void DivPlatformNamcoWSG::tick(bool sysTick) {
|
|||
if (chan[i].std.pitch.had) {
|
||||
if (chan[i].std.pitch.mode) {
|
||||
chan[i].pitch2+=chan[i].std.pitch.val;
|
||||
CLAMP_VAR(chan[i].pitch2,-2048,2048);
|
||||
CLAMP_VAR(chan[i].pitch2,-32768,32767);
|
||||
} else {
|
||||
chan[i].pitch2=chan[i].std.pitch.val;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -284,7 +284,7 @@ void DivPlatformNES::tick(bool sysTick) {
|
|||
if (chan[i].std.pitch.had) {
|
||||
if (chan[i].std.pitch.mode) {
|
||||
chan[i].pitch2+=chan[i].std.pitch.val;
|
||||
CLAMP_VAR(chan[i].pitch2,-2048,2048);
|
||||
CLAMP_VAR(chan[i].pitch2,-32768,32767);
|
||||
} else {
|
||||
chan[i].pitch2=chan[i].std.pitch.val;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -352,7 +352,7 @@ void DivPlatformOPL::tick(bool sysTick) {
|
|||
if (isMuted[i]) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
|
||||
} else {
|
||||
if (isOutputL[ops==4][chan[i].state.alg][j] || i>=melodicChans) {
|
||||
if (isOutputL[ops==4][chan[i].state.alg][j] || i>melodicChans) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[i].outVol&0x3f))/63))|(op.ksl<<6));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
|
||||
|
|
@ -384,7 +384,7 @@ void DivPlatformOPL::tick(bool sysTick) {
|
|||
if (chan[i].std.pitch.had) {
|
||||
if (chan[i].std.pitch.mode) {
|
||||
chan[i].pitch2+=chan[i].std.pitch.val;
|
||||
CLAMP_VAR(chan[i].pitch2,-2048,2048);
|
||||
CLAMP_VAR(chan[i].pitch2,-32768,32767);
|
||||
} else {
|
||||
chan[i].pitch2=chan[i].std.pitch.val;
|
||||
}
|
||||
|
|
@ -392,7 +392,7 @@ void DivPlatformOPL::tick(bool sysTick) {
|
|||
}
|
||||
|
||||
if (chan[i].std.phaseReset.had) {
|
||||
if (chan[i].std.phaseReset.val==1) {
|
||||
if (chan[i].std.phaseReset.val==1 && chan[i].active) {
|
||||
chan[i].keyOn=true;
|
||||
}
|
||||
}
|
||||
|
|
@ -479,7 +479,7 @@ void DivPlatformOPL::tick(bool sysTick) {
|
|||
if (isMuted[i]) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
|
||||
} else {
|
||||
if (isOutputL[ops==4][chan[i].state.alg][j] || i>=melodicChans) {
|
||||
if (isOutputL[ops==4][chan[i].state.alg][j] || i>melodicChans) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[i].outVol&0x3f))/63))|(op.ksl<<6));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
|
||||
|
|
@ -688,7 +688,7 @@ void DivPlatformOPL::muteChannel(int ch, bool mute) {
|
|||
if (isMuted[ch]) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
|
||||
} else {
|
||||
if (isOutputL[ops==4][chan[ch].state.alg][i] || ch>=melodicChans) {
|
||||
if (isOutputL[ops==4][chan[ch].state.alg][i] || ch>melodicChans) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[ch].outVol&0x3f))/63))|(op.ksl<<6));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
|
||||
|
|
@ -734,12 +734,12 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
|||
chan[c.chan].sample=ins->amiga.getSample(c.value);
|
||||
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
|
||||
DivSample* s=parent->getSample(chan[c.chan].sample);
|
||||
immWrite(9,(s->offB>>5)&0xff);
|
||||
immWrite(10,(s->offB>>13)&0xff);
|
||||
immWrite(8,0);
|
||||
immWrite(9,(s->offB>>2)&0xff);
|
||||
immWrite(10,(s->offB>>10)&0xff);
|
||||
int end=s->offB+s->lengthB-1;
|
||||
immWrite(11,(end>>5)&0xff);
|
||||
immWrite(12,(end>>13)&0xff);
|
||||
immWrite(8,1);
|
||||
immWrite(11,(end>>2)&0xff);
|
||||
immWrite(12,(end>>10)&0xff);
|
||||
immWrite(7,(s->loopStart>=0)?0xb0:0xa0); // start/repeat
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].note=c.value;
|
||||
|
|
@ -769,12 +769,12 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
DivSample* s=parent->getSample(12*sampleBank+c.value%12);
|
||||
immWrite(8,0);
|
||||
immWrite(9,(s->offB>>2)&0xff);
|
||||
immWrite(10,(s->offB>>10)&0xff);
|
||||
int end=s->offB+s->lengthB-1;
|
||||
immWrite(11,(end>>2)&0xff);
|
||||
immWrite(12,(end>>10)&0xff);
|
||||
immWrite(8,1);
|
||||
immWrite(7,(s->loopStart>=0)?0xb0:0xa0); // start/repeat
|
||||
int freq=(65536.0*(double)s->rate)/(double)rate;
|
||||
immWrite(16,freq&0xff);
|
||||
|
|
@ -808,7 +808,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
|||
if (isMuted[c.chan]) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
|
||||
} else {
|
||||
if (isOutputL[ops==4][chan[c.chan].state.alg][i] || c.chan>=melodicChans) {
|
||||
if (isOutputL[ops==4][chan[c.chan].state.alg][i] || c.chan>melodicChans) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[c.chan].outVol&0x3f))/63))|(op.ksl<<6));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
|
||||
|
|
@ -911,7 +911,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
|||
if (isMuted[c.chan]) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
|
||||
} else {
|
||||
if (isOutputL[ops==4][chan[c.chan].state.alg][i] || c.chan>=melodicChans) {
|
||||
if (isOutputL[ops==4][chan[c.chan].state.alg][i] || c.chan>melodicChans) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[c.chan].outVol&0x3f))/63))|(op.ksl<<6));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
|
||||
|
|
@ -1060,7 +1060,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
|||
if (isMuted[c.chan]) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
|
||||
} else {
|
||||
if (isOutputL[ops==4][chan[c.chan].state.alg][c.value] || c.chan>=melodicChans) {
|
||||
if (isOutputL[ops==4][chan[c.chan].state.alg][c.value] || c.chan>melodicChans) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[c.chan].outVol&0x3f))/63))|(op.ksl<<6));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
|
||||
|
|
@ -1290,7 +1290,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
|||
if (isMuted[c.chan]) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
|
||||
} else {
|
||||
if (isOutputL[ops==4][chan[c.chan].state.alg][i] || c.chan>=melodicChans) {
|
||||
if (isOutputL[ops==4][chan[c.chan].state.alg][i] || c.chan>melodicChans) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[c.chan].outVol&0x3f))/63))|(op.ksl<<6));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
|
||||
|
|
@ -1307,7 +1307,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
|||
if (isMuted[c.chan]) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
|
||||
} else {
|
||||
if (isOutputL[ops==4][chan[c.chan].state.alg][c.value] || c.chan>=melodicChans) {
|
||||
if (isOutputL[ops==4][chan[c.chan].state.alg][c.value] || c.chan>melodicChans) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[c.chan].outVol&0x3f))/63))|(op.ksl<<6));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
|
||||
|
|
@ -1367,9 +1367,10 @@ void DivPlatformOPL::forceIns() {
|
|||
totalChans=properDrums?11:9;
|
||||
}
|
||||
for (int i=0; i<totalChans; i++) {
|
||||
int ops=(slots[3][i]!=255 && chan[i].state.ops==4 && oplType==3)?4:2;
|
||||
//int ops=(slots[3][i]!=255 && chan[i].state.ops==4 && oplType==3)?4:2;
|
||||
chan[i].insChanged=true;
|
||||
chan[i].freqChanged=true;
|
||||
/*
|
||||
chan[i].fourOp=(ops==4);
|
||||
for (int j=0; j<ops; j++) {
|
||||
unsigned char slot=slots[j][i];
|
||||
|
|
@ -1380,7 +1381,7 @@ void DivPlatformOPL::forceIns() {
|
|||
if (isMuted[i]) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
|
||||
} else {
|
||||
if (isOutputL[ops==4][chan[i].state.alg][j] || i>=melodicChans) {
|
||||
if (isOutputL[ops==4][chan[i].state.alg][j] || i>melodicChans) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[i].outVol&0x3f))/63))|(op.ksl<<6));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
|
||||
|
|
@ -1406,6 +1407,10 @@ void DivPlatformOPL::forceIns() {
|
|||
rWrite(chanMap[i+1]+ADDR_LR_FB_ALG,((chan[i].state.alg>>1)&1)|(chan[i].state.fb<<1)|((chan[i].pan&3)<<4));
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
for (int i=0; i<512; i++) {
|
||||
oldWrites[i]=-1;
|
||||
}
|
||||
immWrite(0xbd,(dam<<7)|(dvb<<6)|(properDrums<<5)|drumState);
|
||||
update4OpMask=true;
|
||||
|
|
@ -1698,7 +1703,7 @@ int DivPlatformOPL::init(DivEngine* p, int channels, int sugRate, unsigned int f
|
|||
adpcmBMemLen=0;
|
||||
iface.adpcmBMem=adpcmBMem;
|
||||
iface.sampleBank=0;
|
||||
adpcmB=new ymfm::adpcm_b_engine(iface,5);
|
||||
adpcmB=new ymfm::adpcm_b_engine(iface,2);
|
||||
}
|
||||
|
||||
reset();
|
||||
|
|
|
|||
|
|
@ -181,7 +181,7 @@ void DivPlatformOPLL::tick(bool sysTick) {
|
|||
if (chan[i].std.pitch.had) {
|
||||
if (chan[i].std.pitch.mode) {
|
||||
chan[i].pitch2+=chan[i].std.pitch.val;
|
||||
CLAMP_VAR(chan[i].pitch2,-2048,2048);
|
||||
CLAMP_VAR(chan[i].pitch2,-32768,32767);
|
||||
} else {
|
||||
chan[i].pitch2=chan[i].std.pitch.val;
|
||||
}
|
||||
|
|
@ -189,7 +189,7 @@ void DivPlatformOPLL::tick(bool sysTick) {
|
|||
}
|
||||
|
||||
if (chan[i].std.phaseReset.had) {
|
||||
if (chan[i].std.phaseReset.val==1) {
|
||||
if (chan[i].std.phaseReset.val==1 && chan[i].active) {
|
||||
chan[i].keyOn=true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -214,7 +214,7 @@ void DivPlatformPCE::tick(bool sysTick) {
|
|||
if (chan[i].std.pitch.had) {
|
||||
if (chan[i].std.pitch.mode) {
|
||||
chan[i].pitch2+=chan[i].std.pitch.val;
|
||||
CLAMP_VAR(chan[i].pitch2,-2048,2048);
|
||||
CLAMP_VAR(chan[i].pitch2,-32768,32767);
|
||||
} else {
|
||||
chan[i].pitch2=chan[i].std.pitch.val;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -216,7 +216,7 @@ void DivPlatformPCSpeaker::tick(bool sysTick) {
|
|||
if (chan[i].std.pitch.had) {
|
||||
if (chan[i].std.pitch.mode) {
|
||||
chan[i].pitch2+=chan[i].std.pitch.val;
|
||||
CLAMP_VAR(chan[i].pitch2,-2048,2048);
|
||||
CLAMP_VAR(chan[i].pitch2,-32768,32767);
|
||||
} else {
|
||||
chan[i].pitch2=chan[i].std.pitch.val;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -331,7 +331,7 @@ void DivPlatformQSound::tick(bool sysTick) {
|
|||
if (chan[i].std.pitch.had) {
|
||||
if (chan[i].std.pitch.mode) {
|
||||
chan[i].pitch2+=chan[i].std.pitch.val;
|
||||
CLAMP_VAR(chan[i].pitch2,-2048,2048);
|
||||
CLAMP_VAR(chan[i].pitch2,-32768,32767);
|
||||
} else {
|
||||
chan[i].pitch2=chan[i].std.pitch.val;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ void DivPlatformRF5C68::tick(bool sysTick) {
|
|||
if (chan[i].std.pitch.had) {
|
||||
if (chan[i].std.pitch.mode) {
|
||||
chan[i].pitch2+=chan[i].std.pitch.val;
|
||||
CLAMP_VAR(chan[i].pitch2,-2048,2048);
|
||||
CLAMP_VAR(chan[i].pitch2,-32768,32767);
|
||||
} else {
|
||||
chan[i].pitch2=chan[i].std.pitch.val;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -189,7 +189,7 @@ void DivPlatformSAA1099::tick(bool sysTick) {
|
|||
if (chan[i].std.pitch.had) {
|
||||
if (chan[i].std.pitch.mode) {
|
||||
chan[i].pitch2+=chan[i].std.pitch.val;
|
||||
CLAMP_VAR(chan[i].pitch2,-2048,2048);
|
||||
CLAMP_VAR(chan[i].pitch2,-32768,32767);
|
||||
} else {
|
||||
chan[i].pitch2=chan[i].std.pitch.val;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -146,7 +146,7 @@ void DivPlatformSCC::tick(bool sysTick) {
|
|||
if (chan[i].std.pitch.had) {
|
||||
if (chan[i].std.pitch.mode) {
|
||||
chan[i].pitch2+=chan[i].std.pitch.val;
|
||||
CLAMP_VAR(chan[i].pitch2,-2048,2048);
|
||||
CLAMP_VAR(chan[i].pitch2,-32768,32767);
|
||||
} else {
|
||||
chan[i].pitch2=chan[i].std.pitch.val;
|
||||
}
|
||||
|
|
@ -161,9 +161,14 @@ void DivPlatformSCC::tick(bool sysTick) {
|
|||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER)-1;
|
||||
if (chan[i].freq<0) chan[i].freq=0;
|
||||
if (chan[i].freq>4095) chan[i].freq=4095;
|
||||
rWrite(regBase+0+i*2,chan[i].freq&0xff);
|
||||
rWrite(regBase+1+i*2,chan[i].freq>>8);
|
||||
if (!chan[i].freqInit || regPool[regBase+0+i*2]!=(chan[i].freq&0xff)) {
|
||||
rWrite(regBase+0+i*2,chan[i].freq&0xff);
|
||||
}
|
||||
if (!chan[i].freqInit || regPool[regBase+1+i*2]!=(chan[i].freq>>8)) {
|
||||
rWrite(regBase+1+i*2,chan[i].freq>>8);
|
||||
}
|
||||
chan[i].freqChanged=false;
|
||||
chan[i].freqInit=!skipRegisterWrites;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -286,6 +291,7 @@ void DivPlatformSCC::forceIns() {
|
|||
for (int i=0; i<5; i++) {
|
||||
chan[i].insChanged=true;
|
||||
chan[i].freqChanged=true;
|
||||
chan[i].freqInit=false;
|
||||
if (isPlus || i<3) {
|
||||
updateWave(i);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
class DivPlatformSCC: public DivDispatch {
|
||||
struct Channel {
|
||||
int freq, baseFreq, pitch, pitch2, note, ins;
|
||||
bool active, insChanged, freqChanged, inPorta;
|
||||
bool active, insChanged, freqChanged, freqInit, inPorta;
|
||||
signed char vol, outVol, wave;
|
||||
signed char waveROM[32] = {0}; // 4 bit PROM per channel on bubble system
|
||||
DivMacroInt std;
|
||||
|
|
@ -48,6 +48,7 @@ class DivPlatformSCC: public DivDispatch {
|
|||
active(false),
|
||||
insChanged(true),
|
||||
freqChanged(false),
|
||||
freqInit(false),
|
||||
inPorta(false),
|
||||
vol(15),
|
||||
outVol(15),
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ void DivPlatformSegaPCM::tick(bool sysTick) {
|
|||
if (chan[i].std.pitch.had) {
|
||||
if (chan[i].std.pitch.mode) {
|
||||
chan[i].pitch2+=chan[i].std.pitch.val;
|
||||
CLAMP_VAR(chan[i].pitch2,-2048,2048);
|
||||
CLAMP_VAR(chan[i].pitch2,-32768,32767);
|
||||
} else {
|
||||
chan[i].pitch2=chan[i].std.pitch.val;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
#include "../engine.h"
|
||||
#include <math.h>
|
||||
|
||||
#define rWrite(v) {if (!skipRegisterWrites) {sn->write(v); if (dumpWrites) {addWrite(0x200,v);}}}
|
||||
#define rWrite(v) {if (!skipRegisterWrites) {writes.push(v); if (dumpWrites) {addWrite(0x200,v);}}}
|
||||
|
||||
const char* regCheatSheetSN[]={
|
||||
"DATA", "0",
|
||||
|
|
@ -41,7 +41,47 @@ const char* DivPlatformSMS::getEffectName(unsigned char effect) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
void DivPlatformSMS::acquire(short* bufL, short* bufR, size_t start, size_t len) {
|
||||
void DivPlatformSMS::acquire_nuked(short* bufL, short* bufR, size_t start, size_t len) {
|
||||
for (size_t h=start; h<start+len; h++) {
|
||||
if (!writes.empty()) {
|
||||
unsigned char w=writes.front();
|
||||
YMPSG_Write(&sn_nuked,w);
|
||||
writes.pop();
|
||||
}
|
||||
YMPSG_Clock(&sn_nuked);
|
||||
YMPSG_Clock(&sn_nuked);
|
||||
YMPSG_Clock(&sn_nuked);
|
||||
YMPSG_Clock(&sn_nuked);
|
||||
YMPSG_Clock(&sn_nuked);
|
||||
YMPSG_Clock(&sn_nuked);
|
||||
YMPSG_Clock(&sn_nuked);
|
||||
YMPSG_Clock(&sn_nuked);
|
||||
YMPSG_Clock(&sn_nuked);
|
||||
YMPSG_Clock(&sn_nuked);
|
||||
YMPSG_Clock(&sn_nuked);
|
||||
YMPSG_Clock(&sn_nuked);
|
||||
YMPSG_Clock(&sn_nuked);
|
||||
YMPSG_Clock(&sn_nuked);
|
||||
YMPSG_Clock(&sn_nuked);
|
||||
YMPSG_Clock(&sn_nuked);
|
||||
bufL[h]=YMPSG_GetOutput(&sn_nuked)*8192.0;
|
||||
/*
|
||||
for (int i=0; i<4; i++) {
|
||||
if (isMuted[i]) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=0;
|
||||
} else {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=sn->get_channel_output(i);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformSMS::acquire_mame(short* bufL, short* bufR, size_t start, size_t len) {
|
||||
while (!writes.empty()) {
|
||||
unsigned char w=writes.front();
|
||||
sn->write(w);
|
||||
writes.pop();
|
||||
}
|
||||
for (size_t h=start; h<start+len; h++) {
|
||||
sn->sound_stream_update(bufL+h,1);
|
||||
for (int i=0; i<4; i++) {
|
||||
|
|
@ -54,6 +94,14 @@ void DivPlatformSMS::acquire(short* bufL, short* bufR, size_t start, size_t len)
|
|||
}
|
||||
}
|
||||
|
||||
void DivPlatformSMS::acquire(short* bufL, short* bufR, size_t start, size_t len) {
|
||||
if (nuked) {
|
||||
acquire_nuked(bufL,bufR,start,len);
|
||||
} else {
|
||||
acquire_mame(bufL,bufR,start,len);
|
||||
}
|
||||
}
|
||||
|
||||
int DivPlatformSMS::acquireOne() {
|
||||
short v;
|
||||
sn->sound_stream_update(&v,1);
|
||||
|
|
@ -112,7 +160,7 @@ void DivPlatformSMS::tick(bool sysTick) {
|
|||
if (chan[i].std.pitch.had) {
|
||||
if (chan[i].std.pitch.mode) {
|
||||
chan[i].pitch2+=chan[i].std.pitch.val;
|
||||
CLAMP_VAR(chan[i].pitch2,-2048,2048);
|
||||
CLAMP_VAR(chan[i].pitch2,-32768,32767);
|
||||
} else {
|
||||
chan[i].pitch2=chan[i].std.pitch.val;
|
||||
}
|
||||
|
|
@ -301,6 +349,7 @@ DivDispatchOscBuffer* DivPlatformSMS::getOscBuffer(int ch) {
|
|||
}
|
||||
|
||||
void DivPlatformSMS::reset() {
|
||||
while (!writes.empty()) writes.pop();
|
||||
for (int i=0; i<4; i++) {
|
||||
chan[i]=DivPlatformSMS::Channel();
|
||||
chan[i].std.setEngine(parent);
|
||||
|
|
@ -309,6 +358,7 @@ void DivPlatformSMS::reset() {
|
|||
addWrite(0xffffffff,0);
|
||||
}
|
||||
sn->device_start();
|
||||
YMPSG_Init(&sn_nuked,isRealSN);
|
||||
snNoiseMode=3;
|
||||
rWrite(0xe7);
|
||||
updateSNMode=false;
|
||||
|
|
@ -352,6 +402,7 @@ void DivPlatformSMS::setFlags(unsigned int flags) {
|
|||
chipClock=COLOR_NTSC;
|
||||
}
|
||||
resetPhase=!(flags&16);
|
||||
|
||||
if (sn!=NULL) delete sn;
|
||||
switch ((flags>>2)&3) {
|
||||
case 1: // TI
|
||||
|
|
@ -377,6 +428,10 @@ void DivPlatformSMS::setFlags(unsigned int flags) {
|
|||
}
|
||||
}
|
||||
|
||||
void DivPlatformSMS::setNuked(bool value) {
|
||||
nuked=value;
|
||||
}
|
||||
|
||||
int DivPlatformSMS::init(DivEngine* p, int channels, int sugRate, unsigned int flags) {
|
||||
parent=p;
|
||||
dumpWrites=false;
|
||||
|
|
|
|||
|
|
@ -23,6 +23,10 @@
|
|||
#include "../dispatch.h"
|
||||
#include "../macroInt.h"
|
||||
#include "sound/sn76496.h"
|
||||
extern "C" {
|
||||
#include "../../../extern/Nuked-PSG/ympsg.h"
|
||||
}
|
||||
#include <queue>
|
||||
|
||||
class DivPlatformSMS: public DivDispatch {
|
||||
struct Channel {
|
||||
|
|
@ -59,8 +63,14 @@ class DivPlatformSMS: public DivDispatch {
|
|||
bool updateSNMode;
|
||||
bool resetPhase;
|
||||
bool isRealSN;
|
||||
bool nuked;
|
||||
sn76496_base_device* sn;
|
||||
ympsg_t sn_nuked;
|
||||
std::queue<unsigned char> writes;
|
||||
friend void putDispatchChan(void*,int,int);
|
||||
|
||||
void acquire_nuked(short* bufL, short* bufR, size_t start, size_t len);
|
||||
void acquire_mame(short* bufL, short* bufR, size_t start, size_t len);
|
||||
public:
|
||||
int acquireOne();
|
||||
void acquire(short* bufL, short* bufR, size_t start, size_t len);
|
||||
|
|
@ -80,6 +90,7 @@ class DivPlatformSMS: public DivDispatch {
|
|||
void poke(std::vector<DivRegWrite>& wlist);
|
||||
const char** getRegisterSheet();
|
||||
const char* getEffectName(unsigned char effect);
|
||||
void setNuked(bool value);
|
||||
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
|
||||
void quit();
|
||||
~DivPlatformSMS();
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@
|
|||
*/
|
||||
|
||||
#include "n163.hpp"
|
||||
#include <string.h>
|
||||
|
||||
void n163_core::tick()
|
||||
{
|
||||
|
|
@ -126,12 +127,12 @@ void n163_core::reset()
|
|||
// reset this chip
|
||||
m_disable = false;
|
||||
m_multiplex = true;
|
||||
std::fill(std::begin(m_ram), std::end(m_ram), 0);
|
||||
memset(m_ram,0,sizeof(m_ram));
|
||||
m_voice_cycle = 0x78;
|
||||
m_addr_latch.reset();
|
||||
m_out = 0;
|
||||
m_acc = 0;
|
||||
std::fill(std::begin(m_ch_out), std::end(m_ch_out), 0);
|
||||
memset(m_ch_out,0,sizeof(m_ch_out));
|
||||
}
|
||||
|
||||
// accessor
|
||||
|
|
|
|||
|
|
@ -243,7 +243,7 @@ unsigned int NES_FDS::Render (int b[2])
|
|||
int((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 4.0f),
|
||||
int((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 5.0f) };
|
||||
|
||||
int v = fout * MASTER[master_vol] >> 8;
|
||||
int v = fout * MASTER[master_vol] >> 1;
|
||||
|
||||
// lowpass RC filter
|
||||
int rc_out = ((rc_accum * rc_k) + (v * rc_l)) >> RC_BITS;
|
||||
|
|
@ -252,8 +252,8 @@ unsigned int NES_FDS::Render (int b[2])
|
|||
|
||||
// output mix
|
||||
int m = mask ? 0 : v;
|
||||
b[0] = (m * sm[0]) >> 7;
|
||||
b[1] = (m * sm[1]) >> 7;
|
||||
b[0] = (m * sm[0]) >> 14;
|
||||
b[1] = (m * sm[1]) >> 14;
|
||||
return 2;
|
||||
}
|
||||
|
||||
|
|
|
|||
214
src/engine/platform/sound/oki/msm6295.cpp
Normal file
214
src/engine/platform/sound/oki/msm6295.cpp
Normal file
|
|
@ -0,0 +1,214 @@
|
|||
/*
|
||||
License: BSD-3-Clause
|
||||
see https://github.com/cam900/vgsound_emu/blob/main/LICENSE for more details
|
||||
|
||||
Copyright holder(s): cam900
|
||||
OKI MSM6295 emulation core
|
||||
|
||||
It is 4 channel ADPCM playback chip from OKI semiconductor.
|
||||
It was becomes de facto standard for ADPCM playback in arcade machine, due to cost performance.
|
||||
|
||||
The chip itself is pretty barebone: there is no "register" in chip.
|
||||
It can't control volume and pitch in currently playing channels, only stopable them.
|
||||
And volume is must be determined at playback start command.
|
||||
|
||||
Command format:
|
||||
|
||||
Playback command (2 byte):
|
||||
|
||||
Byte Bit Description
|
||||
76543210
|
||||
0 1xxxxxxx Phrase select (Header stored in ROM)
|
||||
1 x000---- Play channel 4
|
||||
0x00---- Play channel 3
|
||||
00x0---- Play channel 2
|
||||
000x---- Play channel 1
|
||||
----xxxx Volume
|
||||
----0000 0.0dB
|
||||
----0001 -3.2dB
|
||||
----0010 -6.0dB
|
||||
----0011 -9.2dB
|
||||
----0100 -12.0dB
|
||||
----0101 -14.5dB
|
||||
----0110 -18.0dB
|
||||
----0111 -20.5dB
|
||||
----1000 -24.0dB
|
||||
|
||||
Suspend command (1 byte):
|
||||
|
||||
Byte Bit Description
|
||||
76543210
|
||||
0 0x------ Suspend channel 4
|
||||
0-x----- Suspend channel 3
|
||||
0--x---- Suspend channel 2
|
||||
0---x--- Suspend channel 1
|
||||
|
||||
Frequency calculation:
|
||||
if (SS) then
|
||||
Frequency = Input clock / 165
|
||||
else then
|
||||
Frequency = Input clock / 132
|
||||
|
||||
*/
|
||||
|
||||
#include "msm6295.hpp"
|
||||
|
||||
#define CORE_DIVIDER 3
|
||||
|
||||
#define CHANNEL_DELAY (15/CORE_DIVIDER)
|
||||
#define MASTER_DELAY (33/CORE_DIVIDER)
|
||||
|
||||
void msm6295_core::tick()
|
||||
{
|
||||
// command handler
|
||||
if (m_command_pending)
|
||||
{
|
||||
if (bitfield(m_command, 7)) // play voice
|
||||
{
|
||||
if ((++m_clock) >= ((CHANNEL_DELAY * (m_ss ? 5 : 4))))
|
||||
{
|
||||
m_clock = 0;
|
||||
if (bitfield(m_next_command, 4, 4) != 0)
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
if (bitfield(m_next_command, 4 + i))
|
||||
{
|
||||
if (!m_voice[i].m_busy)
|
||||
{
|
||||
m_voice[i].m_command = m_command;
|
||||
m_voice[i].m_volume = (bitfield(m_next_command, 0, 4) < 9) ? m_volume_table[std::min<u8>(8, bitfield(m_next_command, 0, 4))] : 0;
|
||||
}
|
||||
break; // voices aren't be playable simultaneously at once
|
||||
}
|
||||
}
|
||||
}
|
||||
m_command = 0;
|
||||
m_command_pending = false;
|
||||
}
|
||||
}
|
||||
else if (bitfield(m_next_command, 7)) // select phrase
|
||||
{
|
||||
if ((++m_clock) >= ((CHANNEL_DELAY * (m_ss ? 5 : 4))))
|
||||
{
|
||||
m_clock = 0;
|
||||
m_command = m_next_command;
|
||||
m_command_pending = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (bitfield(m_next_command, 3, 4) != 0) // suspend voices
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
if (bitfield(m_next_command, 3 + i))
|
||||
{
|
||||
if (m_voice[i].m_busy)
|
||||
m_voice[i].m_command = m_next_command;
|
||||
}
|
||||
}
|
||||
m_next_command &= ~0x78;
|
||||
}
|
||||
m_command_pending = false;
|
||||
}
|
||||
}
|
||||
m_out = 0;
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
m_voice[i].tick();
|
||||
if (!m_voice[i].m_muted) m_out += m_voice[i].m_out;
|
||||
}
|
||||
}
|
||||
|
||||
void msm6295_core::reset()
|
||||
{
|
||||
for (auto & elem : m_voice)
|
||||
elem.reset();
|
||||
|
||||
m_command = 0;
|
||||
m_next_command = 0;
|
||||
m_command_pending = false;
|
||||
m_clock = 0;
|
||||
m_out = 0;
|
||||
}
|
||||
|
||||
void msm6295_core::voice_t::tick()
|
||||
{
|
||||
if (!m_busy)
|
||||
{
|
||||
if (bitfield(m_command, 7))
|
||||
{
|
||||
// get phrase header (stored in data memory)
|
||||
const u32 phrase = bitfield(m_command, 0, 7) << 3;
|
||||
// Start address
|
||||
m_addr = (bitfield(m_host.m_intf.read_byte(phrase | 0), 0, 2) << 16)
|
||||
| (m_host.m_intf.read_byte(phrase | 1) << 8)
|
||||
| (m_host.m_intf.read_byte(phrase | 2) << 0);
|
||||
// End address
|
||||
m_end = (bitfield(m_host.m_intf.read_byte(phrase | 3), 0, 2) << 16)
|
||||
| (m_host.m_intf.read_byte(phrase | 4) << 8)
|
||||
| (m_host.m_intf.read_byte(phrase | 5) << 0);
|
||||
m_nibble = 4; // MSB first, LSB second
|
||||
m_command = 0;
|
||||
m_busy = true;
|
||||
vox_decoder_t::reset();
|
||||
}
|
||||
m_out = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// playback
|
||||
if ((++m_clock) >= ((MASTER_DELAY * (m_host.m_ss ? 5 : 4))))
|
||||
{
|
||||
m_clock = 0;
|
||||
bool is_end = (m_command != 0);
|
||||
m_curr.decode(bitfield(m_host.m_intf.read_byte(m_addr), m_nibble, 4));
|
||||
if (m_nibble <= 0)
|
||||
{
|
||||
m_nibble = 4;
|
||||
if (++m_addr > m_end)
|
||||
is_end = true;
|
||||
}
|
||||
else
|
||||
m_nibble -= 4;
|
||||
if (is_end)
|
||||
{
|
||||
m_command = 0;
|
||||
m_busy = false;
|
||||
}
|
||||
m_out = (out() * m_volume) >> 7; // scale out to 12 bit output
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void msm6295_core::voice_t::reset()
|
||||
{
|
||||
vox_decoder_t::reset();
|
||||
m_clock = 0;
|
||||
m_busy = false;
|
||||
m_command = 0;
|
||||
m_addr = 0;
|
||||
m_nibble = 0;
|
||||
m_end = 0;
|
||||
m_volume = 0;
|
||||
m_out = 0;
|
||||
}
|
||||
|
||||
// accessors
|
||||
u8 msm6295_core::busy_r()
|
||||
{
|
||||
return (m_voice[0].m_busy ? 0x01 : 0x00)
|
||||
| (m_voice[1].m_busy ? 0x02 : 0x00)
|
||||
| (m_voice[2].m_busy ? 0x04 : 0x00)
|
||||
| (m_voice[3].m_busy ? 0x08 : 0x00);
|
||||
}
|
||||
|
||||
void msm6295_core::command_w(u8 data)
|
||||
{
|
||||
if (!m_command_pending)
|
||||
{
|
||||
m_next_command = data;
|
||||
m_command_pending = true;
|
||||
}
|
||||
}
|
||||
94
src/engine/platform/sound/oki/msm6295.hpp
Normal file
94
src/engine/platform/sound/oki/msm6295.hpp
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
License: BSD-3-Clause
|
||||
see https://github.com/cam900/vgsound_emu/blob/main/LICENSE for more details
|
||||
|
||||
Copyright holder(s): cam900
|
||||
OKI MSM6295 emulation core
|
||||
|
||||
See msm6295.cpp for more info.
|
||||
*/
|
||||
|
||||
#include "util.hpp"
|
||||
#include "vox.hpp"
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#ifndef _VGSOUND_EMU_MSM6295_HPP
|
||||
#define _VGSOUND_EMU_MSM6295_HPP
|
||||
|
||||
#pragma once
|
||||
|
||||
class msm6295_core : public vox_core
|
||||
{
|
||||
friend class vgsound_emu_mem_intf; // common memory interface
|
||||
public:
|
||||
// constructor
|
||||
msm6295_core(vgsound_emu_mem_intf &intf)
|
||||
: m_voice{{*this,*this},{*this,*this},{*this,*this},{*this,*this}}
|
||||
, m_intf(intf)
|
||||
{
|
||||
}
|
||||
// accessors, getters, setters
|
||||
u8 busy_r();
|
||||
void command_w(u8 data);
|
||||
void ss_w(bool ss) { m_ss = ss; } // SS pin
|
||||
|
||||
// internal state
|
||||
void reset();
|
||||
void tick();
|
||||
|
||||
s32 out() { return m_out; } // built in 12 bit DAC
|
||||
|
||||
private:
|
||||
// Internal volume table, 9 step
|
||||
const s32 m_volume_table[9] = {
|
||||
32/* 0.0dB */,
|
||||
22/* -3.2dB */,
|
||||
16/* -6.0dB */,
|
||||
11/* -9.2dB */,
|
||||
8/* -12.0dB */,
|
||||
6/* -14.5dB */,
|
||||
4/* -18.0dB */,
|
||||
3/* -20.5dB */,
|
||||
2/* -24.0dB */ }; // scale out to 5 bit for optimization
|
||||
|
||||
public:
|
||||
// msm6295 voice structs
|
||||
struct voice_t : vox_decoder_t
|
||||
{
|
||||
// constructor
|
||||
voice_t(vox_core &vox, msm6295_core &host)
|
||||
: vox_decoder_t(vox)
|
||||
, m_host(host)
|
||||
{};
|
||||
|
||||
// internal state
|
||||
virtual void reset() override;
|
||||
void tick();
|
||||
|
||||
// accessors, getters, setters
|
||||
// registers
|
||||
msm6295_core &m_host;
|
||||
u16 m_clock = 0; // clock counter
|
||||
bool m_busy = false; // busy status
|
||||
bool m_muted = false; // muted
|
||||
u8 m_command = 0; // current command
|
||||
u32 m_addr = 0; // current address
|
||||
s8 m_nibble = 0; // current nibble
|
||||
u32 m_end = 0; // end address
|
||||
s32 m_volume = 0; // volume
|
||||
s32 m_out = 0; // output
|
||||
};
|
||||
voice_t m_voice[4];
|
||||
private:
|
||||
vgsound_emu_mem_intf &m_intf; // common memory interface
|
||||
|
||||
bool m_ss = false; // SS pin controls divider, input clock / 33 * (SS ? 5 : 4)
|
||||
u8 m_command = 0; // Command byte
|
||||
u8 m_next_command = 0; // Next command
|
||||
bool m_command_pending = false; // command pending flag
|
||||
u16 m_clock = 0; // clock counter
|
||||
s32 m_out = 0; // 12 bit output
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -11,8 +11,10 @@
|
|||
**********************************************************************************************/
|
||||
|
||||
|
||||
#include "emu.h"
|
||||
#include "okim6258.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#define COMMAND_STOP (1 << 0)
|
||||
#define COMMAND_PLAY (1 << 1)
|
||||
|
|
@ -34,9 +36,6 @@ static int tables_computed = 0;
|
|||
|
||||
|
||||
|
||||
// device type definition
|
||||
DEFINE_DEVICE_TYPE(OKIM6258, okim6258_device, "okim6258", "OKI MSM6258 ADPCM")
|
||||
|
||||
|
||||
//**************************************************************************
|
||||
// LIVE DEVICE
|
||||
|
|
@ -46,19 +45,18 @@ DEFINE_DEVICE_TYPE(OKIM6258, okim6258_device, "okim6258", "OKI MSM6258 ADPCM")
|
|||
// okim6258_device - constructor
|
||||
//-------------------------------------------------
|
||||
|
||||
okim6258_device::okim6258_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
||||
: device_t(mconfig, OKIM6258, tag, owner, clock),
|
||||
device_sound_interface(mconfig, *this),
|
||||
okim6258_device::okim6258_device(uint32_t clock)
|
||||
:
|
||||
m_status(0),
|
||||
m_start_divider(0),
|
||||
m_divider(512),
|
||||
m_adpcm_type(0),
|
||||
m_data_in(0),
|
||||
m_nibble_shift(0),
|
||||
m_stream(nullptr),
|
||||
m_output_bits(0),
|
||||
m_signal(0),
|
||||
m_step(0)
|
||||
m_step(0),
|
||||
m_clock(clock)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -114,12 +112,9 @@ void okim6258_device::device_start()
|
|||
|
||||
m_divider = dividers[m_start_divider];
|
||||
|
||||
m_stream = stream_alloc(0, 1, clock()/m_divider);
|
||||
|
||||
m_signal = -2;
|
||||
m_step = 0;
|
||||
|
||||
state_save_register();
|
||||
m_has_data = false;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -129,11 +124,10 @@ void okim6258_device::device_start()
|
|||
|
||||
void okim6258_device::device_reset()
|
||||
{
|
||||
m_stream->update();
|
||||
|
||||
m_signal = -2;
|
||||
m_step = 0;
|
||||
m_status = 0;
|
||||
m_has_data = false;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -141,7 +135,7 @@ void okim6258_device::device_reset()
|
|||
// sound_stream_update - handle a stream update
|
||||
//-------------------------------------------------
|
||||
|
||||
void okim6258_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
|
||||
void okim6258_device::sound_stream_update(short** outputs, int len)
|
||||
{
|
||||
auto &buffer = outputs[0];
|
||||
|
||||
|
|
@ -149,7 +143,7 @@ void okim6258_device::sound_stream_update(sound_stream &stream, std::vector<read
|
|||
{
|
||||
int nibble_shift = m_nibble_shift;
|
||||
|
||||
for (int sampindex = 0; sampindex < buffer.samples(); sampindex++)
|
||||
for (int sampindex = 0; sampindex < len; sampindex++)
|
||||
{
|
||||
/* Compute the new amplitude and update the current step */
|
||||
int nibble = (m_data_in >> nibble_shift) & 0xf;
|
||||
|
|
@ -159,7 +153,9 @@ void okim6258_device::sound_stream_update(sound_stream &stream, std::vector<read
|
|||
|
||||
nibble_shift ^= 4;
|
||||
|
||||
buffer.put_int(sampindex, sample, 32768);
|
||||
if (nibble_shift==0) m_has_data=false;
|
||||
|
||||
buffer[sampindex]=sample;
|
||||
}
|
||||
|
||||
/* Update the parameters */
|
||||
|
|
@ -167,29 +163,12 @@ void okim6258_device::sound_stream_update(sound_stream &stream, std::vector<read
|
|||
}
|
||||
else
|
||||
{
|
||||
buffer.fill(0);
|
||||
memset(buffer,0,len*sizeof(short));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**********************************************************************************************
|
||||
|
||||
state save support for MAME
|
||||
|
||||
***********************************************************************************************/
|
||||
|
||||
void okim6258_device::state_save_register()
|
||||
{
|
||||
save_item(NAME(m_status));
|
||||
save_item(NAME(m_divider));
|
||||
save_item(NAME(m_data_in));
|
||||
save_item(NAME(m_nibble_shift));
|
||||
save_item(NAME(m_signal));
|
||||
save_item(NAME(m_step));
|
||||
}
|
||||
|
||||
|
||||
int16_t okim6258_device::clock_adpcm(uint8_t nibble)
|
||||
{
|
||||
int32_t max = (1 << (m_output_bits - 1)) - 1;
|
||||
|
|
@ -224,7 +203,6 @@ int16_t okim6258_device::clock_adpcm(uint8_t nibble)
|
|||
void okim6258_device::set_divider(int val)
|
||||
{
|
||||
m_divider = dividers[val];
|
||||
notify_clock_changed();
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -236,7 +214,6 @@ void okim6258_device::set_divider(int val)
|
|||
|
||||
void okim6258_device::device_clock_changed()
|
||||
{
|
||||
m_stream->set_sample_rate(clock() / m_divider);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -248,7 +225,7 @@ void okim6258_device::device_clock_changed()
|
|||
|
||||
int okim6258_device::get_vclk()
|
||||
{
|
||||
return (clock() / m_divider);
|
||||
return (m_clock / m_divider);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -260,8 +237,6 @@ int okim6258_device::get_vclk()
|
|||
|
||||
uint8_t okim6258_device::status_r()
|
||||
{
|
||||
m_stream->update();
|
||||
|
||||
return (m_status & STATUS_PLAYING) ? 0x00 : 0x80;
|
||||
}
|
||||
|
||||
|
|
@ -271,13 +246,13 @@ uint8_t okim6258_device::status_r()
|
|||
okim6258_data_w -- write to the control port of an OKIM6258-compatible chip
|
||||
|
||||
***********************************************************************************************/
|
||||
void okim6258_device::data_w(uint8_t data)
|
||||
bool okim6258_device::data_w(uint8_t data)
|
||||
{
|
||||
/* update the stream */
|
||||
m_stream->update();
|
||||
|
||||
if (m_has_data) return false;
|
||||
m_data_in = data;
|
||||
m_nibble_shift = 0;
|
||||
m_has_data = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -289,11 +264,10 @@ void okim6258_device::data_w(uint8_t data)
|
|||
|
||||
void okim6258_device::ctrl_w(uint8_t data)
|
||||
{
|
||||
m_stream->update();
|
||||
|
||||
if (data & COMMAND_STOP)
|
||||
{
|
||||
m_status &= ~(STATUS_PLAYING | STATUS_RECORDING);
|
||||
m_has_data = false;
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -316,7 +290,7 @@ void okim6258_device::ctrl_w(uint8_t data)
|
|||
|
||||
if (data & COMMAND_RECORD)
|
||||
{
|
||||
logerror("M6258: Record enabled\n");
|
||||
//logerror("M6258: Record enabled\n");
|
||||
m_status |= STATUS_RECORDING;
|
||||
}
|
||||
else
|
||||
|
|
|
|||
|
|
@ -3,8 +3,7 @@
|
|||
#ifndef MAME_SOUND_OKIM6258_H
|
||||
#define MAME_SOUND_OKIM6258_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
//**************************************************************************
|
||||
// TYPE DEFINITIONS
|
||||
|
|
@ -12,9 +11,7 @@
|
|||
|
||||
// ======================> okim6258_device
|
||||
|
||||
class okim6258_device : public device_t,
|
||||
public device_sound_interface
|
||||
{
|
||||
class okim6258_device {
|
||||
public:
|
||||
static constexpr int FOSC_DIV_BY_1024 = 0;
|
||||
static constexpr int FOSC_DIV_BY_768 = 1;
|
||||
|
|
@ -26,7 +23,7 @@ public:
|
|||
static constexpr int OUTPUT_10BITS = 10;
|
||||
static constexpr int OUTPUT_12BITS = 12;
|
||||
|
||||
okim6258_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
|
||||
okim6258_device(uint32_t clock);
|
||||
|
||||
// configuration
|
||||
void set_start_div(int div) { m_start_divider = div; }
|
||||
|
|
@ -34,23 +31,21 @@ public:
|
|||
void set_outbits(int outbit) { m_output_bits = outbit; }
|
||||
|
||||
uint8_t status_r();
|
||||
void data_w(uint8_t data);
|
||||
bool data_w(uint8_t data);
|
||||
void ctrl_w(uint8_t data);
|
||||
|
||||
void set_divider(int val);
|
||||
int get_vclk();
|
||||
|
||||
protected:
|
||||
// device-level overrides
|
||||
virtual void device_start() override;
|
||||
virtual void device_reset() override;
|
||||
virtual void device_clock_changed() override;
|
||||
// device-levels
|
||||
void device_start();
|
||||
void device_reset();
|
||||
void device_clock_changed();
|
||||
|
||||
// sound stream update overrides
|
||||
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
|
||||
// sound stream updates
|
||||
void sound_stream_update(short** outputs, int len);
|
||||
|
||||
private:
|
||||
void state_save_register();
|
||||
int16_t clock_adpcm(uint8_t nibble);
|
||||
|
||||
uint8_t m_status;
|
||||
|
|
@ -59,16 +54,15 @@ private:
|
|||
uint32_t m_divider; /* master clock divider */
|
||||
uint8_t m_adpcm_type; /* 3/4 bit ADPCM select */
|
||||
uint8_t m_data_in; /* ADPCM data-in register */
|
||||
bool m_has_data; /* whether we already have data */
|
||||
uint8_t m_nibble_shift; /* nibble select */
|
||||
sound_stream *m_stream; /* which stream are we playing on? */
|
||||
|
||||
uint8_t m_output_bits; /* D/A precision is 10-bits but 12-bit data can be
|
||||
output serially to an external DAC */
|
||||
|
||||
int32_t m_signal;
|
||||
int32_t m_step;
|
||||
unsigned int m_clock;
|
||||
};
|
||||
|
||||
DECLARE_DEVICE_TYPE(OKIM6258, okim6258_device)
|
||||
|
||||
#endif // MAME_SOUND_OKIM6258_H
|
||||
|
|
|
|||
158
src/engine/platform/sound/oki/util.hpp
Normal file
158
src/engine/platform/sound/oki/util.hpp
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
/*
|
||||
License: BSD-3-Clause
|
||||
see https://github.com/cam900/vgsound_emu/blob/main/LICENSE for more details
|
||||
|
||||
Copyright holders: cam900
|
||||
Various core utilities for vgsound_emu
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <math.h>
|
||||
|
||||
#ifndef _VGSOUND_EMU_CORE_UTIL_HPP
|
||||
#define _VGSOUND_EMU_CORE_UTIL_HPP
|
||||
|
||||
#pragma once
|
||||
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned int u32;
|
||||
typedef unsigned long long u64;
|
||||
typedef signed char s8;
|
||||
typedef signed short s16;
|
||||
typedef signed int s32;
|
||||
typedef signed long long s64;
|
||||
typedef float f32;
|
||||
typedef double f64;
|
||||
|
||||
const f64 PI = 3.1415926535897932384626433832795;
|
||||
|
||||
// get bitfield, bitfield(input, position, len)
|
||||
template<typename T> T bitfield(T in, u8 pos, u8 len = 1)
|
||||
{
|
||||
return (in >> pos) & (len ? (T(1 << len) - 1) : 1);
|
||||
}
|
||||
|
||||
// get sign extended value, sign_ext<type>(input, len)
|
||||
template<typename T> T sign_ext(T in, u8 len)
|
||||
{
|
||||
len = std::max<u8>(0, (8 * sizeof(T)) - len);
|
||||
return T(T(in) << len) >> len;
|
||||
}
|
||||
|
||||
// convert attenuation decibel value to gain
|
||||
inline f32 dB_to_gain(f32 attenuation)
|
||||
{
|
||||
return powf(10.0f, attenuation / 20.0f);
|
||||
}
|
||||
|
||||
class vgsound_emu_mem_intf
|
||||
{
|
||||
public:
|
||||
virtual u8 read_byte(u32 address) { return 0; }
|
||||
virtual u16 read_word(u32 address) { return 0; }
|
||||
virtual u32 read_dword(u32 address) { return 0; }
|
||||
virtual u64 read_qword(u32 address) { return 0; }
|
||||
virtual void write_byte(u32 address, u8 data) { }
|
||||
virtual void write_word(u32 address, u16 data) { }
|
||||
virtual void write_dword(u32 address, u32 data) { }
|
||||
virtual void write_qword(u32 address, u64 data) { }
|
||||
};
|
||||
|
||||
template<typename T, T InitWidth, u8 InitEdge = 0>
|
||||
struct clock_pulse_t
|
||||
{
|
||||
void reset(T init = InitWidth)
|
||||
{
|
||||
m_edge.reset();
|
||||
m_width = m_width_latch = m_counter = init;
|
||||
m_cycle = 0;
|
||||
}
|
||||
|
||||
bool tick(T width = 0)
|
||||
{
|
||||
bool carry = ((--m_counter) <= 0);
|
||||
if (carry)
|
||||
{
|
||||
if (!width)
|
||||
m_width = m_width_latch;
|
||||
else
|
||||
m_width = width; // reset width
|
||||
m_counter = m_width;
|
||||
m_cycle = 0;
|
||||
}
|
||||
else
|
||||
m_cycle++;
|
||||
|
||||
m_edge.tick(carry);
|
||||
return carry;
|
||||
}
|
||||
|
||||
void set_width(T width) { m_width = width; }
|
||||
void set_width_latch(T width) { m_width_latch = width; }
|
||||
|
||||
// Accessors
|
||||
bool current_edge() { return m_edge.m_current; }
|
||||
bool rising_edge() { return m_edge.m_rising; }
|
||||
bool falling_edge() { return m_edge.m_rising; }
|
||||
T cycle() { return m_cycle; }
|
||||
|
||||
struct edge_t
|
||||
{
|
||||
edge_t()
|
||||
: m_current(InitEdge ^ 1)
|
||||
, m_previous(InitEdge)
|
||||
, m_rising(0)
|
||||
, m_falling(0)
|
||||
, m_changed(0)
|
||||
{
|
||||
set(InitEdge);
|
||||
}
|
||||
|
||||
void tick(bool toggle)
|
||||
{
|
||||
u8 current = m_current;
|
||||
if (toggle)
|
||||
current ^= 1;
|
||||
set(current);
|
||||
}
|
||||
|
||||
void set(u8 edge)
|
||||
{
|
||||
edge &= 1;
|
||||
m_rising = m_falling = m_changed = 0;
|
||||
if (m_current != edge)
|
||||
{
|
||||
m_changed = 1;
|
||||
if (m_current && (!edge))
|
||||
m_falling = 1;
|
||||
else if ((!m_current) && edge)
|
||||
m_rising = 1;
|
||||
m_current = edge;
|
||||
}
|
||||
m_previous = m_current;
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
m_previous = InitEdge;
|
||||
m_current = InitEdge ^ 1;
|
||||
set(InitEdge);
|
||||
}
|
||||
|
||||
u8 m_current : 1; // current edge
|
||||
u8 m_previous : 1; // previous edge
|
||||
u8 m_rising : 1; // rising edge
|
||||
u8 m_falling : 1; // falling edge
|
||||
u8 m_changed : 1; // changed flag
|
||||
};
|
||||
|
||||
edge_t m_edge;
|
||||
T m_width = InitWidth; // clock pulse width
|
||||
T m_width_latch = InitWidth; // clock pulse width latch
|
||||
T m_counter = InitWidth; // clock counter
|
||||
T m_cycle = 0; // clock cycle
|
||||
};
|
||||
|
||||
#endif
|
||||
115
src/engine/platform/sound/oki/vox.hpp
Normal file
115
src/engine/platform/sound/oki/vox.hpp
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
License: BSD-3-Clause
|
||||
see https://github.com/cam900/vgsound_emu/blob/main/LICENSE for more details
|
||||
|
||||
Copyright holder(s): cam900
|
||||
Dialogic ADPCM core
|
||||
*/
|
||||
|
||||
#include "util.hpp"
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#ifndef _VGSOUND_EMU_CORE_VOX_HPP
|
||||
#define _VGSOUND_EMU_CORE_VOX_HPP
|
||||
|
||||
#pragma once
|
||||
|
||||
#define MODIFIED_CLAMP(x,xMin,xMax) (std::min(std::max((x),(xMin)),(xMax)))
|
||||
|
||||
class vox_core
|
||||
{
|
||||
protected:
|
||||
struct vox_decoder_t
|
||||
{
|
||||
vox_decoder_t(vox_core &vox)
|
||||
: m_curr(vox)
|
||||
, m_loop(vox)
|
||||
{ };
|
||||
|
||||
virtual void reset()
|
||||
{
|
||||
m_curr.reset();
|
||||
m_loop.reset();
|
||||
m_loop_saved = false;
|
||||
}
|
||||
|
||||
void save()
|
||||
{
|
||||
if (!m_loop_saved)
|
||||
{
|
||||
m_loop.copy(m_curr);
|
||||
m_loop_saved = true;
|
||||
}
|
||||
}
|
||||
|
||||
void restore()
|
||||
{
|
||||
if (m_loop_saved)
|
||||
m_curr.copy(m_loop);
|
||||
}
|
||||
|
||||
s32 out() { return m_curr.m_step; }
|
||||
|
||||
struct decoder_state_t
|
||||
{
|
||||
decoder_state_t(vox_core &vox)
|
||||
: m_vox(vox)
|
||||
{ };
|
||||
|
||||
void reset()
|
||||
{
|
||||
m_index = 0;
|
||||
m_step = 16;
|
||||
}
|
||||
|
||||
void copy(decoder_state_t src)
|
||||
{
|
||||
m_index = src.m_index;
|
||||
m_step = src.m_step;
|
||||
}
|
||||
|
||||
void decode(u8 nibble)
|
||||
{
|
||||
const u8 delta = bitfield(nibble, 0, 3);
|
||||
s16 ss = m_vox.m_step_table[m_index]; // ss(n)
|
||||
|
||||
// d(n) = (ss(n) * B2) + ((ss(n) / 2) * B1) + ((ss(n) / 4) * B0) + (ss(n) / 8)
|
||||
s16 d = ss >> 3;
|
||||
if (bitfield(delta, 2))
|
||||
d += ss;
|
||||
if (bitfield(delta, 1))
|
||||
d += (ss >> 1);
|
||||
if (bitfield(delta, 0))
|
||||
d += (ss >> 2);
|
||||
|
||||
// if (B3 = 1) then d(n) = d(n) * (-1) X(n) = X(n-1) * d(n)
|
||||
if (bitfield(nibble, 3))
|
||||
m_step = std::max(m_step - d, -2048);
|
||||
else
|
||||
m_step = std::min(m_step + d, 2047);
|
||||
|
||||
// adjust step index
|
||||
m_index = MODIFIED_CLAMP(m_index + m_vox.m_index_table[delta], 0, 48);
|
||||
}
|
||||
|
||||
vox_core &m_vox;
|
||||
s8 m_index = 0;
|
||||
s32 m_step = 16;
|
||||
};
|
||||
|
||||
decoder_state_t m_curr;
|
||||
decoder_state_t m_loop;
|
||||
bool m_loop_saved = false;
|
||||
};
|
||||
|
||||
s8 m_index_table[8] = {-1, -1, -1, -1, 2, 4, 6, 8};
|
||||
s32 m_step_table[49] = {
|
||||
16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
|
||||
50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143,
|
||||
157, 173, 190, 209, 230, 253, 279, 307, 337, 371, 408, 449,
|
||||
494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -322,6 +322,7 @@
|
|||
*/
|
||||
|
||||
#include "scc.hpp"
|
||||
#include <string.h>
|
||||
|
||||
// shared SCC features
|
||||
void scc_core::tick()
|
||||
|
|
@ -372,12 +373,12 @@ void scc_core::reset()
|
|||
|
||||
m_test.reset();
|
||||
m_out = 0;
|
||||
std::fill(std::begin(m_reg), std::end(m_reg), 0);
|
||||
memset(m_reg,0,sizeof(m_reg));
|
||||
}
|
||||
|
||||
void scc_core::voice_t::reset()
|
||||
{
|
||||
std::fill(std::begin(wave), std::end(wave), 0);
|
||||
memset(wave,0,sizeof(wave));
|
||||
enable = false;
|
||||
pitch = 0;
|
||||
volume = 0;
|
||||
|
|
@ -454,6 +455,7 @@ void scc_core::freq_vol_enable_w(u8 address, u8 data)
|
|||
if (m_test.resetpos) // Reset address
|
||||
m_voice[voice_freq].addr = 0;
|
||||
m_voice[voice_freq].pitch = (m_voice[voice_freq].pitch & ~0x0ff) | data;
|
||||
m_voice[voice_freq].counter = m_voice[voice_freq].pitch;
|
||||
break;
|
||||
case 0x1: // 0x*1 Voice 0 Pitch MSB
|
||||
case 0x3: // 0x*3 Voice 1 Pitch MSB
|
||||
|
|
@ -463,6 +465,7 @@ void scc_core::freq_vol_enable_w(u8 address, u8 data)
|
|||
if (m_test.resetpos) // Reset address
|
||||
m_voice[voice_freq].addr = 0;
|
||||
m_voice[voice_freq].pitch = (m_voice[voice_freq].pitch & ~0xf00) | (u16(bitfield(data, 0, 4)) << 8);
|
||||
m_voice[voice_freq].counter = m_voice[voice_freq].pitch;
|
||||
break;
|
||||
case 0xa: // 0x*a Voice 0 Volume
|
||||
case 0xb: // 0x*b Voice 1 Volume
|
||||
|
|
|
|||
|
|
@ -81,6 +81,7 @@
|
|||
*/
|
||||
|
||||
#include "vrcvi.hpp"
|
||||
#include <string.h>
|
||||
|
||||
void vrcvi_core::tick()
|
||||
{
|
||||
|
|
@ -119,7 +120,7 @@ void vrcvi_core::reset()
|
|||
m_timer.reset();
|
||||
m_control.reset();
|
||||
m_out = 0;
|
||||
std::fill(std::begin(m_ch_out),std::end(m_ch_out),0);
|
||||
memset(m_ch_out,0,sizeof(m_ch_out));
|
||||
}
|
||||
|
||||
bool vrcvi_core::alu_t::tick()
|
||||
|
|
|
|||
|
|
@ -168,7 +168,7 @@ void DivPlatformSoundUnit::tick(bool sysTick) {
|
|||
if (chan[i].std.pitch.had) {
|
||||
if (chan[i].std.pitch.mode) {
|
||||
chan[i].pitch2+=chan[i].std.pitch.val;
|
||||
CLAMP_VAR(chan[i].pitch2,-2048,2048);
|
||||
CLAMP_VAR(chan[i].pitch2,-32768,32767);
|
||||
} else {
|
||||
chan[i].pitch2=chan[i].std.pitch.val;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -190,7 +190,7 @@ void DivPlatformSwan::tick(bool sysTick) {
|
|||
if (chan[i].std.pitch.had) {
|
||||
if (chan[i].std.pitch.mode) {
|
||||
chan[i].pitch2+=chan[i].std.pitch.val;
|
||||
CLAMP_VAR(chan[i].pitch2,-2048,2048);
|
||||
CLAMP_VAR(chan[i].pitch2,-32768,32767);
|
||||
} else {
|
||||
chan[i].pitch2=chan[i].std.pitch.val;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ void DivPlatformTIA::tick(bool sysTick) {
|
|||
if (chan[i].std.pitch.had) {
|
||||
if (chan[i].std.pitch.mode) {
|
||||
chan[i].pitch2+=chan[i].std.pitch.val;
|
||||
CLAMP_VAR(chan[i].pitch2,-2048,2048);
|
||||
CLAMP_VAR(chan[i].pitch2,-32768,32767);
|
||||
} else {
|
||||
chan[i].pitch2=chan[i].std.pitch.val;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -317,7 +317,7 @@ void DivPlatformTX81Z::tick(bool sysTick) {
|
|||
if (chan[i].std.pitch.had) {
|
||||
if (chan[i].std.pitch.mode) {
|
||||
chan[i].pitch2+=chan[i].std.pitch.val;
|
||||
CLAMP_VAR(chan[i].pitch2,-2048,2048);
|
||||
CLAMP_VAR(chan[i].pitch2,-32768,32767);
|
||||
} else {
|
||||
chan[i].pitch2=chan[i].std.pitch.val;
|
||||
}
|
||||
|
|
@ -325,7 +325,7 @@ void DivPlatformTX81Z::tick(bool sysTick) {
|
|||
}
|
||||
|
||||
if (chan[i].std.phaseReset.had) {
|
||||
if (chan[i].std.phaseReset.val==1) {
|
||||
if (chan[i].std.phaseReset.val==1 && chan[i].active) {
|
||||
chan[i].keyOn=true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -201,7 +201,7 @@ void DivPlatformVERA::tick(bool sysTick) {
|
|||
if (chan[i].std.pitch.had) {
|
||||
if (chan[i].std.pitch.mode) {
|
||||
chan[i].pitch2+=chan[i].std.pitch.val;
|
||||
CLAMP_VAR(chan[i].pitch2,-2048,2048);
|
||||
CLAMP_VAR(chan[i].pitch2,-32768,32767);
|
||||
} else {
|
||||
chan[i].pitch2=chan[i].std.pitch.val;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ void DivPlatformVIC20::tick(bool sysTick) {
|
|||
if (chan[i].std.pitch.had) {
|
||||
if (chan[i].std.pitch.mode) {
|
||||
chan[i].pitch2+=chan[i].std.pitch.val;
|
||||
CLAMP_VAR(chan[i].pitch2,-2048,2048);
|
||||
CLAMP_VAR(chan[i].pitch2,-32768,32767);
|
||||
} else {
|
||||
chan[i].pitch2=chan[i].std.pitch.val;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -189,7 +189,7 @@ void DivPlatformVRC6::tick(bool sysTick) {
|
|||
if (chan[i].std.pitch.had) {
|
||||
if (chan[i].std.pitch.mode) {
|
||||
chan[i].pitch2+=chan[i].std.pitch.val;
|
||||
CLAMP_VAR(chan[i].pitch2,-2048,2048);
|
||||
CLAMP_VAR(chan[i].pitch2,-32768,32767);
|
||||
} else {
|
||||
chan[i].pitch2=chan[i].std.pitch.val;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -390,7 +390,7 @@ void DivPlatformX1_010::tick(bool sysTick) {
|
|||
if (chan[i].std.pitch.had) {
|
||||
if (chan[i].std.pitch.mode) {
|
||||
chan[i].pitch2+=chan[i].std.pitch.val;
|
||||
CLAMP_VAR(chan[i].pitch2,-2048,2048);
|
||||
CLAMP_VAR(chan[i].pitch2,-32768,32767);
|
||||
} else {
|
||||
chan[i].pitch2=chan[i].std.pitch.val;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -373,7 +373,7 @@ void DivPlatformYM2203::tick(bool sysTick) {
|
|||
if (chan[i].std.pitch.had) {
|
||||
if (chan[i].std.pitch.mode) {
|
||||
chan[i].pitch2+=chan[i].std.pitch.val;
|
||||
CLAMP_VAR(chan[i].pitch2,-2048,2048);
|
||||
CLAMP_VAR(chan[i].pitch2,-32768,32767);
|
||||
} else {
|
||||
chan[i].pitch2=chan[i].std.pitch.val;
|
||||
}
|
||||
|
|
@ -381,7 +381,7 @@ void DivPlatformYM2203::tick(bool sysTick) {
|
|||
}
|
||||
|
||||
if (chan[i].std.phaseReset.had) {
|
||||
if (chan[i].std.phaseReset.val==1) {
|
||||
if (chan[i].std.phaseReset.val==1 && chan[i].active) {
|
||||
chan[i].keyOn=true;
|
||||
}
|
||||
}
|
||||
|
|
@ -659,48 +659,7 @@ int DivPlatformYM2203::dispatch(DivCommand c) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
int boundaryBottom=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,0,false);
|
||||
int boundaryTop=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,12,false);
|
||||
int destFreq=NOTE_FNUM_BLOCK(c.value2,11);
|
||||
int newFreq;
|
||||
bool return2=false;
|
||||
if (chan[c.chan].portaPause) {
|
||||
chan[c.chan].baseFreq=chan[c.chan].portaPauseFreq;
|
||||
}
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
newFreq=chan[c.chan].baseFreq+c.value;
|
||||
if (newFreq>=destFreq) {
|
||||
newFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
} else {
|
||||
newFreq=chan[c.chan].baseFreq-c.value;
|
||||
if (newFreq<=destFreq) {
|
||||
newFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
}
|
||||
// check for octave boundary
|
||||
// what the heck!
|
||||
if (!chan[c.chan].portaPause) {
|
||||
if ((newFreq&0x7ff)>boundaryTop && (newFreq&0xf800)<0x3800) {
|
||||
chan[c.chan].portaPauseFreq=(boundaryBottom)|((newFreq+0x800)&0xf800);
|
||||
chan[c.chan].portaPause=true;
|
||||
break;
|
||||
}
|
||||
if ((newFreq&0x7ff)<boundaryBottom && (newFreq&0xf800)>0) {
|
||||
chan[c.chan].portaPauseFreq=newFreq=(boundaryTop-1)|((newFreq-0x800)&0xf800);
|
||||
chan[c.chan].portaPause=true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
chan[c.chan].portaPause=false;
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].baseFreq=newFreq;
|
||||
if (return2) {
|
||||
chan[c.chan].inPorta=false;
|
||||
return 2;
|
||||
}
|
||||
PLEASE_HELP_ME(chan[c.chan]);
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_LEGATO: {
|
||||
|
|
|
|||
|
|
@ -143,52 +143,7 @@ int DivPlatformYM2203Ext::dispatch(DivCommand c) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
int boundaryBottom=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,0,false);
|
||||
int boundaryTop=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,12,false);
|
||||
int destFreq=NOTE_FNUM_BLOCK(c.value2,11);
|
||||
int newFreq;
|
||||
bool return2=false;
|
||||
if (opChan[ch].portaPause) {
|
||||
opChan[ch].baseFreq=opChan[ch].portaPauseFreq;
|
||||
}
|
||||
if (destFreq>opChan[ch].baseFreq) {
|
||||
newFreq=opChan[ch].baseFreq+c.value;
|
||||
if (newFreq>=destFreq) {
|
||||
newFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
} else {
|
||||
newFreq=opChan[ch].baseFreq-c.value;
|
||||
if (newFreq<=destFreq) {
|
||||
newFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
}
|
||||
// what the heck!
|
||||
if (!opChan[ch].portaPause) {
|
||||
if ((newFreq&0x7ff)>boundaryTop && (newFreq&0xf800)<0x3800) {
|
||||
if (parent->song.fbPortaPause) {
|
||||
opChan[ch].portaPauseFreq=(boundaryBottom)|((newFreq+0x800)&0xf800);
|
||||
opChan[ch].portaPause=true;
|
||||
break;
|
||||
} else {
|
||||
newFreq=(newFreq>>1)|((newFreq+0x800)&0xf800);
|
||||
}
|
||||
}
|
||||
if ((newFreq&0x7ff)<boundaryBottom && (newFreq&0xf800)>0) {
|
||||
if (parent->song.fbPortaPause) {
|
||||
opChan[ch].portaPauseFreq=newFreq=(boundaryTop-1)|((newFreq-0x800)&0xf800);
|
||||
opChan[ch].portaPause=true;
|
||||
break;
|
||||
} else {
|
||||
newFreq=(newFreq<<1)|((newFreq-0x800)&0xf800);
|
||||
}
|
||||
}
|
||||
}
|
||||
opChan[ch].portaPause=false;
|
||||
opChan[ch].freqChanged=true;
|
||||
opChan[ch].baseFreq=newFreq;
|
||||
if (return2) return 2;
|
||||
PLEASE_HELP_ME(opChan[ch]);
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_LEGATO: {
|
||||
|
|
|
|||
|
|
@ -27,10 +27,12 @@ class DivPlatformYM2203Ext: public DivPlatformYM2203 {
|
|||
unsigned char freqH, freqL;
|
||||
int freq, baseFreq, pitch, pitch2, portaPauseFreq, ins;
|
||||
signed char konCycles;
|
||||
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause;
|
||||
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta;
|
||||
int vol;
|
||||
unsigned char pan;
|
||||
OpChannel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), pitch2(0), portaPauseFreq(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), vol(0), pan(3) {}
|
||||
// UGLY
|
||||
OpChannel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), pitch2(0), portaPauseFreq(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false),
|
||||
inPorta(false), vol(0), pan(3) {}
|
||||
};
|
||||
OpChannel opChan[4];
|
||||
bool isOpMuted[4];
|
||||
|
|
|
|||
|
|
@ -536,7 +536,7 @@ void DivPlatformYM2608::tick(bool sysTick) {
|
|||
if (chan[i].std.pitch.had) {
|
||||
if (chan[i].std.pitch.mode) {
|
||||
chan[i].pitch2+=chan[i].std.pitch.val;
|
||||
CLAMP_VAR(chan[i].pitch2,-2048,2048);
|
||||
CLAMP_VAR(chan[i].pitch2,-32768,32767);
|
||||
} else {
|
||||
chan[i].pitch2=chan[i].std.pitch.val;
|
||||
}
|
||||
|
|
@ -544,7 +544,7 @@ void DivPlatformYM2608::tick(bool sysTick) {
|
|||
}
|
||||
|
||||
if (chan[i].std.phaseReset.had) {
|
||||
if (chan[i].std.phaseReset.val==1) {
|
||||
if (chan[i].std.phaseReset.val==1 && chan[i].active) {
|
||||
chan[i].keyOn=true;
|
||||
}
|
||||
}
|
||||
|
|
@ -980,48 +980,7 @@ int DivPlatformYM2608::dispatch(DivCommand c) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
int boundaryBottom=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,0,false);
|
||||
int boundaryTop=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,12,false);
|
||||
int destFreq=NOTE_FNUM_BLOCK(c.value2,11);
|
||||
int newFreq;
|
||||
bool return2=false;
|
||||
if (chan[c.chan].portaPause) {
|
||||
chan[c.chan].baseFreq=chan[c.chan].portaPauseFreq;
|
||||
}
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
newFreq=chan[c.chan].baseFreq+c.value;
|
||||
if (newFreq>=destFreq) {
|
||||
newFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
} else {
|
||||
newFreq=chan[c.chan].baseFreq-c.value;
|
||||
if (newFreq<=destFreq) {
|
||||
newFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
}
|
||||
// check for octave boundary
|
||||
// what the heck!
|
||||
if (!chan[c.chan].portaPause) {
|
||||
if ((newFreq&0x7ff)>boundaryTop && (newFreq&0xf800)<0x3800) {
|
||||
chan[c.chan].portaPauseFreq=(boundaryBottom)|((newFreq+0x800)&0xf800);
|
||||
chan[c.chan].portaPause=true;
|
||||
break;
|
||||
}
|
||||
if ((newFreq&0x7ff)<boundaryBottom && (newFreq&0xf800)>0) {
|
||||
chan[c.chan].portaPauseFreq=newFreq=(boundaryTop-1)|((newFreq-0x800)&0xf800);
|
||||
chan[c.chan].portaPause=true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
chan[c.chan].portaPause=false;
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].baseFreq=newFreq;
|
||||
if (return2) {
|
||||
chan[c.chan].inPorta=false;
|
||||
return 2;
|
||||
}
|
||||
PLEASE_HELP_ME(chan[c.chan]);
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_SAMPLE_BANK:
|
||||
|
|
|
|||
|
|
@ -143,52 +143,7 @@ int DivPlatformYM2608Ext::dispatch(DivCommand c) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
int boundaryBottom=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,0,false);
|
||||
int boundaryTop=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,12,false);
|
||||
int destFreq=NOTE_FNUM_BLOCK(c.value2,11);
|
||||
int newFreq;
|
||||
bool return2=false;
|
||||
if (opChan[ch].portaPause) {
|
||||
opChan[ch].baseFreq=opChan[ch].portaPauseFreq;
|
||||
}
|
||||
if (destFreq>opChan[ch].baseFreq) {
|
||||
newFreq=opChan[ch].baseFreq+c.value;
|
||||
if (newFreq>=destFreq) {
|
||||
newFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
} else {
|
||||
newFreq=opChan[ch].baseFreq-c.value;
|
||||
if (newFreq<=destFreq) {
|
||||
newFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
}
|
||||
// what the heck!
|
||||
if (!opChan[ch].portaPause) {
|
||||
if ((newFreq&0x7ff)>boundaryTop && (newFreq&0xf800)<0x3800) {
|
||||
if (parent->song.fbPortaPause) {
|
||||
opChan[ch].portaPauseFreq=(boundaryBottom)|((newFreq+0x800)&0xf800);
|
||||
opChan[ch].portaPause=true;
|
||||
break;
|
||||
} else {
|
||||
newFreq=(newFreq>>1)|((newFreq+0x800)&0xf800);
|
||||
}
|
||||
}
|
||||
if ((newFreq&0x7ff)<boundaryBottom && (newFreq&0xf800)>0) {
|
||||
if (parent->song.fbPortaPause) {
|
||||
opChan[ch].portaPauseFreq=newFreq=(boundaryTop-1)|((newFreq-0x800)&0xf800);
|
||||
opChan[ch].portaPause=true;
|
||||
break;
|
||||
} else {
|
||||
newFreq=(newFreq<<1)|((newFreq-0x800)&0xf800);
|
||||
}
|
||||
}
|
||||
}
|
||||
opChan[ch].portaPause=false;
|
||||
opChan[ch].freqChanged=true;
|
||||
opChan[ch].baseFreq=newFreq;
|
||||
if (return2) return 2;
|
||||
PLEASE_HELP_ME(opChan[ch]);
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_LEGATO: {
|
||||
|
|
|
|||
|
|
@ -27,10 +27,12 @@ class DivPlatformYM2608Ext: public DivPlatformYM2608 {
|
|||
unsigned char freqH, freqL;
|
||||
int freq, baseFreq, pitch, pitch2, portaPauseFreq, ins;
|
||||
signed char konCycles;
|
||||
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause;
|
||||
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta;
|
||||
int vol;
|
||||
unsigned char pan;
|
||||
OpChannel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), pitch2(0), portaPauseFreq(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), vol(0), pan(3) {}
|
||||
// UGLY
|
||||
OpChannel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), pitch2(0), portaPauseFreq(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false),
|
||||
inPorta(false), vol(0), pan(3) {}
|
||||
};
|
||||
OpChannel opChan[4];
|
||||
bool isOpMuted[4];
|
||||
|
|
|
|||
|
|
@ -580,7 +580,7 @@ void DivPlatformYM2610::tick(bool sysTick) {
|
|||
if (chan[i].std.pitch.had) {
|
||||
if (chan[i].std.pitch.mode) {
|
||||
chan[i].pitch2+=chan[i].std.pitch.val;
|
||||
CLAMP_VAR(chan[i].pitch2,-2048,2048);
|
||||
CLAMP_VAR(chan[i].pitch2,-32768,32767);
|
||||
} else {
|
||||
chan[i].pitch2=chan[i].std.pitch.val;
|
||||
}
|
||||
|
|
@ -588,7 +588,7 @@ void DivPlatformYM2610::tick(bool sysTick) {
|
|||
}
|
||||
|
||||
if (chan[i].std.phaseReset.had) {
|
||||
if (chan[i].std.phaseReset.val==1) {
|
||||
if (chan[i].std.phaseReset.val==1 && chan[i].active) {
|
||||
chan[i].keyOn=true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1027,48 +1027,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
int boundaryBottom=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,0,false);
|
||||
int boundaryTop=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,12,false);
|
||||
int destFreq=NOTE_FNUM_BLOCK(c.value2,11);
|
||||
int newFreq;
|
||||
bool return2=false;
|
||||
if (chan[c.chan].portaPause) {
|
||||
chan[c.chan].baseFreq=chan[c.chan].portaPauseFreq;
|
||||
}
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
newFreq=chan[c.chan].baseFreq+c.value;
|
||||
if (newFreq>=destFreq) {
|
||||
newFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
} else {
|
||||
newFreq=chan[c.chan].baseFreq-c.value;
|
||||
if (newFreq<=destFreq) {
|
||||
newFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
}
|
||||
// check for octave boundary
|
||||
// what the heck!
|
||||
if (!chan[c.chan].portaPause) {
|
||||
if ((newFreq&0x7ff)>boundaryTop && (newFreq&0xf800)<0x3800) {
|
||||
chan[c.chan].portaPauseFreq=(boundaryBottom)|((newFreq+0x800)&0xf800);
|
||||
chan[c.chan].portaPause=true;
|
||||
break;
|
||||
}
|
||||
if ((newFreq&0x7ff)<boundaryBottom && (newFreq&0xf800)>0) {
|
||||
chan[c.chan].portaPauseFreq=newFreq=(boundaryTop-1)|((newFreq-0x800)&0xf800);
|
||||
chan[c.chan].portaPause=true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
chan[c.chan].portaPause=false;
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].baseFreq=newFreq;
|
||||
if (return2) {
|
||||
chan[c.chan].inPorta=false;
|
||||
return 2;
|
||||
}
|
||||
PLEASE_HELP_ME(chan[c.chan]);
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_SAMPLE_BANK:
|
||||
|
|
|
|||
|
|
@ -559,7 +559,7 @@ void DivPlatformYM2610B::tick(bool sysTick) {
|
|||
if (chan[i].std.pitch.had) {
|
||||
if (chan[i].std.pitch.mode) {
|
||||
chan[i].pitch2+=chan[i].std.pitch.val;
|
||||
CLAMP_VAR(chan[i].pitch2,-2048,2048);
|
||||
CLAMP_VAR(chan[i].pitch2,-32768,32767);
|
||||
} else {
|
||||
chan[i].pitch2=chan[i].std.pitch.val;
|
||||
}
|
||||
|
|
@ -567,7 +567,7 @@ void DivPlatformYM2610B::tick(bool sysTick) {
|
|||
}
|
||||
|
||||
if (chan[i].std.phaseReset.had) {
|
||||
if (chan[i].std.phaseReset.val==1) {
|
||||
if (chan[i].std.phaseReset.val==1 && chan[i].active) {
|
||||
chan[i].keyOn=true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1005,48 +1005,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
int boundaryBottom=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,0,false);
|
||||
int boundaryTop=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,12,false);
|
||||
int destFreq=NOTE_FNUM_BLOCK(c.value2,11);
|
||||
int newFreq;
|
||||
bool return2=false;
|
||||
if (chan[c.chan].portaPause) {
|
||||
chan[c.chan].baseFreq=chan[c.chan].portaPauseFreq;
|
||||
}
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
newFreq=chan[c.chan].baseFreq+c.value;
|
||||
if (newFreq>=destFreq) {
|
||||
newFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
} else {
|
||||
newFreq=chan[c.chan].baseFreq-c.value;
|
||||
if (newFreq<=destFreq) {
|
||||
newFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
}
|
||||
// check for octave boundary
|
||||
// what the heck!
|
||||
if (!chan[c.chan].portaPause) {
|
||||
if ((newFreq&0x7ff)>boundaryTop && (newFreq&0xf800)<0x3800) {
|
||||
chan[c.chan].portaPauseFreq=(boundaryBottom)|((newFreq+0x800)&0xf800);
|
||||
chan[c.chan].portaPause=true;
|
||||
break;
|
||||
}
|
||||
if ((newFreq&0x7ff)<boundaryBottom && (newFreq&0xf800)>0) {
|
||||
chan[c.chan].portaPauseFreq=newFreq=(boundaryTop-1)|((newFreq-0x800)&0xf800);
|
||||
chan[c.chan].portaPause=true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
chan[c.chan].portaPause=false;
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].baseFreq=newFreq;
|
||||
if (return2) {
|
||||
chan[c.chan].inPorta=false;
|
||||
return 2;
|
||||
}
|
||||
PLEASE_HELP_ME(chan[c.chan]);
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_SAMPLE_BANK:
|
||||
|
|
|
|||
|
|
@ -143,52 +143,7 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
int boundaryBottom=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,0,false);
|
||||
int boundaryTop=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,12,false);
|
||||
int destFreq=NOTE_FNUM_BLOCK(c.value2,11);
|
||||
int newFreq;
|
||||
bool return2=false;
|
||||
if (opChan[ch].portaPause) {
|
||||
opChan[ch].baseFreq=opChan[ch].portaPauseFreq;
|
||||
}
|
||||
if (destFreq>opChan[ch].baseFreq) {
|
||||
newFreq=opChan[ch].baseFreq+c.value;
|
||||
if (newFreq>=destFreq) {
|
||||
newFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
} else {
|
||||
newFreq=opChan[ch].baseFreq-c.value;
|
||||
if (newFreq<=destFreq) {
|
||||
newFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
}
|
||||
// what the heck!
|
||||
if (!opChan[ch].portaPause) {
|
||||
if ((newFreq&0x7ff)>boundaryTop && (newFreq&0xf800)<0x3800) {
|
||||
if (parent->song.fbPortaPause) {
|
||||
opChan[ch].portaPauseFreq=(boundaryBottom)|((newFreq+0x800)&0xf800);
|
||||
opChan[ch].portaPause=true;
|
||||
break;
|
||||
} else {
|
||||
newFreq=(newFreq>>1)|((newFreq+0x800)&0xf800);
|
||||
}
|
||||
}
|
||||
if ((newFreq&0x7ff)<boundaryBottom && (newFreq&0xf800)>0) {
|
||||
if (parent->song.fbPortaPause) {
|
||||
opChan[ch].portaPauseFreq=newFreq=(boundaryTop-1)|((newFreq-0x800)&0xf800);
|
||||
opChan[ch].portaPause=true;
|
||||
break;
|
||||
} else {
|
||||
newFreq=(newFreq<<1)|((newFreq-0x800)&0xf800);
|
||||
}
|
||||
}
|
||||
}
|
||||
opChan[ch].portaPause=false;
|
||||
opChan[ch].freqChanged=true;
|
||||
opChan[ch].baseFreq=newFreq;
|
||||
if (return2) return 2;
|
||||
PLEASE_HELP_ME(opChan[ch]);
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_LEGATO: {
|
||||
|
|
|
|||
|
|
@ -27,10 +27,12 @@ class DivPlatformYM2610BExt: public DivPlatformYM2610B {
|
|||
unsigned char freqH, freqL;
|
||||
int freq, baseFreq, pitch, pitch2, portaPauseFreq, ins;
|
||||
signed char konCycles;
|
||||
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause;
|
||||
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta;
|
||||
int vol;
|
||||
unsigned char pan;
|
||||
OpChannel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), pitch2(0), portaPauseFreq(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), vol(0), pan(3) {}
|
||||
// UGLY
|
||||
OpChannel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), pitch2(0), portaPauseFreq(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false),
|
||||
inPorta(false), vol(0), pan(3) {}
|
||||
};
|
||||
OpChannel opChan[4];
|
||||
bool isOpMuted[4];
|
||||
|
|
|
|||
|
|
@ -143,52 +143,7 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
int boundaryBottom=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,0,false);
|
||||
int boundaryTop=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,12,false);
|
||||
int destFreq=NOTE_FNUM_BLOCK(c.value2,11);
|
||||
int newFreq;
|
||||
bool return2=false;
|
||||
if (opChan[ch].portaPause) {
|
||||
opChan[ch].baseFreq=opChan[ch].portaPauseFreq;
|
||||
}
|
||||
if (destFreq>opChan[ch].baseFreq) {
|
||||
newFreq=opChan[ch].baseFreq+c.value;
|
||||
if (newFreq>=destFreq) {
|
||||
newFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
} else {
|
||||
newFreq=opChan[ch].baseFreq-c.value;
|
||||
if (newFreq<=destFreq) {
|
||||
newFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
}
|
||||
// what the heck!
|
||||
if (!opChan[ch].portaPause) {
|
||||
if ((newFreq&0x7ff)>boundaryTop && (newFreq&0xf800)<0x3800) {
|
||||
if (parent->song.fbPortaPause) {
|
||||
opChan[ch].portaPauseFreq=(boundaryBottom)|((newFreq+0x800)&0xf800);
|
||||
opChan[ch].portaPause=true;
|
||||
break;
|
||||
} else {
|
||||
newFreq=(newFreq>>1)|((newFreq+0x800)&0xf800);
|
||||
}
|
||||
}
|
||||
if ((newFreq&0x7ff)<boundaryBottom && (newFreq&0xf800)>0) {
|
||||
if (parent->song.fbPortaPause) {
|
||||
opChan[ch].portaPauseFreq=newFreq=(boundaryTop-1)|((newFreq-0x800)&0xf800);
|
||||
opChan[ch].portaPause=true;
|
||||
break;
|
||||
} else {
|
||||
newFreq=(newFreq<<1)|((newFreq-0x800)&0xf800);
|
||||
}
|
||||
}
|
||||
}
|
||||
opChan[ch].portaPause=false;
|
||||
opChan[ch].freqChanged=true;
|
||||
opChan[ch].baseFreq=newFreq;
|
||||
if (return2) return 2;
|
||||
PLEASE_HELP_ME(opChan[ch]);
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_LEGATO: {
|
||||
|
|
|
|||
|
|
@ -27,10 +27,12 @@ class DivPlatformYM2610Ext: public DivPlatformYM2610 {
|
|||
unsigned char freqH, freqL;
|
||||
int freq, baseFreq, pitch, pitch2, portaPauseFreq, ins;
|
||||
signed char konCycles;
|
||||
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause;
|
||||
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta;
|
||||
int vol;
|
||||
unsigned char pan;
|
||||
OpChannel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), pitch2(0), portaPauseFreq(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), vol(0), pan(3) {}
|
||||
// UGLY
|
||||
OpChannel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), pitch2(0), portaPauseFreq(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false),
|
||||
inPorta(false), vol(0), pan(3) {}
|
||||
};
|
||||
OpChannel opChan[4];
|
||||
bool isOpMuted[4];
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ void DivPlatformYMZ280B::tick(bool sysTick) {
|
|||
if (chan[i].std.pitch.had) {
|
||||
if (chan[i].std.pitch.mode) {
|
||||
chan[i].pitch2+=chan[i].std.pitch.val;
|
||||
CLAMP_VAR(chan[i].pitch2,-2048,2048);
|
||||
CLAMP_VAR(chan[i].pitch2,-32768,32767);
|
||||
} else {
|
||||
chan[i].pitch2=chan[i].std.pitch.val;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ void DivPlatformZXBeeper::tick(bool sysTick) {
|
|||
if (chan[i].std.pitch.had) {
|
||||
if (chan[i].std.pitch.mode) {
|
||||
chan[i].pitch2+=chan[i].std.pitch.val;
|
||||
CLAMP_VAR(chan[i].pitch2,-2048,2048);
|
||||
CLAMP_VAR(chan[i].pitch2,-32768,32767);
|
||||
} else {
|
||||
chan[i].pitch2=chan[i].std.pitch.val;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue