diff --git a/src/engine/instrument.h b/src/engine/instrument.h index 1c8525436..9327c64c6 100644 --- a/src/engine/instrument.h +++ b/src/engine/instrument.h @@ -485,6 +485,9 @@ enum DivWaveSynthEffects { DIV_WS_SUBTRACT, DIV_WS_AVERAGE, DIV_WS_PHASE, + + DIV_WS_SINGLE_MAX, + // two waveform effects DIV_WS_NONE_DUAL=128, DIV_WS_WIPE, @@ -493,12 +496,14 @@ enum DivWaveSynthEffects { DIV_WS_OVERLAY, DIV_WS_NEGATIVE_OVERLAY, DIV_WS_PHASE_DUAL, + + DIV_WS_DUAL_MAX }; struct DivInstrumentWaveSynth { int wave1, wave2; unsigned char rateDivider, width, height; - DivWaveSynthEffects effect; + unsigned char effect; bool oneShot, enabled, global; unsigned char speed, param1, param2, param3, param4; DivInstrumentWaveSynth(): diff --git a/src/engine/waveSynth.cpp b/src/engine/waveSynth.cpp index ca3f342c7..b82539aaf 100644 --- a/src/engine/waveSynth.cpp +++ b/src/engine/waveSynth.cpp @@ -1,5 +1,52 @@ #include "waveSynth.h" +#include "engine.h" bool DivWaveSynth::tick() { - return false; + bool updated=first; + first=false; + + + return updated; +} + +void DivWaveSynth::setEngine(DivEngine* engine) { + e=engine; +} + +void DivWaveSynth::init(DivInstrument* which, int w, int h, bool insChanged) { + if (e==NULL) return; + if (which==NULL) { + state=DivInstrumentWaveSynth(); + } + state=which->ws; + width=w; + height=h; + pos=0; + stage=0; + divCounter=0; + first=true; + + DivWavetable* w1=e->getWave(state.wave1); + DivWavetable* w2=e->getWave(state.wave2); + for (int i=0; imax<1 || w1->len<1) { + wave1[i]=0; + } else { + int data=w1->data[i*w1->len/width]*height/w1->max; + if (data<0) data=0; + if (data>31) data=31; + wave1[i]=data; + } + } + + for (int i=0; imax<1 || w2->len<1) { + wave2[i]=0; + } else { + int data=w2->data[i*w2->len/width]*height/w2->max; + if (data<0) data=0; + if (data>31) data=31; + wave2[i]=data; + } + } } \ No newline at end of file diff --git a/src/engine/waveSynth.h b/src/engine/waveSynth.h index 5e5fd6540..92a5c9a26 100644 --- a/src/engine/waveSynth.h +++ b/src/engine/waveSynth.h @@ -23,9 +23,15 @@ #include "instrument.h" #include "wavetable.h" +class DivEngine; + class DivWaveSynth { - DivInstrument* ins; - int pos, stage, divCounter; + DivEngine* e; + DivInstrumentWaveSynth state; + int pos, stage, divCounter, width, height; + bool first; + unsigned char wave1[256]; + unsigned char wave2[256]; int output[256]; public: /** @@ -33,12 +39,18 @@ class DivWaveSynth { * @return whether the wave has changed. */ bool tick(); - void init(DivInstrument* ins); + void init(DivInstrument* which, int width, int height, bool insChanged=false); + void setEngine(DivEngine* engine); DivWaveSynth(): - ins(NULL), + e(NULL), pos(0), stage(0), - divCounter(0) { + divCounter(0), + width(32), + height(31), + first(false) { + memset(wave1,0,sizeof(int)*256); + memset(wave2,0,sizeof(int)*256); memset(output,0,sizeof(int)*256); } }; diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index abc4af172..71426dfe9 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -157,6 +157,25 @@ const int orderedOps[4]={ 0, 2, 1, 3 }; +const char* singleWSEffects[6]={ + "None", + "Invert", + "Add", + "Subtract", + "Average", + "Phase", +}; + +const char* dualWSEffects[7]={ + "None (dual)", + "Wipe", + "Fade", + "Wipe (ping-pong)", + "Overlay", + "Negative Overlay", + "Phase (dual)", +}; + String macroHoverNote(int id, float val) { if (val<-60 || val>=120) return "???"; return fmt::sprintf("%d: %s",id,noteNames[(int)val+60]); @@ -2301,6 +2320,107 @@ void FurnaceGUI::drawInsEdit() { } ImGui::EndTabItem(); } + if (ins->type==DIV_INS_GB || + ins->type==DIV_INS_AMIGA || + ins->type==DIV_INS_X1_010 || + ins->type==DIV_INS_N163 || + ins->type==DIV_INS_FDS || + ins->type==DIV_INS_SWAN || + ins->type==DIV_INS_PCE || + ins->type==DIV_INS_SCC) { + if (ImGui::BeginTabItem("Wavetable")) { + ImGui::Checkbox("Enable synthesizer",&ins->ws.enabled); + ImGui::SameLine(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ins->ws.effect&0x80) { + if ((ins->ws.effect&0x7f)>=DIV_WS_DUAL_MAX) { + ins->ws.effect=0; + } + } else { + if ((ins->ws.effect&0x7f)>=DIV_WS_SINGLE_MAX) { + ins->ws.effect=0; + } + } + if (ImGui::BeginCombo("##WSEffect",(ins->ws.effect&0x80)?dualWSEffects[ins->ws.effect&0x7f]:singleWSEffects[ins->ws.effect&0x7f])) { + ImGui::Text("Single-waveform"); + ImGui::Indent(); + for (int i=0; iws.effect=i; + } + } + ImGui::Unindent(); + ImGui::Text("Dual-waveform"); + ImGui::Indent(); + for (int i=129; iws.effect=i; + } + } + ImGui::Unindent(); + ImGui::EndCombo(); + } + if (ImGui::BeginTable("WSPreview",2)) { + DivWavetable* wave1=e->getWave(ins->ws.wave1); + DivWavetable* wave2=e->getWave(ins->ws.wave2); + float wavePreview1[256]; + float wavePreview2[256]; + for (int i=0; ilen; i++) { + if (wave1->data[i]>wave1->max) { + wavePreview1[i]=wave1->max; + } else { + wavePreview1[i]=wave1->data[i]; + } + } + for (int i=0; ilen; i++) { + if (wave2->data[i]>wave2->max) { + wavePreview2[i]=wave2->max; + } else { + wavePreview2[i]=wave2->data[i]; + } + } + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImVec2 size1=ImVec2(ImGui::GetContentRegionAvail().x,64.0f*dpiScale); + PlotNoLerp("##WaveformP1",wavePreview1,wave1->len+1,0,NULL,0,wave1->max,size1); + ImGui::TableNextColumn(); + ImVec2 size2=ImVec2(ImGui::GetContentRegionAvail().x,64.0f*dpiScale); + PlotNoLerp("##WaveformP2",wavePreview2,wave2->len+1,0,NULL,0,wave2->max,size2); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("Wave 1"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::InputInt("##SelWave1",&ins->ws.wave1,1,4)) { + if (ins->ws.wave1<0) ins->ws.wave1=0; + if (ins->ws.wave1>=(int)e->song.wave.size()) ins->ws.wave1=e->song.wave.size()-1; + } + ImGui::TableNextColumn(); + ImGui::Text("Wave 2"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::InputInt("##SelWave2",&ins->ws.wave2,1,4)) { + if (ins->ws.wave2<0) ins->ws.wave2=0; + if (ins->ws.wave2>=(int)e->song.wave.size()) ins->ws.wave2=e->song.wave.size()-1; + } + ImGui::EndTable(); + } + + ImGui::InputScalar("Update Rate",ImGuiDataType_U8,&ins->ws.rateDivider,&_ONE,&_SEVEN); + int speed=ins->ws.speed+1; + if (ImGui::InputInt("Speed",&speed,1,16)) { + if (speed<1) speed=1; + if (speed>256) speed=256; + ins->ws.speed=speed-1; + } + + ImGui::InputScalar("Amount",ImGuiDataType_U8,&ins->ws.param1,&_ONE,&_SEVEN); + + ImGui::EndTabItem(); + } + } if (ImGui::BeginTabItem("Macros")) { float asFloat[256]; int asInt[256];