mirror of
https://github.com/tildearrow/furnace.git
synced 2024-11-22 04:25:11 +00:00
Merge branch 'master' into feature/esfm
This commit is contained in:
commit
215c8c375a
46 changed files with 1596 additions and 829 deletions
|
@ -782,6 +782,7 @@ src/gui/doAction.cpp
|
|||
src/gui/editing.cpp
|
||||
src/gui/editControls.cpp
|
||||
src/gui/effectList.cpp
|
||||
src/gui/exportOptions.cpp
|
||||
src/gui/findReplace.cpp
|
||||
src/gui/fmPreview.cpp
|
||||
src/gui/gradient.cpp
|
||||
|
|
9
TODO.md
9
TODO.md
|
@ -1,15 +1,12 @@
|
|||
# to-do
|
||||
|
||||
- add cheat code to insert bruno time (blank)
|
||||
|
||||
# THE REAL TO-DO LIST
|
||||
|
||||
- finish color import improvements (settings refactor)
|
||||
- new undo stuff
|
||||
- fix some bugs
|
||||
- finish auto-clone
|
||||
|
||||
once you have done all of this (maybe not the first one), release 0.6.1
|
||||
|
||||
Furnace is like alcohol...
|
||||
|
||||
# and then
|
||||
|
||||
- new oscilloscope renderer - custom code that uses texture and fixes two issues: too many vertices, and broken anti-aliasing
|
||||
|
|
BIN
demos/misc/fragments_turbosound.fur
Normal file
BIN
demos/misc/fragments_turbosound.fur
Normal file
Binary file not shown.
BIN
demos/nes/christmas-fever.fur
Normal file
BIN
demos/nes/christmas-fever.fur
Normal file
Binary file not shown.
BIN
doc/2-interface/MIDI-value-input-1.png
Normal file
BIN
doc/2-interface/MIDI-value-input-1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 39 KiB |
BIN
doc/2-interface/MIDI-value-input-2.png
Normal file
BIN
doc/2-interface/MIDI-value-input-2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 39 KiB |
|
@ -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.
|
||||
- **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.
|
||||
- **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.
|
||||
- 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.
|
||||
- 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.
|
||||
- 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:
|
||||
- **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:
|
||||
- ` - octave n -- octave n+1 -`
|
||||
- ` 1 3 6 8 A D F # # # `
|
||||
- `0 2 4 5 7 9 B C E # # # # #`
|
||||
![two octaves layout 1](MIDI-value-input-1.png)
|
||||
- **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:
|
||||
- ` - octave n -- octave n+1 -`
|
||||
- ` A B C D E F # # # # `
|
||||
- `0 1 2 3 4 5 6 7 8 9 # # # #`
|
||||
![two octaves layout 2](MIDI-value-input-2.png)
|
||||
- **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 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.
|
||||
- **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.
|
||||
- the default is 2.0, which matches General MIDI standard.
|
||||
- **Actions**: this allows you to bind note input and control change events to actions.
|
||||
- **`+`** 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.
|
||||
|
@ -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 buttons**
|
||||
- **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**
|
||||
- **Export**
|
||||
- **Reset defaults**
|
||||
- **Guru mode**: exposes all color options (instead of accent colors).
|
||||
- **General**
|
||||
- **Color scheme type:**
|
||||
- **Dark**
|
||||
|
|
|
@ -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.
|
||||
|
||||
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
|
||||
|
||||
|
@ -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!
|
||||
- `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
|
||||
|
||||
this chip uses the [Namco 163](../4-instrument/n163.md) instrument editor.
|
||||
|
|
|
@ -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.
|
||||
- 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.
|
||||
- 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
|
||||
- **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**
|
||||
- support for ADSR on both the modulator and the carrier
|
||||
- sine and half-sine based FM synthesis
|
||||
- 9 octave note control
|
||||
|
|
|
@ -167,4 +167,8 @@ void DivEngine::setConf(String key, String value) {
|
|||
|
||||
bool DivEngine::hasConf(String key) {
|
||||
return conf.has(key);
|
||||
}
|
||||
}
|
||||
|
||||
DivConfig& DivEngine::getConfObject() {
|
||||
return conf;
|
||||
}
|
||||
|
|
|
@ -83,7 +83,7 @@ const char* DivEngine::getEffectDesc(unsigned char effect, int chan, bool notNul
|
|||
case 0x82:
|
||||
return "82xx: Set panning (right channel)";
|
||||
case 0x88:
|
||||
return "88xx: Set panning (rear channels; x: left; y: right)";
|
||||
return "88xy: Set panning (rear channels; x: left; y: right)";
|
||||
break;
|
||||
case 0x89:
|
||||
return "89xx: Set panning (rear left channel)";
|
||||
|
@ -2188,6 +2188,13 @@ int DivEngine::getMaxVolumeChan(int ch) {
|
|||
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() {
|
||||
return prevOrder;
|
||||
}
|
||||
|
@ -3391,6 +3398,10 @@ void DivEngine::setMidiDirect(bool value) {
|
|||
midiIsDirect=value;
|
||||
}
|
||||
|
||||
void DivEngine::setMidiDirectProgram(bool value) {
|
||||
midiIsDirectProgram=value;
|
||||
}
|
||||
|
||||
void DivEngine::setMidiVolExp(float value) {
|
||||
midiVolExp=value;
|
||||
}
|
||||
|
@ -3459,6 +3470,12 @@ void DivEngine::rescanAudioDevices() {
|
|||
audioDevs.clear();
|
||||
if (output!=NULL) {
|
||||
audioDevs=output->listAudioDevices();
|
||||
}
|
||||
}
|
||||
|
||||
void DivEngine::rescanMidiDevices() {
|
||||
if (output!=NULL) {
|
||||
logV("re-scanning midi...");
|
||||
if (output->midiIn!=NULL) {
|
||||
midiIns=output->midiIn->listDevices();
|
||||
}
|
||||
|
|
|
@ -54,8 +54,8 @@ class DivWorkPool;
|
|||
|
||||
#define DIV_UNSTABLE
|
||||
|
||||
#define DIV_VERSION "dev189"
|
||||
#define DIV_ENGINE_VERSION 189
|
||||
#define DIV_VERSION "dev190"
|
||||
#define DIV_ENGINE_VERSION 190
|
||||
// for imports
|
||||
#define DIV_VERSION_MOD 0xff01
|
||||
#define DIV_VERSION_FC 0xff02
|
||||
|
@ -176,16 +176,16 @@ struct DivNoteEvent {
|
|||
signed char channel;
|
||||
unsigned char ins;
|
||||
signed char note, volume;
|
||||
bool on, nop, pad1, pad2;
|
||||
DivNoteEvent(int c, int i, int n, int v, bool o):
|
||||
bool on, nop, insChange, fromMIDI;
|
||||
DivNoteEvent(int c, int i, int n, int v, bool o, bool ic=false, bool fm=false):
|
||||
channel(c),
|
||||
ins(i),
|
||||
note(n),
|
||||
volume(v),
|
||||
on(o),
|
||||
nop(false),
|
||||
pad1(false),
|
||||
pad2(false) {}
|
||||
insChange(ic),
|
||||
fromMIDI(fm) {}
|
||||
DivNoteEvent():
|
||||
channel(-1),
|
||||
ins(0),
|
||||
|
@ -193,8 +193,8 @@ struct DivNoteEvent {
|
|||
volume(-1),
|
||||
on(false),
|
||||
nop(true),
|
||||
pad1(false),
|
||||
pad2(false) {}
|
||||
insChange(false),
|
||||
fromMIDI(false) {}
|
||||
};
|
||||
|
||||
struct DivDispatchContainer {
|
||||
|
@ -415,6 +415,7 @@ class DivEngine {
|
|||
bool firstTick;
|
||||
bool skipping;
|
||||
bool midiIsDirect;
|
||||
bool midiIsDirectProgram;
|
||||
bool lowLatency;
|
||||
bool systemsRegistered;
|
||||
bool hasLoadedSomething;
|
||||
|
@ -701,6 +702,9 @@ class DivEngine {
|
|||
double getConfDouble(String key, double fallback);
|
||||
String getConfString(String key, String fallback);
|
||||
|
||||
// get config object
|
||||
DivConfig& getConfObject();
|
||||
|
||||
// set a config value
|
||||
void setConf(String key, bool value);
|
||||
void setConf(String key, int value);
|
||||
|
@ -851,6 +855,9 @@ class DivEngine {
|
|||
// get channel max volume
|
||||
int getMaxVolumeChan(int chan);
|
||||
|
||||
// map MIDI velocity to volume
|
||||
int mapVelocity(int ch, float vel);
|
||||
|
||||
// get current order
|
||||
unsigned char getOrder();
|
||||
|
||||
|
@ -1070,6 +1077,9 @@ class DivEngine {
|
|||
// rescan audio devices
|
||||
void rescanAudioDevices();
|
||||
|
||||
/** rescan midi devices */
|
||||
void rescanMidiDevices();
|
||||
|
||||
// set the console mode.
|
||||
void setConsoleMode(bool enable);
|
||||
|
||||
|
@ -1185,6 +1195,9 @@ class DivEngine {
|
|||
// set MIDI direct channel map
|
||||
void setMidiDirect(bool value);
|
||||
|
||||
// set MIDI direct program change
|
||||
void setMidiDirectProgram(bool value);
|
||||
|
||||
// set MIDI volume curve exponent
|
||||
void setMidiVolExp(float value);
|
||||
|
||||
|
@ -1257,6 +1270,7 @@ class DivEngine {
|
|||
firstTick(false),
|
||||
skipping(false),
|
||||
midiIsDirect(false),
|
||||
midiIsDirectProgram(false),
|
||||
lowLatency(false),
|
||||
systemsRegistered(false),
|
||||
hasLoadedSomething(false),
|
||||
|
|
|
@ -6511,7 +6511,7 @@ SafeWriter* DivEngine::saveText(bool separatePatterns) {
|
|||
if (ins->type==DIV_INS_GB) {
|
||||
w->writeText("- Game Boy parameters:\n");
|
||||
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(" - sound length: %d\n",ins->gb.soundLen));
|
||||
w->writeText(fmt::sprintf(" - use software envelope: %s\n",trueFalse[ins->gb.softEnv?1:0]));
|
||||
|
|
|
@ -186,6 +186,17 @@ class DivPlatformOPN: public DivPlatformFMBase {
|
|||
void setCombo(bool 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
|
||||
|
|
|
@ -132,7 +132,7 @@ class DivPlatformFMBase: public DivDispatch {
|
|||
// -36: 2: 48
|
||||
// -42: 1: 56
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -1300,6 +1300,12 @@ DivDispatchOscBuffer* DivPlatformGenesis::getOscBuffer(int 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() {
|
||||
return regPool;
|
||||
}
|
||||
|
|
|
@ -112,6 +112,7 @@ class DivPlatformGenesis: public DivPlatformOPN {
|
|||
virtual unsigned short getPan(int chan);
|
||||
DivSamplePos getSamplePos(int ch);
|
||||
DivDispatchOscBuffer* getOscBuffer(int chan);
|
||||
virtual int mapVelocity(int ch, float vel);
|
||||
unsigned char* getRegisterPool();
|
||||
int getRegisterPoolSize();
|
||||
void reset();
|
||||
|
|
|
@ -818,6 +818,12 @@ DivDispatchOscBuffer* DivPlatformGenesisExt::getOscBuffer(int ch) {
|
|||
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() {
|
||||
DivPlatformGenesis::reset();
|
||||
|
||||
|
|
|
@ -36,6 +36,7 @@ class DivPlatformGenesisExt: public DivPlatformGenesis {
|
|||
DivMacroInt* getChanMacroInt(int ch);
|
||||
unsigned short getPan(int chan);
|
||||
DivDispatchOscBuffer* getOscBuffer(int chan);
|
||||
int mapVelocity(int ch, float vel);
|
||||
void reset();
|
||||
void forceIns();
|
||||
void tick(bool sysTick=true);
|
||||
|
|
|
@ -199,7 +199,7 @@ void DivPlatformNamcoWSG::tick(bool sysTick) {
|
|||
for (int i=0; i<chans; i++) {
|
||||
chan[i].std.next();
|
||||
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) {
|
||||
chan[i].noise=chan[i].std.duty.val;
|
||||
|
|
|
@ -2104,6 +2104,21 @@ DivDispatchOscBuffer* DivPlatformOPL::getOscBuffer(int 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() {
|
||||
return regPool;
|
||||
}
|
||||
|
|
|
@ -150,6 +150,7 @@ class DivPlatformOPL: public DivDispatch {
|
|||
unsigned short getPan(int chan);
|
||||
DivChannelPair getPaired(int chan);
|
||||
DivDispatchOscBuffer* getOscBuffer(int chan);
|
||||
int mapVelocity(int ch, float vel);
|
||||
unsigned char* getRegisterPool();
|
||||
int getRegisterPoolSize();
|
||||
void reset();
|
||||
|
|
|
@ -962,6 +962,13 @@ DivDispatchOscBuffer* DivPlatformOPLL::getOscBuffer(int 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() {
|
||||
return regPool;
|
||||
}
|
||||
|
|
|
@ -95,6 +95,7 @@ class DivPlatformOPLL: public DivDispatch {
|
|||
void* getChanState(int chan);
|
||||
DivMacroInt* getChanMacroInt(int ch);
|
||||
DivDispatchOscBuffer* getOscBuffer(int chan);
|
||||
int mapVelocity(int ch, float vel);
|
||||
unsigned char* getRegisterPool();
|
||||
int getRegisterPoolSize();
|
||||
void reset();
|
||||
|
|
|
@ -692,6 +692,12 @@ DivDispatchOscBuffer* DivPlatformYM2203Ext::getOscBuffer(int ch) {
|
|||
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() {
|
||||
DivPlatformYM2203::reset();
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ class DivPlatformYM2203Ext: public DivPlatformYM2203 {
|
|||
void* getChanState(int chan);
|
||||
DivMacroInt* getChanMacroInt(int ch);
|
||||
DivDispatchOscBuffer* getOscBuffer(int chan);
|
||||
int mapVelocity(int ch, float vel);
|
||||
void reset();
|
||||
void forceIns();
|
||||
void tick(bool sysTick=true);
|
||||
|
|
|
@ -767,6 +767,12 @@ DivDispatchOscBuffer* DivPlatformYM2608Ext::getOscBuffer(int ch) {
|
|||
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() {
|
||||
DivPlatformYM2608::reset();
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@ class DivPlatformYM2608Ext: public DivPlatformYM2608 {
|
|||
DivMacroInt* getChanMacroInt(int ch);
|
||||
unsigned short getPan(int chan);
|
||||
DivDispatchOscBuffer* getOscBuffer(int chan);
|
||||
int mapVelocity(int ch, float vel);
|
||||
void reset();
|
||||
void forceIns();
|
||||
void tick(bool sysTick=true);
|
||||
|
|
|
@ -757,6 +757,12 @@ DivDispatchOscBuffer* DivPlatformYM2610BExt::getOscBuffer(int ch) {
|
|||
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() {
|
||||
DivPlatformYM2610B::reset();
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@ class DivPlatformYM2610BExt: public DivPlatformYM2610B {
|
|||
DivMacroInt* getChanMacroInt(int ch);
|
||||
unsigned short getPan(int chan);
|
||||
DivDispatchOscBuffer* getOscBuffer(int chan);
|
||||
int mapVelocity(int ch, float vel);
|
||||
void reset();
|
||||
void forceIns();
|
||||
void tick(bool sysTick=true);
|
||||
|
|
|
@ -757,6 +757,12 @@ DivDispatchOscBuffer* DivPlatformYM2610Ext::getOscBuffer(int ch) {
|
|||
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() {
|
||||
DivPlatformYM2610::reset();
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@ class DivPlatformYM2610Ext: public DivPlatformYM2610 {
|
|||
DivMacroInt* getChanMacroInt(int ch);
|
||||
unsigned short getPan(int chan);
|
||||
DivDispatchOscBuffer* getOscBuffer(int chan);
|
||||
int mapVelocity(int ch, float vel);
|
||||
void reset();
|
||||
void forceIns();
|
||||
void tick(bool sysTick=true);
|
||||
|
|
|
@ -1376,8 +1376,15 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
|
|||
pendingNotes.pop_front();
|
||||
continue;
|
||||
}
|
||||
if (note.insChange) {
|
||||
dispatchCmd(DivCommand(DIV_CMD_INSTRUMENT,note.channel,note.ins,0));
|
||||
pendingNotes.pop_front();
|
||||
continue;
|
||||
}
|
||||
if (note.on) {
|
||||
dispatchCmd(DivCommand(DIV_CMD_INSTRUMENT,note.channel,note.ins,1));
|
||||
if (!(midiIsDirect && midiIsDirectProgram && note.fromMIDI)) {
|
||||
dispatchCmd(DivCommand(DIV_CMD_INSTRUMENT,note.channel,note.ins,1));
|
||||
}
|
||||
if (note.volume>=0 && !disCont[dispatchOfChan[note.channel]].dispatch->isVolGlobal()) {
|
||||
float curvedVol=pow((float)note.volume/127.0f,midiVolExp);
|
||||
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: {
|
||||
if (chan<0 || chan>=chans) break;
|
||||
if (midiIsDirect) {
|
||||
pendingNotes.push_back(DivNoteEvent(chan,-1,-1,-1,false));
|
||||
pendingNotes.push_back(DivNoteEvent(chan,-1,-1,-1,false,false,true));
|
||||
} else {
|
||||
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 (msg.data[1]==0) {
|
||||
if (midiIsDirect) {
|
||||
pendingNotes.push_back(DivNoteEvent(chan,-1,-1,-1,false));
|
||||
pendingNotes.push_back(DivNoteEvent(chan,-1,-1,-1,false,false,true));
|
||||
} else {
|
||||
autoNoteOff(msg.type&15,msg.data[0]-12,msg.data[1]);
|
||||
}
|
||||
} else {
|
||||
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 {
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,7 +76,7 @@ void FurnaceGUI::insListItem(int i, int dir, int asset) {
|
|||
const char* insType="Bug!";
|
||||
if (i>=0 && i<e->song.insLen) {
|
||||
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;
|
||||
|
||||
if (ins->type>=DIV_INS_MAX) {
|
||||
|
|
|
@ -65,6 +65,10 @@ void FurnaceGUI::doAction(int what) {
|
|||
case GUI_ACTION_SAVE_AS:
|
||||
openFileDialog(GUI_FILE_SAVE);
|
||||
break;
|
||||
case GUI_ACTION_EXPORT:
|
||||
curExportType=GUI_EXPORT_NONE;
|
||||
displayExport=true;
|
||||
break;
|
||||
case GUI_ACTION_UNDO:
|
||||
if (curWindow==GUI_WINDOW_SAMPLE_EDIT) {
|
||||
doUndoSample();
|
||||
|
|
|
@ -519,24 +519,8 @@ void FurnaceGUI::drawMobileControls() {
|
|||
openFileDialog(GUI_FILE_SAVE_DMF_LEGACY);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Export Audio")) {
|
||||
openFileDialog(GUI_FILE_EXPORT_AUDIO_ONE);
|
||||
}
|
||||
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("Export")) {
|
||||
doAction(GUI_ACTION_EXPORT);
|
||||
}
|
||||
|
||||
if (ImGui::Button("Restore Backup")) {
|
||||
|
|
|
@ -771,10 +771,10 @@ unsigned int convertEffectMPT_S3M(unsigned char symbol, unsigned int val) {
|
|||
return (0x80<<8)|((val&0xf)<<4);
|
||||
break;
|
||||
case 0xC:
|
||||
return (0xFC<<8)|(val&0xf);
|
||||
return (0xEC<<8)|(val&0xf);
|
||||
break;
|
||||
case 0xD:
|
||||
return (0xFD<<8)|(val&0xf);
|
||||
return (0xED<<8)|(val&0xf);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -1117,7 +1117,7 @@ void FurnaceGUI::doPasteMPT(PasteMode mode, int arg, bool readClipboard, String
|
|||
|
||||
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());
|
||||
break;
|
||||
}
|
||||
|
|
357
src/gui/exportOptions.cpp
Normal file
357
src/gui/exportOptions.cpp
Normal 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;
|
||||
}
|
||||
}
|
316
src/gui/gui.cpp
316
src/gui/gui.cpp
|
@ -1216,7 +1216,7 @@ void FurnaceGUI::noteInput(int num, int key, int vol) {
|
|||
if (latchVol!=-1) {
|
||||
pat->data[cursor.y][3]=MIN(maxVol,latchVol);
|
||||
} 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 (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) \
|
||||
if (x<0) 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);
|
||||
if (it!=valueKeys.cend()) {
|
||||
int num=it->second;
|
||||
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);
|
||||
}
|
||||
orderInput(num);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -2246,6 +2250,8 @@ int FurnaceGUI::load(String path) {
|
|||
showWarning(e->getWarnings(),GUI_WARN_GENERIC);
|
||||
}
|
||||
pushRecentFile(path);
|
||||
// walk song
|
||||
e->walkSong(loopOrder,loopRow,loopEnd);
|
||||
// do not auto-play a backup
|
||||
if (path.find(backupPath)!=0) {
|
||||
if (settings.playOnLoad==2 || (settings.playOnLoad==1 && wasPlaying)) {
|
||||
|
@ -3776,7 +3782,7 @@ bool FurnaceGUI::loop() {
|
|||
noteInput(
|
||||
msg.data[0]-12,
|
||||
0,
|
||||
midiMap.volInput?((int)(pow((double)msg.data[1]/127.0,midiMap.volExp)*127.0)):-1
|
||||
midiMap.volInput?msg.data[1]:-1
|
||||
);
|
||||
}
|
||||
} else {
|
||||
|
@ -3803,7 +3809,7 @@ bool FurnaceGUI::loop() {
|
|||
}
|
||||
break;
|
||||
case TA_MIDI_PROGRAM:
|
||||
if (midiMap.programChange) {
|
||||
if (midiMap.programChange && !(midiMap.directChannel && midiMap.directProgram)) {
|
||||
curIns=msg.data[0];
|
||||
if (curIns>=(int)e->song.ins.size()) curIns=e->song.ins.size()-1;
|
||||
wavePreviewInit=true;
|
||||
|
@ -4099,197 +4105,84 @@ bool FurnaceGUI::loop() {
|
|||
openFileDialog(GUI_FILE_SAVE_DMF_LEGACY);
|
||||
}
|
||||
ImGui::Separator();
|
||||
if (ImGui::BeginMenu("export audio...")) {
|
||||
exitDisabledTimer=1;
|
||||
if (ImGui::MenuItem("one file")) {
|
||||
openFileDialog(GUI_FILE_EXPORT_AUDIO_ONE);
|
||||
if (settings.exportOptionsLayout==0) {
|
||||
if (ImGui::BeginMenu("export audio...")) {
|
||||
drawExportAudio();
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
if (ImGui::MenuItem("multiple files (one per chip)")) {
|
||||
openFileDialog(GUI_FILE_EXPORT_AUDIO_PER_SYS);
|
||||
if (ImGui::BeginMenu("export VGM...")) {
|
||||
drawExportVGM();
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
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();
|
||||
}
|
||||
if (ImGui::BeginMenu("export VGM...")) {
|
||||
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;
|
||||
int numZSMCompat=0;
|
||||
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;
|
||||
if ((e->song.system[i]==DIV_SYSTEM_VERA) || (e->song.system[i]==DIV_SYSTEM_YM2151)) numZSMCompat++;
|
||||
}
|
||||
if (numZSMCompat>0) {
|
||||
if (ImGui::BeginMenu("export ZSM...")) {
|
||||
drawExportZSM();
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
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");
|
||||
int numAmiga=0;
|
||||
for (int i=0; i<e->song.systemLen; i++) {
|
||||
if (e->song.system[i]==DIV_SYSTEM_AMIGA) numAmiga++;
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
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::BeginMenu("export ZSM...")) {
|
||||
exitDisabledTimer=1;
|
||||
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();
|
||||
if (numAmiga && settings.iCannotWait) {
|
||||
if (ImGui::BeginMenu("export Amiga validation data...")) {
|
||||
drawExportAmigaVal();
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
if (ImGui::BeginMenu("export text...")) {
|
||||
drawExportText();
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
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::BeginMenu("export Amiga validation data...")) {
|
||||
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 (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();
|
||||
}
|
||||
if (ImGui::BeginMenu("export command stream...")) {
|
||||
drawExportCommand();
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
if (ImGui::BeginMenu("export text...")) {
|
||||
exitDisabledTimer=1;
|
||||
ImGui::Text(
|
||||
"this option exports the song to a text file.\n"
|
||||
);
|
||||
if (ImGui::Button("export")) {
|
||||
openFileDialog(GUI_FILE_EXPORT_TEXT);
|
||||
} else if (settings.exportOptionsLayout==2) {
|
||||
if (ImGui::MenuItem("export audio...")) {
|
||||
curExportType=GUI_EXPORT_AUDIO;
|
||||
displayExport=true;
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
if (ImGui::BeginMenu("export command stream...")) {
|
||||
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 (ImGui::Button("export (binary)")) {
|
||||
openFileDialog(GUI_FILE_EXPORT_CMDSTREAM_BINARY);
|
||||
if (ImGui::MenuItem("export VGM...")) {
|
||||
curExportType=GUI_EXPORT_VGM;
|
||||
displayExport=true;
|
||||
}
|
||||
if (ImGui::Button("export (text)")) {
|
||||
openFileDialog(GUI_FILE_EXPORT_CMDSTREAM);
|
||||
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::EndMenu();
|
||||
}
|
||||
ImGui::Separator();
|
||||
if (!settings.classicChipOptions) {
|
||||
|
@ -4629,6 +4522,7 @@ bool FurnaceGUI::loop() {
|
|||
ordersOpen=true;
|
||||
curWindow=GUI_WINDOW_ORDERS;
|
||||
MEASURE(orders,drawOrders());
|
||||
MEASURE(piano,drawPiano());
|
||||
break;
|
||||
case GUI_SCENE_INSTRUMENT:
|
||||
insEditOpen=true;
|
||||
|
@ -5485,6 +5379,11 @@ bool FurnaceGUI::loop() {
|
|||
}
|
||||
}
|
||||
|
||||
if (displayExport) {
|
||||
displayExport=false;
|
||||
ImGui::OpenPopup("Export");
|
||||
}
|
||||
|
||||
if (displayEditString) {
|
||||
ImGui::OpenPopup("EditString");
|
||||
}
|
||||
|
@ -5526,6 +5425,12 @@ bool FurnaceGUI::loop() {
|
|||
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);
|
||||
if (ImGui::BeginPopupModal("Error",NULL,ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
ImGui::Text("%s",errorString.c_str());
|
||||
|
@ -7018,6 +6923,7 @@ bool FurnaceGUI::init() {
|
|||
return -2;
|
||||
}
|
||||
|
||||
if (midiMap.directChannel && midiMap.directProgram) return -1;
|
||||
return curIns;
|
||||
});
|
||||
|
||||
|
@ -7726,7 +7632,9 @@ FurnaceGUI::FurnaceGUI():
|
|||
introSkipDo(false),
|
||||
introStopped(false),
|
||||
curTutorial(-1),
|
||||
curTutorialStep(0) {
|
||||
curTutorialStep(0),
|
||||
audioExportType(0),
|
||||
curExportType(GUI_EXPORT_NONE) {
|
||||
// value keys
|
||||
valueKeys[SDLK_0]=0;
|
||||
valueKeys[SDLK_1]=1;
|
||||
|
|
|
@ -101,6 +101,7 @@ enum FurnaceGUIColors {
|
|||
GUI_COLOR_MODAL_BACKDROP,
|
||||
GUI_COLOR_HEADER,
|
||||
GUI_COLOR_TEXT,
|
||||
GUI_COLOR_TEXT_DISABLED,
|
||||
GUI_COLOR_ACCENT_PRIMARY,
|
||||
GUI_COLOR_ACCENT_SECONDARY,
|
||||
GUI_COLOR_TITLE_INACTIVE,
|
||||
|
@ -124,6 +125,36 @@ enum FurnaceGUIColors {
|
|||
GUI_COLOR_NAV_HIGHLIGHT,
|
||||
GUI_COLOR_NAV_WIN_HIGHLIGHT,
|
||||
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_ON,
|
||||
GUI_COLOR_EDITING,
|
||||
|
@ -438,6 +469,21 @@ enum FurnaceGUIMobileScenes {
|
|||
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 {
|
||||
GUI_FILE_OPEN,
|
||||
GUI_FILE_OPEN_BACKUP,
|
||||
|
@ -505,6 +551,17 @@ enum FurnaceGUIWarnings {
|
|||
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 {
|
||||
FM_ALGS_4OP,
|
||||
FM_ALGS_2OP_OPL,
|
||||
|
@ -518,6 +575,7 @@ enum FurnaceGUIActions {
|
|||
GUI_ACTION_OPEN_BACKUP,
|
||||
GUI_ACTION_SAVE,
|
||||
GUI_ACTION_SAVE_AS,
|
||||
GUI_ACTION_EXPORT,
|
||||
GUI_ACTION_UNDO,
|
||||
GUI_ACTION_REDO,
|
||||
GUI_ACTION_PLAY_TOGGLE,
|
||||
|
@ -828,6 +886,14 @@ struct SelectionPoint {
|
|||
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 {
|
||||
GUI_UNDO_CHANGE_ORDER,
|
||||
GUI_UNDO_PATTERN_EDIT,
|
||||
|
@ -937,7 +1003,7 @@ struct MIDIMap {
|
|||
int**** map;
|
||||
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
|
||||
//
|
||||
// 1: C- C# D- D# E- F- F# G- G# A- A# B-
|
||||
|
@ -1000,6 +1066,7 @@ struct MIDIMap {
|
|||
midiClock(false),
|
||||
midiTimeCode(false),
|
||||
yamahaFMResponse(false),
|
||||
directProgram(false),
|
||||
valueInputStyle(1),
|
||||
valueInputControlMSB(0),
|
||||
valueInputControlLSB(0),
|
||||
|
@ -1388,7 +1455,7 @@ class FurnaceGUI {
|
|||
bool vgmExportDirectStream, displayInsTypeList, displayWaveSizeList;
|
||||
bool portrait, injectBackUp, mobileMenuOpen, warnColorPushed;
|
||||
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 mobileEdit;
|
||||
bool killGraphics;
|
||||
|
@ -1644,6 +1711,7 @@ class FurnaceGUI {
|
|||
int centerPopup;
|
||||
int insIconsStyle;
|
||||
int classicChipOptions;
|
||||
int exportOptionsLayout;
|
||||
int wasapiEx;
|
||||
int chanOscThreads;
|
||||
int renderPoolThreads;
|
||||
|
@ -1656,6 +1724,7 @@ class FurnaceGUI {
|
|||
int fontAutoHint;
|
||||
int fontAntiAlias;
|
||||
int selectAssetOnLoad;
|
||||
int basicColors;
|
||||
unsigned int maxUndoSteps;
|
||||
String mainFontPath;
|
||||
String headFontPath;
|
||||
|
@ -1839,11 +1908,12 @@ class FurnaceGUI {
|
|||
centerPopup(1),
|
||||
insIconsStyle(1),
|
||||
classicChipOptions(0),
|
||||
exportOptionsLayout(1),
|
||||
wasapiEx(0),
|
||||
chanOscThreads(0),
|
||||
renderPoolThreads(0),
|
||||
showPool(0),
|
||||
writeInsNames(1),
|
||||
writeInsNames(0),
|
||||
readInsNames(1),
|
||||
fontBackend(1),
|
||||
fontHinting(0),
|
||||
|
@ -1851,6 +1921,7 @@ class FurnaceGUI {
|
|||
fontAutoHint(1),
|
||||
fontAntiAlias(1),
|
||||
selectAssetOnLoad(1),
|
||||
basicColors(1),
|
||||
maxUndoSteps(100),
|
||||
mainFontPath(""),
|
||||
headFontPath(""),
|
||||
|
@ -2245,7 +2316,7 @@ class FurnaceGUI {
|
|||
int pianoOffset, pianoOffsetEdit;
|
||||
int pianoView, pianoInputPadMode;
|
||||
|
||||
//effect sorting
|
||||
// effect sorting
|
||||
bool effectsShow[10];
|
||||
|
||||
// TX81Z
|
||||
|
@ -2285,6 +2356,17 @@ class FurnaceGUI {
|
|||
// tutorial
|
||||
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 drawWaveform(unsigned char type, bool opz, const ImVec2& size);
|
||||
void drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, const ImVec2& size);
|
||||
|
@ -2398,6 +2480,7 @@ class FurnaceGUI {
|
|||
void drawSettings();
|
||||
void drawDebug();
|
||||
void drawNewSong();
|
||||
void drawExport();
|
||||
void drawLog();
|
||||
void drawEffectList();
|
||||
void drawSubSongs(bool asChild=false);
|
||||
|
@ -2423,6 +2506,9 @@ class FurnaceGUI {
|
|||
void resetColors();
|
||||
void resetKeybinds();
|
||||
|
||||
void readConfig(DivConfig& conf, FurnaceGUISettingGroups groups=GUI_SETTINGS_ALL);
|
||||
void writeConfig(DivConfig& conf, FurnaceGUISettingGroups groups=GUI_SETTINGS_ALL);
|
||||
|
||||
void syncSettings();
|
||||
void commitSettings();
|
||||
void syncTutorial();
|
||||
|
@ -2473,6 +2559,7 @@ class FurnaceGUI {
|
|||
DivSystem systemPicker();
|
||||
void noteInput(int num, int key, int vol=-1);
|
||||
void valueInput(int num, bool direct=false, int target=-1);
|
||||
void orderInput(int num);
|
||||
|
||||
void doGenerateWave();
|
||||
|
||||
|
|
|
@ -538,6 +538,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={
|
|||
D("OPEN_BACKUP", "Restore backup", 0),
|
||||
D("SAVE", "Save file", FURKMOD_CMD|SDLK_s),
|
||||
D("SAVE_AS", "Save as", FURKMOD_CMD|FURKMOD_SHIFT|SDLK_s),
|
||||
D("EXPORT", "Export", 0),
|
||||
D("UNDO", "Undo", FURKMOD_CMD|SDLK_z),
|
||||
#ifdef __APPLE__
|
||||
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_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_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_SECONDARY,"",ImVec4(0.26f,0.59f,0.98f,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_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_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_ON,"",ImVec4(0.2f,0.6f,0.2f,1.0f)),
|
||||
D(GUI_COLOR_EDITING,"",ImVec4(0.2f,0.1f,0.1f,1.0f)),
|
||||
|
|
|
@ -2967,7 +2967,11 @@ void FurnaceGUI::drawInsEdit() {
|
|||
updateFMPreview=false;
|
||||
}
|
||||
if (settings.insEditColorize) {
|
||||
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 (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));
|
||||
}
|
||||
}
|
||||
if (ImGui::BeginTable("InsProp",3)) {
|
||||
ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed);
|
||||
|
@ -3028,14 +3032,8 @@ void FurnaceGUI::drawInsEdit() {
|
|||
ImGui::Text("Type");
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
if (ins->type>=DIV_INS_MAX) ins->type=DIV_INS_FM;
|
||||
int insType=ins->type;
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
/*
|
||||
if (ImGui::Combo("##Type",&insType,insTypes,DIV_INS_MAX,DIV_INS_MAX)) {
|
||||
ins->type=(DivInstrumentType)insType;
|
||||
}
|
||||
*/
|
||||
bool warnType=true;
|
||||
for (DivInstrumentType i: e->getPossibleInsTypes()) {
|
||||
if (i==insType) {
|
||||
|
@ -3044,7 +3042,7 @@ void FurnaceGUI::drawInsEdit() {
|
|||
}
|
||||
|
||||
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;
|
||||
if (settings.displayAllInsTypes) {
|
||||
for (int i=0; insTypes[i][0]; i++) {
|
||||
|
@ -6689,7 +6687,7 @@ void FurnaceGUI::drawInsEdit() {
|
|||
ImGui::EndTabItem();
|
||||
}
|
||||
}
|
||||
if (ImGui::BeginTabItem("Macros")) {
|
||||
if (ins->type<DIV_INS_MAX) if (ImGui::BeginTabItem("Macros")) {
|
||||
const char* volumeLabel="Volume";
|
||||
|
||||
int volMax=15;
|
||||
|
@ -7247,6 +7245,12 @@ void FurnaceGUI::drawInsEdit() {
|
|||
ins->type==DIV_INS_VRC6) {
|
||||
insTabSample(ins);
|
||||
}
|
||||
if (ins->type>=DIV_INS_MAX) {
|
||||
if (ImGui::BeginTabItem("Error")) {
|
||||
ImGui::Text("invalid instrument type! change it first.");
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
}
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
if (settings.insEditColorize) {
|
||||
|
|
|
@ -139,6 +139,7 @@ bool MIDIMap::read(String path) {
|
|||
UNDERSTAND_OPTION(midiClock) else
|
||||
UNDERSTAND_OPTION(midiTimeCode) else
|
||||
UNDERSTAND_OPTION(yamahaFMResponse) else
|
||||
UNDERSTAND_OPTION(directProgram) else
|
||||
UNDERSTAND_OPTION(valueInputStyle) else
|
||||
UNDERSTAND_OPTION(valueInputControlMSB) else
|
||||
UNDERSTAND_OPTION(valueInputControlLSB) else
|
||||
|
@ -205,6 +206,7 @@ bool MIDIMap::write(String path) {
|
|||
WRITE_OPTION(midiClock);
|
||||
WRITE_OPTION(midiTimeCode);
|
||||
WRITE_OPTION(yamahaFMResponse);
|
||||
WRITE_OPTION(directProgram);
|
||||
WRITE_OPTION(valueInputStyle);
|
||||
WRITE_OPTION(valueInputControlMSB);
|
||||
WRITE_OPTION(valueInputControlLSB);
|
||||
|
|
|
@ -181,6 +181,10 @@ void FurnaceGUI::drawOrderButtons() {
|
|||
}
|
||||
NEXT_BUTTON;
|
||||
|
||||
if (orderEditMode==0 && mobileUI) {
|
||||
orderEditMode=1;
|
||||
}
|
||||
|
||||
const char* orderEditModeLabel="?##OrderEditMode";
|
||||
if (orderEditMode==3) {
|
||||
orderEditModeLabel=ICON_FA_ARROWS_V "##OrderEditMode";
|
||||
|
@ -193,7 +197,7 @@ void FurnaceGUI::drawOrderButtons() {
|
|||
}
|
||||
if (ImGui::Button(orderEditModeLabel)) { handleUnimportant
|
||||
orderEditMode++;
|
||||
if (orderEditMode>3) orderEditMode=0;
|
||||
if (orderEditMode>3) orderEditMode=mobileUI?1:0;
|
||||
curNibble=false;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
|
@ -219,7 +223,7 @@ void FurnaceGUI::drawOrders() {
|
|||
if (!ordersOpen) return;
|
||||
if (mobileUI) {
|
||||
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::SetNextWindowSize(patWindowSize);
|
||||
} else {
|
||||
|
|
|
@ -42,7 +42,11 @@ const bool isTopKey[12]={
|
|||
|
||||
#define VALUE_DIGIT(x,label) \
|
||||
if (ImGui::Button(label,buttonSize)) { \
|
||||
valueInput(x,false); \
|
||||
if (curWindow==GUI_WINDOW_ORDERS && orderEditMode>0) { \
|
||||
orderInput(x); \
|
||||
} else { \
|
||||
valueInput(x,false); \
|
||||
} \
|
||||
}
|
||||
|
||||
void FurnaceGUI::drawPiano() {
|
||||
|
@ -166,7 +170,7 @@ void FurnaceGUI::drawPiano() {
|
|||
}
|
||||
|
||||
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();
|
||||
if (ImGui::BeginTable("InputPadP",8,ImGuiTableFlags_SizingFixedSame)) {
|
||||
ImGui::TableNextRow();
|
||||
|
@ -443,9 +447,9 @@ void FurnaceGUI::drawPiano() {
|
|||
ImGui::End();
|
||||
|
||||
// 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)) {
|
||||
ImGui::BeginDisabled(cursor.xFine==0);
|
||||
ImGui::BeginDisabled(cursor.xFine==0 && !(curWindow==GUI_WINDOW_ORDERS && orderEditMode>0));
|
||||
if (ImGui::BeginTable("InputPad",3,ImGuiTableFlags_Borders)) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
|
|
1354
src/gui/settings.cpp
1354
src/gui/settings.cpp
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue