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

# Conflicts:
#	CMakeLists.txt
#	papers/doc/7-systems/README.md
#	src/engine/dispatchContainer.cpp
#	src/gui/gui.cpp
#	src/gui/insEdit.cpp
This commit is contained in:
cam900 2022-03-23 02:44:13 +09:00
commit 2eaa6ed0ce
66 changed files with 7735 additions and 5410 deletions

View File

@ -323,6 +323,7 @@ src/engine/platform/swan.cpp
src/engine/platform/vera.cpp
src/engine/platform/bubsyswsg.cpp
src/engine/platform/n163.cpp
src/engine/platform/pet.cpp
src/engine/platform/dummy.cpp
)
@ -359,12 +360,33 @@ src/gui/fileDialog.cpp
src/gui/intConst.cpp
src/gui/guiConst.cpp
src/gui/about.cpp
src/gui/channels.cpp
src/gui/compatFlags.cpp
src/gui/cursor.cpp
src/gui/dataList.cpp
src/gui/debugWindow.cpp
src/gui/doAction.cpp
src/gui/editing.cpp
src/gui/editControls.cpp
src/gui/insEdit.cpp
src/gui/mixer.cpp
src/gui/newSong.cpp
src/gui/orders.cpp
src/gui/osc.cpp
src/gui/pattern.cpp
src/gui/piano.cpp
src/gui/presets.cpp
src/gui/regView.cpp
src/gui/sampleEdit.cpp
src/gui/settings.cpp
src/gui/songInfo.cpp
src/gui/songNotes.cpp
src/gui/stats.cpp
src/gui/sysConf.cpp
src/gui/util.cpp
src/gui/waveEdit.cpp
src/gui/volMeter.cpp
src/gui/gui.cpp
)

View File

