add experimental command stream player
for verification after that I am going to write optimization code
This commit is contained in:
parent
24c39c7819
commit
c4510e16e0
|
@ -469,6 +469,7 @@ src/engine/blip_buf.c
|
|||
src/engine/brrUtils.c
|
||||
src/engine/safeReader.cpp
|
||||
src/engine/safeWriter.cpp
|
||||
src/engine/cmdStream.cpp
|
||||
src/engine/config.cpp
|
||||
src/engine/configEngine.cpp
|
||||
src/engine/dispatchContainer.cpp
|
||||
|
|
301
src/engine/cmdStream.cpp
Normal file
301
src/engine/cmdStream.cpp
Normal file
|
@ -0,0 +1,301 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "cmdStream.h"
|
||||
#include "engine.h"
|
||||
#include "../ta-log.h"
|
||||
|
||||
void DivCSPlayer::cleanup() {
|
||||
delete b;
|
||||
}
|
||||
|
||||
bool DivCSPlayer::tick() {
|
||||
bool ticked=false;
|
||||
for (int i=0; i<e->getTotalChannelCount(); i++) {
|
||||
bool sendVolume=false;
|
||||
if (chan[i].readPos==0) continue;
|
||||
|
||||
ticked=true;
|
||||
|
||||
chan[i].waitTicks--;
|
||||
while (chan[i].waitTicks<=0) {
|
||||
stream.seek(chan[i].readPos,SEEK_SET);
|
||||
unsigned char next=stream.readC();
|
||||
unsigned char command=0;
|
||||
|
||||
if (next<0xb3) { // note
|
||||
e->dispatchCmd(DivCommand(DIV_CMD_NOTE_ON,i,next-60));
|
||||
} else if (next>=0xd0 && next<=0xdf) {
|
||||
command=fastCmds[next&15];
|
||||
} else if (next>=0xe0 && next<=0xef) { // preset delay
|
||||
chan[i].waitTicks=fastDelays[next&15];
|
||||
} else switch (next) {
|
||||
case 0xb4: // note on null
|
||||
e->dispatchCmd(DivCommand(DIV_CMD_NOTE_ON,i,DIV_NOTE_NULL));
|
||||
break;
|
||||
case 0xb5: // note off
|
||||
e->dispatchCmd(DivCommand(DIV_CMD_NOTE_OFF,i));
|
||||
break;
|
||||
case 0xb6: // note off env
|
||||
e->dispatchCmd(DivCommand(DIV_CMD_NOTE_OFF_ENV,i));
|
||||
break;
|
||||
case 0xb7: // env release
|
||||
e->dispatchCmd(DivCommand(DIV_CMD_ENV_RELEASE,i));
|
||||
break;
|
||||
case 0xb8: case 0xbe: case 0xc0: case 0xc2:
|
||||
case 0xc3: case 0xc4: case 0xc5: case 0xc6:
|
||||
case 0xc7: case 0xc8: case 0xc9: case 0xca:
|
||||
command=next-0xb4;
|
||||
break;
|
||||
case 0xf7:
|
||||
command=stream.readC();
|
||||
break;
|
||||
case 0xf8:
|
||||
logE("TODO: CALL");
|
||||
break;
|
||||
case 0xf9:
|
||||
logE("TODO: RET");
|
||||
break;
|
||||
case 0xfa:
|
||||
logE("TODO: JMP");
|
||||
break;
|
||||
case 0xfb:
|
||||
logE("TODO: RATE");
|
||||
break;
|
||||
case 0xfc:
|
||||
chan[i].waitTicks=(unsigned short)stream.readS();
|
||||
break;
|
||||
case 0xfd:
|
||||
chan[i].waitTicks=(unsigned char)stream.readC();
|
||||
break;
|
||||
case 0xfe:
|
||||
chan[i].waitTicks=1;
|
||||
break;
|
||||
case 0xff:
|
||||
chan[i].readPos=0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (chan[i].readPos==0) break;
|
||||
|
||||
if (command) {
|
||||
int arg0=0;
|
||||
int arg1=0;
|
||||
switch (command) {
|
||||
case DIV_CMD_INSTRUMENT:
|
||||
case DIV_CMD_HINT_VIBRATO_RANGE:
|
||||
case DIV_CMD_HINT_VIBRATO_SHAPE:
|
||||
case DIV_CMD_HINT_PITCH:
|
||||
case DIV_CMD_HINT_VOLUME:
|
||||
arg0=(unsigned char)stream.readC();
|
||||
break;
|
||||
case DIV_CMD_PANNING:
|
||||
case DIV_CMD_HINT_VIBRATO:
|
||||
case DIV_CMD_HINT_ARPEGGIO:
|
||||
case DIV_CMD_HINT_PORTA:
|
||||
arg0=(unsigned char)stream.readC();
|
||||
arg1=(unsigned char)stream.readC();
|
||||
break;
|
||||
case DIV_CMD_PRE_PORTA:
|
||||
arg0=(unsigned char)stream.readC();
|
||||
arg1=(arg0&0x40)?1:0;
|
||||
arg0=(arg0&0x80)?1:0;
|
||||
break;
|
||||
case DIV_CMD_HINT_VOL_SLIDE:
|
||||
arg0=(short)stream.readS();
|
||||
break;
|
||||
case DIV_CMD_SAMPLE_MODE:
|
||||
case DIV_CMD_SAMPLE_FREQ:
|
||||
case DIV_CMD_SAMPLE_BANK:
|
||||
case DIV_CMD_SAMPLE_POS:
|
||||
case DIV_CMD_SAMPLE_DIR:
|
||||
case DIV_CMD_FM_HARD_RESET:
|
||||
case DIV_CMD_FM_LFO:
|
||||
case DIV_CMD_FM_LFO_WAVE:
|
||||
case DIV_CMD_FM_FB:
|
||||
case DIV_CMD_FM_EXTCH:
|
||||
case DIV_CMD_FM_AM_DEPTH:
|
||||
case DIV_CMD_FM_PM_DEPTH:
|
||||
case DIV_CMD_STD_NOISE_FREQ:
|
||||
case DIV_CMD_STD_NOISE_MODE:
|
||||
case DIV_CMD_WAVE:
|
||||
case DIV_CMD_GB_SWEEP_TIME:
|
||||
case DIV_CMD_GB_SWEEP_DIR:
|
||||
case DIV_CMD_PCE_LFO_MODE:
|
||||
case DIV_CMD_PCE_LFO_SPEED:
|
||||
case DIV_CMD_NES_DMC:
|
||||
case DIV_CMD_C64_CUTOFF:
|
||||
case DIV_CMD_C64_RESONANCE:
|
||||
case DIV_CMD_C64_FILTER_MODE:
|
||||
case DIV_CMD_C64_RESET_TIME:
|
||||
case DIV_CMD_C64_RESET_MASK:
|
||||
case DIV_CMD_C64_FILTER_RESET:
|
||||
case DIV_CMD_C64_DUTY_RESET:
|
||||
case DIV_CMD_C64_EXTENDED:
|
||||
case DIV_CMD_AY_ENVELOPE_SET:
|
||||
case DIV_CMD_AY_ENVELOPE_LOW:
|
||||
case DIV_CMD_AY_ENVELOPE_HIGH:
|
||||
case DIV_CMD_AY_ENVELOPE_SLIDE:
|
||||
case DIV_CMD_AY_NOISE_MASK_AND:
|
||||
case DIV_CMD_AY_NOISE_MASK_OR:
|
||||
case DIV_CMD_AY_AUTO_ENVELOPE:
|
||||
case DIV_CMD_FDS_MOD_DEPTH:
|
||||
case DIV_CMD_FDS_MOD_HIGH:
|
||||
case DIV_CMD_FDS_MOD_LOW:
|
||||
case DIV_CMD_FDS_MOD_POS:
|
||||
case DIV_CMD_FDS_MOD_WAVE:
|
||||
case DIV_CMD_SAA_ENVELOPE:
|
||||
case DIV_CMD_AMIGA_FILTER:
|
||||
case DIV_CMD_AMIGA_AM:
|
||||
case DIV_CMD_AMIGA_PM:
|
||||
case DIV_CMD_MACRO_OFF:
|
||||
case DIV_CMD_MACRO_ON:
|
||||
arg0=(unsigned char)stream.readC();
|
||||
break;
|
||||
case DIV_CMD_FM_TL:
|
||||
case DIV_CMD_FM_AM:
|
||||
case DIV_CMD_FM_AR:
|
||||
case DIV_CMD_FM_DR:
|
||||
case DIV_CMD_FM_SL:
|
||||
case DIV_CMD_FM_D2R:
|
||||
case DIV_CMD_FM_RR:
|
||||
case DIV_CMD_FM_DT:
|
||||
case DIV_CMD_FM_DT2:
|
||||
case DIV_CMD_FM_RS:
|
||||
case DIV_CMD_FM_KSR:
|
||||
case DIV_CMD_FM_VIB:
|
||||
case DIV_CMD_FM_SUS:
|
||||
case DIV_CMD_FM_WS:
|
||||
case DIV_CMD_FM_SSG:
|
||||
case DIV_CMD_FM_REV:
|
||||
case DIV_CMD_FM_EG_SHIFT:
|
||||
case DIV_CMD_FM_MULT:
|
||||
case DIV_CMD_FM_FINE:
|
||||
case DIV_CMD_AY_IO_WRITE:
|
||||
case DIV_CMD_AY_AUTO_PWM:
|
||||
case DIV_CMD_SURROUND_PANNING:
|
||||
arg0=(unsigned char)stream.readC();
|
||||
arg1=(unsigned char)stream.readC();
|
||||
break;
|
||||
case DIV_CMD_C64_FINE_DUTY:
|
||||
case DIV_CMD_C64_FINE_CUTOFF:
|
||||
case DIV_CMD_LYNX_LFSR_LOAD:
|
||||
arg0=(unsigned short)stream.readS();
|
||||
break;
|
||||
case DIV_CMD_FM_FIXFREQ:
|
||||
arg0=(unsigned short)stream.readS();
|
||||
arg1=arg0&0x7ff;
|
||||
arg0>>=12;
|
||||
break;
|
||||
case DIV_CMD_NES_SWEEP:
|
||||
arg0=(unsigned char)stream.readC();
|
||||
arg1=arg0&0x77;
|
||||
arg0=(arg0&8)?1:0;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (command) {
|
||||
case DIV_CMD_HINT_VOLUME:
|
||||
chan[i].volume=arg0<<8;
|
||||
sendVolume=true;
|
||||
break;
|
||||
case DIV_CMD_HINT_VOL_SLIDE:
|
||||
chan[i].volSpeed=arg0;
|
||||
break;
|
||||
default: // dispatch it
|
||||
e->dispatchCmd(DivCommand((DivDispatchCmds)command,i,arg0,arg1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
chan[i].readPos=stream.tell();
|
||||
}
|
||||
|
||||
if (sendVolume || chan[i].volSpeed!=0) {
|
||||
chan[i].volume+=chan[i].volSpeed;
|
||||
if (chan[i].volume<0) {
|
||||
chan[i].volume=0;
|
||||
}
|
||||
if (chan[i].volume>chan[i].volMax) {
|
||||
chan[i].volume=chan[i].volMax;
|
||||
}
|
||||
|
||||
e->dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8));
|
||||
}
|
||||
}
|
||||
|
||||
return ticked;
|
||||
}
|
||||
|
||||
bool DivCSPlayer::init() {
|
||||
unsigned char magic[4];
|
||||
stream.seek(0,SEEK_SET);
|
||||
stream.read(magic,4);
|
||||
|
||||
if (memcmp(magic,"FCS",4)!=0) return false;
|
||||
|
||||
unsigned int chans=stream.readI();
|
||||
|
||||
for (unsigned int i=0; i<chans; i++) {
|
||||
if (i>=DIV_MAX_CHANS) {
|
||||
stream.readI();
|
||||
continue;
|
||||
}
|
||||
if ((int)i>=e->getTotalChannelCount()) {
|
||||
stream.readI();
|
||||
continue;
|
||||
}
|
||||
chan[i].readPos=stream.readI();
|
||||
}
|
||||
|
||||
stream.read(fastDelays,16);
|
||||
stream.read(fastCmds,16);
|
||||
|
||||
// initialize state
|
||||
for (int i=0; i<e->getTotalChannelCount(); i++) {
|
||||
chan[i].volMax=(e->getDispatch(e->dispatchOfChan[i])->dispatch(DivCommand(DIV_CMD_GET_VOLMAX,e->dispatchChanOfChan[i]))<<8)|0xff;
|
||||
chan[i].volume=chan[i].volMax;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// DivEngine
|
||||
|
||||
bool DivEngine::playStream(unsigned char* f, size_t length) {
|
||||
BUSY_BEGIN;
|
||||
cmdStreamInt=new DivCSPlayer(this,f,length);
|
||||
if (!cmdStreamInt->init()) {
|
||||
logE("not a command stream!");
|
||||
lastError="not a command stream";
|
||||
delete[] f;
|
||||
delete cmdStreamInt;
|
||||
cmdStreamInt=NULL;
|
||||
BUSY_END;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!playing) {
|
||||
reset();
|
||||
freelance=true;
|
||||
playing=true;
|
||||
}
|
||||
BUSY_END;
|
||||
return true;
|
||||
}
|
59
src/engine/cmdStream.h
Normal file
59
src/engine/cmdStream.h
Normal file
|
@ -0,0 +1,59 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef _CMD_STREAM_H
|
||||
#define _CMD_STREAM_H
|
||||
|
||||
#include "defines.h"
|
||||
#include "safeReader.h"
|
||||
|
||||
class DivEngine;
|
||||
|
||||
struct DivCSChannelState {
|
||||
unsigned int readPos;
|
||||
int waitTicks;
|
||||
|
||||
int volume, volMax, volSpeed;
|
||||
|
||||
DivCSChannelState():
|
||||
readPos(0),
|
||||
waitTicks(0),
|
||||
volume(0x7f00),
|
||||
volMax(0),
|
||||
volSpeed(0) {}
|
||||
};
|
||||
|
||||
class DivCSPlayer {
|
||||
DivEngine* e;
|
||||
unsigned char* b;
|
||||
SafeReader stream;
|
||||
DivCSChannelState chan[DIV_MAX_CHANS];
|
||||
unsigned char fastDelays[16];
|
||||
unsigned char fastCmds[16];
|
||||
public:
|
||||
void cleanup();
|
||||
bool tick();
|
||||
bool init();
|
||||
DivCSPlayer(DivEngine* en, unsigned char* buf, size_t len):
|
||||
e(en),
|
||||
b(buf),
|
||||
stream(buf,len) {}
|
||||
};
|
||||
|
||||
#endif
|
|
@ -26,6 +26,7 @@
|
|||
#include "export.h"
|
||||
#include "dataErrors.h"
|
||||
#include "safeWriter.h"
|
||||
#include "cmdStream.h"
|
||||
#include "../audio/taAudio.h"
|
||||
#include "blip_buf.h"
|
||||
#include <atomic>
|
||||
|
@ -405,6 +406,8 @@ class DivEngine {
|
|||
static DivSystem sysFileMapFur[DIV_MAX_CHIP_DEFS];
|
||||
static DivSystem sysFileMapDMF[DIV_MAX_CHIP_DEFS];
|
||||
|
||||
DivCSPlayer* cmdStreamInt;
|
||||
|
||||
struct SamplePreview {
|
||||
double rate;
|
||||
int sample;
|
||||
|
@ -449,7 +452,6 @@ class DivEngine {
|
|||
// MIDI stuff
|
||||
std::function<int(const TAMidiMessage&)> midiCallback=[](const TAMidiMessage&) -> int {return -2;};
|
||||
|
||||
int dispatchCmd(DivCommand c);
|
||||
void processRow(int i, bool afterDelay);
|
||||
void nextOrder();
|
||||
void nextRow();
|
||||
|
@ -537,6 +539,8 @@ class DivEngine {
|
|||
void createNewFromDefaults();
|
||||
// load a file.
|
||||
bool load(unsigned char* f, size_t length);
|
||||
// play a binary command stream.
|
||||
bool playStream(unsigned char* f, size_t length);
|
||||
// save as .dmf.
|
||||
SafeWriter* saveDMF(unsigned char version);
|
||||
// save as .fur.
|
||||
|
@ -567,6 +571,9 @@ class DivEngine {
|
|||
// notify wavetable change
|
||||
void notifyWaveChange(int wave);
|
||||
|
||||
// dispatch a command
|
||||
int dispatchCmd(DivCommand c);
|
||||
|
||||
// get system IDs
|
||||
static DivSystem systemFromFileFur(unsigned char val);
|
||||
static unsigned char systemToFileFur(DivSystem val);
|
||||
|
@ -1152,6 +1159,7 @@ class DivEngine {
|
|||
audioEngine(DIV_AUDIO_NULL),
|
||||
exportMode(DIV_EXPORT_MODE_ONE),
|
||||
exportFadeOut(0.0),
|
||||
cmdStreamInt(NULL),
|
||||
midiBaseChan(0),
|
||||
midiPoly(true),
|
||||
midiAgeCounter(0),
|
||||
|
|
|
@ -1385,6 +1385,15 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
|
|||
}
|
||||
}
|
||||
|
||||
if (subticks==tickMult && cmdStreamInt) {
|
||||
if (!cmdStreamInt->tick()) {
|
||||
cmdStreamInt->cleanup();
|
||||
delete cmdStreamInt;
|
||||
cmdStreamInt=NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
firstTick=false;
|
||||
|
||||
if (shallStop) {
|
||||
|
|
|
@ -58,6 +58,8 @@ void FurnaceGUI::drawDebug() {
|
|||
ImGui::SameLine();
|
||||
if (ImGui::Button("Pattern Advance")) e->haltWhen(DIV_HALT_PATTERN);
|
||||
|
||||
if (ImGui::Button("Play Command Stream")) openFileDialog(GUI_FILE_CMDSTREAM_OPEN);
|
||||
|
||||
if (ImGui::Button("Panic")) e->syncReset();
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Abort")) {
|
||||
|
|
|
@ -526,7 +526,21 @@ void FurnaceGUI::drawMobileControls() {
|
|||
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::BeginTabBar("MobileSong")) {
|
||||
if (ImGui::BeginTabItem("Song Info")) {
|
||||
drawSongInfo(true);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("Subsongs")) {
|
||||
drawSubSongs(true);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("Speed")) {
|
||||
drawSpeed(true);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GUI_SCENE_CHANNELS:
|
||||
|
|
|
@ -1849,6 +1849,17 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) {
|
|||
dpiScale
|
||||
);
|
||||
break;
|
||||
case GUI_FILE_CMDSTREAM_OPEN:
|
||||
if (!dirExists(workingDirROM)) workingDirROM=getHomeDir();
|
||||
hasOpened=fileDialog->openLoad(
|
||||
"Play Command Stream",
|
||||
{"command stream", "*.bin",
|
||||
"all files", "*"},
|
||||
"command stream{.bin},.*",
|
||||
workingDirROM,
|
||||
dpiScale
|
||||
);
|
||||
break;
|
||||
case GUI_FILE_TEST_OPEN:
|
||||
if (!dirExists(workingDirTest)) workingDirTest=getHomeDir();
|
||||
hasOpened=fileDialog->openLoad(
|
||||
|
@ -2103,6 +2114,64 @@ void FurnaceGUI::pushRecentFile(String path) {
|
|||
}
|
||||
}
|
||||
|
||||
int FurnaceGUI::loadStream(String path) {
|
||||
if (!path.empty()) {
|
||||
logI("loading stream...");
|
||||
FILE* f=ps_fopen(path.c_str(),"rb");
|
||||
if (f==NULL) {
|
||||
perror("error");
|
||||
lastError=strerror(errno);
|
||||
return 1;
|
||||
}
|
||||
if (fseek(f,0,SEEK_END)<0) {
|
||||
perror("size error");
|
||||
lastError=fmt::sprintf("on seek: %s",strerror(errno));
|
||||
fclose(f);
|
||||
return 1;
|
||||
}
|
||||
ssize_t len=ftell(f);
|
||||
if (len==(SIZE_MAX>>1)) {
|
||||
perror("could not get file length");
|
||||
lastError=fmt::sprintf("on pre tell: %s",strerror(errno));
|
||||
fclose(f);
|
||||
return 1;
|
||||
}
|
||||
if (len<1) {
|
||||
if (len==0) {
|
||||
logE("that file is empty!");
|
||||
lastError="file is empty";
|
||||
} else {
|
||||
perror("tell error");
|
||||
lastError=fmt::sprintf("on tell: %s",strerror(errno));
|
||||
}
|
||||
fclose(f);
|
||||
return 1;
|
||||
}
|
||||
if (fseek(f,0,SEEK_SET)<0) {
|
||||
perror("size error");
|
||||
lastError=fmt::sprintf("on get size: %s",strerror(errno));
|
||||
fclose(f);
|
||||
return 1;
|
||||
}
|
||||
unsigned char* file=new unsigned char[len];
|
||||
if (fread(file,1,(size_t)len,f)!=(size_t)len) {
|
||||
perror("read error");
|
||||
lastError=fmt::sprintf("on read: %s",strerror(errno));
|
||||
fclose(f);
|
||||
delete[] file;
|
||||
return 1;
|
||||
}
|
||||
fclose(f);
|
||||
if (!e->playStream(file,(size_t)len)) {
|
||||
lastError=e->getLastError();
|
||||
logE("could not open file!");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void FurnaceGUI::exportAudio(String path, DivAudioExportModes mode) {
|
||||
e->saveAudio(path.c_str(),exportLoops+1,mode,exportFadeOut);
|
||||
displayExporting=true;
|
||||
|
@ -4241,6 +4310,9 @@ bool FurnaceGUI::loop() {
|
|||
case GUI_FILE_MU5_ROM_OPEN:
|
||||
workingDirROM=fileDialog->getPath()+DIR_SEPARATOR_STR;
|
||||
break;
|
||||
case GUI_FILE_CMDSTREAM_OPEN:
|
||||
workingDirROM=fileDialog->getPath()+DIR_SEPARATOR_STR;
|
||||
break;
|
||||
case GUI_FILE_TEST_OPEN:
|
||||
case GUI_FILE_TEST_OPEN_MULTI:
|
||||
case GUI_FILE_TEST_SAVE:
|
||||
|
@ -4706,6 +4778,11 @@ bool FurnaceGUI::loop() {
|
|||
case GUI_FILE_MU5_ROM_OPEN:
|
||||
settings.mu5Path=copyOfName;
|
||||
break;
|
||||
case GUI_FILE_CMDSTREAM_OPEN:
|
||||
if (loadStream(copyOfName)>0) {
|
||||
showError(fmt::sprintf("Error while loading file! (%s)",lastError));
|
||||
}
|
||||
break;
|
||||
case GUI_FILE_TEST_OPEN:
|
||||
showWarning(fmt::sprintf("You opened: %s",copyOfName),GUI_WARN_GENERIC);
|
||||
break;
|
||||
|
|
|
@ -383,6 +383,7 @@ enum FurnaceGUIFileDialogs {
|
|||
GUI_FILE_YRW801_ROM_OPEN,
|
||||
GUI_FILE_TG100_ROM_OPEN,
|
||||
GUI_FILE_MU5_ROM_OPEN,
|
||||
GUI_FILE_CMDSTREAM_OPEN,
|
||||
|
||||
GUI_FILE_TEST_OPEN,
|
||||
GUI_FILE_TEST_OPEN_MULTI,
|
||||
|
@ -1982,7 +1983,7 @@ class FurnaceGUI {
|
|||
void drawNewSong();
|
||||
void drawLog();
|
||||
void drawEffectList();
|
||||
void drawSubSongs();
|
||||
void drawSubSongs(bool asChild=false);
|
||||
void drawFindReplace();
|
||||
void drawSpoiler();
|
||||
void drawClock();
|
||||
|
@ -2074,6 +2075,7 @@ class FurnaceGUI {
|
|||
void openFileDialog(FurnaceGUIFileDialogs type);
|
||||
int save(String path, int dmfVersion);
|
||||
int load(String path);
|
||||
int loadStream(String path);
|
||||
void pushRecentFile(String path);
|
||||
void exportAudio(String path, DivAudioExportModes mode);
|
||||
|
||||
|
|
|
@ -4,15 +4,18 @@
|
|||
#include "misc/cpp/imgui_stdlib.h"
|
||||
#include "intConst.h"
|
||||
|
||||
void FurnaceGUI::drawSubSongs() {
|
||||
void FurnaceGUI::drawSubSongs(bool asChild) {
|
||||
if (nextWindow==GUI_WINDOW_SUBSONGS) {
|
||||
subSongsOpen=true;
|
||||
ImGui::SetNextWindowFocus();
|
||||
nextWindow=GUI_WINDOW_NOTHING;
|
||||
}
|
||||
if (!subSongsOpen) return;
|
||||
if (!subSongsOpen && !asChild) return;
|
||||
if (!asChild) {
|
||||
ImGui::SetNextWindowSizeConstraints(ImVec2(64.0f*dpiScale,32.0f*dpiScale),ImVec2(canvasW,canvasH));
|
||||
if (ImGui::Begin("Subsongs",&subSongsOpen,globalWinFlags)) {
|
||||
}
|
||||
bool began=asChild?ImGui::BeginChild("Subsongs"):ImGui::Begin("Subsongs",&subSongsOpen,globalWinFlags);
|
||||
if (began) {
|
||||
char id[1024];
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x-ImGui::GetFrameHeightWithSpacing()*2.0f-ImGui::GetStyle().ItemSpacing.x);
|
||||
if (e->curSubSong->name.empty()) {
|
||||
|
@ -107,12 +110,16 @@ void FurnaceGUI::drawSubSongs() {
|
|||
MARK_MODIFIED;
|
||||
}
|
||||
|
||||
if (ImGui::GetContentRegionAvail().y>(10.0f*dpiScale)) {
|
||||
if (!asChild && ImGui::GetContentRegionAvail().y>(10.0f*dpiScale)) {
|
||||
if (ImGui::InputTextMultiline("##SubSongNotes",&e->curSubSong->notes,ImGui::GetContentRegionAvail(),ImGuiInputTextFlags_UndoRedo)) {
|
||||
MARK_MODIFIED;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_SUBSONGS;
|
||||
if (!asChild && ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_SUBSONGS;
|
||||
if (asChild) {
|
||||
ImGui::EndChild();
|
||||
} else {
|
||||
ImGui::End();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue