diff --git a/src/engine/cmdStream.cpp b/src/engine/cmdStream.cpp index 41c2c3cc8..6b5d9ee93 100644 --- a/src/engine/cmdStream.cpp +++ b/src/engine/cmdStream.cpp @@ -704,7 +704,12 @@ bool DivCSPlayer::init() { // initialize state for (int i=0; igetTotalChannelCount(); i++) { - chan[i].volMax=(e->getDispatch(e->song.dispatchOfChan[i])->dispatch(DivCommand(DIV_CMD_GET_VOLMAX,e->song.dispatchChanOfChan[i]))<<8)|0xff; + if (e->song.dispatchChanOfChan[i]>=0) { + chan[i].volMax=(e->getDispatch(e->song.dispatchOfChan[i])->dispatch(DivCommand(DIV_CMD_GET_VOLMAX,e->song.dispatchChanOfChan[i]))<<8)|0xff; + } else { + // fallback + chan[i].volMax=0xfff; + } chan[i].volume=chan[i].volMax; } diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index a27fe8a69..6fd6fef4f 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -1536,21 +1536,25 @@ DivChannelState* DivEngine::getChanState(int ch) { unsigned short DivEngine::getChanPan(int ch) { if (ch<0 || ch>=song.chans) return 0; + if (song.dispatchChanOfChan[ch]<0) return 0; return disCont[song.dispatchOfChan[ch]].dispatch->getPan(song.dispatchChanOfChan[ch]); } void* DivEngine::getDispatchChanState(int ch) { if (ch<0 || ch>=song.chans) return NULL; + if (song.dispatchChanOfChan[ch]<0) return NULL; return disCont[song.dispatchOfChan[ch]].dispatch->getChanState(song.dispatchChanOfChan[ch]); } void DivEngine::getChanPaired(int ch, std::vector& ret) { if (ch<0 || ch>=song.chans) return; + if (song.dispatchChanOfChan[ch]<0) return; disCont[song.dispatchOfChan[ch]].dispatch->getPaired(song.dispatchChanOfChan[ch],ret); } DivChannelModeHints DivEngine::getChanModeHints(int ch) { if (ch<0 || ch>=song.chans) return DivChannelModeHints(); + if (song.dispatchChanOfChan[ch]<0) return DivChannelModeHints(); return disCont[song.dispatchOfChan[ch]].dispatch->getModeHints(song.dispatchChanOfChan[ch]); } @@ -1564,16 +1568,19 @@ unsigned char* DivEngine::getRegisterPool(int sys, int& size, int& depth) { DivMacroInt* DivEngine::getMacroInt(int chan) { if (chan<0 || chan>=song.chans) return NULL; + if (song.dispatchChanOfChan[chan]<0) return NULL; return disCont[song.dispatchOfChan[chan]].dispatch->getChanMacroInt(song.dispatchChanOfChan[chan]); } DivSamplePos DivEngine::getSamplePos(int chan) { if (chan<0 || chan>=song.chans) return DivSamplePos(); + if (song.dispatchChanOfChan[chan]<0) return DivSamplePos(); return disCont[song.dispatchOfChan[chan]].dispatch->getSamplePos(song.dispatchChanOfChan[chan]); } DivDispatchOscBuffer* DivEngine::getOscBuffer(int chan) { if (chan<0 || chan>=song.chans) return NULL; + if (song.dispatchChanOfChan[chan]<0) return NULL; return disCont[song.dispatchOfChan[chan]].dispatch->getOscBuffer(song.dispatchChanOfChan[chan]); } @@ -2070,6 +2077,7 @@ void DivEngine::stop() { // reset all chan oscs for (int i=0; igetOscBuffer(song.dispatchChanOfChan[i]); if (buf!=NULL) { buf->reset(); @@ -2118,7 +2126,9 @@ void DivEngine::reset() { } for (int i=0; idispatch(DivCommand(DIV_CMD_GET_VOLMAX,song.dispatchChanOfChan[i]))<<8)|0xff; + if (i=0) { + chan[i].volMax=(disCont[song.dispatchOfChan[i]].dispatch->dispatch(DivCommand(DIV_CMD_GET_VOLMAX,song.dispatchChanOfChan[i]))<<8)|0xff; + } chan[i].volume=chan[i].volMax; if (!song.linearPitch) chan[i].vibratoFine=4; } @@ -2393,6 +2403,7 @@ int DivEngine::mapVelocity(int ch, float vel) { if (ch<0) return 0; if (ch>=song.chans) return 0; if (disCont[song.dispatchOfChan[ch]].dispatch==NULL) return 0; + if (song.dispatchChanOfChan[ch]<0) return 0; return disCont[song.dispatchOfChan[ch]].dispatch->mapVelocity(song.dispatchChanOfChan[ch],vel); } @@ -2400,6 +2411,7 @@ float DivEngine::getGain(int ch, int vol) { if (ch<0) return 0; if (ch>=song.chans) return 0; if (disCont[song.dispatchOfChan[ch]].dispatch==NULL) return 0; + if (song.dispatchChanOfChan[ch]<0) return 0; return disCont[song.dispatchOfChan[ch]].dispatch->getGain(song.dispatchChanOfChan[ch],vol); } @@ -2525,14 +2537,14 @@ void DivEngine::toggleSolo(int chan) { if (!solo) { for (int i=0; i=0) { disCont[song.dispatchOfChan[i]].dispatch->muteChannel(song.dispatchChanOfChan[i],isMuted[i]); } } } else { for (int i=0; i=0) { disCont[song.dispatchOfChan[i]].dispatch->muteChannel(song.dispatchChanOfChan[i],isMuted[i]); } } @@ -2543,7 +2555,7 @@ void DivEngine::toggleSolo(int chan) { void DivEngine::muteChannel(int chan, bool mute) { BUSY_BEGIN; isMuted[chan]=mute; - if (disCont[song.dispatchOfChan[chan]].dispatch!=NULL) { + if (disCont[song.dispatchOfChan[chan]].dispatch!=NULL && song.dispatchChanOfChan[chan]>=0) { disCont[song.dispatchOfChan[chan]].dispatch->muteChannel(song.dispatchChanOfChan[chan],isMuted[chan]); } BUSY_END; @@ -2553,7 +2565,7 @@ void DivEngine::unmuteAll() { BUSY_BEGIN; for (int i=0; i=0) { disCont[song.dispatchOfChan[i]].dispatch->muteChannel(song.dispatchChanOfChan[i],isMuted[i]); } } diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index abd79aa6c..f9bda8a9b 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -496,6 +496,11 @@ int DivEngine::dispatchCmd(DivCommand c) { } } + // don't dispatch if the channel doesn't exist + if (song.dispatchChanOfChan[c.dis]<0) { + return 0; + } + // map the channel to channel of chip // c.dis is a copy of c.chan because we'll use it in the next call c.chan=song.dispatchChanOfChan[c.dis]; @@ -784,15 +789,10 @@ void DivEngine::processRow(int i, bool afterDelay) { chan[i].stopOnOff=false; } // depending on the system, portamento may still be disabled - if (disCont[song.dispatchOfChan[i]].dispatch->keyOffAffectsPorta(song.dispatchChanOfChan[i])) { + if (song.dispatchChanOfChan[i]>=0) if (disCont[song.dispatchOfChan[i]].dispatch->keyOffAffectsPorta(song.dispatchChanOfChan[i])) { chan[i].portaNote=-1; chan[i].portaSpeed=-1; dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0))); - // this here is a now-disabled hack which makes the noise channel also stop when square 3 is - /*if (i==2 && song.sysOfChan[i]==DIV_SYSTEM_SMS) { - chan[i+1].portaNote=-1; - chan[i+1].portaSpeed=-1; - }*/ } // another compatibility hack which schedules a second reset later just in case chan[i].scheduledSlideReset=true; @@ -812,14 +812,10 @@ void DivEngine::processRow(int i, bool afterDelay) { dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0))); chan[i].stopOnOff=false; } - if (disCont[song.dispatchOfChan[i]].dispatch->keyOffAffectsPorta(song.dispatchChanOfChan[i])) { + if (song.dispatchChanOfChan[i]>=0) if (disCont[song.dispatchOfChan[i]].dispatch->keyOffAffectsPorta(song.dispatchChanOfChan[i])) { chan[i].portaNote=-1; chan[i].portaSpeed=-1; dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0))); - /*if (i==2 && song.sysOfChan[i]==DIV_SYSTEM_SMS) { - chan[i+1].portaNote=-1; - chan[i+1].portaSpeed=-1; - }*/ } chan[i].scheduledSlideReset=true; } @@ -839,7 +835,7 @@ void DivEngine::processRow(int i, bool afterDelay) { // ...unless there's a way to trigger keyOn twice if (!chan[i].keyOn) { // the behavior of arpeggio reset upon note off varies per system - if (disCont[song.dispatchOfChan[i]].dispatch->keyOffAffectsArp(song.dispatchChanOfChan[i])) { + if (song.dispatchChanOfChan[i]>=0) if (disCont[song.dispatchOfChan[i]].dispatch->keyOffAffectsArp(song.dispatchChanOfChan[i])) { chan[i].arp=0; dispatchCmd(DivCommand(DIV_CMD_HINT_ARPEGGIO,i,chan[i].arp)); } @@ -1039,7 +1035,7 @@ void DivEngine::processRow(int i, bool afterDelay) { // COMPAT FLAG: limit slide range // - this confines pitch slides from dispatch->getPortaFloor to C-8 (I think) // - yep, the lowest portamento note depends on the system... - chan[i].portaNote=song.limitSlides?disCont[song.dispatchOfChan[i]].dispatch->getPortaFloor(song.dispatchChanOfChan[i]):-60; + chan[i].portaNote=(song.limitSlides && song.dispatchChanOfChan[i]>=0)?disCont[song.dispatchOfChan[i]].dispatch->getPortaFloor(song.dispatchChanOfChan[i]):-60; chan[i].portaSpeed=effectVal; dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0))); chan[i].portaStop=true; @@ -1684,7 +1680,7 @@ void DivEngine::processRow(int i, bool afterDelay) { chan[i].portaNote=song.limitSlides?0x60:255; } else { // COMPAT FLAG: limit slide range - chan[i].portaNote=song.limitSlides?disCont[song.dispatchOfChan[i]].dispatch->getPortaFloor(song.dispatchChanOfChan[i]):-60; + chan[i].portaNote=(song.limitSlides && song.dispatchChanOfChan[i]>=0)?disCont[song.dispatchOfChan[i]].dispatch->getPortaFloor(song.dispatchChanOfChan[i]):-60; } chan[i].portaSpeed=effectVal; chan[i].portaStop=true; @@ -2080,7 +2076,10 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) { if (note.volume>=0 && !disCont[song.dispatchOfChan[note.channel]].dispatch->isVolGlobal()) { // map velocity to curve and then to equivalent chip volume float curvedVol=pow((float)note.volume/127.0f,midiVolExp); - int mappedVol=disCont[song.dispatchOfChan[note.channel]].dispatch->mapVelocity(song.dispatchChanOfChan[note.channel],curvedVol); + int mappedVol=0; + if (song.dispatchChanOfChan[note.channel]>=0) { + mappedVol=disCont[song.dispatchOfChan[note.channel]].dispatch->mapVelocity(song.dispatchChanOfChan[note.channel],curvedVol); + } // fire command dispatchCmd(DivCommand(DIV_CMD_VOLUME,note.channel,mappedVol)); } @@ -2094,7 +2093,10 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) { chan[note.channel].lastIns=note.ins; } else { // note off - DivMacroInt* macroInt=disCont[song.dispatchOfChan[note.channel]].dispatch->getChanMacroInt(song.dispatchChanOfChan[note.channel]); + DivMacroInt* macroInt=NULL; + if (song.dispatchChanOfChan[note.channel]>=0) { + macroInt=disCont[song.dispatchOfChan[note.channel]].dispatch->getChanMacroInt(song.dispatchChanOfChan[note.channel]); + } if (macroInt!=NULL) { // if the current instrument has a release point in any macros and // volume is per-channel, send a note release instead of a note off @@ -2463,7 +2465,7 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) { chan[i].stopOnOff=false; } // depending on the system, portamento may still be disabled - if (disCont[song.dispatchOfChan[i]].dispatch->keyOffAffectsPorta(song.dispatchChanOfChan[i])) { + if (song.dispatchChanOfChan[i]>=0) if (disCont[song.dispatchOfChan[i]].dispatch->keyOffAffectsPorta(song.dispatchChanOfChan[i])) { chan[i].portaNote=-1; chan[i].portaSpeed=-1; dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0))); @@ -2565,6 +2567,7 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) { shallStopSched=false; // reset all chan oscs for (int i=0; igetOscBuffer(song.dispatchChanOfChan[i]); if (buf!=NULL) { buf->reset(); diff --git a/src/engine/song.cpp b/src/engine/song.cpp index dad02d708..970dae4b6 100644 --- a/src/engine/song.cpp +++ b/src/engine/song.cpp @@ -710,17 +710,23 @@ void DivSong::recalcChans() { int chanIndex=0; memset(isInsTypePossible,0,DIV_INS_MAX*sizeof(bool)); for (int i=0; imaxChans) { + dispatchChanOfChan[chanIndex]=j; + } else { + dispatchChanOfChan[chanIndex]=-1; + } dispatchFirstChan[chanIndex]=firstChan; chanIndex++; - const DivSysDef* sysDef=DivEngine::getSystemDef(system[i]); if (sysDef!=NULL) { if (sysDef->chanInsType[j][0]!=DIV_INS_NULL) { isInsTypePossible[sysDef->chanInsType[j][0]]=true; diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index 24fd6246b..4d3f78e08 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -314,6 +314,7 @@ const char* DivEngine::getChannelName(int chan) { if (chan<0 || chan>song.chans) return "??"; if (!curSubSong->chanName[chan].empty()) return curSubSong->chanName[chan].c_str(); if (sysDefs[song.sysOfChan[chan]]==NULL) return "??"; + if (song.dispatchChanOfChan[chan]<0) return "??"; const char* ret=sysDefs[song.sysOfChan[chan]]->chanNames[song.dispatchChanOfChan[chan]]; if (ret==NULL) return "??"; @@ -324,6 +325,7 @@ const char* DivEngine::getChannelShortName(int chan) { if (chan<0 || chan>song.chans) return "??"; if (!curSubSong->chanShortName[chan].empty()) return curSubSong->chanShortName[chan].c_str(); if (sysDefs[song.sysOfChan[chan]]==NULL) return "??"; + if (song.dispatchChanOfChan[chan]<0) return "??"; const char* ret=sysDefs[song.sysOfChan[chan]]->chanShortNames[song.dispatchChanOfChan[chan]]; if (ret==NULL) return "??"; @@ -333,18 +335,21 @@ const char* DivEngine::getChannelShortName(int chan) { int DivEngine::getChannelType(int chan) { if (chan<0 || chan>song.chans) return DIV_CH_NOISE; if (sysDefs[song.sysOfChan[chan]]==NULL) return DIV_CH_NOISE; + if (song.dispatchChanOfChan[chan]<0) return DIV_CH_NOISE; return sysDefs[song.sysOfChan[chan]]->chanTypes[song.dispatchChanOfChan[chan]]; } DivInstrumentType DivEngine::getPreferInsType(int chan) { if (chan<0 || chan>song.chans) return DIV_INS_STD; if (sysDefs[song.sysOfChan[chan]]==NULL) return DIV_INS_STD; + if (song.dispatchChanOfChan[chan]<0) return DIV_INS_STD; return sysDefs[song.sysOfChan[chan]]->chanInsType[song.dispatchChanOfChan[chan]][0]; } DivInstrumentType DivEngine::getPreferInsSecondType(int chan) { if (chan<0 || chan>song.chans) return DIV_INS_NULL; if (sysDefs[song.sysOfChan[chan]]==NULL) return DIV_INS_NULL; + if (song.dispatchChanOfChan[chan]<0) return DIV_INS_NULL; return sysDefs[song.sysOfChan[chan]]->chanInsType[song.dispatchChanOfChan[chan]][1]; } diff --git a/src/engine/wavOps.cpp b/src/engine/wavOps.cpp index d65af451d..dbe6f24fa 100644 --- a/src/engine/wavOps.cpp +++ b/src/engine/wavOps.cpp @@ -487,7 +487,7 @@ void DivEngine::runExportThread() { } } for (int j=0; j=0) { disCont[song.dispatchOfChan[j]].dispatch->muteChannel(song.dispatchChanOfChan[j],isMuted[j]); } } @@ -566,7 +566,7 @@ void DivEngine::runExportThread() { for (int i=0; i=0) { disCont[song.dispatchOfChan[i]].dispatch->muteChannel(song.dispatchChanOfChan[i],false); } } diff --git a/src/gui/debugWindow.cpp b/src/gui/debugWindow.cpp index 96d0a2dce..81fae0061 100644 --- a/src/gui/debugWindow.cpp +++ b/src/gui/debugWindow.cpp @@ -121,6 +121,8 @@ void FurnaceGUI::drawDebug() { ImGui::TextColored(uiColors[GUI_COLOR_ACCENT_PRIMARY],"Ch. %d: %d, %d",i,e->song.dispatchOfChan[i],e->song.dispatchChanOfChan[i]); if (ch==NULL) { ImGui::Text("NULL"); + } else if (e->song.dispatchChanOfChan[i]<0) { + ImGui::Text("---"); } else { putDispatchChan(ch,e->song.dispatchChanOfChan[i],e->song.sysOfChan[i]); }