mirror of
https://github.com/tildearrow/furnace.git
synced 2024-11-27 06:53:01 +00:00
channel mute/solo!
This commit is contained in:
parent
c4c019e593
commit
b3b66ecbdb
27 changed files with 416 additions and 48 deletions
|
@ -101,13 +101,70 @@ class DivDispatch {
|
|||
* the engine shall resample to the output 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);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* reset the state of this dispatch.
|
||||
*/
|
||||
virtual void reset();
|
||||
|
||||
/**
|
||||
* ticks this dispatch.
|
||||
*/
|
||||
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();
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* set the region to PAL.
|
||||
* @param pal whether to set it to PAL.
|
||||
*/
|
||||
virtual void setPAL(bool pal);
|
||||
|
||||
/**
|
||||
|
|
|
@ -1271,6 +1271,55 @@ bool DivEngine::isPlaying() {
|
|||
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() {
|
||||
isBusy.lock();
|
||||
DivInstrument* ins=new DivInstrument;
|
||||
|
@ -1509,6 +1558,9 @@ void DivEngine::quitDispatch() {
|
|||
totalCmds=0;
|
||||
lastCmds=0;
|
||||
cmdsPerSecond=0;
|
||||
for (int i=0; i<17; i++) {
|
||||
isMuted[i]=0;
|
||||
}
|
||||
isBusy.unlock();
|
||||
}
|
||||
|
||||
|
@ -1586,6 +1638,10 @@ bool DivEngine::init(String outName) {
|
|||
vibTable[i]=127*sin(((double)i/64.0)*(2*M_PI));
|
||||
}
|
||||
|
||||
for (int i=0; i<17; i++) {
|
||||
isMuted[i]=0;
|
||||
}
|
||||
|
||||
initDispatch();
|
||||
reset();
|
||||
|
||||
|
|
|
@ -77,6 +77,7 @@ class DivEngine {
|
|||
DivStatusView view;
|
||||
DivChannelState chan[17];
|
||||
DivAudioEngines audioEngine;
|
||||
bool isMuted[17];
|
||||
std::mutex isBusy;
|
||||
|
||||
short vibTable[64];
|
||||
|
@ -132,6 +133,18 @@ class DivEngine {
|
|||
// is STD system
|
||||
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
|
||||
const char* getChannelName(int chan);
|
||||
|
||||
|
|
|
@ -6,6 +6,16 @@ void DivDispatch::acquire(short* bufL, short* bufR, size_t start, size_t len) {
|
|||
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) {
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -118,12 +118,14 @@ void DivPlatformArcade::acquire_ymfm(short* bufL, short* bufR, size_t start, siz
|
|||
for (int i=8; i<13; i++) {
|
||||
if (chan[i].pcm.sample>=0 && chan[i].pcm.sample<parent->song.sampleLen) {
|
||||
DivSample* s=parent->song.sample[chan[i].pcm.sample];
|
||||
if (s->depth==8) {
|
||||
pcmL+=(s->rendData[chan[i].pcm.pos>>8]*chan[i].chVolL);
|
||||
pcmR+=(s->rendData[chan[i].pcm.pos>>8]*chan[i].chVolR);
|
||||
} else {
|
||||
pcmL+=(s->rendData[chan[i].pcm.pos>>8]*chan[i].chVolL)>>8;
|
||||
pcmR+=(s->rendData[chan[i].pcm.pos>>8]*chan[i].chVolR)>>8;
|
||||
if (!isMuted[i]) {
|
||||
if (s->depth==8) {
|
||||
pcmL+=(s->rendData[chan[i].pcm.pos>>8]*chan[i].chVolL);
|
||||
pcmR+=(s->rendData[chan[i].pcm.pos>>8]*chan[i].chVolR);
|
||||
} else {
|
||||
pcmL+=(s->rendData[chan[i].pcm.pos>>8]*chan[i].chVolL)>>8;
|
||||
pcmR+=(s->rendData[chan[i].pcm.pos>>8]*chan[i].chVolR)>>8;
|
||||
}
|
||||
}
|
||||
chan[i].pcm.pos+=chan[i].pcm.freq;
|
||||
if (chan[i].pcm.pos>=(s->rendLength<<8)) {
|
||||
|
@ -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) {
|
||||
switch (c.cmd) {
|
||||
case DIV_CMD_NOTE_ON: {
|
||||
|
@ -227,7 +241,11 @@ int DivPlatformArcade::dispatch(DivCommand c) {
|
|||
}
|
||||
}
|
||||
if (chan[c.chan].insChanged) {
|
||||
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));
|
||||
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]+0x38,((ins->fm.fms&7)<<4)|(ins->fm.ams&3));
|
||||
}
|
||||
chan[c.chan].insChanged=false;
|
||||
|
@ -283,7 +301,11 @@ int DivPlatformArcade::dispatch(DivCommand c) {
|
|||
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
|
||||
chan[c.chan].chVolL=((c.value>>4)==1);
|
||||
chan[c.chan].chVolR=((c.value&15)==1);
|
||||
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));
|
||||
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));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -443,6 +465,9 @@ void DivPlatformArcade::setYMFM(bool use) {
|
|||
|
||||
int DivPlatformArcade::init(DivEngine* p, int channels, int sugRate, bool pal) {
|
||||
parent=p;
|
||||
for (int i=0; i<13; i++) {
|
||||
isMuted[i]=false;
|
||||
}
|
||||
if (useYMFM) {
|
||||
rate=447443/8;
|
||||
fm_ymfm=new ymfm::ym2151(iface);
|
||||
|
|
|
@ -48,6 +48,8 @@ class DivPlatformArcade: public DivDispatch {
|
|||
DivArcadeInterface iface;
|
||||
|
||||
bool extMode, useYMFM;
|
||||
|
||||
bool isMuted[13];
|
||||
|
||||
short oldWrites[256];
|
||||
short pendingWrites[256];
|
||||
|
@ -63,6 +65,7 @@ class DivPlatformArcade: public DivDispatch {
|
|||
int dispatch(DivCommand c);
|
||||
void reset();
|
||||
void tick();
|
||||
void muteChannel(int ch, bool mute);
|
||||
bool isStereo();
|
||||
void setYMFM(bool use);
|
||||
int init(DivEngine* parent, int channels, int sugRate, bool pal);
|
||||
|
|
|
@ -55,16 +55,16 @@ void DivPlatformC64::tick() {
|
|||
}
|
||||
if (chan[i].testWhen>0) {
|
||||
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+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) {
|
||||
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) {
|
||||
chan[i].freq=(chan[i].baseFreq*(ONE_SEMITONE+chan[i].pitch))/ONE_SEMITONE;
|
||||
|
@ -72,12 +72,12 @@ void DivPlatformC64::tick() {
|
|||
if (chan[i].keyOn) {
|
||||
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+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+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+1,chan[i].freq>>8);
|
||||
|
@ -278,6 +278,11 @@ int DivPlatformC64::dispatch(DivCommand c) {
|
|||
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() {
|
||||
for (int i=0; i<3; i++) {
|
||||
chan[i]=DivPlatformC64::Channel();
|
||||
|
@ -312,6 +317,9 @@ void DivPlatformC64::setPAL(bool pal) {
|
|||
|
||||
int DivPlatformC64::init(DivEngine* p, int channels, int sugRate, bool pal) {
|
||||
parent=p;
|
||||
for (int i=0; i<3; i++) {
|
||||
isMuted[i]=false;
|
||||
}
|
||||
setPAL(pal);
|
||||
|
||||
reset();
|
||||
|
|
|
@ -45,6 +45,7 @@ class DivPlatformC64: public DivDispatch {
|
|||
vol(15) {}
|
||||
};
|
||||
Channel chan[3];
|
||||
bool isMuted[3];
|
||||
|
||||
unsigned char filtControl, filtRes, vol;
|
||||
int filtCut, resetTime;
|
||||
|
@ -57,6 +58,7 @@ class DivPlatformC64: public DivDispatch {
|
|||
int dispatch(DivCommand c);
|
||||
void reset();
|
||||
void tick();
|
||||
void muteChannel(int ch, bool mute);
|
||||
void setPAL(bool pal);
|
||||
int init(DivEngine* parent, int channels, int sugRate, bool pal);
|
||||
void setChipModel(bool is6581);
|
||||
|
|
|
@ -7,17 +7,21 @@ void DivPlatformDummy::acquire(short* bufL, short* bufR, size_t start, size_t le
|
|||
bufL[i]=0;
|
||||
for (unsigned char j=0; j<chans; j++) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformDummy::muteChannel(int ch, bool mute) {
|
||||
isMuted[ch]=mute;
|
||||
}
|
||||
|
||||
void DivPlatformDummy::tick() {
|
||||
for (unsigned char i=0; i<chans; i++) {
|
||||
chan[i].amp--;
|
||||
if (chan[i].amp<0) chan[i].amp=0;
|
||||
chan[i].amp-=3;
|
||||
if (chan[i].amp<16) chan[i].amp=16;
|
||||
|
||||
if (chan[i].freqChanged) {
|
||||
chan[i].freqChanged=false;
|
||||
|
@ -29,7 +33,7 @@ void DivPlatformDummy::tick() {
|
|||
int DivPlatformDummy::dispatch(DivCommand c) {
|
||||
switch (c.cmd) {
|
||||
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].active=true;
|
||||
chan[c.chan].amp=64;
|
||||
|
@ -49,7 +53,7 @@ int DivPlatformDummy::dispatch(DivCommand c) {
|
|||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
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;
|
||||
break;
|
||||
case DIV_CMD_GET_VOLMAX:
|
||||
|
@ -70,6 +74,9 @@ void DivPlatformDummy::reset() {
|
|||
|
||||
int DivPlatformDummy::init(DivEngine* p, int channels, int sugRate, bool pal) {
|
||||
parent=p;
|
||||
for (int i=0; i<17; i++) {
|
||||
isMuted[i]=false;
|
||||
}
|
||||
rate=65536;
|
||||
chans=channels;
|
||||
reset();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#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.
|
||||
class DivPlatformDummy: public DivDispatch {
|
||||
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 chan[17];
|
||||
bool isMuted[17];
|
||||
unsigned char chans;
|
||||
public:
|
||||
void acquire(short* bufL, short* bufR, size_t start, size_t len);
|
||||
void muteChannel(int ch, bool mute);
|
||||
int dispatch(DivCommand c);
|
||||
void reset();
|
||||
void tick();
|
||||
|
|
|
@ -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]={
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
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) {
|
||||
switch (c.cmd) {
|
||||
case DIV_CMD_NOTE_ON:
|
||||
|
@ -233,7 +249,7 @@ int DivPlatformGB::dispatch(DivCommand c) {
|
|||
lastPan&=~(0x11<<c.chan);
|
||||
if (c.value==0) c.value=0x11;
|
||||
lastPan|=c.value<<c.chan;
|
||||
rWrite(0x25,lastPan);
|
||||
rWrite(0x25,procMute());
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_LEGATO:
|
||||
|
@ -280,8 +296,8 @@ void DivPlatformGB::reset() {
|
|||
GB_set_sample_rate(gb,rate);
|
||||
// enable all channels
|
||||
GB_apu_write(gb,0x26,0x80);
|
||||
GB_apu_write(gb,0x25,0xff);
|
||||
lastPan=0xff;
|
||||
GB_apu_write(gb,0x25,procMute());
|
||||
}
|
||||
|
||||
bool DivPlatformGB::isStereo() {
|
||||
|
@ -289,6 +305,9 @@ bool DivPlatformGB::isStereo() {
|
|||
}
|
||||
|
||||
int DivPlatformGB::init(DivEngine* p, int channels, int sugRate, bool pal) {
|
||||
for (int i=0; i<4; i++) {
|
||||
isMuted[i]=false;
|
||||
}
|
||||
parent=p;
|
||||
rate=262144;
|
||||
gb=new GB_gameboy_t;
|
||||
|
|
|
@ -31,15 +31,18 @@ class DivPlatformGB: public DivDispatch {
|
|||
wave(-1) {}
|
||||
};
|
||||
Channel chan[4];
|
||||
bool isMuted[4];
|
||||
unsigned char lastPan;
|
||||
|
||||
GB_gameboy_t* gb;
|
||||
unsigned char procMute();
|
||||
void updateWave();
|
||||
public:
|
||||
void acquire(short* bufL, short* bufR, size_t start, size_t len);
|
||||
int dispatch(DivCommand c);
|
||||
void reset();
|
||||
void tick();
|
||||
void muteChannel(int ch, bool mute);
|
||||
bool isStereo();
|
||||
int init(DivEngine* parent, int channels, int sugRate, bool pal);
|
||||
void quit();
|
||||
|
|
|
@ -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) {
|
||||
if (c.chan>5) {
|
||||
c.chan-=6;
|
||||
|
@ -195,7 +205,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
|
|||
}
|
||||
if (chan[c.chan].insChanged) {
|
||||
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;
|
||||
|
||||
|
@ -249,7 +259,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
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;
|
||||
}
|
||||
case DIV_CMD_PITCH: {
|
||||
|
@ -410,6 +420,9 @@ void DivPlatformGenesis::setPAL(bool pal) {
|
|||
|
||||
int DivPlatformGenesis::init(DivEngine* p, int channels, int sugRate, bool pal) {
|
||||
parent=p;
|
||||
for (int i=0; i<10; i++) {
|
||||
isMuted[i]=false;
|
||||
}
|
||||
setPAL(pal);
|
||||
// PSG
|
||||
psg.init(p,4,sugRate,pal);
|
||||
|
|
|
@ -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 chan[10];
|
||||
bool isMuted[10];
|
||||
struct QueuedWrite {
|
||||
unsigned short addr;
|
||||
unsigned char val;
|
||||
|
@ -53,6 +54,7 @@ class DivPlatformGenesis: public DivDispatch {
|
|||
int dispatch(DivCommand c);
|
||||
void reset();
|
||||
void tick();
|
||||
void muteChannel(int ch, bool mute);
|
||||
bool isStereo();
|
||||
bool keyOffAffectsArp(int ch);
|
||||
void setPAL(bool pal);
|
||||
|
|
|
@ -20,7 +20,9 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
|
|||
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[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) {
|
||||
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);
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[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));
|
||||
} else {
|
||||
rWrite(baseAddr+0x40,op.tl);
|
||||
|
@ -182,6 +186,30 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
|
|||
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]={
|
||||
0xa9, 0xaa, 0xa8, 0xa2
|
||||
};
|
||||
|
@ -273,6 +301,9 @@ bool DivPlatformGenesisExt::keyOffAffectsArp(int ch) {
|
|||
|
||||
int DivPlatformGenesisExt::init(DivEngine* parent, int channels, int sugRate, bool pal) {
|
||||
DivPlatformGenesis::init(parent,channels,sugRate,pal);
|
||||
for (int i=0; i<4; i++) {
|
||||
isOpMuted[i]=false;
|
||||
}
|
||||
|
||||
reset();
|
||||
return 13;
|
||||
|
|
|
@ -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 opChan[4];
|
||||
bool isOpMuted[4];
|
||||
public:
|
||||
int dispatch(DivCommand c);
|
||||
void reset();
|
||||
void tick();
|
||||
void muteChannel(int ch, bool mute);
|
||||
bool keyOffAffectsArp(int ch);
|
||||
int init(DivEngine* parent, int channels, int sugRate, bool pal);
|
||||
void quit();
|
||||
|
|
|
@ -12,10 +12,12 @@ void DivPlatformNES::acquire(short* bufL, short* bufR, size_t start, size_t len)
|
|||
dacPeriod+=dacRate;
|
||||
if (dacPeriod>=rate) {
|
||||
DivSample* s=parent->song.sample[dacSample];
|
||||
if (s->depth==8) {
|
||||
apu_wr_reg(0x4011,((unsigned char)s->rendData[dacPos++]+0x80)>>1);
|
||||
} else {
|
||||
apu_wr_reg(0x4011,((unsigned short)s->rendData[dacPos++]+0x8000)>>9);
|
||||
if (!isMuted[4]) {
|
||||
if (s->depth==8) {
|
||||
apu_wr_reg(0x4011,((unsigned char)s->rendData[dacPos++]+0x80)>>1);
|
||||
} else {
|
||||
apu_wr_reg(0x4011,((unsigned short)s->rendData[dacPos++]+0x8000)>>9);
|
||||
}
|
||||
}
|
||||
if (dacPos>=s->rendLength) {
|
||||
dacSample=-1;
|
||||
|
@ -272,6 +274,14 @@ int DivPlatformNES::dispatch(DivCommand c) {
|
|||
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() {
|
||||
for (int i=0; i<5; i++) {
|
||||
chan[i]=DivPlatformNES::Channel();
|
||||
|
@ -287,7 +297,7 @@ void DivPlatformNES::reset() {
|
|||
apu.cpu_cycles=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(0x4005,0x08);
|
||||
}
|
||||
|
@ -308,6 +318,9 @@ void DivPlatformNES::setPAL(bool pal) {
|
|||
|
||||
int DivPlatformNES::init(DivEngine* p, int channels, int sugRate, bool pal) {
|
||||
parent=p;
|
||||
for (int i=0; i<5; i++) {
|
||||
isMuted[i]=false;
|
||||
}
|
||||
setPAL(pal);
|
||||
|
||||
init_nla_table(500,500);
|
||||
|
|
|
@ -32,6 +32,7 @@ class DivPlatformNES: public DivDispatch {
|
|||
wave(-1) {}
|
||||
};
|
||||
Channel chan[5];
|
||||
bool isMuted[5];
|
||||
int dacPeriod, dacRate;
|
||||
unsigned int dacPos;
|
||||
int dacSample;
|
||||
|
@ -46,6 +47,7 @@ class DivPlatformNES: public DivDispatch {
|
|||
int dispatch(DivCommand c);
|
||||
void reset();
|
||||
void tick();
|
||||
void muteChannel(int ch, bool mute);
|
||||
bool keyOffAffectsArp(int ch);
|
||||
void setPAL(bool pal);
|
||||
int init(DivEngine* parent, int channels, int sugRate, bool pal);
|
||||
|
|
|
@ -239,7 +239,7 @@ int DivPlatformPCE::dispatch(DivCommand c) {
|
|||
break;
|
||||
case DIV_CMD_PANNING: {
|
||||
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;
|
||||
}
|
||||
case DIV_CMD_LEGATO:
|
||||
|
@ -263,6 +263,11 @@ int DivPlatformPCE::dispatch(DivCommand c) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
void DivPlatformPCE::muteChannel(int ch, bool mute) {
|
||||
isMuted[ch]=mute;
|
||||
chWrite(ch,0x05,isMuted[ch]?0:chan[ch].pan);
|
||||
}
|
||||
|
||||
void DivPlatformPCE::reset() {
|
||||
while (!writes.empty()) writes.pop();
|
||||
for (int i=0; i<6; i++) {
|
||||
|
@ -280,7 +285,7 @@ void DivPlatformPCE::reset() {
|
|||
rWrite(0x01,0xff);
|
||||
// set per-channel initial panning
|
||||
for (int i=0; i<6; i++) {
|
||||
chWrite(i,0x05,chan[i].pan);
|
||||
chWrite(i,0x05,isMuted[i]?0:chan[i].pan);
|
||||
}
|
||||
delay=500;
|
||||
}
|
||||
|
@ -303,6 +308,9 @@ void DivPlatformPCE::setPAL(bool pal) {
|
|||
|
||||
int DivPlatformPCE::init(DivEngine* p, int channels, int sugRate, bool pal) {
|
||||
parent=p;
|
||||
for (int i=0; i<6; i++) {
|
||||
isMuted[i]=false;
|
||||
}
|
||||
setPAL(pal);
|
||||
pce=new PCE_PSG(&tempL,&tempR,PCE_PSG::REVISION_HUC6280);
|
||||
reset();
|
||||
|
|
|
@ -40,6 +40,7 @@ class DivPlatformPCE: public DivDispatch {
|
|||
wave(-1) {}
|
||||
};
|
||||
Channel chan[6];
|
||||
bool isMuted[6];
|
||||
struct QueuedWrite {
|
||||
unsigned char addr;
|
||||
unsigned char val;
|
||||
|
@ -57,6 +58,7 @@ class DivPlatformPCE: public DivDispatch {
|
|||
int dispatch(DivCommand c);
|
||||
void reset();
|
||||
void tick();
|
||||
void muteChannel(int ch, bool mute);
|
||||
bool isStereo();
|
||||
bool keyOffAffectsArp(int ch);
|
||||
void setPAL(bool pal);
|
||||
|
|
|
@ -17,7 +17,7 @@ void DivPlatformSMS::tick() {
|
|||
chan[i].std.next();
|
||||
if (chan[i].std.hadVol) {
|
||||
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.arpMode) {
|
||||
|
@ -84,7 +84,7 @@ int DivPlatformSMS::dispatch(DivCommand c) {
|
|||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
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));
|
||||
break;
|
||||
case DIV_CMD_NOTE_OFF:
|
||||
|
@ -102,7 +102,7 @@ int DivPlatformSMS::dispatch(DivCommand c) {
|
|||
if (!chan[c.chan].std.hasVol) {
|
||||
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;
|
||||
case DIV_CMD_GET_VOLUME:
|
||||
|
@ -159,6 +159,11 @@ int DivPlatformSMS::dispatch(DivCommand c) {
|
|||
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() {
|
||||
for (int i=0; i<4; i++) {
|
||||
chan[i]=DivPlatformSMS::Channel();
|
||||
|
@ -182,6 +187,9 @@ void DivPlatformSMS::setPAL(bool pal) {
|
|||
|
||||
int DivPlatformSMS::init(DivEngine* p, int channels, int sugRate, bool pal) {
|
||||
parent=p;
|
||||
for (int i=0; i<4; i++) {
|
||||
isMuted[i]=false;
|
||||
}
|
||||
setPAL(pal);
|
||||
sn=new sn76496_device(rate);
|
||||
reset();
|
||||
|
|
|
@ -27,6 +27,7 @@ class DivPlatformSMS: public DivDispatch {
|
|||
outVol(15) {}
|
||||
};
|
||||
Channel chan[4];
|
||||
bool isMuted[4];
|
||||
unsigned char snNoiseMode;
|
||||
bool updateSNMode;
|
||||
sn76496_device* sn;
|
||||
|
@ -36,6 +37,7 @@ class DivPlatformSMS: public DivDispatch {
|
|||
int dispatch(DivCommand c);
|
||||
void reset();
|
||||
void tick();
|
||||
void muteChannel(int ch, bool mute);
|
||||
bool keyOffAffectsArp(int ch);
|
||||
void setPAL(bool pal);
|
||||
int init(DivEngine* parent, int channels, int sugRate, bool pal);
|
||||
|
|
|
@ -46,7 +46,11 @@ void DivPlatformYM2610::tick() {
|
|||
if (chan[i].std.hadVol) {
|
||||
chan[i].outVol=chan[i].std.vol-(15-chan[i].vol);
|
||||
if (chan[i].outVol<0) chan[i].outVol=0;
|
||||
rWrite(0x04+i,(chan[i].outVol&15)|((chan[i].psgMode&4)<<2));
|
||||
if (isMuted[i]) {
|
||||
rWrite(0x04+i,0);
|
||||
} else {
|
||||
rWrite(0x04+i,(chan[i].outVol&15)|((chan[i].psgMode&4)<<2));
|
||||
}
|
||||
}
|
||||
if (chan[i].std.hadArp) {
|
||||
if (!chan[i].inPorta) {
|
||||
|
@ -187,7 +191,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
|
|||
int end=s->rendOff+s->adpcmRendLength-1;
|
||||
writes.emplace(0x120+c.chan-7,(end>>8)&0xff);
|
||||
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)));
|
||||
break;
|
||||
}
|
||||
|
@ -200,7 +204,11 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
|
|||
chan[c.chan].active=true;
|
||||
chan[c.chan].keyOn=true;
|
||||
chan[c.chan].std.init(ins);
|
||||
rWrite(0x04+c.chan,(chan[c.chan].vol&15)|((chan[c.chan].psgMode&4)<<2));
|
||||
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));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -227,7 +235,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
|
|||
}
|
||||
if (chan[c.chan].insChanged) {
|
||||
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;
|
||||
|
||||
|
@ -250,14 +258,18 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
|
|||
chan[c.chan].vol=c.value;
|
||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
|
||||
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;
|
||||
}
|
||||
if (c.chan>3) { // PSG
|
||||
if (!chan[c.chan].std.hasVol) {
|
||||
chan[c.chan].outVol=c.value;
|
||||
}
|
||||
rWrite(0x04+c.chan,(chan[c.chan].vol&15)|((chan[c.chan].psgMode&4)<<2));
|
||||
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));
|
||||
}
|
||||
break;
|
||||
}
|
||||
for (int i=0; i<4; i++) {
|
||||
|
@ -294,12 +306,12 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
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;
|
||||
}
|
||||
if (c.chan>3) break;
|
||||
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;
|
||||
}
|
||||
case DIV_CMD_PITCH: {
|
||||
|
@ -428,7 +440,11 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
|
|||
} else {
|
||||
chan[c.chan].psgMode&=~4;
|
||||
}
|
||||
rWrite(0x04+c.chan,(chan[c.chan].vol&15)|((chan[c.chan].psgMode&4)<<2));
|
||||
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));
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_AY_ENVELOPE_LOW:
|
||||
if (c.chan<4 || c.chan>6) break;
|
||||
|
@ -467,6 +483,25 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
|
|||
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() {
|
||||
while (!writes.empty()) writes.pop();
|
||||
fm->reset();
|
||||
|
@ -518,6 +553,9 @@ bool DivPlatformYM2610::keyOffAffectsArp(int ch) {
|
|||
|
||||
int DivPlatformYM2610::init(DivEngine* p, int channels, int sugRate, bool pal) {
|
||||
parent=p;
|
||||
for (int i=0; i<13; i++) {
|
||||
isMuted[i]=false;
|
||||
}
|
||||
if (pal) {
|
||||
rate=500000;
|
||||
} else {
|
||||
|
|
|
@ -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 chan[13];
|
||||
bool isMuted[13];
|
||||
struct QueuedWrite {
|
||||
unsigned short addr;
|
||||
unsigned char val;
|
||||
|
@ -63,6 +64,7 @@ class DivPlatformYM2610: public DivDispatch {
|
|||
int dispatch(DivCommand c);
|
||||
void reset();
|
||||
void tick();
|
||||
void muteChannel(int ch, bool mute);
|
||||
bool isStereo();
|
||||
bool keyOffAffectsArp(int ch);
|
||||
int init(DivEngine* parent, int channels, int sugRate, bool pal);
|
||||
|
|
|
@ -20,7 +20,9 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) {
|
|||
|
||||
unsigned short baseAddr=chanOffs[1]|opOffs[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) {
|
||||
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);
|
||||
unsigned short baseAddr=chanOffs[1]|opOffs[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));
|
||||
} else {
|
||||
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() {
|
||||
DivPlatformYM2610::reset();
|
||||
|
||||
|
@ -252,6 +280,9 @@ bool DivPlatformYM2610Ext::keyOffAffectsArp(int ch) {
|
|||
|
||||
int DivPlatformYM2610Ext::init(DivEngine* parent, int channels, int sugRate, bool pal) {
|
||||
DivPlatformYM2610::init(parent,channels,sugRate,pal);
|
||||
for (int i=0; i<4; i++) {
|
||||
isOpMuted[i]=false;
|
||||
}
|
||||
|
||||
reset();
|
||||
return 16;
|
||||
|
|
|
@ -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 opChan[4];
|
||||
bool isOpMuted[4];
|
||||
public:
|
||||
int dispatch(DivCommand c);
|
||||
void reset();
|
||||
void tick();
|
||||
void muteChannel(int ch, bool mute);
|
||||
bool keyOffAffectsArp(int ch);
|
||||
int init(DivEngine* parent, int channels, int sugRate, bool pal);
|
||||
void quit();
|
||||
|
|
|
@ -782,9 +782,16 @@ void FurnaceGUI::drawPattern() {
|
|||
}
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
char chanID[256];
|
||||
for (int i=0; i<chans; i++) {
|
||||
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 lineHeight=(ImGui::GetTextLineHeight()+2*dpiScale);
|
||||
|
|
Loading…
Reference in a new issue