From 5567746e0bec407a2f1bba376c701a8c3a4cb76d Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 28 Apr 2022 23:58:11 -0500 Subject: [PATCH] QSound: implement panning macro - PLEASE READ the panning strategy for QSound has changed! it's now 08xy where x is left and y is right (muting is not possible though!) this makes it consistent with other chips, plus QSound's pan range was 32 anyway in order to toggle the QSound effect use effect 12xx --- TODO.md | 2 -- papers/doc/7-systems/qsound.md | 9 +++++---- src/engine/dispatch.h | 1 + src/engine/engine.cpp | 25 +++++++++++++++++++++++++ src/engine/engine.h | 4 ++++ src/engine/platform/qsound.cpp | 19 ++++++++++++++++++- src/engine/platform/qsound.h | 4 +++- src/engine/playback.cpp | 4 ++++ src/engine/song.h | 4 +++- src/gui/insEdit.cpp | 26 ++++++++++++++++++++++++-- 10 files changed, 87 insertions(+), 11 deletions(-) diff --git a/TODO.md b/TODO.md index 341d66dde..77eb864fd 100644 --- a/TODO.md +++ b/TODO.md @@ -1,7 +1,5 @@ # to-do for 0.6pre1 -- panning macro - - QSound? - piano/input pad - note input via piano - input pad 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/src/engine/dispatch.h b/src/engine/dispatch.h index 1ab4cba15..d86f1b20f 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -125,6 +125,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 c728e2152..c42f5cc5d 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -1041,6 +1041,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(); diff --git a/src/engine/engine.h b/src/engine/engine.h index 441096a23..9f971d7a8 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -475,6 +475,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/qsound.cpp b/src/engine/platform/qsound.cpp index 8450d965b..9dd34589a 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/playback.cpp b/src/engine/playback.cpp index 13cc495b9..5fd031167 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -127,6 +127,7 @@ const char* cmdName[]={ "QSOUND_ECHO_FEEDBACK", "QSOUND_ECHO_DELAY", "QSOUND_ECHO_LEVEL", + "QSOUND_SURROUND", "X1_010_ENVELOPE_SHAPE", "X1_010_ENVELOPE_ENABLE", @@ -434,6 +435,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/insEdit.cpp b/src/gui/insEdit.cpp index 716683358..fbd843581 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -212,6 +212,7 @@ const char* dualWSEffects[7]={ const char* macroAbsoluteMode="Fixed"; const char* macroRelativeMode="Relative"; +const char* macroQSoundMode="QSound"; const char* macroDummyMode="Bug"; @@ -2839,8 +2840,10 @@ void FurnaceGUI::drawInsEdit() { } if (ins->type==DIV_INS_SAA1099) ex1Max=8; + 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; @@ -2851,6 +2854,15 @@ void FurnaceGUI::drawInsEdit() { if (ins->type==DIV_INS_X1_010 || ins->type==DIV_INS_PCE || ins->type==DIV_INS_MIKEY || ins->type==DIV_INS_SAA1099) { panMax=15; } + 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); @@ -2872,8 +2884,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,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[13],0,panMax,NULL,false); } else { - NORMAL_MACRO(ins->std.panLMacro,0,panMax,"panL","Panning (left)",(31+panMax),ins->std.panLMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[13],0,panMax,NULL,false); - NORMAL_MACRO(ins->std.panRMacro,0,panMax,"panR","Panning (right)",(31+panMax),ins->std.panRMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,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",(31+panMax-panMin),ins->std.panLMacro.open,false,NULL,false,NULL,0,0,0,0,(ins->type==DIV_INS_AMIGA),(ins->type==DIV_INS_AMIGA?1:0),macroQSoundMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[13],panMin,panMax,NULL,false); + } else { + NORMAL_MACRO(ins->std.panLMacro,panMin,panMax,"panL","Panning (left)",(31+panMax-panMin),ins->std.panLMacro.open,false,NULL,false,NULL,0,0,0,0,(ins->type==DIV_INS_AMIGA),(ins->type==DIV_INS_AMIGA?1:0),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,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[14],0,1,NULL,false); + } else { + NORMAL_MACRO(ins->std.panRMacro,panMin,panMax,"panR","Panning (right)",(31+panMax-panMin),ins->std.panRMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,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,1,macroRelativeMode,uiColors[GUI_COLOR_MACRO_PITCH],mmlString[15],-2048,2047,NULL,!ins->std.pitchMacro.mode);