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

This commit is contained in:
cam900 2022-11-07 09:45:54 +09:00
commit 7a803f1e73
27 changed files with 433 additions and 67 deletions

View file

@ -33,7 +33,7 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v3.1.0
with: with:
submodules: recursive submodules: recursive
@ -59,9 +59,9 @@ jobs:
echo "MinGW cross target: ${mingw_target}" echo "MinGW cross target: ${mingw_target}"
fi fi
echo "::set-output name=vswhere-target::${vswhere_target}" echo "vswhere-target=${vswhere_target}" >> $GITHUB_OUTPUT
echo "::set-output name=msvc-target::${msvc_target}" echo "msvc-target=${msvc_target}" >> $GITHUB_OUTPUT
echo "::set-output name=mingw-target::${mingw_target}" echo "mingw-target=${mingw_target}" >> $GITHUB_OUTPUT
- name: Set package identifier - name: Set package identifier
id: package-identify id: package-identify
@ -88,8 +88,8 @@ jobs:
echo "Package identifier: ${package_name}" echo "Package identifier: ${package_name}"
echo "Package file: ${package_name}${package_ext}" echo "Package file: ${package_name}${package_ext}"
echo "::set-output name=id::${package_name}" echo "id=${package_name}" >> $GITHUB_OUTPUT
echo "::set-output name=filename::${package_name}${package_ext}" echo "filename=${package_name}${package_ext}" >> $GITHUB_OUTPUT
- name: Set build cores amount - name: Set build cores amount
id: build-cores id: build-cores
@ -102,11 +102,11 @@ jobs:
echo "Amount of cores we can build with: ${amount}" 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] - name: Setup Toolchain [Windows MSVC]
if: ${{ matrix.config.compiler == 'msvc' }} if: ${{ matrix.config.compiler == 'msvc' }}
uses: seanmiddleditch/gha-setup-vsdevenv@v3 uses: vadz/gha-setup-vsdevenv@avoid-deprecation-warnings
with: with:
arch: ${{ steps.windows-identify.outputs.vswhere-target }} arch: ${{ steps.windows-identify.outputs.vswhere-target }}
@ -314,7 +314,7 @@ jobs:
- name: Upload artifact - name: Upload artifact
if: ${{ github.repository == 'tildearrow/furnace' && github.ref_name == 'master' }} if: ${{ github.repository == 'tildearrow/furnace' && github.ref_name == 'master' }}
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3.1.1
with: with:
name: ${{ steps.package-identify.outputs.id }} name: ${{ steps.package-identify.outputs.id }}
path: ${{ steps.package-identify.outputs.filename }} path: ${{ steps.package-identify.outputs.filename }}

View file

@ -85,9 +85,6 @@ check out the [Releases](https://github.com/tildearrow/furnace/releases) page. a
- available on wavetable chips - available on wavetable chips
- create complex sounds with ease - provide up to two wavetables, select and effect and let go! - create complex sounds with ease - provide up to two wavetables, select and effect and let go!
- MIDI input support - 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: - additional features:
- FM macros! - FM macros!
- negative octaves - negative octaves

View file

@ -1,7 +1,25 @@
package org.tildearrow.furnace; package org.tildearrow.furnace;
import android.content.Intent;
import org.libsdl.app.SDLActivity; 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
}
}
} }

BIN
demos/ComicPartytrack20.fur Normal file

Binary file not shown.

BIN
demos/L’ambreSong.fur Normal file

Binary file not shown.

BIN
demos/MMXStageClear.fur Normal file

Binary file not shown.

View file

@ -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. - `1Bxx`: set attack of operator 2.
- `1Cxx`: set attack of operator 3. - `1Cxx`: set attack of operator 3.
- `1Dxx`: set attack of operator 4. - `1Dxx`: set attack of operator 4.
- `1Exx`: set LFO AM depth.
- `1Fxx`: set LFO PM depth.
- `30xx`: enable envelope hard reset. - `30xx`: enable envelope hard reset.
- this works by inserting a quick release and tiny delay before a new note. - this works by inserting a quick release and tiny delay before a new note.
- `50xy`: set AM of operator. - `50xy`: set AM of operator.

View file

