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
This commit is contained in:
parent
2ac0e8af42
commit
5567746e0b
2
TODO.md
2
TODO.md
|
@ -1,7 +1,5 @@
|
|||
# to-do for 0.6pre1
|
||||
|
||||
- panning macro
|
||||
- QSound?
|
||||
- piano/input pad
|
||||
- note input via piano
|
||||
- input pad
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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<<bits)-1);
|
||||
int diff=panR-panL;
|
||||
float pan=0.5f;
|
||||
if (diff!=0) {
|
||||
pan=(1.0f+((float)diff/(float)MAX(panL,panR)))*0.5f;
|
||||
}
|
||||
return pan*range;
|
||||
}
|
||||
|
||||
unsigned int DivEngine::convertPanLinearToSplit(int val, unsigned char bits, int range) {
|
||||
if (val<0) val=0;
|
||||
if (val>range) val=range;
|
||||
int maxV=(1<<bits)-1;
|
||||
int panL=(((range-val)*maxV*2))/range;
|
||||
int panR=((val)*maxV*2)/range;
|
||||
if (panL>maxV) panL=maxV;
|
||||
if (panR>maxV) panR=maxV;
|
||||
return (panL<<bits)|panR;
|
||||
}
|
||||
|
||||
void DivEngine::play() {
|
||||
BUSY_BEGIN_SOFT;
|
||||
sPreview.sample=-1;
|
||||
|
@ -1473,6 +1495,9 @@ int DivEngine::addInstrument(int refChan) {
|
|||
default:
|
||||
break;
|
||||
}
|
||||
if (sysOfChan[refChan]==DIV_SYSTEM_QSOUND) {
|
||||
*ins=song.nullInsQSound;
|
||||
}
|
||||
ins->name=fmt::sprintf("Instrument %d",insCount);
|
||||
ins->type=prefType;
|
||||
saveLock.lock();
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {}
|
||||
};
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue