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/about.cpp
src/gui/channels.cpp src/gui/channels.cpp
src/gui/chanOsc.cpp
src/gui/compatFlags.cpp src/gui/compatFlags.cpp
src/gui/cursor.cpp src/gui/cursor.cpp
src/gui/dataList.cpp src/gui/dataList.cpp

View File

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

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. - maximum tremolo depth is -60 volume steps.
- `08xy`: set panning. `x` is the left channel and `y` is the right one. - `08xy`: set panning. `x` is the left channel and `y` is the right one.
- not all systems support this effect. - 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. - `09xx`: set speed 1.
- `0Axy`: volume slide. - `0Axy`: volume slide.
- if `x` is 0 then this is a slide down. - if `x` is 0 then this is a slide down.

View File

@ -21,6 +21,7 @@
#define _DISPATCH_H #define _DISPATCH_H
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include <vector> #include <vector>
#define ONE_SEMITONE 2200 #define ONE_SEMITONE 2200
@ -49,7 +50,7 @@ enum DivDispatchCmds {
DIV_CMD_GET_VOLMAX, // () -> volMax DIV_CMD_GET_VOLMAX, // () -> volMax
DIV_CMD_NOTE_PORTA, // (target, speed) -> 2 if target reached DIV_CMD_NOTE_PORTA, // (target, speed) -> 2 if target reached
DIV_CMD_PITCH, // (pitch) DIV_CMD_PITCH, // (pitch)
DIV_CMD_PANNING, // (pan) DIV_CMD_PANNING, // (left, right)
DIV_CMD_LEGATO, // (note) DIV_CMD_LEGATO, // (note)
DIV_CMD_PRE_PORTA, // (inPorta, isPortaOrSlide) DIV_CMD_PRE_PORTA, // (inPorta, isPortaOrSlide)
DIV_CMD_PRE_NOTE, // used in C64 (note) DIV_CMD_PRE_NOTE, // used in C64 (note)
@ -214,6 +215,18 @@ struct DivRegWrite {
addr(a), val(v) {} 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 DivEngine;
class DivMacroInt; class DivMacroInt;
@ -277,6 +290,12 @@ class DivDispatch {
* @return a pointer, or NULL. * @return a pointer, or NULL.
*/ */
virtual DivMacroInt* getChanMacroInt(int chan); 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. * 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"; return "0Dxx: Jump to next pattern";
case 0x0f: case 0x0f:
return "0Fxx: Set speed 2"; 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: case 0xc0: case 0xc1: case 0xc2: case 0xc3:
return "Cxxx: Set tick rate (hz)"; return "Cxxx: Set tick rate (hz)";
case 0xe0: case 0xe0:
@ -645,6 +651,101 @@ void DivEngine::renderSamples() {
es5506MemLen=memPos+256; 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) { void DivEngine::createNew(const int* description) {
quitDispatch(); quitDispatch();
BUSY_BEGIN; BUSY_BEGIN;
@ -652,18 +753,7 @@ void DivEngine::createNew(const int* description) {
song.unload(); song.unload();
song=DivSong(); song=DivSong();
if (description!=NULL) { if (description!=NULL) {
if (description[0]!=0) { initSongWithDesc(description);
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;
}
} }
recalcChans(); recalcChans();
renderSamples(); renderSamples();
@ -930,6 +1020,16 @@ unsigned char* DivEngine::getRegisterPool(int sys, int& size, int& depth) {
return disCont[sys].dispatch->getRegisterPool(); 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) { void DivEngine::enableCommandStream(bool enable) {
cmdStreamEnabled=enable; cmdStreamEnabled=enable;
} }
@ -1083,6 +1183,10 @@ int DivEngine::convertPanSplitToLinear(unsigned int val, unsigned char bits, int
return pan*range; 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) { unsigned int DivEngine::convertPanLinearToSplit(int val, unsigned char bits, int range) {
if (val<0) val=0; if (val<0) val=0;
if (val>range) val=range; if (val>range) val=range;
@ -1231,6 +1335,8 @@ void DivEngine::recalcChans() {
for (int i=0; i<DIV_INS_MAX; i++) { for (int i=0; i<DIV_INS_MAX; i++) {
if (isInsTypePossible[i]) possibleInsTypes.push_back((DivInstrumentType)i); if (isInsTypePossible[i]) possibleInsTypes.push_back((DivInstrumentType)i);
} }
hasLoadedSomething=true;
} }
void DivEngine::reset() { void DivEngine::reset() {
@ -2700,6 +2806,18 @@ bool DivEngine::init() {
loadConf(); 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 // init the rest of engine
bool haveAudio=false; bool haveAudio=false;
if (!initAudioBackend()) { if (!initAudioBackend()) {

View File

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

View File

@ -33,6 +33,10 @@ DivMacroInt* DivDispatch::getChanMacroInt(int chan) {
return NULL; return NULL;
} }
DivDispatchOscBuffer* DivDispatch::getOscBuffer(int chan) {
return NULL;
}
unsigned char* DivDispatch::getRegisterPool() { unsigned char* DivDispatch::getRegisterPool() {
return NULL; 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) { 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++) { for (size_t h=start; h<start+len; h++) {
outL=0; outL=0;
outR=0; outR=0;
for (int i=0; i<4; i++) { 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)) { if (chan[i].useWave || (chan[i].sample>=0 && chan[i].sample<parent->song.sampleLen)) {
chan[i].audSub-=AMIGA_DIVIDER; chan[i].audSub-=AMIGA_DIVIDER;
if (chan[i].audSub<0) { 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]) { if (!isMuted[i]) {
output=chan[i].audDat*chan[i].outVol;
if (i==0 || i==3) { if (i==0 || i==3) {
outL+=((chan[i].audDat*chan[i].outVol)*sep1)>>7; outL+=(output*sep1)>>7;
outR+=((chan[i].audDat*chan[i].outVol)*sep2)>>7; outR+=(output*sep2)>>7;
} else { } else {
outL+=((chan[i].audDat*chan[i].outVol)*sep2)>>7; outL+=(output*sep2)>>7;
outR+=((chan[i].audDat*chan[i].outVol)*sep1)>>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; filter[0][0]+=(filtConst*(outL-filter[0][0]))>>12;
@ -419,6 +426,10 @@ void* DivPlatformAmiga::getChanState(int ch) {
return &chan[ch]; return &chan[ch];
} }
DivDispatchOscBuffer* DivPlatformAmiga::getOscBuffer(int ch) {
return oscBuf[ch];
}
void DivPlatformAmiga::reset() { void DivPlatformAmiga::reset() {
for (int i=0; i<4; i++) { for (int i=0; i<4; i++) {
chan[i]=DivPlatformAmiga::Channel(); chan[i]=DivPlatformAmiga::Channel();
@ -469,6 +480,9 @@ void DivPlatformAmiga::setFlags(unsigned int flags) {
chipClock=COLOR_NTSC; chipClock=COLOR_NTSC;
} }
rate=chipClock/AMIGA_DIVIDER; rate=chipClock/AMIGA_DIVIDER;
for (int i=0; i<4; i++) {
oscBuf[i]->rate=rate;
}
sep1=((flags>>8)&127)+127; sep1=((flags>>8)&127)+127;
sep2=127-((flags>>8)&127); sep2=127-((flags>>8)&127);
amigaModel=flags&2; amigaModel=flags&2;
@ -487,6 +501,7 @@ int DivPlatformAmiga::init(DivEngine* p, int channels, int sugRate, unsigned int
dumpWrites=false; dumpWrites=false;
skipRegisterWrites=false; skipRegisterWrites=false;
for (int i=0; i<4; i++) { for (int i=0; i<4; i++) {
oscBuf[i]=new DivDispatchOscBuffer;
isMuted[i]=false; isMuted[i]=false;
} }
setFlags(flags); setFlags(flags);
@ -495,4 +510,7 @@ int DivPlatformAmiga::init(DivEngine* p, int channels, int sugRate, unsigned int
} }
void DivPlatformAmiga::quit() { 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) {} outVol(64) {}
}; };
Channel chan[4]; Channel chan[4];
DivDispatchOscBuffer* oscBuf[4];
bool isMuted[4]; bool isMuted[4];
bool bypassLimits; bool bypassLimits;
bool amigaModel; bool amigaModel;
@ -91,6 +92,7 @@ class DivPlatformAmiga: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len); void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c); int dispatch(DivCommand c);
void* getChanState(int chan); void* getChanState(int chan);
DivDispatchOscBuffer* getOscBuffer(int chan);
void reset(); void reset();
void forceIns(); void forceIns();
void tick(bool sysTick=true); void tick(bool sysTick=true);

View File

@ -543,8 +543,8 @@ int DivPlatformArcade::dispatch(DivCommand c) {
chan[c.chan].ins=c.value; chan[c.chan].ins=c.value;
break; break;
case DIV_CMD_PANNING: { case DIV_CMD_PANNING: {
chan[c.chan].chVolL=((c.value>>4)>0); chan[c.chan].chVolL=(c.value>0);
chan[c.chan].chVolR=((c.value&15)>0); chan[c.chan].chVolR=(c.value2>0);
if (isMuted[c.chan]) { if (isMuted[c.chan]) {
rWrite(chanOffs[c.chan]+ADDR_LR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3)); rWrite(chanOffs[c.chan]+ADDR_LR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3));
} else { } 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]; 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) { void DivPlatformAY8910::updateOutSel(bool immediate) {
@ -500,6 +506,10 @@ void* DivPlatformAY8910::getChanState(int ch) {
return &chan[ch]; return &chan[ch];
} }
DivDispatchOscBuffer* DivPlatformAY8910::getOscBuffer(int ch) {
return oscBuf[ch];
}
unsigned char* DivPlatformAY8910::getRegisterPool() { unsigned char* DivPlatformAY8910::getRegisterPool() {
return regPool; return regPool;
} }
@ -615,6 +625,9 @@ void DivPlatformAY8910::setFlags(unsigned int flags) {
break; break;
} }
rate=chipClock/8; rate=chipClock/8;
for (int i=0; i<3; i++) {
oscBuf[i]->rate=rate;
}
if (ay!=NULL) delete ay; if (ay!=NULL) delete ay;
switch ((flags>>4)&3) { switch ((flags>>4)&3) {
@ -650,6 +663,7 @@ int DivPlatformAY8910::init(DivEngine* p, int channels, int sugRate, unsigned in
skipRegisterWrites=false; skipRegisterWrites=false;
for (int i=0; i<3; i++) { for (int i=0; i<3; i++) {
isMuted[i]=false; isMuted[i]=false;
oscBuf[i]=new DivDispatchOscBuffer;
} }
ay=NULL; ay=NULL;
setFlags(flags); setFlags(flags);
@ -660,6 +674,9 @@ int DivPlatformAY8910::init(DivEngine* p, int channels, int sugRate, unsigned in
} }
void DivPlatformAY8910::quit() { 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; if (ay!=NULL) delete ay;
} }

View File

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

View File

@ -25,12 +25,21 @@
#define CHIP_FREQBASE 2048 #define CHIP_FREQBASE 2048
void DivPlatformDummy::acquire(short* bufL, short* bufR, size_t start, size_t len) { void DivPlatformDummy::acquire(short* bufL, short* bufR, size_t start, size_t len) {
int chanOut;
for (size_t i=start; i<start+len; i++) { for (size_t i=start; i<start+len; i++) {
int out=0; int out=0;
for (unsigned char j=0; j<chans; j++) { for (unsigned char j=0; j<chans; j++) {
if (chan[j].active) { 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; chan[j].pos+=chan[j].freq;
} else {
oscBuf[j]->data[oscBuf[j]->needle++]=0;
} }
} }
if (out<-32768) out=-32768; if (out<-32768) out=-32768;
@ -61,6 +70,10 @@ void* DivPlatformDummy::getChanState(int ch) {
return &chan[ch]; return &chan[ch];
} }
DivDispatchOscBuffer* DivPlatformDummy::getOscBuffer(int ch) {
return oscBuf[ch];
}
int DivPlatformDummy::dispatch(DivCommand c) { int DivPlatformDummy::dispatch(DivCommand c) {
switch (c.cmd) { switch (c.cmd) {
case DIV_CMD_NOTE_ON: case DIV_CMD_NOTE_ON:
@ -131,6 +144,10 @@ int DivPlatformDummy::init(DivEngine* p, int channels, int sugRate, unsigned int
skipRegisterWrites=false; skipRegisterWrites=false;
for (int i=0; i<DIV_MAX_CHANS; i++) { for (int i=0; i<DIV_MAX_CHANS; i++) {
isMuted[i]=false; isMuted[i]=false;
if (i<channels) {
oscBuf[i]=new DivDispatchOscBuffer;
oscBuf[i]->rate=65536;
}
} }
rate=65536; rate=65536;
chipClock=65536; chipClock=65536;
@ -140,6 +157,9 @@ int DivPlatformDummy::init(DivEngine* p, int channels, int sugRate, unsigned int
} }
void DivPlatformDummy::quit() { void DivPlatformDummy::quit() {
for (int i=0; i<chans; i++) {
delete oscBuf[i];
}
} }
DivPlatformDummy::~DivPlatformDummy() { 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(): freq(0), baseFreq(0), pitch(0), pos(0), active(false), freqChanged(false), vol(0), amp(64) {}
}; };
Channel chan[128]; Channel chan[128];
DivDispatchOscBuffer* oscBuf[128];
bool isMuted[128]; bool isMuted[128];
unsigned char chans; unsigned char chans;
friend void putDispatchChan(void*,int,int); friend void putDispatchChan(void*,int,int);
@ -39,6 +40,7 @@ class DivPlatformDummy: public DivDispatch {
void muteChannel(int ch, bool mute); void muteChannel(int ch, bool mute);
int dispatch(DivCommand c); int dispatch(DivCommand c);
void* getChanState(int chan); void* getChanState(int chan);
DivDispatchOscBuffer* getOscBuffer(int chan);
void reset(); void reset();
void tick(bool sysTick=true); void tick(bool sysTick=true);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags); int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);

View File

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

View File

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

View File

@ -635,10 +635,10 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
chan[c.chan].ins=c.value; chan[c.chan].ins=c.value;
break; break;
case DIV_CMD_PANNING: { case DIV_CMD_PANNING: {
if (c.value==0) { if (c.value==0 && c.value2==0) {
chan[c.chan].pan=3; chan[c.chan].pan=3;
} else { } 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)); 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; break;

View File

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

View File

@ -259,7 +259,7 @@ int DivPlatformLynx::dispatch(DivCommand c) {
} }
break; break;
case DIV_CMD_PANNING: 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); WRITE_ATTEN(c.chan,chan[c.chan].pan);
break; break;
case DIV_CMD_GET_VOLUME: case DIV_CMD_GET_VOLUME:

View File

@ -680,10 +680,10 @@ int DivPlatformOPL::dispatch(DivCommand c) {
break; break;
case DIV_CMD_PANNING: { case DIV_CMD_PANNING: {
if (oplType!=3) break; if (oplType!=3) break;
if (c.value==0) { if (c.value==0 && c.value2==0) {
chan[c.chan].pan=3; chan[c.chan].pan=3;
} else { } 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; int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
if (isMuted[c.chan]) { 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); OPLL_Clock(&fm,o);
unsigned char nextOut=cycleMapOPLL[fm.cycles]; unsigned char nextOut=cycleMapOPLL[fm.cycles];
if ((nextOut>=6 && properDrums) || !isMuted[nextOut]) { if ((nextOut>=6 && properDrums) || !isMuted[nextOut]) {
oscBuf[nextOut]->data[oscBuf[nextOut]->needle++]=(o[0]+o[1])<<6;
os+=(o[0]+o[1]); os+=(o[0]+o[1]);
} else {
oscBuf[nextOut]->data[oscBuf[nextOut]->needle++]=0;
} }
} }
os*=50; os*=50;
@ -731,6 +734,10 @@ void* DivPlatformOPLL::getChanState(int ch) {
return &chan[ch]; return &chan[ch];
} }
DivDispatchOscBuffer* DivPlatformOPLL::getOscBuffer(int ch) {
return oscBuf[ch];
}
unsigned char* DivPlatformOPLL::getRegisterPool() { unsigned char* DivPlatformOPLL::getRegisterPool() {
return regPool; return regPool;
} }
@ -842,6 +849,9 @@ void DivPlatformOPLL::setFlags(unsigned int flags) {
} }
rate=chipClock/36; rate=chipClock/36;
patchSet=flags>>4; 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) { 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; patchSet=0;
for (int i=0; i<11; i++) { for (int i=0; i<11; i++) {
isMuted[i]=false; isMuted[i]=false;
oscBuf[i]=new DivDispatchOscBuffer;
} }
setFlags(flags); setFlags(flags);
reset(); reset();
return 10; return 11;
} }
void DivPlatformOPLL::quit() { void DivPlatformOPLL::quit() {
for (int i=0; i<11; i++) {
delete oscBuf[i];
}
} }
DivPlatformOPLL::~DivPlatformOPLL() { DivPlatformOPLL::~DivPlatformOPLL() {

View File

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

View File

@ -424,7 +424,7 @@ int DivPlatformPCE::dispatch(DivCommand c) {
} }
break; break;
case DIV_CMD_PANNING: { 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); chWrite(c.chan,0x05,isMuted[c.chan]?0:chan[c.chan].pan);
break; break;
} }

View File

@ -441,7 +441,7 @@ int DivPlatformQSound::dispatch(DivCommand c) {
return chan[c.chan].outVol; return chan[c.chan].outVol;
break; break;
case DIV_CMD_PANNING: 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)); immWrite(Q1_PAN+c.chan,chan[c.chan].panning+0x110+(chan[c.chan].surround?0:0x30));
break; break;
case DIV_CMD_QSOUND_ECHO_LEVEL: case DIV_CMD_QSOUND_ECHO_LEVEL:

View File

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

View File

@ -270,7 +270,7 @@ int DivPlatformSoundUnit::dispatch(DivCommand c) {
break; break;
} }
case DIV_CMD_PANNING: { 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); chWrite(c.chan,0x03,chan[c.chan].pan);
break; break;
} }

View File

@ -412,7 +412,7 @@ int DivPlatformSwan::dispatch(DivCommand c) {
} }
break; break;
case DIV_CMD_PANNING: { 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); calcAndWriteOutVol(c.chan,chan[c.chan].std.vol.will?chan[c.chan].std.vol.val:15);
break; break;
} }

View File

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

View File

@ -345,8 +345,8 @@ int DivPlatformVERA::dispatch(DivCommand c) {
break; break;
case DIV_CMD_PANNING: { case DIV_CMD_PANNING: {
tmp=0; tmp=0;
tmp|=(c.value&0x10)?1:0; tmp|=(c.value>0)?1:0;
tmp|=(c.value&0x01)?2:0; tmp|=(c.value2>0)?2:0;
chan[c.chan].pan=tmp&3; chan[c.chan].pan=tmp&3;
if (c.chan<16) { if (c.chan<16) {
rWriteHi(c.chan,2,isMuted[c.chan]?0:chan[c.chan].pan); 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; break;
case DIV_CMD_PANNING: { case DIV_CMD_PANNING: {
if (!stereo) break; if (!stereo) break;
if (chan[c.chan].pan!=c.value) { unsigned char newPan=(c.value&0xf0)|(c.value2>>4);
chan[c.chan].pan=c.value; if (chan[c.chan].pan!=newPan) {
chan[c.chan].pan=newPan;
if (!isMuted[c.chan]) { if (!isMuted[c.chan]) {
chan[c.chan].envChanged=true; chan[c.chan].envChanged=true;
} }

View File

@ -839,10 +839,10 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
chan[c.chan].ins=c.value; chan[c.chan].ins=c.value;
break; break;
case DIV_CMD_PANNING: { case DIV_CMD_PANNING: {
if (c.value==0) { if (c.value==0 && c.value2==0) {
chan[c.chan].pan=3; chan[c.chan].pan=3;
} else { } 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) { if (c.chan>12) {
immWrite(0x11,isMuted[c.chan]?0:(chan[c.chan].pan<<6)); 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; chan[c.chan].ins=c.value;
break; break;
case DIV_CMD_PANNING: { case DIV_CMD_PANNING: {
if (c.value==0) { if (c.value==0 && c.value2==0) {
chan[c.chan].pan=3; chan[c.chan].pan=3;
} else { } 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) { if (c.chan>14) {
immWrite(0x11,isMuted[c.chan]?0:(chan[c.chan].pan<<6)); 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; opChan[ch].ins=c.value;
break; break;
case DIV_CMD_PANNING: { case DIV_CMD_PANNING: {
if (c.value==0) { if (c.value==0 && c.value2==0) {
opChan[ch].pan=3; opChan[ch].pan=3;
} else { } 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); DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
if (parent->song.sharedExtStat) { if (parent->song.sharedExtStat) {

View File

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

View File

@ -1070,6 +1070,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
short lastSlide=-1; short lastSlide=-1;
bool calledPorta=false; bool calledPorta=false;
bool panChanged=false;
// effects // effects
for (int j=0; j<song.pat[i].effectCols; j++) { for (int j=0; j<song.pat[i].effectCols; j++) {
@ -1098,8 +1099,25 @@ void DivEngine::processRow(int i, bool afterDelay) {
changePos=effectVal; changePos=effectVal;
} }
break; break;
case 0x08: // panning case 0x08: // panning (split 4-bit)
dispatchCmd(DivCommand(DIV_CMD_PANNING,i,effectVal)); 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; break;
case 0x01: // ramp up case 0x01: // ramp up
if (song.ignoreDuplicateSlides && (lastSlide==0x01 || lastSlide==0x1337)) break; 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) { if (insChanged && (chan[i].inPorta || calledPorta) && song.newInsTriggersInPorta) {
dispatchCmd(DivCommand(DIV_CMD_NOTE_ON,i,DIV_NOTE_NULL)); 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) { int DivEngine::minVGMVersion(DivSystem which) {
switch (which) { if (sysDefs[which]==NULL) return 0;
case DIV_SYSTEM_YM2612: return sysDefs[which]->vgmVersion;
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;
} }
// define systems like: // define systems like:
@ -544,7 +506,6 @@ void DivEngine::registerSystems() {
{DIV_INS_PET} {DIV_INS_PET}
); );
// TODO: DIV_INS_SNES
sysDefs[DIV_SYSTEM_SNES]=new DivSysDef( sysDefs[DIV_SYSTEM_SNES]=new DivSysDef(
"SNES", NULL, 0x87, 0, 8, false, true, 0, false, "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"}, {"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; selStart=cursor;
} }
selEnd=cursor; selEnd=cursor;
updateScroll(cursor.y); if (!settings.cursorMoveNoScroll) {
updateScroll(cursor.y);
}
e->setMidiBaseChan(cursor.xCoarse); e->setMidiBaseChan(cursor.xCoarse);
} }

View File

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

View File

@ -32,27 +32,7 @@ void FurnaceGUI::drawEffectList() {
ImGui::TableNextRow(); ImGui::TableNextRow();
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::PushFont(patFont); ImGui::PushFont(patFont);
if (i<0x10) { ImGui::PushStyleColor(ImGuiCol_Text,uiColors[fxColors[i]]);
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::Text("%c%c%c%c",name[0],name[1],name[2],name[3]); ImGui::Text("%c%c%c%c",name[0],name[1],name[2],name[3]);
ImGui::PopStyleColor(); ImGui::PopStyleColor();
ImGui::PopFont(); 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() { String FurnaceGUIFileDialog::getPath() {
if (sysDialog) { if (sysDialog) {
if (curPath.size()>1) { if (curPath.size()>1) {

View File

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

View File

@ -2000,27 +2000,7 @@ void FurnaceGUI::editOptions(bool topMenu) {
} else { } else {
const unsigned char data=latchEffect; const unsigned char data=latchEffect;
snprintf(id,63,"%.2x##LatchFX",data); snprintf(id,63,"%.2x##LatchFX",data);
if (data<0x10) { ImGui::PushStyleColor(ImGuiCol_Text,uiColors[fxColors[data]]);
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]]);
}
} }
if (ImGui::Selectable(id,latchTarget==3,ImGuiSelectableFlags_DontClosePopups)) { if (ImGui::Selectable(id,latchTarget==3,ImGuiSelectableFlags_DontClosePopups)) {
@ -2370,7 +2350,9 @@ bool FurnaceGUI::loop() {
demandScrollX=true; demandScrollX=true;
if (cursor.xCoarse==selStart.xCoarse && cursor.xFine==selStart.xFine && cursor.y==selStart.y && 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) { cursor.xCoarse==selEnd.xCoarse && cursor.xFine==selEnd.xFine && cursor.y==selEnd.y) {
updateScroll(cursor.y); if (!settings.cursorMoveNoScroll) {
updateScroll(cursor.y);
}
} }
} }
break; break;
@ -2680,7 +2662,7 @@ bool FurnaceGUI::loop() {
if (ImGui::BeginMenu("configure system...")) { if (ImGui::BeginMenu("configure system...")) {
for (int i=0; i<e->song.systemLen; i++) { 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())) { 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(); ImGui::TreePop();
} }
} }
@ -2775,7 +2757,8 @@ bool FurnaceGUI::loop() {
ImGui::Separator(); ImGui::Separator();
if (ImGui::MenuItem("play/edit controls",BIND_FOR(GUI_ACTION_WINDOW_EDIT_CONTROLS),editControlsOpen)) editControlsOpen=!editControlsOpen; 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("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("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("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; if (ImGui::MenuItem("log viewer",BIND_FOR(GUI_ACTION_WINDOW_LOG),logOpen)) logOpen=!logOpen;
@ -2878,6 +2861,7 @@ bool FurnaceGUI::loop() {
readOsc(); readOsc();
drawOsc(); drawOsc();
drawChanOsc();
drawVolMeter(); drawVolMeter();
drawSettings(); drawSettings();
drawDebug(); drawDebug();
@ -2900,6 +2884,19 @@ bool FurnaceGUI::loop() {
#endif #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))) { if (fileDialog->render(ImVec2(600.0f*dpiScale,400.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale))) {
bool openOpen=false; bool openOpen=false;
//ImGui::GetIO().ConfigFlags&=~ImGuiConfigFlags_NavEnableKeyboard; //ImGui::GetIO().ConfigFlags&=~ImGuiConfigFlags_NavEnableKeyboard;
@ -3561,6 +3558,7 @@ bool FurnaceGUI::init() {
settingsOpen=e->getConfBool("settingsOpen",false); settingsOpen=e->getConfBool("settingsOpen",false);
mixerOpen=e->getConfBool("mixerOpen",false); mixerOpen=e->getConfBool("mixerOpen",false);
oscOpen=e->getConfBool("oscOpen",true); oscOpen=e->getConfBool("oscOpen",true);
chanOscOpen=e->getConfBool("chanOscOpen",false);
volMeterOpen=e->getConfBool("volMeterOpen",true); volMeterOpen=e->getConfBool("volMeterOpen",true);
statsOpen=e->getConfBool("statsOpen",false); statsOpen=e->getConfBool("statsOpen",false);
compatFlagsOpen=e->getConfBool("compatFlagsOpen",false); compatFlagsOpen=e->getConfBool("compatFlagsOpen",false);
@ -3736,6 +3734,7 @@ bool FurnaceGUI::finish() {
e->setConf("settingsOpen",settingsOpen); e->setConf("settingsOpen",settingsOpen);
e->setConf("mixerOpen",mixerOpen); e->setConf("mixerOpen",mixerOpen);
e->setConf("oscOpen",oscOpen); e->setConf("oscOpen",oscOpen);
e->setConf("chanOscOpen",chanOscOpen);
e->setConf("volMeterOpen",volMeterOpen); e->setConf("volMeterOpen",volMeterOpen);
e->setConf("statsOpen",statsOpen); e->setConf("statsOpen",statsOpen);
e->setConf("compatFlagsOpen",compatFlagsOpen); e->setConf("compatFlagsOpen",compatFlagsOpen);
@ -3856,6 +3855,7 @@ FurnaceGUI::FurnaceGUI():
regViewOpen(false), regViewOpen(false),
logOpen(false), logOpen(false),
effectListOpen(false), effectListOpen(false),
chanOscOpen(false),
/* /*
editControlsDocked(false), editControlsDocked(false),
ordersDocked(false), ordersDocked(false),
@ -3882,6 +3882,7 @@ FurnaceGUI::FurnaceGUI():
regViewDocked(false), regViewDocked(false),
logDocked(false), logDocked(false),
effectListDocked(false), effectListDocked(false),
chanOscDocked(false),
*/ */
selecting(false), selecting(false),
curNibble(false), curNibble(false),
@ -3998,6 +3999,7 @@ FurnaceGUI::FurnaceGUI():
oscTotal(0), oscTotal(0),
oscZoom(0.5f), oscZoom(0.5f),
oscZoomSlider(false), oscZoomSlider(false),
chanOscCols(3),
followLog(true), followLog(true),
pianoOctaves(7), pianoOctaves(7),
pianoOptions(false), pianoOptions(false),

View File

@ -230,7 +230,8 @@ enum FurnaceGUIWindows {
GUI_WINDOW_CHANNELS, GUI_WINDOW_CHANNELS,
GUI_WINDOW_REGISTER_VIEW, GUI_WINDOW_REGISTER_VIEW,
GUI_WINDOW_LOG, GUI_WINDOW_LOG,
GUI_WINDOW_EFFECT_LIST GUI_WINDOW_EFFECT_LIST,
GUI_WINDOW_CHAN_OSC
}; };
enum FurnaceGUIFileDialogs { enum FurnaceGUIFileDialogs {
@ -330,6 +331,7 @@ enum FurnaceGUIActions {
GUI_ACTION_WINDOW_REGISTER_VIEW, GUI_ACTION_WINDOW_REGISTER_VIEW,
GUI_ACTION_WINDOW_LOG, GUI_ACTION_WINDOW_LOG,
GUI_ACTION_WINDOW_EFFECT_LIST, GUI_ACTION_WINDOW_EFFECT_LIST,
GUI_ACTION_WINDOW_CHAN_OSC,
GUI_ACTION_COLLAPSE_WINDOW, GUI_ACTION_COLLAPSE_WINDOW,
GUI_ACTION_CLOSE_WINDOW, GUI_ACTION_CLOSE_WINDOW,
@ -857,6 +859,7 @@ class FurnaceGUI {
String audioDevice; String audioDevice;
String midiInDevice; String midiInDevice;
String midiOutDevice; String midiOutDevice;
std::vector<int> initialSys;
Settings(): Settings():
mainFontSize(18), mainFontSize(18),
@ -947,13 +950,13 @@ class FurnaceGUI {
bool editControlsOpen, ordersOpen, insListOpen, songInfoOpen, patternOpen, insEditOpen; bool editControlsOpen, ordersOpen, insListOpen, songInfoOpen, patternOpen, insEditOpen;
bool waveListOpen, waveEditOpen, sampleListOpen, sampleEditOpen, aboutOpen, settingsOpen; bool waveListOpen, waveEditOpen, sampleListOpen, sampleEditOpen, aboutOpen, settingsOpen;
bool mixerOpen, debugOpen, inspectorOpen, oscOpen, volMeterOpen, statsOpen, compatFlagsOpen; 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... /* there ought to be a better way...
bool editControlsDocked, ordersDocked, insListDocked, songInfoDocked, patternDocked, insEditDocked; bool editControlsDocked, ordersDocked, insListDocked, songInfoDocked, patternDocked, insEditDocked;
bool waveListDocked, waveEditDocked, sampleListDocked, sampleEditDocked, aboutDocked, settingsDocked; bool waveListDocked, waveEditDocked, sampleListDocked, sampleEditDocked, aboutDocked, settingsDocked;
bool mixerDocked, debugDocked, inspectorDocked, oscDocked, volMeterDocked, statsDocked, compatFlagsDocked; 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; SelectionPoint selStart, selEnd, cursor;
@ -1096,6 +1099,9 @@ class FurnaceGUI {
float oscZoom; float oscZoom;
bool oscZoomSlider; bool oscZoomSlider;
// per-channel oscilloscope
int chanOscCols;
// visualizer // visualizer
float keyHit[DIV_MAX_CHANS]; float keyHit[DIV_MAX_CHANS];
int lastIns[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 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 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 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. // 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); 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 drawSampleEdit();
void drawMixer(); void drawMixer();
void drawOsc(); void drawOsc();
void drawChanOsc();
void drawVolMeter(); void drawVolMeter();
void drawStats(); void drawStats();
void drawCompatFlags(); void drawCompatFlags();

View File

@ -149,7 +149,7 @@ const char* loopMode[DIV_SAMPLE_LOOPMODE_MAX]={
"Pingpong" "Pingpong"
}; };
const FurnaceGUIColors fxColors[16]={ const FurnaceGUIColors fxColors[256]={
GUI_COLOR_PATTERN_EFFECT_MISC, // 00 GUI_COLOR_PATTERN_EFFECT_MISC, // 00
GUI_COLOR_PATTERN_EFFECT_PITCH, // 01 GUI_COLOR_PATTERN_EFFECT_PITCH, // 01
GUI_COLOR_PATTERN_EFFECT_PITCH, // 02 GUI_COLOR_PATTERN_EFFECT_PITCH, // 02
@ -166,9 +166,242 @@ const FurnaceGUIColors fxColors[16]={
GUI_COLOR_PATTERN_EFFECT_SONG, // 0D GUI_COLOR_PATTERN_EFFECT_SONG, // 0D
GUI_COLOR_PATTERN_EFFECT_INVALID, // 0E GUI_COLOR_PATTERN_EFFECT_INVALID, // 0E
GUI_COLOR_PATTERN_EFFECT_SPEED, // 0F 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_MISC, // E0
GUI_COLOR_PATTERN_EFFECT_PITCH, // E1 GUI_COLOR_PATTERN_EFFECT_PITCH, // E1
GUI_COLOR_PATTERN_EFFECT_PITCH, // E2 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, // FC
GUI_COLOR_PATTERN_EFFECT_INVALID, // FD GUI_COLOR_PATTERN_EFFECT_INVALID, // FD
GUI_COLOR_PATTERN_EFFECT_INVALID, // FE GUI_COLOR_PATTERN_EFFECT_INVALID, // FE
GUI_COLOR_PATTERN_EFFECT_SONG, // FF GUI_COLOR_PATTERN_EFFECT_SONG // FF
}; };
#define D FurnaceGUIActionDef #define D FurnaceGUIActionDef
@ -249,7 +482,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={
D("WINDOW_SETTINGS", "Settings", 0), D("WINDOW_SETTINGS", "Settings", 0),
D("WINDOW_MIXER", "Mixer", 0), D("WINDOW_MIXER", "Mixer", 0),
D("WINDOW_DEBUG", "Debug Menu", 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_VOL_METER", "Volume Meter", 0),
D("WINDOW_STATS", "Statistics", 0), D("WINDOW_STATS", "Statistics", 0),
D("WINDOW_COMPAT_FLAGS", "Compatibility Flags", 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_REGISTER_VIEW", "Register View", 0),
D("WINDOW_LOG", "Log Viewer", 0), D("WINDOW_LOG", "Log Viewer", 0),
D("EFFECT_LIST", "Effect List", 0), D("EFFECT_LIST", "Effect List", 0),
D("WINDOW_CHAN_OSC", "Oscilloscope (per-channel)", 0),
D("COLLAPSE_WINDOW", "Collapse/expand current window", 0), D("COLLAPSE_WINDOW", "Collapse/expand current window", 0),
D("CLOSE_WINDOW", "Close current window", FURKMOD_SHIFT|SDLK_ESCAPE), 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 FurnaceGUIColorDef guiColors[];
extern const int altValues[24]; extern const int altValues[24];
extern const int vgmVersions[6]; extern const int vgmVersions[6];
extern const FurnaceGUIColors fxColors[16]; extern const FurnaceGUIColors fxColors[256];
extern const FurnaceGUIColors extFxColors[32];

View File

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

View File

@ -1341,7 +1341,26 @@ void FurnaceGUI::drawSampleEdit() {
sampleZoom=100.0/zoomPercent; sampleZoom=100.0/zoomPercent;
if (sampleZoom<0.01) sampleZoom=0.01; if (sampleZoom<0.01) sampleZoom=0.01;
sampleZoomAuto=false; sampleZoomAuto=false;
int bounds=((int)sample->samples-round(rectSize.x*sampleZoom));
if (bounds<0) bounds=0;
if (samplePos>bounds) samplePos=bounds;
updateSampleTex=true; 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; int posX=-1;

View File

@ -23,6 +23,7 @@
#include "../fileutils.h" #include "../fileutils.h"
#include "util.h" #include "util.h"
#include "guiConst.h" #include "guiConst.h"
#include "intConst.h"
#include "ImGuiFileDialog.h" #include "ImGuiFileDialog.h"
#include "IconsFontAwesome4.h" #include "IconsFontAwesome4.h"
#include "misc/cpp/imgui_stdlib.h" #include "misc/cpp/imgui_stdlib.h"
@ -219,7 +220,8 @@ void FurnaceGUI::drawSettings() {
} }
if (ImGui::BeginTabBar("settingsTab")) { if (ImGui::BeginTabBar("settingsTab")) {
if (ImGui::BeginTabItem("General")) { if (ImGui::BeginTabItem("General")) {
ImGui::Text("Workspace layout"); ImGui::Text("Workspace layout:");
ImGui::SameLine();
if (ImGui::Button("Import")) { if (ImGui::Button("Import")) {
openFileDialog(GUI_FILE_IMPORT_LAYOUT); openFileDialog(GUI_FILE_IMPORT_LAYOUT);
} }
@ -231,7 +233,108 @@ void FurnaceGUI::drawSettings() {
if (ImGui::Button("Reset")) { if (ImGui::Button("Reset")) {
showWarning("Are you sure you want to reset the workspace layout?",GUI_WARN_RESET_LAYOUT); showWarning("Are you sure you want to reset the workspace layout?",GUI_WARN_RESET_LAYOUT);
} }
ImGui::Separator(); 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:"); ImGui::Text("Toggle channel solo on:");
if (ImGui::RadioButton("Right-click or double-click##soloA",settings.soloAction==0)) { if (ImGui::RadioButton("Right-click or double-click##soloA",settings.soloAction==0)) {
settings.soloAction=0; settings.soloAction=0;
@ -1755,6 +1858,19 @@ void FurnaceGUI::syncSettings() {
clampSetting(settings.moveWindowTitle,0,1); clampSetting(settings.moveWindowTitle,0,1);
clampSetting(settings.hiddenSystems,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 // keybinds
for (int i=0; i<GUI_ACTION_MAX; i++) { for (int i=0; i<GUI_ACTION_MAX; i++) {
if (guiActions[i].defaultBind==-1) continue; // not a bind if (guiActions[i].defaultBind==-1) continue; // not a bind
@ -1849,6 +1965,7 @@ void FurnaceGUI::commitSettings() {
e->setConf("eventDelay",settings.eventDelay); e->setConf("eventDelay",settings.eventDelay);
e->setConf("moveWindowTitle",settings.moveWindowTitle); e->setConf("moveWindowTitle",settings.moveWindowTitle);
e->setConf("hiddenSystems",settings.hiddenSystems); e->setConf("hiddenSystems",settings.hiddenSystems);
e->setConf("initialSys",e->encodeSysDesc(settings.initialSys));
// colors // colors
for (int i=0; i<GUI_COLOR_MAX; i++) { for (int i=0; i<GUI_COLOR_MAX; i++) {

View File

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