channel mute/solo!

This commit is contained in:
tildearrow 2021-12-18 03:25:42 -05:00
parent c4c019e593
commit b3b66ecbdb
27 changed files with 416 additions and 48 deletions

View file

@ -101,13 +101,70 @@ class DivDispatch {
* the engine shall resample to the output rate. * the engine shall resample to the output rate.
*/ */
int rate; int rate;
/**
* fill a buffer with sound data.
* @param bufL the left or mono channel buffer.
* @param bufR the right channel buffer.
* @param start the start offset.
* @param len the amount of samples to fill.
*/
virtual void acquire(short* bufL, short* bufR, size_t start, size_t len); virtual void acquire(short* bufL, short* bufR, size_t start, size_t len);
/**
* send a command to this dispatch.
* @param c a DivCommand.
* @return a return value which varies depending on the command.
*/
virtual int dispatch(DivCommand c); virtual int dispatch(DivCommand c);
/**
* reset the state of this dispatch.
*/
virtual void reset(); virtual void reset();
/**
* ticks this dispatch.
*/
virtual void tick(); virtual void tick();
/**
* get this dispatch's state.
* @return a pointer to the dispatch's state. must be deallocated manually!
*/
virtual void* getState();
/**
* set this dispatch's state.
* @param state a pointer to a state pertaining to this dispatch,
* or NULL if this dispatch does not support state saves.
*/
virtual void setState(void* state);
/**
* mute a channel.
* @param ch the channel to mute.
* @param mute whether to mute or unmute.
*/
virtual void muteChannel(int ch, bool mute);
/**
* test whether this dispatch outputs audio in two channels.
* @return whether it does.
*/
virtual bool isStereo(); virtual bool isStereo();
/**
* test whether sending a key off command to a channel should reset arp too.
* @param ch the channel in question.
* @return whether it does.
*/
virtual bool keyOffAffectsArp(int ch); virtual bool keyOffAffectsArp(int ch);
/**
* set the region to PAL.
* @param pal whether to set it to PAL.
*/
virtual void setPAL(bool pal); virtual void setPAL(bool pal);
/** /**

View file

@ -1271,6 +1271,55 @@ bool DivEngine::isPlaying() {
return playing; return playing;
} }
bool DivEngine::isChannelMuted(int chan) {
return isMuted[chan];
}
void DivEngine::toggleMute(int chan) {
muteChannel(chan,!isMuted[chan]);
}
void DivEngine::toggleSolo(int chan) {
bool solo=false;
for (int i=0; i<chans; i++) {
if (i==chan) {
solo=true;
continue;
} else {
if (!isMuted[i]) {
solo=false;
break;
}
}
}
isBusy.lock();
if (!solo) {
for (int i=0; i<chans; i++) {
isMuted[i]=(i!=chan);
if (dispatch!=NULL) {
dispatch->muteChannel(i,isMuted[i]);
}
}
} else {
for (int i=0; i<chans; i++) {
isMuted[i]=false;
if (dispatch!=NULL) {
dispatch->muteChannel(i,isMuted[i]);
}
}
}
isBusy.unlock();
}
void DivEngine::muteChannel(int chan, bool mute) {
isBusy.lock();
isMuted[chan]=mute;
if (dispatch!=NULL) {
dispatch->muteChannel(chan,isMuted[chan]);
}
isBusy.unlock();
}
int DivEngine::addInstrument() { int DivEngine::addInstrument() {
isBusy.lock(); isBusy.lock();
DivInstrument* ins=new DivInstrument; DivInstrument* ins=new DivInstrument;
@ -1509,6 +1558,9 @@ void DivEngine::quitDispatch() {
totalCmds=0; totalCmds=0;
lastCmds=0; lastCmds=0;
cmdsPerSecond=0; cmdsPerSecond=0;
for (int i=0; i<17; i++) {
isMuted[i]=0;
}
isBusy.unlock(); isBusy.unlock();
} }
@ -1586,6 +1638,10 @@ bool DivEngine::init(String outName) {
vibTable[i]=127*sin(((double)i/64.0)*(2*M_PI)); vibTable[i]=127*sin(((double)i/64.0)*(2*M_PI));
} }
for (int i=0; i<17; i++) {
isMuted[i]=0;
}
initDispatch(); initDispatch();
reset(); reset();

View file

@ -77,6 +77,7 @@ class DivEngine {
DivStatusView view; DivStatusView view;
DivChannelState chan[17]; DivChannelState chan[17];
DivAudioEngines audioEngine; DivAudioEngines audioEngine;
bool isMuted[17];
std::mutex isBusy; std::mutex isBusy;
short vibTable[64]; short vibTable[64];
@ -132,6 +133,18 @@ class DivEngine {
// is STD system // is STD system
bool isSTDSystem(DivSystem sys); bool isSTDSystem(DivSystem sys);
// is channel muted
bool isChannelMuted(int chan);
// toggle mute
void toggleMute(int chan);
// toggle solo
void toggleSolo(int chan);
// set mute status
void muteChannel(int chan, bool mute);
// get channel name // get channel name
const char* getChannelName(int chan); const char* getChannelName(int chan);

View file

@ -6,6 +6,16 @@ void DivDispatch::acquire(short* bufL, short* bufR, size_t start, size_t len) {
void DivDispatch::tick() { void DivDispatch::tick() {
} }
void* DivDispatch::getState() {
return NULL;
}
void DivDispatch::setState(void* state) {
}
void DivDispatch::muteChannel(int ch, bool mute) {
}
int DivDispatch::dispatch(DivCommand c) { int DivDispatch::dispatch(DivCommand c) {
return 1; return 1;
} }

View file

@ -118,6 +118,7 @@ void DivPlatformArcade::acquire_ymfm(short* bufL, short* bufR, size_t start, siz
for (int i=8; i<13; i++) { for (int i=8; i<13; i++) {
if (chan[i].pcm.sample>=0 && chan[i].pcm.sample<parent->song.sampleLen) { if (chan[i].pcm.sample>=0 && chan[i].pcm.sample<parent->song.sampleLen) {
DivSample* s=parent->song.sample[chan[i].pcm.sample]; DivSample* s=parent->song.sample[chan[i].pcm.sample];
if (!isMuted[i]) {
if (s->depth==8) { if (s->depth==8) {
pcmL+=(s->rendData[chan[i].pcm.pos>>8]*chan[i].chVolL); pcmL+=(s->rendData[chan[i].pcm.pos>>8]*chan[i].chVolL);
pcmR+=(s->rendData[chan[i].pcm.pos>>8]*chan[i].chVolR); pcmR+=(s->rendData[chan[i].pcm.pos>>8]*chan[i].chVolR);
@ -125,6 +126,7 @@ void DivPlatformArcade::acquire_ymfm(short* bufL, short* bufR, size_t start, siz
pcmL+=(s->rendData[chan[i].pcm.pos>>8]*chan[i].chVolL)>>8; pcmL+=(s->rendData[chan[i].pcm.pos>>8]*chan[i].chVolL)>>8;
pcmR+=(s->rendData[chan[i].pcm.pos>>8]*chan[i].chVolR)>>8; pcmR+=(s->rendData[chan[i].pcm.pos>>8]*chan[i].chVolR)>>8;
} }
}
chan[i].pcm.pos+=chan[i].pcm.freq; chan[i].pcm.pos+=chan[i].pcm.freq;
if (chan[i].pcm.pos>=(s->rendLength<<8)) { if (chan[i].pcm.pos>=(s->rendLength<<8)) {
chan[i].pcm.sample=-1; chan[i].pcm.sample=-1;
@ -191,6 +193,18 @@ void DivPlatformArcade::tick() {
} }
} }
void DivPlatformArcade::muteChannel(int ch, bool mute) {
isMuted[ch]=mute;
if (ch<8) {
DivInstrument* ins=parent->getIns(chan[ch].ins);
if (isMuted[ch]) {
rWrite(chanOffs[ch]+0x20,(ins->fm.alg&7)|(ins->fm.fb<<3));
} else {
rWrite(chanOffs[ch]+0x20,(ins->fm.alg&7)|(ins->fm.fb<<3)|((chan[ch].chVolL&1)<<6)|((chan[ch].chVolR&1)<<7));
}
}
}
int DivPlatformArcade::dispatch(DivCommand c) { int DivPlatformArcade::dispatch(DivCommand c) {
switch (c.cmd) { switch (c.cmd) {
case DIV_CMD_NOTE_ON: { case DIV_CMD_NOTE_ON: {
@ -227,7 +241,11 @@ int DivPlatformArcade::dispatch(DivCommand c) {
} }
} }
if (chan[c.chan].insChanged) { if (chan[c.chan].insChanged) {
if (isMuted[c.chan]) {
rWrite(chanOffs[c.chan]+0x20,(ins->fm.alg&7)|(ins->fm.fb<<3));
} else {
rWrite(chanOffs[c.chan]+0x20,(ins->fm.alg&7)|(ins->fm.fb<<3)|((chan[c.chan].chVolL&1)<<6)|((chan[c.chan].chVolR&1)<<7)); rWrite(chanOffs[c.chan]+0x20,(ins->fm.alg&7)|(ins->fm.fb<<3)|((chan[c.chan].chVolL&1)<<6)|((chan[c.chan].chVolR&1)<<7));
}
rWrite(chanOffs[c.chan]+0x38,((ins->fm.fms&7)<<4)|(ins->fm.ams&3)); rWrite(chanOffs[c.chan]+0x38,((ins->fm.fms&7)<<4)|(ins->fm.ams&3));
} }
chan[c.chan].insChanged=false; chan[c.chan].insChanged=false;
@ -283,8 +301,12 @@ int DivPlatformArcade::dispatch(DivCommand c) {
DivInstrument* ins=parent->getIns(chan[c.chan].ins); DivInstrument* ins=parent->getIns(chan[c.chan].ins);
chan[c.chan].chVolL=((c.value>>4)==1); chan[c.chan].chVolL=((c.value>>4)==1);
chan[c.chan].chVolR=((c.value&15)==1); chan[c.chan].chVolR=((c.value&15)==1);
if (isMuted[c.chan]) {
rWrite(chanOffs[c.chan]+0x20,(ins->fm.alg&7)|(ins->fm.fb<<3));
} else {
rWrite(chanOffs[c.chan]+0x20,(ins->fm.alg&7)|(ins->fm.fb<<3)|((chan[c.chan].chVolL&1)<<6)|((chan[c.chan].chVolR&1)<<7)); rWrite(chanOffs[c.chan]+0x20,(ins->fm.alg&7)|(ins->fm.fb<<3)|((chan[c.chan].chVolL&1)<<6)|((chan[c.chan].chVolR&1)<<7));
} }
}
break; break;
} }
case DIV_CMD_PITCH: { case DIV_CMD_PITCH: {
@ -443,6 +465,9 @@ void DivPlatformArcade::setYMFM(bool use) {
int DivPlatformArcade::init(DivEngine* p, int channels, int sugRate, bool pal) { int DivPlatformArcade::init(DivEngine* p, int channels, int sugRate, bool pal) {
parent=p; parent=p;
for (int i=0; i<13; i++) {
isMuted[i]=false;
}
if (useYMFM) { if (useYMFM) {
rate=447443/8; rate=447443/8;
fm_ymfm=new ymfm::ym2151(iface); fm_ymfm=new ymfm::ym2151(iface);

View file

@ -49,6 +49,8 @@ class DivPlatformArcade: public DivDispatch {
bool extMode, useYMFM; bool extMode, useYMFM;
bool isMuted[13];
short oldWrites[256]; short oldWrites[256];
short pendingWrites[256]; short pendingWrites[256];
@ -63,6 +65,7 @@ class DivPlatformArcade: public DivDispatch {
int dispatch(DivCommand c); int dispatch(DivCommand c);
void reset(); void reset();
void tick(); void tick();
void muteChannel(int ch, bool mute);
bool isStereo(); bool isStereo();
void setYMFM(bool use); void setYMFM(bool use);
int init(DivEngine* parent, int channels, int sugRate, bool pal); int init(DivEngine* parent, int channels, int sugRate, bool pal);

View file

@ -55,16 +55,16 @@ void DivPlatformC64::tick() {
} }
if (chan[i].testWhen>0) { if (chan[i].testWhen>0) {
if (--chan[i].testWhen<1) { if (--chan[i].testWhen<1) {
if (!chan[i].resetMask) { if (!chan[i].resetMask && !isMuted[i]) {
sid.write(i*7+5,0); sid.write(i*7+5,0);
sid.write(i*7+6,0); sid.write(i*7+6,0);
sid.write(i*7+4,(chan[i].wave<<4)|8|(chan[i].ring<<2)|(chan[i].sync<<1)); sid.write(i*7+4,(isMuted[i]?0:(chan[i].wave<<4))|8|(chan[i].ring<<2)|(chan[i].sync<<1));
} }
} }
} }
if (chan[i].std.hadWave) { if (chan[i].std.hadWave) {
chan[i].wave=chan[i].std.wave; chan[i].wave=chan[i].std.wave;
sid.write(i*7+4,(chan[i].wave<<4)|(chan[i].ring<<2)|(chan[i].sync<<1)|chan[i].active); sid.write(i*7+4,(isMuted[i]?0:(chan[i].wave<<4))|(chan[i].ring<<2)|(chan[i].sync<<1)|chan[i].active);
} }
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
chan[i].freq=(chan[i].baseFreq*(ONE_SEMITONE+chan[i].pitch))/ONE_SEMITONE; chan[i].freq=(chan[i].baseFreq*(ONE_SEMITONE+chan[i].pitch))/ONE_SEMITONE;
@ -72,12 +72,12 @@ void DivPlatformC64::tick() {
if (chan[i].keyOn) { if (chan[i].keyOn) {
sid.write(i*7+5,(chan[i].attack<<4)|(chan[i].decay)); sid.write(i*7+5,(chan[i].attack<<4)|(chan[i].decay));
sid.write(i*7+6,(chan[i].sustain<<4)|(chan[i].release)); sid.write(i*7+6,(chan[i].sustain<<4)|(chan[i].release));
sid.write(i*7+4,(chan[i].wave<<4)|(chan[i].ring<<2)|(chan[i].sync<<1)|1); sid.write(i*7+4,(isMuted[i]?0:(chan[i].wave<<4))|(chan[i].ring<<2)|(chan[i].sync<<1)|1);
} }
if (chan[i].keyOff) { if (chan[i].keyOff && !isMuted[i]) {
sid.write(i*7+5,(chan[i].attack<<4)|(chan[i].decay)); sid.write(i*7+5,(chan[i].attack<<4)|(chan[i].decay));
sid.write(i*7+6,(chan[i].sustain<<4)|(chan[i].release)); sid.write(i*7+6,(chan[i].sustain<<4)|(chan[i].release));
sid.write(i*7+4,(chan[i].wave<<4)|(chan[i].ring<<2)|(chan[i].sync<<1)|0); sid.write(i*7+4,(isMuted[i]?0:(chan[i].wave<<4))|(chan[i].ring<<2)|(chan[i].sync<<1)|0);
} }
sid.write(i*7,chan[i].freq&0xff); sid.write(i*7,chan[i].freq&0xff);
sid.write(i*7+1,chan[i].freq>>8); sid.write(i*7+1,chan[i].freq>>8);
@ -278,6 +278,11 @@ int DivPlatformC64::dispatch(DivCommand c) {
return 1; return 1;
} }
void DivPlatformC64::muteChannel(int ch, bool mute) {
isMuted[ch]=mute;
sid.write(ch*7+4,(isMuted[ch]?0:(chan[ch].wave<<4))|(chan[ch].ring<<2)|(chan[ch].sync<<1)|chan[ch].active);
}
void DivPlatformC64::reset() { void DivPlatformC64::reset() {
for (int i=0; i<3; i++) { for (int i=0; i<3; i++) {
chan[i]=DivPlatformC64::Channel(); chan[i]=DivPlatformC64::Channel();
@ -312,6 +317,9 @@ void DivPlatformC64::setPAL(bool pal) {
int DivPlatformC64::init(DivEngine* p, int channels, int sugRate, bool pal) { int DivPlatformC64::init(DivEngine* p, int channels, int sugRate, bool pal) {
parent=p; parent=p;
for (int i=0; i<3; i++) {
isMuted[i]=false;
}
setPAL(pal); setPAL(pal);
reset(); reset();

View file

@ -45,6 +45,7 @@ class DivPlatformC64: public DivDispatch {
vol(15) {} vol(15) {}
}; };
Channel chan[3]; Channel chan[3];
bool isMuted[3];
unsigned char filtControl, filtRes, vol; unsigned char filtControl, filtRes, vol;
int filtCut, resetTime; int filtCut, resetTime;
@ -57,6 +58,7 @@ class DivPlatformC64: public DivDispatch {
int dispatch(DivCommand c); int dispatch(DivCommand c);
void reset(); void reset();
void tick(); void tick();
void muteChannel(int ch, bool mute);
void setPAL(bool pal); void setPAL(bool pal);
int init(DivEngine* parent, int channels, int sugRate, bool pal); int init(DivEngine* parent, int channels, int sugRate, bool pal);
void setChipModel(bool is6581); void setChipModel(bool is6581);

View file

@ -7,17 +7,21 @@ void DivPlatformDummy::acquire(short* bufL, short* bufR, size_t start, size_t le
bufL[i]=0; bufL[i]=0;
for (unsigned char j=0; j<chans; j++) { for (unsigned char j=0; j<chans; j++) {
if (chan[j].active) { if (chan[j].active) {
bufL[i]+=((chan[j].pos>=0x8000)?chan[j].vol:-chan[j].vol)*chan[j].amp; if (!isMuted[j]) bufL[i]+=(((signed short)chan[j].pos)*chan[j].amp*chan[j].vol)>>13;
chan[j].pos+=chan[j].freq; chan[j].pos+=chan[j].freq;
} }
} }
} }
} }
void DivPlatformDummy::muteChannel(int ch, bool mute) {
isMuted[ch]=mute;
}
void DivPlatformDummy::tick() { void DivPlatformDummy::tick() {
for (unsigned char i=0; i<chans; i++) { for (unsigned char i=0; i<chans; i++) {
chan[i].amp--; chan[i].amp-=3;
if (chan[i].amp<0) chan[i].amp=0; if (chan[i].amp<16) chan[i].amp=16;
if (chan[i].freqChanged) { if (chan[i].freqChanged) {
chan[i].freqChanged=false; chan[i].freqChanged=false;
@ -29,7 +33,7 @@ void DivPlatformDummy::tick() {
int DivPlatformDummy::dispatch(DivCommand c) { int DivPlatformDummy::dispatch(DivCommand c) {
switch (c.cmd) { switch (c.cmd) {
case DIV_CMD_NOTE_ON: case DIV_CMD_NOTE_ON:
chan[c.chan].baseFreq=16.4f*pow(2.0f,((float)c.value/12.0f)); chan[c.chan].baseFreq=65.6f*pow(2.0f,((float)c.value/12.0f));
chan[c.chan].freqChanged=true; chan[c.chan].freqChanged=true;
chan[c.chan].active=true; chan[c.chan].active=true;
chan[c.chan].amp=64; chan[c.chan].amp=64;
@ -49,7 +53,7 @@ int DivPlatformDummy::dispatch(DivCommand c) {
chan[c.chan].freqChanged=true; chan[c.chan].freqChanged=true;
break; break;
case DIV_CMD_LEGATO: case DIV_CMD_LEGATO:
chan[c.chan].baseFreq=16.4f*pow(2.0f,((float)c.value/12.0f)); chan[c.chan].baseFreq=65.6f*pow(2.0f,((float)c.value/12.0f));
chan[c.chan].freqChanged=true; chan[c.chan].freqChanged=true;
break; break;
case DIV_CMD_GET_VOLMAX: case DIV_CMD_GET_VOLMAX:
@ -70,6 +74,9 @@ void DivPlatformDummy::reset() {
int DivPlatformDummy::init(DivEngine* p, int channels, int sugRate, bool pal) { int DivPlatformDummy::init(DivEngine* p, int channels, int sugRate, bool pal) {
parent=p; parent=p;
for (int i=0; i<17; i++) {
isMuted[i]=false;
}
rate=65536; rate=65536;
chans=channels; chans=channels;
reset(); reset();

View file

@ -1,6 +1,6 @@
#include "../dispatch.h" #include "../dispatch.h"
// the dummy platform outputs square waves, interprets STD instruments and plays samples. // the dummy platform outputs saw waves.
// used when a DivDispatch for a system is not found. // used when a DivDispatch for a system is not found.
class DivPlatformDummy: public DivDispatch { class DivPlatformDummy: public DivDispatch {
struct Channel { struct Channel {
@ -13,9 +13,11 @@ class DivPlatformDummy: public DivDispatch {
Channel(): freq(0), baseFreq(0), pitch(0), pos(0), active(false), freqChanged(false), vol(0), amp(64) {} Channel(): freq(0), baseFreq(0), pitch(0), pos(0), active(false), freqChanged(false), vol(0), amp(64) {}
}; };
Channel chan[17]; Channel chan[17];
bool isMuted[17];
unsigned char chans; unsigned char chans;
public: public:
void acquire(short* bufL, short* bufR, size_t start, size_t len); void acquire(short* bufL, short* bufR, size_t start, size_t len);
void muteChannel(int ch, bool mute);
int dispatch(DivCommand c); int dispatch(DivCommand c);
void reset(); void reset();
void tick(); void tick();

View file

@ -24,6 +24,17 @@ void DivPlatformGB::updateWave() {
} }
} }
static unsigned char chanMuteMask[4]={
0xee, 0xdd, 0xbb, 0x77
};
unsigned char DivPlatformGB::procMute() {
return lastPan&(isMuted[0]?chanMuteMask[0]:0xff)
&(isMuted[1]?chanMuteMask[1]:0xff)
&(isMuted[2]?chanMuteMask[2]:0xff)
&(isMuted[3]?chanMuteMask[3]:0xff);
}
static unsigned char gbVolMap[16]={ static unsigned char gbVolMap[16]={
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60,
@ -153,6 +164,11 @@ void DivPlatformGB::tick() {
} }
} }
void DivPlatformGB::muteChannel(int ch, bool mute) {
isMuted[ch]=mute;
rWrite(0x25,procMute());
}
int DivPlatformGB::dispatch(DivCommand c) { int DivPlatformGB::dispatch(DivCommand c) {
switch (c.cmd) { switch (c.cmd) {
case DIV_CMD_NOTE_ON: case DIV_CMD_NOTE_ON:
@ -233,7 +249,7 @@ int DivPlatformGB::dispatch(DivCommand c) {
lastPan&=~(0x11<<c.chan); lastPan&=~(0x11<<c.chan);
if (c.value==0) c.value=0x11; if (c.value==0) c.value=0x11;
lastPan|=c.value<<c.chan; lastPan|=c.value<<c.chan;
rWrite(0x25,lastPan); rWrite(0x25,procMute());
break; break;
} }
case DIV_CMD_LEGATO: case DIV_CMD_LEGATO:
@ -280,8 +296,8 @@ void DivPlatformGB::reset() {
GB_set_sample_rate(gb,rate); GB_set_sample_rate(gb,rate);
// enable all channels // enable all channels
GB_apu_write(gb,0x26,0x80); GB_apu_write(gb,0x26,0x80);
GB_apu_write(gb,0x25,0xff);
lastPan=0xff; lastPan=0xff;
GB_apu_write(gb,0x25,procMute());
} }
bool DivPlatformGB::isStereo() { bool DivPlatformGB::isStereo() {
@ -289,6 +305,9 @@ bool DivPlatformGB::isStereo() {
} }
int DivPlatformGB::init(DivEngine* p, int channels, int sugRate, bool pal) { int DivPlatformGB::init(DivEngine* p, int channels, int sugRate, bool pal) {
for (int i=0; i<4; i++) {
isMuted[i]=false;
}
parent=p; parent=p;
rate=262144; rate=262144;
gb=new GB_gameboy_t; gb=new GB_gameboy_t;

View file

@ -31,15 +31,18 @@ class DivPlatformGB: public DivDispatch {
wave(-1) {} wave(-1) {}
}; };
Channel chan[4]; Channel chan[4];
bool isMuted[4];
unsigned char lastPan; unsigned char lastPan;
GB_gameboy_t* gb; GB_gameboy_t* gb;
unsigned char procMute();
void updateWave(); void updateWave();
public: public:
void acquire(short* bufL, short* bufR, size_t start, size_t len); void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c); int dispatch(DivCommand c);
void reset(); void reset();
void tick(); void tick();
void muteChannel(int ch, bool mute);
bool isStereo(); bool isStereo();
int init(DivEngine* parent, int channels, int sugRate, bool pal); int init(DivEngine* parent, int channels, int sugRate, bool pal);
void quit(); void quit();

View file

@ -151,6 +151,16 @@ int DivPlatformGenesis::toFreq(int freq) {
} }
} }
void DivPlatformGenesis::muteChannel(int ch, bool mute) {
if (ch>5) {
psg.muteChannel(ch,mute);
return;
}
isMuted[ch]=mute;
DivInstrument* ins=parent->getIns(chan[ch].ins);
rWrite(chanOffs[ch]+0xb4,(isMuted[ch]?0:(chan[ch].pan<<6))|(ins->fm.fms&7)|((ins->fm.ams&3)<<4));
}
int DivPlatformGenesis::dispatch(DivCommand c) { int DivPlatformGenesis::dispatch(DivCommand c) {
if (c.chan>5) { if (c.chan>5) {
c.chan-=6; c.chan-=6;
@ -195,7 +205,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
} }
if (chan[c.chan].insChanged) { if (chan[c.chan].insChanged) {
rWrite(chanOffs[c.chan]+0xb0,(ins->fm.alg&7)|(ins->fm.fb<<3)); rWrite(chanOffs[c.chan]+0xb0,(ins->fm.alg&7)|(ins->fm.fb<<3));
rWrite(chanOffs[c.chan]+0xb4,(chan[c.chan].pan<<6)|(ins->fm.fms&7)|((ins->fm.ams&3)<<4)); rWrite(chanOffs[c.chan]+0xb4,(isMuted[c.chan]?0:(chan[c.chan].pan<<6))|(ins->fm.fms&7)|((ins->fm.ams&3)<<4));
} }
chan[c.chan].insChanged=false; chan[c.chan].insChanged=false;
@ -249,7 +259,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break; break;
} }
DivInstrument* ins=parent->getIns(chan[c.chan].ins); DivInstrument* ins=parent->getIns(chan[c.chan].ins);
rWrite(chanOffs[c.chan]+0xb4,(chan[c.chan].pan<<6)|(ins->fm.fms&7)|((ins->fm.ams&3)<<4)); rWrite(chanOffs[c.chan]+0xb4,(isMuted[c.chan]?0:(chan[c.chan].pan<<6))|(ins->fm.fms&7)|((ins->fm.ams&3)<<4));
break; break;
} }
case DIV_CMD_PITCH: { case DIV_CMD_PITCH: {
@ -410,6 +420,9 @@ void DivPlatformGenesis::setPAL(bool pal) {
int DivPlatformGenesis::init(DivEngine* p, int channels, int sugRate, bool pal) { int DivPlatformGenesis::init(DivEngine* p, int channels, int sugRate, bool pal) {
parent=p; parent=p;
for (int i=0; i<10; i++) {
isMuted[i]=false;
}
setPAL(pal); setPAL(pal);
// PSG // PSG
psg.init(p,4,sugRate,pal); psg.init(p,4,sugRate,pal);

View file

@ -19,6 +19,7 @@ class DivPlatformGenesis: public DivDispatch {
Channel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), vol(0), pan(3) {} Channel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), vol(0), pan(3) {}
}; };
Channel chan[10]; Channel chan[10];
bool isMuted[10];
struct QueuedWrite { struct QueuedWrite {
unsigned short addr; unsigned short addr;
unsigned char val; unsigned char val;
@ -53,6 +54,7 @@ class DivPlatformGenesis: public DivDispatch {
int dispatch(DivCommand c); int dispatch(DivCommand c);
void reset(); void reset();
void tick(); void tick();
void muteChannel(int ch, bool mute);
bool isStereo(); bool isStereo();
bool keyOffAffectsArp(int ch); bool keyOffAffectsArp(int ch);
void setPAL(bool pal); void setPAL(bool pal);

View file

@ -20,7 +20,9 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
unsigned short baseAddr=chanOffs[2]|opOffs[ordch]; unsigned short baseAddr=chanOffs[2]|opOffs[ordch];
DivInstrumentFM::Operator op=ins->fm.op[ordch]; DivInstrumentFM::Operator op=ins->fm.op[ordch];
if (isOutput[ins->fm.alg][ordch]) { if (isOpMuted[ch]) {
rWrite(baseAddr+0x40,127);
} else if (isOutput[ins->fm.alg][ordch]) {
if (!opChan[ch].active || opChan[ch].insChanged) { if (!opChan[ch].active || opChan[ch].insChanged) {
rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch].vol&0x7f))/127)); rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch].vol&0x7f))/127));
} }
@ -58,7 +60,9 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
DivInstrument* ins=parent->getIns(opChan[ch].ins); DivInstrument* ins=parent->getIns(opChan[ch].ins);
unsigned short baseAddr=chanOffs[2]|opOffs[ordch]; unsigned short baseAddr=chanOffs[2]|opOffs[ordch];
DivInstrumentFM::Operator op=ins->fm.op[ordch]; DivInstrumentFM::Operator op=ins->fm.op[ordch];
if (isOutput[ins->fm.alg][ordch]) { if (isOpMuted[ch]) {
rWrite(baseAddr+0x40,127);
} else if (isOutput[ins->fm.alg][ordch]) {
rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch].vol&0x7f))/127)); rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch].vol&0x7f))/127));
} else { } else {
rWrite(baseAddr+0x40,op.tl); rWrite(baseAddr+0x40,op.tl);
@ -182,6 +186,30 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
return 1; return 1;
} }
void DivPlatformGenesisExt::muteChannel(int ch, bool mute) {
if (ch<2) {
DivPlatformGenesis::muteChannel(ch,mute);
return;
}
if (ch>5) {
DivPlatformGenesis::muteChannel(ch-3,mute);
return;
}
isOpMuted[ch-2]=mute;
int ordch=orderedOps[ch];
DivInstrument* ins=parent->getIns(opChan[ch].ins);
unsigned short baseAddr=chanOffs[2]|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-(((127-op.tl)*(opChan[ch].vol&0x7f))/127));
} else {
rWrite(baseAddr+0x40,op.tl);
}
}
static int opChanOffsL[4]={ static int opChanOffsL[4]={
0xa9, 0xaa, 0xa8, 0xa2 0xa9, 0xaa, 0xa8, 0xa2
}; };
@ -273,6 +301,9 @@ bool DivPlatformGenesisExt::keyOffAffectsArp(int ch) {
int DivPlatformGenesisExt::init(DivEngine* parent, int channels, int sugRate, bool pal) { int DivPlatformGenesisExt::init(DivEngine* parent, int channels, int sugRate, bool pal) {
DivPlatformGenesis::init(parent,channels,sugRate,pal); DivPlatformGenesis::init(parent,channels,sugRate,pal);
for (int i=0; i<4; i++) {
isOpMuted[i]=false;
}
reset(); reset();
return 13; return 13;

View file

@ -14,10 +14,12 @@ class DivPlatformGenesisExt: public DivPlatformGenesis {
OpChannel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), vol(0), pan(3) {} OpChannel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), vol(0), pan(3) {}
}; };
OpChannel opChan[4]; OpChannel opChan[4];
bool isOpMuted[4];
public: public:
int dispatch(DivCommand c); int dispatch(DivCommand c);
void reset(); void reset();
void tick(); void tick();
void muteChannel(int ch, bool mute);
bool keyOffAffectsArp(int ch); bool keyOffAffectsArp(int ch);
int init(DivEngine* parent, int channels, int sugRate, bool pal); int init(DivEngine* parent, int channels, int sugRate, bool pal);
void quit(); void quit();

View file

@ -12,11 +12,13 @@ void DivPlatformNES::acquire(short* bufL, short* bufR, size_t start, size_t len)
dacPeriod+=dacRate; dacPeriod+=dacRate;
if (dacPeriod>=rate) { if (dacPeriod>=rate) {
DivSample* s=parent->song.sample[dacSample]; DivSample* s=parent->song.sample[dacSample];
if (!isMuted[4]) {
if (s->depth==8) { if (s->depth==8) {
apu_wr_reg(0x4011,((unsigned char)s->rendData[dacPos++]+0x80)>>1); apu_wr_reg(0x4011,((unsigned char)s->rendData[dacPos++]+0x80)>>1);
} else { } else {
apu_wr_reg(0x4011,((unsigned short)s->rendData[dacPos++]+0x8000)>>9); apu_wr_reg(0x4011,((unsigned short)s->rendData[dacPos++]+0x8000)>>9);
} }
}
if (dacPos>=s->rendLength) { if (dacPos>=s->rendLength) {
dacSample=-1; dacSample=-1;
} }
@ -272,6 +274,14 @@ int DivPlatformNES::dispatch(DivCommand c) {
return 1; return 1;
} }
void DivPlatformNES::muteChannel(int ch, bool mute) {
isMuted[ch]=mute;
apu_wr_reg(0x4015,(!isMuted[0])|((!isMuted[1])<<1)|((!isMuted[2])<<2)|((!isMuted[3])<<3)|((!isMuted[4])<<4));
if (isMuted[4]) {
apu_wr_reg(0x4011,0);
}
}
void DivPlatformNES::reset() { void DivPlatformNES::reset() {
for (int i=0; i<5; i++) { for (int i=0; i<5; i++) {
chan[i]=DivPlatformNES::Channel(); chan[i]=DivPlatformNES::Channel();
@ -287,7 +297,7 @@ void DivPlatformNES::reset() {
apu.cpu_cycles=0; apu.cpu_cycles=0;
apu.cpu_opcode_cycle=0; apu.cpu_opcode_cycle=0;
apu_wr_reg(0x4015,0x1f); apu_wr_reg(0x4015,(!isMuted[0])|((!isMuted[1])<<1)|((!isMuted[2])<<2)|((!isMuted[3])<<3)|((!isMuted[4])<<4));
apu_wr_reg(0x4001,0x08); apu_wr_reg(0x4001,0x08);
apu_wr_reg(0x4005,0x08); apu_wr_reg(0x4005,0x08);
} }
@ -308,6 +318,9 @@ void DivPlatformNES::setPAL(bool pal) {
int DivPlatformNES::init(DivEngine* p, int channels, int sugRate, bool pal) { int DivPlatformNES::init(DivEngine* p, int channels, int sugRate, bool pal) {
parent=p; parent=p;
for (int i=0; i<5; i++) {
isMuted[i]=false;
}
setPAL(pal); setPAL(pal);
init_nla_table(500,500); init_nla_table(500,500);

View file

@ -32,6 +32,7 @@ class DivPlatformNES: public DivDispatch {
wave(-1) {} wave(-1) {}
}; };
Channel chan[5]; Channel chan[5];
bool isMuted[5];
int dacPeriod, dacRate; int dacPeriod, dacRate;
unsigned int dacPos; unsigned int dacPos;
int dacSample; int dacSample;
@ -46,6 +47,7 @@ class DivPlatformNES: public DivDispatch {
int dispatch(DivCommand c); int dispatch(DivCommand c);
void reset(); void reset();
void tick(); void tick();
void muteChannel(int ch, bool mute);
bool keyOffAffectsArp(int ch); bool keyOffAffectsArp(int ch);
void setPAL(bool pal); void setPAL(bool pal);
int init(DivEngine* parent, int channels, int sugRate, bool pal); int init(DivEngine* parent, int channels, int sugRate, bool pal);

View file

@ -239,7 +239,7 @@ int DivPlatformPCE::dispatch(DivCommand c) {
break; break;
case DIV_CMD_PANNING: { case DIV_CMD_PANNING: {
chan[c.chan].pan=c.value; chan[c.chan].pan=c.value;
chWrite(c.chan,0x05,chan[c.chan].pan); chWrite(c.chan,0x05,isMuted[c.chan]?0:chan[c.chan].pan);
break; break;
} }
case DIV_CMD_LEGATO: case DIV_CMD_LEGATO:
@ -263,6 +263,11 @@ int DivPlatformPCE::dispatch(DivCommand c) {
return 1; return 1;
} }
void DivPlatformPCE::muteChannel(int ch, bool mute) {
isMuted[ch]=mute;
chWrite(ch,0x05,isMuted[ch]?0:chan[ch].pan);
}
void DivPlatformPCE::reset() { void DivPlatformPCE::reset() {
while (!writes.empty()) writes.pop(); while (!writes.empty()) writes.pop();
for (int i=0; i<6; i++) { for (int i=0; i<6; i++) {
@ -280,7 +285,7 @@ void DivPlatformPCE::reset() {
rWrite(0x01,0xff); rWrite(0x01,0xff);
// set per-channel initial panning // set per-channel initial panning
for (int i=0; i<6; i++) { for (int i=0; i<6; i++) {
chWrite(i,0x05,chan[i].pan); chWrite(i,0x05,isMuted[i]?0:chan[i].pan);
} }
delay=500; delay=500;
} }
@ -303,6 +308,9 @@ void DivPlatformPCE::setPAL(bool pal) {
int DivPlatformPCE::init(DivEngine* p, int channels, int sugRate, bool pal) { int DivPlatformPCE::init(DivEngine* p, int channels, int sugRate, bool pal) {
parent=p; parent=p;
for (int i=0; i<6; i++) {
isMuted[i]=false;
}
setPAL(pal); setPAL(pal);
pce=new PCE_PSG(&tempL,&tempR,PCE_PSG::REVISION_HUC6280); pce=new PCE_PSG(&tempL,&tempR,PCE_PSG::REVISION_HUC6280);
reset(); reset();

View file

@ -40,6 +40,7 @@ class DivPlatformPCE: public DivDispatch {
wave(-1) {} wave(-1) {}
}; };
Channel chan[6]; Channel chan[6];
bool isMuted[6];
struct QueuedWrite { struct QueuedWrite {
unsigned char addr; unsigned char addr;
unsigned char val; unsigned char val;
@ -57,6 +58,7 @@ class DivPlatformPCE: public DivDispatch {
int dispatch(DivCommand c); int dispatch(DivCommand c);
void reset(); void reset();
void tick(); void tick();
void muteChannel(int ch, bool mute);
bool isStereo(); bool isStereo();
bool keyOffAffectsArp(int ch); bool keyOffAffectsArp(int ch);
void setPAL(bool pal); void setPAL(bool pal);

View file

@ -17,7 +17,7 @@ void DivPlatformSMS::tick() {
chan[i].std.next(); chan[i].std.next();
if (chan[i].std.hadVol) { if (chan[i].std.hadVol) {
chan[i].outVol=(chan[i].vol*chan[i].std.vol)>>4; chan[i].outVol=(chan[i].vol*chan[i].std.vol)>>4;
sn->write(0x90|(i<<5)|(15-(chan[i].outVol&15))); sn->write(0x90|(i<<5)|(isMuted[i]?15:(15-(chan[i].outVol&15))));
} }
if (chan[i].std.hadArp) { if (chan[i].std.hadArp) {
if (chan[i].std.arpMode) { if (chan[i].std.arpMode) {
@ -84,7 +84,7 @@ int DivPlatformSMS::dispatch(DivCommand c) {
chan[c.chan].freqChanged=true; chan[c.chan].freqChanged=true;
chan[c.chan].note=c.value; chan[c.chan].note=c.value;
chan[c.chan].active=true; chan[c.chan].active=true;
sn->write(0x90|c.chan<<5|(15-(chan[c.chan].vol&15))); sn->write(0x90|c.chan<<5|(isMuted[c.chan]?15:(15-(chan[c.chan].vol&15))));
chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); chan[c.chan].std.init(parent->getIns(chan[c.chan].ins));
break; break;
case DIV_CMD_NOTE_OFF: case DIV_CMD_NOTE_OFF:
@ -102,7 +102,7 @@ int DivPlatformSMS::dispatch(DivCommand c) {
if (!chan[c.chan].std.hasVol) { if (!chan[c.chan].std.hasVol) {
chan[c.chan].outVol=c.value; chan[c.chan].outVol=c.value;
} }
sn->write(0x90|c.chan<<5|(15-(chan[c.chan].vol&15))); sn->write(0x90|c.chan<<5|(isMuted[c.chan]?15:(15-(chan[c.chan].vol&15))));
} }
break; break;
case DIV_CMD_GET_VOLUME: case DIV_CMD_GET_VOLUME:
@ -159,6 +159,11 @@ int DivPlatformSMS::dispatch(DivCommand c) {
return 1; return 1;
} }
void DivPlatformSMS::muteChannel(int ch, bool mute) {
isMuted[ch]=mute;
if (chan[ch].active) sn->write(0x90|ch<<5|(isMuted[ch]?15:(15-(chan[ch].outVol&15))));
}
void DivPlatformSMS::reset() { void DivPlatformSMS::reset() {
for (int i=0; i<4; i++) { for (int i=0; i<4; i++) {
chan[i]=DivPlatformSMS::Channel(); chan[i]=DivPlatformSMS::Channel();
@ -182,6 +187,9 @@ void DivPlatformSMS::setPAL(bool pal) {
int DivPlatformSMS::init(DivEngine* p, int channels, int sugRate, bool pal) { int DivPlatformSMS::init(DivEngine* p, int channels, int sugRate, bool pal) {
parent=p; parent=p;
for (int i=0; i<4; i++) {
isMuted[i]=false;
}
setPAL(pal); setPAL(pal);
sn=new sn76496_device(rate); sn=new sn76496_device(rate);
reset(); reset();

View file

@ -27,6 +27,7 @@ class DivPlatformSMS: public DivDispatch {
outVol(15) {} outVol(15) {}
}; };
Channel chan[4]; Channel chan[4];
bool isMuted[4];
unsigned char snNoiseMode; unsigned char snNoiseMode;
bool updateSNMode; bool updateSNMode;
sn76496_device* sn; sn76496_device* sn;
@ -36,6 +37,7 @@ class DivPlatformSMS: public DivDispatch {
int dispatch(DivCommand c); int dispatch(DivCommand c);
void reset(); void reset();
void tick(); void tick();
void muteChannel(int ch, bool mute);
bool keyOffAffectsArp(int ch); bool keyOffAffectsArp(int ch);
void setPAL(bool pal); void setPAL(bool pal);
int init(DivEngine* parent, int channels, int sugRate, bool pal); int init(DivEngine* parent, int channels, int sugRate, bool pal);

View file

@ -46,8 +46,12 @@ void DivPlatformYM2610::tick() {
if (chan[i].std.hadVol) { if (chan[i].std.hadVol) {
chan[i].outVol=chan[i].std.vol-(15-chan[i].vol); chan[i].outVol=chan[i].std.vol-(15-chan[i].vol);
if (chan[i].outVol<0) chan[i].outVol=0; if (chan[i].outVol<0) chan[i].outVol=0;
if (isMuted[i]) {
rWrite(0x04+i,0);
} else {
rWrite(0x04+i,(chan[i].outVol&15)|((chan[i].psgMode&4)<<2)); rWrite(0x04+i,(chan[i].outVol&15)|((chan[i].psgMode&4)<<2));
} }
}
if (chan[i].std.hadArp) { if (chan[i].std.hadArp) {
if (!chan[i].inPorta) { if (!chan[i].inPorta) {
if (chan[i].std.arpMode) { if (chan[i].std.arpMode) {
@ -187,7 +191,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
int end=s->rendOff+s->adpcmRendLength-1; int end=s->rendOff+s->adpcmRendLength-1;
writes.emplace(0x120+c.chan-7,(end>>8)&0xff); writes.emplace(0x120+c.chan-7,(end>>8)&0xff);
writes.emplace(0x128+c.chan-7,end>>16); writes.emplace(0x128+c.chan-7,end>>16);
writes.emplace(0x108+(c.chan-7),(chan[c.chan].pan<<6)|chan[c.chan].vol); writes.emplace(0x108+(c.chan-7),isMuted[c.chan]?0:((chan[c.chan].pan<<6)|chan[c.chan].vol));
writes.emplace(0x100,0x00|(1<<(c.chan-7))); writes.emplace(0x100,0x00|(1<<(c.chan-7)));
break; break;
} }
@ -200,7 +204,11 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
chan[c.chan].active=true; chan[c.chan].active=true;
chan[c.chan].keyOn=true; chan[c.chan].keyOn=true;
chan[c.chan].std.init(ins); chan[c.chan].std.init(ins);
if (isMuted[c.chan]) {
rWrite(0x04+c.chan,0);
} else {
rWrite(0x04+c.chan,(chan[c.chan].vol&15)|((chan[c.chan].psgMode&4)<<2)); rWrite(0x04+c.chan,(chan[c.chan].vol&15)|((chan[c.chan].psgMode&4)<<2));
}
break; break;
} }
@ -227,7 +235,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
} }
if (chan[c.chan].insChanged) { if (chan[c.chan].insChanged) {
rWrite(chanOffs[c.chan]+0xb0,(ins->fm.alg&7)|(ins->fm.fb<<3)); rWrite(chanOffs[c.chan]+0xb0,(ins->fm.alg&7)|(ins->fm.fb<<3));
rWrite(chanOffs[c.chan]+0xb4,(chan[c.chan].pan<<6)|(ins->fm.fms&7)|((ins->fm.ams&3)<<4)); rWrite(chanOffs[c.chan]+0xb4,(isMuted[c.chan]?0:(chan[c.chan].pan<<6))|(ins->fm.fms&7)|((ins->fm.ams&3)<<4));
} }
chan[c.chan].insChanged=false; chan[c.chan].insChanged=false;
@ -250,14 +258,18 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
chan[c.chan].vol=c.value; chan[c.chan].vol=c.value;
DivInstrument* ins=parent->getIns(chan[c.chan].ins); DivInstrument* ins=parent->getIns(chan[c.chan].ins);
if (c.chan>6) { // ADPCM if (c.chan>6) { // ADPCM
writes.emplace(0x108+(c.chan-7),(chan[c.chan].pan<<6)|chan[c.chan].vol); writes.emplace(0x108+(c.chan-7),isMuted[c.chan]?0:((chan[c.chan].pan<<6)|chan[c.chan].vol));
break; break;
} }
if (c.chan>3) { // PSG if (c.chan>3) { // PSG
if (!chan[c.chan].std.hasVol) { if (!chan[c.chan].std.hasVol) {
chan[c.chan].outVol=c.value; chan[c.chan].outVol=c.value;
} }
if (isMuted[c.chan]) {
rWrite(0x04+c.chan,0);
} else {
rWrite(0x04+c.chan,(chan[c.chan].vol&15)|((chan[c.chan].psgMode&4)<<2)); rWrite(0x04+c.chan,(chan[c.chan].vol&15)|((chan[c.chan].psgMode&4)<<2));
}
break; break;
} }
for (int i=0; i<4; i++) { for (int i=0; i<4; i++) {
@ -294,12 +306,12 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
break; break;
} }
if (c.chan>6) { if (c.chan>6) {
writes.emplace(0x108+(c.chan-7),(chan[c.chan].pan<<6)|chan[c.chan].vol); writes.emplace(0x108+(c.chan-7),isMuted[c.chan]?0:((chan[c.chan].pan<<6)|chan[c.chan].vol));
break; break;
} }
if (c.chan>3) break; if (c.chan>3) break;
DivInstrument* ins=parent->getIns(chan[c.chan].ins); DivInstrument* ins=parent->getIns(chan[c.chan].ins);
rWrite(chanOffs[c.chan]+0xb4,(chan[c.chan].pan<<6)|(ins->fm.fms&7)|((ins->fm.ams&3)<<4)); rWrite(chanOffs[c.chan]+0xb4,(isMuted[c.chan]?0:(chan[c.chan].pan<<6))|(ins->fm.fms&7)|((ins->fm.ams&3)<<4));
break; break;
} }
case DIV_CMD_PITCH: { case DIV_CMD_PITCH: {
@ -428,7 +440,11 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
} else { } else {
chan[c.chan].psgMode&=~4; chan[c.chan].psgMode&=~4;
} }
if (isMuted[c.chan]) {
rWrite(0x04+c.chan,0);
} else {
rWrite(0x04+c.chan,(chan[c.chan].vol&15)|((chan[c.chan].psgMode&4)<<2)); rWrite(0x04+c.chan,(chan[c.chan].vol&15)|((chan[c.chan].psgMode&4)<<2));
}
break; break;
case DIV_CMD_AY_ENVELOPE_LOW: case DIV_CMD_AY_ENVELOPE_LOW:
if (c.chan<4 || c.chan>6) break; if (c.chan<4 || c.chan>6) break;
@ -467,6 +483,25 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
return 1; return 1;
} }
void DivPlatformYM2610::muteChannel(int ch, bool mute) {
isMuted[ch]=mute;
if (ch>6) { // ADPCM
writes.emplace(0x108+(ch-7),isMuted[ch]?0:((chan[ch].pan<<6)|chan[ch].vol));
return;
}
if (ch>3) { // PSG
if (isMuted[ch]) {
rWrite(0x04+ch,0);
} else {
rWrite(0x04+ch,(chan[ch].outVol&15)|((chan[ch].psgMode&4)<<2));
}
return;
}
// FM
DivInstrument* ins=parent->getIns(chan[ch].ins);
rWrite(chanOffs[ch]+0xb4,(isMuted[ch]?0:(chan[ch].pan<<6))|(ins->fm.fms&7)|((ins->fm.ams&3)<<4));
}
void DivPlatformYM2610::reset() { void DivPlatformYM2610::reset() {
while (!writes.empty()) writes.pop(); while (!writes.empty()) writes.pop();
fm->reset(); fm->reset();
@ -518,6 +553,9 @@ bool DivPlatformYM2610::keyOffAffectsArp(int ch) {
int DivPlatformYM2610::init(DivEngine* p, int channels, int sugRate, bool pal) { int DivPlatformYM2610::init(DivEngine* p, int channels, int sugRate, bool pal) {
parent=p; parent=p;
for (int i=0; i<13; i++) {
isMuted[i]=false;
}
if (pal) { if (pal) {
rate=500000; rate=500000;
} else { } else {

View file

@ -28,6 +28,7 @@ class DivPlatformYM2610: public DivDispatch {
Channel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), ins(-1), note(0), psgMode(1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), inPorta(false), vol(0), outVol(15), pan(3) {} Channel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), ins(-1), note(0), psgMode(1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), inPorta(false), vol(0), outVol(15), pan(3) {}
}; };
Channel chan[13]; Channel chan[13];
bool isMuted[13];
struct QueuedWrite { struct QueuedWrite {
unsigned short addr; unsigned short addr;
unsigned char val; unsigned char val;
@ -63,6 +64,7 @@ class DivPlatformYM2610: public DivDispatch {
int dispatch(DivCommand c); int dispatch(DivCommand c);
void reset(); void reset();
void tick(); void tick();
void muteChannel(int ch, bool mute);
bool isStereo(); bool isStereo();
bool keyOffAffectsArp(int ch); bool keyOffAffectsArp(int ch);
int init(DivEngine* parent, int channels, int sugRate, bool pal); int init(DivEngine* parent, int channels, int sugRate, bool pal);

View file

@ -20,7 +20,9 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) {
unsigned short baseAddr=chanOffs[1]|opOffs[ordch]; unsigned short baseAddr=chanOffs[1]|opOffs[ordch];
DivInstrumentFM::Operator op=ins->fm.op[ordch]; DivInstrumentFM::Operator op=ins->fm.op[ordch];
if (isOutput[ins->fm.alg][ordch]) { if (isOpMuted[ch]) {
rWrite(baseAddr+0x40,127);
} else if (isOutput[ins->fm.alg][ordch]) {
if (!opChan[ch].active || opChan[ch].insChanged) { if (!opChan[ch].active || opChan[ch].insChanged) {
rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch].vol&0x7f))/127)); rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch].vol&0x7f))/127));
} }
@ -58,7 +60,9 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) {
DivInstrument* ins=parent->getIns(opChan[ch].ins); DivInstrument* ins=parent->getIns(opChan[ch].ins);
unsigned short baseAddr=chanOffs[1]|opOffs[ordch]; unsigned short baseAddr=chanOffs[1]|opOffs[ordch];
DivInstrumentFM::Operator op=ins->fm.op[ordch]; DivInstrumentFM::Operator op=ins->fm.op[ordch];
if (isOutput[ins->fm.alg][ordch]) { if (isOpMuted[ch]) {
rWrite(baseAddr+0x40,127);
} else if (isOutput[ins->fm.alg][ordch]) {
rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch].vol&0x7f))/127)); rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch].vol&0x7f))/127));
} else { } else {
rWrite(baseAddr+0x40,op.tl); rWrite(baseAddr+0x40,op.tl);
@ -233,6 +237,30 @@ void DivPlatformYM2610Ext::tick() {
} }
} }
void DivPlatformYM2610Ext::muteChannel(int ch, bool mute) {
if (ch<1) {
DivPlatformYM2610::muteChannel(ch,mute);
return;
}
if (ch>4) {
DivPlatformYM2610::muteChannel(ch-3,mute);
return;
}
isOpMuted[ch-1]=mute;
int ordch=orderedOps[ch];
DivInstrument* ins=parent->getIns(opChan[ch].ins);
unsigned short baseAddr=chanOffs[1]|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-(((127-op.tl)*(opChan[ch].vol&0x7f))/127));
} else {
rWrite(baseAddr+0x40,op.tl);
}
}
void DivPlatformYM2610Ext::reset() { void DivPlatformYM2610Ext::reset() {
DivPlatformYM2610::reset(); DivPlatformYM2610::reset();
@ -252,6 +280,9 @@ bool DivPlatformYM2610Ext::keyOffAffectsArp(int ch) {
int DivPlatformYM2610Ext::init(DivEngine* parent, int channels, int sugRate, bool pal) { int DivPlatformYM2610Ext::init(DivEngine* parent, int channels, int sugRate, bool pal) {
DivPlatformYM2610::init(parent,channels,sugRate,pal); DivPlatformYM2610::init(parent,channels,sugRate,pal);
for (int i=0; i<4; i++) {
isOpMuted[i]=false;
}
reset(); reset();
return 16; return 16;

View file

@ -14,10 +14,12 @@ class DivPlatformYM2610Ext: public DivPlatformYM2610 {
OpChannel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), vol(0), pan(3) {} OpChannel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), vol(0), pan(3) {}
}; };
OpChannel opChan[4]; OpChannel opChan[4];
bool isOpMuted[4];
public: public:
int dispatch(DivCommand c); int dispatch(DivCommand c);
void reset(); void reset();
void tick(); void tick();
void muteChannel(int ch, bool mute);
bool keyOffAffectsArp(int ch); bool keyOffAffectsArp(int ch);
int init(DivEngine* parent, int channels, int sugRate, bool pal); int init(DivEngine* parent, int channels, int sugRate, bool pal);
void quit(); void quit();

View file

@ -782,9 +782,16 @@ void FurnaceGUI::drawPattern() {
} }
ImGui::TableNextRow(); ImGui::TableNextRow();
ImGui::TableNextColumn(); ImGui::TableNextColumn();
char chanID[256];
for (int i=0; i<chans; i++) { for (int i=0; i<chans; i++) {
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::Text("%s",e->getChannelName(i)); snprintf(chanID,256," %s##_CH%d",e->getChannelName(i),i);
if (ImGui::Selectable(chanID,!e->isChannelMuted(i),ImGuiSelectableFlags_NoPadWithHalfSpacing)) {
e->toggleMute(i);
}
if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
e->toggleSolo(i);
}
} }
float oneCharSize=ImGui::CalcTextSize("A").x; float oneCharSize=ImGui::CalcTextSize("A").x;
float lineHeight=(ImGui::GetTextLineHeight()+2*dpiScale); float lineHeight=(ImGui::GetTextLineHeight()+2*dpiScale);