diff --git a/src/engine/instrument.cpp b/src/engine/instrument.cpp index b30f7a9e5..c821bf6bd 100644 --- a/src/engine/instrument.cpp +++ b/src/engine/instrument.cpp @@ -269,6 +269,8 @@ bool DivInstrumentSID3::operator==(const DivInstrumentSID3& other) { _C(separateNoisePitch) && _C(special_wave) && _C(doWavetable) && + _C(phaseInv) && + _C(feedback) && _C(filt[0]) && _C(filt[1]) && _C(filt[2]) && diff --git a/src/engine/instrument.h b/src/engine/instrument.h index a736694df..1bd80ca93 100644 --- a/src/engine/instrument.h +++ b/src/engine/instrument.h @@ -874,6 +874,8 @@ struct DivInstrumentSID3 bool separateNoisePitch; unsigned char special_wave; bool doWavetable; + unsigned char phaseInv; + unsigned char feedback; struct Filter { @@ -940,7 +942,9 @@ struct DivInstrumentSID3 oneBitNoise(false), separateNoisePitch(false), special_wave(0), - doWavetable(false) + doWavetable(false), + phaseInv(0), + feedback(0) { filt[0].mode = 16 | 32; //default settings so filter just works, connect to input and channel output filt[0].output_volume = 0xff; diff --git a/src/engine/platform/sid3.cpp b/src/engine/platform/sid3.cpp index f04b848dd..cdbc121d3 100644 --- a/src/engine/platform/sid3.cpp +++ b/src/engine/platform/sid3.cpp @@ -308,6 +308,10 @@ void DivPlatformSID3::tick(bool sysTick) panChanged = true; chan[i].panRight = chan[i].std.panR.val & 0xff; } + if (chan[i].std.op[2].ar.had) { //channel signal inversion + chan[i].phaseInv = chan[i].std.op[2].ar.val & 3; + rWrite(SID3_REGISTER_PHASE_INVERSION + i * SID3_REGISTERS_PER_CHANNEL, chan[i].phaseInv); + } if (chan[i].std.op[0].am.had) { //key on/off chan[i].gate = chan[i].std.op[0].am.val & 1; flagsChanged = true; @@ -330,6 +334,10 @@ void DivPlatformSID3::tick(bool sysTick) chan[i].phaseSrc = chan[i].std.fb.val & 0xff; rWrite(SID3_REGISTER_PHASE_MOD_SRC + i * SID3_REGISTERS_PER_CHANNEL, chan[i].phaseSrc); } + if (chan[i].std.op[3].ar.had) { //feedback + chan[i].feedback = chan[i].std.op[3].ar.val & 0xff; + rWrite(SID3_REGISTER_FEEDBACK + i * SID3_REGISTERS_PER_CHANNEL, chan[i].feedback); + } if (chan[i].std.phaseReset.had) { chan[i].phaseReset = chan[i].std.phaseReset.val & 1; @@ -517,6 +525,9 @@ void DivPlatformSID3::tick(bool sysTick) rWrite(SID3_REGISTER_SYNC_SRC + i * SID3_REGISTERS_PER_CHANNEL, chan[i].syncSrc); //hard sync source rWrite(SID3_REGISTER_PHASE_MOD_SRC + i * SID3_REGISTERS_PER_CHANNEL, chan[i].phaseSrc); //phase mod source + rWrite(SID3_REGISTER_PHASE_INVERSION + i * SID3_REGISTERS_PER_CHANNEL, chan[i].phaseInv); //signal inversion + rWrite(SID3_REGISTER_FEEDBACK + i * SID3_REGISTERS_PER_CHANNEL, chan[i].feedback); //feedback + updateEnvelope(i); updateFlags(i, false); //gate off TODO: make it properly? @@ -677,9 +688,13 @@ int DivPlatformSID3::dispatch(DivCommand c) { chan[c.chan].ringSrc = ins->sid3.ring_mod_source; chan[c.chan].syncSrc = ins->sid3.sync_source; + chan[c.chan].phaseSrc = ins->sid3.phase_mod_source; chan[c.chan].independentNoiseFreq = ins->sid3.separateNoisePitch; + chan[c.chan].phaseInv = ins->sid3.phaseInv; + chan[c.chan].feedback = ins->sid3.feedback; + for(int j = 0; j < SID3_NUM_FILTERS; j++) { if(ins->sid3.filt[j].init) diff --git a/src/engine/platform/sid3.h b/src/engine/platform/sid3.h index c6416350d..caa499328 100644 --- a/src/engine/platform/sid3.h +++ b/src/engine/platform/sid3.h @@ -76,6 +76,9 @@ class DivPlatformSID3: public DivDispatch { unsigned long long dacPos; int dacSample; + unsigned char phaseInv; + unsigned char feedback; + void handleArpNoise(int offset=0) { DivMacroStruct& m = this->std.op[3].am; @@ -164,14 +167,16 @@ class DivPlatformSID3: public DivDispatch { panRight(0xff), noiseFreq(0), independentNoiseFreq(false), - noiseLFSRMask((1 << 29) | (1 << 5) | (1 << 3) | 1), + noiseLFSRMask((1 << 29) | (1 << 5) | (1 << 3) | 1), //https://docs.amd.com/v/u/en-US/xapp052 for 30 bits: 30, 6, 4, 1 pcm(false), wavetable(-1), dacPeriod(0), dacRate(0), dacOut(0), dacPos(0), - dacSample(-1) {} //https://docs.amd.com/v/u/en-US/xapp052 for 30 bits: 30, 6, 4, 1 + dacSample(-1), + phaseInv(0), + feedback(0) {} }; Channel chan[SID3_NUM_CHANNELS]; DivDispatchOscBuffer* oscBuf[SID3_NUM_CHANNELS]; diff --git a/src/engine/platform/sound/sid3.c b/src/engine/platform/sound/sid3.c index d7057553f..66b794a56 100644 --- a/src/engine/platform/sound/sid3.c +++ b/src/engine/platform/sound/sid3.c @@ -3061,6 +3061,11 @@ void sid3_clock(SID3* sid3) ch->noise_accumulator += ch->phase_mod_source == SID3_NUM_CHANNELS - 1 ? ((uint64_t)sid3->wave_channel_output << 18) : ((uint64_t)sid3->channel_output[ch->phase_mod_source] << 18); } + if(ch->feedback) + { + ch->accumulator += (ch->prev_output + ch->prev_output2) * ch->feedback; + } + ch->accumulator &= SID3_ACC_MASK; if((prev_noise_acc & ((uint32_t)1 << (SID3_ACC_BITS - 6))) != (ch->noise_accumulator & ((uint32_t)1 << (SID3_ACC_BITS - 6)))) @@ -3097,11 +3102,17 @@ void sid3_clock(SID3* sid3) { output = ch->output_before_filter; } + + if(ch->feedback) + { + ch->prev_output2 = ch->prev_output; + ch->prev_output = output + 0xffff; + } if(!sid3->muted[i]) { - sid3->output_l += output * ch->panning_left / 0x8f0; - sid3->output_r += output * ch->panning_right / 0x8f0; + sid3->output_l += output * ch->panning_left / 0x8f0 * ((ch->phase_inv & SID3_INV_SIGNAL_LEFT) ? -1 : 1); + sid3->output_r += output * ch->panning_right / 0x8f0 * ((ch->phase_inv & SID3_INV_SIGNAL_RIGHT) ? -1 : 1); } sid3->channel_output[i] = output; @@ -3189,8 +3200,8 @@ void sid3_clock(SID3* sid3) if(!sid3->muted[SID3_NUM_CHANNELS - 1]) { - sid3->output_l += output * ch->panning_left / 0x8f0; - sid3->output_r += output * ch->panning_right / 0x8f0; + sid3->output_l += output * ch->panning_left / 0x8f0 * ((ch->phase_inv & SID3_INV_SIGNAL_LEFT) ? -1 : 1); + sid3->output_r += output * ch->panning_right / 0x8f0 * ((ch->phase_inv & SID3_INV_SIGNAL_RIGHT) ? -1 : 1); } sid3->wave_channel_output = output; @@ -3726,6 +3737,26 @@ void sid3_write(SID3* sid3, uint16_t address, uint8_t data) } break; } + case SID3_REGISTER_PHASE_INVERSION: + { + if(channel != SID3_NUM_CHANNELS - 1) + { + sid3->chan[channel].phase_inv = data; + } + else + { + sid3->wave_chan.phase_inv = data; + } + break; + } + case SID3_REGISTER_FEEDBACK: + { + if(channel != SID3_NUM_CHANNELS - 1) + { + sid3->chan[channel].feedback = (uint32_t)data * (uint32_t)data; + } + break; + } default: break; } } diff --git a/src/engine/platform/sound/sid3.h b/src/engine/platform/sound/sid3.h index 24756a6c6..2b517a516 100644 --- a/src/engine/platform/sound/sid3.h +++ b/src/engine/platform/sound/sid3.h @@ -42,6 +42,12 @@ enum Flags SID3_CHAN_1_BIT_NOISE = 128, }; +enum PhaseInversion +{ + SID3_INV_SIGNAL_RIGHT = 1, + SID3_INV_SIGNAL_LEFT = 2, +}; + enum Waveforms { SID3_WAVE_TRIANGLE = 1, @@ -127,6 +133,10 @@ enum Registers SID3_REGISTER_STREAMED_SAMPLE_HIGH = SID3_REGISTER_AFTER_FILT_1ST_REG + 10, SID3_REGISTER_STREAMED_SAMPLE_LOW = SID3_REGISTER_AFTER_FILT_1ST_REG + 11, + + SID3_REGISTER_PHASE_INVERSION = SID3_REGISTER_AFTER_FILT_1ST_REG + 12, + + SID3_REGISTER_FEEDBACK = SID3_REGISTER_AFTER_FILT_1ST_REG + 13, }; typedef struct @@ -200,9 +210,13 @@ typedef struct uint8_t clock_filter; uint8_t panning_left, panning_right; - bool invert_left, invert_right; //invert channel signal int32_t output_before_filter; + + uint8_t phase_inv; + uint32_t feedback; + + int32_t prev_output, prev_output2; } sid3_channel; typedef struct @@ -229,9 +243,10 @@ typedef struct uint8_t clock_filter; uint8_t panning_left, panning_right; - bool invert_left, invert_right; //invert channel signal int32_t output_before_filter; + + uint8_t phase_inv; } sid3_wavetable_chan; typedef struct diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index d1e28e70b..5baff3310 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -5915,6 +5915,24 @@ void FurnaceGUI::drawInsSID3(DivInstrument* ins) ImGui::SetTooltip(_("Forces waveform macro to control wavetable index.")); } + bool invLeft=ins->sid3.phaseInv & SID3_INV_SIGNAL_LEFT; + if (ImGui::Checkbox(_("Inv. left"),&invLeft)) { PARAMETER + ins->sid3.phaseInv^=SID3_INV_SIGNAL_LEFT; + } + if (ImGui::IsItemHovered()) + { + ImGui::SetTooltip(_("Invert left channel signal")); + } + ImGui::SameLine(); + bool invRight=ins->sid3.phaseInv & SID3_INV_SIGNAL_RIGHT; + if (ImGui::Checkbox(_("Inv. right"),&invRight)) { PARAMETER + ins->sid3.phaseInv^=SID3_INV_SIGNAL_RIGHT; + } + if (ImGui::IsItemHovered()) + { + ImGui::SetTooltip(_("Invert right channel signal")); + } + ImGui::TableNextColumn(); CENTER_TEXT(_("Special wave preview")); @@ -5975,6 +5993,7 @@ void FurnaceGUI::drawInsSID3(DivInstrument* ins) strncpy(buffer,macroSID3WaveMixMode(0,(float)ins->sid2.mixMode,NULL).c_str(),40); P(CWSliderScalar(_("Wave Mix Mode"),ImGuiDataType_U8,&ins->sid2.mixMode,&_ZERO,&_FOUR,buffer)); P(CWSliderScalar(_("Duty"),ImGuiDataType_U16,&ins->c64.duty,&_ZERO,&_SIXTY_FIVE_THOUSAND_FIVE_HUNDRED_THIRTY_FIVE)); rightClickable + P(CWSliderScalar(_("Feedback"),ImGuiDataType_U8,&ins->sid3.feedback,&_ZERO,&_TWO_HUNDRED_FIFTY_FIVE)); bool resetDuty=ins->c64.resetDuty; if (ImGui::Checkbox(_("Reset duty on new note"),&resetDuty)) { PARAMETER @@ -6261,6 +6280,8 @@ void FurnaceGUI::drawInsSID3(DivInstrument* ins) macroList.push_back(FurnaceGUIMacroDesc(_("Panning (left)"),&ins->std.panLMacro,0,255,160,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL)); macroList.push_back(FurnaceGUIMacroDesc(_("Panning (right)"),&ins->std.panRMacro,0,255,160,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc(_("Channel inversion"),&ins->std.opMacros[2].arMacro,0,2,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,minModModeBits)); + macroList.push_back(FurnaceGUIMacroDesc(_("Key On/Off"),&ins->std.opMacros[0].amMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); macroList.push_back(FurnaceGUIMacroDesc(_("Special"),&ins->std.ex1Macro,0,3,48,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,sid3ControlBits)); @@ -6268,6 +6289,11 @@ void FurnaceGUI::drawInsSID3(DivInstrument* ins) macroList.push_back(FurnaceGUIMacroDesc(_("Ring Mod Source"),&ins->std.fmsMacro,0,SID3_NUM_CHANNELS,64,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,macroSID3SourceChan)); macroList.push_back(FurnaceGUIMacroDesc(_("Hard Sync Source"),&ins->std.amsMacro,0,SID3_NUM_CHANNELS - 1,64,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,macroSID3SourceChan)); macroList.push_back(FurnaceGUIMacroDesc(_("Phase Mod Source"),&ins->std.fbMacro,0,SID3_NUM_CHANNELS - 1,64,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,macroSID3SourceChan)); + + if(!ins->sid3.doWavetable) + { + macroList.push_back(FurnaceGUIMacroDesc(_("Feedback"),&ins->std.opMacros[3].arMacro,0,255,160,uiColors[GUI_COLOR_MACRO_OTHER])); + } macroList.push_back(FurnaceGUIMacroDesc(_("Phase Reset"),&ins->std.phaseResetMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true));