Merge branches 'master' and 'k053260' of https://github.com/tildearrow/furnace into k053260

This commit is contained in:
cam900 2023-06-26 19:47:25 +09:00
commit 7b49b4b8e0
161 changed files with 1190 additions and 864 deletions

View file

@ -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) {

View file

@ -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) {}
};

View file

@ -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;

View file

@ -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);

View file

@ -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;
}

View file

@ -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();

View file

@ -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) {

View file

@ -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();

View file

@ -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();

View file

@ -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),

View file

@ -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);

View file

@ -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)),

View file

@ -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, "")

View file

@ -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) {

View file

@ -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

View file

@ -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");
}