mirror of
https://github.com/tildearrow/furnace.git
synced 2024-11-26 22:43:01 +00:00
Merge branch 'master' of https://github.com/tildearrow/furnace into k053260
This commit is contained in:
commit
f2ae73aee9
26 changed files with 364 additions and 90 deletions
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
|
@ -11,7 +11,7 @@ defaults:
|
|||
shell: bash
|
||||
|
||||
env:
|
||||
BUILD_TYPE: RelWithDebInfo
|
||||
BUILD_TYPE: Debug
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
|
BIN
demos/arcade/Maximum_Overdrive_NamcoWSG.fur
Normal file
BIN
demos/arcade/Maximum_Overdrive_NamcoWSG.fur
Normal file
Binary file not shown.
BIN
demos/ay8910/Second_Addition.fur
Normal file
BIN
demos/ay8910/Second_Addition.fur
Normal file
Binary file not shown.
BIN
demos/misc/BlueBolt_VIC20.fur
Normal file
BIN
demos/misc/BlueBolt_VIC20.fur
Normal file
Binary file not shown.
BIN
demos/misc/GreenIdeas_PET.fur
Normal file
BIN
demos/misc/GreenIdeas_PET.fur
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
demos/multichip/MegaMari-Cirno.fur
Normal file
BIN
demos/multichip/MegaMari-Cirno.fur
Normal file
Binary file not shown.
4
extern/nfd-modified/src/nfd_win.cpp
vendored
4
extern/nfd-modified/src/nfd_win.cpp
vendored
|
@ -204,7 +204,9 @@ static void CopyNFDCharToWChar( const nfdchar_t *inStr, wchar_t **outStr )
|
|||
|
||||
#ifdef _DEBUG
|
||||
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
|
||||
_NFD_UNUSED(ret);
|
||||
#endif
|
||||
|
|
|
@ -902,13 +902,14 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
|||
}
|
||||
#endif
|
||||
|
||||
int scaledLen=(double)length/samplePitches[pitch];
|
||||
int scaledLen=ceil((double)length/samplePitches[pitch]);
|
||||
|
||||
if (scaledLen>0) {
|
||||
// resample
|
||||
logD("%d: scaling from %d...",i,pitch);
|
||||
|
||||
short* newData=new short[scaledLen];
|
||||
memset(newData,0,scaledLen*sizeof(short));
|
||||
int k=0;
|
||||
float mult=(float)(vol)/50.0f;
|
||||
for (double j=0; j<length; j+=samplePitches[pitch]) {
|
||||
|
@ -928,15 +929,17 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
|||
data=newData;
|
||||
}
|
||||
|
||||
logV("length: %d. scaledLen: %d.",length,scaledLen);
|
||||
|
||||
if (ds.version>=0x1b) {
|
||||
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";
|
||||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
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";
|
||||
delete[] file;
|
||||
return false;
|
||||
|
|
|
@ -433,7 +433,6 @@ void DivPlatformES5506::tick(bool sysTick) {
|
|||
off=(double)center/8363.0;
|
||||
}
|
||||
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;
|
||||
}
|
||||
// get loop mode
|
||||
|
@ -618,10 +617,6 @@ void DivPlatformES5506::tick(bool sysTick) {
|
|||
} else {
|
||||
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.loopEnd=(chan[i].pcm.start+((s->loopEnd-1)<<11))&0xffffff80;
|
||||
chan[i].pcm.freqOffs=PITCH_OFFSET*off;
|
||||
|
|
|
@ -433,13 +433,8 @@ void DivPlatformGenesisExt::muteChannel(int ch, bool mute) {
|
|||
DivInstrumentFM::Operator op=chan[2].state.op[ordch];
|
||||
if (isOpMuted[ch-2] || !op.enable) {
|
||||
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 {
|
||||
rWrite(baseAddr+0x40,op.tl);
|
||||
immWrite(baseAddr+0x40,op.tl);
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-2].outVol&0x7f,127));
|
||||
}
|
||||
|
||||
rWrite(chanOffs[2]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch-2].pan<<6))|(chan[2].state.fms&7)|((chan[2].state.ams&3)<<4));
|
||||
|
|
|
@ -34,7 +34,7 @@ const char* regCheatSheetK007232[]={
|
|||
"CHX_StartM", "X*6+3",
|
||||
"CHX_StartH", "X*6+4",
|
||||
"CHX_Keyon", "X*6+5",
|
||||
"SLEV", "C", // external IO
|
||||
"SLEV", "C", // external IO (Volume for Mono speaker)
|
||||
"Loop", "D",
|
||||
// off-chip
|
||||
"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));
|
||||
chan[i].prevPan=newPan;
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
const unsigned char prevVolume=lastVolume;
|
||||
lastVolume=(lastVolume&~(0xf<<(i<<2)))|((chan[i].resVol&0xf)<<(i<<2));
|
||||
if (prevVolume!=lastVolume) {
|
||||
|
@ -480,6 +479,7 @@ void DivPlatformK007232::setFlags(const DivConfig& flags) {
|
|||
rate=chipClock/4;
|
||||
stereo=flags.getBool("stereo",false);
|
||||
for (int i=0; i<2; i++) {
|
||||
chan[i].volumeChanged=true;
|
||||
oscBuf[i]->rate=rate;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -138,9 +138,7 @@ void DivPlatformSoundUnit::tick(bool sysTick) {
|
|||
//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);
|
||||
if (chan[i].pcm) {
|
||||
DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU);
|
||||
// TODO: sample map?
|
||||
DivSample* sample=parent->getSample(ins->amiga.getSample(chan[i].note));
|
||||
DivSample* sample=parent->getSample(chan[i].sample);
|
||||
if (sample!=NULL) {
|
||||
double off=0.25;
|
||||
if (sample->centerRate<1) {
|
||||
|
@ -209,6 +207,12 @@ int DivPlatformSoundUnit::dispatch(DivCommand c) {
|
|||
writeControlUpper(c.chan);
|
||||
}
|
||||
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) {
|
||||
chan[c.chan].baseFreq=NOTE_SU(c.chan,c.value);
|
||||
chan[c.chan].freqChanged=true;
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
|
||||
class DivPlatformSoundUnit: public DivDispatch {
|
||||
struct Channel: public SharedChannel<signed char> {
|
||||
int cutoff, baseCutoff, res, control, hasOffset;
|
||||
int cutoff, baseCutoff, res, control, hasOffset, sample;
|
||||
signed char pan;
|
||||
unsigned char duty;
|
||||
bool noise, pcm, phaseReset, filterPhaseReset, switchRoles;
|
||||
|
@ -43,6 +43,7 @@ class DivPlatformSoundUnit: public DivDispatch {
|
|||
res(0),
|
||||
control(0),
|
||||
hasOffset(0),
|
||||
sample(-1),
|
||||
pan(0),
|
||||
duty(63),
|
||||
noise(false),
|
||||
|
|
|
@ -542,13 +542,8 @@ void DivPlatformYM2203Ext::muteChannel(int ch, bool mute) {
|
|||
DivInstrumentFM::Operator op=chan[2].state.op[ordch];
|
||||
if (isOpMuted[ch-2] || !op.enable) {
|
||||
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 {
|
||||
rWrite(baseAddr+0x40,op.tl);
|
||||
immWrite(baseAddr+0x40,op.tl);
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-2].outVol&0x7f,127));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -564,13 +564,8 @@ void DivPlatformYM2608Ext::muteChannel(int ch, bool mute) {
|
|||
DivInstrumentFM::Operator op=chan[2].state.op[ordch];
|
||||
if (isOpMuted[ch-2] || !op.enable) {
|
||||
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 {
|
||||
rWrite(baseAddr+0x40,op.tl);
|
||||
immWrite(baseAddr+0x40,op.tl);
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-2].outVol&0x7f,127));
|
||||
}
|
||||
|
||||
rWrite(chanOffs[2]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch-2].pan<<6))|(chan[2].state.fms&7)|((chan[2].state.ams&3)<<4));
|
||||
|
|
|
@ -560,13 +560,8 @@ void DivPlatformYM2610BExt::muteChannel(int ch, bool mute) {
|
|||
DivInstrumentFM::Operator op=chan[extChanOffs].state.op[ordch];
|
||||
if (isOpMuted[ch-extChanOffs] || !op.enable) {
|
||||
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 {
|
||||
rWrite(baseAddr+0x40,op.tl);
|
||||
immWrite(baseAddr+0x40,op.tl);
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-extChanOffs].outVol&0x7f,127));
|
||||
}
|
||||
|
||||
rWrite(chanOffs[extChanOffs]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch-extChanOffs].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4));
|
||||
|
|
|
@ -560,13 +560,8 @@ void DivPlatformYM2610Ext::muteChannel(int ch, bool mute) {
|
|||
DivInstrumentFM::Operator op=chan[extChanOffs].state.op[ordch];
|
||||
if (isOpMuted[ch-extChanOffs] || !op.enable) {
|
||||
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 {
|
||||
rWrite(baseAddr+0x40,op.tl);
|
||||
immWrite(baseAddr+0x40,op.tl);
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-extChanOffs].outVol&0x7f,127));
|
||||
}
|
||||
|
||||
rWrite(chanOffs[extChanOffs]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch-extChanOffs].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4));
|
||||
|
|
|
@ -32,11 +32,11 @@ const char** DivPlatformZXBeeperQuadTone::getRegisterSheet() {
|
|||
void DivPlatformZXBeeperQuadTone::acquire(short** buf, size_t len) {
|
||||
bool o=false;
|
||||
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) {
|
||||
DivSample* s=parent->getSample(curSample);
|
||||
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;
|
||||
// (theoretical) 32KiB limit
|
||||
if (curSamplePos>=32768*8) curSample=-1;
|
||||
|
@ -59,6 +59,7 @@ void DivPlatformZXBeeperQuadTone::acquire(short** buf, size_t len) {
|
|||
if ((outputClock&1)==0) {
|
||||
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);
|
||||
if (isMuted[ch]) chan[ch].out=0;
|
||||
}
|
||||
if ((outputClock&3)==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;
|
||||
oscBuf[ch]->data[oscBuf[ch]->needle++]=o?32767:0;
|
||||
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;
|
||||
buf[0][h]=o?32767:0;
|
||||
|
@ -283,7 +300,7 @@ int DivPlatformZXBeeperQuadTone::dispatch(DivCommand c) {
|
|||
void DivPlatformZXBeeperQuadTone::writeOutVol(int ch) {
|
||||
if (ch>=4) return;
|
||||
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) {
|
||||
|
|
|
@ -1327,6 +1327,41 @@ void FurnaceGUI::keyDown(SDL_Event& ev) {
|
|||
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
|
||||
switch (curWindow) {
|
||||
case GUI_WINDOW_PATTERN:
|
||||
|
@ -2905,7 +2940,7 @@ int FurnaceGUI::processEvent(SDL_Event* ev) {
|
|||
}
|
||||
#endif
|
||||
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;
|
||||
switch (curWindow) {
|
||||
case GUI_WINDOW_SAMPLE_EDIT:
|
||||
|
@ -4913,6 +4948,8 @@ bool FurnaceGUI::loop() {
|
|||
}
|
||||
|
||||
if (displayNew) {
|
||||
newSongQuery="";
|
||||
newSongFirstFrame=true;
|
||||
displayNew=false;
|
||||
ImGui::OpenPopup("New Song");
|
||||
}
|
||||
|
@ -5562,8 +5599,56 @@ bool FurnaceGUI::loop() {
|
|||
}
|
||||
}
|
||||
|
||||
sampleMapWaitingInput=(curWindow==GUI_WINDOW_INS_EDIT && sampleMapFocused);
|
||||
|
||||
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,
|
||||
uiColors[GUI_COLOR_BACKGROUND].y*255,
|
||||
uiColors[GUI_COLOR_BACKGROUND].z*255,
|
||||
|
@ -6426,8 +6511,12 @@ FurnaceGUI::FurnaceGUI():
|
|||
samplePreviewOn(false),
|
||||
samplePreviewKey((SDL_Scancode)0),
|
||||
samplePreviewNote(0),
|
||||
arpMacroScroll(-12),
|
||||
pitchMacroScroll(-80),
|
||||
sampleMapSelStart(-1),
|
||||
sampleMapSelEnd(-1),
|
||||
sampleMapDigit(0),
|
||||
sampleMapColumn(0),
|
||||
sampleMapFocused(false),
|
||||
sampleMapWaitingInput(false),
|
||||
macroDragStart(0,0),
|
||||
macroDragAreaSize(0,0),
|
||||
macroDragCTarget(NULL),
|
||||
|
|
|
@ -1566,6 +1566,7 @@ class FurnaceGUI {
|
|||
|
||||
double exportFadeOut;
|
||||
|
||||
bool newSongFirstFrame;
|
||||
bool editControlsOpen, ordersOpen, insListOpen, songInfoOpen, patternOpen, insEditOpen;
|
||||
bool waveListOpen, waveEditOpen, sampleListOpen, sampleEditOpen, aboutOpen, settingsOpen;
|
||||
bool mixerOpen, debugOpen, inspectorOpen, oscOpen, volMeterOpen, statsOpen, compatFlagsOpen;
|
||||
|
@ -1680,8 +1681,11 @@ class FurnaceGUI {
|
|||
std::vector<TouchPoint> pressedPoints;
|
||||
std::vector<TouchPoint> releasedPoints;
|
||||
|
||||
int arpMacroScroll;
|
||||
int pitchMacroScroll;
|
||||
int sampleMapSelStart;
|
||||
int sampleMapSelEnd;
|
||||
int sampleMapDigit;
|
||||
int sampleMapColumn;
|
||||
bool sampleMapFocused, sampleMapWaitingInput;
|
||||
|
||||
ImVec2 macroDragStart;
|
||||
ImVec2 macroDragAreaSize;
|
||||
|
@ -1936,6 +1940,7 @@ class FurnaceGUI {
|
|||
|
||||
void drawMacroEdit(FurnaceGUIMacroDesc& i, int totalFit, float availableWidth, int index);
|
||||
void drawMacros(std::vector<FurnaceGUIMacroDesc>& macros, FurnaceGUIMacroEditState& state);
|
||||
void alterSampleMap(bool isNote, int val);
|
||||
|
||||
void drawOrderButtons();
|
||||
|
||||
|
|
|
@ -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) \
|
||||
ImGui::TableNextRow(); \
|
||||
ImGui::TableNextColumn(); \
|
||||
|
@ -4342,6 +4395,7 @@ void FurnaceGUI::drawInsEdit() {
|
|||
ins->type==DIV_INS_K053260) {
|
||||
if (ImGui::BeginTabItem((ins->type==DIV_INS_SU)?"Sound Unit":"Sample")) {
|
||||
String sName;
|
||||
bool wannaOpenSMPopup=false;
|
||||
if (ins->amiga.initSample<0 || ins->amiga.initSample>=e->song.sampleLen) {
|
||||
sName="none selected";
|
||||
} else {
|
||||
|
@ -4406,71 +4460,191 @@ void FurnaceGUI::drawInsEdit() {
|
|||
ImGui::BeginDisabled(ins->amiga.useWave);
|
||||
P(ImGui::Checkbox("Use sample map",&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("c1",ImGuiTableColumnFlags_WidthStretch);
|
||||
ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthStretch);
|
||||
ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthFixed);
|
||||
ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthFixed);
|
||||
ImGui::TableSetupColumn("c3",ImGuiTableColumnFlags_WidthStretch);
|
||||
|
||||
ImGui::TableSetupScrollFreeze(0,1);
|
||||
|
||||
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("Sample");
|
||||
ImGui::Text("#");
|
||||
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++) {
|
||||
DivInstrumentAmiga::SampleMap& sampleMap=ins->amiga.noteMap[i];
|
||||
ImGui::TableNextRow();
|
||||
ImGui::PushID(fmt::sprintf("NM_%d",i).c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg,ImGui::GetColorU32(ImGuiCol_TableHeaderBg));
|
||||
ImGui::Text("%s",noteNames[60+i]);
|
||||
ImGui::TableNextColumn();
|
||||
if (sampleMap.map<0 || sampleMap.map>=e->song.sampleLen) {
|
||||
sName="-- empty --";
|
||||
sName=fmt::sprintf("---##SM%d",i);
|
||||
sampleMap.map=-1;
|
||||
} else {
|
||||
sName=e->song.sample[sampleMap.map]->name;
|
||||
sName=fmt::sprintf("%3d##SM%d",sampleMap.map,i);
|
||||
}
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
if (ImGui::BeginCombo("##SM",sName.c_str())) {
|
||||
String id;
|
||||
if (ImGui::Selectable("-- empty --",sampleMap.map==-1)) { PARAMETER
|
||||
sampleMap.map=-1;
|
||||
ImGui::PushFont(patFont);
|
||||
ImGui::SetNextItemWidth(ImGui::CalcTextSize("00000").x);
|
||||
ImGui::Selectable(sName.c_str(),(sampleMapWaitingInput && sampleMapColumn==0 && i>=sampleMapMin && i<=sampleMapMax));
|
||||
if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) {
|
||||
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++) {
|
||||
id=fmt::sprintf("%d: %s",j,e->song.sample[j]->name);
|
||||
if (ImGui::Selectable(id.c_str(),sampleMap.map==j)) { PARAMETER
|
||||
sampleMap.map=j;
|
||||
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=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::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
const char* nName="???";
|
||||
sName="???";
|
||||
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;
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
if (sampleMapFocused) {
|
||||
wannaOpenSMPopup=true;
|
||||
}
|
||||
}
|
||||
ImGui::PopFont();
|
||||
|
||||
ImGui::PopID();
|
||||
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();
|
||||
}
|
||||
} else {
|
||||
sampleMapFocused=false;
|
||||
}
|
||||
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();
|
||||
} else {
|
||||
sampleMapFocused=false;
|
||||
}
|
||||
}
|
||||
if (ins->type==DIV_INS_N163) if (ImGui::BeginTabItem(settings.c163Name.c_str())) {
|
||||
|
|
|
@ -34,6 +34,8 @@ void FurnaceGUI::drawNewSong() {
|
|||
avail.y-=ImGui::GetFrameHeightWithSpacing();
|
||||
|
||||
if (ImGui::BeginChild("sysPickerC",avail,false,ImGuiWindowFlags_NoScrollWithMouse|ImGuiWindowFlags_NoScrollbar)) {
|
||||
if (newSongFirstFrame)
|
||||
ImGui::SetKeyboardFocusHere();
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
if (ImGui::InputTextWithHint("##SysSearch","Search...",&newSongQuery)) {
|
||||
String lowerCase=newSongQuery;
|
||||
|
@ -159,4 +161,6 @@ void FurnaceGUI::drawNewSong() {
|
|||
updateWindowTitle();
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
newSongFirstFrame=false;
|
||||
}
|
||||
|
|
|
@ -221,8 +221,9 @@ void FurnaceGUI::drawOsc() {
|
|||
waveform[i]=ImLerp(inRect.Min,inRect.Max,ImVec2(x,0.5f-y));
|
||||
}
|
||||
if (settings.oscEscapesBoundary) {
|
||||
ImDrawList* dlf=ImGui::GetForegroundDrawList();
|
||||
dlf->AddPolyline(waveform,512,color,ImDrawFlags_None,dpiScale);
|
||||
dl->PushClipRectFullScreen();
|
||||
dl->AddPolyline(waveform,512,color,ImDrawFlags_None,dpiScale);
|
||||
dl->PopClipRect();
|
||||
} else {
|
||||
dl->AddPolyline(waveform,512,color,ImDrawFlags_None,dpiScale);
|
||||
}
|
||||
|
|
|
@ -415,10 +415,14 @@ void FurnaceGUI::drawPiano() {
|
|||
e->previewSample(curSample,note);
|
||||
break;
|
||||
default:
|
||||
e->synchronized([this,note]() {
|
||||
e->autoNoteOn(-1,curIns,note);
|
||||
});
|
||||
if (edit && curWindow!=GUI_WINDOW_INS_LIST && curWindow!=GUI_WINDOW_INS_EDIT) noteInput(note,0);
|
||||
if (sampleMapWaitingInput) {
|
||||
alterSampleMap(true,note);
|
||||
} else {
|
||||
e->synchronized([this,note]() {
|
||||
e->autoNoteOn(-1,curIns,note);
|
||||
});
|
||||
if (edit && curWindow!=GUI_WINDOW_INS_LIST && curWindow!=GUI_WINDOW_INS_EDIT) noteInput(note,0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue