Merge branch 'master' into feature/esfm

This commit is contained in:
Kagamiin~ 2024-01-01 14:45:54 -03:00
commit 215c8c375a
46 changed files with 1596 additions and 829 deletions

View file

@ -782,6 +782,7 @@ src/gui/doAction.cpp
src/gui/editing.cpp src/gui/editing.cpp
src/gui/editControls.cpp src/gui/editControls.cpp
src/gui/effectList.cpp src/gui/effectList.cpp
src/gui/exportOptions.cpp
src/gui/findReplace.cpp src/gui/findReplace.cpp
src/gui/fmPreview.cpp src/gui/fmPreview.cpp
src/gui/gradient.cpp src/gui/gradient.cpp

View file

@ -1,15 +1,12 @@
# to-do # to-do
- add cheat code to insert bruno time (blank) - finish color import improvements (settings refactor)
- new undo stuff
# THE REAL TO-DO LIST - fix some bugs
- finish auto-clone - finish auto-clone
once you have done all of this (maybe not the first one), release 0.6.1 once you have done all of this (maybe not the first one), release 0.6.1
Furnace is like alcohol...
# and then # and then
- new oscilloscope renderer - custom code that uses texture and fixes two issues: too many vertices, and broken anti-aliasing - new oscilloscope renderer - custom code that uses texture and fixes two issues: too many vertices, and broken anti-aliasing

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

View file

@ -123,22 +123,21 @@ settings are saved when clicking the **OK** or **Apply** buttons at the bottom o
- **Note input**: enables note input. disable if you intend to use this device only for binding actions. - **Note input**: enables note input. disable if you intend to use this device only for binding actions.
- **Velocity input**: enables velocity input when entering notes in the pattern. - **Velocity input**: enables velocity input when entering notes in the pattern.
- **Map MIDI channels to direct channels**: when enabled, notes from MIDI channels will be mapped to channels rather than the cursor position. - **Map MIDI channels to direct channels**: when enabled, notes from MIDI channels will be mapped to channels rather than the cursor position.
- **Program change pass-through**: when enabled, program change events are sent to each channel as instrument change commands.
- this option is only available when the previous one is enabled.
- **Map Yamaha FM voice data to instruments**: when enabled, Furnace will listen for any transmitted Yamaha SysEx patches. - **Map Yamaha FM voice data to instruments**: when enabled, Furnace will listen for any transmitted Yamaha SysEx patches.
- this option is only useful if you have a Yamaha FM synthesizer (e.g. TX81Z). - this option is only useful if you have a Yamaha FM synthesizer (e.g. TX81Z).
- selecting a voice or using the "Voice Transmit?" option will send a patch, and Furnace will create a new instrument with its data. - selecting a voice or using the "Voice Transmit?" option will send a patch, and Furnace will create a new instrument with its data.
- this may also be triggered by clicking on "Receive from TX81Z" in the instrument editor (OPZ only). - this may also be triggered by clicking on "Receive from TX81Z" in the instrument editor (OPZ only).
- **Program change is instrument selection**: changes the current instrument when a program change event is received. - **Program change is instrument selection**: changes the current instrument when a program change event is received.
- this option is not available when "Program change pass-through" is enabled.
- **Value input style**: changes the way values are entered when the pattern cursor is not in the Note column. the following styles are available: - **Value input style**: changes the way values are entered when the pattern cursor is not in the Note column. the following styles are available:
- **Disabled/custom**: no value input through MIDI. - **Disabled/custom**: no value input through MIDI.
- **Two octaves (0 is C-4, F is D#5)**: maps keys in two octaves to single nibble input. the layout is: - **Two octaves (0 is C-4, F is D#5)**: maps keys in two octaves to single nibble input. the layout is:
- ` - octave n -- octave n+1 -` ![two octaves layout 1](MIDI-value-input-1.png)
- ` 1 3 6 8 A D F # # # `
- `0 2 4 5 7 9 B C E # # # # #`
- **Raw (note number is value)**: the note number becomes the input value. not useful if you want to input anything above 7F. - **Raw (note number is value)**: the note number becomes the input value. not useful if you want to input anything above 7F.
- **Two octaves alternate (lower keys are 0-9, upper keys are A-F)**: maps keys in two octaves, but with a different layout: - **Two octaves alternate (lower keys are 0-9, upper keys are A-F)**: maps keys in two octaves, but with a different layout:
- ` - octave n -- octave n+1 -` ![two octaves layout 2](MIDI-value-input-2.png)
- ` A B C D E F # # # # `
- `0 1 2 3 4 5 6 7 8 9 # # # #`
- **Use dual control change (one for each nibble)**: maps two control change events to the nibbles of a value. - **Use dual control change (one for each nibble)**: maps two control change events to the nibbles of a value.
- **CC of upper nibble**: select the CC number that will change the upper nibble. - **CC of upper nibble**: select the CC number that will change the upper nibble.
- **CC of lower nibble**: select the CC number that will change the lower nibble. - **CC of lower nibble**: select the CC number that will change the lower nibble.
@ -149,6 +148,7 @@ settings are saved when clicking the **OK** or **Apply** buttons at the bottom o
- **Control**: select the CC number that will change the value. - **Control**: select the CC number that will change the value.
- **Per-column control change**: when enabled, you can map several control change events to a channel's columns. - **Per-column control change**: when enabled, you can map several control change events to a channel's columns.
- **Volume curve**: adjust the velocity to volume curve. - **Volume curve**: adjust the velocity to volume curve.
- the default is 2.0, which matches General MIDI standard.
- **Actions**: this allows you to bind note input and control change events to actions. - **Actions**: this allows you to bind note input and control change events to actions.
- **`+`** button: adds a new action. - **`+`** button: adds a new action.
- window-with-arrow button: new action with learning! press a button or move a slider/knob/something on your device. - window-with-arrow button: new action with learning! press a button or move a slider/knob/something on your device.
@ -498,7 +498,7 @@ below all the binds, select a key from the dropdown list to add it. it will appe
- **Rounded window corners** - **Rounded window corners**
- **Rounded buttons** - **Rounded buttons**
- **Rounded menu corners** - **Rounded menu corners**
- **Borders around widgets**: draws thin borders on buttons, checkboxes, text widgets, and the like. - **Borders around widgets**: draws borders on buttons, checkboxes, text widgets, and the like.
@ -509,6 +509,7 @@ below all the binds, select a key from the dropdown list to add it. it will appe
- **Import** - **Import**
- **Export** - **Export**
- **Reset defaults** - **Reset defaults**
- **Guru mode**: exposes all color options (instead of accent colors).
- **General** - **General**
- **Color scheme type:** - **Color scheme type:**
- **Dark** - **Dark**

View file

@ -6,7 +6,7 @@ it has 256 nibbles (128 bytes) of internal RAM which is shared between channel s
wavetables are variable in size and may be allocated anywhere in RAM. at least 128 nibbles (64 bytes) can be dedicated to waves, with more available if not all channels are used - waveform RAM area becomes smaller as more channels are activated, since channel registers consume 8 bytes for each channel. wavetables are variable in size and may be allocated anywhere in RAM. at least 128 nibbles (64 bytes) can be dedicated to waves, with more available if not all channels are used - waveform RAM area becomes smaller as more channels are activated, since channel registers consume 8 bytes for each channel.
Namco 163 uses time-division multiplexing for its output. this means that only one channel is output per sample (like OPLL and OPN2). therefore, its sound quality gets worse as more channels are activated. Namco 163 uses time-division multiplexing (TDM) for its output. this means that only one channel is output per sample (like OPLL and OPN2). therefore, its sound quality gets worse as more channels are activated.
## waveform load position versus waveform position ## waveform load position versus waveform position
@ -39,6 +39,13 @@ if the waveform changes (e.g. ins change, wave macro or wave synth), or the **lo
- make sure to use `21xx` first! - make sure to use `21xx` first!
- `21xx`: **set position for 20xx.** - `21xx`: **set position for 20xx.**
## chip options
- **Initial channel limit**: sets the number of channels that will be active. higher values reduce volume and make TDM artifacts more noticeable.
- **Disable hissing**: remove TDM artifacts by mixing. sacrifices some accuracy!
- **Scale frequency to wave length**: automatically adjusts note frequency to account for differing waveform lengths.
- if disabled, note frequencies ignore waveveform length. this is how FamiTracker behaves.
## info ## info
this chip uses the [Namco 163](../4-instrument/n163.md) instrument editor. this chip uses the [Namco 163](../4-instrument/n163.md) instrument editor.

View file

@ -15,8 +15,8 @@ the YM2413 is equipped with the following features:
- a drum/percussion mode, replacing the last 3 voices with 5 rhythm channels, with drum mode tones hard-defined in the chip itself, like FM instruments. only pitch might be altered. - a drum/percussion mode, replacing the last 3 voices with 5 rhythm channels, with drum mode tones hard-defined in the chip itself, like FM instruments. only pitch might be altered.
- drum mode works like following: FM channel 7 is for Kick Drum, which is a normal FM channel but routed through mixer twice for 2× volume, like all drum sounds. FM channel 8 splits to Snare, Drum, and Hi-Hat. Snare Drum is the carrier and it works with a special 1 bit noise generator combined with a square wave, all possible by overriding phase-generator with some different synthesis method. Hi-Hat is the modulator and it works with the noise generator and also the special synthesis. channel 9 splits to Top-Cymbal and Tom-Tom, Top-Cymbal is the carrier and only has the special synthesis, while Tom-Tom is basically a 1op wave. - drum mode works like following: FM channel 7 is for Kick Drum, which is a normal FM channel but routed through mixer twice for 2× volume, like all drum sounds. FM channel 8 splits to Snare, Drum, and Hi-Hat. Snare Drum is the carrier and it works with a special 1 bit noise generator combined with a square wave, all possible by overriding phase-generator with some different synthesis method. Hi-Hat is the modulator and it works with the noise generator and also the special synthesis. channel 9 splits to Top-Cymbal and Tom-Tom, Top-Cymbal is the carrier and only has the special synthesis, while Tom-Tom is basically a 1op wave.
- special synthesis mentioned already is: 5 square waves are gathered from 4×, 64× and 128× the pitch of channel 8 and 16× and 64× the pitch of channel 9 and they go through a process where 2 HH bits OR'd together, then 1 HH and 1 TC bit OR'd, then the two TC bits OR'd together, and those 3 results get XOR'd. - special synthesis mentioned already is: 5 square waves are gathered from 4×, 64× and 128× the pitch of channel 8 and 16× and 64× the pitch of channel 9 and they go through a process where 2 HH bits OR'd together, then 1 HH and 1 TC bit OR'd, then the two TC bits OR'd together, and those 3 results get XOR'd.
- 1 user-definable patch (this patch can be changed throughout the course of the song) - **1 user-definable patch (this patch can be changed throughout the course of the song)**
- 15 pre-defined patches which can all be used at the same time - **15 pre-defined patches which can all be used at the same time**
- support for ADSR on both the modulator and the carrier - support for ADSR on both the modulator and the carrier
- sine and half-sine based FM synthesis - sine and half-sine based FM synthesis
- 9 octave note control - 9 octave note control

View file

@ -168,3 +168,7 @@ void DivEngine::setConf(String key, String value) {
bool DivEngine::hasConf(String key) { bool DivEngine::hasConf(String key) {
return conf.has(key); return conf.has(key);
} }
DivConfig& DivEngine::getConfObject() {
return conf;
}

View file

@ -83,7 +83,7 @@ const char* DivEngine::getEffectDesc(unsigned char effect, int chan, bool notNul
case 0x82: case 0x82:
return "82xx: Set panning (right channel)"; return "82xx: Set panning (right channel)";
case 0x88: case 0x88:
return "88xx: Set panning (rear channels; x: left; y: right)"; return "88xy: Set panning (rear channels; x: left; y: right)";
break; break;
case 0x89: case 0x89:
return "89xx: Set panning (rear left channel)"; return "89xx: Set panning (rear left channel)";
@ -2188,6 +2188,13 @@ int DivEngine::getMaxVolumeChan(int ch) {
return chan[ch].volMax>>8; return chan[ch].volMax>>8;
} }
int DivEngine::mapVelocity(int ch, float vel) {
if (ch<0) return 0;
if (ch>=chans) return 0;
if (disCont[dispatchOfChan[ch]].dispatch==NULL) return 0;
return disCont[dispatchOfChan[ch]].dispatch->mapVelocity(dispatchChanOfChan[ch],vel);
}
unsigned char DivEngine::getOrder() { unsigned char DivEngine::getOrder() {
return prevOrder; return prevOrder;
} }
@ -3391,6 +3398,10 @@ void DivEngine::setMidiDirect(bool value) {
midiIsDirect=value; midiIsDirect=value;
} }
void DivEngine::setMidiDirectProgram(bool value) {
midiIsDirectProgram=value;
}
void DivEngine::setMidiVolExp(float value) { void DivEngine::setMidiVolExp(float value) {
midiVolExp=value; midiVolExp=value;
} }
@ -3459,6 +3470,12 @@ void DivEngine::rescanAudioDevices() {
audioDevs.clear(); audioDevs.clear();
if (output!=NULL) { if (output!=NULL) {
audioDevs=output->listAudioDevices(); audioDevs=output->listAudioDevices();
}
}
void DivEngine::rescanMidiDevices() {
if (output!=NULL) {
logV("re-scanning midi...");
if (output->midiIn!=NULL) { if (output->midiIn!=NULL) {
midiIns=output->midiIn->listDevices(); midiIns=output->midiIn->listDevices();
} }

View file

@ -54,8 +54,8 @@ class DivWorkPool;
#define DIV_UNSTABLE #define DIV_UNSTABLE
#define DIV_VERSION "dev189" #define DIV_VERSION "dev190"
#define DIV_ENGINE_VERSION 189 #define DIV_ENGINE_VERSION 190
// for imports // for imports
#define DIV_VERSION_MOD 0xff01 #define DIV_VERSION_MOD 0xff01
#define DIV_VERSION_FC 0xff02 #define DIV_VERSION_FC 0xff02
@ -176,16 +176,16 @@ struct DivNoteEvent {
signed char channel; signed char channel;
unsigned char ins; unsigned char ins;
signed char note, volume; signed char note, volume;
bool on, nop, pad1, pad2; bool on, nop, insChange, fromMIDI;
DivNoteEvent(int c, int i, int n, int v, bool o): DivNoteEvent(int c, int i, int n, int v, bool o, bool ic=false, bool fm=false):
channel(c), channel(c),
ins(i), ins(i),
note(n), note(n),
volume(v), volume(v),
on(o), on(o),
nop(false), nop(false),
pad1(false), insChange(ic),
pad2(false) {} fromMIDI(fm) {}
DivNoteEvent(): DivNoteEvent():
channel(-1), channel(-1),
ins(0), ins(0),
@ -193,8 +193,8 @@ struct DivNoteEvent {
volume(-1), volume(-1),
on(false), on(false),
nop(true), nop(true),
pad1(false), insChange(false),
pad2(false) {} fromMIDI(false) {}
}; };
struct DivDispatchContainer { struct DivDispatchContainer {
@ -415,6 +415,7 @@ class DivEngine {
bool firstTick; bool firstTick;
bool skipping; bool skipping;
bool midiIsDirect; bool midiIsDirect;
bool midiIsDirectProgram;
bool lowLatency; bool lowLatency;
bool systemsRegistered; bool systemsRegistered;
bool hasLoadedSomething; bool hasLoadedSomething;
@ -701,6 +702,9 @@ class DivEngine {
double getConfDouble(String key, double fallback); double getConfDouble(String key, double fallback);
String getConfString(String key, String fallback); String getConfString(String key, String fallback);
// get config object
DivConfig& getConfObject();
// set a config value // set a config value
void setConf(String key, bool value); void setConf(String key, bool value);
void setConf(String key, int value); void setConf(String key, int value);
@ -851,6 +855,9 @@ class DivEngine {
// get channel max volume // get channel max volume
int getMaxVolumeChan(int chan); int getMaxVolumeChan(int chan);
// map MIDI velocity to volume
int mapVelocity(int ch, float vel);
// get current order // get current order
unsigned char getOrder(); unsigned char getOrder();
@ -1070,6 +1077,9 @@ class DivEngine {
// rescan audio devices // rescan audio devices
void rescanAudioDevices(); void rescanAudioDevices();
/** rescan midi devices */
void rescanMidiDevices();
// set the console mode. // set the console mode.
void setConsoleMode(bool enable); void setConsoleMode(bool enable);
@ -1185,6 +1195,9 @@ class DivEngine {
// set MIDI direct channel map // set MIDI direct channel map
void setMidiDirect(bool value); void setMidiDirect(bool value);
// set MIDI direct program change
void setMidiDirectProgram(bool value);
// set MIDI volume curve exponent // set MIDI volume curve exponent
void setMidiVolExp(float value); void setMidiVolExp(float value);
@ -1257,6 +1270,7 @@ class DivEngine {
firstTick(false), firstTick(false),
skipping(false), skipping(false),
midiIsDirect(false), midiIsDirect(false),
midiIsDirectProgram(false),
lowLatency(false), lowLatency(false),
systemsRegistered(false), systemsRegistered(false),
hasLoadedSomething(false), hasLoadedSomething(false),

View file

@ -6511,7 +6511,7 @@ SafeWriter* DivEngine::saveText(bool separatePatterns) {
if (ins->type==DIV_INS_GB) { if (ins->type==DIV_INS_GB) {
w->writeText("- Game Boy parameters:\n"); w->writeText("- Game Boy parameters:\n");
w->writeText(fmt::sprintf(" - volume: %d\n",ins->gb.envVol)); w->writeText(fmt::sprintf(" - volume: %d\n",ins->gb.envVol));
w->writeText(fmt::sprintf(" - direction: %d\n",gbEnvDir[ins->gb.envDir?1:0])); w->writeText(fmt::sprintf(" - direction: %s\n",gbEnvDir[ins->gb.envDir?1:0]));
w->writeText(fmt::sprintf(" - length: %d\n",ins->gb.envLen)); w->writeText(fmt::sprintf(" - length: %d\n",ins->gb.envLen));
w->writeText(fmt::sprintf(" - sound length: %d\n",ins->gb.soundLen)); w->writeText(fmt::sprintf(" - sound length: %d\n",ins->gb.soundLen));
w->writeText(fmt::sprintf(" - use software envelope: %s\n",trueFalse[ins->gb.softEnv?1:0])); w->writeText(fmt::sprintf(" - use software envelope: %s\n",trueFalse[ins->gb.softEnv?1:0]));

View file

@ -186,6 +186,17 @@ class DivPlatformOPN: public DivPlatformFMBase {
void setCombo(bool combo) { void setCombo(bool combo) {
useCombo=combo; useCombo=combo;
} }
virtual int mapVelocity(int ch, float vel) {
if (ch==csmChan) return vel*127.0;
if (ch==adpcmBChanOffs) return vel*255.0;
if (ch>=adpcmAChanOffs) {
if (vel==0) return 0;
if (vel>=1.0) return 31;
return CLAMP(round(32.0-(56.0-log2(vel*127.0)*8.0)),0,31);
}
if (ch>=psgChanOffs) return round(15.0*pow(vel,0.33));
return DivPlatformFMBase::mapVelocity(ch,vel);
}
}; };
#endif #endif

View file

@ -132,7 +132,7 @@ class DivPlatformFMBase: public DivDispatch {
// -36: 2: 48 // -36: 2: 48
// -42: 1: 56 // -42: 1: 56
if (vel==0) return 0; if (vel==0) return 0;
if (vel==127) return 127; if (vel>=1.0) return 127;
return CLAMP(round(128.0-(56.0-log2(vel*127.0)*8.0)),0,127); return CLAMP(round(128.0-(56.0-log2(vel*127.0)*8.0)),0,127);
} }

View file

@ -1300,6 +1300,12 @@ DivDispatchOscBuffer* DivPlatformGenesis::getOscBuffer(int ch) {
return oscBuf[ch]; return oscBuf[ch];
} }
int DivPlatformGenesis::mapVelocity(int ch, float vel) {
if (ch==csmChan) return DivPlatformOPN::mapVelocity(ch,vel);
if (ch>5) return DivPlatformOPN::mapVelocity(5,vel);
return DivPlatformOPN::mapVelocity(ch,vel);
}
unsigned char* DivPlatformGenesis::getRegisterPool() { unsigned char* DivPlatformGenesis::getRegisterPool() {
return regPool; return regPool;
} }

View file

@ -112,6 +112,7 @@ class DivPlatformGenesis: public DivPlatformOPN {
virtual unsigned short getPan(int chan); virtual unsigned short getPan(int chan);
DivSamplePos getSamplePos(int ch); DivSamplePos getSamplePos(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan); DivDispatchOscBuffer* getOscBuffer(int chan);
virtual int mapVelocity(int ch, float vel);
unsigned char* getRegisterPool(); unsigned char* getRegisterPool();
int getRegisterPoolSize(); int getRegisterPoolSize();
void reset(); void reset();

View file

@ -818,6 +818,12 @@ DivDispatchOscBuffer* DivPlatformGenesisExt::getOscBuffer(int ch) {
return NULL; return NULL;
} }
int DivPlatformGenesisExt::mapVelocity(int ch, float vel) {
if (ch>=extChanOffs+4) return DivPlatformGenesis::mapVelocity(ch-3,vel);
if (ch>=extChanOffs) return DivPlatformGenesis::mapVelocity(extChanOffs,vel);
return DivPlatformGenesis::mapVelocity(ch,vel);
}
void DivPlatformGenesisExt::reset() { void DivPlatformGenesisExt::reset() {
DivPlatformGenesis::reset(); DivPlatformGenesis::reset();

View file

@ -36,6 +36,7 @@ class DivPlatformGenesisExt: public DivPlatformGenesis {
DivMacroInt* getChanMacroInt(int ch); DivMacroInt* getChanMacroInt(int ch);
unsigned short getPan(int chan); unsigned short getPan(int chan);
DivDispatchOscBuffer* getOscBuffer(int chan); DivDispatchOscBuffer* getOscBuffer(int chan);
int mapVelocity(int ch, float vel);
void reset(); void reset();
void forceIns(); void forceIns();
void tick(bool sysTick=true); void tick(bool sysTick=true);

View file

@ -199,7 +199,7 @@ void DivPlatformNamcoWSG::tick(bool sysTick) {
for (int i=0; i<chans; i++) { for (int i=0; i<chans; i++) {
chan[i].std.next(); chan[i].std.next();
if (chan[i].std.vol.had) { if (chan[i].std.vol.had) {
chan[i].outVol=((chan[i].vol&15)*MIN(15,chan[i].std.vol.val))>>4; chan[i].outVol=VOL_SCALE_LINEAR(chan[i].vol,chan[i].std.vol.val,15);
} }
if (chan[i].std.duty.had) { if (chan[i].std.duty.had) {
chan[i].noise=chan[i].std.duty.val; chan[i].noise=chan[i].std.duty.val;

View file

@ -2104,6 +2104,21 @@ DivDispatchOscBuffer* DivPlatformOPL::getOscBuffer(int ch) {
return oscBuf[ch]; return oscBuf[ch];
} }
int DivPlatformOPL::mapVelocity(int ch, float vel) {
if (ch==adpcmChan) return vel*255.0;
// -0.75dB per step
// -6: 64: 8
// -12: 32: 16
// -18: 16: 24
// -24: 8: 32
// -30: 4: 40
// -36: 2: 48
// -42: 1: 56
if (vel==0) return 0;
if (vel>=1.0) return 63;
return CLAMP(round(64.0-(56.0-log2(vel*127.0)*8.0)),0,63);
}
unsigned char* DivPlatformOPL::getRegisterPool() { unsigned char* DivPlatformOPL::getRegisterPool() {
return regPool; return regPool;
} }

View file

@ -150,6 +150,7 @@ class DivPlatformOPL: public DivDispatch {
unsigned short getPan(int chan); unsigned short getPan(int chan);
DivChannelPair getPaired(int chan); DivChannelPair getPaired(int chan);
DivDispatchOscBuffer* getOscBuffer(int chan); DivDispatchOscBuffer* getOscBuffer(int chan);
int mapVelocity(int ch, float vel);
unsigned char* getRegisterPool(); unsigned char* getRegisterPool();
int getRegisterPoolSize(); int getRegisterPoolSize();
void reset(); void reset();

View file

@ -962,6 +962,13 @@ DivDispatchOscBuffer* DivPlatformOPLL::getOscBuffer(int ch) {
return oscBuf[ch]; return oscBuf[ch];
} }
int DivPlatformOPLL::mapVelocity(int ch, float vel) {
// -3dB per step
if (vel==0) return 0;
if (vel>=1.0) return 15;
return CLAMP(round(16.0-(14.0-log2(vel*127.0)*2.0)),0,15);
}
unsigned char* DivPlatformOPLL::getRegisterPool() { unsigned char* DivPlatformOPLL::getRegisterPool() {
return regPool; return regPool;
} }

View file

@ -95,6 +95,7 @@ class DivPlatformOPLL: public DivDispatch {
void* getChanState(int chan); void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch); DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan); DivDispatchOscBuffer* getOscBuffer(int chan);
int mapVelocity(int ch, float vel);
unsigned char* getRegisterPool(); unsigned char* getRegisterPool();
int getRegisterPoolSize(); int getRegisterPoolSize();
void reset(); void reset();

View file

@ -692,6 +692,12 @@ DivDispatchOscBuffer* DivPlatformYM2203Ext::getOscBuffer(int ch) {
return NULL; return NULL;
} }
int DivPlatformYM2203Ext::mapVelocity(int ch, float vel) {
if (ch>=extChanOffs+4) return DivPlatformOPN::mapVelocity(ch-3,vel);
if (ch>=extChanOffs) return DivPlatformOPN::mapVelocity(extChanOffs,vel);
return DivPlatformOPN::mapVelocity(ch,vel);
}
void DivPlatformYM2203Ext::reset() { void DivPlatformYM2203Ext::reset() {
DivPlatformYM2203::reset(); DivPlatformYM2203::reset();

View file

@ -34,6 +34,7 @@ class DivPlatformYM2203Ext: public DivPlatformYM2203 {
void* getChanState(int chan); void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch); DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan); DivDispatchOscBuffer* getOscBuffer(int chan);
int mapVelocity(int ch, float vel);
void reset(); void reset();
void forceIns(); void forceIns();
void tick(bool sysTick=true); void tick(bool sysTick=true);

View file

@ -767,6 +767,12 @@ DivDispatchOscBuffer* DivPlatformYM2608Ext::getOscBuffer(int ch) {
return NULL; return NULL;
} }
int DivPlatformYM2608Ext::mapVelocity(int ch, float vel) {
if (ch>=extChanOffs+4) return DivPlatformOPN::mapVelocity(ch-3,vel);
if (ch>=extChanOffs) return DivPlatformOPN::mapVelocity(extChanOffs,vel);
return DivPlatformOPN::mapVelocity(ch,vel);
}
void DivPlatformYM2608Ext::reset() { void DivPlatformYM2608Ext::reset() {
DivPlatformYM2608::reset(); DivPlatformYM2608::reset();

View file

@ -35,6 +35,7 @@ class DivPlatformYM2608Ext: public DivPlatformYM2608 {
DivMacroInt* getChanMacroInt(int ch); DivMacroInt* getChanMacroInt(int ch);
unsigned short getPan(int chan); unsigned short getPan(int chan);
DivDispatchOscBuffer* getOscBuffer(int chan); DivDispatchOscBuffer* getOscBuffer(int chan);
int mapVelocity(int ch, float vel);
void reset(); void reset();
void forceIns(); void forceIns();
void tick(bool sysTick=true); void tick(bool sysTick=true);

View file

@ -757,6 +757,12 @@ DivDispatchOscBuffer* DivPlatformYM2610BExt::getOscBuffer(int ch) {
return NULL; return NULL;
} }
int DivPlatformYM2610BExt::mapVelocity(int ch, float vel) {
if (ch>=extChanOffs+4) return DivPlatformOPN::mapVelocity(ch-3,vel);
if (ch>=extChanOffs) return DivPlatformOPN::mapVelocity(extChanOffs,vel);
return DivPlatformOPN::mapVelocity(ch,vel);
}
void DivPlatformYM2610BExt::reset() { void DivPlatformYM2610BExt::reset() {
DivPlatformYM2610B::reset(); DivPlatformYM2610B::reset();

View file

@ -35,6 +35,7 @@ class DivPlatformYM2610BExt: public DivPlatformYM2610B {
DivMacroInt* getChanMacroInt(int ch); DivMacroInt* getChanMacroInt(int ch);
unsigned short getPan(int chan); unsigned short getPan(int chan);
DivDispatchOscBuffer* getOscBuffer(int chan); DivDispatchOscBuffer* getOscBuffer(int chan);
int mapVelocity(int ch, float vel);
void reset(); void reset();
void forceIns(); void forceIns();
void tick(bool sysTick=true); void tick(bool sysTick=true);

View file

@ -757,6 +757,12 @@ DivDispatchOscBuffer* DivPlatformYM2610Ext::getOscBuffer(int ch) {
return NULL; return NULL;
} }
int DivPlatformYM2610Ext::mapVelocity(int ch, float vel) {
if (ch>=extChanOffs+4) return DivPlatformOPN::mapVelocity(ch-3,vel);
if (ch>=extChanOffs) return DivPlatformOPN::mapVelocity(extChanOffs,vel);
return DivPlatformOPN::mapVelocity(ch,vel);
}
void DivPlatformYM2610Ext::reset() { void DivPlatformYM2610Ext::reset() {
DivPlatformYM2610::reset(); DivPlatformYM2610::reset();

View file

@ -35,6 +35,7 @@ class DivPlatformYM2610Ext: public DivPlatformYM2610 {
DivMacroInt* getChanMacroInt(int ch); DivMacroInt* getChanMacroInt(int ch);
unsigned short getPan(int chan); unsigned short getPan(int chan);
DivDispatchOscBuffer* getOscBuffer(int chan); DivDispatchOscBuffer* getOscBuffer(int chan);
int mapVelocity(int ch, float vel);
void reset(); void reset();
void forceIns(); void forceIns();
void tick(bool sysTick=true); void tick(bool sysTick=true);

View file

@ -1376,8 +1376,15 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
pendingNotes.pop_front(); pendingNotes.pop_front();
continue; continue;
} }
if (note.insChange) {
dispatchCmd(DivCommand(DIV_CMD_INSTRUMENT,note.channel,note.ins,0));
pendingNotes.pop_front();
continue;
}
if (note.on) { if (note.on) {
if (!(midiIsDirect && midiIsDirectProgram && note.fromMIDI)) {
dispatchCmd(DivCommand(DIV_CMD_INSTRUMENT,note.channel,note.ins,1)); dispatchCmd(DivCommand(DIV_CMD_INSTRUMENT,note.channel,note.ins,1));
}
if (note.volume>=0 && !disCont[dispatchOfChan[note.channel]].dispatch->isVolGlobal()) { if (note.volume>=0 && !disCont[dispatchOfChan[note.channel]].dispatch->isVolGlobal()) {
float curvedVol=pow((float)note.volume/127.0f,midiVolExp); float curvedVol=pow((float)note.volume/127.0f,midiVolExp);
int mappedVol=disCont[dispatchOfChan[note.channel]].dispatch->mapVelocity(dispatchChanOfChan[note.channel],curvedVol); int mappedVol=disCont[dispatchOfChan[note.channel]].dispatch->mapVelocity(dispatchChanOfChan[note.channel],curvedVol);
@ -1838,7 +1845,7 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
case TA_MIDI_NOTE_OFF: { case TA_MIDI_NOTE_OFF: {
if (chan<0 || chan>=chans) break; if (chan<0 || chan>=chans) break;
if (midiIsDirect) { if (midiIsDirect) {
pendingNotes.push_back(DivNoteEvent(chan,-1,-1,-1,false)); pendingNotes.push_back(DivNoteEvent(chan,-1,-1,-1,false,false,true));
} else { } else {
autoNoteOff(msg.type&15,msg.data[0]-12,msg.data[1]); autoNoteOff(msg.type&15,msg.data[0]-12,msg.data[1]);
} }
@ -1853,13 +1860,13 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
if (chan<0 || chan>=chans) break; if (chan<0 || chan>=chans) break;
if (msg.data[1]==0) { if (msg.data[1]==0) {
if (midiIsDirect) { if (midiIsDirect) {
pendingNotes.push_back(DivNoteEvent(chan,-1,-1,-1,false)); pendingNotes.push_back(DivNoteEvent(chan,-1,-1,-1,false,false,true));
} else { } else {
autoNoteOff(msg.type&15,msg.data[0]-12,msg.data[1]); autoNoteOff(msg.type&15,msg.data[0]-12,msg.data[1]);
} }
} else { } else {
if (midiIsDirect) { if (midiIsDirect) {
pendingNotes.push_back(DivNoteEvent(chan,ins,msg.data[0]-12,msg.data[1],true)); pendingNotes.push_back(DivNoteEvent(chan,ins,msg.data[0]-12,msg.data[1],true,false,true));
} else { } else {
autoNoteOn(msg.type&15,ins,msg.data[0]-12,msg.data[1]); autoNoteOn(msg.type&15,ins,msg.data[0]-12,msg.data[1]);
} }
@ -1867,7 +1874,9 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
break; break;
} }
case TA_MIDI_PROGRAM: { case TA_MIDI_PROGRAM: {
// TODO: change instrument event thingy if (midiIsDirect && midiIsDirectProgram) {
pendingNotes.push_back(DivNoteEvent(chan,msg.data[0],0,0,false,true,true));
}
break; break;
} }
} }

View file

@ -76,7 +76,7 @@ void FurnaceGUI::insListItem(int i, int dir, int asset) {
const char* insType="Bug!"; const char* insType="Bug!";
if (i>=0 && i<e->song.insLen) { if (i>=0 && i<e->song.insLen) {
DivInstrument* ins=e->song.ins[i]; DivInstrument* ins=e->song.ins[i];
insType=(ins->type>DIV_INS_MAX)?"Unknown":insTypes[ins->type][0]; insType=(ins->type>=DIV_INS_MAX)?"Unknown":insTypes[ins->type][0];
const char** insIcon=NULL; const char** insIcon=NULL;
if (ins->type>=DIV_INS_MAX) { if (ins->type>=DIV_INS_MAX) {

View file

@ -65,6 +65,10 @@ void FurnaceGUI::doAction(int what) {
case GUI_ACTION_SAVE_AS: case GUI_ACTION_SAVE_AS:
openFileDialog(GUI_FILE_SAVE); openFileDialog(GUI_FILE_SAVE);
break; break;
case GUI_ACTION_EXPORT:
curExportType=GUI_EXPORT_NONE;
displayExport=true;
break;
case GUI_ACTION_UNDO: case GUI_ACTION_UNDO:
if (curWindow==GUI_WINDOW_SAMPLE_EDIT) { if (curWindow==GUI_WINDOW_SAMPLE_EDIT) {
doUndoSample(); doUndoSample();

View file

@ -519,24 +519,8 @@ void FurnaceGUI::drawMobileControls() {
openFileDialog(GUI_FILE_SAVE_DMF_LEGACY); openFileDialog(GUI_FILE_SAVE_DMF_LEGACY);
} }
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Button("Export Audio")) { if (ImGui::Button("Export")) {
openFileDialog(GUI_FILE_EXPORT_AUDIO_ONE); doAction(GUI_ACTION_EXPORT);
}
ImGui::SameLine();
if (ImGui::Button("Export VGM")) {
openFileDialog(GUI_FILE_EXPORT_VGM);
}
if (ImGui::Button("CmdStream")) {
openFileDialog(GUI_FILE_EXPORT_CMDSTREAM_BINARY);
}
ImGui::SameLine();
if (ImGui::Button("CmdStream Text")) {
openFileDialog(GUI_FILE_EXPORT_CMDSTREAM);
}
ImGui::SameLine();
if (ImGui::Button("Text")) {
openFileDialog(GUI_FILE_EXPORT_TEXT);
} }
if (ImGui::Button("Restore Backup")) { if (ImGui::Button("Restore Backup")) {

View file

@ -771,10 +771,10 @@ unsigned int convertEffectMPT_S3M(unsigned char symbol, unsigned int val) {
return (0x80<<8)|((val&0xf)<<4); return (0x80<<8)|((val&0xf)<<4);
break; break;
case 0xC: case 0xC:
return (0xFC<<8)|(val&0xf); return (0xEC<<8)|(val&0xf);
break; break;
case 0xD: case 0xD:
return (0xFD<<8)|(val&0xf); return (0xED<<8)|(val&0xf);
break; break;
default: default:
break; break;
@ -1117,7 +1117,7 @@ void FurnaceGUI::doPasteMPT(PasteMode mode, int arg, bool readClipboard, String
if (invalidData) if (invalidData)
{ {
logW("invalid OpenMPT clipboard data! failed at line %d char %d",i,charPos); logW("invalid clipboard data! failed at line %d char %d",i,charPos);
logW("%s",line.c_str()); logW("%s",line.c_str());
break; break;
} }

357
src/gui/exportOptions.cpp Normal file
View file

@ -0,0 +1,357 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "gui.h"
#include "guiConst.h"
#include "../fileutils.h"
#include "misc/cpp/imgui_stdlib.h"
#include <imgui.h>
void FurnaceGUI::drawExportAudio(bool onWindow) {
exitDisabledTimer=1;
ImGui::RadioButton("one file",&audioExportType,0);
ImGui::RadioButton("multiple files (one per chip)",&audioExportType,1);
ImGui::RadioButton("multiple files (one per channel)",&audioExportType,2);
if (ImGui::InputInt("Loops",&exportLoops,1,2)) {
if (exportLoops<0) exportLoops=0;
}
if (ImGui::InputDouble("Fade out (seconds)",&exportFadeOut,1.0,2.0,"%.1f")) {
if (exportFadeOut<0.0) exportFadeOut=0.0;
}
if (onWindow) {
ImGui::Separator();
if (ImGui::Button("Cancel",ImVec2(200.0f*dpiScale,0))) ImGui::CloseCurrentPopup();
ImGui::SameLine();
}
if (ImGui::Button("Export",ImVec2(200.0f*dpiScale,0))) {
switch (audioExportType) {
case 0:
openFileDialog(GUI_FILE_EXPORT_AUDIO_ONE);
break;
case 1:
openFileDialog(GUI_FILE_EXPORT_AUDIO_PER_SYS);
break;
case 2:
openFileDialog(GUI_FILE_EXPORT_AUDIO_PER_CHANNEL);
break;
}
ImGui::CloseCurrentPopup();
}
}
void FurnaceGUI::drawExportVGM(bool onWindow) {
exitDisabledTimer=1;
ImGui::Text("settings:");
if (ImGui::BeginCombo("format version",fmt::sprintf("%d.%.2x",vgmExportVersion>>8,vgmExportVersion&0xff).c_str())) {
for (int i=0; i<7; i++) {
if (ImGui::Selectable(fmt::sprintf("%d.%.2x",vgmVersions[i]>>8,vgmVersions[i]&0xff).c_str(),vgmExportVersion==vgmVersions[i])) {
vgmExportVersion=vgmVersions[i];
}
}
ImGui::EndCombo();
}
ImGui::Checkbox("loop",&vgmExportLoop);
if (vgmExportLoop && e->song.loopModality==2) {
ImGui::Text("loop trail:");
ImGui::Indent();
if (ImGui::RadioButton("auto-detect",vgmExportTrailingTicks==-1)) {
vgmExportTrailingTicks=-1;
}
if (ImGui::RadioButton("add one loop",vgmExportTrailingTicks==-2)) {
vgmExportTrailingTicks=-2;
}
if (ImGui::RadioButton("custom",vgmExportTrailingTicks>=0)) {
vgmExportTrailingTicks=0;
}
if (vgmExportTrailingTicks>=0) {
ImGui::SameLine();
if (ImGui::InputInt("##TrailTicks",&vgmExportTrailingTicks,1,100)) {
if (vgmExportTrailingTicks<0) vgmExportTrailingTicks=0;
}
}
ImGui::Unindent();
}
ImGui::Checkbox("add pattern change hints",&vgmExportPatternHints);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip(
"inserts data blocks on pattern changes.\n"
"useful if you are writing a playback routine.\n\n"
"the format of a pattern change data block is:\n"
"67 66 FE ll ll ll ll 01 oo rr pp pp pp ...\n"
"- ll: length, a 32-bit little-endian number\n"
"- oo: order\n"
"- rr: initial row (a 0Dxx effect is able to select a different row)\n"
"- pp: pattern index (one per channel)\n\n"
"pattern indexes are ordered as they appear in the song."
);
}
ImGui::Checkbox("direct stream mode",&vgmExportDirectStream);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip(
"required for DualPCM and MSM6258 export.\n\n"
"allows for volume/direction changes when playing samples,\n"
"at the cost of a massive increase in file size."
);
}
ImGui::Text("chips to export:");
bool hasOneAtLeast=false;
for (int i=0; i<e->song.systemLen; i++) {
int minVersion=e->minVGMVersion(e->song.system[i]);
ImGui::BeginDisabled(minVersion>vgmExportVersion || minVersion==0);
ImGui::Checkbox(fmt::sprintf("%d. %s##_SYSV%d",i+1,getSystemName(e->song.system[i]),i).c_str(),&willExport[i]);
ImGui::EndDisabled();
if (minVersion>vgmExportVersion) {
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
ImGui::SetTooltip("this chip is only available in VGM %d.%.2x and higher!",minVersion>>8,minVersion&0xff);
}
} else if (minVersion==0) {
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
ImGui::SetTooltip("this chip is not supported by the VGM format!");
}
} else {
if (willExport[i]) hasOneAtLeast=true;
}
}
ImGui::Text("select the chip you wish to export, but only up to %d of each type.",(vgmExportVersion>=0x151)?2:1);
if (hasOneAtLeast) {
if (onWindow) {
ImGui::Separator();
if (ImGui::Button("Cancel",ImVec2(200.0f*dpiScale,0))) ImGui::CloseCurrentPopup();
ImGui::SameLine();
}
if (ImGui::Button("Export",ImVec2(200.0f*dpiScale,0))) {
openFileDialog(GUI_FILE_EXPORT_VGM);
ImGui::CloseCurrentPopup();
}
} else {
ImGui::Text("nothing to export");
if (onWindow) {
ImGui::Separator();
if (ImGui::Button("Cancel",ImVec2(400.0f*dpiScale,0))) ImGui::CloseCurrentPopup();
}
}
}
void FurnaceGUI::drawExportZSM(bool onWindow) {
exitDisabledTimer=1;
ImGui::Text("Commander X16 Zsound Music File");
if (ImGui::InputInt("Tick Rate (Hz)",&zsmExportTickRate,1,2)) {
if (zsmExportTickRate<1) zsmExportTickRate=1;
if (zsmExportTickRate>44100) zsmExportTickRate=44100;
}
ImGui::Checkbox("loop",&zsmExportLoop);
ImGui::SameLine();
ImGui::Checkbox("optimize size",&zsmExportOptimize);
if (onWindow) {
ImGui::Separator();
if (ImGui::Button("Cancel",ImVec2(200.0f*dpiScale,0))) ImGui::CloseCurrentPopup();
ImGui::SameLine();
}
if (ImGui::Button("Export",ImVec2(200.0f*dpiScale,0))) {
openFileDialog(GUI_FILE_EXPORT_ZSM);
ImGui::CloseCurrentPopup();
}
}
void FurnaceGUI::drawExportAmigaVal(bool onWindow) {
exitDisabledTimer=1;
ImGui::Text(
"this is NOT ROM export! only use for making sure the\n"
"Furnace Amiga emulator is working properly by\n"
"comparing it with real Amiga output."
);
ImGui::AlignTextToFramePadding();
ImGui::Text("Directory");
ImGui::SameLine();
ImGui::InputText("##AVDPath",&workingDirROMExport);
if (onWindow) {
ImGui::Separator();
if (ImGui::Button("Cancel",ImVec2(200.0f*dpiScale,0))) ImGui::CloseCurrentPopup();
ImGui::SameLine();
}
if (ImGui::Button("Bake Data",ImVec2(200.0f*dpiScale,0))) {
std::vector<DivROMExportOutput> out=e->buildROM(DIV_ROM_AMIGA_VALIDATION);
if (workingDirROMExport.size()>0) {
if (workingDirROMExport[workingDirROMExport.size()-1]!=DIR_SEPARATOR) workingDirROMExport+=DIR_SEPARATOR_STR;
}
for (DivROMExportOutput& i: out) {
String path=workingDirROMExport+i.name;
FILE* outFile=ps_fopen(path.c_str(),"wb");
if (outFile!=NULL) {
fwrite(i.data->getFinalBuf(),1,i.data->size(),outFile);
fclose(outFile);
}
i.data->finish();
delete i.data;
}
showError(fmt::sprintf("Done! Baked %d files.",(int)out.size()));
ImGui::CloseCurrentPopup();
}
}
void FurnaceGUI::drawExportText(bool onWindow) {
exitDisabledTimer=1;
ImGui::Text(
"this option exports the song to a text file.\n"
);
if (onWindow) {
ImGui::Separator();
if (ImGui::Button("Cancel",ImVec2(200.0f*dpiScale,0))) ImGui::CloseCurrentPopup();
ImGui::SameLine();
}
if (ImGui::Button("Export",ImVec2(200.0f*dpiScale,0))) {
openFileDialog(GUI_FILE_EXPORT_TEXT);
ImGui::CloseCurrentPopup();
}
}
void FurnaceGUI::drawExportCommand(bool onWindow) {
exitDisabledTimer=1;
ImGui::Text(
"this option exports a text or binary file which\n"
"contains a dump of the internal command stream\n"
"produced when playing the song.\n\n"
"technical/development use only!"
);
if (onWindow) {
ImGui::Separator();
if (ImGui::Button("Cancel",ImVec2(133.3f*dpiScale,0))) ImGui::CloseCurrentPopup();
ImGui::SameLine();
}
if (ImGui::Button("Export (binary)",ImVec2(133.3f*dpiScale,0))) {
openFileDialog(GUI_FILE_EXPORT_CMDSTREAM_BINARY);
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if (ImGui::Button("Export (text)",ImVec2(133.3f*dpiScale,0))) {
openFileDialog(GUI_FILE_EXPORT_CMDSTREAM);
ImGui::CloseCurrentPopup();
}
}
void FurnaceGUI::drawExport() {
if (settings.exportOptionsLayout==1 || curExportType==GUI_EXPORT_NONE) {
if (ImGui::BeginTabBar("ExportTypes")) {
if (ImGui::BeginTabItem("Audio")) {
drawExportAudio(true);
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("VGM")) {
drawExportVGM(true);
ImGui::EndTabItem();
}
int numZSMCompat=0;
for (int i=0; i<e->song.systemLen; i++) {
if ((e->song.system[i]==DIV_SYSTEM_VERA) || (e->song.system[i]==DIV_SYSTEM_YM2151)) numZSMCompat++;
}
if (numZSMCompat>0) {
if (ImGui::BeginTabItem("ZSM")) {
drawExportZSM(true);
ImGui::EndTabItem();
}
}
int numAmiga=0;
for (int i=0; i<e->song.systemLen; i++) {
if (e->song.system[i]==DIV_SYSTEM_AMIGA) numAmiga++;
}
if (numAmiga && settings.iCannotWait) {
if (ImGui::BeginTabItem("Amiga Validation")) {
drawExportAmigaVal(true);
ImGui::EndTabItem();
}
}
if (ImGui::BeginTabItem("Text")) {
drawExportText(true);
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Command Stream")) {
drawExportCommand(true);
ImGui::EndTabItem();
}
ImGui::EndTabBar();
}
} else switch (curExportType) {
case GUI_EXPORT_AUDIO:
drawExportAudio(true);
break;
case GUI_EXPORT_VGM:
drawExportVGM(true);
break;
case GUI_EXPORT_ZSM:
drawExportZSM(true);
break;
case GUI_EXPORT_AMIGA_VAL:
drawExportAmigaVal(true);
break;
case GUI_EXPORT_TEXT:
drawExportText(true);
break;
case GUI_EXPORT_CMD_STREAM:
drawExportCommand(true);
break;
default:
ImGui::Text("congratulations! you've unlocked a secret panel.");
if (ImGui::Button("Toggle hidden systems")) {
settings.hiddenSystems=!settings.hiddenSystems;
ImGui::CloseCurrentPopup();
}
if (ImGui::Button("Toggle all instrument types")) {
settings.displayAllInsTypes=!settings.displayAllInsTypes;
ImGui::CloseCurrentPopup();
}
if (ImGui::Button("Set pitch linearity to Partial")) {
e->song.linearPitch=1;
ImGui::CloseCurrentPopup();
}
if (ImGui::Button("Enable multi-threading settings")) {
settings.showPool=1;
ImGui::CloseCurrentPopup();
}
if (ImGui::Button("Set fat to max")) {
ImGuiStyle& sty=ImGui::GetStyle();
sty.FramePadding=ImVec2(20.0f*dpiScale,20.0f*dpiScale);
sty.ItemSpacing=ImVec2(10.0f*dpiScale,10.0f*dpiScale);
sty.ItemInnerSpacing=ImVec2(10.0f*dpiScale,10.0f*dpiScale);
ImGui::CloseCurrentPopup();
}
if (ImGui::Button("Set muscle and fat to zero")) {
ImGuiStyle& sty=ImGui::GetStyle();
sty.FramePadding=ImVec2(0,0);
sty.ItemSpacing=ImVec2(0,0);
sty.ItemInnerSpacing=ImVec2(0,0);
ImGui::CloseCurrentPopup();
}
if (ImGui::Button("Tell tildearrow this must be a mistake")) {
showError("yeah, it's a bug. write a bug report in the GitHub page and tell me how did you get here.");
ImGui::CloseCurrentPopup();
}
break;
}
}

View file

@ -1216,7 +1216,7 @@ void FurnaceGUI::noteInput(int num, int key, int vol) {
if (latchVol!=-1) { if (latchVol!=-1) {
pat->data[cursor.y][3]=MIN(maxVol,latchVol); pat->data[cursor.y][3]=MIN(maxVol,latchVol);
} else if (vol!=-1) { } else if (vol!=-1) {
pat->data[cursor.y][3]=(vol*maxVol)/127; pat->data[cursor.y][3]=e->mapVelocity(cursor.xCoarse,pow((float)vol/127.0f,midiMap.volExp));
} }
if (latchEffect!=-1) pat->data[cursor.y][4]=latchEffect; if (latchEffect!=-1) pat->data[cursor.y][4]=latchEffect;
if (latchEffectVal!=-1) pat->data[cursor.y][5]=latchEffectVal; if (latchEffectVal!=-1) pat->data[cursor.y][5]=latchEffectVal;
@ -1319,6 +1319,32 @@ void FurnaceGUI::valueInput(int num, bool direct, int target) {
} }
} }
void FurnaceGUI::orderInput(int num) {
if (orderCursor>=0 && orderCursor<e->getTotalChannelCount()) {
prepareUndo(GUI_UNDO_CHANGE_ORDER);
e->lockSave([this,num]() {
if (!curNibble && !settings.pushNibble) e->curOrders->ord[orderCursor][curOrder]=0;
e->curOrders->ord[orderCursor][curOrder]=((e->curOrders->ord[orderCursor][curOrder]<<4)|num);
});
MARK_MODIFIED;
curNibble=!curNibble;
if (orderEditMode==2 || orderEditMode==3) {
if (!curNibble) {
if (orderEditMode==2) {
orderCursor++;
if (orderCursor>=e->getTotalChannelCount()) orderCursor=0;
} else if (orderEditMode==3) {
if (curOrder<e->curSubSong->ordersLen-1) {
setOrder(curOrder+1);
}
}
}
}
e->walkSong(loopOrder,loopRow,loopEnd);
makeUndo(GUI_UNDO_CHANGE_ORDER);
}
}
#define changeLatch(x) \ #define changeLatch(x) \
if (x<0) x=0; \ if (x<0) x=0; \
if (!latchNibble && !settings.pushNibble) x=0; \ if (!latchNibble && !settings.pushNibble) x=0; \
@ -1529,29 +1555,7 @@ void FurnaceGUI::keyDown(SDL_Event& ev) {
auto it=valueKeys.find(ev.key.keysym.sym); auto it=valueKeys.find(ev.key.keysym.sym);
if (it!=valueKeys.cend()) { if (it!=valueKeys.cend()) {
int num=it->second; int num=it->second;
if (orderCursor>=0 && orderCursor<e->getTotalChannelCount()) { orderInput(num);
prepareUndo(GUI_UNDO_CHANGE_ORDER);
e->lockSave([this,num]() {
if (!curNibble && !settings.pushNibble) e->curOrders->ord[orderCursor][curOrder]=0;
e->curOrders->ord[orderCursor][curOrder]=((e->curOrders->ord[orderCursor][curOrder]<<4)|num);
});
MARK_MODIFIED;
curNibble=!curNibble;
if (orderEditMode==2 || orderEditMode==3) {
if (!curNibble) {
if (orderEditMode==2) {
orderCursor++;
if (orderCursor>=e->getTotalChannelCount()) orderCursor=0;
} else if (orderEditMode==3) {
if (curOrder<e->curSubSong->ordersLen-1) {
setOrder(curOrder+1);
}
}
}
}
e->walkSong(loopOrder,loopRow,loopEnd);
makeUndo(GUI_UNDO_CHANGE_ORDER);
}
} }
} }
break; break;
@ -2246,6 +2250,8 @@ int FurnaceGUI::load(String path) {
showWarning(e->getWarnings(),GUI_WARN_GENERIC); showWarning(e->getWarnings(),GUI_WARN_GENERIC);
} }
pushRecentFile(path); pushRecentFile(path);
// walk song
e->walkSong(loopOrder,loopRow,loopEnd);
// do not auto-play a backup // do not auto-play a backup
if (path.find(backupPath)!=0) { if (path.find(backupPath)!=0) {
if (settings.playOnLoad==2 || (settings.playOnLoad==1 && wasPlaying)) { if (settings.playOnLoad==2 || (settings.playOnLoad==1 && wasPlaying)) {
@ -3776,7 +3782,7 @@ bool FurnaceGUI::loop() {
noteInput( noteInput(
msg.data[0]-12, msg.data[0]-12,
0, 0,
midiMap.volInput?((int)(pow((double)msg.data[1]/127.0,midiMap.volExp)*127.0)):-1 midiMap.volInput?msg.data[1]:-1
); );
} }
} else { } else {
@ -3803,7 +3809,7 @@ bool FurnaceGUI::loop() {
} }
break; break;
case TA_MIDI_PROGRAM: case TA_MIDI_PROGRAM:
if (midiMap.programChange) { if (midiMap.programChange && !(midiMap.directChannel && midiMap.directProgram)) {
curIns=msg.data[0]; curIns=msg.data[0];
if (curIns>=(int)e->song.ins.size()) curIns=e->song.ins.size()-1; if (curIns>=(int)e->song.ins.size()) curIns=e->song.ins.size()-1;
wavePreviewInit=true; wavePreviewInit=true;
@ -4099,131 +4105,22 @@ bool FurnaceGUI::loop() {
openFileDialog(GUI_FILE_SAVE_DMF_LEGACY); openFileDialog(GUI_FILE_SAVE_DMF_LEGACY);
} }
ImGui::Separator(); ImGui::Separator();
if (settings.exportOptionsLayout==0) {
if (ImGui::BeginMenu("export audio...")) { if (ImGui::BeginMenu("export audio...")) {
exitDisabledTimer=1; drawExportAudio();
if (ImGui::MenuItem("one file")) {
openFileDialog(GUI_FILE_EXPORT_AUDIO_ONE);
}
if (ImGui::MenuItem("multiple files (one per chip)")) {
openFileDialog(GUI_FILE_EXPORT_AUDIO_PER_SYS);
}
if (ImGui::MenuItem("multiple files (one per channel)")) {
openFileDialog(GUI_FILE_EXPORT_AUDIO_PER_CHANNEL);
}
if (ImGui::InputInt("Loops",&exportLoops,1,2)) {
if (exportLoops<0) exportLoops=0;
}
if (ImGui::InputDouble("Fade out (seconds)",&exportFadeOut,1.0,2.0,"%.1f")) {
if (exportFadeOut<0.0) exportFadeOut=0.0;
}
ImGui::EndMenu(); ImGui::EndMenu();
} }
if (ImGui::BeginMenu("export VGM...")) { if (ImGui::BeginMenu("export VGM...")) {
exitDisabledTimer=1; drawExportVGM();
ImGui::Text("settings:");
if (ImGui::BeginCombo("format version",fmt::sprintf("%d.%.2x",vgmExportVersion>>8,vgmExportVersion&0xff).c_str())) {
for (int i=0; i<7; i++) {
if (ImGui::Selectable(fmt::sprintf("%d.%.2x",vgmVersions[i]>>8,vgmVersions[i]&0xff).c_str(),vgmExportVersion==vgmVersions[i])) {
vgmExportVersion=vgmVersions[i];
}
}
ImGui::EndCombo();
}
ImGui::Checkbox("loop",&vgmExportLoop);
if (vgmExportLoop && e->song.loopModality==2) {
ImGui::Text("loop trail:");
ImGui::Indent();
if (ImGui::RadioButton("auto-detect",vgmExportTrailingTicks==-1)) {
vgmExportTrailingTicks=-1;
}
if (ImGui::RadioButton("add one loop",vgmExportTrailingTicks==-2)) {
vgmExportTrailingTicks=-2;
}
if (ImGui::RadioButton("custom",vgmExportTrailingTicks>=0)) {
vgmExportTrailingTicks=0;
}
if (vgmExportTrailingTicks>=0) {
ImGui::SameLine();
if (ImGui::InputInt("##TrailTicks",&vgmExportTrailingTicks,1,100)) {
if (vgmExportTrailingTicks<0) vgmExportTrailingTicks=0;
}
}
ImGui::Unindent();
}
ImGui::Checkbox("add pattern change hints",&vgmExportPatternHints);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip(
"inserts data blocks on pattern changes.\n"
"useful if you are writing a playback routine.\n\n"
"the format of a pattern change data block is:\n"
"67 66 FE ll ll ll ll 01 oo rr pp pp pp ...\n"
"- ll: length, a 32-bit little-endian number\n"
"- oo: order\n"
"- rr: initial row (a 0Dxx effect is able to select a different row)\n"
"- pp: pattern index (one per channel)\n\n"
"pattern indexes are ordered as they appear in the song."
);
}
ImGui::Checkbox("direct stream mode",&vgmExportDirectStream);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip(
"required for DualPCM and MSM6258 export.\n\n"
"allows for volume/direction changes when playing samples,\n"
"at the cost of a massive increase in file size."
);
}
ImGui::Text("chips to export:");
bool hasOneAtLeast=false;
for (int i=0; i<e->song.systemLen; i++) {
int minVersion=e->minVGMVersion(e->song.system[i]);
ImGui::BeginDisabled(minVersion>vgmExportVersion || minVersion==0);
ImGui::Checkbox(fmt::sprintf("%d. %s##_SYSV%d",i+1,getSystemName(e->song.system[i]),i).c_str(),&willExport[i]);
ImGui::EndDisabled();
if (minVersion>vgmExportVersion) {
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
ImGui::SetTooltip("this chip is only available in VGM %d.%.2x and higher!",minVersion>>8,minVersion&0xff);
}
} else if (minVersion==0) {
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
ImGui::SetTooltip("this chip is not supported by the VGM format!");
}
} else {
if (willExport[i]) hasOneAtLeast=true;
}
}
ImGui::Text("select the chip you wish to export,");
ImGui::Text("but only up to %d of each type.",(vgmExportVersion>=0x151)?2:1);
if (hasOneAtLeast) {
if (ImGui::MenuItem("click to export")) {
openFileDialog(GUI_FILE_EXPORT_VGM);
}
} else {
ImGui::Text("nothing to export");
}
ImGui::EndMenu(); ImGui::EndMenu();
} }
int numZSMCompat=0; int numZSMCompat=0;
for (int i=0; i<e->song.systemLen; i++) { for (int i=0; i<e->song.systemLen; i++) {
if ((e->song.system[i] == DIV_SYSTEM_VERA) || (e->song.system[i] == DIV_SYSTEM_YM2151)) numZSMCompat++; if ((e->song.system[i]==DIV_SYSTEM_VERA) || (e->song.system[i]==DIV_SYSTEM_YM2151)) numZSMCompat++;
} }
if (numZSMCompat > 0) { if (numZSMCompat>0) {
if (ImGui::BeginMenu("export ZSM...")) { if (ImGui::BeginMenu("export ZSM...")) {
exitDisabledTimer=1; drawExportZSM();
ImGui::Text("Commander X16 Zsound Music File");
if (ImGui::InputInt("Tick Rate (Hz)",&zsmExportTickRate,1,10)) {
if (zsmExportTickRate<1) zsmExportTickRate=1;
if (zsmExportTickRate>44100) zsmExportTickRate=44100;
}
ImGui::Checkbox("loop",&zsmExportLoop);
ImGui::SameLine();
ImGui::Checkbox("optimize size",&zsmExportOptimize);
ImGui::SameLine();
if (ImGui::Button("Begin Export")) {
openFileDialog(GUI_FILE_EXPORT_ZSM);
ImGui::CloseCurrentPopup();
}
ImGui::EndMenu(); ImGui::EndMenu();
} }
} }
@ -4233,64 +4130,60 @@ bool FurnaceGUI::loop() {
} }
if (numAmiga && settings.iCannotWait) { if (numAmiga && settings.iCannotWait) {
if (ImGui::BeginMenu("export Amiga validation data...")) { if (ImGui::BeginMenu("export Amiga validation data...")) {
exitDisabledTimer=1; drawExportAmigaVal();
ImGui::Text(
"this is NOT ROM export! only use for making sure the\n"
"Furnace Amiga emulator is working properly by\n"
"comparing it with real Amiga output."
);
ImGui::AlignTextToFramePadding();
ImGui::Text("Directory");
ImGui::SameLine();
ImGui::InputText("##AVDPath",&workingDirROMExport);
if (ImGui::Button("Bake Data")) {
std::vector<DivROMExportOutput> out=e->buildROM(DIV_ROM_AMIGA_VALIDATION);
if (workingDirROMExport.size()>0) {
if (workingDirROMExport[workingDirROMExport.size()-1]!=DIR_SEPARATOR) workingDirROMExport+=DIR_SEPARATOR_STR;
}
for (DivROMExportOutput& i: out) {
String path=workingDirROMExport+i.name;
FILE* outFile=ps_fopen(path.c_str(),"wb");
if (outFile!=NULL) {
fwrite(i.data->getFinalBuf(),1,i.data->size(),outFile);
fclose(outFile);
}
i.data->finish();
delete i.data;
}
showError(fmt::sprintf("Done! Baked %d files.",(int)out.size()));
ImGui::CloseCurrentPopup();
}
ImGui::EndMenu(); ImGui::EndMenu();
} }
} }
if (ImGui::BeginMenu("export text...")) { if (ImGui::BeginMenu("export text...")) {
exitDisabledTimer=1; drawExportText();
ImGui::Text(
"this option exports the song to a text file.\n"
);
if (ImGui::Button("export")) {
openFileDialog(GUI_FILE_EXPORT_TEXT);
}
ImGui::EndMenu(); ImGui::EndMenu();
} }
if (ImGui::BeginMenu("export command stream...")) { if (ImGui::BeginMenu("export command stream...")) {
exitDisabledTimer=1; drawExportCommand();
ImGui::Text(
"this option exports a text or binary file which\n"
"contains a dump of the internal command stream\n"
"produced when playing the song.\n\n"
"technical/development use only!"
);
if (ImGui::Button("export (binary)")) {
openFileDialog(GUI_FILE_EXPORT_CMDSTREAM_BINARY);
}
if (ImGui::Button("export (text)")) {
openFileDialog(GUI_FILE_EXPORT_CMDSTREAM);
}
ImGui::EndMenu(); ImGui::EndMenu();
} }
} else if (settings.exportOptionsLayout==2) {
if (ImGui::MenuItem("export audio...")) {
curExportType=GUI_EXPORT_AUDIO;
displayExport=true;
}
if (ImGui::MenuItem("export VGM...")) {
curExportType=GUI_EXPORT_VGM;
displayExport=true;
}
int numZSMCompat=0;
for (int i=0; i<e->song.systemLen; i++) {
if ((e->song.system[i]==DIV_SYSTEM_VERA) || (e->song.system[i]==DIV_SYSTEM_YM2151)) numZSMCompat++;
}
if (numZSMCompat>0) {
if (ImGui::MenuItem("export ZSM...")) {
curExportType=GUI_EXPORT_ZSM;
displayExport=true;
}
}
int numAmiga=0;
for (int i=0; i<e->song.systemLen; i++) {
if (e->song.system[i]==DIV_SYSTEM_AMIGA) numAmiga++;
}
if (numAmiga && settings.iCannotWait) {
if (ImGui::MenuItem("export Amiga validation data...")) {
curExportType=GUI_EXPORT_AMIGA_VAL;
displayExport=true;
}
}
if (ImGui::MenuItem("export text...")) {
curExportType=GUI_EXPORT_TEXT;
displayExport=true;
}
if (ImGui::MenuItem("export command stream...")) {
curExportType=GUI_EXPORT_CMD_STREAM;
displayExport=true;
}
} else {
if (ImGui::MenuItem("export...",BIND_FOR(GUI_ACTION_EXPORT))) {
displayExport=true;
}
}
ImGui::Separator(); ImGui::Separator();
if (!settings.classicChipOptions) { if (!settings.classicChipOptions) {
if (ImGui::MenuItem("manage chips")) { if (ImGui::MenuItem("manage chips")) {
@ -4629,6 +4522,7 @@ bool FurnaceGUI::loop() {
ordersOpen=true; ordersOpen=true;
curWindow=GUI_WINDOW_ORDERS; curWindow=GUI_WINDOW_ORDERS;
MEASURE(orders,drawOrders()); MEASURE(orders,drawOrders());
MEASURE(piano,drawPiano());
break; break;
case GUI_SCENE_INSTRUMENT: case GUI_SCENE_INSTRUMENT:
insEditOpen=true; insEditOpen=true;
@ -5485,6 +5379,11 @@ bool FurnaceGUI::loop() {
} }
} }
if (displayExport) {
displayExport=false;
ImGui::OpenPopup("Export");
}
if (displayEditString) { if (displayEditString) {
ImGui::OpenPopup("EditString"); ImGui::OpenPopup("EditString");
} }
@ -5526,6 +5425,12 @@ bool FurnaceGUI::loop() {
ImGui::EndPopup(); ImGui::EndPopup();
} }
if (ImGui::BeginPopupModal("Export",NULL,ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoScrollWithMouse|ImGuiWindowFlags_NoScrollbar|ImGuiWindowFlags_AlwaysAutoResize)) {
ImGui::SetWindowPos(ImVec2(((canvasW)-ImGui::GetWindowSize().x)*0.5,((canvasH)-ImGui::GetWindowSize().y)*0.5));
drawExport();
ImGui::EndPopup();
}
centerNextWindow("Error",canvasW,canvasH); centerNextWindow("Error",canvasW,canvasH);
if (ImGui::BeginPopupModal("Error",NULL,ImGuiWindowFlags_AlwaysAutoResize)) { if (ImGui::BeginPopupModal("Error",NULL,ImGuiWindowFlags_AlwaysAutoResize)) {
ImGui::Text("%s",errorString.c_str()); ImGui::Text("%s",errorString.c_str());
@ -7018,6 +6923,7 @@ bool FurnaceGUI::init() {
return -2; return -2;
} }
if (midiMap.directChannel && midiMap.directProgram) return -1;
return curIns; return curIns;
}); });
@ -7726,7 +7632,9 @@ FurnaceGUI::FurnaceGUI():
introSkipDo(false), introSkipDo(false),
introStopped(false), introStopped(false),
curTutorial(-1), curTutorial(-1),
curTutorialStep(0) { curTutorialStep(0),
audioExportType(0),
curExportType(GUI_EXPORT_NONE) {
// value keys // value keys
valueKeys[SDLK_0]=0; valueKeys[SDLK_0]=0;
valueKeys[SDLK_1]=1; valueKeys[SDLK_1]=1;

View file

@ -101,6 +101,7 @@ enum FurnaceGUIColors {
GUI_COLOR_MODAL_BACKDROP, GUI_COLOR_MODAL_BACKDROP,
GUI_COLOR_HEADER, GUI_COLOR_HEADER,
GUI_COLOR_TEXT, GUI_COLOR_TEXT,
GUI_COLOR_TEXT_DISABLED,
GUI_COLOR_ACCENT_PRIMARY, GUI_COLOR_ACCENT_PRIMARY,
GUI_COLOR_ACCENT_SECONDARY, GUI_COLOR_ACCENT_SECONDARY,
GUI_COLOR_TITLE_INACTIVE, GUI_COLOR_TITLE_INACTIVE,
@ -124,6 +125,36 @@ enum FurnaceGUIColors {
GUI_COLOR_NAV_HIGHLIGHT, GUI_COLOR_NAV_HIGHLIGHT,
GUI_COLOR_NAV_WIN_HIGHLIGHT, GUI_COLOR_NAV_WIN_HIGHLIGHT,
GUI_COLOR_NAV_WIN_BACKDROP, GUI_COLOR_NAV_WIN_BACKDROP,
GUI_COLOR_PLOT_LINES,
GUI_COLOR_PLOT_LINES_HOVER,
GUI_COLOR_PLOT_HISTOGRAM,
GUI_COLOR_PLOT_HISTOGRAM_HOVER,
GUI_COLOR_BUTTON,
GUI_COLOR_BUTTON_HOVER,
GUI_COLOR_BUTTON_ACTIVE,
GUI_COLOR_TAB,
GUI_COLOR_TAB_HOVER,
GUI_COLOR_TAB_ACTIVE,
GUI_COLOR_TAB_UNFOCUSED,
GUI_COLOR_TAB_UNFOCUSED_ACTIVE,
GUI_COLOR_IMGUI_HEADER,
GUI_COLOR_IMGUI_HEADER_HOVER,
GUI_COLOR_IMGUI_HEADER_ACTIVE,
GUI_COLOR_RESIZE_GRIP,
GUI_COLOR_RESIZE_GRIP_HOVER,
GUI_COLOR_RESIZE_GRIP_ACTIVE,
GUI_COLOR_WIDGET_BACKGROUND,
GUI_COLOR_WIDGET_BACKGROUND_HOVER,
GUI_COLOR_WIDGET_BACKGROUND_ACTIVE,
GUI_COLOR_SLIDER_GRAB,
GUI_COLOR_SLIDER_GRAB_ACTIVE,
GUI_COLOR_TITLE_BACKGROUND_ACTIVE,
GUI_COLOR_CHECK_MARK,
GUI_COLOR_TEXT_SELECTION,
GUI_COLOR_TABLE_ROW_EVEN,
GUI_COLOR_TABLE_ROW_ODD,
GUI_COLOR_TOGGLE_OFF, GUI_COLOR_TOGGLE_OFF,
GUI_COLOR_TOGGLE_ON, GUI_COLOR_TOGGLE_ON,
GUI_COLOR_EDITING, GUI_COLOR_EDITING,
@ -438,6 +469,21 @@ enum FurnaceGUIMobileScenes {
GUI_SCENE_OTHER, GUI_SCENE_OTHER,
}; };
enum FurnaceGUISettingGroups: unsigned int {
GUI_SETTINGS_GENERAL=1,
GUI_SETTINGS_AUDIO=2,
GUI_SETTINGS_MIDI=4,
GUI_SETTINGS_KEYBOARD=8,
GUI_SETTINGS_BEHAVIOR=16,
GUI_SETTINGS_FONT=32,
GUI_SETTINGS_APPEARANCE=64,
GUI_SETTINGS_LAYOUTS=128,
GUI_SETTINGS_COLOR=256,
GUI_SETTINGS_EMULATION=512,
GUI_SETTINGS_ALL=0xffffffff
};
enum FurnaceGUIFileDialogs { enum FurnaceGUIFileDialogs {
GUI_FILE_OPEN, GUI_FILE_OPEN,
GUI_FILE_OPEN_BACKUP, GUI_FILE_OPEN_BACKUP,
@ -505,6 +551,17 @@ enum FurnaceGUIWarnings {
GUI_WARN_GENERIC GUI_WARN_GENERIC
}; };
enum FurnaceGUIExportTypes {
GUI_EXPORT_NONE=-1,
GUI_EXPORT_AUDIO=0,
GUI_EXPORT_VGM,
GUI_EXPORT_ZSM,
GUI_EXPORT_CMD_STREAM,
GUI_EXPORT_AMIGA_VAL,
GUI_EXPORT_TEXT
};
enum FurnaceGUIFMAlgs { enum FurnaceGUIFMAlgs {
FM_ALGS_4OP, FM_ALGS_4OP,
FM_ALGS_2OP_OPL, FM_ALGS_2OP_OPL,
@ -518,6 +575,7 @@ enum FurnaceGUIActions {
GUI_ACTION_OPEN_BACKUP, GUI_ACTION_OPEN_BACKUP,
GUI_ACTION_SAVE, GUI_ACTION_SAVE,
GUI_ACTION_SAVE_AS, GUI_ACTION_SAVE_AS,
GUI_ACTION_EXPORT,
GUI_ACTION_UNDO, GUI_ACTION_UNDO,
GUI_ACTION_REDO, GUI_ACTION_REDO,
GUI_ACTION_PLAY_TOGGLE, GUI_ACTION_PLAY_TOGGLE,
@ -828,6 +886,14 @@ struct SelectionPoint {
xCoarse(0), xFine(0), y(0) {} xCoarse(0), xFine(0), y(0) {}
}; };
struct UndoRegion {
struct UndoRegionPoint {
int ord, x, y;
UndoRegionPoint():
ord(0), x(0), y(0) {}
} begin, end;
};
enum ActionType { enum ActionType {
GUI_UNDO_CHANGE_ORDER, GUI_UNDO_CHANGE_ORDER,
GUI_UNDO_PATTERN_EDIT, GUI_UNDO_PATTERN_EDIT,
@ -937,7 +1003,7 @@ struct MIDIMap {
int**** map; int**** map;
std::vector<MIDIBind> binds; std::vector<MIDIBind> binds;
bool noteInput, volInput, rawVolume, polyInput, directChannel, programChange, midiClock, midiTimeCode, yamahaFMResponse; bool noteInput, volInput, rawVolume, polyInput, directChannel, programChange, midiClock, midiTimeCode, yamahaFMResponse, directProgram;
// 0: disabled // 0: disabled
// //
// 1: C- C# D- D# E- F- F# G- G# A- A# B- // 1: C- C# D- D# E- F- F# G- G# A- A# B-
@ -1000,6 +1066,7 @@ struct MIDIMap {
midiClock(false), midiClock(false),
midiTimeCode(false), midiTimeCode(false),
yamahaFMResponse(false), yamahaFMResponse(false),
directProgram(false),
valueInputStyle(1), valueInputStyle(1),
valueInputControlMSB(0), valueInputControlMSB(0),
valueInputControlLSB(0), valueInputControlLSB(0),
@ -1388,7 +1455,7 @@ class FurnaceGUI {
bool vgmExportDirectStream, displayInsTypeList, displayWaveSizeList; bool vgmExportDirectStream, displayInsTypeList, displayWaveSizeList;
bool portrait, injectBackUp, mobileMenuOpen, warnColorPushed; bool portrait, injectBackUp, mobileMenuOpen, warnColorPushed;
bool wantCaptureKeyboard, oldWantCaptureKeyboard, displayMacroMenu; bool wantCaptureKeyboard, oldWantCaptureKeyboard, displayMacroMenu;
bool displayNew, fullScreen, preserveChanPos, wantScrollList, noteInputPoly, notifyWaveChange; bool displayNew, displayExport, fullScreen, preserveChanPos, wantScrollList, noteInputPoly, notifyWaveChange;
bool displayPendingIns, pendingInsSingle, displayPendingRawSample, snesFilterHex, modTableHex, displayEditString; bool displayPendingIns, pendingInsSingle, displayPendingRawSample, snesFilterHex, modTableHex, displayEditString;
bool mobileEdit; bool mobileEdit;
bool killGraphics; bool killGraphics;
@ -1644,6 +1711,7 @@ class FurnaceGUI {
int centerPopup; int centerPopup;
int insIconsStyle; int insIconsStyle;
int classicChipOptions; int classicChipOptions;
int exportOptionsLayout;
int wasapiEx; int wasapiEx;
int chanOscThreads; int chanOscThreads;
int renderPoolThreads; int renderPoolThreads;
@ -1656,6 +1724,7 @@ class FurnaceGUI {
int fontAutoHint; int fontAutoHint;
int fontAntiAlias; int fontAntiAlias;
int selectAssetOnLoad; int selectAssetOnLoad;
int basicColors;
unsigned int maxUndoSteps; unsigned int maxUndoSteps;
String mainFontPath; String mainFontPath;
String headFontPath; String headFontPath;
@ -1839,11 +1908,12 @@ class FurnaceGUI {
centerPopup(1), centerPopup(1),
insIconsStyle(1), insIconsStyle(1),
classicChipOptions(0), classicChipOptions(0),
exportOptionsLayout(1),
wasapiEx(0), wasapiEx(0),
chanOscThreads(0), chanOscThreads(0),
renderPoolThreads(0), renderPoolThreads(0),
showPool(0), showPool(0),
writeInsNames(1), writeInsNames(0),
readInsNames(1), readInsNames(1),
fontBackend(1), fontBackend(1),
fontHinting(0), fontHinting(0),
@ -1851,6 +1921,7 @@ class FurnaceGUI {
fontAutoHint(1), fontAutoHint(1),
fontAntiAlias(1), fontAntiAlias(1),
selectAssetOnLoad(1), selectAssetOnLoad(1),
basicColors(1),
maxUndoSteps(100), maxUndoSteps(100),
mainFontPath(""), mainFontPath(""),
headFontPath(""), headFontPath(""),
@ -2245,7 +2316,7 @@ class FurnaceGUI {
int pianoOffset, pianoOffsetEdit; int pianoOffset, pianoOffsetEdit;
int pianoView, pianoInputPadMode; int pianoView, pianoInputPadMode;
//effect sorting // effect sorting
bool effectsShow[10]; bool effectsShow[10];
// TX81Z // TX81Z
@ -2285,6 +2356,17 @@ class FurnaceGUI {
// tutorial // tutorial
int curTutorial, curTutorialStep; int curTutorial, curTutorialStep;
// export options
int audioExportType;
FurnaceGUIExportTypes curExportType;
void drawExportAudio(bool onWindow=false);
void drawExportVGM(bool onWindow=false);
void drawExportZSM(bool onWindow=false);
void drawExportAmigaVal(bool onWindow=false);
void drawExportText(bool onWindow=false);
void drawExportCommand(bool onWindow=false);
void drawSSGEnv(unsigned char type, const ImVec2& size); void drawSSGEnv(unsigned char type, const ImVec2& size);
void drawWaveform(unsigned char type, bool opz, const ImVec2& size); void drawWaveform(unsigned char type, bool opz, const ImVec2& size);
void drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, const ImVec2& size); void drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, const ImVec2& size);
@ -2398,6 +2480,7 @@ class FurnaceGUI {
void drawSettings(); void drawSettings();
void drawDebug(); void drawDebug();
void drawNewSong(); void drawNewSong();
void drawExport();
void drawLog(); void drawLog();
void drawEffectList(); void drawEffectList();
void drawSubSongs(bool asChild=false); void drawSubSongs(bool asChild=false);
@ -2423,6 +2506,9 @@ class FurnaceGUI {
void resetColors(); void resetColors();
void resetKeybinds(); void resetKeybinds();
void readConfig(DivConfig& conf, FurnaceGUISettingGroups groups=GUI_SETTINGS_ALL);
void writeConfig(DivConfig& conf, FurnaceGUISettingGroups groups=GUI_SETTINGS_ALL);
void syncSettings(); void syncSettings();
void commitSettings(); void commitSettings();
void syncTutorial(); void syncTutorial();
@ -2473,6 +2559,7 @@ class FurnaceGUI {
DivSystem systemPicker(); DivSystem systemPicker();
void noteInput(int num, int key, int vol=-1); void noteInput(int num, int key, int vol=-1);
void valueInput(int num, bool direct=false, int target=-1); void valueInput(int num, bool direct=false, int target=-1);
void orderInput(int num);
void doGenerateWave(); void doGenerateWave();

View file

@ -538,6 +538,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={
D("OPEN_BACKUP", "Restore backup", 0), D("OPEN_BACKUP", "Restore backup", 0),
D("SAVE", "Save file", FURKMOD_CMD|SDLK_s), D("SAVE", "Save file", FURKMOD_CMD|SDLK_s),
D("SAVE_AS", "Save as", FURKMOD_CMD|FURKMOD_SHIFT|SDLK_s), D("SAVE_AS", "Save as", FURKMOD_CMD|FURKMOD_SHIFT|SDLK_s),
D("EXPORT", "Export", 0),
D("UNDO", "Undo", FURKMOD_CMD|SDLK_z), D("UNDO", "Undo", FURKMOD_CMD|SDLK_z),
#ifdef __APPLE__ #ifdef __APPLE__
D("REDO", "Redo", FURKMOD_CMD|FURKMOD_SHIFT|SDLK_z), D("REDO", "Redo", FURKMOD_CMD|FURKMOD_SHIFT|SDLK_z),
@ -795,6 +796,7 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={
D(GUI_COLOR_MODAL_BACKDROP,"",ImVec4(0.0f,0.0f,0.0f,0.55f)), D(GUI_COLOR_MODAL_BACKDROP,"",ImVec4(0.0f,0.0f,0.0f,0.55f)),
D(GUI_COLOR_HEADER,"",ImVec4(0.2f,0.2f,0.2f,1.0f)), D(GUI_COLOR_HEADER,"",ImVec4(0.2f,0.2f,0.2f,1.0f)),
D(GUI_COLOR_TEXT,"",ImVec4(1.0f,1.0f,1.0f,1.0f)), D(GUI_COLOR_TEXT,"",ImVec4(1.0f,1.0f,1.0f,1.0f)),
D(GUI_COLOR_TEXT_DISABLED,"",ImVec4(0.5f,0.5f,0.5f,1.0f)),
D(GUI_COLOR_ACCENT_PRIMARY,"",ImVec4(0.06f,0.53f,0.98f,1.0f)), D(GUI_COLOR_ACCENT_PRIMARY,"",ImVec4(0.06f,0.53f,0.98f,1.0f)),
D(GUI_COLOR_ACCENT_SECONDARY,"",ImVec4(0.26f,0.59f,0.98f,1.0f)), D(GUI_COLOR_ACCENT_SECONDARY,"",ImVec4(0.26f,0.59f,0.98f,1.0f)),
D(GUI_COLOR_TITLE_INACTIVE,"",ImVec4(0.04f,0.04f,0.04f,1.0f)), D(GUI_COLOR_TITLE_INACTIVE,"",ImVec4(0.04f,0.04f,0.04f,1.0f)),
@ -818,6 +820,36 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={
D(GUI_COLOR_NAV_HIGHLIGHT,"",ImVec4(0.26f,0.59f,0.98f,1.0f)), D(GUI_COLOR_NAV_HIGHLIGHT,"",ImVec4(0.26f,0.59f,0.98f,1.0f)),
D(GUI_COLOR_NAV_WIN_HIGHLIGHT,"",ImVec4(1.0f,1.0f,1.0f,0.7f)), D(GUI_COLOR_NAV_WIN_HIGHLIGHT,"",ImVec4(1.0f,1.0f,1.0f,0.7f)),
D(GUI_COLOR_NAV_WIN_BACKDROP,"",ImVec4(0.8f,0.8f,0.8f,0.2f)), D(GUI_COLOR_NAV_WIN_BACKDROP,"",ImVec4(0.8f,0.8f,0.8f,0.2f)),
D(GUI_COLOR_PLOT_LINES,"",ImVec4(0.61f,0.61f,0.61f,1.0f)),
D(GUI_COLOR_PLOT_LINES_HOVER,"",ImVec4(1.00f,0.43f,0.35f,1.00f)),
D(GUI_COLOR_PLOT_HISTOGRAM,"",ImVec4(0.0f,0.9f,1.0f,1.0f)),
D(GUI_COLOR_PLOT_HISTOGRAM_HOVER,"",ImVec4(0.0f,0.9f,1.0f,1.0f)),
D(GUI_COLOR_BUTTON,"",ImVec4(0.085f,0.216f,0.343f,1.0f)),
D(GUI_COLOR_BUTTON_HOVER,"",ImVec4(0.075f,0.287f,0.49f,1.0f)),
D(GUI_COLOR_BUTTON_ACTIVE,"",ImVec4(0.06f,0.53f,0.98f,1.0f)),
D(GUI_COLOR_TAB,"",ImVec4(0.085f,0.216f,0.343f,1.0f)),
D(GUI_COLOR_TAB_HOVER,"",ImVec4(0.165f,0.313f,0.49f,1.0f)),
D(GUI_COLOR_TAB_ACTIVE,"",ImVec4(0.25f,0.47f,0.735f,1.0f)),
D(GUI_COLOR_TAB_UNFOCUSED,"",ImVec4(0.085f,0.216f,0.343f,1.0f)),
D(GUI_COLOR_TAB_UNFOCUSED_ACTIVE,"",ImVec4(0.075f,0.287f,0.49f,1.0f)),
D(GUI_COLOR_IMGUI_HEADER,"",ImVec4(0.083f,0.156f,0.245f,1.0f)),
D(GUI_COLOR_IMGUI_HEADER_HOVER,"",ImVec4(0.165f,0.313f,0.49f,1.0f)),
D(GUI_COLOR_IMGUI_HEADER_ACTIVE,"",ImVec4(0.26f,0.59f,0.98f,1.0f)),
D(GUI_COLOR_RESIZE_GRIP,"",ImVec4(0.083f,0.156f,0.245f,1.0f)),
D(GUI_COLOR_RESIZE_GRIP_HOVER,"",ImVec4(0.165f,0.313f,0.49f,1.0f)),
D(GUI_COLOR_RESIZE_GRIP_ACTIVE,"",ImVec4(0.26f,0.59f,0.98f,1.0f)),
D(GUI_COLOR_WIDGET_BACKGROUND,"",ImVec4(0.083f,0.156f,0.245f,1.0f)),
D(GUI_COLOR_WIDGET_BACKGROUND_HOVER,"",ImVec4(0.165f,0.313f,0.49f,1.0f)),
D(GUI_COLOR_WIDGET_BACKGROUND_ACTIVE,"",ImVec4(0.26f,0.59f,0.98f,1.0f)),
D(GUI_COLOR_SLIDER_GRAB,"",ImVec4(0.06f,0.53f,0.98f,1.0f)),
D(GUI_COLOR_SLIDER_GRAB_ACTIVE,"",ImVec4(0.06f,0.53f,0.98f,1.0f)),
D(GUI_COLOR_TITLE_BACKGROUND_ACTIVE,"",ImVec4(0.085f,0.216f,0.343f,1.0f)),
D(GUI_COLOR_CHECK_MARK,"",ImVec4(0.06f,0.53f,0.98f,1.0f)),
D(GUI_COLOR_TEXT_SELECTION,"",ImVec4(0.165f,0.313f,0.49f,1.0f)),
D(GUI_COLOR_TABLE_ROW_EVEN,"",ImVec4(0.0f,0.0f,0.0f,0.0f)),
D(GUI_COLOR_TABLE_ROW_ODD,"",ImVec4(1.0f,1.0f,1.0f,0.06f)),
D(GUI_COLOR_TOGGLE_OFF,"",ImVec4(0.2f,0.2f,0.2f,1.0f)), D(GUI_COLOR_TOGGLE_OFF,"",ImVec4(0.2f,0.2f,0.2f,1.0f)),
D(GUI_COLOR_TOGGLE_ON,"",ImVec4(0.2f,0.6f,0.2f,1.0f)), D(GUI_COLOR_TOGGLE_ON,"",ImVec4(0.2f,0.6f,0.2f,1.0f)),
D(GUI_COLOR_EDITING,"",ImVec4(0.2f,0.1f,0.1f,1.0f)), D(GUI_COLOR_EDITING,"",ImVec4(0.2f,0.1f,0.1f,1.0f)),

View file

@ -2967,8 +2967,12 @@ void FurnaceGUI::drawInsEdit() {
updateFMPreview=false; updateFMPreview=false;
} }
if (settings.insEditColorize) { if (settings.insEditColorize) {
if (ins->type>=DIV_INS_MAX) {
pushAccentColors(uiColors[GUI_COLOR_INSTR_UNKNOWN],uiColors[GUI_COLOR_INSTR_UNKNOWN],uiColors[GUI_COLOR_INSTR_UNKNOWN],ImVec4(0.0f,0.0f,0.0f,0.0f));
} else {
pushAccentColors(uiColors[GUI_COLOR_INSTR_STD+ins->type],uiColors[GUI_COLOR_INSTR_STD+ins->type],uiColors[GUI_COLOR_INSTR_STD+ins->type],ImVec4(0.0f,0.0f,0.0f,0.0f)); pushAccentColors(uiColors[GUI_COLOR_INSTR_STD+ins->type],uiColors[GUI_COLOR_INSTR_STD+ins->type],uiColors[GUI_COLOR_INSTR_STD+ins->type],ImVec4(0.0f,0.0f,0.0f,0.0f));
} }
}
if (ImGui::BeginTable("InsProp",3)) { if (ImGui::BeginTable("InsProp",3)) {
ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed); ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthFixed); ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthFixed);
@ -3028,14 +3032,8 @@ void FurnaceGUI::drawInsEdit() {
ImGui::Text("Type"); ImGui::Text("Type");
ImGui::TableNextColumn(); ImGui::TableNextColumn();
if (ins->type>=DIV_INS_MAX) ins->type=DIV_INS_FM;
int insType=ins->type; int insType=ins->type;
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
/*
if (ImGui::Combo("##Type",&insType,insTypes,DIV_INS_MAX,DIV_INS_MAX)) {
ins->type=(DivInstrumentType)insType;
}
*/
bool warnType=true; bool warnType=true;
for (DivInstrumentType i: e->getPossibleInsTypes()) { for (DivInstrumentType i: e->getPossibleInsTypes()) {
if (i==insType) { if (i==insType) {
@ -3044,7 +3042,7 @@ void FurnaceGUI::drawInsEdit() {
} }
pushWarningColor(warnType,warnType && failedNoteOn); pushWarningColor(warnType,warnType && failedNoteOn);
if (ImGui::BeginCombo("##Type",insTypes[insType][0])) { if (ImGui::BeginCombo("##Type",(insType>=DIV_INS_MAX)?"Unknown":insTypes[insType][0])) {
std::vector<DivInstrumentType> insTypeList; std::vector<DivInstrumentType> insTypeList;
if (settings.displayAllInsTypes) { if (settings.displayAllInsTypes) {
for (int i=0; insTypes[i][0]; i++) { for (int i=0; insTypes[i][0]; i++) {
@ -6689,7 +6687,7 @@ void FurnaceGUI::drawInsEdit() {
ImGui::EndTabItem(); ImGui::EndTabItem();
} }
} }
if (ImGui::BeginTabItem("Macros")) { if (ins->type<DIV_INS_MAX) if (ImGui::BeginTabItem("Macros")) {
const char* volumeLabel="Volume"; const char* volumeLabel="Volume";
int volMax=15; int volMax=15;
@ -7247,6 +7245,12 @@ void FurnaceGUI::drawInsEdit() {
ins->type==DIV_INS_VRC6) { ins->type==DIV_INS_VRC6) {
insTabSample(ins); insTabSample(ins);
} }
if (ins->type>=DIV_INS_MAX) {
if (ImGui::BeginTabItem("Error")) {
ImGui::Text("invalid instrument type! change it first.");
ImGui::EndTabItem();
}
}
ImGui::EndTabBar(); ImGui::EndTabBar();
} }
if (settings.insEditColorize) { if (settings.insEditColorize) {

View file

@ -139,6 +139,7 @@ bool MIDIMap::read(String path) {
UNDERSTAND_OPTION(midiClock) else UNDERSTAND_OPTION(midiClock) else
UNDERSTAND_OPTION(midiTimeCode) else UNDERSTAND_OPTION(midiTimeCode) else
UNDERSTAND_OPTION(yamahaFMResponse) else UNDERSTAND_OPTION(yamahaFMResponse) else
UNDERSTAND_OPTION(directProgram) else
UNDERSTAND_OPTION(valueInputStyle) else UNDERSTAND_OPTION(valueInputStyle) else
UNDERSTAND_OPTION(valueInputControlMSB) else UNDERSTAND_OPTION(valueInputControlMSB) else
UNDERSTAND_OPTION(valueInputControlLSB) else UNDERSTAND_OPTION(valueInputControlLSB) else
@ -205,6 +206,7 @@ bool MIDIMap::write(String path) {
WRITE_OPTION(midiClock); WRITE_OPTION(midiClock);
WRITE_OPTION(midiTimeCode); WRITE_OPTION(midiTimeCode);
WRITE_OPTION(yamahaFMResponse); WRITE_OPTION(yamahaFMResponse);
WRITE_OPTION(directProgram);
WRITE_OPTION(valueInputStyle); WRITE_OPTION(valueInputStyle);
WRITE_OPTION(valueInputControlMSB); WRITE_OPTION(valueInputControlMSB);
WRITE_OPTION(valueInputControlLSB); WRITE_OPTION(valueInputControlLSB);

View file

@ -181,6 +181,10 @@ void FurnaceGUI::drawOrderButtons() {
} }
NEXT_BUTTON; NEXT_BUTTON;
if (orderEditMode==0 && mobileUI) {
orderEditMode=1;
}
const char* orderEditModeLabel="?##OrderEditMode"; const char* orderEditModeLabel="?##OrderEditMode";
if (orderEditMode==3) { if (orderEditMode==3) {
orderEditModeLabel=ICON_FA_ARROWS_V "##OrderEditMode"; orderEditModeLabel=ICON_FA_ARROWS_V "##OrderEditMode";
@ -193,7 +197,7 @@ void FurnaceGUI::drawOrderButtons() {
} }
if (ImGui::Button(orderEditModeLabel)) { handleUnimportant if (ImGui::Button(orderEditModeLabel)) { handleUnimportant
orderEditMode++; orderEditMode++;
if (orderEditMode>3) orderEditMode=0; if (orderEditMode>3) orderEditMode=mobileUI?1:0;
curNibble=false; curNibble=false;
} }
if (ImGui::IsItemHovered()) { if (ImGui::IsItemHovered()) {
@ -219,7 +223,7 @@ void FurnaceGUI::drawOrders() {
if (!ordersOpen) return; if (!ordersOpen) return;
if (mobileUI) { if (mobileUI) {
patWindowPos=(portrait?ImVec2(0.0f,(mobileMenuPos*-0.65*canvasH)):ImVec2((0.16*canvasH)+0.5*canvasW*mobileMenuPos,0.0f)); patWindowPos=(portrait?ImVec2(0.0f,(mobileMenuPos*-0.65*canvasH)):ImVec2((0.16*canvasH)+0.5*canvasW*mobileMenuPos,0.0f));
patWindowSize=(portrait?ImVec2(canvasW,canvasH-(0.16*canvasW)):ImVec2(canvasW-(0.16*canvasH),canvasH)); patWindowSize=(portrait?ImVec2(canvasW,canvasH-(0.16*canvasW)-(pianoOpen?(0.4*canvasW):0.0f)):ImVec2(canvasW-(0.16*canvasH),canvasH-(pianoOpen?(0.3*canvasH):0.0f)));
ImGui::SetNextWindowPos(patWindowPos); ImGui::SetNextWindowPos(patWindowPos);
ImGui::SetNextWindowSize(patWindowSize); ImGui::SetNextWindowSize(patWindowSize);
} else { } else {

View file

@ -42,7 +42,11 @@ const bool isTopKey[12]={
#define VALUE_DIGIT(x,label) \ #define VALUE_DIGIT(x,label) \
if (ImGui::Button(label,buttonSize)) { \ if (ImGui::Button(label,buttonSize)) { \
if (curWindow==GUI_WINDOW_ORDERS && orderEditMode>0) { \
orderInput(x); \
} else { \
valueInput(x,false); \ valueInput(x,false); \
} \
} }
void FurnaceGUI::drawPiano() { void FurnaceGUI::drawPiano() {
@ -166,7 +170,7 @@ void FurnaceGUI::drawPiano() {
} }
ImGui::TableNextColumn(); ImGui::TableNextColumn();
if (pianoInputPadMode==PIANO_INPUT_PAD_REPLACE && cursor.xFine>0 && curWindow==GUI_WINDOW_PATTERN) { if (pianoInputPadMode==PIANO_INPUT_PAD_REPLACE && ((cursor.xFine>0 && curWindow==GUI_WINDOW_PATTERN) || (curWindow==GUI_WINDOW_ORDERS && orderEditMode>0))) {
ImVec2 buttonSize=ImGui::GetContentRegionAvail(); ImVec2 buttonSize=ImGui::GetContentRegionAvail();
if (ImGui::BeginTable("InputPadP",8,ImGuiTableFlags_SizingFixedSame)) { if (ImGui::BeginTable("InputPadP",8,ImGuiTableFlags_SizingFixedSame)) {
ImGui::TableNextRow(); ImGui::TableNextRow();
@ -443,9 +447,9 @@ void FurnaceGUI::drawPiano() {
ImGui::End(); ImGui::End();
// draw input pad if necessary // draw input pad if necessary
if ((curWindow==GUI_WINDOW_PATTERN || !mobileUI) && ((pianoInputPadMode==PIANO_INPUT_PAD_SPLIT_AUTO && cursor.xFine>0) || pianoInputPadMode==PIANO_INPUT_PAD_SPLIT_VISIBLE)) { if ((curWindow==GUI_WINDOW_ORDERS || curWindow==GUI_WINDOW_PATTERN || !mobileUI) && ((pianoInputPadMode==PIANO_INPUT_PAD_SPLIT_AUTO && (cursor.xFine>0 || (curWindow==GUI_WINDOW_ORDERS && orderEditMode>0))) || pianoInputPadMode==PIANO_INPUT_PAD_SPLIT_VISIBLE)) {
if (ImGui::Begin("Input Pad",NULL,ImGuiWindowFlags_NoTitleBar)) { if (ImGui::Begin("Input Pad",NULL,ImGuiWindowFlags_NoTitleBar)) {
ImGui::BeginDisabled(cursor.xFine==0); ImGui::BeginDisabled(cursor.xFine==0 && !(curWindow==GUI_WINDOW_ORDERS && orderEditMode>0));
if (ImGui::BeginTable("InputPad",3,ImGuiTableFlags_Borders)) { if (ImGui::BeginTable("InputPad",3,ImGuiTableFlags_Borders)) {
ImGui::TableNextRow(); ImGui::TableNextRow();
ImGui::TableNextColumn(); ImGui::TableNextColumn();

File diff suppressed because it is too large Load diff