diff --git a/hi_core/hi_modules/synthesisers/editors/WaveSynthBody.cpp b/hi_core/hi_modules/synthesisers/editors/WaveSynthBody.cpp index 54a5af778f..75d8eb6998 100644 --- a/hi_core/hi_modules/synthesisers/editors/WaveSynthBody.cpp +++ b/hi_core/hi_modules/synthesisers/editors/WaveSynthBody.cpp @@ -182,6 +182,17 @@ WaveSynthBody::WaveSynthBody (ProcessorEditor *p) pulseSlider2->setTextBoxStyle (Slider::TextBoxRight, false, 40, 20); pulseSlider2->addListener (this); + addAndMakeVisible (semiToneSlider1 = new HiSlider ("SemiTones 1")); + semiToneSlider1->setRange (-12, 12, 1); + semiToneSlider1->setSliderStyle (Slider::RotaryHorizontalVerticalDrag); + semiToneSlider1->setTextBoxStyle (Slider::TextBoxRight, false, 40, 20); + semiToneSlider1->addListener (this); + + addAndMakeVisible (semiToneSlider2 = new HiSlider ("SemiTones 2")); + semiToneSlider2->setRange (-12, 12, 1); + semiToneSlider2->setSliderStyle (Slider::RotaryHorizontalVerticalDrag); + semiToneSlider2->setTextBoxStyle (Slider::TextBoxRight, false, 40, 20); + semiToneSlider2->addListener (this); //[UserPreSize] @@ -219,6 +230,14 @@ WaveSynthBody::WaveSynthBody (ProcessorEditor *p) pulseSlider2->setup(getProcessor(), WaveSynth::SpecialParameters::PulseWidth2, "Pulse Width 2"); pulseSlider2->setMode(HiSlider::Mode::NormalizedPercentage); + semiToneSlider1->setup(getProcessor(), WaveSynth::SpecialParameters::SemiTones1, "SemiTones 1"); + semiToneSlider1->setMode(HiSlider::Discrete, -12.0, 12.0); + semiToneSlider1->setRange(-12.0, 12.0, 1.0); + + semiToneSlider2->setup(getProcessor(), WaveSynth::SpecialParameters::SemiTones2, "SemiTones 2"); + semiToneSlider2->setMode(HiSlider::Discrete, -12.0, 12.0); + semiToneSlider2->setRange(-12.0, 12.0, 1.0); + voiceAmountEditor->setFont(GLOBAL_FONT()); voiceAmountLabel->setFont(GLOBAL_FONT()); fadeTimeEditor->setFont(GLOBAL_FONT()); @@ -258,6 +277,8 @@ WaveSynthBody::~WaveSynthBody() enableSecondButton = nullptr; pulseSlider1 = nullptr; pulseSlider2 = nullptr; + semiToneSlider1 = nullptr; + semiToneSlider2 = nullptr; //[Destructor]. You can add your own custom destruction code here.. @@ -297,26 +318,28 @@ void WaveSynthBody::resized() //[UserPreResize] Add your own custom resize code here.. //[/UserPreResize] - octaveSlider->setBounds (158, 15, 128, 48); - waveFormSelector->setBounds (26, 65, 128, 24); - waveformDisplay->setBounds (26, 15, 128, 48); + octaveSlider->setBounds (160, 17, 128, 48); + waveFormSelector->setBounds (25, 42, 128, 24); + waveformDisplay->setBounds (25, 15, 128, 24); fadeTimeLabel->setBounds ((getWidth() / 2) + 7, 61, 79, 24); voiceAmountLabel->setBounds ((getWidth() / 2) + -69, 62, 79, 24); voiceAmountEditor->setBounds ((getWidth() / 2) + -30 - (68 / 2), 80, 68, 16); fadeTimeEditor->setBounds ((getWidth() / 2) + 12, 80, 51, 16); - octaveSlider2->setBounds (getWidth() - 161 - 128, 15, 128, 48); - waveFormSelector2->setBounds (getWidth() - 26 - 128, 65, 128, 24); - waveformDisplay2->setBounds (getWidth() - 26 - 128, 15, 128, 48); + octaveSlider2->setBounds (getWidth() - 161 - 128, 17, 128, 48); + waveFormSelector2->setBounds (getWidth() - 26 - 128, 42, 128, 24); + waveformDisplay2->setBounds (getWidth() - 26 - 128, 15, 128, 24); mixSlider->setBounds ((getWidth() / 2) - (128 / 2), 13, 128, 48); - panSlider->setBounds (160, 73, 128, 48); - panSlider2->setBounds (getWidth() - 161 - 128, 73, 128, 48); - detuneSlider2->setBounds (getWidth() - 26 - 128, 94, 128, 48); - detuneSlider->setBounds (25, 96, 128, 48); + panSlider->setBounds (25, 134, 128, 48); + panSlider2->setBounds (getWidth() - 26 - 128, 134, 128, 48); + detuneSlider2->setBounds (getWidth() - 161 - 128, 134, 128, 48); + detuneSlider->setBounds (160, 134, 128, 48); enableSecondButton->setBounds ((getWidth() / 2) + -64, 136, 128, 28); enableSyncButton->setBounds((getWidth() / 2) + -64, 166, 128, 28); - pulseSlider1->setBounds (160, 132, 128, 48); - pulseSlider2->setBounds (getWidth() - 161 - 128, 132, 128, 48); + pulseSlider1->setBounds (25, 77, 128, 48); + pulseSlider2->setBounds (getWidth() - 26 - 128, 77, 128, 48); + semiToneSlider1->setBounds (160, 77, 128, 48); + semiToneSlider2->setBounds (getWidth() - 161 - 128, 77, 128, 48); //[UserResized] Add your own custom resize handling here.. //[/UserResized] } @@ -371,6 +394,16 @@ void WaveSynthBody::sliderValueChanged (Slider* sliderThatWasMoved) //[UserSliderCode_pulseSlider2] -- add your slider handling code here.. //[/UserSliderCode_pulseSlider2] } + else if (sliderThatWasMoved == semiToneSlider1) + { + //[UserSliderCode_semiToneSlider1] -- add your slider handling code here.. + //[/UserSliderCode_semiToneSlider1] + } + else if (sliderThatWasMoved == semiToneSlider2) + { + //[UserSliderCode_semiToneSlider2] -- add your slider handling code here.. + //[/UserSliderCode_semiToneSlider2] + } //[UsersliderValueChanged_Post] //[/UsersliderValueChanged_Post] @@ -384,13 +417,13 @@ void WaveSynthBody::comboBoxChanged (ComboBox* comboBoxThatHasChanged) if (comboBoxThatHasChanged == waveFormSelector) { //[UserComboBoxCode_waveFormSelector] -- add your combo box handling code here.. - + //[/UserComboBoxCode_waveFormSelector] } else if (comboBoxThatHasChanged == waveFormSelector2) { //[UserComboBoxCode_waveFormSelector2] -- add your combo box handling code here.. - + //[/UserComboBoxCode_waveFormSelector2] } @@ -566,6 +599,16 @@ BEGIN_JUCER_METADATA int="0.010000000000000000208" style="RotaryHorizontalVerticalDrag" textBoxPos="TextBoxRight" textBoxEditable="1" textBoxWidth="40" textBoxHeight="20" skewFactor="1" needsCallback="1"/> + + END_JUCER_METADATA diff --git a/hi_core/hi_modules/synthesisers/editors/WaveSynthBody.h b/hi_core/hi_modules/synthesisers/editors/WaveSynthBody.h index 269d3ea5a0..a30f2a19b8 100644 --- a/hi_core/hi_modules/synthesisers/editors/WaveSynthBody.h +++ b/hi_core/hi_modules/synthesisers/editors/WaveSynthBody.h @@ -60,6 +60,9 @@ class WaveSynthBody : public ProcessorEditorBody, octaveSlider->updateValue(); octaveSlider2->updateValue(); + semiToneSlider1->updateValue(); + semiToneSlider2->updateValue(); + detuneSlider->updateValue(); detuneSlider2->updateValue(); @@ -87,7 +90,7 @@ class WaveSynthBody : public ProcessorEditorBody, octaveSlider2->setEnabled(false); detuneSlider2->setEnabled(false); panSlider2->setEnabled(false); - + } }; @@ -105,8 +108,8 @@ class WaveSynthBody : public ProcessorEditorBody, { return h; } - - + + //[/UserMethods] @@ -144,6 +147,8 @@ class WaveSynthBody : public ProcessorEditorBody, ScopedPointer enableSyncButton; ScopedPointer pulseSlider1; ScopedPointer pulseSlider2; + ScopedPointer semiToneSlider1; + ScopedPointer semiToneSlider2; //============================================================================== diff --git a/hi_core/hi_modules/synthesisers/synths/WaveSynth.cpp b/hi_core/hi_modules/synthesisers/synths/WaveSynth.cpp index 5e41175b8b..2d889976b9 100644 --- a/hi_core/hi_modules/synthesisers/synths/WaveSynth.cpp +++ b/hi_core/hi_modules/synthesisers/synths/WaveSynth.cpp @@ -41,6 +41,8 @@ WaveSynth::WaveSynth(MainController *mc, const String &id, int numVoices) : ModulatorSynth(mc, id, numVoices), octaveTranspose1((int)getDefaultValue(OctaveTranspose1)), octaveTranspose2((int)getDefaultValue(OctaveTranspose2)), + semiTones1((int)getDefaultValue(SemiTones1)), + semiTones2((int)getDefaultValue(SemiTones2)), detune1(getDefaultValue(Detune1)), detune2(getDefaultValue(Detune2)), pan1(getDefaultValue(Pan1)), @@ -68,10 +70,12 @@ WaveSynth::WaveSynth(MainController *mc, const String &id, int numVoices) : scaleFunction = [](float input) { return input * 2.0f - 1.0f; }; parameterNames.add("OctaveTranspose1"); + parameterNames.add("SemiTones1"); parameterNames.add("WaveForm1"); parameterNames.add("Detune1"); parameterNames.add("Pan1"); parameterNames.add("OctaveTranspose2"); + parameterNames.add("SemiTones2"); parameterNames.add("WaveForm2"); parameterNames.add("Detune2"); parameterNames.add("Pan2"); @@ -91,7 +95,7 @@ WaveSynth::WaveSynth(MainController *mc, const String &id, int numVoices) : for (int i = 0; i < numVoices; i++) addVoice(new WaveSynthVoice(this)); - + addSound(new WaveSound()); } @@ -100,7 +104,9 @@ void WaveSynth::restoreFromValueTree(const ValueTree &v) ModulatorSynth::restoreFromValueTree(v); loadAttribute(OctaveTranspose1, "OctaveTranspose1"); + loadAttribute(SemiTones1, "SemiTones1"); loadAttribute(OctaveTranspose2, "OctaveTranspose2"); + loadAttribute(SemiTones2, "SemiTones2"); loadAttribute(Detune1, "Detune1"); loadAttribute(Detune2, "Detune2"); loadAttribute(WaveForm1, "WaveForm1"); @@ -119,7 +125,9 @@ ValueTree WaveSynth::exportAsValueTree() const ValueTree v = ModulatorSynth::exportAsValueTree(); saveAttribute(OctaveTranspose1, "OctaveTranspose1"); + saveAttribute(SemiTones1, "SemiTones1"); saveAttribute(OctaveTranspose2, "OctaveTranspose2"); + saveAttribute(SemiTones2, "SemiTones2"); saveAttribute(Detune1, "Detune1"); saveAttribute(Detune2, "Detune2"); saveAttribute(WaveForm1, "WaveForm1"); @@ -174,10 +182,12 @@ float WaveSynth::getDefaultValue(int parameterIndex) const switch (parameterIndex) { case OctaveTranspose1: return 0.0f; + case SemiTones1: return 0.0f; case WaveForm1: return (float)WaveformComponent::WaveformType::Saw; case Detune1: return 0.0f; case Pan1: return 0.0f; case OctaveTranspose2: return 0.0f; + case SemiTones2: return 0.0f; case WaveForm2: return (float)WaveformComponent::WaveformType::Saw; case Detune2: return 0.0f; case Pan2: return 0.0f; @@ -229,10 +239,12 @@ float WaveSynth::getAttribute(int parameterIndex) const switch (parameterIndex) { case OctaveTranspose1: return (float)octaveTranspose1; + case SemiTones1: return (float)semiTones1; case WaveForm1: return (float)waveForm1; case Detune1: return detune1; case Pan1: return pan1; case OctaveTranspose2: return (float)octaveTranspose2; + case SemiTones2: return (float)semiTones2; case WaveForm2: return (float)waveForm2; case Detune2: return detune2; case Pan2: return pan2; @@ -258,9 +270,15 @@ void WaveSynth::setInternalAttribute(int parameterIndex, float newValue) case OctaveTranspose1: octaveTranspose1 = (int)newValue; refreshPitchValues(true); break; + case SemiTones1: semiTones1 = (int)newValue; + refreshPitchValues(true); + break; case OctaveTranspose2: octaveTranspose2 = (int)newValue; refreshPitchValues(false); break; + case SemiTones2: semiTones2 = (int)newValue; + refreshPitchValues(false); + break; case Detune1: detune1 = newValue; refreshPitchValues(true); break; @@ -331,11 +349,11 @@ void WaveSynth::refreshPulseWidth(bool left) double WaveSynth::getPitchValue(bool getLeftValue) { - const double octaveValue = pow(2.0, (double)getLeftValue ? octaveTranspose1 : octaveTranspose2); - + const double octaveValue = pow(2.0, (double)(getLeftValue ? octaveTranspose1 : octaveTranspose2)); + const double semiToneValue = pow(2.0, (double)(getLeftValue ? semiTones1 : semiTones2) / 12.0); const double detuneValue = pow(2.0, (getLeftValue ? detune1 : detune2) / 1200.0); - return octaveValue * detuneValue; + return octaveValue * semiToneValue * detuneValue; } @@ -358,7 +376,7 @@ WaveSynthVoice::WaveSynthVoice(ModulatorSynth *ownerSynth) : #else octaveTransposeFactor2(1.0) #endif - + { setWaveForm(WaveformComponent::Saw, true); setWaveForm(WaveformComponent::Saw, false); @@ -388,7 +406,7 @@ void WaveSynthVoice::startNote(int midiNoteNumber, float /*velocity*/, Synthesis rightGenerator.setFrequency(cyclesPerSecond * octaveTransposeFactor2); leftGenerator.setStartOffset((double)getCurrentHiseEvent().getStartOffset()); - + if(enableSecondOsc) rightGenerator.setStartOffset((double)getCurrentHiseEvent().getStartOffset()); @@ -464,18 +482,18 @@ void WaveSynthVoice::calculateBlock(int startSample, int numSamples) while (--numSamples >= 0) { auto leftDelta = (float)uptimeDelta; - + if (voicePitchValues != nullptr) leftDelta *= *voicePitchValues; leftGenerator.setFreqModulationValue(leftDelta); - + auto rightDelta = (float)uptimeDelta; - + if (voicePitchValues != nullptr) rightDelta *= *voicePitchValues; - + if (secondPitchValues != nullptr) rightDelta *= *secondPitchValues; @@ -511,7 +529,7 @@ void WaveSynthVoice::calculateBlock(int startSample, int numSamples) leftGenerator.setFreqModulationValue(leftDelta); - + voicePitchValues++; *outL = leftGenerator.getAndInc(); @@ -520,9 +538,9 @@ void WaveSynthVoice::calculateBlock(int startSample, int numSamples) } } - + - + #else @@ -559,7 +577,7 @@ void WaveSynthVoice::calculateBlock(int startSample, int numSamples) #endif - + #if HISE_USE_WRONG_VOICE_RENDERING_ORDER getOwnerSynth()->effectChain->renderVoice(voiceIndex, voiceBuffer, startIndex, samplesToCopy); @@ -572,7 +590,7 @@ void WaveSynthVoice::calculateBlock(int startSample, int numSamples) auto leftSamples = voiceBuffer.getWritePointer(0, startIndex); auto rightSamples = voiceBuffer.getWritePointer(1, startIndex); - + auto& tBuffer = wavesynth->getTempBufferForMixCalculation(); @@ -641,7 +659,7 @@ void WaveSynthVoice::setWaveForm(WaveformComponent::WaveformType type, bool left { switch ((int)type) { - case hise::WaveformComponent::Sine: + case hise::WaveformComponent::Sine: left ? leftGenerator.setWaveform(mf::PolyBLEP::SINE) : rightGenerator.setWaveform(mf::PolyBLEP::SINE); break; case hise::WaveformComponent::Triangle: left ? leftGenerator.setWaveform(mf::PolyBLEP::TRIANGLE) : rightGenerator.setWaveform(mf::PolyBLEP::TRIANGLE); break; @@ -663,7 +681,7 @@ void WaveSynthVoice::setWaveForm(WaveformComponent::WaveformType type, bool left break; } - + } diff --git a/hi_core/hi_modules/synthesisers/synths/WaveSynth.h b/hi_core/hi_modules/synthesisers/synths/WaveSynth.h index b36893f2f1..ff680feb02 100644 --- a/hi_core/hi_modules/synthesisers/synths/WaveSynth.h +++ b/hi_core/hi_modules/synthesisers/synths/WaveSynth.h @@ -103,7 +103,7 @@ class WaveSynthVoice: public ModulatorSynthVoice { return noiseGenerator.nextFloat(); } - + static float getPulse(double voiceUptime, double uptimeDelta) { const double pulseWidth = 0.5; @@ -116,7 +116,7 @@ class WaveSynthVoice: public ModulatorSynthVoice const double phase = fmod(voiceUptime, 1.0) * 1024.0; int index = (int)phase; - + float v1 = sinTable[index & 2047]; float v2 = sinTable[(index +1) & 2047]; @@ -128,7 +128,7 @@ class WaveSynthVoice: public ModulatorSynthVoice return currentSample; } - static float getBoxFilteredSaw(double phase, double kernelSize) + static float getBoxFilteredSaw(double phase, double kernelSize) { double a, b; @@ -232,6 +232,8 @@ class WaveSynth: public ModulatorSynth, PulseWidth1, ///< 0 ... **1** | Determines the first pulse width for waveforms that support this (eg. square) PulseWidth2, ///< 0 ... **1** | Determines the second pulse width for waveforms that support this (eg. square) HardSync, ///< **Off** ... On | Syncs the second oscillator to the first + SemiTones1, ///< -12 ... **0** ... 12 | The semitone transpose amount for the first Oscillator. + SemiTones2, ///< -12 ... **0** ... 12 | The semitone transpose amount for the second Oscillator. numWaveSynthParameters }; @@ -256,7 +258,7 @@ class WaveSynth: public ModulatorSynth, const Processor *getChildProcessor(int processorIndex) const override; - + float getDefaultValue(int parameterIndex) const override;; void getWaveformTableValues(int displayIndex, float const** tableValues, int& numValues, float& normalizeValue) override; @@ -327,6 +329,7 @@ class WaveSynth: public ModulatorSynth, AudioSampleBuffer tempBuffer; int octaveTranspose1, octaveTranspose2; + int semiTones1, semiTones2; float mix;