Merge branch 'master' of https://github.com/tildearrow/furnace into es5506_alt
This commit is contained in:
commit
7a803f1e73
|
@ -33,7 +33,7 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3.1.0
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
|
@ -59,9 +59,9 @@ jobs:
|
|||
echo "MinGW cross target: ${mingw_target}"
|
||||
fi
|
||||
|
||||
echo "::set-output name=vswhere-target::${vswhere_target}"
|
||||
echo "::set-output name=msvc-target::${msvc_target}"
|
||||
echo "::set-output name=mingw-target::${mingw_target}"
|
||||
echo "vswhere-target=${vswhere_target}" >> $GITHUB_OUTPUT
|
||||
echo "msvc-target=${msvc_target}" >> $GITHUB_OUTPUT
|
||||
echo "mingw-target=${mingw_target}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Set package identifier
|
||||
id: package-identify
|
||||
|
@ -88,8 +88,8 @@ jobs:
|
|||
echo "Package identifier: ${package_name}"
|
||||
echo "Package file: ${package_name}${package_ext}"
|
||||
|
||||
echo "::set-output name=id::${package_name}"
|
||||
echo "::set-output name=filename::${package_name}${package_ext}"
|
||||
echo "id=${package_name}" >> $GITHUB_OUTPUT
|
||||
echo "filename=${package_name}${package_ext}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Set build cores amount
|
||||
id: build-cores
|
||||
|
@ -102,11 +102,11 @@ jobs:
|
|||
|
||||
echo "Amount of cores we can build with: ${amount}"
|
||||
|
||||
echo "::set-output name=amount::${amount}"
|
||||
echo "amount=${amount}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Setup Toolchain [Windows MSVC]
|
||||
if: ${{ matrix.config.compiler == 'msvc' }}
|
||||
uses: seanmiddleditch/gha-setup-vsdevenv@v3
|
||||
uses: vadz/gha-setup-vsdevenv@avoid-deprecation-warnings
|
||||
with:
|
||||
arch: ${{ steps.windows-identify.outputs.vswhere-target }}
|
||||
|
||||
|
@ -314,7 +314,7 @@ jobs:
|
|||
|
||||
- name: Upload artifact
|
||||
if: ${{ github.repository == 'tildearrow/furnace' && github.ref_name == 'master' }}
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v3.1.1
|
||||
with:
|
||||
name: ${{ steps.package-identify.outputs.id }}
|
||||
path: ${{ steps.package-identify.outputs.filename }}
|
||||
|
|
|
@ -85,9 +85,6 @@ check out the [Releases](https://github.com/tildearrow/furnace/releases) page. a
|
|||
- available on wavetable chips
|
||||
- create complex sounds with ease - provide up to two wavetables, select and effect and let go!
|
||||
- MIDI input support
|
||||
- [Fractal Sound](https://gitlab.com/Natsumi/Fractal-Sound) support!
|
||||
- the game-ready Sega Genesis/Mega Drive sound driver!
|
||||
- compose your songs in Furnace using the Fractal Sound presets and then use them in your games with Fractal!
|
||||
- additional features:
|
||||
- FM macros!
|
||||
- negative octaves
|
||||
|
|
|
@ -1,7 +1,25 @@
|
|||
package org.tildearrow.furnace;
|
||||
|
||||
import android.content.Intent;
|
||||
|
||||
import org.libsdl.app.SDLActivity;
|
||||
|
||||
public class MainActivity extends SDLActivity
|
||||
{
|
||||
public class MainActivity extends SDLActivity {
|
||||
static final int TA_FILE_REQUEST=1000;
|
||||
|
||||
public boolean showFileDialog() {
|
||||
Intent picker=new Intent(Intent.ACTION_GET_CONTENT);
|
||||
picker.setType("*/*");
|
||||
picker=Intent.createChooser(picker,"test");
|
||||
startActivityForResult(picker,TA_FILE_REQUEST);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override protected void onActivityResult(int request, int result, Intent intent) {
|
||||
super.onActivityResult(request,result,intent);
|
||||
if (request==TA_FILE_REQUEST) {
|
||||
// TODO: fire an event here
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -28,6 +28,8 @@ in most arcade boards the chip was used in combination with a PCM chip, like [Se
|
|||
- `1Bxx`: set attack of operator 2.
|
||||
- `1Cxx`: set attack of operator 3.
|
||||
- `1Dxx`: set attack of operator 4.
|
||||
- `1Exx`: set LFO AM depth.
|
||||
- `1Fxx`: set LFO PM depth.
|
||||
- `30xx`: enable envelope hard reset.
|
||||
- this works by inserting a quick release and tiny delay before a new note.
|
||||
- `50xy`: set AM of operator.
|
||||
|
|
|
@ -2438,6 +2438,29 @@ int DivEngine::getEffectiveSampleRate(int rate) {
|
|||
|
||||
void DivEngine::previewSample(int sample, int note, int pStart, int pEnd) {
|
||||
BUSY_BEGIN;
|
||||
previewSampleNoLock(sample,note,pStart,pEnd);
|
||||
BUSY_END;
|
||||
}
|
||||
|
||||
void DivEngine::stopSamplePreview() {
|
||||
BUSY_BEGIN;
|
||||
stopSamplePreviewNoLock();
|
||||
BUSY_END;
|
||||
}
|
||||
|
||||
void DivEngine::previewWave(int wave, int note) {
|
||||
BUSY_BEGIN;
|
||||
previewWaveNoLock(wave,note);
|
||||
BUSY_END;
|
||||
}
|
||||
|
||||
void DivEngine::stopWavePreview() {
|
||||
BUSY_BEGIN;
|
||||
stopWavePreviewNoLock();
|
||||
BUSY_END;
|
||||
}
|
||||
|
||||
void DivEngine::previewSampleNoLock(int sample, int note, int pStart, int pEnd) {
|
||||
sPreview.pBegin=pStart;
|
||||
sPreview.pEnd=pEnd;
|
||||
sPreview.dir=false;
|
||||
|
@ -2445,7 +2468,6 @@ void DivEngine::previewSample(int sample, int note, int pStart, int pEnd) {
|
|||
sPreview.sample=-1;
|
||||
sPreview.pos=0;
|
||||
sPreview.dir=false;
|
||||
BUSY_END;
|
||||
return;
|
||||
}
|
||||
blip_clear(samp_bb);
|
||||
|
@ -2462,28 +2484,22 @@ void DivEngine::previewSample(int sample, int note, int pStart, int pEnd) {
|
|||
sPreview.sample=sample;
|
||||
sPreview.wave=-1;
|
||||
sPreview.dir=false;
|
||||
BUSY_END;
|
||||
}
|
||||
|
||||
void DivEngine::stopSamplePreview() {
|
||||
BUSY_BEGIN;
|
||||
void DivEngine::stopSamplePreviewNoLock() {
|
||||
sPreview.sample=-1;
|
||||
sPreview.pos=0;
|
||||
sPreview.dir=false;
|
||||
BUSY_END;
|
||||
}
|
||||
|
||||
void DivEngine::previewWave(int wave, int note) {
|
||||
BUSY_BEGIN;
|
||||
void DivEngine::previewWaveNoLock(int wave, int note) {
|
||||
if (wave<0 || wave>=(int)song.wave.size()) {
|
||||
sPreview.wave=-1;
|
||||
sPreview.pos=0;
|
||||
sPreview.dir=false;
|
||||
BUSY_END;
|
||||
return;
|
||||
}
|
||||
if (song.wave[wave]->len<=0) {
|
||||
BUSY_END;
|
||||
return;
|
||||
}
|
||||
blip_clear(samp_bb);
|
||||
|
@ -2496,15 +2512,12 @@ void DivEngine::previewWave(int wave, int note) {
|
|||
sPreview.sample=-1;
|
||||
sPreview.wave=wave;
|
||||
sPreview.dir=false;
|
||||
BUSY_END;
|
||||
}
|
||||
|
||||
void DivEngine::stopWavePreview() {
|
||||
BUSY_BEGIN;
|
||||
void DivEngine::stopWavePreviewNoLock() {
|
||||
sPreview.wave=-1;
|
||||
sPreview.pos=0;
|
||||
sPreview.dir=false;
|
||||
BUSY_END;
|
||||
}
|
||||
|
||||
bool DivEngine::isPreviewingSample() {
|
||||
|
|
|
@ -616,6 +616,14 @@ class DivEngine {
|
|||
void previewWave(int wave, int note);
|
||||
void stopWavePreview();
|
||||
|
||||
// trigger sample preview
|
||||
void previewSampleNoLock(int sample, int note=-1, int pStart=-1, int pEnd=-1);
|
||||
void stopSamplePreviewNoLock();
|
||||
|
||||
// trigger wave preview
|
||||
void previewWaveNoLock(int wave, int note);
|
||||
void stopWavePreviewNoLock();
|
||||
|
||||
// get config path
|
||||
String getConfigPath();
|
||||
|
||||
|
|
|
@ -1412,7 +1412,7 @@ void DivEngine::convertOldFlags(unsigned int oldFlags, DivConfig& newFlags, DivS
|
|||
newFlags.set("chipType",0);
|
||||
break;
|
||||
case 1:
|
||||
newFlags.set("chipType",0);
|
||||
newFlags.set("chipType",1);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -60,7 +60,7 @@ bool DivWaveSynth::tick(bool skipSubDiv) {
|
|||
break;
|
||||
case DIV_WS_SUBTRACT:
|
||||
for (int i=0; i<=state.speed; i++) {
|
||||
output[pos]+=MIN(height,state.param1);
|
||||
output[pos]-=MIN(height,state.param1);
|
||||
if (output[pos]<0) output[pos]+=height;
|
||||
if (++pos>=width) pos=0;
|
||||
}
|
||||
|
|
|
@ -104,6 +104,7 @@ const char* aboutLine[]={
|
|||
"fd",
|
||||
"GENATARi",
|
||||
"host12prog",
|
||||
"Lumigado",
|
||||
"Lunathir",
|
||||
"plane",
|
||||
"TheEssem",
|
||||
|
|
|
@ -410,7 +410,7 @@ void FurnaceGUI::drawInsList(bool asChild) {
|
|||
curIns=i;
|
||||
wavePreviewInit=true;
|
||||
}
|
||||
if (ImGui::IsItemHovered() && i>=0) {
|
||||
if (ImGui::IsItemHovered() && i>=0 && !mobileUI) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_TEXT]);
|
||||
ImGui::SetTooltip("%s",insType);
|
||||
ImGui::PopStyleColor();
|
||||
|
@ -679,7 +679,7 @@ void FurnaceGUI::actualSampleList() {
|
|||
updateSampleTex=true;
|
||||
}
|
||||
if (wantScrollList && curSample==i) ImGui::SetScrollHereY();
|
||||
if (ImGui::IsItemHovered()) {
|
||||
if (ImGui::IsItemHovered() && !mobileUI) {
|
||||
ImGui::SetTooltip("Bank %d: %s",i/12,sampleNote[i%12]);
|
||||
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
|
||||
sampleEditOpen=true;
|
||||
|
|
|
@ -244,6 +244,14 @@ void FurnaceGUI::drawMobileControls() {
|
|||
mobileMenuOpen=false;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Log")) {
|
||||
logOpen=!logOpen;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Debug")) {
|
||||
debugOpen=!debugOpen;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("About")) {
|
||||
mobileMenuOpen=false;
|
||||
mobileMenuPos=0.0f;
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
#ifdef USE_NFD
|
||||
#include <nfd.h>
|
||||
#elif defined(ANDROID)
|
||||
#include <SDL.h>
|
||||
#else
|
||||
#include "../../extern/pfd-fixed/portable-file-dialogs.h"
|
||||
#endif
|
||||
|
@ -87,6 +89,45 @@ bool FurnaceGUIFileDialog::openLoad(String header, std::vector<String> filter, c
|
|||
#else
|
||||
dialogO=new std::thread(_nfdThread,NFDState(false,header,filter,path,clickCallback,allowMultiple),&dialogOK,&nfdResult,&hasError);
|
||||
#endif
|
||||
#elif defined(ANDROID)
|
||||
hasError=false;
|
||||
if (jniEnv==NULL) {
|
||||
jniEnv=(JNIEnv*)SDL_AndroidGetJNIEnv();
|
||||
if (jniEnv==NULL) {
|
||||
hasError=true;
|
||||
logE("could not acquire JNI env!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
jobject activity=(jobject)SDL_AndroidGetActivity();
|
||||
if (activity==NULL) {
|
||||
hasError=true;
|
||||
logE("the Activity is NULL!");
|
||||
return false;
|
||||
}
|
||||
|
||||
jclass class_=jniEnv->GetObjectClass(activity);
|
||||
jmethodID showFileDialog=jniEnv->GetMethodID(class_,"showFileDialog","()B");
|
||||
|
||||
if (showFileDialog==NULL) {
|
||||
logE("method showFileDialog not found!");
|
||||
hasError=true;
|
||||
jniEnv->DeleteLocalRef(class_);
|
||||
jniEnv->DeleteLocalRef(activity);
|
||||
return false;
|
||||
}
|
||||
|
||||
jboolean mret=jniEnv->CallBooleanMethod(activity,showFileDialog);
|
||||
|
||||
if (!(bool)mret) {
|
||||
hasError=true;
|
||||
logW("could not open Android file picker...");
|
||||
}
|
||||
|
||||
jniEnv->DeleteLocalRef(class_);
|
||||
jniEnv->DeleteLocalRef(activity);
|
||||
return true;
|
||||
#else
|
||||
dialogO=new pfd::open_file(header,path,filter,allowMultiple?(pfd::opt::multiselect):(pfd::opt::none));
|
||||
hasError=!pfd::settings::available();
|
||||
|
@ -113,6 +154,8 @@ bool FurnaceGUIFileDialog::openSave(String header, std::vector<String> filter, c
|
|||
#else
|
||||
dialogS=new std::thread(_nfdThread,NFDState(true,header,filter,path,NULL,false),&dialogOK,&nfdResult,&hasError);
|
||||
#endif
|
||||
#elif defined(ANDROID)
|
||||
hasError=true; // TODO
|
||||
#else
|
||||
dialogS=new pfd::save_file(header,path,filter);
|
||||
hasError=!pfd::settings::available();
|
||||
|
@ -141,7 +184,9 @@ void FurnaceGUIFileDialog::close() {
|
|||
#ifdef USE_NFD
|
||||
dialogS->join();
|
||||
#endif
|
||||
#ifndef ANDROID
|
||||
delete dialogS;
|
||||
#endif
|
||||
dialogS=NULL;
|
||||
}
|
||||
} else {
|
||||
|
@ -149,7 +194,9 @@ void FurnaceGUIFileDialog::close() {
|
|||
#ifdef USE_NFD
|
||||
dialogO->join();
|
||||
#endif
|
||||
#ifndef ANDROID
|
||||
delete dialogO;
|
||||
#endif
|
||||
dialogO=NULL;
|
||||
}
|
||||
}
|
||||
|
@ -179,6 +226,9 @@ bool FurnaceGUIFileDialog::render(const ImVec2& min, const ImVec2& max) {
|
|||
return true;
|
||||
}
|
||||
return false;
|
||||
#elif defined(ANDROID)
|
||||
// TODO: detect when file picker is closed
|
||||
return false;
|
||||
#else
|
||||
if (saving) {
|
||||
if (dialogS!=NULL) {
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
#define NFD_NON_THREADED
|
||||
#endif
|
||||
|
||||
#elif defined(ANDROID)
|
||||
#include <jni.h>
|
||||
#else
|
||||
namespace pfd {
|
||||
class open_file;
|
||||
|
@ -36,6 +38,10 @@ class FurnaceGUIFileDialog {
|
|||
std::thread* dialogS;
|
||||
std::atomic<bool> dialogOK;
|
||||
std::vector<String> nfdResult;
|
||||
#elif defined(ANDROID)
|
||||
JNIEnv* jniEnv;
|
||||
void* dialogO;
|
||||
void* dialogS;
|
||||
#else
|
||||
pfd::open_file* dialogO;
|
||||
pfd::save_file* dialogS;
|
||||
|
@ -55,6 +61,9 @@ class FurnaceGUIFileDialog {
|
|||
opened(false),
|
||||
saving(false),
|
||||
hasError(false),
|
||||
#ifdef ANDROID
|
||||
jniEnv(NULL),
|
||||
#endif
|
||||
dialogO(NULL),
|
||||
dialogS(NULL) {}
|
||||
};
|
||||
|
|
|
@ -2977,6 +2977,10 @@ bool FurnaceGUI::loop() {
|
|||
}
|
||||
eventTimeBegin=SDL_GetPerformanceCounter();
|
||||
bool updateWindow=false;
|
||||
if (injectBackUp) {
|
||||
ImGui::GetIO().AddKeyEvent(ImGuiKey_Backspace,false);
|
||||
injectBackUp=false;
|
||||
}
|
||||
while (SDL_PollEvent(&ev)) {
|
||||
WAKE_UP;
|
||||
ImGui_ImplSDL2_ProcessEvent(&ev);
|
||||
|
@ -3039,6 +3043,9 @@ bool FurnaceGUI::loop() {
|
|||
if (!ImGui::GetIO().WantCaptureKeyboard) {
|
||||
keyDown(ev);
|
||||
}
|
||||
#ifdef IS_MOBILE
|
||||
injectBackUp=true;
|
||||
#endif
|
||||
break;
|
||||
case SDL_KEYUP:
|
||||
// for now
|
||||
|
@ -3738,6 +3745,10 @@ bool FurnaceGUI::loop() {
|
|||
drawPiano();
|
||||
break;
|
||||
}
|
||||
|
||||
globalWinFlags=0;
|
||||
drawDebug();
|
||||
drawLog();
|
||||
} else {
|
||||
globalWinFlags=0;
|
||||
ImGui::DockSpaceOverViewport(NULL,lockLayout?(ImGuiDockNodeFlags_NoWindowMenuButton|ImGuiDockNodeFlags_NoMove|ImGuiDockNodeFlags_NoResize|ImGuiDockNodeFlags_NoCloseButton|ImGuiDockNodeFlags_NoDocking|ImGuiDockNodeFlags_NoDockingSplitMe|ImGuiDockNodeFlags_NoDockingSplitOther):0);
|
||||
|
@ -3808,7 +3819,7 @@ bool FurnaceGUI::loop() {
|
|||
}
|
||||
#endif
|
||||
|
||||
if (fileDialog->render(ImVec2(600.0f*dpiScale,400.0f*dpiScale),ImVec2(canvasW,canvasH))) {
|
||||
if (fileDialog->render(mobileUI?ImVec2(canvasW-(portrait?0:(60.0*dpiScale)),canvasH-60.0*dpiScale):ImVec2(600.0f*dpiScale,400.0f*dpiScale),ImVec2(canvasW-((mobileUI && !portrait)?(60.0*dpiScale):0),canvasH-(mobileUI?(60.0*dpiScale):0)))) {
|
||||
bool openOpen=false;
|
||||
//ImGui::GetIO().ConfigFlags&=~ImGuiConfigFlags_NavEnableKeyboard;
|
||||
if ((curFileDialog==GUI_FILE_INS_OPEN || curFileDialog==GUI_FILE_INS_OPEN_REPLACE) && prevIns!=-3) {
|
||||
|
@ -3896,8 +3907,12 @@ bool FurnaceGUI::loop() {
|
|||
if (fileDialog->isError()) {
|
||||
#if defined(_WIN32) || defined(__APPLE__)
|
||||
showError("there was an error in the file dialog! you may want to report this issue to:\nhttps://github.com/tildearrow/furnace/issues\ncheck the Log Viewer (window > log viewer) for more information.\n\nfor now please disable the system file picker in Settings > General.");
|
||||
#else
|
||||
#ifdef ANDROID
|
||||
showError("can't do anything without Storage permissions!");
|
||||
#else
|
||||
showError("Zenity/KDialog not available!\nplease install one of these, or disable the system file picker in Settings > General.");
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
if (fileDialog->accepted()) {
|
||||
|
@ -4920,6 +4935,8 @@ bool FurnaceGUI::loop() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
curWindowThreadSafe=curWindow;
|
||||
|
||||
SDL_SetRenderDrawColor(sdlRend,uiColors[GUI_COLOR_BACKGROUND].x*255,
|
||||
uiColors[GUI_COLOR_BACKGROUND].y*255,
|
||||
|
@ -5026,6 +5043,10 @@ bool FurnaceGUI::init() {
|
|||
followOrders=e->getConfBool("followOrders",true);
|
||||
followPattern=e->getConfBool("followPattern",true);
|
||||
noteInputPoly=e->getConfBool("noteInputPoly",true);
|
||||
exportLoops=e->getConfInt("exportLoops",0);
|
||||
if (exportLoops<0) exportLoops=0;
|
||||
exportFadeOut=e->getConfDouble("exportFadeOut",0.0);
|
||||
if (exportFadeOut<0.0) exportFadeOut=0.0;
|
||||
orderEditMode=e->getConfInt("orderEditMode",0);
|
||||
if (orderEditMode<0) orderEditMode=0;
|
||||
if (orderEditMode>3) orderEditMode=3;
|
||||
|
@ -5060,6 +5081,11 @@ bool FurnaceGUI::init() {
|
|||
|
||||
syncSettings();
|
||||
|
||||
if (!settings.persistFadeOut) {
|
||||
exportLoops=settings.exportLoops;
|
||||
exportFadeOut=settings.exportFadeOut;
|
||||
}
|
||||
|
||||
for (int i=0; i<settings.maxRecentFile; i++) {
|
||||
String r=e->getConfString(fmt::sprintf("recentFile%d",i),"");
|
||||
if (!r.empty()) {
|
||||
|
@ -5276,6 +5302,31 @@ bool FurnaceGUI::init() {
|
|||
if (!midiMap.noteInput) return -2;
|
||||
if (learning!=-1) return -2;
|
||||
if (midiMap.at(msg)) return -2;
|
||||
|
||||
if (curWindowThreadSafe==GUI_WINDOW_WAVE_EDIT || curWindowThreadSafe==GUI_WINDOW_WAVE_LIST) {
|
||||
if ((msg.type&0xf0)==TA_MIDI_NOTE_ON) {
|
||||
e->previewWaveNoLock(curWave,msg.data[0]-12);
|
||||
wavePreviewNote=msg.data[0]-12;
|
||||
} else if ((msg.type&0xf0)==TA_MIDI_NOTE_OFF) {
|
||||
if (wavePreviewNote==msg.data[0]-12) {
|
||||
e->stopWavePreviewNoLock();
|
||||
}
|
||||
}
|
||||
return -2;
|
||||
}
|
||||
|
||||
if (curWindowThreadSafe==GUI_WINDOW_SAMPLE_EDIT || curWindowThreadSafe==GUI_WINDOW_SAMPLE_LIST) {
|
||||
if ((msg.type&0xf0)==TA_MIDI_NOTE_ON) {
|
||||
e->previewSampleNoLock(curSample,msg.data[0]-12);
|
||||
samplePreviewNote=msg.data[0]-12;
|
||||
} else if ((msg.type&0xf0)==TA_MIDI_NOTE_OFF) {
|
||||
if (samplePreviewNote==msg.data[0]-12) {
|
||||
e->stopSamplePreviewNoLock();
|
||||
}
|
||||
}
|
||||
return -2;
|
||||
}
|
||||
|
||||
return curIns;
|
||||
});
|
||||
|
||||
|
@ -5359,6 +5410,10 @@ bool FurnaceGUI::finish() {
|
|||
e->setConf("followPattern",followPattern);
|
||||
e->setConf("orderEditMode",orderEditMode);
|
||||
e->setConf("noteInputPoly",noteInputPoly);
|
||||
if (settings.persistFadeOut) {
|
||||
e->setConf("exportLoops",exportLoops);
|
||||
e->setConf("exportFadeOut",exportFadeOut);
|
||||
}
|
||||
|
||||
// commit oscilloscope state
|
||||
e->setConf("oscZoom",oscZoom);
|
||||
|
@ -5431,6 +5486,7 @@ FurnaceGUI::FurnaceGUI():
|
|||
vgmExportPatternHints(false),
|
||||
vgmExportDirectStream(false),
|
||||
portrait(false),
|
||||
injectBackUp(false),
|
||||
mobileMenuOpen(false),
|
||||
wantCaptureKeyboard(false),
|
||||
oldWantCaptureKeyboard(false),
|
||||
|
@ -5443,6 +5499,7 @@ FurnaceGUI::FurnaceGUI():
|
|||
displayPendingIns(false),
|
||||
pendingInsSingle(false),
|
||||
displayPendingRawSample(false),
|
||||
snesFilterHex(false),
|
||||
vgmExportVersion(0x171),
|
||||
drawHalt(10),
|
||||
zsmExportTickRate(60),
|
||||
|
@ -5572,6 +5629,7 @@ FurnaceGUI::FurnaceGUI():
|
|||
curWindow(GUI_WINDOW_NOTHING),
|
||||
nextWindow(GUI_WINDOW_NOTHING),
|
||||
curWindowLast(GUI_WINDOW_NOTHING),
|
||||
curWindowThreadSafe(GUI_WINDOW_NOTHING),
|
||||
lastPatternWidth(0.0f),
|
||||
nextDesc(NULL),
|
||||
latchNote(-1),
|
||||
|
|
|
@ -1028,7 +1028,7 @@ class FurnaceGUI {
|
|||
String workingDirVGMExport, workingDirZSMExport, workingDirROMExport, workingDirFont, workingDirColors, workingDirKeybinds;
|
||||
String workingDirLayout, workingDirROM, workingDirTest;
|
||||
String mmlString[32];
|
||||
String mmlStringW;
|
||||
String mmlStringW, mmlStringSNES;
|
||||
|
||||
std::vector<DivSystem> sysSearchResults;
|
||||
std::vector<FurnaceGUISysDef> newSongSearchResults;
|
||||
|
@ -1036,10 +1036,10 @@ class FurnaceGUI {
|
|||
|
||||
bool quit, warnQuit, willCommit, edit, modified, displayError, displayExporting, vgmExportLoop, zsmExportLoop, vgmExportPatternHints;
|
||||
bool vgmExportDirectStream;
|
||||
bool portrait, mobileMenuOpen;
|
||||
bool portrait, injectBackUp, mobileMenuOpen;
|
||||
bool wantCaptureKeyboard, oldWantCaptureKeyboard, displayMacroMenu;
|
||||
bool displayNew, fullScreen, preserveChanPos, wantScrollList, noteInputPoly;
|
||||
bool displayPendingIns, pendingInsSingle, displayPendingRawSample;
|
||||
bool displayPendingIns, pendingInsSingle, displayPendingRawSample, snesFilterHex;
|
||||
bool willExport[32];
|
||||
int vgmExportVersion;
|
||||
int drawHalt;
|
||||
|
@ -1212,6 +1212,10 @@ class FurnaceGUI {
|
|||
int midiOutMode;
|
||||
int maxRecentFile;
|
||||
int centerPattern;
|
||||
int ordersCursor;
|
||||
int persistFadeOut;
|
||||
int exportLoops;
|
||||
double exportFadeOut;
|
||||
unsigned int maxUndoSteps;
|
||||
String mainFontPath;
|
||||
String patFontPath;
|
||||
|
@ -1255,7 +1259,7 @@ class FurnaceGUI {
|
|||
wrapVertical(0),
|
||||
macroView(0),
|
||||
fmNames(0),
|
||||
allowEditDocking(0),
|
||||
allowEditDocking(1),
|
||||
chipNames(0),
|
||||
overflowHighlight(0),
|
||||
partyTime(0),
|
||||
|
@ -1338,6 +1342,10 @@ class FurnaceGUI {
|
|||
midiOutMode(1),
|
||||
maxRecentFile(10),
|
||||
centerPattern(0),
|
||||
ordersCursor(1),
|
||||
persistFadeOut(1),
|
||||
exportLoops(0),
|
||||
exportFadeOut(0.0),
|
||||
maxUndoSteps(100),
|
||||
mainFontPath(""),
|
||||
patFontPath(""),
|
||||
|
@ -1374,6 +1382,7 @@ class FurnaceGUI {
|
|||
bool collapseWindow, demandScrollX, fancyPattern, wantPatName, firstFrame, tempoView, waveHex, waveSigned, waveGenVisible, lockLayout, editOptsVisible, latchNibble, nonLatchNibble;
|
||||
bool keepLoopAlive;
|
||||
FurnaceGUIWindows curWindow, nextWindow, curWindowLast;
|
||||
std::atomic<FurnaceGUIWindows> curWindowThreadSafe;
|
||||
float peak[2];
|
||||
float patChanX[DIV_MAX_CHANS+1];
|
||||
float patChanSlideY[DIV_MAX_CHANS+1];
|
||||
|
|
|
@ -1852,6 +1852,11 @@ void FurnaceGUI::drawMacros(std::vector<FurnaceGUIMacroDesc>& macros) {
|
|||
#define CENTER_TEXT_20(text) \
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX()+0.5*(20.0f*dpiScale-ImGui::CalcTextSize(text).x));
|
||||
|
||||
#define TOOLTIP_TEXT(text) \
|
||||
if (ImGui::IsItemHovered()) { \
|
||||
ImGui::SetTooltip("%s", text); \
|
||||
}
|
||||
|
||||
#define OP_DRAG_POINT \
|
||||
if (ImGui::Button(ICON_FA_ARROWS)) { \
|
||||
} \
|
||||
|
@ -2329,69 +2334,85 @@ void FurnaceGUI::drawInsEdit() {
|
|||
ImGui::TableNextColumn();
|
||||
CENTER_TEXT(FM_SHORT_NAME(FM_AR));
|
||||
ImGui::TextUnformatted(FM_SHORT_NAME(FM_AR));
|
||||
TOOLTIP_TEXT(FM_NAME(FM_AR));
|
||||
ImGui::TableNextColumn();
|
||||
CENTER_TEXT(FM_SHORT_NAME(FM_DR));
|
||||
ImGui::TextUnformatted(FM_SHORT_NAME(FM_DR));
|
||||
TOOLTIP_TEXT(FM_NAME(FM_DR));
|
||||
if (settings.susPosition==0) {
|
||||
ImGui::TableNextColumn();
|
||||
CENTER_TEXT(FM_SHORT_NAME(FM_SL));
|
||||
ImGui::TextUnformatted(FM_SHORT_NAME(FM_SL));
|
||||
TOOLTIP_TEXT(FM_NAME(FM_SL));
|
||||
}
|
||||
if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPM) {
|
||||
ImGui::TableNextColumn();
|
||||
CENTER_TEXT(FM_SHORT_NAME(FM_D2R));
|
||||
ImGui::TextUnformatted(FM_SHORT_NAME(FM_D2R));
|
||||
TOOLTIP_TEXT(FM_NAME(FM_D2R));
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
CENTER_TEXT(FM_SHORT_NAME(FM_RR));
|
||||
ImGui::TextUnformatted(FM_SHORT_NAME(FM_RR));
|
||||
TOOLTIP_TEXT(FM_NAME(FM_RR));
|
||||
if (settings.susPosition==1) {
|
||||
ImGui::TableNextColumn();
|
||||
CENTER_TEXT(FM_SHORT_NAME(FM_SL));
|
||||
ImGui::TextUnformatted(FM_SHORT_NAME(FM_SL));
|
||||
TOOLTIP_TEXT(FM_NAME(FM_SL));
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TableNextColumn();
|
||||
CENTER_TEXT(FM_SHORT_NAME(FM_TL));
|
||||
ImGui::TextUnformatted(FM_SHORT_NAME(FM_TL));
|
||||
TOOLTIP_TEXT(FM_NAME(FM_TL));
|
||||
ImGui::TableNextColumn();
|
||||
if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPM) {
|
||||
CENTER_TEXT(FM_SHORT_NAME(FM_RS));
|
||||
ImGui::TextUnformatted(FM_SHORT_NAME(FM_RS));
|
||||
TOOLTIP_TEXT(FM_NAME(FM_RS));
|
||||
} else {
|
||||
CENTER_TEXT(FM_SHORT_NAME(FM_KSL));
|
||||
ImGui::TextUnformatted(FM_SHORT_NAME(FM_KSL));
|
||||
TOOLTIP_TEXT(FM_NAME(FM_KSL));
|
||||
}
|
||||
if (ins->type==DIV_INS_OPZ) {
|
||||
ImGui::TableNextColumn();
|
||||
CENTER_TEXT(FM_SHORT_NAME(FM_EGSHIFT));
|
||||
ImGui::TextUnformatted(FM_SHORT_NAME(FM_EGSHIFT));
|
||||
TOOLTIP_TEXT(FM_NAME(FM_EGSHIFT));
|
||||
ImGui::TableNextColumn();
|
||||
CENTER_TEXT(FM_SHORT_NAME(FM_REV));
|
||||
ImGui::TextUnformatted(FM_SHORT_NAME(FM_REV));
|
||||
TOOLTIP_TEXT(FM_NAME(FM_REV));
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
CENTER_TEXT(FM_SHORT_NAME(FM_MULT));
|
||||
ImGui::TextUnformatted(FM_SHORT_NAME(FM_MULT));
|
||||
TOOLTIP_TEXT(FM_NAME(FM_MULT));
|
||||
if (ins->type==DIV_INS_OPZ) {
|
||||
ImGui::TableNextColumn();
|
||||
CENTER_TEXT(FM_SHORT_NAME(FM_FINE));
|
||||
ImGui::TextUnformatted(FM_SHORT_NAME(FM_FINE));
|
||||
TOOLTIP_TEXT(FM_NAME(FM_FINE));
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPM) {
|
||||
CENTER_TEXT(FM_SHORT_NAME(FM_DT));
|
||||
ImGui::TextUnformatted(FM_SHORT_NAME(FM_DT));
|
||||
TOOLTIP_TEXT(FM_NAME(FM_DT));
|
||||
ImGui::TableNextColumn();
|
||||
}
|
||||
if (ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPM) {
|
||||
CENTER_TEXT(FM_SHORT_NAME(FM_DT2));
|
||||
ImGui::TextUnformatted(FM_SHORT_NAME(FM_DT2));
|
||||
TOOLTIP_TEXT(FM_NAME(FM_DT2));
|
||||
ImGui::TableNextColumn();
|
||||
}
|
||||
if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPM) {
|
||||
CENTER_TEXT(FM_SHORT_NAME(FM_AM));
|
||||
ImGui::TextUnformatted(FM_SHORT_NAME(FM_AM));
|
||||
TOOLTIP_TEXT(FM_NAME(FM_AM));
|
||||
} else {
|
||||
CENTER_TEXT("Other");
|
||||
ImGui::TextUnformatted("Other");
|
||||
|
@ -2703,7 +2724,7 @@ void FurnaceGUI::drawInsEdit() {
|
|||
char tempID[1024];
|
||||
ImVec2 oldPadding=ImGui::GetStyle().CellPadding;
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding,ImVec2(8.0f*dpiScale,4.0f*dpiScale));
|
||||
if (ImGui::BeginTable("KGE93BSIEO3NOWBDJZBA",columns,ImGuiTableFlags_SizingStretchSame|ImGuiTableFlags_BordersInner)) {
|
||||
if (ImGui::BeginTable("AltFMOperators",columns,ImGuiTableFlags_SizingStretchSame|ImGuiTableFlags_BordersInner)) {
|
||||
for (int i=0; i<opCount; i++) {
|
||||
DivInstrumentFM::Operator& op=ins->fm.op[(opCount==4 && ins->type!=DIV_INS_OPL_DRUMS)?opOrder[i]:i];
|
||||
if ((settings.fmLayout!=6 && ((i+1)&1)) || i==0 || settings.fmLayout==5) ImGui::TableNextRow();
|
||||
|
@ -2804,6 +2825,7 @@ void FurnaceGUI::drawInsEdit() {
|
|||
float textY=ImGui::GetCursorPosY();
|
||||
CENTER_TEXT_20(FM_SHORT_NAME(FM_AR));
|
||||
ImGui::TextUnformatted(FM_SHORT_NAME(FM_AR));
|
||||
TOOLTIP_TEXT(FM_NAME(FM_AR));
|
||||
ImGui::TableNextColumn();
|
||||
if (ins->type==DIV_INS_FM) {
|
||||
ImGui::Text("SSG-EG");
|
||||
|
@ -2814,7 +2836,8 @@ void FurnaceGUI::drawInsEdit() {
|
|||
ImGui::Text("Envelope");
|
||||
ImGui::TableNextColumn();
|
||||
CENTER_TEXT(FM_SHORT_NAME(FM_TL));
|
||||
ImGui::Text("TL");
|
||||
ImGui::Text(FM_SHORT_NAME(FM_TL));
|
||||
TOOLTIP_TEXT(FM_NAME(FM_TL));
|
||||
|
||||
// A/D/S/R
|
||||
ImGui::TableNextColumn();
|
||||
|
@ -2861,19 +2884,23 @@ void FurnaceGUI::drawInsEdit() {
|
|||
ImGui::SetCursorPos(ImVec2(textX_DR,textY));
|
||||
CENTER_TEXT_20(FM_SHORT_NAME(FM_DR));
|
||||
ImGui::TextUnformatted(FM_SHORT_NAME(FM_DR));
|
||||
TOOLTIP_TEXT(FM_NAME(FM_DR));
|
||||
|
||||
ImGui::SetCursorPos(ImVec2(textX_SL,textY));
|
||||
CENTER_TEXT_20(FM_SHORT_NAME(FM_SL));
|
||||
ImGui::TextUnformatted(FM_SHORT_NAME(FM_SL));
|
||||
TOOLTIP_TEXT(FM_NAME(FM_SL));
|
||||
|
||||
ImGui::SetCursorPos(ImVec2(textX_RR,textY));
|
||||
CENTER_TEXT_20(FM_SHORT_NAME(FM_RR));
|
||||
ImGui::TextUnformatted(FM_SHORT_NAME(FM_RR));
|
||||
TOOLTIP_TEXT(FM_NAME(FM_RR));
|
||||
|
||||
if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPM) {
|
||||
ImGui::SetCursorPos(ImVec2(textX_D2R,textY));
|
||||
CENTER_TEXT_20(FM_SHORT_NAME(FM_D2R));
|
||||
ImGui::TextUnformatted(FM_SHORT_NAME(FM_D2R));
|
||||
TOOLTIP_TEXT(FM_NAME(FM_D2R));
|
||||
}
|
||||
|
||||
ImGui::SetCursorPos(prevCurPos);
|
||||
|
@ -3141,6 +3168,7 @@ void FurnaceGUI::drawInsEdit() {
|
|||
if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPM) {
|
||||
CENTER_TEXT(FM_SHORT_NAME(FM_AM));
|
||||
ImGui::TextUnformatted(FM_SHORT_NAME(FM_AM));
|
||||
TOOLTIP_TEXT(FM_NAME(FM_AM));
|
||||
bool amOn=op.am;
|
||||
if (ImGui::Checkbox("##AM",&amOn)) { PARAMETER
|
||||
op.am=amOn;
|
||||
|
@ -4475,6 +4503,50 @@ void FurnaceGUI::drawInsEdit() {
|
|||
ins->type==DIV_INS_SNES ||
|
||||
ins->type==DIV_INS_NAMCO) {
|
||||
if (ImGui::BeginTabItem("Wavetable")) {
|
||||
switch (ins->type) {
|
||||
case DIV_INS_GB:
|
||||
case DIV_INS_NAMCO:
|
||||
case DIV_INS_SWAN:
|
||||
wavePreviewLen=32;
|
||||
wavePreviewHeight=15;
|
||||
break;
|
||||
case DIV_INS_PCE:
|
||||
wavePreviewLen=32;
|
||||
wavePreviewHeight=31;
|
||||
break;
|
||||
case DIV_INS_VBOY:
|
||||
wavePreviewLen=32;
|
||||
wavePreviewHeight=63;
|
||||
break;
|
||||
case DIV_INS_SCC:
|
||||
wavePreviewLen=32;
|
||||
wavePreviewHeight=255;
|
||||
break;
|
||||
case DIV_INS_FDS:
|
||||
wavePreviewLen=64;
|
||||
wavePreviewHeight=63;
|
||||
break;
|
||||
case DIV_INS_N163:
|
||||
wavePreviewLen=ins->n163.waveLen;
|
||||
wavePreviewHeight=15;
|
||||
break;
|
||||
case DIV_INS_X1_010:
|
||||
wavePreviewLen=128;
|
||||
wavePreviewHeight=255;
|
||||
break;
|
||||
case DIV_INS_AMIGA:
|
||||
wavePreviewLen=ins->amiga.waveLen+1;
|
||||
wavePreviewHeight=255;
|
||||
break;
|
||||
case DIV_INS_SNES:
|
||||
wavePreviewLen=ins->amiga.waveLen+1;
|
||||
wavePreviewHeight=15;
|
||||
break;
|
||||
default:
|
||||
wavePreviewLen=32;
|
||||
wavePreviewHeight=31;
|
||||
break;
|
||||
}
|
||||
if (ImGui::Checkbox("Enable synthesizer",&ins->ws.enabled)) {
|
||||
wavePreviewInit=true;
|
||||
}
|
||||
|
@ -4512,7 +4584,8 @@ void FurnaceGUI::drawInsEdit() {
|
|||
ImGui::Unindent();
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
if (ImGui::BeginTable("WSPreview",3)) {
|
||||
const bool isSingleWaveFX=(ins->ws.effect>=128);
|
||||
if (ImGui::BeginTable("WSPreview",isSingleWaveFX?3:2)) {
|
||||
DivWavetable* wave1=e->getWave(ins->ws.wave1);
|
||||
DivWavetable* wave2=e->getWave(ins->ws.wave2);
|
||||
if (wavePreviewInit) {
|
||||
|
@ -4545,15 +4618,19 @@ void FurnaceGUI::drawInsEdit() {
|
|||
}
|
||||
}
|
||||
|
||||
float ySize=(isSingleWaveFX?96.0f:128.0f)*dpiScale;
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImVec2 size1=ImVec2(ImGui::GetContentRegionAvail().x,64.0f*dpiScale);
|
||||
ImVec2 size1=ImVec2(ImGui::GetContentRegionAvail().x,ySize);
|
||||
PlotNoLerp("##WaveformP1",wavePreview1,wave1->len+1,0,"Wave 1",0,wave1->max,size1);
|
||||
if (isSingleWaveFX) {
|
||||
ImGui::TableNextColumn();
|
||||
ImVec2 size2=ImVec2(ImGui::GetContentRegionAvail().x,ySize);
|
||||
PlotNoLerp("##WaveformP2",wavePreview2,wave2->len+1,0,"Wave 2",0,wave2->max,size2);
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
ImVec2 size2=ImVec2(ImGui::GetContentRegionAvail().x,64.0f*dpiScale);
|
||||
PlotNoLerp("##WaveformP2",wavePreview2,wave2->len+1,0,"Wave 2",0,wave2->max,size2);
|
||||
ImGui::TableNextColumn();
|
||||
ImVec2 size3=ImVec2(ImGui::GetContentRegionAvail().x,64.0f*dpiScale);
|
||||
ImVec2 size3=ImVec2(ImGui::GetContentRegionAvail().x,ySize);
|
||||
PlotNoLerp("##WaveformP3",wavePreview3,wavePreviewLen,0,"Result",0,wavePreviewHeight,size3);
|
||||
|
||||
ImGui::TableNextRow();
|
||||
|
@ -4566,28 +4643,23 @@ void FurnaceGUI::drawInsEdit() {
|
|||
if (ins->ws.wave1>=(int)e->song.wave.size()) ins->ws.wave1=e->song.wave.size()-1;
|
||||
wavePreviewInit=true;
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("Wave 2");
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
if (ImGui::InputInt("##SelWave2",&ins->ws.wave2,1,4)) {
|
||||
if (ins->ws.wave2<0) ins->ws.wave2=0;
|
||||
if (ins->ws.wave2>=(int)e->song.wave.size()) ins->ws.wave2=e->song.wave.size()-1;
|
||||
wavePreviewInit=true;
|
||||
if (isSingleWaveFX) {
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("Wave 2");
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
if (ImGui::InputInt("##SelWave2",&ins->ws.wave2,1,4)) {
|
||||
if (ins->ws.wave2<0) ins->ws.wave2=0;
|
||||
if (ins->ws.wave2>=(int)e->song.wave.size()) ins->ws.wave2=e->song.wave.size()-1;
|
||||
wavePreviewInit=true;
|
||||
}
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Button(ICON_FA_REPEAT "##WSRestart")) {
|
||||
wavePreviewInit=true;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("Preview Width");
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
if (ImGui::InputInt("##SelWave3",&wavePreviewLen,1,4)) {
|
||||
if (wavePreviewLen<1) wavePreviewLen=1;
|
||||
if (wavePreviewLen>256) wavePreviewLen=256;
|
||||
wavePreviewInit=true;
|
||||
}
|
||||
ImGui::Text("(%d×%d)",wavePreviewLen,wavePreviewHeight+1);
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
|
|
|
@ -29,6 +29,14 @@ void FurnaceGUI::drawOrders() {
|
|||
nextWindow=GUI_WINDOW_NOTHING;
|
||||
}
|
||||
if (!ordersOpen) return;
|
||||
if (mobileUI) {
|
||||
patWindowPos=(portrait?ImVec2(0.0f,(mobileMenuPos*-0.65*canvasH)):ImVec2((0.16*canvasH)+0.5*canvasW*mobileMenuPos,0.0f));
|
||||
patWindowSize=(portrait?ImVec2(canvasW,canvasH-(0.16*canvasW)):ImVec2(canvasW-(0.16*canvasH),canvasH));
|
||||
ImGui::SetNextWindowPos(patWindowPos);
|
||||
ImGui::SetNextWindowSize(patWindowSize);
|
||||
} else {
|
||||
//ImGui::SetNextWindowSizeConstraints(ImVec2(440.0f*dpiScale,400.0f*dpiScale),ImVec2(canvasW,canvasH));
|
||||
}
|
||||
if (ImGui::Begin("Orders",&ordersOpen,globalWinFlags)) {
|
||||
float regionX=ImGui::GetContentRegionAvail().x;
|
||||
ImVec2 prevSpacing=ImGui::GetStyle().ItemSpacing;
|
||||
|
@ -94,7 +102,7 @@ void FurnaceGUI::drawOrders() {
|
|||
//}
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_Text,(curOrder==i || e->curOrders->ord[j][i]==e->curOrders->ord[j][curOrder])?uiColors[GUI_COLOR_ORDER_SIMILAR]:uiColors[GUI_COLOR_ORDER_INACTIVE]);
|
||||
if (ImGui::Selectable(selID,(orderEditMode!=0 && curOrder==i && orderCursor==j))) {
|
||||
if (ImGui::Selectable(selID,settings.ordersCursor?(cursor.xCoarse==j && oldOrder1!=i):false)) {
|
||||
if (curOrder==i) {
|
||||
if (orderEditMode==0) {
|
||||
prepareUndo(GUI_UNDO_CHANGE_ORDER);
|
||||
|
@ -127,6 +135,11 @@ void FurnaceGUI::drawOrders() {
|
|||
}
|
||||
}
|
||||
ImGui::PopStyleColor();
|
||||
if (orderEditMode!=0 && curOrder==i && orderCursor==j) {
|
||||
// draw a border
|
||||
ImDrawList* dl=ImGui::GetWindowDrawList();
|
||||
dl->AddRect(ImGui::GetItemRectMin(),ImGui::GetItemRectMax(),ImGui::GetColorU32(uiColors[GUI_COLOR_TEXT]),2.0f*dpiScale);
|
||||
}
|
||||
if (!pat->name.empty() && ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("%s",pat->name.c_str());
|
||||
}
|
||||
|
|
|
@ -437,7 +437,7 @@ void FurnaceGUI::drawPattern() {
|
|||
if (ImGui::Selectable((extraChannelButtons==2)?" --##ExtraChannelButtons":" ++##ExtraChannelButtons",false,ImGuiSelectableFlags_NoPadWithHalfSpacing,ImVec2(0.0f,lineHeight+1.0f*dpiScale))) {
|
||||
if (++extraChannelButtons>2) extraChannelButtons=0;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
if (ImGui::IsItemHovered() && !mobileUI) {
|
||||
if (extraChannelButtons==2) {
|
||||
ImGui::SetTooltip("Pattern names (click to collapse)\nRight-click for visualizer");
|
||||
} else if (extraChannelButtons==1) {
|
||||
|
@ -706,7 +706,7 @@ void FurnaceGUI::drawPattern() {
|
|||
|
||||
if (extraChannelButtons==0 || settings.channelVolStyle!=0) ImGui::PopStyleVar();
|
||||
|
||||
if (displayTooltip && ImGui::IsItemHovered()) {
|
||||
if (displayTooltip && ImGui::IsItemHovered() && !mobileUI) {
|
||||
ImGui::SetTooltip("%s",e->getChannelName(i));
|
||||
}
|
||||
if (settings.channelFont==0) ImGui::PopFont();
|
||||
|
|
|
@ -405,7 +405,7 @@ void FurnaceGUI::drawPiano() {
|
|||
break;
|
||||
case GUI_WINDOW_SAMPLE_LIST:
|
||||
case GUI_WINDOW_SAMPLE_EDIT:
|
||||
e->previewSample(curWave,note);
|
||||
e->previewSample(curSample,note);
|
||||
break;
|
||||
default:
|
||||
e->synchronized([this,note]() {
|
||||
|
|
|
@ -196,11 +196,20 @@ double getScaleFactor(const char* driverHint) {
|
|||
#endif
|
||||
|
||||
// SDL fallback
|
||||
#ifdef ANDROID
|
||||
float dpiScaleF=192.0f;
|
||||
if (SDL_GetDisplayDPI(0,&dpiScaleF,NULL,NULL)==0) {
|
||||
ret=round(dpiScaleF/192.0f);
|
||||
if (ret<1) ret=1;
|
||||
}
|
||||
|
||||
#else
|
||||
float dpiScaleF=96.0f;
|
||||
if (SDL_GetDisplayDPI(0,&dpiScaleF,NULL,NULL)==0) {
|
||||
ret=round(dpiScaleF/96.0f);
|
||||
if (ret<1) ret=1;
|
||||
}
|
||||
#endif
|
||||
|
||||
// couldn't detect scaling factor :<
|
||||
return ret;
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
#define POWER_SAVE_DEFAULT 0
|
||||
#endif
|
||||
|
||||
#ifdef __HAIKU__
|
||||
#if defined(__HAIKU__)
|
||||
// NFD doesn't support Haiku
|
||||
#define SYS_FILE_DIALOG_DEFAULT 0
|
||||
#else
|
||||
|
@ -548,6 +548,24 @@ void FurnaceGUI::drawSettings() {
|
|||
settings.saveUnusedPatterns=saveUnusedPatternsB;
|
||||
}
|
||||
|
||||
ImGui::Text("Audio export loop/fade out time:");
|
||||
if (ImGui::RadioButton("Set to these values on start-up:##fot0",settings.persistFadeOut==0)) {
|
||||
settings.persistFadeOut=0;
|
||||
}
|
||||
ImGui::BeginDisabled(settings.persistFadeOut);
|
||||
if (ImGui::InputInt("Loops",&settings.exportLoops,1,2)) {
|
||||
if (exportLoops<0) exportLoops=0;
|
||||
exportLoops=settings.exportLoops;
|
||||
}
|
||||
if (ImGui::InputDouble("Fade out (seconds)",&settings.exportFadeOut,1.0,2.0,"%.1f")) {
|
||||
if (exportFadeOut<0.0) exportFadeOut=0.0;
|
||||
exportFadeOut=settings.exportFadeOut;
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
if (ImGui::RadioButton("Remember last values##fot1",settings.persistFadeOut==1)) {
|
||||
settings.persistFadeOut=1;
|
||||
}
|
||||
|
||||
ImGui::Text("Note preview behavior:");
|
||||
if (ImGui::RadioButton("Never##npb0",settings.notePreviewBehavior==0)) {
|
||||
settings.notePreviewBehavior=0;
|
||||
|
@ -1459,6 +1477,11 @@ void FurnaceGUI::drawSettings() {
|
|||
settings.sysSeparators=sysSeparatorsB;
|
||||
}*/
|
||||
|
||||
bool ordersCursorB=settings.ordersCursor;
|
||||
if (ImGui::Checkbox("Highlight channel at cursor in Orders",&ordersCursorB)) {
|
||||
settings.ordersCursor=ordersCursorB;
|
||||
}
|
||||
|
||||
bool partyTimeB=settings.partyTime;
|
||||
if (ImGui::Checkbox("About screen party time",&partyTimeB)) {
|
||||
settings.partyTime=partyTimeB;
|
||||
|
@ -2280,7 +2303,7 @@ void FurnaceGUI::syncSettings() {
|
|||
settings.wrapVertical=e->getConfInt("wrapVertical",0);
|
||||
settings.macroView=e->getConfInt("macroView",0);
|
||||
settings.fmNames=e->getConfInt("fmNames",0);
|
||||
settings.allowEditDocking=e->getConfInt("allowEditDocking",0);
|
||||
settings.allowEditDocking=e->getConfInt("allowEditDocking",1);
|
||||
settings.chipNames=e->getConfInt("chipNames",0);
|
||||
settings.overflowHighlight=e->getConfInt("overflowHighlight",0);
|
||||
settings.partyTime=e->getConfInt("partyTime",0);
|
||||
|
@ -2370,6 +2393,10 @@ void FurnaceGUI::syncSettings() {
|
|||
settings.midiOutClock=e->getConfInt("midiOutClock",0);
|
||||
settings.midiOutMode=e->getConfInt("midiOutMode",1);
|
||||
settings.centerPattern=e->getConfInt("centerPattern",0);
|
||||
settings.ordersCursor=e->getConfInt("ordersCursor",1);
|
||||
settings.persistFadeOut=e->getConfInt("persistFadeOut",1);
|
||||
settings.exportLoops=e->getConfInt("exportLoops",0);
|
||||
settings.exportFadeOut=e->getConfDouble("exportFadeOut",0.0);
|
||||
|
||||
clampSetting(settings.mainFontSize,2,96);
|
||||
clampSetting(settings.patFontSize,2,96);
|
||||
|
@ -2474,6 +2501,11 @@ void FurnaceGUI::syncSettings() {
|
|||
clampSetting(settings.midiOutClock,0,1);
|
||||
clampSetting(settings.midiOutMode,0,2);
|
||||
clampSetting(settings.centerPattern,0,1);
|
||||
clampSetting(settings.ordersCursor,0,1);
|
||||
clampSetting(settings.persistFadeOut,0,1);
|
||||
|
||||
if (settings.exportLoops<0.0) settings.exportLoops=0.0;
|
||||
if (settings.exportFadeOut<0.0) settings.exportFadeOut=0.0;
|
||||
|
||||
String initialSys2=e->getConfString("initialSys2","");
|
||||
if (initialSys2.empty()) {
|
||||
|
@ -2639,6 +2671,10 @@ void FurnaceGUI::commitSettings() {
|
|||
e->setConf("midiOutClock",settings.midiOutClock);
|
||||
e->setConf("midiOutMode",settings.midiOutMode);
|
||||
e->setConf("centerPattern",settings.centerPattern);
|
||||
e->setConf("ordersCursor",settings.ordersCursor);
|
||||
e->setConf("persistFadeOut",settings.persistFadeOut);
|
||||
e->setConf("exportLoops",settings.exportLoops);
|
||||
e->setConf("exportFadeOut",settings.exportFadeOut);
|
||||
|
||||
// colors
|
||||
for (int i=0; i<GUI_COLOR_MAX; i++) {
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
*/
|
||||
|
||||
#include "gui.h"
|
||||
#include "misc/cpp/imgui_stdlib.h"
|
||||
#include <imgui.h>
|
||||
|
||||
bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool modifyOnChange) {
|
||||
|
@ -1289,6 +1290,51 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo
|
|||
} rightClickable
|
||||
}
|
||||
|
||||
if (ImGui::Button(snesFilterHex?"Hex##SNESFHex":"Dec##SNESFHex")) {
|
||||
snesFilterHex=!snesFilterHex;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); // wavetable text input size found here
|
||||
if (ImGui::InputText("##MMLWave",&mmlStringSNES)) {
|
||||
int actualData[256];
|
||||
int discardIt=0;
|
||||
memset(actualData,0,256*sizeof(int));
|
||||
decodeMMLStrW(mmlStringSNES,actualData,discardIt,snesFilterHex?0:-128,snesFilterHex?255:127,snesFilterHex);
|
||||
if (snesFilterHex) {
|
||||
for (int i=0; i<8; i++) {
|
||||
if (actualData[i]>=128) actualData[i]-=256;
|
||||
}
|
||||
}
|
||||
memcpy(echoFilter,actualData,8*sizeof(int));
|
||||
altered=true;
|
||||
}
|
||||
if (!ImGui::IsItemActive()) {
|
||||
int actualData[8];
|
||||
for (int i=0; i<8; i++) {
|
||||
if (echoFilter[i]<0 && snesFilterHex) {
|
||||
actualData[i]=echoFilter[i]+256;
|
||||
} else {
|
||||
actualData[i]=echoFilter[i];
|
||||
}
|
||||
}
|
||||
encodeMMLStr(mmlStringSNES,actualData,8,-1,-1,snesFilterHex);
|
||||
}
|
||||
|
||||
int filterSum=(
|
||||
echoFilter[0]+
|
||||
echoFilter[1]+
|
||||
echoFilter[2]+
|
||||
echoFilter[3]+
|
||||
echoFilter[4]+
|
||||
echoFilter[5]+
|
||||
echoFilter[6]+
|
||||
echoFilter[7]
|
||||
);
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_Text,(filterSum<-128 || filterSum>127)?uiColors[GUI_COLOR_LOGLEVEL_WARNING]:uiColors[GUI_COLOR_TEXT]);
|
||||
ImGui::Text("sum: %d",filterSum);
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
if (altered) {
|
||||
e->lockSave([&]() {
|
||||
flags.set("volScaleL",127-vsL);
|
||||
|
|
|
@ -32,6 +32,15 @@
|
|||
#endif
|
||||
|
||||
String getHomeDir() {
|
||||
#ifdef IS_MOBILE
|
||||
|
||||
#ifdef ANDROID
|
||||
return "/storage/emulated/0/";
|
||||
#else
|
||||
return "/";
|
||||
#endif
|
||||
|
||||
#else
|
||||
String ret;
|
||||
char tempDir[4096];
|
||||
|
||||
|
@ -73,6 +82,7 @@ String getHomeDir() {
|
|||
}
|
||||
|
||||
return ret;
|
||||
#endif
|
||||
}
|
||||
|
||||
String getKeyName(int key, bool emptyNone) {
|
||||
|
@ -101,4 +111,4 @@ String getKeyName(int key, bool emptyNone) {
|
|||
ret+=name;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -317,6 +317,13 @@ void reportError(String what) {
|
|||
logE("%s",what);
|
||||
MessageBox(NULL,what.c_str(),"Furnace",MB_OK|MB_ICONERROR);
|
||||
}
|
||||
#elif defined(ANDROID)
|
||||
void reportError(String what) {
|
||||
logE("%s",what);
|
||||
#ifdef HAVE_SDL2
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR,"Error",what.c_str(),NULL);
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
void reportError(String what) {
|
||||
logE("%s",what);
|
||||
|
|
Loading…
Reference in New Issue