diff --git a/CMakeLists.txt b/CMakeLists.txt index c121ec34..6d1d201a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -284,6 +284,8 @@ src/engine/platform/sound/x1_010/x1_010.cpp src/engine/platform/sound/swan.cpp +src/engine/platform/sound/su.cpp + src/engine/platform/sound/k005289/k005289.cpp src/engine/platform/sound/n163/n163.cpp @@ -348,6 +350,7 @@ src/engine/platform/segapcm.cpp src/engine/platform/qsound.cpp src/engine/platform/x1_010.cpp src/engine/platform/lynx.cpp +src/engine/platform/su.cpp src/engine/platform/swan.cpp src/engine/platform/vera.cpp src/engine/platform/bubsyswsg.cpp diff --git a/src/engine/dispatchContainer.cpp b/src/engine/dispatchContainer.cpp index d905b89d..67209579 100644 --- a/src/engine/dispatchContainer.cpp +++ b/src/engine/dispatchContainer.cpp @@ -44,6 +44,7 @@ #include "platform/qsound.h" #include "platform/vera.h" #include "platform/x1_010.h" +#include "platform/su.h" #include "platform/swan.h" #include "platform/lynx.h" #include "platform/bubsyswsg.h" @@ -315,6 +316,9 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do case DIV_SYSTEM_ES5506: dispatch=new DivPlatformES5506; break; + case DIV_SYSTEM_SOUND_UNIT: + dispatch=new DivPlatformSoundUnit; + break; case DIV_SYSTEM_DUMMY: dispatch=new DivPlatformDummy; break; diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index ad7b085a..2701a4e2 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -1070,8 +1070,10 @@ const char** DivEngine::getRegisterSheet(int sys) { } void DivEngine::recalcChans() { + bool isInsTypePossible[DIV_INS_MAX]; chans=0; int chanIndex=0; + memset(isInsTypePossible,0,DIV_INS_MAX*sizeof(bool)); for (int i=0; ichanInsType[j][0]!=DIV_INS_NULL) { + isInsTypePossible[sysDefs[song.system[i]]->chanInsType[j][0]]=true; + logV("Marking"); + } + + if (sysDefs[song.system[i]]->chanInsType[j][1]!=DIV_INS_NULL) { + isInsTypePossible[sysDefs[song.system[i]]->chanInsType[j][1]]=true; + } + } } } + + possibleInsTypes.clear(); + for (int i=0; i midiIns; std::vector midiOuts; std::vector cmdStream; + std::vector possibleInsTypes; DivSysDef* sysDefs[256]; DivSystem sysFileMapFur[256]; DivSystem sysFileMapDMF[256]; @@ -503,6 +507,9 @@ class DivEngine { // get channel count int getTotalChannelCount(); + // get instrument types available for use + std::vector& getPossibleInsTypes(); + // get effect description const char* getEffectDesc(unsigned char effect, int chan, bool notNull=false); diff --git a/src/engine/platform/sound/su.cpp b/src/engine/platform/sound/su.cpp index daf710fc..69b02a78 100644 --- a/src/engine/platform/sound/su.cpp +++ b/src/engine/platform/sound/su.cpp @@ -1,3 +1,4 @@ +#define _USE_MATH_DEFINES #include "su.h" #include @@ -211,6 +212,10 @@ void SoundUnit::NextSample(short* l, short* r) { ocycle[i]=0; chan[i].flags.resosc=0; } + if (muted[i]) { + nsL[i]=0; + nsR[i]=0; + } } tnsL=(nsL[0]+nsL[1]+nsL[2]+nsL[3]+nsL[4]+nsL[5]+nsL[6]+nsL[7])>>2; tnsR=(nsR[0]+nsR[1]+nsR[2]+nsR[3]+nsR[4]+nsR[5]+nsR[6]+nsR[7])>>2; @@ -233,22 +238,38 @@ void SoundUnit::Init() { SCpantabR[128+i]=i-1; } SCpantabR[128]=0; + for (int i=0; i<8; i++) { + muted[i]=false; + } } void SoundUnit::Reset() { for (int i=0; i<8; i++) { + ocycle[i]=0; cycle[i]=0; + rcycle[i]=0; resetfreq[i]=0; voldcycles[i]=0; volicycles[i]=0; fscycles[i]=0; sweep[i]=0; ns[i]=0; + fns[i]=0; + nsL[i]=0; + nsR[i]=0; + nslow[i]=0; + nshigh[i]=0; + nsband[i]=0; swvolt[i]=1; swfreqt[i]=1; swcutt[i]=1; lfsr[i]=0xaaaa; + oldfreq[i]=0; + oldflags[i]=0; + pcmdec[i]=0; } + tnsL=0; + tnsR=0; memset(chan,0,sizeof(SUChannel)*8); } @@ -258,4 +279,5 @@ void SoundUnit::Write(unsigned char addr, unsigned char data) { SoundUnit::SoundUnit() { Init(); + memset(pcm,0,SOUNDCHIP_PCM_SIZE); } diff --git a/src/engine/platform/sound/su.h b/src/engine/platform/sound/su.h index a201e955..972d5343 100644 --- a/src/engine/platform/sound/su.h +++ b/src/engine/platform/sound/su.h @@ -85,6 +85,7 @@ class SoundUnit { unsigned short restimer; } chan[8]; signed char pcm[SOUNDCHIP_PCM_SIZE]; + bool muted[8]; void Write(unsigned char addr, unsigned char data); void NextSample(short* l, short* r); void Init(); diff --git a/src/engine/platform/su.cpp b/src/engine/platform/su.cpp new file mode 100644 index 00000000..f98b1994 --- /dev/null +++ b/src/engine/platform/su.cpp @@ -0,0 +1,325 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2022 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "su.h" +#include "../engine.h" +#include + +//#define rWrite(a,v) pendingWrites[a]=v; +#define rWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} } +#define chWrite(c,a,v) rWrite(((c)<<5)|(a),v); + +#define CHIP_FREQBASE 524288 + +const char** DivPlatformSoundUnit::getRegisterSheet() { + return NULL; +} + +const char* DivPlatformSoundUnit::getEffectName(unsigned char effect) { + return NULL; +} + +void DivPlatformSoundUnit::acquire(short* bufL, short* bufR, size_t start, size_t len) { + for (size_t h=start; hWrite(w.addr,w.val); + writes.pop(); + } + su->NextSample(&bufL[h],&bufR[h]); + } +} + +void DivPlatformSoundUnit::writeControl(int ch) { + chWrite(ch,0x04,(chan[ch].wave&7)|(chan[ch].pcm<<3)|(chan[ch].control<<4)); +} + +void DivPlatformSoundUnit::tick(bool sysTick) { + for (int i=0; i<8; i++) { + chan[i].std.next(); + if (chan[i].std.vol.had) { + chan[i].outVol=((chan[i].vol&127)*MIN(127,chan[i].std.vol.val))>>7; + chWrite(i,0x02,chan[i].outVol); + } + if (chan[i].std.arp.had) { + if (!chan[i].inPorta) { + if (chan[i].std.arp.mode) { + chan[i].baseFreq=NOTE_FREQUENCY(chan[i].std.arp.val); + } else { + chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note+chan[i].std.arp.val); + } + } + chan[i].freqChanged=true; + } else { + if (chan[i].std.arp.mode && chan[i].std.arp.finished) { + chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note); + chan[i].freqChanged=true; + } + } + if (chan[i].std.duty.had) { + chan[i].duty=chan[i].std.duty.val; + chWrite(i,0x08,chan[i].duty); + } + if (chan[i].std.wave.had) { + chan[i].wave=chan[i].std.wave.val&7; + writeControl(i); + } + if (chan[i].std.panL.had) { + chan[i].pan=chan[i].std.panL.val; + chWrite(i,0x03,chan[i].pan); + } + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } + if (chan[i].std.ex1.had) { + chan[i].cutoff=chan[i].std.ex1.val; + chWrite(i,0x06,chan[i].cutoff&0xff); + chWrite(i,0x07,chan[i].cutoff>>8); + } + if (chan[i].std.ex2.had) { + chan[i].res=chan[i].std.ex2.val; + chWrite(i,0x09,chan[i].res); + } + if (chan[i].std.ex3.had) { + chan[i].control=chan[i].std.ex3.val&15; + writeControl(i); + } + if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { + //DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false)+chan[i].std.pitch.val; + chWrite(i,0x00,chan[i].freq&0xff); + chWrite(i,0x01,chan[i].freq>>8); + if (chan[i].freq>65535) chan[i].freq=65535; + if (chan[i].keyOn) { + //rWrite(16+i*5,0x80); + //chWrite(i,0x04,0x80|chan[i].vol); + } + if (chan[i].keyOff) { + chWrite(i,0x02,0); + } + if (chan[i].keyOn) chan[i].keyOn=false; + if (chan[i].keyOff) chan[i].keyOff=false; + chan[i].freqChanged=false; + } + } +} + +int DivPlatformSoundUnit::dispatch(DivCommand c) { + switch (c.cmd) { + case DIV_CMD_NOTE_ON: { + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_SU); + if (c.value!=DIV_NOTE_NULL) { + chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value); + chan[c.chan].freqChanged=true; + chan[c.chan].note=c.value; + } + chan[c.chan].active=true; + chan[c.chan].keyOn=true; + chWrite(c.chan,0x02,chan[c.chan].vol); + chan[c.chan].std.init(ins); + chan[c.chan].insChanged=false; + break; + } + case DIV_CMD_NOTE_OFF: + chan[c.chan].active=false; + chan[c.chan].keyOff=true; + chan[c.chan].std.init(NULL); + break; + case DIV_CMD_NOTE_OFF_ENV: + case DIV_CMD_ENV_RELEASE: + chan[c.chan].std.release(); + break; + case DIV_CMD_INSTRUMENT: + if (chan[c.chan].ins!=c.value || c.value2==1) { + chan[c.chan].ins=c.value; + chan[c.chan].insChanged=true; + } + break; + case DIV_CMD_VOLUME: + if (chan[c.chan].vol!=c.value) { + chan[c.chan].vol=c.value; + if (!chan[c.chan].std.vol.has) { + chan[c.chan].outVol=c.value; + if (chan[c.chan].active) chWrite(c.chan,0x02,chan[c.chan].outVol); + } + } + break; + case DIV_CMD_GET_VOLUME: + if (chan[c.chan].std.vol.has) { + return chan[c.chan].vol; + } + return chan[c.chan].outVol; + break; + case DIV_CMD_PITCH: + chan[c.chan].pitch=c.value; + chan[c.chan].freqChanged=true; + break; + case DIV_CMD_WAVE: + chan[c.chan].wave=c.value; + chan[c.chan].keyOn=true; + break; + case DIV_CMD_NOTE_PORTA: { + int destFreq=NOTE_FREQUENCY(c.value2); + bool return2=false; + if (destFreq>chan[c.chan].baseFreq) { + chan[c.chan].baseFreq+=c.value*(1+(chan[c.chan].baseFreq>>9)); + if (chan[c.chan].baseFreq>=destFreq) { + chan[c.chan].baseFreq=destFreq; + return2=true; + } + } else { + chan[c.chan].baseFreq-=c.value*(1+(chan[c.chan].baseFreq>>9)); + if (chan[c.chan].baseFreq<=destFreq) { + chan[c.chan].baseFreq=destFreq; + return2=true; + } + } + chan[c.chan].freqChanged=true; + if (return2) { + chan[c.chan].inPorta=false; + return 2; + } + break; + } + case DIV_CMD_PANNING: { + chan[c.chan].pan=c.value; + chWrite(c.chan,0x03,chan[c.chan].pan); + break; + } + case DIV_CMD_LEGATO: + chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value+((chan[c.chan].std.arp.will && !chan[c.chan].std.arp.mode)?(chan[c.chan].std.arp.val):(0))); + chan[c.chan].freqChanged=true; + chan[c.chan].note=c.value; + break; + case DIV_CMD_PRE_PORTA: + if (chan[c.chan].active && c.value2) { + if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_SU)); + } + chan[c.chan].inPorta=c.value; + break; + case DIV_CMD_GET_VOLMAX: + return 127; + break; + case DIV_ALWAYS_SET_VOLUME: + return 1; + break; + default: + break; + } + return 1; +} + +void DivPlatformSoundUnit::muteChannel(int ch, bool mute) { + isMuted[ch]=mute; + su->muted[ch]=mute; +} + +void DivPlatformSoundUnit::forceIns() { + for (int i=0; i<8; i++) { + chan[i].insChanged=true; + chan[i].freqChanged=true; + } +} + +void* DivPlatformSoundUnit::getChanState(int ch) { + return &chan[ch]; +} + +unsigned char* DivPlatformSoundUnit::getRegisterPool() { + return (unsigned char*)su->chan; +} + +int DivPlatformSoundUnit::getRegisterPoolSize() { + return 256; +} + +void DivPlatformSoundUnit::reset() { + while (!writes.empty()) writes.pop(); + memset(regPool,0,128); + for (int i=0; i<8; i++) { + chan[i]=DivPlatformSoundUnit::Channel(); + chan[i].std.setEngine(parent); + } + if (dumpWrites) { + addWrite(0xffffffff,0); + } + su->Reset(); + for (int i=0; i<8; i++) { + chWrite(i,0x08,0x3f); + } + lastPan=0xff; + cycles=0; + curChan=-1; + sampleBank=0; + lfoMode=0; + lfoSpeed=255; + delay=500; +} + +bool DivPlatformSoundUnit::isStereo() { + return true; +} + +bool DivPlatformSoundUnit::keyOffAffectsArp(int ch) { + return true; +} + +void DivPlatformSoundUnit::notifyInsDeletion(void* ins) { + for (int i=0; i<8; i++) { + chan[i].std.notifyInsDeletion((DivInstrument*)ins); + } +} + +void DivPlatformSoundUnit::setFlags(unsigned int flags) { + if (flags&1) { + chipClock=1190000; + } else { + chipClock=1236000; + } + rate=chipClock/4; +} + +void DivPlatformSoundUnit::poke(unsigned int addr, unsigned short val) { + rWrite(addr,val); +} + +void DivPlatformSoundUnit::poke(std::vector& wlist) { + for (DivRegWrite& i: wlist) rWrite(i.addr,i.val); +} + +int DivPlatformSoundUnit::init(DivEngine* p, int channels, int sugRate, unsigned int flags) { + parent=p; + dumpWrites=false; + skipRegisterWrites=false; + for (int i=0; i<8; i++) { + isMuted[i]=false; + } + setFlags(flags); + su=new SoundUnit(); + su->Init(); + reset(); + return 6; +} + +void DivPlatformSoundUnit::quit() { + delete su; +} + +DivPlatformSoundUnit::~DivPlatformSoundUnit() { +} diff --git a/src/engine/platform/su.h b/src/engine/platform/su.h new file mode 100644 index 00000000..5baf13f0 --- /dev/null +++ b/src/engine/platform/su.h @@ -0,0 +1,102 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2022 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef _SU_H +#define _SU_H + +#include "../dispatch.h" +#include +#include "../macroInt.h" +#include "sound/su.h" + +class DivPlatformSoundUnit: public DivDispatch { + struct Channel { + int freq, baseFreq, pitch, note; + int ins, cutoff, res, control; + signed char pan; + unsigned char duty; + bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, noise, pcm; + signed char vol, outVol, wave; + DivMacroInt std; + Channel(): + freq(0), + baseFreq(0), + pitch(0), + note(0), + ins(-1), + cutoff(65535), + res(0), + control(0), + pan(0), + duty(63), + active(false), + insChanged(true), + freqChanged(false), + keyOn(false), + keyOff(false), + inPorta(false), + noise(false), + pcm(false), + vol(127), + outVol(127), + wave(-1) {} + }; + Channel chan[8]; + bool isMuted[8]; + struct QueuedWrite { + unsigned char addr; + unsigned char val; + QueuedWrite(unsigned char a, unsigned char v): addr(a), val(v) {} + }; + std::queue writes; + unsigned char lastPan; + + int cycles, curChan, delay; + short tempL; + short tempR; + unsigned char sampleBank, lfoMode, lfoSpeed; + SoundUnit* su; + unsigned char regPool[128]; + void writeControl(int ch); + + friend void putDispatchChan(void*,int,int); + public: + void acquire(short* bufL, short* bufR, size_t start, size_t len); + int dispatch(DivCommand c); + void* getChanState(int chan); + unsigned char* getRegisterPool(); + int getRegisterPoolSize(); + void reset(); + void forceIns(); + void tick(bool sysTick=true); + void muteChannel(int ch, bool mute); + bool isStereo(); + bool keyOffAffectsArp(int ch); + void setFlags(unsigned int flags); + void notifyInsDeletion(void* ins); + void poke(unsigned int addr, unsigned short val); + void poke(std::vector& wlist); + const char** getRegisterSheet(); + const char* getEffectName(unsigned char effect); + int init(DivEngine* parent, int channels, int sugRate, unsigned int flags); + void quit(); + ~DivPlatformSoundUnit(); +}; + +#endif diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index 2c5e3c73..bbf5db2d 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -48,6 +48,10 @@ int DivEngine::getTotalChannelCount() { return chans; } +std::vector& DivEngine::getPossibleInsTypes() { + return possibleInsTypes; +} + // TODO: rewrite this function (again). it's an unreliable mess. const char* DivEngine::getSongSystemName() { switch (song.systemLen) { @@ -358,7 +362,7 @@ int DivEngine::minVGMVersion(DivSystem which) { void DivEngine::registerSystems() { logD("registering systems..."); - + sysDefs[DIV_SYSTEM_YMU759]=new DivSysDef( "Yamaha YMU759", NULL, 0x01, 0x01, 17, true, false, 0, false, {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8", "Channel 9", "Channel 10", "Channel 11", "Channel 12", "Channel 13", "Channel 14", "Channel 15", "Channel 16", "PCM" }, // name diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 372cf998..2b0b3c75 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -2729,7 +2729,7 @@ bool FurnaceGUI::loop() { editOptions(true); ImGui::Separator(); if (ImGui::MenuItem("clear...")) { - showWarning("Are you sure you want to clear...",GUI_WARN_CLEAR); + showWarning("Are you sure you want to clear... (cannot be undone!)",GUI_WARN_CLEAR); } ImGui::EndMenu(); } diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index a5de2f29..38174498 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -179,6 +179,10 @@ const char* es5506FilterModes[4]={ "HP/K2, HP/K2", "HP/K2, LP/K1", "LP/K2, LP/K2", "LP/K2, LP/K1", }; +const char* suControlBits[5]={ + "ring mod", "low pass", "band pass", "high pass", NULL +}; + const char* panBits[3]={ "right", "left", NULL }; @@ -1418,9 +1422,19 @@ void FurnaceGUI::drawInsEdit() { if (ins->type>=DIV_INS_MAX) ins->type=DIV_INS_FM; int insType=ins->type; ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + /* if (ImGui::Combo("##Type",&insType,insTypes,DIV_INS_MAX,DIV_INS_MAX)) { ins->type=(DivInstrumentType)insType; } + */ + if (ImGui::BeginCombo("##Type",insTypes[insType])) { + for (DivInstrumentType i: e->getPossibleInsTypes()) { + if (ImGui::Selectable(insTypes[i],insType==i)) { + ins->type=i; + } + } + ImGui::EndCombo(); + } ImGui::EndTable(); } @@ -2776,7 +2790,7 @@ void FurnaceGUI::drawInsEdit() { if (ins->type==DIV_INS_AMIGA) { volMax=64; } - if (ins->type==DIV_INS_FM || ins->type == DIV_INS_MIKEY) { + if (ins->type==DIV_INS_FM || ins->type==DIV_INS_MIKEY || ins->type==DIV_INS_SU) { volMax=127; } if (ins->type==DIV_INS_GB) { @@ -2851,6 +2865,9 @@ void FurnaceGUI::drawInsEdit() { dutyLabel="Filter Mode"; dutyMax=3; } + if (ins->type==DIV_INS_SU) { + dutyMax=127; + } bool dutyIsRel=(ins->type==DIV_INS_C64 && !ins->c64.dutyIsAbs); const char* waveLabel="Waveform"; @@ -2866,6 +2883,7 @@ void FurnaceGUI::drawInsEdit() { if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPZ) waveMax=0; if (ins->type==DIV_INS_MIKEY) waveMax=0; if (ins->type==DIV_INS_ES5506) waveMax=255; + if (ins->type==DIV_INS_SU) waveMax=7; if (ins->type==DIV_INS_PET) { waveMax=8; bitMode=true; @@ -2901,6 +2919,10 @@ void FurnaceGUI::drawInsEdit() { ex1Max=63; ex2Max=4095; } + if (ins->type==DIV_INS_SU) { + ex1Max=65535; + ex2Max=255; + } if (ins->type==DIV_INS_SAA1099) ex1Max=8; if (ins->type==DIV_INS_ES5506) { ex1Max=65535; @@ -2979,6 +3001,8 @@ void FurnaceGUI::drawInsEdit() { NORMAL_MACRO(ins->std.ex1Macro,0,ex1Max,"ex1","Mod Depth",160,ins->std.ex1Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],0,ex1Max,NULL,false); } else if (ins->type==DIV_INS_ES5506) { NORMAL_MACRO(ins->std.ex1Macro,((ins->std.ex1Macro.mode!=1)?(-ex1Max):0),ex1Max,"ex1","Filter K1",160,ins->std.ex1Macro.open,false,NULL,false,NULL,0,0,0,0,true,2,macroFilterMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],((ins->std.ex1Macro.mode!=1)?(-ex1Max):0),ex1Max,NULL,false); + } else if (ins->type==DIV_INS_SU) { + NORMAL_MACRO(ins->std.ex1Macro,0,ex1Max,"ex1","Cutoff",160,ins->std.ex1Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],0,ex1Max,NULL,false); } else { NORMAL_MACRO(ins->std.ex1Macro,0,ex1Max,"ex1","Duty",160,ins->std.ex1Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],0,ex1Max,NULL,false); } @@ -2992,6 +3016,8 @@ void FurnaceGUI::drawInsEdit() { NORMAL_MACRO(ins->std.ex2Macro,0,ex2Max,"ex2","Mod Speed",160,ins->std.ex2Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[5],0,ex2Max,NULL,false); } else if (ins->type==DIV_INS_ES5506) { NORMAL_MACRO(ins->std.ex2Macro,((ins->std.ex2Macro.mode!=1)?(-ex2Max):0),ex2Max,"ex2","Filter K2",160,ins->std.ex2Macro.open,false,NULL,false,NULL,0,0,0,0,true,2,macroFilterMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[5],((ins->std.ex2Macro.mode!=1)?(-ex2Max):0),ex2Max,NULL,false); + } else if (ins->type==DIV_INS_SU) { + NORMAL_MACRO(ins->std.ex2Macro,0,ex2Max,"ex2","Resonance",160,ins->std.ex2Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[5],0,ex2Max,NULL,false); } else { NORMAL_MACRO(ins->std.ex2Macro,0,ex2Max,"ex2","Envelope",ex2Bit?64:160,ins->std.ex2Macro.open,ex2Bit,ayEnvBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[5],0,ex2Max,NULL,false); } @@ -3025,6 +3051,9 @@ void FurnaceGUI::drawInsEdit() { NORMAL_MACRO(ins->std.ex7Macro,-128,127,"ex7","Envelope K2 ramp",160,ins->std.ex7Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[10],-128,127,NULL,false); NORMAL_MACRO(ins->std.ex8Macro,0,2,"ex8","Envelope mode",64,ins->std.ex8Macro.open,true,es5506EnvelopeModes,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[11],0,2,NULL,false); } + if (ins->type==DIV_INS_SU) { + NORMAL_MACRO(ins->std.ex3Macro,0,4,"ex3","Control",64,ins->std.ex3Macro.open,true,suControlBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[6],0,4,NULL,false); + } MACRO_END; } else { // classic view (TODO: possibly remove)