Merge branch 'master' of https://github.com/tildearrow/furnace into es5506_alt

* 'master' of https://github.com/tildearrow/furnace:
  maybe uninitialized
  GUI: prepare for a per-channel oscilloscope?
  GUI: add backdrop if system file picker is open
  GUI: add ability to customize startup system
  GUI: finally implement "don't scroll when moving c ursor" setting
  new panning effects (80/81/82)
  GUI: fix sample paste crash
  GUI: implement sample scroll with mouse wheel
  sysDef oopsie

# Conflicts:
#	src/gui/guiConst.cpp
This commit is contained in:
cam900 2022-05-01 00:25:21 +09:00
commit 5414213710
52 changed files with 1021 additions and 342 deletions

View File

@ -397,6 +397,7 @@ src/gui/guiConst.cpp
src/gui/about.cpp
src/gui/channels.cpp
src/gui/chanOsc.cpp
src/gui/compatFlags.cpp
src/gui/cursor.cpp
src/gui/dataList.cpp

View File

@ -19,12 +19,9 @@
- maybe YMU759 ADPCM channel
- ADPCM chips
- more effects for FM param control
- ability to customize startup system
- store system presets in new file
- Game Boy envelope macro/sequence
- option to display chip names instead of "multi-system" on title bar
- rewrite the system name detection function anyway
- add nightly.link
- scroll instrument/wave/sample list when selecting item
- unified data view
- volume commands should work on Game Boy
@ -35,7 +32,6 @@
- try to find out why does VSlider not accept keyboard input
- finish lock layout
- if macros have release, note off should release them
- add "don't scroll on cursor movement" option
- add ability to select entire row when clicking on row number
- store edit/followOrders/followPattern state in config
- add ability to select a column by double clicking
@ -44,5 +40,4 @@
- settings: OK/Cancel buttons should be always visible
- Apply button in settings
- better FM chip names (number and codename)
- find and replace
- precise panning effects (80xx linear, 81xx/82xx per-channel)
- find and replace

View File

@ -14,6 +14,15 @@ however, effects are continuous, which means you only need to type it once and t
- maximum tremolo depth is -60 volume steps.
- `08xy`: set panning. `x` is the left channel and `y` is the right one.
- not all systems support this effect.
- `80xx`: set panning (linear). this effect behaves more like other trackers:
- `00` is left.
- `80` is center.
- `FF` is right.
- not all systems support this effect.
- `81xx`: set volume of left channel (from `00` to `FF`).
- not all systems support this effect.
- `82xx`: set volume of right channel (from `00` to `FF`).
- not all systems support this effect.
- `09xx`: set speed 1.
- `0Axy`: volume slide.
- if `x` is 0 then this is a slide down.

View File