@ -2438,6 +2438,29 @@ int DivEngine::getEffectiveSampleRate(int rate) {
void DivEngine::previewSample(int sample, int note, int pStart, int pEnd) { void DivEngine::previewSample(int sample, int note, int pStart, int pEnd) {
BUSY_BEGIN; 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.pBegin=pStart;
sPreview.pEnd=pEnd; sPreview.pEnd=pEnd;
sPreview.dir=false; sPreview.dir=false;
@ -2445,7 +2468,6 @@ void DivEngine::previewSample(int sample, int note, int pStart, int pEnd) {
sPreview.sample=-1; sPreview.sample=-1;
sPreview.pos=0; sPreview.pos=0;
sPreview.dir=false; sPreview.dir=false;
BUSY_END;
return; return;
} }
blip_clear(samp_bb); blip_clear(samp_bb);
@ -2462,28 +2484,22 @@ void DivEngine::previewSample(int sample, int note, int pStart, int pEnd) {
sPreview.sample=sample; sPreview.sample=sample;
sPreview.wave=-1; sPreview.wave=-1;
sPreview.dir=false; sPreview.dir=false;
BUSY_END;
} }
void DivEngine::stopSamplePreview() { void DivEngine::stopSamplePreviewNoLock() {
BUSY_BEGIN;
sPreview.sample=-1; sPreview.sample=-1;
sPreview.pos=0; sPreview.pos=0;
sPreview.dir=false; sPreview.dir=false;
BUSY_END;
} }
void DivEngine::previewWave(int wave, int note) { void DivEngine::previewWaveNoLock(int wave, int note) {
BUSY_BEGIN;
if (wave<0 || wave>=(int)song.wave.size()) { if (wave<0 || wave>=(int)song.wave.size()) {
sPreview.wave=-1; sPreview.wave=-1;
sPreview.pos=0; sPreview.pos=0;
sPreview.dir=false; sPreview.dir=false;
BUSY_END;
return; return;
} }
if (song.wave[wave]->len<=0) { if (song.wave[wave]->len<=0) {
BUSY_END;
return; return;
} }
blip_clear(samp_bb); blip_clear(samp_bb);
@ -2496,15 +2512,12 @@ void DivEngine::previewWave(int wave, int note) {
sPreview.sample=-1; sPreview.sample=-1;
sPreview.wave=wave; sPreview.wave=wave;
sPreview.dir=false; sPreview.dir=false;
BUSY_END;
} }
void DivEngine::stopWavePreview() { void DivEngine::stopWavePreviewNoLock() {
BUSY_BEGIN;
sPreview.wave=-1; sPreview.wave=-1;
sPreview.pos=0; sPreview.pos=0;
sPreview.dir=false; sPreview.dir=false;
BUSY_END;
} }
bool DivEngine::isPreviewingSample() { bool DivEngine::isPreviewingSample() {

View file

@ -616,6 +616,14 @@ class DivEngine {
void previewWave(int wave, int note); void previewWave(int wave, int note);
void stopWavePreview(); 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 // get config path
String getConfigPath(); String getConfigPath();

View file

@ -1412,7 +1412,7 @@ void DivEngine::convertOldFlags(unsigned int oldFlags, DivConfig& newFlags, DivS
newFlags.set("chipType",0); newFlags.set("chipType",0);
break; break;
case 1: case 1:
newFlags.set("chipType",0); newFlags.set("chipType",1);
break; break;
} }
break; break;

View file

@ -60,7 +60,7 @@ bool DivWaveSynth::tick(bool skipSubDiv) {
break; break;
case DIV_WS_SUBTRACT: case DIV_WS_SUBTRACT:
for (int i=0; i<=state.speed; i++) { 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 (output[pos]<0) output[pos]+=height;
if (++pos>=width) pos=0; if (++pos>=width) pos=0;
} }

View file

@ -104,6 +104,7 @@ const char* aboutLine[]={
"fd", "fd",
"GENATARi", "GENATARi",
"host12prog", "host12prog",
"Lumigado",
"Lunathir", "Lunathir",
"plane", "plane",
"TheEssem", "TheEssem",

View file

@ -410,7 +410,7 @@ void FurnaceGUI::drawInsList(bool asChild) {
curIns=i; curIns=i;
wavePreviewInit=true; wavePreviewInit=true;
} }
if (ImGui::IsItemHovered() && i>=0) { if (ImGui::IsItemHovered() && i>=0 && !mobileUI) {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_TEXT]); ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_TEXT]);
ImGui::SetTooltip("%s",insType); ImGui::SetTooltip("%s",insType);
ImGui::PopStyleColor(); ImGui::PopStyleColor();
@ -679,7 +679,7 @@ void FurnaceGUI::actualSampleList() {
updateSampleTex=true; updateSampleTex=true;
} }
if (wantScrollList && curSample==i) ImGui::SetScrollHereY(); if (wantScrollList && curSample==i) ImGui::SetScrollHereY();
if (ImGui::IsItemHovered()) { if (ImGui::IsItemHovered() && !mobileUI) {
ImGui::SetTooltip("Bank %d: %s",i/12,sampleNote[i%12]); ImGui::SetTooltip("Bank %d: %s",i/12,sampleNote[i%12]);
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) { if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
sampleEditOpen=true; sampleEditOpen=true;

View file

@ -244,6 +244,14 @@ void FurnaceGUI::drawMobileControls() {
mobileMenuOpen=false; mobileMenuOpen=false;
} }
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Button("Log")) {
logOpen=!logOpen;
}
ImGui::SameLine();
if (ImGui::Button("Debug")) {
debugOpen=!debugOpen;
}
ImGui::SameLine();
if (ImGui::Button("About")) { if (ImGui::Button("About")) {
mobileMenuOpen=false; mobileMenuOpen=false;
mobileMenuPos=0.0f; mobileMenuPos=0.0f;

View file

@ -4,6 +4,8 @@
#ifdef USE_NFD #ifdef USE_NFD
#include <nfd.h> #include <nfd.h>
#elif defined(ANDROID)
#include <SDL.h>
#else #else
#include "../../extern/pfd-fixed/portable-file-dialogs.h" #include "../../extern/pfd-fixed/portable-file-dialogs.h"
#endif #endif
@ -87,6 +89,45 @@ bool FurnaceGUIFileDialog::openLoad(String header, std::vector<String> filter, c
#else #else
dialogO=new std::thread(_nfdThread,NFDState(false,header,filter,path,clickCallback,allowMultiple),&dialogOK,&nfdResult,&hasError); dialogO=new std::thread(_nfdThread,NFDState(false,header,filter,path,clickCallback,allowMultiple),&dialogOK,&nfdResult,&hasError);
#endif #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 #else
dialogO=new pfd::open_file(header,path,filter,allowMultiple?(pfd::opt::multiselect):(pfd::opt::none)); dialogO=new pfd::open_file(header,path,filter,allowMultiple?(pfd::opt::multiselect):(pfd::opt::none));
hasError=!pfd::settings::available(); hasError=!pfd::settings::available();
@ -113,6 +154,8 @@ bool FurnaceGUIFileDialog::openSave(String header, std::vector<String> filter, c
#else #else
dialogS=new std::thread(_nfdThread,NFDState(true,header,filter,path,NULL,false),&dialogOK,&nfdResult,&hasError); dialogS=new std::thread(_nfdThread,NFDState(true,header,filter,path,NULL,false),&dialogOK,&nfdResult,&hasError);
#endif #endif
#elif defined(ANDROID)
hasError=true; // TODO
#else #else
dialogS=new pfd::save_file(header,path,filter); dialogS=new pfd::save_file(header,path,filter);
hasError=!pfd::settings::available(); hasError=!pfd::settings::available();
@ -141,7 +184,9 @@ void FurnaceGUIFileDialog::close() {
#ifdef USE_NFD #ifdef USE_NFD
dialogS->join(); dialogS->join();
#endif #endif
#ifndef ANDROID
delete dialogS; delete dialogS;
#endif
dialogS=NULL; dialogS=NULL;
} }
} else { } else {
@ -149,7 +194,9 @@ void FurnaceGUIFileDialog::close() {
#ifdef USE_NFD #ifdef USE_NFD
dialogO->join(); dialogO->join();
#endif #endif
#ifndef ANDROID
delete dialogO; delete dialogO;
#endif
dialogO=NULL; dialogO=NULL;
} }
} }
@ -179,6 +226,9 @@ bool FurnaceGUIFileDialog::render(const ImVec2& min, const ImVec2& max) {
return true; return true;
} }
return false; return false;
#elif defined(ANDROID)
// TODO: detect when file picker is closed
return false;
#else #else
if (saving) { if (saving) {
if (dialogS!=NULL) { if (dialogS!=NULL) {

View file

@ -15,6 +15,8 @@
#define NFD_NON_THREADED #define NFD_NON_THREADED
#endif #endif
#elif defined(ANDROID)
#include <jni.h>
#else #else
namespace pfd { namespace pfd {
class open_file; class open_file;
@ -36,6 +38,10 @@ class FurnaceGUIFileDialog {
std::thread* dialogS; std::thread* dialogS;
std::atomic<bool> dialogOK; std::atomic<bool> dialogOK;
std::vector<String> nfdResult; std::vector<String> nfdResult;
#elif defined(ANDROID)
JNIEnv* jniEnv;
void* dialogO;
void* dialogS;
#else #else
pfd::open_file* dialogO; pfd::open_file* dialogO;
pfd::save_file* dialogS; pfd::save_file* dialogS;
@ -55,6 +61,9 @@ class FurnaceGUIFileDialog {
opened(false), opened(false),
saving(false), saving(false),
hasError(false), hasError(false),
#ifdef ANDROID
jniEnv(NULL),
#endif
dialogO(NULL), dialogO(NULL),
dialogS(NULL) {} dialogS(NULL) {}
}; };

View file

@ -2977,6 +2977,10 @@ bool FurnaceGUI::loop() {
} }
eventTimeBegin=SDL_GetPerformanceCounter(); eventTimeBegin=SDL_GetPerformanceCounter();
bool updateWindow=false; bool updateWindow=false;
if (injectBackUp) {
ImGui::GetIO().AddKeyEvent(ImGuiKey_Backspace,false);
injectBackUp=false;
}
while (SDL_PollEvent(&ev)) { while (SDL_PollEvent(&ev)) {
WAKE_UP; WAKE_UP;
ImGui_ImplSDL2_ProcessEvent(&ev); ImGui_ImplSDL2_ProcessEvent(&ev);
@ -3039,6 +3043,9 @@ bool FurnaceGUI::loop() {
if (!ImGui::GetIO().WantCaptureKeyboard) { if (!ImGui::GetIO().WantCaptureKeyboard) {
keyDown(ev); keyDown(ev);
} }
#ifdef IS_MOBILE
injectBackUp=true;
#endif
break; break;
case SDL_KEYUP: case SDL_KEYUP:
// for now // for now
@ -3738,6 +3745,10 @@ bool FurnaceGUI::loop() {
drawPiano(); drawPiano();
break; break;
} }
globalWinFlags=0;
drawDebug();
drawLog();
} else { } else {
globalWinFlags=0; globalWinFlags=0;
ImGui::DockSpaceOverViewport(NULL,lockLayout?(ImGuiDockNodeFlags_NoWindowMenuButton|ImGuiDockNodeFlags_NoMove|ImGuiDockNodeFlags_NoResize|ImGuiDockNodeFlags_NoCloseButton|ImGuiDockNodeFlags_NoDocking|ImGuiDockNodeFlags_NoDockingSplitMe|ImGuiDockNodeFlags_NoDockingSplitOther):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 #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; bool openOpen=false;
//ImGui::GetIO().ConfigFlags&=~ImGuiConfigFlags_NavEnableKeyboard; //ImGui::GetIO().ConfigFlags&=~ImGuiConfigFlags_NavEnableKeyboard;
if ((curFileDialog==GUI_FILE_INS_OPEN || curFileDialog==GUI_FILE_INS_OPEN_REPLACE) && prevIns!=-3) { 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 (fileDialog->isError()) {
#if defined(_WIN32) || defined(__APPLE__) #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."); 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 #else
showError("Zenity/KDialog not available!\nplease install one of these, or disable the system file picker in Settings > General."); showError("Zenity/KDialog not available!\nplease install one of these, or disable the system file picker in Settings > General.");
#endif
#endif #endif
} }
if (fileDialog->accepted()) { if (fileDialog->accepted()) {
@ -4920,6 +4935,8 @@ bool FurnaceGUI::loop() {
} }
} }
} }
curWindowThreadSafe=curWindow;
SDL_SetRenderDrawColor(sdlRend,uiColors[GUI_COLOR_BACKGROUND].x*255, SDL_SetRenderDrawColor(sdlRend,uiColors[GUI_COLOR_BACKGROUND].x*255,
uiColors[GUI_COLOR_BACKGROUND].y*255, uiColors[GUI_COLOR_BACKGROUND].y*255,
@ -5026,6 +5043,10 @@ bool FurnaceGUI::init() {
followOrders=e->getConfBool("followOrders",true); followOrders=e->getConfBool("followOrders",true);
followPattern=e->getConfBool("followPattern",true); followPattern=e->getConfBool("followPattern",true);
noteInputPoly=e->getConfBool("noteInputPoly",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); orderEditMode=e->getConfInt("orderEditMode",0);
if (orderEditMode<0) orderEditMode=0; if (orderEditMode<0) orderEditMode=0;
if (orderEditMode>3) orderEditMode=3; if (orderEditMode>3) orderEditMode=3;
@ -5060,6 +5081,11 @@ bool FurnaceGUI::init() {
syncSettings(); syncSettings();
if (!settings.persistFadeOut) {
exportLoops=settings.exportLoops;
exportFadeOut=settings.exportFadeOut;
}
for (int i=0; i<settings.maxRecentFile; i++) { for (int i=0; i<settings.maxRecentFile; i++) {
String r=e->getConfString(fmt::sprintf("recentFile%d",i),""); String r=e->getConfString(fmt::sprintf("recentFile%d",i),"");
if (!r.empty()) { if (!r.empty()) {
@ -5276,6 +5302,31 @@ bool FurnaceGUI::init() {
if (!midiMap.noteInput) return -2; if (!midiMap.noteInput) return -2;
if (learning!=-1) return -2; if (learning!=-1) return -2;
if (midiMap.at(msg)) 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; return curIns;
}); });
@ -5359,6 +5410,10 @@ bool FurnaceGUI::finish() {
e->setConf("followPattern",followPattern); e->setConf("followPattern",followPattern);
e->setConf("orderEditMode",orderEditMode); e->setConf("orderEditMode",orderEditMode);
e->setConf("noteInputPoly",noteInputPoly); e->setConf("noteInputPoly",noteInputPoly);
if (settings.persistFadeOut) {
e->setConf("exportLoops",exportLoops);
e->setConf("exportFadeOut",exportFadeOut);
}
// commit oscilloscope state // commit oscilloscope state
e->setConf("oscZoom",oscZoom); e->setConf("oscZoom",oscZoom);
@ -5431,6 +5486,7 @@ FurnaceGUI::FurnaceGUI():
vgmExportPatternHints(false), vgmExportPatternHints(false),
vgmExportDirectStream(false), vgmExportDirectStream(false),
portrait(false), portrait(false),
injectBackUp(false),
mobileMenuOpen(false), mobileMenuOpen(false),
wantCaptureKeyboard(false), wantCaptureKeyboard(false),
oldWantCaptureKeyboard(false), oldWantCaptureKeyboard(false),
@ -5443,6 +5499,7 @@ FurnaceGUI::FurnaceGUI():
displayPendingIns(false), displayPendingIns(false),
pendingInsSingle(false), pendingInsSingle(false),
displayPendingRawSample(false), displayPendingRawSample(false),
snesFilterHex(false),
vgmExportVersion(0x171), vgmExportVersion(0x171),
drawHalt(10), drawHalt(10),
zsmExportTickRate(60), zsmExportTickRate(60),
@ -5572,6 +5629,7 @@ FurnaceGUI::FurnaceGUI():
curWindow(GUI_WINDOW_NOTHING), curWindow(GUI_WINDOW_NOTHING),
nextWindow(GUI_WINDOW_NOTHING), nextWindow(GUI_WINDOW_NOTHING),
curWindowLast(GUI_WINDOW_NOTHING), curWindowLast(GUI_WINDOW_NOTHING),
curWindowThreadSafe(GUI_WINDOW_NOTHING),
lastPatternWidth(0.0f), lastPatternWidth(0.0f),
nextDesc(NULL), nextDesc(NULL),
latchNote(-1), latchNote(-1),

View file

@ -1028,7 +1028,7 @@ class FurnaceGUI {
String workingDirVGMExport, workingDirZSMExport, workingDirROMExport, workingDirFont, workingDirColors, workingDirKeybinds; String workingDirVGMExport, workingDirZSMExport, workingDirROMExport, workingDirFont, workingDirColors, workingDirKeybinds;
String workingDirLayout, workingDirROM, workingDirTest; String workingDirLayout, workingDirROM, workingDirTest;
String mmlString[32]; String mmlString[32];
String mmlStringW; String mmlStringW, mmlStringSNES;
std::vector<DivSystem> sysSearchResults; std::vector<DivSystem> sysSearchResults;
std::vector<FurnaceGUISysDef> newSongSearchResults; std::vector<FurnaceGUISysDef> newSongSearchResults;
@ -1036,10 +1036,10 @@ class FurnaceGUI {
bool quit, warnQuit, willCommit, edit, modified, displayError, displayExporting, vgmExportLoop, zsmExportLoop, vgmExportPatternHints; bool quit, warnQuit, willCommit, edit, modified, displayError, displayExporting, vgmExportLoop, zsmExportLoop, vgmExportPatternHints;
bool vgmExportDirectStream; bool vgmExportDirectStream;
bool portrait, mobileMenuOpen; bool portrait, injectBackUp, mobileMenuOpen;
bool wantCaptureKeyboard, oldWantCaptureKeyboard, displayMacroMenu; bool wantCaptureKeyboard, oldWantCaptureKeyboard, displayMacroMenu;
bool displayNew, fullScreen, preserveChanPos, wantScrollList, noteInputPoly; bool displayNew, fullScreen, preserveChanPos, wantScrollList, noteInputPoly;
bool displayPendingIns, pendingInsSingle, displayPendingRawSample; bool displayPendingIns, pendingInsSingle, displayPendingRawSample, snesFilterHex;
bool willExport[32]; bool willExport[32];
int vgmExportVersion; int vgmExportVersion;
int drawHalt; int drawHalt;
@ -1212,6 +1212,10 @@ class FurnaceGUI {
int midiOutMode; int midiOutMode;
int maxRecentFile; int maxRecentFile;
int centerPattern; int centerPattern;
int ordersCursor;
int persistFadeOut;
int exportLoops;
double exportFadeOut;
unsigned int maxUndoSteps; unsigned int maxUndoSteps;
String mainFontPath; String mainFontPath;
String patFontPath; String patFontPath;
@ -1255,7 +1259,7 @@ class FurnaceGUI {
wrapVertical(0), wrapVertical(0),
macroView(0), macroView(0),
fmNames(0), fmNames(0),
allowEditDocking(0), allowEditDocking(1),
chipNames(0), chipNames(0),
overflowHighlight(0), overflowHighlight(0),
partyTime(0), partyTime(0),
@ -1338,6 +1342,10 @@ class FurnaceGUI {
midiOutMode(1), midiOutMode(1),
maxRecentFile(10), maxRecentFile(10),
centerPattern(0), centerPattern(0),
ordersCursor(1),
persistFadeOut(1),
exportLoops(0),
exportFadeOut(0.0),
maxUndoSteps(100), maxUndoSteps(100),
mainFontPath(""), mainFontPath(""),
patFontPath(""), patFontPath(""),
@ -1374,6 +1382,7 @@ class FurnaceGUI {
bool collapseWindow, demandScrollX, fancyPattern, wantPatName, firstFrame, tempoView, waveHex, waveSigned, waveGenVisible, lockLayout, editOptsVisible, latchNibble, nonLatchNibble; bool collapseWindow, demandScrollX, fancyPattern, wantPatName, firstFrame, tempoView, waveHex, waveSigned, waveGenVisible, lockLayout, editOptsVisible, latchNibble, nonLatchNibble;
bool keepLoopAlive; bool keepLoopAlive;
FurnaceGUIWindows curWindow, nextWindow, curWindowLast; FurnaceGUIWindows curWindow, nextWindow, curWindowLast;
std::atomic<FurnaceGUIWindows> curWindowThreadSafe;
float peak[2]; float peak[2];
float patChanX[DIV_MAX_CHANS+1]; float patChanX[DIV_MAX_CHANS+1];
float patChanSlideY[DIV_MAX_CHANS+1]; float patChanSlideY[DIV_MAX_CHANS+1];

View file

@ -1852,6 +1852,11 @@ void FurnaceGUI::drawMacros(std::vector<FurnaceGUIMacroDesc>& macros) {
#define CENTER_TEXT_20(text) \ #define CENTER_TEXT_20(text) \
ImGui::SetCursorPosX(ImGui::GetCursorPosX()+0.5*(20.0f*dpiScale-ImGui::CalcTextSize(text).x)); 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 \ #define OP_DRAG_POINT \
if (ImGui::Button(ICON_FA_ARROWS)) { \ if (ImGui::Button(ICON_FA_ARROWS)) { \
} \ } \
@ -2329,69 +2334,85 @@ void FurnaceGUI::drawInsEdit() {
ImGui::TableNextColumn(); ImGui::TableNextColumn();
CENTER_TEXT(FM_SHORT_NAME(FM_AR)); CENTER_TEXT(FM_SHORT_NAME(FM_AR));
ImGui::TextUnformatted(FM_SHORT_NAME(FM_AR)); ImGui::TextUnformatted(FM_SHORT_NAME(FM_AR));
TOOLTIP_TEXT(FM_NAME(FM_AR));
ImGui::TableNextColumn(); ImGui::TableNextColumn();
CENTER_TEXT(FM_SHORT_NAME(FM_DR)); CENTER_TEXT(FM_SHORT_NAME(FM_DR));
ImGui::TextUnformatted(FM_SHORT_NAME(FM_DR)); ImGui::TextUnformatted(FM_SHORT_NAME(FM_DR));
TOOLTIP_TEXT(FM_NAME(FM_DR));
if (settings.susPosition==0) { if (settings.susPosition==0) {
ImGui::TableNextColumn(); ImGui::TableNextColumn();
CENTER_TEXT(FM_SHORT_NAME(FM_SL)); CENTER_TEXT(FM_SHORT_NAME(FM_SL));
ImGui::TextUnformatted(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) { if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPM) {
ImGui::TableNextColumn(); ImGui::TableNextColumn();
CENTER_TEXT(FM_SHORT_NAME(FM_D2R)); CENTER_TEXT(FM_SHORT_NAME(FM_D2R));
ImGui::TextUnformatted(FM_SHORT_NAME(FM_D2R)); ImGui::TextUnformatted(FM_SHORT_NAME(FM_D2R));
TOOLTIP_TEXT(FM_NAME(FM_D2R));
} }
ImGui::TableNextColumn(); ImGui::TableNextColumn();
CENTER_TEXT(FM_SHORT_NAME(FM_RR)); CENTER_TEXT(FM_SHORT_NAME(FM_RR));
ImGui::TextUnformatted(FM_SHORT_NAME(FM_RR)); ImGui::TextUnformatted(FM_SHORT_NAME(FM_RR));
TOOLTIP_TEXT(FM_NAME(FM_RR));
if (settings.susPosition==1) { if (settings.susPosition==1) {
ImGui::TableNextColumn(); ImGui::TableNextColumn();
CENTER_TEXT(FM_SHORT_NAME(FM_SL)); CENTER_TEXT(FM_SHORT_NAME(FM_SL));
ImGui::TextUnformatted(FM_SHORT_NAME(FM_SL)); ImGui::TextUnformatted(FM_SHORT_NAME(FM_SL));
TOOLTIP_TEXT(FM_NAME(FM_SL));
} }
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::TableNextColumn(); ImGui::TableNextColumn();
CENTER_TEXT(FM_SHORT_NAME(FM_TL)); CENTER_TEXT(FM_SHORT_NAME(FM_TL));
ImGui::TextUnformatted(FM_SHORT_NAME(FM_TL)); ImGui::TextUnformatted(FM_SHORT_NAME(FM_TL));
TOOLTIP_TEXT(FM_NAME(FM_TL));
ImGui::TableNextColumn(); ImGui::TableNextColumn();
if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPM) { if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPM) {
CENTER_TEXT(FM_SHORT_NAME(FM_RS)); CENTER_TEXT(FM_SHORT_NAME(FM_RS));
ImGui::TextUnformatted(FM_SHORT_NAME(FM_RS)); ImGui::TextUnformatted(FM_SHORT_NAME(FM_RS));
TOOLTIP_TEXT(FM_NAME(FM_RS));
} else { } else {
CENTER_TEXT(FM_SHORT_NAME(FM_KSL)); CENTER_TEXT(FM_SHORT_NAME(FM_KSL));
ImGui::TextUnformatted(FM_SHORT_NAME(FM_KSL)); ImGui::TextUnformatted(FM_SHORT_NAME(FM_KSL));
TOOLTIP_TEXT(FM_NAME(FM_KSL));
} }
if (ins->type==DIV_INS_OPZ) { if (ins->type==DIV_INS_OPZ) {
ImGui::TableNextColumn(); ImGui::TableNextColumn();
CENTER_TEXT(FM_SHORT_NAME(FM_EGSHIFT)); CENTER_TEXT(FM_SHORT_NAME(FM_EGSHIFT));
ImGui::TextUnformatted(FM_SHORT_NAME(FM_EGSHIFT)); ImGui::TextUnformatted(FM_SHORT_NAME(FM_EGSHIFT));
TOOLTIP_TEXT(FM_NAME(FM_EGSHIFT));
ImGui::TableNextColumn(); ImGui::TableNextColumn();
CENTER_TEXT(FM_SHORT_NAME(FM_REV)); CENTER_TEXT(FM_SHORT_NAME(FM_REV));
ImGui::TextUnformatted(FM_SHORT_NAME(FM_REV)); ImGui::TextUnformatted(FM_SHORT_NAME(FM_REV));
TOOLTIP_TEXT(FM_NAME(FM_REV));
} }
ImGui::TableNextColumn(); ImGui::TableNextColumn();
CENTER_TEXT(FM_SHORT_NAME(FM_MULT)); CENTER_TEXT(FM_SHORT_NAME(FM_MULT));
ImGui::TextUnformatted(FM_SHORT_NAME(FM_MULT)); ImGui::TextUnformatted(FM_SHORT_NAME(FM_MULT));
TOOLTIP_TEXT(FM_NAME(FM_MULT));
if (ins->type==DIV_INS_OPZ) { if (ins->type==DIV_INS_OPZ) {
ImGui::TableNextColumn(); ImGui::TableNextColumn();
CENTER_TEXT(FM_SHORT_NAME(FM_FINE)); CENTER_TEXT(FM_SHORT_NAME(FM_FINE));
ImGui::TextUnformatted(FM_SHORT_NAME(FM_FINE)); ImGui::TextUnformatted(FM_SHORT_NAME(FM_FINE));
TOOLTIP_TEXT(FM_NAME(FM_FINE));
} }
ImGui::TableNextColumn(); ImGui::TableNextColumn();
if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPM) { if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPM) {
CENTER_TEXT(FM_SHORT_NAME(FM_DT)); CENTER_TEXT(FM_SHORT_NAME(FM_DT));
ImGui::TextUnformatted(FM_SHORT_NAME(FM_DT)); ImGui::TextUnformatted(FM_SHORT_NAME(FM_DT));
TOOLTIP_TEXT(FM_NAME(FM_DT));
ImGui::TableNextColumn(); ImGui::TableNextColumn();
} }
if (ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPM) { if (ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPM) {
CENTER_TEXT(FM_SHORT_NAME(FM_DT2)); CENTER_TEXT(FM_SHORT_NAME(FM_DT2));
ImGui::TextUnformatted(FM_SHORT_NAME(FM_DT2)); ImGui::TextUnformatted(FM_SHORT_NAME(FM_DT2));
TOOLTIP_TEXT(FM_NAME(FM_DT2));
ImGui::TableNextColumn(); ImGui::TableNextColumn();
} }
if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPM) { if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPM) {
CENTER_TEXT(FM_SHORT_NAME(FM_AM)); CENTER_TEXT(FM_SHORT_NAME(FM_AM));
ImGui::TextUnformatted(FM_SHORT_NAME(FM_AM)); ImGui::TextUnformatted(FM_SHORT_NAME(FM_AM));
TOOLTIP_TEXT(FM_NAME(FM_AM));
} else { } else {
CENTER_TEXT("Other"); CENTER_TEXT("Other");
ImGui::TextUnformatted("Other"); ImGui::TextUnformatted("Other");
@ -2703,7 +2724,7 @@ void FurnaceGUI::drawInsEdit() {
char tempID[1024]; char tempID[1024];
ImVec2 oldPadding=ImGui::GetStyle().CellPadding; ImVec2 oldPadding=ImGui::GetStyle().CellPadding;
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding,ImVec2(8.0f*dpiScale,4.0f*dpiScale)); 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++) { for (int i=0; i<opCount; i++) {
DivInstrumentFM::Operator& op=ins->fm.op[(opCount==4 && ins->type!=DIV_INS_OPL_DRUMS)?opOrder[i]: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(); 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(); float textY=ImGui::GetCursorPosY();
CENTER_TEXT_20(FM_SHORT_NAME(FM_AR)); CENTER_TEXT_20(FM_SHORT_NAME(FM_AR));
ImGui::TextUnformatted(FM_SHORT_NAME(FM_AR)); ImGui::TextUnformatted(FM_SHORT_NAME(FM_AR));
TOOLTIP_TEXT(FM_NAME(FM_AR));
ImGui::TableNextColumn(); ImGui::TableNextColumn();
if (ins->type==DIV_INS_FM) { if (ins->type==DIV_INS_FM) {
ImGui::Text("SSG-EG"); ImGui::Text("SSG-EG");
@ -2814,7 +2836,8 @@ void FurnaceGUI::drawInsEdit() {
ImGui::Text("Envelope"); ImGui::Text("Envelope");
ImGui::TableNextColumn(); ImGui::TableNextColumn();
CENTER_TEXT(FM_SHORT_NAME(FM_TL)); 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 // A/D/S/R
ImGui::TableNextColumn(); ImGui::TableNextColumn();
@ -2861,19 +2884,23 @@ void FurnaceGUI::drawInsEdit() {
ImGui::SetCursorPos(ImVec2(textX_DR,textY)); ImGui::SetCursorPos(ImVec2(textX_DR,textY));
CENTER_TEXT_20(FM_SHORT_NAME(FM_DR)); CENTER_TEXT_20(FM_SHORT_NAME(FM_DR));
ImGui::TextUnformatted(FM_SHORT_NAME(FM_DR)); ImGui::TextUnformatted(FM_SHORT_NAME(FM_DR));
TOOLTIP_TEXT(FM_NAME(FM_DR));
ImGui::SetCursorPos(ImVec2(textX_SL,textY)); ImGui::SetCursorPos(ImVec2(textX_SL,textY));
CENTER_TEXT_20(FM_SHORT_NAME(FM_SL)); CENTER_TEXT_20(FM_SHORT_NAME(FM_SL));
ImGui::TextUnformatted(FM_SHORT_NAME(FM_SL)); ImGui::TextUnformatted(FM_SHORT_NAME(FM_SL));
TOOLTIP_TEXT(FM_NAME(FM_SL));
ImGui::SetCursorPos(ImVec2(textX_RR,textY)); ImGui::SetCursorPos(ImVec2(textX_RR,textY));
CENTER_TEXT_20(FM_SHORT_NAME(FM_RR)); CENTER_TEXT_20(FM_SHORT_NAME(FM_RR));
ImGui::TextUnformatted(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) { if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPM) {
ImGui::SetCursorPos(ImVec2(textX_D2R,textY)); ImGui::SetCursorPos(ImVec2(textX_D2R,textY));
CENTER_TEXT_20(FM_SHORT_NAME(FM_D2R)); CENTER_TEXT_20(FM_SHORT_NAME(FM_D2R));
ImGui::TextUnformatted(FM_SHORT_NAME(FM_D2R)); ImGui::TextUnformatted(FM_SHORT_NAME(FM_D2R));
TOOLTIP_TEXT(FM_NAME(FM_D2R));
} }
ImGui::SetCursorPos(prevCurPos); ImGui::SetCursorPos(prevCurPos);
@ -3141,6 +3168,7 @@ void FurnaceGUI::drawInsEdit() {
if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPM) { if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPM) {
CENTER_TEXT(FM_SHORT_NAME(FM_AM)); CENTER_TEXT(FM_SHORT_NAME(FM_AM));
ImGui::TextUnformatted(FM_SHORT_NAME(FM_AM)); ImGui::TextUnformatted(FM_SHORT_NAME(FM_AM));
TOOLTIP_TEXT(FM_NAME(FM_AM));
bool amOn=op.am; bool amOn=op.am;
if (ImGui::Checkbox("##AM",&amOn)) { PARAMETER if (ImGui::Checkbox("##AM",&amOn)) { PARAMETER
op.am=amOn; op.am=amOn;
@ -4475,6 +4503,50 @@ void FurnaceGUI::drawInsEdit() {
ins->type==DIV_INS_SNES || ins->type==DIV_INS_SNES ||
ins->type==DIV_INS_NAMCO) { ins->type==DIV_INS_NAMCO) {
if (ImGui::BeginTabItem("Wavetable")) { 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)) { if (ImGui::Checkbox("Enable synthesizer",&ins->ws.enabled)) {
wavePreviewInit=true; wavePreviewInit=true;
} }
@ -4512,7 +4584,8 @@ void FurnaceGUI::drawInsEdit() {
ImGui::Unindent(); ImGui::Unindent();
ImGui::EndCombo(); 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* wave1=e->getWave(ins->ws.wave1);
DivWavetable* wave2=e->getWave(ins->ws.wave2); DivWavetable* wave2=e->getWave(ins->ws.wave2);
if (wavePreviewInit) { if (wavePreviewInit) {
@ -4545,15 +4618,19 @@ void FurnaceGUI::drawInsEdit() {
} }
} }
float ySize=(isSingleWaveFX?96.0f:128.0f)*dpiScale;
ImGui::TableNextRow(); ImGui::TableNextRow();
ImGui::TableNextColumn(); 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); 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(); ImGui::TableNextColumn();
ImVec2 size2=ImVec2(ImGui::GetContentRegionAvail().x,64.0f*dpiScale); ImVec2 size3=ImVec2(ImGui::GetContentRegionAvail().x,ySize);
PlotNoLerp("##WaveformP2",wavePreview2,wave2->len+1,0,"Wave 2",0,wave2->max,size2);
ImGui::TableNextColumn();
ImVec2 size3=ImVec2(ImGui::GetContentRegionAvail().x,64.0f*dpiScale);
PlotNoLerp("##WaveformP3",wavePreview3,wavePreviewLen,0,"Result",0,wavePreviewHeight,size3); PlotNoLerp("##WaveformP3",wavePreview3,wavePreviewLen,0,"Result",0,wavePreviewHeight,size3);
ImGui::TableNextRow(); 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; if (ins->ws.wave1>=(int)e->song.wave.size()) ins->ws.wave1=e->song.wave.size()-1;
wavePreviewInit=true; wavePreviewInit=true;
} }
ImGui::TableNextColumn(); if (isSingleWaveFX) {
ImGui::Text("Wave 2"); ImGui::TableNextColumn();
ImGui::SameLine(); ImGui::Text("Wave 2");
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); ImGui::SameLine();
if (ImGui::InputInt("##SelWave2",&ins->ws.wave2,1,4)) { ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
if (ins->ws.wave2<0) ins->ws.wave2=0; if (ImGui::InputInt("##SelWave2",&ins->ws.wave2,1,4)) {
if (ins->ws.wave2>=(int)e->song.wave.size()) ins->ws.wave2=e->song.wave.size()-1; if (ins->ws.wave2<0) ins->ws.wave2=0;
wavePreviewInit=true; if (ins->ws.wave2>=(int)e->song.wave.size()) ins->ws.wave2=e->song.wave.size()-1;
wavePreviewInit=true;
}
} }
ImGui::TableNextColumn(); ImGui::TableNextColumn();
if (ImGui::Button(ICON_FA_REPEAT "##WSRestart")) { if (ImGui::Button(ICON_FA_REPEAT "##WSRestart")) {
wavePreviewInit=true; wavePreviewInit=true;
} }
ImGui::SameLine(); ImGui::SameLine();
ImGui::Text("Preview Width"); ImGui::Text("(%d×%d)",wavePreviewLen,wavePreviewHeight+1);
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::EndTable(); ImGui::EndTable();
} }

View file

@ -29,6 +29,14 @@ void FurnaceGUI::drawOrders() {
nextWindow=GUI_WINDOW_NOTHING; nextWindow=GUI_WINDOW_NOTHING;
} }
if (!ordersOpen) return; 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)) { if (ImGui::Begin("Orders",&ordersOpen,globalWinFlags)) {
float regionX=ImGui::GetContentRegionAvail().x; float regionX=ImGui::GetContentRegionAvail().x;
ImVec2 prevSpacing=ImGui::GetStyle().ItemSpacing; 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]); 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 (curOrder==i) {
if (orderEditMode==0) { if (orderEditMode==0) {
prepareUndo(GUI_UNDO_CHANGE_ORDER); prepareUndo(GUI_UNDO_CHANGE_ORDER);
@ -127,6 +135,11 @@ void FurnaceGUI::drawOrders() {
} }
} }
ImGui::PopStyleColor(); 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()) { if (!pat->name.empty() && ImGui::IsItemHovered()) {
ImGui::SetTooltip("%s",pat->name.c_str()); ImGui::SetTooltip("%s",pat->name.c_str());
} }

View file

@ -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 (ImGui::Selectable((extraChannelButtons==2)?" --##ExtraChannelButtons":" ++##ExtraChannelButtons",false,ImGuiSelectableFlags_NoPadWithHalfSpacing,ImVec2(0.0f,lineHeight+1.0f*dpiScale))) {
if (++extraChannelButtons>2) extraChannelButtons=0; if (++extraChannelButtons>2) extraChannelButtons=0;
} }
if (ImGui::IsItemHovered()) { if (ImGui::IsItemHovered() && !mobileUI) {
if (extraChannelButtons==2) { if (extraChannelButtons==2) {
ImGui::SetTooltip("Pattern names (click to collapse)\nRight-click for visualizer"); ImGui::SetTooltip("Pattern names (click to collapse)\nRight-click for visualizer");
} else if (extraChannelButtons==1) { } else if (extraChannelButtons==1) {
@ -706,7 +706,7 @@ void FurnaceGUI::drawPattern() {
if (extraChannelButtons==0 || settings.channelVolStyle!=0) ImGui::PopStyleVar(); if (extraChannelButtons==0 || settings.channelVolStyle!=0) ImGui::PopStyleVar();
if (displayTooltip && ImGui::IsItemHovered()) { if (displayTooltip && ImGui::IsItemHovered() && !mobileUI) {
ImGui::SetTooltip("%s",e->getChannelName(i)); ImGui::SetTooltip("%s",e->getChannelName(i));
} }
if (settings.channelFont==0) ImGui::PopFont(); if (settings.channelFont==0) ImGui::PopFont();

View file

@ -405,7 +405,7 @@ void FurnaceGUI::drawPiano() {
break; break;
case GUI_WINDOW_SAMPLE_LIST: case GUI_WINDOW_SAMPLE_LIST:
case GUI_WINDOW_SAMPLE_EDIT: case GUI_WINDOW_SAMPLE_EDIT:
e->previewSample(curWave,note); e->previewSample(curSample,note);
break; break;
default: default:
e->synchronized([this,note]() { e->synchronized([this,note]() {

View file

@ -196,11 +196,20 @@ double getScaleFactor(const char* driverHint) {
#endif #endif
// SDL fallback // 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; float dpiScaleF=96.0f;
if (SDL_GetDisplayDPI(0,&dpiScaleF,NULL,NULL)==0) { if (SDL_GetDisplayDPI(0,&dpiScaleF,NULL,NULL)==0) {
ret=round(dpiScaleF/96.0f); ret=round(dpiScaleF/96.0f);
if (ret<1) ret=1; if (ret<1) ret=1;
} }
#endif
// couldn't detect scaling factor :< // couldn't detect scaling factor :<
return ret; return ret;

View file

@ -40,7 +40,7 @@
#define POWER_SAVE_DEFAULT 0 #define POWER_SAVE_DEFAULT 0
#endif #endif
#ifdef __HAIKU__ #if defined(__HAIKU__)
// NFD doesn't support Haiku // NFD doesn't support Haiku
#define SYS_FILE_DIALOG_DEFAULT 0 #define SYS_FILE_DIALOG_DEFAULT 0
#else #else
@ -548,6 +548,24 @@ void FurnaceGUI::drawSettings() {
settings.saveUnusedPatterns=saveUnusedPatternsB; 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:"); ImGui::Text("Note preview behavior:");
if (ImGui::RadioButton("Never##npb0",settings.notePreviewBehavior==0)) { if (ImGui::RadioButton("Never##npb0",settings.notePreviewBehavior==0)) {
settings.notePreviewBehavior=0; settings.notePreviewBehavior=0;
@ -1459,6 +1477,11 @@ void FurnaceGUI::drawSettings() {
settings.sysSeparators=sysSeparatorsB; settings.sysSeparators=sysSeparatorsB;
}*/ }*/
bool ordersCursorB=settings.ordersCursor;
if (ImGui::Checkbox("Highlight channel at cursor in Orders",&ordersCursorB)) {
settings.ordersCursor=ordersCursorB;
}
bool partyTimeB=settings.partyTime; bool partyTimeB=settings.partyTime;
if (ImGui::Checkbox("About screen party time",&partyTimeB)) { if (ImGui::Checkbox("About screen party time",&partyTimeB)) {
settings.partyTime=partyTimeB; settings.partyTime=partyTimeB;
@ -2280,7 +2303,7 @@ void FurnaceGUI::syncSettings() {
settings.wrapVertical=e->getConfInt("wrapVertical",0); settings.wrapVertical=e->getConfInt("wrapVertical",0);
settings.macroView=e->getConfInt("macroView",0); settings.macroView=e->getConfInt("macroView",0);
settings.fmNames=e->getConfInt("fmNames",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.chipNames=e->getConfInt("chipNames",0);
settings.overflowHighlight=e->getConfInt("overflowHighlight",0); settings.overflowHighlight=e->getConfInt("overflowHighlight",0);
settings.partyTime=e->getConfInt("partyTime",0); settings.partyTime=e->getConfInt("partyTime",0);
@ -2370,6 +2393,10 @@ void FurnaceGUI::syncSettings() {
settings.midiOutClock=e->getConfInt("midiOutClock",0); settings.midiOutClock=e->getConfInt("midiOutClock",0);
settings.midiOutMode=e->getConfInt("midiOutMode",1); settings.midiOutMode=e->getConfInt("midiOutMode",1);
settings.centerPattern=e->getConfInt("centerPattern",0); 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.mainFontSize,2,96);
clampSetting(settings.patFontSize,2,96); clampSetting(settings.patFontSize,2,96);
@ -2474,6 +2501,11 @@ void FurnaceGUI::syncSettings() {
clampSetting(settings.midiOutClock,0,1); clampSetting(settings.midiOutClock,0,1);
clampSetting(settings.midiOutMode,0,2); clampSetting(settings.midiOutMode,0,2);
clampSetting(settings.centerPattern,0,1); 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",""); String initialSys2=e->getConfString("initialSys2","");
if (initialSys2.empty()) { if (initialSys2.empty()) {
@ -2639,6 +2671,10 @@ void FurnaceGUI::commitSettings() {
e->setConf("midiOutClock",settings.midiOutClock); e->setConf("midiOutClock",settings.midiOutClock);
e->setConf("midiOutMode",settings.midiOutMode); e->setConf("midiOutMode",settings.midiOutMode);
e->setConf("centerPattern",settings.centerPattern); 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 // colors
for (int i=0; i<GUI_COLOR_MAX; i++) { for (int i=0; i<GUI_COLOR_MAX; i++) {

View file

@ -18,6 +18,7 @@
*/ */
#include "gui.h" #include "gui.h"
#include "misc/cpp/imgui_stdlib.h"
#include <imgui.h> #include <imgui.h>
bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool modifyOnChange) { 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 } 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) { if (altered) {
e->lockSave([&]() { e->lockSave([&]() {
flags.set("volScaleL",127-vsL); flags.set("volScaleL",127-vsL);

View file

@ -32,6 +32,15 @@
#endif #endif
String getHomeDir() { String getHomeDir() {
#ifdef IS_MOBILE
#ifdef ANDROID
return "/storage/emulated/0/";
#else
return "/";
#endif
#else
String ret; String ret;
char tempDir[4096]; char tempDir[4096];
@ -73,6 +82,7 @@ String getHomeDir() {
} }
return ret; return ret;
#endif
} }
String getKeyName(int key, bool emptyNone) { String getKeyName(int key, bool emptyNone) {
@ -101,4 +111,4 @@ String getKeyName(int key, bool emptyNone) {
ret+=name; ret+=name;
} }
return ret; return ret;
} }

View file

@ -317,6 +317,13 @@ void reportError(String what) {
logE("%s",what); logE("%s",what);
MessageBox(NULL,what.c_str(),"Furnace",MB_OK|MB_ICONERROR); 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 #else
void reportError(String what) { void reportError(String what) {
logE("%s",what); logE("%s",what);