diff --git a/src/engine/fileOps/it.cpp b/src/engine/fileOps/it.cpp index 5fcfd559b..e3be8b09b 100644 --- a/src/engine/fileOps/it.cpp +++ b/src/engine/fileOps/it.cpp @@ -39,17 +39,11 @@ bool DivEngine::loadIT(unsigned char* file, size_t len) { try { DivSong ds; ds.version=DIV_VERSION_XM; - //ds.linearPitch=0; - //ds.pitchMacroIsLinear=false; ds.noSlidesOnFirstTick=true; ds.rowResetsArpPos=true; ds.ignoreJumpAtEnd=false; ds.pitchSlideSpeed=4; - ds.system[0]=DIV_SYSTEM_ES5506; - ds.systemFlags[0].set("amigaVol",true); - ds.systemLen=1; - logV("Impulse Tracker module"); // load here @@ -81,6 +75,12 @@ bool DivEngine::loadIT(unsigned char* file, size_t len) { unsigned short flags=reader.readS(); unsigned short special=reader.readS(); + if (flags&8) { + ds.linearPitch=2; + } else { + ds.linearPitch=0; + } + unsigned char globalVol=reader.readC(); unsigned char masterVol=reader.readC(); unsigned char initSpeed=reader.readC(); @@ -399,6 +399,7 @@ bool DivEngine::loadIT(unsigned char* file, size_t len) { } // read patterns + int maxChan=0; for (int i=0; imaxChan) maxChan=chan; + if (mask[chan]&1) { note[chan]=reader.readC(); hasNote=true; @@ -510,8 +513,14 @@ bool DivEngine::loadIT(unsigned char* file, size_t len) { } } - logV("DAMN IT: %x",insPtr[0]); - logV("DAMN IT: %x",samplePtr[0]); + for (int i=0; i<(maxChan+32)>>5; i++) { + ds.system[i]=DIV_SYSTEM_ES5506; + ds.systemFlags[i].set("amigaVol",true); + if (ds.linearPitch!=2) { + ds.systemFlags[i].set("amigaPitch",true); + } + } + ds.systemLen=(maxChan+32)>>5; if (active) quitDispatch(); BUSY_BEGIN_SOFT; diff --git a/src/engine/fileOps/s3m.cpp b/src/engine/fileOps/s3m.cpp index 7b3198b55..49008037b 100644 --- a/src/engine/fileOps/s3m.cpp +++ b/src/engine/fileOps/s3m.cpp @@ -67,8 +67,6 @@ bool DivEngine::loadS3M(unsigned char* file, size_t len) { bool doesVolSlide[32]; bool doesArp[32]; - bool mustCommitPanning=true; - memset(doesPitchSlide,0,32*sizeof(bool)); memset(doesVibrato,0,32*sizeof(bool)); memset(doesPanning,0,32*sizeof(bool)); @@ -78,8 +76,8 @@ bool DivEngine::loadS3M(unsigned char* file, size_t len) { try { DivSong ds; ds.version=DIV_VERSION_S3M; - //ds.linearPitch=0; - //ds.pitchMacroIsLinear=false; + ds.linearPitch=0; + ds.pitchMacroIsLinear=false; ds.noSlidesOnFirstTick=true; ds.rowResetsArpPos=true; ds.ignoreJumpAtEnd=false; @@ -167,8 +165,6 @@ bool DivEngine::loadS3M(unsigned char* file, size_t len) { reader.readC(); // UC unsigned char defaultPan=(unsigned char)reader.readC(); - mustCommitPanning=masterVol&128; - logV("defaultPan: %d",defaultPan); reader.readS(); // reserved @@ -251,16 +247,16 @@ bool DivEngine::loadS3M(unsigned char* file, size_t len) { if (hasFM && hasPCM) break; } + logV("numChans: %d",numChans); + ds.systemName="PC"; - // would use ES5506 but it has log volume if (hasPCM) { - for (int i=0; itype=DIV_INS_AMIGA; + ins->type=DIV_INS_ES5506; } else if (memcmp(magic,"SCRI",4)==0) { ins->type=DIV_INS_OPL; } else { @@ -305,7 +301,7 @@ bool DivEngine::loadS3M(unsigned char* file, size_t len) { unsigned char type=reader.readC(); - if (ins->type==DIV_INS_AMIGA) { + if (ins->type==DIV_INS_ES5506) { if (type>1) { logE("invalid instrument type! %d",type); lastError="invalid instrument!"; @@ -325,7 +321,7 @@ bool DivEngine::loadS3M(unsigned char* file, size_t len) { String dosName=reader.readString(12); - if (ins->type==DIV_INS_AMIGA) { + if (ins->type==DIV_INS_ES5506) { unsigned int memSeg=0; memSeg=(unsigned char)reader.readC(); memSeg|=((unsigned short)reader.readS())<<8; @@ -491,6 +487,77 @@ bool DivEngine::loadS3M(unsigned char* file, size_t len) { } ds.sampleLen=ds.sample.size(); + // scan pattern data for effect use + for (int i=0; i=64) break; + continue; + } + + unsigned char chan=what&31; + bool hasNoteIns=what&32; + bool hasVol=what&64; + bool hasEffect=what&128; + + if (hasNoteIns) { + reader.readC(); + reader.readC(); + } + if (hasVol) { + reader.readC(); + } + if (hasEffect) { + unsigned char effect=reader.readC(); + reader.readC(); // effect val + + switch (effect+'A'-1) { + case 'D': // vol slide + doesVolSlide[chan]=true; + break; + case 'E': // pitch down + doesPitchSlide[chan]=true; + break; + case 'F': // pitch up + doesPitchSlide[chan]=true; + break; + case 'G': // porta + doesPitchSlide[chan]=true; + break; + case 'H': // vibrato + doesVibrato[chan]=true; + break; + case 'J': // arp + doesArp[chan]=true; + break; + case 'K': // vol slide + vibrato + doesVolSlide[chan]=true; + doesVibrato[chan]=true; + break; + case 'L': // vol slide + porta + doesVolSlide[chan]=true; + doesPitchSlide[chan]=true; + break; + } + } + } + } + // load pattern data for (int i=0; idata[curRow][effectCol[j]++]=0; } - // TEMPORARY: shall be moved to the end after subsong copying. - if (j<16) { - if (mustCommitPanning && i==ds.subsong[0]->orders.ord[j][0]) { - p->data[curRow][effectCol[j]++]=0x80; - if (chanPan[j]&16) { - p->data[curRow][effectCol[j]++]=(j&1)?0xcc:0x33; - } else { - p->data[curRow][effectCol[j]++]=(chanPan[j]&15)|((chanPan[j]&15)<<4); - } - } - } - if ((effectCol[j]>>1)-2>ds.subsong[0]->pat[j].effectCols) { ds.subsong[0]->pat[j].effectCols=(effectCol[j]>>1)-1; } } - if (i==ds.subsong[0]->orders.ord[0][0]) { - mustCommitPanning=false; - } - curRow++; memset(effectCol,4,32); memcpy(vibingOld,vibing,32*sizeof(bool)); @@ -821,17 +872,38 @@ bool DivEngine::loadS3M(unsigned char* file, size_t len) { } } } + } - // copy patterns to the rest of subsongs - for (size_t i=1; ipat[j].data[k]) ds.subsong[0]->pat[j].data[k]->copyOn(ds.subsong[i]->pat[j].getPattern(k,true)); - } - ds.subsong[i]->pat[j].effectCols=ds.subsong[0]->pat[j].effectCols; + // copy patterns to the rest of subsongs + for (size_t i=1; ipat[j].data[k]) ds.subsong[0]->pat[j].data[k]->copyOn(ds.subsong[i]->pat[j].getPattern(k,true)); + } + ds.subsong[i]->pat[j].effectCols=ds.subsong[0]->pat[j].effectCols; + } + ds.subsong[i]->speeds=ds.subsong[0]->speeds; + ds.subsong[i]->hz=ds.subsong[0]->hz; + } + + // populate subsongs with default panning values + if (masterVol&128) { // only in stereo mode + for (size_t i=0; ipat[j].getPattern(ds.subsong[i]->orders.ord[j][0],true); + for (int k=0; kdata[0][4+(k<<1)]==-1) { + p->data[0][4+(k<<1)]=0x80; + if (chanPan[j]&16) { + p->data[0][5+(k<<1)]=(j&1)?0xcc:0x33; + } else { + p->data[0][5+(k<<1)]=(chanPan[j]&15)|((chanPan[j]&15)<<4); + } + if (ds.subsong[i]->pat[j].effectCols<=k) ds.subsong[i]->pat[j].effectCols=k+1; + break; + } + } } - ds.subsong[i]->speeds=ds.subsong[0]->speeds; - ds.subsong[i]->hz=ds.subsong[0]->hz; } } diff --git a/src/engine/fileOps/xm.cpp b/src/engine/fileOps/xm.cpp index bc4589046..8ebc32785 100644 --- a/src/engine/fileOps/xm.cpp +++ b/src/engine/fileOps/xm.cpp @@ -103,6 +103,7 @@ bool DivEngine::loadXM(unsigned char* file, size_t len) { for (int i=0; i<(totalChans+31)>>5; i++) { ds.system[i]=DIV_SYSTEM_ES5506; ds.systemFlags[i].set("amigaVol",true); + ds.systemFlags[i].set("amigaPitch",(ds.linearPitch==0)); } ds.systemLen=(totalChans+31)>>5; diff --git a/src/engine/platform/es5506.cpp b/src/engine/platform/es5506.cpp index 2992464e8..a8103a546 100644 --- a/src/engine/platform/es5506.cpp +++ b/src/engine/platform/es5506.cpp @@ -23,7 +23,7 @@ #include #define PITCH_OFFSET ((double)(16*2048*(chanMax+1))) -#define NOTE_ES5506(c,note) (parent->calcBaseFreq(chipClock,chan[c].pcm.freqOffs,note,false)) +#define NOTE_ES5506(c,note) ((amigaPitch && parent->song.linearPitch!=2)?parent->calcBaseFreq(COLOR_NTSC,chan[c].pcm.freqOffs,note,true):parent->calcBaseFreq(chipClock,chan[c].pcm.freqOffs,note,false)) #define rWrite(a,...) {if(!skipRegisterWrites) {hostIntf32.push_back(QueuedHostIntf(4,(a),__VA_ARGS__)); }} #define immWrite(a,...) {hostIntf32.push_back(QueuedHostIntf(4,(a),__VA_ARGS__));} @@ -183,7 +183,7 @@ void DivPlatformES5506::irqb(bool state) { // modified GUSVolumeTable from Impulse Tracker (SoundDrivers/GUS.INC) static const short amigaVolTable[65]={ - 0x400, 0x8FF, 0x9FF, 0xA80, + 0x000, 0x8FF, 0x9FF, 0xA80, 0xAFF, 0xB40, 0xB80, 0xBC0, 0xBFF, 0xC20, 0xC40, 0xC60, 0xC80, 0xCA0, 0xCC0, 0xCE0, @@ -202,6 +202,26 @@ static const short amigaVolTable[65]={ 0xEFF }; +// same thing +static const short amigaPanTable[128]={ + 0x000, 0x8FF, 0x9FF, 0xA80, 0xAFF, 0xB40, 0xB80, 0xBC0, + 0xBFF, 0xC20, 0xC40, 0xC60, 0xC80, 0xCA0, 0xCC0, 0xCE0, + 0xCFF, 0xD10, 0xD20, 0xD30, 0xD40, 0xD50, 0xD60, 0xD70, + 0xD80, 0xD90, 0xDA0, 0xDB0, 0xDC0, 0xDD0, 0xDE0, 0xDF0, + 0xDFF, 0xE08, 0xE10, 0xE18, 0xE20, 0xE28, 0xE30, 0xE38, + 0xE40, 0xE48, 0xE50, 0xE58, 0xE60, 0xE68, 0xE70, 0xE78, + 0xE80, 0xE88, 0xE90, 0xE98, 0xEA0, 0xEA8, 0xEB0, 0xEB8, + 0xEC0, 0xEC8, 0xED0, 0xED8, 0xEE0, 0xEE8, 0xEF0, 0xEF8, + 0xEFF, 0xF04, 0xF08, 0xF0C, 0xF10, 0xF14, 0xF18, 0xF1C, + 0xF20, 0xF24, 0xF28, 0xF2C, 0xF30, 0xF34, 0xF38, 0xF3C, + 0xF40, 0xF44, 0xF48, 0xF4C, 0xF50, 0xF54, 0xF58, 0xF5C, + 0xF60, 0xF64, 0xF68, 0xF6C, 0xF70, 0xF74, 0xF78, 0xF7C, + 0xF80, 0xF84, 0xF88, 0xF8C, 0xF90, 0xF94, 0xF98, 0xF9C, + 0xFA0, 0xFA4, 0xFA8, 0xFAC, 0xFB0, 0xFB4, 0xFB8, 0xFBC, + 0xFC0, 0xFC4, 0xFC8, 0xFCC, 0xFD0, 0xFD4, 0xFD8, 0xFDC, + 0xFE0, 0xFE4, 0xFE8, 0xFEC, 0xFF0, 0xFF4, 0xFF8, 0xFFF +}; + void DivPlatformES5506::tick(bool sysTick) { for (int i=0; i<=chanMax; i++) { chan[i].std.next(); @@ -453,7 +473,7 @@ void DivPlatformES5506::tick(bool sysTick) { const unsigned int length=s->samples-1; const unsigned int end=start+(length<<11); const unsigned int nextBank=(offES5506>>22)&3; - const double nextFreqOffs=PITCH_OFFSET*off; + const double nextFreqOffs=((amigaPitch && parent->song.linearPitch!=2)?16:PITCH_OFFSET)*off; chan[i].pcm.loopMode=loopMode; chan[i].pcm.bank=nextBank; chan[i].pcm.start=start; @@ -616,7 +636,12 @@ void DivPlatformES5506::tick(bool sysTick) { chan[i].pcm.nextPos=0; } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=CLAMP(parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,false,2,chan[i].pitch2,chipClock,chan[i].pcm.freqOffs),0,0x1ffff); + if (amigaPitch && parent->song.linearPitch!=2) { + chan[i].freq=CLAMP(parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,true,2,chan[i].pitch2,COLOR_NTSC,chan[i].pcm.freqOffs),1,0xffff); + chan[i].freq=32768*(COLOR_NTSC/chan[i].freq)/(chipClock/32.0); + } else { + chan[i].freq=CLAMP(parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,false,2,chan[i].pitch2,chipClock,chan[i].pcm.freqOffs),0,0x1ffff); + } if (chan[i].keyOn) { if (chan[i].pcm.index>=0 && chan[i].pcm.indexsong.sampleLen) { const int ind=chan[i].pcm.index; @@ -631,7 +656,7 @@ void DivPlatformES5506::tick(bool sysTick) { } chan[i].pcm.loopStart=(chan[i].pcm.start+(s->loopStart<<11))&0xfffff800; chan[i].pcm.loopEnd=(chan[i].pcm.start+((s->loopEnd)<<11))&0xffffff80; - chan[i].pcm.freqOffs=PITCH_OFFSET*off; + chan[i].pcm.freqOffs=((amigaPitch && parent->song.linearPitch!=2)?16:PITCH_OFFSET)*off; unsigned int startPos=chan[i].pcm.direction?chan[i].pcm.end:chan[i].pcm.start; if (chan[i].pcm.nextPos) { const unsigned int start=chan[i].pcm.start; @@ -817,11 +842,20 @@ int DivPlatformES5506::dispatch(DivCommand c) { chan[c.chan].outVol=(0xfff*chan[c.chan].vol)/0xff; } } - if (!chan[c.chan].std.panL.will) { - chan[c.chan].outLVol=(0xfff*chan[c.chan].lVol)/0xff; - } - if (!chan[c.chan].std.panR.will) { - chan[c.chan].outRVol=(0xfff*chan[c.chan].rVol)/0xff; + if (amigaVol) { + if (!chan[c.chan].std.panL.will) { + chan[c.chan].outLVol=amigaPanTable[(chan[c.chan].lVol>>1)&127]; + } + if (!chan[c.chan].std.panR.will) { + chan[c.chan].outRVol=amigaPanTable[(chan[c.chan].rVol>>1)&127]; + } + } else { + if (!chan[c.chan].std.panL.will) { + chan[c.chan].outLVol=(0xfff*chan[c.chan].lVol)/0xff; + } + if (!chan[c.chan].std.panR.will) { + chan[c.chan].outRVol=(0xfff*chan[c.chan].rVol)/0xff; + } } chan[c.chan].active=true; chan[c.chan].keyOn=true; @@ -865,20 +899,39 @@ int DivPlatformES5506::dispatch(DivCommand c) { chan[c.chan].ca=0; chan[c.chan].volChanged.ca=1; } - // Left volume - if (chan[c.chan].lVol!=c.value) { - chan[c.chan].lVol=c.value; - if (!chan[c.chan].std.panL.has) { - chan[c.chan].outLVol=(0xfff*c.value)/0xff; - chan[c.chan].volChanged.lVol=1; + if (amigaVol) { + // Left volume + if (chan[c.chan].lVol!=c.value) { + chan[c.chan].lVol=c.value; + if (!chan[c.chan].std.panL.has) { + chan[c.chan].outLVol=amigaPanTable[(c.value>>1)&127]; + chan[c.chan].volChanged.lVol=1; + } } - } - // Right volume - if (chan[c.chan].rVol!=c.value2) { - chan[c.chan].rVol=c.value2; - if (!chan[c.chan].std.panR.has) { - chan[c.chan].outRVol=(0xfff*c.value2)/0xff; - chan[c.chan].volChanged.rVol=1; + // Right volume + if (chan[c.chan].rVol!=c.value2) { + chan[c.chan].rVol=c.value2; + if (!chan[c.chan].std.panR.has) { + chan[c.chan].outRVol=amigaPanTable[(c.value2>>1)&127]; + chan[c.chan].volChanged.rVol=1; + } + } + } else { + // Left volume + if (chan[c.chan].lVol!=c.value) { + chan[c.chan].lVol=c.value; + if (!chan[c.chan].std.panL.has) { + chan[c.chan].outLVol=(0xfff*c.value)/0xff; + chan[c.chan].volChanged.lVol=1; + } + } + // Right volume + if (chan[c.chan].rVol!=c.value2) { + chan[c.chan].rVol=c.value2; + if (!chan[c.chan].std.panR.has) { + chan[c.chan].outRVol=(0xfff*c.value2)/0xff; + chan[c.chan].volChanged.rVol=1; + } } } break; @@ -1028,7 +1081,7 @@ int DivPlatformES5506::dispatch(DivCommand c) { break; case DIV_CMD_NOTE_PORTA: { int nextFreq=chan[c.chan].baseFreq; - const int destFreq=NOTE_ES5506(c.chan,c.value2+chan[c.chan].sampleNoteDelta); + int destFreq=NOTE_ES5506(c.chan,c.value2+chan[c.chan].sampleNoteDelta); bool return2=false; if (destFreq>nextFreq) { nextFreq+=c.value; @@ -1193,6 +1246,7 @@ void DivPlatformES5506::setFlags(const DivConfig& flags) { initChanMax=MAX(4,flags.getInt("channels",0x1f)&0x1f); volScale=4095-flags.getInt("volScale",4095); amigaVol=flags.getBool("amigaVol",false); + amigaPitch=flags.getBool("amigaPitch",false); chanMax=initChanMax; pageWriteMask(0x00,0x60,0x0b,chanMax); diff --git a/src/engine/platform/es5506.h b/src/engine/platform/es5506.h index 7c5735a6e..de368c77e 100644 --- a/src/engine/platform/es5506.h +++ b/src/engine/platform/es5506.h @@ -272,7 +272,7 @@ class DivPlatformES5506: public DivDispatch, public es550x_intf { unsigned char maskedVal; unsigned int irqv; bool isMasked, isReaded; - bool irqTrigger, amigaVol; + bool irqTrigger, amigaVol, amigaPitch; unsigned int curCR; unsigned char initChanMax, chanMax; diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 64a04e3c6..d6007fedb 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -7173,8 +7173,17 @@ void FurnaceGUI::drawInsEdit() { macroList.push_back(FurnaceGUIMacroDesc(_("Arpeggio"),&ins->std.arpMacro,-120,120,160,uiColors[GUI_COLOR_MACRO_PITCH],true,NULL,macroHoverNote,false,NULL,true,ins->std.arpMacro.val)); macroList.push_back(FurnaceGUIMacroDesc(_("Pitch"),&ins->std.pitchMacro,-2048,2047,160,uiColors[GUI_COLOR_MACRO_PITCH],true,macroRelativeMode)); break; - case DIV_INS_ES5506: - macroList.push_back(FurnaceGUIMacroDesc(_("Volume"),&ins->std.volMacro,0,4095,160,uiColors[GUI_COLOR_MACRO_VOLUME])); + case DIV_INS_ES5506: { + float usesAmigaVol=true; + for (int i=0; isong.systemLen; i++) { + if (e->song.system[i]==DIV_SYSTEM_ES5506) { + if (!e->song.systemFlags[i].getBool("amigaVol",false)) { + usesAmigaVol=false; + break; + } + } + } + macroList.push_back(FurnaceGUIMacroDesc(_("Volume"),&ins->std.volMacro,0,usesAmigaVol?64:4095,160,uiColors[GUI_COLOR_MACRO_VOLUME])); macroList.push_back(FurnaceGUIMacroDesc(_("Arpeggio"),&ins->std.arpMacro,-120,120,160,uiColors[GUI_COLOR_MACRO_PITCH],true,NULL,macroHoverNote,false,NULL,true,ins->std.arpMacro.val)); macroList.push_back(FurnaceGUIMacroDesc(_("Filter Mode"),&ins->std.dutyMacro,0,3,160,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,¯oHoverES5506FilterMode)); macroList.push_back(FurnaceGUIMacroDesc(_("Panning (left)"),&ins->std.panLMacro,0,4095,160,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL)); @@ -7186,6 +7195,7 @@ void FurnaceGUI::drawInsEdit() { macroList.push_back(FurnaceGUIMacroDesc(_("Outputs"),&ins->std.fbMacro,0,5,64,uiColors[GUI_COLOR_MACRO_OTHER])); macroList.push_back(FurnaceGUIMacroDesc(_("Control"),&ins->std.algMacro,0,2,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,es5506ControlModes)); break; + } case DIV_INS_MULTIPCM: macroList.push_back(FurnaceGUIMacroDesc(_("Volume"),&ins->std.volMacro,0,127,160,uiColors[GUI_COLOR_MACRO_VOLUME])); macroList.push_back(FurnaceGUIMacroDesc(_("Arpeggio"),&ins->std.arpMacro,-120,120,160,uiColors[GUI_COLOR_MACRO_PITCH],true,NULL,macroHoverNote,false,NULL,true,ins->std.arpMacro.val)); diff --git a/src/gui/sysConf.cpp b/src/gui/sysConf.cpp index 06d61996c..fea0ad025 100644 --- a/src/gui/sysConf.cpp +++ b/src/gui/sysConf.cpp @@ -1273,6 +1273,7 @@ bool FurnaceGUI::drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& fl int channels=flags.getInt("channels",0x1f)+1; int volScale=flags.getInt("volScale",4095); bool amigaVol=flags.getBool("amigaVol",false); + bool amigaPitch=flags.getBool("amigaPitch",false); ImGui::Text(_("Initial channel limit:")); if (CWSliderInt("##OTTO_InitialChannelLimit",&channels,5,32)) { if (channels<5) channels=5; @@ -1291,12 +1292,21 @@ bool FurnaceGUI::drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& fl if (ImGui::Checkbox(_("Amiga channel volumes (64)"),&amigaVol)) { altered=true; } + pushWarningColor(amigaPitch && e->song.linearPitch==2); + if (ImGui::Checkbox(_("Amiga-like pitch (non-linear pitch only)"),&amigaPitch)) { + altered=true; + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("pitch linearity is set to linear. this won't do anything!"); + } + popWarningColor(); if (altered) { e->lockSave([&]() { flags.set("channels",channels-1); flags.set("volScale",volScale); flags.set("amigaVol",amigaVol); + flags.set("amigaPitch",amigaPitch); }); } break;