diff --git a/TODO.md b/TODO.md index 7df17b348..1dd87e8d8 100644 --- a/TODO.md +++ b/TODO.md @@ -6,8 +6,6 @@ # to-do for 0.6pre1 -- panning macro - - QSound? - piano/input pad - note input via piano - input pad @@ -47,3 +45,4 @@ - Apply button in settings - better FM chip names (number and codename) - find and replace +- precise panning effects (80xx linear, 81xx/82xx per-channel) diff --git a/papers/doc/7-systems/nes.md b/papers/doc/7-systems/nes.md index 3de0e3076..45ce95bc3 100644 --- a/papers/doc/7-systems/nes.md +++ b/papers/doc/7-systems/nes.md @@ -2,10 +2,13 @@ the console from Nintendo that plays Super Mario Bros. and helped revive the agonizing video game market in the US during mid-80s. -also known as Famicom. It is a five-channel PSG: first two channels play pulse wave with three different duty cycles, third is a fixed-volume triangle channel, fourth is a noise channel (can work in both pseudo-random and periodic modes) and fifth is a (D)PCM sample channel +also known as Famicom. It is a five-channel PSG: first two channels play pulse wave with three different duty cycles, third is a fixed-volume triangle channel, fourth is a noise channel (can work in both pseudo-random and periodic modes) and fifth is a (D)PCM sample channel. # effects +- `11xx`: write to delta modulation counter. + - this may be used to attenuate the triangle and noise channels. + - will not work if a sample is playing. - `12xx`: set duty cycle or noise mode of channel. - may be 0-3 for the pulse channels and 0-1 for the noise channel. - `13xy`: setup sweep up. @@ -15,4 +18,4 @@ also known as Famicom. It is a five-channel PSG: first two channels play pulse w - `14xy`: setup sweep down. - `x` is the time. - `y` is the shift. - - set to 0 to disable it. + - set to 0 to disable it. \ No newline at end of file diff --git a/papers/doc/7-systems/qsound.md b/papers/doc/7-systems/qsound.md index 9da79d822..5d6078aa7 100644 --- a/papers/doc/7-systems/qsound.md +++ b/papers/doc/7-systems/qsound.md @@ -12,7 +12,8 @@ There are also 3 ADPCM channels, however they cannot be used in Furnace yet. The # effects -- `08xx`: Set panning. Valid range is 00-20. 00 for full left, 10 for center and 20 for full right. It is also possible to bypass the QSound algorithm by using the range 30-50. -- `10xx`: Set echo feedback level. This effect will apply to all channels. -- `11xx`: Set echo level. -- `3xxx`: Set the length of the echo delay buffer. +- `10xx`: set echo feedback level. + - this effect will apply to all channels. +- `11xx`: set echo level. +- `12xx`: toggle QSound algorithm (on by default). +- `3xxx`: set the length of the echo delay buffer. diff --git a/papers/format.md b/papers/format.md index feafbe603..570529c4e 100644 --- a/papers/format.md +++ b/papers/format.md @@ -213,6 +213,8 @@ size | description | - 0xb3: Yamaha Y8950 drums - 12 channels | - 0xb4: Konami SCC+ - 5 channels | - 0xb5: tildearrow Sound Unit - 8 channels + | - 0xb6: OPN extended - 9 channels + | - 0xb7: PC-98 extended - 19 channels | - 0xde: YM2610B extended - 19 channels | - 0xe0: QSound - 19 channels | - 0xfd: Dummy System - 8 channels diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index 3a66784d4..198777a85 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -86,6 +86,7 @@ enum DivDispatchCmds { DIV_CMD_PCE_LFO_SPEED, // (speed) DIV_CMD_NES_SWEEP, // (direction, value) + DIV_CMD_NES_DMC, // (value) DIV_CMD_C64_CUTOFF, // (value) DIV_CMD_C64_RESONANCE, // (value) @@ -125,6 +126,7 @@ enum DivDispatchCmds { DIV_CMD_QSOUND_ECHO_FEEDBACK, DIV_CMD_QSOUND_ECHO_DELAY, DIV_CMD_QSOUND_ECHO_LEVEL, + DIV_CMD_QSOUND_SURROUND, DIV_CMD_X1_010_ENVELOPE_SHAPE, DIV_CMD_X1_010_ENVELOPE_ENABLE, diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 751870434..cd4dfa466 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -695,6 +695,20 @@ void DivEngine::swapChannels(int src, int dest) { song.pat[src].effectCols^=song.pat[dest].effectCols; song.pat[dest].effectCols^=song.pat[src].effectCols; song.pat[src].effectCols^=song.pat[dest].effectCols; + + String prevChanName=song.chanName[src]; + String prevChanShortName=song.chanShortName[src]; + bool prevChanShow=song.chanShow[src]; + bool prevChanCollapse=song.chanCollapse[src]; + + song.chanName[src]=song.chanName[dest]; + song.chanShortName[src]=song.chanShortName[dest]; + song.chanShow[src]=song.chanShow[dest]; + song.chanCollapse[src]=song.chanCollapse[dest]; + song.chanName[dest]=prevChanName; + song.chanShortName[dest]=prevChanShortName; + song.chanShow[dest]=prevChanShow; + song.chanCollapse[dest]=prevChanCollapse; } void DivEngine::stompChannel(int ch) { @@ -704,6 +718,10 @@ void DivEngine::stompChannel(int ch) { } song.pat[ch].wipePatterns(); song.pat[ch].effectCols=1; + song.chanName[ch]=""; + song.chanShortName[ch]=""; + song.chanShow[ch]=true; + song.chanCollapse[ch]=false; } void DivEngine::swapChannelsP(int src, int dest) { @@ -1054,6 +1072,28 @@ int DivEngine::calcFreq(int base, int pitch, bool period, int octave, int pitch2 base+((pitch*octave)>>1)+pitch2; } +int DivEngine::convertPanSplitToLinear(unsigned int val, unsigned char bits, int range) { + int panL=val>>bits; + int panR=val&((1<range) val=range; + int maxV=(1<maxV) panL=maxV; + if (panR>maxV) panR=maxV; + return (panL<name=fmt::sprintf("Instrument %d",insCount); ins->type=prefType; saveLock.lock(); @@ -2184,6 +2227,7 @@ void DivEngine::noteOff(int chan) { void DivEngine::autoNoteOn(int ch, int ins, int note, int vol) { bool isViable[DIV_MAX_CHANS]; bool canPlayAnyway=false; + bool notInViableChannel=false; if (midiBaseChan<0) midiBaseChan=0; if (midiBaseChan>=chans) midiBaseChan=chans-1; int finalChan=midiBaseChan; @@ -2197,6 +2241,7 @@ void DivEngine::autoNoteOn(int ch, int ins, int note, int vol) { // 1. check which channels are viable for this instrument DivInstrument* insInst=getIns(ins); + if (getPreferInsType(finalChan)!=insInst->type && getPreferInsSecondType(finalChan)!=insInst->type) notInViableChannel=true; for (int i=0; i=song.insLen || getPreferInsType(i)==insInst->type || getPreferInsSecondType(i)==insInst->type) { if (insInst->type==DIV_INS_OPL) { @@ -2219,7 +2264,7 @@ void DivEngine::autoNoteOn(int ch, int ins, int note, int vol) { // 2. find a free channel do { - if (isViable[finalChan] && chan[finalChan].midiNote==-1 && (insInst->type==DIV_INS_OPL || getChannelType(finalChan)==finalChanType)) { + if (isViable[finalChan] && chan[finalChan].midiNote==-1 && (insInst->type==DIV_INS_OPL || getChannelType(finalChan)==finalChanType || notInViableChannel)) { chan[finalChan].midiNote=note; chan[finalChan].midiAge=midiAgeCounter++; pendingNotes.push(DivNoteEvent(finalChan,ins,note,vol,true)); @@ -2233,7 +2278,7 @@ void DivEngine::autoNoteOn(int ch, int ins, int note, int vol) { // 3. find the oldest channel int candidate=finalChan; do { - if (isViable[finalChan] && (insInst->type==DIV_INS_OPL || getChannelType(finalChan)==finalChanType) && chan[finalChan].midiAgetype==DIV_INS_OPL || getChannelType(finalChan)==finalChanType || notInViableChannel) && chan[finalChan].midiAge=chans) { diff --git a/src/engine/engine.h b/src/engine/engine.h index 0eca8dabf..6c8393c68 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -477,6 +477,10 @@ class DivEngine { // calculate frequency/period int calcFreq(int base, int pitch, bool period=false, int octave=0, int pitch2=0); + // convert panning formats + int convertPanSplitToLinear(unsigned int val, unsigned char bits, int range); + unsigned int convertPanLinearToSplit(int val, unsigned char bits, int range); + // find song loop position void walkSong(int& loopOrder, int& loopRow, int& loopEnd); diff --git a/src/engine/platform/es5506.h b/src/engine/platform/es5506.h index 47a7e2935..f997ff74f 100644 --- a/src/engine/platform/es5506.h +++ b/src/engine/platform/es5506.h @@ -90,9 +90,9 @@ class DivPlatformES5506: public DivDispatch, public es550x_intf { } envChanged; signed int k1Offs, k2Offs; - int vol, lVol, rVol; - int outVol, outLVol, outRVol; - int resLVol, resRVol; + unsigned int vol, lVol, rVol; + unsigned int outVol, outLVol, outRVol; + unsigned int resLVol, resRVol; DivInstrumentES5506::Filter filter; DivInstrumentES5506::Envelope envelope; DivMacroInt std; diff --git a/src/engine/platform/nes.cpp b/src/engine/platform/nes.cpp index 7da1a0908..08c2f79fe 100644 --- a/src/engine/platform/nes.cpp +++ b/src/engine/platform/nes.cpp @@ -59,6 +59,9 @@ const char** DivPlatformNES::getRegisterSheet() { const char* DivPlatformNES::getEffectName(unsigned char effect) { switch (effect) { + case 0x11: + return "Write to delta modulation counter (0 to 7F)"; + break; case 0x12: return "12xx: Set duty cycle/noise mode (pulse: 0 to 3; noise: 0 or 1)"; break; @@ -421,6 +424,9 @@ int DivPlatformNES::dispatch(DivCommand c) { } rWrite(0x4001+(c.chan*4),chan[c.chan].sweep); break; + case DIV_CMD_NES_DMC: + rWrite(0x4011,c.value&0x7f); + break; case DIV_CMD_SAMPLE_BANK: sampleBank=c.value; if (sampleBank>(parent->song.sample.size()/12)) { diff --git a/src/engine/platform/qsound.cpp b/src/engine/platform/qsound.cpp index a38bde376..5887dd9ca 100644 --- a/src/engine/platform/qsound.cpp +++ b/src/engine/platform/qsound.cpp @@ -257,6 +257,9 @@ const char* DivPlatformQSound::getEffectName(unsigned char effect) { case 0x11: return "11xx: Set channel echo level (00 to FF)"; break; + case 0x12: + return "12xx: Toggle QSound algorithm (0: disabled; 1: enabled)"; + break; default: if ((effect & 0xf0) == 0x30) { return "3xxx: Set echo delay buffer length (000 to AA5)"; @@ -335,6 +338,15 @@ void DivPlatformQSound::tick(bool sysTick) { } chan[i].freqChanged=true; } + if (chan[i].std.panL.had) { // panning + chan[i].panning=chan[i].std.panL.val+16; + } + if (chan[i].std.panR.had) { // surround + chan[i].surround=chan[i].std.panR.val; + } + if (chan[i].std.panL.had || chan[i].std.panR.had) { + immWrite(Q1_PAN+i,chan[i].panning+0x110+(chan[i].surround?0:0x30)); + } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { //DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_AMIGA); chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].pitch2); @@ -429,7 +441,8 @@ int DivPlatformQSound::dispatch(DivCommand c) { return chan[c.chan].outVol; break; case DIV_CMD_PANNING: - immWrite(Q1_PAN+c.chan, c.value + 0x110); + chan[c.chan].panning=parent->convertPanSplitToLinear(c.value,4,32); + immWrite(Q1_PAN+c.chan,chan[c.chan].panning+0x110+(chan[c.chan].surround?0:0x30)); break; case DIV_CMD_QSOUND_ECHO_LEVEL: immWrite(Q1_ECHO+c.chan, c.value << 7); @@ -440,6 +453,10 @@ int DivPlatformQSound::dispatch(DivCommand c) { case DIV_CMD_QSOUND_ECHO_DELAY: immWrite(Q1_ECHO_LENGTH, (c.value > 2725 ? 0xfff : 0xfff - (2725 - c.value))); break; + case DIV_CMD_QSOUND_SURROUND: + chan[c.chan].surround=c.value; + immWrite(Q1_PAN+c.chan,chan[c.chan].panning+0x110+(chan[c.chan].surround?0:0x30)); + break; case DIV_CMD_PITCH: chan[c.chan].pitch=c.value; chan[c.chan].freqChanged=true; diff --git a/src/engine/platform/qsound.h b/src/engine/platform/qsound.h index 1eb9b2751..6eb178d08 100644 --- a/src/engine/platform/qsound.h +++ b/src/engine/platform/qsound.h @@ -33,7 +33,7 @@ class DivPlatformQSound: public DivDispatch { int sample, wave, ins; int note; int panning; - bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, useWave; + bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, useWave, surround; int vol, outVol; DivMacroInt std; void macroInit(DivInstrument* which) { @@ -57,6 +57,8 @@ class DivPlatformQSound: public DivDispatch { keyOn(false), keyOff(false), inPorta(false), + useWave(false), + surround(true), vol(255), outVol(255) {} }; diff --git a/src/engine/platform/su.cpp b/src/engine/platform/su.cpp index 6efa0a482..4733e5b24 100644 --- a/src/engine/platform/su.cpp +++ b/src/engine/platform/su.cpp @@ -270,7 +270,7 @@ int DivPlatformSoundUnit::dispatch(DivCommand c) { break; } case DIV_CMD_PANNING: { - chan[c.chan].pan=c.value; + chan[c.chan].pan=parent->convertPanSplitToLinear(c.value,4,254)-127; chWrite(c.chan,0x03,chan[c.chan].pan); break; } diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index cb7e823e1..57d89be71 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -88,6 +88,7 @@ const char* cmdName[]={ "PCE_LFO_SPEED", "NES_SWEEP", + "NES_DMC", "C64_CUTOFF", "C64_RESONANCE", @@ -127,6 +128,7 @@ const char* cmdName[]={ "QSOUND_ECHO_FEEDBACK", "QSOUND_ECHO_DELAY", "QSOUND_ECHO_LEVEL", + "QSOUND_SURROUND", "X1_010_ENVELOPE_SHAPE", "X1_010_ENVELOPE_ENABLE", @@ -325,6 +327,9 @@ bool DivEngine::perSystemEffect(int ch, unsigned char effect, unsigned char effe case DIV_SYSTEM_NES: case DIV_SYSTEM_MMC5: switch (effect) { + case 0x11: // DMC write + dispatchCmd(DivCommand(DIV_CMD_NES_DMC,ch,effectVal)); + break; case 0x12: // duty or noise mode dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_MODE,ch,effectVal)); break; @@ -443,6 +448,9 @@ bool DivEngine::perSystemEffect(int ch, unsigned char effect, unsigned char effe case 0x11: // echo level dispatchCmd(DivCommand(DIV_CMD_QSOUND_ECHO_LEVEL,ch,effectVal)); break; + case 0x12: // surround + dispatchCmd(DivCommand(DIV_CMD_QSOUND_SURROUND,ch,effectVal)); + break; default: if ((effect&0xf0)==0x30) { dispatchCmd(DivCommand(DIV_CMD_QSOUND_ECHO_DELAY,ch,((effect & 0x0f) << 8) | effectVal)); diff --git a/src/engine/song.h b/src/engine/song.h index 087d8c7b0..8e67cce83 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -347,7 +347,7 @@ struct DivSong { bool chanShow[DIV_MAX_CHANS]; bool chanCollapse[DIV_MAX_CHANS]; - DivInstrument nullIns, nullInsOPLL, nullInsOPL; + DivInstrument nullIns, nullInsOPLL, nullInsOPL, nullInsQSound; DivWavetable nullWave; DivSample nullSample; @@ -473,6 +473,8 @@ struct DivSong { nullInsOPL.fm.op[1].rr=12; nullInsOPL.fm.op[1].mult=1; nullInsOPL.name="This is a bug! Report!"; + + nullInsQSound.std.panLMacro.mode=true; } }; diff --git a/src/gui/debug.cpp b/src/gui/debug.cpp index 5915739cb..6394b40c3 100644 --- a/src/gui/debug.cpp +++ b/src/gui/debug.cpp @@ -337,7 +337,7 @@ void putDispatchChan(void* data, int chanNum, int type) { case DIV_SYSTEM_ES5506: { DivPlatformES5506::Channel* ch=(DivPlatformES5506::Channel*)data; ImGui::Text("> ES5506"); - ImGui::Text("* freq: %.4x",ch->freq); + ImGui::Text("* freq: %.5x",ch->freq); ImGui::Text(" - base: %d",ch->baseFreq); ImGui::Text(" - pitch: %d",ch->pitch); ImGui::Text(" - pitch2: %d",ch->pitch2); diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 56e046f08..74a303542 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -2903,6 +2903,10 @@ bool FurnaceGUI::loop() { if (fileDialog->render(ImVec2(600.0f*dpiScale,400.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale))) { bool openOpen=false; //ImGui::GetIO().ConfigFlags&=~ImGuiConfigFlags_NavEnableKeyboard; + if (curFileDialog==GUI_FILE_INS_OPEN && prevIns!=-3) { + curIns=prevIns; + prevIns=-3; + } switch (curFileDialog) { case GUI_FILE_OPEN: case GUI_FILE_SAVE: @@ -2947,9 +2951,6 @@ bool FurnaceGUI::loop() { workingDirLayout=fileDialog->getPath()+DIR_SEPARATOR_STR; break; } - if (prevIns!=-3) { - curIns=prevIns; - } if (fileDialog->accepted()) { fileName=fileDialog->getFileName(); if (fileName!="") { @@ -3787,6 +3788,7 @@ FurnaceGUI::FurnaceGUI(): preserveChanPos(false), vgmExportVersion(0x171), drawHalt(10), + macroPointSize(16), curFileDialog(GUI_FILE_OPEN), warnAction(GUI_WARN_OPEN), postWarnAction(GUI_WARN_GENERIC), diff --git a/src/gui/gui.h b/src/gui/gui.h index 14b735b22..31ab566f1 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -739,6 +739,7 @@ class FurnaceGUI { bool willExport[32]; int vgmExportVersion; int drawHalt; + int macroPointSize; FurnaceGUIFileDialogs curFileDialog; FurnaceGUIWarnings warnAction; diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 42d377288..cc1c6f49d 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -228,6 +228,11 @@ const char* macroRelativeMode[3]={ "Relative", NULL }; +const char* macroQSoundMode[3]={ + "Independent", + "QSound", + NULL +}; const char* macroDummyMode[1]={NULL}; @@ -1120,7 +1125,7 @@ void FurnaceGUI::drawGBEnv(unsigned char vol, unsigned char len, unsigned char s } \ if (macroMode && displayModeName[0]!=NULL) { \ for (unsigned int m=0; displayModeName[m]!=NULL; m++) { \ - if (ImGui::RadioButton(displayModeName[m],macro.mode==m)) { \ + if (ImGui::RadioButton(fmt::sprintf("%s##IMacroMode_%s",displayModeName[m],macro.name).c_str(),macro.mode==m)) { \ macro.mode=m; \ } \ } \ @@ -1166,6 +1171,11 @@ void FurnaceGUI::drawGBEnv(unsigned char vol, unsigned char len, unsigned char s processDrags(ImGui::GetMousePos().x,ImGui::GetMousePos().y); \ } \ if (displayLoop) { \ + if (ImGui::IsItemHovered() && ctrlWheeling) { \ + macroPointSize+=wheelY; \ + if (macroPointSize<1) macroPointSize=1; \ + if (macroPointSize>256) macroPointSize=256; \ + } \ if (drawSlider) { \ ImGui::SameLine(); \ CWVSliderInt("##IMacroPos_" macroName,ImVec2(20.0f*dpiScale,displayHeight*dpiScale),sliderVal,sliderLow,sliderHigh); \ @@ -1215,7 +1225,7 @@ void FurnaceGUI::drawGBEnv(unsigned char vol, unsigned char len, unsigned char s } \ if (macroMode && displayModeName[0]!=NULL) { \ for (unsigned int m=0; displayModeName[m]!=NULL; m++) { \ - if (ImGui::RadioButton(displayModeName[m],macro.mode==m)) { \ + if (ImGui::RadioButton(fmt::sprintf("%s##IOPMacroMode_%d%s",displayModeName[m],op,macro.name).c_str(),macro.mode==m)) { \ macro.mode=m; \ } \ } \ @@ -1301,7 +1311,7 @@ if (ImGui::BeginTable("MacroSpace",2)) { \ ImGui::Dummy(ImVec2(120.0f*dpiScale,dpiScale)); \ ImGui::TableNextColumn(); \ float availableWidth=ImGui::GetContentRegionAvail().x-reservedSpace; \ - int totalFit=MIN(127,availableWidth/(16*dpiScale)); \ + int totalFit=MIN(127,availableWidth/MAX(1,macroPointSize*dpiScale)); \ if (macroDragScroll>127-totalFit) { \ macroDragScroll=127-totalFit; \ } \ @@ -2934,8 +2944,10 @@ void FurnaceGUI::drawInsEdit() { ex2Max=65535; } + int panMin=0; int panMax=0; bool panSingle=false; + bool panSingleNoBit=false; if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPL || ins->type==DIV_INS_GB || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_VERA) { panMax=1; panSingle=true; @@ -2949,6 +2961,15 @@ void FurnaceGUI::drawInsEdit() { if (ins->type==DIV_INS_ES5506) { panMax=65535; } + if (ins->type==DIV_INS_AMIGA && ins->std.panLMacro.mode) { + panMin=-16; + panMax=16; + } + if (ins->type==DIV_INS_SU) { + panMin=-127; + panMax=127; + panSingleNoBit=true; + } if (settings.macroView==0) { // modern view MACRO_BEGIN(28*dpiScale); @@ -2974,8 +2995,18 @@ void FurnaceGUI::drawInsEdit() { if (panSingle) { NORMAL_MACRO(ins->std.panLMacro,0,2,"panL","Panning",32,ins->std.panLMacro.open,true,panBits,false,NULL,0,0,0,0,false,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[13],0,panMax,NULL,false); } else { - NORMAL_MACRO(ins->std.panLMacro,0,panMax,"panL","Panning (left)",MIN(160,(31+panMax)),ins->std.panLMacro.open,false,NULL,false,NULL,0,0,0,0,false,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[13],0,panMax,NULL,false); - NORMAL_MACRO(ins->std.panRMacro,0,panMax,"panR","Panning (right)",MIN(160,(31+panMax)),ins->std.panRMacro.open,false,NULL,false,NULL,0,0,0,0,false,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[14],0,panMax,NULL,false); + if (panSingleNoBit || (ins->type==DIV_INS_AMIGA && ins->std.panLMacro.mode)) { + NORMAL_MACRO(ins->std.panLMacro,panMin,panMax,"panL","Panning",MIN(160,(31+panMax-panMin)),ins->std.panLMacro.open,false,NULL,false,NULL,0,0,0,0,(ins->type==DIV_INS_AMIGA),macroQSoundMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[13],panMin,panMax,NULL,false); + } else { + NORMAL_MACRO(ins->std.panLMacro,panMin,panMax,"panL","Panning (left)",MIN(160,(31+panMax-panMin)),ins->std.panLMacro.open,false,NULL,false,NULL,0,0,0,0,(ins->type==DIV_INS_AMIGA),macroQSoundMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[13],panMin,panMax,NULL,false); + } + if (!panSingleNoBit) { + if (ins->type==DIV_INS_AMIGA && ins->std.panLMacro.mode) { + NORMAL_MACRO(ins->std.panRMacro,0,1,"panR","Surround",32,ins->std.panRMacro.open,true,NULL,false,NULL,0,0,0,0,false,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[14],0,1,NULL,false); + } else { + NORMAL_MACRO(ins->std.panRMacro,panMin,panMax,"panR","Panning (right)",MIN(160,(31+panMax-panMin)),ins->std.panRMacro.open,false,NULL,false,NULL,0,0,0,0,false,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[14],panMin,panMax,NULL,false); + } + } } } NORMAL_MACRO(ins->std.pitchMacro,pitchMacroScroll,pitchMacroScroll+160,"pitch","Pitch",160,ins->std.pitchMacro.open,false,NULL,true,&pitchMacroScroll,-2048,2047,0,0,true,macroRelativeMode,uiColors[GUI_COLOR_MACRO_PITCH],mmlString[15],-2048,2047,NULL,!ins->std.pitchMacro.mode); diff --git a/src/gui/newSong.cpp b/src/gui/newSong.cpp index d6188b3e5..2bf11fb5c 100644 --- a/src/gui/newSong.cpp +++ b/src/gui/newSong.cpp @@ -66,6 +66,22 @@ void FurnaceGUI::drawNewSong() { ImGui::EndTable(); } + if (ImGui::Button("I'm feeling lucky")) { + if (sysCategories.size()==0) { + ImGui::CloseCurrentPopup(); + } else { + FurnaceGUISysCategory* newSystemCat=&sysCategories[rand()%sysCategories.size()]; + if (newSystemCat->systems.size()==0) { + ImGui::CloseCurrentPopup(); + } else { + nextDesc=newSystemCat->systems[rand()%newSystemCat->systems.size()].definition.data(); + accepted=true; + } + } + } + + ImGui::SameLine(); + if (ImGui::Button("Cancel")) { ImGui::CloseCurrentPopup(); } @@ -87,4 +103,4 @@ void FurnaceGUI::drawNewSong() { updateWindowTitle(); ImGui::CloseCurrentPopup(); } -} \ No newline at end of file +} diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp index 42f9cb8f8..b206f680f 100644 --- a/src/gui/sampleEdit.cpp +++ b/src/gui/sampleEdit.cpp @@ -1333,6 +1333,17 @@ void FurnaceGUI::drawSampleEdit() { } if (ImGui::IsItemHovered()) { + if (ctrlWheeling) { + double zoomPercent=100.0/sampleZoom; + zoomPercent+=wheelY*10.0; + if (zoomPercent>10000.0) zoomPercent=10000.0; + if (zoomPercent<1.0) zoomPercent=1.0; + sampleZoom=100.0/zoomPercent; + if (sampleZoom<0.01) sampleZoom=0.01; + sampleZoomAuto=false; + updateSampleTex=true; + } + int posX=-1; int posY=0; ImVec2 pos=ImGui::GetMousePos();