@ -21,6 +21,7 @@
#define _DISPATCH_H
#include <stdlib.h>
#include <string.h>
#include <vector>
#define ONE_SEMITONE 2200
@ -49,7 +50,7 @@ enum DivDispatchCmds {
DIV_CMD_GET_VOLMAX, // () -> volMax
DIV_CMD_NOTE_PORTA, // (target, speed) -> 2 if target reached
DIV_CMD_PITCH, // (pitch)
DIV_CMD_PANNING, // (pan)
DIV_CMD_PANNING, // (left, right)
DIV_CMD_LEGATO, // (note)
DIV_CMD_PRE_PORTA, // (inPorta, isPortaOrSlide)
DIV_CMD_PRE_NOTE, // used in C64 (note)
@ -214,6 +215,18 @@ struct DivRegWrite {
addr(a), val(v) {}
};
struct DivDispatchOscBuffer {
unsigned int rate;
unsigned short needle;
short data[65536];
DivDispatchOscBuffer():
rate(65536),
needle(0) {
memset(data,0,65536*sizeof(short));
}
};
class DivEngine;
class DivMacroInt;
@ -277,6 +290,12 @@ class DivDispatch {
* @return a pointer, or NULL.
*/
virtual DivMacroInt* getChanMacroInt(int chan);
/**
* get an oscilloscope buffer for a channel.
* @return a pointer to a DivDispatchOscBuffer, or NULL if not supported.
*/
virtual DivDispatchOscBuffer* getOscBuffer(int chan);
/**
* get the register pool of this dispatch.

View File

@ -69,6 +69,12 @@ const char* DivEngine::getEffectDesc(unsigned char effect, int chan, bool notNul
return "0Dxx: Jump to next pattern";
case 0x0f:
return "0Fxx: Set speed 2";
case 0x80:
return "80xx: Set panning (00: left; 80: center; FF: right)";
case 0x81:
return "81xx: Set panning (left channel)";
case 0x82:
return "82xx: Set panning (right channel)";
case 0xc0: case 0xc1: case 0xc2: case 0xc3:
return "Cxxx: Set tick rate (hz)";
case 0xe0:
@ -645,6 +651,101 @@ void DivEngine::renderSamples() {
es5506MemLen=memPos+256;
}
String DivEngine::encodeSysDesc(std::vector<int>& desc) {
String ret;
if (desc[0]!=0) {
int index=0;
for (size_t i=0; i<desc.size(); i+=4) {
ret+=fmt::sprintf("%d %d %d %d ",systemToFileFur((DivSystem)desc[i]),desc[i+1],desc[i+2],desc[i+3]);
index++;
if (index>=32) break;
}
}
return ret;
}
std::vector<int> DivEngine::decodeSysDesc(String desc) {
std::vector<int> ret;
bool hasVal=false;
bool negative=false;
int val=0;
int curStage=0;
int sysID=0;
int sysVol=0;
int sysPan=0;
int sysFlags=0;
desc+=' '; // ha
for (char i: desc) {
switch (i) {
case ' ':
if (hasVal) {
if (negative) val=-val;
switch (curStage) {
case 0:
sysID=val;
curStage++;
break;
case 1:
sysVol=val;
curStage++;
break;
case 2:
sysPan=val;
curStage++;
break;
case 3:
sysFlags=val;
if (systemFromFileFur(sysID)!=0) {
if (sysVol<-128) sysVol=-128;
if (sysVol>127) sysVol=127;
if (sysPan<-128) sysPan=-128;
if (sysPan>127) sysPan=127;
ret.push_back(systemFromFileFur(sysID));
ret.push_back(sysVol);
ret.push_back(sysPan);
ret.push_back(sysFlags);
}
curStage=0;
break;
}
hasVal=false;
negative=false;
val=0;
}
break;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
val=(val*10)+(i-'0');
hasVal=true;
break;
case '-':
if (!hasVal) negative=true;
break;
}
}
return ret;
}
void DivEngine::initSongWithDesc(const int* description) {
int chanCount=0;
if (description[0]!=0) {
int index=0;
for (int i=0; description[i]; i+=4) {
song.system[index]=(DivSystem)description[i];
song.systemVol[index]=description[i+1];
song.systemPan[index]=description[i+2];
song.systemFlags[index]=description[i+3];
index++;
chanCount+=getChannelCount(song.system[index]);
if (chanCount>=63) break;
if (index>=32) break;
}
song.systemLen=index;
}
}
void DivEngine::createNew(const int* description) {
quitDispatch();
BUSY_BEGIN;
@ -652,18 +753,7 @@ void DivEngine::createNew(const int* description) {
song.unload();
song=DivSong();
if (description!=NULL) {
if (description[0]!=0) {
int index=0;
for (int i=0; description[i]; i+=4) {
song.system[index]=(DivSystem)description[i];
song.systemVol[index]=description[i+1];
song.systemPan[index]=description[i+2];
song.systemFlags[index]=description[i+3];
index++;
if (index>=32) break;
}
song.systemLen=index;
}
initSongWithDesc(description);
}
recalcChans();
renderSamples();
@ -930,6 +1020,16 @@ unsigned char* DivEngine::getRegisterPool(int sys, int& size, int& depth) {
return disCont[sys].dispatch->getRegisterPool();
}
DivMacroInt* DivEngine::getMacroInt(int chan) {
if (chan<0 || chan>=chans) return NULL;
return disCont[dispatchOfChan[chan]].dispatch->getChanMacroInt(dispatchChanOfChan[chan]);
}
DivDispatchOscBuffer* DivEngine::getOscBuffer(int chan) {
if (chan<0 || chan>=chans) return NULL;
return disCont[dispatchOfChan[chan]].dispatch->getOscBuffer(dispatchChanOfChan[chan]);
}
void DivEngine::enableCommandStream(bool enable) {
cmdStreamEnabled=enable;
}
@ -1083,6 +1183,10 @@ int DivEngine::convertPanSplitToLinear(unsigned int val, unsigned char bits, int
return pan*range;
}
int DivEngine::convertPanSplitToLinearLR(unsigned char left, unsigned char right, int range) {
return convertPanSplitToLinear((left<<8)|right,8,range);
}
unsigned int DivEngine::convertPanLinearToSplit(int val, unsigned char bits, int range) {
if (val<0) val=0;
if (val>range) val=range;
@ -1231,6 +1335,8 @@ void DivEngine::recalcChans() {
for (int i=0; i<DIV_INS_MAX; i++) {
if (isInsTypePossible[i]) possibleInsTypes.push_back((DivInstrumentType)i);
}
hasLoadedSomething=true;
}
void DivEngine::reset() {
@ -2700,6 +2806,18 @@ bool DivEngine::init() {
loadConf();
// set default system preset
if (!hasLoadedSomething) {
logI("setting");
std::vector<int> preset=decodeSysDesc(getConfString("initialSys",""));
logI("preset size %ld",preset.size());
if (preset.size()>0 && (preset.size()&3)==0) {
preset.push_back(0);
initSongWithDesc(preset.data());
}
hasLoadedSomething=true;
}
// init the rest of engine
bool haveAudio=false;
if (!initAudioBackend()) {

View File

@ -84,7 +84,7 @@ struct DivChannelState {
int delayOrder, delayRow, retrigSpeed, retrigTick;
int vibratoDepth, vibratoRate, vibratoPos, vibratoDir, vibratoFine;
int tremoloDepth, tremoloRate, tremoloPos;
unsigned char arp, arpStage, arpTicks;
unsigned char arp, arpStage, arpTicks, panL, panR;
bool doNote, legato, portaStop, keyOn, keyOff, nowYouCanStop, stopOnOff;
bool arpYield, delayLocked, inPorta, scheduledSlideReset, shorthandPorta, noteOnInhibit, resetArp;
@ -119,6 +119,8 @@ struct DivChannelState {
arp(0),
arpStage(-1),
arpTicks(1),
panL(255),
panR(255),
doNote(false),
legato(false),
portaStop(false),
@ -296,6 +298,7 @@ class DivEngine {
bool midiIsDirect;
bool lowLatency;
bool systemsRegistered;
bool hasLoadedSomething;
int softLockCount;
int subticks, ticks, curRow, curOrder, remainingLoops, nextSpeed;
double divider;
@ -398,6 +401,7 @@ class DivEngine {
bool deinitAudioBackend();
void registerSystems();
void initSongWithDesc(const int* description);
void exchangeIns(int one, int two);
void swapChannels(int src, int dest);
@ -420,6 +424,9 @@ class DivEngine {
DivInstrument* getIns(int index, DivInstrumentType fallbackType=DIV_INS_FM);
DivWavetable* getWave(int index);
DivSample* getSample(int index);
// parse system setup description
String encodeSysDesc(std::vector<int>& desc);
std::vector<int> decodeSysDesc(String desc);
// start fresh
void createNew(const int* description);
// load a file.
@ -479,6 +486,7 @@ class DivEngine {
// convert panning formats
int convertPanSplitToLinear(unsigned int val, unsigned char bits, int range);
int convertPanSplitToLinearLR(unsigned char left, unsigned char right, int range);
unsigned int convertPanLinearToSplit(int val, unsigned char bits, int range);
// find song loop position
@ -716,6 +724,12 @@ class DivEngine {
// get register pool
unsigned char* getRegisterPool(int sys, int& size, int& depth);
// get macro interpreter
DivMacroInt* getMacroInt(int chan);
// get osc buffer
DivDispatchOscBuffer* getOscBuffer(int chan);
// enable command stream dumping
void enableCommandStream(bool enable);
@ -877,6 +891,7 @@ class DivEngine {
midiIsDirect(false),
lowLatency(false),
systemsRegistered(false),
hasLoadedSomething(false),
softLockCount(0),
subticks(0),
ticks(0),

View File

@ -33,6 +33,10 @@ DivMacroInt* DivDispatch::getChanMacroInt(int chan) {
return NULL;
}
DivDispatchOscBuffer* DivDispatch::getOscBuffer(int chan) {
return NULL;
}
unsigned char* DivDispatch::getRegisterPool() {
return NULL;
}

View File

@ -91,12 +91,15 @@ const char* DivPlatformAmiga::getEffectName(unsigned char effect) {
}
void DivPlatformAmiga::acquire(short* bufL, short* bufR, size_t start, size_t len) {
static int outL, outR;
static int outL, outR, output;
for (size_t h=start; h<start+len; h++) {
outL=0;
outR=0;
for (int i=0; i<4; i++) {
if (!chan[i].active) continue;
if (!chan[i].active) {
oscBuf[i]->data[oscBuf[i]->needle++]=0;
continue;
}
if (chan[i].useWave || (chan[i].sample>=0 && chan[i].sample<parent->song.sampleLen)) {
chan[i].audSub-=AMIGA_DIVIDER;
if (chan[i].audSub<0) {
@ -139,13 +142,17 @@ void DivPlatformAmiga::acquire(short* bufL, short* bufR, size_t start, size_t le
}
}
if (!isMuted[i]) {
output=chan[i].audDat*chan[i].outVol;
if (i==0 || i==3) {
outL+=((chan[i].audDat*chan[i].outVol)*sep1)>>7;
outR+=((chan[i].audDat*chan[i].outVol)*sep2)>>7;
outL+=(output*sep1)>>7;
outR+=(output*sep2)>>7;
} else {
outL+=((chan[i].audDat*chan[i].outVol)*sep2)>>7;
outR+=((chan[i].audDat*chan[i].outVol)*sep1)>>7;
outL+=(output*sep2)>>7;
outR+=(output*sep1)>>7;
}
oscBuf[i]->data[oscBuf[i]->needle++]=output<<2;
} else {
oscBuf[i]->data[oscBuf[i]->needle++]=0;
}
}
filter[0][0]+=(filtConst*(outL-filter[0][0]))>>12;
@ -419,6 +426,10 @@ void* DivPlatformAmiga::getChanState(int ch) {
return &chan[ch];
}
DivDispatchOscBuffer* DivPlatformAmiga::getOscBuffer(int ch) {
return oscBuf[ch];
}
void DivPlatformAmiga::reset() {
for (int i=0; i<4; i++) {
chan[i]=DivPlatformAmiga::Channel();
@ -469,6 +480,9 @@ void DivPlatformAmiga::setFlags(unsigned int flags) {
chipClock=COLOR_NTSC;
}
rate=chipClock/AMIGA_DIVIDER;
for (int i=0; i<4; i++) {
oscBuf[i]->rate=rate;
}
sep1=((flags>>8)&127)+127;
sep2=127-((flags>>8)&127);
amigaModel=flags&2;
@ -487,6 +501,7 @@ int DivPlatformAmiga::init(DivEngine* p, int channels, int sugRate, unsigned int
dumpWrites=false;
skipRegisterWrites=false;
for (int i=0; i<4; i++) {
oscBuf[i]=new DivDispatchOscBuffer;
isMuted[i]=false;
}
setFlags(flags);
@ -495,4 +510,7 @@ int DivPlatformAmiga::init(DivEngine* p, int channels, int sugRate, unsigned int
}
void DivPlatformAmiga::quit() {
for (int i=0; i<4; i++) {
delete oscBuf[i];
}
}

View File

@ -74,6 +74,7 @@ class DivPlatformAmiga: public DivDispatch {
outVol(64) {}
};
Channel chan[4];
DivDispatchOscBuffer* oscBuf[4];
bool isMuted[4];
bool bypassLimits;
bool amigaModel;
@ -91,6 +92,7 @@ class DivPlatformAmiga: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivDispatchOscBuffer* getOscBuffer(int chan);
void reset();
void forceIns();
void tick(bool sysTick=true);

View File

@ -543,8 +543,8 @@ int DivPlatformArcade::dispatch(DivCommand c) {
chan[c.chan].ins=c.value;
break;
case DIV_CMD_PANNING: {
chan[c.chan].chVolL=((c.value>>4)>0);
chan[c.chan].chVolR=((c.value&15)>0);
chan[c.chan].chVolL=(c.value>0);
chan[c.chan].chVolR=(c.value2>0);
if (isMuted[c.chan]) {
rWrite(chanOffs[c.chan]+ADDR_LR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3));
} else {

View File

@ -146,6 +146,12 @@ void DivPlatformAY8910::acquire(short* bufL, short* bufR, size_t start, size_t l
bufR[i+start]=bufL[i+start];
}
}
for (int ch=0; ch<3; ch++) {
for (size_t i=0; i<len; i++) {
oscBuf[ch]->data[oscBuf[ch]->needle++]=ayBuf[ch][i];
}
}
}
void DivPlatformAY8910::updateOutSel(bool immediate) {
@ -500,6 +506,10 @@ void* DivPlatformAY8910::getChanState(int ch) {
return &chan[ch];
}
DivDispatchOscBuffer* DivPlatformAY8910::getOscBuffer(int ch) {
return oscBuf[ch];
}
unsigned char* DivPlatformAY8910::getRegisterPool() {
return regPool;
}
@ -615,6 +625,9 @@ void DivPlatformAY8910::setFlags(unsigned int flags) {
break;
}
rate=chipClock/8;
for (int i=0; i<3; i++) {
oscBuf[i]->rate=rate;
}
if (ay!=NULL) delete ay;
switch ((flags>>4)&3) {
@ -650,6 +663,7 @@ int DivPlatformAY8910::init(DivEngine* p, int channels, int sugRate, unsigned in
skipRegisterWrites=false;
for (int i=0; i<3; i++) {
isMuted[i]=false;
oscBuf[i]=new DivDispatchOscBuffer;
}
ay=NULL;
setFlags(flags);
@ -660,6 +674,9 @@ int DivPlatformAY8910::init(DivEngine* p, int channels, int sugRate, unsigned in
}
void DivPlatformAY8910::quit() {
for (int i=0; i<3; i++) delete[] ayBuf[i];
for (int i=0; i<3; i++) {
delete oscBuf[i];
delete[] ayBuf[i];
}
if (ay!=NULL) delete ay;
}

View File

@ -56,6 +56,7 @@ class DivPlatformAY8910: public DivDispatch {
};
std::queue<QueuedWrite> writes;
ay8910_device* ay;
DivDispatchOscBuffer* oscBuf[3];
unsigned char regPool[16];
unsigned char lastBusy;
@ -90,6 +91,7 @@ class DivPlatformAY8910: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();
void flushWrites();

View File

@ -25,12 +25,21 @@
#define CHIP_FREQBASE 2048
void DivPlatformDummy::acquire(short* bufL, short* bufR, size_t start, size_t len) {
int chanOut;
for (size_t i=start; i<start+len; i++) {
int out=0;
for (unsigned char j=0; j<chans; j++) {
if (chan[j].active) {
if (!isMuted[j]) out+=(((signed short)chan[j].pos)*chan[j].amp*chan[j].vol)>>12;
if (!isMuted[j]) {
chanOut=(((signed short)chan[j].pos)*chan[j].amp*chan[j].vol)>>12;
oscBuf[j]->data[oscBuf[j]->needle++]=chanOut;
out+=chanOut;
} else {
oscBuf[j]->data[oscBuf[j]->needle++]=0;
}
chan[j].pos+=chan[j].freq;
} else {
oscBuf[j]->data[oscBuf[j]->needle++]=0;
}
}
if (out<-32768) out=-32768;
@ -61,6 +70,10 @@ void* DivPlatformDummy::getChanState(int ch) {
return &chan[ch];
}
DivDispatchOscBuffer* DivPlatformDummy::getOscBuffer(int ch) {
return oscBuf[ch];
}
int DivPlatformDummy::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON:
@ -131,6 +144,10 @@ int DivPlatformDummy::init(DivEngine* p, int channels, int sugRate, unsigned int
skipRegisterWrites=false;
for (int i=0; i<DIV_MAX_CHANS; i++) {
isMuted[i]=false;
if (i<channels) {
oscBuf[i]=new DivDispatchOscBuffer;
oscBuf[i]->rate=65536;
}
}
rate=65536;
chipClock=65536;
@ -140,6 +157,9 @@ int DivPlatformDummy::init(DivEngine* p, int channels, int sugRate, unsigned int
}
void DivPlatformDummy::quit() {
for (int i=0; i<chans; i++) {
delete oscBuf[i];
}
}
DivPlatformDummy::~DivPlatformDummy() {

View File

@ -31,6 +31,7 @@ class DivPlatformDummy: public DivDispatch {
Channel(): freq(0), baseFreq(0), pitch(0), pos(0), active(false), freqChanged(false), vol(0), amp(64) {}
};
Channel chan[128];
DivDispatchOscBuffer* oscBuf[128];
bool isMuted[128];
unsigned char chans;
friend void putDispatchChan(void*,int,int);
@ -39,6 +40,7 @@ class DivPlatformDummy: public DivDispatch {
void muteChannel(int ch, bool mute);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivDispatchOscBuffer* getOscBuffer(int chan);
void reset();
void tick(bool sysTick=true);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);

View File

@ -642,24 +642,21 @@ int DivPlatformES5506::dispatch(DivCommand c) {
break;
case DIV_CMD_PANNING: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
// 08LR, each nibble means volume multipler for each channels
// Left volume
const unsigned int lVol=(0xff*((c.value>>4)&0xf))/0xf;
if (chan[c.chan].lVol!=lVol) {
chan[c.chan].lVol=lVol;
if (chan[c.chan].lVol!=(unsigned int)(c.value)) {
chan[c.chan].lVol=c.value;
if (!chan[c.chan].std.panL.has) {
chan[c.chan].outLVol=(ins->es5506.lVol*lVol)/0xff;
chan[c.chan].outLVol=(ins->es5506.lVol*c.value)/0xff;
if (!isMuted[c.chan]) {
chan[c.chan].volChanged.lVol=1;
}
}
}
// Right volume
const unsigned int rVol=(0xff*((c.value>>0)&0xf))/0xf;
if (chan[c.chan].rVol!=rVol) {
chan[c.chan].rVol=rVol;
if (chan[c.chan].rVol!=(unsigned int)(c.value2)) {
chan[c.chan].rVol=c.value2;
if (!chan[c.chan].std.panR.has) {
chan[c.chan].outRVol=(ins->es5506.rVol*rVol)/0xff;
chan[c.chan].outRVol=(ins->es5506.rVol*c.value2)/0xff;
if (!isMuted[c.chan]) {
chan[c.chan].volChanged.rVol=1;
}

View File

@ -371,9 +371,11 @@ int DivPlatformGB::dispatch(DivCommand c) {
break;
case DIV_CMD_PANNING: {
lastPan&=~(0x11<<c.chan);
if (c.value==0) c.value=0x11;
c.value=((c.value&15)>0)|(((c.value>>4)>0)<<4);
lastPan|=c.value<<c.chan;
int pan=0;
if (c.value>0) pan|=0x10;
if (c.value2>0) pan|=0x01;
if (pan==0) pan=0x11;
lastPan|=pan<<c.chan;
rWrite(0x25,procMute());
break;
}

View File

@ -635,10 +635,10 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
chan[c.chan].ins=c.value;
break;
case DIV_CMD_PANNING: {
if (c.value==0) {
if (c.value==0 && c.value2==0) {
chan[c.chan].pan=3;
} else {
chan[c.chan].pan=((c.value&15)>0)|(((c.value>>4)>0)<<1);
chan[c.chan].pan=(c.value2>0)|((c.value>0)<<1);
}
rWrite(chanOffs[c.chan]+ADDR_LRAF,(isMuted[c.chan]?0:(chan[c.chan].pan<<6))|(chan[c.chan].state.fms&7)|((chan[c.chan].state.ams&3)<<4));
break;

View File

@ -107,10 +107,10 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
opChan[ch].ins=c.value;
break;
case DIV_CMD_PANNING: {
if (c.value==0) {
if (c.value==0 && c.value2==0) {
opChan[ch].pan=3;
} else {
opChan[ch].pan=((c.value&15)>0)|(((c.value>>4)>0)<<1);
opChan[ch].pan=(c.value2>0)|((c.value>0)<<1);
}
if (parent->song.sharedExtStat) {
for (int i=0; i<4; i++) {

View File

@ -259,7 +259,7 @@ int DivPlatformLynx::dispatch(DivCommand c) {
}
break;
case DIV_CMD_PANNING:
chan[c.chan].pan=c.value;
chan[c.chan].pan=(c.value&0xf0)|(c.value2>>4);
WRITE_ATTEN(c.chan,chan[c.chan].pan);
break;
case DIV_CMD_GET_VOLUME:

View File

@ -680,10 +680,10 @@ int DivPlatformOPL::dispatch(DivCommand c) {
break;
case DIV_CMD_PANNING: {
if (oplType!=3) break;
if (c.value==0) {
if (c.value==0 && c.value2==0) {
chan[c.chan].pan=3;
} else {
chan[c.chan].pan=(((c.value&15)>0)<<1)|((c.value>>4)>0);
chan[c.chan].pan=(c.value2>0)|((c.value>0)<<1);
}
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
if (isMuted[c.chan]) {

View File

@ -94,7 +94,10 @@ void DivPlatformOPLL::acquire_nuked(short* bufL, short* bufR, size_t start, size
OPLL_Clock(&fm,o);
unsigned char nextOut=cycleMapOPLL[fm.cycles];
if ((nextOut>=6 && properDrums) || !isMuted[nextOut]) {
oscBuf[nextOut]->data[oscBuf[nextOut]->needle++]=(o[0]+o[1])<<6;
os+=(o[0]+o[1]);
} else {
oscBuf[nextOut]->data[oscBuf[nextOut]->needle++]=0;
}
}
os*=50;
@ -731,6 +734,10 @@ void* DivPlatformOPLL::getChanState(int ch) {
return &chan[ch];
}
DivDispatchOscBuffer* DivPlatformOPLL::getOscBuffer(int ch) {
return oscBuf[ch];
}
unsigned char* DivPlatformOPLL::getRegisterPool() {
return regPool;
}
@ -842,6 +849,9 @@ void DivPlatformOPLL::setFlags(unsigned int flags) {
}
rate=chipClock/36;
patchSet=flags>>4;
for (int i=0; i<11; i++) {
oscBuf[i]->rate=rate/2;
}
}
int DivPlatformOPLL::init(DivEngine* p, int channels, int sugRate, unsigned int flags) {
@ -851,14 +861,18 @@ int DivPlatformOPLL::init(DivEngine* p, int channels, int sugRate, unsigned int
patchSet=0;
for (int i=0; i<11; i++) {
isMuted[i]=false;
oscBuf[i]=new DivDispatchOscBuffer;
}
setFlags(flags);
reset();
return 10;
return 11;
}
void DivPlatformOPLL::quit() {
for (int i=0; i<11; i++) {
delete oscBuf[i];
}
}
DivPlatformOPLL::~DivPlatformOPLL() {

View File

@ -63,6 +63,7 @@ class DivPlatformOPLL: public DivDispatch {
};
Channel chan[11];
bool isMuted[11];
DivDispatchOscBuffer* oscBuf[11];
struct QueuedWrite {
unsigned short addr;
unsigned char val;
@ -100,6 +101,7 @@ class DivPlatformOPLL: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();
void reset();

View File

@ -424,7 +424,7 @@ int DivPlatformPCE::dispatch(DivCommand c) {
}
break;
case DIV_CMD_PANNING: {
chan[c.chan].pan=c.value;
chan[c.chan].pan=(c.value&0xf0)|(c.value2>>4);
chWrite(c.chan,0x05,isMuted[c.chan]?0:chan[c.chan].pan);
break;
}

View File

@ -441,7 +441,7 @@ int DivPlatformQSound::dispatch(DivCommand c) {
return chan[c.chan].outVol;
break;
case DIV_CMD_PANNING:
chan[c.chan].panning=parent->convertPanSplitToLinear(c.value,4,32);
chan[c.chan].panning=parent->convertPanSplitToLinearLR(c.value,c.value2,32);
immWrite(Q1_PAN+c.chan,chan[c.chan].panning+0x110+(chan[c.chan].surround?0:0x30));
break;
case DIV_CMD_QSOUND_ECHO_LEVEL:

View File

@ -331,7 +331,7 @@ int DivPlatformSAA1099::dispatch(DivCommand c) {
break;
}
case DIV_CMD_PANNING:
chan[c.chan].pan=c.value;
chan[c.chan].pan=(c.value&0xf0)|(c.value2>>4);
if (isMuted[c.chan]) {
rWrite(c.chan,0);
} else {

View File

@ -46,9 +46,11 @@ void DivPlatformSegaPCM::acquire(short* bufL, short* bufR, size_t start, size_t
DivSample* s=parent->getSample(chan[i].pcm.sample);
if (s->samples<=0) {
chan[i].pcm.sample=-1;
oscBuf[i]->data[oscBuf[i]->needle++]=0;
continue;
}
if (!isMuted[i]) {
oscBuf[i]->data[oscBuf[i]->needle++]=s->data8[chan[i].pcm.pos>>8]*(chan[i].chVolL+chan[i].chVolR)>>1;
pcmL+=(s->data8[chan[i].pcm.pos>>8]*chan[i].chVolL);
pcmR+=(s->data8[chan[i].pcm.pos>>8]*chan[i].chVolR);
}
@ -60,6 +62,8 @@ void DivPlatformSegaPCM::acquire(short* bufL, short* bufR, size_t start, size_t
chan[i].pcm.sample=-1;
}
}
} else {
oscBuf[i]->data[oscBuf[i]->needle++]=0;
}
}
@ -268,8 +272,8 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
chan[c.chan].ins=c.value;
break;
case DIV_CMD_PANNING: {
chan[c.chan].chVolL=(c.value>>4)|(((c.value>>4)>>1)<<4);
chan[c.chan].chVolR=(c.value&15)|(((c.value&15)>>1)<<4);
chan[c.chan].chVolL=c.value>>1;
chan[c.chan].chVolR=c.value2>>1;
if (dumpWrites) {
addWrite(0x10002+(c.chan<<3),chan[c.chan].chVolL);
addWrite(0x10003+(c.chan<<3),chan[c.chan].chVolR);
@ -359,6 +363,10 @@ void* DivPlatformSegaPCM::getChanState(int ch) {
return &chan[ch];
}
DivDispatchOscBuffer* DivPlatformSegaPCM::getOscBuffer(int ch) {
return oscBuf[ch];
}
unsigned char* DivPlatformSegaPCM::getRegisterPool() {
return regPool;
}
@ -408,6 +416,9 @@ void DivPlatformSegaPCM::reset() {
void DivPlatformSegaPCM::setFlags(unsigned int flags) {
chipClock=8000000.0;
rate=31250;
for (int i=0; i<16; i++) {
oscBuf[i]->rate=rate;
}
}
bool DivPlatformSegaPCM::isStereo() {
@ -420,6 +431,7 @@ int DivPlatformSegaPCM::init(DivEngine* p, int channels, int sugRate, unsigned i
skipRegisterWrites=false;
for (int i=0; i<16; i++) {
isMuted[i]=false;
oscBuf[i]=new DivDispatchOscBuffer;
}
setFlags(flags);
reset();
@ -428,6 +440,9 @@ int DivPlatformSegaPCM::init(DivEngine* p, int channels, int sugRate, unsigned i
}
void DivPlatformSegaPCM::quit() {
for (int i=0; i<16; i++) {
delete oscBuf[i];
}
}
DivPlatformSegaPCM::~DivPlatformSegaPCM() {

View File

@ -49,6 +49,7 @@ class DivPlatformSegaPCM: public DivDispatch {
Channel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), pitch2(0), note(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), inPorta(false), portaPause(false), furnacePCM(false), vol(0), outVol(0), chVolL(127), chVolR(127) {}
};
Channel chan[16];
DivDispatchOscBuffer* oscBuf[16];
struct QueuedWrite {
unsigned short addr;
unsigned char val;
@ -77,6 +78,7 @@ class DivPlatformSegaPCM: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();
void reset();

View File

@ -270,7 +270,7 @@ int DivPlatformSoundUnit::dispatch(DivCommand c) {
break;
}
case DIV_CMD_PANNING: {
chan[c.chan].pan=parent->convertPanSplitToLinear(c.value,4,254)-127;
chan[c.chan].pan=parent->convertPanSplitToLinearLR(c.value,c.value2,254)-127;
chWrite(c.chan,0x03,chan[c.chan].pan);
break;
}

View File

@ -412,7 +412,7 @@ int DivPlatformSwan::dispatch(DivCommand c) {
}
break;
case DIV_CMD_PANNING: {
chan[c.chan].pan=c.value;
chan[c.chan].pan=(c.value&0xf0)|(c.value2>>4);
calcAndWriteOutVol(c.chan,chan[c.chan].std.vol.will?chan[c.chan].std.vol.val:15);
break;
}

View File

@ -525,8 +525,8 @@ int DivPlatformTX81Z::dispatch(DivCommand c) {
chan[c.chan].ins=c.value;
break;
case DIV_CMD_PANNING: {
chan[c.chan].chVolL=((c.value>>4)>0);
chan[c.chan].chVolR=((c.value&15)>0);
chan[c.chan].chVolL=(c.value>0);
chan[c.chan].chVolR=(c.value2>0);
chan[c.chan].freqChanged=true;
/*
if (isMuted[c.chan]) {

View File

@ -345,8 +345,8 @@ int DivPlatformVERA::dispatch(DivCommand c) {
break;
case DIV_CMD_PANNING: {
tmp=0;
tmp|=(c.value&0x10)?1:0;
tmp|=(c.value&0x01)?2:0;
tmp|=(c.value>0)?1:0;
tmp|=(c.value2>0)?2:0;
chan[c.chan].pan=tmp&3;
if (c.chan<16) {
rWriteHi(c.chan,2,isMuted[c.chan]?0:chan[c.chan].pan);

View File

@ -694,8 +694,9 @@ int DivPlatformX1_010::dispatch(DivCommand c) {
break;
case DIV_CMD_PANNING: {
if (!stereo) break;
if (chan[c.chan].pan!=c.value) {
chan[c.chan].pan=c.value;
unsigned char newPan=(c.value&0xf0)|(c.value2>>4);
if (chan[c.chan].pan!=newPan) {
chan[c.chan].pan=newPan;
if (!isMuted[c.chan]) {
chan[c.chan].envChanged=true;
}

View File

@ -839,10 +839,10 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
chan[c.chan].ins=c.value;
break;
case DIV_CMD_PANNING: {
if (c.value==0) {
if (c.value==0 && c.value2==0) {
chan[c.chan].pan=3;
} else {
chan[c.chan].pan=((c.value&15)>0)|(((c.value>>4)>0)<<1);
chan[c.chan].pan=(c.value2>0)|((c.value>0)<<1);
}
if (c.chan>12) {
immWrite(0x11,isMuted[c.chan]?0:(chan[c.chan].pan<<6));

View File

@ -902,10 +902,10 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
chan[c.chan].ins=c.value;
break;
case DIV_CMD_PANNING: {
if (c.value==0) {
if (c.value==0 && c.value2==0) {
chan[c.chan].pan=3;
} else {
chan[c.chan].pan=((c.value&15)>0)|(((c.value>>4)>0)<<1);
chan[c.chan].pan=(c.value2>0)|((c.value>0)<<1);
}
if (c.chan>14) {
immWrite(0x11,isMuted[c.chan]?0:(chan[c.chan].pan<<6));

View File

@ -99,10 +99,10 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) {
opChan[ch].ins=c.value;
break;
case DIV_CMD_PANNING: {
if (c.value==0) {
if (c.value==0 && c.value2==0) {
opChan[ch].pan=3;
} else {
opChan[ch].pan=((c.value&15)>0)|(((c.value>>4)>0)<<1);
opChan[ch].pan=(c.value2>0)|((c.value>0)<<1);
}
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
if (parent->song.sharedExtStat) {

View File

@ -99,10 +99,10 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) {
opChan[ch].ins=c.value;
break;
case DIV_CMD_PANNING: {
if (c.value==0) {
if (c.value==0 && c.value2==0) {
opChan[ch].pan=3;
} else {
opChan[ch].pan=((c.value&15)>0)|(((c.value>>4)>0)<<1);
opChan[ch].pan=(c.value2>0)|((c.value>0)<<1);
}
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
if (parent->song.sharedExtStat) {

View File

@ -1070,6 +1070,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
short lastSlide=-1;
bool calledPorta=false;
bool panChanged=false;
// effects
for (int j=0; j<song.pat[i].effectCols; j++) {
@ -1098,8 +1099,25 @@ void DivEngine::processRow(int i, bool afterDelay) {
changePos=effectVal;
}
break;
case 0x08: // panning
dispatchCmd(DivCommand(DIV_CMD_PANNING,i,effectVal));
case 0x08: // panning (split 4-bit)
chan[i].panL=(effectVal>>4)|(effectVal&0xf0);
chan[i].panR=(effectVal&15)|((effectVal&15)<<4);
panChanged=true;
break;
case 0x80: { // panning (linear)
unsigned short pan=convertPanLinearToSplit(effectVal,8,255);
chan[i].panL=pan>>8;
chan[i].panR=pan&0xff;
panChanged=true;
break;
}
case 0x81: // panning left (split 8-bit)
chan[i].panL=effectVal;
panChanged=true;
break;
case 0x82: // panning right (split 8-bit)
chan[i].panR=effectVal;
panChanged=true;
break;
case 0x01: // ramp up
if (song.ignoreDuplicateSlides && (lastSlide==0x01 || lastSlide==0x1337)) break;
@ -1354,6 +1372,10 @@ void DivEngine::processRow(int i, bool afterDelay) {
}
}
if (panChanged) {
dispatchCmd(DivCommand(DIV_CMD_PANNING,i,chan[i].panL,chan[i].panR));
}
if (insChanged && (chan[i].inPorta || calledPorta) && song.newInsTriggersInPorta) {
dispatchCmd(DivCommand(DIV_CMD_NOTE_ON,i,DIV_NOTE_NULL));
}

View File

@ -312,46 +312,8 @@ DivInstrumentType DivEngine::getPreferInsSecondType(int chan) {
}
int DivEngine::minVGMVersion(DivSystem which) {
switch (which) {
case DIV_SYSTEM_YM2612:
case DIV_SYSTEM_YM2612_EXT:
case DIV_SYSTEM_SMS:
case DIV_SYSTEM_OPLL:
case DIV_SYSTEM_OPLL_DRUMS:
case DIV_SYSTEM_VRC7:
case DIV_SYSTEM_YM2151:
return 0x150; // due to usage of data blocks
case DIV_SYSTEM_SEGAPCM:
case DIV_SYSTEM_SEGAPCM_COMPAT:
case DIV_SYSTEM_YM2610:
case DIV_SYSTEM_YM2610_EXT:
case DIV_SYSTEM_YM2610_FULL:
case DIV_SYSTEM_YM2610_FULL_EXT:
case DIV_SYSTEM_YM2610B:
case DIV_SYSTEM_YM2610B_EXT:
case DIV_SYSTEM_OPL:
case DIV_SYSTEM_OPL_DRUMS:
case DIV_SYSTEM_OPL2:
case DIV_SYSTEM_OPL2_DRUMS:
case DIV_SYSTEM_OPL3:
case DIV_SYSTEM_OPL3_DRUMS:
case DIV_SYSTEM_AY8910:
case DIV_SYSTEM_AY8930:
return 0x151;
case DIV_SYSTEM_GB:
case DIV_SYSTEM_PCE:
case DIV_SYSTEM_NES:
case DIV_SYSTEM_FDS:
case DIV_SYSTEM_QSOUND:
return 0x161;
case DIV_SYSTEM_SAA1099:
case DIV_SYSTEM_X1_010:
case DIV_SYSTEM_SWAN:
return 0x171;
default:
return 0;
}
return 0;
if (sysDefs[which]==NULL) return 0;
return sysDefs[which]->vgmVersion;
}
// define systems like:
@ -544,7 +506,6 @@ void DivEngine::registerSystems() {
{DIV_INS_PET}
);
// TODO: DIV_INS_SNES
sysDefs[DIV_SYSTEM_SNES]=new DivSysDef(
"SNES", NULL, 0x87, 0, 8, false, true, 0, false,
{"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8"},

101
src/gui/chanOsc.cpp Normal file
View File

@ -0,0 +1,101 @@
/**
* 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 "gui.h"
#include "imgui.h"
#include "imgui_internal.h"
void FurnaceGUI::drawChanOsc() {
if (nextWindow==GUI_WINDOW_CHAN_OSC) {
chanOscOpen=true;
ImGui::SetNextWindowFocus();
nextWindow=GUI_WINDOW_NOTHING;
}
if (!chanOscOpen) return;
ImGui::SetNextWindowSizeConstraints(ImVec2(64.0f*dpiScale,32.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale));
if (ImGui::Begin("Oscilloscope (per-channel)",&chanOscOpen)) {
if (ImGui::InputInt("Columns",&chanOscCols,1,1)) {
if (chanOscCols<1) chanOscCols=1;
if (chanOscCols>64) chanOscCols=64;
}
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding,ImVec2(0.0f,0.0f));
float availY=ImGui::GetContentRegionAvail().y;
if (ImGui::BeginTable("ChanOsc",chanOscCols,ImGuiTableFlags_Borders)) {
int chans=e->getTotalChannelCount();
int rows=(chans+(chanOscCols-1))/chanOscCols;
ImDrawList* dl=ImGui::GetWindowDrawList();
ImGuiWindow* window=ImGui::GetCurrentWindow();
ImVec2 waveform[512];
ImGuiStyle& style=ImGui::GetStyle();
ImU32 color=ImGui::GetColorU32(uiColors[GUI_COLOR_OSC_WAVE]);
for (int i=0; i<chans; i++) {
if (i%chanOscCols==0) ImGui::TableNextRow();
ImGui::TableNextColumn();
DivDispatchOscBuffer* buf=e->getOscBuffer(i);
if (buf==NULL) {
ImGui::Text("Not Available");
} else {
ImVec2 size=ImGui::GetContentRegionAvail();
size.y=availY/rows;
int displaySize=(buf->rate)/30;
ImVec2 minArea=window->DC.CursorPos;
ImVec2 maxArea=ImVec2(
minArea.x+size.x,
minArea.y+size.y
);
ImRect rect=ImRect(minArea,maxArea);
ImRect inRect=rect;
inRect.Min.x+=dpiScale;
inRect.Min.y+=dpiScale;
inRect.Max.x-=dpiScale;
inRect.Max.y-=dpiScale;
ImGui::ItemSize(size,style.FramePadding.y);
if (ImGui::ItemAdd(rect,ImGui::GetID("chOscDisplay"))) {
if (!e->isPlaying()) {
for (unsigned short i=0; i<512; i++) {
float x=(float)i/512.0f;
waveform[i]=ImLerp(inRect.Min,inRect.Max,ImVec2(x,0.5f));
}
} else {
unsigned short needlePos=buf->needle-displaySize;
for (unsigned short i=0; i<512; i++) {
float x=(float)i/512.0f;
float y=(float)buf->data[(unsigned short)(needlePos+(i*displaySize/512))]/65536.0f;
if (y<-0.5f) y=-0.5f;
if (y>0.5f) y=0.5f;
waveform[i]=ImLerp(inRect.Min,inRect.Max,ImVec2(x,0.5f-y));
}
}
dl->AddPolyline(waveform,512,color,ImDrawFlags_None,dpiScale);
}
}
}
ImGui::EndTable();
}
ImGui::PopStyleVar();
}
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_CHAN_OSC;
ImGui::End();
}

View File

@ -193,7 +193,9 @@ void FurnaceGUI::moveCursor(int x, int y, bool select) {
selStart=cursor;
}
selEnd=cursor;
updateScroll(cursor.y);
if (!settings.cursorMoveNoScroll) {
updateScroll(cursor.y);
}
e->setMidiBaseChan(cursor.xCoarse);
}

View File

@ -18,6 +18,7 @@
*/
#include "gui.h"
#include "../ta-log.h"
#include <fmt/printf.h>
#include <imgui.h>
@ -216,6 +217,9 @@ void FurnaceGUI::doAction(int what) {
case GUI_ACTION_WINDOW_EFFECT_LIST:
nextWindow=GUI_WINDOW_EFFECT_LIST;
break;
case GUI_ACTION_WINDOW_CHAN_OSC:
nextWindow=GUI_WINDOW_CHAN_OSC;
break;
case GUI_ACTION_COLLAPSE_WINDOW:
collapseWindow=true;
@ -294,6 +298,9 @@ void FurnaceGUI::doAction(int what) {
case GUI_WINDOW_EFFECT_LIST:
effectListOpen=false;
break;
case GUI_WINDOW_CHAN_OSC:
chanOscOpen=false;
break;
default:
break;
}
@ -714,6 +721,9 @@ void FurnaceGUI::doAction(int what) {
DivSample* sample=e->song.sample[curSample];
sample->prepareUndo(true);
int pos=(sampleSelStart==-1 || sampleSelStart==sampleSelEnd)?sample->samples:sampleSelStart;
if (pos>=(int)sample->samples) pos=sample->samples-1;
if (pos<0) pos=0;
logV("paste position: %d",pos);
e->lockEngine([this,sample,pos]() {
if (!sample->insert(pos,sampleClipboardLen)) {
@ -741,6 +751,8 @@ void FurnaceGUI::doAction(int what) {
DivSample* sample=e->song.sample[curSample];
sample->prepareUndo(true);
int pos=(sampleSelStart==-1 || sampleSelStart==sampleSelEnd)?0:sampleSelStart;
if (pos>=(int)sample->samples) pos=sample->samples-1;
if (pos<0) pos=0;
e->lockEngine([this,sample,pos]() {
if (sample->depth==8) {
@ -769,6 +781,8 @@ void FurnaceGUI::doAction(int what) {
DivSample* sample=e->song.sample[curSample];
sample->prepareUndo(true);
int pos=(sampleSelStart==-1 || sampleSelStart==sampleSelEnd)?0:sampleSelStart;
if (pos>=(int)sample->samples) pos=sample->samples-1;
if (pos<0) pos=0;
e->lockEngine([this,sample,pos]() {
if (sample->depth==8) {

View File

@ -32,27 +32,7 @@ void FurnaceGUI::drawEffectList() {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::PushFont(patFont);
if (i<0x10) {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[fxColors[i]]);
} else if (i<0x20) {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY]);
} else if (i<0x30) {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY]);
} else if (i<0x48) {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY]);
} else if (i<0x90) {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_INVALID]);
} else if (i<0xa0) {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_MISC]);
} else if (i<0xc0) {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_INVALID]);
} else if (i<0xd0) {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_SPEED]);
} else if (i<0xe0) {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_INVALID]);
} else {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[extFxColors[i-0xe0]]);
}
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[fxColors[i]]);
ImGui::Text("%c%c%c%c",name[0],name[1],name[2],name[3]);
ImGui::PopStyleColor();
ImGui::PopFont();