@ -2,7 +2,7 @@
![screenshot](papers/screenshot1.png)
this is a work-in-progress chiptune tracker compatible with DefleMask modules (.dmf).
a multi-system chiptune tracker.
[downloads](#downloads) | [discussion](#discussion) | [help](#help) | [developer info](#developer-info)
@ -23,6 +23,7 @@ this is a work-in-progress chiptune tracker compatible with DefleMask modules (.
- Amiga
- TIA (Atari 2600/7800)
- multiple sound chips in a single song!
- DefleMask compatibility - loads .dmf modules, .dmp instruments and .dmw wavetables
- clean-room design (guesswork and ABX tests only, no decompilation involved)
- bug/quirk implementation for increased playback accuracy
- VGM and audio file export
@ -64,7 +65,7 @@ some people have provided packages for Unix/Unix-like distributions. here's a li
## Nix
(TODO)
[package](https://search.nixos.org/packages?channel=unstable&show=furnace&from=0&size=50&sort=relevance&type=packages&query=furnace) thanks to OPNA2608.
# developer info

View File

@ -4,6 +4,7 @@ this is a list of systems that Furnace supports, including each system's effects
- [Sega Genesis/Mega Drive](genesis.md)
- [Sega Master System](sms.md)
- [Yamaha OPLL](opll.md)
- [Game Boy](game-boy.md)
- [PC Engine/TurboGrafx-16](pce.md)
- [NES](nes.md)
@ -15,12 +16,17 @@ this is a list of systems that Furnace supports, including each system's effects
- [Amiga](amiga.md)
- [Yamaha YM2612 standalone](ym2612.md)
- [Yamaha YM2151 standalone](ym2151.md)
- [SegaPCM](segapcm.md)
- [Atari 2600](tia.md)
- [Philips SAA1099](saa1099.md)
- [Microchip AY8930](ay8930.md)
- [Seta/Allumer X1-010](x1_010.md)
- [VERA](vera.md)
- [Seta/Allumer X1-010](x1-010.md)
- [WonderSwan](wonderswan.md)
- [Bubble System WSG](bubblesystem.md)
- [Namco 163](n163.md)
- [Yamaha OPL](opl.md)
- [PC Speaker](pcspkr.md)
- [Commodore PET](pet.md)
Furnace also reads .dmf files with the [Yamaha YMU759](ymu759.md) system, but does not emulate the chip at all.
Furnace also reads .dmf files with the [Yamaha YMU759](ymu759.md) system, but...

View File

@ -1,7 +1,7 @@
# Arcade (Yamaha YM2151/PCM)
this chip combination was used in the Sega OutRun, X and Y arcade boards, and perhaps a few others.
the actual PCM chip had 16 channels, but the number has been cut to 5 in DefleMask for seemingly arbitrary reasons. a system with all 16 channels may be coming soon.
the actual PCM chip had 16 channels, but the number has been cut to 5 in DefleMask for seemingly arbitrary reasons.
# effects

View File

@ -5,7 +5,7 @@ a backwards-compatible successor to the AY-3-8910, with increased volume resolut
sadly, this soundchip has only ever observed minimal success, and has remained rather obscure since.
it is known for being used in the Covox Sound Master, which didn't sell well either.
while emulation of this chip is mostly complete, the additional noise setup registers are not emulated (yet). whether it ever has been emulated at some point in a now-abandoned tracker with similar goal as Furnace is unknown.
while emulation of this chip is mostly complete, hardware comparison hasn't been performed yet due to its scarcity. it also was emulated in a now-abandoned tracker with similar goal as Furnace, which sparked interest on the chip.
# effects

View File

@ -8,7 +8,7 @@ Also known as K005289, but that's just part of the logic used for pitch and wave
Waveform select and Volume control are tied with single AY-3-8910 IO for both channels.
Another AY-3-8910 IO is used for reading sound hardware status.
furnace emulates this configurations as single system, waveform format is 15 level and 32 width.
Furnace emulates this configurations as single system, waveform format is 15 level and 32 width.
# effects

View File

@ -2,7 +2,7 @@
The Atari Lynx is a 16 bit handheld console developed by (obviously) Atari Corporation, and initially released in September of 1989, with the worldwide release being in 1990.
The Lynx, while being an incredible handheld for the time (and a lot more powerful than a Game Boy), unfortunately meant nothing in the end due to the Lynx being a market failiure, and ending up as one of the things that contributed to the downfall of Atari.
The Lynx, while being an incredible handheld for the time (and a lot more powerful than a Game Boy), unfortunately meant nothing in the end due to the Lynx being a market failure, and ending up as one of the things that contributed to the downfall of Atari.
Although the Lynx is still getting (rather impressive) homebrew developed for it, it does not mean that the Lynx is a popular system at all.
@ -17,4 +17,6 @@ The Atari Lynx's custom sound chip and CPU (MIKEY) is a 6502-based 8 bit CPU run
- The MIKEY also has a variety of pitches to choose from, and they go from 32Hz to "above the range of human hearing", according to Atari.
## Effect commands
- `3xxx`: Load LFSR (0 to FFF). For it to work, duty macro in instrument editor must be set to some value, without it LFSR will not be fed with any bits.
- `3xxx`: Load LFSR (0 to FFF).
- this is a bitmask.
- for it to work, duty macro in instrument editor must be set to some value, without it LFSR will not be fed with any bits.

View File

@ -0,0 +1,47 @@
# Yamaha OPL
a series of FM sound chips which were very popular in DOS land. it was so popular that even Yamaha made a logo for it!
essentially a downgraded version of Yamaha's other FM chips, with only 2 operators per channel.
however, it also had a drums mode, and later chips in the series added more waveforms (than just the typical sine) and even a 4-operator mode.
the original OPL was present as an expansion for the Commodore 64 and MSX computers (erm, a variant of it). it only had 9 channels and drums mode.
its successor, the OPL2, added 3 more waveforms and was one of the more popular chips because it was present on the AdLib card for PC.
later Creative would borrow the chip to make the Sound Blaster, and totally destroyed AdLib's dominance.
the OPL3 added 9 more channels, 4 more waveforms, 4-operator mode (pairing up to 12 channels to make up to six 4-operator channels), quadraphonic output (sadly Furnace only supports stereo) and some other things.
it was overkill.
afterwards everyone moved to Windows and software mixing...
# effects
- 10xx: set AM depth. the following values are accepted:
- 0: 1dB (shallow)
- 1: 4.8dB (deep)
- this effect applies to all channels.
- `11xx`: set feedback of channel.
- `12xx`: set operator 1 level.
- `13xx`: set operator 2 level.
- `14xx`: set operator 3 level.
- only in 4-op mode (OPL3).
- `15xx`: set operator 4 level.
- only in 4-op mode (OPL3).
- `16xy`: set multiplier of operator.
- `x` is the operator (1-4; last 2 operators only in 4-op mode).
- `y` is the mutliplier.
- 17xx: set vibrato depth. the following values are accepted:
- 0: normal
- 1: double
- this effect applies to all channels.
- `18xx`: toggle drums mode.
- 0 disables it and 1 enables it.
- only in drums system.
- `19xx`: set attack of all operators.
- `1Axx`: set attack of operator 1.
- `1Bxx`: set attack of operator 2.
- `1Cxx`: set attack of operator 3.
- only in 4-op mode (OPL3).
- `1Dxx`: set attack of operator 4.
- only in 4-op mode (OPL3).

View File

@ -1,21 +1,34 @@
# Yamaha YM2413/OPLL
The YM2413, otherwise known as OPLL, is a cost-reduced FM synthesis sound chip manufactured by Yamaha Corporation and based on the Yamaha YM3812 sound chip (OPL2).
As of Furnace version 0.5.7pre4, the OPLL sound chip is not yet emulated. It is, however, emulated in Deflemask as of version 1.1.0. Support for loading .DMFs which contain the YM2413 was added in Furnace version 0.5.7pre4.
the YM2413, otherwise known as OPLL, is a cost-reduced FM synthesis sound chip manufactured by Yamaha Corporation and based on the Yamaha YM3812 sound chip (OPL2). thought OPL was downgraded enough? :p
## Technical specifications
The YM2413 is equipped with the following features:
- 9 channels of 2 operator FM synthesis
- A drum/percussion mode, replacing the last 3 voices with 3 rhythm channels
- 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
- 4096 different frequencies for channels
- 16 unique volume levels (NOTE: Volume 0 is NOT silent.)
- Modulator and carrier key scaling
- Built-in hardware vibrato support
# technical specifications
## Effect commands
TODO: Add effect commands here when YM2413 emulation is added.
the YM2413 is equipped with the following features:
- 9 channels of 2 operator FM synthesis
- A drum/percussion mode, replacing the last 3 voices with 3 rhythm channels
- 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
- 4096 different frequencies for channels
- 16 unique volume levels (NOTE: Volume 0 is NOT silent.)
- Modulator and carrier key scaling
- Built-in hardware vibrato support
# effects
- `11xx`: set feedback of channel.
- `12xx`: set operator 1 level.
- `13xx`: set operator 2 level.
- `16xy`: set multiplier of operator.
- `x` is the operator (1 or 2).
- `y` is the mutliplier.
- `18xx`: toggle drums mode.
- 0 disables it and 1 enables it.
- only in drums system.
- `19xx`: set attack of all operators.
- `1Axx`: set attack of operator 1.
- `1Bxx`: set attack of operator 2.

View File

@ -0,0 +1,7 @@
# PC Speaker
40 years of one square beep - and still going!
# effects
ha! effects...

View File

@ -0,0 +1,11 @@
# Commodore PET
a computer from 1977 which was leader on US schools back then. subsequently the Apple II took its throne.
maybe no better than a computer terminal, but somebody discovered a way to update the screen at turbo rate - and eventually its sound "chip" (it was nothing more than an 8-bit shift register) was abused as well.
some of these didn't even have sound...
# effects
- 10xx: set waveform. `xx` is a bitmask.

View File

@ -0,0 +1,12 @@
# SegaPCM
16 channels of PCM? no way!
yep, that's right! 16 channels of PCM!
a chip used in the Sega OutRun/X/Y arcade boards. eventually the MultiPCM surpassed it with 24 channels, and later they joined the software mixing gang.
# effects
- `20xx`: set PCM frequency.
- `xx` is a 256th fraction of 31250Hz.
- this effect exists for mostly DefleMask compatibility - it is otherwise recommended to use Sample type instruments.

View File

@ -0,0 +1,15 @@
# VERA
this is a video and sound generator chip used in the Commander X16, a modern 8-bit computer created by The 8-Bit Guy.
it has 16 channels of pulse/triangle/saw/noise and one stereo PCM channel.
currently Furnace does not support the PCM channel's stereo mode, though (except for panning).
# effects
- `20xx`: set waveform. the following values are accepted:
- 0: pulse
- 1: saw
- 2: triangle
- 3: noise
- `22xx`: set duty cycle. `xx` may go from 0 to 3F.

View File

@ -2,7 +2,7 @@
One of sound chip originally designed by Seta, mainly used at their arcade hardwares at late-80s to early-2000s.
It has 2 output channels, but no known hardware using this feature for stereo sound.
Later hardware paired this with external bankswitching logic, but its logic is not emulated now in current furnace revision.
Later hardware paired this with external bankswitching logic, but this isn't emulated yet.
Allumer one is just rebadged Seta's thing for use in their arcade hardwares.
It has 16 channels, and all channels can be switchable to PCM sample or wavetable playback mode.
@ -26,9 +26,9 @@ In furnace, You can set envelope shape split mode. When it sets, its waveform wi
# effects
- `10xx`: change wave.
- `11xx`: change envelope shape. (also wavetable)
- `11xx`: change envelope shape (also wavetable).
- `17xx`: toggle PCM mode.
- `20xx`: set PCM frequency. (1 to FF)*
- `20xx`: set PCM frequency (1 to FF).
- `22xx`: set envelope mode.
- bit 0 sets whether envelope will affect this channel.
- bit 1 sets whether envelope one-shot mode. when it sets, channel is halted after envelope cycle is ended.

View File

@ -1,6 +1,8 @@
# Yamaha YM2151
the sound chip powering the Arcade system, available for standalone use if you want to make X68000 music or pair it with a 16-channel Sega PCM when it comes.
the sound chip powering several arcade boards and the Sharp X1/X68000.
it also was present on several pinball machines and synthesizers of the era, and later surpassed by the YM2414 (OPZ) present in the world-famous TX81Z.
# effects

View File

@ -1,6 +1,7 @@
# Taito Arcade/Yamaha YM2610B
YM2610B is basically YM2610 with 2 extra FM channels used at some 90s Taito Arcade hardwares, it's backward compatible with non-B chip.
YM2610B is basically YM2610 with 2 extra FM channels used at some 90s Taito arcade hardware.
it is backward compatible with the original chip.
# effects
@ -54,4 +55,4 @@ YM2610B is basically YM2610 with 2 extra FM channels used at some 90s Taito Arca
- in this mode the envelope period is set to the channel's notes, multiplied by a fraction.
- `x` is the numerator.
- `y` is the denominator.
- if `x` or `y` are 0 this will disable auto-envelope mode.
- if `x` or `y` are 0 this will disable auto-envelope mode.

View File

@ -4,7 +4,8 @@ the Yamaha YMU759 is a sound chip designed for feature phones during the early 2
it is also known as MA-2.
sadly Yamaha didn't care about these chips too much, and the register specs were completely unavailable, which means the YMU759 is totally unsupported and unemulated besides Yamaha's official emulator for it built into MidRadio.
hence Furnace does not emulate the chip (and doesn't even let you add it to a song).
Furnace 0.6 loads DefleMask modules written for this system; however, it doesn't support any of its effects and is simulated using the OPL core.
# effects

View File

@ -47,6 +47,7 @@
#include "platform/lynx.h"
#include "platform/bubsyswsg.h"
#include "platform/n163.h"
#include "platform/pet.h"
#include "platform/dummy.h"
#include "../ta-log.h"
#include "song.h"
@ -279,6 +280,9 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
case DIV_SYSTEM_N163:
dispatch=new DivPlatformN163;
break;
case DIV_SYSTEM_PET:
dispatch=new DivPlatformPET;
break;
default:
logW("this system is not supported yet! using dummy platform.\n");
dispatch=new DivPlatformDummy;

View File

@ -584,6 +584,7 @@ void DivEngine::renderSamples() {
void DivEngine::createNew(const int* description) {
quitDispatch();
isBusy.lock();
saveLock.lock();
song.unload();
song=DivSong();
if (description!=NULL) {
@ -602,6 +603,7 @@ void DivEngine::createNew(const int* description) {
}
recalcChans();
renderSamples();
saveLock.unlock();
isBusy.unlock();
initDispatch();
isBusy.lock();
@ -612,9 +614,11 @@ void DivEngine::createNew(const int* description) {
void DivEngine::changeSystem(int index, DivSystem which) {
quitDispatch();
isBusy.lock();
saveLock.lock();
song.system[index]=which;
song.systemFlags[index]=0;
recalcChans();
saveLock.unlock();
isBusy.unlock();
initDispatch();
isBusy.lock();
@ -635,11 +639,13 @@ bool DivEngine::addSystem(DivSystem which) {
}
quitDispatch();
isBusy.lock();
saveLock.lock();
song.system[song.systemLen]=which;
song.systemVol[song.systemLen]=64;
song.systemPan[song.systemLen]=0;
song.systemFlags[song.systemLen++]=0;
recalcChans();
saveLock.unlock();
isBusy.unlock();
initDispatch();
isBusy.lock();
@ -660,12 +666,14 @@ bool DivEngine::removeSystem(int index) {
}
quitDispatch();
isBusy.lock();
saveLock.lock();
song.system[index]=DIV_SYSTEM_NULL;
song.systemLen--;
for (int i=index; i<song.systemLen; i++) {
song.system[i]=song.system[i+1];
}
recalcChans();
saveLock.unlock();
isBusy.unlock();
initDispatch();
isBusy.lock();
@ -1029,6 +1037,7 @@ void DivEngine::previewSample(int sample, int note) {
rate=(song.tuning*pow(2.0,(double)(note+3)/12.0)*((double)song.sample[sample]->centerRate/8363.0));
if (rate<=0) rate=song.sample[sample]->rate;
}
if (rate<100) rate=100;
blip_set_rates(samp_bb,rate,got.rate);
samp_prevSample=0;
sPreview.pos=0;
@ -1057,7 +1066,9 @@ void DivEngine::previewWave(int wave, int note) {
return;
}
blip_clear(samp_bb);
blip_set_rates(samp_bb,song.wave[wave]->len*((song.tuning*0.0625)*pow(2.0,(double)(note+3)/12.0)),got.rate);
double rate=song.wave[wave]->len*((song.tuning*0.0625)*pow(2.0,(double)(note+3)/12.0));
if (rate<100) rate=100;
blip_set_rates(samp_bb,rate,got.rate);
samp_prevSample=0;
sPreview.pos=0;
sPreview.sample=-1;
@ -1211,8 +1222,10 @@ int DivEngine::addInstrument(int refChan) {
int insCount=(int)song.ins.size();
ins->name=fmt::sprintf("Instrument %d",insCount);
ins->type=getPreferInsType(refChan);
saveLock.lock();
song.ins.push_back(ins);
song.insLen=insCount+1;
saveLock.unlock();
isBusy.unlock();
return insCount;
}
@ -1227,6 +1240,9 @@ enum DivInsFormats {
DIV_INSFORMAT_SBI,
};
// TODO: re-organize this function to:
// - support replacing instruments
// - support instrument formats which contain multiple instruments
bool DivEngine::addInstrumentFromFile(const char* path) {
warnings="";
@ -1351,7 +1367,7 @@ bool DivEngine::addInstrumentFromFile(const char* path) {
}
}
// TDOO these really should be refactored to separate functions/cpp files per instrument file type.
// TDOO these really should be re-organized to separate functions per instrument file type.
switch (format) {
case DIV_INSFORMAT_DMP: {
// this is a ridiculous mess
@ -1934,15 +1950,18 @@ bool DivEngine::addInstrumentFromFile(const char* path) {
}
isBusy.lock();
saveLock.lock();
int insCount=(int)song.ins.size();
song.ins.push_back(ins);
song.insLen=insCount+1;
saveLock.unlock();
isBusy.unlock();
return true;
}
void DivEngine::delInstrument(int index) {
isBusy.lock();
saveLock.lock();
if (index>=0 && index<(int)song.ins.size()) {
for (int i=0; i<song.systemLen; i++) {
disCont[i].dispatch->notifyInsDeletion(song.ins[index]);
@ -1961,15 +1980,18 @@ void DivEngine::delInstrument(int index) {
}
}
}
saveLock.unlock();
isBusy.unlock();
}
int DivEngine::addWave() {
isBusy.lock();
saveLock.lock();
DivWavetable* wave=new DivWavetable;
int waveCount=(int)song.wave.size();
song.wave.push_back(wave);
song.waveLen=waveCount+1;
saveLock.unlock();
isBusy.unlock();
return waveCount;
}
@ -2085,30 +2107,36 @@ bool DivEngine::addWaveFromFile(const char* path) {
}
isBusy.lock();
saveLock.lock();
int waveCount=(int)song.wave.size();
song.wave.push_back(wave);
song.waveLen=waveCount+1;
saveLock.unlock();
isBusy.unlock();
return true;
}
void DivEngine::delWave(int index) {
isBusy.lock();
saveLock.lock();
if (index>=0 && index<(int)song.wave.size()) {
delete song.wave[index];
song.wave.erase(song.wave.begin()+index);
song.waveLen=song.wave.size();
}
saveLock.unlock();
isBusy.unlock();
}
int DivEngine::addSample() {
isBusy.lock();
saveLock.lock();
DivSample* sample=new DivSample;
int sampleCount=(int)song.sample.size();
sample->name=fmt::sprintf("Sample %d",sampleCount);
song.sample.push_back(sample);
song.sampleLen=sampleCount+1;
saveLock.unlock();
renderSamples();
isBusy.unlock();
return sampleCount;
@ -2186,8 +2214,10 @@ bool DivEngine::addSampleFromFile(const char* path) {
if (sample->centerRate<4000) sample->centerRate=4000;
if (sample->centerRate>64000) sample->centerRate=64000;
sf_close(f);
saveLock.lock();
song.sample.push_back(sample);
song.sampleLen=sampleCount+1;
saveLock.unlock();
renderSamples();
isBusy.unlock();
return sampleCount;
@ -2195,12 +2225,14 @@ bool DivEngine::addSampleFromFile(const char* path) {
void DivEngine::delSample(int index) {
isBusy.lock();
saveLock.lock();
if (index>=0 && index<(int)song.sample.size()) {
delete song.sample[index];
song.sample.erase(song.sample.begin()+index);
song.sampleLen=song.sample.size();
renderSamples();
}
saveLock.unlock();
isBusy.unlock();
}
@ -2229,11 +2261,14 @@ void DivEngine::addOrder(bool duplicate, bool where) {
}
}
if (where) { // at the end
saveLock.lock();
for (int i=0; i<DIV_MAX_CHANS; i++) {
song.orders.ord[i][song.ordersLen]=order[i];
}
song.ordersLen++;
saveLock.unlock();
} else { // after current order
saveLock.lock();
for (int i=0; i<DIV_MAX_CHANS; i++) {
for (int j=song.ordersLen; j>curOrder; j--) {
song.orders.ord[i][j]=song.orders.ord[i][j-1];
@ -2241,6 +2276,7 @@ void DivEngine::addOrder(bool duplicate, bool where) {
song.orders.ord[i][curOrder+1]=order[i];
}
song.ordersLen++;
saveLock.unlock();
curOrder++;
if (playing && !freelance) {
playSub(false);
@ -2277,11 +2313,14 @@ void DivEngine::deepCloneOrder(bool where) {
}
}
if (where) { // at the end
saveLock.lock();
for (int i=0; i<chans; i++) {
song.orders.ord[i][song.ordersLen]=order[i];
}
song.ordersLen++;
saveLock.unlock();
} else { // after current order
saveLock.lock();
for (int i=0; i<chans; i++) {
for (int j=song.ordersLen; j>curOrder; j--) {
song.orders.ord[i][j]=song.orders.ord[i][j-1];
@ -2289,6 +2328,7 @@ void DivEngine::deepCloneOrder(bool where) {
song.orders.ord[i][curOrder+1]=order[i];
}
song.ordersLen++;
saveLock.unlock();
curOrder++;
if (playing && !freelance) {
playSub(false);
@ -2300,12 +2340,14 @@ void DivEngine::deepCloneOrder(bool where) {
void DivEngine::deleteOrder() {
if (song.ordersLen<=1) return;
isBusy.lock();
saveLock.lock();
for (int i=0; i<DIV_MAX_CHANS; i++) {
for (int j=curOrder; j<song.ordersLen; j++) {
song.orders.ord[i][j]=song.orders.ord[i][j+1];
}
}
song.ordersLen--;
saveLock.unlock();
if (curOrder>=song.ordersLen) curOrder=song.ordersLen-1;
if (playing && !freelance) {
playSub(false);
@ -2319,11 +2361,13 @@ void DivEngine::moveOrderUp() {
isBusy.unlock();
return;
}
saveLock.lock();
for (int i=0; i<DIV_MAX_CHANS; i++) {
song.orders.ord[i][curOrder]^=song.orders.ord[i][curOrder-1];
song.orders.ord[i][curOrder-1]^=song.orders.ord[i][curOrder];
song.orders.ord[i][curOrder]^=song.orders.ord[i][curOrder-1];
}
saveLock.unlock();
curOrder--;
if (playing && !freelance) {
playSub(false);
@ -2337,11 +2381,13 @@ void DivEngine::moveOrderDown() {
isBusy.unlock();
return;
}
saveLock.lock();
for (int i=0; i<DIV_MAX_CHANS; i++) {
song.orders.ord[i][curOrder]^=song.orders.ord[i][curOrder+1];
song.orders.ord[i][curOrder+1]^=song.orders.ord[i][curOrder];
song.orders.ord[i][curOrder]^=song.orders.ord[i][curOrder+1];
}
saveLock.unlock();
curOrder++;
if (playing && !freelance) {
playSub(false);
@ -2368,9 +2414,11 @@ bool DivEngine::moveInsUp(int which) {
if (which<1 || which>=(int)song.ins.size()) return false;
isBusy.lock();
DivInstrument* prev=song.ins[which];
saveLock.lock();
song.ins[which]=song.ins[which-1];
song.ins[which-1]=prev;
exchangeIns(which,which-1);
saveLock.unlock();
isBusy.unlock();
return true;
}
@ -2379,8 +2427,10 @@ bool DivEngine::moveWaveUp(int which) {
if (which<1 || which>=(int)song.wave.size()) return false;
isBusy.lock();
DivWavetable* prev=song.wave[which];
saveLock.lock();
song.wave[which]=song.wave[which-1];
song.wave[which-1]=prev;
saveLock.unlock();
isBusy.unlock();
return true;
}
@ -2389,8 +2439,10 @@ bool DivEngine::moveSampleUp(int which) {
if (which<1 || which>=(int)song.sample.size()) return false;
isBusy.lock();
DivSample* prev=song.sample[which];
saveLock.lock();
song.sample[which]=song.sample[which-1];
song.sample[which-1]=prev;
saveLock.unlock();
isBusy.unlock();
return true;
}
@ -2399,9 +2451,11 @@ bool DivEngine::moveInsDown(int which) {
if (which<0 || which>=((int)song.ins.size())-1) return false;
isBusy.lock();
DivInstrument* prev=song.ins[which];
saveLock.lock();
song.ins[which]=song.ins[which+1];
song.ins[which+1]=prev;
exchangeIns(which,which+1);
saveLock.unlock();
isBusy.unlock();
return true;
}
@ -2410,8 +2464,10 @@ bool DivEngine::moveWaveDown(int which) {
if (which<0 || which>=((int)song.wave.size())-1) return false;
isBusy.lock();
DivWavetable* prev=song.wave[which];
saveLock.lock();
song.wave[which]=song.wave[which+1];
song.wave[which+1]=prev;
saveLock.unlock();
isBusy.unlock();
return true;
}
@ -2420,8 +2476,10 @@ bool DivEngine::moveSampleDown(int which) {
if (which<0 || which>=((int)song.sample.size())-1) return false;
isBusy.lock();
DivSample* prev=song.sample[which];
saveLock.lock();
song.sample[which]=song.sample[which+1];
song.sample[which+1]=prev;
saveLock.unlock();
isBusy.unlock();
return true;
}
@ -2462,7 +2520,9 @@ void DivEngine::setOrder(unsigned char order) {
void DivEngine::setSysFlags(int system, unsigned int flags, bool restart) {
isBusy.lock();
saveLock.lock();
song.systemFlags[system]=flags;
saveLock.unlock();
disCont[system].dispatch->setFlags(song.systemFlags[system]);
disCont[system].setRates(got.rate);
if (restart && isPlaying()) {
@ -2473,6 +2533,7 @@ void DivEngine::setSysFlags(int system, unsigned int flags, bool restart) {
void DivEngine::setSongRate(float hz, bool pal) {
isBusy.lock();
saveLock.lock();
song.pal=!pal;
song.hz=hz;
// what?
@ -2487,6 +2548,7 @@ void DivEngine::setSongRate(float hz, bool pal) {
divider=50;
}
}
saveLock.unlock();
isBusy.unlock();
}
@ -2536,6 +2598,20 @@ void DivEngine::synchronized(const std::function<void()>& what) {
isBusy.unlock();
}
void DivEngine::lockSave(const std::function<void()>& what) {
saveLock.lock();
what();
saveLock.unlock();
}
void DivEngine::lockEngine(const std::function<void()>& what) {
isBusy.lock();
saveLock.lock();
what();
saveLock.unlock();
isBusy.unlock();
}
TAAudioDesc& DivEngine::getAudioDescWant() {
return want;
}

View File

@ -201,7 +201,7 @@ class DivEngine {
std::map<String,String> conf;
std::queue<DivNoteEvent> pendingNotes;
bool isMuted[DIV_MAX_CHANS];
std::mutex isBusy;
std::mutex isBusy, saveLock;
String configPath;
String configFile;
String lastError;
@ -278,7 +278,8 @@ class DivEngine {
// save as .dmf.
SafeWriter* saveDMF(unsigned char version);
// save as .fur.
SafeWriter* saveFur();
// if notPrimary is true then the song will not be altered
SafeWriter* saveFur(bool notPrimary=false);
// build a ROM file (TODO).
// specify system to build ROM for.
SafeWriter* buildROM(int sys);
@ -620,6 +621,12 @@ class DivEngine {
// perform secure/sync operation
void synchronized(const std::function<void()>& what);
// perform secure/sync song operation
void lockSave(const std::function<void()>& what);
// perform secure/sync song operation (and lock audio too)
void lockEngine(const std::function<void()>& what);
// get audio desc want
TAAudioDesc& getAudioDescWant();

View File

@ -733,10 +733,12 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
if (active) quitDispatch();
isBusy.lock();
saveLock.lock();
song.unload();
song=ds;
recalcChans();
renderSamples();
saveLock.unlock();
isBusy.unlock();
if (active) {
initDispatch();
@ -1028,6 +1030,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
reader.read(samplePtr,ds.sampleLen*4);
for (int i=0; i<numberOfPats; i++) patPtr.push_back(reader.readI());
logD("reading orders (%d)...\n",ds.ordersLen);
for (int i=0; i<tchans; i++) {
for (int j=0; j<ds.ordersLen; j++) {
ds.orders.ord[i][j]=reader.readC();
@ -1067,6 +1070,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
// read instruments
for (int i=0; i<ds.insLen; i++) {
DivInstrument* ins=new DivInstrument;
logD("reading instrument %d at %x...\n",i,insPtr[i]);
reader.seek(insPtr[i],SEEK_SET);
if (ins->readInsData(reader,ds.version)!=DIV_DATA_SUCCESS) {
@ -1082,6 +1086,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
// read wavetables
for (int i=0; i<ds.waveLen; i++) {
DivWavetable* wave=new DivWavetable;
logD("reading wavetable %d at %x...\n",i,wavePtr[i]);
reader.seek(wavePtr[i],SEEK_SET);
if (wave->readWaveData(reader,ds.version)!=DIV_DATA_SUCCESS) {
@ -1109,6 +1114,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
}
reader.readI();
DivSample* sample=new DivSample;
logD("reading sample %d at %x...\n",i,samplePtr[i]);
sample->name=reader.readString();
sample->samples=reader.readI();
@ -1182,6 +1188,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
for (int i: patPtr) {
reader.seek(i,SEEK_SET);
reader.read(magic,4);
logD("reading pattern in %x...\n",i);
if (strcmp(magic,"PATR")!=0) {
logE("%x: invalid pattern header!\n",i);
lastError="invalid pattern header!";
@ -1194,6 +1201,8 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
int index=reader.readS();
reader.readI();
logD("- %d, %d\n",chan,index);
DivPattern* pat=ds.pat[chan].getPattern(index,true);
for (int j=0; j<ds.patLen; j++) {
pat->data[j][0]=reader.readS();
@ -1219,10 +1228,12 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
if (active) quitDispatch();
isBusy.lock();
saveLock.lock();
song.unload();
song=ds;
recalcChans();
renderSamples();
saveLock.unlock();
isBusy.unlock();
if (active) {
initDispatch();
@ -1583,10 +1594,12 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
if (active) quitDispatch();
isBusy.lock();
saveLock.lock();
song.unload();
song=ds;
recalcChans();
renderSamples();
saveLock.unlock();
isBusy.unlock();
if (active) {
initDispatch();
@ -1728,7 +1741,8 @@ bool DivEngine::load(unsigned char* f, size_t slen) {
return false;
}
SafeWriter* DivEngine::saveFur() {
SafeWriter* DivEngine::saveFur(bool notPrimary) {
saveLock.lock();
int insPtr[256];
int wavePtr[256];
int samplePtr[256];
@ -1736,8 +1750,10 @@ SafeWriter* DivEngine::saveFur() {
size_t ptrSeek;
warnings="";
song.isDMF=false;
song.version=DIV_ENGINE_VERSION;
if (!notPrimary) {
song.isDMF=false;
song.version=DIV_ENGINE_VERSION;
}
SafeWriter* w=new SafeWriter;
w->init();
@ -1967,6 +1983,7 @@ SafeWriter* DivEngine::saveFur() {
w->writeI(i);
}
saveLock.unlock();
return w;
}
@ -2026,6 +2043,7 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
lastError="this system is not possible on .dmf";
return NULL;
}
saveLock.lock();
warnings="";
song.version=version;
song.isDMF=true;
@ -2257,7 +2275,8 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
w->writeC(16);
w->write(i->data16,i->length16);
}
saveLock.unlock();
return w;
}

View File

@ -75,7 +75,7 @@ void DivPlatformAmiga::acquire(short* bufL, short* bufR, size_t start, size_t le
if (s->samples>0) {
chan[i].audDat=s->data8[chan[i].audPos++];
if (chan[i].audPos>=s->samples || chan[i].audPos>=131071) {
if (s->loopStart>=0 && s->loopStart<=(int)s->samples) {
if (s->loopStart>=0 && s->loopStart<(int)s->samples) {
chan[i].audPos=s->loopStart;
} else {
chan[i].sample=-1;

View File

@ -95,7 +95,7 @@ void DivPlatformGenesis::acquire_nuked(short* bufL, short* bufR, size_t start, s
urgentWrite(0x2a,(unsigned char)s->data8[dacPos]+0x80);
}
if (++dacPos>=s->samples) {
if (s->loopStart>=0 && s->loopStart<=(int)s->samples) {
if (s->loopStart>=0 && s->loopStart<(int)s->samples) {
dacPos=s->loopStart;
} else {
dacSample=-1;
@ -162,7 +162,7 @@ void DivPlatformGenesis::acquire_ymfm(short* bufL, short* bufR, size_t start, si
urgentWrite(0x2a,(unsigned char)s->data8[dacPos]+0x80);
}
if (++dacPos>=s->samples) {
if (s->loopStart>=0 && s->loopStart<=(int)s->samples) {
if (s->loopStart>=0 && s->loopStart<(int)s->samples) {
dacPos=s->loopStart;
} else {
dacSample=-1;

View File

@ -81,7 +81,7 @@ void DivPlatformNES::acquire(short* bufL, short* bufR, size_t start, size_t len)
rWrite(0x4011,((unsigned char)s->data8[dacPos]+0x80)>>1);
}
if (++dacPos>=s->samples) {
if (s->loopStart>=0 && s->loopStart<=(int)s->samples) {
if (s->loopStart>=0 && s->loopStart<(int)s->samples) {
dacPos=s->loopStart;
} else {
dacSample=-1;

View File

@ -189,9 +189,6 @@ const char* DivPlatformOPL::getEffectName(unsigned char effect) {
case 0x1d:
return "1Dxx: Set attack of operator 4 (0 to F; 4-op only)";
break;
case 0x20:
return "20xy: Set PSG noise mode (x: preset freq/ch3 freq; y: thin pulse/noise)";
break;
}
return NULL;
}

View File

@ -91,7 +91,7 @@ void DivPlatformPCE::acquire(short* bufL, short* bufR, size_t start, size_t len)
chWrite(i,0x06,(((unsigned char)s->data8[chan[i].dacPos]+0x80)>>3));
chan[i].dacPos++;
if (chan[i].dacPos>=s->samples) {
if (s->loopStart>=0 && s->loopStart<=(int)s->samples) {
if (s->loopStart>=0 && s->loopStart<(int)s->samples) {
chan[i].dacPos=s->loopStart;
} else {
chan[i].dacSample=-1;

285
src/engine/platform/pet.cpp Normal file
View File

@ -0,0 +1,285 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "pet.h"
#include "../engine.h"
#include <math.h>
#define rWrite(a,v) {regPool[(a)]=(v)&0xff; if((a)==10) {chan.sreg=(v); chan.cnt=2;}}
#define CHIP_DIVIDER 16
#define SAMP_DIVIDER 4
const char* regCheatSheet6522[]={
"T2L", "08",
"SR", "0A",
"ACR", "0B",
NULL
};
const char** DivPlatformPET::getRegisterSheet() {
return regCheatSheet6522;
}
const char* DivPlatformPET::getEffectName(unsigned char effect) {
switch (effect) {
case 0x10:
return "10xx: Change waveform";
break;
}
return NULL;
}
void DivPlatformPET::acquire(short* bufL, short* bufR, size_t start, size_t len) {
// high-level emulation of 6522 shift register for now
int t2=regPool[8]*2+4;
if (((regPool[11]>>2)&7)==4) {
for (size_t h=start; h<start+len; h++) {
int cycs=SAMP_DIVIDER;
while (cycs>0) {
int adv=MIN(cycs,chan.cnt);
chan.cnt-=adv;
cycs-=adv;
if (chan.cnt==0) {
chan.out=(chan.sreg&1)*32767;
chan.sreg=(chan.sreg>>1)|((chan.sreg&1)<<7);
chan.cnt=t2;
}
}
bufL[h]=chan.out;
bufR[h]=chan.out;
}
} else {
chan.out=0;
for (size_t h=start; h<start+len; h++) {
bufL[h]=0;
bufR[h]=0;
}
}
}
void DivPlatformPET::writeOutVol() {
if (chan.active && !isMuted && chan.outVol>0) {
if (regPool[11]!=16) {
rWrite(11,16);
rWrite(10,chan.wave);
}
} else {
rWrite(11,0);
}
}
void DivPlatformPET::tick() {
chan.std.next();
if (chan.std.hadVol) {
chan.outVol=chan.std.vol&chan.vol;
writeOutVol();
}
if (chan.std.hadArp) {
if (!chan.inPorta) {
if (chan.std.arpMode) {
chan.baseFreq=NOTE_PERIODIC(chan.std.arp);
} else {
chan.baseFreq=NOTE_PERIODIC(chan.note+chan.std.arp);
}
}
chan.freqChanged=true;
} else {
if (chan.std.arpMode && chan.std.finishedArp) {
chan.baseFreq=NOTE_PERIODIC(chan.note);
chan.freqChanged=true;
}
}
if (chan.std.hadWave) {
if (chan.wave!=chan.std.wave) {
chan.wave=chan.std.wave;
rWrite(10,chan.wave);
}
}
if (chan.freqChanged || chan.keyOn || chan.keyOff) {
chan.freq=parent->calcFreq(chan.baseFreq,chan.pitch,true);
if (chan.freq>257) chan.freq=257;
if (chan.freq<2) chan.freq=2;
rWrite(8,chan.freq-2);
if (chan.keyOn) {
if (!chan.std.willVol) {
chan.outVol=chan.vol;
writeOutVol();
}
chan.keyOn=false;
}
if (chan.keyOff) {
rWrite(11,0);
chan.keyOff=false;
}
chan.freqChanged=false;
}
}
int DivPlatformPET::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan.ins);
if (c.value!=DIV_NOTE_NULL) {
chan.baseFreq=NOTE_PERIODIC(c.value);
chan.freqChanged=true;
chan.note=c.value;
}
chan.active=true;
chan.keyOn=true;
chan.std.init(ins);
break;
}
case DIV_CMD_NOTE_OFF:
chan.active=false;
chan.keyOff=true;
chan.std.init(NULL);
break;
case DIV_CMD_NOTE_OFF_ENV:
case DIV_CMD_ENV_RELEASE:
chan.std.release();
break;
case DIV_CMD_INSTRUMENT:
if (chan.ins!=c.value || c.value2==1) {
chan.ins=c.value;
}
break;
case DIV_CMD_VOLUME:
if (chan.vol!=c.value) {
chan.vol=c.value;
if (!chan.std.hadVol) {
chan.outVol=chan.vol;
writeOutVol();
}
}
break;
case DIV_CMD_GET_VOLUME:
return chan.vol;
break;
case DIV_CMD_PITCH:
chan.pitch=c.value;
chan.freqChanged=true;
break;
case DIV_CMD_WAVE:
chan.wave=c.value;
rWrite(10,chan.wave);
break;
case DIV_CMD_NOTE_PORTA: {
int destFreq=NOTE_PERIODIC(c.value2);
bool return2=false;
if (destFreq>chan.baseFreq) {
chan.baseFreq+=c.value;
if (chan.baseFreq>=destFreq) {
chan.baseFreq=destFreq;
return2=true;
}
} else {
chan.baseFreq-=c.value;
if (chan.baseFreq<=destFreq) {
chan.baseFreq=destFreq;
return2=true;
}
}
chan.freqChanged=true;
if (return2) {
chan.inPorta=false;
return 2;
}
break;
}
case DIV_CMD_LEGATO:
chan.baseFreq=NOTE_PERIODIC(c.value+((chan.std.willArp && !chan.std.arpMode)?(chan.std.arp):(0)));
chan.freqChanged=true;
chan.note=c.value;
break;
case DIV_CMD_PRE_PORTA:
if (chan.active && c.value2) {
if (parent->song.resetMacroOnPorta) chan.std.init(parent->getIns(chan.ins));
}
chan.inPorta=c.value;
break;
case DIV_CMD_GET_VOLMAX:
return 1;
break;
case DIV_ALWAYS_SET_VOLUME:
return 1;
break;
default:
break;
}
return 1;
}
void DivPlatformPET::muteChannel(int ch, bool mute) {
isMuted=mute;
writeOutVol();
}
void DivPlatformPET::forceIns() {
chan.insChanged=true;
chan.freqChanged=true;
writeOutVol();
}
void* DivPlatformPET::getChanState(int ch) {
return &chan;
}
unsigned char* DivPlatformPET::getRegisterPool() {
return regPool;
}
int DivPlatformPET::getRegisterPoolSize() {
return 16;
}
void DivPlatformPET::reset() {
memset(regPool,0,16);
chan=Channel();
}
bool DivPlatformPET::isStereo() {
return false;
}
void DivPlatformPET::notifyInsDeletion(void* ins) {
chan.std.notifyInsDeletion((DivInstrument*)ins);
}
void DivPlatformPET::poke(unsigned int addr, unsigned short val) {
rWrite(addr,val);
}
void DivPlatformPET::poke(std::vector<DivRegWrite>& wlist) {
for (DivRegWrite& i: wlist) rWrite(i.addr,i.val);
}
int DivPlatformPET::init(DivEngine* p, int channels, int sugRate, unsigned int flags) {
parent=p;
dumpWrites=false;
skipRegisterWrites=false;
chipClock=1000000;
rate=chipClock/SAMP_DIVIDER; // = 250000kHz
isMuted=false;
reset();
return 1;
}
DivPlatformPET::~DivPlatformPET() {
}

82
src/engine/platform/pet.h Normal file
View File

@ -0,0 +1,82 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef _PET_H
#define _PET_H
#include "../dispatch.h"
#include "../macroInt.h"
class DivPlatformPET: public DivDispatch {
struct Channel {
int freq, baseFreq, pitch, note;
unsigned char ins;
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta;
int vol, outVol, wave;
unsigned char sreg;
int cnt;
short out;
DivMacroInt std;
Channel():
freq(0),
baseFreq(0),
pitch(0),
note(0),
ins(-1),
active(false),
insChanged(true),
freqChanged(false),
keyOn(false),
keyOff(false),
inPorta(false),
vol(1),
outVol(1),
wave(0b00001111),
sreg(0),
cnt(0),
out(0) {}
};
Channel chan;
bool isMuted;
unsigned char regPool[16];
friend void putDispatchChan(void*,int,int);
public:
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();
void reset();
void forceIns();
void tick();
void muteChannel(int ch, bool mute);
void notifyInsDeletion(void* ins);
bool isStereo();
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
~DivPlatformPET();
private:
void writeOutVol();
};
#endif

View File

@ -54,7 +54,7 @@ void DivPlatformSegaPCM::acquire(short* bufL, short* bufR, size_t start, size_t
}
chan[i].pcm.pos+=chan[i].pcm.freq;
if (chan[i].pcm.pos>=(s->samples<<8)) {
if (s->loopStart>=0 && s->loopStart<=(int)s->samples) {
if (s->loopStart>=0 && s->loopStart<(int)s->samples) {
chan[i].pcm.pos=s->loopStart<<8;
} else {
chan[i].pcm.sample=-1;

View File

@ -84,7 +84,7 @@ void DivPlatformSwan::acquire(short* bufL, short* bufR, size_t start, size_t len
}
rWrite(0x09,(unsigned char)s->data8[dacPos++]+0x80);
if (dacPos>=s->samples) {
if (s->loopStart>=0 && s->loopStart<=(int)s->samples) {
if (s->loopStart>=0 && s->loopStart<(int)s->samples) {
dacPos=s->loopStart;
} else {
dacSample=-1;

View File

@ -57,7 +57,7 @@ const char* DivPlatformVERA::getEffectName(unsigned char effect) {
return "20xx: Change waveform";
break;
case 0x22:
return "22xx: Set duty cycle (0 to 63)";
return "22xx: Set duty cycle (0 to 3F)";
break;
}
return NULL;
@ -97,7 +97,7 @@ void DivPlatformVERA::acquire(short* bufL, short* bufR, size_t start, size_t len
}
chan[16].pcm.pos++;
if (chan[16].pcm.pos>=s->samples) {
if (s->loopStart>=0 && s->loopStart<=(int)s->samples) {
if (s->loopStart>=0 && s->loopStart<(int)s->samples) {
chan[16].pcm.pos=s->loopStart;
} else {
chan[16].pcm.sample=-1;

View File

@ -371,6 +371,7 @@ bool DivEngine::perSystemEffect(int ch, unsigned char effect, unsigned char effe
}
break;
case DIV_SYSTEM_BUBSYS_WSG:
case DIV_SYSTEM_PET:
switch (effect) {
case 0x10: // select waveform
dispatchCmd(DivCommand(DIV_CMD_WAVE,ch,effectVal));
@ -1617,7 +1618,7 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
return;
}
logD("attempts: %d\n",attempts);
//logD("attempts: %d\n",attempts);
if (attempts>=100) {
logE("hang detected! stopping! at %d seconds %d micro\n",totalSeconds,totalTicks);
freelance=false;

View File

@ -179,6 +179,121 @@ bool DivSample::resize(unsigned int count) {
return false;
}
bool DivSample::strip(unsigned int begin, unsigned int end) {
if (begin>samples) begin=samples;
if (end>samples) end=samples;
int count=samples-(end-begin);
if (count<=0) return resize(0);
if (depth==8) {
if (data8!=NULL) {
signed char* oldData8=data8;
data8=NULL;
initInternal(8,count);
if (begin>0) {
memcpy(data8,oldData8,begin);
}
if (samples-end>0) {
memcpy(data8+begin,oldData8+end,samples-end);
}
delete[] oldData8;
} else {
// do nothing
return true;
}
samples=count;
return true;
} else if (depth==16) {
if (data16!=NULL) {
short* oldData16=data16;
data16=NULL;
initInternal(16,count);
if (begin>0) {
memcpy(data16,oldData16,sizeof(short)*begin);
}
if (samples-end>0) {
memcpy(&(data16[begin]),&(oldData16[end]),sizeof(short)*(samples-end));
}
delete[] oldData16;
} else {
// do nothing
return true;
}
samples=count;
return true;
}
return false;
}
bool DivSample::trim(unsigned int begin, unsigned int end) {
int count=end-begin;
if (count==0) return true;
if (begin==0 && end==samples) return true;
if (depth==8) {
if (data8!=NULL) {
signed char* oldData8=data8;
data8=NULL;
initInternal(8,count);
memcpy(data8,oldData8+begin,count);
delete[] oldData8;
} else {
// do nothing
return true;
}
samples=count;
return true;
} else if (depth==16) {
if (data16!=NULL) {
short* oldData16=data16;
data16=NULL;
initInternal(16,count);
memcpy(data16,&(oldData16[begin]),sizeof(short)*count);
delete[] oldData16;
} else {
// do nothing
return true;
}
samples=count;
return true;
}
return false;
}
// TODO: for clipboard
bool DivSample::insert(unsigned int pos, unsigned int length) {
unsigned int count=samples+length;
if (depth==8) {
if (data8!=NULL) {
signed char* oldData8=data8;
data8=NULL;
initInternal(8,count);
if (pos>0) {
memcpy(data8,oldData8,pos);
}
if (count-pos-length>0) {
memcpy(data8+pos+length,oldData8+pos,count-pos-length);
}
delete[] oldData8;
} else {
initInternal(8,count);
}
samples=count;
return true;
} else if (depth==16) {
if (data16!=NULL) {
short* oldData16=data16;
data16=NULL;
initInternal(16,count);
memcpy(data16,oldData16,sizeof(short)*count);
delete[] oldData16;
} else {
initInternal(16,count);
}
samples=count;
return true;
}
return false;
}
#define RESAMPLE_BEGIN \
if (samples<1) return true; \
int finalCount=(double)samples*(r/(double)rate); \
@ -199,6 +314,8 @@ bool DivSample::resize(unsigned int count) {
}
#define RESAMPLE_END \
if (loopStart>=0) loopStart=(double)loopStart*(r/(double)rate); \
rate=r; \
samples=finalCount; \
if (depth==16) { \
delete[] oldData16; \
@ -229,8 +346,6 @@ bool DivSample::resampleNone(double r) {
}
}
rate=r;
RESAMPLE_END;
return true;
}
@ -270,8 +385,6 @@ bool DivSample::resampleLinear(double r) {
}
}
rate=r;
RESAMPLE_END;
return true;
}
@ -326,8 +439,6 @@ bool DivSample::resampleCubic(double r) {
}
}
rate=r;
RESAMPLE_END;
return true;
}
@ -380,7 +491,7 @@ bool DivSample::resampleBlep(double r) {
}
}
} else if (depth==8) {
memset(data8,0,finalCount*sizeof(short));
memset(data8,0,finalCount);
for (int i=0; i<finalCount; i++) {
if (posInt<samples) {
int result=data8[i]+oldData8[posInt];
@ -417,8 +528,6 @@ bool DivSample::resampleBlep(double r) {
}
}
rate=r;
RESAMPLE_END;
return true;
}
@ -486,8 +595,6 @@ bool DivSample::resampleSinc(double r) {
}
}
rate=r;
RESAMPLE_END;
return true;
}

View File

@ -102,6 +102,33 @@ struct DivSample {
*/
bool resize(unsigned int count);
/**
* remove part of the sample data.
* @warning do not attempt to strip a sample outside of a synchronized block!
* @param start the beginning.
* @param end the end.
* @return whether it was successful.
*/
bool strip(unsigned int begin, unsigned int end);
/**
* clip the sample data to specified boundaries.
* @warning do not attempt to trim a sample outside of a synchronized block!
* @param start the beginning.
* @param end the end.
* @return whether it was successful.
*/
bool trim(unsigned int begin, unsigned int end);
/**
* insert silence at specified position.
* @warning do not attempt to do this outside of a synchronized block!
* @param pos the beginning.
* @param length how many samples to insert.
* @return whether it was successful.
*/
bool insert(unsigned int pos, unsigned int length);
/**
* change the sample rate.
* @warning do not attempt to resample outside of a synchronized block!

213
src/gui/about.cpp Normal file
View File

@ -0,0 +1,213 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#define _USE_MATH_DEFINES
#include "gui.h"
#include <math.h>
const char* aboutLine[]={
"tildearrow",
"is proud to present",
"",
("Furnace " DIV_VERSION),
"",
"the free software chiptune tracker,",
"compatible with DefleMask modules.",
"",
"zero disassembly.",
"just clean-room design,",
"time and dedication.",
"",
"> CREDITS <",
"",
"-- program --",
"tildearrow",
"akumanatt",
"cam900",
"djtuBIG-MaliceX",
"laoo",
"superctr",
"",
"-- graphics/UI design --",
"tildearrow",
"BlastBrothers",
"",
"-- documentation --",
"tildearrow",
"freq-mod",
"nicco1690",
"DeMOSic",
"cam900",
"",
"-- demo songs --",
"0x5066",
"ActualNK358",
"breakthetargets",
"CaptainMalware",
"kleeder",
"Mahbod Karamoozian",
"nicco1690",
"NikonTeen",
"SuperJet Spade",
"TheDuccinator",
"TheRealHedgehogSonic",
"tildearrow",
"Ultraprogramer",
"",
"-- additional feedback/fixes --",
"fd",
"OPNA2608",
"plane",
"TheEssem",
"",
"powered by:",
"Dear ImGui by Omar Cornut",
"SDL2 by Sam Lantinga",
"zlib by Jean-loup Gailly",
"and Mark Adler",
"libsndfile by Erik de Castro Lopo",
"Nuked-OPM & Nuked-OPN2 by Nuke.YKT",
"ymfm by Aaron Giles",
"MAME SN76496 by Nicola Salmoria",
"MAME AY-3-8910 by Couriersud",
"with AY8930 fixes by Eulous",
"MAME SAA1099 by Juergen Buchmueller and Manuel Abadia",
"SAASound",
"SameBoy by Lior Halphon",
"Mednafen PCE",
"puNES by FHorse",
"reSID by Dag Lem",
"Stella by Stella Team",
"QSound emulator by Ian Karlsson and Valley Bell",
"",
"greetings to:",
"Delek",
"fd",
"ILLUMIDARO",
"all members of Deflers of Noice!",
"",
"copyright © 2021-2022 tildearrow",
"(and contributors).",
"licensed under GPLv2+! see",
"LICENSE for more information.",
"",
"help Furnace grow:",
"https://github.com/tildearrow/furnace",
"",
"contact tildearrow at:",
"https://tildearrow.org/?p=contact",
"",
"disclaimer:",
"despite the fact this program works",
"with the .dmf file format, it is NOT",
"affiliated with Delek or DefleMask in",
"any way, nor it is a replacement for",
"the original program.",
"",
"it also comes with ABSOLUTELY NO WARRANTY.",
"",
"thanks to all contributors/bug reporters!"
};
const size_t aboutCount=sizeof(aboutLine)/sizeof(aboutLine[0]);
void FurnaceGUI::drawAbout() {
// do stuff
if (ImGui::Begin("About Furnace",NULL,ImGuiWindowFlags_Modal|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoDocking|ImGuiWindowFlags_NoTitleBar)) {
ImGui::SetWindowPos(ImVec2(0,0));
ImGui::SetWindowSize(ImVec2(scrW*dpiScale,scrH*dpiScale));
ImGui::PushFont(bigFont);
ImDrawList* dl=ImGui::GetWindowDrawList();
float r=0;
float g=0;
float b=0;
float peakMix=settings.partyTime?((peak[0]+peak[1])*0.5):0.3;
ImGui::ColorConvertHSVtoRGB(aboutHue,1.0,0.25+MIN(0.75f,peakMix*0.75f),r,g,b);
dl->AddRectFilled(ImVec2(0,0),ImVec2(scrW*dpiScale,scrH*dpiScale),0xff000000);
bool skip=false;
bool skip2=false;
for (int i=(-80-sin(double(aboutSin)*2*M_PI/120.0)*80.0)*2; i<scrW; i+=160) {
skip2=!skip2;
skip=skip2;
for (int j=(-80-cos(double(aboutSin)*2*M_PI/150.0)*80.0)*2; j<scrH; j+=160) {
skip=!skip;
if (skip) continue;
dl->AddRectFilled(ImVec2(i*dpiScale,j*dpiScale),ImVec2((i+160)*dpiScale,(j+160)*dpiScale),ImGui::GetColorU32(ImVec4(r*0.25,g*0.25,b*0.25,1.0)));
}
}
skip=false;
skip2=false;
for (int i=(-80-cos(double(aboutSin)*2*M_PI/120.0)*80.0)*2; i<scrW; i+=160) {
skip2=!skip2;
skip=skip2;
for (int j=(-80-sin(double(aboutSin)*2*M_PI/150.0)*80.0)*2; j<scrH; j+=160) {
skip=!skip;
if (skip) continue;
dl->AddRectFilled(ImVec2(i*dpiScale,j*dpiScale),ImVec2((i+160)*dpiScale,(j+160)*dpiScale),ImGui::GetColorU32(ImVec4(r*0.5,g*0.5,b*0.5,1.0)));
}
}
skip=false;
skip2=false;
for (int i=(-160+fmod(aboutSin*2,160))*2; i<scrW; i+=160) {
skip2=!skip2;
skip=skip2;
for (int j=(-240-cos(double(aboutSin*M_PI/300.0))*240.0)*2; j<scrH; j+=160) {
skip=!skip;
if (skip) continue;
dl->AddRectFilled(ImVec2(i*dpiScale,j*dpiScale),ImVec2((i+160)*dpiScale,(j+160)*dpiScale),ImGui::GetColorU32(ImVec4(r*0.75,g*0.75,b*0.75,1.0)));
}
}
for (size_t i=0; i<aboutCount; i++) {
double posX=(scrW*dpiScale/2.0)+(sin(double(i)*0.5+double(aboutScroll)/90.0)*120*dpiScale)-(ImGui::CalcTextSize(aboutLine[i]).x*0.5);
double posY=(scrH-aboutScroll+42*i)*dpiScale;
if (posY<-80*dpiScale || posY>scrH*dpiScale) continue;
dl->AddText(bigFont,bigFont->FontSize,
ImVec2(posX+dpiScale,posY+dpiScale),
0xff000000,aboutLine[i]);
dl->AddText(bigFont,bigFont->FontSize,
ImVec2(posX+dpiScale,posY-dpiScale),
0xff000000,aboutLine[i]);
dl->AddText(bigFont,bigFont->FontSize,
ImVec2(posX-dpiScale,posY+dpiScale),
0xff000000,aboutLine[i]);
dl->AddText(bigFont,bigFont->FontSize,
ImVec2(posX-dpiScale,posY-dpiScale),
0xff000000,aboutLine[i]);
dl->AddText(bigFont,bigFont->FontSize,
ImVec2(posX,posY),
0xffffffff,aboutLine[i]);
}
ImGui::PopFont();
float timeScale=60.0f*ImGui::GetIO().DeltaTime;
aboutHue+=(0.001+peakMix*0.004)*timeScale;
aboutScroll+=(2+(peakMix>0.78)*3)*timeScale;
aboutSin+=(1+(peakMix>0.75)*2)*timeScale;
while (aboutHue>1) aboutHue--;
while (aboutSin>=2400) aboutSin-=2400;
if (aboutScroll>(42*aboutCount+scrH)) aboutScroll=-20;
}
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_ABOUT;
ImGui::End();
}

41
src/gui/actionUtil.h Normal file
View File

@ -0,0 +1,41 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#define DETERMINE_FIRST \
int firstChannel=0; \
for (int i=0; i<e->getTotalChannelCount(); i++) { \
if (e->song.chanShow[i]) { \
firstChannel=i; \
break; \
} \
}
#define DETERMINE_LAST \
int lastChannel=0; \
for (int i=e->getTotalChannelCount()-1; i>=0; i--) { \
if (e->song.chanShow[i]) { \
lastChannel=i+1; \
break; \
} \
}
#define DETERMINE_FIRST_LAST \
DETERMINE_FIRST \
DETERMINE_LAST

53
src/gui/channels.cpp Normal file
View File

@ -0,0 +1,53 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "gui.h"
#include "misc/cpp/imgui_stdlib.h"
void FurnaceGUI::drawChannels() {
if (nextWindow==GUI_WINDOW_CHANNELS) {
channelsOpen=true;
ImGui::SetNextWindowFocus();
nextWindow=GUI_WINDOW_NOTHING;
}
if (!channelsOpen) return;
if (ImGui::Begin("Channels",&channelsOpen)) {
if (ImGui::BeginTable("ChannelList",3)) {
ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed,0.0);
ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.0);
ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthFixed,48.0f*dpiScale);
for (int i=0; i<e->getTotalChannelCount(); i++) {
ImGui::PushID(i);
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Checkbox("##Visible",&e->song.chanShow[i]);
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
ImGui::InputTextWithHint("##ChanName",e->getChannelName(i),&e->song.chanName[i]);
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
ImGui::InputTextWithHint("##ChanShortName",e->getChannelShortName(i),&e->song.chanShortName[i]);
ImGui::PopID();
}
ImGui::EndTable();
}
}
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_CHANNELS;
ImGui::End();
}

136
src/gui/compatFlags.cpp Normal file
View File

@ -0,0 +1,136 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "gui.h"
void FurnaceGUI::drawCompatFlags() {
if (nextWindow==GUI_WINDOW_COMPAT_FLAGS) {
compatFlagsOpen=true;
ImGui::SetNextWindowFocus();
nextWindow=GUI_WINDOW_NOTHING;
}
if (!compatFlagsOpen) return;
if (ImGui::Begin("Compatibility Flags",&compatFlagsOpen)) {
ImGui::TextWrapped("these flags are designed to provide better DefleMask/older Furnace compatibility.");
ImGui::Checkbox("Limit slide range",&e->song.limitSlides);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("when enabled, slides are limited to a compatible range.\nmay cause problems with slides in negative octaves.");
}
ImGui::Checkbox("Linear pitch control",&e->song.linearPitch);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("linear pitch:\n- slides work in frequency/period space\n- E5xx and 04xx effects work in tonality space\nnon-linear pitch:\n- slides work in frequency/period space\n- E5xx and 04xx effects work on frequency/period space");
}
ImGui::Checkbox("Proper noise layout on NES and PC Engine",&e->song.properNoiseLayout);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("use a proper noise channel note mapping (0-15) instead of a rather unusual compatible one.\nunlocks all noise frequencies on PC Engine.");
}
ImGui::Checkbox("Game Boy instrument duty is wave volume",&e->song.waveDutyIsVol);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("if enabled, an instrument with duty macro in the wave channel will be mapped to wavetable volume.");
}
ImGui::Checkbox("Restart macro on portamento",&e->song.resetMacroOnPorta);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("when enabled, a portamento effect will reset the channel's macro if used in combination with a note.");
}
ImGui::Checkbox("Legacy volume slides",&e->song.legacyVolumeSlides);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("simulate glitchy volume slide behavior by silently overflowing the volume when the slide goes below 0.");
}
ImGui::Checkbox("Compatible arpeggio",&e->song.compatibleArpeggio);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("delay arpeggio by one tick on every new note.");
}
ImGui::Checkbox("Reset slides after note off",&e->song.noteOffResetsSlides);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("when enabled, note off will reset the channel's slide effect.");
}
ImGui::Checkbox("Reset portamento after reaching target",&e->song.targetResetsSlides);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("when enabled, the slide effect is disabled after it reaches its target.");
}
ImGui::Checkbox("Ignore duplicate slide effects",&e->song.ignoreDuplicateSlides);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("if this is on, only the first slide of a row in a channel will be considered.");
}
ImGui::Checkbox("Continuous vibrato",&e->song.continuousVibrato);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("when enabled, vibrato will not be reset on a new note.");
}
ImGui::Checkbox("Broken DAC mode",&e->song.brokenDACMode);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("when enabled, the DAC in YM2612 will be disabled if there isn't any sample playing.");
}
ImGui::Checkbox("Auto-insert one tick gap between notes",&e->song.oneTickCut);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("when enabled, a one-tick note cut will be inserted between non-legato/non-portamento notes.\nthis simulates the behavior of some Amiga/SNES music engines.");
}
ImGui::Text("Loop modality:");
if (ImGui::RadioButton("Reset channels",e->song.loopModality==0)) {
e->song.loopModality=0;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("select to reset channels on loop. may trigger a voltage click on every loop!");
}
if (ImGui::RadioButton("Soft reset channels",e->song.loopModality==1)) {
e->song.loopModality=1;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("select to turn channels off on loop.");
}
if (ImGui::RadioButton("Do nothing",e->song.loopModality==2)) {
e->song.loopModality=2;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("select to not reset channels on loop.");
}
ImGui::Separator();
ImGui::TextWrapped("the following flags are for compatibility with older Furnace versions.");
ImGui::Checkbox("Arpeggio inhibits non-porta slides",&e->song.arpNonPorta);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("behavior changed in 0.5.5");
}
ImGui::Checkbox("Wack FM algorithm macro",&e->song.algMacroBehavior);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("behavior changed in 0.5.5");
}
ImGui::Checkbox("Broken shortcut slides (E1xy/E2xy)",&e->song.brokenShortcutSlides);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("behavior changed in 0.5.7");
}
ImGui::Checkbox("Stop portamento on note off",&e->song.stopPortaOnNoteOff);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("behavior changed in 0.6");
}
ImGui::Checkbox("Allow instrument change during slides",&e->song.newInsTriggersInPorta);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("behavior changed in 0.6");
}
ImGui::Checkbox("Reset note to base on arpeggio stop",&e->song.arp0Reset);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("behavior changed in 0.6");
}
}
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_COMPAT_FLAGS;
ImGui::End();
}

285
src/gui/cursor.cpp Normal file
View File

@ -0,0 +1,285 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "gui.h"
#include "actionUtil.h"
void FurnaceGUI::startSelection(int xCoarse, int xFine, int y) {
if (xCoarse!=selStart.xCoarse || xFine!=selStart.xFine || y!=selStart.y) {
curNibble=false;
}
cursor.xCoarse=xCoarse;
cursor.xFine=xFine;
cursor.y=y;
selStart.xCoarse=xCoarse;
selStart.xFine=xFine;
selStart.y=y;
selEnd.xCoarse=xCoarse;
selEnd.xFine=xFine;
selEnd.y=y;
selecting=true;
}
void FurnaceGUI::updateSelection(int xCoarse, int xFine, int y) {
if (!selecting) return;
selEnd.xCoarse=xCoarse;
selEnd.xFine=xFine;
selEnd.y=y;
}
void FurnaceGUI::finishSelection() {
// swap points if needed
if (selEnd.y<selStart.y) {
selEnd.y^=selStart.y;
selStart.y^=selEnd.y;
selEnd.y^=selStart.y;
}
if (selEnd.xCoarse<selStart.xCoarse) {
selEnd.xCoarse^=selStart.xCoarse;
selStart.xCoarse^=selEnd.xCoarse;
selEnd.xCoarse^=selStart.xCoarse;
selEnd.xFine^=selStart.xFine;
selStart.xFine^=selEnd.xFine;
selEnd.xFine^=selStart.xFine;
} else if (selEnd.xCoarse==selStart.xCoarse && selEnd.xFine<selStart.xFine) {
selEnd.xFine^=selStart.xFine;
selStart.xFine^=selEnd.xFine;
selEnd.xFine^=selStart.xFine;
}
selecting=false;
// boundary check
int chanCount=e->getTotalChannelCount();
if (selStart.xCoarse<0) selStart.xCoarse=0;
if (selStart.xCoarse>=chanCount) selStart.xCoarse=chanCount-1;
if (selStart.y<0) selStart.y=0;
if (selStart.y>=e->song.patLen) selStart.y=e->song.patLen-1;
if (selEnd.xCoarse<0) selEnd.xCoarse=0;
if (selEnd.xCoarse>=chanCount) selEnd.xCoarse=chanCount-1;
if (selEnd.y<0) selEnd.y=0;
if (selEnd.y>=e->song.patLen) selEnd.y=e->song.patLen-1;
if (cursor.xCoarse<0) cursor.xCoarse=0;
if (cursor.xCoarse>=chanCount) cursor.xCoarse=chanCount-1;
if (cursor.y<0) cursor.y=0;
if (cursor.y>=e->song.patLen) cursor.y=e->song.patLen-1;
if (e->song.chanCollapse[selEnd.xCoarse]) {
selStart.xFine=0;
}
if (e->song.chanCollapse[selEnd.xCoarse]) {
selEnd.xFine=2+e->song.pat[cursor.xCoarse].effectRows*2;
}
}
void FurnaceGUI::moveCursor(int x, int y, bool select) {
if (!select) {
finishSelection();
}
DETERMINE_FIRST_LAST;
curNibble=false;
if (x!=0) {
demandScrollX=true;
if (x>0) {
for (int i=0; i<x; i++) {
if (++cursor.xFine>=(e->song.chanCollapse[cursor.xCoarse]?1:(3+e->song.pat[cursor.xCoarse].effectRows*2))) {
cursor.xFine=0;
if (++cursor.xCoarse>=lastChannel) {
if (settings.wrapHorizontal!=0 && !select) {
cursor.xCoarse=firstChannel;
if (settings.wrapHorizontal==2) y++;
} else {
cursor.xCoarse=lastChannel-1;
cursor.xFine=e->song.chanCollapse[cursor.xCoarse]?0:(2+e->song.pat[cursor.xCoarse].effectRows*2);
}
} else {
while (!e->song.chanShow[cursor.xCoarse]) {
cursor.xCoarse++;
if (cursor.xCoarse>=e->getTotalChannelCount()) break;
}
}
}
}
} else {
for (int i=0; i<-x; i++) {
if (--cursor.xFine<0) {
if (--cursor.xCoarse<firstChannel) {
if (settings.wrapHorizontal!=0 && !select) {
cursor.xCoarse=lastChannel-1;
cursor.xFine=2+e->song.pat[cursor.xCoarse].effectRows*2;
if (settings.wrapHorizontal==2) y--;
} else {
cursor.xCoarse=firstChannel;
cursor.xFine=0;
}
} else {
while (!e->song.chanShow[cursor.xCoarse]) {
cursor.xCoarse--;
if (cursor.xCoarse<0) break;
}
if (e->song.chanCollapse[cursor.xCoarse]) {
cursor.xFine=0;
} else {
cursor.xFine=2+e->song.pat[cursor.xCoarse].effectRows*2;
}
}
}
}
}
}
if (y!=0) {
if (y>0) {
for (int i=0; i<y; i++) {
cursor.y++;
if (cursor.y>=e->song.patLen) {
if (settings.wrapVertical!=0 && !select) {
cursor.y=0;
if (settings.wrapVertical==2) {
if (!e->isPlaying() && e->getOrder()<(e->song.ordersLen-1)) {
e->setOrder(e->getOrder()+1);
} else {
cursor.y=e->song.patLen-1;
}
}
} else {
cursor.y=e->song.patLen-1;
}
}
}
} else {
for (int i=0; i<-y; i++) {
cursor.y--;
if (cursor.y<0) {
if (settings.wrapVertical!=0 && !select) {
cursor.y=e->song.patLen-1;
if (settings.wrapVertical==2) {
if (!e->isPlaying() && e->getOrder()>0) {
e->setOrder(e->getOrder()-1);
} else {
cursor.y=0;
}
}
} else {
cursor.y=0;
}
}
}
}
}
if (!select) {
selStart=cursor;
}
selEnd=cursor;
updateScroll(cursor.y);
}
void FurnaceGUI::moveCursorPrevChannel(bool overflow) {
finishSelection();
curNibble=false;
DETERMINE_FIRST_LAST;
do {
cursor.xCoarse--;
if (cursor.xCoarse<0) break;
} while (!e->song.chanShow[cursor.xCoarse]);
if (cursor.xCoarse<firstChannel) {
if (overflow) {
cursor.xCoarse=lastChannel-1;
} else {
cursor.xCoarse=firstChannel;
}
}
selStart=cursor;
selEnd=cursor;
demandScrollX=true;
}
void FurnaceGUI::moveCursorNextChannel(bool overflow) {
finishSelection();
curNibble=false;
DETERMINE_FIRST_LAST;
do {
cursor.xCoarse++;
if (cursor.xCoarse>=e->getTotalChannelCount()) break;
} while (!e->song.chanShow[cursor.xCoarse]);
if (cursor.xCoarse>=lastChannel) {
if (overflow) {
cursor.xCoarse=firstChannel;
} else {
cursor.xCoarse=lastChannel-1;
}
}
selStart=cursor;
selEnd=cursor;
demandScrollX=true;
}
void FurnaceGUI::moveCursorTop(bool select) {
finishSelection();
curNibble=false;
if (cursor.y==0) {
DETERMINE_FIRST;
cursor.xCoarse=firstChannel;
cursor.xFine=0;
demandScrollX=true;
} else {
cursor.y=0;
}
selStart=cursor;
if (!select) {
selEnd=cursor;
}
updateScroll(cursor.y);
}
void FurnaceGUI::moveCursorBottom(bool select) {
finishSelection();
curNibble=false;
if (cursor.y==e->song.patLen-1) {
DETERMINE_LAST;
cursor.xCoarse=lastChannel-1;
cursor.xFine=2+e->song.pat[cursor.xCoarse].effectRows*2;
demandScrollX=true;
} else {
cursor.y=e->song.patLen-1;
}
if (!select) {
selStart=cursor;
}
selEnd=cursor;
updateScroll(cursor.y);
}
void FurnaceGUI::editAdvance() {
finishSelection();
cursor.y+=editStep;
if (cursor.y>=e->song.patLen) cursor.y=e->song.patLen-1;
selStart=cursor;
selEnd=cursor;
updateScroll(cursor.y);
}

367
src/gui/dataList.cpp Normal file
View File

@ -0,0 +1,367 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "gui.h"
#include "IconsFontAwesome4.h"
#include "misc/cpp/imgui_stdlib.h"
#include "plot_nolerp.h"
#include "guiConst.h"
#include <fmt/printf.h>
const char* sampleNote[12]={
"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"
};
void FurnaceGUI::drawInsList() {
if (nextWindow==GUI_WINDOW_INS_LIST) {
insListOpen=true;
ImGui::SetNextWindowFocus();
nextWindow=GUI_WINDOW_NOTHING;
}
if (!insListOpen) return;
if (ImGui::Begin("Instruments",&insListOpen)) {
if (ImGui::Button(ICON_FA_PLUS "##InsAdd")) {
doAction(GUI_ACTION_INS_LIST_ADD);
}
ImGui::SameLine();
if (ImGui::Button(ICON_FA_FILES_O "##InsClone")) {
doAction(GUI_ACTION_INS_LIST_DUPLICATE);
}
ImGui::SameLine();
if (ImGui::Button(ICON_FA_FOLDER_OPEN "##InsLoad")) {
doAction(GUI_ACTION_INS_LIST_OPEN);
}
ImGui::SameLine();
if (ImGui::Button(ICON_FA_FLOPPY_O "##InsSave")) {
doAction(GUI_ACTION_INS_LIST_SAVE);
}
ImGui::SameLine();
if (ImGui::ArrowButton("InsUp",ImGuiDir_Up)) {
doAction(GUI_ACTION_INS_LIST_MOVE_UP);
}
ImGui::SameLine();
if (ImGui::ArrowButton("InsDown",ImGuiDir_Down)) {
doAction(GUI_ACTION_INS_LIST_MOVE_DOWN);
}
ImGui::SameLine();
if (ImGui::Button(ICON_FA_TIMES "##InsDelete")) {
doAction(GUI_ACTION_INS_LIST_DELETE);
}
ImGui::Separator();
if (ImGui::BeginTable("InsListScroll",1,ImGuiTableFlags_ScrollY)) {
if (settings.unifiedDataView) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text(ICON_FA_TASKS " Instruments");
ImGui::Indent();
}
for (int i=0; i<(int)e->song.ins.size(); i++) {
DivInstrument* ins=e->song.ins[i];
String name;
switch (ins->type) {
case DIV_INS_FM:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_FM]);
name=fmt::sprintf(ICON_FA_AREA_CHART " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_STD:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_STD]);
name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_GB:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_GB]);
name=fmt::sprintf(ICON_FA_GAMEPAD " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_C64:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_C64]);
name=fmt::sprintf(ICON_FA_KEYBOARD_O " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_AMIGA:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_AMIGA]);
name=fmt::sprintf(ICON_FA_VOLUME_UP " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_PCE:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_PCE]);
name=fmt::sprintf(ICON_FA_ID_BADGE " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_AY:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_AY]);
name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_AY8930:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_AY8930]);
name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_TIA:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_TIA]);
name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_SAA1099:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_SAA1099]);
name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_VIC:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VIC]);
name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_PET:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_PET]);
name=fmt::sprintf(ICON_FA_SQUARE " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_VRC6:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VRC6]);
name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_OPLL:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_OPLL]);
name=fmt::sprintf(ICON_FA_AREA_CHART " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_OPL:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_OPL]);
name=fmt::sprintf(ICON_FA_AREA_CHART " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_FDS:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_FDS]);
name=fmt::sprintf(ICON_FA_FLOPPY_O " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_VBOY:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VBOY]);
name=fmt::sprintf(ICON_FA_BINOCULARS " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_N163:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_N163]);
name=fmt::sprintf(ICON_FA_CALCULATOR " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_SCC:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_SCC]);
name=fmt::sprintf(ICON_FA_CALCULATOR " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_OPZ:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_OPZ]);
name=fmt::sprintf(ICON_FA_AREA_CHART " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_POKEY:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_POKEY]);
name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_BEEPER:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_BEEPER]);
name=fmt::sprintf(ICON_FA_SQUARE " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_SWAN:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_SWAN]);
name=fmt::sprintf(ICON_FA_GAMEPAD " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_MIKEY:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_MIKEY]);
name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_VERA:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VERA]);
name=fmt::sprintf(ICON_FA_KEYBOARD_O " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_X1_010:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_X1_010]);
name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i);
break;
default:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_UNKNOWN]);
name=fmt::sprintf(ICON_FA_QUESTION " %.2X: %s##_INS%d",i,ins->name,i);
break;
}
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (ImGui::Selectable(name.c_str(),curIns==i)) {
curIns=i;
}
if (settings.insFocusesPattern && patternOpen && ImGui::IsItemActivated()) {
nextWindow=GUI_WINDOW_PATTERN;
curIns=i;
}
ImGui::PopStyleColor();
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("%s",(ins->type>DIV_INS_MAX)?"Unknown":insTypes[ins->type]);
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
insEditOpen=true;
nextWindow=GUI_WINDOW_INS_EDIT;
}
}
}
if (settings.unifiedDataView) {
ImGui::Unindent();
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text(ICON_FA_AREA_CHART " Wavetables");
ImGui::Indent();
actualWaveList();
ImGui::Unindent();
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text(ICON_FA_VOLUME_UP " Samples");
ImGui::Indent();
actualSampleList();
ImGui::Unindent();
}
ImGui::EndTable();
}
}
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_INS_LIST;
ImGui::End();
}
void FurnaceGUI::drawWaveList() {
if (nextWindow==GUI_WINDOW_WAVE_LIST) {
waveListOpen=true;
ImGui::SetNextWindowFocus();
nextWindow=GUI_WINDOW_NOTHING;
}
if (!waveListOpen) return;
if (ImGui::Begin("Wavetables",&waveListOpen)) {
if (ImGui::Button(ICON_FA_PLUS "##WaveAdd")) {
doAction(GUI_ACTION_WAVE_LIST_ADD);
}
ImGui::SameLine();
if (ImGui::Button(ICON_FA_FILES_O "##WaveClone")) {
doAction(GUI_ACTION_WAVE_LIST_DUPLICATE);
}
ImGui::SameLine();
if (ImGui::Button(ICON_FA_FOLDER_OPEN "##WaveLoad")) {
doAction(GUI_ACTION_WAVE_LIST_OPEN);
}
ImGui::SameLine();
if (ImGui::Button(ICON_FA_FLOPPY_O "##WaveSave")) {
doAction(GUI_ACTION_WAVE_LIST_SAVE);
}
ImGui::SameLine();
if (ImGui::ArrowButton("WaveUp",ImGuiDir_Up)) {
doAction(GUI_ACTION_WAVE_LIST_UP);
}
ImGui::SameLine();
if (ImGui::ArrowButton("WaveDown",ImGuiDir_Down)) {
doAction(GUI_ACTION_WAVE_LIST_DOWN);
}
ImGui::SameLine();
if (ImGui::Button(ICON_FA_TIMES "##WaveDelete")) {
doAction(GUI_ACTION_WAVE_LIST_DELETE);
}
ImGui::Separator();
if (ImGui::BeginTable("WaveListScroll",1,ImGuiTableFlags_ScrollY)) {
actualWaveList();
ImGui::EndTable();
}
}
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_WAVE_LIST;
ImGui::End();
}
void FurnaceGUI::drawSampleList() {
if (nextWindow==GUI_WINDOW_SAMPLE_LIST) {
sampleListOpen=true;
ImGui::SetNextWindowFocus();
nextWindow=GUI_WINDOW_NOTHING;
}
if (!sampleListOpen) return;
if (ImGui::Begin("Samples",&sampleListOpen)) {
if (ImGui::Button(ICON_FA_PLUS "##SampleAdd")) {
doAction(GUI_ACTION_SAMPLE_LIST_ADD);
}
ImGui::SameLine();
if (ImGui::Button(ICON_FA_FOLDER_OPEN "##SampleLoad")) {
doAction(GUI_ACTION_SAMPLE_LIST_OPEN);
}
ImGui::SameLine();
if (ImGui::Button(ICON_FA_FLOPPY_O "##SampleSave")) {
doAction(GUI_ACTION_SAMPLE_LIST_SAVE);
}
ImGui::SameLine();
if (ImGui::ArrowButton("SampleUp",ImGuiDir_Up)) {
doAction(GUI_ACTION_SAMPLE_LIST_MOVE_UP);
}
ImGui::SameLine();
if (ImGui::ArrowButton("SampleDown",ImGuiDir_Down)) {
doAction(GUI_ACTION_SAMPLE_LIST_MOVE_DOWN);
}
ImGui::SameLine();
if (ImGui::Button(ICON_FA_TIMES "##SampleDelete")) {
doAction(GUI_ACTION_SAMPLE_LIST_DELETE);
}
ImGui::SameLine();
if (ImGui::Button(ICON_FA_VOLUME_UP "##PreviewSampleL")) {
doAction(GUI_ACTION_SAMPLE_LIST_PREVIEW);
}
ImGui::SameLine();
if (ImGui::Button(ICON_FA_VOLUME_OFF "##StopSampleL")) {
doAction(GUI_ACTION_SAMPLE_LIST_STOP_PREVIEW);
}
ImGui::Separator();
if (ImGui::BeginTable("SampleListScroll",1,ImGuiTableFlags_ScrollY)) {
actualSampleList();
ImGui::EndTable();
}
ImGui::Unindent();
}
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_SAMPLE_LIST;
ImGui::End();
}
void FurnaceGUI::actualWaveList() {
float wavePreview[256];
for (int i=0; i<(int)e->song.wave.size(); i++) {
DivWavetable* wave=e->song.wave[i];
for (int i=0; i<wave->len; i++) {
wavePreview[i]=wave->data[i];
}
if (wave->len>0) wavePreview[wave->len]=wave->data[wave->len-1];
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (ImGui::Selectable(fmt::sprintf("%d##_WAVE%d\n",i,i).c_str(),curWave==i)) {
curWave=i;
}
if (ImGui::IsItemHovered()) {
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
waveEditOpen=true;
}
}
ImGui::SameLine();
PlotNoLerp(fmt::sprintf("##_WAVEP%d",i).c_str(),wavePreview,wave->len+1,0,NULL,0,wave->max);
}
}
void FurnaceGUI::actualSampleList() {
for (int i=0; i<(int)e->song.sample.size(); i++) {
DivSample* sample=e->song.sample[i];
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (ImGui::Selectable(fmt::sprintf("%d: %s##_SAM%d",i,sample->name,i).c_str(),curSample==i)) {
curSample=i;
samplePos=0;
updateSampleTex=true;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Bank %d: %s",i/12,sampleNote[i%12]);
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
sampleEditOpen=true;
}
}
}
}

250
src/gui/debugWindow.cpp Normal file
View File

@ -0,0 +1,250 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "gui.h"
#include "debug.h"
#include "IconsFontAwesome4.h"
#include <fmt/printf.h>
void FurnaceGUI::drawDebug() {
static int bpOrder;
static int bpRow;
static int bpTick;
static bool bpOn;
if (nextWindow==GUI_WINDOW_DEBUG) {
debugOpen=true;
ImGui::SetNextWindowFocus();
nextWindow=GUI_WINDOW_NOTHING;
}
if (!debugOpen) return;
ImGui::SetNextWindowSizeConstraints(ImVec2(400.0f*dpiScale,200.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale));
if (ImGui::Begin("Debug",&debugOpen,ImGuiWindowFlags_NoDocking)) {
ImGui::Text("NOTE: use with caution.");
if (ImGui::TreeNode("Debug Controls")) {
if (e->isHalted()) {
if (ImGui::Button("Resume")) e->resume();
} else {
if (ImGui::Button("Pause")) e->halt();
}
ImGui::SameLine();
if (ImGui::Button("Frame Advance")) e->haltWhen(DIV_HALT_TICK);
ImGui::SameLine();
if (ImGui::Button("Row Advance")) e->haltWhen(DIV_HALT_ROW);
ImGui::SameLine();
if (ImGui::Button("Pattern Advance")) e->haltWhen(DIV_HALT_PATTERN);
if (ImGui::Button("Panic")) e->syncReset();
ImGui::SameLine();
if (ImGui::Button("Abort")) {
abort();
}
ImGui::TreePop();
}
if (ImGui::TreeNode("Breakpoint")) {
ImGui::InputInt("Order",&bpOrder);
ImGui::InputInt("Row",&bpRow);
ImGui::InputInt("Tick",&bpTick);
ImGui::Checkbox("Enable",&bpOn);
ImGui::TreePop();
}
if (ImGui::TreeNode("Dispatch Status")) {
ImGui::Text("for best results set latency to minimum or use the Frame Advance button.");
ImGui::Columns(e->getTotalChannelCount());
for (int i=0; i<e->getTotalChannelCount(); i++) {
void* ch=e->getDispatchChanState(i);
ImGui::TextColored(uiColors[GUI_COLOR_ACCENT_PRIMARY],"Ch. %d: %d, %d",i,e->dispatchOfChan[i],e->dispatchChanOfChan[i]);
if (ch==NULL) {
ImGui::Text("NULL");
} else {
putDispatchChan(ch,e->dispatchChanOfChan[i],e->sysOfChan[i]);
}
ImGui::NextColumn();
}
ImGui::Columns();
ImGui::TreePop();
}
if (ImGui::TreeNode("Playback Status")) {
ImGui::Text("for best results set latency to minimum or use the Frame Advance button.");
ImGui::Columns(e->getTotalChannelCount());
for (int i=0; i<e->getTotalChannelCount(); i++) {
DivChannelState* ch=e->getChanState(i);
ImGui::TextColored(uiColors[GUI_COLOR_ACCENT_PRIMARY],"Channel %d:",i);
if (ch==NULL) {
ImGui::Text("NULL");
} else {
ImGui::Text("* General:");
ImGui::Text("- note = %d",ch->note);
ImGui::Text("- oldNote = %d",ch->oldNote);
ImGui::Text("- pitch = %d",ch->pitch);
ImGui::Text("- portaSpeed = %d",ch->portaSpeed);
ImGui::Text("- portaNote = %d",ch->portaNote);
ImGui::Text("- volume = %.4x",ch->volume);
ImGui::Text("- volSpeed = %d",ch->volSpeed);
ImGui::Text("- cut = %d",ch->cut);
ImGui::Text("- rowDelay = %d",ch->rowDelay);
ImGui::Text("- volMax = %.4x",ch->volMax);
ImGui::Text("- delayOrder = %d",ch->delayOrder);
ImGui::Text("- delayRow = %d",ch->delayRow);
ImGui::Text("- retrigSpeed = %d",ch->retrigSpeed);
ImGui::Text("- retrigTick = %d",ch->retrigTick);
ImGui::PushStyleColor(ImGuiCol_Text,(ch->vibratoDepth>0)?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_TEXT]);
ImGui::Text("* Vibrato:");
ImGui::Text("- depth = %d",ch->vibratoDepth);
ImGui::Text("- rate = %d",ch->vibratoRate);
ImGui::Text("- pos = %d",ch->vibratoPos);
ImGui::Text("- dir = %d",ch->vibratoDir);
ImGui::Text("- fine = %d",ch->vibratoFine);
ImGui::PopStyleColor();
ImGui::PushStyleColor(ImGuiCol_Text,(ch->tremoloDepth>0)?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_TEXT]);
ImGui::Text("* Tremolo:");
ImGui::Text("- depth = %d",ch->tremoloDepth);
ImGui::Text("- rate = %d",ch->tremoloRate);
ImGui::Text("- pos = %d",ch->tremoloPos);
ImGui::PopStyleColor();
ImGui::PushStyleColor(ImGuiCol_Text,(ch->arp>0)?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_TEXT]);
ImGui::Text("* Arpeggio:");
ImGui::Text("- arp = %.2X",ch->arp);
ImGui::Text("- stage = %d",ch->arpStage);
ImGui::Text("- ticks = %d",ch->arpTicks);
ImGui::PopStyleColor();
ImGui::Text("* Miscellaneous:");
ImGui::TextColored(ch->doNote?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> Do Note");
ImGui::TextColored(ch->legato?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> Legato");
ImGui::TextColored(ch->portaStop?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> PortaStop");
ImGui::TextColored(ch->keyOn?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> Key On");
ImGui::TextColored(ch->keyOff?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> Key Off");
ImGui::TextColored(ch->nowYouCanStop?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> NowYouCanStop");
ImGui::TextColored(ch->stopOnOff?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> Stop on Off");
ImGui::TextColored(ch->arpYield?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> Arp Yield");
ImGui::TextColored(ch->delayLocked?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> DelayLocked");
ImGui::TextColored(ch->inPorta?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> InPorta");
ImGui::TextColored(ch->scheduledSlideReset?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> SchedSlide");
}
ImGui::NextColumn();
}
ImGui::Columns();
ImGui::TreePop();
}
if (ImGui::TreeNode("Playground")) {
if (pgSys<0 || pgSys>=e->song.systemLen) pgSys=0;
if (ImGui::BeginCombo("System",fmt::sprintf("%d. %s",pgSys+1,e->getSystemName(e->song.system[pgSys])).c_str())) {
for (int i=0; i<e->song.systemLen; i++) {
if (ImGui::Selectable(fmt::sprintf("%d. %s",i+1,e->getSystemName(e->song.system[i])).c_str())) {
pgSys=i;
break;
}
}
ImGui::EndCombo();
}
ImGui::Text("Program");
if (pgProgram.empty()) {
ImGui::Text("-nothing here-");
} else {
char id[32];
for (size_t index=0; index<pgProgram.size(); index++) {
DivRegWrite& i=pgProgram[index];
snprintf(id,31,"pgw%d",(int)index);
ImGui::PushID(id);
ImGui::SetNextItemWidth(100.0f*dpiScale);
ImGui::InputScalar("##PAddress",ImGuiDataType_U32,&i.addr,NULL,NULL,"%.2X",ImGuiInputTextFlags_CharsHexadecimal);
ImGui::SameLine();
ImGui::Text("=");
ImGui::SameLine();
ImGui::SetNextItemWidth(100.0f*dpiScale);
ImGui::InputScalar("##PValue",ImGuiDataType_U16,&i.val,NULL,NULL,"%.2X",ImGuiInputTextFlags_CharsHexadecimal);
ImGui::SameLine();
if (ImGui::Button(ICON_FA_TIMES "##PRemove")) {
pgProgram.erase(pgProgram.begin()+index);
index--;
}
ImGui::PopID();
}
}
if (ImGui::Button("Execute")) {
e->poke(pgSys,pgProgram);
}
ImGui::SameLine();
if (ImGui::Button("Clear")) {
pgProgram.clear();
}
ImGui::Text("Address");
ImGui::SameLine();
ImGui::SetNextItemWidth(100.0f*dpiScale);
ImGui::InputInt("##PAddress",&pgAddr,0,0,ImGuiInputTextFlags_CharsHexadecimal);
ImGui::SameLine();
ImGui::Text("Value");
ImGui::SameLine();
ImGui::SetNextItemWidth(100.0f*dpiScale);
ImGui::InputInt("##PValue",&pgVal,0,0,ImGuiInputTextFlags_CharsHexadecimal);
ImGui::SameLine();
if (ImGui::Button("Write")) {
e->poke(pgSys,pgAddr,pgVal);
}
ImGui::SameLine();
if (ImGui::Button("Add")) {
pgProgram.push_back(DivRegWrite(pgAddr,pgVal));
}
if (ImGui::TreeNode("Register Cheatsheet")) {
const char** sheet=e->getRegisterSheet(pgSys);
if (sheet==NULL) {
ImGui::Text("no cheatsheet available for this system.");
} else {
if (ImGui::BeginTable("RegisterSheet",2,ImGuiTableFlags_SizingFixedSame)) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("Name");
ImGui::TableNextColumn();
ImGui::Text("Address");
for (int i=0; sheet[i]!=NULL; i+=2) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("%s",sheet[i]);
ImGui::TableNextColumn();
ImGui::Text("$%s",sheet[i+1]);
}
ImGui::EndTable();
}
}
ImGui::TreePop();
}
ImGui::TreePop();
}
if (ImGui::TreeNode("User Interface")) {
if (ImGui::Button("Inspect")) {
inspectorOpen=!inspectorOpen;
}
ImGui::TreePop();
}
if (ImGui::TreeNode("Settings")) {
if (ImGui::Button("Sync")) syncSettings();
ImGui::SameLine();
if (ImGui::Button("Commit")) commitSettings();
ImGui::SameLine();
if (ImGui::Button("Force Load")) e->loadConf();
ImGui::SameLine();
if (ImGui::Button("Force Save")) e->saveConf();
ImGui::TreePop();
}
ImGui::Text("Song format version %d",e->song.version);
ImGui::Text("Furnace version " DIV_VERSION " (%d)",DIV_ENGINE_VERSION);
}
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_DEBUG;
ImGui::End();
}

1053
src/gui/doAction.cpp Normal file

File diff suppressed because it is too large Load Diff

338
src/gui/editControls.cpp Normal file
View File

@ -0,0 +1,338 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "gui.h"
#include "IconsFontAwesome4.h"
void FurnaceGUI::drawEditControls() {
if (nextWindow==GUI_WINDOW_EDIT_CONTROLS) {
editControlsOpen=true;
ImGui::SetNextWindowFocus();
nextWindow=GUI_WINDOW_NOTHING;
}
if (!editControlsOpen) return;
switch (settings.controlLayout) {
case 0: // classic
if (ImGui::Begin("Play/Edit Controls",&editControlsOpen)) {
ImGui::Text("Octave");
ImGui::SameLine();
if (ImGui::InputInt("##Octave",&curOctave,1,1)) {
if (curOctave>7) curOctave=7;
if (curOctave<-5) curOctave=-5;
for (size_t i=0; i<activeNotes.size(); i++) {
e->noteOff(activeNotes[i].chan);
}
activeNotes.clear();
if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) {
nextWindow=GUI_WINDOW_PATTERN;
}
}
ImGui::Text("Edit Step");
ImGui::SameLine();
if (ImGui::InputInt("##EditStep",&editStep,1,1)) {
if (editStep>=e->song.patLen) editStep=e->song.patLen-1;
if (editStep<0) editStep=0;
if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) {
nextWindow=GUI_WINDOW_PATTERN;
}
}
ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(e->isPlaying()));
if (ImGui::Button(ICON_FA_PLAY "##Play")) {
play();
}
ImGui::PopStyleColor();
ImGui::SameLine();
if (ImGui::Button(ICON_FA_STOP "##Stop")) {
stop();
}
ImGui::SameLine();
ImGui::Checkbox("Edit",&edit);
ImGui::SameLine();
bool metro=e->getMetronome();
if (ImGui::Checkbox("Metronome",&metro)) {
e->setMetronome(metro);
}
ImGui::Text("Follow");
ImGui::SameLine();
unimportant(ImGui::Checkbox("Orders",&followOrders));
ImGui::SameLine();
unimportant(ImGui::Checkbox("Pattern",&followPattern));
bool repeatPattern=e->getRepeatPattern();
if (ImGui::Checkbox("Repeat pattern",&repeatPattern)) {
e->setRepeatPattern(repeatPattern);
}
ImGui::SameLine();
if (ImGui::Button(ICON_FA_ARROW_DOWN "##StepOne")) {
e->stepOne(cursor.y);
}
}
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_EDIT_CONTROLS;
ImGui::End();
break;
case 1: // compact
if (ImGui::Begin("Play/Edit Controls",&editControlsOpen,ImGuiWindowFlags_NoScrollbar|ImGuiWindowFlags_NoScrollWithMouse)) {
if (ImGui::Button(ICON_FA_STOP "##Stop")) {
stop();
}
ImGui::SameLine();
ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(e->isPlaying()));
if (ImGui::Button(ICON_FA_PLAY "##Play")) {
play();
}
ImGui::PopStyleColor();
ImGui::SameLine();
if (ImGui::Button(ICON_FA_ARROW_DOWN "##StepOne")) {
e->stepOne(cursor.y);
}
ImGui::SameLine();
bool repeatPattern=e->getRepeatPattern();
ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(repeatPattern));
if (ImGui::Button(ICON_FA_REPEAT "##RepeatPattern")) {
e->setRepeatPattern(!repeatPattern);
}
ImGui::PopStyleColor();
ImGui::SameLine();
ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(edit));
if (ImGui::Button(ICON_FA_CIRCLE "##Edit")) {
edit=!edit;
}
ImGui::PopStyleColor();
ImGui::SameLine();
bool metro=e->getMetronome();
ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(metro));
if (ImGui::Button(ICON_FA_BELL_O "##Metronome")) {
e->setMetronome(!metro);
}
ImGui::PopStyleColor();
ImGui::SameLine();
ImGui::Text("Octave");
ImGui::SameLine();
ImGui::SetNextItemWidth(96.0f*dpiScale);
if (ImGui::InputInt("##Octave",&curOctave,1,1)) {
if (curOctave>7) curOctave=7;
if (curOctave<-5) curOctave=-5;
for (size_t i=0; i<activeNotes.size(); i++) {
e->noteOff(activeNotes[i].chan);
}
activeNotes.clear();
if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) {
nextWindow=GUI_WINDOW_PATTERN;
}
}
ImGui::SameLine();
ImGui::Text("Edit Step");
ImGui::SameLine();
ImGui::SetNextItemWidth(96.0f*dpiScale);
if (ImGui::InputInt("##EditStep",&editStep,1,1)) {
if (editStep>=e->song.patLen) editStep=e->song.patLen-1;
if (editStep<0) editStep=0;
if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) {
nextWindow=GUI_WINDOW_PATTERN;
}
}
ImGui::SameLine();
ImGui::Text("Follow");
ImGui::SameLine();
unimportant(ImGui::Checkbox("Orders",&followOrders));
ImGui::SameLine();
unimportant(ImGui::Checkbox("Pattern",&followPattern));
}
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_EDIT_CONTROLS;
ImGui::End();
break;
case 2: // compact vertical
if (ImGui::Begin("Play/Edit Controls",&editControlsOpen,ImGuiWindowFlags_NoScrollbar|ImGuiWindowFlags_NoScrollWithMouse)) {
ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(e->isPlaying()));
if (ImGui::Button(ICON_FA_PLAY "##Play")) {
play();
}
ImGui::PopStyleColor();
if (ImGui::Button(ICON_FA_STOP "##Stop")) {
stop();
}
if (ImGui::Button(ICON_FA_ARROW_DOWN "##StepOne")) {
e->stepOne(cursor.y);
}
bool repeatPattern=e->getRepeatPattern();
ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(repeatPattern));
if (ImGui::Button(ICON_FA_REPEAT "##RepeatPattern")) {
e->setRepeatPattern(!repeatPattern);
}
ImGui::PopStyleColor();
ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(edit));
if (ImGui::Button(ICON_FA_CIRCLE "##Edit")) {
edit=!edit;
}
ImGui::PopStyleColor();
bool metro=e->getMetronome();
ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(metro));
if (ImGui::Button(ICON_FA_BELL_O "##Metronome")) {
e->setMetronome(!metro);
}
ImGui::PopStyleColor();
ImGui::Text("Oct.");
float avail=ImGui::GetContentRegionAvail().x;
ImGui::SetNextItemWidth(avail);
if (ImGui::InputInt("##Octave",&curOctave,0,0)) {
if (curOctave>7) curOctave=7;
if (curOctave<-5) curOctave=-5;
for (size_t i=0; i<activeNotes.size(); i++) {
e->noteOff(activeNotes[i].chan);
}
activeNotes.clear();
if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) {
nextWindow=GUI_WINDOW_PATTERN;
}
}
ImGui::Text("Step");
ImGui::SetNextItemWidth(avail);
if (ImGui::InputInt("##EditStep",&editStep,0,0)) {
if (editStep>=e->song.patLen) editStep=e->song.patLen-1;
if (editStep<0) editStep=0;
if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) {
nextWindow=GUI_WINDOW_PATTERN;
}
}
ImGui::Text("Foll.");
ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(followOrders));
if (ImGui::SmallButton("Ord##FollowOrders")) { handleUnimportant
followOrders=!followOrders;
}
ImGui::PopStyleColor();
ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(followPattern));
if (ImGui::SmallButton("Pat##FollowPattern")) { handleUnimportant
followPattern=!followPattern;
}
ImGui::PopStyleColor();
}
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_EDIT_CONTROLS;
ImGui::End();
break;
case 3: // split
if (ImGui::Begin("Play Controls",&editControlsOpen,ImGuiWindowFlags_NoScrollbar|ImGuiWindowFlags_NoScrollWithMouse)) {
if (e->isPlaying()) {
ImGui::PushStyleColor(ImGuiCol_Button,uiColors[GUI_COLOR_TOGGLE_ON]);
if (ImGui::Button(ICON_FA_STOP "##Stop")) {
stop();
}
ImGui::PopStyleColor();
} else {
if (ImGui::Button(ICON_FA_PLAY "##Play")) {
play();
}
}
ImGui::SameLine();
if (ImGui::Button(ICON_FA_PLAY_CIRCLE "##PlayAgain")) {
play();
}
ImGui::SameLine();
if (ImGui::Button(ICON_FA_ARROW_DOWN "##StepOne")) {
e->stepOne(cursor.y);
}
ImGui::SameLine();
ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(edit));
if (ImGui::Button(ICON_FA_CIRCLE "##Edit")) {
edit=!edit;
}
ImGui::PopStyleColor();
bool metro=e->getMetronome();
ImGui::SameLine();
ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(metro));
if (ImGui::Button(ICON_FA_BELL_O "##Metronome")) {
e->setMetronome(!metro);
}
ImGui::PopStyleColor();
ImGui::SameLine();
bool repeatPattern=e->getRepeatPattern();
ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(repeatPattern));
if (ImGui::Button(ICON_FA_REPEAT "##RepeatPattern")) {
e->setRepeatPattern(!repeatPattern);
}
ImGui::PopStyleColor();
}
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_EDIT_CONTROLS;
ImGui::End();
if (ImGui::Begin("Edit Controls",&editControlsOpen)) {
ImGui::Columns(2);
ImGui::Text("Octave");
ImGui::SameLine();
float cursor=ImGui::GetCursorPosX();
float avail=ImGui::GetContentRegionAvail().x;
ImGui::SetNextItemWidth(avail);
if (ImGui::InputInt("##Octave",&curOctave,1,1)) {
if (curOctave>7) curOctave=7;
if (curOctave<-5) curOctave=-5;
for (size_t i=0; i<activeNotes.size(); i++) {
e->noteOff(activeNotes[i].chan);
}
activeNotes.clear();
if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) {
nextWindow=GUI_WINDOW_PATTERN;
}
}
ImGui::Text("Step");
ImGui::SameLine();
ImGui::SetCursorPosX(cursor);
ImGui::SetNextItemWidth(avail);
if (ImGui::InputInt("##EditStep",&editStep,1,1)) {
if (editStep>=e->song.patLen) editStep=e->song.patLen-1;
if (editStep<0) editStep=0;
if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) {
nextWindow=GUI_WINDOW_PATTERN;
}
}
ImGui::NextColumn();
unimportant(ImGui::Checkbox("Follow orders",&followOrders));
unimportant(ImGui::Checkbox("Follow pattern",&followPattern));
}
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_EDIT_CONTROLS;
ImGui::End();
break;
}
}

1015
src/gui/editing.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -46,3 +46,8 @@ extern const unsigned int builtinFontLen[];
extern const unsigned int* builtinFontM[];
extern const unsigned int builtinFontMLen[];
#endif
// "(Glimmer of hope: the atlas system will be rewritten in the future to make scaling more flexible.)""
// not just that. somebody rewrite it already so I can load these glyphs at run-time and support
// all languages at once, instead of having to load Unifont's 65k+ characters and blow the GPU up in the
// process!

File diff suppressed because it is too large Load Diff

View File

@ -17,6 +17,9 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef _FUR_GUI_H
#define _FUR_GUI_H
#include "../engine/engine.h"
#include "imgui.h"
#include "imgui_impl_sdl.h"
@ -25,6 +28,8 @@
#include <deque>
#include <initializer_list>
#include <map>
#include <future>
#include <mutex>
#include <vector>
#include "fileDialog.h"
@ -34,6 +39,10 @@
#define handleUnimportant if (settings.insFocusesPattern && patternOpen) {nextWindow=GUI_WINDOW_PATTERN;}
#define unimportant(x) if (x) {handleUnimportant}
#define MARK_MODIFIED modified=true;
#define TOGGLE_COLOR(x) ((x)?uiColors[GUI_COLOR_TOGGLE_ON]:uiColors[GUI_COLOR_TOGGLE_OFF])
enum FurnaceGUIColors {
GUI_COLOR_BACKGROUND=0,
GUI_COLOR_FRAME_BACKGROUND,
@ -42,6 +51,8 @@ enum FurnaceGUIColors {
GUI_COLOR_TEXT,
GUI_COLOR_ACCENT_PRIMARY,
GUI_COLOR_ACCENT_SECONDARY,
GUI_COLOR_TOGGLE_OFF,
GUI_COLOR_TOGGLE_ON,
GUI_COLOR_EDITING,
GUI_COLOR_SONG_LOOP,
@ -185,6 +196,7 @@ enum FurnaceGUIWarnings {
GUI_WARN_QUIT,
GUI_WARN_NEW,
GUI_WARN_OPEN,
GUI_WARN_OPEN_BACKUP,
GUI_WARN_OPEN_DROP,
GUI_WARN_RESET_LAYOUT,
GUI_WARN_GENERIC
@ -199,6 +211,7 @@ enum FurnaceGUIFMAlgs {
enum FurnaceGUIActions {
GUI_ACTION_GLOBAL_MIN=0,
GUI_ACTION_OPEN,
GUI_ACTION_OPEN_BACKUP,
GUI_ACTION_SAVE,
GUI_ACTION_SAVE_AS,
GUI_ACTION_UNDO,
@ -351,6 +364,35 @@ enum FurnaceGUIActions {
GUI_ACTION_SAMPLE_LIST_STOP_PREVIEW,
GUI_ACTION_SAMPLE_LIST_MAX,
GUI_ACTION_SAMPLE_MIN,
GUI_ACTION_SAMPLE_SELECT,
GUI_ACTION_SAMPLE_DRAW,
GUI_ACTION_SAMPLE_CUT,
GUI_ACTION_SAMPLE_COPY,
GUI_ACTION_SAMPLE_PASTE,
GUI_ACTION_SAMPLE_PASTE_REPLACE,
GUI_ACTION_SAMPLE_PASTE_MIX,
GUI_ACTION_SAMPLE_SELECT_ALL,
GUI_ACTION_SAMPLE_RESIZE,
GUI_ACTION_SAMPLE_RESAMPLE,
GUI_ACTION_SAMPLE_AMPLIFY,
GUI_ACTION_SAMPLE_NORMALIZE,
GUI_ACTION_SAMPLE_FADE_IN,
GUI_ACTION_SAMPLE_FADE_OUT,
GUI_ACTION_SAMPLE_SILENCE,
GUI_ACTION_SAMPLE_DELETE,
GUI_ACTION_SAMPLE_TRIM,
GUI_ACTION_SAMPLE_REVERSE,
GUI_ACTION_SAMPLE_INVERT,
GUI_ACTION_SAMPLE_SIGN,
GUI_ACTION_SAMPLE_FILTER,
GUI_ACTION_SAMPLE_PREVIEW,
GUI_ACTION_SAMPLE_STOP_PREVIEW,
GUI_ACTION_SAMPLE_ZOOM_IN,
GUI_ACTION_SAMPLE_ZOOM_OUT,
GUI_ACTION_SAMPLE_ZOOM_AUTO,
GUI_ACTION_SAMPLE_MAX,
GUI_ACTION_ORDERS_MIN,
GUI_ACTION_ORDERS_UP,
GUI_ACTION_ORDERS_DOWN,
@ -512,10 +554,16 @@ class FurnaceGUI {
double aboutScroll, aboutSin;
float aboutHue;
double backupTimer;
std::future<bool> backupTask;
std::mutex backupLock;
String backupPath;
ImFont* mainFont;
ImFont* iconFont;
ImFont* patFont;
ImFont* bigFont;
ImWchar* fontRange;
ImVec4 uiColors[GUI_COLOR_MAX];
ImVec4 volColors[128];
ImU32 pitchGrad[256];
@ -567,6 +615,10 @@ class FurnaceGUI {
int unifiedDataView;
int sysFileDialog;
// end
int roundedWindows;
int roundedButtons;
int roundedMenus;
int loadJapanese;
unsigned int maxUndoSteps;
String mainFontPath;
String patFontPath;
@ -613,6 +665,10 @@ class FurnaceGUI {
stepOnInsert(0),
unifiedDataView(0),
sysFileDialog(1),
roundedWindows(1),
roundedButtons(1),
roundedMenus(0),
loadJapanese(0),
maxUndoSteps(100),
mainFontPath(""),
patFontPath(""),
@ -625,7 +681,7 @@ class FurnaceGUI {
int loopOrder, loopRow, loopEnd, isClipping, extraChannelButtons, patNameTarget, newSongCategory;
bool editControlsOpen, ordersOpen, insListOpen, songInfoOpen, patternOpen, insEditOpen;
bool waveListOpen, waveEditOpen, sampleListOpen, sampleEditOpen, aboutOpen, settingsOpen;
bool mixerOpen, debugOpen, oscOpen, volMeterOpen, statsOpen, compatFlagsOpen;
bool mixerOpen, debugOpen, inspectorOpen, oscOpen, volMeterOpen, statsOpen, compatFlagsOpen;
bool pianoOpen, notesOpen, channelsOpen, regViewOpen;
SelectionPoint selStart, selEnd, cursor;
bool selecting, curNibble, orderNibble, followOrders, followPattern, changeAllOrders;
@ -650,6 +706,7 @@ class FurnaceGUI {
std::map<int,int> actionMapGlobal;
std::map<int,int> actionMapPat;
std::map<int,int> actionMapOrders;
std::map<int,int> actionMapSample;
std::map<int,int> actionMapInsList;
std::map<int,int> actionMapWaveList;
std::map<int,int> actionMapSampleList;
@ -737,16 +794,23 @@ class FurnaceGUI {
// sample editor specific
double sampleZoom;
double prevSampleZoom;
int samplePos;
int resizeSize;
double resampleTarget;
int resampleStrat;
float amplifyVol;
int sampleSelStart, sampleSelEnd;
bool sampleDragActive, sampleDragMode, sampleDrag16;
bool sampleDragActive, sampleDragMode, sampleDrag16, sampleZoomAuto;
void* sampleDragTarget;
ImVec2 sampleDragStart;
ImVec2 sampleDragAreaSize;
unsigned int sampleDragLen;
float sampleFilterL, sampleFilterB, sampleFilterH, sampleFilterRes, sampleFilterCutStart, sampleFilterCutEnd;
unsigned char sampleFilterPower;
short* sampleClipboard;
size_t sampleClipboardLen;
bool openSampleResizeOpt, openSampleResampleOpt, openSampleAmplifyOpt, openSampleFilterOpt;
// visualizer
float keyHit[DIV_MAX_CHANS];
@ -754,6 +818,7 @@ class FurnaceGUI {
void drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, const ImVec2& size);
void drawFMEnv(unsigned char tl, unsigned char ar, unsigned char dr, unsigned char d2r, unsigned char rr, unsigned char sl, float maxTl, float maxArDr, const ImVec2& size);
void drawSysConf(int i);
void updateWindowTitle();
void prepareLayout();
@ -844,6 +909,7 @@ class FurnaceGUI {
void exportAudio(String path, DivAudioExportModes mode);
void applyUISettings();
void initSystemPresets();
void encodeMMLStr(String& target, int* macro, int macroLen, int macroLoop, int macroRel);
void encodeMMLStr(String& target, unsigned char* macro, unsigned char macroLen, signed char macroLoop, signed char macroRel);
@ -865,8 +931,11 @@ class FurnaceGUI {
void updateScroll(int amount);
void addScroll(int amount);
void setFileName(String name);
void runBackupThread();
bool loop();
bool finish();
bool init();
FurnaceGUI();
};
#endif

View File

@ -19,7 +19,7 @@
// guiConst: constants used in the GUI like arrays, strings and other stuff
#include "guiConst.h"
#include "../engine/instrument.h"
#include "../engine/song.h"
const int opOrder[4]={
0, 2, 1, 3
@ -122,3 +122,48 @@ const char* resampleStrats[]={
"sinc",
"best possible"
};
// define systems.
const int availableSystems[]={
DIV_SYSTEM_YM2612,
DIV_SYSTEM_YM2612_EXT,
DIV_SYSTEM_SMS,
DIV_SYSTEM_GB,
DIV_SYSTEM_PCE,
DIV_SYSTEM_NES,
DIV_SYSTEM_C64_8580,
DIV_SYSTEM_C64_6581,
DIV_SYSTEM_YM2151,
DIV_SYSTEM_SEGAPCM,
DIV_SYSTEM_SEGAPCM_COMPAT,
DIV_SYSTEM_YM2610,
DIV_SYSTEM_YM2610_EXT,
DIV_SYSTEM_YM2610_FULL,
DIV_SYSTEM_YM2610_FULL_EXT,
DIV_SYSTEM_YM2610B,
DIV_SYSTEM_YM2610B_EXT,
DIV_SYSTEM_AY8910,
DIV_SYSTEM_AMIGA,
DIV_SYSTEM_PCSPKR,
DIV_SYSTEM_OPLL,
DIV_SYSTEM_OPLL_DRUMS,
DIV_SYSTEM_VRC7,
DIV_SYSTEM_OPL,
DIV_SYSTEM_OPL_DRUMS,
DIV_SYSTEM_OPL2,
DIV_SYSTEM_OPL2_DRUMS,
DIV_SYSTEM_OPL3,
DIV_SYSTEM_OPL3_DRUMS,
DIV_SYSTEM_TIA,
DIV_SYSTEM_SAA1099,
DIV_SYSTEM_AY8930,
DIV_SYSTEM_LYNX,
DIV_SYSTEM_QSOUND,
DIV_SYSTEM_X1_010,
DIV_SYSTEM_SWAN,
DIV_SYSTEM_VERA,
DIV_SYSTEM_BUBSYS_WSG,
DIV_SYSTEM_N163,
DIV_SYSTEM_PET,
0 // don't remove this last one!
};

View File

@ -25,4 +25,5 @@ extern const char* noteNamesG[180];
extern const char* pitchLabel[11];
extern const char* insTypes[];
extern const char* sampleDepths[17];
extern const char* resampleStrats[];
extern const char* resampleStrats[];
extern const int availableSystems[];

View File

@ -673,11 +673,11 @@ void FurnaceGUI::drawFMEnv(unsigned char tl, unsigned char ar, unsigned char dr,
}
#define P(x) if (x) { \
modified=true; \
MARK_MODIFIED; \
e->notifyInsChange(curIns); \
}
#define PARAMETER modified=true; e->notifyInsChange(curIns);
#define PARAMETER MARK_MODIFIED; e->notifyInsChange(curIns);
#define NORMAL_MACRO(macro,macroLen,macroLoop,macroRel,macroMin,macroHeight,macroName,displayName,displayHeight,displayLoop,bitfield,bfVal,drawSlider,sliderVal,sliderLow,macroDispMin,bitOff,macroMode,macroColor,mmlStr,macroAMin,macroAMax,hoverFunc,blockMode) \
ImGui::TableNextRow(); \
@ -689,7 +689,7 @@ void FurnaceGUI::drawFMEnv(unsigned char tl, unsigned char ar, unsigned char dr,
} \
if (displayLoop) { \
ImGui::SetNextItemWidth(lenAvail); \
if (ImGui::InputScalar("##IMacroLen_" macroName,ImGuiDataType_U8,&macroLen,&_ONE,&_THREE)) { \
if (ImGui::InputScalar("##IMacroLen_" macroName,ImGuiDataType_U8,&macroLen,&_ONE,&_THREE)) { MARK_MODIFIED \
if (macroLen>127) macroLen=127; \
} \
if (macroMode!=NULL) { \
@ -778,7 +778,7 @@ void FurnaceGUI::drawFMEnv(unsigned char tl, unsigned char ar, unsigned char dr,
} \
if (displayLoop) { \
ImGui::SetNextItemWidth(lenAvail); \
if (ImGui::InputScalar("##IOPMacroLen_" #op macroName,ImGuiDataType_U8,&macroLen,&_ONE,&_THREE)) { \
if (ImGui::InputScalar("##IOPMacroLen_" #op macroName,ImGuiDataType_U8,&macroLen,&_ONE,&_THREE)) { MARK_MODIFIED \
if (macroLen>127) macroLen=127; \
} \
} \
@ -928,7 +928,9 @@ void FurnaceGUI::drawInsEdit() {
ImGui::Text("no instrument selected");
} else {
DivInstrument* ins=e->song.ins[curIns];
ImGui::InputText("Name",&ins->name);
if (ImGui::InputText("Name",&ins->name)) {
MARK_MODIFIED;
}
if (ins->type<0 || ins->type>=DIV_INS_MAX) ins->type=DIV_INS_FM;
int insType=ins->type;
if (ImGui::Combo("Type",&insType,insTypes,DIV_INS_MAX,DIV_INS_MAX)) {
@ -1372,25 +1374,25 @@ void FurnaceGUI::drawInsEdit() {
if (ins->type==DIV_INS_C64) if (ImGui::BeginTabItem("C64")) {
ImGui::Text("Waveform");
ImGui::SameLine();
ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(ins->c64.triOn)?0.6f:0.2f,0.2f,1.0f));
ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(ins->c64.triOn));
if (ImGui::Button("tri")) { PARAMETER
ins->c64.triOn=!ins->c64.triOn;
}
ImGui::PopStyleColor();
ImGui::SameLine();
ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(ins->c64.sawOn)?0.6f:0.2f,0.2f,1.0f));
ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(ins->c64.sawOn));
if (ImGui::Button("saw")) { PARAMETER
ins->c64.sawOn=!ins->c64.sawOn;
}
ImGui::PopStyleColor();
ImGui::SameLine();
ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(ins->c64.pulseOn)?0.6f:0.2f,0.2f,1.0f));
ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(ins->c64.pulseOn));
if (ImGui::Button("pulse")) { PARAMETER
ins->c64.pulseOn=!ins->c64.pulseOn;
}
ImGui::PopStyleColor();
ImGui::SameLine();
ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(ins->c64.noiseOn)?0.6f:0.2f,0.2f,1.0f));
ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(ins->c64.noiseOn));
if (ImGui::Button("noise")) { PARAMETER
ins->c64.noiseOn=!ins->c64.noiseOn;
}
@ -1419,25 +1421,25 @@ void FurnaceGUI::drawInsEdit() {
ImGui::Text("Filter Mode");
ImGui::SameLine();
ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(ins->c64.lp)?0.6f:0.2f,0.2f,1.0f));
ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(ins->c64.lp));
if (ImGui::Button("low")) { PARAMETER
ins->c64.lp=!ins->c64.lp;
}
ImGui::PopStyleColor();
ImGui::SameLine();
ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(ins->c64.bp)?0.6f:0.2f,0.2f,1.0f));
ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(ins->c64.bp));
if (ImGui::Button("band")) { PARAMETER
ins->c64.bp=!ins->c64.bp;
}
ImGui::PopStyleColor();
ImGui::SameLine();
ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(ins->c64.hp)?0.6f:0.2f,0.2f,1.0f));
ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(ins->c64.hp));
if (ImGui::Button("high")) { PARAMETER
ins->c64.hp=!ins->c64.hp;
}
ImGui::PopStyleColor();
ImGui::SameLine();
ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(ins->c64.ch3off)?0.6f:0.2f,0.2f,1.0f));
ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(ins->c64.ch3off));
if (ImGui::Button("ch3off")) { PARAMETER
ins->c64.ch3off=!ins->c64.ch3off;
}
@ -1554,6 +1556,9 @@ void FurnaceGUI::drawInsEdit() {
if (ins->type==DIV_INS_GB) {
volMax=0;
}
if (ins->type==DIV_INS_PET) {
volMax=1;
}
bool arpMode=ins->std.arpMacroMode;
@ -1583,7 +1588,7 @@ void FurnaceGUI::drawInsEdit() {
if (ins->type==DIV_INS_AY8930) {
dutyMax=255;
}
if (ins->type==DIV_INS_TIA || ins->type==DIV_INS_AMIGA || ins->type==DIV_INS_SCC) {
if (ins->type==DIV_INS_TIA || ins->type==DIV_INS_AMIGA || ins->type==DIV_INS_SCC || ins->type==DIV_INS_PET) {
dutyMax=0;
}
if (ins->type==DIV_INS_PCE) {
@ -1617,8 +1622,13 @@ void FurnaceGUI::drawInsEdit() {
if (ins->type==DIV_INS_SAA1099) waveMax=2;
if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPLL || ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPZ) waveMax=0;
if (ins->type==DIV_INS_MIKEY) waveMax=0;
if (ins->type==DIV_INS_PET) {
waveMax=8;
bitMode=true;
}
const char** waveNames=ayShapeBits;
const char** waveNames=NULL;
if (ins->type==DIV_INS_AY || ins->type==DIV_INS_AY8930 || ins->type==DIV_INS_SAA1099) waveNames=ayShapeBits;
if (ins->type==DIV_INS_C64) waveNames=c64ShapeBits;
int ex1Max=(ins->type==DIV_INS_AY8930)?8:0;
@ -1959,131 +1969,3 @@ void FurnaceGUI::drawInsEdit() {
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_INS_EDIT;
ImGui::End();
}
#undef P
#undef PARAMETER
void FurnaceGUI::drawWaveList() {
if (nextWindow==GUI_WINDOW_WAVE_LIST) {
waveListOpen=true;
ImGui::SetNextWindowFocus();
nextWindow=GUI_WINDOW_NOTHING;
}
if (!waveListOpen) return;
if (ImGui::Begin("Wavetables",&waveListOpen)) {
if (ImGui::Button(ICON_FA_PLUS "##WaveAdd")) {
doAction(GUI_ACTION_WAVE_LIST_ADD);
}
ImGui::SameLine();
if (ImGui::Button(ICON_FA_FILES_O "##WaveClone")) {
doAction(GUI_ACTION_WAVE_LIST_DUPLICATE);
}
ImGui::SameLine();
if (ImGui::Button(ICON_FA_FOLDER_OPEN "##WaveLoad")) {
doAction(GUI_ACTION_WAVE_LIST_OPEN);
}
ImGui::SameLine();
if (ImGui::Button(ICON_FA_FLOPPY_O "##WaveSave")) {
doAction(GUI_ACTION_WAVE_LIST_SAVE);
}
ImGui::SameLine();
if (ImGui::ArrowButton("WaveUp",ImGuiDir_Up)) {
doAction(GUI_ACTION_WAVE_LIST_UP);
}
ImGui::SameLine();
if (ImGui::ArrowButton("WaveDown",ImGuiDir_Down)) {
doAction(GUI_ACTION_WAVE_LIST_DOWN);
}
ImGui::SameLine();
if (ImGui::Button(ICON_FA_TIMES "##WaveDelete")) {
doAction(GUI_ACTION_WAVE_LIST_DELETE);
}
ImGui::Separator();
if (ImGui::BeginTable("WaveListScroll",1,ImGuiTableFlags_ScrollY)) {
actualWaveList();
ImGui::EndTable();
}
}
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_WAVE_LIST;
ImGui::End();
}
void FurnaceGUI::drawWaveEdit() {
if (nextWindow==GUI_WINDOW_WAVE_EDIT) {
waveEditOpen=true;
ImGui::SetNextWindowFocus();
nextWindow=GUI_WINDOW_NOTHING;
}
if (!waveEditOpen) return;
float wavePreview[256];
ImGui::SetNextWindowSizeConstraints(ImVec2(450.0f*dpiScale,300.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale));
if (ImGui::Begin("Wavetable Editor",&waveEditOpen,settings.allowEditDocking?0:ImGuiWindowFlags_NoDocking)) {
if (curWave<0 || curWave>=(int)e->song.wave.size()) {
ImGui::Text("no wavetable selected");
} else {
DivWavetable* wave=e->song.wave[curWave];
ImGui::Text("Width");
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("use a width of:\n- 32 on Game Boy, PC Engine, WonderSwan and Bubble System WSG\n- 128 on X1-010\nany other widths will be scaled during playback.");
}
ImGui::SameLine();
ImGui::SetNextItemWidth(128.0f*dpiScale);
if (ImGui::InputInt("##_WTW",&wave->len,1,2)) {
if (wave->len>256) wave->len=256;
if (wave->len<1) wave->len=1;
e->notifyWaveChange(curWave);
if (wavePreviewOn) e->previewWave(curWave,wavePreviewNote);
modified=true;
}
ImGui::SameLine();
ImGui::Text("Height");
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("use a height of:\n- 15 for Game Boy, WonderSwan, X1-010 envelope shape, Bubble System WSG and N163\n- 31 for PC Engine\n- 255 for X1-010 waveform\nany other heights will be scaled during playback.");
}
ImGui::SameLine();
ImGui::SetNextItemWidth(128.0f*dpiScale);
if (ImGui::InputInt("##_WTH",&wave->max,1,2)) {
if (wave->max>255) wave->max=255;
if (wave->max<1) wave->max=1;
e->notifyWaveChange(curWave);
modified=true;
}
for (int i=0; i<wave->len; i++) {
if (wave->data[i]>wave->max) wave->data[i]=wave->max;
wavePreview[i]=wave->data[i];
}
if (wave->len>0) wavePreview[wave->len]=wave->data[wave->len-1];
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); //wavetable text input size found here
if (ImGui::InputText("##MMLWave",&mmlStringW)) {
decodeMMLStrW(mmlStringW,wave->data,wave->len,wave->max);
}
if (!ImGui::IsItemActive()) {
encodeMMLStr(mmlStringW,wave->data,wave->len,-1,-1);
}
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding,ImVec2(0.0f,0.0f));
ImVec2 contentRegion=ImGui::GetContentRegionAvail(); //wavetable graph size determined here
if (ImGui::GetContentRegionAvail().y > (ImGui::GetContentRegionAvail().x / 2.0f)) {
contentRegion=ImVec2(ImGui::GetContentRegionAvail().x,ImGui::GetContentRegionAvail().x / 2.0f);
}
PlotNoLerp("##Waveform",wavePreview,wave->len+1,0,NULL,0,wave->max,contentRegion);
if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) {
waveDragStart=ImGui::GetItemRectMin();
waveDragAreaSize=contentRegion;
waveDragMin=0;
waveDragMax=wave->max;
waveDragLen=wave->len;
waveDragActive=true;
waveDragTarget=wave->data;
processDrags(ImGui::GetMousePos().x,ImGui::GetMousePos().y);
e->notifyWaveChange(curWave);
modified=true;
}
ImGui::PopStyleVar();
}
}
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_WAVE_EDIT;
ImGui::End();
}

59
src/gui/mixer.cpp Normal file
View File

@ -0,0 +1,59 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "gui.h"
#include "intConst.h"
void FurnaceGUI::drawMixer() {
if (nextWindow==GUI_WINDOW_MIXER) {
mixerOpen=true;
ImGui::SetNextWindowFocus();
nextWindow=GUI_WINDOW_NOTHING;
}
if (!mixerOpen) return;
ImGui::SetNextWindowSizeConstraints(ImVec2(400.0f*dpiScale,200.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale));
if (ImGui::Begin("Mixer",&mixerOpen,settings.allowEditDocking?0:ImGuiWindowFlags_NoDocking)) {
char id[32];
if (ImGui::SliderFloat("Master Volume",&e->song.masterVol,0,3,"%.2fx")) {
if (e->song.masterVol<0) e->song.masterVol=0;
if (e->song.masterVol>3) e->song.masterVol=3;
} rightClickable
for (int i=0; i<e->song.systemLen; i++) {
snprintf(id,31,"MixS%d",i);
bool doInvert=e->song.systemVol[i]&128;
signed char vol=e->song.systemVol[i]&127;
ImGui::PushID(id);
ImGui::Text("%d. %s",i+1,getSystemName(e->song.system[i]));
ImGui::SameLine(ImGui::GetWindowWidth()-(82.0f*dpiScale));
if (ImGui::Checkbox("Invert",&doInvert)) {
e->song.systemVol[i]^=128;
}
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x-(50.0f*dpiScale));
if (ImGui::SliderScalar("Volume",ImGuiDataType_S8,&vol,&_ZERO,&_ONE_HUNDRED_TWENTY_SEVEN)) {
e->song.systemVol[i]=(e->song.systemVol[i]&128)|vol;
} rightClickable
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x-(50.0f*dpiScale));
ImGui::SliderScalar("Panning",ImGuiDataType_S8,&e->song.systemPan[i],&_MINUS_ONE_HUNDRED_TWENTY_SEVEN,&_ONE_HUNDRED_TWENTY_SEVEN); rightClickable
ImGui::PopID();
}
}
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_MIXER;
ImGui::End();
}

90
src/gui/newSong.cpp Normal file
View File

@ -0,0 +1,90 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "gui.h"
void FurnaceGUI::drawNewSong() {
bool accepted=false;
ImGui::PushFont(bigFont);
ImGui::SetCursorPosX((ImGui::GetContentRegionAvail().x-ImGui::CalcTextSize("Choose a System!").x)*0.5);
ImGui::Text("Choose a System!");
ImGui::PopFont();
if (ImGui::BeginTable("sysPicker",2)) {
ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed,0.0f);
ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.0f);
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
ImGui::TableNextColumn();
ImGui::Text("Categories");
ImGui::TableNextColumn();
ImGui::Text("Systems");
ImGui::TableNextRow();
// CATEGORIES
ImGui::TableNextColumn();
int index=0;
for (FurnaceGUISysCategory& i: sysCategories) {
if (ImGui::Selectable(i.name,newSongCategory==index,ImGuiSelectableFlags_DontClosePopups)) { \
newSongCategory=index;
}
index++;
}
// SYSTEMS
ImGui::TableNextColumn();
if (ImGui::BeginTable("Systems",1,ImGuiTableFlags_BordersInnerV|ImGuiTableFlags_ScrollY)) {
for (FurnaceGUISysDef& i: sysCategories[newSongCategory].systems) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (ImGui::Selectable(i.name,false,ImGuiSelectableFlags_DontClosePopups)) {
nextDesc=i.definition.data();
accepted=true;
}
}
ImGui::EndTable();
}
ImGui::EndTable();
}
if (ImGui::Button("Cancel")) {
ImGui::CloseCurrentPopup();
}
if (accepted) {
e->createNew(nextDesc);
undoHist.clear();
redoHist.clear();
curFileName="";
modified=false;
curNibble=false;
orderNibble=false;
orderCursor=-1;
samplePos=0;
updateSampleTex=true;
selStart=SelectionPoint();
selEnd=SelectionPoint();
cursor=SelectionPoint();
updateWindowTitle();
ImGui::CloseCurrentPopup();
}
}

View File

@ -97,13 +97,15 @@ void FurnaceGUI::drawOrders() {
if (curOrder==i) {
if (orderEditMode==0) {
prepareUndo(GUI_UNDO_CHANGE_ORDER);
if (changeAllOrders) {
for (int k=0; k<e->getTotalChannelCount(); k++) {
if (e->song.orders.ord[k][i]<0x7f) e->song.orders.ord[k][i]++;
e->lockSave([this,i,j]() {
if (changeAllOrders) {
for (int k=0; k<e->getTotalChannelCount(); k++) {
if (e->song.orders.ord[k][i]<0x7f) e->song.orders.ord[k][i]++;
}
} else {
if (e->song.orders.ord[j][i]<0x7f) e->song.orders.ord[j][i]++;
}
} else {
if (e->song.orders.ord[j][i]<0x7f) e->song.orders.ord[j][i]++;
}
});
e->walkSong(loopOrder,loopRow,loopEnd);
makeUndo(GUI_UNDO_CHANGE_ORDER);
} else {
@ -130,13 +132,15 @@ void FurnaceGUI::drawOrders() {
if (curOrder==i) {
if (orderEditMode==0) {
prepareUndo(GUI_UNDO_CHANGE_ORDER);
if (changeAllOrders) {
for (int k=0; k<e->getTotalChannelCount(); k++) {
if (e->song.orders.ord[k][i]>0) e->song.orders.ord[k][i]--;
e->lockSave([this,i,j]() {
if (changeAllOrders) {
for (int k=0; k<e->getTotalChannelCount(); k++) {
if (e->song.orders.ord[k][i]>0) e->song.orders.ord[k][i]--;
}
} else {
if (e->song.orders.ord[j][i]>0) e->song.orders.ord[j][i]--;
}
} else {
if (e->song.orders.ord[j][i]>0) e->song.orders.ord[j][i]--;
}
});
e->walkSong(loopOrder,loopRow,loopEnd);
makeUndo(GUI_UNDO_CHANGE_ORDER);
} else {

47
src/gui/osc.cpp Normal file
View File

@ -0,0 +1,47 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "gui.h"
void FurnaceGUI::drawOsc() {
if (nextWindow==GUI_WINDOW_OSCILLOSCOPE) {
oscOpen=true;
ImGui::SetNextWindowFocus();
nextWindow=GUI_WINDOW_NOTHING;
}
if (!oscOpen) return;
ImGui::SetNextWindowSizeConstraints(ImVec2(64.0f*dpiScale,32.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale));
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding,ImVec2(0,0));
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing,ImVec2(0,0));
ImGui::PushStyleVar(ImGuiStyleVar_ItemInnerSpacing,ImVec2(0,0));
if (ImGui::Begin("Oscilloscope",&oscOpen)) {
float values[512];
for (int i=0; i<512; i++) {
int pos=i*e->oscSize/512;
values[i]=(e->oscBuf[0][pos]+e->oscBuf[1][pos])*0.5f;
}
//ImGui::SetCursorPos(ImVec2(0,0));
ImGui::BeginDisabled();
ImGui::PlotLines("##SingleOsc",values,512,0,NULL,-1.0f,1.0f,ImGui::GetContentRegionAvail());
ImGui::EndDisabled();
}
ImGui::PopStyleVar(3);
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_OSCILLOSCOPE;
ImGui::End();
}

46
src/gui/piano.cpp Normal file
View File

@ -0,0 +1,46 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "gui.h"
#include "guiConst.h"
void FurnaceGUI::drawPiano() {
if (nextWindow==GUI_WINDOW_PIANO) {
pianoOpen=true;
ImGui::SetNextWindowFocus();
nextWindow=GUI_WINDOW_NOTHING;
}
if (!pianoOpen) return;
if (ImGui::Begin("Piano",&pianoOpen)) {
for (int i=0; i<e->getTotalChannelCount(); i++) {
DivChannelState* cs=e->getChanState(i);
if (cs->keyOn) {
const char* noteName=NULL;
if (cs->note<-60 || cs->note>120) {
noteName="???";
} else {
noteName=noteNames[cs->note+60];
}
ImGui::Text("%d: %s",i,noteName);
}
}
}
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_PIANO;
ImGui::End();
}

713
src/gui/presets.cpp Normal file
View File

@ -0,0 +1,713 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "gui.h"
// add system configurations here.
// every entry is written in the following format:
// cat.systems.push_back(FurnaceGUISysDef(
// "System Name", {
// DIV_SYSTEM_???, Volume, Panning, Flags,
// DIV_SYSTEM_???, Volume, Panning, Flags,
// ...
// 0
// }
// ));
void FurnaceGUI::initSystemPresets() {
FurnaceGUISysCategory cat;
cat=FurnaceGUISysCategory("FM");
cat.systems.push_back(FurnaceGUISysDef(
"Yamaha YM2612", {
DIV_SYSTEM_YM2612, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Yamaha YM2612 (extended channel 3)", {
DIV_SYSTEM_YM2612_EXT, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Yamaha YM2151", {
DIV_SYSTEM_YM2151, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Yamaha YM2610", {
DIV_SYSTEM_YM2610_FULL, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Yamaha YM2610 (extended channel 2)", {
DIV_SYSTEM_YM2610_FULL_EXT, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Yamaha YM2610B", {
DIV_SYSTEM_YM2610B, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Yamaha YM2610B (extended channel 3)", {
DIV_SYSTEM_YM2610B_EXT, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Yamaha YM2413", {
DIV_SYSTEM_OPLL, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Yamaha YM2413 (drums mode)", {
DIV_SYSTEM_OPLL_DRUMS, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Yamaha YM3526", {
DIV_SYSTEM_OPL, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Yamaha YM3526 (drums mode)", {
DIV_SYSTEM_OPL_DRUMS, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Yamaha YM3812", {
DIV_SYSTEM_OPL2, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Yamaha YM3812 (drums mode)", {
DIV_SYSTEM_OPL2_DRUMS, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Yamaha YMF262", {
DIV_SYSTEM_OPL3, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Yamaha YMF262 (drums mode)", {
DIV_SYSTEM_OPL3_DRUMS, 64, 0, 0,
0
}
));
sysCategories.push_back(cat);
cat=FurnaceGUISysCategory("Square");
cat.systems.push_back(FurnaceGUISysDef(
"TI SN76489", {
DIV_SYSTEM_SMS, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"AY-3-8910", {
DIV_SYSTEM_AY8910, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Philips SAA1099", {
DIV_SYSTEM_SAA1099, 64, 0, 0,
0
}
));
sysCategories.push_back(cat);
cat=FurnaceGUISysCategory("Sample");
cat.systems.push_back(FurnaceGUISysDef(
"Amiga", {
DIV_SYSTEM_AMIGA, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"SegaPCM", {
DIV_SYSTEM_SEGAPCM, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Capcom QSound", {
DIV_SYSTEM_QSOUND, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Seta/Allumer X1-010", {
DIV_SYSTEM_X1_010, 64, 0, 0,
0
}
));
sysCategories.push_back(cat);
cat=FurnaceGUISysCategory("Game consoles");
cat.systems.push_back(FurnaceGUISysDef(
"Sega Genesis", {
DIV_SYSTEM_YM2612, 64, 0, 0,
DIV_SYSTEM_SMS, 24, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Sega Genesis (extended channel 3)", {
DIV_SYSTEM_YM2612_EXT, 64, 0, 0,
DIV_SYSTEM_SMS, 24, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Sega Master System", {
DIV_SYSTEM_SMS, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Sega Master System (with FM expansion)", {
DIV_SYSTEM_SMS, 64, 0, 0,
DIV_SYSTEM_OPLL, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Sega Master System (with FM expansion in drums mode)", {
DIV_SYSTEM_SMS, 64, 0, 0,
DIV_SYSTEM_OPLL_DRUMS, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Game Boy", {
DIV_SYSTEM_GB, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"NEC PC Engine/TurboGrafx-16", {
DIV_SYSTEM_PCE, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"NES", {
DIV_SYSTEM_NES, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"NES with Konami VRC7", {
DIV_SYSTEM_NES, 64, 0, 0,
DIV_SYSTEM_VRC7, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"NES with Sunsoft 5B", {
DIV_SYSTEM_NES, 64, 0, 0,
DIV_SYSTEM_AY8910, 64, 0, 38,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"NES with Namco 163", {
DIV_SYSTEM_NES, 64, 0, 0,
DIV_SYSTEM_N163, 64, 0, 112,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Mattel Intellivision", {
DIV_SYSTEM_AY8910, 64, 0, 48,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Vectrex", {
DIV_SYSTEM_AY8910, 64, 0, 4,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Neo Geo AES", {
DIV_SYSTEM_YM2610_FULL, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Neo Geo AES (extended channel 2)", {
DIV_SYSTEM_YM2610_FULL_EXT, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Atari 2600/7800", {
DIV_SYSTEM_TIA, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Atari Lynx", {
DIV_SYSTEM_LYNX, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"WonderSwan", {
DIV_SYSTEM_SWAN, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Gamate", {
DIV_SYSTEM_AY8910, 64, 0, 73,
0
}
));
sysCategories.push_back(cat);
cat=FurnaceGUISysCategory("Computers");
cat.systems.push_back(FurnaceGUISysDef(
"Commodore PET", {
DIV_SYSTEM_PET, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Commodore VIC-20", {
DIV_SYSTEM_VIC20, 64, 0, 1,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Commodore 64 (6581 SID)", {
DIV_SYSTEM_C64_6581, 64, 0, 1,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Commodore 64 (8580 SID)", {
DIV_SYSTEM_C64_8580, 64, 0, 1,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Commodore 64 (6581 SID + Sound Expander)", {
DIV_SYSTEM_OPL, 64, 0, 0,
DIV_SYSTEM_C64_6581, 64, 0, 1,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Commodore 64 (6581 SID + Sound Expander with drums mode)", {
DIV_SYSTEM_OPL_DRUMS, 64, 0, 0,
DIV_SYSTEM_C64_6581, 64, 0, 1,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Commodore 64 (8580 SID + Sound Expander)", {
DIV_SYSTEM_OPL, 64, 0, 0,
DIV_SYSTEM_C64_8580, 64, 0, 1,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Commodore 64 (8580 SID + Sound Expander with drums mode)", {
DIV_SYSTEM_OPL_DRUMS, 64, 0, 0,
DIV_SYSTEM_C64_8580, 64, 0, 1,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Amiga", {
DIV_SYSTEM_AMIGA, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"MSX", {
DIV_SYSTEM_AY8910, 64, 0, 16,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"MSX + SFG-01", {
DIV_SYSTEM_YM2151, 64, 0, 0,
DIV_SYSTEM_AY8910, 64, 0, 16,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"MSX + MSX-MUSIC", {
DIV_SYSTEM_OPLL, 64, 0, 0,
DIV_SYSTEM_AY8910, 64, 0, 16,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"MSX + MSX-MUSIC (drums mode)", {
DIV_SYSTEM_OPLL_DRUMS, 64, 0, 0,
DIV_SYSTEM_AY8910, 64, 0, 16,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"ZX Spectrum (48K)", {
DIV_SYSTEM_AY8910, 64, 0, 2,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"ZX Spectrum (128K)", {
DIV_SYSTEM_AY8910, 64, 0, 1,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Amstrad CPC", {
DIV_SYSTEM_AY8910, 64, 0, 5,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"SAM Coupé", {
DIV_SYSTEM_SAA1099, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"BBC Micro", {
DIV_SYSTEM_SMS, 64, 0, 6,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"PC (barebones)", {
DIV_SYSTEM_PCSPKR, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"PC + Covox Sound Master", {
DIV_SYSTEM_AY8930, 64, 0, 3,
DIV_SYSTEM_PCSPKR, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"PC + SSI 2001", {
DIV_SYSTEM_C64_6581, 64, 0, 2,
DIV_SYSTEM_PCSPKR, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"PC + Game Blaster", {
DIV_SYSTEM_SAA1099, 64, -127, 1,
DIV_SYSTEM_SAA1099, 64, 127, 1,
DIV_SYSTEM_PCSPKR, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"PC + AdLib/Sound Blaster", {
DIV_SYSTEM_OPL2, 64, 0, 0,
DIV_SYSTEM_PCSPKR, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"PC + AdLib/Sound Blaster (drums mode)", {
DIV_SYSTEM_OPL2_DRUMS, 64, 0, 0,
DIV_SYSTEM_PCSPKR, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"PC + Sound Blaster w/Game Blaster Compatible", {
DIV_SYSTEM_OPL2, 64, 0, 0,
DIV_SYSTEM_SAA1099, 64, -127, 1,
DIV_SYSTEM_SAA1099, 64, 127, 1,
DIV_SYSTEM_PCSPKR, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"PC + Sound Blaster w/Game Blaster Compatible (drums mode)", {
DIV_SYSTEM_OPL2_DRUMS, 64, 0, 0,
DIV_SYSTEM_SAA1099, 64, -127, 1,
DIV_SYSTEM_SAA1099, 64, 127, 1,
DIV_SYSTEM_PCSPKR, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"PC + Sound Blaster Pro", {
DIV_SYSTEM_OPL2, 64, -127, 0,
DIV_SYSTEM_OPL2, 64, 127, 0,
DIV_SYSTEM_PCSPKR, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"PC + Sound Blaster Pro (drums mode)", {
DIV_SYSTEM_OPL2_DRUMS, 64, -127, 0,
DIV_SYSTEM_OPL2_DRUMS, 64, 127, 0,
DIV_SYSTEM_PCSPKR, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"PC + Sound Blaster Pro 2", {
DIV_SYSTEM_OPL3, 64, 0, 0,
DIV_SYSTEM_PCSPKR, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"PC + Sound Blaster Pro 2 (drums mode)", {
DIV_SYSTEM_OPL3_DRUMS, 64, 0, 0,
DIV_SYSTEM_PCSPKR, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"PC + SAAYM", {
DIV_SYSTEM_YM2151, 64, 0, 0, // 3.58MHz or 4MHz selectable via jumper
DIV_SYSTEM_SAA1099, 64, -127, 1, // 7.16MHz or 8MHz selectable via jumper
DIV_SYSTEM_SAA1099, 64, 127, 1, // ""
DIV_SYSTEM_PCSPKR, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Sharp X1", {
DIV_SYSTEM_AY8910, 64, 0, 3,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Sharp X1 + FM Addon", {
DIV_SYSTEM_AY8910, 64, 0, 3,
DIV_SYSTEM_YM2151, 64, 0, 2,
0
}
));
/*
cat.systems.push_back(FurnaceGUISysDef(
"Sharp X68000", {
DIV_SYSTEM_YM2151, 64, 0, 2,
DIV_SYSTEM_MSM6258, 64, 0, 0,
0
}
));*/
cat.systems.push_back(FurnaceGUISysDef(
"Commander X16", {
DIV_SYSTEM_YM2151, 64, 0, 0,
DIV_SYSTEM_VERA, 64, 0, 0,
0
}
));
sysCategories.push_back(cat);
cat=FurnaceGUISysCategory("Arcade systems");
cat.systems.push_back(FurnaceGUISysDef(
"Bally Midway MCR", {
DIV_SYSTEM_AY8910, 64, 0, 0,
DIV_SYSTEM_AY8910, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Sega Kyugo", {
DIV_SYSTEM_AY8910, 64, 0, 4,
DIV_SYSTEM_AY8910, 64, 0, 4,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Sega OutRun/X Board", {
DIV_SYSTEM_YM2151, 64, 0, 2,
DIV_SYSTEM_SEGAPCM, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Neo Geo MVS", {
DIV_SYSTEM_YM2610_FULL, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Neo Geo MVS (extended channel 2)", {
DIV_SYSTEM_YM2610_FULL_EXT, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Taito Arcade", {
DIV_SYSTEM_YM2610B, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Taito Arcade (extended channel 3)", {
DIV_SYSTEM_YM2610B_EXT, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Capcom CPS-2 (QSound)", {
DIV_SYSTEM_QSOUND, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Seta 1", {
DIV_SYSTEM_X1_010, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Seta 1 + FM addon", {
DIV_SYSTEM_YM2612, 64, 0, 2, // Discrete YM3438
DIV_SYSTEM_X1_010, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Seta 2", {
DIV_SYSTEM_X1_010, 64, 0, 1,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Konami Bubble System", {
DIV_SYSTEM_AY8910, 64, 0, 0,
DIV_SYSTEM_AY8910, 64, 0, 0,
DIV_SYSTEM_BUBSYS_WSG, 64, 0, 0,
// VLM5030 exists but not used for music at all
0
}
));
sysCategories.push_back(cat);
cat=FurnaceGUISysCategory("DefleMask-compatible");
cat.systems.push_back(FurnaceGUISysDef(
"Sega Genesis", {
DIV_SYSTEM_YM2612, 64, 0, 0,
DIV_SYSTEM_SMS, 24, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Sega Genesis (extended channel 3)", {
DIV_SYSTEM_YM2612_EXT, 64, 0, 0,
DIV_SYSTEM_SMS, 24, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Sega Master System", {
DIV_SYSTEM_SMS, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Sega Master System (with FM expansion)", {
DIV_SYSTEM_SMS, 64, 0, 0,
DIV_SYSTEM_OPLL, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Game Boy", {
DIV_SYSTEM_GB, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"NEC PC Engine/TurboGrafx-16", {
DIV_SYSTEM_PCE, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"NES", {
DIV_SYSTEM_NES, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"NES with Konami VRC7", {
DIV_SYSTEM_NES, 64, 0, 0,
DIV_SYSTEM_VRC7, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Commodore 64 (6581 SID)", {
DIV_SYSTEM_C64_6581, 64, 0, 1,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Commodore 64 (8580 SID)", {
DIV_SYSTEM_C64_8580, 64, 0, 1,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Arcade (YM2151 and SegaPCM)", {
DIV_SYSTEM_YM2151, 64, 0, 0,
DIV_SYSTEM_SEGAPCM_COMPAT, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Neo Geo CD", {
DIV_SYSTEM_YM2610, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Neo Geo CD (extended channel 2)", {
DIV_SYSTEM_YM2610_EXT, 64, 0, 0,
0
}
));
sysCategories.push_back(cat);
}

71
src/gui/regView.cpp Normal file
View File

@ -0,0 +1,71 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "gui.h"
void FurnaceGUI::drawRegView() {
if (nextWindow==GUI_WINDOW_REGISTER_VIEW) {
channelsOpen=true;
ImGui::SetNextWindowFocus();
nextWindow=GUI_WINDOW_NOTHING;
}
if (!regViewOpen) return;
if (ImGui::Begin("Register View",&regViewOpen)) {
for (int i=0; i<e->song.systemLen; i++) {
ImGui::Text("%d. %s",i+1,getSystemName(e->song.system[i]));
int size=0;
int depth=8;
unsigned char* regPool=e->getRegisterPool(i,size,depth);
unsigned short* regPoolW=(unsigned short*)regPool;
if (regPool==NULL) {
ImGui::Text("- no register pool available");
} else {
ImGui::PushFont(patFont);
if (ImGui::BeginTable("Memory",17)) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
for (int i=0; i<16; i++) {
ImGui::TableNextColumn();
ImGui::TextColored(uiColors[GUI_COLOR_PATTERN_ROW_INDEX]," %X",i);
}
for (int i=0; i<=((size-1)>>4); i++) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextColored(uiColors[GUI_COLOR_PATTERN_ROW_INDEX],"%.2X",i*16);
for (int j=0; j<16; j++) {
ImGui::TableNextColumn();
if (i*16+j>=size) continue;
if (depth == 8) {
ImGui::Text("%.2x",regPool[i*16+j]);
} else if (depth == 16) {
ImGui::Text("%.4x",regPoolW[i*16+j]);
} else {
ImGui::Text("??");
}
}
}
ImGui::EndTable();
}
ImGui::PopFont();
}
}
}
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_REGISTER_VIEW;
ImGui::End();
}

View File

@ -18,12 +18,14 @@
*/
#include "gui.h"
#include <imgui.h>
#include <imgui_internal.h>
#include <math.h>
#include "../ta-log.h"
#include "IconsFontAwesome4.h"
#include "misc/cpp/imgui_stdlib.h"
#include <fmt/printf.h>
#include "guiConst.h"
#include "sampleUtil.h"
void FurnaceGUI::drawSampleEdit() {
if (nextWindow==GUI_WINDOW_SAMPLE_EDIT) {
@ -46,7 +48,9 @@ void FurnaceGUI::drawSampleEdit() {
ImGui::Text("Name");
ImGui::SameLine();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
ImGui::InputText("##SampleName",&sample->name);
if (ImGui::InputText("##SampleName",&sample->name)) {
MARK_MODIFIED;
}
if (ImGui::BeginTable("SampleProps",4,ImGuiTableFlags_SizingStretchSame)) {
ImGui::TableNextRow();
@ -61,6 +65,7 @@ void FurnaceGUI::drawSampleEdit() {
sample->depth=i;
e->renderSamplesP();
updateSampleTex=true;
MARK_MODIFIED;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("no undo for sample type change operations!");
@ -73,7 +78,7 @@ void FurnaceGUI::drawSampleEdit() {
ImGui::Text("Rate (Hz)");
ImGui::SameLine();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
if (ImGui::InputInt("##SampleRate",&sample->rate,10,200)) {
if (ImGui::InputInt("##SampleRate",&sample->rate,10,200)) { MARK_MODIFIED
if (sample->rate<100) sample->rate=100;
if (sample->rate>96000) sample->rate=96000;
}
@ -82,14 +87,14 @@ void FurnaceGUI::drawSampleEdit() {
ImGui::Text("C-4 (Hz)");
ImGui::SameLine();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
if (ImGui::InputInt("##SampleCenter",&sample->centerRate,10,200)) {
if (ImGui::InputInt("##SampleCenter",&sample->centerRate,10,200)) { MARK_MODIFIED
if (sample->centerRate<100) sample->centerRate=100;
if (sample->centerRate>65535) sample->centerRate=65535;
}
ImGui::TableNextColumn();
bool doLoop=(sample->loopStart>=0);
if (ImGui::Checkbox("Loop",&doLoop)) {
if (ImGui::Checkbox("Loop",&doLoop)) { MARK_MODIFIED
if (doLoop) {
sample->loopStart=0;
} else {
@ -100,7 +105,7 @@ void FurnaceGUI::drawSampleEdit() {
if (doLoop) {
ImGui::SameLine();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
if (ImGui::InputInt("##LoopPosition",&sample->loopStart,1,10)) {
if (ImGui::InputInt("##LoopPosition",&sample->loopStart,1,10)) { MARK_MODIFIED
if (sample->loopStart<0 || sample->loopStart>=(int)sample->samples) {
sample->loopStart=0;
}
@ -110,40 +115,30 @@ void FurnaceGUI::drawSampleEdit() {
ImGui::EndTable();
}
if (ImGui::InputDouble("Zoom",&sampleZoom,0.1,2.0)) {
if (sampleZoom<0.01) sampleZoom=0.01;
updateSampleTex=true;
}
if (ImGui::InputInt("Pos",&samplePos,1,10)) {
if (samplePos>=(int)sample->samples) samplePos=sample->samples-1;
if (samplePos<0) samplePos=0;
updateSampleTex=true;
}
/*
if (ImGui::Button("Apply")) {
e->renderSamplesP();
}
ImGui::SameLine();
if (ImGui::Button(ICON_FA_VOLUME_UP "##PreviewSample")) {
e->previewSample(curSample);
}
ImGui::SameLine();
if (ImGui::Button(ICON_FA_VOLUME_OFF "##StopSample")) {
e->stopSamplePreview();
}*/
*/
ImGui::Separator();
ImGui::BeginDisabled(sample->depth!=8 && sample->depth!=16);
ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(!sampleDragMode));
if (ImGui::Button(ICON_FA_I_CURSOR "##SSelect")) {
sampleDragMode=false;
}
ImGui::PopStyleColor();
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Edit mode: Select");
}
ImGui::SameLine();
ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(sampleDragMode));
if (ImGui::Button(ICON_FA_PENCIL "##SDraw")) {
sampleDragMode=true;
}
ImGui::PopStyleColor();
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Edit mode: Draw");
}
@ -157,19 +152,26 @@ void FurnaceGUI::drawSampleEdit() {
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Resize");
}
if (openSampleResizeOpt) {
openSampleResizeOpt=false;
ImGui::OpenPopup("SResizeOpt");
}
if (ImGui::BeginPopupContextItem("SResizeOpt",ImGuiPopupFlags_MouseButtonLeft)) {
if (ImGui::InputInt("Samples",&resizeSize,1,64)) {
if (resizeSize<0) resizeSize=0;
if (resizeSize>16777215) resizeSize=16777215;
}
if (ImGui::Button("Resize")) {
e->synchronized([this,sample]() {
e->lockEngine([this,sample]() {
if (!sample->resize(resizeSize)) {
showError("couldn't resize! make sure your sample is 8 or 16-bit.");
}
e->renderSamples();
});
updateSampleTex=true;
sampleSelStart=-1;
sampleSelEnd=-1;
MARK_MODIFIED;
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
@ -184,6 +186,10 @@ void FurnaceGUI::drawSampleEdit() {
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Resample");
}
if (openSampleResampleOpt) {
openSampleResampleOpt=false;
ImGui::OpenPopup("SResampleOpt");
}
if (ImGui::BeginPopupContextItem("SResampleOpt",ImGuiPopupFlags_MouseButtonLeft)) {
ImGui::Text("Rate");
if (ImGui::InputDouble("##SRRate",&resampleTarget,1.0,50.0,"%g")) {
@ -210,13 +216,16 @@ void FurnaceGUI::drawSampleEdit() {
}
ImGui::Combo("Filter",&resampleStrat,resampleStrats,6);
if (ImGui::Button("Resample")) {
e->synchronized([this,sample]() {
e->lockEngine([this,sample]() {
if (!sample->resample(resampleTarget,resampleStrat)) {
showError("couldn't resample! make sure your sample is 8 or 16-bit.");
}
e->renderSamples();
});
updateSampleTex=true;
sampleSelStart=-1;
sampleSelEnd=-1;
MARK_MODIFIED;
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
@ -230,6 +239,10 @@ void FurnaceGUI::drawSampleEdit() {
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Amplify");
}
if (openSampleAmplifyOpt) {
openSampleAmplifyOpt=false;
ImGui::OpenPopup("SAmplifyOpt");
}
if (ImGui::BeginPopupContextItem("SAmplifyOpt",ImGuiPopupFlags_MouseButtonLeft)) {
ImGui::Text("Volume");
if (ImGui::InputFloat("##SRVolume",&amplifyVol,10.0,50.0,"%g%%")) {
@ -238,33 +251,98 @@ void FurnaceGUI::drawSampleEdit() {
}
ImGui::SameLine();
ImGui::Text("(%.1fdB)",20.0*log10(amplifyVol/100.0f));
ImGui::Button("Apply");
if (ImGui::Button("Apply")) {
e->lockEngine([this,sample]() {
SAMPLE_OP_BEGIN;
float vol=amplifyVol/100.0f;
if (sample->depth==16) {
for (unsigned int i=start; i<end; i++) {
float val=sample->data16[i]*vol;
if (val<-32768) val=-32768;
if (val>32767) val=32767;
sample->data16[i]=val;
}
} else if (sample->depth==8) {
for (unsigned int i=start; i<end; i++) {
float val=sample->data8[i]*vol;
if (val<-128) val=-128;
if (val>127) val=127;
sample->data8[i]=val;
}
}
updateSampleTex=true;
e->renderSamples();
});
MARK_MODIFIED;
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
ImGui::SameLine();
ImGui::Button(ICON_FA_ARROWS_V "##SNormalize");
if (ImGui::Button(ICON_FA_ARROWS_V "##SNormalize")) {
doAction(GUI_ACTION_SAMPLE_NORMALIZE);
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Normalize");
}
ImGui::SameLine();
ImGui::Button(ICON_FA_ERASER "##SSilence");
if (ImGui::Button(ICON_FA_ARROW_UP "##SFadeIn")) {
doAction(GUI_ACTION_SAMPLE_FADE_IN);
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Fade in");
}
ImGui::SameLine();
if (ImGui::Button(ICON_FA_ARROW_DOWN "##SFadeOut")) {
doAction(GUI_ACTION_SAMPLE_FADE_OUT);
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Fade out");
}
ImGui::SameLine();
if (ImGui::Button(ICON_FA_ERASER "##SSilence")) {
doAction(GUI_ACTION_SAMPLE_SILENCE);
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Apply silence");
}
ImGui::SameLine();
if (ImGui::Button(ICON_FA_TIMES "##SDelete")) {
doAction(GUI_ACTION_SAMPLE_DELETE);
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Delete");
}
ImGui::SameLine();
if (ImGui::Button(ICON_FA_CROP "##STrim")) {
doAction(GUI_ACTION_SAMPLE_TRIM);
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Trim");
}
ImGui::SameLine();
ImGui::Dummy(ImVec2(4.0*dpiScale,dpiScale));
ImGui::SameLine();
ImGui::Button(ICON_FA_BACKWARD "##SReverse");
if (ImGui::Button(ICON_FA_BACKWARD "##SReverse")) {
doAction(GUI_ACTION_SAMPLE_REVERSE);
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Reverse");
}
ImGui::SameLine();
ImGui::Button(ICON_FA_SORT_AMOUNT_ASC "##SInvert");
if (ImGui::Button(ICON_FA_SORT_AMOUNT_ASC "##SInvert")) {
doAction(GUI_ACTION_SAMPLE_INVERT);
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Invert");
}
ImGui::SameLine();
ImGui::Button(ICON_FA_LEVEL_DOWN "##SSign");
if (ImGui::Button(ICON_FA_LEVEL_DOWN "##SSign")) {
doAction(GUI_ACTION_SAMPLE_SIGN);
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Signed/unsigned exchange");
}
@ -273,14 +351,178 @@ void FurnaceGUI::drawSampleEdit() {
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Apply filter");
}
if (openSampleFilterOpt) {
openSampleFilterOpt=false;
ImGui::OpenPopup("SFilterOpt");
}
if (ImGui::BeginPopupContextItem("SFilterOpt",ImGuiPopupFlags_MouseButtonLeft)) {
float lowP=sampleFilterL*100.0f;
float bandP=sampleFilterB*100.0f;
float highP=sampleFilterH*100.0f;
float resP=sampleFilterRes*100.0f;
ImGui::Text("Cutoff:");
if (ImGui::SliderFloat("From",&sampleFilterCutStart,0.0f,sample->rate*0.5,"%.0fHz")) {
if (sampleFilterCutStart<0.0) sampleFilterCutStart=0.0;
if (sampleFilterCutStart>sample->rate*0.5) sampleFilterCutStart=sample->rate*0.5;
}
if (ImGui::SliderFloat("To",&sampleFilterCutEnd,0.0f,sample->rate*0.5,"%.0fHz")) {
if (sampleFilterCutEnd<0.0) sampleFilterCutEnd=0.0;
if (sampleFilterCutEnd>sample->rate*0.5) sampleFilterCutEnd=sample->rate*0.5;
}
ImGui::Separator();
if (ImGui::SliderFloat("Resonance",&resP,0.0f,99.0f,"%.1f%%")) {
sampleFilterRes=resP/100.0f;
if (sampleFilterRes<0.0f) sampleFilterRes=0.0f;
if (sampleFilterRes>0.99f) sampleFilterRes=0.99f;
}
ImGui::Text("Power");
ImGui::SameLine();
if (ImGui::RadioButton("1x",sampleFilterPower==1)) {
sampleFilterPower=1;
}
ImGui::SameLine();
if (ImGui::RadioButton("2x",sampleFilterPower==2)) {
sampleFilterPower=2;
}
ImGui::SameLine();
if (ImGui::RadioButton("3x",sampleFilterPower==3)) {
sampleFilterPower=3;
}
ImGui::Separator();
if (ImGui::SliderFloat("Low-pass",&lowP,0.0f,100.0f,"%.1f%%")) {
sampleFilterL=lowP/100.0f;
if (sampleFilterL<0.0f) sampleFilterL=0.0f;
if (sampleFilterL>1.0f) sampleFilterL=1.0f;
}
if (ImGui::SliderFloat("Band-pass",&bandP,0.0f,100.0f,"%.1f%%")) {
sampleFilterB=bandP/100.0f;
if (sampleFilterB<0.0f) sampleFilterB=0.0f;
if (sampleFilterB>1.0f) sampleFilterB=1.0f;
}
if (ImGui::SliderFloat("High-pass",&highP,0.0f,100.0f,"%.1f%%")) {
sampleFilterH=highP/100.0f;
if (sampleFilterH<0.0f) sampleFilterH=0.0f;
if (sampleFilterH>1.0f) sampleFilterH=1.0f;
}
if (ImGui::Button("Apply")) {
e->lockEngine([this,sample]() {
SAMPLE_OP_BEGIN;
float res=1.0-pow(sampleFilterRes,0.5f);
float low=0;
float band=0;
float high=0;
double power=(sampleFilterCutStart>sampleFilterCutEnd)?0.5:2.0;
if (sample->depth==16) {
for (unsigned int i=start; i<end; i++) {
double freq=sampleFilterCutStart+(sampleFilterCutEnd-sampleFilterCutStart)*pow(double(i-start)/double(end-start),power);
double cut=sin((freq/double(sample->rate))*M_PI);
for (int j=0; j<sampleFilterPower; j++) {
low=low+cut*band;
high=float(sample->data16[i])-low-(res*band);
band=cut*high+band;
}
float val=low*sampleFilterL+band*sampleFilterB+high*sampleFilterH;
if (val<-32768) val=-32768;
if (val>32767) val=32767;
sample->data16[i]=val;
}
} else if (sample->depth==8) {
for (unsigned int i=start; i<end; i++) {
double freq=sampleFilterCutStart+(sampleFilterCutEnd-sampleFilterCutStart)*pow(double(i-start)/double(end-start),power);
double cut=sin((freq/double(sample->rate))*M_PI);
for (int j=0; j<sampleFilterPower; j++) {
low=low+cut*band;
high=float(sample->data8[i])-low-(res*band);
band=cut*high+band;
}
float val=low*sampleFilterL+band*sampleFilterB+high*sampleFilterH;
if (val<-128) val=-128;
if (val>127) val=127;
sample->data8[i]=val;
}
}
updateSampleTex=true;
e->renderSamples();
});
MARK_MODIFIED;
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
ImGui::SameLine();
ImGui::Dummy(ImVec2(4.0*dpiScale,dpiScale));
ImGui::SameLine();
if (ImGui::Button(ICON_FA_VOLUME_UP "##PreviewSample")) {
e->previewSample(curSample);
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Preview sample");
}
ImGui::SameLine();
if (ImGui::Button(ICON_FA_VOLUME_OFF "##StopSample")) {
e->stopSamplePreview();
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Stop sample preview");
}
ImGui::SameLine();
double zoomPercent=100.0/sampleZoom;
ImGui::Text("Zoom");
ImGui::SameLine();
ImGui::SetNextItemWidth(150.0f*dpiScale);
if (ImGui::InputDouble("##SZoom",&zoomPercent,5.0,20.0,"%g%%")) {
if (zoomPercent>10000.0) zoomPercent=10000.0;
if (zoomPercent<1.0) zoomPercent=1.0;
sampleZoom=100.0/zoomPercent;
if (sampleZoom<0.01) sampleZoom=0.01;
sampleZoomAuto=false;
updateSampleTex=true;
}
ImGui::SameLine();
if (sampleZoomAuto) {
if (ImGui::Button("100%")) {
sampleZoom=1.0;
sampleZoomAuto=false;
updateSampleTex=true;
}
} else {
if (ImGui::Button("Auto")) {
sampleZoomAuto=true;
updateSampleTex=true;
}
}
ImGui::Separator();
ImVec2 avail=ImGui::GetContentRegionAvail();
avail.y-=ImGui::GetFontSize()+ImGui::GetStyle().ItemSpacing.y;
avail.y-=ImGui::GetFontSize()+ImGui::GetStyle().ItemSpacing.y+ImGui::GetStyle().ScrollbarSize;
int availX=avail.x;
int availY=avail.y;
if (sampleZoomAuto) {
samplePos=0;
if (sample->samples<1 || avail.x<=0) {
sampleZoom=1.0;
} else {
sampleZoom=(double)sample->samples/avail.x;
}
if (sampleZoom!=prevSampleZoom) {
prevSampleZoom=sampleZoom;
updateSampleTex=true;
}
}
if (sampleTex==NULL || sampleTexW!=avail.x || sampleTexH!=avail.y) {
if (sampleTex!=NULL) {
SDL_DestroyTexture(sampleTex);
@ -320,7 +562,11 @@ void FurnaceGUI::drawSampleEdit() {
if (xCoarse>=sample->samples) break;
int y1, y2;
int totalAdvance=0;
y1=((unsigned short)sample->data16[xCoarse]^0x8000)*availY/65536;
if (sample->depth==8) {
y1=((unsigned char)sample->data8[xCoarse]^0x80)*availY/256;
} else {
y1=((unsigned short)sample->data16[xCoarse]^0x8000)*availY/65536;
}
xFine+=xAdvanceFine;
if (xFine>=16777216) {
xFine-=16777216;
@ -329,14 +575,22 @@ void FurnaceGUI::drawSampleEdit() {
totalAdvance+=xAdvanceCoarse;
if (xCoarse>=sample->samples) break;
do {
y2=((unsigned short)sample->data16[xCoarse]^0x8000)*availY/65536;
if (sample->depth==8) {
y2=((unsigned char)sample->data8[xCoarse]^0x80)*availY/256;
} else {
y2=((unsigned short)sample->data16[xCoarse]^0x8000)*availY/65536;
}
if (y1>y2) {
y2^=y1;
y1^=y2;
y2^=y1;
}
if (y1<0) y1=0;
if (y1>=availY) y1=availY-1;
if (y2<0) y2=0;
if (y2>=availY) y2=availY-1;
for (int j=y1; j<=y2; j++) {
data[i+availX*j]=lineColor;
data[i+availX*(availY-j-1)]=lineColor;
}
if (totalAdvance>0) xCoarse++;
} while ((totalAdvance--)>0);
@ -347,13 +601,35 @@ void FurnaceGUI::drawSampleEdit() {
}
ImGui::ImageButton(sampleTex,avail,ImVec2(0,0),ImVec2(1,1),0);
ImVec2 rectMin=ImGui::GetItemRectMin();
ImVec2 rectMax=ImGui::GetItemRectMax();
ImVec2 rectSize=ImGui::GetItemRectSize();
if (ImGui::IsItemClicked()) {
logD("drawing\n");
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
sampleDragActive=false;
sampleSelStart=0;
sampleSelEnd=sample->samples;
} else {
if (sample->samples>0 && (sample->depth==16 || sample->depth==8)) {
sampleDragStart=rectMin;
sampleDragAreaSize=rectSize;
sampleDrag16=(sample->depth==16);
sampleDragTarget=(sample->depth==16)?((void*)sample->data16):((void*)sample->data8);
sampleDragLen=sample->samples;
sampleDragActive=true;
sampleSelStart=-1;
sampleSelEnd=-1;
processDrags(ImGui::GetMousePos().x,ImGui::GetMousePos().y);
}
}
}
String statusBar=sampleDragMode?"Draw":"Select";
bool drawSelection=false;
if (!sampleDragMode) {
if (sampleSelStart>=0 && sampleSelEnd>=0) {
if (sampleSelStart>=0 && sampleSelEnd>=0 && sampleSelStart!=sampleSelEnd) {
int start=sampleSelStart;
int end=sampleSelEnd;
if (start>end) {
@ -362,6 +638,7 @@ void FurnaceGUI::drawSampleEdit() {
start^=end;
}
statusBar+=fmt::sprintf(" (%d-%d)",start,end);
drawSelection=true;
}
}
@ -369,19 +646,56 @@ void FurnaceGUI::drawSampleEdit() {
int posX=-1;
int posY=0;
ImVec2 pos=ImGui::GetMousePos();
pos.x-=ImGui::GetItemRectMin().x;
pos.y-=ImGui::GetItemRectMin().y;
pos.x-=rectMin.x;
pos.y-=rectMin.y;
if (sampleZoom>0) {
posX=samplePos+pos.x*sampleZoom;
if (posX>(int)sample->samples) posX=-1;
}
posY=(0.5-pos.y/ImGui::GetItemRectSize().y)*((sample->depth==8)?255:32767);
posY=(0.5-pos.y/rectSize.y)*((sample->depth==8)?255:32767);
if (posX>=0) {
statusBar+=fmt::sprintf(" | (%d, %d)",posX,posY);
}
}
if (drawSelection) {
int start=sampleSelStart;
int end=sampleSelEnd;
if (start>end) {
start^=end;
end^=start;
start^=end;
}
ImDrawList* dl=ImGui::GetWindowDrawList();
ImVec2 p1=rectMin;
p1.x+=start/sampleZoom-samplePos;
ImVec2 p2=ImVec2(rectMin.x+end/sampleZoom-samplePos,rectMax.y);
ImVec4 selColor=uiColors[GUI_COLOR_ACCENT_SECONDARY];
selColor.w*=0.25;
dl->AddRectFilled(p1,p2,ImGui::GetColorU32(selColor));
}
ImS64 scrollV=samplePos;
ImS64 availV=round(rectSize.x*sampleZoom);
ImS64 contentsV=MAX(sample->samples,MAX(availV,1));
if (ImGui::ScrollbarEx(ImRect(ImVec2(rectMin.x,rectMax.y),ImVec2(rectMax.x,rectMax.y+ImGui::GetStyle().ScrollbarSize)),ImGui::GetID("sampleScroll"),ImGuiAxis_X,&scrollV,availV,contentsV,0)) {
if (!sampleZoomAuto && samplePos!=scrollV) {
samplePos=scrollV;
updateSampleTex=true;
}
}
if (sample->depth!=8 && sample->depth!=16) {
statusBar="Non-8/16-bit samples cannot be edited without prior conversion.";
}
ImGui::EndDisabled();
ImGui::SetCursorPosY(ImGui::GetCursorPosY()+ImGui::GetStyle().ScrollbarSize);
ImGui::Text("%s",statusBar.c_str());
}

31
src/gui/sampleUtil.h Normal file
View File

@ -0,0 +1,31 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#define SAMPLE_OP_BEGIN \
unsigned int start=0; \
unsigned int end=sample->samples; \
if (sampleSelStart!=-1 && sampleSelEnd!=-1 && sampleSelStart!=sampleSelEnd) { \
start=sampleSelStart; \
end=sampleSelEnd; \
if (start>end) { \
start^=end; \
end^=start; \
start^=end; \
} \
}

View File

@ -18,10 +18,13 @@
*/
#include "gui.h"
#include "fonts.h"
#include "../ta-log.h"
#include "util.h"
#include "ImGuiFileDialog.h"
#include "IconsFontAwesome4.h"
#include "misc/cpp/imgui_stdlib.h"
#include <SDL_keycode.h>
#include <SDL_scancode.h>
#include <fmt/printf.h>
#include <imgui.h>
@ -344,6 +347,19 @@ void FurnaceGUI::drawSettings() {
if (settings.patFontSize>96) settings.patFontSize=96;
}
bool loadJapaneseB=settings.loadJapanese;
if (ImGui::Checkbox("Display Japanese characters",&loadJapaneseB)) {
settings.loadJapanese=loadJapaneseB;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip(
"Only toggle this option if you have enough graphics memory.\n"
"This is a temporary solution until dynamic font atlas is implemented in Dear ImGui.\n\n"
"このオプションは、十分なグラフィックメモリがある場合にのみ切り替えてください。\n"
"これは、Dear ImGuiにダイナミックフォントアトラスが実装されるまでの一時的な解決策です。"
);
}
ImGui::Separator();
ImGui::Text("Orders row number format:");
@ -449,6 +465,23 @@ void FurnaceGUI::drawSettings() {
ImGui::Separator();
bool roundedWindowsB=settings.roundedWindows;
if (ImGui::Checkbox("Rounded window corners",&roundedWindowsB)) {
settings.roundedWindows=roundedWindowsB;
}
bool roundedButtonsB=settings.roundedButtons;
if (ImGui::Checkbox("Rounded buttons",&roundedButtonsB)) {
settings.roundedButtons=roundedButtonsB;
}
bool roundedMenusB=settings.roundedMenus;
if (ImGui::Checkbox("Rounded menu corners",&roundedMenusB)) {
settings.roundedMenus=roundedMenusB;
}
ImGui::Separator();
if (ImGui::TreeNode("Color scheme")) {
if (ImGui::TreeNode("General")) {
ImGui::Text("Color scheme type:");
@ -465,6 +498,8 @@ void FurnaceGUI::drawSettings() {
UI_COLOR_CONFIG(GUI_COLOR_TEXT,"Text");
UI_COLOR_CONFIG(GUI_COLOR_ACCENT_PRIMARY,"Primary");
UI_COLOR_CONFIG(GUI_COLOR_ACCENT_SECONDARY,"Secondary");
UI_COLOR_CONFIG(GUI_COLOR_TOGGLE_ON,"Toggle on");
UI_COLOR_CONFIG(GUI_COLOR_TOGGLE_OFF,"Toggle off");
UI_COLOR_CONFIG(GUI_COLOR_EDITING,"Editing");
UI_COLOR_CONFIG(GUI_COLOR_SONG_LOOP,"Song loop");
UI_COLOR_CONFIG(GUI_COLOR_PLAYBACK_STAT,"Playback status");
@ -562,6 +597,7 @@ void FurnaceGUI::drawSettings() {
KEYBIND_CONFIG_BEGIN("keysGlobal");
UI_KEYBIND_CONFIG(GUI_ACTION_OPEN,"Open file");
UI_KEYBIND_CONFIG(GUI_ACTION_OPEN_BACKUP,"Restore backup");
UI_KEYBIND_CONFIG(GUI_ACTION_SAVE,"Save file");
UI_KEYBIND_CONFIG(GUI_ACTION_SAVE_AS,"Save as");
UI_KEYBIND_CONFIG(GUI_ACTION_UNDO,"Undo");
@ -830,6 +866,39 @@ void FurnaceGUI::drawSettings() {
KEYBIND_CONFIG_END;
ImGui::TreePop();
}
if (ImGui::TreeNode("Sample editor")) {
KEYBIND_CONFIG_BEGIN("keysSampleEdit");
UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_SELECT,"Edit mode: Select");
UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_DRAW,"Edit mode: Draw");
UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_CUT,"Cut");
UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_COPY,"Copy");
UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_PASTE,"Paste");
UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_PASTE_REPLACE,"Paste replace");
UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_PASTE_MIX,"Paste mix");
UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_SELECT_ALL,"Select all");
UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_RESIZE,"Resize");
UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_RESAMPLE,"Resample");
UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_AMPLIFY,"Amplify");
UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_NORMALIZE,"Normalize");
UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_FADE_IN,"Fade in");
UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_FADE_OUT,"Fade out");
UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_SILENCE,"Apply silence");
UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_DELETE,"Delete");
UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_TRIM,"Trim");
UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_REVERSE,"Reverse");
UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_INVERT,"Invert");
UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_SIGN,"Signed/unsigned exchange");
UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_FILTER,"Apply filter");
UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_PREVIEW,"Preview sample");
UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_STOP_PREVIEW,"Stop sample preview");
UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_ZOOM_IN,"Zoom in");
UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_ZOOM_OUT,"Zoom out");
UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_ZOOM_AUTO,"Toggle auto-zoom");
KEYBIND_CONFIG_END;
ImGui::TreePop();
}
ImGui::EndTabItem();
}
ImGui::EndTabBar();
@ -904,6 +973,10 @@ void FurnaceGUI::syncSettings() {
settings.stepOnInsert=e->getConfInt("stepOnInsert",0);
settings.unifiedDataView=e->getConfInt("unifiedDataView",0);
settings.sysFileDialog=e->getConfInt("sysFileDialog",1);
settings.roundedWindows=e->getConfInt("roundedWindows",1);
settings.roundedButtons=e->getConfInt("roundedButtons",1);
settings.roundedMenus=e->getConfInt("roundedMenus",0);
settings.loadJapanese=e->getConfInt("loadJapanese",0);
clampSetting(settings.mainFontSize,2,96);
clampSetting(settings.patFontSize,2,96);
@ -944,9 +1017,14 @@ void FurnaceGUI::syncSettings() {
clampSetting(settings.stepOnInsert,0,1);
clampSetting(settings.unifiedDataView,0,1);
clampSetting(settings.sysFileDialog,0,1);
clampSetting(settings.roundedWindows,0,1);
clampSetting(settings.roundedButtons,0,1);
clampSetting(settings.roundedMenus,0,1);
clampSetting(settings.loadJapanese,0,1);
// keybinds
LOAD_KEYBIND(GUI_ACTION_OPEN,FURKMOD_CMD|SDLK_o);
LOAD_KEYBIND(GUI_ACTION_OPEN_BACKUP,0);
LOAD_KEYBIND(GUI_ACTION_SAVE,FURKMOD_CMD|SDLK_s);
LOAD_KEYBIND(GUI_ACTION_SAVE_AS,FURKMOD_CMD|FURKMOD_SHIFT|SDLK_s);
LOAD_KEYBIND(GUI_ACTION_UNDO,FURKMOD_CMD|SDLK_z);
@ -1075,6 +1153,33 @@ void FurnaceGUI::syncSettings() {
LOAD_KEYBIND(GUI_ACTION_SAMPLE_LIST_PREVIEW,0);
LOAD_KEYBIND(GUI_ACTION_SAMPLE_LIST_STOP_PREVIEW,0);
LOAD_KEYBIND(GUI_ACTION_SAMPLE_SELECT,FURKMOD_SHIFT|SDLK_i);
LOAD_KEYBIND(GUI_ACTION_SAMPLE_DRAW,FURKMOD_SHIFT|SDLK_d);
LOAD_KEYBIND(GUI_ACTION_SAMPLE_CUT,FURKMOD_CMD|SDLK_x);
LOAD_KEYBIND(GUI_ACTION_SAMPLE_COPY,FURKMOD_CMD|SDLK_c);
LOAD_KEYBIND(GUI_ACTION_SAMPLE_PASTE,FURKMOD_CMD|SDLK_v);
LOAD_KEYBIND(GUI_ACTION_SAMPLE_PASTE_REPLACE,FURKMOD_CMD|FURKMOD_SHIFT|SDLK_x);
LOAD_KEYBIND(GUI_ACTION_SAMPLE_PASTE_MIX,FURKMOD_CMD|FURKMOD_ALT|SDLK_x);
LOAD_KEYBIND(GUI_ACTION_SAMPLE_SELECT_ALL,FURKMOD_CMD|SDLK_a);
LOAD_KEYBIND(GUI_ACTION_SAMPLE_RESIZE,FURKMOD_CMD|SDLK_r);
LOAD_KEYBIND(GUI_ACTION_SAMPLE_RESAMPLE,FURKMOD_CMD|SDLK_e);
LOAD_KEYBIND(GUI_ACTION_SAMPLE_AMPLIFY,FURKMOD_CMD|SDLK_b);
LOAD_KEYBIND(GUI_ACTION_SAMPLE_NORMALIZE,FURKMOD_CMD|SDLK_n);
LOAD_KEYBIND(GUI_ACTION_SAMPLE_FADE_IN,FURKMOD_CMD|SDLK_i);
LOAD_KEYBIND(GUI_ACTION_SAMPLE_FADE_OUT,FURKMOD_CMD|SDLK_o);
LOAD_KEYBIND(GUI_ACTION_SAMPLE_SILENCE,FURKMOD_SHIFT|SDLK_DELETE);
LOAD_KEYBIND(GUI_ACTION_SAMPLE_DELETE,SDLK_DELETE);
LOAD_KEYBIND(GUI_ACTION_SAMPLE_TRIM,FURKMOD_CMD|SDLK_DELETE);
LOAD_KEYBIND(GUI_ACTION_SAMPLE_REVERSE,FURKMOD_CMD|SDLK_t);
LOAD_KEYBIND(GUI_ACTION_SAMPLE_INVERT,FURKMOD_CMD|FURKMOD_SHIFT|SDLK_t);
LOAD_KEYBIND(GUI_ACTION_SAMPLE_SIGN,FURKMOD_CMD|SDLK_u);
LOAD_KEYBIND(GUI_ACTION_SAMPLE_FILTER,FURKMOD_CMD|SDLK_f);
LOAD_KEYBIND(GUI_ACTION_SAMPLE_PREVIEW,0);
LOAD_KEYBIND(GUI_ACTION_SAMPLE_STOP_PREVIEW,0);
LOAD_KEYBIND(GUI_ACTION_SAMPLE_ZOOM_IN,FURKMOD_CMD|SDLK_EQUALS);
LOAD_KEYBIND(GUI_ACTION_SAMPLE_ZOOM_OUT,FURKMOD_CMD|SDLK_MINUS);
LOAD_KEYBIND(GUI_ACTION_SAMPLE_ZOOM_AUTO,FURKMOD_CMD|SDLK_0);
LOAD_KEYBIND(GUI_ACTION_ORDERS_UP,SDLK_UP);
LOAD_KEYBIND(GUI_ACTION_ORDERS_DOWN,SDLK_DOWN);
LOAD_KEYBIND(GUI_ACTION_ORDERS_LEFT,SDLK_LEFT);
@ -1145,6 +1250,10 @@ void FurnaceGUI::commitSettings() {
e->setConf("stepOnInsert",settings.stepOnInsert);
e->setConf("unifiedDataView",settings.unifiedDataView);
e->setConf("sysFileDialog",settings.sysFileDialog);
e->setConf("roundedWindows",settings.roundedWindows);
e->setConf("roundedButtons",settings.roundedButtons);
e->setConf("roundedMenus",settings.roundedMenus);
e->setConf("loadJapanese",settings.loadJapanese);
PUT_UI_COLOR(GUI_COLOR_BACKGROUND);
PUT_UI_COLOR(GUI_COLOR_FRAME_BACKGROUND);
@ -1153,6 +1262,8 @@ void FurnaceGUI::commitSettings() {
PUT_UI_COLOR(GUI_COLOR_TEXT);
PUT_UI_COLOR(GUI_COLOR_ACCENT_PRIMARY);
PUT_UI_COLOR(GUI_COLOR_ACCENT_SECONDARY);
PUT_UI_COLOR(GUI_COLOR_TOGGLE_ON);
PUT_UI_COLOR(GUI_COLOR_TOGGLE_OFF);
PUT_UI_COLOR(GUI_COLOR_EDITING);
PUT_UI_COLOR(GUI_COLOR_SONG_LOOP);
PUT_UI_COLOR(GUI_COLOR_VOLMETER_LOW);
@ -1225,6 +1336,7 @@ void FurnaceGUI::commitSettings() {
PUT_UI_COLOR(GUI_COLOR_PLAYBACK_STAT);
SAVE_KEYBIND(GUI_ACTION_OPEN);
SAVE_KEYBIND(GUI_ACTION_OPEN_BACKUP);
SAVE_KEYBIND(GUI_ACTION_SAVE);
SAVE_KEYBIND(GUI_ACTION_SAVE_AS);
SAVE_KEYBIND(GUI_ACTION_UNDO);
@ -1353,6 +1465,33 @@ void FurnaceGUI::commitSettings() {
SAVE_KEYBIND(GUI_ACTION_SAMPLE_LIST_PREVIEW);
SAVE_KEYBIND(GUI_ACTION_SAMPLE_LIST_STOP_PREVIEW);
SAVE_KEYBIND(GUI_ACTION_SAMPLE_SELECT);
SAVE_KEYBIND(GUI_ACTION_SAMPLE_DRAW);
SAVE_KEYBIND(GUI_ACTION_SAMPLE_CUT);
SAVE_KEYBIND(GUI_ACTION_SAMPLE_COPY);
SAVE_KEYBIND(GUI_ACTION_SAMPLE_PASTE);
SAVE_KEYBIND(GUI_ACTION_SAMPLE_PASTE_REPLACE);
SAVE_KEYBIND(GUI_ACTION_SAMPLE_PASTE_MIX);
SAVE_KEYBIND(GUI_ACTION_SAMPLE_SELECT_ALL);
SAVE_KEYBIND(GUI_ACTION_SAMPLE_RESIZE);
SAVE_KEYBIND(GUI_ACTION_SAMPLE_RESAMPLE);
SAVE_KEYBIND(GUI_ACTION_SAMPLE_AMPLIFY);
SAVE_KEYBIND(GUI_ACTION_SAMPLE_NORMALIZE);
SAVE_KEYBIND(GUI_ACTION_SAMPLE_FADE_IN);
SAVE_KEYBIND(GUI_ACTION_SAMPLE_FADE_OUT);
SAVE_KEYBIND(GUI_ACTION_SAMPLE_SILENCE);
SAVE_KEYBIND(GUI_ACTION_SAMPLE_DELETE);
SAVE_KEYBIND(GUI_ACTION_SAMPLE_TRIM);
SAVE_KEYBIND(GUI_ACTION_SAMPLE_REVERSE);
SAVE_KEYBIND(GUI_ACTION_SAMPLE_INVERT);
SAVE_KEYBIND(GUI_ACTION_SAMPLE_SIGN);
SAVE_KEYBIND(GUI_ACTION_SAMPLE_FILTER);
SAVE_KEYBIND(GUI_ACTION_SAMPLE_PREVIEW);
SAVE_KEYBIND(GUI_ACTION_SAMPLE_STOP_PREVIEW);
SAVE_KEYBIND(GUI_ACTION_SAMPLE_ZOOM_IN);
SAVE_KEYBIND(GUI_ACTION_SAMPLE_ZOOM_OUT);
SAVE_KEYBIND(GUI_ACTION_SAMPLE_ZOOM_AUTO);
SAVE_KEYBIND(GUI_ACTION_ORDERS_UP);
SAVE_KEYBIND(GUI_ACTION_ORDERS_DOWN);
SAVE_KEYBIND(GUI_ACTION_ORDERS_LEFT);
@ -1396,3 +1535,434 @@ void FurnaceGUI::commitSettings() {
}
}
}
void FurnaceGUI::parseKeybinds() {
actionMapGlobal.clear();
actionMapPat.clear();
actionMapInsList.clear();
actionMapWaveList.clear();
actionMapSampleList.clear();
actionMapSample.clear();
actionMapOrders.clear();
for (int i=GUI_ACTION_GLOBAL_MIN+1; i<GUI_ACTION_GLOBAL_MAX; i++) {
if (actionKeys[i]&FURK_MASK) {
actionMapGlobal[actionKeys[i]]=i;
}
}
for (int i=GUI_ACTION_PAT_MIN+1; i<GUI_ACTION_PAT_MAX; i++) {
if (actionKeys[i]&FURK_MASK) {
actionMapPat[actionKeys[i]]=i;
}
}
for (int i=GUI_ACTION_INS_LIST_MIN+1; i<GUI_ACTION_INS_LIST_MAX; i++) {
if (actionKeys[i]&FURK_MASK) {
actionMapInsList[actionKeys[i]]=i;
}
}
for (int i=GUI_ACTION_WAVE_LIST_MIN+1; i<GUI_ACTION_WAVE_LIST_MAX; i++) {
if (actionKeys[i]&FURK_MASK) {
actionMapWaveList[actionKeys[i]]=i;
}
}
for (int i=GUI_ACTION_SAMPLE_LIST_MIN+1; i<GUI_ACTION_SAMPLE_LIST_MAX; i++) {
if (actionKeys[i]&FURK_MASK) {
actionMapSampleList[actionKeys[i]]=i;
}
}
for (int i=GUI_ACTION_SAMPLE_MIN+1; i<GUI_ACTION_SAMPLE_MAX; i++) {
if (actionKeys[i]&FURK_MASK) {
actionMapSample[actionKeys[i]]=i;
}
}
for (int i=GUI_ACTION_ORDERS_MIN+1; i<GUI_ACTION_ORDERS_MAX; i++) {
if (actionKeys[i]&FURK_MASK) {
actionMapOrders[actionKeys[i]]=i;
}
}
}
#define IGFD_FileStyleByExtension IGFD_FileStyleByExtention
#define GET_UI_COLOR(target,def) \
uiColors[target]=ImGui::ColorConvertU32ToFloat4(e->getConfInt(#target,ImGui::GetColorU32(def)));
#ifdef _WIN32
#define SYSTEM_FONT_PATH_1 "C:\\Windows\\Fonts\\segoeui.ttf"
#define SYSTEM_FONT_PATH_2 "C:\\Windows\\Fonts\\tahoma.ttf"
// TODO!
#define SYSTEM_FONT_PATH_3 "C:\\Windows\\Fonts\\tahoma.ttf"
// TODO!
#define SYSTEM_PAT_FONT_PATH_1 "C:\\Windows\\Fonts\\consola.ttf"
#define SYSTEM_PAT_FONT_PATH_2 "C:\\Windows\\Fonts\\cour.ttf"
// GOOD LUCK WITH THIS ONE - UNTESTED
#define SYSTEM_PAT_FONT_PATH_3 "C:\\Windows\\Fonts\\vgasys.fon"
#elif defined(__APPLE__)
#define SYSTEM_FONT_PATH_1 "/System/Library/Fonts/SFAANS.ttf"
#define SYSTEM_FONT_PATH_2 "/System/Library/Fonts/Helvetica.ttc"
#define SYSTEM_FONT_PATH_3 "/System/Library/Fonts/Helvetica.dfont"
#define SYSTEM_PAT_FONT_PATH_1 "/System/Library/Fonts/SFNSMono.ttf"
#define SYSTEM_PAT_FONT_PATH_2 "/System/Library/Fonts/Courier New.ttf"
#define SYSTEM_PAT_FONT_PATH_3 "/System/Library/Fonts/Courier New.ttf"
#else
#define SYSTEM_FONT_PATH_1 "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"
#define SYSTEM_FONT_PATH_2 "/usr/share/fonts/TTF/DejaVuSans.ttf"
#define SYSTEM_FONT_PATH_3 "/usr/share/fonts/ubuntu/Ubuntu-R.ttf"
#define SYSTEM_PAT_FONT_PATH_1 "/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf"
#define SYSTEM_PAT_FONT_PATH_2 "/usr/share/fonts/TTF/DejaVuSansMono.ttf"
#define SYSTEM_PAT_FONT_PATH_3 "/usr/share/fonts/ubuntu/UbuntuMono-R.ttf"
#endif
void FurnaceGUI::applyUISettings() {
ImGuiStyle sty;
if (settings.guiColorsBase) {
ImGui::StyleColorsLight(&sty);
} else {
ImGui::StyleColorsDark(&sty);
}
if (settings.dpiScale>=0.5f) dpiScale=settings.dpiScale;
GET_UI_COLOR(GUI_COLOR_BACKGROUND,ImVec4(0.1f,0.1f,0.1f,1.0f));
GET_UI_COLOR(GUI_COLOR_FRAME_BACKGROUND,ImVec4(0.0f,0.0f,0.0f,0.85f));
GET_UI_COLOR(GUI_COLOR_MODAL_BACKDROP,ImVec4(0.0f,0.0f,0.0f,0.55f));
GET_UI_COLOR(GUI_COLOR_HEADER,ImVec4(0.2f,0.2f,0.2f,1.0f));
GET_UI_COLOR(GUI_COLOR_TEXT,ImVec4(1.0f,1.0f,1.0f,1.0f));
GET_UI_COLOR(GUI_COLOR_ACCENT_PRIMARY,ImVec4(0.06f,0.53f,0.98f,1.0f));
GET_UI_COLOR(GUI_COLOR_ACCENT_SECONDARY,ImVec4(0.26f,0.59f,0.98f,1.0f));
GET_UI_COLOR(GUI_COLOR_TOGGLE_ON,ImVec4(0.2f,0.6f,0.2f,1.0f));
GET_UI_COLOR(GUI_COLOR_TOGGLE_OFF,ImVec4(0.2f,0.2f,0.2f,1.0f));
GET_UI_COLOR(GUI_COLOR_EDITING,ImVec4(0.2f,0.1f,0.1f,1.0f));
GET_UI_COLOR(GUI_COLOR_SONG_LOOP,ImVec4(0.3f,0.5f,0.8f,0.4f));
GET_UI_COLOR(GUI_COLOR_VOLMETER_LOW,ImVec4(0.2f,0.6f,0.2f,1.0f));
GET_UI_COLOR(GUI_COLOR_VOLMETER_HIGH,ImVec4(1.0f,0.9f,0.2f,1.0f));
GET_UI_COLOR(GUI_COLOR_VOLMETER_PEAK,ImVec4(1.0f,0.1f,0.1f,1.0f));
GET_UI_COLOR(GUI_COLOR_MACRO_VOLUME,ImVec4(0.2f,1.0f,0.0f,1.0f));
GET_UI_COLOR(GUI_COLOR_MACRO_PITCH,ImVec4(1.0f,0.8f,0.0f,1.0f));
GET_UI_COLOR(GUI_COLOR_MACRO_OTHER,ImVec4(0.0f,0.9f,1.0f,1.0f));
GET_UI_COLOR(GUI_COLOR_MACRO_WAVE,ImVec4(1.0f,0.4f,0.0f,1.0f));
GET_UI_COLOR(GUI_COLOR_INSTR_FM,ImVec4(0.6f,0.9f,1.0f,1.0f));
GET_UI_COLOR(GUI_COLOR_INSTR_STD,ImVec4(0.6f,1.0f,0.5f,1.0f));
GET_UI_COLOR(GUI_COLOR_INSTR_GB,ImVec4(1.0f,1.0f,0.5f,1.0f));
GET_UI_COLOR(GUI_COLOR_INSTR_C64,ImVec4(0.85f,0.8f,1.0f,1.0f));
GET_UI_COLOR(GUI_COLOR_INSTR_AMIGA,ImVec4(1.0f,0.5f,0.5f,1.0f));
GET_UI_COLOR(GUI_COLOR_INSTR_PCE,ImVec4(1.0f,0.8f,0.5f,1.0f));
GET_UI_COLOR(GUI_COLOR_INSTR_AY,ImVec4(1.0f,0.5f,1.0f,1.0f));
GET_UI_COLOR(GUI_COLOR_INSTR_AY8930,ImVec4(0.7f,0.5f,1.0f,1.0f));
GET_UI_COLOR(GUI_COLOR_INSTR_TIA,ImVec4(1.0f,0.6f,0.4f,1.0f));
GET_UI_COLOR(GUI_COLOR_INSTR_SAA1099,ImVec4(0.3f,0.3f,1.0f,1.0f));
GET_UI_COLOR(GUI_COLOR_INSTR_VIC,ImVec4(0.2f,1.0f,0.6f,1.0f));
GET_UI_COLOR(GUI_COLOR_INSTR_PET,ImVec4(1.0f,1.0f,0.8f,1.0f));
GET_UI_COLOR(GUI_COLOR_INSTR_VRC6,ImVec4(1.0f,0.9f,0.5f,1.0f));
GET_UI_COLOR(GUI_COLOR_INSTR_OPLL,ImVec4(0.6f,0.7f,1.0f,1.0f));
GET_UI_COLOR(GUI_COLOR_INSTR_OPL,ImVec4(0.3f,1.0f,0.9f,1.0f));
GET_UI_COLOR(GUI_COLOR_INSTR_FDS,ImVec4(0.8f,0.5f,1.0f,1.0f));
GET_UI_COLOR(GUI_COLOR_INSTR_VBOY,ImVec4(1.0f,0.1f,0.1f,1.0f));
GET_UI_COLOR(GUI_COLOR_INSTR_N163,ImVec4(1.0f,0.4f,0.1f,1.0f));
GET_UI_COLOR(GUI_COLOR_INSTR_SCC,ImVec4(0.7f,1.0f,0.3f,1.0f));
GET_UI_COLOR(GUI_COLOR_INSTR_OPZ,ImVec4(0.2f,0.8f,1.0f,1.0f));
GET_UI_COLOR(GUI_COLOR_INSTR_POKEY,ImVec4(0.5f,1.0f,0.3f,1.0f));
GET_UI_COLOR(GUI_COLOR_INSTR_BEEPER,ImVec4(0.0f,1.0f,0.0f,1.0f));
GET_UI_COLOR(GUI_COLOR_INSTR_SWAN,ImVec4(0.3f,0.5f,1.0f,1.0f));
GET_UI_COLOR(GUI_COLOR_INSTR_MIKEY,ImVec4(0.5f,1.0f,0.3f,1.0f));
GET_UI_COLOR(GUI_COLOR_INSTR_VERA,ImVec4(0.4f,0.6f,1.0f,1.0f));
GET_UI_COLOR(GUI_COLOR_INSTR_X1_010,ImVec4(0.3f,0.5f,1.0f,1.0f));
GET_UI_COLOR(GUI_COLOR_INSTR_UNKNOWN,ImVec4(0.3f,0.3f,0.3f,1.0f));
GET_UI_COLOR(GUI_COLOR_CHANNEL_FM,ImVec4(0.2f,0.8f,1.0f,1.0f));
GET_UI_COLOR(GUI_COLOR_CHANNEL_PULSE,ImVec4(0.4f,1.0f,0.2f,1.0f));
GET_UI_COLOR(GUI_COLOR_CHANNEL_NOISE,ImVec4(0.8f,0.8f,0.8f,1.0f));
GET_UI_COLOR(GUI_COLOR_CHANNEL_PCM,ImVec4(1.0f,0.9f,0.2f,1.0f));
GET_UI_COLOR(GUI_COLOR_CHANNEL_WAVE,ImVec4(1.0f,0.5f,0.2f,1.0f));
GET_UI_COLOR(GUI_COLOR_CHANNEL_OP,ImVec4(0.2f,0.4f,1.0f,1.0f));
GET_UI_COLOR(GUI_COLOR_CHANNEL_MUTED,ImVec4(0.5f,0.5f,0.5f,1.0f));
GET_UI_COLOR(GUI_COLOR_PATTERN_CURSOR,ImVec4(0.1f,0.3f,0.5f,1.0f));
GET_UI_COLOR(GUI_COLOR_PATTERN_CURSOR_HOVER,ImVec4(0.2f,0.4f,0.6f,1.0f));
GET_UI_COLOR(GUI_COLOR_PATTERN_CURSOR_ACTIVE,ImVec4(0.2f,0.5f,0.7f,1.0f));
GET_UI_COLOR(GUI_COLOR_PATTERN_SELECTION,ImVec4(0.15f,0.15f,0.2f,1.0f));
GET_UI_COLOR(GUI_COLOR_PATTERN_SELECTION_HOVER,ImVec4(0.2f,0.2f,0.3f,1.0f));
GET_UI_COLOR(GUI_COLOR_PATTERN_SELECTION_ACTIVE,ImVec4(0.4f,0.4f,0.5f,1.0f));
GET_UI_COLOR(GUI_COLOR_PATTERN_HI_1,ImVec4(0.6f,0.6f,0.6f,0.2f));
GET_UI_COLOR(GUI_COLOR_PATTERN_HI_2,ImVec4(0.5f,0.8f,1.0f,0.2f));
GET_UI_COLOR(GUI_COLOR_PATTERN_ROW_INDEX,ImVec4(0.5f,0.8f,1.0f,1.0f));
GET_UI_COLOR(GUI_COLOR_PATTERN_ACTIVE,ImVec4(1.0f,1.0f,1.0f,1.0f));
GET_UI_COLOR(GUI_COLOR_PATTERN_INACTIVE,ImVec4(0.5f,0.5f,0.5f,1.0f));
GET_UI_COLOR(GUI_COLOR_PATTERN_INS,ImVec4(0.4f,0.7f,1.0f,1.0f));
GET_UI_COLOR(GUI_COLOR_PATTERN_VOLUME_MIN,ImVec4(0.0f,0.5f,0.0f,1.0f));
GET_UI_COLOR(GUI_COLOR_PATTERN_VOLUME_HALF,ImVec4(0.0f,0.75f,0.0f,1.0f));
GET_UI_COLOR(GUI_COLOR_PATTERN_VOLUME_MAX,ImVec4(0.0f,1.0f,0.0f,1.0f));
GET_UI_COLOR(GUI_COLOR_PATTERN_EFFECT_INVALID,ImVec4(1.0f,0.0f,0.0f,1.0f));
GET_UI_COLOR(GUI_COLOR_PATTERN_EFFECT_PITCH,ImVec4(1.0f,1.0f,0.0f,1.0f));
GET_UI_COLOR(GUI_COLOR_PATTERN_EFFECT_VOLUME,ImVec4(0.0f,1.0f,0.0f,1.0f));
GET_UI_COLOR(GUI_COLOR_PATTERN_EFFECT_PANNING,ImVec4(0.0f,1.0f,1.0f,1.0f));
GET_UI_COLOR(GUI_COLOR_PATTERN_EFFECT_SONG,ImVec4(1.0f,0.0f,0.0f,1.0f));
GET_UI_COLOR(GUI_COLOR_PATTERN_EFFECT_TIME,ImVec4(0.5f,0.0f,1.0f,1.0f));
GET_UI_COLOR(GUI_COLOR_PATTERN_EFFECT_SPEED,ImVec4(1.0f,0.0f,1.0f,1.0f));
GET_UI_COLOR(GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,ImVec4(0.5f,1.0f,0.0f,1.0f));
GET_UI_COLOR(GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY,ImVec4(0.0f,1.0f,0.5f,1.0f));
GET_UI_COLOR(GUI_COLOR_PATTERN_EFFECT_MISC,ImVec4(0.3f,0.3f,1.0f,1.0f));
GET_UI_COLOR(GUI_COLOR_EE_VALUE,ImVec4(0.0f,1.0f,1.0f,1.0f));
GET_UI_COLOR(GUI_COLOR_PLAYBACK_STAT,ImVec4(0.6f,0.6f,0.6f,1.0f));
for (int i=0; i<64; i++) {
ImVec4 col1=uiColors[GUI_COLOR_PATTERN_VOLUME_MIN];
ImVec4 col2=uiColors[GUI_COLOR_PATTERN_VOLUME_HALF];
ImVec4 col3=uiColors[GUI_COLOR_PATTERN_VOLUME_MAX];
volColors[i]=ImVec4(col1.x+((col2.x-col1.x)*float(i)/64.0f),
col1.y+((col2.y-col1.y)*float(i)/64.0f),
col1.z+((col2.z-col1.z)*float(i)/64.0f),
1.0f);
volColors[i+64]=ImVec4(col2.x+((col3.x-col2.x)*float(i)/64.0f),
col2.y+((col3.y-col2.y)*float(i)/64.0f),
col2.z+((col3.z-col2.z)*float(i)/64.0f),
1.0f);
}
float hue, sat, val;
ImVec4 primaryActive=uiColors[GUI_COLOR_ACCENT_PRIMARY];
ImVec4 primaryHover, primary;
primaryHover.w=primaryActive.w;
primary.w=primaryActive.w;
ImGui::ColorConvertRGBtoHSV(primaryActive.x,primaryActive.y,primaryActive.z,hue,sat,val);
if (settings.guiColorsBase) {
primary=primaryActive;
ImGui::ColorConvertHSVtoRGB(hue,sat*0.9,val*0.9,primaryHover.x,primaryHover.y,primaryHover.z);
ImGui::ColorConvertHSVtoRGB(hue,sat,val*0.5,primaryActive.x,primaryActive.y,primaryActive.z);
} else {
ImGui::ColorConvertHSVtoRGB(hue,sat*0.9,val*0.5,primaryHover.x,primaryHover.y,primaryHover.z);
ImGui::ColorConvertHSVtoRGB(hue,sat*0.8,val*0.35,primary.x,primary.y,primary.z);
}
ImVec4 secondaryActive=uiColors[GUI_COLOR_ACCENT_SECONDARY];
ImVec4 secondaryHover, secondary, secondarySemiActive;
secondarySemiActive.w=secondaryActive.w;
secondaryHover.w=secondaryActive.w;
secondary.w=secondaryActive.w;
ImGui::ColorConvertRGBtoHSV(secondaryActive.x,secondaryActive.y,secondaryActive.z,hue,sat,val);
if (settings.guiColorsBase) {
secondary=secondaryActive;
ImGui::ColorConvertHSVtoRGB(hue,sat*0.9,val*0.7,secondarySemiActive.x,secondarySemiActive.y,secondarySemiActive.z);
ImGui::ColorConvertHSVtoRGB(hue,sat*0.9,val*0.9,secondaryHover.x,secondaryHover.y,secondaryHover.z);
ImGui::ColorConvertHSVtoRGB(hue,sat,val*0.5,secondaryActive.x,secondaryActive.y,secondaryActive.z);
} else {
ImGui::ColorConvertHSVtoRGB(hue,sat*0.9,val*0.75,secondarySemiActive.x,secondarySemiActive.y,secondarySemiActive.z);
ImGui::ColorConvertHSVtoRGB(hue,sat*0.9,val*0.5,secondaryHover.x,secondaryHover.y,secondaryHover.z);
ImGui::ColorConvertHSVtoRGB(hue,sat*0.9,val*0.25,secondary.x,secondary.y,secondary.z);
}
sty.Colors[ImGuiCol_WindowBg]=uiColors[GUI_COLOR_FRAME_BACKGROUND];
sty.Colors[ImGuiCol_ModalWindowDimBg]=uiColors[GUI_COLOR_MODAL_BACKDROP];
sty.Colors[ImGuiCol_Text]=uiColors[GUI_COLOR_TEXT];
sty.Colors[ImGuiCol_Button]=primary;
sty.Colors[ImGuiCol_ButtonHovered]=primaryHover;
sty.Colors[ImGuiCol_ButtonActive]=primaryActive;
sty.Colors[ImGuiCol_Tab]=primary;
sty.Colors[ImGuiCol_TabHovered]=secondaryHover;
sty.Colors[ImGuiCol_TabActive]=secondarySemiActive;
sty.Colors[ImGuiCol_TabUnfocused]=primary;
sty.Colors[ImGuiCol_TabUnfocusedActive]=primaryHover;
sty.Colors[ImGuiCol_Header]=secondary;
sty.Colors[ImGuiCol_HeaderHovered]=secondaryHover;
sty.Colors[ImGuiCol_HeaderActive]=secondaryActive;
sty.Colors[ImGuiCol_ResizeGrip]=secondary;
sty.Colors[ImGuiCol_ResizeGripHovered]=secondaryHover;
sty.Colors[ImGuiCol_ResizeGripActive]=secondaryActive;
sty.Colors[ImGuiCol_FrameBg]=secondary;
sty.Colors[ImGuiCol_FrameBgHovered]=secondaryHover;
sty.Colors[ImGuiCol_FrameBgActive]=secondaryActive;
sty.Colors[ImGuiCol_SliderGrab]=primaryActive;
sty.Colors[ImGuiCol_SliderGrabActive]=primaryActive;
sty.Colors[ImGuiCol_TitleBgActive]=primary;
sty.Colors[ImGuiCol_CheckMark]=primaryActive;
sty.Colors[ImGuiCol_TextSelectedBg]=secondaryHover;
sty.Colors[ImGuiCol_PlotHistogram]=uiColors[GUI_COLOR_MACRO_OTHER];
sty.Colors[ImGuiCol_PlotHistogramHovered]=uiColors[GUI_COLOR_MACRO_OTHER];
if (settings.roundedWindows) sty.WindowRounding=8.0f;
if (settings.roundedButtons) {
sty.FrameRounding=6.0f;
sty.GrabRounding=6.0f;
}
if (settings.roundedMenus) sty.PopupRounding=8.0f;
sty.ScaleAllSizes(dpiScale);
ImGui::GetStyle()=sty;
for (int i=0; i<256; i++) {
ImVec4& base=uiColors[GUI_COLOR_PATTERN_EFFECT_PITCH];
pitchGrad[i]=ImGui::GetColorU32(ImVec4(base.x,base.y,base.z,((float)i/255.0f)*base.w));
}
for (int i=0; i<256; i++) {
ImVec4& base=uiColors[GUI_COLOR_PATTERN_ACTIVE];
noteGrad[i]=ImGui::GetColorU32(ImVec4(base.x,base.y,base.z,((float)i/255.0f)*base.w));
}
for (int i=0; i<256; i++) {
ImVec4& base=uiColors[GUI_COLOR_PATTERN_EFFECT_PANNING];
panGrad[i]=ImGui::GetColorU32(ImVec4(base.x,base.y,base.z,((float)i/255.0f)*base.w));
}
for (int i=0; i<256; i++) {
ImVec4& base=uiColors[GUI_COLOR_PATTERN_INS];
insGrad[i]=ImGui::GetColorU32(ImVec4(base.x,base.y,base.z,((float)i/255.0f)*base.w));
}
for (int i=0; i<256; i++) {
ImVec4& base=volColors[i/2];
volGrad[i]=ImGui::GetColorU32(ImVec4(base.x,base.y,base.z,((float)i/255.0f)*base.w));
}
for (int i=0; i<256; i++) {
ImVec4& base=uiColors[GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY];
sysCmd1Grad[i]=ImGui::GetColorU32(ImVec4(base.x,base.y,base.z,((float)i/255.0f)*base.w));
}
for (int i=0; i<256; i++) {
ImVec4& base=uiColors[GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY];
sysCmd2Grad[i]=ImGui::GetColorU32(ImVec4(base.x,base.y,base.z,((float)i/255.0f)*base.w));
}
// set to 800 for now due to problems with unifont
static const ImWchar upTo800[]={0x20,0x7e,0xa0,0x800,0};
ImFontGlyphRangesBuilder range;
ImVector<ImWchar> outRange;
range.AddRanges(upTo800);
if (settings.loadJapanese) {
range.AddRanges(ImGui::GetIO().Fonts->GetGlyphRangesJapanese());
}
// I'm terribly sorry
range.UsedChars[0x80>>5]=0;
range.BuildRanges(&outRange);
if (fontRange!=NULL) delete[] fontRange;
fontRange=new ImWchar[outRange.size()];
int index=0;
for (ImWchar& i: outRange) {
fontRange[index++]=i;
}
if (settings.mainFont<0 || settings.mainFont>6) settings.mainFont=0;
if (settings.patFont<0 || settings.patFont>6) settings.patFont=0;
if (settings.mainFont==6 && settings.mainFontPath.empty()) {
logW("UI font path is empty! reverting to default font\n");
settings.mainFont=0;
}
if (settings.patFont==6 && settings.patFontPath.empty()) {
logW("pattern font path is empty! reverting to default font\n");
settings.patFont=0;
}
ImFontConfig fc1;
fc1.MergeMode=true;
if (settings.mainFont==6) { // custom font
if ((mainFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(settings.mainFontPath.c_str(),e->getConfInt("mainFontSize",18)*dpiScale,NULL,fontRange))==NULL) {
logW("could not load UI font! reverting to default font\n");
settings.mainFont=0;
if ((mainFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFont[settings.mainFont],builtinFontLen[settings.mainFont],e->getConfInt("mainFontSize",18)*dpiScale,NULL,fontRange))==NULL) {
logE("could not load UI font! falling back to Proggy Clean.\n");
mainFont=ImGui::GetIO().Fonts->AddFontDefault();
}
}
} else if (settings.mainFont==5) { // system font
if ((mainFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_FONT_PATH_1,e->getConfInt("mainFontSize",18)*dpiScale,NULL,fontRange))==NULL) {
if ((mainFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_FONT_PATH_2,e->getConfInt("mainFontSize",18)*dpiScale,NULL,fontRange))==NULL) {
if ((mainFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_FONT_PATH_3,e->getConfInt("mainFontSize",18)*dpiScale,NULL,fontRange))==NULL) {
logW("could not load UI font! reverting to default font\n");
settings.mainFont=0;
if ((mainFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFont[settings.mainFont],builtinFontLen[settings.mainFont],e->getConfInt("mainFontSize",18)*dpiScale,NULL,fontRange))==NULL) {
logE("could not load UI font! falling back to Proggy Clean.\n");
mainFont=ImGui::GetIO().Fonts->AddFontDefault();
}
}
}
}
} else {
if ((mainFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFont[settings.mainFont],builtinFontLen[settings.mainFont],e->getConfInt("mainFontSize",18)*dpiScale,NULL,fontRange))==NULL) {
logE("could not load UI font! falling back to Proggy Clean.\n");
mainFont=ImGui::GetIO().Fonts->AddFontDefault();
}
}
// two fallback fonts
mainFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(font_liberationSans_compressed_data,font_liberationSans_compressed_size,e->getConfInt("mainFontSize",18)*dpiScale,&fc1,fontRange);
mainFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(font_unifont_compressed_data,font_unifont_compressed_size,e->getConfInt("mainFontSize",18)*dpiScale,&fc1,fontRange);
ImFontConfig fc;
fc.MergeMode=true;
fc.GlyphMinAdvanceX=e->getConfInt("iconSize",16)*dpiScale;
static const ImWchar fontRangeIcon[]={ICON_MIN_FA,ICON_MAX_FA,0};
if ((iconFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(iconFont_compressed_data,iconFont_compressed_size,e->getConfInt("iconSize",16)*dpiScale,&fc,fontRangeIcon))==NULL) {
logE("could not load icon font!\n");
}
if (settings.mainFontSize==settings.patFontSize && settings.patFont<5 && builtinFontM[settings.patFont]==builtinFont[settings.mainFont]) {
logD("using main font for pat font.\n");
patFont=mainFont;
} else {
if (settings.patFont==6) { // custom font
if ((patFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(settings.patFontPath.c_str(),e->getConfInt("patFontSize",18)*dpiScale,NULL,upTo800))==NULL) {
logW("could not load pattern font! reverting to default font\n");
settings.patFont=0;
if ((patFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFontM[settings.patFont],builtinFontMLen[settings.patFont],e->getConfInt("patFontSize",18)*dpiScale,NULL,upTo800))==NULL) {
logE("could not load pattern font! falling back to Proggy Clean.\n");
patFont=ImGui::GetIO().Fonts->AddFontDefault();
}
}
} else if (settings.patFont==5) { // system font
if ((patFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_PAT_FONT_PATH_1,e->getConfInt("patFontSize",18)*dpiScale,NULL,upTo800))==NULL) {
if ((patFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_PAT_FONT_PATH_2,e->getConfInt("patFontSize",18)*dpiScale,NULL,upTo800))==NULL) {
if ((patFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_PAT_FONT_PATH_3,e->getConfInt("patFontSize",18)*dpiScale,NULL,upTo800))==NULL) {
logW("could not load pattern font! reverting to default font\n");
settings.patFont=0;
if ((patFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFontM[settings.patFont],builtinFontMLen[settings.patFont],e->getConfInt("patFontSize",18)*dpiScale,NULL,upTo800))==NULL) {
logE("could not load pattern font! falling back to Proggy Clean.\n");
patFont=ImGui::GetIO().Fonts->AddFontDefault();
}
}
}
}
} else {
if ((patFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFontM[settings.patFont],builtinFontMLen[settings.patFont],e->getConfInt("patFontSize",18)*dpiScale,NULL,upTo800))==NULL) {
logE("could not load pattern font!\n");
patFont=ImGui::GetIO().Fonts->AddFontDefault();
}
}
}
if ((bigFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(font_plexSans_compressed_data,font_plexSans_compressed_size,40*dpiScale))==NULL) {
logE("could not load big UI font!\n");
}
mainFont->FallbackChar='?';
mainFont->DotChar='.';
// TODO: allow changing these colors.
ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByTypeDir,"",ImVec4(0.0f,1.0f,1.0f,1.0f),ICON_FA_FOLDER_O);
ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByTypeFile,"",ImVec4(0.7f,0.7f,0.7f,1.0f),ICON_FA_FILE_O);
ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".fur",ImVec4(0.5f,1.0f,0.5f,1.0f),ICON_FA_FILE);
ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".fui",ImVec4(1.0f,0.5f,0.5f,1.0f),ICON_FA_FILE);
ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".fuw",ImVec4(1.0f,0.75f,0.5f,1.0f),ICON_FA_FILE);
ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".dmf",ImVec4(0.5f,1.0f,0.5f,1.0f),ICON_FA_FILE);
ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".dmp",ImVec4(1.0f,0.5f,0.5f,1.0f),ICON_FA_FILE);
ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".dmw",ImVec4(1.0f,0.75f,0.5f,1.0f),ICON_FA_FILE);
ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".wav",ImVec4(1.0f,1.0f,0.5f,1.0f),ICON_FA_FILE_AUDIO_O);
ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".vgm",ImVec4(1.0f,1.0f,0.5f,1.0f),ICON_FA_FILE_AUDIO_O);
ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".ttf",ImVec4(0.3f,1.0f,0.6f,1.0f),ICON_FA_FONT);
ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".otf",ImVec4(0.3f,1.0f,0.6f,1.0f),ICON_FA_FONT);
ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".ttc",ImVec4(0.3f,1.0f,0.6f,1.0f),ICON_FA_FONT);
ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".tfi",ImVec4(1.0f,0.5f,0.5f,1.0f),ICON_FA_FILE);
ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".vgi",ImVec4(1.0f,0.5f,0.5f,1.0f),ICON_FA_FILE);
ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".s3i",ImVec4(1.0f,0.5f,0.5f,1.0f),ICON_FA_FILE);
ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".sbi",ImVec4(1.0f,0.5f,0.5f,1.0f),ICON_FA_FILE);
ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".fti",ImVec4(1.0f,0.5f,0.5f,1.0f),ICON_FA_FILE);
ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".bti",ImVec4(1.0f,0.5f,0.5f,1.0f),ICON_FA_FILE);
if (fileDialog!=NULL) delete fileDialog;
fileDialog=new FurnaceGUIFileDialog(settings.sysFileDialog);
}

173
src/gui/songInfo.cpp Normal file
View File

@ -0,0 +1,173 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "gui.h"
#include "misc/cpp/imgui_stdlib.h"
#include "intConst.h"
void FurnaceGUI::drawSongInfo() {
if (nextWindow==GUI_WINDOW_SONG_INFO) {
songInfoOpen=true;
ImGui::SetNextWindowFocus();
nextWindow=GUI_WINDOW_NOTHING;
}
if (!songInfoOpen) return;
if (ImGui::Begin("Song Information",&songInfoOpen)) {
if (ImGui::BeginTable("NameAuthor",2,ImGuiTableFlags_SizingStretchProp)) {
ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed,0.0);
ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.0);
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("Name");
ImGui::TableNextColumn();
float avail=ImGui::GetContentRegionAvail().x;
ImGui::SetNextItemWidth(avail);
if (ImGui::InputText("##Name",&e->song.name)) { MARK_MODIFIED
updateWindowTitle();
}
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("Author");
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(avail);
if (ImGui::InputText("##Author",&e->song.author)) {
MARK_MODIFIED;
}
ImGui::EndTable();
}
if (ImGui::BeginTable("OtherProps",3,ImGuiTableFlags_SizingStretchProp)) {
ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed,0.0);
ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.0);
ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthStretch,0.0);
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("TimeBase");
ImGui::TableNextColumn();
float avail=ImGui::GetContentRegionAvail().x;
ImGui::SetNextItemWidth(avail);
unsigned char realTB=e->song.timeBase+1;
if (ImGui::InputScalar("##TimeBase",ImGuiDataType_U8,&realTB,&_ONE,&_THREE)) { MARK_MODIFIED
if (realTB<1) realTB=1;
if (realTB>16) realTB=16;
e->song.timeBase=realTB-1;
}
ImGui::TableNextColumn();
ImGui::Text("%.2f BPM",calcBPM(e->song.speed1,e->song.speed2,e->song.hz));
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("Speed");
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(avail);
if (ImGui::InputScalar("##Speed1",ImGuiDataType_U8,&e->song.speed1,&_ONE,&_THREE)) { MARK_MODIFIED
if (e->song.speed1<1) e->song.speed1=1;
if (e->isPlaying()) play();
}
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(avail);
if (ImGui::InputScalar("##Speed2",ImGuiDataType_U8,&e->song.speed2,&_ONE,&_THREE)) { MARK_MODIFIED
if (e->song.speed2<1) e->song.speed2=1;
if (e->isPlaying()) play();
}
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("Highlight");
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(avail);
if (ImGui::InputScalar("##Highlight1",ImGuiDataType_U8,&e->song.hilightA,&_ONE,&_THREE)) {
MARK_MODIFIED;
}
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(avail);
if (ImGui::InputScalar("##Highlight2",ImGuiDataType_U8,&e->song.hilightB,&_ONE,&_THREE)) {
MARK_MODIFIED;
}
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("Pattern Length");
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(avail);
int patLen=e->song.patLen;
if (ImGui::InputInt("##PatLength",&patLen,1,3)) { MARK_MODIFIED
if (patLen<1) patLen=1;
if (patLen>256) patLen=256;
e->song.patLen=patLen;
}
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("Song Length");
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(avail);
int ordLen=e->song.ordersLen;
if (ImGui::InputInt("##OrdLength",&ordLen,1,3)) { MARK_MODIFIED
if (ordLen<1) ordLen=1;
if (ordLen>127) ordLen=127;
e->song.ordersLen=ordLen;
}
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (ImGui::Selectable(tempoView?"Base Tempo##TempoOrHz":"Tick Rate##TempoOrHz")) {
tempoView=!tempoView;
}
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(avail);
float setHz=tempoView?e->song.hz*2.5:e->song.hz;
if (ImGui::InputFloat("##Rate",&setHz,1.0f,1.0f,"%g")) { MARK_MODIFIED
if (tempoView) setHz/=2.5;
if (setHz<10) setHz=10;
if (setHz>999) setHz=999;
e->setSongRate(setHz,setHz<52);
}
if (tempoView) {
ImGui::TableNextColumn();
ImGui::Text("= %gHz",e->song.hz);
} else {
if (e->song.hz>=49.98 && e->song.hz<=50.02) {
ImGui::TableNextColumn();
ImGui::Text("PAL");
}
if (e->song.hz>=59.9 && e->song.hz<=60.11) {
ImGui::TableNextColumn();
ImGui::Text("NTSC");
}
}
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("Tuning (A-4)");
ImGui::TableNextColumn();
float tune=e->song.tuning;
ImGui::SetNextItemWidth(avail);
if (ImGui::InputFloat("##Tuning",&tune,1.0f,3.0f,"%g")) { MARK_MODIFIED
if (tune<220.0f) tune=220.0f;
if (tune>880.0f) tune=880.0f;
e->song.tuning=tune;
}
ImGui::EndTable();
}
}
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_SONG_INFO;
ImGui::End();
}

37
src/gui/songNotes.cpp Normal file
View File

@ -0,0 +1,37 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "gui.h"
#include "misc/cpp/imgui_stdlib.h"
// NOTE: please don't ask me to enable text wrap.
// Dear ImGui doesn't have that feature. D:
void FurnaceGUI::drawNotes() {
if (nextWindow==GUI_WINDOW_NOTES) {
notesOpen=true;
ImGui::SetNextWindowFocus();
nextWindow=GUI_WINDOW_NOTHING;
}
if (!notesOpen) return;
if (ImGui::Begin("Song Comments",&notesOpen)) {
ImGui::InputTextMultiline("##SongNotes",&e->song.notes,ImGui::GetContentRegionAvail());
}
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_NOTES;
ImGui::End();
}

50
src/gui/stats.cpp Normal file
View File

@ -0,0 +1,50 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "gui.h"
#include <fmt/printf.h>
void FurnaceGUI::drawStats() {
if (nextWindow==GUI_WINDOW_STATS) {
statsOpen=true;
ImGui::SetNextWindowFocus();
nextWindow=GUI_WINDOW_NOTHING;
}
if (!statsOpen) return;
if (ImGui::Begin("Statistics",&statsOpen)) {
String adpcmAUsage=fmt::sprintf("%d/16384KB",e->adpcmAMemLen/1024);
String adpcmBUsage=fmt::sprintf("%d/16384KB",e->adpcmBMemLen/1024);
String qsoundUsage=fmt::sprintf("%d/16384KB",e->qsoundMemLen/1024);
String x1_010Usage=fmt::sprintf("%d/1024KB",e->x1_010MemLen/1024);
ImGui::Text("ADPCM-A");
ImGui::SameLine();
ImGui::ProgressBar(((float)e->adpcmAMemLen)/16777216.0f,ImVec2(-FLT_MIN,0),adpcmAUsage.c_str());
ImGui::Text("ADPCM-B");
ImGui::SameLine();
ImGui::ProgressBar(((float)e->adpcmBMemLen)/16777216.0f,ImVec2(-FLT_MIN,0),adpcmBUsage.c_str());
ImGui::Text("QSound");
ImGui::SameLine();
ImGui::ProgressBar(((float)e->qsoundMemLen)/16777216.0f,ImVec2(-FLT_MIN,0),qsoundUsage.c_str());
ImGui::Text("X1-010");
ImGui::SameLine();
ImGui::ProgressBar(((float)e->x1_010MemLen)/1048576.0f,ImVec2(-FLT_MIN,0),x1_010Usage.c_str());
}
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_STATS;
ImGui::End();
}

395
src/gui/sysConf.cpp Normal file
View File

@ -0,0 +1,395 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "gui.h"
void FurnaceGUI::drawSysConf(int i) {
unsigned int flags=e->song.systemFlags[i];
bool restart=settings.restartOnFlagChange;
bool sysPal=flags&1;
switch (e->song.system[i]) {
case DIV_SYSTEM_YM2612:
case DIV_SYSTEM_YM2612_EXT: {
if (ImGui::RadioButton("NTSC (7.67MHz)",(flags&3)==0)) {
e->setSysFlags(i,(flags&0x80000000)|0,restart);
updateWindowTitle();
}
if (ImGui::RadioButton("PAL (7.61MHz)",(flags&3)==1)) {
e->setSysFlags(i,(flags&0x80000000)|1,restart);
updateWindowTitle();
}
if (ImGui::RadioButton("FM Towns (8MHz)",(flags&3)==2)) {
e->setSysFlags(i,(flags&0x80000000)|2,restart);
updateWindowTitle();
}
if (ImGui::RadioButton("AtGames Genesis (6.13MHz)",(flags&3)==3)) {
e->setSysFlags(i,(flags&0x80000000)|3,restart);
updateWindowTitle();
}
bool ladder=flags&0x80000000;
if (ImGui::Checkbox("Enable DAC distortion",&ladder)) {
e->setSysFlags(i,(flags&(~0x80000000))|(ladder?0x80000000:0),restart);
updateWindowTitle();
}
break;
}
case DIV_SYSTEM_SMS: {
ImGui::Text("Clock rate:");
if (ImGui::RadioButton("NTSC (3.58MHz)",(flags&3)==0)) {
e->setSysFlags(i,(flags&(~3))|0,restart);
updateWindowTitle();
}
if (ImGui::RadioButton("PAL (3.55MHz)",(flags&3)==1)) {
e->setSysFlags(i,(flags&(~3))|1,restart);
updateWindowTitle();
}
if (ImGui::RadioButton("BBC Micro (4MHz)",(flags&3)==2)) {
e->setSysFlags(i,(flags&(~3))|2,restart);
updateWindowTitle();
}
if (ImGui::RadioButton("Half NTSC (1.79MHz)",(flags&3)==3)) {
e->setSysFlags(i,(flags&(~3))|3,restart);
updateWindowTitle();
}
ImGui::Text("Chip type:");
if (ImGui::RadioButton("Sega VDP/Master System",((flags>>2)&3)==0)) {
e->setSysFlags(i,(flags&(~12))|0,restart);
updateWindowTitle();
}
if (ImGui::RadioButton("TI SN76489",((flags>>2)&3)==1)) {
e->setSysFlags(i,(flags&(~12))|4,restart);
updateWindowTitle();
}
if (ImGui::RadioButton("TI SN76489 with Atari-like short noise",((flags>>2)&3)==2)) {
e->setSysFlags(i,(flags&(~12))|8,restart);
updateWindowTitle();
}
/*if (ImGui::RadioButton("Game Gear",(flags>>2)==3)) {
e->setSysFlags(i,(flags&3)|12);
}*/
bool noPhaseReset=flags&16;
if (ImGui::Checkbox("Disable noise period change phase reset",&noPhaseReset)) {
e->setSysFlags(i,(flags&(~16))|(noPhaseReset<<4),restart);
updateWindowTitle();
}
break;
}
case DIV_SYSTEM_OPLL:
case DIV_SYSTEM_OPLL_DRUMS:
case DIV_SYSTEM_VRC7: {
ImGui::Text("Clock rate:");
if (ImGui::RadioButton("NTSC (3.58MHz)",(flags&15)==0)) {
e->setSysFlags(i,(flags&(~15))|0,restart);
updateWindowTitle();
}
if (ImGui::RadioButton("PAL (3.55MHz)",(flags&15)==1)) {
e->setSysFlags(i,(flags&(~15))|1,restart);
updateWindowTitle();
}
if (ImGui::RadioButton("BBC Micro (4MHz)",(flags&15)==2)) {
e->setSysFlags(i,(flags&(~15))|2,restart);
updateWindowTitle();
}
if (ImGui::RadioButton("Half NTSC (1.79MHz)",(flags&15)==3)) {
e->setSysFlags(i,(flags&(~15))|3,restart);
updateWindowTitle();
}
if (e->song.system[i]!=DIV_SYSTEM_VRC7) {
ImGui::Text("Patch set:");
if (ImGui::RadioButton("Yamaha YM2413",((flags>>4)&15)==0)) {
e->setSysFlags(i,(flags&(~0xf0))|0,restart);
updateWindowTitle();
}
if (ImGui::RadioButton("Yamaha YMF281",((flags>>4)&15)==1)) {
e->setSysFlags(i,(flags&(~0xf0))|0x10,restart);
updateWindowTitle();
}
if (ImGui::RadioButton("Yamaha YM2423",((flags>>4)&15)==2)) {
e->setSysFlags(i,(flags&(~0xf0))|0x20,restart);
updateWindowTitle();
}
if (ImGui::RadioButton("Konami VRC7",((flags>>4)&15)==3)) {
e->setSysFlags(i,(flags&(~0xf0))|0x30,restart);
updateWindowTitle();
}
}
break;
}
case DIV_SYSTEM_YM2151:
if (ImGui::RadioButton("NTSC/X16 (3.58MHz)",flags==0)) {
e->setSysFlags(i,0,restart);
updateWindowTitle();
}
if (ImGui::RadioButton("PAL (3.55MHz)",flags==1)) {
e->setSysFlags(i,1,restart);
updateWindowTitle();
}
if (ImGui::RadioButton("X1/X68000 (4MHz)",flags==2)) {
e->setSysFlags(i,2,restart);
updateWindowTitle();
}
break;
case DIV_SYSTEM_NES:
if (ImGui::RadioButton("NTSC (1.79MHz)",flags==0)) {
e->setSysFlags(i,0,restart);
updateWindowTitle();
}
if (ImGui::RadioButton("PAL (1.67MHz)",flags==1)) {
e->setSysFlags(i,1,restart);
updateWindowTitle();
}
if (ImGui::RadioButton("Dendy (1.77MHz)",flags==2)) {
e->setSysFlags(i,2,restart);
updateWindowTitle();
}
break;
case DIV_SYSTEM_C64_8580:
case DIV_SYSTEM_C64_6581:
if (ImGui::RadioButton("NTSC (1.02MHz)",flags==0)) {
e->setSysFlags(i,0,restart);
updateWindowTitle();
}
if (ImGui::RadioButton("PAL (0.99MHz)",flags==1)) {
e->setSysFlags(i,1,restart);
updateWindowTitle();
}
if (ImGui::RadioButton("SSI 2001 (0.89MHz)",flags==2)) {
e->setSysFlags(i,2,restart);
updateWindowTitle();
}
break;
case DIV_SYSTEM_AY8910:
case DIV_SYSTEM_AY8930: {
ImGui::Text("Clock rate:");
if (ImGui::RadioButton("1.79MHz (ZX Spectrum NTSC/MSX)",(flags&15)==0)) {
e->setSysFlags(i,(flags&(~15))|0,restart);
updateWindowTitle();
}
if (ImGui::RadioButton("1.77MHz (ZX Spectrum)",(flags&15)==1)) {
e->setSysFlags(i,(flags&(~15))|1,restart);
updateWindowTitle();
}
if (ImGui::RadioButton("1.75MHz (ZX Spectrum)",(flags&15)==2)) {
e->setSysFlags(i,(flags&(~15))|2,restart);
updateWindowTitle();
}
if (ImGui::RadioButton("2MHz (Atari ST/Sharp X1)",(flags&15)==3)) {
e->setSysFlags(i,(flags&(~15))|3,restart);
updateWindowTitle();
}
if (ImGui::RadioButton("1.5MHz (Vectrex)",(flags&15)==4)) {
e->setSysFlags(i,(flags&(~15))|4,restart);
updateWindowTitle();
}
if (ImGui::RadioButton("1MHz (Amstrad CPC)",(flags&15)==5)) {
e->setSysFlags(i,(flags&(~15))|5,restart);
updateWindowTitle();
}
if (ImGui::RadioButton("0.89MHz (Sunsoft 5B)",(flags&15)==6)) {
e->setSysFlags(i,(flags&(~15))|6,restart);
updateWindowTitle();
}
if (ImGui::RadioButton("1.67MHz (?)",(flags&15)==7)) {
e->setSysFlags(i,(flags&(~15))|7,restart);
updateWindowTitle();
}
if (ImGui::RadioButton("0.83MHz (Sunsoft 5B on PAL)",(flags&15)==8)) {
e->setSysFlags(i,(flags&(~15))|8,restart);
updateWindowTitle();
}
if (ImGui::RadioButton("1.10MHz (Gamate/VIC-20 PAL)",(flags&15)==9)) {
e->setSysFlags(i,(flags&(~15))|9,restart);
updateWindowTitle();
}
if (ImGui::RadioButton("2^21Hz (Game Boy)",(flags&15)==10)) {
e->setSysFlags(i,(flags&(~15))|10,restart);
updateWindowTitle();
}
if (e->song.system[i]==DIV_SYSTEM_AY8910) {
ImGui::Text("Chip type:");
if (ImGui::RadioButton("AY-3-8910",(flags&0x30)==0)) {
e->setSysFlags(i,(flags&(~0x30))|0,restart);
updateWindowTitle();
}
if (ImGui::RadioButton("YM2149(F)",(flags&0x30)==16)) {
e->setSysFlags(i,(flags&(~0x30))|16,restart);
updateWindowTitle();
}
if (ImGui::RadioButton("Sunsoft 5B",(flags&0x30)==32)) {
e->setSysFlags(i,(flags&(~0x30))|32,restart);
updateWindowTitle();
}
if (ImGui::RadioButton("AY-3-8914",(flags&0x30)==48)) {
e->setSysFlags(i,(flags&(~0x30))|48,restart);
updateWindowTitle();
}
}
bool stereo=flags&0x40;
ImGui::BeginDisabled((flags&0x30)==32);
if (ImGui::Checkbox("Stereo##_AY_STEREO",&stereo)) {
e->setSysFlags(i,(flags&(~0x40))|(stereo?0x40:0),restart);
updateWindowTitle();
}
ImGui::EndDisabled();
break;
}
case DIV_SYSTEM_SAA1099:
if (ImGui::RadioButton("SAM Coupé (8MHz)",flags==0)) {
e->setSysFlags(i,0,restart);
updateWindowTitle();
}
if (ImGui::RadioButton("NTSC (7.15MHz)",flags==1)) {
e->setSysFlags(i,1,restart);
updateWindowTitle();
}
if (ImGui::RadioButton("PAL (7.09MHz)",flags==2)) {
e->setSysFlags(i,2,restart);
updateWindowTitle();
}
break;
case DIV_SYSTEM_AMIGA: {
ImGui::Text("Stereo separation:");
int stereoSep=(flags>>8)&127;
if (ImGui::SliderInt("##StereoSep",&stereoSep,0,127)) {
if (stereoSep<0) stereoSep=0;
if (stereoSep>127) stereoSep=127;
e->setSysFlags(i,(flags&(~0x7f00))|((stereoSep&127)<<8),restart);
updateWindowTitle();
} rightClickable
if (ImGui::RadioButton("Amiga 500 (OCS)",(flags&2)==0)) {
e->setSysFlags(i,flags&(~2),restart);
}
if (ImGui::RadioButton("Amiga 1200 (AGA)",(flags&2)==2)) {
e->setSysFlags(i,(flags&(~2))|2,restart);
}
sysPal=flags&1;
if (ImGui::Checkbox("PAL",&sysPal)) {
e->setSysFlags(i,(flags&(~1))|sysPal,restart);
updateWindowTitle();
}
bool bypassLimits=flags&4;
if (ImGui::Checkbox("Bypass frequency limits",&bypassLimits)) {
e->setSysFlags(i,(flags&(~4))|(bypassLimits<<2),restart);
updateWindowTitle();
}
break;
}
case DIV_SYSTEM_PCSPKR: {
ImGui::Text("Speaker type:");
if (ImGui::RadioButton("Unfiltered",(flags&3)==0)) {
e->setSysFlags(i,(flags&(~3))|0,restart);
updateWindowTitle();
}
if (ImGui::RadioButton("Cone",(flags&3)==1)) {
e->setSysFlags(i,(flags&(~3))|1,restart);
updateWindowTitle();
}
if (ImGui::RadioButton("Piezo",(flags&3)==2)) {
e->setSysFlags(i,(flags&(~3))|2,restart);
updateWindowTitle();
}
if (ImGui::RadioButton("Use system beeper (Linux only!)",(flags&3)==3)) {
e->setSysFlags(i,(flags&(~3))|3,restart);
updateWindowTitle();
}
break;
}
case DIV_SYSTEM_QSOUND: {
ImGui::Text("Echo delay:");
int echoBufSize=2725 - (flags & 4095);
if (ImGui::SliderInt("##EchoBufSize",&echoBufSize,0,2725)) {
if (echoBufSize<0) echoBufSize=0;
if (echoBufSize>2725) echoBufSize=2725;
e->setSysFlags(i,(flags & ~4095) | ((2725 - echoBufSize) & 4095),restart);
updateWindowTitle();
} rightClickable
ImGui::Text("Echo feedback:");
int echoFeedback=(flags>>12)&255;
if (ImGui::SliderInt("##EchoFeedback",&echoFeedback,0,255)) {
if (echoFeedback<0) echoFeedback=0;
if (echoFeedback>255) echoFeedback=255;
e->setSysFlags(i,(flags & ~(255 << 12)) | ((echoFeedback & 255) << 12),restart);
updateWindowTitle();
} rightClickable
break;
}
case DIV_SYSTEM_X1_010: {
ImGui::Text("Clock rate:");
if (ImGui::RadioButton("16MHz (Seta 1)",(flags&15)==0)) {
e->setSysFlags(i,(flags&(~15))|0,restart);
updateWindowTitle();
}
if (ImGui::RadioButton("16.67MHz (Seta 2)",(flags&15)==1)) {
e->setSysFlags(i,(flags&(~15))|1,restart);
updateWindowTitle();
}
bool x1_010Stereo=flags&16;
if (ImGui::Checkbox("Stereo",&x1_010Stereo)) {
e->setSysFlags(i,(flags&(~16))|(x1_010Stereo<<4),restart);
updateWindowTitle();
}
break;
}
case DIV_SYSTEM_N163: {
ImGui::Text("Clock rate:");
if (ImGui::RadioButton("NTSC (1.79MHz)",(flags&15)==0)) {
e->setSysFlags(i,(flags&(~15))|0,restart);
updateWindowTitle();
}
if (ImGui::RadioButton("PAL (1.67MHz)",(flags&15)==1)) {
e->setSysFlags(i,(flags&(~15))|1,restart);
updateWindowTitle();
}
if (ImGui::RadioButton("Dendy (1.77MHz)",(flags&15)==2)) {
e->setSysFlags(i,(flags&(~15))|2,restart);
updateWindowTitle();
}
ImGui::Text("Initial channel limit:");
int initialChannelLimit=((flags>>4)&7)+1;
if (ImGui::SliderInt("##InitialChannelLimit",&initialChannelLimit,1,8)) {
if (initialChannelLimit<1) initialChannelLimit=1;
if (initialChannelLimit>8) initialChannelLimit=8;
e->setSysFlags(i,(flags & ~(7 << 4)) | (((initialChannelLimit-1) & 7) << 4),restart);
updateWindowTitle();
} rightClickable
break;
}
case DIV_SYSTEM_GB:
case DIV_SYSTEM_SWAN:
case DIV_SYSTEM_VERA:
case DIV_SYSTEM_BUBSYS_WSG:
case DIV_SYSTEM_YM2610:
case DIV_SYSTEM_YM2610_EXT:
case DIV_SYSTEM_YM2610_FULL:
case DIV_SYSTEM_YM2610_FULL_EXT:
case DIV_SYSTEM_YM2610B:
case DIV_SYSTEM_YM2610B_EXT:
case DIV_SYSTEM_YMU759:
case DIV_SYSTEM_PET:
ImGui::Text("nothing to configure");
break;
default:
if (ImGui::Checkbox("PAL",&sysPal)) {
e->setSysFlags(i,sysPal,restart);
updateWindowTitle();
}
break;
}
}

111
src/gui/volMeter.cpp Normal file
View File

@ -0,0 +1,111 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "gui.h"
#include "imgui_internal.h"
void FurnaceGUI::drawVolMeter() {
if (nextWindow==GUI_WINDOW_VOL_METER) {
volMeterOpen=true;
ImGui::SetNextWindowFocus();
nextWindow=GUI_WINDOW_NOTHING;
}
if (!volMeterOpen) return;
if (--isClipping<0) isClipping=0;
ImGui::SetNextWindowSizeConstraints(ImVec2(6.0f*dpiScale,6.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale));
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding,ImVec2(0,0));
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding,ImVec2(0,0));
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing,ImVec2(0,0));
ImGui::PushStyleVar(ImGuiStyleVar_ItemInnerSpacing,ImVec2(0,0));
if (ImGui::Begin("Volume Meter",&volMeterOpen)) {
ImDrawList* dl=ImGui::GetWindowDrawList();
bool aspectRatio=(ImGui::GetWindowSize().x/ImGui::GetWindowSize().y)>1.0;
ImVec2 minArea=ImVec2(
ImGui::GetWindowPos().x+ImGui::GetCursorPos().x,
ImGui::GetWindowPos().y+ImGui::GetCursorPos().y
);
ImVec2 maxArea=ImVec2(
ImGui::GetWindowPos().x+ImGui::GetCursorPos().x+ImGui::GetContentRegionAvail().x,
ImGui::GetWindowPos().y+ImGui::GetCursorPos().y+ImGui::GetContentRegionAvail().y
);
ImRect rect=ImRect(minArea,maxArea);
ImGuiStyle& style=ImGui::GetStyle();
ImGui::ItemSize(ImVec2(4.0f,4.0f),style.FramePadding.y);
ImU32 lowColor=ImGui::GetColorU32(uiColors[GUI_COLOR_VOLMETER_LOW]);
float peakDecay=0.05f*60.0f*ImGui::GetIO().DeltaTime;
if (ImGui::ItemAdd(rect,ImGui::GetID("volMeter"))) {
ImGui::RenderFrame(rect.Min,rect.Max,ImGui::GetColorU32(ImGuiCol_FrameBg),true,style.FrameRounding);
for (int i=0; i<2; i++) {
peak[i]*=1.0-peakDecay;
if (peak[i]<0.0001) peak[i]=0.0;
for (int j=0; j<e->oscSize; j++) {
if (fabs(e->oscBuf[i][j])>peak[i]) {
peak[i]=fabs(e->oscBuf[i][j]);
}
}
float logPeak=(20*log10(peak[i])/36.0);
if (logPeak==NAN) logPeak=0.0;
if (logPeak<-1.0) logPeak=-1.0;
if (logPeak>0.0) {
isClipping=8;
logPeak=0.0;
}
logPeak+=1.0;
ImU32 highColor=ImGui::GetColorU32(
ImLerp(uiColors[GUI_COLOR_VOLMETER_LOW],uiColors[GUI_COLOR_VOLMETER_HIGH],logPeak)
);
ImRect s;
if (aspectRatio) {
s=ImRect(
ImLerp(rect.Min,rect.Max,ImVec2(0,float(i)*0.5)),
ImLerp(rect.Min,rect.Max,ImVec2(logPeak,float(i+1)*0.5))
);
if (i==0) s.Max.y-=dpiScale;
if (isClipping) {
dl->AddRectFilled(s.Min,s.Max,ImGui::GetColorU32(uiColors[GUI_COLOR_VOLMETER_PEAK]));
} else {
dl->AddRectFilledMultiColor(s.Min,s.Max,lowColor,highColor,highColor,lowColor);
}
} else {
s=ImRect(
ImLerp(rect.Min,rect.Max,ImVec2(float(i)*0.5,1.0-logPeak)),
ImLerp(rect.Min,rect.Max,ImVec2(float(i+1)*0.5,1.0))
);
if (i==0) s.Max.x-=dpiScale;
if (isClipping) {
dl->AddRectFilled(s.Min,s.Max,ImGui::GetColorU32(uiColors[GUI_COLOR_VOLMETER_PEAK]));
} else {
dl->AddRectFilledMultiColor(s.Min,s.Max,highColor,highColor,lowColor,lowColor);
}
}
}
if (ImGui::IsItemHovered()) {
if (aspectRatio) {
ImGui::SetTooltip("%.1fdB",36*((ImGui::GetMousePos().x-ImGui::GetItemRectMin().x)/(rect.Max.x-rect.Min.x)-1.0));
} else {
ImGui::SetTooltip("%.1fdB",-(36+36*((ImGui::GetMousePos().y-ImGui::GetItemRectMin().y)/(rect.Max.y-rect.Min.y)-1.0)));
}
}
}
}
ImGui::PopStyleVar(4);
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_VOL_METER;
ImGui::End();
}

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

@ -0,0 +1,101 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "gui.h"
#include "plot_nolerp.h"
#include "misc/cpp/imgui_stdlib.h"
void FurnaceGUI::drawWaveEdit() {
if (nextWindow==GUI_WINDOW_WAVE_EDIT) {
waveEditOpen=true;
ImGui::SetNextWindowFocus();
nextWindow=GUI_WINDOW_NOTHING;
}
if (!waveEditOpen) return;
float wavePreview[256];
ImGui::SetNextWindowSizeConstraints(ImVec2(450.0f*dpiScale,300.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale));
if (ImGui::Begin("Wavetable Editor",&waveEditOpen,settings.allowEditDocking?0:ImGuiWindowFlags_NoDocking)) {
if (curWave<0 || curWave>=(int)e->song.wave.size()) {
ImGui::Text("no wavetable selected");
} else {
DivWavetable* wave=e->song.wave[curWave];
ImGui::Text("Width");
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("use a width of:\n- any on Amiga/N163\n- 32 on Game Boy, PC Engine and WonderSwan\n- 128 on X1-010\nany other widths will be scaled during playback.");
}
ImGui::SameLine();
ImGui::SetNextItemWidth(128.0f*dpiScale);
if (ImGui::InputInt("##_WTW",&wave->len,1,2)) {
if (wave->len>256) wave->len=256;
if (wave->len<1) wave->len=1;
e->notifyWaveChange(curWave);
if (wavePreviewOn) e->previewWave(curWave,wavePreviewNote);
MARK_MODIFIED;
}
ImGui::SameLine();
ImGui::Text("Height");
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("use a height of:\n- 15 for Game Boy, WonderSwan, X1-010 Envelope shape and N163\n- 31 for PC Engine\n- 255 for X1-010\nany other heights will be scaled during playback.");
}
ImGui::SameLine();
ImGui::SetNextItemWidth(128.0f*dpiScale);
if (ImGui::InputInt("##_WTH",&wave->max,1,2)) {
if (wave->max>255) wave->max=255;
if (wave->max<1) wave->max=1;
e->notifyWaveChange(curWave);
MARK_MODIFIED;
}
for (int i=0; i<wave->len; i++) {
if (wave->data[i]>wave->max) wave->data[i]=wave->max;
wavePreview[i]=wave->data[i];
}
if (wave->len>0) wavePreview[wave->len]=wave->data[wave->len-1];
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); // wavetable text input size found here
if (ImGui::InputText("##MMLWave",&mmlStringW)) {
decodeMMLStrW(mmlStringW,wave->data,wave->len,wave->max);
}
if (!ImGui::IsItemActive()) {
encodeMMLStr(mmlStringW,wave->data,wave->len,-1,-1);
}
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding,ImVec2(0.0f,0.0f));
ImVec2 contentRegion=ImGui::GetContentRegionAvail(); // wavetable graph size determined here
if (ImGui::GetContentRegionAvail().y > (ImGui::GetContentRegionAvail().x / 2.0f)) {
contentRegion=ImVec2(ImGui::GetContentRegionAvail().x,ImGui::GetContentRegionAvail().x / 2.0f);
}
PlotNoLerp("##Waveform",wavePreview,wave->len+1,0,NULL,0,wave->max,contentRegion);
if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) {
waveDragStart=ImGui::GetItemRectMin();
waveDragAreaSize=contentRegion;
waveDragMin=0;
waveDragMax=wave->max;
waveDragLen=wave->len;
waveDragActive=true;
waveDragTarget=wave->data;
processDrags(ImGui::GetMousePos().x,ImGui::GetMousePos().y);
e->notifyWaveChange(curWave);
modified=true;
}
ImGui::PopStyleVar();
}
}
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_WAVE_EDIT;
ImGui::End();
}