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
10 changed files with 87 additions and 11 deletions
|
|
@ -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…
Add table
Add a link
Reference in a new issue