NB: No proprietary assets have been added from the forkpull/115/head
@@ -0,0 +1,268 @@ | |||
/* | |||
============================================================================== | |||
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... | |||
#include "../PluginEditor.h" | |||
//[/Headers] | |||
#include "PresetBar.h" | |||
//[MiscUserDefs] You can add your own user definitions and misc code here... | |||
//[/MiscUserDefs] | |||
//============================================================================== | |||
PresetBar::PresetBar (ObxdAudioProcessorEditor &gui) | |||
: editor(gui) | |||
{ | |||
//[Constructor_pre] You can add your own custom stuff here.. | |||
//[/Constructor_pre] | |||
presetNameLb.reset (new CustomLabel ("new label", | |||
TRANS("---\n"))); | |||
addAndMakeVisible (presetNameLb.get()); | |||
#ifdef JUCE_MAC | |||
presetNameLb->setFont (juce::Font ("Helvetica Neue", 16.00f, juce::Font::plain).withTypefaceStyle ("Bold")); | |||
#endif | |||
#ifdef JUCE_WINDOWS | |||
presetNameLb->setFont (juce::Font ("Arial", 16.00f, juce::Font::plain).withTypefaceStyle ("Bold")); | |||
#endif | |||
#ifdef JUCE_LINUX | |||
presetNameLb->setFont (juce::Font ("DejaVu Sans", 16.00f, juce::Font::plain).withTypefaceStyle ("Bold")); | |||
#endif | |||
presetNameLb->setJustificationType (juce::Justification::centred); | |||
presetNameLb->setEditable (false, false, false); | |||
presetNameLb->setColour (juce::TextEditor::textColourId, juce::Colours::black); | |||
presetNameLb->setColour (juce::TextEditor::backgroundColourId, juce::Colour (0x00000000)); | |||
presetNameLb->setBounds (24, 8, 368, 24); | |||
previousBtn.reset (new juce::ImageButton ("new button")); | |||
addAndMakeVisible (previousBtn.get()); | |||
previousBtn->setButtonText (juce::String()); | |||
previousBtn->addListener (this); | |||
previousBtn->setImages (false, true, true, | |||
juce::Image(), 1.000f, juce::Colour (0x00000000), | |||
juce::Image(), 1.000f, juce::Colour (0x00000000), | |||
juce::Image(), 1.000f, juce::Colour (0x00000000)); | |||
previousBtn->setBounds (407, 8, 20, 24); | |||
nextBtn.reset (new juce::ImageButton ("new button")); | |||
addAndMakeVisible (nextBtn.get()); | |||
nextBtn->setButtonText (juce::String()); | |||
nextBtn->addListener (this); | |||
nextBtn->setImages (false, true, true, | |||
juce::Image(), 1.000f, juce::Colour (0x00000000), | |||
juce::Image(), 1.000f, juce::Colour (0x00000000), | |||
juce::Image(), 1.000f, juce::Colour (0x00000000)); | |||
nextBtn->setBounds (435, 8, 20, 24); | |||
drawable1 = juce::Drawable::createFromImageData (presetnavigation_svg, presetnavigation_svgSize); | |||
//[UserPreSize] | |||
presetNameLb->leftClicked = [this](juce::Point<int> pos){ | |||
this->leftClicked(pos); | |||
}; | |||
//[/UserPreSize] | |||
setSize (471, 40); | |||
//[Constructor] You can add your own custom stuff here.. | |||
startTimer(50); | |||
//[/Constructor] | |||
} | |||
PresetBar::~PresetBar() | |||
{ | |||
//[Destructor_pre]. You can add your own custom destruction code here.. | |||
//[/Destructor_pre] | |||
presetNameLb = nullptr; | |||
previousBtn = nullptr; | |||
nextBtn = nullptr; | |||
drawable1 = nullptr; | |||
//[Destructor]. You can add your own custom destruction code here.. | |||
//[/Destructor] | |||
} | |||
//============================================================================== | |||
void PresetBar::paint (juce::Graphics& g) | |||
{ | |||
//[UserPrePaint] Add your own custom painting code here.. | |||
//[/UserPrePaint] | |||
g.fillAll (juce::Colours::black); | |||
{ | |||
int x = 0, y = 0, width = 471, height = 40; | |||
//[UserPaintCustomArguments] Customize the painting arguments here.. | |||
//[/UserPaintCustomArguments] | |||
g.setColour (juce::Colours::black); | |||
jassert (drawable1 != nullptr); | |||
if (drawable1 != nullptr) | |||
drawable1->drawWithin (g, juce::Rectangle<int> (x, y, width, height).toFloat(), | |||
juce::RectanglePlacement::centred, 1.000f); | |||
} | |||
//[UserPaint] Add your own custom painting code here.. | |||
//[/UserPaint] | |||
} | |||
void PresetBar::resized() | |||
{ | |||
//[UserPreResize] Add your own custom resize code here.. | |||
//[/UserPreResize] | |||
//[UserResized] Add your own custom resize handling here.. | |||
//[/UserResized] | |||
} | |||
void PresetBar::buttonClicked (juce::Button* buttonThatWasClicked) | |||
{ | |||
//[UserbuttonClicked_Pre] | |||
//[/UserbuttonClicked_Pre] | |||
if (buttonThatWasClicked == previousBtn.get()) | |||
{ | |||
//[UserButtonCode_previousBtn] -- add your button handler code here.. | |||
editor.prevProgram(); | |||
//update(); | |||
//[/UserButtonCode_previousBtn] | |||
} | |||
else if (buttonThatWasClicked == nextBtn.get()) | |||
{ | |||
//[UserButtonCode_nextBtn] -- add your button handler code here.. | |||
editor.nextProgram(); | |||
//update(); | |||
//[/UserButtonCode_nextBtn] | |||
} | |||
//[UserbuttonClicked_Post] | |||
//[/UserbuttonClicked_Post] | |||
} | |||
//[MiscUserCode] You can add your own definitions of your custom methods or any other code here... | |||
void PresetBar::timerCallback() { | |||
update(); | |||
} | |||
void PresetBar::update(){ | |||
presetNameLb->setText(editor.getCurrentProgramName(), NotificationType::dontSendNotification); | |||
} | |||
//[/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="PresetBar" componentName="" | |||
parentClasses="public juce::Component, public Timer" constructorParams="ObxdAudioProcessorEditor &gui" | |||
variableInitialisers="editor(gui)" snapPixels="8" snapActive="1" | |||
snapShown="1" overlayOpacity="0.330" fixedSize="1" initialWidth="471" | |||
initialHeight="40"> | |||
<BACKGROUND backgroundColour="ff000000"> | |||
<IMAGE pos="0 0 471 40" resource="presetnavigation_svg" opacity="1.0" | |||
mode="1"/> | |||
</BACKGROUND> | |||
<LABEL name="new label" id="3debca48aa2294c8" memberName="presetNameLb" | |||
virtualName="" explicitFocusOrder="0" pos="24 8 368 24" edTextCol="ff000000" | |||
edBkgCol="0" labelText="--- " editableSingleClick="0" editableDoubleClick="0" | |||
focusDiscardsChanges="0" fontname="Default font" fontsize="15.0" | |||
kerning="0.0" bold="0" italic="0" justification="36"/> | |||
<IMAGEBUTTON name="new button" id="ecc6403235eead2" memberName="previousBtn" | |||
virtualName="" explicitFocusOrder="0" pos="407 8 20 24" buttonText="" | |||
connectedEdges="0" needsCallback="1" radioGroupId="0" keepProportions="1" | |||
resourceNormal="" opacityNormal="1.0" colourNormal="0" resourceOver="" | |||
opacityOver="1.0" colourOver="0" resourceDown="" opacityDown="1.0" | |||
colourDown="0"/> | |||
<IMAGEBUTTON name="new button" id="fab648fbebd2d318" memberName="nextBtn" | |||
virtualName="" explicitFocusOrder="0" pos="435 8 20 24" buttonText="" | |||
connectedEdges="0" needsCallback="1" radioGroupId="0" keepProportions="1" | |||
resourceNormal="" opacityNormal="1.0" colourNormal="0" resourceOver="" | |||
opacityOver="1.0" colourOver="0" resourceDown="" opacityDown="1.0" | |||
colourDown="0"/> | |||
</JUCER_COMPONENT> | |||
END_JUCER_METADATA | |||
*/ | |||
#endif | |||
//============================================================================== | |||
// Binary resources - be careful not to edit any of these sections! | |||
// JUCER_RESOURCE: presetnavigation_svg, 2078, "../../../../GitHub/OB-Xd/Source/Images/presetnavigation.svg" | |||
static const unsigned char resource_PresetBar_presetnavigation_svg[] = { 60,63,120,109,108,32,118,101,114,115,105,111,110,61,34,49,46,48,34,32,101,110,99,111,100,105,110,103,61,34,85,84,70,45,56,34,32, | |||
115,116,97,110,100,97,108,111,110,101,61,34,110,111,34,63,62,10,60,33,68,79,67,84,89,80,69,32,115,118,103,32,80,85,66,76,73,67,32,34,45,47,47,87,51,67,47,47,68,84,68,32,83,86,71,32,49,46,49,47,47,69,78, | |||
34,32,34,104,116,116,112,58,47,47,119,119,119,46,119,51,46,111,114,103,47,71,114,97,112,104,105,99,115,47,83,86,71,47,49,46,49,47,68,84,68,47,115,118,103,49,49,46,100,116,100,34,62,10,60,115,118,103,32, | |||
119,105,100,116,104,61,34,49,48,48,37,34,32,104,101,105,103,104,116,61,34,49,48,48,37,34,32,118,105,101,119,66,111,120,61,34,48,32,48,32,52,55,49,32,51,57,34,32,118,101,114,115,105,111,110,61,34,49,46, | |||
49,34,32,120,109,108,110,115,61,34,104,116,116,112,58,47,47,119,119,119,46,119,51,46,111,114,103,47,50,48,48,48,47,115,118,103,34,32,120,109,108,110,115,58,120,108,105,110,107,61,34,104,116,116,112,58, | |||
47,47,119,119,119,46,119,51,46,111,114,103,47,49,57,57,57,47,120,108,105,110,107,34,32,120,109,108,58,115,112,97,99,101,61,34,112,114,101,115,101,114,118,101,34,32,120,109,108,110,115,58,115,101,114,105, | |||
102,61,34,104,116,116,112,58,47,47,119,119,119,46,115,101,114,105,102,46,99,111,109,47,34,32,115,116,121,108,101,61,34,102,105,108,108,45,114,117,108,101,58,101,118,101,110,111,100,100,59,99,108,105,112, | |||
45,114,117,108,101,58,101,118,101,110,111,100,100,59,115,116,114,111,107,101,45,108,105,110,101,99,97,112,58,114,111,117,110,100,59,115,116,114,111,107,101,45,108,105,110,101,106,111,105,110,58,114,111, | |||
117,110,100,59,115,116,114,111,107,101,45,109,105,116,101,114,108,105,109,105,116,58,49,46,53,59,34,62,10,32,32,32,32,60,114,101,99,116,32,120,61,34,48,34,32,121,61,34,48,34,32,119,105,100,116,104,61, | |||
34,52,55,49,34,32,104,101,105,103,104,116,61,34,51,57,34,47,62,10,32,32,32,32,60,112,97,116,104,32,100,61,34,77,51,57,56,44,57,46,50,48,53,67,51,57,56,44,55,46,52,51,54,32,51,57,54,46,53,54,52,44,54,32, | |||
51,57,52,46,55,57,53,44,54,76,50,52,46,50,48,53,44,54,67,50,50,46,52,51,54,44,54,32,50,49,44,55,46,52,51,54,32,50,49,44,57,46,50,48,53,76,50,49,44,50,57,46,55,57,53,67,50,49,44,51,49,46,53,54,52,32,50, | |||
50,46,52,51,54,44,51,51,32,50,52,46,50,48,53,44,51,51,76,51,57,52,46,55,57,53,44,51,51,67,51,57,54,46,53,54,52,44,51,51,32,51,57,56,44,51,49,46,53,54,52,32,51,57,56,44,50,57,46,55,57,53,76,51,57,56,44, | |||
57,46,50,48,53,90,34,32,115,116,121,108,101,61,34,115,116,114,111,107,101,58,114,103,98,40,49,50,49,44,49,50,49,44,49,50,49,41,59,115,116,114,111,107,101,45,119,105,100,116,104,58,49,112,120,59,34,47, | |||
62,10,32,32,32,32,60,103,32,116,114,97,110,115,102,111,114,109,61,34,109,97,116,114,105,120,40,49,44,48,44,48,44,49,44,52,46,50,50,50,54,54,44,49,49,46,48,50,52,53,41,34,62,10,32,32,32,32,32,32,32,32, | |||
60,103,32,116,114,97,110,115,102,111,114,109,61,34,109,97,116,114,105,120,40,50,52,44,48,44,48,44,50,52,44,52,48,54,46,54,50,49,44,49,55,46,50,54,52,53,41,34,62,10,32,32,32,32,32,32,32,32,32,32,32,32, | |||
60,112,97,116,104,32,100,61,34,77,48,46,52,50,53,44,45,48,46,49,53,54,67,48,46,52,51,53,44,45,48,46,49,52,55,32,48,46,52,51,57,44,45,48,46,49,51,53,32,48,46,52,51,57,44,45,48,46,49,50,50,67,48,46,52,51, | |||
57,44,45,48,46,49,48,57,32,48,46,52,51,53,44,45,48,46,48,57,55,32,48,46,52,50,53,44,45,48,46,48,56,56,67,48,46,52,49,53,44,45,48,46,48,55,56,32,48,46,52,48,52,44,45,48,46,48,55,51,32,48,46,51,57,49,44, | |||
45,48,46,48,55,51,67,48,46,51,55,55,44,45,48,46,48,55,51,32,48,46,51,54,54,44,45,48,46,48,55,56,32,48,46,51,53,54,44,45,48,46,48,56,55,76,48,46,49,49,50,44,45,48,46,51,51,50,67,48,46,49,48,50,44,45,48, | |||
46,51,52,49,32,48,46,48,57,56,44,45,48,46,51,53,51,32,48,46,48,57,56,44,45,48,46,51,54,54,67,48,46,48,57,56,44,45,48,46,51,56,32,48,46,49,48,51,44,45,48,46,51,57,49,32,48,46,49,49,50,44,45,48,46,52,48, | |||
49,76,48,46,51,53,54,44,45,48,46,54,52,53,67,48,46,51,54,54,44,45,48,46,54,53,52,32,48,46,51,55,55,44,45,48,46,54,53,57,32,48,46,51,57,49,44,45,48,46,54,53,57,67,48,46,52,48,52,44,45,48,46,54,53,57,32, | |||
48,46,52,49,53,44,45,48,46,54,53,52,32,48,46,52,50,53,44,45,48,46,54,52,53,67,48,46,52,51,53,44,45,48,46,54,51,53,32,48,46,52,51,57,44,45,48,46,54,50,52,32,48,46,52,51,57,44,45,48,46,54,49,67,48,46,52, | |||
51,57,44,45,48,46,53,57,55,32,48,46,52,51,53,44,45,48,46,53,56,53,32,48,46,52,50,53,44,45,48,46,53,55,54,76,48,46,50,49,53,44,45,48,46,51,54,54,76,48,46,52,50,53,44,45,48,46,49,53,54,90,34,32,115,116, | |||
121,108,101,61,34,102,105,108,108,58,114,103,98,40,49,50,49,44,49,50,49,44,49,50,49,41,59,102,105,108,108,45,114,117,108,101,58,110,111,110,122,101,114,111,59,34,47,62,10,32,32,32,32,32,32,32,32,60,47, | |||
103,62,10,32,32,32,32,60,47,103,62,10,32,32,32,32,60,103,32,116,114,97,110,115,102,111,114,109,61,34,109,97,116,114,105,120,40,49,44,48,44,48,44,49,44,51,49,46,50,50,50,55,44,49,49,46,48,50,52,53,41,34, | |||
62,10,32,32,32,32,32,32,32,32,60,103,32,116,114,97,110,115,102,111,114,109,61,34,109,97,116,114,105,120,40,50,52,44,48,44,48,44,50,52,44,52,48,54,46,54,50,49,44,49,55,46,50,54,52,53,41,34,62,10,32,32, | |||
32,32,32,32,32,32,32,32,32,32,60,112,97,116,104,32,100,61,34,77,48,46,51,50,50,44,45,48,46,51,54,54,76,48,46,49,49,50,44,45,48,46,53,55,54,67,48,46,49,48,51,44,45,48,46,53,56,53,32,48,46,48,57,56,44,45, | |||
48,46,53,57,55,32,48,46,48,57,56,44,45,48,46,54,49,67,48,46,48,57,56,44,45,48,46,54,50,52,32,48,46,49,48,50,44,45,48,46,54,51,53,32,48,46,49,49,50,44,45,48,46,54,52,53,67,48,46,49,50,50,44,45,48,46,54, | |||
53,52,32,48,46,49,51,51,44,45,48,46,54,53,57,32,48,46,49,52,54,44,45,48,46,54,53,57,67,48,46,49,54,44,45,48,46,54,53,57,32,48,46,49,55,49,44,45,48,46,54,53,52,32,48,46,49,56,49,44,45,48,46,54,52,53,76, | |||
48,46,52,50,53,44,45,48,46,52,48,49,67,48,46,52,51,53,44,45,48,46,51,57,49,32,48,46,52,51,57,44,45,48,46,51,56,32,48,46,52,51,57,44,45,48,46,51,54,54,67,48,46,52,51,57,44,45,48,46,51,53,51,32,48,46,52, | |||
51,53,44,45,48,46,51,52,49,32,48,46,52,50,53,44,45,48,46,51,51,50,76,48,46,49,56,49,44,45,48,46,48,56,55,67,48,46,49,55,49,44,45,48,46,48,55,56,32,48,46,49,54,44,45,48,46,48,55,51,32,48,46,49,52,54,44, | |||
45,48,46,48,55,51,67,48,46,49,51,51,44,45,48,46,48,55,51,32,48,46,49,50,50,44,45,48,46,48,55,56,32,48,46,49,49,50,44,45,48,46,48,56,56,67,48,46,49,48,50,44,45,48,46,48,57,55,32,48,46,48,57,56,44,45,48, | |||
46,49,48,57,32,48,46,48,57,56,44,45,48,46,49,50,50,67,48,46,48,57,56,44,45,48,46,49,51,53,32,48,46,49,48,51,44,45,48,46,49,52,55,32,48,46,49,49,50,44,45,48,46,49,53,54,76,48,46,51,50,50,44,45,48,46,51, | |||
54,54,90,34,32,115,116,121,108,101,61,34,102,105,108,108,58,114,103,98,40,49,50,49,44,49,50,49,44,49,50,49,41,59,102,105,108,108,45,114,117,108,101,58,110,111,110,122,101,114,111,59,34,47,62,10,32,32, | |||
32,32,32,32,32,32,60,47,103,62,10,32,32,32,32,60,47,103,62,10,60,47,115,118,103,62,10,0,0}; | |||
const char* PresetBar::presetnavigation_svg = (const char*) resource_PresetBar_presetnavigation_svg; | |||
const int PresetBar::presetnavigation_svgSize = 2078; | |||
//[EndFile] You can add extra defines here... | |||
//[/EndFile] | |||
@@ -0,0 +1,95 @@ | |||
/* | |||
============================================================================== | |||
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> | |||
class ObxdAudioProcessorEditor; | |||
class CustomLabel: public juce::Label { | |||
public: | |||
CustomLabel (const String& componentName = String(), | |||
const String& labelText = String()): juce::Label(componentName, labelText) { | |||
} | |||
std::function<void(juce::Point<int> pos)> leftClicked; | |||
void mouseDown (const juce::MouseEvent& event) override { | |||
if (this->getBounds().contains(event.getMouseDownPosition()) && event.mods.isLeftButtonDown()){ | |||
leftClicked(event.getScreenPosition()); | |||
} | |||
}; | |||
}; | |||
//[/Headers] | |||
//============================================================================== | |||
/** | |||
//[Comments] | |||
An auto-generated component, created by the Projucer. | |||
Describe your class and how it works here! | |||
//[/Comments] | |||
*/ | |||
class PresetBar : public juce::Component, | |||
public Timer, | |||
public juce::Button::Listener | |||
{ | |||
public: | |||
//============================================================================== | |||
PresetBar (ObxdAudioProcessorEditor &gui); | |||
~PresetBar() override; | |||
//============================================================================== | |||
//[UserMethods] -- You can add your own custom methods in this section. | |||
void timerCallback() override; | |||
void update(); | |||
std::function<void(juce::Point<int> &pos)> leftClicked; | |||
//[/UserMethods] | |||
void paint (juce::Graphics& g) override; | |||
void resized() override; | |||
void buttonClicked (juce::Button* buttonThatWasClicked) override; | |||
// Binary resources: | |||
static const char* presetnavigation_svg; | |||
static const int presetnavigation_svgSize; | |||
private: | |||
//[UserVariables] -- You can add your own custom variables in this section. | |||
ObxdAudioProcessorEditor &editor; | |||
//[/UserVariables] | |||
//============================================================================== | |||
std::unique_ptr<CustomLabel> presetNameLb; | |||
std::unique_ptr<juce::ImageButton> previousBtn; | |||
std::unique_ptr<juce::ImageButton> nextBtn; | |||
std::unique_ptr<juce::Drawable> drawable1; | |||
//============================================================================== | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PresetBar) | |||
}; | |||
//[EndFile] You can add extra defines here... | |||
//[/EndFile] | |||
@@ -0,0 +1,138 @@ | |||
/* | |||
============================================================================== | |||
ScaleComponent.cpp | |||
Created: 27 Aug 2021 1:26:08pm | |||
Author: discoDSP | |||
============================================================================== | |||
*/ | |||
#include "../PluginProcessor.h" | |||
#include "ScaleComponent.h" | |||
//============================================================================== | |||
ScalableComponent::ScalableComponent(ObxdAudioProcessor* owner_) | |||
: scaleFactor(0.0f), | |||
isHighResolutionDisplay(false), | |||
processor(owner_) | |||
{ | |||
setScaleFactor(1.0f, false); | |||
} | |||
ScalableComponent::~ScalableComponent() | |||
{ | |||
} | |||
void ScalableComponent::setScaleFactor(float newScaleFactor, bool newIsHighResolutionDisplay) | |||
{ | |||
// until we get a freely scalable editor !!! | |||
jassert(newScaleFactor == 1.0f || newScaleFactor == 1.5f || newScaleFactor == 2.0f); | |||
if (scaleFactor != newScaleFactor || isHighResolutionDisplay != newIsHighResolutionDisplay) | |||
{ | |||
scaleFactor = newScaleFactor; | |||
isHighResolutionDisplay = newIsHighResolutionDisplay; | |||
scaleFactorChanged(); | |||
} | |||
} | |||
float ScalableComponent::getScaleImage(){ | |||
float scale = 1.0; | |||
if (!isHighResolutionDisplay) | |||
{ | |||
if (getScaleFactor() == 1.5f) | |||
{ | |||
scale= 0.75f; | |||
} | |||
else if (getScaleFactor() == 2.0f) | |||
{ | |||
scale= 0.5f; | |||
} | |||
} else { | |||
if (getScaleFactor() == 1.0f) //2x image | |||
{ | |||
scale= 0.5f; | |||
} | |||
else if (getScaleFactor() == 1.5f) //4x images =>150% | |||
{ | |||
scale= (0.25f + 0.125f); | |||
} | |||
else if (getScaleFactor() == 2.0f) //4x images =>200x | |||
{ | |||
scale= 0.5f; | |||
} | |||
} | |||
return scale; | |||
}; | |||
float ScalableComponent::getScaleFactor() const | |||
{ | |||
return scaleFactor; | |||
} | |||
bool ScalableComponent::getIsHighResolutionDisplay() const | |||
{ | |||
return isHighResolutionDisplay; | |||
} | |||
int ScalableComponent::getScaleInt(){ | |||
int scaleFactorInt = 1; | |||
if (scaleFactor == 1.5f) | |||
scaleFactorInt = 2; | |||
if (scaleFactor == 2.0f) | |||
scaleFactorInt = 4; | |||
if (isHighResolutionDisplay){ | |||
scaleFactorInt *= 2; | |||
} | |||
if (scaleFactorInt> 4){ | |||
scaleFactorInt=4; | |||
} | |||
return scaleFactorInt; | |||
} | |||
Image ScalableComponent::getScaledImageFromCache(const String& imageName, | |||
float scaleFactor, | |||
bool isHighResolutionDisplay) | |||
{ | |||
jassert(scaleFactor == 1.0f || scaleFactor == 1.5f || scaleFactor == 2.0f); | |||
this->isHighResolutionDisplay = isHighResolutionDisplay; | |||
int scaleFactorInt = getScaleInt(); | |||
String resourceName = imageName + "_png"; | |||
if (scaleFactorInt != 1){ | |||
resourceName = imageName + String::formatted("%dx_png", scaleFactorInt); | |||
} | |||
int size = 0; | |||
File skin; | |||
if (processor){ | |||
File f(processor->getCurrentSkinFolder()); | |||
if (f.isDirectory()){ | |||
skin=f; | |||
} | |||
} | |||
const char* data = nullptr; | |||
String image_file = imageName; | |||
if (scaleFactorInt ==1) | |||
image_file += ".png"; | |||
else | |||
image_file += String::formatted("@%dx.png", scaleFactorInt); | |||
DBG(" Loaf image: " << image_file); | |||
File file = skin.getChildFile(image_file); | |||
if (file.exists()){ | |||
return ImageCache::getFromFile(file); | |||
} else { | |||
data = BinaryData::getNamedResource((const char*)resourceName.toUTF8(), size); | |||
DBG(" Image: " << resourceName); | |||
return ImageCache::getFromMemory(data, size); | |||
} | |||
} | |||
void ScalableComponent::scaleFactorChanged() | |||
{ | |||
} |
@@ -0,0 +1,86 @@ | |||
/* | |||
============================================================================== | |||
ScaleComponent.h | |||
Created: 27 Aug 2021 1:26:08pm | |||
Author: discoDSP | |||
============================================================================== | |||
*/ | |||
#pragma once | |||
//============================================================================== | |||
#include <JuceHeader.h> | |||
class ObxdAudioProcessor; | |||
//============================================================================== | |||
class ScalableComponent | |||
{ | |||
public: | |||
~ScalableComponent(); | |||
int getScaleInt(); | |||
void setScaleFactor(float newScaleFactor, bool newIsHighResolutionDisplay); | |||
float getScaleImage(); | |||
float getScaleFactor() const; | |||
bool getIsHighResolutionDisplay() const; | |||
virtual void scaleFactorChanged(); | |||
protected: | |||
ScalableComponent(ObxdAudioProcessor* owner_); | |||
Image getScaledImageFromCache(const String& imageName, float scaleFactor, bool isHighResolutionDisplay); | |||
private: | |||
ObxdAudioProcessor* processor; | |||
float scaleFactor; | |||
bool isHighResolutionDisplay; | |||
}; | |||
//============================================================================== | |||
class CustomLookAndFeel : public LookAndFeel_V4, | |||
public ScalableComponent | |||
{ | |||
public: | |||
CustomLookAndFeel(ObxdAudioProcessor* owner_):LookAndFeel_V4(), ScalableComponent(owner_) { | |||
this->setColour(PopupMenu::backgroundColourId, Colour(20, 20, 20)); | |||
this->setColour(PopupMenu::textColourId, Colour(245, 245, 245)); | |||
this->setColour(PopupMenu::highlightedBackgroundColourId, Colour(60, 60, 60)); | |||
this->setColour(Label::textColourId, Colour(245, 245, 245)); | |||
}; | |||
PopupMenu::Options getOptionsForComboBoxPopupMenu (ComboBox& box, Label& label) override | |||
{ | |||
PopupMenu::Options option = LookAndFeel_V4::getOptionsForComboBoxPopupMenu(box, label); | |||
return option.withStandardItemHeight (label.getHeight()/ getScaleFactor()); | |||
}; | |||
Font getPopupMenuFont () override | |||
{ | |||
float scaleFactor = getScaleFactor(); | |||
DBG("getPopupMenuFont::scaleFactor " << scaleFactor); | |||
if (scaleFactor > 1.0) scaleFactor *= 0.85; | |||
#ifdef JUCE_MAC | |||
return Font("Helvetica Neue", 18.0 * scaleFactor, Font::plain); | |||
#endif | |||
#ifdef JUCE_WINDOWS | |||
return Font("Arial", 18.0 * scaleFactor, Font::plain); | |||
#endif | |||
#ifdef JUCE_LINUX | |||
return Font("DejaVu Sans", 18.0 * scaleFactor, Font::plain); | |||
#endif | |||
} | |||
}; |
@@ -0,0 +1,206 @@ | |||
/* | |||
============================================================================== | |||
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 "SetPresetNameWindow.h" | |||
//[MiscUserDefs] You can add your own user definitions and misc code here... | |||
//[/MiscUserDefs] | |||
//============================================================================== | |||
SetPresetNameWindow::SetPresetNameWindow () | |||
{ | |||
//[Constructor_pre] You can add your own custom stuff here.. | |||
//[/Constructor_pre] | |||
nameTextEditor.reset (new juce::TextEditor ("nameTextEditor")); | |||
addAndMakeVisible (nameTextEditor.get()); | |||
nameTextEditor->setMultiLine (false); | |||
nameTextEditor->setReturnKeyStartsNewLine (false); | |||
nameTextEditor->setReadOnly (false); | |||
nameTextEditor->setScrollbarsShown (true); | |||
nameTextEditor->setCaretVisible (true); | |||
nameTextEditor->setPopupMenuEnabled (false); | |||
nameTextEditor->setColour (juce::TextEditor::backgroundColourId, juce::Colours::black); | |||
nameTextEditor->setColour (juce::CaretComponent::caretColourId, juce::Colours::white); | |||
nameTextEditor->setText (juce::String()); | |||
cancel.reset (new juce::TextButton ("cancel")); | |||
addAndMakeVisible (cancel.get()); | |||
cancel->setButtonText (TRANS("Cancel")); | |||
cancel->addListener (this); | |||
cancel->setColour (juce::TextButton::buttonColourId, juce::Colours::black); | |||
Ok.reset (new juce::TextButton ("Ok")); | |||
addAndMakeVisible (Ok.get()); | |||
Ok->setButtonText (TRANS("OK")); | |||
Ok->addListener (this); | |||
Ok->setColour (juce::TextButton::buttonColourId, juce::Colours::black); | |||
//[UserPreSize] | |||
cancel->setColour (juce::ComboBox::ColourIds::outlineColourId, juce::Colours::white); | |||
Ok->setColour (juce::ComboBox::ColourIds::outlineColourId, juce::Colours::white); | |||
//[/UserPreSize] | |||
setSize (300, 150); | |||
//[Constructor] You can add your own custom stuff here.. | |||
//[/Constructor] | |||
} | |||
SetPresetNameWindow::~SetPresetNameWindow() | |||
{ | |||
//[Destructor_pre]. You can add your own custom destruction code here.. | |||
//[/Destructor_pre] | |||
nameTextEditor = nullptr; | |||
cancel = nullptr; | |||
Ok = nullptr; | |||
//[Destructor]. You can add your own custom destruction code here.. | |||
//[/Destructor] | |||
} | |||
//============================================================================== | |||
void SetPresetNameWindow::paint (juce::Graphics& g) | |||
{ | |||
//[UserPrePaint] Add your own custom painting code here.. | |||
//[/UserPrePaint] | |||
g.fillAll (juce::Colours::black); | |||
{ | |||
int x = 0, y = proportionOfHeight (0.0000f), width = proportionOfWidth (1.0000f), height = proportionOfHeight (1.0000f); | |||
juce::Colour fillColour = juce::Colours::black; | |||
juce::Colour strokeColour = juce::Colour (0xff666666); | |||
//[UserPaintCustomArguments] Customize the painting arguments here.. | |||
//[/UserPaintCustomArguments] | |||
g.setColour (fillColour); | |||
g.fillRect (x, y, width, height); | |||
g.setColour (strokeColour); | |||
g.drawRect (x, y, width, height, 1); | |||
} | |||
{ | |||
int x = proportionOfWidth (0.0000f), y = proportionOfHeight (0.1000f), width = proportionOfWidth (1.0000f), height = proportionOfHeight (0.2000f); | |||
juce::String text (TRANS("Preset Name")); | |||
juce::Colour fillColour = juce::Colours::white; | |||
//[UserPaintCustomArguments] Customize the painting arguments here.. | |||
//[/UserPaintCustomArguments] | |||
g.setColour (fillColour); | |||
g.setFont (juce::Font (15.00f, juce::Font::plain).withTypefaceStyle ("Regular")); | |||
g.drawText (text, x, y, width, height, | |||
juce::Justification::centred, true); | |||
} | |||
//[UserPaint] Add your own custom painting code here.. | |||
//[/UserPaint] | |||
} | |||
void SetPresetNameWindow::resized() | |||
{ | |||
//[UserPreResize] Add your own custom resize code here.. | |||
//[/UserPreResize] | |||
nameTextEditor->setBounds (proportionOfWidth (0.1500f), proportionOfHeight (0.3467f), proportionOfWidth (0.7000f), proportionOfHeight (0.1733f)); | |||
cancel->setBounds (proportionOfWidth (0.2000f), proportionOfHeight (0.7000f), proportionOfWidth (0.2500f), proportionOfHeight (0.1600f)); | |||
Ok->setBounds (proportionOfWidth (0.5500f), proportionOfHeight (0.7000f), proportionOfWidth (0.2500f), proportionOfHeight (0.1600f)); | |||
//[UserResized] Add your own custom resize handling here.. | |||
//[/UserResized] | |||
} | |||
void SetPresetNameWindow::buttonClicked (juce::Button* buttonThatWasClicked) | |||
{ | |||
//[UserbuttonClicked_Pre] | |||
//[/UserbuttonClicked_Pre] | |||
if (buttonThatWasClicked == cancel.get()) | |||
{ | |||
//[UserButtonCode_cancel] -- add your button handler code here.. | |||
callback(0, nameTextEditor->getText()); | |||
//[/UserButtonCode_cancel] | |||
} | |||
else if (buttonThatWasClicked == Ok.get()) | |||
{ | |||
//[UserButtonCode_Ok] -- add your button handler code here.. | |||
callback(1, nameTextEditor->getText()); | |||
//[/UserButtonCode_Ok] | |||
} | |||
//[UserbuttonClicked_Post] | |||
//[/UserbuttonClicked_Post] | |||
} | |||
//[MiscUserCode] You can add your own definitions of your custom methods or any other code here... | |||
void SetPresetNameWindow::grabTextEditorFocus() | |||
{ | |||
nameTextEditor->grabKeyboardFocus(); | |||
}; | |||
//[/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="SetPresetNameWindow" componentName="" | |||
parentClasses="public juce::Component" constructorParams="" variableInitialisers="" | |||
snapPixels="8" snapActive="1" snapShown="1" overlayOpacity="0.330" | |||
fixedSize="1" initialWidth="300" initialHeight="150"> | |||
<BACKGROUND backgroundColour="ff000000"> | |||
<RECT pos="0 0% 100% 100%" fill="solid: ff000000" hasStroke="1" stroke="1, mitered, butt" | |||
strokeColour="solid: ff666666"/> | |||
<TEXT pos="0% 10% 100% 20%" fill="solid: ffffffff" hasStroke="0" text="Preset Name" | |||
fontname="Default font" fontsize="15.0" kerning="0.0" bold="0" | |||
italic="0" justification="36"/> | |||
</BACKGROUND> | |||
<TEXTEDITOR name="nameTextEditor" id="13e287a1045d7d6d" memberName="nameTextEditor" | |||
virtualName="" explicitFocusOrder="0" pos="15% 34.667% 70% 17.333%" | |||
bkgcol="ff000000" caretcol="ffffffff" initialText="" multiline="0" | |||
retKeyStartsLine="0" readonly="0" scrollbars="1" caret="1" popupmenu="0"/> | |||
<TEXTBUTTON name="cancel" id="873979f2630a3992" memberName="cancel" virtualName="" | |||
explicitFocusOrder="0" pos="20% 70% 25% 16%" bgColOff="ff000000" | |||
buttonText="Cancel" connectedEdges="0" needsCallback="1" radioGroupId="0"/> | |||
<TEXTBUTTON name="Ok" id="2874357d53dac91e" memberName="Ok" virtualName="" | |||
explicitFocusOrder="0" pos="55% 70% 25% 16%" bgColOff="ff000000" | |||
buttonText="OK" connectedEdges="0" needsCallback="1" radioGroupId="0"/> | |||
</JUCER_COMPONENT> | |||
END_JUCER_METADATA | |||
*/ | |||
#endif | |||
//[EndFile] You can add extra defines here... | |||
//[/EndFile] | |||
@@ -0,0 +1,75 @@ | |||
/* | |||
============================================================================== | |||
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> | |||
//[/Headers] | |||
//============================================================================== | |||
/** | |||
//[Comments] | |||
An auto-generated component, created by the Projucer. | |||
Describe your class and how it works here! | |||
//[/Comments] | |||
*/ | |||
class SetPresetNameWindow : public juce::Component, | |||
public juce::Button::Listener | |||
{ | |||
public: | |||
//============================================================================== | |||
SetPresetNameWindow (); | |||
~SetPresetNameWindow() override; | |||
//============================================================================== | |||
//[UserMethods] -- You can add your own custom methods in this section. | |||
std::function<void(int, juce::String)> callback; | |||
void setText(const String &txt){ | |||
nameTextEditor->setText(txt); | |||
} | |||
void grabTextEditorFocus(); | |||
//[/UserMethods] | |||
void paint (juce::Graphics& g) override; | |||
void resized() override; | |||
void buttonClicked (juce::Button* buttonThatWasClicked) override; | |||
private: | |||
//[UserVariables] -- You can add your own custom variables in this section. | |||
//[/UserVariables] | |||
//============================================================================== | |||
std::unique_ptr<juce::TextEditor> nameTextEditor; | |||
std::unique_ptr<juce::TextButton> cancel; | |||
std::unique_ptr<juce::TextButton> Ok; | |||
//============================================================================== | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SetPresetNameWindow) | |||
}; | |||
//[EndFile] You can add extra defines here... | |||
//[/EndFile] | |||
@@ -26,6 +26,7 @@ | |||
#include "VoiceQueue.h" | |||
#include "SynthEngine.h" | |||
#include "Lfo.h" | |||
#include "Tuning.h" | |||
class Motherboard | |||
{ | |||
@@ -42,14 +43,16 @@ private: | |||
float sampleRate,sampleRateInv; | |||
//JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Motherboard) | |||
public: | |||
Tuning tuning; | |||
bool asPlayedMode; | |||
Lfo mlfo,vibratoLfo; | |||
float vibratoAmount; | |||
bool vibratoEnabled; | |||
float Volume; | |||
const static int MAX_VOICES=8; | |||
float pannings[MAX_VOICES]; | |||
const static int MAX_VOICES = 32; | |||
const static int MAX_PANNINGS = 8; | |||
float pannings[MAX_PANNINGS]; | |||
ObxdVoice voices[MAX_VOICES]; | |||
bool uni; | |||
bool Oversample; | |||
@@ -79,7 +82,7 @@ public: | |||
// pannings = new float[MAX_VOICES]; | |||
totalvc = MAX_VOICES; | |||
vq = VoiceQueue(MAX_VOICES,voices); | |||
for(int i = 0 ; i < MAX_VOICES;++i) | |||
for(int i = 0 ; i < MAX_PANNINGS;++i) | |||
{ | |||
pannings[i]= 0.5; | |||
} | |||
@@ -342,13 +345,14 @@ public: | |||
} | |||
void processSample(float* sm1,float* sm2) | |||
{ | |||
tuning.updateMTSESPStatus(); | |||
mlfo.update(); | |||
vibratoLfo.update(); | |||
float vl=0,vr=0; | |||
float vlo = 0 , vro = 0 ; | |||
float lfovalue = mlfo.getVal(); | |||
float viblfo = vibratoEnabled?(vibratoLfo.getVal() * vibratoAmount):0; | |||
float lfovalue2=0,viblfo2=0; | |||
float lfovalue2=0,viblfo2; | |||
if(Oversample) | |||
{ | |||
mlfo.update(); | |||
@@ -359,15 +363,16 @@ public: | |||
for(int i = 0 ; i < totalvc;i++) | |||
{ | |||
voices[i].initTuning(&tuning); | |||
float x1 = processSynthVoice(voices[i],lfovalue,viblfo); | |||
if(Oversample) | |||
{ | |||
float x2 = processSynthVoice(voices[i],lfovalue2,viblfo2); | |||
vlo+=x2*(1-pannings[i]); | |||
vro+=x2*(pannings[i]); | |||
vlo+=x2*(1-pannings[i % MAX_PANNINGS]); | |||
vro+=x2*(pannings[i % MAX_PANNINGS]); | |||
} | |||
vl+=x1*(1-pannings[i]); | |||
vr+=x1*(pannings[i]); | |||
vl+=x1*(1-pannings[i % MAX_PANNINGS]); | |||
vr+=x1*(pannings[i % MAX_PANNINGS]); | |||
} | |||
if(Oversample) | |||
{ | |||
@@ -27,6 +27,7 @@ | |||
#include "Filter.h" | |||
#include "Decimator.h" | |||
#include "APInterpolator.h" | |||
#include "Tuning.h" | |||
class ObxdVoice | |||
{ | |||
@@ -41,6 +42,8 @@ private: | |||
float c1,c2; | |||
bool hq; | |||
Tuning* tuning; | |||
//JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ObxdVoice) | |||
public: | |||
@@ -168,11 +171,17 @@ public: | |||
// delete lenvd; | |||
// delete fenvd; | |||
} | |||
void initTuning(Tuning* t) | |||
{ | |||
tuning = t; | |||
} | |||
inline float ProcessSample() | |||
{ | |||
double tunedMidiNote = tuning->tunedMidiNote(midiIndx); | |||
//portamento on osc input voltage | |||
//implements rc circuit | |||
float ptNote =tptlpupw(prtst, midiIndx-81, porta * (1+PortaDetune*PortaDetuneAmt),sampleRateInv); | |||
float ptNote = tptlpupw(prtst, tunedMidiNote - 81, porta * (1+PortaDetune*PortaDetuneAmt),sampleRateInv); | |||
osc.notePlaying = ptNote; | |||
//both envelopes and filter cv need a delay equal to osc internal delay | |||
float lfoDelayed = lfod.feedReturn(lfoIn); | |||
@@ -41,7 +41,7 @@ public: | |||
{ | |||
values[k] = 0.0f; | |||
} | |||
values[VOICE_COUNT] = 1.0f; | |||
values[VOICE_COUNT] = 0.2f; | |||
values[BRIGHTNESS]=1.0f; | |||
values[OCTAVE]=0.5; | |||
values[TUNE] = 0.5f; | |||
@@ -26,7 +26,7 @@ | |||
enum ObxdParameters | |||
{ | |||
UNDEFINED, | |||
UNUSED_1, | |||
MIDILEARN, | |||
VOLUME, | |||
VOICE_COUNT, | |||
TUNE, | |||
@@ -80,7 +80,7 @@ enum ObxdParameters | |||
FREL, | |||
ENVDER,FILTERDER,PORTADER, | |||
PAN1,PAN2,PAN3,PAN4,PAN5,PAN6,PAN7,PAN8, | |||
UNUSED_2, | |||
UNLEARN, | |||
ECONOMY_MODE, | |||
LFO_SYNC, | |||
PW_ENV, | |||
@@ -166,7 +166,7 @@ public: | |||
} | |||
void setVoiceCount(float param) | |||
{ | |||
synth.setVoiceCount(roundToInt((param*7) +1)); | |||
synth.setVoiceCount(roundToInt((param*(synth.MAX_VOICES-1)) +1)); | |||
} | |||
void procPitchWheelAmount(float param) | |||
{ | |||
@@ -184,7 +184,7 @@ public: | |||
} | |||
void processPan(float param,int idx) | |||
{ | |||
synth.pannings[idx-1] = param; | |||
synth.pannings[(idx-1) % synth.MAX_PANNINGS] = param; | |||
} | |||
void processTune(float param) | |||
{ | |||
@@ -0,0 +1,80 @@ | |||
// | |||
// Tuning.h | |||
// OB-Xd | |||
// | |||
// Created by Natalia Escalera on 12/28/21. | |||
// Copyright © 2021 discoDSP. All rights reserved. | |||
// | |||
#include "../MTS/libMTSClient.h" | |||
#pragma once | |||
class Tuning | |||
{ | |||
private: | |||
MTSClient* mts_client{nullptr}; | |||
enum Mode { | |||
MTS_ESP, | |||
TWELVE_TET | |||
} mode{TWELVE_TET}; | |||
public: | |||
Tuning() | |||
{ | |||
} | |||
~Tuning() | |||
{ | |||
if (mts_client != nullptr) | |||
{ | |||
MTS_DeregisterClient(mts_client); | |||
mts_client = nullptr; | |||
} | |||
} | |||
void updateMTSESPStatus() | |||
{ | |||
if (mts_client == nullptr) | |||
{ | |||
mts_client = MTS_RegisterClient(); | |||
} | |||
mode = hasMTSMaster() ? MTS_ESP : TWELVE_TET; | |||
} | |||
double midiNoteFromMTS(int midiIndex) | |||
{ | |||
return midiIndex + MTS_RetuningInSemitones(mts_client, midiIndex, -1); | |||
} | |||
double tunedMidiNote(int midiIndex) | |||
{ | |||
switch(mode) | |||
{ | |||
case TWELVE_TET: | |||
return midiIndex; | |||
break; | |||
case MTS_ESP: | |||
return midiNoteFromMTS(midiIndex); | |||
break; | |||
} | |||
return midiIndex; | |||
} | |||
/* | |||
These methods can be later be used for implementing other steps in the MTS-ESP guide: | |||
https://github.com/ODDSound/MTS-ESP/blob/main/Client/libMTSClient.h | |||
*/ | |||
bool hasMTSMaster() | |||
{ | |||
return MTS_HasMaster(mts_client); | |||
} | |||
const char *getMTSScale() | |||
{ | |||
return MTS_GetScaleName(mts_client); | |||
} | |||
}; |
@@ -2,7 +2,7 @@ | |||
============================================================================== | |||
This file is part of Obxd synthesizer. | |||
Copyright © 2013-2014 Filatov Vadim | |||
Copyright © 2013-2014 Filatov Vadim | |||
Contact author via email : | |||
justdat_@_e1.ru | |||
@@ -23,13 +23,419 @@ | |||
*/ | |||
#pragma once | |||
#include "SynthEngine.h" | |||
#include "ParamsEnum.h" | |||
#include "../JuceLibraryCode/JuceHeader.h" | |||
class MidiMap | |||
{ | |||
public: | |||
int controllers[255]; | |||
int controllers_default[255]; | |||
std::map<String, int> mapping; | |||
bool loaded = false; | |||
MidiMap() | |||
{ | |||
for(int i = 0 ; i < 255;i++) | |||
controllers[i] = 0; | |||
reset(); | |||
set_default(); | |||
} | |||
}; | |||
void reset(){ | |||
for(int i = 0 ; i < 255;i++){ | |||
controllers[i] = -1; | |||
controllers_default[i] = -1; | |||
} | |||
} | |||
void set_default(){ | |||
int midicc = 71; | |||
controllers[midicc] = controllers_default[midicc] = VOLUME; | |||
mapping["VOLUME"]=VOLUME; | |||
midicc = 15; | |||
controllers[midicc] = controllers_default[midicc] = VOICE_COUNT; | |||
mapping["VOICE_COUNT"]=VOICE_COUNT; | |||
midicc = 33; | |||
controllers[midicc] = controllers_default[midicc] = TUNE; | |||
mapping["TUNE"]=TUNE; | |||
midicc = 17; | |||
controllers[midicc] = controllers_default[midicc] = OCTAVE; | |||
mapping["OCTAVE"]=OCTAVE; | |||
midicc = 118; | |||
controllers[midicc] = controllers_default[midicc] = BENDRANGE; | |||
mapping["BENDRANGE"]=BENDRANGE; | |||
midicc = 34; | |||
controllers[midicc] = controllers_default[midicc] = BENDOSC2; | |||
mapping["BENDOSC2"]=BENDOSC2; | |||
midicc = 35; | |||
controllers[midicc] = controllers_default[midicc] = LEGATOMODE; | |||
mapping["LEGATOMODE"]=LEGATOMODE; | |||
midicc = 75; | |||
controllers[midicc] = controllers_default[midicc] = BENDLFORATE; | |||
mapping["BENDLFORATE"]=BENDLFORATE; | |||
midicc = 76; | |||
controllers[midicc] = controllers_default[midicc] = VFLTENV; | |||
mapping["VFLTENV"]=VFLTENV; | |||
midicc = 20; | |||
controllers[midicc] = controllers_default[midicc] = VAMPENV; | |||
mapping["VAMPENV"]=VAMPENV; | |||
midicc = 21; | |||
controllers[midicc] = controllers_default[midicc] = ASPLAYEDALLOCATION; | |||
mapping["ASPLAYEDALLOCATION"]=ASPLAYEDALLOCATION; | |||
midicc = 23; | |||
controllers[midicc] = controllers_default[midicc] = PORTAMENTO; | |||
mapping["PORTAMENTO"]=PORTAMENTO; | |||
midicc = 16; | |||
controllers[midicc] = controllers_default[midicc] = UNISON; | |||
mapping["UNISON"]=UNISON; | |||
midicc = 24; | |||
controllers[midicc] = controllers_default[midicc] = UDET; | |||
mapping["UDET"]=UDET; | |||
midicc = 43; | |||
controllers[midicc] = controllers_default[midicc] = OSC2_DET; | |||
mapping["OSC2_DET"]=OSC2_DET; | |||
midicc = 19; | |||
controllers[midicc] = controllers_default[midicc] = LFOFREQ; | |||
mapping["LFOFREQ"]=LFOFREQ; | |||
midicc = 44; | |||
controllers[midicc] = controllers_default[midicc] = LFOSINWAVE; | |||
mapping["LFOSINWAVE"]=LFOSINWAVE; | |||
midicc = 45; | |||
controllers[midicc] = controllers_default[midicc] = LFOSQUAREWAVE; | |||
mapping["LFOSQUAREWAVE"]=LFOSQUAREWAVE; | |||
midicc = 46; | |||
controllers[midicc] = controllers_default[midicc] = LFOSHWAVE; | |||
mapping["LFOSHWAVE"]=LFOSHWAVE; | |||
midicc = 22; | |||
controllers[midicc] = controllers_default[midicc] = LFO1AMT; | |||
mapping["LFO1AMT"]=LFO1AMT; | |||
midicc = 25; | |||
controllers[midicc] = controllers_default[midicc] = LFO2AMT; | |||
mapping["LFO2AMT"]=LFO2AMT; | |||
midicc = 47; | |||
controllers[midicc] = controllers_default[midicc] = LFOOSC1; | |||
mapping["LFOOSC1"]=LFOOSC1; | |||
midicc = 48; | |||
controllers[midicc] = controllers_default[midicc] = LFOOSC2; | |||
mapping["LFOOSC2"]=LFOOSC2; | |||
midicc = 49; | |||
controllers[midicc] = controllers_default[midicc] = LFOFILTER; | |||
mapping["LFOFILTER"]=LFOFILTER; | |||
midicc = 50; | |||
controllers[midicc] = controllers_default[midicc] = LFOPW1; | |||
mapping["LFOPW1"]=LFOPW1; | |||
midicc = 51; | |||
controllers[midicc] = controllers_default[midicc] = LFOPW2; | |||
mapping["LFOPW2"]=LFOPW2; | |||
midicc = 52; | |||
controllers[midicc] = controllers_default[midicc] = OSC2HS; | |||
mapping["OSC2HS"]=OSC2HS; | |||
midicc = 53; | |||
controllers[midicc] = controllers_default[midicc] = XMOD; | |||
mapping["XMOD"]=XMOD; | |||
midicc = 54; | |||
controllers[midicc] = controllers_default[midicc] = OSC1P; | |||
mapping["OSC1P"]=OSC1P; | |||
midicc = 55; | |||
controllers[midicc] = controllers_default[midicc] = OSC2P; | |||
mapping["OSC2P"]=OSC2P; | |||
midicc = 56; | |||
controllers[midicc] = controllers_default[midicc] = OSCQuantize; | |||
mapping["OSCQuantize"]=OSCQuantize; | |||
midicc = 57; | |||
controllers[midicc] = controllers_default[midicc] = OSC1Saw; | |||
mapping["OSC1Saw"]=OSC1Saw; | |||
midicc = 58; | |||
controllers[midicc] = controllers_default[midicc] = OSC1Pul; | |||
mapping["OSC1Pul"]=OSC1Pul; | |||
midicc = 59; | |||
controllers[midicc] = controllers_default[midicc] = OSC2Saw; | |||
mapping["OSC2Saw"]=OSC2Saw; | |||
midicc = 60; | |||
controllers[midicc] = controllers_default[midicc] = OSC2Pul; | |||
mapping["OSC2Pul"]=OSC2Pul; | |||
midicc = 61; | |||
controllers[midicc] = controllers_default[midicc] = PW; | |||
mapping["PW"]=PW; | |||
midicc = 62; | |||
controllers[midicc] = controllers_default[midicc] = BRIGHTNESS; | |||
mapping["BRIGHTNESS"]=BRIGHTNESS; | |||
midicc = 63; | |||
controllers[midicc] = controllers_default[midicc] = ENVPITCH; | |||
mapping["ENVPITCH"]=ENVPITCH; | |||
midicc = 77; | |||
controllers[midicc] = controllers_default[midicc] = OSC1MIX; | |||
mapping["OSC1MIX"]=OSC1MIX; | |||
midicc = 78; | |||
controllers[midicc] = controllers_default[midicc] = OSC2MIX; | |||
mapping["OSC2MIX"]=OSC2MIX; | |||
midicc = 102; | |||
controllers[midicc] = controllers_default[midicc] = NOISEMIX; | |||
mapping["NOISEMIX"]=NOISEMIX; | |||
midicc = 103; | |||
controllers[midicc] = controllers_default[midicc] = FLT_KF; | |||
mapping["FLT_KF"]=FLT_KF; | |||
midicc = 74; | |||
controllers[midicc] = controllers_default[midicc] = CUTOFF; | |||
mapping["CUTOFF"]=CUTOFF; | |||
midicc = 42; | |||
controllers[midicc] = controllers_default[midicc] = RESONANCE; | |||
mapping["RESONANCE"]=RESONANCE; | |||
midicc = 104; | |||
controllers[midicc] = controllers_default[midicc] = MULTIMODE; | |||
mapping["MULTIMODE"]=MULTIMODE; | |||
midicc = 18; | |||
controllers[midicc] = controllers_default[midicc] = FILTER_WARM; | |||
mapping["FILTER_WARM"]=FILTER_WARM; | |||
midicc = 105; | |||
controllers[midicc] = controllers_default[midicc] = BANDPASS; | |||
mapping["BANDPASS"]=BANDPASS; | |||
midicc = 106; | |||
controllers[midicc] = controllers_default[midicc] = FOURPOLE; | |||
mapping["FOURPOLE"]=FOURPOLE; | |||
midicc = 107; | |||
controllers[midicc] = controllers_default[midicc] = ENVELOPE_AMT; | |||
mapping["ENVELOPE_AMT"]=ENVELOPE_AMT; | |||
midicc = 73; | |||
controllers[midicc] = controllers_default[midicc] = LATK; | |||
mapping["LATK"]=LATK; | |||
midicc = 36; | |||
controllers[midicc] = controllers_default[midicc] = LDEC; | |||
mapping["LDEC"]=LDEC; | |||
midicc = 37; | |||
controllers[midicc] = controllers_default[midicc] = LSUS; | |||
mapping["LSUS"]=LSUS; | |||
midicc = 72; | |||
controllers[midicc] = controllers_default[midicc] = LREL; | |||
mapping["LREL"]=LREL; | |||
midicc = 38; | |||
controllers[midicc] = controllers_default[midicc] = FATK; | |||
mapping["FATK"]=FATK; | |||
midicc = 39; | |||
controllers[midicc] = controllers_default[midicc] = FDEC; | |||
mapping["FDEC"]=FDEC; | |||
midicc = 40; | |||
controllers[midicc] = controllers_default[midicc] = FSUS; | |||
mapping["FSUS"]=FSUS; | |||
midicc = 41; | |||
controllers[midicc] = controllers_default[midicc] = FREL; | |||
mapping["FREL"]=FREL; | |||
midicc = 108; | |||
controllers[midicc] = controllers_default[midicc] = ENVDER; | |||
mapping["ENVDER"]=ENVDER; | |||
midicc = 109; | |||
controllers[midicc] = controllers_default[midicc] = FILTERDER; | |||
mapping["FILTERDER"]=FILTERDER; | |||
midicc = 110; | |||
controllers[midicc] = controllers_default[midicc] = PORTADER; | |||
mapping["PORTADER"]=PORTADER; | |||
midicc = 81; | |||
controllers[midicc] = controllers_default[midicc] = PAN1; | |||
mapping["PAN1"]=PAN1; | |||
midicc = 82; | |||
controllers[midicc] = controllers_default[midicc] = PAN2; | |||
mapping["PAN2"]=PAN2; | |||
midicc = 83; | |||
controllers[midicc] = controllers_default[midicc] = PAN3; | |||
mapping["PAN3"]=PAN3; | |||
midicc = 84; | |||
controllers[midicc] = controllers_default[midicc] = PAN4; | |||
mapping["PAN4"]=PAN4; | |||
midicc = 85; | |||
controllers[midicc] = controllers_default[midicc] = PAN5; | |||
mapping["PAN5"]=PAN5; | |||
midicc = 86; | |||
controllers[midicc] = controllers_default[midicc] = PAN6; | |||
mapping["PAN6"]=PAN6; | |||
midicc = 87; | |||
controllers[midicc] = controllers_default[midicc] = PAN7; | |||
mapping["PAN7"]=PAN7; | |||
midicc = 88; | |||
controllers[midicc] = controllers_default[midicc] = PAN8; | |||
mapping["PAN8"]=PAN8; | |||
midicc = 111; | |||
controllers[midicc] = controllers_default[midicc] = ECONOMY_MODE; | |||
mapping["ECONOMY_MODE"]=ECONOMY_MODE; | |||
//midicc = 112; | |||
//controllers[midicc] = controllers_default[midicc] = LFO_SYNC; | |||
midicc = 113; | |||
controllers[midicc] = controllers_default[midicc] = PW_ENV; | |||
mapping["PW_ENV"]=PW_ENV; | |||
midicc = 114; | |||
controllers[midicc] = controllers_default[midicc] = PW_ENV_BOTH; | |||
mapping["PW_ENV_BOTH"]=PW_ENV_BOTH; | |||
midicc = 115; | |||
controllers[midicc] = controllers_default[midicc] = ENV_PITCH_BOTH; | |||
mapping["ENV_PITCH_BOTH"]=ENV_PITCH_BOTH; | |||
midicc = 116; | |||
controllers[midicc] = controllers_default[midicc] = FENV_INVERT; | |||
mapping["FENV_INVERT"]=FENV_INVERT; | |||
midicc = 117; | |||
controllers[midicc] = controllers_default[midicc] = PW_OSC2_OFS; | |||
mapping["PW_OSC2_OFS"]=PW_OSC2_OFS; | |||
midicc = 118; | |||
controllers[midicc] = controllers_default[midicc] = LEVEL_DIF; | |||
mapping["LEVEL_DIF"]=LEVEL_DIF; | |||
midicc = 119; | |||
controllers[midicc] = controllers_default[midicc] = SELF_OSC_PUSH; | |||
mapping["SELF_OSC_PUSH"]=SELF_OSC_PUSH; | |||
} | |||
int& operator[](int index) | |||
{ | |||
if (index >= 255) { | |||
exit(0); | |||
} | |||
return controllers[index]; | |||
} | |||
void setXml( XmlElement &xml){ | |||
for (int i = 0; i < 255; ++i) | |||
{ | |||
if (controllers[i]!= -1){ | |||
xml.setAttribute("MIDI_" +String(i), getTag(controllers[i])); | |||
} | |||
} | |||
} | |||
String getTag(int paraId){ | |||
for (std::map<String, int>::iterator it = this->mapping.begin(); it != this->mapping.end(); it ++){ | |||
if (paraId == it->second){ | |||
return it->first; | |||
} | |||
} | |||
return "undefine"; | |||
} | |||
int getParaId(String tagName){ | |||
return mapping[tagName]; | |||
} | |||
void getXml(XmlElement &xml){ | |||
for (int i = 0; i < 255; ++i) | |||
{ | |||
String tmp = xml.getStringAttribute("MIDI_" + String(i), "undefine"); | |||
if (tmp != "undefine"){ | |||
controllers[i] = getParaId(tmp); | |||
} | |||
} | |||
// Backward keys | |||
if (controllers[100] > 0) { | |||
controllers[77] = controllers_default[77]; | |||
controllers[100] = 0; | |||
} | |||
if ( controllers[101] > 0) { | |||
controllers[78] = controllers_default[78]; | |||
controllers[101] = 0; | |||
} | |||
} | |||
bool loadFile(File& xml){ | |||
reset(); | |||
set_default(); | |||
if (xml.existsAsFile()){ | |||
XmlDocument xmlDoc (xml); | |||
this->getXml(*xmlDoc.getDocumentElement()); | |||
return true; | |||
} | |||
return false; | |||
} | |||
void updateCC(int idx_para, int midiCC) { | |||
for (int i =0; i < 255; i++) { | |||
if (controllers[i] == idx_para){ | |||
controllers[i] = -1; | |||
} | |||
} | |||
controllers[midiCC] = idx_para; | |||
} | |||
void saveFile(File& xml){ | |||
XmlElement ele("Data"); | |||
this->setXml(ele); | |||
ele.writeToFile(xml, String()); | |||
} | |||
void clean(){ | |||
reset(); | |||
} | |||
}; |
@@ -22,39 +22,97 @@ | |||
============================================================================== | |||
*/ | |||
#pragma once | |||
#include "../Engine/SynthEngine.h" | |||
class ButtonList : public ComboBox{ | |||
private : | |||
int count; | |||
Image kni; | |||
int w2,h2; | |||
#include "../Source/Engine/SynthEngine.h" | |||
class ObxdAudioProcessor; | |||
class ButtonList : public ComboBox, public ScalableComponent | |||
{ | |||
juce::String img_name; | |||
public: | |||
ButtonList(Image k,int fh) :ComboBox("cb") | |||
ButtonList (juce::String nameImg, int fh, ObxdAudioProcessor *owner) : ComboBox("cb"), ScalableComponent(owner), img_name(nameImg) | |||
{ | |||
kni = k; | |||
scaleFactorChanged(); | |||
count = 0; | |||
h2 =fh; | |||
w2 = k.getWidth(); | |||
h2 = fh; | |||
w2 = kni.getWidth(); | |||
} | |||
//int addItem | |||
void addChoise(String name) | |||
void scaleFactorChanged() override | |||
{ | |||
kni = getScaledImageFromCache(img_name, getScaleFactor(), getIsHighResolutionDisplay()); | |||
/* | |||
backgroundImage = | |||
allImage.getClippedImage(Rectangle<int>(0, | |||
allImage.getHeight() / 2, | |||
allImage.getWidth(), | |||
allImage.getHeight() / 2)); | |||
*/ | |||
repaint(); | |||
} | |||
// Source: https://git.iem.at/audioplugins/IEMPluginSuite/-/blob/master/resources/customComponents/ReverseSlider.h | |||
public: | |||
class ButtonListAttachment : public juce::AudioProcessorValueTreeState::ComboBoxAttachment | |||
{ | |||
RangedAudioParameter* parameter = nullptr; | |||
ButtonList* buttonListToControl = nullptr; | |||
public: | |||
ButtonListAttachment (juce::AudioProcessorValueTreeState& stateToControl, | |||
const juce::String& parameterID, | |||
ButtonList& buttonListToControl) : AudioProcessorValueTreeState::ComboBoxAttachment (stateToControl, parameterID, buttonListToControl), buttonListToControl(&buttonListToControl) | |||
{ | |||
parameter = stateToControl.getParameter (parameterID); | |||
buttonListToControl.setParameter (parameter); | |||
} | |||
/* | |||
ButtonListAttachment (juce::AudioProcessorValueTreeState& stateToControl, | |||
const juce::String& parameterID, | |||
ComboBox& buttonListToControl) : AudioProcessorValueTreeState::ComboBoxAttachment (stateToControl, parameterID, buttonListToControl) | |||
{ | |||
} | |||
*/ | |||
void updateToSlider(){ | |||
float val = parameter->getValue(); | |||
//buttonListToControl->setValue(val, NotificationType::dontSendNotification); | |||
//buttonListToControl->setValue(parameter->convertFrom0to1(val0to1), NotificationType::dontSendNotification); | |||
buttonListToControl->setValue(val, NotificationType::dontSendNotification); | |||
} | |||
virtual ~ButtonListAttachment() = default; | |||
}; | |||
void setParameter (const AudioProcessorParameter* p) | |||
{ | |||
if (parameter == p) | |||
return; | |||
parameter = p; | |||
repaint(); | |||
} | |||
void addChoice (String name) | |||
{ | |||
addItem(name,++count); | |||
addItem (name, ++count); | |||
} | |||
float getValue() | |||
{ | |||
return ((getSelectedId()-1)/ (float)(count-1)); | |||
return ((getSelectedId() - 1) / (float) (count - 1)); | |||
} | |||
void setValue(float val,NotificationType notify) | |||
void setValue (float val, NotificationType notify) | |||
{ | |||
setSelectedId((int)(val*(count -1) + 1),notify); | |||
setSelectedId ((int) (val * (count - 1) + 1), notify); | |||
} | |||
void paintOverChildren(Graphics& g) | |||
void paintOverChildren (Graphics& g) override | |||
{ | |||
int ofs = getSelectedId()-1; | |||
g.drawImage(kni, 0, 0, getWidth(), getHeight(), | |||
0, h2*ofs, w2, h2); | |||
int ofs = getSelectedId() - 1; | |||
g.drawImage(kni, 0, 0, getWidth(), getHeight(), 0, h2 * ofs * getScaleInt(), w2 * getScaleInt() , h2* getScaleInt()); | |||
} | |||
private: | |||
int count; | |||
Image kni; | |||
int w2, h2; | |||
const AudioProcessorParameter* parameter {nullptr}; | |||
}; |
@@ -0,0 +1,87 @@ | |||
/* | |||
============================================================================== | |||
This file is part of Obxd synthesizer. | |||
Copyright ďż˝ 2013-2014 Filatov Vadim | |||
Contact author via email : | |||
justdat_@_e1.ru | |||
This file may be licensed under the terms of of the | |||
GNU General Public License Version 2 (the ``GPL''). | |||
Software distributed under the License is distributed | |||
on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either | |||
express or implied. See the GPL for the specific language | |||
governing rights and limitations. | |||
You should have received a copy of the GPL along with this | |||
program. If not, go to http://www.gnu.org/licenses/gpl.html | |||
or write to the Free Software Foundation, Inc., | |||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
============================================================================== | |||
*/ | |||
#pragma once | |||
#include "../Source/Engine/SynthEngine.h" | |||
class ObxdAudioProcessor; | |||
class ImageMenu : public ImageButton, | |||
public ScalableComponent | |||
{ | |||
juce::String img_name; | |||
public: | |||
ImageMenu(juce::String nameImg, ObxdAudioProcessor* owner_) | |||
: ScalableComponent(owner_), img_name(nameImg) | |||
{ | |||
scaleFactorChanged(); | |||
setOpaque(false); | |||
setVisible(true); | |||
} | |||
void scaleFactorChanged() override | |||
{ | |||
const float scaleFactor = getScaleFactor(); | |||
const bool isHighResolutionDisplay = getIsHighResolutionDisplay(); | |||
Image normalImage = getScaledImageFromCache(img_name, scaleFactor, isHighResolutionDisplay); | |||
Image downImage = getScaledImageFromCache(img_name, scaleFactor, isHighResolutionDisplay); | |||
const bool resizeButtonNowToFitThisImage = false; | |||
const bool rescaleImagesWhenButtonSizeChanges = true; | |||
const bool preserveImageProportions = true; | |||
/* | |||
false, | |||
true, | |||
true, | |||
image, | |||
1.0f, // menu transparency | |||
Colour(), | |||
image, | |||
1.0f, // menu hover transparency | |||
Colour(), | |||
image, | |||
0.3f, // menu click transparency | |||
Colour()); | |||
*/ | |||
setImages(resizeButtonNowToFitThisImage, | |||
rescaleImagesWhenButtonSizeChanges, | |||
preserveImageProportions, | |||
normalImage, | |||
1.0f, // menu transparency | |||
Colour(), | |||
normalImage, | |||
1.0f, // menu hover transparency | |||
Colour(), | |||
downImage, | |||
0.3f, // menu click transparency | |||
Colour()); | |||
repaint(); | |||
} | |||
protected: | |||
}; |
@@ -2,7 +2,7 @@ | |||
============================================================================== | |||
This file is part of Obxd synthesizer. | |||
Copyright © 2013-2014 Filatov Vadim | |||
Copyright � 2013-2014 Filatov Vadim | |||
Contact author via email : | |||
justdat_@_e1.ru | |||
@@ -22,9 +22,30 @@ | |||
============================================================================== | |||
*/ | |||
#pragma once | |||
#include "../Engine/SynthEngine.h" | |||
class Knob : public Slider | |||
#include "../Source/Engine/SynthEngine.h" | |||
#include "../Components/ScaleComponent.h" | |||
class ObxdAudioProcessor; | |||
class KnobLookAndFeel : public LookAndFeel_V4 | |||
{ | |||
public: | |||
KnobLookAndFeel() | |||
{ | |||
setColour(BubbleComponent::ColourIds::backgroundColourId, Colours::white.withAlpha(0.8f)); | |||
setColour(BubbleComponent::ColourIds::outlineColourId, Colours::transparentBlack); | |||
setColour(TooltipWindow::textColourId, Colours::black); | |||
} | |||
int getSliderPopupPlacement(Slider&) override | |||
{ | |||
return BubbleComponent::BubblePlacement::above; | |||
} | |||
private: | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(KnobLookAndFeel) | |||
}; | |||
class Knob : public Slider, public ScalableComponent, public ActionBroadcaster | |||
{ | |||
juce::String img_name; | |||
public: | |||
//Knob(Image image, const int numFrames, const bool stripIsHorizontal) : Slider("Knob") | |||
//{ | |||
@@ -33,23 +54,131 @@ public: | |||
// setSliderStyle(RotaryVerticalDrag); | |||
// setRange(0.0f, 1.0f, 0.001f); | |||
//} | |||
Knob(Image k,int fh) : Slider("Knob") | |||
Knob (juce::String name, int fh, ObxdAudioProcessor* owner_) : Slider("Knob"), ScalableComponent(owner_), img_name(name) | |||
{ | |||
h2 =fh; | |||
w2 = k.getWidth(); | |||
numFr = k.getHeight() / h2; | |||
kni = k; | |||
}; | |||
void paint(Graphics& g) | |||
scaleFactorChanged(); | |||
h2 = fh; | |||
w2 = kni.getWidth(); | |||
numFr = kni.getHeight() / h2; | |||
setLookAndFeel(&lookAndFeel); | |||
setVelocityModeParameters(1.0, 1, 0.0, true, ModifierKeys::ctrlModifier); | |||
} | |||
~Knob() override | |||
{ | |||
setLookAndFeel(nullptr); | |||
} | |||
void scaleFactorChanged() override | |||
{ | |||
kni = getScaledImageFromCache(img_name, getScaleFactor(), getIsHighResolutionDisplay()); | |||
/* | |||
backgroundImage = | |||
allImage.getClippedImage(Rectangle<int>(0, | |||
allImage.getHeight() / 2, | |||
allImage.getWidth(), | |||
allImage.getHeight() / 2)); | |||
*/ | |||
repaint(); | |||
} | |||
void mouseDown(const MouseEvent& event) override | |||
{ | |||
if (event.mods.isShiftDown()) | |||
{ | |||
if (shouldResetOnShiftClick) | |||
{ | |||
sendActionMessage(resetActionMessage); | |||
} | |||
} | |||
Slider::mouseDown(event); | |||
} | |||
void mouseDrag(const MouseEvent& event) override | |||
{ | |||
int ofs = (int)((getValue() - getMinimum()) / (getMaximum() - getMinimum()) * (numFr - 1)); | |||
g.drawImage(kni, 0, 0, getWidth(), getHeight(), | |||
0, h2*ofs, w2, h2); | |||
Slider::mouseDrag(event); | |||
if (event.mods.isShiftDown()) | |||
{ | |||
if (shiftDragCallback) | |||
{ | |||
setValue(shiftDragCallback(getValue()), sendNotificationAsync); | |||
} | |||
} | |||
if (event.mods.isAltDown()) | |||
{ | |||
if (altDragCallback) | |||
{ | |||
setValue(altDragCallback(getValue()), sendNotificationAsync); | |||
} | |||
} | |||
if (alternativeValueMapCallback) | |||
{ | |||
setValue(alternativeValueMapCallback(getValue()), sendNotificationAsync); | |||
} | |||
} | |||
// Source: https://git.iem.at/audioplugins/IEMPluginSuite/-/blob/master/resources/customComponents/ReverseSlider.h | |||
public: | |||
class KnobAttachment : public juce::AudioProcessorValueTreeState::SliderAttachment | |||
{ | |||
RangedAudioParameter* parameter = nullptr; | |||
Knob* sliderToControl = nullptr; | |||
public: | |||
KnobAttachment (juce::AudioProcessorValueTreeState& stateToControl, | |||
const juce::String& parameterID, | |||
Knob& sliderToControl) : AudioProcessorValueTreeState::SliderAttachment (stateToControl, parameterID, sliderToControl), sliderToControl(&sliderToControl) | |||
{ | |||
parameter = stateToControl.getParameter (parameterID); | |||
sliderToControl.setParameter (parameter); | |||
} | |||
/*KnobAttachment (juce::AudioProcessorValueTreeState& stateToControl, | |||
const juce::String& parameterID, | |||
Slider& sliderToControl) : AudioProcessorValueTreeState::SliderAttachment (stateToControl, parameterID, sliderToControl) | |||
{ | |||
}*/ | |||
void updateToSlider(){ | |||
float val = parameter->getValue(); | |||
//sliderToControl->setValue(parameter->convertFrom0to1(val0to1)); | |||
sliderToControl->setValue(val, NotificationType::dontSendNotification); | |||
//DBG(" Slider: " << parameter->name << " " << sliderToControl->getValue() << " Parameter: "<< " " << parameter->getValue()); | |||
} | |||
virtual ~KnobAttachment() = default; | |||
}; | |||
void setParameter (AudioProcessorParameter* p) | |||
{ | |||
if (parameter == p) | |||
return; | |||
parameter = p; | |||
updateText(); | |||
repaint(); | |||
} | |||
void paint (Graphics& g) override | |||
{ | |||
int ofs = (int) ((getValue() - getMinimum()) / (getMaximum() - getMinimum()) * (numFr - 1)); | |||
g.drawImage (kni, 0, 0, getWidth(), getHeight(), 0, h2 * ofs * getScaleInt(), w2 * getScaleInt(), h2 * getScaleInt()); | |||
} | |||
void resetOnShiftClick(bool value, const String& identifier) | |||
{ | |||
shouldResetOnShiftClick = value; | |||
resetActionMessage = identifier; | |||
} | |||
std::function<double(double)> shiftDragCallback, altDragCallback, alternativeValueMapCallback; | |||
private: | |||
Image kni; | |||
int fh,numFr; | |||
int w2,h2; | |||
}; | |||
int fh, numFr; | |||
int w2, h2; | |||
bool shouldResetOnShiftClick{ false }; | |||
String resetActionMessage{}; | |||
AudioProcessorParameter* parameter {nullptr}; | |||
KnobLookAndFeel lookAndFeel; | |||
}; |
@@ -2,7 +2,7 @@ | |||
============================================================================== | |||
This file is part of Obxd synthesizer. | |||
Copyright © 2013-2014 Filatov Vadim | |||
Copyright � 2013-2014 Filatov Vadim | |||
Contact author via email : | |||
justdat_@_e1.ru | |||
@@ -22,25 +22,82 @@ | |||
============================================================================== | |||
*/ | |||
#pragma once | |||
#include "../Engine/SynthEngine.h" | |||
class TooglableButton : public ImageButton | |||
#include "../Source/Engine/SynthEngine.h" | |||
#include "../Components/ScaleComponent.h" | |||
class ObxdAudioProcessor; | |||
class TooglableButton : public ImageButton, public ScalableComponent | |||
{ | |||
juce::String img_name; | |||
public: | |||
bool toogled; | |||
TooglableButton(Image k) :ImageButton() | |||
TooglableButton (juce::String name, ObxdAudioProcessor *owner) : ImageButton(),ScalableComponent(owner), img_name(name) | |||
{ | |||
//this->setImages | |||
kni = k; | |||
toogled = false; | |||
//kni = k; | |||
scaleFactorChanged(); | |||
//toogled = false; | |||
width = kni.getWidth(); | |||
height = kni.getHeight(); | |||
w2=width; | |||
w2 = width; | |||
h2 = height / 2; | |||
this->setClickingTogglesState(true); | |||
this->setClickingTogglesState (true); | |||
} | |||
void clicked() | |||
void scaleFactorChanged() override | |||
{ | |||
kni = getScaledImageFromCache(img_name, getScaleFactor(), getIsHighResolutionDisplay()); | |||
/* | |||
backgroundImage = | |||
allImage.getClippedImage(Rectangle<int>(0, | |||
allImage.getHeight() / 2, | |||
allImage.getWidth(), | |||
allImage.getHeight() / 2)); | |||
*/ | |||
repaint(); | |||
} | |||
~TooglableButton() override{ | |||
}; | |||
// Source: https://git.iem.at/audioplugins/IEMPluginSuite/-/blob/master/resources/customComponents/ReverseSlider.h | |||
public: | |||
class ToggleAttachment : public juce::AudioProcessorValueTreeState::ButtonAttachment | |||
{ | |||
RangedAudioParameter* parameter = nullptr; | |||
TooglableButton* buttonToControl = nullptr; | |||
public: | |||
ToggleAttachment (juce::AudioProcessorValueTreeState& stateToControl, | |||
const juce::String& parameterID, | |||
TooglableButton& buttonToControl) : AudioProcessorValueTreeState::ButtonAttachment (stateToControl, parameterID, buttonToControl), buttonToControl(&buttonToControl) | |||
{ | |||
parameter = stateToControl.getParameter (parameterID); | |||
//buttonToControl.setParameter (parameter); | |||
} | |||
/* | |||
ToggleAttachment (juce::AudioProcessorValueTreeState& stateToControl, | |||
const juce::String& parameterID, | |||
Button& buttonToControl) : AudioProcessorValueTreeState::ButtonAttachment (stateToControl, parameterID, buttonToControl) | |||
{ | |||
}*/ | |||
void updateToSlider(){ | |||
float val = parameter->getValue(); | |||
//buttonToControl->setValue(parameter->convertFrom0to1(val0to1), NotificationType::dontSendNotification); | |||
DBG("Toggle Parameter: " << parameter->name << " Val: " << val); | |||
buttonToControl->setToggleState(val, NotificationType::dontSendNotification); | |||
} | |||
virtual ~ToggleAttachment() = default; | |||
}; | |||
/* | |||
void setParameter (const AudioProcessorParameter* p) | |||
{ | |||
if (parameter == p) | |||
return; | |||
parameter = p; | |||
repaint(); | |||
}*/ | |||
/* | |||
void clicked() override | |||
{ | |||
toogled = !toogled; | |||
toogled = ! toogled; | |||
//this->setColour(1,Colours::blue); | |||
//if(toogled) | |||
// this->setColour(TextButton::ColourIds::buttonColourId,Colours::lightgreen); | |||
@@ -49,35 +106,45 @@ public: | |||
//this->setColour(DrawableButton::ColourIds::backgroundColourId,Colours::lightpink); | |||
Button::clicked(); | |||
}; | |||
void paintButton(Graphics& g, bool isMouseOverButton, bool isButtonDown) | |||
};*/ | |||
void paintButton (Graphics& g, bool isMouseOverButton, bool isButtonDown) override | |||
{ | |||
int offset = 0; | |||
if (toogled) | |||
int offset = 0; | |||
//if (toogled) | |||
if (getToggleState()) | |||
{ | |||
offset = 1; | |||
} | |||
g.drawImage(kni, 0, 0, getWidth(), getHeight(), | |||
0, offset *h2, w2,h2); | |||
g.drawImage(kni, 0, 0, getWidth(), getHeight(), 0, offset * h2 * getScaleInt() , w2 * getScaleInt(), h2 * getScaleInt()); | |||
} | |||
void setValue(float state,int notify) | |||
/* | |||
void setValue (float state, int notify) | |||
{ | |||
if(state > 0.5) | |||
toogled = true; | |||
if (state > 0.5) | |||
toogled = true; | |||
else toogled = false; | |||
repaint(); | |||
} | |||
float getValue() | |||
}*/ | |||
/*float getValue() | |||
{ | |||
if(toogled) | |||
return 1; | |||
if (toogled) | |||
return 1; | |||
else return 0; | |||
} | |||
}*/ | |||
//void paint(Graphics& g) | |||
//{ | |||
// g.drawImageTransformed(kni,AffineTransform::rotation(((getValue() - getMinimum())/(getMaximum() - getMinimum()))*float_Pi - float_Pi*2)); | |||
//} | |||
//bool toogled; | |||
private: | |||
Image kni; | |||
int width,height,w2,h2; | |||
}; | |||
int width, height, w2, h2; | |||
//const AudioProcessorParameter* parameter; | |||
}; |
@@ -0,0 +1,16 @@ | |||
<?xml version="1.0" encoding="UTF-8" standalone="no"?> | |||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> | |||
<svg width="100%" height="100%" viewBox="0 0 471 39" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;"> | |||
<rect x="0" y="0" width="471" height="39"/> | |||
<path d="M398,9.205C398,7.436 396.564,6 394.795,6L24.205,6C22.436,6 21,7.436 21,9.205L21,29.795C21,31.564 22.436,33 24.205,33L394.795,33C396.564,33 398,31.564 398,29.795L398,9.205Z" style="stroke:rgb(121,121,121);stroke-width:1px;"/> | |||
<g transform="matrix(1,0,0,1,4.22266,11.0245)"> | |||
<g transform="matrix(24,0,0,24,406.621,17.2645)"> | |||
<path d="M0.425,-0.156C0.435,-0.147 0.439,-0.135 0.439,-0.122C0.439,-0.109 0.435,-0.097 0.425,-0.088C0.415,-0.078 0.404,-0.073 0.391,-0.073C0.377,-0.073 0.366,-0.078 0.356,-0.087L0.112,-0.332C0.102,-0.341 0.098,-0.353 0.098,-0.366C0.098,-0.38 0.103,-0.391 0.112,-0.401L0.356,-0.645C0.366,-0.654 0.377,-0.659 0.391,-0.659C0.404,-0.659 0.415,-0.654 0.425,-0.645C0.435,-0.635 0.439,-0.624 0.439,-0.61C0.439,-0.597 0.435,-0.585 0.425,-0.576L0.215,-0.366L0.425,-0.156Z" style="fill:rgb(121,121,121);fill-rule:nonzero;"/> | |||
</g> | |||
</g> | |||
<g transform="matrix(1,0,0,1,31.2227,11.0245)"> | |||
<g transform="matrix(24,0,0,24,406.621,17.2645)"> | |||
<path d="M0.322,-0.366L0.112,-0.576C0.103,-0.585 0.098,-0.597 0.098,-0.61C0.098,-0.624 0.102,-0.635 0.112,-0.645C0.122,-0.654 0.133,-0.659 0.146,-0.659C0.16,-0.659 0.171,-0.654 0.181,-0.645L0.425,-0.401C0.435,-0.391 0.439,-0.38 0.439,-0.366C0.439,-0.353 0.435,-0.341 0.425,-0.332L0.181,-0.087C0.171,-0.078 0.16,-0.073 0.146,-0.073C0.133,-0.073 0.122,-0.078 0.112,-0.088C0.102,-0.097 0.098,-0.109 0.098,-0.122C0.098,-0.135 0.103,-0.147 0.112,-0.156L0.322,-0.366Z" style="fill:rgb(121,121,121);fill-rule:nonzero;"/> | |||
</g> | |||
</g> | |||
</svg> |
@@ -0,0 +1,390 @@ | |||
/* | |||
Copyright (C) 2021 by ODDSound Ltd. info@oddsound.com | |||
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. | |||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF | |||
THIS SOFTWARE. | |||
*/ | |||
#include "libMTSClient.h" | |||
#include <math.h> | |||
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) || defined(__TOS_WIN__) || defined(_MSC_VER) | |||
#define MTS_ESP_WIN | |||
#define WIN32_LEAN_AND_MEAN | |||
#include <windows.h> | |||
typedef HRESULT (WINAPI* SHGetKnownFolderPathFunc) (const GUID*,DWORD,HANDLE,PWSTR*); | |||
typedef void (WINAPI* CoTaskMemFreeFunc) (LPVOID); | |||
#else | |||
#include <dlfcn.h> | |||
#endif | |||
const static double ln2=0.693147180559945309417; | |||
const static double ratioToSemitones=17.31234049066756088832; // 12./log(2.) | |||
typedef void (*mts_void)(void); | |||
typedef bool (*mts_bool)(void); | |||
typedef bool (*mts_bcc)(char,char); | |||
typedef const double *(*mts_cd)(void); | |||
typedef const double *(*mts_cdc)(char); | |||
typedef bool (*mts_bc)(char); | |||
typedef const char *(*mts_pcc)(void); | |||
struct mtsclientglobal | |||
{ | |||
mtsclientglobal() : RegisterClient(0), DeregisterClient(0), HasMaster(0), ShouldFilterNote(0), ShouldFilterNoteMultiChannel(0), GetTuning(0), GetMultiChannelTuning(0), UseMultiChannelTuning(0), GetScaleName(0), esp_retuning(0), handle(0) | |||
{ | |||
for (int i=0;i<128;i++) iet[i]=1./(440.*pow(2.,(i-69.)/12.)); | |||
load_lib(); | |||
if (GetTuning) esp_retuning=GetTuning(); | |||
for (int i=0;i<16;i++) multi_channel_esp_retuning[i]=GetMultiChannelTuning?GetMultiChannelTuning(static_cast<char>(i)):0; | |||
} | |||
virtual inline bool isOnline() const {return esp_retuning && HasMaster && HasMaster();} | |||
mts_void RegisterClient,DeregisterClient;mts_bool HasMaster;mts_bcc ShouldFilterNote,ShouldFilterNoteMultiChannel;mts_cd GetTuning;mts_cdc GetMultiChannelTuning;mts_bc UseMultiChannelTuning;mts_pcc GetScaleName; // Interface to lib | |||
double iet[128];const double *esp_retuning;const double *multi_channel_esp_retuning[16]; // tuning tables | |||
#ifdef MTS_ESP_WIN | |||
virtual void load_lib() | |||
{ | |||
SHGetKnownFolderPathFunc SHGetKnownFolderPath=0; | |||
CoTaskMemFreeFunc CoTaskMemFree=0; | |||
HMODULE shell32Module=GetModuleHandleW(L"Shell32.dll"); | |||
HMODULE ole32Module=GetModuleHandleW(L"Ole32.dll"); | |||
if (shell32Module) SHGetKnownFolderPath=(SHGetKnownFolderPathFunc)GetProcAddress(shell32Module,"SHGetKnownFolderPath"); | |||
if (ole32Module) CoTaskMemFree=(CoTaskMemFreeFunc)GetProcAddress(ole32Module,"CoTaskMemFree"); | |||
if (SHGetKnownFolderPath && CoTaskMemFree) | |||
{ | |||
const GUID FOLDERID_ProgramFilesCommonGUID={0xF7F1ED05,0x9F6D,0x47A2,0xAA,0xAE,0x29,0xD3,0x17,0xC6,0xF0,0x66}; | |||
PWSTR cf=NULL; | |||
if (SHGetKnownFolderPath(&FOLDERID_ProgramFilesCommonGUID,0,0,&cf)>=0) | |||
{ | |||
WCHAR buffer[MAX_PATH];buffer[0]=L'\0'; | |||
if (cf) wcsncpy(buffer,cf,MAX_PATH); | |||
CoTaskMemFree(cf); | |||
buffer[MAX_PATH-1]=L'\0'; | |||
const WCHAR *libpath=L"\\MTS-ESP\\LIBMTS.dll"; | |||
DWORD cfLen=wcslen(buffer); | |||
wcsncat(buffer,libpath,MAX_PATH-cfLen-1); | |||
if (!(handle=LoadLibraryW(buffer))) return; | |||
} | |||
else {CoTaskMemFree(cf);return;} | |||
} | |||
else return; | |||
RegisterClient =(mts_void) GetProcAddress(handle,"MTS_RegisterClient"); | |||
DeregisterClient =(mts_void) GetProcAddress(handle,"MTS_DeregisterClient"); | |||
HasMaster =(mts_bool) GetProcAddress(handle,"MTS_HasMaster"); | |||
ShouldFilterNote =(mts_bcc) GetProcAddress(handle,"MTS_ShouldFilterNote"); | |||
ShouldFilterNoteMultiChannel =(mts_bcc) GetProcAddress(handle,"MTS_ShouldFilterNoteMultiChannel"); | |||
GetTuning =(mts_cd) GetProcAddress(handle,"MTS_GetTuningTable"); | |||
GetMultiChannelTuning =(mts_cdc) GetProcAddress(handle,"MTS_GetMultiChannelTuningTable"); | |||
UseMultiChannelTuning =(mts_bc) GetProcAddress(handle,"MTS_UseMultiChannelTuning"); | |||
GetScaleName =(mts_pcc) GetProcAddress(handle,"MTS_GetScaleName"); | |||
} | |||
virtual ~mtsclientglobal() {if (handle) FreeLibrary(handle);} | |||
HINSTANCE handle; | |||
#else | |||
virtual void load_lib() | |||
{ | |||
if (!(handle=dlopen("/Library/Application Support/MTS-ESP/libMTS.dylib",RTLD_NOW)) && | |||
!(handle=dlopen("/usr/local/lib/libMTS.so",RTLD_NOW))) return; | |||
RegisterClient =(mts_void) dlsym(handle,"MTS_RegisterClient"); | |||
DeregisterClient =(mts_void) dlsym(handle,"MTS_DeregisterClient"); | |||
HasMaster =(mts_bool) dlsym(handle,"MTS_HasMaster"); | |||
ShouldFilterNote =(mts_bcc) dlsym(handle,"MTS_ShouldFilterNote"); | |||
ShouldFilterNoteMultiChannel =(mts_bcc) dlsym(handle,"MTS_ShouldFilterNoteMultiChannel"); | |||
GetTuning =(mts_cd) dlsym(handle,"MTS_GetTuningTable"); | |||
GetMultiChannelTuning =(mts_cdc) dlsym(handle,"MTS_GetMultiChannelTuningTable"); | |||
UseMultiChannelTuning =(mts_bc) dlsym(handle,"MTS_UseMultiChannelTuning"); | |||
GetScaleName =(mts_pcc) dlsym(handle,"MTS_GetScaleName"); | |||
} | |||
virtual ~mtsclientglobal() {if (handle) dlclose(handle);} | |||
void *handle; | |||
#endif | |||
}; | |||
static mtsclientglobal global; | |||
struct MTSClient | |||
{ | |||
MTSClient() : tuningName("12-TET"), supportsNoteFiltering(false), supportsMultiChannelNoteFiltering(false), supportsMultiChannelTuning(false), freqRequestReceived(false), supportsMTSSysex(false) | |||
{ | |||
for (int i=0;i<128;i++) retuning[i]=440.*pow(2.,(i-69.)/12.); | |||
if (global.RegisterClient) global.RegisterClient(); | |||
} | |||
virtual ~MTSClient() {if (global.DeregisterClient) global.DeregisterClient();} | |||
bool hasMaster() {return global.isOnline();} | |||
inline double freq(char midinote,char midichannel) | |||
{ | |||
freqRequestReceived=true; | |||
supportsMultiChannelTuning=!(midichannel&~15); | |||
if (!global.isOnline()) return retuning[midinote&127]; | |||
if ((!supportsNoteFiltering || supportsMultiChannelNoteFiltering) && supportsMultiChannelTuning && global.UseMultiChannelTuning && global.UseMultiChannelTuning(midichannel) && global.multi_channel_esp_retuning[midichannel&15]) | |||
{ | |||
return global.multi_channel_esp_retuning[midichannel&15][midinote&127]; | |||
} | |||
return global.esp_retuning[midinote&127]; | |||
} | |||
inline double ratio(char midinote,char midichannel) | |||
{ | |||
freqRequestReceived=true; | |||
supportsMultiChannelTuning=!(midichannel&~15); | |||
if (!global.isOnline()) return supportsMTSSysex?retuning[midinote&127]*global.iet[midinote&127]:1.; | |||
if ((!supportsNoteFiltering || supportsMultiChannelNoteFiltering) && supportsMultiChannelTuning && global.UseMultiChannelTuning && global.UseMultiChannelTuning(midichannel) && global.multi_channel_esp_retuning[midichannel&15]) | |||
{ | |||
return global.multi_channel_esp_retuning[midichannel&15][midinote&127]*global.iet[midinote&127]; | |||
} | |||
return global.esp_retuning[midinote&127]*global.iet[midinote&127]; | |||
} | |||
inline double semitones(char midinote,char midichannel) | |||
{ | |||
freqRequestReceived=true; | |||
supportsMultiChannelTuning=!(midichannel&~15); | |||
if (!global.isOnline()) return supportsMTSSysex?ratioToSemitones*log(retuning[midinote&127]*global.iet[midinote&127]):0.; | |||
if ((!supportsNoteFiltering || supportsMultiChannelNoteFiltering) && supportsMultiChannelTuning && global.UseMultiChannelTuning && global.UseMultiChannelTuning(midichannel) && global.multi_channel_esp_retuning[midichannel&15]) | |||
{ | |||
return ratioToSemitones*log(global.multi_channel_esp_retuning[midichannel&15][midinote&127]*global.iet[midinote&127]); | |||
} | |||
return ratioToSemitones*log(global.esp_retuning[midinote&127]*global.iet[midinote&127]); | |||
} | |||
inline bool shouldFilterNote(char midinote,char midichannel) | |||
{ | |||
supportsNoteFiltering=true; | |||
supportsMultiChannelNoteFiltering=!(midichannel&~15); | |||
if (!freqRequestReceived) supportsMultiChannelTuning=supportsMultiChannelNoteFiltering; // assume it supports multi channel tuning until a request is received for a frequency and can verify | |||
if (!global.isOnline()) return false; | |||
if (supportsMultiChannelNoteFiltering && supportsMultiChannelTuning && global.UseMultiChannelTuning && global.UseMultiChannelTuning(midichannel)) | |||
{ | |||
return global.ShouldFilterNoteMultiChannel?global.ShouldFilterNoteMultiChannel(midinote&127,midichannel):false; | |||
} | |||
return global.ShouldFilterNote?global.ShouldFilterNote(midinote&127,midichannel):false; | |||
} | |||
inline char freqToNote(double freq,char midichannel) | |||
{ | |||
bool online=global.isOnline(),multiChannel=false; | |||
const double *freqs=online?global.esp_retuning:retuning; | |||
if (online && !(midichannel&~15) && global.UseMultiChannelTuning && global.UseMultiChannelTuning(midichannel) && global.multi_channel_esp_retuning[midichannel]) | |||
{ | |||
freqs=global.multi_channel_esp_retuning[midichannel]; | |||
multiChannel=true; | |||
} | |||
int iLower,iUpper;iLower=0;iUpper=0; | |||
double dLower,dUpper;dLower=0;dUpper=0; | |||
for (int i=0;i<128;i++) | |||
{ | |||
if (online) | |||
{ | |||
if (multiChannel && global.ShouldFilterNoteMultiChannel && global.ShouldFilterNoteMultiChannel(static_cast<char>(i),midichannel)) continue; | |||
else if (global.ShouldFilterNote && global.ShouldFilterNote(static_cast<char>(i),midichannel)) continue; | |||
} | |||
double d=freqs[i]-freq; | |||
if (d==0.) return static_cast<char>(i); | |||
if (d<0) {if (dLower==0. || d>dLower) {dLower=d;iLower=i;}} | |||
else if (dUpper==0. || d<dUpper) {dUpper=d;iUpper=i;} | |||
} | |||
if (dLower==0.) return static_cast<char>(iUpper); | |||
if (dUpper==0. || iLower==iUpper) return static_cast<char>(iLower); | |||
double fmid=freqs[iLower]*pow(2.,0.5*(log(freqs[iUpper]/freqs[iLower])/ln2)); | |||
return freq<fmid?static_cast<char>(iLower):static_cast<char>(iUpper); | |||
} | |||
inline char freqToNote(double freq,char *midichannel) | |||
{ | |||
if (!midichannel) return freqToNote(freq,static_cast<char>(-1)); | |||
if (global.isOnline() && global.UseMultiChannelTuning) | |||
{ | |||
int channelsInUse[16];int nMultiChannels=0; | |||
for (int i=0;i<16;i++) if (global.UseMultiChannelTuning(i) && global.multi_channel_esp_retuning[i]) channelsInUse[nMultiChannels++]=i; | |||
if (nMultiChannels) | |||
{ | |||
const int nFreqs=128*nMultiChannels; | |||
int iLower,iUpper,channel,note;iLower=0;iUpper=0;channel=0;note=0; | |||
double dLower,dUpper;dLower=0;dUpper=0; | |||
for (int i=0;i<nFreqs;i++) | |||
{ | |||
channel=channelsInUse[i>>7];note=i&127; | |||
if (global.ShouldFilterNoteMultiChannel && global.ShouldFilterNoteMultiChannel(static_cast<char>(note),static_cast<char>(channel))) continue; | |||
double d=global.multi_channel_esp_retuning[channel][note]-freq; | |||
if (d==0.) {*midichannel=static_cast<char>(channel);return static_cast<char>(note);} | |||
if (d<0) {if (dLower==0. || d>dLower) {dLower=d;iLower=i;}} | |||
else if (dUpper==0. || d<dUpper) {dUpper=d;iUpper=i;} | |||
} | |||
if (dLower==0.) {*midichannel=static_cast<char>(channelsInUse[iUpper>>7]);return static_cast<char>(iUpper&127);} | |||
if (dUpper==0. || iLower==iUpper) {*midichannel=static_cast<char>(channelsInUse[iLower>>7]);return static_cast<char>(iLower&127);} | |||
double fLower=global.multi_channel_esp_retuning[channelsInUse[iLower>>7]][iLower&127]; | |||
double fUpper=global.multi_channel_esp_retuning[channelsInUse[iUpper>>7]][iUpper&127]; | |||
double fmid=fLower*pow(2.,0.5*(log(fUpper/fLower)/ln2)); | |||
if (freq<fmid) {*midichannel=static_cast<char>(channelsInUse[iLower>>7]);return static_cast<char>(iLower&127);} | |||
*midichannel=static_cast<char>(channelsInUse[iUpper>>7]);return static_cast<char>(iUpper&127); | |||
} | |||
} | |||
*midichannel=static_cast<char>(0); | |||
return freqToNote(freq,static_cast<char>(0)); | |||
} | |||
inline void parseMIDIData(const unsigned char *buffer,int len) | |||
{ | |||
supportsMTSSysex=true; | |||
int sysex_ctr=0,sysex_value=0,note=0,numTunings=0; | |||
/*int bank=-1,prog=0,checksum=0,deviceID=0;short int channelBitmap=0;bool realtime=false;*/ // unused for now | |||
eSysexState state=eIgnoring;eMTSFormat format=eBulk; | |||
for (int i=0;i<len;i++) | |||
{ | |||
unsigned char b=buffer[i]; | |||
if (b==0xF7) {state=eIgnoring;continue;} | |||
if (b>0x7F && b!=0xF0) continue; | |||
switch (state) | |||
{ | |||
case eIgnoring: | |||
if (b==0xF0) state=eMatchingSysex; | |||
break; | |||
case eMatchingSysex: | |||
sysex_ctr=0; | |||
if (b==0x7E) state=eSysexValid; | |||
else if (b==0x7F) {/*realtime=true;*/state=eSysexValid;} | |||
else state=eIgnoring; | |||
break; | |||
case eSysexValid: | |||
switch (sysex_ctr++) // handle device ID | |||
{ | |||
case 0: /*deviceID=b;*/ break; | |||
case 1: if (b==0x08) state=eMatchingMTS; break; | |||
default: state=eIgnoring; break; // it's not an MTS message | |||
} | |||
break; | |||
case eMatchingMTS: | |||
sysex_ctr=0; | |||
switch (b) | |||
{ | |||
case 0: format=eRequest;state=eMatchingProg; break; | |||
case 1: format=eBulk;state=eMatchingProg; break; | |||
case 2: format=eSingle;state=eMatchingProg; break; | |||
case 3: format=eRequest;state=eMatchingBank; break; | |||
case 4: format=eBulk;state=eMatchingBank; break; | |||
case 5: format=eScaleOctOneByte;state=eMatchingBank; break; | |||
case 6: format=eScaleOctTwoByte;state=eMatchingBank; break; | |||
case 7: format=eSingle;state=eMatchingBank; break; | |||
case 8: format=eScaleOctOneByteExt;state=eMatchingChannel; break; | |||
case 9: format=eScaleOctTwoByteExt;state=eMatchingChannel; break; | |||
default: state=eIgnoring; break; // it's not a valid MTS format | |||
} | |||
break; | |||
case eMatchingBank: | |||
/*bank=b;*/ | |||
state=eMatchingProg; | |||
break; | |||
case eMatchingProg: | |||
/*prog=b;*/ | |||
if (format==eSingle) state=eNumTunings;else {state=eTuningName;tuningName[0]='\0';} | |||
break; | |||
case eTuningName: | |||
tuningName[sysex_ctr]=static_cast<char>(b); | |||
if (++sysex_ctr>=16) {tuningName[16]='\0';sysex_ctr=0;state=eTuningData;} | |||
break; | |||
case eNumTunings: | |||
numTunings=b;sysex_ctr=0;state=eTuningData; | |||
break; | |||
case eMatchingChannel: | |||
switch (sysex_ctr++) | |||
{ | |||
case 0: /*for (int j=14;j<16;j++) channelBitmap|=(1<<j);*/ break; | |||
case 1: /*for (int j=7;j<14;j++) channelBitmap|=(1<<j);*/ break; | |||
case 2: /*for (int j=0;j<7;j++) channelBitmap|=(1<<j);*/sysex_ctr=0;state=eTuningData; break; | |||
} | |||
break; | |||
case eTuningData: | |||
switch (format) | |||
{ | |||
case eBulk: | |||
sysex_value=(sysex_value<<7)|b; | |||
sysex_ctr++; | |||
if ((sysex_ctr&3)==3) | |||
{ | |||
if (!(note==0x7F && sysex_value==16383)) updateTuning(note,(sysex_value>>14)&127,(sysex_value&16383)/16383.); | |||
sysex_value=0;sysex_ctr++; | |||
if (++note>=128) state=eCheckSum; | |||
} | |||
break; | |||
case eSingle: | |||
sysex_value=(sysex_value<<7)|b; | |||
sysex_ctr++; | |||
if (!(sysex_ctr&3)) | |||
{ | |||
if (!(note==0x7F && sysex_value==16383)) updateTuning((sysex_value>>21)&127,(sysex_value>>14)&127,(sysex_value&16383)/16383.); | |||
sysex_value=0; | |||
if (++note>=numTunings) state=eIgnoring; | |||
} | |||
break; | |||
case eScaleOctOneByte: case eScaleOctOneByteExt: | |||
for (int j=sysex_ctr;j<128;j+=12) updateTuning(j,j,(static_cast<double>(b)-64.)*0.01); | |||
if (++sysex_ctr>=12) state=format==eScaleOctOneByte?eCheckSum:eIgnoring; | |||
break; | |||
case eScaleOctTwoByte: case eScaleOctTwoByteExt: | |||
sysex_value=(sysex_value<<7)|b; | |||
sysex_ctr++; | |||
if (!(sysex_ctr&1)) | |||
{ | |||
double detune=(static_cast<double>(sysex_value&16383)-8192.)/(sysex_value>8192?8191.:8192.); | |||
for (int j=note;j<128;j+=12) updateTuning(j,j,detune); | |||
if (++note>=12) state=format==eScaleOctTwoByte?eCheckSum:eIgnoring; | |||
} | |||
break; | |||
default: state=eIgnoring; break; | |||
} | |||
break; | |||
case eCheckSum: | |||
/*checksum=b;*/ | |||
state=eIgnoring; | |||
break; | |||
} | |||
} | |||
} | |||
inline void updateTuning(int note,int retuneNote,double detune) | |||
{ | |||
if (note<0 || note>127 || retuneNote<0 || retuneNote>127) return; | |||
retuning[note]=440.*pow(2.,((retuneNote+detune)-69.)/12.); | |||
} | |||
const char *getScaleName() {return global.isOnline() && global.GetScaleName?global.GetScaleName():tuningName;} | |||
enum eSysexState {eIgnoring=0,eMatchingSysex,eSysexValid,eMatchingMTS,eMatchingBank,eMatchingProg,eMatchingChannel,eTuningName,eNumTunings,eTuningData,eCheckSum}; | |||
enum eMTSFormat {eRequest=0,eBulk,eSingle,eScaleOctOneByte,eScaleOctTwoByte,eScaleOctOneByteExt,eScaleOctTwoByteExt}; | |||
double retuning[128]; | |||
char tuningName[17]; | |||
bool supportsNoteFiltering,supportsMultiChannelNoteFiltering,supportsMultiChannelTuning,freqRequestReceived,supportsMTSSysex; | |||
}; | |||
static char freqToNoteET(double freq) | |||
{ | |||
static double freqs[128];static bool init=false; | |||
if (!init) {for (int i=0;i<128;i++) freqs[i]=440.*pow(2.,(i-69.)/12.);init=true;} | |||
if (freq<=freqs[0]) return 0; | |||
if (freq>=freqs[127]) return 127; | |||
int mid=0;int n=-1;int n2=-1; | |||
for (int first=0,last=127;freq!=freqs[(mid=first+(last-first)/2)];(freq<freqs[mid])?last=mid-1:first=mid+1) if (first>last) | |||
{ | |||
if (!mid) {n=mid;break;} | |||
if (mid>127) mid=127; | |||
n=mid-((freq-freqs[mid-1])<(freqs[mid]-freq)); | |||
break; | |||
} | |||
if (n==-1) {if (freq==freqs[mid]) n=mid;else return 60;} | |||
if (!n) n2=1; | |||
else if (n==127) n2=126; | |||
else n2=n+1*(fabs(freqs[n-1]-freq)<fabs(freqs[n+1]-freq)?-1:1); | |||
if (n2<n) {int t=n;n=n2;n2=t;} | |||
double fmid=freqs[n]*pow(2.,0.5*(log(freqs[n2]/freqs[n])/ln2)); | |||
return freq<fmid?static_cast<char>(n):static_cast<char>(n2); | |||
} | |||
// Exported functions: | |||
MTSClient* MTS_RegisterClient() {return new MTSClient;} | |||
void MTS_DeregisterClient(MTSClient* c) {delete c;} | |||
bool MTS_HasMaster(MTSClient* c) {return c?c->hasMaster():false;} | |||
bool MTS_ShouldFilterNote(MTSClient* c,char midinote,char midichannel) {return c?c->shouldFilterNote(midinote&127,midichannel):false;} | |||
double MTS_NoteToFrequency(MTSClient* c,char midinote,char midichannel) {return c?c->freq(midinote,midichannel):(1./global.iet[midinote&127]);} | |||
double MTS_RetuningAsRatio(MTSClient* c,char midinote,char midichannel) {return c?c->ratio(midinote,midichannel):1.;} | |||
double MTS_RetuningInSemitones(MTSClient* c,char midinote,char midichannel) {return c?c->semitones(midinote,midichannel):0.;} | |||
char MTS_FrequencyToNote(MTSClient *c,double freq,char midichannel) {return c?c->freqToNote(freq,midichannel):freqToNoteET(freq);} | |||
char MTS_FrequencyToNoteAndChannel(MTSClient *c,double freq,char *midichannel) {if (c) return c->freqToNote(freq,midichannel);if (midichannel) *midichannel=0;return freqToNoteET(freq);} | |||
const char *MTS_GetScaleName(MTSClient *c) {return c?c->getScaleName():"";} | |||
void MTS_ParseMIDIDataU(MTSClient *c,const unsigned char *buffer,int len) {if (c) c->parseMIDIData(buffer,len);} | |||
void MTS_ParseMIDIData(MTSClient *c,const char *buffer,int len) {if (c) c->parseMIDIData(reinterpret_cast<const unsigned char*>(buffer),len);} | |||
@@ -0,0 +1,161 @@ | |||
/* | |||
Copyright (C) 2021 by ODDSound Ltd. info@oddsound.com | |||
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. | |||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF | |||
THIS SOFTWARE. | |||
*/ | |||
#ifndef libMTSClient_h | |||
#define libMTSClient_h | |||
#ifdef __cplusplus | |||
extern "C" { | |||
#endif | |||
/* | |||
Steps for using the MTS-ESP client API to add microtuning support to a plug-in. | |||
Steps 1 and 2 are required, however it is recommended to include further steps when | |||
integrating: | |||
1. REQUIRED: Register and de-register a plug-in instance as a client with MTS-ESP. | |||
On startup in the plug-in constructor call: | |||
MTSClient *client = MTS_RegisterClient(); | |||
Store the returned MTSClient pointer to supply when calling other MTS-ESP client API | |||
functions. On shutdown in the plug-in destructor call: | |||
MTS_DeregisterClient(client); | |||
2. REQUIRED: Query retuning when a note-on message is received and adjust tuning accordingly. | |||
When given a note call: | |||
double freq = MTS_NoteToFrequency(client, midinote, midichannel); | |||
OR | |||
double retune_semitones = MTS_RetuningInSemitones(client, midinote, midichannel); | |||
OR | |||
double retune_ratio = MTS_RetuningAsRatio(client, midinote, midichannel); | |||
MIDI channel arguments should use the range [0,15] however if you don’t know the MIDI | |||
channel, use -1 (see step 6 for more on MIDI channels). | |||
3. RECOMMENDED: Continuously query retuning whilst a note is held, allowing tuning to change | |||
along the flight of a note. Do this if you can and as often as possible, ideally at the same | |||
time as processing any other pitch modulation sources (envelopes, MIDI controllers, LFOs etc.). | |||
4. RECOMMENDED: Provide an option to the user to select whether tuning is queried at note-on | |||
only, as in step 2, or continuously, as in step 3. There are creative and practical | |||
advantages to both, depending on the use case, and offering an option to the user will | |||
provide the most useful MTS-ESP integration. If not offering such an option, continuous | |||
retuning should be preferred over note-on only retuning. | |||
5. RECOMMENDED: Query whether a note should be sounded when a note-on message is received. | |||
The Scala .kbm keyboard mapping format allows for MIDI keys to be unmapped i.e. no frequency | |||
is specified for them, and the MTS-ESP library supports this too. You can query whether a note | |||
is unmapped and should be ignored with: | |||
bool should_ignore_note = MTS_ShouldFilterNote(client, midinote, midichannel); | |||
If this returns true, ignore the note-on and don’t play anything. Calling this function is | |||
recommended but optional and a valid value for frequency/retuning will be returned for an | |||
unmapped note. MIDI channel arguments should use the range [0,15] however if you don’t | |||
know the MIDI channel, use -1. | |||
6. RECOMMENDED: Always supply a MIDI channel when querying retuning or note filtering. Doing | |||
so allows your plug-in to multi-channel tuning tables, useful for microtonal MIDI controllers | |||
with more than 128 keys or working with large scales. Even if multi-channel tables are not | |||
in use, a master may still make use of channel-specific note filtering for functions such as | |||
key switches to change tunings. If your plug-in supports MPE and has a switch for enabling MPE | |||
support, it is recommended to NOT supply a MIDI channel if MPE is enabled. | |||
7. RECOMMENDED: If you are adding MTS-ESP support to a plug-in that already has some kind | |||
of microtuning support, e.g. loading .scl or .tun files, let the local tuning automatically | |||
override MTS-ESP, or provide an option for MTS-ESP retuning to be explicitly disabled. | |||
This affords a user the option to use a different tuning to the global MTS-ESP table | |||
for a specific plug-in instance. | |||
8. OPTIONAL: Add support for MIDI Tuning Standard (or MTS, from the MIDI specification) SysEx | |||
messages to your plug-in. When not connected to an MTS-ESP master plug-in, these can be used | |||
to retune it instead, providing microtuing support even when MTS-ESP is not in use. | |||
When a SysEx message is received, call: | |||
MTS_ParseMIDIData(client, buffer, len); // if buffer is signed char * | |||
OR | |||
MTS_ParseMIDIDataU(client, buffer, len); // if buffer is unsigned char * | |||
These will update a local tuning table which is used when querying retuning as in steps 2 | |||
and 3. | |||
9. OPTIONAL: If you want to display to the user whether the plug-in is "connnected" to an | |||
MTS-ESP master plug-in, call: | |||
bool has_master = MTS_HasMaster(client); | |||
10: OPTIONAL: It is possible to query the name of the current scale. This function is necessarily | |||
supplied for the case where a client is sending MTS SysEx messages, however it can be used | |||
to display the current scale name to the user on your UI too: | |||
const char *name = MTS_GetScaleName(client); | |||
11: EXTRAS: Helper functions are available which return the MIDI note whose pitch is nearest | |||
a given frequency. The MIDI note returned is guaranteed to be mapped. If you intend to | |||
generate a note-on message using the returned note number, you may already know which MIDI | |||
channel it will be sent on, in which case you must specify this in the call, else the client | |||
library can prescribe a channel for you. This is done so that multi-channel mapping | |||
and note filtering can be respected. See below for further details. | |||
*/ | |||
// Opaque datatype for MTSClient. | |||
typedef struct MTSClient MTSClient; | |||
// Register/deregister as a client. Call from the plugin constructor and destructor. | |||
extern MTSClient *MTS_RegisterClient(); | |||
extern void MTS_DeregisterClient(MTSClient *client); | |||
// Check if the client is currently connected to a master plugin. | |||
extern bool MTS_HasMaster(MTSClient *client); | |||
// Returns true if note should not be played. MIDI channel argument should be included if possible (0-15), else set to -1. | |||
extern bool MTS_ShouldFilterNote(MTSClient *client, char midinote, char midichannel); | |||
// Retuning a midi note. Pick the version that makes your life easiest! MIDI channel argument should be included if possible (0-15), else set to -1. | |||
extern double MTS_NoteToFrequency(MTSClient *client, char midinote, char midichannel); | |||
extern double MTS_RetuningInSemitones(MTSClient *client, char midinote, char midichannel); | |||
extern double MTS_RetuningAsRatio(MTSClient *client, char midinote, char midichannel); | |||
// MTS_FrequencyToNote() is a helper function returning the note number whose pitch is closest to the supplied frequency. Two versions are provided: | |||
// The first is for the simplest case: supply a frequency and get a note number back. | |||
// If you intend to use the returned note number to generate a note-on message on a specific, pre-determined MIDI channel, set the midichannel argument to the destination channel (0-15), else set to -1. | |||
// If a MIDI channel is supplied, the corresponding multi-channel tuning table will be queried if in use, else multi-channel tables are ignored. | |||
extern char MTS_FrequencyToNote(MTSClient *client, double freq, char midichannel); | |||
// Use the second version if you intend to use the returned note number to generate a note-on message and where you have the possibility to send it on any MIDI channel. | |||
// The midichannel argument is a pointer to a char which will receive the MIDI channel on which the note message should be sent (0-15). | |||
// Multi-channel tuning tables are queried if in use. | |||
extern char MTS_FrequencyToNoteAndChannel(MTSClient *client, double freq, char *midichannel); | |||
// Returns the name of the current scale. | |||
extern const char *MTS_GetScaleName(MTSClient *client); | |||
// Parse incoming MIDI data to update local retuning. All formats of MTS sysex message accepted. | |||
extern void MTS_ParseMIDIDataU(MTSClient *client, const unsigned char *buffer, int len); | |||
extern void MTS_ParseMIDIData(MTSClient *client, const char *buffer, int len); | |||
#ifdef __cplusplus | |||
} | |||
#endif | |||
#endif | |||
@@ -12,77 +12,301 @@ | |||
#ifndef PLUGINEDITOR_H_INCLUDED | |||
#define PLUGINEDITOR_H_INCLUDED | |||
#include "JuceHeader.h" | |||
#include "../JuceLibraryCode/JuceHeader.h" | |||
#include "PluginProcessor.h" | |||
#include "Gui/Knob.h" | |||
#include "Gui/TooglableButton.h" | |||
#include "Gui/ButtonList.h" | |||
#include "Components/SetPresetNameWindow.h" | |||
#include "Components/PresetBar.h" | |||
#include "Components/ScaleComponent.h" | |||
enum KeyPressCommandIDs | |||
{ | |||
buttonNextProgram = 1, | |||
buttonPrevProgram, | |||
buttonPadNextProgram, | |||
buttonPadPrevProgram, | |||
}; | |||
enum MenuAction | |||
{ | |||
Cancel = 0, | |||
ToggleMidiKeyboard, | |||
ImportPreset, | |||
ImportBank, | |||
ExportBank, | |||
ExportPreset, | |||
SavePreset, | |||
NewPreset, | |||
RenamePreset, | |||
DeletePreset, | |||
DeleteBank, | |||
ShowBanks, | |||
CopyPreset, | |||
PastePreset, | |||
LoadBank // LoadBank must be the last enum value | |||
}; | |||
//============================================================================== | |||
/** | |||
*/ | |||
class ObxdAudioProcessorEditor : | |||
public AudioProcessorEditor, | |||
// public AudioProcessorListener, | |||
public ChangeListener, | |||
public Slider::Listener, | |||
public Button::Listener, | |||
public ComboBox::Listener | |||
class ObxdAudioProcessorEditor : public AudioProcessorEditor | |||
// , public AudioProcessorListener | |||
, public AsyncUpdater | |||
, public ChangeListener | |||
// , public Slider::Listener | |||
, public Button::Listener | |||
// , public ComboBox::Listener | |||
, public ActionListener | |||
, public ApplicationCommandTarget | |||
, public Timer | |||
, public FileDragAndDropTarget | |||
, public ScalableComponent | |||
{ | |||
public: | |||
ObxdAudioProcessorEditor(ObxdAudioProcessor* ownerFilter); | |||
ObxdAudioProcessorEditor(ObxdAudioProcessor& ownerFilter); | |||
~ObxdAudioProcessorEditor(); | |||
void mouseUp(const MouseEvent& e); | |||
void paint(Graphics& g); | |||
bool isInterestedInFileDrag(const StringArray& files) override; | |||
void filesDropped(const StringArray& files, int x, int y) override; | |||
void scaleFactorChanged() override; | |||
void mouseUp (const MouseEvent& e) override; | |||
void paint (Graphics& g) override; | |||
void updateFromHost(); | |||
void handleAsyncUpdate() override; | |||
String getCurrentProgramName(){ | |||
return processor.getProgramName(processor.getCurrentProgram()); | |||
} | |||
void updatePresetBar(bool resize=true); | |||
//============================================================================== | |||
void changeListenerCallback (ChangeBroadcaster* source); | |||
void changeListenerCallback (ChangeBroadcaster* source) override; | |||
void buttonClicked (Button *) override; | |||
//bool keyPressed(const KeyPress & press) override; | |||
void timerCallback() override { | |||
private: | |||
Knob* addNormalKnob(int x , int y ,ObxdAudioProcessor* filter, int parameter,String name,float defval); | |||
Knob* addTinyKnob(int x , int y ,ObxdAudioProcessor* filter, int parameter,String name,float defval); | |||
void placeLabel(int x , int y,String text); | |||
TooglableButton* addNormalTooglableButton(int x , int y , ObxdAudioProcessor* filter,int parameter,String name); | |||
TooglableButton* addTinyTooglableButton(int x , int y , ObxdAudioProcessor* filter,int parameter,String name); | |||
countTimer ++; | |||
if (countTimer == 4 && needNotifytoHost){ | |||
countTimer = 0; | |||
needNotifytoHost= false; | |||
processor.updateHostDisplay(); | |||
} | |||
ButtonList* addNormalButtonList(int x , int y ,int width, ObxdAudioProcessor* filter,int parameter,String name,Image img); | |||
void sliderValueChanged (Slider*); | |||
void buttonClicked (Button *); | |||
void comboBoxChanged(ComboBox*); | |||
countTimerForLed++; | |||
if (midiUnlearnButton && midiUnlearnButton->getToggleState() && countTimerForLed > 3) { | |||
midiUnlearnButton->setToggleState(false, NotificationType::sendNotification); | |||
countTimerForLed = 0; | |||
} | |||
} | |||
ApplicationCommandTarget* getNextCommandTarget() override { | |||
return nullptr; | |||
}; | |||
void getAllCommands (Array<CommandID>& commands) override { | |||
Array<CommandID> ids { KeyPressCommandIDs::buttonNextProgram, KeyPressCommandIDs::buttonPrevProgram, | |||
KeyPressCommandIDs::buttonPadNextProgram, KeyPressCommandIDs::buttonPadPrevProgram | |||
}; | |||
Knob* addNormalKnobClassic(int x , int y ,ObxdAudioProcessor* filter, int parameter,String name,float defval); | |||
Knob* addTinyKnobClassic(int x , int y ,ObxdAudioProcessor* filter, int parameter,String name,float defval); | |||
TooglableButton* addNormalTooglableButtonClassic(int x , int y , ObxdAudioProcessor* filter,int parameter,String name); | |||
ButtonList* addNormalButtonListClassic(int x , int y ,int width, ObxdAudioProcessor* filter,int parameter,String name,Image img); | |||
commands.addArray (ids); | |||
}; | |||
void getCommandInfo (CommandID commandID, ApplicationCommandInfo& result) override { | |||
switch (commandID) | |||
{ | |||
case KeyPressCommandIDs::buttonNextProgram: | |||
result.setInfo ("Move up", "Move the button + ", "Button", 0); | |||
result.addDefaultKeypress ('+', 0); | |||
result.setActive (true); | |||
break; | |||
case KeyPressCommandIDs::buttonPrevProgram: | |||
result.setInfo ("Move right", "Move the button - ", "Button", 0); | |||
result.addDefaultKeypress ('-', 0); | |||
result.setActive (true); | |||
break; | |||
case KeyPressCommandIDs::buttonPadNextProgram: | |||
result.setInfo ("Move down", "Move the button Pad + ", "Button", 0); | |||
result.addDefaultKeypress (KeyPress::numberPadAdd, 0); | |||
result.setActive (true); | |||
break; | |||
case KeyPressCommandIDs::buttonPadPrevProgram: | |||
result.setInfo ("Move left", "Move the button Pad -", "Button", 0); | |||
result.addDefaultKeypress (KeyPress::numberPadSubtract, 0); | |||
result.setActive (true); | |||
break; | |||
default: | |||
break; | |||
} | |||
}; | |||
bool perform (const InvocationInfo& info) override { | |||
void rebuildComponents(); | |||
switch (info.commandID) | |||
{ | |||
case KeyPressCommandIDs::buttonNextProgram: | |||
case KeyPressCommandIDs::buttonPadNextProgram: | |||
nextProgram(); | |||
grabKeyboardFocus(); | |||
break; | |||
//============================================================================== | |||
ObxdAudioProcessor* getFilter() noexcept { return (ObxdAudioProcessor*)getAudioProcessor();} | |||
case KeyPressCommandIDs::buttonPrevProgram: | |||
case KeyPressCommandIDs::buttonPadPrevProgram: | |||
prevProgram(); | |||
grabKeyboardFocus(); | |||
break; | |||
default: | |||
return false; | |||
} | |||
return true; | |||
};/* | |||
bool keyPressed (const KeyPress& key, | |||
Component* originatingComponent) override { | |||
DBG("--- " << key.getKeyCode()); | |||
};*/ | |||
void nextProgram(); | |||
void prevProgram(); | |||
void MenuActionCallback(int action); | |||
void deleteBank(); | |||
void resized() override; | |||
bool isHighResolutionDisplay() const | |||
{ | |||
return processor.physicalPixelScaleFactor > 1.0; | |||
} | |||
void actionListenerCallback(const String& message) override; | |||
private: | |||
Rectangle<int> transformBounds(int x, int y, int w, int h); | |||
Knob* addKnob (int x, int y, int d, ObxdAudioProcessor& filter, int parameter, String name, float defval); | |||
void placeLabel (int x, int y, String text); | |||
TooglableButton* addButton (int x, int y, int w, int h, ObxdAudioProcessor& filter, int parameter, String name); | |||
ButtonList* addList(int x, int y, int w, int h, ObxdAudioProcessor& filter, int parameter, String name, String nameImg); | |||
ImageButton* addMenuButton (int x, int y, int d, String nameImg); | |||
void createMenu (); | |||
void createMidi(int, PopupMenu &); | |||
void resultFromMenu (const Point<int>); | |||
void clean(); | |||
void rebuildComponents (ObxdAudioProcessor&); | |||
void loadSkin(ObxdAudioProcessor&); | |||
//============================================================================== | |||
public: | |||
ObxdAudioProcessor& processor; | |||
private: | |||
// images | |||
Image backgroundImage; | |||
std::map<String, Component*> mappingComps; | |||
//============================================================================== | |||
Knob* cutoffKnob,*resonanceKnob,*osc1PitchKnob,*osc2PitchKnob,*osc2DetuneKnob,*volumeKnob, | |||
*portamentoKnob,*voiceDetuneKnob,*filterEnvelopeAmtKnob,*pulseWidthKnob,*xmodKnob,*multimodeKnob,*attackKnob,*decayKnob,*sustainKnob,*releaseKnob, | |||
*fattackKnob,*fdecayKnob,*fsustainKnob,*freleaseKnob,*osc1MixKnob,*osc2MixKnob,*noiseMixKnob, | |||
*filterDetuneKnob,*envelopeDetuneKnob,*portamentoDetuneKnob, | |||
*tuneKnob, | |||
*lfoFrequencyKnob,*lfoAmt1Knob,*lfoAmt2Knob, | |||
*pan1Knob,*pan2Knob,*pan3Knob,*pan4Knob,*pan5Knob,*pan6Knob,*pan7Knob,*pan8Knob, | |||
*brightnessKnob,*envPitchModKnob, | |||
*bendLfoRateKnob,*veloAmpEnvKnob,*veloFltEnvKnob,*transposeKnob; | |||
TooglableButton* hardSyncButton,*osc1SawButton,*osc2SawButton,*osc1PulButton,*osc2PulButton,*filterKeyFollowButton,*unisonButton,*pitchQuantButton, | |||
*filterHQButton,*filterBPBlendButton, | |||
*lfoSinButton,*lfoSquareButton,*lfoSHButton,*lfoOsc1Button,*lfoOsc2Button,*lfoFilterButton, | |||
*lfoPwm1Button,*lfoPwm2Button, | |||
*bendRangeButton,*bendOsc2OnlyButton, | |||
*fourPoleButton,*asPlayedAllocButton; | |||
ButtonList *voiceSwitch,*legatoSwitch; | |||
Knob* cutoffKnob=nullptr, | |||
*resonanceKnob=nullptr, | |||
*osc1PitchKnob=nullptr, | |||
*osc2PitchKnob=nullptr, | |||
*osc2DetuneKnob=nullptr, | |||
*volumeKnob=nullptr, | |||
*portamentoKnob=nullptr, | |||
*voiceDetuneKnob=nullptr, | |||
*filterEnvelopeAmtKnob=nullptr, | |||
*pulseWidthKnob=nullptr, | |||
*xmodKnob=nullptr, | |||
*multimodeKnob=nullptr, | |||
*attackKnob=nullptr, | |||
*decayKnob=nullptr, | |||
*sustainKnob=nullptr, | |||
*releaseKnob=nullptr, | |||
*fattackKnob=nullptr, | |||
*fdecayKnob=nullptr, | |||
*fsustainKnob=nullptr, | |||
*freleaseKnob=nullptr, | |||
*osc1MixKnob=nullptr, | |||
*osc2MixKnob=nullptr, | |||
*noiseMixKnob=nullptr, | |||
*filterDetuneKnob=nullptr, | |||
*envelopeDetuneKnob=nullptr, | |||
*portamentoDetuneKnob=nullptr, | |||
*tuneKnob=nullptr, | |||
*lfoFrequencyKnob=nullptr, | |||
*lfoAmt1Knob=nullptr, | |||
*lfoAmt2Knob=nullptr, | |||
*pan1Knob=nullptr, | |||
*pan2Knob=nullptr, | |||
*pan3Knob=nullptr, | |||
*pan4Knob=nullptr, | |||
*pan5Knob=nullptr, | |||
*pan6Knob=nullptr, | |||
*pan7Knob=nullptr, | |||
*pan8Knob=nullptr, | |||
*brightnessKnob=nullptr, | |||
*envPitchModKnob=nullptr, | |||
*bendLfoRateKnob=nullptr, | |||
*veloAmpEnvKnob=nullptr, | |||
*veloFltEnvKnob=nullptr, | |||
*transposeKnob=nullptr; | |||
TooglableButton* hardSyncButton=nullptr, | |||
*osc1SawButton=nullptr, | |||
*osc2SawButton=nullptr, | |||
*osc1PulButton=nullptr, | |||
*osc2PulButton=nullptr, | |||
*filterKeyFollowButton=nullptr, | |||
*unisonButton=nullptr, | |||
*pitchQuantButton=nullptr, | |||
*filterHQButton=nullptr, | |||
*filterBPBlendButton=nullptr, | |||
*lfoSinButton=nullptr, | |||
*lfoSquareButton=nullptr, | |||
*lfoSHButton=nullptr, | |||
*lfoOsc1Button=nullptr, | |||
*lfoOsc2Button=nullptr, | |||
*lfoFilterButton=nullptr, | |||
*lfoPwm1Button=nullptr, | |||
*lfoPwm2Button=nullptr, | |||
*bendRangeButton=nullptr, | |||
*bendOsc2OnlyButton=nullptr, | |||
*fourPoleButton=nullptr, | |||
*asPlayedAllocButton=nullptr, | |||
*midiLearnButton=nullptr, | |||
*midiUnlearnButton=nullptr; | |||
std::unique_ptr<ButtonList> voiceSwitch, legatoSwitch; | |||
File skinFolder; | |||
//============================================================================== | |||
OwnedArray<Knob::KnobAttachment> knobAttachments; | |||
OwnedArray<AudioProcessorValueTreeState::ButtonAttachment> toggleAttachments; | |||
OwnedArray<ButtonList::ButtonListAttachment> buttonListAttachments; | |||
OwnedArray<ImageButton> imageButtons; | |||
OwnedArray<PopupMenu> popupMenus; | |||
bool notLoadSkin = false; | |||
int progStart; | |||
int bankStart; | |||
int skinStart; | |||
Array<File> skins; | |||
Array<File> banks; | |||
std::unique_ptr<SetPresetNameWindow> setPresetNameWindow; | |||
std::unique_ptr<PresetBar> presetBar; | |||
std::unique_ptr<FileChooser> fileChooser; | |||
// Command manager | |||
ApplicationCommandManager commandManager; | |||
int countTimer =0; | |||
bool needNotifytoHost = false; | |||
Array<String> midiFiles; | |||
int menuMidiNum; | |||
int menuScaleNum; | |||
int countTimerForLed = 0; | |||
struct Action | |||
{ | |||
static const String panReset; | |||
}; | |||
}; | |||
#endif // PLUGINEDITOR_H_INCLUDED |
@@ -25,7 +25,7 @@ | |||
#ifndef PLUGINPROCESSOR_H_INCLUDED | |||
#define PLUGINPROCESSOR_H_INCLUDED | |||
#include "JuceHeader.h" | |||
#include "../JuceLibraryCode/JuceHeader.h" | |||
#include "Engine/SynthEngine.h" | |||
//#include <stack> | |||
#include "Engine/midiMap.h" | |||
@@ -110,13 +110,18 @@ static inline float fxbSwapFloat (const float x) noexcept | |||
#endif | |||
} | |||
enum class Tooltip | |||
{ | |||
Disable = 0, | |||
StandardDisplay, | |||
FullDisplay | |||
}; | |||
//============================================================================== | |||
/** | |||
*/ | |||
class ObxdAudioProcessor : | |||
public AudioProcessor, | |||
// public AudioProcessorListener, | |||
public ChangeBroadcaster | |||
class ObxdAudioProcessor : public AudioProcessor, | |||
public AudioProcessorValueTreeState::Listener, | |||
public ChangeBroadcaster | |||
{ | |||
public: | |||
//============================================================================== | |||
@@ -124,77 +129,113 @@ public: | |||
~ObxdAudioProcessor(); | |||
//============================================================================== | |||
void prepareToPlay (double sampleRate, int samplesPerBlock); | |||
void releaseResources(); | |||
void prepareToPlay (double sampleRate, int samplesPerBlock) override; | |||
void releaseResources() override; | |||
void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages); | |||
void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) override; | |||
//============================================================================== | |||
AudioProcessorEditor* createEditor(); | |||
bool hasEditor() const; | |||
AudioProcessorEditor* createEditor() override; | |||
bool hasEditor() const override; | |||
//============================================================================== | |||
void processMidiPerSample(MidiBuffer::Iterator* iter,const int samplePos); | |||
bool getNextEvent(MidiBuffer::Iterator* iter,const int samplePos); | |||
void processMidiPerSample (MidiBuffer::Iterator* iter, const int samplePos); | |||
bool getNextEvent (MidiBuffer::Iterator* iter, const int samplePos); | |||
//============================================================================== | |||
void initAllParams(); | |||
int getNumParameters(); | |||
float getParameter (int index); | |||
void setParameter (int index, float newValue); | |||
const String getParameterName (int index); | |||
const String getParameterText (int index); | |||
const String getInputChannelName (int channelIndex) const; | |||
const String getOutputChannelName (int channelIndex) const; | |||
bool isInputChannelStereoPair (int index) const; | |||
bool isOutputChannelStereoPair (int index) const; | |||
bool acceptsMidi() const; | |||
bool producesMidi() const; | |||
bool silenceInProducesSilenceOut() const; | |||
double getTailLengthSeconds() const; | |||
const String getName() const; | |||
void initAllParams(); | |||
void initMidi(); | |||
const String getInputChannelName (int channelIndex) const override; // WATCH OUT! | |||
const String getOutputChannelName (int channelIndex) const override; // WATCH OUT! | |||
bool isInputChannelStereoPair (int index) const override; // WATCH OUT! | |||
bool isOutputChannelStereoPair (int index) const override; // WATCH OUT! | |||
bool acceptsMidi() const override; | |||
bool producesMidi() const override; | |||
double getTailLengthSeconds() const override; | |||
const String getName() const override; | |||
//============================================================================== | |||
int getNumPrograms(); | |||
int getCurrentProgram(); | |||
void setCurrentProgram (int index); | |||
const String getProgramName (int index); | |||
void changeProgramName (int index, const String& newName); | |||
int getNumPrograms() override; | |||
int getCurrentProgram() override; | |||
void setCurrentProgram (int index) override; | |||
void setCurrentProgram (int index, bool updateHost); | |||
const String getProgramName (int index) override; | |||
void changeProgramName (int index, const String& newName) override; | |||
//============================================================================== | |||
void getStateInformation (MemoryBlock& destData); | |||
void setStateInformation (const void* data, int sizeInBytes); | |||
void setCurrentProgramStateInformation(const void* data,int sizeInBytes); | |||
void getCurrentProgramStateInformation(MemoryBlock& destData); | |||
void getStateInformation (MemoryBlock& destData) override; | |||
void setStateInformation (const void* data, int sizeInBytes) override; | |||
void setCurrentProgramStateInformation (const void* data,int sizeInBytes) override; | |||
void getCurrentProgramStateInformation (MemoryBlock& destData) override; | |||
//============================================================================== | |||
void scanAndUpdateBanks(); | |||
void scanAndUpdateSkins(); | |||
const Array<File>& getBankFiles() const; | |||
const Array<File>& getSkinFiles() const; | |||
bool deleteBank(); | |||
bool loadPreset(const File& fxpFile); | |||
bool savePreset(const File& fxpFile); | |||
void serializePreset(MemoryBlock& memoryBlock); // Copy the current Preset into a MemoryBlock | |||
void changePresetName(const String &name); | |||
void newPreset(const String &name); | |||
void deletePreset(); | |||
bool loadFromFXPFile(const File& fxbFile); | |||
bool loadFromFXBFile(const File& fxbFile); | |||
bool isMemoryBlockAPreset(const MemoryBlock& memoryBlock); | |||
bool loadFromMemoryBlock(MemoryBlock& memoryBlock); | |||
bool saveFXBFile(const File& fxbFile); | |||
bool saveFXPFile(const File& fxpFile); | |||
bool saveBank(const File& fxbFile); | |||
bool restoreProgramSettings(const fxProgram* const prog); | |||
File getCurrentBankFile() const; | |||
MidiMap &getMidiMap(){ return bindings; } | |||
//============================================================================== | |||
const ObxdBank& getPrograms() const { return programs; } | |||
//============================================================================== | |||
File getDocumentFolder() const; | |||
File getSkinFolder() const; | |||
File getPresetsFolder() const; | |||
File getBanksFolder() const; | |||
File getMidiFolder() const; | |||
File getCurrentSkinFolder() const; | |||
void setCurrentSkinFolder(const String& folderName); | |||
void setGuiSize(const int gui_size); | |||
Tooltip getTooltipBehavior() const; | |||
void setTooltipBehavior(const Tooltip tooltip); | |||
//============================================================================== | |||
static String getEngineParameterId (size_t); | |||
static String getTrueParameterValueFromNormalizedRange(size_t, float normalizedValue); | |||
int getParameterIndexFromId (String); | |||
void setEngineParameterValue (int, float, bool notifyToHost= false); | |||
void parameterChanged (const String&, float) override; | |||
AudioProcessorValueTreeState& getPluginState(); | |||
bool getShowPresetBar(){ | |||
return this->showPresetBar; | |||
} | |||
void setShowPresetBar(bool val){ | |||
this->showPresetBar = val; | |||
config->setValue("presetnavigation", this->showPresetBar); | |||
} | |||
private: | |||
//============================================================================== | |||
bool isHostAutomatedChange; | |||
MidiMessage nextMidi, midiMsg; | |||
int lastMovedController; | |||
int lastUsedParameter; | |||
MidiMessage* nextMidi; | |||
MidiMessage* midiMsg; | |||
bool midiControlledParamSet; | |||
bool hasMidiMessage; | |||
int midiEventPos; | |||
@@ -202,12 +243,34 @@ private: | |||
SynthEngine synth; | |||
ObxdBank programs; | |||
String currentSkin; | |||
public: | |||
float physicalPixelScaleFactor; | |||
int gui_size; | |||
String currentSkin; | |||
String currentBank; | |||
File currentBankFile; | |||
void saveBank(); | |||
String currentMidiPath; | |||
String currentPreset; | |||
File currentPresetFile; | |||
void savePreset(); | |||
MidiMap bindings; | |||
bool showPresetBar = false; | |||
void updateConfig(); | |||
private: | |||
Array<File> bankFiles; | |||
Array<File> skinFiles; | |||
Tooltip tooltipBehavior; | |||
ScopedPointer<PropertiesFile> config; | |||
std::unique_ptr<PropertiesFile> config; | |||
InterProcessLock configLock; | |||
//============================================================================== | |||
AudioProcessorValueTreeState apvtState; | |||
UndoManager undoManager; | |||
//============================================================================== | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ObxdAudioProcessor) | |||
@@ -0,0 +1,15 @@ | |||
#pragma once | |||
#include "Utils.h" | |||
void openInPdf(const File& file) | |||
{ | |||
if (file.existsAsFile()) { | |||
file.startAsProcess(); | |||
} | |||
else { | |||
NativeMessageBox::showMessageBox(AlertWindow::WarningIcon, "Error", "OB-Xd Manual.pdf not found."); | |||
} | |||
} |
@@ -0,0 +1,3 @@ | |||
#pragma once | |||
#include "../JuceLibraryCode/JuceHeader.h" | |||
void openInPdf(const File& file); |