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:
cam900 2022-08-27 16:27:36 +09:00
parent 86baa8c014
commit 4cc79fb49d
53 changed files with 2928 additions and 1301 deletions

View file

@ -18,31 +18,27 @@
*/
#include "ym2610ext.h"
#include "../engine.h"
#include <math.h>
#define CHIP_FREQBASE fmFreqBase
#define CHIP_DIVIDER fmDivBase
int DivPlatformYM2610Ext::dispatch(DivCommand c) {
if (c.chan<1) {
if (c.chan<extChanOffs) {
return DivPlatformYM2610::dispatch(c);
}
if (c.chan>4) {
if (c.chan>(extChanOffs+3)) {
c.chan-=3;
return DivPlatformYM2610::dispatch(c);
}
int ch=c.chan-1;
int ch=c.chan-extChanOffs;
int ordch=orderedOps[ch];
if (!extMode) {
c.chan=2;
c.chan=extChanOffs;
return DivPlatformYM2610::dispatch(c);
}
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
unsigned short baseAddr=chanOffs[1]|opOffs[ordch];
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[ordch];
DivInstrumentFM::Operator op=ins->fm.op[ordch];
// TODO: how does this work?!
if (isOpMuted[ch]) {
@ -61,8 +57,8 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) {
rWrite(baseAddr+0x90,op.ssgEnv&15);
}
if (opChan[ch].insChanged) { // TODO how does this work?
rWrite(chanOffs[1]+0xb0,(ins->fm.alg&7)|(ins->fm.fb<<3));
rWrite(chanOffs[1]+0xb4,(opChan[ch].pan<<6)|(ins->fm.fms&7)|((ins->fm.ams&3)<<4));
rWrite(chanOffs[extChanOffs]+0xb0,(ins->fm.alg&7)|(ins->fm.fb<<3));
rWrite(chanOffs[extChanOffs]+0xb4,(opChan[ch].pan<<6)|(ins->fm.fms&7)|((ins->fm.ams&3)<<4));
}
opChan[ch].insChanged=false;
@ -83,7 +79,7 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) {
case DIV_CMD_VOLUME: {
opChan[ch].vol=c.value;
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
unsigned short baseAddr=chanOffs[1]|opOffs[ordch];
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[ordch];
DivInstrumentFM::Operator op=ins->fm.op[ordch];
if (isOpMuted[ch]) {
rWrite(baseAddr+0x40,127);
@ -115,7 +111,7 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) {
opChan[i].pan=opChan[ch].pan;
}
}
rWrite(chanOffs[1]+0xb4,(opChan[ch].pan<<6)|(ins->fm.fms&7)|((ins->fm.ams&3)<<4));
rWrite(chanOffs[extChanOffs]+0xb4,(opChan[ch].pan<<6)|(ins->fm.fms&7)|((ins->fm.ams&3)<<4));
break;
}
case DIV_CMD_PITCH: {
@ -165,19 +161,19 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_FB: {
chan[1].state.fb=c.value&7;
rWrite(chanOffs[1]+ADDR_FB_ALG,(chan[1].state.alg&7)|(chan[1].state.fb<<3));
chan[extChanOffs].state.fb=c.value&7;
rWrite(chanOffs[extChanOffs]+ADDR_FB_ALG,(chan[extChanOffs].state.alg&7)|(chan[extChanOffs].state.fb<<3));
break;
}
case DIV_CMD_FM_MULT: { // TODO
unsigned short baseAddr=chanOffs[1]|opOffs[orderedOps[c.value]];
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[orderedOps[c.value]];
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
DivInstrumentFM::Operator op=ins->fm.op[orderedOps[c.value]];
rWrite(baseAddr+0x30,(c.value2&15)|(dtTable[op.dt&7]<<4));
break;
}
case DIV_CMD_FM_TL: { // TODO
unsigned short baseAddr=chanOffs[1]|opOffs[orderedOps[c.value]];
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[orderedOps[c.value]];
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
if (isOutput[ins->fm.alg][c.value]) {
rWrite(baseAddr+0x40,127-(((127-c.value2)*(opChan[ch].vol&0x7f))/127));
@ -189,15 +185,15 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) {
case DIV_CMD_FM_AR: {
if (c.value<0) {
for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[1].state.op[i];
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[i];
op.ar=c.value2&31;
unsigned short baseAddr=chanOffs[1]|opOffs[i];
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[i];
rWrite(baseAddr+0x50,(op.ar&31)|(op.rs<<6));
}
} else {
DivInstrumentFM::Operator& op=chan[1].state.op[orderedOps[c.value]];
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[orderedOps[c.value]];
op.ar=c.value2&31;
unsigned short baseAddr=chanOffs[1]|opOffs[orderedOps[c.value]];
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[orderedOps[c.value]];
rWrite(baseAddr+0x50,(op.ar&31)|(op.rs<<6));
}
break;
@ -205,15 +201,15 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) {
case DIV_CMD_FM_RS: {
if (c.value<0) {
for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[1].state.op[i];
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[i];
op.rs=c.value2&3;
unsigned short baseAddr=chanOffs[1]|opOffs[i];
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[i];
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
}
} else if (c.value<4) {
DivInstrumentFM::Operator& op=chan[1].state.op[orderedOps[c.value]];
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[orderedOps[c.value]];
op.rs=c.value2&3;
unsigned short baseAddr=chanOffs[1]|opOffs[orderedOps[c.value]];
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[orderedOps[c.value]];
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
}
break;
@ -221,15 +217,15 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) {
case DIV_CMD_FM_AM: {
if (c.value<0) {
for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[1].state.op[i];
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[i];
op.am=c.value2&1;
unsigned short baseAddr=chanOffs[1]|opOffs[i];
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[i];
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
}
} else if (c.value<4) {
DivInstrumentFM::Operator& op=chan[1].state.op[orderedOps[c.value]];
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[orderedOps[c.value]];
op.am=c.value2&1;
unsigned short baseAddr=chanOffs[1]|opOffs[orderedOps[c.value]];
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[orderedOps[c.value]];
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
}
break;
@ -237,15 +233,15 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) {
case DIV_CMD_FM_DR: {
if (c.value<0) {
for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[1].state.op[i];
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[i];
op.dr=c.value2&31;
unsigned short baseAddr=chanOffs[1]|opOffs[i];
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[i];
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
}
} else if (c.value<4) {
DivInstrumentFM::Operator& op=chan[1].state.op[orderedOps[c.value]];
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[orderedOps[c.value]];
op.dr=c.value2&31;
unsigned short baseAddr=chanOffs[1]|opOffs[orderedOps[c.value]];
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[orderedOps[c.value]];
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
}
break;
@ -253,15 +249,15 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) {
case DIV_CMD_FM_SL: {
if (c.value<0) {
for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[1].state.op[i];
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[i];
op.sl=c.value2&15;
unsigned short baseAddr=chanOffs[1]|opOffs[i];
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[i];
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
}
} else if (c.value<4) {
DivInstrumentFM::Operator& op=chan[1].state.op[orderedOps[c.value]];
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[orderedOps[c.value]];
op.sl=c.value2&15;
unsigned short baseAddr=chanOffs[1]|opOffs[orderedOps[c.value]];
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[orderedOps[c.value]];
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
}
break;
@ -269,15 +265,15 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) {
case DIV_CMD_FM_RR: {
if (c.value<0) {
for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[1].state.op[i];
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[i];
op.rr=c.value2&15;
unsigned short baseAddr=chanOffs[1]|opOffs[i];
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[i];
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
}
} else if (c.value<4) {
DivInstrumentFM::Operator& op=chan[1].state.op[orderedOps[c.value]];
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[orderedOps[c.value]];
op.rr=c.value2&15;
unsigned short baseAddr=chanOffs[1]|opOffs[orderedOps[c.value]];
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[orderedOps[c.value]];
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
}
break;
@ -285,15 +281,15 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) {
case DIV_CMD_FM_D2R: {
if (c.value<0) {
for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[1].state.op[i];
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[i];
op.d2r=c.value2&31;
unsigned short baseAddr=chanOffs[1]|opOffs[i];
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[i];
rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31);
}
} else if (c.value<4) {
DivInstrumentFM::Operator& op=chan[1].state.op[orderedOps[c.value]];
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[orderedOps[c.value]];
op.d2r=c.value2&31;
unsigned short baseAddr=chanOffs[1]|opOffs[orderedOps[c.value]];
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[orderedOps[c.value]];
rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31);
}
break;
@ -301,15 +297,15 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) {
case DIV_CMD_FM_DT: {
if (c.value<0) {
for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[1].state.op[i];
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[i];
op.dt=c.value&7;
unsigned short baseAddr=chanOffs[1]|opOffs[i];
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[i];
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
}
} else if (c.value<4) {
DivInstrumentFM::Operator& op=chan[1].state.op[orderedOps[c.value]];
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[orderedOps[c.value]];
op.dt=c.value2&7;
unsigned short baseAddr=chanOffs[1]|opOffs[orderedOps[c.value]];
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[orderedOps[c.value]];
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
}
break;
@ -317,15 +313,15 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) {
case DIV_CMD_FM_SSG: {
if (c.value<0) {
for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[1].state.op[i];
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[i];
op.ssgEnv=8^(c.value2&15);
unsigned short baseAddr=chanOffs[1]|opOffs[i];
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[i];
rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15);
}
} else if (c.value<4) {
DivInstrumentFM::Operator& op=chan[1].state.op[orderedOps[c.value]];
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[orderedOps[c.value]];
op.ssgEnv=8^(c.value2&15);
unsigned short baseAddr=chanOffs[1]|opOffs[orderedOps[c.value]];
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[orderedOps[c.value]];
rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15);
}
break;
@ -408,31 +404,31 @@ void DivPlatformYM2610Ext::tick(bool sysTick) {
}
void DivPlatformYM2610Ext::muteChannel(int ch, bool mute) {
if (ch<1) {
if (ch<extChanOffs) {
DivPlatformYM2610::muteChannel(ch,mute);
return;
}
if (ch>4) {
if (ch>(extChanOffs+3)) {
DivPlatformYM2610::muteChannel(ch-3,mute);
return;
}
isOpMuted[ch-1]=mute;
isOpMuted[ch-extChanOffs]=mute;
int ordch=orderedOps[ch-1];
int ordch=orderedOps[ch-extChanOffs];
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
unsigned short baseAddr=chanOffs[1]|opOffs[ordch];
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[ordch];
DivInstrumentFM::Operator op=ins->fm.op[ordch];
if (isOpMuted[ch]) {
rWrite(baseAddr+0x40,127);
} else if (isOutput[ins->fm.alg][ordch]) {
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[ch].vol&0x7f,127));
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[ch-extChanOffs].vol&0x7f,127));
} else {
rWrite(baseAddr+0x40,op.tl);
}
}
void DivPlatformYM2610Ext::forceIns() {
for (int i=0; i<4; 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];
@ -469,7 +465,7 @@ void DivPlatformYM2610Ext::forceIns() {
chan[i].freqChanged=true;
}
}
for (int i=4; i<14; i++) {
for (int i=adpcmAChanOffs; i<=adpcmBChanOffs; i++) {
chan[i].insChanged=true;
}
ay->forceIns();
@ -488,21 +484,21 @@ void DivPlatformYM2610Ext::forceIns() {
}
void* DivPlatformYM2610Ext::getChanState(int ch) {
if (ch>=5) return &chan[ch-3];
if (ch>=1) return &opChan[ch-1];
if (ch>=(extChanOffs+4)) return &chan[ch-3];
if (ch>=extChanOffs) return &opChan[ch-extChanOffs];
return &chan[ch];
}
DivMacroInt* DivPlatformYM2610Ext::getChanMacroInt(int ch) {
if (ch>=7 && ch<10) return ay->getChanMacroInt(ch-7);
if (ch>=5) return &chan[ch-3].std;
if (ch>=1) return NULL; // currently not implemented
if (ch>=(psgChanOffs+3) && ch<(adpcmAChanOffs+3)) return ay->getChanMacroInt(ch-psgChanOffs-3);
if (ch>=(extChanOffs+4)) return &chan[ch-3].std;
if (ch>=extChanOffs) return NULL; // currently not implemented
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformYM2610Ext::getOscBuffer(int ch) {
if (ch>=5) return oscBuf[ch-3];
if (ch<2) return oscBuf[ch];
if (ch>=(extChanOffs+4)) return oscBuf[ch-3];
if (ch<(extChanOffs+1)) return oscBuf[ch];
return NULL;
}
@ -520,7 +516,7 @@ void DivPlatformYM2610Ext::reset() {
}
bool DivPlatformYM2610Ext::keyOffAffectsArp(int ch) {
return (ch>7);
return (ch>=(psgChanOffs+3));
}
void DivPlatformYM2610Ext::notifyInsChange(int ins) {