Sample playable, but still incomplete
This commit is contained in:
parent
c2bbe0a98a
commit
6a5a58ad13
|
@ -947,10 +947,95 @@ double DivPlatformOPL::NOTE_ADPCMB(int note) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double DivPlatformOPL::NOTE_OPL4(int ch, int note) { // TODO
|
||||||
|
if (pcmChanOffs<0) return 0;
|
||||||
|
if (chan[ch].sample>=0 && chan[ch].sample<parent->song.sampleLen) {
|
||||||
|
double off=65535.0*(double)(parent->getSample(chan[ch].sample)->centerRate)/8363.0;
|
||||||
|
return parent->calcBaseFreq((double)chipClock/768,off,note,false);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
void DivPlatformOPL::tick(bool sysTick) {
|
void DivPlatformOPL::tick(bool sysTick) {
|
||||||
for (int i=0; i<totalChans; i++) {
|
for (int i=0; i<totalChans; i++) {
|
||||||
if (i>=pcmChanOffs) { // OPL4 PCM
|
if (i>=pcmChanOffs) { // OPL4 PCM
|
||||||
chan[i].std.next();
|
chan[i].std.next();
|
||||||
|
if (chan[i].std.vol.had) {
|
||||||
|
chan[i].outVol=VOL_SCALE_LOG((chan[i].vol&0x7f),(0x7f*chan[i].std.vol.val)/chan[i].macroVolMul,0x7f);
|
||||||
|
immWrite(PCM_ADDR_TL+(i-pcmChanOffs),((0x7f-chan[i].outVol)<<1)|(chan[i].levelDirect?1:0));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
||||||
|
DivSample* s=parent->getSample(chan[i].sample);
|
||||||
|
unsigned char ctrl=0;
|
||||||
|
double off=(s->centerRate>=1)?((double)s->centerRate/8363.0):1.0;
|
||||||
|
chan[i].freq=(int)(off*parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,false,2,chan[i].pitch2,chipClock,(524288*768)));
|
||||||
|
if (chan[i].freq<0x400) chan[i].freq=0x400;
|
||||||
|
if (chan[i].freq>0x4000000) chan[i].freq=0x4000000;
|
||||||
|
if (chan[i].freq>=0x2000000) {
|
||||||
|
chan[i].freqH=15;
|
||||||
|
} else if (chan[i].freq>=0x1000000) {
|
||||||
|
chan[i].freqH=14;
|
||||||
|
} else if (chan[i].freq>=0x800000) {
|
||||||
|
chan[i].freqH=13;
|
||||||
|
} else if (chan[i].freq>=0x400000) {
|
||||||
|
chan[i].freqH=12;
|
||||||
|
} else if (chan[i].freq>=0x200000) {
|
||||||
|
chan[i].freqH=11;
|
||||||
|
} else if (chan[i].freq>=0x100000) {
|
||||||
|
chan[i].freqH=10;
|
||||||
|
} else if (chan[i].freq>=0x80000) {
|
||||||
|
chan[i].freqH=9;
|
||||||
|
} else if (chan[i].freq>=0x40000) {
|
||||||
|
chan[i].freqH=8;
|
||||||
|
} else if (chan[i].freq>=0x20000) {
|
||||||
|
chan[i].freqH=7;
|
||||||
|
} else if (chan[i].freq>=0x10000) {
|
||||||
|
chan[i].freqH=6;
|
||||||
|
} else if (chan[i].freq>=0x8000) {
|
||||||
|
chan[i].freqH=5;
|
||||||
|
} else if (chan[i].freq>=0x4000) {
|
||||||
|
chan[i].freqH=4;
|
||||||
|
} else if (chan[i].freq>=0x2000) {
|
||||||
|
chan[i].freqH=3;
|
||||||
|
} else if (chan[i].freq>=0x1000) {
|
||||||
|
chan[i].freqH=2;
|
||||||
|
} else if (chan[i].freq>=0x800) {
|
||||||
|
chan[i].freqH=1;
|
||||||
|
} else {
|
||||||
|
chan[i].freqH=0;
|
||||||
|
}
|
||||||
|
chan[i].freqL=(chan[i].freq>>chan[i].freqH)&0x3ff;
|
||||||
|
chan[i].freqH=8^chan[i].freqH;
|
||||||
|
ctrl|=(chan[i].active?0x80:0)|(chan[i].damp?0x40:0)|(isMuted[i]?8:(chan[i].pan&0xf));
|
||||||
|
unsigned int waveNum=chan[i].sample;
|
||||||
|
if (ramSize<=0x200000) {
|
||||||
|
waveNum=MIN(waveNum,0x7f)|0x180;
|
||||||
|
}
|
||||||
|
if (chan[i].keyOn) {
|
||||||
|
rWrite(PCM_ADDR_KEY_DAMP_LFORST_CH_PAN+(i-pcmChanOffs),ctrl&~0x80); // force keyoff first
|
||||||
|
rWrite(PCM_ADDR_WAVE_H_FN_L+(i-pcmChanOffs),((chan[i].freqL&0x7f)<<1)|((waveNum>>8)&1));
|
||||||
|
rWrite(PCM_ADDR_WAVE_L+(i-pcmChanOffs),waveNum&0xff);
|
||||||
|
if (!chan[i].std.vol.had) {
|
||||||
|
chan[i].outVol=chan[i].vol;
|
||||||
|
}
|
||||||
|
chan[i].writeCtrl=true;
|
||||||
|
chan[i].keyOn=false;
|
||||||
|
}
|
||||||
|
if (chan[i].keyOff) {
|
||||||
|
chan[i].writeCtrl=true;
|
||||||
|
chan[i].keyOff=false;
|
||||||
|
}
|
||||||
|
if (chan[i].freqChanged) {
|
||||||
|
rWrite(PCM_ADDR_WAVE_H_FN_L+(i-pcmChanOffs),((chan[i].freqL&0x7f)<<1)|((waveNum>>8)&1));
|
||||||
|
rWrite(PCM_ADDR_FN_H_PR_OCT+(i-pcmChanOffs),((chan[i].freqH&0xf)<<4)|(chan[i].pseudoReverb?0x08:0x00)|((chan[i].freqL>>7)&0x7));
|
||||||
|
chan[i].freqChanged=false;
|
||||||
|
}
|
||||||
|
if (chan[i].writeCtrl) {
|
||||||
|
rWrite(PCM_ADDR_KEY_DAMP_LFORST_CH_PAN+(i-pcmChanOffs),ctrl);
|
||||||
|
chan[i].writeCtrl=false;
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
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].std.next();
|
chan[i].std.next();
|
||||||
|
@ -1341,6 +1426,11 @@ int DivPlatformOPL::toFreq(int freq) {
|
||||||
|
|
||||||
void DivPlatformOPL::muteChannel(int ch, bool mute) {
|
void DivPlatformOPL::muteChannel(int ch, bool mute) {
|
||||||
isMuted[ch]=mute;
|
isMuted[ch]=mute;
|
||||||
|
if (ch>=pcmChanOffs) {
|
||||||
|
chan[ch].freqChanged=true;
|
||||||
|
chan[ch].writeCtrl=true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (ch==adpcmChan) {
|
if (ch==adpcmChan) {
|
||||||
immWrite(18,(isMuted[adpcmChan]?0:chan[adpcmChan].outVol));
|
immWrite(18,(isMuted[adpcmChan]?0:chan[adpcmChan].outVol));
|
||||||
return;
|
return;
|
||||||
|
@ -1494,7 +1584,31 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
||||||
switch (c.cmd) {
|
switch (c.cmd) {
|
||||||
case DIV_CMD_NOTE_ON: {
|
case DIV_CMD_NOTE_ON: {
|
||||||
if (c.chan>=pcmChanOffs) { // OPL4 PCM
|
if (c.chan>=pcmChanOffs) { // OPL4 PCM
|
||||||
|
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
|
||||||
|
chan[c.chan].macroVolMul=ins->type==DIV_INS_AMIGA?64:127;
|
||||||
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
chan[c.chan].sample=ins->amiga.getSample(c.value);
|
||||||
|
chan[c.chan].sampleNote=c.value;
|
||||||
|
c.value=ins->amiga.getFreq(c.value);
|
||||||
|
chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote;
|
||||||
|
}
|
||||||
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);
|
||||||
|
}
|
||||||
|
if (chan[c.chan].sample<0 || chan[c.chan].sample>=parent->song.sampleLen) {
|
||||||
|
chan[c.chan].sample=-1;
|
||||||
|
}
|
||||||
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
chan[c.chan].freqChanged=true;
|
||||||
|
chan[c.chan].note=c.value;
|
||||||
|
}
|
||||||
|
chan[c.chan].active=true;
|
||||||
|
chan[c.chan].keyOn=true;
|
||||||
|
chan[c.chan].macroInit(ins);
|
||||||
|
if (!chan[c.chan].std.vol.will) {
|
||||||
|
chan[c.chan].outVol=chan[c.chan].vol;
|
||||||
|
}
|
||||||
|
break;
|
||||||
} else if (c.chan==adpcmChan) { // ADPCM
|
} else if (c.chan==adpcmChan) { // ADPCM
|
||||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM);
|
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM);
|
||||||
chan[c.chan].macroVolMul=ins->type==DIV_INS_AMIGA?64:255;
|
chan[c.chan].macroVolMul=ins->type==DIV_INS_AMIGA?64:255;
|
||||||
|
@ -1619,6 +1733,10 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
||||||
chan[c.chan].keyOff=true;
|
chan[c.chan].keyOff=true;
|
||||||
chan[c.chan].keyOn=false;
|
chan[c.chan].keyOn=false;
|
||||||
chan[c.chan].active=false;
|
chan[c.chan].active=false;
|
||||||
|
if (c.chan>=pcmChanOffs) {
|
||||||
|
chan[c.chan].sample=-1;
|
||||||
|
chan[c.chan].macroInit(NULL);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case DIV_CMD_NOTE_OFF_ENV:
|
case DIV_CMD_NOTE_OFF_ENV:
|
||||||
chan[c.chan].keyOff=true;
|
chan[c.chan].keyOff=true;
|
||||||
|
@ -1639,6 +1757,10 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
||||||
if (!chan[c.chan].std.vol.has) {
|
if (!chan[c.chan].std.vol.has) {
|
||||||
chan[c.chan].outVol=c.value;
|
chan[c.chan].outVol=c.value;
|
||||||
}
|
}
|
||||||
|
if (c.chan>=pcmChanOffs) { // OPL4 PCM
|
||||||
|
immWrite(PCM_ADDR_TL+(c.chan-pcmChanOffs),((0x7f-chan[c.chan].outVol)<<1)|(chan[c.chan].levelDirect?1:0));
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (c.chan==adpcmChan) { // ADPCM-B
|
if (c.chan==adpcmChan) { // ADPCM-B
|
||||||
immWrite(18,(isMuted[adpcmChan]?0:chan[adpcmChan].outVol));
|
immWrite(18,(isMuted[adpcmChan]?0:chan[adpcmChan].outVol));
|
||||||
break;
|
break;
|
||||||
|
@ -2275,6 +2397,7 @@ DivDispatchOscBuffer* DivPlatformOPL::getOscBuffer(int ch) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int DivPlatformOPL::mapVelocity(int ch, float vel) {
|
int DivPlatformOPL::mapVelocity(int ch, float vel) {
|
||||||
|
if (ch>=pcmChanOffs) return vel*127.0;
|
||||||
if (ch==adpcmChan) return vel*255.0;
|
if (ch==adpcmChan) return vel*255.0;
|
||||||
// -0.75dB per step
|
// -0.75dB per step
|
||||||
// -6: 64: 8
|
// -6: 64: 8
|
||||||
|
@ -2389,8 +2512,9 @@ void DivPlatformOPL::reset() {
|
||||||
for (int i=0; i<totalChans; i++) {
|
for (int i=0; i<totalChans; i++) {
|
||||||
chan[i]=DivPlatformOPL::Channel();
|
chan[i]=DivPlatformOPL::Channel();
|
||||||
chan[i].std.setEngine(parent);
|
chan[i].std.setEngine(parent);
|
||||||
chan[i].vol=0x3f;
|
chan[i].vol=(i>=pcmChanOffs)?0x7f:0x3f;
|
||||||
chan[i].outVol=0x3f;
|
chan[i].outVol=(i>=pcmChanOffs)?0x7f:0x3f;
|
||||||
|
chan[i].pan=(i>=pcmChanOffs)?0:3;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (adpcmChan>=0) {
|
if (adpcmChan>=0) {
|
||||||
|
@ -2746,8 +2870,9 @@ void DivPlatformOPL::renderSamples(int sysID) {
|
||||||
|
|
||||||
if (pcmChanOffs>=0) { // OPL4 PCM
|
if (pcmChanOffs>=0) { // OPL4 PCM
|
||||||
size_t memPos=((ramSize<=0x200000)?0x200600:0x1800);
|
size_t memPos=((ramSize<=0x200000)?0x200600:0x1800);
|
||||||
|
const int maxSample=(ramSize<=0x200000)?127:511;
|
||||||
int sampleCount=parent->song.sampleLen;
|
int sampleCount=parent->song.sampleLen;
|
||||||
if (sampleCount>511) sampleCount=511;
|
if (sampleCount>maxSample) sampleCount=maxSample;
|
||||||
for (int i=0; i<sampleCount; i++) {
|
for (int i=0; i<sampleCount; i++) {
|
||||||
DivSample* s=parent->song.sample[i];
|
DivSample* s=parent->song.sample[i];
|
||||||
if (!s->renderOn[0][sysID]) {
|
if (!s->renderOn[0][sysID]) {
|
||||||
|
@ -2795,8 +2920,8 @@ void DivPlatformOPL::renderSamples(int sysID) {
|
||||||
DivSample* s=parent->song.sample[i];
|
DivSample* s=parent->song.sample[i];
|
||||||
unsigned int insAddr=(i*12)+((ramSize<=0x200000)?0x200000:0);
|
unsigned int insAddr=(i*12)+((ramSize<=0x200000)?0x200000:0);
|
||||||
unsigned char bitDepth;
|
unsigned char bitDepth;
|
||||||
int loop=CLAMP(s->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT),0,0xffff);
|
int endPos=CLAMP(s->loopEnd,1,0x10000);
|
||||||
int endPos=CLAMP(s->getLoopEndPosition(DIV_SAMPLE_DEPTH_8BIT),1,0x10000);
|
int loop=s->isLoopable()?CLAMP(s->loopStart,0,endPos-1):(endPos-1);
|
||||||
switch (s->depth) {
|
switch (s->depth) {
|
||||||
default:
|
default:
|
||||||
case DIV_SAMPLE_DEPTH_8BIT:
|
case DIV_SAMPLE_DEPTH_8BIT:
|
||||||
|
@ -2811,13 +2936,13 @@ void DivPlatformOPL::renderSamples(int sysID) {
|
||||||
pcmMem[2+insAddr]=(sampleOffPCM[i])&0xff;
|
pcmMem[2+insAddr]=(sampleOffPCM[i])&0xff;
|
||||||
pcmMem[3+insAddr]=(loop>>8)&0xff;
|
pcmMem[3+insAddr]=(loop>>8)&0xff;
|
||||||
pcmMem[4+insAddr]=(loop)&0xff;
|
pcmMem[4+insAddr]=(loop)&0xff;
|
||||||
pcmMem[5+insAddr]=(endPos>>8)&0xff;
|
pcmMem[5+insAddr]=((~(endPos-1))>>8)&0xff;
|
||||||
pcmMem[6+insAddr]=(endPos)&0xff;
|
pcmMem[6+insAddr]=(~(endPos-1))&0xff;
|
||||||
// TODO: how to fill in rest of instrument table?
|
// TODO: how to fill in rest of instrument table?
|
||||||
pcmMem[7+insAddr]=0; // LFO, VIB
|
pcmMem[7+insAddr]=0; // LFO, VIB
|
||||||
pcmMem[8+insAddr]=0; // AR, D1R
|
pcmMem[8+insAddr]=(0xf << 4) | (0xf << 0); // AR, D1R
|
||||||
pcmMem[9+insAddr]=0; // DL, D2R
|
pcmMem[9+insAddr]=0; // DL, D2R
|
||||||
pcmMem[10+insAddr]=0; // RC, RR
|
pcmMem[10+insAddr]=(0xf << 4) | (0xf << 0); // RC, RR
|
||||||
pcmMem[11+insAddr]=0; // AM
|
pcmMem[11+insAddr]=0; // AM
|
||||||
}
|
}
|
||||||
if (ramSize<=0x200000) {
|
if (ramSize<=0x200000) {
|
||||||
|
@ -2825,7 +2950,6 @@ void DivPlatformOPL::renderSamples(int sysID) {
|
||||||
}
|
}
|
||||||
|
|
||||||
memCompo.used=pcmMemLen;
|
memCompo.used=pcmMemLen;
|
||||||
memCompo.capacity=getSampleMemCapacity(0);
|
|
||||||
} else if (adpcmChan>=0) { // ADPCM
|
} else if (adpcmChan>=0) { // ADPCM
|
||||||
size_t memPos=0;
|
size_t memPos=0;
|
||||||
for (int i=0; i<parent->song.sampleLen; i++) {
|
for (int i=0; i<parent->song.sampleLen; i++) {
|
||||||
|
@ -2857,8 +2981,8 @@ void DivPlatformOPL::renderSamples(int sysID) {
|
||||||
adpcmBMemLen=memPos+256;
|
adpcmBMemLen=memPos+256;
|
||||||
|
|
||||||
memCompo.used=adpcmBMemLen;
|
memCompo.used=adpcmBMemLen;
|
||||||
memCompo.capacity=getSampleMemCapacity(0);
|
|
||||||
}
|
}
|
||||||
|
memCompo.capacity=getSampleMemCapacity(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int DivPlatformOPL::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) {
|
int DivPlatformOPL::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) {
|
||||||
|
|
|
@ -45,10 +45,11 @@ class DivPlatformOPL: public DivDispatch {
|
||||||
protected:
|
protected:
|
||||||
struct Channel: public SharedChannel<int> {
|
struct Channel: public SharedChannel<int> {
|
||||||
DivInstrumentFM state;
|
DivInstrumentFM state;
|
||||||
unsigned char freqH, freqL;
|
unsigned int freqH, freqL;
|
||||||
int sample, fixedFreq;
|
int sample, fixedFreq;
|
||||||
bool furnacePCM, fourOp, hardReset;
|
bool furnacePCM, fourOp, hardReset, writeCtrl;
|
||||||
unsigned char pan;
|
bool levelDirect, damp, pseudoReverb;
|
||||||
|
int pan;
|
||||||
int macroVolMul;
|
int macroVolMul;
|
||||||
Channel():
|
Channel():
|
||||||
SharedChannel<int>(0),
|
SharedChannel<int>(0),
|
||||||
|
@ -59,6 +60,10 @@ class DivPlatformOPL: public DivDispatch {
|
||||||
furnacePCM(false),
|
furnacePCM(false),
|
||||||
fourOp(false),
|
fourOp(false),
|
||||||
hardReset(false),
|
hardReset(false),
|
||||||
|
writeCtrl(false),
|
||||||
|
levelDirect(true),
|
||||||
|
damp(false),
|
||||||
|
pseudoReverb(false),
|
||||||
pan(3),
|
pan(3),
|
||||||
macroVolMul(64) {
|
macroVolMul(64) {
|
||||||
state.ops=2;
|
state.ops=2;
|
||||||
|
@ -138,6 +143,7 @@ class DivPlatformOPL: public DivDispatch {
|
||||||
int octave(int freq);
|
int octave(int freq);
|
||||||
int toFreq(int freq);
|
int toFreq(int freq);
|
||||||
double NOTE_ADPCMB(int note);
|
double NOTE_ADPCMB(int note);
|
||||||
|
double NOTE_OPL4(int ch, int note);
|
||||||
void commitState(int ch, DivInstrument* ins);
|
void commitState(int ch, DivInstrument* ins);
|
||||||
|
|
||||||
friend void putDispatchChip(void*,int);
|
friend void putDispatchChip(void*,int);
|
||||||
|
|
Loading…
Reference in a new issue