Merge branches 'master' and 'k053260' of https://github.com/tildearrow/furnace into k053260
This commit is contained in:
commit
7b49b4b8e0
161 changed files with 1190 additions and 864 deletions
|
|
@ -160,6 +160,7 @@ bool TAMidiInRtMidi::quit() {
|
|||
|
||||
bool TAMidiOutRtMidi::send(const TAMidiMessage& what) {
|
||||
if (!isOpen) return false;
|
||||
if (!isWorking) return false;
|
||||
if (what.type<0x80) return false;
|
||||
size_t len=0;
|
||||
switch (what.type&0xf0) {
|
||||
|
|
@ -190,6 +191,7 @@ bool TAMidiOutRtMidi::send(const TAMidiMessage& what) {
|
|||
port->sendMessage(what.sysExData.get(),len);
|
||||
} catch (RtMidiError& e) {
|
||||
logE("MIDI output error! %s",e.what());
|
||||
isWorking=false;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
|
@ -209,6 +211,7 @@ bool TAMidiOutRtMidi::send(const TAMidiMessage& what) {
|
|||
port->sendMessage((const unsigned char*)&what.type,len);
|
||||
} catch (RtMidiError& e) {
|
||||
logE("MIDI output error! %s",e.what());
|
||||
isWorking=false;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
|
@ -237,17 +240,20 @@ bool TAMidiOutRtMidi::openDevice(String name) {
|
|||
}
|
||||
isOpen=portOpen;
|
||||
if (!portOpen) logW("could not find MIDI out device...");
|
||||
isWorking=true;
|
||||
return portOpen;
|
||||
} catch (RtMidiError& e) {
|
||||
logW("could not open MIDI out device! %s",e.what());
|
||||
return false;
|
||||
}
|
||||
isWorking=true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TAMidiOutRtMidi::closeDevice() {
|
||||
if (port==NULL) return false;
|
||||
if (!isOpen) return false;
|
||||
isWorking=false;
|
||||
try {
|
||||
port->closePort();
|
||||
} catch (RtMidiError& e) {
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ class TAMidiInRtMidi: public TAMidiIn {
|
|||
|
||||
class TAMidiOutRtMidi: public TAMidiOut {
|
||||
RtMidiOut* port;
|
||||
bool isOpen;
|
||||
bool isOpen, isWorking;
|
||||
public:
|
||||
bool send(const TAMidiMessage& what);
|
||||
bool isDeviceOpen();
|
||||
|
|
@ -49,5 +49,6 @@ class TAMidiOutRtMidi: public TAMidiOut {
|
|||
bool init();
|
||||
TAMidiOutRtMidi():
|
||||
port(NULL),
|
||||
isOpen(false) {}
|
||||
isOpen(false),
|
||||
isWorking(false) {}
|
||||
};
|
||||
|
|
@ -284,6 +284,8 @@ struct DivRegWrite {
|
|||
* - xx is the instance ID
|
||||
* - 0xffffxx03: set sample playback direction
|
||||
* - x is the instance ID
|
||||
* - 0xffffxx04: switch sample bank
|
||||
* - for use in VGM export
|
||||
* - 0xffffffff: reset
|
||||
*/
|
||||
unsigned int addr;
|
||||
|
|
|
|||
|
|
@ -489,7 +489,7 @@ class DivEngine {
|
|||
void processRow(int i, bool afterDelay);
|
||||
void nextOrder();
|
||||
void nextRow();
|
||||
void performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write, int streamOff, double* loopTimer, double* loopFreq, int* loopSample, bool* sampleDir, bool isSecond, int* pendingFreq, int* playingSample, bool directStream);
|
||||
void performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write, int streamOff, double* loopTimer, double* loopFreq, int* loopSample, bool* sampleDir, bool isSecond, int* pendingFreq, int* playingSample, size_t bankOffset, bool directStream);
|
||||
// returns true if end of song.
|
||||
bool nextTick(bool noAccum=false, bool inhibitLowLat=false);
|
||||
bool perSystemEffect(int ch, unsigned char effect, unsigned char effectVal);
|
||||
|
|
|
|||
|
|
@ -1166,6 +1166,11 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
#define DRSLD2R(x) \
|
||||
if (chan[i].state.op[x].dr<dr) dr=chan[i].state.op[x].dr; \
|
||||
if (chan[i].state.op[x].sl<sl) sl=chan[i].state.op[x].sl; \
|
||||
if (chan[i].state.op[x].d2r<d2r) d2r=chan[i].state.op[x].d2r;
|
||||
|
||||
void DivPlatformGenesis::forceIns() {
|
||||
for (int i=0; i<6; i++) {
|
||||
for (int j=0; j<4; j++) {
|
||||
|
|
@ -1190,7 +1195,29 @@ void DivPlatformGenesis::forceIns() {
|
|||
rWrite(chanOffs[i]+ADDR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3));
|
||||
rWrite(chanOffs[i]+ADDR_LRAF,(IS_REALLY_MUTED(i)?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4));
|
||||
if (chan[i].active) {
|
||||
if (i<5 || !chan[i].dacMode) {
|
||||
bool sustained=false;
|
||||
unsigned char dr=chan[i].state.op[3].dr;
|
||||
unsigned char sl=chan[i].state.op[3].sl;
|
||||
unsigned char d2r=chan[i].state.op[3].d2r;
|
||||
|
||||
switch (chan[i].state.alg&7) {
|
||||
case 4:
|
||||
DRSLD2R(2);
|
||||
break;
|
||||
case 5:
|
||||
case 6:
|
||||
DRSLD2R(2);
|
||||
DRSLD2R(1);
|
||||
break;
|
||||
case 7:
|
||||
DRSLD2R(2);
|
||||
DRSLD2R(1);
|
||||
DRSLD2R(3);
|
||||
break;
|
||||
}
|
||||
|
||||
if (dr<2 || (sl<15 && d2r<2)) sustained=true;
|
||||
if ((i<5 || !chan[i].dacMode) && sustained) {
|
||||
chan[i].keyOn=true;
|
||||
chan[i].freqChanged=true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -346,7 +346,11 @@ void DivPlatformNES::tick(bool sysTick) {
|
|||
rWrite(0x4012,(dpcmAddr>>6)&0xff);
|
||||
rWrite(0x4013,dpcmLen&0xff);
|
||||
rWrite(0x4015,31);
|
||||
dpcmBank=dpcmAddr>>14;
|
||||
if (dpcmBank!=(dpcmAddr>>14)) {
|
||||
dpcmBank=dpcmAddr>>14;
|
||||
logV("switching bank to %d",dpcmBank);
|
||||
if (dumpWrites) addWrite(0xffff0004,dpcmBank);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (nextDPCMFreq>=0) {
|
||||
|
|
@ -425,7 +429,11 @@ int DivPlatformNES::dispatch(DivCommand c) {
|
|||
rWrite(0x4012,(dpcmAddr>>6)&0xff);
|
||||
rWrite(0x4013,dpcmLen&0xff);
|
||||
rWrite(0x4015,31);
|
||||
dpcmBank=dpcmAddr>>14;
|
||||
if (dpcmBank!=(dpcmAddr>>14)) {
|
||||
dpcmBank=dpcmAddr>>14;
|
||||
logV("switching bank to %d",dpcmBank);
|
||||
if (dumpWrites) addWrite(0xffff0004,dpcmBank);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
@ -854,6 +862,7 @@ int DivPlatformNES::init(DivEngine* p, int channels, int sugRate, const DivConfi
|
|||
dpcmMem=new unsigned char[262144];
|
||||
dpcmMemLen=0;
|
||||
dpcmBank=0;
|
||||
if (dumpWrites) addWrite(0xffff0004,dpcmBank);
|
||||
|
||||
init_nla_table(500,500);
|
||||
reset();
|
||||
|
|
|
|||
|
|
@ -1691,6 +1691,10 @@ void DivEngine::runMidiTime(int totalCycles) {
|
|||
}
|
||||
|
||||
void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsigned int size) {
|
||||
if (!size) {
|
||||
logW("nextBuf called with size 0!");
|
||||
return;
|
||||
}
|
||||
lastLoopPos=-1;
|
||||
|
||||
if (out!=NULL) {
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
|
||||
constexpr int MASTER_CLOCK_PREC=(sizeof(void*)==8)?8:0;
|
||||
|
||||
void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write, int streamOff, double* loopTimer, double* loopFreq, int* loopSample, bool* sampleDir, bool isSecond, int* pendingFreq, int* playingSample, bool directStream) {
|
||||
void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write, int streamOff, double* loopTimer, double* loopFreq, int* loopSample, bool* sampleDir, bool isSecond, int* pendingFreq, int* playingSample, size_t bankOffset, bool directStream) {
|
||||
unsigned char baseAddr1=isSecond?0xa0:0x50;
|
||||
unsigned char baseAddr2=isSecond?0x80:0;
|
||||
unsigned short baseAddr2S=isSecond?0x8000:0;
|
||||
|
|
@ -587,6 +587,28 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
|
|||
break;
|
||||
}
|
||||
}
|
||||
if (write.addr==0xffff0004) { // switch sample bank
|
||||
switch (sys) {
|
||||
case DIV_SYSTEM_NES: {
|
||||
unsigned int bankAddr=bankOffset+(write.val<<14);
|
||||
w->writeC(0x68);
|
||||
w->writeC(0x6c);
|
||||
w->writeC(0x07|(isSecond?0x80:0x00));
|
||||
w->writeC(bankAddr&0xff);
|
||||
w->writeC((bankAddr>>8)&0xff);
|
||||
w->writeC((bankAddr>>16)&0xff);
|
||||
w->writeC(0x00);
|
||||
w->writeC(0xc0);
|
||||
w->writeC(0x00);
|
||||
w->writeC(0x00);
|
||||
w->writeC(0x40);
|
||||
w->writeC(0x00);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (write.addr>=0xffff0000) { // Furnace special command
|
||||
if (!directStream) {
|
||||
unsigned char streamID=streamOff+((write.addr&0xff00)>>8);
|
||||
|
|
@ -610,11 +632,14 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
|
|||
}
|
||||
}
|
||||
break;
|
||||
case 1: // set sample freq
|
||||
case 1: { // set sample freq
|
||||
int realFreq=write.val;
|
||||
if (realFreq<0) realFreq=0;
|
||||
if (realFreq>44100) realFreq=44100;
|
||||
w->writeC(0x92);
|
||||
w->writeC(streamID);
|
||||
w->writeI(write.val);
|
||||
loopFreq[streamID]=write.val;
|
||||
w->writeI(realFreq);
|
||||
loopFreq[streamID]=realFreq;
|
||||
if (pendingFreq[streamID]!=-1) {
|
||||
DivSample* sample=song.sample[pendingFreq[streamID]];
|
||||
w->writeC(0x95);
|
||||
|
|
@ -629,6 +654,7 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
|
|||
pendingFreq[streamID]=-1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2: // stop sample
|
||||
w->writeC(0x94);
|
||||
w->writeC(streamID);
|
||||
|
|
@ -1069,6 +1095,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
|||
bool willExport[DIV_MAX_CHIPS];
|
||||
bool isSecond[DIV_MAX_CHIPS];
|
||||
int streamIDs[DIV_MAX_CHIPS];
|
||||
size_t bankOffset[DIV_MAX_CHIPS];
|
||||
double loopTimer[DIV_MAX_CHANS];
|
||||
double loopFreq[DIV_MAX_CHANS];
|
||||
int loopSample[DIV_MAX_CHANS];
|
||||
|
|
@ -1086,6 +1113,8 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
|||
bool mayWriteRate=(fmod(curSubSong->hz,1.0)<0.00001 || fmod(curSubSong->hz,1.0)>0.99999);
|
||||
int countDown=MAX(0,trailingTicks)+1;
|
||||
|
||||
memset(bankOffset,0,DIV_MAX_CHIPS*sizeof(size_t));
|
||||
|
||||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||
loopTimer[i]=0;
|
||||
loopFreq[i]=0;
|
||||
|
|
@ -1110,6 +1139,12 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
|||
DivDispatch* writeMSM6295[2]={NULL,NULL};
|
||||
DivDispatch* writeGA20[2]={NULL,NULL};
|
||||
DivDispatch* writeK053260[2]={NULL,NULL};
|
||||
DivDispatch* writeNES[2]={NULL,NULL};
|
||||
|
||||
int writeNESIndex[2]={0,0};
|
||||
|
||||
size_t bankOffsetNESCurrent=0;
|
||||
size_t bankOffsetNES[2]={0,0};
|
||||
|
||||
for (int i=0; i<song.systemLen; i++) {
|
||||
willExport[i]=false;
|
||||
|
|
@ -1180,11 +1215,15 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
|||
CHIP_VOL(20,1.7);
|
||||
willExport[i]=true;
|
||||
writeNESSamples=true;
|
||||
writeNES[0]=disCont[i].dispatch;
|
||||
writeNESIndex[0]=i;
|
||||
} else if (!(hasNES&0x40000000)) {
|
||||
isSecond[i]=true;
|
||||
CHIP_VOL_SECOND(20,1.7);
|
||||
willExport[i]=true;
|
||||
hasNES|=0x40000000;
|
||||
writeNES[1]=disCont[i].dispatch;
|
||||
writeNESIndex[1]=i;
|
||||
howManyChips++;
|
||||
}
|
||||
break;
|
||||
|
|
@ -1865,6 +1904,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
|||
for (unsigned int j=0; j<sample->length8; j++) {
|
||||
w->writeC(((unsigned char)sample->data8[j]+0x80)>>1);
|
||||
}
|
||||
bankOffsetNESCurrent+=sample->length8;
|
||||
}
|
||||
|
||||
if (writePCESamples && !directStream) for (int i=0; i<song.sampleLen; i++) {
|
||||
|
|
@ -2008,6 +2048,30 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
|||
w->writeI(0);
|
||||
w->write(writeK053260[i]->getSampleMem(),writeK053260[i]->getSampleMemUsage());
|
||||
}
|
||||
if (writeNES[i]!=NULL && writeNES[i]->getSampleMemUsage()>0) {
|
||||
size_t howMuchWillBeWritten=writeNES[i]->getSampleMemUsage();
|
||||
w->writeC(0x67);
|
||||
w->writeC(0x66);
|
||||
w->writeC(7);
|
||||
w->writeI(howMuchWillBeWritten);
|
||||
w->write(writeNES[i]->getSampleMem(),howMuchWillBeWritten);
|
||||
bankOffsetNES[i]=bankOffsetNESCurrent;
|
||||
bankOffset[writeNESIndex[i]]=bankOffsetNES[i];
|
||||
bankOffsetNESCurrent+=howMuchWillBeWritten;
|
||||
// force the first bank
|
||||
w->writeC(0x68);
|
||||
w->writeC(0x6c);
|
||||
w->writeC(0x07|(i?0x80:0x00));
|
||||
w->writeC(bankOffsetNES[i]&0xff);
|
||||
w->writeC((bankOffsetNES[i]>>8)&0xff);
|
||||
w->writeC((bankOffsetNES[i]>>16)&0xff);
|
||||
w->writeC(0x00);
|
||||
w->writeC(0xc0);
|
||||
w->writeC(0x00);
|
||||
w->writeC(0x00);
|
||||
w->writeC(0x40);
|
||||
w->writeC(0x00);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO
|
||||
|
|
@ -2217,7 +2281,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
|||
for (int i=0; i<song.systemLen; i++) {
|
||||
std::vector<DivRegWrite>& writes=disCont[i].dispatch->getRegisterWrites();
|
||||
for (DivRegWrite& j: writes) {
|
||||
performVGMWrite(w,song.system[i],j,streamIDs[i],loopTimer,loopFreq,loopSample,sampleDir,isSecond[i],pendingFreq,playingSample,directStream);
|
||||
performVGMWrite(w,song.system[i],j,streamIDs[i],loopTimer,loopFreq,loopSample,sampleDir,isSecond[i],pendingFreq,playingSample,bankOffset[i],directStream);
|
||||
writeCount++;
|
||||
}
|
||||
writes.clear();
|
||||
|
|
@ -2257,7 +2321,9 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
|||
lastOne=i.second.time;
|
||||
}
|
||||
// write write
|
||||
performVGMWrite(w,song.system[i.first],i.second.write,streamIDs[i.first],loopTimer,loopFreq,loopSample,sampleDir,isSecond[i.first],pendingFreq,playingSample,directStream);
|
||||
performVGMWrite(w,song.system[i.first],i.second.write,streamIDs[i.first],loopTimer,loopFreq,loopSample,sampleDir,isSecond[i.first],pendingFreq,playingSample,bankOffset[i.first],directStream);
|
||||
// handle global Furnace commands
|
||||
|
||||
writeCount++;
|
||||
}
|
||||
sortedWrites.clear();
|
||||
|
|
|
|||
|
|
@ -697,6 +697,7 @@ void FurnaceGUI::drawInsList(bool asChild) {
|
|||
}
|
||||
}
|
||||
ImGui::SameLine();
|
||||
pushDestColor();
|
||||
if (ImGui::Button(ICON_FA_TIMES "##InsDelete")) {
|
||||
if (settings.unifiedDataView) {
|
||||
switch (lastAssetType) {
|
||||
|
|
@ -714,6 +715,7 @@ void FurnaceGUI::drawInsList(bool asChild) {
|
|||
doAction(GUI_ACTION_INS_LIST_DELETE);
|
||||
}
|
||||
}
|
||||
popDestColor();
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Delete");
|
||||
}
|
||||
|
|
@ -934,9 +936,11 @@ void FurnaceGUI::drawWaveList(bool asChild) {
|
|||
}
|
||||
}
|
||||
ImGui::SameLine();
|
||||
pushDestColor();
|
||||
if (ImGui::Button(ICON_FA_TIMES "##WaveDelete")) {
|
||||
doAction(GUI_ACTION_WAVE_LIST_DELETE);
|
||||
}
|
||||
popDestColor();
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Delete");
|
||||
}
|
||||
|
|
@ -1074,13 +1078,6 @@ void FurnaceGUI::drawSampleList(bool asChild) {
|
|||
}
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(ICON_FA_TIMES "##SampleDelete")) {
|
||||
doAction(GUI_ACTION_SAMPLE_LIST_DELETE);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Delete");
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(ICON_FA_VOLUME_UP "##PreviewSampleL")) {
|
||||
doAction(GUI_ACTION_SAMPLE_LIST_PREVIEW);
|
||||
}
|
||||
|
|
@ -1094,6 +1091,15 @@ void FurnaceGUI::drawSampleList(bool asChild) {
|
|||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Stop preview");
|
||||
}
|
||||
ImGui::SameLine();
|
||||
pushDestColor();
|
||||
if (ImGui::Button(ICON_FA_TIMES "##SampleDelete")) {
|
||||
doAction(GUI_ACTION_SAMPLE_LIST_DELETE);
|
||||
}
|
||||
popDestColor();
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Delete");
|
||||
}
|
||||
ImGui::Separator();
|
||||
if (ImGui::BeginTable("SampleListScroll",1,ImGuiTableFlags_ScrollY)) {
|
||||
actualSampleList();
|
||||
|
|
|
|||
|
|
@ -5076,7 +5076,24 @@ bool FurnaceGUI::loop() {
|
|||
newSongQuery="";
|
||||
newSongFirstFrame=true;
|
||||
displayNew=false;
|
||||
ImGui::OpenPopup("New Song");
|
||||
if (settings.newSongBehavior==1) {
|
||||
e->createNewFromDefaults();
|
||||
undoHist.clear();
|
||||
redoHist.clear();
|
||||
curFileName="";
|
||||
modified=false;
|
||||
curNibble=false;
|
||||
orderNibble=false;
|
||||
orderCursor=-1;
|
||||
samplePos=0;
|
||||
updateSampleTex=true;
|
||||
selStart=SelectionPoint();
|
||||
selEnd=SelectionPoint();
|
||||
cursor=SelectionPoint();
|
||||
updateWindowTitle();
|
||||
} else {
|
||||
ImGui::OpenPopup("New Song");
|
||||
}
|
||||
}
|
||||
|
||||
if (displayEditString) {
|
||||
|
|
@ -6575,6 +6592,7 @@ FurnaceGUI::FurnaceGUI():
|
|||
portrait(false),
|
||||
injectBackUp(false),
|
||||
mobileMenuOpen(false),
|
||||
warnColorPushed(false),
|
||||
wantCaptureKeyboard(false),
|
||||
oldWantCaptureKeyboard(false),
|
||||
displayMacroMenu(false),
|
||||
|
|
|
|||
|
|
@ -126,6 +126,9 @@ enum FurnaceGUIColors {
|
|||
GUI_COLOR_TOGGLE_ON,
|
||||
GUI_COLOR_EDITING,
|
||||
GUI_COLOR_SONG_LOOP,
|
||||
GUI_COLOR_DESTRUCTIVE,
|
||||
GUI_COLOR_WARNING,
|
||||
GUI_COLOR_ERROR,
|
||||
|
||||
GUI_COLOR_FILE_DIR,
|
||||
GUI_COLOR_FILE_SONG_NATIVE,
|
||||
|
|
@ -1297,7 +1300,7 @@ class FurnaceGUI {
|
|||
|
||||
bool quit, warnQuit, willCommit, edit, modified, displayError, displayExporting, vgmExportLoop, zsmExportLoop, vgmExportPatternHints;
|
||||
bool vgmExportDirectStream, displayInsTypeList;
|
||||
bool portrait, injectBackUp, mobileMenuOpen;
|
||||
bool portrait, injectBackUp, mobileMenuOpen, warnColorPushed;
|
||||
bool wantCaptureKeyboard, oldWantCaptureKeyboard, displayMacroMenu;
|
||||
bool displayNew, fullScreen, preserveChanPos, wantScrollList, noteInputPoly;
|
||||
bool displayPendingIns, pendingInsSingle, displayPendingRawSample, snesFilterHex, modTableHex, displayEditString;
|
||||
|
|
@ -1506,6 +1509,7 @@ class FurnaceGUI {
|
|||
int renderClearPos;
|
||||
int insertBehavior;
|
||||
int pullDeleteRow;
|
||||
int newSongBehavior;
|
||||
unsigned int maxUndoSteps;
|
||||
String mainFontPath;
|
||||
String patFontPath;
|
||||
|
|
@ -1657,6 +1661,7 @@ class FurnaceGUI {
|
|||
renderClearPos(0),
|
||||
insertBehavior(1),
|
||||
pullDeleteRow(1),
|
||||
newSongBehavior(0),
|
||||
maxUndoSteps(100),
|
||||
mainFontPath(""),
|
||||
patFontPath(""),
|
||||
|
|
@ -2082,6 +2087,11 @@ class FurnaceGUI {
|
|||
void pushAccentColors(const ImVec4& one, const ImVec4& two, const ImVec4& border, const ImVec4& borderShadow);
|
||||
void popAccentColors();
|
||||
|
||||
void pushDestColor();
|
||||
void popDestColor();
|
||||
void pushWarningColor(bool warnCond, bool errorCond=false);
|
||||
void popWarningColor();
|
||||
|
||||
float calcBPM(const DivGroovePattern& speeds, float hz, int vN, int vD);
|
||||
|
||||
void patternRow(int i, bool isPlaying, float lineHeight, int chans, int ord, const DivPattern** patCache, bool inhibitSel);
|
||||
|
|
|
|||
|
|
@ -743,6 +743,9 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={
|
|||
D(GUI_COLOR_TOGGLE_ON,"",ImVec4(0.2f,0.6f,0.2f,1.0f)),
|
||||
D(GUI_COLOR_EDITING,"",ImVec4(0.2f,0.1f,0.1f,1.0f)),
|
||||
D(GUI_COLOR_SONG_LOOP,"",ImVec4(0.3f,0.5f,0.8f,0.4f)),
|
||||
D(GUI_COLOR_DESTRUCTIVE,"",ImVec4(1.0f,0.2f,0.2f,1.0f)),
|
||||
D(GUI_COLOR_WARNING,"",ImVec4(0.98f,0.98f,0.06f,1.0f)),
|
||||
D(GUI_COLOR_ERROR,"",ImVec4(0.98f,0.06f,0.11f,1.0f)),
|
||||
|
||||
D(GUI_COLOR_FILE_DIR,"",ImVec4(0.0f,1.0f,1.0f,1.0f)),
|
||||
D(GUI_COLOR_FILE_SONG_NATIVE,"",ImVec4(0.5f,1.0f,0.5f,1.0f)),
|
||||
|
|
|
|||
|
|
@ -822,10 +822,15 @@ void FurnaceGUI::initSystemPresets() {
|
|||
}
|
||||
);
|
||||
ENTRY(
|
||||
"ZX Spectrum (48K)", {
|
||||
"ZX Spectrum (48K, SFX-like engine)", {
|
||||
CH(DIV_SYSTEM_SFX_BEEPER, 1.0f, 0, "")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"ZX Spectrum (48K, QuadTone engine)", {
|
||||
CH(DIV_SYSTEM_SFX_BEEPER_QUADTONE, 1.0f, 0, "")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"ZX Spectrum (128K)", {
|
||||
CH(DIV_SYSTEM_AY8910, 1.0f, 0, "clockSel=1") //beeper was also included
|
||||
|
|
@ -2635,10 +2640,15 @@ void FurnaceGUI::initSystemPresets() {
|
|||
}
|
||||
);
|
||||
ENTRY(
|
||||
"ZX Spectrum (beeper only)", {
|
||||
"ZX Spectrum (beeper only, SFX-like engine)", {
|
||||
CH(DIV_SYSTEM_SFX_BEEPER, 1.0f, 0, "")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"ZX Spectrum (beeper only, QuadTone engine)", {
|
||||
CH(DIV_SYSTEM_SFX_BEEPER_QUADTONE, 1.0f, 0, "")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Sharp SM8521", {
|
||||
CH(DIV_SYSTEM_SM8521, 1.0f, 0, "")
|
||||
|
|
|
|||
|
|
@ -43,6 +43,12 @@ const double timeMultipliers[13]={
|
|||
#define CENTER_TEXT(text) \
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX()+0.5*(ImGui::GetContentRegionAvail().x-ImGui::CalcTextSize(text).x));
|
||||
|
||||
#define SAMPLE_WARN(_x,_text) \
|
||||
if (_x.find(_text)==String::npos) { \
|
||||
if (!_x.empty()) _x+='\n'; \
|
||||
_x+=_text; \
|
||||
}
|
||||
|
||||
void FurnaceGUI::drawSampleEdit() {
|
||||
if (nextWindow==GUI_WINDOW_SAMPLE_EDIT) {
|
||||
sampleEditOpen=true;
|
||||
|
|
@ -166,6 +172,9 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
|
||||
ImGui::Separator();
|
||||
|
||||
String warnLoop, warnLoopMode, warnLoopPos;
|
||||
String warnLength;
|
||||
|
||||
bool isChipVisible[DIV_MAX_CHIPS];
|
||||
bool isTypeVisible[DIV_MAX_SAMPLE_TYPE];
|
||||
bool isMemVisible[DIV_MAX_SAMPLE_TYPE][DIV_MAX_CHIPS];
|
||||
|
|
@ -174,7 +183,83 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
memset(isTypeVisible,0,DIV_MAX_SAMPLE_TYPE*sizeof(bool));
|
||||
memset(isMemVisible,0,DIV_MAX_CHIPS*DIV_MAX_SAMPLE_TYPE*sizeof(bool));
|
||||
memset(isMemWarning,0,DIV_MAX_CHIPS*DIV_MAX_SAMPLE_TYPE*sizeof(bool));
|
||||
|
||||
for (int i=0; i<e->song.systemLen; i++) {
|
||||
// warnings
|
||||
switch (e->song.system[i]) {
|
||||
case DIV_SYSTEM_SNES:
|
||||
if (sample->loop) {
|
||||
if (sample->loopStart&15 || sample->loopEnd&15) {
|
||||
SAMPLE_WARN(warnLoopPos,"SNES: loop must be a multiple of 16");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DIV_SYSTEM_QSOUND:
|
||||
if (sample->loop) {
|
||||
if (sample->loopEnd-sample->loopStart>32767) {
|
||||
SAMPLE_WARN(warnLoopPos,"QSound: loop cannot be longer than 32767 samples");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DIV_SYSTEM_NES:
|
||||
if (sample->loop) {
|
||||
if (sample->loopStart!=0 || sample->loopEnd!=(int)(sample->samples)) {
|
||||
SAMPLE_WARN(warnLoopPos,"NES: loop point ignored on DPCM (may only loop entire sample)");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DIV_SYSTEM_X1_010:
|
||||
if (sample->loop) {
|
||||
SAMPLE_WARN(warnLoop,"X1-010: samples can't loop");
|
||||
}
|
||||
break;
|
||||
case DIV_SYSTEM_GA20:
|
||||
if (sample->loop) {
|
||||
SAMPLE_WARN(warnLoop,"GA20: samples can't loop");
|
||||
}
|
||||
break;
|
||||
case DIV_SYSTEM_YM2608:
|
||||
case DIV_SYSTEM_YM2608_EXT:
|
||||
case DIV_SYSTEM_YM2608_CSM:
|
||||
if (sample->loop) {
|
||||
if (sample->loopStart!=0 || sample->loopEnd!=(int)(sample->samples)) {
|
||||
SAMPLE_WARN(warnLoopPos,"YM2608: loop point ignored on ADPCM-B (may only loop entire sample)");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DIV_SYSTEM_YM2610_FULL:
|
||||
case DIV_SYSTEM_YM2610_FULL_EXT:
|
||||
case DIV_SYSTEM_YM2610_CSM:
|
||||
case DIV_SYSTEM_YM2610B:
|
||||
case DIV_SYSTEM_YM2610B_EXT:
|
||||
if (sample->loop) {
|
||||
SAMPLE_WARN(warnLoop,"YM2610: ADPCM-A samples can't loop");
|
||||
if (sample->loopStart!=0 || sample->loopEnd!=(int)(sample->samples)) {
|
||||
SAMPLE_WARN(warnLoopPos,"YM2608: loop point ignored on ADPCM-B (may only loop entire sample)");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DIV_SYSTEM_AMIGA:
|
||||
if (sample->loop) {
|
||||
if (sample->loopStart&1 || sample->loopEnd&1) {
|
||||
SAMPLE_WARN(warnLoopPos,"Amiga: loop must be a multiple of 2");
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (e->song.system[i]!=DIV_SYSTEM_PCM_DAC) {
|
||||
if (e->song.system[i]==DIV_SYSTEM_ES5506) {
|
||||
if (sample->loopMode==DIV_SAMPLE_LOOP_BACKWARD) {
|
||||
SAMPLE_WARN(warnLoopMode,"ES5506: backward loop mode isn't supported");
|
||||
}
|
||||
} else if (sample->loopMode!=DIV_SAMPLE_LOOP_FORWARD) {
|
||||
SAMPLE_WARN(warnLoopMode,"backward/ping-pong only supported in Generic PCM DAC\nping-pong also on ES5506");
|
||||
}
|
||||
}
|
||||
|
||||
// chips grid
|
||||
DivDispatch* dispatch=e->getDispatch(i);
|
||||
if (dispatch==NULL) continue;
|
||||
|
||||
|
|
@ -186,6 +271,7 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
if (!dispatch->isSampleLoaded(j,curSample)) isMemWarning[j][i]=true;
|
||||
}
|
||||
}
|
||||
|
||||
int selColumns=1;
|
||||
for (int i=0; i<DIV_MAX_CHIPS; i++) {
|
||||
if (isChipVisible[i]) selColumns++;
|
||||
|
|
@ -218,6 +304,7 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
popToggleColors();
|
||||
ImGui::TableNextColumn();
|
||||
bool doLoop=(sample->loop);
|
||||
pushWarningColor(!warnLoop.empty());
|
||||
if (ImGui::Checkbox("Loop",&doLoop)) { MARK_MODIFIED
|
||||
if (doLoop) {
|
||||
sample->loop=true;
|
||||
|
|
@ -238,8 +325,12 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
e->renderSamplesP();
|
||||
}
|
||||
}
|
||||
if (ImGui::IsItemHovered() && sample->depth==DIV_SAMPLE_DEPTH_BRR) {
|
||||
ImGui::SetTooltip("changing the loop in a BRR sample may result in glitches!");
|
||||
popWarningColor();
|
||||
if (ImGui::IsItemHovered() && (!warnLoop.empty() || sample->depth==DIV_SAMPLE_DEPTH_BRR)) {
|
||||
if (sample->depth==DIV_SAMPLE_DEPTH_BRR) {
|
||||
SAMPLE_WARN(warnLoop,"changing the loop in a BRR sample may result in glitches!");
|
||||
}
|
||||
ImGui::SetTooltip("%s",warnLoop.c_str());
|
||||
}
|
||||
|
||||
if (selColumns>1) {
|
||||
|
|
@ -417,6 +508,7 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
keepLoopAlive=false;
|
||||
ImGui::Text("Mode");
|
||||
ImGui::SameLine();
|
||||
pushWarningColor(!warnLoopMode.empty());
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
if (ImGui::BeginCombo("##SampleLoopMode",loopType.c_str())) {
|
||||
for (int i=0; i<DIV_SAMPLE_LOOP_MAX; i++) {
|
||||
|
|
@ -431,7 +523,12 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
if (ImGui::IsItemHovered() && !warnLoopMode.empty()) {
|
||||
ImGui::SetTooltip("%s",warnLoopMode.c_str());
|
||||
}
|
||||
popWarningColor();
|
||||
|
||||
pushWarningColor(!warnLoopPos.empty());
|
||||
ImGui::Text("Start");
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
|
|
@ -450,8 +547,11 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
if (ImGui::IsItemActive()) {
|
||||
keepLoopAlive=true;
|
||||
}
|
||||
if (ImGui::IsItemHovered() && sample->depth==DIV_SAMPLE_DEPTH_BRR) {
|
||||
ImGui::SetTooltip("changing the loop in a BRR sample may result in glitches!");
|
||||
if (ImGui::IsItemHovered() && (!warnLoopPos.empty() || sample->depth==DIV_SAMPLE_DEPTH_BRR)) {
|
||||
if (sample->depth==DIV_SAMPLE_DEPTH_BRR) {
|
||||
SAMPLE_WARN(warnLoopPos,"changing the loop in a BRR sample may result in glitches!");
|
||||
}
|
||||
ImGui::SetTooltip("%s",warnLoopPos.c_str());
|
||||
}
|
||||
|
||||
ImGui::Text("End");
|
||||
|
|
@ -472,9 +572,13 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
if (ImGui::IsItemActive()) {
|
||||
keepLoopAlive=true;
|
||||
}
|
||||
if (ImGui::IsItemHovered() && sample->depth==DIV_SAMPLE_DEPTH_BRR) {
|
||||
ImGui::SetTooltip("changing the loop in a BRR sample may result in glitches!");
|
||||
if (ImGui::IsItemHovered() && (!warnLoopPos.empty() || sample->depth==DIV_SAMPLE_DEPTH_BRR)) {
|
||||
if (sample->depth==DIV_SAMPLE_DEPTH_BRR) {
|
||||
SAMPLE_WARN(warnLoopPos,"changing the loop in a BRR sample may result in glitches!");
|
||||
}
|
||||
ImGui::SetTooltip("%s",warnLoopPos.c_str());
|
||||
}
|
||||
popWarningColor();
|
||||
ImGui::EndDisabled();
|
||||
|
||||
if (selColumns>1) {
|
||||
|
|
|
|||
|
|
@ -508,6 +508,14 @@ void FurnaceGUI::drawSettings() {
|
|||
settings.alwaysPlayIntro=3;
|
||||
}
|
||||
|
||||
ImGui::Text("When creating new song:");
|
||||
if (ImGui::RadioButton("Display system preset selector##NSB0",settings.newSongBehavior==0)) {
|
||||
settings.newSongBehavior=0;
|
||||
}
|
||||
if (ImGui::RadioButton("Start with initial system##NSB1",settings.newSongBehavior==1)) {
|
||||
settings.newSongBehavior=1;
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (CWSliderFloat("Double-click time (seconds)",&settings.doubleClickTime,0.02,1.0,"%.2f")) {
|
||||
|
|
@ -1922,6 +1930,9 @@ void FurnaceGUI::drawSettings() {
|
|||
UI_COLOR_CONFIG(GUI_COLOR_EDITING,"Editing");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_SONG_LOOP,"Song loop");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_PLAYBACK_STAT,"Playback status");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_DESTRUCTIVE,"Destructive hint");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_WARNING,"Warning hint");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_ERROR,"Error hint");
|
||||
ImGui::TreePop();
|
||||
}
|
||||
if (ImGui::TreeNode("File Picker (built-in)")) {
|
||||
|
|
@ -2755,6 +2766,7 @@ void FurnaceGUI::syncSettings() {
|
|||
settings.renderClearPos=e->getConfInt("renderClearPos",0);
|
||||
settings.insertBehavior=e->getConfInt("insertBehavior",1);
|
||||
settings.pullDeleteRow=e->getConfInt("pullDeleteRow",1);
|
||||
settings.newSongBehavior=e->getConfInt("newSongBehavior",0);
|
||||
|
||||
clampSetting(settings.mainFontSize,2,96);
|
||||
clampSetting(settings.patFontSize,2,96);
|
||||
|
|
@ -2880,6 +2892,7 @@ void FurnaceGUI::syncSettings() {
|
|||
clampSetting(settings.renderClearPos,0,1);
|
||||
clampSetting(settings.insertBehavior,0,1);
|
||||
clampSetting(settings.pullDeleteRow,0,1);
|
||||
clampSetting(settings.newSongBehavior,0,1);
|
||||
|
||||
if (settings.exportLoops<0.0) settings.exportLoops=0.0;
|
||||
if (settings.exportFadeOut<0.0) settings.exportFadeOut=0.0;
|
||||
|
|
@ -3101,6 +3114,7 @@ void FurnaceGUI::commitSettings() {
|
|||
e->setConf("renderClearPos",settings.renderClearPos);
|
||||
e->setConf("insertBehavior",settings.insertBehavior);
|
||||
e->setConf("pullDeleteRow",settings.pullDeleteRow);
|
||||
e->setConf("newSongBehavior",settings.newSongBehavior);
|
||||
|
||||
// colors
|
||||
for (int i=0; i<GUI_COLOR_MAX; i++) {
|
||||
|
|
@ -3493,6 +3507,35 @@ void FurnaceGUI::popAccentColors() {
|
|||
ImGui::PopStyleColor(24);
|
||||
}
|
||||
|
||||
void FurnaceGUI::pushDestColor() {
|
||||
pushAccentColors(uiColors[GUI_COLOR_DESTRUCTIVE],uiColors[GUI_COLOR_DESTRUCTIVE],uiColors[GUI_COLOR_DESTRUCTIVE],ImVec4(0.0f,0.0f,0.0f,0.0f));
|
||||
}
|
||||
|
||||
void FurnaceGUI::popDestColor() {
|
||||
popAccentColors();
|
||||
}
|
||||
|
||||
void FurnaceGUI::pushWarningColor(bool warnCond, bool errorCond) {
|
||||
if (warnColorPushed) {
|
||||
logE("warnColorPushed");
|
||||
abort();
|
||||
}
|
||||
if (errorCond) {
|
||||
pushAccentColors(uiColors[GUI_COLOR_ERROR],uiColors[GUI_COLOR_ERROR],uiColors[GUI_COLOR_ERROR],ImVec4(0.0f,0.0f,0.0f,0.0f));
|
||||
warnColorPushed=true;
|
||||
} else if (warnCond) {
|
||||
pushAccentColors(uiColors[GUI_COLOR_WARNING],uiColors[GUI_COLOR_WARNING],uiColors[GUI_COLOR_WARNING],ImVec4(0.0f,0.0f,0.0f,0.0f));
|
||||
warnColorPushed=true;
|
||||
}
|
||||
}
|
||||
|
||||
void FurnaceGUI::popWarningColor() {
|
||||
if (warnColorPushed) {
|
||||
popAccentColors();
|
||||
warnColorPushed=false;
|
||||
}
|
||||
}
|
||||
|
||||
#define IGFD_FileStyleByExtension IGFD_FileStyleByExtention
|
||||
|
||||
#ifdef _WIN32
|
||||
|
|
|
|||
|
|
@ -92,6 +92,7 @@ void FurnaceGUI::drawSubSongs(bool asChild) {
|
|||
ImGui::SetTooltip("Add");
|
||||
}
|
||||
ImGui::SameLine();
|
||||
pushDestColor();
|
||||
if (ImGui::Button(ICON_FA_MINUS "##SubSongDel")) {
|
||||
if (e->song.subsong.size()<=1) {
|
||||
showError("this is the only subsong!");
|
||||
|
|
@ -99,6 +100,7 @@ void FurnaceGUI::drawSubSongs(bool asChild) {
|
|||
showWarning("are you sure you want to remove this subsong?",GUI_WARN_SUBSONG_DEL);
|
||||
}
|
||||
}
|
||||
popDestColor();
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Remove");
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue