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

This commit is contained in:
cam900 2023-04-13 12:04:31 +09:00
commit f2ae73aee9
26 changed files with 364 additions and 90 deletions

View file

@ -11,7 +11,7 @@ defaults:
shell: bash shell: bash
env: env:
BUILD_TYPE: RelWithDebInfo BUILD_TYPE: Debug
jobs: jobs:
build: build:

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -204,7 +204,9 @@ static void CopyNFDCharToWChar( const nfdchar_t *inStr, wchar_t **outStr )
#ifdef _DEBUG #ifdef _DEBUG
int inStrCharacterCount = static_cast<int>(NFDi_UTF8_Strlen(inStr)); int inStrCharacterCount = static_cast<int>(NFDi_UTF8_Strlen(inStr));
assert( ret == inStrCharacterCount ); if (ret!=inStrCharacterCount) {
logW("length does not match! %d != %d",ret,inStrCharacterCount);
}
#else #else
_NFD_UNUSED(ret); _NFD_UNUSED(ret);
#endif #endif

View file

@ -902,13 +902,14 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
} }
#endif #endif
int scaledLen=(double)length/samplePitches[pitch]; int scaledLen=ceil((double)length/samplePitches[pitch]);
if (scaledLen>0) { if (scaledLen>0) {
// resample // resample
logD("%d: scaling from %d...",i,pitch); logD("%d: scaling from %d...",i,pitch);
short* newData=new short[scaledLen]; short* newData=new short[scaledLen];
memset(newData,0,scaledLen*sizeof(short));
int k=0; int k=0;
float mult=(float)(vol)/50.0f; float mult=(float)(vol)/50.0f;
for (double j=0; j<length; j+=samplePitches[pitch]) { for (double j=0; j<length; j+=samplePitches[pitch]) {
@ -928,15 +929,17 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
data=newData; data=newData;
} }
logV("length: %d. scaledLen: %d.",length,scaledLen);
if (ds.version>=0x1b) { if (ds.version>=0x1b) {
if (cutStart<0 || cutStart>scaledLen) { if (cutStart<0 || cutStart>scaledLen) {
logE("cutStart is out of range! (%d)",cutStart); logE("cutStart is out of range! (%d, scaledLen: %d)",cutStart,scaledLen);
lastError="file is corrupt or unreadable at samples"; lastError="file is corrupt or unreadable at samples";
delete[] file; delete[] file;
return false; return false;
} }
if (cutEnd<0 || cutEnd>scaledLen) { if (cutEnd<0 || cutEnd>scaledLen) {
logE("cutEnd is out of range! (%d)",cutEnd); logE("cutEnd is out of range! (%d, scaledLen: %d)",cutEnd,scaledLen);
lastError="file is corrupt or unreadable at samples"; lastError="file is corrupt or unreadable at samples";
delete[] file; delete[] file;
return false; return false;

View file

@ -433,7 +433,6 @@ void DivPlatformES5506::tick(bool sysTick) {
off=(double)center/8363.0; off=(double)center/8363.0;
} }
if (ins->amiga.useNoteMap) { if (ins->amiga.useNoteMap) {
off*=(double)noteMapind.freq/((double)MAX(1,center)*pow(2.0,((double)next-48.0)/12.0));
chan[i].pcm.note=next; chan[i].pcm.note=next;
} }
// get loop mode // get loop mode
@ -618,10 +617,6 @@ void DivPlatformES5506::tick(bool sysTick) {
} else { } else {
off=(double)center/8363.0; off=(double)center/8363.0;
} }
if (ins->amiga.useNoteMap) {
DivInstrumentAmiga::SampleMap& noteMapind=ins->amiga.noteMap[chan[i].pcm.note];
off*=(double)noteMapind.freq/((double)MAX(1,center)*pow(2.0,((double)chan[i].pcm.note-48.0)/12.0));
}
chan[i].pcm.loopStart=(chan[i].pcm.start+(s->loopStart<<11))&0xfffff800; chan[i].pcm.loopStart=(chan[i].pcm.start+(s->loopStart<<11))&0xfffff800;
chan[i].pcm.loopEnd=(chan[i].pcm.start+((s->loopEnd-1)<<11))&0xffffff80; chan[i].pcm.loopEnd=(chan[i].pcm.start+((s->loopEnd-1)<<11))&0xffffff80;
chan[i].pcm.freqOffs=PITCH_OFFSET*off; chan[i].pcm.freqOffs=PITCH_OFFSET*off;

View file

@ -433,13 +433,8 @@ void DivPlatformGenesisExt::muteChannel(int ch, bool mute) {
DivInstrumentFM::Operator op=chan[2].state.op[ordch]; DivInstrumentFM::Operator op=chan[2].state.op[ordch];
if (isOpMuted[ch-2] || !op.enable) { if (isOpMuted[ch-2] || !op.enable) {
rWrite(baseAddr+0x40,127); rWrite(baseAddr+0x40,127);
immWrite(baseAddr+0x40,127);
} else if (KVS(2,ordch)) {
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-2].outVol&0x7f,127));
immWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-2].outVol&0x7f,127));
} else { } else {
rWrite(baseAddr+0x40,op.tl); rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-2].outVol&0x7f,127));
immWrite(baseAddr+0x40,op.tl);
} }
rWrite(chanOffs[2]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch-2].pan<<6))|(chan[2].state.fms&7)|((chan[2].state.ams&3)<<4)); rWrite(chanOffs[2]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch-2].pan<<6))|(chan[2].state.fms&7)|((chan[2].state.ams&3)<<4));

View file

@ -34,7 +34,7 @@ const char* regCheatSheetK007232[]={
"CHX_StartM", "X*6+3", "CHX_StartM", "X*6+3",
"CHX_StartH", "X*6+4", "CHX_StartH", "X*6+4",
"CHX_Keyon", "X*6+5", "CHX_Keyon", "X*6+5",
"SLEV", "C", // external IO "SLEV", "C", // external IO (Volume for Mono speaker)
"Loop", "D", "Loop", "D",
// off-chip // off-chip
"CHX_Volume", "X*2+10", "CHX_Volume", "X*2+10",
@ -157,8 +157,7 @@ void DivPlatformK007232::tick(bool sysTick) {
rWrite(0x10+i,(chan[i].lvol&0xf)|((chan[i].rvol&0xf)<<4)); rWrite(0x10+i,(chan[i].lvol&0xf)|((chan[i].rvol&0xf)<<4));
chan[i].prevPan=newPan; chan[i].prevPan=newPan;
} }
} } else {
else {
const unsigned char prevVolume=lastVolume; const unsigned char prevVolume=lastVolume;
lastVolume=(lastVolume&~(0xf<<(i<<2)))|((chan[i].resVol&0xf)<<(i<<2)); lastVolume=(lastVolume&~(0xf<<(i<<2)))|((chan[i].resVol&0xf)<<(i<<2));
if (prevVolume!=lastVolume) { if (prevVolume!=lastVolume) {
@ -480,6 +479,7 @@ void DivPlatformK007232::setFlags(const DivConfig& flags) {
rate=chipClock/4; rate=chipClock/4;
stereo=flags.getBool("stereo",false); stereo=flags.getBool("stereo",false);
for (int i=0; i<2; i++) { for (int i=0; i<2; i++) {
chan[i].volumeChanged=true;
oscBuf[i]->rate=rate; oscBuf[i]->rate=rate;
} }
} }

View file

@ -138,9 +138,7 @@ void DivPlatformSoundUnit::tick(bool sysTick) {
//DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU); //DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU);
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,chan[i].switchRoles,2,chan[i].pitch2,chipClock,chan[i].switchRoles?CHIP_DIVIDER:CHIP_FREQBASE); chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,chan[i].switchRoles,2,chan[i].pitch2,chipClock,chan[i].switchRoles?CHIP_DIVIDER:CHIP_FREQBASE);
if (chan[i].pcm) { if (chan[i].pcm) {
DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU); DivSample* sample=parent->getSample(chan[i].sample);
// TODO: sample map?
DivSample* sample=parent->getSample(ins->amiga.getSample(chan[i].note));
if (sample!=NULL) { if (sample!=NULL) {
double off=0.25; double off=0.25;
if (sample->centerRate<1) { if (sample->centerRate<1) {
@ -209,6 +207,12 @@ int DivPlatformSoundUnit::dispatch(DivCommand c) {
writeControlUpper(c.chan); writeControlUpper(c.chan);
} }
chan[c.chan].pcm=(ins->type==DIV_INS_AMIGA || ins->amiga.useSample); chan[c.chan].pcm=(ins->type==DIV_INS_AMIGA || ins->amiga.useSample);
if (chan[c.chan].pcm) {
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].sample=ins->amiga.getSample(c.value);
c.value=ins->amiga.getFreq(c.value);
}
}
if (c.value!=DIV_NOTE_NULL) { if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].baseFreq=NOTE_SU(c.chan,c.value); chan[c.chan].baseFreq=NOTE_SU(c.chan,c.value);
chan[c.chan].freqChanged=true; chan[c.chan].freqChanged=true;

View file

@ -26,7 +26,7 @@
class DivPlatformSoundUnit: public DivDispatch { class DivPlatformSoundUnit: public DivDispatch {
struct Channel: public SharedChannel<signed char> { struct Channel: public SharedChannel<signed char> {
int cutoff, baseCutoff, res, control, hasOffset; int cutoff, baseCutoff, res, control, hasOffset, sample;
signed char pan; signed char pan;
unsigned char duty; unsigned char duty;
bool noise, pcm, phaseReset, filterPhaseReset, switchRoles; bool noise, pcm, phaseReset, filterPhaseReset, switchRoles;
@ -43,6 +43,7 @@ class DivPlatformSoundUnit: public DivDispatch {
res(0), res(0),
control(0), control(0),
hasOffset(0), hasOffset(0),
sample(-1),
pan(0), pan(0),
duty(63), duty(63),
noise(false), noise(false),

View file

@ -542,13 +542,8 @@ void DivPlatformYM2203Ext::muteChannel(int ch, bool mute) {
DivInstrumentFM::Operator op=chan[2].state.op[ordch]; DivInstrumentFM::Operator op=chan[2].state.op[ordch];
if (isOpMuted[ch-2] || !op.enable) { if (isOpMuted[ch-2] || !op.enable) {
rWrite(baseAddr+0x40,127); rWrite(baseAddr+0x40,127);
immWrite(baseAddr+0x40,127);
} else if (KVS(2,ordch)) {
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-2].outVol&0x7f,127));
immWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-2].outVol&0x7f,127));
} else { } else {
rWrite(baseAddr+0x40,op.tl); rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-2].outVol&0x7f,127));
immWrite(baseAddr+0x40,op.tl);
} }
} }

View file

@ -564,13 +564,8 @@ void DivPlatformYM2608Ext::muteChannel(int ch, bool mute) {
DivInstrumentFM::Operator op=chan[2].state.op[ordch]; DivInstrumentFM::Operator op=chan[2].state.op[ordch];
if (isOpMuted[ch-2] || !op.enable) { if (isOpMuted[ch-2] || !op.enable) {
rWrite(baseAddr+0x40,127); rWrite(baseAddr+0x40,127);
immWrite(baseAddr+0x40,127);
} else if (KVS(2,ordch)) {
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-2].outVol&0x7f,127));
immWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-2].outVol&0x7f,127));
} else { } else {
rWrite(baseAddr+0x40,op.tl); rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-2].outVol&0x7f,127));
immWrite(baseAddr+0x40,op.tl);
} }
rWrite(chanOffs[2]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch-2].pan<<6))|(chan[2].state.fms&7)|((chan[2].state.ams&3)<<4)); rWrite(chanOffs[2]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch-2].pan<<6))|(chan[2].state.fms&7)|((chan[2].state.ams&3)<<4));

View file

@ -560,13 +560,8 @@ void DivPlatformYM2610BExt::muteChannel(int ch, bool mute) {
DivInstrumentFM::Operator op=chan[extChanOffs].state.op[ordch]; DivInstrumentFM::Operator op=chan[extChanOffs].state.op[ordch];
if (isOpMuted[ch-extChanOffs] || !op.enable) { if (isOpMuted[ch-extChanOffs] || !op.enable) {
rWrite(baseAddr+0x40,127); rWrite(baseAddr+0x40,127);
immWrite(baseAddr+0x40,127);
} else if (KVS(2,ordch)) {
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-extChanOffs].outVol&0x7f,127));
immWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-extChanOffs].outVol&0x7f,127));
} else { } else {
rWrite(baseAddr+0x40,op.tl); rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-extChanOffs].outVol&0x7f,127));
immWrite(baseAddr+0x40,op.tl);
} }
rWrite(chanOffs[extChanOffs]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch-extChanOffs].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4)); rWrite(chanOffs[extChanOffs]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch-extChanOffs].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4));

View file

@ -560,13 +560,8 @@ void DivPlatformYM2610Ext::muteChannel(int ch, bool mute) {
DivInstrumentFM::Operator op=chan[extChanOffs].state.op[ordch]; DivInstrumentFM::Operator op=chan[extChanOffs].state.op[ordch];
if (isOpMuted[ch-extChanOffs] || !op.enable) { if (isOpMuted[ch-extChanOffs] || !op.enable) {
rWrite(baseAddr+0x40,127); rWrite(baseAddr+0x40,127);
immWrite(baseAddr+0x40,127);
} else if (KVS(2,ordch)) {
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-extChanOffs].outVol&0x7f,127));
immWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-extChanOffs].outVol&0x7f,127));
} else { } else {
rWrite(baseAddr+0x40,op.tl); rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-extChanOffs].outVol&0x7f,127));
immWrite(baseAddr+0x40,op.tl);
} }
rWrite(chanOffs[extChanOffs]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch-extChanOffs].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4)); rWrite(chanOffs[extChanOffs]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch-extChanOffs].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4));

View file

@ -32,11 +32,11 @@ const char** DivPlatformZXBeeperQuadTone::getRegisterSheet() {
void DivPlatformZXBeeperQuadTone::acquire(short** buf, size_t len) { void DivPlatformZXBeeperQuadTone::acquire(short** buf, size_t len) {
bool o=false; bool o=false;
for (size_t h=0; h<len; h++) { for (size_t h=0; h<len; h++) {
if (curSample>=0 && curSample<parent->song.sampleLen) { if (curSample>=0 && curSample<parent->song.sampleLen && !isMuted[4]) {
while (curSamplePeriod>=chan[4].freq) { while (curSamplePeriod>=chan[4].freq) {
DivSample* s=parent->getSample(curSample); DivSample* s=parent->getSample(curSample);
if (s->samples>0) { if (s->samples>0) {
o=(!isMuted[4]&&s->data8[curSamplePos++]>0); if (!isMuted[4]) o=(s->data8[curSamplePos++]>0);
if (curSamplePos>=s->samples) curSample=-1; if (curSamplePos>=s->samples) curSample=-1;
// (theoretical) 32KiB limit // (theoretical) 32KiB limit
if (curSamplePos>=32768*8) curSample=-1; if (curSamplePos>=32768*8) curSample=-1;
@ -59,6 +59,7 @@ void DivPlatformZXBeeperQuadTone::acquire(short** buf, size_t len) {
if ((outputClock&1)==0) { if ((outputClock&1)==0) {
chan[ch].sPosition+=(regPool[1+b]<<8)|regPool[0+b]; chan[ch].sPosition+=(regPool[1+b]<<8)|regPool[0+b];
chan[ch].out=regPool[3+b]+((((chan[ch].sPosition>>8)&0xff)<regPool[2+b])?1:0); chan[ch].out=regPool[3+b]+((((chan[ch].sPosition>>8)&0xff)<regPool[2+b])?1:0);
if (isMuted[ch]) chan[ch].out=0;
} }
if ((outputClock&3)==0) { if ((outputClock&3)==0) {
oscBuf[4]->data[oscBuf[4]->needle++]=0; oscBuf[4]->data[oscBuf[4]->needle++]=0;
@ -66,6 +67,22 @@ void DivPlatformZXBeeperQuadTone::acquire(short** buf, size_t len) {
o=chan[ch].out&0x10; o=chan[ch].out&0x10;
oscBuf[ch]->data[oscBuf[ch]->needle++]=o?32767:0; oscBuf[ch]->data[oscBuf[ch]->needle++]=o?32767:0;
chan[ch].out<<=1; chan[ch].out<<=1;
// if muted, ztill run sample
if (curSample>=0 && curSample<parent->song.sampleLen && isMuted[4]) {
while (curSamplePeriod>=chan[4].freq) {
DivSample* s=parent->getSample(curSample);
if (s->samples>0) {
if (curSamplePos>=s->samples) curSample=-1;
// (theoretical) 32KiB limit
if (curSamplePos>=32768*8) curSample=-1;
} else {
curSample=-1;
}
curSamplePeriod-=chan[4].freq;
}
curSamplePeriod+=40;
}
} }
outputClock=(outputClock+1)&7; outputClock=(outputClock+1)&7;
buf[0][h]=o?32767:0; buf[0][h]=o?32767:0;
@ -283,7 +300,7 @@ int DivPlatformZXBeeperQuadTone::dispatch(DivCommand c) {
void DivPlatformZXBeeperQuadTone::writeOutVol(int ch) { void DivPlatformZXBeeperQuadTone::writeOutVol(int ch) {
if (ch>=4) return; if (ch>=4) return;
unsigned char val=(chan[ch].outVol>=1)?((chan[ch].outVol>=2)?31:7):0; unsigned char val=(chan[ch].outVol>=1)?((chan[ch].outVol>=2)?31:7):0;
rWrite(3+ch*4,(!isMuted[ch]&&chan[ch].active)?val:0); rWrite(3+ch*4,(chan[ch].active)?val:0);
} }
void DivPlatformZXBeeperQuadTone::muteChannel(int ch, bool mute) { void DivPlatformZXBeeperQuadTone::muteChannel(int ch, bool mute) {

View file

@ -1327,6 +1327,41 @@ void FurnaceGUI::keyDown(SDL_Event& ev) {
return; return;
} }
if (sampleMapWaitingInput) {
if (sampleMapColumn==1) {
// TODO: map?
if (ev.key.keysym.scancode==SDL_SCANCODE_DELETE) {
alterSampleMap(true,-1);
return;
}
try {
int key=noteKeys.at(ev.key.keysym.scancode);
int num=12*curOctave+key;
if (num<-60) num=-60; // C-(-5)
if (num>119) num=119; // B-9
alterSampleMap(true,num);
return;
} catch (std::out_of_range& e) {
}
} else {
// TODO: map?
if (ev.key.keysym.scancode==SDL_SCANCODE_DELETE) {
alterSampleMap(false,-1);
return;
}
try {
int num=valueKeys.at(ev.key.keysym.sym);
if (num<10) {
alterSampleMap(false,num);
return;
}
} catch (std::out_of_range& e) {
}
}
}
// PER-WINDOW KEYS // PER-WINDOW KEYS
switch (curWindow) { switch (curWindow) {
case GUI_WINDOW_PATTERN: case GUI_WINDOW_PATTERN:
@ -2905,7 +2940,7 @@ int FurnaceGUI::processEvent(SDL_Event* ev) {
} }
#endif #endif
if (ev->type==SDL_KEYDOWN) { if (ev->type==SDL_KEYDOWN) {
if (!ev->key.repeat && latchTarget==0 && !wantCaptureKeyboard && (ev->key.keysym.mod&(~(KMOD_NUM|KMOD_CAPS|KMOD_SCROLL)))==0) { if (!ev->key.repeat && latchTarget==0 && !wantCaptureKeyboard && !sampleMapWaitingInput && (ev->key.keysym.mod&(~(KMOD_NUM|KMOD_CAPS|KMOD_SCROLL)))==0) {
if (settings.notePreviewBehavior==0) return 1; if (settings.notePreviewBehavior==0) return 1;
switch (curWindow) { switch (curWindow) {
case GUI_WINDOW_SAMPLE_EDIT: case GUI_WINDOW_SAMPLE_EDIT:
@ -4913,6 +4948,8 @@ bool FurnaceGUI::loop() {
} }
if (displayNew) { if (displayNew) {
newSongQuery="";
newSongFirstFrame=true;
displayNew=false; displayNew=false;
ImGui::OpenPopup("New Song"); ImGui::OpenPopup("New Song");
} }
@ -5562,8 +5599,56 @@ bool FurnaceGUI::loop() {
} }
} }
sampleMapWaitingInput=(curWindow==GUI_WINDOW_INS_EDIT && sampleMapFocused);
curWindowThreadSafe=curWindow; curWindowThreadSafe=curWindow;
if (curWindow!=curWindowLast) {
int curWindowCat=0;
int lastWindowCat=0;
switch (curWindow) {
case GUI_WINDOW_WAVE_LIST:
case GUI_WINDOW_WAVE_EDIT:
curWindowCat=1;
break;
case GUI_WINDOW_SAMPLE_LIST:
case GUI_WINDOW_SAMPLE_EDIT:
curWindowCat=2;
break;
default:
curWindowCat=0;
break;
}
switch (curWindowLast) {
case GUI_WINDOW_WAVE_LIST:
case GUI_WINDOW_WAVE_EDIT:
lastWindowCat=1;
break;
case GUI_WINDOW_SAMPLE_LIST:
case GUI_WINDOW_SAMPLE_EDIT:
lastWindowCat=2;
break;
default:
lastWindowCat=0;
break;
}
if (curWindowCat!=lastWindowCat) {
switch (lastWindowCat) {
case 0:
e->autoNoteOffAll();
break;
case 1:
e->stopWavePreview();
break;
case 2:
e->stopSamplePreview();
break;
}
}
}
SDL_SetRenderDrawColor(sdlRend,uiColors[GUI_COLOR_BACKGROUND].x*255, SDL_SetRenderDrawColor(sdlRend,uiColors[GUI_COLOR_BACKGROUND].x*255,
uiColors[GUI_COLOR_BACKGROUND].y*255, uiColors[GUI_COLOR_BACKGROUND].y*255,
uiColors[GUI_COLOR_BACKGROUND].z*255, uiColors[GUI_COLOR_BACKGROUND].z*255,
@ -6426,8 +6511,12 @@ FurnaceGUI::FurnaceGUI():
samplePreviewOn(false), samplePreviewOn(false),
samplePreviewKey((SDL_Scancode)0), samplePreviewKey((SDL_Scancode)0),
samplePreviewNote(0), samplePreviewNote(0),
arpMacroScroll(-12), sampleMapSelStart(-1),
pitchMacroScroll(-80), sampleMapSelEnd(-1),
sampleMapDigit(0),
sampleMapColumn(0),
sampleMapFocused(false),
sampleMapWaitingInput(false),
macroDragStart(0,0), macroDragStart(0,0),
macroDragAreaSize(0,0), macroDragAreaSize(0,0),
macroDragCTarget(NULL), macroDragCTarget(NULL),

View file

@ -1566,6 +1566,7 @@ class FurnaceGUI {
double exportFadeOut; double exportFadeOut;
bool newSongFirstFrame;
bool editControlsOpen, ordersOpen, insListOpen, songInfoOpen, patternOpen, insEditOpen; bool editControlsOpen, ordersOpen, insListOpen, songInfoOpen, patternOpen, insEditOpen;
bool waveListOpen, waveEditOpen, sampleListOpen, sampleEditOpen, aboutOpen, settingsOpen; bool waveListOpen, waveEditOpen, sampleListOpen, sampleEditOpen, aboutOpen, settingsOpen;
bool mixerOpen, debugOpen, inspectorOpen, oscOpen, volMeterOpen, statsOpen, compatFlagsOpen; bool mixerOpen, debugOpen, inspectorOpen, oscOpen, volMeterOpen, statsOpen, compatFlagsOpen;
@ -1680,8 +1681,11 @@ class FurnaceGUI {
std::vector<TouchPoint> pressedPoints; std::vector<TouchPoint> pressedPoints;
std::vector<TouchPoint> releasedPoints; std::vector<TouchPoint> releasedPoints;
int arpMacroScroll; int sampleMapSelStart;
int pitchMacroScroll; int sampleMapSelEnd;
int sampleMapDigit;
int sampleMapColumn;
bool sampleMapFocused, sampleMapWaitingInput;
ImVec2 macroDragStart; ImVec2 macroDragStart;
ImVec2 macroDragAreaSize; ImVec2 macroDragAreaSize;
@ -1936,6 +1940,7 @@ class FurnaceGUI {
void drawMacroEdit(FurnaceGUIMacroDesc& i, int totalFit, float availableWidth, int index); void drawMacroEdit(FurnaceGUIMacroDesc& i, int totalFit, float availableWidth, int index);
void drawMacros(std::vector<FurnaceGUIMacroDesc>& macros, FurnaceGUIMacroEditState& state); void drawMacros(std::vector<FurnaceGUIMacroDesc>& macros, FurnaceGUIMacroEditState& state);
void alterSampleMap(bool isNote, int val);
void drawOrderButtons(); void drawOrderButtons();

View file

@ -2023,6 +2023,59 @@ void FurnaceGUI::drawMacros(std::vector<FurnaceGUIMacroDesc>& macros, FurnaceGUI
} }
} }
void FurnaceGUI::alterSampleMap(bool isNote, int val) {
if (curIns<0 || curIns>=(int)e->song.ins.size()) return;
DivInstrument* ins=e->song.ins[curIns];
int sampleMapMin=sampleMapSelStart;
int sampleMapMax=sampleMapSelEnd;
if (sampleMapMin>sampleMapMax) {
sampleMapMin^=sampleMapMax;
sampleMapMax^=sampleMapMin;
sampleMapMin^=sampleMapMax;
}
for (int i=sampleMapMin; i<=sampleMapMax; i++) {
if (i<0 || i>=120) continue;
if (sampleMapColumn==1 && isNote) {
ins->amiga.noteMap[i].freq=val;
} else if (sampleMapColumn==0 && !isNote) {
if (val<0) {
ins->amiga.noteMap[i].map=-1;
} else if (sampleMapDigit>0) {
ins->amiga.noteMap[i].map*=10;
ins->amiga.noteMap[i].map+=val;
} else {
ins->amiga.noteMap[i].map=val;
}
if (ins->amiga.noteMap[i].map>=(int)e->song.sample.size()) {
ins->amiga.noteMap[i].map=((int)e->song.sample.size())-1;
}
}
}
bool advance=false;
if (sampleMapColumn==1 && isNote) {
advance=true;
} else if (sampleMapColumn==0 && !isNote) {
int digits=1;
if (e->song.sample.size()>=10) digits=2;
if (e->song.sample.size()>=100) digits=3;
if (++sampleMapDigit>=digits) {
sampleMapDigit=0;
advance=true;
}
}
if (advance && sampleMapMin==sampleMapMax) {
sampleMapSelStart++;
if (sampleMapSelStart>119) sampleMapSelStart=119;
sampleMapSelEnd=sampleMapSelStart;
}
MARK_MODIFIED;
}
#define DRUM_FREQ(name,db,df,prop) \ #define DRUM_FREQ(name,db,df,prop) \
ImGui::TableNextRow(); \ ImGui::TableNextRow(); \
ImGui::TableNextColumn(); \ ImGui::TableNextColumn(); \
@ -4342,6 +4395,7 @@ void FurnaceGUI::drawInsEdit() {
ins->type==DIV_INS_K053260) { ins->type==DIV_INS_K053260) {
if (ImGui::BeginTabItem((ins->type==DIV_INS_SU)?"Sound Unit":"Sample")) { if (ImGui::BeginTabItem((ins->type==DIV_INS_SU)?"Sound Unit":"Sample")) {
String sName; String sName;
bool wannaOpenSMPopup=false;
if (ins->amiga.initSample<0 || ins->amiga.initSample>=e->song.sampleLen) { if (ins->amiga.initSample<0 || ins->amiga.initSample>=e->song.sampleLen) {
sName="none selected"; sName="none selected";
} else { } else {
@ -4406,71 +4460,191 @@ void FurnaceGUI::drawInsEdit() {
ImGui::BeginDisabled(ins->amiga.useWave); ImGui::BeginDisabled(ins->amiga.useWave);
P(ImGui::Checkbox("Use sample map",&ins->amiga.useNoteMap)); P(ImGui::Checkbox("Use sample map",&ins->amiga.useNoteMap));
if (ins->amiga.useNoteMap) { if (ins->amiga.useNoteMap) {
if (ImGui::BeginTable("NoteMap",3,ImGuiTableFlags_ScrollY|ImGuiTableFlags_Borders|ImGuiTableFlags_SizingStretchSame)) { if (ImGui::IsMouseClicked(ImGuiMouseButton_Left) && ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows)) sampleMapFocused=false;
if (curWindowLast!=GUI_WINDOW_INS_EDIT) sampleMapFocused=false;
if (!sampleMapFocused) sampleMapDigit=0;
if (ImGui::BeginTable("NoteMap",4,ImGuiTableFlags_ScrollY|ImGuiTableFlags_Borders|ImGuiTableFlags_SizingStretchSame)) {
ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed); ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch); ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthStretch); ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("c3",ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupScrollFreeze(0,1); ImGui::TableSetupScrollFreeze(0,1);
ImGui::TableNextRow(ImGuiTableRowFlags_Headers); ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::Text("Sample"); ImGui::Text("#");
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::Text("Note"); ImGui::Text("note");
ImGui::TableNextColumn();
ImGui::Text("sample name");
int sampleMapMin=sampleMapSelStart;
int sampleMapMax=sampleMapSelEnd;
if (sampleMapMin>sampleMapMax) {
sampleMapMin^=sampleMapMax;
sampleMapMax^=sampleMapMin;
sampleMapMin^=sampleMapMax;
}
ImGui::PushStyleColor(ImGuiCol_Header,ImGui::GetColorU32(ImGuiCol_HeaderHovered));
ImGui::PushStyleColor(ImGuiCol_HeaderActive,ImGui::GetColorU32(ImGuiCol_HeaderHovered));
for (int i=0; i<120; i++) { for (int i=0; i<120; i++) {
DivInstrumentAmiga::SampleMap& sampleMap=ins->amiga.noteMap[i]; DivInstrumentAmiga::SampleMap& sampleMap=ins->amiga.noteMap[i];
ImGui::TableNextRow(); ImGui::TableNextRow();
ImGui::PushID(fmt::sprintf("NM_%d",i).c_str());
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg,ImGui::GetColorU32(ImGuiCol_TableHeaderBg));
ImGui::Text("%s",noteNames[60+i]); ImGui::Text("%s",noteNames[60+i]);
ImGui::TableNextColumn(); ImGui::TableNextColumn();
if (sampleMap.map<0 || sampleMap.map>=e->song.sampleLen) { if (sampleMap.map<0 || sampleMap.map>=e->song.sampleLen) {
sName="-- empty --"; sName=fmt::sprintf("---##SM%d",i);
sampleMap.map=-1; sampleMap.map=-1;
} else { } else {
sName=e->song.sample[sampleMap.map]->name; sName=fmt::sprintf("%3d##SM%d",sampleMap.map,i);
} }
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); ImGui::PushFont(patFont);
if (ImGui::BeginCombo("##SM",sName.c_str())) { ImGui::SetNextItemWidth(ImGui::CalcTextSize("00000").x);
String id; ImGui::Selectable(sName.c_str(),(sampleMapWaitingInput && sampleMapColumn==0 && i>=sampleMapMin && i<=sampleMapMax));
if (ImGui::Selectable("-- empty --",sampleMap.map==-1)) { PARAMETER if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) {
sampleMap.map=-1; sampleMapFocused=true;
sampleMapColumn=0;
sampleMapDigit=0;
sampleMapSelStart=i;
sampleMapSelEnd=i;
sampleMapMin=sampleMapSelStart;
sampleMapMax=sampleMapSelEnd;
if (sampleMapMin>sampleMapMax) {
sampleMapMin^=sampleMapMax;
sampleMapMax^=sampleMapMin;
sampleMapMin^=sampleMapMax;
} }
for (int j=0; j<e->song.sampleLen; j++) { ImGui::InhibitInertialScroll();
id=fmt::sprintf("%d: %s",j,e->song.sample[j]->name); }
if (ImGui::Selectable(id.c_str(),sampleMap.map==j)) { PARAMETER if (sampleMapFocused && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) && ImGui::IsMouseDown(ImGuiMouseButton_Left)) {
sampleMap.map=j; sampleMapSelEnd=i;
}
if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
if (sampleMapSelStart==sampleMapSelEnd) {
sampleMapFocused=true;
sampleMapColumn=0;
sampleMapDigit=0;
sampleMapSelStart=i;
sampleMapSelEnd=i;
sampleMapMin=sampleMapSelStart;
sampleMapMax=sampleMapSelEnd;
if (sampleMapMin>sampleMapMax) {
sampleMapMin^=sampleMapMax;
sampleMapMax^=sampleMapMin;
sampleMapMin^=sampleMapMax;
} }
} }
ImGui::EndCombo(); if (sampleMapFocused) {
wannaOpenSMPopup=true;
} }
}
ImGui::PopFont();
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); sName="???";
const char* nName="???";
if ((sampleMap.freq+60)>0 && (sampleMap.freq+60)<180) { if ((sampleMap.freq+60)>0 && (sampleMap.freq+60)<180) {
nName=noteNames[sampleMap.freq+60]; sName=noteNames[sampleMap.freq+60];
}
if (ImGui::BeginCombo("##SN",nName)) {
for (int j=0; j<180; j++) {
const char* nName2="???";
nName2=noteNames[j];
if (ImGui::Selectable(nName2,(sampleMap.freq+60)==j)) {
sampleMap.freq=j-60;
}
}
ImGui::EndCombo();
} }
sName+=fmt::sprintf("##SN%d",i);
ImGui::PushFont(patFont);
ImGui::SetNextItemWidth(ImGui::CalcTextSize("00000").x);
ImGui::Selectable(sName.c_str(),(sampleMapWaitingInput && sampleMapColumn==1 && i>=sampleMapMin && i<=sampleMapMax));
if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) {
sampleMapFocused=true;
sampleMapColumn=1;
sampleMapDigit=0;
sampleMapSelStart=i;
sampleMapSelEnd=i;
ImGui::PopID(); sampleMapMin=sampleMapSelStart;
sampleMapMax=sampleMapSelEnd;
if (sampleMapMin>sampleMapMax) {
sampleMapMin^=sampleMapMax;
sampleMapMax^=sampleMapMin;
sampleMapMin^=sampleMapMax;
} }
ImGui::InhibitInertialScroll();
}
if (sampleMapFocused && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) && ImGui::IsMouseDown(ImGuiMouseButton_Left)) {
sampleMapSelEnd=i;
}
if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
if (sampleMapSelStart==sampleMapSelEnd) {
sampleMapFocused=true;
sampleMapColumn=1;
sampleMapDigit=0;
sampleMapSelStart=i;
sampleMapSelEnd=i;
sampleMapMin=sampleMapSelStart;
sampleMapMax=sampleMapSelEnd;
if (sampleMapMin>sampleMapMax) {
sampleMapMin^=sampleMapMax;
sampleMapMax^=sampleMapMin;
sampleMapMin^=sampleMapMax;
}
}
if (sampleMapFocused) {
wannaOpenSMPopup=true;
}
}
ImGui::PopFont();
ImGui::TableNextColumn();
if (sampleMap.map>=0 && sampleMap.map<e->song.sampleLen) {
ImGui::TextUnformatted(e->song.sample[sampleMap.map]->name.c_str());
}
}
ImGui::PopStyleColor(2);
ImGui::EndTable(); ImGui::EndTable();
} }
} else {
sampleMapFocused=false;
} }
ImGui::EndDisabled(); ImGui::EndDisabled();
if (wannaOpenSMPopup) {
ImGui::OpenPopup("SampleMapUtils");
}
if (ImGui::BeginPopup("SampleMapUtils",ImGuiWindowFlags_NoMove|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings)) {
if (sampleMapSelStart==sampleMapSelEnd && sampleMapSelStart>=0 && sampleMapSelStart<120) {
if (ImGui::MenuItem("set entire map to this note")) {
if (sampleMapSelStart>=0 && sampleMapSelStart<120) {
for (int i=0; i<120; i++) {
if (i==sampleMapSelStart) continue;
ins->amiga.noteMap[i].freq=ins->amiga.noteMap[sampleMapSelStart].freq;
}
}
}
if (ImGui::MenuItem("set entire map to this sample")) {
if (sampleMapSelStart>=0 && sampleMapSelStart<120) {
for (int i=0; i<120; i++) {
if (i==sampleMapSelStart) continue;
ins->amiga.noteMap[i].map=ins->amiga.noteMap[sampleMapSelStart].map;
}
}
}
}
if (ImGui::MenuItem("reset notes")) {
for (int i=0; i<120; i++) {
ins->amiga.noteMap[i].freq=i;
}
}
if (ImGui::MenuItem("clear map samples")) {
for (int i=0; i<120; i++) {
ins->amiga.noteMap[i].map=-1;
}
}
ImGui::EndPopup();
}
ImGui::EndTabItem(); ImGui::EndTabItem();
} else {
sampleMapFocused=false;
} }
} }
if (ins->type==DIV_INS_N163) if (ImGui::BeginTabItem(settings.c163Name.c_str())) { if (ins->type==DIV_INS_N163) if (ImGui::BeginTabItem(settings.c163Name.c_str())) {

View file

@ -34,6 +34,8 @@ void FurnaceGUI::drawNewSong() {
avail.y-=ImGui::GetFrameHeightWithSpacing(); avail.y-=ImGui::GetFrameHeightWithSpacing();
if (ImGui::BeginChild("sysPickerC",avail,false,ImGuiWindowFlags_NoScrollWithMouse|ImGuiWindowFlags_NoScrollbar)) { if (ImGui::BeginChild("sysPickerC",avail,false,ImGuiWindowFlags_NoScrollWithMouse|ImGuiWindowFlags_NoScrollbar)) {
if (newSongFirstFrame)
ImGui::SetKeyboardFocusHere();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
if (ImGui::InputTextWithHint("##SysSearch","Search...",&newSongQuery)) { if (ImGui::InputTextWithHint("##SysSearch","Search...",&newSongQuery)) {
String lowerCase=newSongQuery; String lowerCase=newSongQuery;
@ -159,4 +161,6 @@ void FurnaceGUI::drawNewSong() {
updateWindowTitle(); updateWindowTitle();
ImGui::CloseCurrentPopup(); ImGui::CloseCurrentPopup();
} }
newSongFirstFrame=false;
} }

View file

@ -221,8 +221,9 @@ void FurnaceGUI::drawOsc() {
waveform[i]=ImLerp(inRect.Min,inRect.Max,ImVec2(x,0.5f-y)); waveform[i]=ImLerp(inRect.Min,inRect.Max,ImVec2(x,0.5f-y));
} }
if (settings.oscEscapesBoundary) { if (settings.oscEscapesBoundary) {
ImDrawList* dlf=ImGui::GetForegroundDrawList(); dl->PushClipRectFullScreen();
dlf->AddPolyline(waveform,512,color,ImDrawFlags_None,dpiScale); dl->AddPolyline(waveform,512,color,ImDrawFlags_None,dpiScale);
dl->PopClipRect();
} else { } else {
dl->AddPolyline(waveform,512,color,ImDrawFlags_None,dpiScale); dl->AddPolyline(waveform,512,color,ImDrawFlags_None,dpiScale);
} }

View file

@ -415,10 +415,14 @@ void FurnaceGUI::drawPiano() {
e->previewSample(curSample,note); e->previewSample(curSample,note);
break; break;
default: default:
if (sampleMapWaitingInput) {
alterSampleMap(true,note);
} else {
e->synchronized([this,note]() { e->synchronized([this,note]() {
e->autoNoteOn(-1,curIns,note); e->autoNoteOn(-1,curIns,note);
}); });
if (edit && curWindow!=GUI_WINDOW_INS_LIST && curWindow!=GUI_WINDOW_INS_EDIT) noteInput(note,0); if (edit && curWindow!=GUI_WINDOW_INS_LIST && curWindow!=GUI_WINDOW_INS_EDIT) noteInput(note,0);
}
break; break;
} }
} }