diff --git a/src/engine/instrument.cpp b/src/engine/instrument.cpp index b04f56afa..5706b260c 100644 --- a/src/engine/instrument.cpp +++ b/src/engine/instrument.cpp @@ -265,6 +265,7 @@ bool DivInstrumentSID3::operator==(const DivInstrumentSID3& other) { _C(sync_source) && _C(specialWaveOn) && _C(oneBitNoise) && + _C(separateNoisePitch) && _C(special_wave) && _C(filt[0]) && _C(filt[1]) && diff --git a/src/engine/instrument.h b/src/engine/instrument.h index fc3f56b5c..9ae772c07 100644 --- a/src/engine/instrument.h +++ b/src/engine/instrument.h @@ -870,6 +870,7 @@ struct DivInstrumentSID3 unsigned char phase_mod_source, ring_mod_source, sync_source; bool specialWaveOn; bool oneBitNoise; + bool separateNoisePitch; unsigned char special_wave; struct Filter @@ -914,6 +915,7 @@ struct DivInstrumentSID3 sync_source(0), specialWaveOn(false), oneBitNoise(false), + separateNoisePitch(false), special_wave(0) { filt[0].mode = 16 | 32; //default settings so filter just works, connect to input and channel output diff --git a/src/engine/platform/sid3.cpp b/src/engine/platform/sid3.cpp index 0fabcf531..40981f328 100644 --- a/src/engine/platform/sid3.cpp +++ b/src/engine/platform/sid3.cpp @@ -142,6 +142,13 @@ void DivPlatformSID3::updateFreq(int channel) rWrite(SID3_REGISTER_FREQ_LOW + channel*SID3_REGISTERS_PER_CHANNEL,chan[channel].freq & 0xff); } +void DivPlatformSID3::updateNoiseFreq(int channel) +{ + rWrite(SID3_REGISTER_NOISE_FREQ_HIGH + channel*SID3_REGISTERS_PER_CHANNEL,(chan[channel].noiseFreq >> 16) & 0xff); + rWrite(SID3_REGISTER_NOISE_FREQ_MID + channel*SID3_REGISTERS_PER_CHANNEL,(chan[channel].noiseFreq >> 8) & 0xff); + rWrite(SID3_REGISTER_NOISE_FREQ_LOW + channel*SID3_REGISTERS_PER_CHANNEL,chan[channel].noiseFreq & 0xff); +} + void DivPlatformSID3::updateDuty(int channel) { rWrite(SID3_REGISTER_PW_HIGH + channel*SID3_REGISTERS_PER_CHANNEL,(chan[channel].duty >> 8) & 0xff); @@ -170,6 +177,8 @@ void DivPlatformSID3::tick(bool sysTick) chan[i].std.next(); bool panChanged = false; + bool flagsChanged = false; + bool envChanged = false; if (chan[i].std.vol.had) { @@ -203,6 +212,22 @@ void DivPlatformSID3::tick(bool sysTick) chan[i].duty&=65535; updateDuty(i); } + if (chan[i].std.wave.had) { + chan[i].wave = chan[i].std.wave.val & 0xff; + rWrite(SID3_REGISTER_WAVEFORM + i * SID3_REGISTERS_PER_CHANNEL, chan[i].wave); + } + if (chan[i].std.alg.had) { //special wave + chan[i].special_wave = chan[i].std.alg.val & 0xff; + rWrite(SID3_REGISTER_SPECIAL_WAVE + i * SID3_REGISTERS_PER_CHANNEL, chan[i].special_wave); + } + if (chan[i].std.op[3].am.had) { //noise arpeggio + chan[i].handleArpNoise(0); + chan[i].noiseFreqChanged = true; + } + if (chan[i].std.op[0].ar.had) { //noise pitch + chan[i].handlePitchNoise(); + chan[i].noiseFreqChanged = true; + } if (chan[i].std.panL.had) { panChanged = true; chan[i].panLeft = chan[i].std.panL.val & 0xff; @@ -211,21 +236,89 @@ void DivPlatformSID3::tick(bool sysTick) panChanged = true; chan[i].panRight = chan[i].std.panR.val & 0xff; } + if (chan[i].std.op[0].am.had) { //key on/off + chan[i].gate = chan[i].std.op[0].am.val & 1; + flagsChanged = true; + } + if (chan[i].std.ex1.had) { //ring mod, hard sync, phase mod + chan[i].phase = chan[i].std.ex1.val & 1; + chan[i].sync = chan[i].std.ex1.val & 2; + chan[i].ring = chan[i].std.ex1.val & 4; + flagsChanged = true; + } + if (chan[i].std.ams.had) { //hard sync source + chan[i].syncSrc = chan[i].std.ams.val & 0xff; + rWrite(SID3_REGISTER_SYNC_SRC + i * SID3_REGISTERS_PER_CHANNEL, chan[i].syncSrc); + } + if (chan[i].std.fms.had) { //ring mod source + chan[i].ringSrc = chan[i].std.fms.val & 0xff; + rWrite(SID3_REGISTER_RING_MOD_SRC + i * SID3_REGISTERS_PER_CHANNEL, chan[i].ringSrc); + } + if (chan[i].std.fb.had) { //phase mod source + 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.phaseReset.had) { chan[i].phaseReset = chan[i].std.phaseReset.val & 1; if(chan[i].phaseReset) { - updateFlags(i, chan[i].gate); + flagsChanged = true; } + } + if (chan[i].std.op[1].am.had) { //noise phase reset + chan[i].phaseResetNoise = chan[i].std.op[1].am.val & 1; - chan[i].phaseReset = false; + if(chan[i].phaseResetNoise) + { + flagsChanged = true; + } + } + if (chan[i].std.op[2].am.had) { //envelope reset + chan[i].envReset = chan[i].std.op[2].am.val & 1; + + if(chan[i].envReset) + { + flagsChanged = true; + } + } + if (chan[i].std.ex2.had) { //attack + chan[i].attack = chan[i].std.ex2.val & 0xff; + envChanged = true; + } + if (chan[i].std.ex3.had) { //decay + chan[i].decay = chan[i].std.ex3.val & 0xff; + envChanged = true; + } + if (chan[i].std.ex4.had) { //sustain + chan[i].sustain = chan[i].std.ex4.val & 0xff; + envChanged = true; + } + if (chan[i].std.ex5.had) { //sustain rate + chan[i].sr = chan[i].std.ex5.val & 0xff; + envChanged = true; + } + if (chan[i].std.ex6.had) { //release + chan[i].release = chan[i].std.ex6.val & 0xff; + envChanged = true; } if(panChanged) { updatePanning(i); } + if(flagsChanged) + { + updateFlags(i, chan[i].gate); + + chan[i].phaseReset = false; + chan[i].phaseResetNoise = false; + chan[i].envReset = false; + } + if(envChanged) + { + updateEnvelope(i); + } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { @@ -265,6 +358,11 @@ void DivPlatformSID3::tick(bool sysTick) updateFreq(i); + if(!chan[i].independentNoiseFreq) + { + chan[i].noiseFreqChanged = true; + } + //rWrite(i*7,chan[i].freq&0xff); //rWrite(i*7+1,chan[i].freq>>8); //rWrite(0x1e, (chan[0].noise_mode) | (chan[1].noise_mode << 2) | (chan[2].noise_mode << 4) | ((chan[0].freq >> 16) << 6) | ((chan[1].freq >> 16) << 7)); @@ -273,6 +371,25 @@ void DivPlatformSID3::tick(bool sysTick) if (chan[i].keyOff) chan[i].keyOff=false; chan[i].freqChanged=false; } + + if(chan[i].noiseFreqChanged) + { + if(chan[i].independentNoiseFreq) + { + chan[i].noiseFreq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].noise_fixedArp?chan[i].noise_baseNoteOverride:chan[i].noise_arpOff,chan[i].noise_fixedArp,false,2,chan[i].noise_pitch2,chipClock,CHIP_FREQBASE * 64); + + if (chan[i].noiseFreq<0) chan[i].noiseFreq=0; + if (chan[i].noiseFreq>0xffffff) chan[i].noiseFreq=0xffffff; + } + else + { + chan[i].noiseFreq = chan[i].freq; + } + + updateNoiseFreq(i); + + chan[i].noiseFreqChanged = false; + } } } @@ -330,6 +447,8 @@ 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].independentNoiseFreq = ins->sid3.separateNoisePitch; + for(int j = 0; j < SID3_NUM_FILTERS; j++) { if(ins->sid3.filt[j].init) @@ -555,6 +674,8 @@ void DivPlatformSID3::reset() { } chan[i].panLeft = chan[i].panRight = 0xff; + + chan[i].freq = chan[i].noiseFreq = 0; updatePanning(i); } diff --git a/src/engine/platform/sid3.h b/src/engine/platform/sid3.h index 1d918cb1b..268d58b11 100644 --- a/src/engine/platform/sid3.h +++ b/src/engine/platform/sid3.h @@ -31,11 +31,14 @@ class DivPlatformSID3: public DivDispatch { unsigned short duty; bool resetMask, resetFilter, resetDuty, gate, ring, sync, phase, oneBitNoise; bool phaseReset, envReset, phaseResetNoise; + bool noiseFreqChanged; unsigned char vol; unsigned char noise_mode; unsigned char mix_mode; unsigned char ringSrc, syncSrc, phaseSrc; unsigned char panLeft, panRight; + unsigned int noiseFreq; + bool independentNoiseFreq; struct Filter { @@ -57,6 +60,68 @@ class DivPlatformSID3: public DivDispatch { filter_matrix(0) {} } filt[SID3_NUM_FILTERS]; + int noise_baseNoteOverride; + bool noise_fixedArp; + int noise_arpOff; + int noise_pitch2; + bool noise_hasArp; + bool noise_hasPitch; + + void handleArpNoise(int offset=0) + { + DivMacroStruct& m = this->std.op[3].am; + + if (m.had) { + noise_hasArp=true; + + if (m.val<0) { + if (!(m.val&0x40000000)) { + noise_baseNoteOverride=(m.val|0x40000000)+offset; + noise_fixedArp=true; + } else { + noise_arpOff=m.val; + noise_fixedArp=false; + } + } else { + if (m.val&0x40000000) { + noise_baseNoteOverride=(m.val&(~0x40000000))+offset; + noise_fixedArp=true; + } else { + noise_arpOff=m.val; + noise_fixedArp=false; + } + } + noiseFreqChanged=true; + } + + else + { + noise_hasArp=false; + } + } + + void handlePitchNoise() + { + DivMacroStruct& m = this->std.op[0].ar; + + if (m.had) { + noise_hasPitch=true; + + if (m.mode) { + noise_pitch2+=m.val; + CLAMP_VAR(noise_pitch2,-131071,131071); + } else { + noise_pitch2=m.val; + } + noiseFreqChanged=true; + } + + else + { + noise_hasPitch=false; + } + } + Channel(): SharedChannel(SID3_MAX_VOL), prevFreq(0x1ffff), @@ -79,6 +144,7 @@ class DivPlatformSID3: public DivDispatch { phaseReset(false), envReset(false), phaseResetNoise(false), + noiseFreqChanged(false), vol(SID3_MAX_VOL), noise_mode(0), mix_mode(0), @@ -86,7 +152,9 @@ class DivPlatformSID3: public DivDispatch { syncSrc(0), phaseSrc(0), panLeft(0xff), - panRight(0xff) {} + panRight(0xff), + noiseFreq(0), + independentNoiseFreq(false) {} }; Channel chan[SID3_NUM_CHANNELS]; DivDispatchOscBuffer* oscBuf[SID3_NUM_CHANNELS]; @@ -111,6 +179,7 @@ class DivPlatformSID3: public DivDispatch { void updateFlags(int channel, bool gate); void updateFilter(int channel, int filter); void updateFreq(int channel); + void updateNoiseFreq(int channel); void updateDuty(int channel); void updateEnvelope(int channel); void updatePanning(int channel); diff --git a/src/engine/platform/sound/sid3.c b/src/engine/platform/sound/sid3.c index 2616e0919..2259461b1 100644 --- a/src/engine/platform/sound/sid3.c +++ b/src/engine/platform/sound/sid3.c @@ -2315,6 +2315,9 @@ SID3* sid3_create() { sid3->env_counter_to_exponential_output[i] = (exp(-2.0 + (double)i * 4.0 / (double)SID3_EXPONENTIAL_LUT_LENGTH) - min_val) * scale_factor; } + + sid3->env_counter_to_exponential_output[0] = 0; //just in case + for(uint32_t i = 0; i < SID3_EXPONENTIAL_LUT_LENGTH; i++) { sid3->exponential_output_to_envelope_counter[(uint64_t)sid3->env_counter_to_exponential_output[i] * (uint64_t)SID3_EXPONENTIAL_LUT_LENGTH / (uint64_t)0xffff] = i << 8; @@ -2362,6 +2365,8 @@ void sid3_reset(SID3* sid3) } //TODO: wavetable chan + memset(&sid3->wave_chan, 0, sizeof(sid3_wavetable_chan)); + sid3->wave_chan.adsr.hold_zero = true; } void sid3_gate_bit(SID3* sid3, uint8_t gate, sid3_channel_adsr* adsr) @@ -2885,8 +2890,10 @@ void sid3_clock(SID3* sid3) sid3_channel* ch = &sid3->chan[i]; uint32_t prev_acc = ch->accumulator; + uint32_t prev_noise_acc = ch->noise_accumulator; ch->accumulator += ch->frequency; + ch->noise_accumulator += ch->noise_frequency; ch->sync_bit = 0; @@ -2897,22 +2904,38 @@ void sid3_clock(SID3* sid3) if(ch->flags & SID3_CHAN_ENABLE_HARD_SYNC) { - if(sid3->chan[ch->hard_sync_src].sync_bit) + if(ch->hard_sync_src == SID3_NUM_CHANNELS - 1) //wave chan { - ch->accumulator = 0; + if(sid3->wave_chan.sync_bit) + { + ch->accumulator = 0; + ch->noise_accumulator = 0; + ch->lfsr = 0x1; + } + } + else + { + if(sid3->chan[ch->hard_sync_src].sync_bit) + { + ch->accumulator = 0; + ch->noise_accumulator = 0; + ch->lfsr = 0x1; + } } } uint32_t acc_state = ch->accumulator; + uint32_t noise_acc_state = ch->noise_accumulator; if(ch->flags & SID3_CHAN_ENABLE_PHASE_MOD) { ch->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); + 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); } ch->accumulator &= SID3_ACC_MASK; - if((prev_acc & ((uint32_t)1 << (SID3_ACC_BITS - 6))) != (ch->accumulator & ((uint32_t)1 << (SID3_ACC_BITS - 6)))) + if((prev_noise_acc & ((uint32_t)1 << (SID3_ACC_BITS - 6))) != (ch->noise_accumulator & ((uint32_t)1 << (SID3_ACC_BITS - 6)))) { sid3_clock_lfsr(ch); } @@ -2920,6 +2943,7 @@ void sid3_clock(SID3* sid3) int32_t waveform = sid3_get_waveform(sid3, ch); ch->accumulator = acc_state & SID3_ACC_MASK; + ch->noise_accumulator = noise_acc_state & SID3_ACC_MASK; sid3->channel_signals_before_ADSR[i] = waveform; @@ -2985,6 +3009,7 @@ void sid3_write(SID3* sid3, uint16_t address, uint8_t data) sid3->chan[channel].adsr.envelope_counter = 0; sid3->chan[channel].adsr.state = ATTACK; sid3->chan[channel].adsr.envelope_speed = envspd_a(sid3->chan[channel].adsr.a); + sid3->chan[channel].adsr.hold_zero = false; } if(sid3->chan[channel].flags & SID3_CHAN_PHASE_RESET) @@ -3381,6 +3406,33 @@ void sid3_write(SID3* sid3, uint16_t address, uint8_t data) } break; } + case SID3_REGISTER_NOISE_FREQ_HIGH: + { + if(channel != SID3_NUM_CHANNELS - 1) + { + sid3->chan[channel].noise_frequency &= 0x00ffff; + sid3->chan[channel].noise_frequency |= data << 16; + } + break; + } + case SID3_REGISTER_NOISE_FREQ_MID: + { + if(channel != SID3_NUM_CHANNELS - 1) + { + sid3->chan[channel].noise_frequency &= 0xff00ff; + sid3->chan[channel].noise_frequency |= data << 8; + } + break; + } + case SID3_REGISTER_NOISE_FREQ_LOW: + { + if(channel != SID3_NUM_CHANNELS - 1) + { + sid3->chan[channel].noise_frequency &= 0xffff00; + sid3->chan[channel].noise_frequency |= data; + } + break; + } default: break; } } diff --git a/src/engine/platform/sound/sid3.h b/src/engine/platform/sound/sid3.h index ed9353f30..a68a9bb5d 100644 --- a/src/engine/platform/sound/sid3.h +++ b/src/engine/platform/sound/sid3.h @@ -112,8 +112,18 @@ enum Registers SID3_REGISTER_AFTER_FILT_1ST_REG = SID3_REGISTER_FILT_BASE + SID3_REGISTERS_PER_FILTER * SID3_NUM_FILTERS, SID3_REGISTER_PHASE_MOD_SRC = SID3_REGISTER_AFTER_FILT_1ST_REG, + SID3_REGISTER_PAN_LEFT = SID3_REGISTER_AFTER_FILT_1ST_REG + 1, SID3_REGISTER_PAN_RIGHT = SID3_REGISTER_AFTER_FILT_1ST_REG + 2, + + SID3_REGISTER_NOISE_FREQ_HIGH = SID3_REGISTER_AFTER_FILT_1ST_REG + 3, + SID3_REGISTER_NOISE_FREQ_MID = SID3_REGISTER_AFTER_FILT_1ST_REG + 4, + SID3_REGISTER_NOISE_FREQ_LOW = SID3_REGISTER_AFTER_FILT_1ST_REG + 5, + + SID3_REGISTER_NOISE_LFSR_HIGHEST = SID3_REGISTER_AFTER_FILT_1ST_REG + 6, + SID3_REGISTER_NOISE_LFSR_HIGH = SID3_REGISTER_AFTER_FILT_1ST_REG + 7, + SID3_REGISTER_NOISE_LFSR_MID = SID3_REGISTER_AFTER_FILT_1ST_REG + 8, + SID3_REGISTER_NOISE_LFSR_LOW = SID3_REGISTER_AFTER_FILT_1ST_REG + 9, }; typedef struct diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index 2fc237a2c..4a134d33a 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -2160,7 +2160,7 @@ void DivEngine::registerSystems() { ); sysDefs[DIV_SYSTEM_SID3]=new DivSysDef( - _("SID3"), NULL, 0xf5, 0, 7, false, true, 0, false, 0, 0, 0, + _("SID3"), NULL, 0xf5, 0, 7, false, true, 0, false, (1U << DIV_SAMPLE_DEPTH_8BIT) | (1U << DIV_SAMPLE_DEPTH_16BIT), 256, 256, _("a fantasy sound chip created by LTVA. it is a big rework of SID chip with probably too much features added on top."), {_("Channel 1"), _("Channel 2"), _("Channel 3"), _("Channel 4"), _("Channel 5"), _("Channel 6"), _("Sample")}, {"CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "PCM"}, diff --git a/src/gui/gui.h b/src/gui/gui.h index 011b0dfee..aa4fea567 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -2762,6 +2762,7 @@ class FurnaceGUI { void insTabFMModernHeader(DivInstrument* ins); void insTabFM(DivInstrument* ins); + void insTabWavetable(DivInstrument* ins); void insTabSample(DivInstrument* ins); void drawOrderButtons(); diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 9c4f11978..5e640dcd8 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -244,6 +244,13 @@ const char* sid2ControlBits[4]={ NULL }; +const char* sid3ControlBits[4]={ + _N("phase"), + _N("sync"), + _N("ring"), + NULL +}; + const char* sid3WaveMixModes[6]={ _N("8580 SID"), _N("Bitwise AND"), @@ -253,7 +260,7 @@ const char* sid3WaveMixModes[6]={ NULL }; -const char* sid3Waveforms[] = { +const char* sid3SpecialWaveforms[] = { _N("Sine"), _N("Rect. Sine"), _N("Abs. Sine"), @@ -434,6 +441,15 @@ const char* ayShapeBits[4]={ NULL }; +const char* sid3ShapeBits[6]={ + _N("triangle"), + _N("saw"), + _N("pulse"), + _N("noise"), + _N("special wave"), + NULL +}; + const char* ayEnvBits[4]={ _N("hold"), _N("alternate"), @@ -752,6 +768,38 @@ String macroLFOWaves(int id, float val, void* u) { return "???"; } +String macroSID3SpecialWaves(int id, float val, void* u) +{ + if((int)val >= SID3_NUM_SPECIAL_WAVES) return "???"; + + return sid3SpecialWaveforms[(int)val % SID3_NUM_SPECIAL_WAVES]; +} + +String macroSID3SourceChan(int id, float val, void* u) +{ + if((int)val > SID3_NUM_CHANNELS) return "???"; + + if((int)val == SID3_NUM_CHANNELS) + { + return _("Self"); + } + else if((int)val == SID3_NUM_CHANNELS - 1) + { + return _("PCM/Wave channel"); + } + else + { + return fmt::sprintf("Channel %d", (int)val + 1); + } +} + +String macroSID3WaveMixMode(int id, float val, void* u) +{ + if((int)val > 4) return "???"; + + return sid3WaveMixModes[(int)val]; +} + void addAALine(ImDrawList* dl, const ImVec2& p1, const ImVec2& p2, const ImU32 color, float thickness=1.0f) { ImVec2 pt[2]; pt[0]=p1; @@ -2921,6 +2969,273 @@ void FurnaceGUI::alterSampleMap(int column, int val) { ImGui::EndDragDropTarget(); \ } +void FurnaceGUI::insTabWavetable(DivInstrument* ins) +{ + if (ImGui::BeginTabItem(_("Wavetable"))) { + switch (ins->type) { + case DIV_INS_GB: + case DIV_INS_NAMCO: + case DIV_INS_SM8521: + case DIV_INS_SWAN: + wavePreviewLen=32; + wavePreviewHeight=15; + break; + case DIV_INS_PCE: + wavePreviewLen=32; + wavePreviewHeight=31; + break; + case DIV_INS_VBOY: + wavePreviewLen=32; + wavePreviewHeight=63; + break; + case DIV_INS_SCC: + wavePreviewLen=32; + wavePreviewHeight=255; + break; + case DIV_INS_FDS: + wavePreviewLen=64; + wavePreviewHeight=63; + break; + case DIV_INS_N163: + wavePreviewLen=ins->n163.waveLen; + wavePreviewHeight=15; + break; + case DIV_INS_X1_010: + wavePreviewLen=128; + wavePreviewHeight=255; + break; + case DIV_INS_AMIGA: + case DIV_INS_GBA_DMA: + wavePreviewLen=ins->amiga.waveLen+1; + wavePreviewHeight=255; + break; + case DIV_INS_SNES: + wavePreviewLen=ins->amiga.waveLen+1; + wavePreviewHeight=15; + break; + case DIV_INS_GBA_MINMOD: + wavePreviewLen=ins->amiga.waveLen+1; + wavePreviewHeight=255; + break; + case DIV_INS_SID3: + wavePreviewLen=256; + wavePreviewHeight=255; + break; + default: + wavePreviewLen=32; + wavePreviewHeight=31; + break; + } + if (ImGui::Checkbox(_("Enable synthesizer"),&ins->ws.enabled)) { + wavePreviewInit=true; + } + if (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; + wavePreviewInit=true; + } + } else { + if ((ins->ws.effect&0x7f)>=DIV_WS_SINGLE_MAX) { + ins->ws.effect=0; + wavePreviewInit=true; + } + } + 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; + wavePreviewInit=true; + } + } + ImGui::Unindent(); + ImGui::Text(_("Dual-waveform")); + ImGui::Indent(); + for (int i=129; iws.effect=i; + wavePreviewInit=true; + } + } + ImGui::Unindent(); + ImGui::EndCombo(); + } + const bool isSingleWaveFX=(ins->ws.effect>=128); + if (ImGui::BeginTable("WSPreview",isSingleWaveFX?3:2)) { + DivWavetable* wave1=e->getWave(ins->ws.wave1); + DivWavetable* wave2=e->getWave(ins->ws.wave2); + if (wavePreviewInit) { + wavePreview.init(ins,wavePreviewLen,wavePreviewHeight,true); + wavePreviewInit=false; + } + float wavePreview1[257]; + float wavePreview2[257]; + float wavePreview3[257]; + for (int i=0; ilen; i++) { + if (wave1->data[i]>wave1->max) { + wavePreview1[i]=wave1->max; + } else { + wavePreview1[i]=wave1->data[i]; + } + } + if (wave1->len>0) { + wavePreview1[wave1->len]=wave1->data[wave1->len-1]; + } + for (int i=0; ilen; i++) { + if (wave2->data[i]>wave2->max) { + wavePreview2[i]=wave2->max; + } else { + wavePreview2[i]=wave2->data[i]; + } + } + if (wave2->len>0) { + wavePreview2[wave2->len]=wave2->data[wave2->len-1]; + } + if (ins->ws.enabled && (!wavePreviewPaused || wavePreviewInit)) { + wavePreview.tick(true); + WAKE_UP; + } + for (int i=0; i0) { + wavePreview3[wavePreviewLen]=wavePreview3[wavePreviewLen-1]; + } + + float ySize=(isSingleWaveFX?96.0f:128.0f)*dpiScale; + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImVec2 size1=ImVec2(ImGui::GetContentRegionAvail().x,ySize); + PlotNoLerp("##WaveformP1",wavePreview1,wave1->len+1,0,"Wave 1",0,wave1->max,size1); + if (isSingleWaveFX) { + ImGui::TableNextColumn(); + ImVec2 size2=ImVec2(ImGui::GetContentRegionAvail().x,ySize); + PlotNoLerp("##WaveformP2",wavePreview2,wave2->len+1,0,"Wave 2",0,wave2->max,size2); + } + ImGui::TableNextColumn(); + ImVec2 size3=ImVec2(ImGui::GetContentRegionAvail().x,ySize); + PlotNoLerp("##WaveformP3",wavePreview3,wavePreviewLen+1,0,"Result",0,wavePreviewHeight,size3); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + if (ins->std.waveMacro.len>0) { + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_WARNING]); + ImGui::AlignTextToFramePadding(); + ImGui::Text(_("Wave 1")); + ImGui::SameLine(); + ImGui::Text(ICON_FA_EXCLAMATION_TRIANGLE); + ImGui::PopStyleColor(); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip(_("waveform macro is controlling wave 1!\nthis value will be ineffective.")); + } + } else { + ImGui::AlignTextToFramePadding(); + 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; + wavePreviewInit=true; + } + if (ins->std.waveMacro.len>0) { + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip(_("waveform macro is controlling wave 1!\nthis value will be ineffective.")); + } + } + if (isSingleWaveFX) { + ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); + 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; + wavePreviewInit=true; + } + } + ImGui::TableNextColumn(); + if (ImGui::Button(wavePreviewPaused?(ICON_FA_PLAY "##WSPause"):(ICON_FA_PAUSE "##WSPause"))) { + wavePreviewPaused=!wavePreviewPaused; + } + if (ImGui::IsItemHovered()) { + if (wavePreviewPaused) { + ImGui::SetTooltip(_("Resume preview")); + } else { + ImGui::SetTooltip(_("Pause preview")); + } + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_REPEAT "##WSRestart")) { + wavePreviewInit=true; + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip(_("Restart preview")); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UPLOAD "##WSCopy")) { + curWave=e->addWave(); + if (curWave==-1) { + showError(_("too many wavetables!")); + } else { + wantScrollListWave=true; + MARK_MODIFIED; + RESET_WAVE_MACRO_ZOOM; + nextWindow=GUI_WINDOW_WAVE_EDIT; + + DivWavetable* copyWave=e->song.wave[curWave]; + copyWave->len=wavePreviewLen; + copyWave->max=wavePreviewHeight; + memcpy(copyWave->data,wavePreview.output,256*sizeof(int)); + } + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip(_("Copy to new wavetable")); + } + ImGui::SameLine(); + ImGui::Text("(%d×%d)",wavePreviewLen,wavePreviewHeight+1); + ImGui::EndTable(); + } + + if (ImGui::InputScalar(_("Update Rate"),ImGuiDataType_U8,&ins->ws.rateDivider,&_ONE,&_EIGHT)) { + wavePreviewInit=true; + } + int speed=ins->ws.speed+1; + if (ImGui::InputInt(_("Speed"),&speed,1,8)) { + if (speed<1) speed=1; + if (speed>256) speed=256; + ins->ws.speed=speed-1; + wavePreviewInit=true; + } + + if (ImGui::InputScalar(_("Amount"),ImGuiDataType_U8,&ins->ws.param1,&_ONE,&_EIGHT)) { + wavePreviewInit=true; + } + + if (ins->ws.effect==DIV_WS_PHASE_MOD) { + if (ImGui::InputScalar(_("Power"),ImGuiDataType_U8,&ins->ws.param2,&_ONE,&_EIGHT)) { + wavePreviewInit=true; + } + } + + if (ImGui::Checkbox(_("Global"),&ins->ws.global)) { + wavePreviewInit=true; + } + } else { + ImGui::TextWrapped(_("wavetable synthesizer disabled.\nuse the Waveform macro to set the wave for this instrument.")); + } + + ImGui::EndTabItem(); + } +} + void FurnaceGUI::insTabSample(DivInstrument* ins) { const char* sampleTabName=_("Sample"); if (ins->type==DIV_INS_NES) sampleTabName=_("DPCM"); @@ -5566,7 +5881,7 @@ void FurnaceGUI::drawInsSID3(DivInstrument* ins) } popToggleColors(); - P(CWSliderScalar(_("Special wave"),ImGuiDataType_U8,&ins->sid3.special_wave,&_ZERO,&_SID3_SPECIAL_WAVES,sid3Waveforms[ins->sid3.special_wave % SID3_NUM_SPECIAL_WAVES])); rightClickable + P(CWSliderScalar(_("Special wave"),ImGuiDataType_U8,&ins->sid3.special_wave,&_ZERO,&_SID3_SPECIAL_WAVES,sid3SpecialWaveforms[ins->sid3.special_wave % SID3_NUM_SPECIAL_WAVES])); rightClickable ImGui::TableNextColumn(); @@ -5623,7 +5938,10 @@ void FurnaceGUI::drawInsSID3(DivInstrument* ins) ImGui::EndTable(); } - P(CWSliderScalar(_("Wave Mix Mode"),ImGuiDataType_U8,&ins->sid2.mixMode,&_ZERO,&_FOUR,sid3WaveMixModes[ins->sid2.mixMode % 5])); + char buffer[40]; + + 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 bool ringMod=ins->c64.ringMod; @@ -5631,23 +5949,9 @@ void FurnaceGUI::drawInsSID3(DivInstrument* ins) ins->c64.ringMod=ringMod; } - char buffer[40]; - - if(ins->sid3.ring_mod_source == SID3_NUM_CHANNELS) - { - snprintf(buffer, 40, _("Self")); - } - else if(ins->sid3.ring_mod_source == SID3_NUM_CHANNELS - 1) - { - snprintf(buffer, 40, _("PCM channel")); - } - else - { - snprintf(buffer, 40, "%d", ins->sid3.ring_mod_source + 1); - } - ImGui::SameLine(); + strncpy(buffer,macroSID3SourceChan(0,(float)ins->sid3.ring_mod_source,NULL).c_str(),40); P(CWSliderScalar(_("Source channel##rmsrc"),ImGuiDataType_U8,&ins->sid3.ring_mod_source,&_ZERO,&_SID3_NUM_CHANNELS,buffer)); bool oscSync=ins->c64.oscSync; @@ -5657,8 +5961,8 @@ void FurnaceGUI::drawInsSID3(DivInstrument* ins) ImGui::SameLine(); - snprintf(buffer, 40, "%d", ins->sid3.sync_source + 1); - P(CWSliderScalar(_("Source channel##hssrc"),ImGuiDataType_U8,&ins->sid3.sync_source,&_ZERO,&_SID3_NUM_CHANNELS_MINUS_ONE)); + strncpy(buffer,macroSID3SourceChan(0,(float)ins->sid3.sync_source,NULL).c_str(),40); + P(CWSliderScalar(_("Source channel##hssrc"),ImGuiDataType_U8,&ins->sid3.sync_source,&_ZERO,&_SID3_NUM_CHANNELS_MINUS_ONE,buffer)); bool phaseMod=ins->sid3.phase_mod; if (ImGui::Checkbox(_("Phase modulation"),&phaseMod)) { PARAMETER @@ -5667,8 +5971,18 @@ void FurnaceGUI::drawInsSID3(DivInstrument* ins) ImGui::SameLine(); - snprintf(buffer, 40, "%d", ins->sid3.phase_mod_source + 1); - P(CWSliderScalar(_("Source channel##pmsrc"),ImGuiDataType_U8,&ins->sid3.phase_mod_source,&_ZERO,&_SID3_NUM_CHANNELS_MINUS_ONE)); + strncpy(buffer,macroSID3SourceChan(0,(float)ins->sid3.phase_mod_source,NULL).c_str(),40); + P(CWSliderScalar(_("Source channel##pmsrc"),ImGuiDataType_U8,&ins->sid3.phase_mod_source,&_ZERO,&_SID3_NUM_CHANNELS_MINUS_ONE,buffer)); + + ImGui::Separator(); + bool sepNoisePitch=ins->sid3.separateNoisePitch; + if (ImGui::Checkbox(_("Separate noise pitch"),&sepNoisePitch)) { PARAMETER + ins->sid3.separateNoisePitch=sepNoisePitch; + } + if (ImGui::IsItemHovered()) + { + ImGui::SetTooltip(_("Make noise pitch independent from other waves' pitch.\nNoise pitch will be controllable via macros.")); + } for(int i = 0; i < SID3_NUM_FILTERS; i++) { @@ -5838,17 +6152,53 @@ void FurnaceGUI::drawInsSID3(DivInstrument* ins) ImGui::EndTabItem(); } + insTabWavetable(ins); + insTabSample(ins); + std::vector macroList; if (ImGui::BeginTabItem(_("Macros"))) { macroList.push_back(FurnaceGUIMacroDesc(_("Volume"),&ins->std.volMacro,0,255,160,uiColors[GUI_COLOR_MACRO_VOLUME])); + macroList.push_back(FurnaceGUIMacroDesc(_("Arpeggio"),&ins->std.arpMacro,-120,120,160,uiColors[GUI_COLOR_MACRO_PITCH],true,NULL,macroHoverNote,false,NULL,true,ins->std.arpMacro.val)); macroList.push_back(FurnaceGUIMacroDesc(_("Pitch"),&ins->std.pitchMacro,-2048,2047,160,uiColors[GUI_COLOR_MACRO_PITCH],true,macroRelativeMode)); + macroList.push_back(FurnaceGUIMacroDesc(_("Duty"),&ins->std.dutyMacro,ins->c64.dutyIsAbs?0:-65535,65535,160,uiColors[GUI_COLOR_MACRO_OTHER])); + + macroList.push_back(FurnaceGUIMacroDesc(_("Waveform"),&ins->std.waveMacro,0,5,16 * 5,uiColors[GUI_COLOR_MACRO_WAVE],false,NULL,NULL,true,sid3ShapeBits)); + macroList.push_back(FurnaceGUIMacroDesc(_("Special Wave"),&ins->std.algMacro,0,SID3_NUM_SPECIAL_WAVES - 1,160,uiColors[GUI_COLOR_MACRO_WAVE],false,NULL,macroSID3SpecialWaves)); + + if(ins->sid3.separateNoisePitch) + { + macroList.push_back(FurnaceGUIMacroDesc(_("Noise Arpeggio"),&ins->std.opMacros[3].amMacro,-120,120,160,uiColors[GUI_COLOR_MACRO_PITCH],true,NULL,macroHoverNote,false,NULL,true,ins->std.opMacros[3].amMacro.val,true)); + macroList.push_back(FurnaceGUIMacroDesc(_("Noise Pitch"),&ins->std.opMacros[0].arMacro,-2048,2047,160,uiColors[GUI_COLOR_MACRO_PITCH],true,macroRelativeMode,NULL,false,NULL,false,NULL,false,true)); + } + 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(_("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)); + + 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(_("Ring Mod Source"),&ins->std.fmsMacro,0,SID3_NUM_CHANNELS,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)); + macroList.push_back(FurnaceGUIMacroDesc(_("Phase Reset"),&ins->std.phaseResetMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); + macroList.push_back(FurnaceGUIMacroDesc(_("Noise Phase Reset"),&ins->std.opMacros[1].amMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); + macroList.push_back(FurnaceGUIMacroDesc(_("Envelope Reset"),&ins->std.opMacros[2].amMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); + + macroList.push_back(FurnaceGUIMacroDesc(_("Attack"),&ins->std.ex2Macro,0,255,160,uiColors[GUI_COLOR_MACRO_ENVELOPE])); + macroList.push_back(FurnaceGUIMacroDesc(_("Decay"),&ins->std.ex3Macro,0,255,160,uiColors[GUI_COLOR_MACRO_ENVELOPE])); + macroList.push_back(FurnaceGUIMacroDesc(_("Sustain"),&ins->std.ex4Macro,0,255,160,uiColors[GUI_COLOR_MACRO_ENVELOPE])); + macroList.push_back(FurnaceGUIMacroDesc(_("Sustain Rate"),&ins->std.ex5Macro,0,255,160,uiColors[GUI_COLOR_MACRO_ENVELOPE])); + macroList.push_back(FurnaceGUIMacroDesc(_("Release"),&ins->std.ex6Macro,0,255,160,uiColors[GUI_COLOR_MACRO_ENVELOPE])); + + macroList.push_back(FurnaceGUIMacroDesc(_("Noise LFSR bits"),&ins->std.ex7Macro,0,29,16 * 30,uiColors[GUI_COLOR_MACRO_NOISE],false,NULL,NULL,true)); + macroList.push_back(FurnaceGUIMacroDesc(_("1-Bit Noise/Sample Mode"),&ins->std.opMacros[1].arMacro,0,1,32,uiColors[GUI_COLOR_MACRO_NOISE],false,NULL,NULL,true)); + macroList.push_back(FurnaceGUIMacroDesc(_("Wave Mix"),&ins->std.ex8Macro,0,4,64,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,macroSID3WaveMixMode)); drawMacros(macroList,macroEditStateMacros); @@ -7327,266 +7677,9 @@ void FurnaceGUI::drawInsEdit() { ins->type==DIV_INS_SNES || ins->type==DIV_INS_NAMCO || ins->type==DIV_INS_SM8521 || - (ins->type==DIV_INS_GBA_MINMOD && ins->amiga.useWave)) { - if (ImGui::BeginTabItem(_("Wavetable"))) { - switch (ins->type) { - case DIV_INS_GB: - case DIV_INS_NAMCO: - case DIV_INS_SM8521: - case DIV_INS_SWAN: - wavePreviewLen=32; - wavePreviewHeight=15; - break; - case DIV_INS_PCE: - wavePreviewLen=32; - wavePreviewHeight=31; - break; - case DIV_INS_VBOY: - wavePreviewLen=32; - wavePreviewHeight=63; - break; - case DIV_INS_SCC: - wavePreviewLen=32; - wavePreviewHeight=255; - break; - case DIV_INS_FDS: - wavePreviewLen=64; - wavePreviewHeight=63; - break; - case DIV_INS_N163: - wavePreviewLen=ins->n163.waveLen; - wavePreviewHeight=15; - break; - case DIV_INS_X1_010: - wavePreviewLen=128; - wavePreviewHeight=255; - break; - case DIV_INS_AMIGA: - case DIV_INS_GBA_DMA: - wavePreviewLen=ins->amiga.waveLen+1; - wavePreviewHeight=255; - break; - case DIV_INS_SNES: - wavePreviewLen=ins->amiga.waveLen+1; - wavePreviewHeight=15; - break; - case DIV_INS_GBA_MINMOD: - wavePreviewLen=ins->amiga.waveLen+1; - wavePreviewHeight=255; - break; - default: - wavePreviewLen=32; - wavePreviewHeight=31; - break; - } - if (ImGui::Checkbox(_("Enable synthesizer"),&ins->ws.enabled)) { - wavePreviewInit=true; - } - if (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; - wavePreviewInit=true; - } - } else { - if ((ins->ws.effect&0x7f)>=DIV_WS_SINGLE_MAX) { - ins->ws.effect=0; - wavePreviewInit=true; - } - } - 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; - wavePreviewInit=true; - } - } - ImGui::Unindent(); - ImGui::Text(_("Dual-waveform")); - ImGui::Indent(); - for (int i=129; iws.effect=i; - wavePreviewInit=true; - } - } - ImGui::Unindent(); - ImGui::EndCombo(); - } - const bool isSingleWaveFX=(ins->ws.effect>=128); - if (ImGui::BeginTable("WSPreview",isSingleWaveFX?3:2)) { - DivWavetable* wave1=e->getWave(ins->ws.wave1); - DivWavetable* wave2=e->getWave(ins->ws.wave2); - if (wavePreviewInit) { - wavePreview.init(ins,wavePreviewLen,wavePreviewHeight,true); - wavePreviewInit=false; - } - float wavePreview1[257]; - float wavePreview2[257]; - float wavePreview3[257]; - for (int i=0; ilen; i++) { - if (wave1->data[i]>wave1->max) { - wavePreview1[i]=wave1->max; - } else { - wavePreview1[i]=wave1->data[i]; - } - } - if (wave1->len>0) { - wavePreview1[wave1->len]=wave1->data[wave1->len-1]; - } - for (int i=0; ilen; i++) { - if (wave2->data[i]>wave2->max) { - wavePreview2[i]=wave2->max; - } else { - wavePreview2[i]=wave2->data[i]; - } - } - if (wave2->len>0) { - wavePreview2[wave2->len]=wave2->data[wave2->len-1]; - } - if (ins->ws.enabled && (!wavePreviewPaused || wavePreviewInit)) { - wavePreview.tick(true); - WAKE_UP; - } - for (int i=0; i0) { - wavePreview3[wavePreviewLen]=wavePreview3[wavePreviewLen-1]; - } - - float ySize=(isSingleWaveFX?96.0f:128.0f)*dpiScale; - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImVec2 size1=ImVec2(ImGui::GetContentRegionAvail().x,ySize); - PlotNoLerp("##WaveformP1",wavePreview1,wave1->len+1,0,"Wave 1",0,wave1->max,size1); - if (isSingleWaveFX) { - ImGui::TableNextColumn(); - ImVec2 size2=ImVec2(ImGui::GetContentRegionAvail().x,ySize); - PlotNoLerp("##WaveformP2",wavePreview2,wave2->len+1,0,"Wave 2",0,wave2->max,size2); - } - ImGui::TableNextColumn(); - ImVec2 size3=ImVec2(ImGui::GetContentRegionAvail().x,ySize); - PlotNoLerp("##WaveformP3",wavePreview3,wavePreviewLen+1,0,"Result",0,wavePreviewHeight,size3); - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - if (ins->std.waveMacro.len>0) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_WARNING]); - ImGui::AlignTextToFramePadding(); - ImGui::Text(_("Wave 1")); - ImGui::SameLine(); - ImGui::Text(ICON_FA_EXCLAMATION_TRIANGLE); - ImGui::PopStyleColor(); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip(_("waveform macro is controlling wave 1!\nthis value will be ineffective.")); - } - } else { - ImGui::AlignTextToFramePadding(); - 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; - wavePreviewInit=true; - } - if (ins->std.waveMacro.len>0) { - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip(_("waveform macro is controlling wave 1!\nthis value will be ineffective.")); - } - } - if (isSingleWaveFX) { - ImGui::TableNextColumn(); - ImGui::AlignTextToFramePadding(); - 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; - wavePreviewInit=true; - } - } - ImGui::TableNextColumn(); - if (ImGui::Button(wavePreviewPaused?(ICON_FA_PLAY "##WSPause"):(ICON_FA_PAUSE "##WSPause"))) { - wavePreviewPaused=!wavePreviewPaused; - } - if (ImGui::IsItemHovered()) { - if (wavePreviewPaused) { - ImGui::SetTooltip(_("Resume preview")); - } else { - ImGui::SetTooltip(_("Pause preview")); - } - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_REPEAT "##WSRestart")) { - wavePreviewInit=true; - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip(_("Restart preview")); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_UPLOAD "##WSCopy")) { - curWave=e->addWave(); - if (curWave==-1) { - showError(_("too many wavetables!")); - } else { - wantScrollListWave=true; - MARK_MODIFIED; - RESET_WAVE_MACRO_ZOOM; - nextWindow=GUI_WINDOW_WAVE_EDIT; - - DivWavetable* copyWave=e->song.wave[curWave]; - copyWave->len=wavePreviewLen; - copyWave->max=wavePreviewHeight; - memcpy(copyWave->data,wavePreview.output,256*sizeof(int)); - } - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip(_("Copy to new wavetable")); - } - ImGui::SameLine(); - ImGui::Text("(%d×%d)",wavePreviewLen,wavePreviewHeight+1); - ImGui::EndTable(); - } - - if (ImGui::InputScalar(_("Update Rate"),ImGuiDataType_U8,&ins->ws.rateDivider,&_ONE,&_EIGHT)) { - wavePreviewInit=true; - } - int speed=ins->ws.speed+1; - if (ImGui::InputInt(_("Speed"),&speed,1,8)) { - if (speed<1) speed=1; - if (speed>256) speed=256; - ins->ws.speed=speed-1; - wavePreviewInit=true; - } - - if (ImGui::InputScalar(_("Amount"),ImGuiDataType_U8,&ins->ws.param1,&_ONE,&_EIGHT)) { - wavePreviewInit=true; - } - - if (ins->ws.effect==DIV_WS_PHASE_MOD) { - if (ImGui::InputScalar(_("Power"),ImGuiDataType_U8,&ins->ws.param2,&_ONE,&_EIGHT)) { - wavePreviewInit=true; - } - } - - if (ImGui::Checkbox(_("Global"),&ins->ws.global)) { - wavePreviewInit=true; - } - } else { - ImGui::TextWrapped(_("wavetable synthesizer disabled.\nuse the Waveform macro to set the wave for this instrument.")); - } - - ImGui::EndTabItem(); - } + (ins->type==DIV_INS_GBA_MINMOD && ins->amiga.useWave)) + { + insTabWavetable(ins); } if (ins->type