mirror of
https://github.com/yokemura/Magical8bitPlug2.git
synced 2025-05-24 23:00:21 -04:00
Merge pull request #19 from yokemura/feature/monoMode
Feature/mono mode
This commit is contained in:
commit
098b268001
@ -50,6 +50,10 @@
|
|||||||
file="Source/EnvelopeParamsComponent.cpp"/>
|
file="Source/EnvelopeParamsComponent.cpp"/>
|
||||||
<FILE id="zBFywa" name="EnvelopeParamsComponent.h" compile="0" resource="0"
|
<FILE id="zBFywa" name="EnvelopeParamsComponent.h" compile="0" resource="0"
|
||||||
file="Source/EnvelopeParamsComponent.h"/>
|
file="Source/EnvelopeParamsComponent.h"/>
|
||||||
|
<FILE id="SHaDYI" name="MonophonicComponent.cpp" compile="1" resource="0"
|
||||||
|
file="Source/MonophonicComponent.cpp"/>
|
||||||
|
<FILE id="cyIRRW" name="MonophonicComponent.h" compile="0" resource="0"
|
||||||
|
file="Source/MonophonicComponent.h"/>
|
||||||
<FILE id="RF89Wr" name="NoiseParamsComponent.cpp" compile="1" resource="0"
|
<FILE id="RF89Wr" name="NoiseParamsComponent.cpp" compile="1" resource="0"
|
||||||
file="Source/NoiseParamsComponent.cpp"/>
|
file="Source/NoiseParamsComponent.cpp"/>
|
||||||
<FILE id="jPVIvX" name="NoiseParamsComponent.h" compile="0" resource="0"
|
<FILE id="jPVIvX" name="NoiseParamsComponent.h" compile="0" resource="0"
|
||||||
@ -68,6 +72,8 @@
|
|||||||
file="Source/VibratoParamsComponent.h"/>
|
file="Source/VibratoParamsComponent.h"/>
|
||||||
</GROUP>
|
</GROUP>
|
||||||
<GROUP id="{630C27C7-03AA-CFC6-2FC4-4CE2DBE3640A}" name="Source">
|
<GROUP id="{630C27C7-03AA-CFC6-2FC4-4CE2DBE3640A}" name="Source">
|
||||||
|
<FILE id="LHv5E3" name="CustomSynth.cpp" compile="1" resource="0" file="Source/CustomSynth.cpp"/>
|
||||||
|
<FILE id="kpCPIX" name="CustomSynth.h" compile="0" resource="0" file="Source/CustomSynth.h"/>
|
||||||
<FILE id="H2Vgbj" name="FrameSequenceParseErrors.cpp" compile="1" resource="0"
|
<FILE id="H2Vgbj" name="FrameSequenceParseErrors.cpp" compile="1" resource="0"
|
||||||
file="Source/FrameSequenceParseErrors.cpp"/>
|
file="Source/FrameSequenceParseErrors.cpp"/>
|
||||||
<FILE id="Gwt2vB" name="FrameSequenceParseErrors.h" compile="0" resource="0"
|
<FILE id="Gwt2vB" name="FrameSequenceParseErrors.h" compile="0" resource="0"
|
||||||
|
@ -218,6 +218,13 @@ void BaseVoice::renderNextBlock (AudioSampleBuffer& outputBuffer, int startSampl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BaseVoice::changeNote (int midiNoteNumber, float velocity) {
|
||||||
|
noteNumber = midiNoteNumber;
|
||||||
|
|
||||||
|
ampByVelocityAndGain = * (settingRefs->gain) * velocity; // velocity value range is 0.0f-1.0f
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void BaseVoice::calculateAngleDelta()
|
void BaseVoice::calculateAngleDelta()
|
||||||
{
|
{
|
||||||
auto cyclesPerSecond = MidiMessage::getMidiNoteInHertz (noteNumber);
|
auto cyclesPerSecond = MidiMessage::getMidiNoteInHertz (noteNumber);
|
||||||
|
@ -25,6 +25,8 @@ struct BaseVoice : public SynthesiserVoice
|
|||||||
void controllerMoved (int, int) override {}
|
void controllerMoved (int, int) override {}
|
||||||
void renderNextBlock (AudioSampleBuffer& outputBuffer, int startSample, int numSamples) override;
|
void renderNextBlock (AudioSampleBuffer& outputBuffer, int startSample, int numSamples) override;
|
||||||
|
|
||||||
|
void changeNote (int midiNoteNumber, float velocity);
|
||||||
|
|
||||||
virtual float voltageForAngle (double angle) = 0;
|
virtual float voltageForAngle (double angle) = 0;
|
||||||
virtual void onFrameAdvanced() {};
|
virtual void onFrameAdvanced() {};
|
||||||
virtual void advanceControlFrame();
|
virtual void advanceControlFrame();
|
||||||
|
@ -49,7 +49,7 @@ BasicParamsComponent::BasicParamsComponent (Magical8bitPlug2AudioProcessor& p, M
|
|||||||
addAndMakeVisible (gainSlider.get());
|
addAndMakeVisible (gainSlider.get());
|
||||||
gainSlider->setName ("gain slider");
|
gainSlider->setName ("gain slider");
|
||||||
|
|
||||||
gainSlider->setBounds (0, 32, 360, 32);
|
gainSlider->setBounds (0, 32, 380, 32);
|
||||||
|
|
||||||
oscChoice.reset (new ChoiceComponent (p, "osc", "OSC Type"));
|
oscChoice.reset (new ChoiceComponent (p, "osc", "OSC Type"));
|
||||||
addAndMakeVisible (oscChoice.get());
|
addAndMakeVisible (oscChoice.get());
|
||||||
@ -66,7 +66,7 @@ BasicParamsComponent::BasicParamsComponent (Magical8bitPlug2AudioProcessor& p, M
|
|||||||
|
|
||||||
polyNumberInput->setBounds (268, 4, 86, 24);
|
polyNumberInput->setBounds (268, 4, 86, 24);
|
||||||
|
|
||||||
advancedSwitch.reset (new CheckBoxComponent (p, "isAdvancedPanelOpen_raw", "Show Advanced Options"));
|
advancedSwitch.reset (new CheckBoxComponent (p, "isAdvancedPanelOpen_raw", "Advanced Options"));
|
||||||
addAndMakeVisible (advancedSwitch.get());
|
addAndMakeVisible (advancedSwitch.get());
|
||||||
advancedSwitch->setName ("advanced option switch");
|
advancedSwitch->setName ("advanced option switch");
|
||||||
|
|
||||||
@ -74,6 +74,13 @@ BasicParamsComponent::BasicParamsComponent (Magical8bitPlug2AudioProcessor& p, M
|
|||||||
addAndMakeVisible (colorSchemeChoice.get());
|
addAndMakeVisible (colorSchemeChoice.get());
|
||||||
colorSchemeChoice->setName ("color selector");
|
colorSchemeChoice->setName ("color selector");
|
||||||
|
|
||||||
|
monoButton.reset (new juce::TextButton ("mono button"));
|
||||||
|
addAndMakeVisible (monoButton.get());
|
||||||
|
monoButton->setButtonText (TRANS("mono"));
|
||||||
|
monoButton->addListener (this);
|
||||||
|
|
||||||
|
monoButton->setBounds (360, 4, 54, 24);
|
||||||
|
|
||||||
|
|
||||||
//[UserPreSize]
|
//[UserPreSize]
|
||||||
//[/UserPreSize]
|
//[/UserPreSize]
|
||||||
@ -104,6 +111,7 @@ BasicParamsComponent::~BasicParamsComponent()
|
|||||||
polyNumberInput = nullptr;
|
polyNumberInput = nullptr;
|
||||||
advancedSwitch = nullptr;
|
advancedSwitch = nullptr;
|
||||||
colorSchemeChoice = nullptr;
|
colorSchemeChoice = nullptr;
|
||||||
|
monoButton = nullptr;
|
||||||
|
|
||||||
|
|
||||||
//[Destructor]. You can add your own custom destruction code here..
|
//[Destructor]. You can add your own custom destruction code here..
|
||||||
@ -125,7 +133,7 @@ void BasicParamsComponent::resized()
|
|||||||
//[UserPreResize] Add your own custom resize code here..
|
//[UserPreResize] Add your own custom resize code here..
|
||||||
//[/UserPreResize]
|
//[/UserPreResize]
|
||||||
|
|
||||||
advancedSwitch->setBounds (getWidth() - 240, 4, 240, 28);
|
advancedSwitch->setBounds (getWidth() - 180, 4, 180, 28);
|
||||||
colorSchemeChoice->setBounds (getWidth() - 4 - 185, 32, 185, 28);
|
colorSchemeChoice->setBounds (getWidth() - 4 - 185, 32, 185, 28);
|
||||||
//[UserResized] Add your own custom resize handling here..
|
//[UserResized] Add your own custom resize handling here..
|
||||||
//[/UserResized]
|
//[/UserResized]
|
||||||
@ -140,6 +148,7 @@ void BasicParamsComponent::sliderValueChanged (juce::Slider* sliderThatWasMoved)
|
|||||||
{
|
{
|
||||||
//[UserSliderCode_polyNumberInput] -- add your slider handling code here..
|
//[UserSliderCode_polyNumberInput] -- add your slider handling code here..
|
||||||
processor.setupVoice();
|
processor.setupVoice();
|
||||||
|
editor.resizeWholePanel();
|
||||||
//[/UserSliderCode_polyNumberInput]
|
//[/UserSliderCode_polyNumberInput]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,6 +156,25 @@ void BasicParamsComponent::sliderValueChanged (juce::Slider* sliderThatWasMoved)
|
|||||||
//[/UsersliderValueChanged_Post]
|
//[/UsersliderValueChanged_Post]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BasicParamsComponent::buttonClicked (juce::Button* buttonThatWasClicked)
|
||||||
|
{
|
||||||
|
//[UserbuttonClicked_Pre]
|
||||||
|
//[/UserbuttonClicked_Pre]
|
||||||
|
|
||||||
|
if (buttonThatWasClicked == monoButton.get())
|
||||||
|
{
|
||||||
|
//[UserButtonCode_monoButton] -- add your button handler code here..
|
||||||
|
polyNumberInput.get()->setValue(1);
|
||||||
|
return;
|
||||||
|
//[/UserButtonCode_monoButton]
|
||||||
|
}
|
||||||
|
|
||||||
|
//[UserbuttonClicked_Post]
|
||||||
|
colorSchemeChoice->setVisible (buttonThatWasClicked->getToggleState());
|
||||||
|
editor.resizeWholePanel();
|
||||||
|
//[/UserbuttonClicked_Post]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//[MiscUserCode] You can add your own definitions of your custom methods or any other code here...
|
//[MiscUserCode] You can add your own definitions of your custom methods or any other code here...
|
||||||
@ -156,12 +184,6 @@ void BasicParamsComponent::comboBoxChanged (ComboBox* comboBoxThatHasChanged)
|
|||||||
editor.resized();
|
editor.resized();
|
||||||
printf ("setup voice!!\n");
|
printf ("setup voice!!\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void BasicParamsComponent::buttonClicked (Button* buttonThatWasClicked)
|
|
||||||
{
|
|
||||||
colorSchemeChoice->setVisible (buttonThatWasClicked->getToggleState());
|
|
||||||
editor.resizeWholePanel();
|
|
||||||
}
|
|
||||||
//[/MiscUserCode]
|
//[/MiscUserCode]
|
||||||
|
|
||||||
|
|
||||||
@ -175,8 +197,7 @@ void BasicParamsComponent::buttonClicked (Button* buttonThatWasClicked)
|
|||||||
BEGIN_JUCER_METADATA
|
BEGIN_JUCER_METADATA
|
||||||
|
|
||||||
<JUCER_COMPONENT documentType="Component" className="BasicParamsComponent" componentName=""
|
<JUCER_COMPONENT documentType="Component" className="BasicParamsComponent" componentName=""
|
||||||
parentClasses="public Component, public ComboBox::Listener, public Button::Listener"
|
parentClasses="public Component, public ComboBox::Listener" constructorParams="Magical8bitPlug2AudioProcessor& p, Magical8bitPlug2AudioProcessorEditor& e"
|
||||||
constructorParams="Magical8bitPlug2AudioProcessor& p, Magical8bitPlug2AudioProcessorEditor& e"
|
|
||||||
variableInitialisers="processor(p),editor(e)" snapPixels="8"
|
variableInitialisers="processor(p),editor(e)" snapPixels="8"
|
||||||
snapActive="1" snapShown="1" overlayOpacity="0.330" fixedSize="1"
|
snapActive="1" snapShown="1" overlayOpacity="0.330" fixedSize="1"
|
||||||
initialWidth="700" initialHeight="64">
|
initialWidth="700" initialHeight="64">
|
||||||
@ -187,7 +208,7 @@ BEGIN_JUCER_METADATA
|
|||||||
focusDiscardsChanges="0" fontname="Default font" fontsize="15.0"
|
focusDiscardsChanges="0" fontname="Default font" fontsize="15.0"
|
||||||
kerning="0.0" bold="0" italic="0" justification="33"/>
|
kerning="0.0" bold="0" italic="0" justification="33"/>
|
||||||
<GENERICCOMPONENT name="gain slider" id="4bb22b329fed19e9" memberName="gainSlider"
|
<GENERICCOMPONENT name="gain slider" id="4bb22b329fed19e9" memberName="gainSlider"
|
||||||
virtualName="" explicitFocusOrder="0" pos="0 32 360 32" class="SliderComponent"
|
virtualName="" explicitFocusOrder="0" pos="0 32 380 32" class="SliderComponent"
|
||||||
params="p, "gain", "Gain""/>
|
params="p, "gain", "Gain""/>
|
||||||
<GENERICCOMPONENT name="osc selector" id="fa2387d441a3005d" memberName="oscChoice"
|
<GENERICCOMPONENT name="osc selector" id="fa2387d441a3005d" memberName="oscChoice"
|
||||||
virtualName="" explicitFocusOrder="0" pos="0 4 224 28" class="ChoiceComponent"
|
virtualName="" explicitFocusOrder="0" pos="0 4 224 28" class="ChoiceComponent"
|
||||||
@ -198,11 +219,14 @@ BEGIN_JUCER_METADATA
|
|||||||
textBoxEditable="1" textBoxWidth="30" textBoxHeight="20" skewFactor="1.0"
|
textBoxEditable="1" textBoxWidth="30" textBoxHeight="20" skewFactor="1.0"
|
||||||
needsCallback="1"/>
|
needsCallback="1"/>
|
||||||
<GENERICCOMPONENT name="advanced option switch" id="9d35239102eeb521" memberName="advancedSwitch"
|
<GENERICCOMPONENT name="advanced option switch" id="9d35239102eeb521" memberName="advancedSwitch"
|
||||||
virtualName="" explicitFocusOrder="0" pos="0Rr 4 240 28" class="CheckBoxComponent"
|
virtualName="" explicitFocusOrder="0" pos="0Rr 4 180 28" class="CheckBoxComponent"
|
||||||
params="p, "isAdvancedPanelOpen_raw", "Show Advanced Options""/>
|
params="p, "isAdvancedPanelOpen_raw", "Advanced Options""/>
|
||||||
<GENERICCOMPONENT name="color selector" id="21d73ddc37680dd7" memberName="colorSchemeChoice"
|
<GENERICCOMPONENT name="color selector" id="21d73ddc37680dd7" memberName="colorSchemeChoice"
|
||||||
virtualName="" explicitFocusOrder="0" pos="4Rr 32 185 28" class="ChoiceComponent"
|
virtualName="" explicitFocusOrder="0" pos="4Rr 32 185 28" class="ChoiceComponent"
|
||||||
params="p, "colorScheme", "Color""/>
|
params="p, "colorScheme", "Color""/>
|
||||||
|
<TEXTBUTTON name="mono button" id="9265bb1a11f90786" memberName="monoButton"
|
||||||
|
virtualName="" explicitFocusOrder="0" pos="360 4 54 24" buttonText="mono"
|
||||||
|
connectedEdges="0" needsCallback="1" radioGroupId="0"/>
|
||||||
</JUCER_COMPONENT>
|
</JUCER_COMPONENT>
|
||||||
|
|
||||||
END_JUCER_METADATA
|
END_JUCER_METADATA
|
||||||
|
@ -40,8 +40,8 @@ class Magical8bitPlug2AudioProcessorEditor;
|
|||||||
*/
|
*/
|
||||||
class BasicParamsComponent : public Component,
|
class BasicParamsComponent : public Component,
|
||||||
public ComboBox::Listener,
|
public ComboBox::Listener,
|
||||||
public Button::Listener,
|
public juce::Slider::Listener,
|
||||||
public juce::Slider::Listener
|
public juce::Button::Listener
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
@ -51,12 +51,12 @@ public:
|
|||||||
//==============================================================================
|
//==============================================================================
|
||||||
//[UserMethods] -- You can add your own custom methods in this section.
|
//[UserMethods] -- You can add your own custom methods in this section.
|
||||||
void comboBoxChanged (ComboBox* comboBoxThatHasChanged) override;
|
void comboBoxChanged (ComboBox* comboBoxThatHasChanged) override;
|
||||||
void buttonClicked (Button* buttonThatWasClicked) override;
|
|
||||||
//[/UserMethods]
|
//[/UserMethods]
|
||||||
|
|
||||||
void paint (juce::Graphics& g) override;
|
void paint (juce::Graphics& g) override;
|
||||||
void resized() override;
|
void resized() override;
|
||||||
void sliderValueChanged (juce::Slider* sliderThatWasMoved) override;
|
void sliderValueChanged (juce::Slider* sliderThatWasMoved) override;
|
||||||
|
void buttonClicked (juce::Button* buttonThatWasClicked) override;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -75,6 +75,7 @@ private:
|
|||||||
std::unique_ptr<juce::Slider> polyNumberInput;
|
std::unique_ptr<juce::Slider> polyNumberInput;
|
||||||
std::unique_ptr<CheckBoxComponent> advancedSwitch;
|
std::unique_ptr<CheckBoxComponent> advancedSwitch;
|
||||||
std::unique_ptr<ChoiceComponent> colorSchemeChoice;
|
std::unique_ptr<ChoiceComponent> colorSchemeChoice;
|
||||||
|
std::unique_ptr<juce::TextButton> monoButton;
|
||||||
|
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
@ -115,7 +115,7 @@ void ChoiceComponent::comboBoxChanged (juce::ComboBox* comboBoxThatHasChanged)
|
|||||||
if (comboBoxThatHasChanged == comboBox.get())
|
if (comboBoxThatHasChanged == comboBox.get())
|
||||||
{
|
{
|
||||||
//[UserComboBoxCode_comboBox] -- add your combo box handling code here..
|
//[UserComboBoxCode_comboBox] -- add your combo box handling code here..
|
||||||
printf ("value = %d\n", comboBoxThatHasChanged->getSelectedId());
|
// printf ("value = %d\n", comboBoxThatHasChanged->getSelectedId());
|
||||||
//[/UserComboBoxCode_comboBox]
|
//[/UserComboBoxCode_comboBox]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
147
Source/CustomSynth.cpp
Normal file
147
Source/CustomSynth.cpp
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
/*
|
||||||
|
==============================================================================
|
||||||
|
|
||||||
|
CustomSynth.cpp
|
||||||
|
Created: 17 May 2021 11:29:59pm
|
||||||
|
Author: 除村武志
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "CustomSynth.h"
|
||||||
|
#include "BaseVoice.h"
|
||||||
|
#include "TonalVoice.h"
|
||||||
|
#include "PluginProcessor.h"
|
||||||
|
|
||||||
|
CustomSynth::CustomSynth(Magical8bitPlug2AudioProcessor& p) : processor(p) {}
|
||||||
|
|
||||||
|
TonalVoice* CustomSynth::getVoiceIfShouldProcessInMonoMode() {
|
||||||
|
if (!processor.settingRefs.isMonophonic()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
if (processor.settingRefs.monophonicBehavior() == kNonLegato) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try casting into TonalVoice* and return it if success(otherwise returns nullptr)
|
||||||
|
return dynamic_cast<TonalVoice *>(voices.getFirst());
|
||||||
|
}
|
||||||
|
|
||||||
|
void CustomSynth::noteOn(int midiChannel, int midiNoteNumber, float velocity) {
|
||||||
|
TonalVoice *voice = getVoiceIfShouldProcessInMonoMode();
|
||||||
|
|
||||||
|
if (voice == nullptr) {
|
||||||
|
Synthesiser::noteOn(midiChannel, midiNoteNumber, velocity);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mono
|
||||||
|
|
||||||
|
// Start thread guard
|
||||||
|
const ScopedLock sl (lock);
|
||||||
|
|
||||||
|
if (voice->isKeyDown()) {
|
||||||
|
// Already key on
|
||||||
|
switch (processor.settingRefs.monophonicBehavior()) {
|
||||||
|
case kLegato:
|
||||||
|
voice->addLegatoNote(midiNoteNumber, velocity);
|
||||||
|
break;
|
||||||
|
case kArpeggioUp:
|
||||||
|
voice->addArpeggioNoteAscending(midiNoteNumber);
|
||||||
|
break;
|
||||||
|
case kArpeggioDown:
|
||||||
|
voice->addArpeggioNoteDescending(midiNoteNumber);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// no-op
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch (processor.settingRefs.monophonicBehavior()) {
|
||||||
|
case kLegato:
|
||||||
|
// start note and set legato mode
|
||||||
|
Synthesiser::noteOn(midiChannel, midiNoteNumber, velocity);
|
||||||
|
voice->setLegatoMode(*(processor.settingRefs.portamentoTime), midiChannel);
|
||||||
|
break;
|
||||||
|
case kArpeggioUp:
|
||||||
|
case kArpeggioDown:
|
||||||
|
// start note and calc arpeggio interval
|
||||||
|
Synthesiser::noteOn(midiChannel, midiNoteNumber, velocity);
|
||||||
|
voice->setArpeggioMode(calcArpeggioInterval(), midiChannel);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// no-op
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double CustomSynth::calcArpeggioInterval() {
|
||||||
|
switch (processor.settingRefs.apreggioIntervalType()) {
|
||||||
|
case k1frame:
|
||||||
|
return 1.0 / 60.0;
|
||||||
|
case k2frames:
|
||||||
|
return 1.0 / 30.0;
|
||||||
|
case k3frames:
|
||||||
|
return 1.0 / 20.0;
|
||||||
|
case k64th:
|
||||||
|
return 240.0 / (processor.getCurrentBPM() * 64);
|
||||||
|
case k48th:
|
||||||
|
return 240.0 / (processor.getCurrentBPM() * 48);
|
||||||
|
case k32nd:
|
||||||
|
return 240.0 / (processor.getCurrentBPM() * 32);
|
||||||
|
case k24th:
|
||||||
|
return 240.0 / (processor.getCurrentBPM() * 24);
|
||||||
|
case kSlider:
|
||||||
|
return *(processor.settingRefs.arpeggioIntervalSliderValue);
|
||||||
|
default:
|
||||||
|
return 1.0 / 60.0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CustomSynth::noteOff(int midiChannel, int midiNoteNumber, float velocity, bool allowTailOff) {
|
||||||
|
TonalVoice *voice = getVoiceIfShouldProcessInMonoMode();
|
||||||
|
|
||||||
|
if (voice == nullptr) {
|
||||||
|
Synthesiser::noteOff(midiChannel, midiNoteNumber, velocity, allowTailOff);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// mono
|
||||||
|
|
||||||
|
// Start thread guard
|
||||||
|
const ScopedLock sl (lock);
|
||||||
|
|
||||||
|
if (!voice->isKeyDown()) {
|
||||||
|
// key is already off
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (processor.settingRefs.monophonicBehavior()) {
|
||||||
|
case kLegato:
|
||||||
|
{
|
||||||
|
int numBuffer = voice->removeLegatoNote(midiNoteNumber);
|
||||||
|
if (numBuffer < 1) {
|
||||||
|
Synthesiser::noteOff(voice->primaryMidiChannel, voice->getCurrentlyPlayingNote(), velocity, allowTailOff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case kArpeggioUp:
|
||||||
|
case kArpeggioDown:
|
||||||
|
{
|
||||||
|
int numBuffer = voice->removeArpeggioNote(midiNoteNumber);
|
||||||
|
if (numBuffer < 1) {
|
||||||
|
Synthesiser::noteOff(voice->primaryMidiChannel, voice->getCurrentlyPlayingNote(), velocity, allowTailOff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CustomSynth::allNotesOff (const int midiChannel, const bool allowTailOff) {
|
||||||
|
Synthesiser::allNotesOff(midiChannel, allowTailOff);
|
||||||
|
}
|
||||||
|
|
29
Source/CustomSynth.h
Normal file
29
Source/CustomSynth.h
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
==============================================================================
|
||||||
|
|
||||||
|
CustomSynth.h
|
||||||
|
Created: 17 May 2021 11:29:59pm
|
||||||
|
Author: 除村武志
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "../JuceLibraryCode/JuceHeader.h"
|
||||||
|
|
||||||
|
class Magical8bitPlug2AudioProcessor;
|
||||||
|
class TonalVoice;
|
||||||
|
|
||||||
|
class CustomSynth : public Synthesiser {
|
||||||
|
public:
|
||||||
|
CustomSynth(Magical8bitPlug2AudioProcessor& p);
|
||||||
|
|
||||||
|
void noteOn(int midiChannel, int midiNoteNumber, float velocity) override;
|
||||||
|
void noteOff(int midiChannel, int midiNoteNumber, float velocity, bool allowTailOff) override;
|
||||||
|
void allNotesOff (const int midiChannel, const bool allowTailOff) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
TonalVoice* getVoiceIfShouldProcessInMonoMode();
|
||||||
|
double calcArpeggioInterval();
|
||||||
|
Magical8bitPlug2AudioProcessor& processor;
|
||||||
|
};
|
200
Source/MonophonicComponent.cpp
Normal file
200
Source/MonophonicComponent.cpp
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
/*
|
||||||
|
==============================================================================
|
||||||
|
|
||||||
|
This is an automatically generated GUI class created by the Projucer!
|
||||||
|
|
||||||
|
Be careful when adding custom code to these files, as only the code within
|
||||||
|
the "//[xyz]" and "//[/xyz]" sections will be retained when the file is loaded
|
||||||
|
and re-saved.
|
||||||
|
|
||||||
|
Created with Projucer version: 6.0.8
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
The Projucer is part of the JUCE library.
|
||||||
|
Copyright (c) 2020 - Raw Material Software Limited.
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
//[Headers] You can add your own extra header files here...
|
||||||
|
//[/Headers]
|
||||||
|
|
||||||
|
#include "MonophonicComponent.h"
|
||||||
|
|
||||||
|
|
||||||
|
//[MiscUserDefs] You can add your own user definitions and misc code here...
|
||||||
|
//[/MiscUserDefs]
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
MonophonicComponent::MonophonicComponent (Magical8bitPlug2AudioProcessor& p)
|
||||||
|
: processor(p)
|
||||||
|
{
|
||||||
|
//[Constructor_pre] You can add your own custom stuff here..
|
||||||
|
//[/Constructor_pre]
|
||||||
|
|
||||||
|
label.reset (new juce::Label ("label",
|
||||||
|
TRANS("Monophonic Options")));
|
||||||
|
addAndMakeVisible (label.get());
|
||||||
|
label->setFont (juce::Font (17.00f, juce::Font::plain).withTypefaceStyle ("Regular"));
|
||||||
|
label->setJustificationType (juce::Justification::centredLeft);
|
||||||
|
label->setEditable (false, false, false);
|
||||||
|
label->setColour (juce::TextEditor::textColourId, juce::Colours::black);
|
||||||
|
label->setColour (juce::TextEditor::backgroundColourId, juce::Colour (0x00000000));
|
||||||
|
|
||||||
|
label->setBounds (0, 4, 150, 22);
|
||||||
|
|
||||||
|
behaviorChoice.reset (new ChoiceComponent (p, "monophonicBehavior_raw", "Behavior"));
|
||||||
|
addAndMakeVisible (behaviorChoice.get());
|
||||||
|
behaviorChoice->setName ("behavior selector");
|
||||||
|
|
||||||
|
behaviorChoice->setBounds (0, 28, 224, 26);
|
||||||
|
|
||||||
|
intervalChoice.reset (new ChoiceComponent (p, "arpeggioIntervalType_raw", "Interval"));
|
||||||
|
addAndMakeVisible (intervalChoice.get());
|
||||||
|
intervalChoice->setName ("interval selector");
|
||||||
|
|
||||||
|
intervalChoice->setBounds (228, 28, 185, 28);
|
||||||
|
|
||||||
|
intervalSlider.reset (new juce::Slider ("interval slider"));
|
||||||
|
addAndMakeVisible (intervalSlider.get());
|
||||||
|
intervalSlider->setRange (0, 10, 0.01);
|
||||||
|
intervalSlider->setSliderStyle (juce::Slider::LinearHorizontal);
|
||||||
|
intervalSlider->setTextBoxStyle (juce::Slider::TextBoxRight, false, 50, 20);
|
||||||
|
|
||||||
|
portamentoSlider.reset (new SliderComponent (p, "portamentoTime", "Portamento"));
|
||||||
|
addAndMakeVisible (portamentoSlider.get());
|
||||||
|
portamentoSlider->setName ("portamento slider");
|
||||||
|
|
||||||
|
|
||||||
|
//[UserPreSize]
|
||||||
|
//[/UserPreSize]
|
||||||
|
|
||||||
|
setSize (640, 82);
|
||||||
|
|
||||||
|
|
||||||
|
//[Constructor] You can add your own custom stuff here..
|
||||||
|
attc.reset (new SliderAttachment (p.parameters, "arpeggioIntervalSliderValue", *intervalSlider));
|
||||||
|
behaviorChoice->setListener(this);
|
||||||
|
intervalChoice->setListener(this);
|
||||||
|
updateVisibility();
|
||||||
|
//[/Constructor]
|
||||||
|
}
|
||||||
|
|
||||||
|
MonophonicComponent::~MonophonicComponent()
|
||||||
|
{
|
||||||
|
//[Destructor_pre]. You can add your own custom destruction code here..
|
||||||
|
attc.reset();
|
||||||
|
//[/Destructor_pre]
|
||||||
|
|
||||||
|
label = nullptr;
|
||||||
|
behaviorChoice = nullptr;
|
||||||
|
intervalChoice = nullptr;
|
||||||
|
intervalSlider = nullptr;
|
||||||
|
portamentoSlider = nullptr;
|
||||||
|
|
||||||
|
|
||||||
|
//[Destructor]. You can add your own custom destruction code here..
|
||||||
|
//[/Destructor]
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
void MonophonicComponent::paint (juce::Graphics& g)
|
||||||
|
{
|
||||||
|
//[UserPrePaint] Add your own custom painting code here..
|
||||||
|
//[/UserPrePaint]
|
||||||
|
|
||||||
|
//[UserPaint] Add your own custom painting code here..
|
||||||
|
//[/UserPaint]
|
||||||
|
}
|
||||||
|
|
||||||
|
void MonophonicComponent::resized()
|
||||||
|
{
|
||||||
|
//[UserPreResize] Add your own custom resize code here..
|
||||||
|
//[/UserPreResize]
|
||||||
|
|
||||||
|
intervalSlider->setBounds (getWidth() - (getWidth() - 420), 28, getWidth() - 420, 24);
|
||||||
|
portamentoSlider->setBounds (getWidth() - proportionOfWidth (0.5000f), 28, proportionOfWidth (0.5000f), 28);
|
||||||
|
//[UserResized] Add your own custom resize handling here..
|
||||||
|
//[/UserResized]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//[MiscUserCode] You can add your own definitions of your custom methods or any other code here...
|
||||||
|
void MonophonicComponent::comboBoxChanged(juce::ComboBox *comboBoxThatHasChanged) {
|
||||||
|
updateVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MonophonicComponent::updateVisibility()
|
||||||
|
{
|
||||||
|
portamentoSlider->setVisible(false);
|
||||||
|
intervalSlider->setVisible(false);
|
||||||
|
intervalChoice->setVisible(false);
|
||||||
|
|
||||||
|
switch (processor.settingRefs.monophonicBehavior()) {
|
||||||
|
case kLegato:
|
||||||
|
portamentoSlider->setVisible(true);
|
||||||
|
break;
|
||||||
|
case kArpeggioUp:
|
||||||
|
case kArpeggioDown:
|
||||||
|
intervalChoice->setVisible(true);
|
||||||
|
switch (processor.settingRefs.apreggioIntervalType()) {
|
||||||
|
case kSlider:
|
||||||
|
intervalSlider->setVisible(true);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//[/MiscUserCode]
|
||||||
|
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
#if 0
|
||||||
|
/* -- Projucer information section --
|
||||||
|
|
||||||
|
This is where the Projucer stores the metadata that describe this GUI layout, so
|
||||||
|
make changes in here at your peril!
|
||||||
|
|
||||||
|
BEGIN_JUCER_METADATA
|
||||||
|
|
||||||
|
<JUCER_COMPONENT documentType="Component" className="MonophonicComponent" componentName=""
|
||||||
|
parentClasses="public Component, public ComboBox::Listener" constructorParams="Magical8bitPlug2AudioProcessor& p"
|
||||||
|
variableInitialisers="processor(p)" snapPixels="8" snapActive="1"
|
||||||
|
snapShown="1" overlayOpacity="0.330" fixedSize="1" initialWidth="640"
|
||||||
|
initialHeight="82">
|
||||||
|
<BACKGROUND backgroundColour="ffffff"/>
|
||||||
|
<LABEL name="label" id="bae3132bcad681ce" memberName="label" virtualName=""
|
||||||
|
explicitFocusOrder="0" pos="0 4 150 22" edTextCol="ff000000"
|
||||||
|
edBkgCol="0" labelText="Monophonic Options" editableSingleClick="0"
|
||||||
|
editableDoubleClick="0" focusDiscardsChanges="0" fontname="Default font"
|
||||||
|
fontsize="17.0" kerning="0.0" bold="0" italic="0" justification="33"/>
|
||||||
|
<GENERICCOMPONENT name="behavior selector" id="fa2387d441a3005d" memberName="behaviorChoice"
|
||||||
|
virtualName="" explicitFocusOrder="0" pos="0 28 224 26" class="ChoiceComponent"
|
||||||
|
params="p, "monophonicBehavior_raw", "Behavior""/>
|
||||||
|
<GENERICCOMPONENT name="interval selector" id="21d73ddc37680dd7" memberName="intervalChoice"
|
||||||
|
virtualName="" explicitFocusOrder="0" pos="228 28 185 28" class="ChoiceComponent"
|
||||||
|
params="p, "arpeggioIntervalType_raw", "Interval""/>
|
||||||
|
<SLIDER name="interval slider" id="2d6901c46e73c1e" memberName="intervalSlider"
|
||||||
|
virtualName="" explicitFocusOrder="0" pos="0Rr 28 420M 24" min="0.0"
|
||||||
|
max="10.0" int="0.01" style="LinearHorizontal" textBoxPos="TextBoxRight"
|
||||||
|
textBoxEditable="1" textBoxWidth="50" textBoxHeight="20" skewFactor="1.0"
|
||||||
|
needsCallback="0"/>
|
||||||
|
<GENERICCOMPONENT name="portamento slider" id="b01ddc412ec6dc27" memberName="portamentoSlider"
|
||||||
|
virtualName="" explicitFocusOrder="0" pos="0Rr 28 50% 28" class="SliderComponent"
|
||||||
|
params="p, "portamentoTime", "Portamento""/>
|
||||||
|
</JUCER_COMPONENT>
|
||||||
|
|
||||||
|
END_JUCER_METADATA
|
||||||
|
*/
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
//[EndFile] You can add extra defines here...
|
||||||
|
//[/EndFile]
|
||||||
|
|
78
Source/MonophonicComponent.h
Normal file
78
Source/MonophonicComponent.h
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
==============================================================================
|
||||||
|
|
||||||
|
This is an automatically generated GUI class created by the Projucer!
|
||||||
|
|
||||||
|
Be careful when adding custom code to these files, as only the code within
|
||||||
|
the "//[xyz]" and "//[/xyz]" sections will be retained when the file is loaded
|
||||||
|
and re-saved.
|
||||||
|
|
||||||
|
Created with Projucer version: 6.0.8
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
The Projucer is part of the JUCE library.
|
||||||
|
Copyright (c) 2020 - Raw Material Software Limited.
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
//[Headers] -- You can add your own extra header files here --
|
||||||
|
#include <JuceHeader.h>
|
||||||
|
#include "SliderComponent.h"
|
||||||
|
#include "ChoiceComponent.h"
|
||||||
|
//[/Headers]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
/**
|
||||||
|
//[Comments]
|
||||||
|
An auto-generated component, created by the Projucer.
|
||||||
|
|
||||||
|
Describe your class and how it works here!
|
||||||
|
//[/Comments]
|
||||||
|
*/
|
||||||
|
class MonophonicComponent : public Component,
|
||||||
|
public ComboBox::Listener
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
//==============================================================================
|
||||||
|
MonophonicComponent (Magical8bitPlug2AudioProcessor& p);
|
||||||
|
~MonophonicComponent() override;
|
||||||
|
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
//[UserMethods] -- You can add your own custom methods in this section.
|
||||||
|
void comboBoxChanged(juce::ComboBox *comboBoxThatHasChanged) override;
|
||||||
|
void updateVisibility();
|
||||||
|
//[/UserMethods]
|
||||||
|
|
||||||
|
void paint (juce::Graphics& g) override;
|
||||||
|
void resized() override;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
//[UserVariables] -- You can add your own custom variables in this section.
|
||||||
|
std::unique_ptr<SliderAttachment> attc;
|
||||||
|
Magical8bitPlug2AudioProcessor& processor;
|
||||||
|
//[/UserVariables]
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
std::unique_ptr<juce::Label> label;
|
||||||
|
std::unique_ptr<ChoiceComponent> behaviorChoice;
|
||||||
|
std::unique_ptr<ChoiceComponent> intervalChoice;
|
||||||
|
std::unique_ptr<juce::Slider> intervalSlider;
|
||||||
|
std::unique_ptr<SliderComponent> portamentoSlider;
|
||||||
|
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MonophonicComponent)
|
||||||
|
};
|
||||||
|
|
||||||
|
//[EndFile] You can add extra defines here...
|
||||||
|
//[/EndFile]
|
||||||
|
|
@ -11,6 +11,7 @@
|
|||||||
#include "PluginProcessor.h"
|
#include "PluginProcessor.h"
|
||||||
#include "PluginEditor.h"
|
#include "PluginEditor.h"
|
||||||
#include "AdvancedParamsComponent.h"
|
#include "AdvancedParamsComponent.h"
|
||||||
|
#include "MonophonicComponent.h"
|
||||||
#include "PulseParamsComponent.h"
|
#include "PulseParamsComponent.h"
|
||||||
#include "BasicParamsComponent.h"
|
#include "BasicParamsComponent.h"
|
||||||
#include "EnvelopeParamsComponent.h"
|
#include "EnvelopeParamsComponent.h"
|
||||||
@ -25,11 +26,16 @@ Magical8bitPlug2AudioProcessorEditor::Magical8bitPlug2AudioProcessorEditor (Magi
|
|||||||
{
|
{
|
||||||
// Make sure that before the constructor has finished, you've set the
|
// Make sure that before the constructor has finished, you've set the
|
||||||
// editor's size to whatever you need it to be.
|
// editor's size to whatever you need it to be.
|
||||||
|
isComponentsReady = false;
|
||||||
|
|
||||||
applyLookAndFeel();
|
applyLookAndFeel();
|
||||||
|
|
||||||
basicCompo.reset (new BasicParamsComponent (p, *this));
|
basicCompo.reset (new BasicParamsComponent (p, *this));
|
||||||
addAndMakeVisible (basicCompo.get());
|
addAndMakeVisible (basicCompo.get());
|
||||||
|
|
||||||
|
monoCompo.reset (new MonophonicComponent (p));
|
||||||
|
addAndMakeVisible(monoCompo.get());
|
||||||
|
|
||||||
envCompo.reset (new EnvelopeParamsComponent (p));
|
envCompo.reset (new EnvelopeParamsComponent (p));
|
||||||
addAndMakeVisible (envCompo.get());
|
addAndMakeVisible (envCompo.get());
|
||||||
|
|
||||||
@ -54,7 +60,7 @@ Magical8bitPlug2AudioProcessorEditor::Magical8bitPlug2AudioProcessorEditor (Magi
|
|||||||
(p.parameters.getParameter ("isVolumeSequenceEnabled_raw"))->addListener (this);
|
(p.parameters.getParameter ("isVolumeSequenceEnabled_raw"))->addListener (this);
|
||||||
(p.parameters.getParameter ("isDutySequenceEnabled_raw"))->addListener (this);
|
(p.parameters.getParameter ("isDutySequenceEnabled_raw"))->addListener (this);
|
||||||
|
|
||||||
|
isComponentsReady = true;
|
||||||
resizeWholePanel();
|
resizeWholePanel();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,6 +136,9 @@ struct
|
|||||||
|
|
||||||
const int basCompoHeight = componentMargin * 2
|
const int basCompoHeight = componentMargin * 2
|
||||||
+ genericControlHeight * 2;
|
+ genericControlHeight * 2;
|
||||||
|
const int monoCompoHeight = componentMargin * 2
|
||||||
|
+ indexHeight
|
||||||
|
+ customEnvelopeHeight;
|
||||||
const int toneSpecificControlHeight = componentMargin * 2
|
const int toneSpecificControlHeight = componentMargin * 2
|
||||||
+ indexHeight
|
+ indexHeight
|
||||||
+ genericControlHeight;
|
+ genericControlHeight;
|
||||||
@ -148,30 +157,25 @@ struct
|
|||||||
const int advCompoHeight = componentMargin * 2
|
const int advCompoHeight = componentMargin * 2
|
||||||
+ indexHeight
|
+ indexHeight
|
||||||
+ customEnvelopeHeight * 3;
|
+ customEnvelopeHeight * 3;
|
||||||
const int totalHeight (bool isAdvOptOn)
|
const int totalHeight (bool isAdvOptOn, bool isMono)
|
||||||
{
|
{
|
||||||
|
int retHeight = topMargin
|
||||||
|
+ basCompoHeight
|
||||||
|
+ sectionSeparatorHeight
|
||||||
|
+ toneSpecificControlHeight
|
||||||
|
+ envCompoHeight
|
||||||
|
+ bendCompoHeight
|
||||||
|
+ bottomMargin;
|
||||||
if (isAdvOptOn)
|
if (isAdvOptOn)
|
||||||
{
|
{
|
||||||
return topMargin
|
retHeight += sectionSeparatorHeight
|
||||||
+ basCompoHeight
|
+ advCompoHeight;
|
||||||
+ sectionSeparatorHeight
|
|
||||||
+ toneSpecificControlHeight
|
|
||||||
+ envCompoHeight
|
|
||||||
+ bendCompoHeight
|
|
||||||
+ sectionSeparatorHeight
|
|
||||||
+ advCompoHeight
|
|
||||||
+ bottomMargin;
|
|
||||||
}
|
}
|
||||||
else
|
if (isMono)
|
||||||
{
|
{
|
||||||
return topMargin
|
retHeight += monoCompoHeight;
|
||||||
+ basCompoHeight
|
|
||||||
+ sectionSeparatorHeight
|
|
||||||
+ toneSpecificControlHeight
|
|
||||||
+ envCompoHeight
|
|
||||||
+ bendCompoHeight
|
|
||||||
+ bottomMargin;
|
|
||||||
}
|
}
|
||||||
|
return retHeight;
|
||||||
}
|
}
|
||||||
} sizes;
|
} sizes;
|
||||||
|
|
||||||
@ -205,26 +209,37 @@ void Magical8bitPlug2AudioProcessorEditor::resized()
|
|||||||
int w = sizes.halfComponentWidth;
|
int w = sizes.halfComponentWidth;
|
||||||
basicCompo->setBounds (x, y, sizes.fullComponentWidth, sizes.basCompoHeight);
|
basicCompo->setBounds (x, y, sizes.fullComponentWidth, sizes.basCompoHeight);
|
||||||
|
|
||||||
|
y += sizes.basCompoHeight + sizes.sectionSeparatorHeight;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Monophonic
|
||||||
|
//
|
||||||
|
if (processor.settingRefs.isMonophonic()) {
|
||||||
|
monoCompo->setBounds(x, y, sizes.fullComponentWidth, sizes.monoCompoHeight);
|
||||||
|
y += sizes.monoCompoHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
int y1 = y;
|
||||||
|
int y2 = y;
|
||||||
|
|
||||||
//
|
//
|
||||||
// Main part - Left
|
// Main part - Left
|
||||||
//
|
//
|
||||||
y = sizes.topMargin + sizes.basCompoHeight + sizes.sectionSeparatorHeight;
|
|
||||||
|
|
||||||
pulCompo->setBounds (x, y, w, sizes.toneSpecificControlHeight);
|
pulCompo->setBounds (x, y1, w, sizes.toneSpecificControlHeight);
|
||||||
noiCompo->setBounds (x, y, w, sizes.toneSpecificControlHeight);
|
noiCompo->setBounds (x, y1, w, sizes.toneSpecificControlHeight);
|
||||||
y += sizes.toneSpecificControlHeight;
|
y1 += sizes.toneSpecificControlHeight;
|
||||||
|
|
||||||
envCompo->setBounds (x, y, w, sizes.envCompoHeight);
|
envCompo->setBounds (x, y1, w, sizes.envCompoHeight);
|
||||||
y += sizes.envCompoHeight;
|
y1 += sizes.envCompoHeight;
|
||||||
|
|
||||||
bendCompo->setBounds (x, y, w, sizes.bendCompoHeight);
|
bendCompo->setBounds (x, y1, w, sizes.bendCompoHeight);
|
||||||
y += sizes.bendCompoHeight;
|
y1 += sizes.bendCompoHeight;
|
||||||
|
|
||||||
//
|
//
|
||||||
// Main part - Right
|
// Main part - Right
|
||||||
//
|
//
|
||||||
x = sizes.leftMargin + sizes.halfComponentWidth + sizes.verticalSeparatorWidth;
|
x = sizes.leftMargin + sizes.halfComponentWidth + sizes.verticalSeparatorWidth;
|
||||||
int y2 = sizes.topMargin + sizes.basCompoHeight + sizes.sectionSeparatorHeight;
|
|
||||||
|
|
||||||
sweepCompo->setBounds (x, y2, w, sizes.sweepCompoHeight);
|
sweepCompo->setBounds (x, y2, w, sizes.sweepCompoHeight);
|
||||||
y2 += sizes.sweepCompoHeight;
|
y2 += sizes.sweepCompoHeight;
|
||||||
@ -236,7 +251,7 @@ void Magical8bitPlug2AudioProcessorEditor::resized()
|
|||||||
// Advanced part
|
// Advanced part
|
||||||
//
|
//
|
||||||
x = sizes.leftMargin;
|
x = sizes.leftMargin;
|
||||||
int y3 = y > y2 ? y : y2;
|
int y3 = y1 > y2 ? y1 : y2;
|
||||||
y3 += sizes.sectionSeparatorHeight;
|
y3 += sizes.sectionSeparatorHeight;
|
||||||
advCompo->setBounds (x, y3, sizes.fullComponentWidth, sizes.advCompoHeight);
|
advCompo->setBounds (x, y3, sizes.fullComponentWidth, sizes.advCompoHeight);
|
||||||
|
|
||||||
@ -261,6 +276,8 @@ void Magical8bitPlug2AudioProcessorEditor::resized()
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
monoCompo->setVisible(processor.settingRefs.isMonophonic());
|
||||||
|
|
||||||
//
|
//
|
||||||
// Enable/Disable
|
// Enable/Disable
|
||||||
//
|
//
|
||||||
@ -272,7 +289,10 @@ void Magical8bitPlug2AudioProcessorEditor::resized()
|
|||||||
|
|
||||||
void Magical8bitPlug2AudioProcessorEditor::resizeWholePanel()
|
void Magical8bitPlug2AudioProcessorEditor::resizeWholePanel()
|
||||||
{
|
{
|
||||||
setSize (sizes.totalWidth, sizes.totalHeight (processor.settingRefs.isAdvancedPanelOpen()));
|
if (!isComponentsReady) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setSize (sizes.totalWidth, sizes.totalHeight (processor.settingRefs.isAdvancedPanelOpen(), processor.settingRefs.isMonophonic()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Magical8bitPlug2AudioProcessorEditor::parameterValueChanged (int parameterIndex, float newValue)
|
void Magical8bitPlug2AudioProcessorEditor::parameterValueChanged (int parameterIndex, float newValue)
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
#include "../JuceLibraryCode/JuceHeader.h"
|
#include "../JuceLibraryCode/JuceHeader.h"
|
||||||
#include "PluginProcessor.h"
|
#include "PluginProcessor.h"
|
||||||
class AdvancedParamsComponent;
|
class AdvancedParamsComponent;
|
||||||
|
class MonophonicComponent;
|
||||||
class PulseParamsComponent;
|
class PulseParamsComponent;
|
||||||
class BasicParamsComponent;
|
class BasicParamsComponent;
|
||||||
class EnvelopeParamsComponent;
|
class EnvelopeParamsComponent;
|
||||||
@ -44,6 +45,7 @@ private:
|
|||||||
Magical8bitPlug2AudioProcessor& processor;
|
Magical8bitPlug2AudioProcessor& processor;
|
||||||
|
|
||||||
std::unique_ptr<BasicParamsComponent> basicCompo;
|
std::unique_ptr<BasicParamsComponent> basicCompo;
|
||||||
|
std::unique_ptr<MonophonicComponent> monoCompo;
|
||||||
std::unique_ptr<EnvelopeParamsComponent> envCompo;
|
std::unique_ptr<EnvelopeParamsComponent> envCompo;
|
||||||
std::unique_ptr<AdvancedParamsComponent> advCompo;
|
std::unique_ptr<AdvancedParamsComponent> advCompo;
|
||||||
std::unique_ptr<PulseParamsComponent> pulCompo;
|
std::unique_ptr<PulseParamsComponent> pulCompo;
|
||||||
@ -52,5 +54,7 @@ private:
|
|||||||
std::unique_ptr<SweepParamsComponent> sweepCompo;
|
std::unique_ptr<SweepParamsComponent> sweepCompo;
|
||||||
std::unique_ptr<VibratoParamsComponent> vibCompo;
|
std::unique_ptr<VibratoParamsComponent> vibCompo;
|
||||||
|
|
||||||
|
bool isComponentsReady;
|
||||||
|
|
||||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Magical8bitPlug2AudioProcessorEditor)
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Magical8bitPlug2AudioProcessorEditor)
|
||||||
};
|
};
|
||||||
|
@ -66,11 +66,18 @@ Magical8bitPlug2AudioProcessor::Magical8bitPlug2AudioProcessor()
|
|||||||
0.5f), //skew
|
0.5f), //skew
|
||||||
0.0f), //default
|
0.0f), //default
|
||||||
//
|
//
|
||||||
// Arpeggio
|
// Monophonic
|
||||||
//
|
//
|
||||||
std::make_unique<AudioParameterBool> ("isArpeggioEnabled_raw", "Enabled", false),
|
std::make_unique<AudioParameterChoice> ("monophonicBehavior_raw", "Behavior", StringArray ({"Legato", "Arpeggio Up", "Arpeggio Down", "Non-legato"}), 0),
|
||||||
std::make_unique<AudioParameterFloat> ("arpeggioTime", "Time", 0.0f, 0.3f, 0.033f),
|
std::make_unique<AudioParameterChoice> ("arpeggioIntervalType_raw", "Interval", StringArray ({"1 frame", "2 frames", "3 frames", "96th", "64th", "48th", "32nd", "24th", "Slider"}), 0),
|
||||||
std::make_unique<AudioParameterChoice> ("arpeggioDirection", "Direction", StringArray ({"up", "down"}), 0),
|
std::make_unique<AudioParameterFloat> ("arpeggioIntervalSliderValue", //ID
|
||||||
|
"Interval", //name
|
||||||
|
NormalisableRange<float> (0.001f, //min
|
||||||
|
0.3f, //max
|
||||||
|
0.001f, //step
|
||||||
|
0.5f), //skew
|
||||||
|
0.001f), //default
|
||||||
|
std::make_unique<AudioParameterFloat> ("portamentoTime", "Portamento Time", 0.0f, 1.0f, 0.0f),
|
||||||
//
|
//
|
||||||
// Bend
|
// Bend
|
||||||
//
|
//
|
||||||
@ -118,6 +125,7 @@ Magical8bitPlug2AudioProcessor::Magical8bitPlug2AudioProcessor()
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
, settingRefs (¶meters)
|
, settingRefs (¶meters)
|
||||||
|
, synth(*this)
|
||||||
#ifndef JucePlugin_PreferredChannelConfigurations
|
#ifndef JucePlugin_PreferredChannelConfigurations
|
||||||
, AudioProcessor (BusesProperties()
|
, AudioProcessor (BusesProperties()
|
||||||
#if ! JucePlugin_IsMidiEffect
|
#if ! JucePlugin_IsMidiEffect
|
||||||
@ -175,6 +183,17 @@ void Magical8bitPlug2AudioProcessor::setupVoice()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double Magical8bitPlug2AudioProcessor::getCurrentBPM()
|
||||||
|
{
|
||||||
|
auto ph = getPlayHead();
|
||||||
|
if (ph == NULL) {
|
||||||
|
return 120.0;
|
||||||
|
}
|
||||||
|
juce::AudioPlayHead::CurrentPositionInfo result;
|
||||||
|
ph->getCurrentPosition(result);
|
||||||
|
|
||||||
|
return result.bpm > 0 ? result.bpm : 120.0;
|
||||||
|
}
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
const String Magical8bitPlug2AudioProcessor::getName() const
|
const String Magical8bitPlug2AudioProcessor::getName() const
|
||||||
@ -281,67 +300,13 @@ bool Magical8bitPlug2AudioProcessor::isBusesLayoutSupported (const BusesLayout&
|
|||||||
|
|
||||||
void Magical8bitPlug2AudioProcessor::processBlock (AudioBuffer<float>& buffer, MidiBuffer& midiMessages)
|
void Magical8bitPlug2AudioProcessor::processBlock (AudioBuffer<float>& buffer, MidiBuffer& midiMessages)
|
||||||
{
|
{
|
||||||
|
synth.renderNextBlock (buffer, midiMessages, 0, buffer.getNumSamples());
|
||||||
synth.renderNextBlock (buffer, midiMessages, 0, buffer.getNumSamples()); // [5]
|
|
||||||
|
|
||||||
/*
|
|
||||||
buffer.clear();
|
|
||||||
MidiBuffer processedMidi;
|
|
||||||
int time;
|
|
||||||
MidiMessage m;
|
|
||||||
for (MidiBuffer::Iterator i (midiMessages); i.getNextEvent (m, time);)
|
|
||||||
{
|
|
||||||
if (m.isNoteOn())
|
|
||||||
{
|
|
||||||
uint8 newVel = (uint8)noteOnVel;
|
|
||||||
m = MidiMessage::noteOn(m.getChannel(), m.getNoteNumber(), newVel);
|
|
||||||
}
|
|
||||||
else if (m.isNoteOff())
|
|
||||||
{
|
|
||||||
}
|
|
||||||
else if (m.isAftertouch())
|
|
||||||
{
|
|
||||||
}
|
|
||||||
else if (m.isPitchWheel())
|
|
||||||
{
|
|
||||||
}
|
|
||||||
processedMidi.addEvent (m, time);
|
|
||||||
}
|
|
||||||
midiMessages.swapWith (processedMidi);
|
|
||||||
|
|
||||||
|
|
||||||
ScopedNoDenormals noDenormals;
|
|
||||||
auto totalNumInputChannels = getTotalNumInputChannels();
|
|
||||||
auto totalNumOutputChannels = getTotalNumOutputChannels();
|
|
||||||
|
|
||||||
// In case we have more outputs than inputs, this code clears any output
|
|
||||||
// channels that didn't contain input data, (because these aren't
|
|
||||||
// guaranteed to be empty - they may contain garbage).
|
|
||||||
// This is here to avoid people getting screaming feedback
|
|
||||||
// when they first compile a plugin, but obviously you don't need to keep
|
|
||||||
// this code if your algorithm always overwrites all the output channels.
|
|
||||||
for (auto i = totalNumInputChannels; i < totalNumOutputChannels; ++i)
|
|
||||||
buffer.clear (i, 0, buffer.getNumSamples());
|
|
||||||
|
|
||||||
// This is the place where you'd normally do the guts of your plugin's
|
|
||||||
// audio processing...
|
|
||||||
// Make sure to reset the state if your inner loop is processing
|
|
||||||
// the samples and the outer loop is handling the channels.
|
|
||||||
// Alternatively, you can process the samples with the channels
|
|
||||||
// interleaved by keeping the same state.
|
|
||||||
for (int channel = 0; channel < totalNumInputChannels; ++channel)
|
|
||||||
{
|
|
||||||
auto* channelData = buffer.getWritePointer (channel);
|
|
||||||
|
|
||||||
// ..do something to the data...
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
bool Magical8bitPlug2AudioProcessor::hasEditor() const
|
bool Magical8bitPlug2AudioProcessor::hasEditor() const
|
||||||
{
|
{
|
||||||
return true; // (change this to false if you choose to not supply an editor)
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioProcessorEditor* Magical8bitPlug2AudioProcessor::createEditor()
|
AudioProcessorEditor* Magical8bitPlug2AudioProcessor::createEditor()
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
#include "../JuceLibraryCode/JuceHeader.h"
|
#include "../JuceLibraryCode/JuceHeader.h"
|
||||||
#include "Settings.h"
|
#include "Settings.h"
|
||||||
#include "Voices.h"
|
#include "Voices.h"
|
||||||
|
#include "CustomSynth.h"
|
||||||
|
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
@ -71,13 +72,14 @@ public:
|
|||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
void setupVoice();
|
void setupVoice();
|
||||||
|
double getCurrentBPM();
|
||||||
|
|
||||||
AudioProcessorValueTreeState parameters;
|
AudioProcessorValueTreeState parameters;
|
||||||
SettingRefs settingRefs;
|
SettingRefs settingRefs;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
Synthesiser synth;
|
CustomSynth synth;
|
||||||
|
|
||||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Magical8bitPlug2AudioProcessor)
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Magical8bitPlug2AudioProcessor)
|
||||||
};
|
};
|
||||||
|
@ -35,6 +35,32 @@ struct PluginSettings
|
|||||||
double bendRange = 2;
|
double bendRange = 2;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//---------------------------------------------
|
||||||
|
//
|
||||||
|
// Monophonic Options
|
||||||
|
//
|
||||||
|
//---------------------------------------------
|
||||||
|
enum MonophonicBehavior
|
||||||
|
{
|
||||||
|
kLegato = 0,
|
||||||
|
kArpeggioUp,
|
||||||
|
kArpeggioDown,
|
||||||
|
kNonLegato,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ArpeggioIntervalType
|
||||||
|
{
|
||||||
|
k1frame = 0,
|
||||||
|
k2frames,
|
||||||
|
k3frames,
|
||||||
|
k96th,
|
||||||
|
k64th,
|
||||||
|
k48th,
|
||||||
|
k32nd,
|
||||||
|
k24th,
|
||||||
|
kSlider,
|
||||||
|
};
|
||||||
|
|
||||||
//---------------------------------------------
|
//---------------------------------------------
|
||||||
//
|
//
|
||||||
// Tone Specific
|
// Tone Specific
|
||||||
@ -95,10 +121,11 @@ struct SettingRefs
|
|||||||
float* decay = nullptr;
|
float* decay = nullptr;
|
||||||
float* suslevel = nullptr;
|
float* suslevel = nullptr;
|
||||||
float* release = nullptr;
|
float* release = nullptr;
|
||||||
// Arpeggio
|
// Monophonic
|
||||||
float* isArpeggioEnabled_raw = nullptr;
|
float* monophonicBehavior_raw = nullptr;
|
||||||
float* arpeggioTime = nullptr;
|
float* arpeggioIntervalType_raw = nullptr;
|
||||||
float* arpeggioDirection = nullptr;
|
float* arpeggioIntervalSliderValue = nullptr;
|
||||||
|
float* portamentoTime = nullptr;
|
||||||
// Bend
|
// Bend
|
||||||
float* bendRange = nullptr;
|
float* bendRange = nullptr;
|
||||||
// Vibrato
|
// Vibrato
|
||||||
@ -139,10 +166,10 @@ struct SettingRefs
|
|||||||
// accessors
|
// accessors
|
||||||
//
|
//
|
||||||
int oscillatorType() { return (int) (*osc); }
|
int oscillatorType() { return (int) (*osc); }
|
||||||
|
bool isMonophonic() { return (int)(*maxPoly) == 1; }
|
||||||
bool isAdvancedPanelOpen() { return *isAdvancedPanelOpen_raw > 0.5; }
|
bool isAdvancedPanelOpen() { return *isAdvancedPanelOpen_raw > 0.5; }
|
||||||
ColorSchemeType colorSchemeType() { return (ColorSchemeType) ((int) (*colorScheme)); }
|
ColorSchemeType colorSchemeType() { return (ColorSchemeType) ((int) (*colorScheme)); }
|
||||||
|
|
||||||
bool isArpeggioEnabled() { return *isArpeggioEnabled_raw > 0.5; }
|
|
||||||
NoiseAlgorithm noiseAlgorithm() { return (NoiseAlgorithm) ((int) (*noiseAlgorithm_raw)); }
|
NoiseAlgorithm noiseAlgorithm() { return (NoiseAlgorithm) ((int) (*noiseAlgorithm_raw)); }
|
||||||
|
|
||||||
bool vibratoIgnoresWheel() { return *vibratoIgnoresWheel_raw > 0.5; }
|
bool vibratoIgnoresWheel() { return *vibratoIgnoresWheel_raw > 0.5; }
|
||||||
@ -151,7 +178,8 @@ struct SettingRefs
|
|||||||
bool isPitchSequenceEnabled() { return *isPitchSequenceEnabled_raw > 0.5; }
|
bool isPitchSequenceEnabled() { return *isPitchSequenceEnabled_raw > 0.5; }
|
||||||
bool isDutySequenceEnabled() { return *isDutySequenceEnabled_raw > 0.5; }
|
bool isDutySequenceEnabled() { return *isDutySequenceEnabled_raw > 0.5; }
|
||||||
PitchSequenceMode pitchSequenceMode() { return (PitchSequenceMode) ((int) (*pitchSequenceMode_raw)); }
|
PitchSequenceMode pitchSequenceMode() { return (PitchSequenceMode) ((int) (*pitchSequenceMode_raw)); }
|
||||||
|
MonophonicBehavior monophonicBehavior() { return (MonophonicBehavior) ((int) (*monophonicBehavior_raw)); }
|
||||||
|
ArpeggioIntervalType apreggioIntervalType() { return (ArpeggioIntervalType) ((int) (*arpeggioIntervalType_raw)); }
|
||||||
|
|
||||||
//
|
//
|
||||||
// constructor
|
// constructor
|
||||||
@ -170,10 +198,11 @@ struct SettingRefs
|
|||||||
decay = (float*) parameters->getRawParameterValue ("decay");
|
decay = (float*) parameters->getRawParameterValue ("decay");
|
||||||
suslevel = (float*) parameters->getRawParameterValue ("suslevel");
|
suslevel = (float*) parameters->getRawParameterValue ("suslevel");
|
||||||
release = (float*) parameters->getRawParameterValue ("release");
|
release = (float*) parameters->getRawParameterValue ("release");
|
||||||
// Arpeggio
|
// Monophonic
|
||||||
isArpeggioEnabled_raw = (float*) parameters->getRawParameterValue ("isArpeggioEnabled_raw");
|
monophonicBehavior_raw = (float*) parameters->getRawParameterValue ("monophonicBehavior_raw");
|
||||||
arpeggioTime = (float*) parameters->getRawParameterValue ("arpeggioTime");
|
arpeggioIntervalType_raw = (float*) parameters->getRawParameterValue ("arpeggioIntervalType_raw");
|
||||||
arpeggioDirection = (float*) parameters->getRawParameterValue ("arpeggioDirection");
|
arpeggioIntervalSliderValue = (float*) parameters->getRawParameterValue ("arpeggioIntervalSliderValue");
|
||||||
|
portamentoTime = (float*) parameters->getRawParameterValue ("portamentoTime");
|
||||||
// Bend
|
// Bend
|
||||||
bendRange = (float*) parameters->getRawParameterValue ("bendRange");
|
bendRange = (float*) parameters->getRawParameterValue ("bendRange");
|
||||||
// Vibrato
|
// Vibrato
|
||||||
|
@ -30,6 +30,15 @@ void TonalVoice::startNote (int midiNoteNumber, float velocity, SynthesiserSound
|
|||||||
float time = * (settingRefs->sweepTime);
|
float time = * (settingRefs->sweepTime);
|
||||||
currentAutoBendAmount = iniPitch;
|
currentAutoBendAmount = iniPitch;
|
||||||
autoBendDelta = -1.0 * iniPitch / (time * getSampleRate());
|
autoBendDelta = -1.0 * iniPitch / (time * getSampleRate());
|
||||||
|
|
||||||
|
portamentoTime = 0;
|
||||||
|
currentArpeggioFrame = 0;
|
||||||
|
arpeggioFrameTimer = 0;
|
||||||
|
arpeggioFrameLength = 0;
|
||||||
|
currentNumNoteBuffer = 0;
|
||||||
|
for (int i=0; i<NUMNOTEBUFFER; i++) { noteBuffer[i] = 0; }
|
||||||
|
currentNumRetireBuffer = 0;
|
||||||
|
for (int i=0; i<NUMNOTEBUFFER; i++) { retireBuffer[i] = 0; }
|
||||||
}
|
}
|
||||||
|
|
||||||
void TonalVoice::advanceControlFrame()
|
void TonalVoice::advanceControlFrame()
|
||||||
@ -90,6 +99,108 @@ void TonalVoice::controllerMoved (int type, int amount)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void TonalVoice::setLegatoMode(double time, int midiCh) {
|
||||||
|
portamentoTime = time;
|
||||||
|
primaryMidiChannel = midiCh;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The interface says "add" but the implementation is just using the latest value.
|
||||||
|
// It is because the original intension was to keep all the pressing keys and choose the apropriate one with certain algorithm
|
||||||
|
void TonalVoice::addLegatoNote (int midiNoteNumber, float velocity) {
|
||||||
|
if (currentNumNoteBuffer >= NUMNOTEBUFFER) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int previousNoteNo = noteNumber;
|
||||||
|
BaseVoice::changeNote(midiNoteNumber, velocity);
|
||||||
|
|
||||||
|
// Portamento implementation is just applying auto bend
|
||||||
|
if (portamentoTime > 0) {
|
||||||
|
currentAutoBendAmount = (double)(previousNoteNo - midiNoteNumber);
|
||||||
|
autoBendDelta = -1.0 * currentAutoBendAmount / (portamentoTime * getSampleRate());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int TonalVoice::removeLegatoNote(int midiNoteNumber) {
|
||||||
|
if (midiNoteNumber == noteNumber) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void TonalVoice::setArpeggioMode(double interval, int midiCh)
|
||||||
|
{
|
||||||
|
arpeggioFrameLength = interval;
|
||||||
|
arpeggioFrameTimer = 0;
|
||||||
|
currentArpeggioFrame = 0;
|
||||||
|
currentNumNoteBuffer = 1;
|
||||||
|
noteBuffer[0] = noteNumber;
|
||||||
|
primaryMidiChannel = midiCh;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TonalVoice::addArpeggioNoteAscending(int midiNoteNumber)
|
||||||
|
{
|
||||||
|
if (currentNumNoteBuffer >= NUMNOTEBUFFER) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int i;
|
||||||
|
for (i = 0; i<currentNumNoteBuffer; i++) {
|
||||||
|
if (noteBuffer[i] > midiNoteNumber) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
pushNoteBuffer(i, midiNoteNumber);
|
||||||
|
|
||||||
|
currentNumNoteBuffer++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TonalVoice::addArpeggioNoteDescending(int midiNoteNumber)
|
||||||
|
{
|
||||||
|
if (currentNumNoteBuffer >= NUMNOTEBUFFER) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int i;
|
||||||
|
for (i = 0; i<currentNumNoteBuffer; i++) {
|
||||||
|
if (noteBuffer[i] < midiNoteNumber) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
pushNoteBuffer(i, midiNoteNumber);
|
||||||
|
|
||||||
|
currentNumNoteBuffer++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns number of remaining arpeggio notes
|
||||||
|
int TonalVoice::removeArpeggioNote(int midiNoteNumber)
|
||||||
|
{
|
||||||
|
if (currentNumNoteBuffer==0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Just push the note number to Retire Buffer
|
||||||
|
// Actual retirement happens in advanceFrameBuffer
|
||||||
|
retireBuffer[currentNumRetireBuffer] = midiNoteNumber;
|
||||||
|
currentNumRetireBuffer++;
|
||||||
|
|
||||||
|
// Observed like current number of notes already decreased
|
||||||
|
// so the Synthsizer can determine if it should enter Release phase
|
||||||
|
return currentNumNoteBuffer - currentNumRetireBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TonalVoice::pushNoteBuffer(int index, int value) {
|
||||||
|
for (int i=NUMNOTEBUFFER-1; i>index; i--) {
|
||||||
|
noteBuffer[i] = noteBuffer[i-1];
|
||||||
|
}
|
||||||
|
noteBuffer[index] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TonalVoice::shiftNoteBuffer(int index) {
|
||||||
|
for (int i=index; i<NUMNOTEBUFFER-1; i++) {
|
||||||
|
noteBuffer[i] = noteBuffer[i+1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
double TonalVoice::noteNoToHeltzDouble (double noteNoInDouble, const double frequencyOfA)
|
double TonalVoice::noteNoToHeltzDouble (double noteNoInDouble, const double frequencyOfA)
|
||||||
{
|
{
|
||||||
return frequencyOfA * std::pow (2.0, (noteNoInDouble - 69) / 12.0);
|
return frequencyOfA * std::pow (2.0, (noteNoInDouble - 69) / 12.0);
|
||||||
@ -133,4 +244,72 @@ void TonalVoice::onFrameAdvanced()
|
|||||||
autoBendDelta = 0;
|
autoBendDelta = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (arpeggioFrameLength > 0) { // Arpeggio mode is on
|
||||||
|
arpeggioFrameTimer += 1.0 / getSampleRate();
|
||||||
|
|
||||||
|
if (arpeggioFrameTimer >= arpeggioFrameLength)
|
||||||
|
{ // Arpeggio phase advances
|
||||||
|
if (!isInReleasePhase()) {
|
||||||
|
// Process the retirements first
|
||||||
|
for(int j = 0; j<currentNumRetireBuffer; j++) {
|
||||||
|
int target = retireBuffer[j];
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
// Find first match
|
||||||
|
for (i=0; i<currentNumNoteBuffer; i++) {
|
||||||
|
if (noteBuffer[i] == target) break;
|
||||||
|
}
|
||||||
|
if (i == currentNumNoteBuffer) { // not found
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
shiftNoteBuffer(i);
|
||||||
|
currentNumNoteBuffer--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear Retire Buffer
|
||||||
|
currentNumRetireBuffer = 0;
|
||||||
|
for (int i=0; i<NUMNOTEBUFFER; i++) { retireBuffer[i] = 0; }
|
||||||
|
} /* else {
|
||||||
|
Retirements should not happen in Release Phase
|
||||||
|
to keep the arpeggio playing during the release.
|
||||||
|
} */
|
||||||
|
|
||||||
|
if (noteBuffer[currentArpeggioFrame] == noteNumber || noteBuffer[currentArpeggioFrame] == 0) {
|
||||||
|
// Normally 'currentArpaggioFrame' will be updated every frame with
|
||||||
|
// 'noteBuffer[currentArpeggioFrame] == noteNumber' condition,
|
||||||
|
// but when the retirement happens currentArpeggioFrame may point at the slot already gone.
|
||||||
|
// to handle this situation the condition 'noteBuffer[currentArpeggioFrame] == 0' is added.
|
||||||
|
|
||||||
|
currentArpeggioFrame++;
|
||||||
|
|
||||||
|
if (currentArpeggioFrame >= currentNumNoteBuffer) {
|
||||||
|
currentArpeggioFrame = 0;
|
||||||
|
}
|
||||||
|
} /* else {
|
||||||
|
In cases like retirement happens, or new arpeggio note is added
|
||||||
|
the note at currentArpeggioFrame is no longer the same,
|
||||||
|
so currentArpeggioFrame doesn't have to be updated.
|
||||||
|
Moreover, updating currentArpeggioFrame in this case
|
||||||
|
often results in sounding the same note over two frames.
|
||||||
|
} */
|
||||||
|
|
||||||
|
noteNumber = noteBuffer[currentArpeggioFrame];
|
||||||
|
|
||||||
|
while (arpeggioFrameTimer >= arpeggioFrameLength)
|
||||||
|
{
|
||||||
|
arpeggioFrameTimer -= arpeggioFrameLength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bool TonalVoice::isInReleasePhase() {
|
||||||
|
if (settingRefs->isVolumeSequenceEnabled()) {
|
||||||
|
return settingRefs->volumeSequence.isInRelease(currentVolumeSequenceFrame);
|
||||||
|
} else {
|
||||||
|
return envelopePhase == kEnvelopePhaseR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -11,9 +11,10 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "BaseVoice.h"
|
#include "BaseVoice.h"
|
||||||
|
|
||||||
|
#define NUMNOTEBUFFER 10
|
||||||
|
|
||||||
struct TonalVoice : public BaseVoice // The base for Pulse and Triangle
|
struct TonalVoice : public BaseVoice // The base for Pulse and Triangle
|
||||||
{
|
{
|
||||||
|
|
||||||
TonalVoice (SettingRefs* sRefs);
|
TonalVoice (SettingRefs* sRefs);
|
||||||
float voltageForAngle (double angle) override = 0;
|
float voltageForAngle (double angle) override = 0;
|
||||||
|
|
||||||
@ -36,6 +37,21 @@ struct TonalVoice : public BaseVoice // The base for Pulse and Triangle
|
|||||||
// Custom Pitch/Note states
|
// Custom Pitch/Note states
|
||||||
int currentPitchSequenceFrame = 0;
|
int currentPitchSequenceFrame = 0;
|
||||||
|
|
||||||
|
// Legato/Arpeggio
|
||||||
|
int noteBuffer[NUMNOTEBUFFER];
|
||||||
|
int currentNumNoteBuffer = 0;
|
||||||
|
int primaryMidiChannel = 1;
|
||||||
|
|
||||||
|
// Legato
|
||||||
|
double portamentoTime = 0;
|
||||||
|
|
||||||
|
// Arpeggio
|
||||||
|
int currentArpeggioFrame = 0;
|
||||||
|
double arpeggioFrameTimer = 0;
|
||||||
|
double arpeggioFrameLength = 0; // Unit: seconds. Set non-zero value to enable arpeggio
|
||||||
|
int retireBuffer[NUMNOTEBUFFER];
|
||||||
|
int currentNumRetireBuffer = 0;
|
||||||
|
|
||||||
void startNote (int midiNoteNumber, float velocity,
|
void startNote (int midiNoteNumber, float velocity,
|
||||||
SynthesiserSound*, int currentPitchWheelPosition) override;
|
SynthesiserSound*, int currentPitchWheelPosition) override;
|
||||||
void advanceControlFrame() override;
|
void advanceControlFrame() override;
|
||||||
@ -43,7 +59,24 @@ struct TonalVoice : public BaseVoice // The base for Pulse and Triangle
|
|||||||
void pitchWheelMoved (int) override;
|
void pitchWheelMoved (int) override;
|
||||||
void controllerMoved (int, int) override;
|
void controllerMoved (int, int) override;
|
||||||
|
|
||||||
|
void setLegatoMode(double time, int midiCh);
|
||||||
|
void addLegatoNote (int midiNoteNumber, float velocity);
|
||||||
|
int removeLegatoNote(int midiNoteNumber);
|
||||||
|
|
||||||
|
void setArpeggioMode(double interval, int midiCh);
|
||||||
|
void addArpeggioNoteAscending(int midiNoteNumber);
|
||||||
|
void addArpeggioNoteDescending(int midiNoteNumber);
|
||||||
|
int removeArpeggioNote(int midiNoteNumber);
|
||||||
|
|
||||||
|
void pushNoteBuffer(int index, int value);
|
||||||
|
void shiftNoteBuffer(int index);
|
||||||
|
|
||||||
double noteNoToHeltzDouble (double noteNoInDouble, const double frequencyOfA = 440);
|
double noteNoToHeltzDouble (double noteNoInDouble, const double frequencyOfA = 440);
|
||||||
|
|
||||||
void onFrameAdvanced() override;
|
void onFrameAdvanced() override;
|
||||||
|
|
||||||
|
bool isArpeggioEnabled() {
|
||||||
|
return arpeggioFrameLength > 0;
|
||||||
|
}
|
||||||
|
bool isInReleasePhase();
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user