View File

@ -95,6 +95,10 @@ bool FurnaceGUIFileDialog::render(const ImVec2& min, const ImVec2& max) {
}
}
bool FurnaceGUIFileDialog::isOpen() {
return opened;
}
String FurnaceGUIFileDialog::getPath() {
if (sysDialog) {
if (curPath.size()>1) {

View File

@ -24,6 +24,7 @@ class FurnaceGUIFileDialog {
bool accepted();
void close();
bool render(const ImVec2& min, const ImVec2& max);
bool isOpen();
String getPath();
String getFileName();
explicit FurnaceGUIFileDialog(bool system):

View File

@ -2000,27 +2000,7 @@ void FurnaceGUI::editOptions(bool topMenu) {
} else {
const unsigned char data=latchEffect;
snprintf(id,63,"%.2x##LatchFX",data);
if (data<0x10) {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[fxColors[data]]);
} else if (data<0x20) {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY]);
} else if (data<0x30) {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY]);
} else if (data<0x48) {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY]);
} else if (data<0x90) {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_INVALID]);
} else if (data<0xa0) {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_MISC]);
} else if (data<0xc0) {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_INVALID]);
} else if (data<0xd0) {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_SPEED]);
} else if (data<0xe0) {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_INVALID]);
} else {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[extFxColors[data-0xe0]]);
}
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[fxColors[data]]);
}
if (ImGui::Selectable(id,latchTarget==3,ImGuiSelectableFlags_DontClosePopups)) {
@ -2370,7 +2350,9 @@ bool FurnaceGUI::loop() {
demandScrollX=true;
if (cursor.xCoarse==selStart.xCoarse && cursor.xFine==selStart.xFine && cursor.y==selStart.y &&
cursor.xCoarse==selEnd.xCoarse && cursor.xFine==selEnd.xFine && cursor.y==selEnd.y) {
updateScroll(cursor.y);
if (!settings.cursorMoveNoScroll) {
updateScroll(cursor.y);
}
}
}
break;
@ -2680,7 +2662,7 @@ bool FurnaceGUI::loop() {
if (ImGui::BeginMenu("configure system...")) {
for (int i=0; i<e->song.systemLen; i++) {
if (ImGui::TreeNode(fmt::sprintf("%d. %s##_SYSP%d",i+1,getSystemName(e->song.system[i]),i).c_str())) {
drawSysConf(i);
drawSysConf(i,e->song.system[i],e->song.systemFlags[i],true);
ImGui::TreePop();
}
}
@ -2775,7 +2757,8 @@ bool FurnaceGUI::loop() {
ImGui::Separator();
if (ImGui::MenuItem("play/edit controls",BIND_FOR(GUI_ACTION_WINDOW_EDIT_CONTROLS),editControlsOpen)) editControlsOpen=!editControlsOpen;
if (ImGui::MenuItem("piano/input pad",BIND_FOR(GUI_ACTION_WINDOW_PIANO),pianoOpen)) pianoOpen=!pianoOpen;
if (ImGui::MenuItem("oscilloscope",BIND_FOR(GUI_ACTION_WINDOW_OSCILLOSCOPE),oscOpen)) oscOpen=!oscOpen;
if (ImGui::MenuItem("oscilloscope (master)",BIND_FOR(GUI_ACTION_WINDOW_OSCILLOSCOPE),oscOpen)) oscOpen=!oscOpen;
if (ImGui::MenuItem("oscilloscope (per-channel)",BIND_FOR(GUI_ACTION_WINDOW_CHAN_OSC),chanOscOpen)) chanOscOpen=!chanOscOpen;
if (ImGui::MenuItem("volume meter",BIND_FOR(GUI_ACTION_WINDOW_VOL_METER),volMeterOpen)) volMeterOpen=!volMeterOpen;
if (ImGui::MenuItem("register view",BIND_FOR(GUI_ACTION_WINDOW_REGISTER_VIEW),regViewOpen)) regViewOpen=!regViewOpen;
if (ImGui::MenuItem("log viewer",BIND_FOR(GUI_ACTION_WINDOW_LOG),logOpen)) logOpen=!logOpen;
@ -2878,6 +2861,7 @@ bool FurnaceGUI::loop() {
readOsc();
drawOsc();
drawChanOsc();
drawVolMeter();
drawSettings();
drawDebug();
@ -2900,6 +2884,19 @@ bool FurnaceGUI::loop() {
#endif
}
if (fileDialog->isOpen() && settings.sysFileDialog) {
ImGui::OpenPopup("System File Dialog Pending");
}
if (ImGui::BeginPopupModal("System File Dialog Pending",NULL,ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoBackground|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoMove)) {
if (!fileDialog->isOpen()) {
ImGui::CloseCurrentPopup();
}
ImDrawList* dl=ImGui::GetForegroundDrawList();
dl->AddRectFilled(ImVec2(0.0f,0.0f),ImVec2(scrW*dpiScale,scrH*dpiScale),ImGui::ColorConvertFloat4ToU32(uiColors[GUI_COLOR_MODAL_BACKDROP]));
ImGui::EndPopup();
}
if (fileDialog->render(ImVec2(600.0f*dpiScale,400.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale))) {
bool openOpen=false;
//ImGui::GetIO().ConfigFlags&=~ImGuiConfigFlags_NavEnableKeyboard;
@ -3561,6 +3558,7 @@ bool FurnaceGUI::init() {
settingsOpen=e->getConfBool("settingsOpen",false);
mixerOpen=e->getConfBool("mixerOpen",false);
oscOpen=e->getConfBool("oscOpen",true);
chanOscOpen=e->getConfBool("chanOscOpen",false);
volMeterOpen=e->getConfBool("volMeterOpen",true);
statsOpen=e->getConfBool("statsOpen",false);
compatFlagsOpen=e->getConfBool("compatFlagsOpen",false);
@ -3736,6 +3734,7 @@ bool FurnaceGUI::finish() {
e->setConf("settingsOpen",settingsOpen);
e->setConf("mixerOpen",mixerOpen);
e->setConf("oscOpen",oscOpen);
e->setConf("chanOscOpen",chanOscOpen);
e->setConf("volMeterOpen",volMeterOpen);
e->setConf("statsOpen",statsOpen);
e->setConf("compatFlagsOpen",compatFlagsOpen);
@ -3856,6 +3855,7 @@ FurnaceGUI::FurnaceGUI():
regViewOpen(false),
logOpen(false),
effectListOpen(false),
chanOscOpen(false),
/*
editControlsDocked(false),
ordersDocked(false),
@ -3882,6 +3882,7 @@ FurnaceGUI::FurnaceGUI():
regViewDocked(false),
logDocked(false),
effectListDocked(false),
chanOscDocked(false),
*/
selecting(false),
curNibble(false),
@ -3998,6 +3999,7 @@ FurnaceGUI::FurnaceGUI():
oscTotal(0),
oscZoom(0.5f),
oscZoomSlider(false),
chanOscCols(3),
followLog(true),
pianoOctaves(7),
pianoOptions(false),

View File

@ -230,7 +230,8 @@ enum FurnaceGUIWindows {
GUI_WINDOW_CHANNELS,
GUI_WINDOW_REGISTER_VIEW,
GUI_WINDOW_LOG,
GUI_WINDOW_EFFECT_LIST
GUI_WINDOW_EFFECT_LIST,
GUI_WINDOW_CHAN_OSC
};
enum FurnaceGUIFileDialogs {
@ -330,6 +331,7 @@ enum FurnaceGUIActions {
GUI_ACTION_WINDOW_REGISTER_VIEW,
GUI_ACTION_WINDOW_LOG,
GUI_ACTION_WINDOW_EFFECT_LIST,
GUI_ACTION_WINDOW_CHAN_OSC,
GUI_ACTION_COLLAPSE_WINDOW,
GUI_ACTION_CLOSE_WINDOW,
@ -857,6 +859,7 @@ class FurnaceGUI {
String audioDevice;
String midiInDevice;
String midiOutDevice;
std::vector<int> initialSys;
Settings():
mainFontSize(18),
@ -947,13 +950,13 @@ class FurnaceGUI {
bool editControlsOpen, ordersOpen, insListOpen, songInfoOpen, patternOpen, insEditOpen;
bool waveListOpen, waveEditOpen, sampleListOpen, sampleEditOpen, aboutOpen, settingsOpen;
bool mixerOpen, debugOpen, inspectorOpen, oscOpen, volMeterOpen, statsOpen, compatFlagsOpen;
bool pianoOpen, notesOpen, channelsOpen, regViewOpen, logOpen, effectListOpen;
bool pianoOpen, notesOpen, channelsOpen, regViewOpen, logOpen, effectListOpen, chanOscOpen;
/* there ought to be a better way...
bool editControlsDocked, ordersDocked, insListDocked, songInfoDocked, patternDocked, insEditDocked;
bool waveListDocked, waveEditDocked, sampleListDocked, sampleEditDocked, aboutDocked, settingsDocked;
bool mixerDocked, debugDocked, inspectorDocked, oscDocked, volMeterDocked, statsDocked, compatFlagsDocked;
bool pianoDocked, notesDocked, channelsDocked, regViewDocked, logDocked, effectListDocked;
bool pianoDocked, notesDocked, channelsDocked, regViewDocked, logDocked, effectListDocked, chanOscDocked;
*/
SelectionPoint selStart, selEnd, cursor;
@ -1096,6 +1099,9 @@ class FurnaceGUI {
float oscZoom;
bool oscZoomSlider;
// per-channel oscilloscope
int chanOscCols;
// visualizer
float keyHit[DIV_MAX_CHANS];
int lastIns[DIV_MAX_CHANS];
@ -1114,7 +1120,7 @@ class FurnaceGUI {
void drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, const ImVec2& size);
void drawFMEnv(unsigned char tl, unsigned char ar, unsigned char dr, unsigned char d2r, unsigned char rr, unsigned char sl, unsigned char sus, unsigned char egt, unsigned char algOrGlobalSus, float maxTl, float maxArDr, const ImVec2& size, unsigned short instType);
void drawGBEnv(unsigned char vol, unsigned char len, unsigned char sLen, bool dir, const ImVec2& size);
void drawSysConf(int i);
void drawSysConf(int chan, DivSystem type, unsigned int& flags, bool modifyOnChange);
// these ones offer ctrl-wheel fine value changes.
bool CWSliderScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format=NULL, ImGuiSliderFlags flags=0);
@ -1150,6 +1156,7 @@ class FurnaceGUI {
void drawSampleEdit();
void drawMixer();
void drawOsc();
void drawChanOsc();
void drawVolMeter();
void drawStats();
void drawCompatFlags();

View File

@ -149,7 +149,7 @@ const char* loopMode[DIV_SAMPLE_LOOPMODE_MAX]={
"Pingpong"
};
const FurnaceGUIColors fxColors[16]={
const FurnaceGUIColors fxColors[256]={
GUI_COLOR_PATTERN_EFFECT_MISC, // 00
GUI_COLOR_PATTERN_EFFECT_PITCH, // 01
GUI_COLOR_PATTERN_EFFECT_PITCH, // 02
@ -166,9 +166,242 @@ const FurnaceGUIColors fxColors[16]={
GUI_COLOR_PATTERN_EFFECT_SONG, // 0D
GUI_COLOR_PATTERN_EFFECT_INVALID, // 0E
GUI_COLOR_PATTERN_EFFECT_SPEED, // 0F
};
const FurnaceGUIColors extFxColors[32]={
// 10-1F
GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,
GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,
GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,
GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,
GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,
GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,
GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,
GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,
GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,
GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,
GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,
GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,
GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,
GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,
GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,
GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,
// 20-2F
GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY,
GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY,
GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY,
GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY,
GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY,
GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY,
GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY,
GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY,
GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY,
GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY,
GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY,
GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY,
GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY,
GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY,
GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY,
GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY,
// 30-3F
GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,
GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,
GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,
GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,
GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,
GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,
GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,
GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,
GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,
GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,
GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,
GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,
GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,
GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,
GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,
GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,
// 40-4F
GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,
GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,
GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,
GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,
GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,
GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,
GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,
GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,
GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,
GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,
GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,
GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,
GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,
GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,
GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,
GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,
// 50-5F
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
// 60-6F
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
// 70-7F
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
// 80-8F
GUI_COLOR_PATTERN_EFFECT_PANNING,
GUI_COLOR_PATTERN_EFFECT_PANNING,
GUI_COLOR_PATTERN_EFFECT_PANNING,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
// 90-9F
GUI_COLOR_PATTERN_EFFECT_MISC,
GUI_COLOR_PATTERN_EFFECT_MISC,
GUI_COLOR_PATTERN_EFFECT_MISC,
GUI_COLOR_PATTERN_EFFECT_MISC,
GUI_COLOR_PATTERN_EFFECT_MISC,
GUI_COLOR_PATTERN_EFFECT_MISC,
GUI_COLOR_PATTERN_EFFECT_MISC,
GUI_COLOR_PATTERN_EFFECT_MISC,
GUI_COLOR_PATTERN_EFFECT_MISC,
GUI_COLOR_PATTERN_EFFECT_MISC,
GUI_COLOR_PATTERN_EFFECT_MISC,
GUI_COLOR_PATTERN_EFFECT_MISC,
GUI_COLOR_PATTERN_EFFECT_MISC,
GUI_COLOR_PATTERN_EFFECT_MISC,
GUI_COLOR_PATTERN_EFFECT_MISC,
GUI_COLOR_PATTERN_EFFECT_MISC,
// A0-AF
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
// B0-BF
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
// C0-CF
GUI_COLOR_PATTERN_EFFECT_SPEED,
GUI_COLOR_PATTERN_EFFECT_SPEED,
GUI_COLOR_PATTERN_EFFECT_SPEED,
GUI_COLOR_PATTERN_EFFECT_SPEED,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
// D0-DF
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID,
// E0-FF extended effects
GUI_COLOR_PATTERN_EFFECT_MISC, // E0
GUI_COLOR_PATTERN_EFFECT_PITCH, // E1
GUI_COLOR_PATTERN_EFFECT_PITCH, // E2
@ -200,7 +433,7 @@ const FurnaceGUIColors extFxColors[32]={
GUI_COLOR_PATTERN_EFFECT_INVALID, // FC
GUI_COLOR_PATTERN_EFFECT_INVALID, // FD
GUI_COLOR_PATTERN_EFFECT_INVALID, // FE
GUI_COLOR_PATTERN_EFFECT_SONG, // FF
GUI_COLOR_PATTERN_EFFECT_SONG // FF
};
#define D FurnaceGUIActionDef
@ -249,7 +482,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={
D("WINDOW_SETTINGS", "Settings", 0),
D("WINDOW_MIXER", "Mixer", 0),
D("WINDOW_DEBUG", "Debug Menu", 0),
D("WINDOW_OSCILLOSCOPE", "Oscilloscope", 0),
D("WINDOW_OSCILLOSCOPE", "Oscilloscope (master)", 0),
D("WINDOW_VOL_METER", "Volume Meter", 0),
D("WINDOW_STATS", "Statistics", 0),
D("WINDOW_COMPAT_FLAGS", "Compatibility Flags", 0),
@ -259,6 +492,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={
D("WINDOW_REGISTER_VIEW", "Register View", 0),
D("WINDOW_LOG", "Log Viewer", 0),
D("EFFECT_LIST", "Effect List", 0),
D("WINDOW_CHAN_OSC", "Oscilloscope (per-channel)", 0),
D("COLLAPSE_WINDOW", "Collapse/expand current window", 0),
D("CLOSE_WINDOW", "Close current window", FURKMOD_SHIFT|SDLK_ESCAPE),

View File

@ -48,5 +48,4 @@ extern const FurnaceGUIActionDef guiActions[];
extern const FurnaceGUIColorDef guiColors[];
extern const int altValues[24];
extern const int vgmVersions[6];
extern const FurnaceGUIColors fxColors[16];
extern const FurnaceGUIColors extFxColors[32];
extern const FurnaceGUIColors fxColors[256];

View File

@ -235,27 +235,7 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int
} else {
const unsigned char data=pat->data[i][index];
sprintf(id,"%.2X##PE%d_%d_%d",data,k,i,j);
if (data<0x10) {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[fxColors[data]]);
} else if (data<0x20) {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY]);
} else if (data<0x30) {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY]);
} else if (data<0x48) {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY]);
} else if (data<0x90) {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_INVALID]);
} else if (data<0xa0) {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_MISC]);
} else if (data<0xc0) {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_INVALID]);
} else if (data<0xd0) {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_SPEED]);
} else if (data<0xe0) {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_INVALID]);
} else {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[extFxColors[data-0xe0]]);
}
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[fxColors[data]]);
}
}
ImGui::SameLine(0.0f,0.0f);
@ -721,17 +701,14 @@ void FurnaceGUI::drawPattern() {
break;
}
case DIV_CMD_PANNING: {
if (i.value==0) {
num=0;
break;
}
float ratio=float(((i.value>>4)&15)-(i.value&15))/MAX(((i.value>>4)&15),(i.value&15));
float ratio=(float)(128-e->convertPanSplitToLinearLR(i.value,i.value2,256))/128.0f;
logV("ratio %f",ratio);
speedX=-22.0f*sin(ratio*M_PI*0.5);
speedY=-22.0f*cos(ratio*M_PI*0.5);
spread=5.0f+fabs(sin(ratio*M_PI*0.5))*7.0f;
grav=0.0f;
frict=0.96f;
if (((i.value>>4)&15)==(i.value&15)) {
if (i.value==i.value2) {
partIcon=ICON_FA_ARROWS_H;
} else if (ratio>0) {
partIcon=ICON_FA_ARROW_LEFT;

View File

@ -1341,7 +1341,26 @@ void FurnaceGUI::drawSampleEdit() {
sampleZoom=100.0/zoomPercent;
if (sampleZoom<0.01) sampleZoom=0.01;
sampleZoomAuto=false;
int bounds=((int)sample->samples-round(rectSize.x*sampleZoom));
if (bounds<0) bounds=0;
if (samplePos>bounds) samplePos=bounds;
updateSampleTex=true;
} else {
if (wheelY!=0) {
if (!sampleZoomAuto) {
double scrollAmount=MAX(fabs((double)wheelY*sampleZoom*60.0),1.0);
if (wheelY>0) {
samplePos+=scrollAmount;
} else {
samplePos-=scrollAmount;
}
if (samplePos<0) samplePos=0;
int bounds=((int)sample->samples-round(rectSize.x*sampleZoom));
if (bounds<0) bounds=0;
if (samplePos>bounds) samplePos=bounds;
updateSampleTex=true;
}
}
}
int posX=-1;

View File

@ -23,6 +23,7 @@
#include "../fileutils.h"
#include "util.h"
#include "guiConst.h"
#include "intConst.h"
#include "ImGuiFileDialog.h"
#include "IconsFontAwesome4.h"
#include "misc/cpp/imgui_stdlib.h"
@ -219,7 +220,8 @@ void FurnaceGUI::drawSettings() {
}
if (ImGui::BeginTabBar("settingsTab")) {
if (ImGui::BeginTabItem("General")) {
ImGui::Text("Workspace layout");
ImGui::Text("Workspace layout:");
ImGui::SameLine();
if (ImGui::Button("Import")) {
openFileDialog(GUI_FILE_IMPORT_LAYOUT);
}
@ -231,7 +233,108 @@ void FurnaceGUI::drawSettings() {
if (ImGui::Button("Reset")) {
showWarning("Are you sure you want to reset the workspace layout?",GUI_WARN_RESET_LAYOUT);
}
ImGui::Separator();
ImGui::Text("Initial system/chips:");
ImGui::SameLine();
if (ImGui::Button("Current systems")) {
settings.initialSys.clear();
for (int i=0; i<e->song.systemLen; i++) {
settings.initialSys.push_back(e->song.system[i]);
settings.initialSys.push_back(e->song.systemVol[i]);
settings.initialSys.push_back(e->song.systemPan[i]);
settings.initialSys.push_back(e->song.systemFlags[i]);
}
}
ImGui::SameLine();
if (ImGui::Button("Randomize")) {
settings.initialSys.clear();
int howMany=1+rand()%3;
int totalAvailSys=0;
for (totalAvailSys=0; availableSystems[totalAvailSys]; totalAvailSys++);
if (totalAvailSys>0) {
for (int i=0; i<howMany; i++) {
settings.initialSys.push_back(availableSystems[rand()%totalAvailSys]);
settings.initialSys.push_back(64);
settings.initialSys.push_back(0);
settings.initialSys.push_back(0);
}
} else {
settings.initialSys.push_back(DIV_SYSTEM_DUMMY);
settings.initialSys.push_back(64);
settings.initialSys.push_back(0);
settings.initialSys.push_back(0);
}
}
ImGui::SameLine();
if (ImGui::Button("Reset to defaults")) {
settings.initialSys.clear();
settings.initialSys.push_back(DIV_SYSTEM_YM2612);
settings.initialSys.push_back(64);
settings.initialSys.push_back(0);
settings.initialSys.push_back(0);
settings.initialSys.push_back(DIV_SYSTEM_SMS);
settings.initialSys.push_back(32);
settings.initialSys.push_back(0);
settings.initialSys.push_back(0);
}
for (size_t i=0; i<settings.initialSys.size(); i+=4) {
bool doRemove=false;
bool doInvert=settings.initialSys[i+1]&128;
signed char vol=settings.initialSys[i+1]&127;
ImGui::PushID(i);
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x-ImGui::CalcTextSize("Invert").x-ImGui::GetFrameHeightWithSpacing()*2.0-ImGui::GetStyle().ItemSpacing.x);
if (ImGui::BeginCombo("##System",getSystemName((DivSystem)settings.initialSys[i]))) {
for (int j=0; availableSystems[j]; j++) {
if (ImGui::Selectable(getSystemName((DivSystem)availableSystems[j]),settings.initialSys[i]==availableSystems[j])) {
settings.initialSys[i]=availableSystems[j];
settings.initialSys[i+3]=0;
}
}
ImGui::EndCombo();
}
ImGui::SameLine();
if (ImGui::Checkbox("Invert",&doInvert)) {
settings.initialSys[i+1]^=128;
}
ImGui::SameLine();
ImGui::BeginDisabled(settings.initialSys.size()<=4);
if (ImGui::Button(ICON_FA_MINUS "##InitSysRemove")) {
doRemove=true;
}
ImGui::EndDisabled();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x-(50.0f*dpiScale));
if (CWSliderScalar("Volume",ImGuiDataType_S8,&vol,&_ZERO,&_ONE_HUNDRED_TWENTY_SEVEN)) {
settings.initialSys[i+1]=(settings.initialSys[i+1]&128)|vol;
} rightClickable
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x-(50.0f*dpiScale));
CWSliderScalar("Panning",ImGuiDataType_S8,&settings.initialSys[i+2],&_MINUS_ONE_HUNDRED_TWENTY_SEVEN,&_ONE_HUNDRED_TWENTY_SEVEN); rightClickable
// oh please MSVC don't cry
if (ImGui::TreeNode("Configure")) {
drawSysConf(-1,(DivSystem)settings.initialSys[i],(unsigned int&)settings.initialSys[i+3],false);
ImGui::TreePop();
}
ImGui::PopID();
if (doRemove && settings.initialSys.size()>=8) {
settings.initialSys.erase(settings.initialSys.begin()+i,settings.initialSys.begin()+i+4);
i-=4;
}
}
if (ImGui::Button(ICON_FA_PLUS "##InitSysAdd")) {
settings.initialSys.push_back(DIV_SYSTEM_YM2612);
settings.initialSys.push_back(64);
settings.initialSys.push_back(0);
settings.initialSys.push_back(0);
}
ImGui::Separator();
ImGui::Text("Toggle channel solo on:");
if (ImGui::RadioButton("Right-click or double-click##soloA",settings.soloAction==0)) {
settings.soloAction=0;
@ -1755,6 +1858,19 @@ void FurnaceGUI::syncSettings() {
clampSetting(settings.moveWindowTitle,0,1);
clampSetting(settings.hiddenSystems,0,1);
settings.initialSys=e->decodeSysDesc(e->getConfString("initialSys",""));
if (settings.initialSys.size()<4) {
settings.initialSys.clear();
settings.initialSys.push_back(DIV_SYSTEM_YM2612);
settings.initialSys.push_back(64);
settings.initialSys.push_back(0);
settings.initialSys.push_back(0);
settings.initialSys.push_back(DIV_SYSTEM_SMS);
settings.initialSys.push_back(32);
settings.initialSys.push_back(0);
settings.initialSys.push_back(0);
}
// keybinds
for (int i=0; i<GUI_ACTION_MAX; i++) {
if (guiActions[i].defaultBind==-1) continue; // not a bind
@ -1849,6 +1965,7 @@ void FurnaceGUI::commitSettings() {
e->setConf("eventDelay",settings.eventDelay);
e->setConf("moveWindowTitle",settings.moveWindowTitle);
e->setConf("hiddenSystems",settings.hiddenSystems);
e->setConf("initialSys",e->encodeSysDesc(settings.initialSys));
// colors
for (int i=0; i<GUI_COLOR_MAX; i++) {

View File

@ -19,75 +19,70 @@
#include "gui.h"
void FurnaceGUI::drawSysConf(int i) {
unsigned int flags=e->song.systemFlags[i];
bool restart=settings.restartOnFlagChange;
void FurnaceGUI::drawSysConf(int chan, DivSystem type, unsigned int& flags, bool modifyOnChange) {
bool restart=settings.restartOnFlagChange && modifyOnChange;
bool sysPal=flags&1;
switch (e->song.system[i]) {
unsigned int copyOfFlags=flags;
switch (type) {
case DIV_SYSTEM_YM2612:
case DIV_SYSTEM_YM2612_EXT: {
if (ImGui::RadioButton("NTSC (7.67MHz)",(flags&3)==0)) {
e->setSysFlags(i,(flags&0x80000000)|0,restart);
updateWindowTitle();
copyOfFlags=(flags&0x80000000)|0;
}
if (ImGui::RadioButton("PAL (7.61MHz)",(flags&3)==1)) {
e->setSysFlags(i,(flags&0x80000000)|1,restart);
updateWindowTitle();
copyOfFlags=(flags&0x80000000)|1;
}
if (ImGui::RadioButton("FM Towns (8MHz)",(flags&3)==2)) {
e->setSysFlags(i,(flags&0x80000000)|2,restart);
updateWindowTitle();
copyOfFlags=(flags&0x80000000)|2;
}
if (ImGui::RadioButton("AtGames Genesis (6.13MHz)",(flags&3)==3)) {
e->setSysFlags(i,(flags&0x80000000)|3,restart);
updateWindowTitle();
copyOfFlags=(flags&0x80000000)|3;
}
bool ladder=flags&0x80000000;
if (ImGui::Checkbox("Enable DAC distortion",&ladder)) {
e->setSysFlags(i,(flags&(~0x80000000))|(ladder?0x80000000:0),restart);
updateWindowTitle();
copyOfFlags=(flags&(~0x80000000))|(ladder?0x80000000:0);
}
break;
}
case DIV_SYSTEM_SMS: {
ImGui::Text("Clock rate:");
if (ImGui::RadioButton("NTSC (3.58MHz)",(flags&3)==0)) {
e->setSysFlags(i,(flags&(~3))|0,restart);
updateWindowTitle();
copyOfFlags=(flags&(~3))|0;
}
if (ImGui::RadioButton("PAL (3.55MHz)",(flags&3)==1)) {
e->setSysFlags(i,(flags&(~3))|1,restart);
updateWindowTitle();
copyOfFlags=(flags&(~3))|1;
}
if (ImGui::RadioButton("BBC Micro (4MHz)",(flags&3)==2)) {
e->setSysFlags(i,(flags&(~3))|2,restart);
updateWindowTitle();
copyOfFlags=(flags&(~3))|2;
}
if (ImGui::RadioButton("Half NTSC (1.79MHz)",(flags&3)==3)) {
e->setSysFlags(i,(flags&(~3))|3,restart);
updateWindowTitle();
copyOfFlags=(flags&(~3))|3;
}
ImGui::Text("Chip type:");
if (ImGui::RadioButton("Sega VDP/Master System",((flags>>2)&3)==0)) {
e->setSysFlags(i,(flags&(~12))|0,restart);
updateWindowTitle();
copyOfFlags=(flags&(~12))|0;
}
if (ImGui::RadioButton("TI SN76489",((flags>>2)&3)==1)) {
e->setSysFlags(i,(flags&(~12))|4,restart);
updateWindowTitle();
copyOfFlags=(flags&(~12))|4;
}
if (ImGui::RadioButton("TI SN76489 with Atari-like short noise",((flags>>2)&3)==2)) {
e->setSysFlags(i,(flags&(~12))|8,restart);
updateWindowTitle();
copyOfFlags=(flags&(~12))|8;
}
/*if (ImGui::RadioButton("Game Gear",(flags>>2)==3)) {
e->setSysFlags(i,(flags&3)|12);
copyOfFlags=(flags&3)|12);
}*/
bool noPhaseReset=flags&16;
if (ImGui::Checkbox("Disable noise period change phase reset",&noPhaseReset)) {
e->setSysFlags(i,(flags&(~16))|(noPhaseReset<<4),restart);
updateWindowTitle();
copyOfFlags=(flags&(~16))|(noPhaseReset<<4);
}
break;
}
@ -96,54 +91,54 @@ void FurnaceGUI::drawSysConf(int i) {
case DIV_SYSTEM_VRC7: {
ImGui::Text("Clock rate:");
if (ImGui::RadioButton("NTSC (3.58MHz)",(flags&15)==0)) {
e->setSysFlags(i,(flags&(~15))|0,restart);
updateWindowTitle();
copyOfFlags=(flags&(~15))|0;
}
if (ImGui::RadioButton("PAL (3.55MHz)",(flags&15)==1)) {
e->setSysFlags(i,(flags&(~15))|1,restart);
updateWindowTitle();
copyOfFlags=(flags&(~15))|1;
}
if (ImGui::RadioButton("BBC Micro (4MHz)",(flags&15)==2)) {
e->setSysFlags(i,(flags&(~15))|2,restart);
updateWindowTitle();
copyOfFlags=(flags&(~15))|2;
}
if (ImGui::RadioButton("Half NTSC (1.79MHz)",(flags&15)==3)) {
e->setSysFlags(i,(flags&(~15))|3,restart);
updateWindowTitle();
copyOfFlags=(flags&(~15))|3;
}
if (e->song.system[i]!=DIV_SYSTEM_VRC7) {
if (type!=DIV_SYSTEM_VRC7) {
ImGui::Text("Patch set:");
if (ImGui::RadioButton("Yamaha YM2413",((flags>>4)&15)==0)) {
e->setSysFlags(i,(flags&(~0xf0))|0,restart);
updateWindowTitle();
copyOfFlags=(flags&(~0xf0))|0;
}
if (ImGui::RadioButton("Yamaha YMF281",((flags>>4)&15)==1)) {
e->setSysFlags(i,(flags&(~0xf0))|0x10,restart);
updateWindowTitle();
copyOfFlags=(flags&(~0xf0))|0x10;
}
if (ImGui::RadioButton("Yamaha YM2423",((flags>>4)&15)==2)) {
e->setSysFlags(i,(flags&(~0xf0))|0x20,restart);
updateWindowTitle();
copyOfFlags=(flags&(~0xf0))|0x20;
}
if (ImGui::RadioButton("Konami VRC7",((flags>>4)&15)==3)) {
e->setSysFlags(i,(flags&(~0xf0))|0x30,restart);
updateWindowTitle();
copyOfFlags=(flags&(~0xf0))|0x30;
}
}
break;
}
case DIV_SYSTEM_YM2151:
if (ImGui::RadioButton("NTSC/X16 (3.58MHz)",flags==0)) {
e->setSysFlags(i,0,restart);
updateWindowTitle();
copyOfFlags=0;
}
if (ImGui::RadioButton("PAL (3.55MHz)",flags==1)) {
e->setSysFlags(i,1,restart);
updateWindowTitle();
copyOfFlags=1;
}
if (ImGui::RadioButton("X1/X68000 (4MHz)",flags==2)) {
e->setSysFlags(i,2,restart);
updateWindowTitle();
copyOfFlags=2;
}
break;
case DIV_SYSTEM_NES:
@ -151,120 +146,117 @@ void FurnaceGUI::drawSysConf(int i) {
case DIV_SYSTEM_FDS:
case DIV_SYSTEM_MMC5:
if (ImGui::RadioButton("NTSC (1.79MHz)",flags==0)) {
e->setSysFlags(i,0,restart);
updateWindowTitle();
copyOfFlags=0;
}
if (ImGui::RadioButton("PAL (1.67MHz)",flags==1)) {
e->setSysFlags(i,1,restart);
updateWindowTitle();
copyOfFlags=1;
}
if (ImGui::RadioButton("Dendy (1.77MHz)",flags==2)) {
e->setSysFlags(i,2,restart);
updateWindowTitle();
copyOfFlags=2;
}
break;
case DIV_SYSTEM_C64_8580:
case DIV_SYSTEM_C64_6581:
if (ImGui::RadioButton("NTSC (1.02MHz)",flags==0)) {
e->setSysFlags(i,0,restart);
updateWindowTitle();
copyOfFlags=0;
}
if (ImGui::RadioButton("PAL (0.99MHz)",flags==1)) {
e->setSysFlags(i,1,restart);
updateWindowTitle();
copyOfFlags=1;
}
if (ImGui::RadioButton("SSI 2001 (0.89MHz)",flags==2)) {
e->setSysFlags(i,2,restart);
updateWindowTitle();
copyOfFlags=2;
}
break;
case DIV_SYSTEM_AY8910:
case DIV_SYSTEM_AY8930: {
ImGui::Text("Clock rate:");
if (ImGui::RadioButton("1.79MHz (ZX Spectrum NTSC/MSX)",(flags&15)==0)) {
e->setSysFlags(i,(flags&(~15))|0,restart);
updateWindowTitle();
copyOfFlags=(flags&(~15))|0;
}
if (ImGui::RadioButton("1.77MHz (ZX Spectrum)",(flags&15)==1)) {
e->setSysFlags(i,(flags&(~15))|1,restart);
updateWindowTitle();
copyOfFlags=(flags&(~15))|1;
}
if (ImGui::RadioButton("1.75MHz (ZX Spectrum)",(flags&15)==2)) {
e->setSysFlags(i,(flags&(~15))|2,restart);
updateWindowTitle();
copyOfFlags=(flags&(~15))|2;
}
if (ImGui::RadioButton("2MHz (Atari ST/Sharp X1)",(flags&15)==3)) {
e->setSysFlags(i,(flags&(~15))|3,restart);
updateWindowTitle();
copyOfFlags=(flags&(~15))|3;
}
if (ImGui::RadioButton("1.5MHz (Vectrex)",(flags&15)==4)) {
e->setSysFlags(i,(flags&(~15))|4,restart);
updateWindowTitle();
copyOfFlags=(flags&(~15))|4;
}
if (ImGui::RadioButton("1MHz (Amstrad CPC)",(flags&15)==5)) {
e->setSysFlags(i,(flags&(~15))|5,restart);
updateWindowTitle();
copyOfFlags=(flags&(~15))|5;
}
if (ImGui::RadioButton("0.89MHz (Sunsoft 5B)",(flags&15)==6)) {
e->setSysFlags(i,(flags&(~15))|6,restart);
updateWindowTitle();
copyOfFlags=(flags&(~15))|6;
}
if (ImGui::RadioButton("1.67MHz (?)",(flags&15)==7)) {
e->setSysFlags(i,(flags&(~15))|7,restart);
updateWindowTitle();
copyOfFlags=(flags&(~15))|7;
}
if (ImGui::RadioButton("0.83MHz (Sunsoft 5B on PAL)",(flags&15)==8)) {
e->setSysFlags(i,(flags&(~15))|8,restart);
updateWindowTitle();
copyOfFlags=(flags&(~15))|8;
}
if (ImGui::RadioButton("1.10MHz (Gamate/VIC-20 PAL)",(flags&15)==9)) {
e->setSysFlags(i,(flags&(~15))|9,restart);
updateWindowTitle();
copyOfFlags=(flags&(~15))|9;
}
if (ImGui::RadioButton("2^21Hz (Game Boy)",(flags&15)==10)) {
e->setSysFlags(i,(flags&(~15))|10,restart);
updateWindowTitle();
copyOfFlags=(flags&(~15))|10;
}
if (e->song.system[i]==DIV_SYSTEM_AY8910) {
if (type==DIV_SYSTEM_AY8910) {
ImGui::Text("Chip type:");
if (ImGui::RadioButton("AY-3-8910",(flags&0x30)==0)) {
e->setSysFlags(i,(flags&(~0x30))|0,restart);
updateWindowTitle();
copyOfFlags=(flags&(~0x30))|0;
}
if (ImGui::RadioButton("YM2149(F)",(flags&0x30)==16)) {
e->setSysFlags(i,(flags&(~0x30))|16,restart);
updateWindowTitle();
copyOfFlags=(flags&(~0x30))|16;
}
if (ImGui::RadioButton("Sunsoft 5B",(flags&0x30)==32)) {
e->setSysFlags(i,(flags&(~0x30))|32,restart);
updateWindowTitle();
copyOfFlags=(flags&(~0x30))|32;
}
if (ImGui::RadioButton("AY-3-8914",(flags&0x30)==48)) {
e->setSysFlags(i,(flags&(~0x30))|48,restart);
updateWindowTitle();
copyOfFlags=(flags&(~0x30))|48;
}
}
bool stereo=flags&0x40;
ImGui::BeginDisabled((flags&0x30)==32);
if (ImGui::Checkbox("Stereo##_AY_STEREO",&stereo)) {
e->setSysFlags(i,(flags&(~0x40))|(stereo?0x40:0),restart);
updateWindowTitle();
copyOfFlags=(flags&(~0x40))|(stereo?0x40:0);
}
ImGui::EndDisabled();
break;
}
case DIV_SYSTEM_SAA1099:
if (ImGui::RadioButton("SAM Coupé (8MHz)",flags==0)) {
e->setSysFlags(i,0,restart);
updateWindowTitle();
copyOfFlags=0;
}
if (ImGui::RadioButton("NTSC (7.15MHz)",flags==1)) {
e->setSysFlags(i,1,restart);
updateWindowTitle();
copyOfFlags=1;
}
if (ImGui::RadioButton("PAL (7.09MHz)",flags==2)) {
e->setSysFlags(i,2,restart);
updateWindowTitle();
copyOfFlags=2;
}
break;
case DIV_SYSTEM_AMIGA: {
@ -273,44 +265,37 @@ void FurnaceGUI::drawSysConf(int i) {
if (CWSliderInt("##StereoSep",&stereoSep,0,127)) {
if (stereoSep<0) stereoSep=0;
if (stereoSep>127) stereoSep=127;
e->setSysFlags(i,(flags&(~0x7f00))|((stereoSep&127)<<8),restart);
updateWindowTitle();
copyOfFlags=(flags&(~0x7f00))|((stereoSep&127)<<8);
} rightClickable
if (ImGui::RadioButton("Amiga 500 (OCS)",(flags&2)==0)) {
e->setSysFlags(i,flags&(~2),restart);
copyOfFlags=flags&(~2);
}
if (ImGui::RadioButton("Amiga 1200 (AGA)",(flags&2)==2)) {
e->setSysFlags(i,(flags&(~2))|2,restart);
copyOfFlags=(flags&(~2))|2;
}
sysPal=flags&1;
if (ImGui::Checkbox("PAL",&sysPal)) {
e->setSysFlags(i,(flags&(~1))|(unsigned int)sysPal,restart);
updateWindowTitle();
copyOfFlags=(flags&(~1))|(unsigned int)sysPal;
}
bool bypassLimits=flags&4;
if (ImGui::Checkbox("Bypass frequency limits",&bypassLimits)) {
e->setSysFlags(i,(flags&(~4))|(bypassLimits<<2),restart);
updateWindowTitle();
copyOfFlags=(flags&(~4))|(bypassLimits<<2);
}
break;
}
case DIV_SYSTEM_PCSPKR: {
ImGui::Text("Speaker type:");
if (ImGui::RadioButton("Unfiltered",(flags&3)==0)) {
e->setSysFlags(i,(flags&(~3))|0,restart);
updateWindowTitle();
copyOfFlags=(flags&(~3))|0;
}
if (ImGui::RadioButton("Cone",(flags&3)==1)) {
e->setSysFlags(i,(flags&(~3))|1,restart);
updateWindowTitle();
copyOfFlags=(flags&(~3))|1;
}
if (ImGui::RadioButton("Piezo",(flags&3)==2)) {
e->setSysFlags(i,(flags&(~3))|2,restart);
updateWindowTitle();
copyOfFlags=(flags&(~3))|2;
}
if (ImGui::RadioButton("Use system beeper (Linux only!)",(flags&3)==3)) {
e->setSysFlags(i,(flags&(~3))|3,restart);
updateWindowTitle();
copyOfFlags=(flags&(~3))|3;
}
break;
}
@ -320,62 +305,53 @@ void FurnaceGUI::drawSysConf(int i) {
if (CWSliderInt("##EchoBufSize",&echoBufSize,0,2725)) {
if (echoBufSize<0) echoBufSize=0;
if (echoBufSize>2725) echoBufSize=2725;
e->setSysFlags(i,(flags & ~4095) | ((2725 - echoBufSize) & 4095),restart);
updateWindowTitle();
copyOfFlags=(flags & ~4095) | ((2725 - echoBufSize) & 4095);
} rightClickable
ImGui::Text("Echo feedback:");
int echoFeedback=(flags>>12)&255;
if (CWSliderInt("##EchoFeedback",&echoFeedback,0,255)) {
if (echoFeedback<0) echoFeedback=0;
if (echoFeedback>255) echoFeedback=255;
e->setSysFlags(i,(flags & ~(255 << 12)) | ((echoFeedback & 255) << 12),restart);
updateWindowTitle();
copyOfFlags=(flags & ~(255 << 12)) | ((echoFeedback & 255) << 12);
} rightClickable
break;
}
case DIV_SYSTEM_X1_010: {
ImGui::Text("Clock rate:");
if (ImGui::RadioButton("16MHz (Seta 1)",(flags&15)==0)) {
e->setSysFlags(i,(flags&(~15))|0,restart);
updateWindowTitle();
copyOfFlags=(flags&(~15))|0;
}
if (ImGui::RadioButton("16.67MHz (Seta 2)",(flags&15)==1)) {
e->setSysFlags(i,(flags&(~15))|1,restart);
updateWindowTitle();
copyOfFlags=(flags&(~15))|1;
}
bool x1_010Stereo=flags&16;
if (ImGui::Checkbox("Stereo",&x1_010Stereo)) {
e->setSysFlags(i,(flags&(~16))|(x1_010Stereo<<4),restart);
updateWindowTitle();
copyOfFlags=(flags&(~16))|(x1_010Stereo<<4);
}
break;
}
case DIV_SYSTEM_N163: {
ImGui::Text("Clock rate:");
if (ImGui::RadioButton("NTSC (1.79MHz)",(flags&15)==0)) {
e->setSysFlags(i,(flags&(~15))|0,restart);
updateWindowTitle();
copyOfFlags=(flags&(~15))|0;
}
if (ImGui::RadioButton("PAL (1.67MHz)",(flags&15)==1)) {
e->setSysFlags(i,(flags&(~15))|1,restart);
updateWindowTitle();
copyOfFlags=(flags&(~15))|1;
}
if (ImGui::RadioButton("Dendy (1.77MHz)",(flags&15)==2)) {
e->setSysFlags(i,(flags&(~15))|2,restart);
updateWindowTitle();
copyOfFlags=(flags&(~15))|2;
}
ImGui::Text("Initial channel limit:");
int initialChannelLimit=((flags>>4)&7)+1;
if (CWSliderInt("##N163_InitialChannelLimit",&initialChannelLimit,1,8)) {
if (initialChannelLimit<1) initialChannelLimit=1;
if (initialChannelLimit>8) initialChannelLimit=8;
e->setSysFlags(i,(flags & ~(7 << 4)) | (((initialChannelLimit-1) & 7) << 4),restart);
updateWindowTitle();
copyOfFlags=(flags & ~(7 << 4)) | (((initialChannelLimit-1) & 7) << 4);
} rightClickable
bool n163Multiplex=flags&128;
if (ImGui::Checkbox("Disable hissing",&n163Multiplex)) {
e->setSysFlags(i,(flags&(~128))|(n163Multiplex<<7),restart);
updateWindowTitle();
copyOfFlags=(flags&(~128))|(n163Multiplex<<7);
}
break;
}
@ -406,9 +382,17 @@ void FurnaceGUI::drawSysConf(int i) {
break;
default:
if (ImGui::Checkbox("PAL",&sysPal)) {
e->setSysFlags(i,sysPal,restart);
updateWindowTitle();
copyOfFlags=sysPal;
}
break;
}
if (copyOfFlags!=flags) {
if (chan>=0) {
e->setSysFlags(chan,copyOfFlags,restart);
updateWindowTitle();
} else {
flags=copyOfFlags;
}
}
}