diff --git a/CMakeLists.txt b/CMakeLists.txt index 601f01370..5888a0340 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -741,6 +741,7 @@ src/engine/platform/dave.cpp src/engine/platform/gbadma.cpp src/engine/platform/gbaminmod.cpp src/engine/platform/nds.cpp +src/engine/platform/bifurcator.cpp src/engine/platform/pcmdac.cpp src/engine/platform/dummy.cpp diff --git a/demos/misc/biffy_bifurcator.fur b/demos/misc/biffy_bifurcator.fur new file mode 100644 index 000000000..bc36b5aef Binary files /dev/null and b/demos/misc/biffy_bifurcator.fur differ diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index 12238cfe2..6757fe1e8 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -260,6 +260,9 @@ enum DivDispatchCmds { DIV_CMD_MINMOD_ECHO, + DIV_CMD_BIFURCATOR_STATE_LOAD, + DIV_CMD_BIFURCATOR_PARAMETER, + DIV_CMD_MAX }; diff --git a/src/engine/dispatchContainer.cpp b/src/engine/dispatchContainer.cpp index c4bf8f7fb..1cf7f5f14 100644 --- a/src/engine/dispatchContainer.cpp +++ b/src/engine/dispatchContainer.cpp @@ -88,6 +88,7 @@ #include "platform/powernoise.h" #include "platform/dave.h" #include "platform/nds.h" +#include "platform/bifurcator.h" #include "platform/dummy.h" #include "../ta-log.h" #include "song.h" @@ -708,6 +709,9 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do case DIV_SYSTEM_GBA_MINMOD: dispatch=new DivPlatformGBAMinMod; break; + case DIV_SYSTEM_BIFURCATOR: + dispatch=new DivPlatformBifurcator; + break; case DIV_SYSTEM_PCM_DAC: dispatch=new DivPlatformPCMDAC; break; diff --git a/src/engine/instrument.cpp b/src/engine/instrument.cpp index ba99b28f4..457bb40b2 100644 --- a/src/engine/instrument.cpp +++ b/src/engine/instrument.cpp @@ -1112,6 +1112,8 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song, bo featureSM=true; featureSL=true; break; + case DIV_INS_BIFURCATOR: + break; case DIV_INS_MAX: break; case DIV_INS_NULL: diff --git a/src/engine/instrument.h b/src/engine/instrument.h index 21de82121..59221a6f9 100644 --- a/src/engine/instrument.h +++ b/src/engine/instrument.h @@ -92,6 +92,7 @@ enum DivInstrumentType: unsigned short { DIV_INS_NDS=59, DIV_INS_GBA_DMA=60, DIV_INS_GBA_MINMOD=61, + DIV_INS_BIFURCATOR=62, DIV_INS_MAX, DIV_INS_NULL }; diff --git a/src/engine/platform/bifurcator.cpp b/src/engine/platform/bifurcator.cpp new file mode 100644 index 000000000..800f2b8f9 --- /dev/null +++ b/src/engine/platform/bifurcator.cpp @@ -0,0 +1,379 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2023 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. + */ + +#define _USE_MATH_DEFINES +#include "bifurcator.h" +#include "../engine.h" +#include "../filter.h" +#include + +#define CHIP_FREQBASE 65536 + +#define rWrite(a,v) {if(!skipRegisterWrites) {regPool[a]=v; if(dumpWrites) addWrite(a,v); }} + +const char* regCheatSheetBifurcator[]={ + "CHx_State", "x*8+0", + "CHx_Param", "x*8+2", + "CHx_Freq", "x*8+4", + "CHx_LVol", "x*8+6", + "CHx_RVol", "x*8+7", + NULL +}; + +const char** DivPlatformBifurcator::getRegisterSheet() { + return regCheatSheetBifurcator; +} + +void DivPlatformBifurcator::acquire(short** buf, size_t len) { + for (int i=0; i<4; i++) { + chan[i].curx=regPool[i*8]|(regPool[i*8+1]<<8); + chan[i].param=regPool[i*8+2]|(regPool[i*8+3]<<8); + chan[i].freq=regPool[i*8+4]|(regPool[i*8+5]<<8); + chan[i].chVolL=regPool[i*8+6]; + chan[i].chVolR=regPool[i*8+7]; + } + for (size_t h=0; h=65536) { + long long newx=(long long)chan[i].curx*(chan[i].param+65536)/32768; + newx*=65536-chan[i].curx; + chan[i].curx=(int)(newx/65536); + chan[i].audSub&=65535; + } + int out=chan[i].curx-32768; + int outL=out*chan[i].chVolL/256; + int outR=out*chan[i].chVolR/256; + oscBuf[i]->data[oscBuf[i]->needle++]=(short)((outL+outR)/2); + l+=outL/4; + r+=outR/4; + } + buf[0][h]=(short)l; + buf[1][h]=(short)r; + } + for (int i=0; i<4; i++) { + regPool[i*8]=chan[i].curx&0xff; + regPool[i*8+1]=chan[i].curx>>8; + } +} + +void DivPlatformBifurcator::tick(bool sysTick) { + for (int i=0; i<4; i++) { + chan[i].std.next(); + if (chan[i].std.vol.had) { + chan[i].outVol=(chan[i].vol*MIN(chan[i].std.vol.val,255))/255; + chan[i].volChangedL=true; + chan[i].volChangedR=true; + } + if (NEW_ARP_STRAT) { + chan[i].handleArp(); + } else if (chan[i].std.arp.had) { + if (!chan[i].inPorta) { + chan[i].baseFreq=NOTE_FREQUENCY(parent->calcArp(chan[i].note,chan[i].std.arp.val)); + } + chan[i].freqChanged=true; + } + if (chan[i].std.duty.had) { + rWrite(i*8+2,chan[i].std.duty.val&0xff); + rWrite(i*8+3,chan[i].std.duty.val>>8); + } + if (chan[i].std.pitch.had) { + if (chan[i].std.pitch.mode) { + chan[i].pitch2+=chan[i].std.pitch.val; + CLAMP_VAR(chan[i].pitch2,-32768,32767); + } else { + chan[i].pitch2=chan[i].std.pitch.val; + } + chan[i].freqChanged=true; + } + if (chan[i].std.panL.had) { + chan[i].chPanL=(255*(chan[i].std.panL.val&255))/255; + chan[i].volChangedL=true; + } + + if (chan[i].std.panR.had) { + chan[i].chPanR=(255*(chan[i].std.panR.val&255))/255; + chan[i].volChangedR=true; + } + if (chan[i].std.phaseReset.had) { + if ((chan[i].std.phaseReset.val==1) && chan[i].active) { + rWrite(i*8,1); + rWrite(i*8+1,0); + } + } + if (chan[i].std.ex1.had) { + rWrite(i*8,chan[i].std.ex1.val&0xff); + rWrite(i*8+1,chan[i].std.ex1.val>>8); + } + if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,false,2,chan[i].pitch2,chipClock,CHIP_FREQBASE); + if (chan[i].freq>65535) chan[i].freq=65535; + rWrite(i*8+4,chan[i].freq&0xff); + rWrite(i*8+5,chan[i].freq>>8); + if (chan[i].keyOn) { + if (!chan[i].std.vol.had) { + chan[i].outVol=chan[i].vol; + } + chan[i].volChangedL=true; + chan[i].volChangedR=true; + chan[i].keyOn=false; + } + if (chan[i].keyOff) { + chan[i].volChangedL=true; + chan[i].volChangedR=true; + chan[i].keyOff=false; + } + if (chan[i].freqChanged) { + chan[i].freqChanged=false; + } + } + if (chan[i].volChangedL) { + int vol=(isMuted[i] || !chan[i].active)?0:(chan[i].outVol*chan[i].chPanL/255); + rWrite(i*8+6,vol); + chan[i].volChangedL=false; + } + if (chan[i].volChangedR) { + int vol=(isMuted[i] || !chan[i].active)?0:(chan[i].outVol*chan[i].chPanR/255); + rWrite(i*8+7,vol); + chan[i].volChangedR=false; + } + } +} + +int DivPlatformBifurcator::dispatch(DivCommand c) { + switch (c.cmd) { + case DIV_CMD_NOTE_ON: { + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_BIFURCATOR); + if (c.value!=DIV_NOTE_NULL) { + chan[c.chan].baseFreq=round(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; + chan[c.chan].macroInit(ins); + if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) { + chan[c.chan].outVol=chan[c.chan].vol; + } + break; + } + case DIV_CMD_NOTE_OFF: + chan[c.chan].active=false; + chan[c.chan].keyOff=true; + chan[c.chan].macroInit(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; + } + break; + case DIV_CMD_VOLUME: + chan[c.chan].vol=c.value; + if (!chan[c.chan].std.vol.has) { + chan[c.chan].outVol=c.value; + } + chan[c.chan].volChangedL=true; + chan[c.chan].volChangedR=true; + 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_PANNING: + chan[c.chan].chPanL=c.value; + chan[c.chan].chPanR=c.value2; + chan[c.chan].volChangedL=true; + chan[c.chan].volChangedR=true; + break; + case DIV_CMD_PITCH: + chan[c.chan].pitch=c.value; + chan[c.chan].freqChanged=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; + if (chan[c.chan].baseFreq>=destFreq) { + chan[c.chan].baseFreq=destFreq; + return2=true; + } + } else { + chan[c.chan].baseFreq-=c.value; + 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_LEGATO: { + chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val-12):(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].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA)); + } + if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_FREQUENCY(chan[c.chan].note); + chan[c.chan].inPorta=c.value; + break; + case DIV_CMD_BIFURCATOR_STATE_LOAD: + rWrite(c.chan*8+c.value,c.value2); + break; + case DIV_CMD_BIFURCATOR_PARAMETER: + rWrite(c.chan*8+2+c.value,c.value2); + break; + case DIV_CMD_GET_VOLMAX: + return 255; + break; + case DIV_CMD_MACRO_OFF: + chan[c.chan].std.mask(c.value,true); + break; + case DIV_CMD_MACRO_ON: + chan[c.chan].std.mask(c.value,false); + break; + case DIV_CMD_MACRO_RESTART: + chan[c.chan].std.restart(c.value); + break; + default: + break; + } + return 1; +} + +void DivPlatformBifurcator::muteChannel(int ch, bool mute) { + isMuted[ch]=mute; + chan[ch].volChangedL=true; + chan[ch].volChangedR=true; +} + +void DivPlatformBifurcator::forceIns() { + for (int i=0; i<4; i++) { + chan[i].insChanged=true; + chan[i].volChangedL=true; + chan[i].volChangedR=true; + chan[i].active=false; + } +} + +void* DivPlatformBifurcator::getChanState(int ch) { + return &chan[ch]; +} + +DivDispatchOscBuffer* DivPlatformBifurcator::getOscBuffer(int ch) { + return oscBuf[ch]; +} + +unsigned char* DivPlatformBifurcator::getRegisterPool() { + return (unsigned char*)regPool; +} + +int DivPlatformBifurcator::getRegisterPoolSize() { + return 8*4; +} + +void DivPlatformBifurcator::reset() { + memset(regPool,0,8*4); + for (int i=0; i<4; i++) { + chan[i]=DivPlatformBifurcator::Channel(); + chan[i].std.setEngine(parent); + rWrite(i*8,chan[i].curx&0xff); + rWrite(i*8+1,chan[i].curx>>8); + rWrite(i*8+2,chan[i].param&0xff); + rWrite(i*8+3,chan[i].param>>8); + } +} + +int DivPlatformBifurcator::getOutputCount() { + return 2; +} + +DivMacroInt* DivPlatformBifurcator::getChanMacroInt(int ch) { + return &chan[ch].std; +} + +unsigned short DivPlatformBifurcator::getPan(int ch) { + return (chan[ch].chPanL<<8)|(chan[ch].chPanR); +} + +void DivPlatformBifurcator::notifyInsChange(int ins) { + for (int i=0; i<4; i++) { + if (chan[i].ins==ins) { + chan[i].insChanged=true; + } + } +} + +void DivPlatformBifurcator::notifyInsDeletion(void* ins) { + for (int i=0; i<4; i++) { + chan[i].std.notifyInsDeletion((DivInstrument*)ins); + } +} + +void DivPlatformBifurcator::setFlags(const DivConfig& flags) { + chipClock=1000000; + CHECK_CUSTOM_CLOCK; + rate=chipClock/16; + for (int i=0; i<4; i++) { + oscBuf[i]->rate=rate; + } +} + +void DivPlatformBifurcator::poke(unsigned int addr, unsigned short val) { + rWrite(addr,val); +} + +void DivPlatformBifurcator::poke(std::vector& wlist) { + for (DivRegWrite& i: wlist) rWrite(i.addr,i.val); +} + +int DivPlatformBifurcator::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) { + parent=p; + dumpWrites=false; + skipRegisterWrites=false; + for (int i=0; i<4; i++) { + isMuted[i]=false; + oscBuf[i]=new DivDispatchOscBuffer; + } + setFlags(flags); + reset(); + return 4; +} + +void DivPlatformBifurcator::quit() { + for (int i=0; i<4; i++) { + delete oscBuf[i]; + } +} diff --git a/src/engine/platform/bifurcator.h b/src/engine/platform/bifurcator.h new file mode 100644 index 000000000..6d2a04379 --- /dev/null +++ b/src/engine/platform/bifurcator.h @@ -0,0 +1,77 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2023 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 _BIFURCATOR_H +#define _BIFURCATOR_H + +#include "../dispatch.h" +#include "../waveSynth.h" + +class DivPlatformBifurcator: public DivDispatch { + struct Channel: public SharedChannel { + int param, curx; + int audSub; + bool volChangedL, volChangedR; + int chPanL, chPanR; + int chVolL, chVolR; + Channel(): + SharedChannel(255), + param(47360), + curx(1), + audSub(0), + volChangedL(false), + volChangedR(false), + chPanL(255), + chPanR(255), + chVolL(0), + chVolR(0) {} + }; + Channel chan[4]; + DivDispatchOscBuffer* oscBuf[4]; + bool isMuted[4]; + unsigned char regPool[8*4]; + + friend void putDispatchChip(void*,int); + friend void putDispatchChan(void*,int,int); + + public: + void acquire(short** buf, size_t len); + int dispatch(DivCommand c); + void* getChanState(int chan); + DivDispatchOscBuffer* getOscBuffer(int chan); + unsigned char* getRegisterPool(); + int getRegisterPoolSize(); + void reset(); + void forceIns(); + void tick(bool sysTick=true); + void muteChannel(int ch, bool mute); + int getOutputCount(); + DivMacroInt* getChanMacroInt(int ch); + unsigned short getPan(int chan); + void setFlags(const DivConfig& flags); + void notifyInsChange(int ins); + void notifyInsDeletion(void* ins); + void poke(unsigned int addr, unsigned short val); + void poke(std::vector& wlist); + const char** getRegisterSheet(); + int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags); + void quit(); +}; + +#endif diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 1e7d823c2..82d07d636 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -259,6 +259,9 @@ const char* cmdName[]={ "DAVE_CLOCK_DIV", "MINMOD_ECHO", + + "BIFURCATOR_STATE_LOAD", + "BIFURCATOR_PARAMETER" }; static_assert((sizeof(cmdName)/sizeof(void*))==DIV_CMD_MAX,"update cmdName!"); diff --git a/src/engine/song.h b/src/engine/song.h index f9983ba9e..459bd394e 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -139,6 +139,7 @@ enum DivSystem { DIV_SYSTEM_GBA_DMA, DIV_SYSTEM_GBA_MINMOD, DIV_SYSTEM_5E01, + DIV_SYSTEM_BIFURCATOR, }; enum DivEffectType: unsigned short { diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index 087124dcb..b7c62a9cf 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -2091,6 +2091,22 @@ void DivEngine::registerSystems() { } ); + sysDefs[DIV_SYSTEM_BIFURCATOR]=new DivSysDef( + "Bifurcator", NULL, 0xf2, 0, 4, false, true, 0, false, 0, 0, 0, + "a fantasy sound chip using logistic map iterations to generate sound.", + {"Channel 1", "Channel 2", "Channel 3", "Channel 4"}, + {"CH1", "CH2", "CH3", "CH4"}, + {DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE}, + {DIV_INS_BIFURCATOR, DIV_INS_BIFURCATOR, DIV_INS_BIFURCATOR, DIV_INS_BIFURCATOR}, + {}, + { + {0x10, {DIV_CMD_BIFURCATOR_STATE_LOAD, "10xx: Load low byte of channel sample state", constVal<0>, effectVal}}, + {0x11, {DIV_CMD_BIFURCATOR_STATE_LOAD, "11xx: Load high byte of channel sample state", constVal<1>, effectVal}}, + {0x12, {DIV_CMD_BIFURCATOR_PARAMETER, "12xx: Set low byte of channel parameter", constVal<0>, effectVal}}, + {0x13, {DIV_CMD_BIFURCATOR_PARAMETER, "13xx: Set high byte of channel parameter", constVal<1>, effectVal}}, + } + ); + sysDefs[DIV_SYSTEM_DUMMY]=new DivSysDef( "Dummy System", NULL, 0xfd, 0, 8, false, true, 0, false, 0, 0, 0, "this is a system designed for testing purposes.", diff --git a/src/gui/gui.h b/src/gui/gui.h index 5571f660b..f2bbbfabb 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -319,6 +319,7 @@ enum FurnaceGUIColors { GUI_COLOR_INSTR_NDS, GUI_COLOR_INSTR_GBA_DMA, GUI_COLOR_INSTR_GBA_MINMOD, + GUI_COLOR_INSTR_BIFURCATOR, GUI_COLOR_INSTR_UNKNOWN, GUI_COLOR_CHANNEL_BG, diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index 3c15519b7..343f77918 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -182,6 +182,7 @@ const char* insTypes[DIV_INS_MAX+1][3]={ {"Nintendo DS",ICON_FA_BAR_CHART,ICON_FUR_INS_NDS}, {"GBA DMA",ICON_FA_GAMEPAD,ICON_FUR_INS_GBA_DMA}, {"GBA MinMod",ICON_FA_VOLUME_UP,ICON_FUR_INS_GBA_MINMOD}, + {"Bifurcator",ICON_FA_LINE_CHART,ICON_FA_QUESTION}, {NULL,ICON_FA_QUESTION,ICON_FA_QUESTION} }; @@ -999,6 +1000,7 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={ D(GUI_COLOR_INSTR_NDS,"",ImVec4(0.7f,0.7f,0.8f,1.0f)), D(GUI_COLOR_INSTR_GBA_DMA,"",ImVec4(0.6f,0.4f,1.0f,1.0f)), D(GUI_COLOR_INSTR_GBA_MINMOD,"",ImVec4(0.5f,0.45f,0.7f,1.0f)), + D(GUI_COLOR_INSTR_BIFURCATOR,"",ImVec4(0.8925f,0.8925f,0.8925f,1.0f)), D(GUI_COLOR_INSTR_UNKNOWN,"",ImVec4(0.3f,0.3f,0.3f,1.0f)), D(GUI_COLOR_CHANNEL_BG,"",ImVec4(0.4f,0.6f,0.8f,1.0f)), @@ -1245,6 +1247,7 @@ const int availableSystems[]={ DIV_SYSTEM_DAVE, DIV_SYSTEM_NDS, DIV_SYSTEM_5E01, + DIV_SYSTEM_BIFURCATOR, 0 // don't remove this last one! }; @@ -1338,6 +1341,7 @@ const int chipsSpecial[]={ DIV_SYSTEM_DAVE, DIV_SYSTEM_NDS, DIV_SYSTEM_5E01, + DIV_SYSTEM_BIFURCATOR, 0 // don't remove this last one! }; diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 85598899d..73d65d143 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -6782,7 +6782,8 @@ void FurnaceGUI::drawInsEdit() { volMax=31; } if (ins->type==DIV_INS_ADPCMB || ins->type==DIV_INS_YMZ280B || ins->type==DIV_INS_RF5C68 || - ins->type==DIV_INS_GA20 || ins->type==DIV_INS_C140 || ins->type==DIV_INS_C219 || ins->type==DIV_INS_GBA_MINMOD) { + ins->type==DIV_INS_GA20 || ins->type==DIV_INS_C140 || ins->type==DIV_INS_C219 || ins->type==DIV_INS_GBA_MINMOD || + ins->type==DIV_INS_BIFURCATOR) { volMax=255; } if (ins->type==DIV_INS_QSOUND) { @@ -6942,6 +6943,10 @@ void FurnaceGUI::drawInsEdit() { dutyLabel="Duty"; dutyMax=ins->amiga.useSample?0:7; } + if (ins->type==DIV_INS_BIFURCATOR) { + dutyLabel="Parameter"; + dutyMax=65535; + } const char* waveLabel="Waveform"; int waveMax=(ins->type==DIV_INS_VERA)?3:(MAX(1,e->song.waveLen-1)); @@ -6981,6 +6986,7 @@ void FurnaceGUI::drawInsEdit() { if (ins->type==DIV_INS_SU || ins->type==DIV_INS_POKEY) waveMax=7; if (ins->type==DIV_INS_DAVE) waveMax=4; if (ins->type==DIV_INS_NDS) waveMax=0; + if (ins->type==DIV_INS_BIFURCATOR) waveMax=0; if (ins->type==DIV_INS_PET) { waveMax=8; waveBitMode=true; @@ -7056,6 +7062,9 @@ void FurnaceGUI::drawInsEdit() { if (ins->type==DIV_INS_GBA_MINMOD) { ex1Max=2; } + if (ins->type==DIV_INS_BIFURCATOR) { + ex1Max=65535; + } int panMin=0; int panMax=0; @@ -7121,7 +7130,8 @@ void FurnaceGUI::drawInsEdit() { panMin=0; panMax=127; } - if (ins->type==DIV_INS_C140 || ins->type==DIV_INS_C219 || ins->type==DIV_INS_GBA_MINMOD) { + if (ins->type==DIV_INS_C140 || ins->type==DIV_INS_C219 || ins->type==DIV_INS_GBA_MINMOD || + ins->type==DIV_INS_BIFURCATOR) { panMin=0; panMax=255; } @@ -7275,6 +7285,8 @@ void FurnaceGUI::drawInsEdit() { macroList.push_back(FurnaceGUIMacroDesc("Load LFSR",&ins->std.ex1Macro,0,12,160,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); } else if (ins->type==DIV_INS_GBA_MINMOD) { macroList.push_back(FurnaceGUIMacroDesc("Special",&ins->std.ex1Macro,0,ex1Max,96,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,minModModeBits)); + } else if (ins->type==DIV_INS_BIFURCATOR) { + macroList.push_back(FurnaceGUIMacroDesc("Load Value",&ins->std.ex1Macro,0,ex1Max,160,uiColors[GUI_COLOR_MACRO_OTHER])); } else { macroList.push_back(FurnaceGUIMacroDesc("Duty",&ins->std.ex1Macro,0,ex1Max,160,uiColors[GUI_COLOR_MACRO_OTHER])); } diff --git a/src/gui/presets.cpp b/src/gui/presets.cpp index a3ccff037..92b9e24b2 100644 --- a/src/gui/presets.cpp +++ b/src/gui/presets.cpp @@ -3031,6 +3031,11 @@ void FurnaceGUI::initSystemPresets() { CH(DIV_SYSTEM_NDS, 1.0f, 0, "") } ); + ENTRY( + "Bifurcator", { + CH(DIV_SYSTEM_BIFURCATOR, 1.0f, 0, "") + } + ); CATEGORY_END; CATEGORY_BEGIN("DefleMask-compatible","these configurations are compatible with DefleMask.\nselect this if you need to save as .dmf or work with that program."); diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index a75e6927f..cfafb381d 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -3750,6 +3750,7 @@ void FurnaceGUI::drawSettings() { UI_COLOR_CONFIG(GUI_COLOR_INSTR_NDS,"Nintendo DS"); UI_COLOR_CONFIG(GUI_COLOR_INSTR_GBA_DMA,"GBA DMA"); UI_COLOR_CONFIG(GUI_COLOR_INSTR_GBA_MINMOD,"GBA MinMod"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_BIFURCATOR,"Bifurcator"); UI_COLOR_CONFIG(GUI_COLOR_INSTR_UNKNOWN,"Other/Unknown"); ImGui::TreePop(); } diff --git a/src/gui/sysConf.cpp b/src/gui/sysConf.cpp index 653a7b299..94c963662 100644 --- a/src/gui/sysConf.cpp +++ b/src/gui/sysConf.cpp @@ -2431,6 +2431,7 @@ bool FurnaceGUI::drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& fl case DIV_SYSTEM_PV1000: case DIV_SYSTEM_VERA: case DIV_SYSTEM_C219: + case DIV_SYSTEM_BIFURCATOR: break; case DIV_SYSTEM_YMU759: case DIV_SYSTEM_ESFM: