Prepare for split sample chip instrument
(MSM6258, MSM6295, QSound, Sega PCM, ADPCM-A, ADPCM-B, YMZ280B, RF5C68) Instrument color and icons are placeholder. different volume range, hard panned/soft panned and/or independent volume per output, chip-dependent features (global volume, echo, etc) Allow use sample in instrument tab for chip with sample support Prepare to support X1-010 Seta 2 style bankswitch behavior Prepare to support AY89x0 PCM DAC Support volume for PCE sample (DAC) Fix Lynx, Y8950 sample pitch matches to sample preview Support PCM DAC with backward and pingpong loop mode Reduce some codes Add Sega PCM, AY89x0, QSound, PCM DAC, Lynx per-channel debug support
This commit is contained in:
parent
86baa8c014
commit
4cc79fb49d
53 changed files with 2928 additions and 1301 deletions
|
|
@ -18,14 +18,8 @@
|
|||
*/
|
||||
|
||||
#include "ym2610b.h"
|
||||
#include "sound/ymfm/ymfm.h"
|
||||
#include "../engine.h"
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#define CHIP_FREQBASE fmFreqBase
|
||||
#define CHIP_DIVIDER fmDivBase
|
||||
|
||||
const char* regCheatSheetYM2610B[]={
|
||||
// SSG
|
||||
"SSG_FreqL_A", "000",
|
||||
|
|
@ -422,24 +416,6 @@ const char* DivPlatformYM2610B::getEffectName(unsigned char effect) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
double DivPlatformYM2610B::NOTE_OPNB(int ch, int note) {
|
||||
if (ch>8) { // ADPCM-B
|
||||
return NOTE_ADPCMB(note);
|
||||
} else if (ch>5) { // PSG
|
||||
return NOTE_PERIODIC(note);
|
||||
}
|
||||
// FM
|
||||
return NOTE_FNUM_BLOCK(note,11);
|
||||
}
|
||||
|
||||
double DivPlatformYM2610B::NOTE_ADPCMB(int note) {
|
||||
if (chan[15].sample>=0 && chan[15].sample<parent->song.sampleLen) {
|
||||
double off=65535.0*(double)(parent->getSample(chan[15].sample)->centerRate)/8363.0;
|
||||
return parent->calcBaseFreq((double)chipClock/144,off,note,false);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DivPlatformYM2610B::acquire(short* bufL, short* bufR, size_t start, size_t len) {
|
||||
static int os[2];
|
||||
|
||||
|
|
@ -484,20 +460,20 @@ void DivPlatformYM2610B::acquire(short* bufL, short* bufR, size_t start, size_t
|
|||
bufR[h]=os[1];
|
||||
|
||||
|
||||
for (int i=0; i<6; i++) {
|
||||
for (int i=0; i<psgChanOffs; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=(fmChan[i]->debug_output(0)+fmChan[i]->debug_output(1));
|
||||
}
|
||||
|
||||
ssge->get_last_out(ssgOut);
|
||||
for (int i=6; i<9; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=ssgOut.data[i-6];
|
||||
for (int i=psgChanOffs; i<adpcmAChanOffs; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=ssgOut.data[i-psgChanOffs];
|
||||
}
|
||||
|
||||
for (int i=9; i<15; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=adpcmAChan[i-9]->get_last_out(0)+adpcmAChan[i-9]->get_last_out(1);
|
||||
for (int i=adpcmAChanOffs; i<adpcmBChanOffs; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=adpcmAChan[i-adpcmAChanOffs]->get_last_out(0)+adpcmAChan[i-adpcmAChanOffs]->get_last_out(1);
|
||||
}
|
||||
|
||||
oscBuf[15]->data[oscBuf[15]->needle++]=abe->get_last_out(0)+abe->get_last_out(1);
|
||||
oscBuf[adpcmBChanOffs]->data[oscBuf[adpcmBChanOffs]->needle++]=abe->get_last_out(0)+abe->get_last_out(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -511,7 +487,7 @@ void DivPlatformYM2610B::tick(bool sysTick) {
|
|||
ay->getRegisterWrites().clear();
|
||||
|
||||
// FM
|
||||
for (int i=0; i<6; i++) {
|
||||
for (int i=0; i<psgChanOffs; i++) {
|
||||
if (i==2 && extMode) continue;
|
||||
chan[i].std.next();
|
||||
|
||||
|
|
@ -671,41 +647,108 @@ void DivPlatformYM2610B::tick(bool sysTick) {
|
|||
chan[i].keyOff=false;
|
||||
}
|
||||
}
|
||||
// ADPCM-B
|
||||
if (chan[15].furnacePCM) {
|
||||
chan[15].std.next();
|
||||
|
||||
if (chan[15].std.vol.had) {
|
||||
chan[15].outVol=(chan[15].vol*MIN(64,chan[15].std.vol.val))/64;
|
||||
immWrite(0x1b,chan[15].outVol);
|
||||
}
|
||||
|
||||
if (chan[15].std.arp.had) {
|
||||
if (!chan[15].inPorta) {
|
||||
if (chan[15].std.arp.mode) {
|
||||
chan[15].baseFreq=NOTE_ADPCMB(chan[15].std.arp.val);
|
||||
} else {
|
||||
chan[15].baseFreq=NOTE_ADPCMB(chan[15].note+(signed char)chan[15].std.arp.val);
|
||||
// ADPCM-A
|
||||
for (int i=adpcmAChanOffs; i<adpcmBChanOffs; i++) {
|
||||
if (chan[i].furnacePCM) {
|
||||
chan[i].std.next();
|
||||
if (chan[i].std.vol.had) {
|
||||
chan[i].outVol=(chan[i].vol*MIN(chan[i].macroVolMul,chan[i].std.vol.val))/chan[i].macroVolMul;
|
||||
}
|
||||
if (chan[i].std.duty.had) {
|
||||
if (globalADPCMAVolume!=(chan[i].std.duty.val&0x3f)) {
|
||||
globalADPCMAVolume=chan[i].std.duty.val&0x3f;
|
||||
immWrite(0x101,globalADPCMAVolume);
|
||||
}
|
||||
}
|
||||
chan[15].freqChanged=true;
|
||||
} else {
|
||||
if (chan[15].std.arp.mode && chan[15].std.arp.finished) {
|
||||
chan[15].baseFreq=NOTE_ADPCMB(chan[15].note);
|
||||
chan[15].freqChanged=true;
|
||||
if (chan[i].std.panL.had) {
|
||||
chan[i].pan=chan[i].std.panL.val&3;
|
||||
}
|
||||
if (chan[i].std.phaseReset.had) {
|
||||
if ((chan[i].std.phaseReset.val==1) && chan[i].active) {
|
||||
chan[i].keyOn=true;
|
||||
}
|
||||
}
|
||||
if (!isMuted[i] && (chan[i].std.vol.had || chan[i].std.panL.had)) {
|
||||
immWrite(0x108+(i-adpcmAChanOffs),isMuted[i]?0:((chan[i].pan<<6)|chan[i].outVol));
|
||||
}
|
||||
if (chan[i].keyOff) {
|
||||
writeADPCMAOff|=(1<<(i-adpcmAChanOffs));
|
||||
chan[i].keyOff=false;
|
||||
}
|
||||
if (chan[i].keyOn) {
|
||||
if (chan[i].sample>=0 && chan[i].sample<parent->song.sampleLen) {
|
||||
writeADPCMAOn|=(1<<(i-adpcmAChanOffs));
|
||||
}
|
||||
chan[i].keyOn=false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (chan[15].freqChanged) {
|
||||
if (chan[15].sample>=0 && chan[15].sample<parent->song.sampleLen) {
|
||||
double off=65535.0*(double)(parent->getSample(chan[15].sample)->centerRate)/8363.0;
|
||||
chan[15].freq=parent->calcFreq(chan[15].baseFreq,chan[15].pitch,false,4,chan[15].pitch2,(double)chipClock/144,off);
|
||||
} else {
|
||||
chan[15].freq=0;
|
||||
// ADPCM-B
|
||||
if (chan[adpcmBChanOffs].furnacePCM) {
|
||||
chan[adpcmBChanOffs].std.next();
|
||||
|
||||
if (chan[adpcmBChanOffs].std.vol.had) {
|
||||
chan[adpcmBChanOffs].outVol=(chan[adpcmBChanOffs].vol*MIN(chan[adpcmBChanOffs].macroVolMul,chan[adpcmBChanOffs].std.vol.val))/chan[adpcmBChanOffs].macroVolMul;
|
||||
immWrite(0x1b,chan[adpcmBChanOffs].outVol);
|
||||
}
|
||||
immWrite(0x19,chan[15].freq&0xff);
|
||||
immWrite(0x1a,(chan[15].freq>>8)&0xff);
|
||||
chan[15].freqChanged=false;
|
||||
|
||||
if (chan[adpcmBChanOffs].std.arp.had) {
|
||||
if (!chan[adpcmBChanOffs].inPorta) {
|
||||
if (chan[adpcmBChanOffs].std.arp.mode) {
|
||||
chan[adpcmBChanOffs].baseFreq=NOTE_ADPCMB(chan[adpcmBChanOffs].std.arp.val);
|
||||
} else {
|
||||
chan[adpcmBChanOffs].baseFreq=NOTE_ADPCMB(chan[adpcmBChanOffs].note+(signed char)chan[adpcmBChanOffs].std.arp.val);
|
||||
}
|
||||
}
|
||||
chan[adpcmBChanOffs].freqChanged=true;
|
||||
} else {
|
||||
if (chan[adpcmBChanOffs].std.arp.mode && chan[adpcmBChanOffs].std.arp.finished) {
|
||||
chan[adpcmBChanOffs].baseFreq=NOTE_ADPCMB(chan[adpcmBChanOffs].note);
|
||||
chan[adpcmBChanOffs].freqChanged=true;
|
||||
}
|
||||
}
|
||||
if (chan[adpcmBChanOffs].std.panL.had) {
|
||||
if (chan[adpcmBChanOffs].pan!=(chan[adpcmBChanOffs].std.panL.val&3)) {
|
||||
chan[adpcmBChanOffs].pan=chan[adpcmBChanOffs].std.panL.val&3;
|
||||
if (!isMuted[adpcmBChanOffs]) {
|
||||
immWrite(0x11,(isMuted[adpcmBChanOffs]?0:(chan[adpcmBChanOffs].pan<<6)));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (chan[adpcmBChanOffs].std.phaseReset.had) {
|
||||
if ((chan[adpcmBChanOffs].std.phaseReset.val==1) && chan[adpcmBChanOffs].active) {
|
||||
chan[adpcmBChanOffs].keyOn=true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (chan[adpcmBChanOffs].freqChanged || chan[adpcmBChanOffs].keyOn || chan[adpcmBChanOffs].keyOff) {
|
||||
if (chan[adpcmBChanOffs].furnacePCM) {
|
||||
if (chan[adpcmBChanOffs].sample>=0 && chan[adpcmBChanOffs].sample<parent->song.sampleLen) {
|
||||
double off=65535.0*(double)(parent->getSample(chan[adpcmBChanOffs].sample)->centerRate)/8363.0;
|
||||
chan[adpcmBChanOffs].freq=parent->calcFreq(chan[adpcmBChanOffs].baseFreq,chan[adpcmBChanOffs].pitch,false,4,chan[adpcmBChanOffs].pitch2,(double)chipClock/144,off);
|
||||
} else {
|
||||
chan[adpcmBChanOffs].freq=0;
|
||||
}
|
||||
immWrite(0x19,chan[adpcmBChanOffs].freq&0xff);
|
||||
immWrite(0x1a,(chan[adpcmBChanOffs].freq>>8)&0xff);
|
||||
}
|
||||
if (chan[adpcmBChanOffs].keyOn || chan[adpcmBChanOffs].keyOff) {
|
||||
immWrite(0x10,0x01); // reset
|
||||
if (chan[adpcmBChanOffs].active && chan[adpcmBChanOffs].keyOn && !chan[adpcmBChanOffs].keyOff) {
|
||||
if (chan[adpcmBChanOffs].sample>=0 && chan[adpcmBChanOffs].sample<parent->song.sampleLen) {
|
||||
DivSample* s=parent->getSample(chan[adpcmBChanOffs].sample);
|
||||
immWrite(0x10,(s->isLoopable())?0x90:0x80); // start/repeat
|
||||
}
|
||||
}
|
||||
chan[adpcmBChanOffs].keyOn=false;
|
||||
chan[adpcmBChanOffs].keyOff=false;
|
||||
}
|
||||
chan[adpcmBChanOffs].freqChanged=false;
|
||||
}
|
||||
|
||||
if (writeADPCMAOff) {
|
||||
immWrite(0x100,0x80|writeADPCMAOff);
|
||||
writeADPCMAOff=0;
|
||||
}
|
||||
|
||||
for (int i=16; i<512; i++) {
|
||||
|
|
@ -715,7 +758,7 @@ void DivPlatformYM2610B::tick(bool sysTick) {
|
|||
}
|
||||
}
|
||||
|
||||
for (int i=0; i<6; i++) {
|
||||
for (int i=0; i<psgChanOffs; i++) {
|
||||
if (i==2 && extMode) continue;
|
||||
if (chan[i].freqChanged) {
|
||||
if (parent->song.linearPitch==2) {
|
||||
|
|
@ -743,18 +786,24 @@ void DivPlatformYM2610B::tick(bool sysTick) {
|
|||
chan[i].keyOn=false;
|
||||
}
|
||||
}
|
||||
|
||||
if (writeADPCMAOn) {
|
||||
immWrite(0x100,writeADPCMAOn);
|
||||
writeADPCMAOn=0;
|
||||
}
|
||||
}
|
||||
|
||||
int DivPlatformYM2610B::dispatch(DivCommand c) {
|
||||
if (c.chan>5 && c.chan<9) {
|
||||
c.chan-=6;
|
||||
if (c.chan>=psgChanOffs && c.chan<adpcmAChanOffs) {
|
||||
c.chan-=psgChanOffs;
|
||||
return ay->dispatch(c);
|
||||
}
|
||||
switch (c.cmd) {
|
||||
case DIV_CMD_NOTE_ON: {
|
||||
if (c.chan>14) { // ADPCM-B
|
||||
if (c.chan>=adpcmBChanOffs) { // ADPCM-B
|
||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM);
|
||||
if (ins->type==DIV_INS_AMIGA) {
|
||||
chan[c.chan].macroVolMul=(ins->type==DIV_INS_AMIGA)?64:255;
|
||||
if (ins->type==DIV_INS_AMIGA || ins->type==DIV_INS_ADPCMB) {
|
||||
chan[c.chan].furnacePCM=true;
|
||||
} else {
|
||||
chan[c.chan].furnacePCM=false;
|
||||
|
|
@ -775,7 +824,6 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
|
|||
immWrite(0x14,(end>>8)&0xff);
|
||||
immWrite(0x15,end>>16);
|
||||
immWrite(0x11,isMuted[c.chan]?0:(chan[c.chan].pan<<6));
|
||||
immWrite(0x10,(s->isLoopable())?0x90:0x80); // start/repeat
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].note=c.value;
|
||||
chan[c.chan].baseFreq=NOTE_ADPCMB(chan[c.chan].note);
|
||||
|
|
@ -796,45 +844,99 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
|
|||
chan[c.chan].macroInit(NULL);
|
||||
chan[c.chan].outVol=chan[c.chan].vol;
|
||||
if ((12*sampleBank+c.value%12)>=parent->song.sampleLen) {
|
||||
immWrite(0x10,0x01); // reset
|
||||
immWrite(0x12,0);
|
||||
immWrite(0x13,0);
|
||||
immWrite(0x14,0);
|
||||
immWrite(0x15,0);
|
||||
break;
|
||||
}
|
||||
DivSample* s=parent->getSample(12*sampleBank+c.value%12);
|
||||
immWrite(0x12,(s->offB>>8)&0xff);
|
||||
immWrite(0x13,s->offB>>16);
|
||||
int end=s->offB+s->lengthB-1;
|
||||
immWrite(0x14,(end>>8)&0xff);
|
||||
immWrite(0x15,end>>16);
|
||||
immWrite(0x11,isMuted[c.chan]?0:(chan[c.chan].pan<<6));
|
||||
immWrite(0x10,(s->isLoopable())?0x90:0x80); // start/repeat
|
||||
int freq=(65536.0*(double)s->rate)/((double)chipClock/144.0);
|
||||
immWrite(0x19,freq&0xff);
|
||||
immWrite(0x1a,(freq>>8)&0xff);
|
||||
chan[c.chan].sample=12*sampleBank+c.value%12;
|
||||
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
|
||||
DivSample* s=parent->getSample(12*sampleBank+c.value%12);
|
||||
immWrite(0x12,(s->offB>>8)&0xff);
|
||||
immWrite(0x13,s->offB>>16);
|
||||
int end=s->offB+s->lengthB-1;
|
||||
immWrite(0x14,(end>>8)&0xff);
|
||||
immWrite(0x15,end>>16);
|
||||
immWrite(0x11,isMuted[c.chan]?0:(chan[c.chan].pan<<6));
|
||||
int freq=(65536.0*(double)s->rate)/((double)chipClock/144.0);
|
||||
immWrite(0x19,freq&0xff);
|
||||
immWrite(0x1a,(freq>>8)&0xff);
|
||||
immWrite(0x1b,chan[c.chan].outVol);
|
||||
chan[c.chan].active=true;
|
||||
chan[c.chan].keyOn=true;
|
||||
} else {
|
||||
immWrite(0x10,0x01); // reset
|
||||
immWrite(0x12,0);
|
||||
immWrite(0x13,0);
|
||||
immWrite(0x14,0);
|
||||
immWrite(0x15,0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (c.chan>8) { // ADPCM-A
|
||||
if (skipRegisterWrites) break;
|
||||
if ((12*sampleBank+c.value%12)>=parent->song.sampleLen) {
|
||||
immWrite(0x100,0x80|(1<<(c.chan-9)));
|
||||
immWrite(0x110+c.chan-9,0);
|
||||
immWrite(0x118+c.chan-9,0);
|
||||
immWrite(0x120+c.chan-9,0);
|
||||
immWrite(0x128+c.chan-9,0);
|
||||
break;
|
||||
if (c.chan>=adpcmAChanOffs) { // ADPCM-A
|
||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM);
|
||||
chan[c.chan].macroVolMul=(ins->type==DIV_INS_AMIGA)?64:31;
|
||||
if (ins->type==DIV_INS_AMIGA || ins->type==DIV_INS_ADPCMA) {
|
||||
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);
|
||||
immWrite(0x110+c.chan-adpcmAChanOffs,(s->offA>>8)&0xff);
|
||||
immWrite(0x118+c.chan-adpcmAChanOffs,s->offA>>16);
|
||||
int end=s->offA+s->lengthA-1;
|
||||
immWrite(0x120+c.chan-adpcmAChanOffs,(end>>8)&0xff);
|
||||
immWrite(0x128+c.chan-adpcmAChanOffs,end>>16);
|
||||
immWrite(0x108+c.chan-adpcmAChanOffs,isMuted[c.chan]?0:((chan[c.chan].pan<<6)|chan[c.chan].outVol));
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].note=c.value;
|
||||
chan[c.chan].baseFreq=NOTE_ADPCMB(chan[c.chan].note);
|
||||
chan[c.chan].freqChanged=true;
|
||||
}
|
||||
chan[c.chan].active=true;
|
||||
chan[c.chan].keyOn=true;
|
||||
} else {
|
||||
writeADPCMAOff|=(1<<(c.chan-adpcmAChanOffs));
|
||||
immWrite(0x110+c.chan-adpcmAChanOffs,0);
|
||||
immWrite(0x118+c.chan-adpcmAChanOffs,0);
|
||||
immWrite(0x120+c.chan-adpcmAChanOffs,0);
|
||||
immWrite(0x128+c.chan-adpcmAChanOffs,0);
|
||||
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;
|
||||
}
|
||||
chan[c.chan].sample=12*sampleBank+c.value%12;
|
||||
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
|
||||
DivSample* s=parent->getSample(12*sampleBank+c.value%12);
|
||||
immWrite(0x110+c.chan-adpcmAChanOffs,(s->offA>>8)&0xff);
|
||||
immWrite(0x118+c.chan-adpcmAChanOffs,s->offA>>16);
|
||||
int end=s->offA+s->lengthA-1;
|
||||
immWrite(0x120+c.chan-adpcmAChanOffs,(end>>8)&0xff);
|
||||
immWrite(0x128+c.chan-adpcmAChanOffs,end>>16);
|
||||
immWrite(0x108+c.chan-adpcmAChanOffs,isMuted[c.chan]?0:((chan[c.chan].pan<<6)|chan[c.chan].outVol));
|
||||
chan[c.chan].active=true;
|
||||
chan[c.chan].keyOn=true;
|
||||
} else {
|
||||
writeADPCMAOff|=(1<<(c.chan-adpcmAChanOffs));
|
||||
immWrite(0x110+c.chan-adpcmAChanOffs,0);
|
||||
immWrite(0x118+c.chan-adpcmAChanOffs,0);
|
||||
immWrite(0x120+c.chan-adpcmAChanOffs,0);
|
||||
immWrite(0x128+c.chan-adpcmAChanOffs,0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
DivSample* s=parent->getSample(12*sampleBank+c.value%12);
|
||||
immWrite(0x110+c.chan-9,(s->offA>>8)&0xff);
|
||||
immWrite(0x118+c.chan-9,s->offA>>16);
|
||||
int end=s->offA+s->lengthA-1;
|
||||
immWrite(0x120+c.chan-9,(end>>8)&0xff);
|
||||
immWrite(0x128+c.chan-9,end>>16);
|
||||
immWrite(0x108+(c.chan-9),isMuted[c.chan]?0:((chan[c.chan].pan<<6)|chan[c.chan].vol));
|
||||
immWrite(0x100,0x00|(1<<(c.chan-9)));
|
||||
break;
|
||||
}
|
||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM);
|
||||
|
|
@ -887,28 +989,12 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_NOTE_OFF:
|
||||
if (c.chan>14) {
|
||||
immWrite(0x10,0x01); // reset
|
||||
break;
|
||||
}
|
||||
if (c.chan>8) {
|
||||
immWrite(0x100,0x80|(1<<(c.chan-9)));
|
||||
break;
|
||||
}
|
||||
chan[c.chan].keyOff=true;
|
||||
chan[c.chan].keyOn=false;
|
||||
chan[c.chan].active=false;
|
||||
chan[c.chan].macroInit(NULL);
|
||||
break;
|
||||
case DIV_CMD_NOTE_OFF_ENV:
|
||||
if (c.chan>14) {
|
||||
immWrite(0x10,0x01); // reset
|
||||
break;
|
||||
}
|
||||
if (c.chan>8) {
|
||||
immWrite(0x100,0x80|(1<<(c.chan-9)));
|
||||
break;
|
||||
}
|
||||
chan[c.chan].keyOff=true;
|
||||
chan[c.chan].keyOn=false;
|
||||
chan[c.chan].active=false;
|
||||
|
|
@ -922,12 +1008,12 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
|
|||
if (!chan[c.chan].std.vol.has) {
|
||||
chan[c.chan].outVol=c.value;
|
||||
}
|
||||
if (c.chan>14) { // ADPCM-B
|
||||
if (c.chan>=adpcmBChanOffs) { // ADPCM-B
|
||||
immWrite(0x1b,chan[c.chan].outVol);
|
||||
break;
|
||||
}
|
||||
if (c.chan>8) { // ADPCM-A
|
||||
immWrite(0x108+(c.chan-9),isMuted[c.chan]?0:((chan[c.chan].pan<<6)|chan[c.chan].vol));
|
||||
if (c.chan>=adpcmAChanOffs) { // ADPCM-A
|
||||
immWrite(0x108+(c.chan-adpcmAChanOffs),isMuted[c.chan]?0:((chan[c.chan].pan<<6)|chan[c.chan].outVol));
|
||||
break;
|
||||
}
|
||||
for (int i=0; i<4; i++) {
|
||||
|
|
@ -941,6 +1027,13 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_ADPCMA_GLOBAL_VOLUME: {
|
||||
if (globalADPCMAVolume!=(c.value&0x3f)) {
|
||||
globalADPCMAVolume=c.value&0x3f;
|
||||
immWrite(0x101,globalADPCMAVolume&0x3f);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_GET_VOLUME: {
|
||||
return chan[c.chan].vol;
|
||||
break;
|
||||
|
|
@ -957,25 +1050,25 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
|
|||
} else {
|
||||
chan[c.chan].pan=(c.value2>0)|((c.value>0)<<1);
|
||||
}
|
||||
if (c.chan>14) {
|
||||
if (c.chan>=adpcmBChanOffs) {
|
||||
immWrite(0x11,isMuted[c.chan]?0:(chan[c.chan].pan<<6));
|
||||
break;
|
||||
}
|
||||
if (c.chan>8) {
|
||||
immWrite(0x108+(c.chan-9),isMuted[c.chan]?0:((chan[c.chan].pan<<6)|chan[c.chan].vol));
|
||||
if (c.chan>=adpcmAChanOffs) {
|
||||
immWrite(0x108+(c.chan-adpcmAChanOffs),isMuted[c.chan]?0:((chan[c.chan].pan<<6)|chan[c.chan].outVol));
|
||||
break;
|
||||
}
|
||||
rWrite(chanOffs[c.chan]+ADDR_LRAF,(isMuted[c.chan]?0:(chan[c.chan].pan<<6))|(chan[c.chan].state.fms&7)|((chan[c.chan].state.ams&3)<<4));
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_PITCH: {
|
||||
if (c.chan==15 && !chan[c.chan].furnacePCM) break;
|
||||
if (c.chan==adpcmBChanOffs && !chan[c.chan].furnacePCM) break;
|
||||
chan[c.chan].pitch=c.value;
|
||||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
if (c.chan>5 || parent->song.linearPitch==2) { // PSG, ADPCM-B
|
||||
if (c.chan>=psgChanOffs || parent->song.linearPitch==2) { // PSG, ADPCM-B
|
||||
int destFreq=NOTE_OPNB(c.chan,c.value2);
|
||||
bool return2=false;
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
|
|
@ -1009,7 +1102,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
|
|||
iface.sampleBank=sampleBank;
|
||||
break;
|
||||
case DIV_CMD_LEGATO: {
|
||||
if (c.chan==15 && !chan[c.chan].furnacePCM) break;
|
||||
if (c.chan==adpcmBChanOffs && !chan[c.chan].furnacePCM) break;
|
||||
chan[c.chan].baseFreq=NOTE_OPNB(c.chan,c.value);
|
||||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
|
|
@ -1026,13 +1119,13 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_FM_FB: {
|
||||
if (c.chan>5) break;
|
||||
if (c.chan>=psgChanOffs) break;
|
||||
chan[c.chan].state.fb=c.value&7;
|
||||
rWrite(chanOffs[c.chan]+ADDR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3));
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_MULT: {
|
||||
if (c.chan>5) break;
|
||||
if (c.chan>=psgChanOffs) break;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
op.mult=c.value2&15;
|
||||
|
|
@ -1040,7 +1133,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_FM_TL: {
|
||||
if (c.chan>5) break;
|
||||
if (c.chan>=psgChanOffs) break;
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
op.tl=c.value2;
|
||||
|
|
@ -1052,7 +1145,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_FM_AR: {
|
||||
if (c.chan>5) break;
|
||||
if (c.chan>=psgChanOffs) break;
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
|
|
@ -1203,13 +1296,13 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
|
|||
return 0;
|
||||
break;
|
||||
case DIV_CMD_GET_VOLMAX:
|
||||
if (c.chan>14) return 255;
|
||||
if (c.chan>8) return 31;
|
||||
if (c.chan>5) return 15;
|
||||
if (c.chan>=adpcmBChanOffs) return 255;
|
||||
if (c.chan>=adpcmAChanOffs) return 31;
|
||||
if (c.chan>=psgChanOffs) return 15;
|
||||
return 127;
|
||||
break;
|
||||
case DIV_CMD_PRE_PORTA:
|
||||
if (c.chan>5) {
|
||||
if (c.chan>=psgChanOffs) {
|
||||
if (chan[c.chan].active && c.value2) {
|
||||
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_FM));
|
||||
}
|
||||
|
|
@ -1227,15 +1320,8 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
|
|||
|
||||
void DivPlatformYM2610B::muteChannel(int ch, bool mute) {
|
||||
isMuted[ch]=mute;
|
||||
if (ch>14) { // ADPCM-B
|
||||
immWrite(0x11,isMuted[ch]?0:(chan[ch].pan<<6));
|
||||
}
|
||||
if (ch>8) { // ADPCM-A
|
||||
immWrite(0x108+(ch-9),isMuted[ch]?0:((chan[ch].pan<<6)|chan[ch].vol));
|
||||
return;
|
||||
}
|
||||
if (ch>5) { // PSG
|
||||
ay->muteChannel(ch-6,mute);
|
||||
if (ch>=psgChanOffs) { // PSG
|
||||
DivPlatformYM2610Base::muteChannel(ch,mute);
|
||||
return;
|
||||
}
|
||||
// FM
|
||||
|
|
@ -1243,7 +1329,7 @@ void DivPlatformYM2610B::muteChannel(int ch, bool mute) {
|
|||
}
|
||||
|
||||
void DivPlatformYM2610B::forceIns() {
|
||||
for (int i=0; i<6; i++) {
|
||||
for (int i=0; i<psgChanOffs; i++) {
|
||||
for (int j=0; j<4; j++) {
|
||||
unsigned short baseAddr=chanOffs[i]|opOffs[j];
|
||||
DivInstrumentFM::Operator& op=chan[i].state.op[j];
|
||||
|
|
@ -1266,7 +1352,7 @@ void DivPlatformYM2610B::forceIns() {
|
|||
chan[i].freqChanged=true;
|
||||
}
|
||||
}
|
||||
for (int i=9; i<16; i++) {
|
||||
for (int i=adpcmAChanOffs; i<=adpcmBChanOffs; i++) {
|
||||
chan[i].insChanged=true;
|
||||
}
|
||||
|
||||
|
|
@ -1283,7 +1369,7 @@ void* DivPlatformYM2610B::getChanState(int ch) {
|
|||
}
|
||||
|
||||
DivMacroInt* DivPlatformYM2610B::getChanMacroInt(int ch) {
|
||||
if (ch>=6 && ch<9) return ay->getChanMacroInt(ch-6);
|
||||
if (ch>=psgChanOffs && ch<adpcmAChanOffs) return ay->getChanMacroInt(ch-psgChanOffs);
|
||||
return &chan[ch].std;
|
||||
}
|
||||
|
||||
|
|
@ -1318,17 +1404,20 @@ void DivPlatformYM2610B::reset() {
|
|||
chan[i]=DivPlatformYM2610B::Channel();
|
||||
chan[i].std.setEngine(parent);
|
||||
}
|
||||
for (int i=0; i<6; i++) {
|
||||
for (int i=0; i<psgChanOffs; i++) {
|
||||
chan[i].vol=0x7f;
|
||||
chan[i].outVol=0x7f;
|
||||
}
|
||||
for (int i=6; i<9; i++) {
|
||||
for (int i=psgChanOffs; i<adpcmAChanOffs; i++) {
|
||||
chan[i].vol=0x0f;
|
||||
chan[i].outVol=0x0f;
|
||||
}
|
||||
for (int i=9; i<15; i++) {
|
||||
for (int i=adpcmAChanOffs; i<adpcmBChanOffs; i++) {
|
||||
chan[i].vol=0x1f;
|
||||
chan[i].outVol=0x1f;
|
||||
}
|
||||
chan[15].vol=0xff;
|
||||
chan[adpcmBChanOffs].vol=0xff;
|
||||
chan[adpcmBChanOffs].outVol=0xff;
|
||||
|
||||
for (int i=0; i<512; i++) {
|
||||
oldWrites[i]=-1;
|
||||
|
|
@ -1337,6 +1426,7 @@ void DivPlatformYM2610B::reset() {
|
|||
|
||||
lastBusy=60;
|
||||
sampleBank=0;
|
||||
DivPlatformYM2610Base::reset();
|
||||
|
||||
delay=0;
|
||||
|
||||
|
|
@ -1359,7 +1449,7 @@ bool DivPlatformYM2610B::isStereo() {
|
|||
}
|
||||
|
||||
bool DivPlatformYM2610B::keyOffAffectsArp(int ch) {
|
||||
return (ch>5);
|
||||
return (ch>=psgChanOffs);
|
||||
}
|
||||
|
||||
void DivPlatformYM2610B::notifyInsChange(int ins) {
|
||||
|
|
@ -1380,46 +1470,13 @@ void DivPlatformYM2610B::setSkipRegisterWrites(bool value) {
|
|||
ay->setSkipRegisterWrites(value);
|
||||
}
|
||||
|
||||
void DivPlatformYM2610B::setFlags(unsigned int flags) {
|
||||
switch (flags&0xff) {
|
||||
default:
|
||||
case 0x00:
|
||||
chipClock=8000000.0;
|
||||
break;
|
||||
case 0x01:
|
||||
chipClock=24167829/3;
|
||||
break;
|
||||
}
|
||||
rate=chipClock/16;
|
||||
for (int i=0; i<16; i++) {
|
||||
oscBuf[i]->rate=rate;
|
||||
}
|
||||
}
|
||||
|
||||
int DivPlatformYM2610B::init(DivEngine* p, int channels, int sugRate, unsigned int flags) {
|
||||
DivPlatformYM2610Base::init(p, channels, sugRate, flags);
|
||||
dumpWrites=false;
|
||||
skipRegisterWrites=false;
|
||||
for (int i=0; i<16; i++) {
|
||||
isMuted[i]=false;
|
||||
oscBuf[i]=new DivDispatchOscBuffer;
|
||||
}
|
||||
fm=new ymfm::ym2610b(iface);
|
||||
setFlags(flags);
|
||||
// YM2149, 2MHz
|
||||
ay=new DivPlatformAY8910(true,chipClock,32);
|
||||
ay->init(p,3,sugRate,16);
|
||||
ay->toggleRegisterDump(true);
|
||||
reset();
|
||||
return 16;
|
||||
}
|
||||
|
||||
void DivPlatformYM2610B::quit() {
|
||||
for (int i=0; i<16; i++) {
|
||||
delete oscBuf[i];
|
||||
}
|
||||
ay->quit();
|
||||
delete ay;
|
||||
delete fm;
|
||||
DivPlatformYM2610Base::quit();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue