diff --git a/Dexed.jucer b/Dexed.jucer index 32840720..4a54304c 100644 --- a/Dexed.jucer +++ b/Dexed.jucer @@ -16,6 +16,25 @@ jucerFormatVersion="1"> + + + + + + + + + + + diff --git a/Resources/CMakeLists.txt b/Resources/CMakeLists.txt index 3b3ff081..b25cb163 100644 --- a/Resources/CMakeLists.txt +++ b/Resources/CMakeLists.txt @@ -7,20 +7,20 @@ juce_add_binary_data(DexedResources SOURCES ui/ButtonUnlabeled_50x30.png ui/dexedIcon.png ui/dexed-logo.png - ui/GlobalEditor_864x144.png + ui/GlobalEditor_1728x288.png ui/HelpButton.png ui/HelpButton.svg - ui/Knob_34x34.png + ui/Knob_68x68.png ui/LFO_36_26.png - ui/Light_14x14.png + ui/Light_28x28.png ui/Meter_140x8.png ui/NotoSans-Bold.ttf ui/NotoSans-Regular.ttf - ui/OperatorEditor_287x218.png + ui/OperatorEditor_574x436.png ui/Scaling_36_26.png - ui/Slider_26x26.png - ui/Switch_32x32.png - ui/Switch_48x26.png + ui/Slider_52x52.png + ui/Switch_64x64.png + ui/Switch_96x52.png ui/SwitchLighted_48x26.png builtin_pgm.zip ) diff --git a/Resources/ui/ButtonUnlabeled_100x60.png b/Resources/ui/ButtonUnlabeled_100x60.png new file mode 100644 index 00000000..45912623 Binary files /dev/null and b/Resources/ui/ButtonUnlabeled_100x60.png differ diff --git a/Resources/ui/GlobalEditor_1728x288.png b/Resources/ui/GlobalEditor_1728x288.png new file mode 100644 index 00000000..3ac17c30 Binary files /dev/null and b/Resources/ui/GlobalEditor_1728x288.png differ diff --git a/Resources/ui/Knob_68x68.png b/Resources/ui/Knob_68x68.png new file mode 100644 index 00000000..9f7d019e Binary files /dev/null and b/Resources/ui/Knob_68x68.png differ diff --git a/Resources/ui/LFO_72_52.png b/Resources/ui/LFO_72_52.png new file mode 100644 index 00000000..a9afcc9c Binary files /dev/null and b/Resources/ui/LFO_72_52.png differ diff --git a/Resources/ui/Light_28x28.png b/Resources/ui/Light_28x28.png new file mode 100644 index 00000000..ce3f00d9 Binary files /dev/null and b/Resources/ui/Light_28x28.png differ diff --git a/Resources/ui/Meter_280x16.png b/Resources/ui/Meter_280x16.png new file mode 100644 index 00000000..283d7acf Binary files /dev/null and b/Resources/ui/Meter_280x16.png differ diff --git a/Resources/ui/OperatorEditor_574x436.png b/Resources/ui/OperatorEditor_574x436.png new file mode 100644 index 00000000..41478207 Binary files /dev/null and b/Resources/ui/OperatorEditor_574x436.png differ diff --git a/Resources/ui/Scaling_72-52.png b/Resources/ui/Scaling_72-52.png new file mode 100644 index 00000000..e2679623 Binary files /dev/null and b/Resources/ui/Scaling_72-52.png differ diff --git a/Resources/ui/Slider_52x52.png b/Resources/ui/Slider_52x52.png new file mode 100644 index 00000000..c3f2a4bc Binary files /dev/null and b/Resources/ui/Slider_52x52.png differ diff --git a/Resources/ui/Switch_64x64.png b/Resources/ui/Switch_64x64.png new file mode 100644 index 00000000..0c164e02 Binary files /dev/null and b/Resources/ui/Switch_64x64.png differ diff --git a/Resources/ui/Switch_96x52.png b/Resources/ui/Switch_96x52.png new file mode 100644 index 00000000..272e7859 Binary files /dev/null and b/Resources/ui/Switch_96x52.png differ diff --git a/Resources/ui/dexedIcon.png b/Resources/ui/dexedIcon.png old mode 100755 new mode 100644 diff --git a/Resources/ui/source/Global.skin b/Resources/ui/source/Global.skin index a7b7a923..215e91c3 100644 Binary files a/Resources/ui/source/Global.skin and b/Resources/ui/source/Global.skin differ diff --git a/Resources/ui/source/Knob_68x68.knob b/Resources/ui/source/Knob_68x68.knob new file mode 100644 index 00000000..bff0b2f2 Binary files /dev/null and b/Resources/ui/source/Knob_68x68.knob differ diff --git a/Resources/ui/source/Operator.skin b/Resources/ui/source/Operator.skin index 534a051a..efe57222 100644 Binary files a/Resources/ui/source/Operator.skin and b/Resources/ui/source/Operator.skin differ diff --git a/Resources/ui/source/dexed-logo.xcf b/Resources/ui/source/dexed-logo.xcf old mode 100755 new mode 100644 diff --git a/Source/AlgoDisplay.cpp b/Source/AlgoDisplay.cpp index 03567f42..a478bf72 100644 --- a/Source/AlgoDisplay.cpp +++ b/Source/AlgoDisplay.cpp @@ -115,8 +115,7 @@ void AlgoDisplay::displayOp(Graphics &g, char id, int x, int y, char link, char } -void AlgoDisplay::paint(Graphics &g) { - +void AlgoDisplay::paint(Graphics &g) { g.setColour(DXLookNFeel::fillColour); g.fillRect(1, 3, 20, 15); String n = String(*algo +1); diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 6ce0d34a..c26ec561 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -86,6 +86,7 @@ target_compile_definitions(${BaseTargetName} PUBLIC JUCE_MODAL_LOOPS_PERMITTED=1 # needed for FileBrowser in CartManager JUCE_DISPLAY_SPLASH_SCREEN=0 JUCE_REPORT_APP_USAGE=0 + JUCE_COREGRAPHICS_RENDER_WITH_MULTIPLE_PAINT_CALLS=1 ) target_link_libraries(${BaseTargetName} diff --git a/Source/DXLookNFeel.cpp b/Source/DXLookNFeel.cpp index 525f2c9f..4c7350c2 100644 --- a/Source/DXLookNFeel.cpp +++ b/Source/DXLookNFeel.cpp @@ -62,17 +62,17 @@ DXLookNFeel::DXLookNFeel() { REG_COLOUR(DirectoryContentsDisplayComponent::highlightColourId, fillColour); REG_COLOUR(DirectoryContentsDisplayComponent::textColourId, Colours::white); - imageKnob = ImageCache::getFromMemory(BinaryData::Knob_34x34_png, BinaryData::Knob_34x34_pngSize); - imageSwitch = ImageCache::getFromMemory(BinaryData::Switch_48x26_png, BinaryData::Switch_48x26_pngSize); + imageKnob = ImageCache::getFromMemory(BinaryData::Knob_68x68_png, BinaryData::Knob_68x68_pngSize); // 2x + imageSwitch = ImageCache::getFromMemory(BinaryData::Switch_96x52_png, BinaryData::Switch_96x52_pngSize); // 2x imageSwitchLighted = ImageCache::getFromMemory(BinaryData::SwitchLighted_48x26_png, BinaryData::SwitchLighted_48x26_pngSize); - imageSwitchOperator = ImageCache::getFromMemory(BinaryData::Switch_32x32_png, BinaryData::Switch_32x32_pngSize); + imageSwitchOperator = ImageCache::getFromMemory(BinaryData::Switch_64x64_png, BinaryData::Switch_64x64_pngSize); // 2x imageButton = ImageCache::getFromMemory(BinaryData::ButtonUnlabeled_50x30_png, BinaryData::ButtonUnlabeled_50x30_pngSize); - imageSlider = ImageCache::getFromMemory(BinaryData::Slider_26x26_png, BinaryData::Slider_26x26_pngSize); - imageScaling = ImageCache::getFromMemory(BinaryData::Scaling_36_26_png, BinaryData::Scaling_36_26_pngSize);; - imageLight = ImageCache::getFromMemory(BinaryData::Light_14x14_png, BinaryData::Light_14x14_pngSize); + imageSlider = ImageCache::getFromMemory(BinaryData::Slider_52x52_png, BinaryData::Slider_52x52_pngSize); // 2x + imageScaling = ImageCache::getFromMemory(BinaryData::Scaling_36_26_png, BinaryData::Scaling_36_26_pngSize); // 2x + imageLight = ImageCache::getFromMemory(BinaryData::Light_28x28_png, BinaryData::Light_28x28_pngSize); // 2x imageLFO = ImageCache::getFromMemory(BinaryData::LFO_36_26_png, BinaryData::LFO_36_26_pngSize); - imageOperator = ImageCache::getFromMemory(BinaryData::OperatorEditor_287x218_png, BinaryData::OperatorEditor_287x218_pngSize); - imageGlobal = ImageCache::getFromMemory (BinaryData::GlobalEditor_864x144_png, BinaryData::GlobalEditor_864x144_pngSize); + imageOperator = ImageCache::getFromMemory(BinaryData::OperatorEditor_574x436_png, BinaryData::OperatorEditor_574x436_pngSize); // 2x + imageGlobal = ImageCache::getFromMemory (BinaryData::GlobalEditor_1728x288_png, BinaryData::GlobalEditor_1728x288_pngSize); // 2x File dexedTheme = DexedAudioProcessor::dexedAppDir.getChildFile("DexedTheme.xml"); @@ -107,6 +107,7 @@ DXLookNFeel::DXLookNFeel() { } } + // TODO: THIS IS DEAD CODE. NOBODY IS USING THIS. forEachXmlChildElementWithTagName(*root, image, "image") { String name = image->getStringAttribute("id", ""); String path = image->getStringAttribute("path", ""); @@ -163,6 +164,7 @@ Typeface::Ptr DXLookNFeel::getTypefaceForFont(const Font &) { void DXLookNFeel::drawRotarySlider( Graphics &g, int x, int y, int width, int height, float sliderPosProportional, float rotaryStartAngle, float rotaryEndAngle, Slider &slider ) { + TRACE("knob render"); if ( imageKnob.isNull() ) { LookAndFeel_V3::drawRotarySlider(g, x, y, width, height, sliderPosProportional, rotaryStartAngle, rotaryEndAngle, slider); return; @@ -171,13 +173,13 @@ void DXLookNFeel::drawRotarySlider( Graphics &g, int x, int y, int width, int he const double fractRotation = (slider.getValue() - slider.getMinimum()) / (slider.getMaximum() - slider.getMinimum()); //value between 0 and 1 for current amount of rotation const int nFrames = imageKnob.getHeight()/imageKnob.getWidth(); // number of frames for vertical film strip const int frameIdx = (int)ceil(fractRotation * ((double)nFrames-1.0) ); // current index from 0 --> nFrames-1 - + const float radius = jmin (width / 2.0f, height / 2.0f) ; const float centreX = x + width * 0.5f; const float centreY = y + height * 0.5f; const float rx = centreX - radius - 1.0f; const float ry = centreY - radius - 1.0f; - + g.drawImage(imageKnob, (int)rx, (int)ry, 2*(int)radius, 2*(int)radius, 0, frameIdx*imageKnob.getWidth(), imageKnob.getWidth(), imageKnob.getWidth()); }; @@ -198,7 +200,7 @@ void DXLookNFeel::drawToggleButton(Graphics& g, ToggleButton& button, bool isMou g.drawImage(imageSwitchLighted, 0, 0, 48, 26, 0, button.getToggleState() ? 0 : 26, 48, 26); } else - g.drawImage(imageSwitch, 0, 0, 48, 26, 0, button.getToggleState() ? 0 : 26, 48, 26); + g.drawImage(imageSwitch, 0, 0, 48, 26, 0, button.getToggleState() ? 0 : 52, 96, 52); } @@ -234,7 +236,7 @@ void DXLookNFeel::drawLinearSliderThumb (Graphics& g, int x, int y, int width, i int p = sliderPos - minSliderPos; p -= 6; - g.drawImage(imageSlider, p, 0, 26, 26, 0, 0, 26, 26); + g.drawImage(imageSlider, p, 0, 26, 26, 0, 0, 52, 52); } void DXLookNFeel::positionComboBoxText(ComboBox& box, Label& label) { diff --git a/Source/GlobalEditor.cpp b/Source/GlobalEditor.cpp index a7f2eab7..38bc0a4e 100644 --- a/Source/GlobalEditor.cpp +++ b/Source/GlobalEditor.cpp @@ -7,7 +7,7 @@ the "//[xyz]" and "//[/xyz]" sections will be retained when the file is loaded and re-saved. - Created with Projucer version: 6.0.1 + Created with Projucer version: 6.0.7 ------------------------------------------------------------------------------ @@ -66,7 +66,7 @@ class AboutBox : public DialogWindow { HyperlinkButton dexed; HyperlinkButton surge; - AboutBox(Component *parent) : DialogWindow("About", Colour(0xFF000000), true), + AboutBox(Component *parent) : DialogWindow("About", Colour(0xFF000000), true), dexed("https://asb2m10.github.io/dexed/", URL("https://asb2m10.github.io/dexed/")), surge("https://surge-synthesizer.github.io/", URL("https://surge-synthesizer.github.io/")) { setUsingNativeTitleBar(false); @@ -76,11 +76,11 @@ class AboutBox : public DialogWindow { centreAroundComponent(parent, getWidth(), getHeight()); dexed.setColour(HyperlinkButton::ColourIds::textColourId, Colour(0xFF4ea097)); - dexed.setJustificationType(Justification::left); + dexed.setJustificationType(Justification::left); dexed.setBounds(18, 433, getWidth() - 36, 30); addAndMakeVisible(&dexed); surge.setColour(HyperlinkButton::ColourIds::textColourId, Colour(0xFF4ea097)); - surge.setJustificationType(Justification::left); + surge.setJustificationType(Justification::left); surge.setBounds(18, 458, getWidth() - 36, 30); addAndMakeVisible(&surge); } @@ -462,13 +462,13 @@ GlobalEditor::~GlobalEditor() void GlobalEditor::paint (juce::Graphics& g) { //[UserPrePaint] Add your own custom painting code here.. - g.drawImage(background, 0, 0, 864, 144, 0, 0, 864, 144); + g.drawImage(background, 0, 0, 864, 144, 0, 0, 1728, 288); //[/UserPrePaint] //[UserPaint] Add your own custom painting code here.. - g.drawImage(imageLight, 300, 70, 14, 14, 0, monoMode->getToggleState() ? 14 : 0, 14, 14); - g.drawImage(imageLight, 619, 102, 14, 14, 0, lfoSync->getToggleState() ? 14 : 0, 14, 14); - g.drawImage(imageLight, 705, 102, 14, 14, 0, oscSync->getToggleState() ? 14 : 0, 14, 14); + g.drawImage(imageLight, 300, 70, 14, 14, 0, monoMode->getToggleState() ? 28 : 0, 28, 28); + g.drawImage(imageLight, 619, 102, 14, 14, 0, lfoSync->getToggleState() ? 28 : 0, 28, 28); + g.drawImage(imageLight, 705, 102, 14, 14, 0, oscSync->getToggleState() ? 28 : 0, 28, 28); //[/UserPaint] } diff --git a/Source/GlobalEditor.h b/Source/GlobalEditor.h index 34faadce..36c4c582 100644 --- a/Source/GlobalEditor.h +++ b/Source/GlobalEditor.h @@ -7,7 +7,7 @@ the "//[xyz]" and "//[/xyz]" sections will be retained when the file is loaded and re-saved. - Created with Projucer version: 6.0.1 + Created with Projucer version: 6.0.7 ------------------------------------------------------------------------------ diff --git a/Source/OperatorEditor.cpp b/Source/OperatorEditor.cpp index 682888c1..b55d6372 100644 --- a/Source/OperatorEditor.cpp +++ b/Source/OperatorEditor.cpp @@ -38,7 +38,7 @@ public : } void paintButton (Graphics& g, bool isMouseOverButton, bool isButtonDown) { - g.drawImage(image, 0, 0, 32, 32, 0, getToggleState() ? 0 : 32, 32, 32); + g.drawImage(image, 0, 0, 32, 32, 0, getToggleState() ? 0 : 64, 64, 64); } }; @@ -336,7 +336,7 @@ OperatorEditor::~OperatorEditor() void OperatorEditor::paint (juce::Graphics& g) { //[UserPrePaint] Add your own custom painting code here.. - g.drawImage(background, 0, 0, 287, 218, 0, 0, 287, 218); + g.drawImage(background, 0, 0, 287, 218, 0, 0, 574, 436); //[/UserPrePaint] //[UserPaint] Add your own custom painting code here.. @@ -352,9 +352,9 @@ void OperatorEditor::paint (juce::Graphics& g) bool state = opMode->getToggleState(); // 129 x 24 - g.drawImage(light, 127, 24, 14, 14, 0, state ? 0 : 14, 14, 14); + g.drawImage(light, 127, 24, 14, 14, 0, state ? 0 : 28, 28, 28); // 199 x 24 - g.drawImage(light, 198, 24, 14, 14, 0, !state ? 0 : 14, 14, 14); + g.drawImage(light, 198, 24, 14, 14, 0, !state ? 0 : 28, 28, 28); //[/UserPaint] } diff --git a/Source/ParamDialog.cpp b/Source/ParamDialog.cpp index 75ca17f8..842c49a0 100644 --- a/Source/ParamDialog.cpp +++ b/Source/ParamDialog.cpp @@ -7,12 +7,12 @@ the "//[xyz]" and "//[/xyz]" sections will be retained when the file is loaded and re-saved. - Created with Projucer version: 5.4.7 + Created with Projucer version: 6.0.7 ------------------------------------------------------------------------------ The Projucer is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2020 - Raw Material Software Limited. ============================================================================== */ @@ -33,58 +33,58 @@ ParamDialog::ParamDialog () //[Constructor_pre] You can add your own custom stuff here.. //[/Constructor_pre] - pitchRangeDn.reset (new Slider ("pitchRangeDn")); + pitchRangeDn.reset (new juce::Slider ("pitchRangeDn")); addAndMakeVisible (pitchRangeDn.get()); pitchRangeDn->setRange (0, 48, 1); - pitchRangeDn->setSliderStyle (Slider::RotaryVerticalDrag); - pitchRangeDn->setTextBoxStyle (Slider::TextBoxLeft, false, 80, 20); + pitchRangeDn->setSliderStyle (juce::Slider::RotaryVerticalDrag); + pitchRangeDn->setTextBoxStyle (juce::Slider::TextBoxLeft, false, 80, 20); pitchRangeDn->addListener (this); pitchRangeDn->setBounds (264, 16, 72, 24); - pitchStep.reset (new Slider ("pitchStep")); + pitchStep.reset (new juce::Slider ("pitchStep")); addAndMakeVisible (pitchStep.get()); pitchStep->setRange (0, 12, 1); - pitchStep->setSliderStyle (Slider::RotaryVerticalDrag); - pitchStep->setTextBoxStyle (Slider::TextBoxLeft, false, 80, 20); + pitchStep->setSliderStyle (juce::Slider::RotaryVerticalDrag); + pitchStep->setTextBoxStyle (juce::Slider::TextBoxLeft, false, 80, 20); pitchStep->addListener (this); pitchStep->setBounds (264, 56, 72, 24); - sysexIn.reset (new ComboBox ("sysexIn")); + sysexIn.reset (new juce::ComboBox ("sysexIn")); addAndMakeVisible (sysexIn.get()); sysexIn->setEditableText (false); - sysexIn->setJustificationType (Justification::centredLeft); - sysexIn->setTextWhenNothingSelected (String()); + sysexIn->setJustificationType (juce::Justification::centredLeft); + sysexIn->setTextWhenNothingSelected (juce::String()); sysexIn->setTextWhenNoChoicesAvailable (TRANS("(no choices)")); sysexIn->addListener (this); sysexIn->setBounds (104, 244, 224, 24); - sysexOut.reset (new ComboBox ("sysexOut")); + sysexOut.reset (new juce::ComboBox ("sysexOut")); addAndMakeVisible (sysexOut.get()); sysexOut->setEditableText (false); - sysexOut->setJustificationType (Justification::centredLeft); - sysexOut->setTextWhenNothingSelected (String()); + sysexOut->setJustificationType (juce::Justification::centredLeft); + sysexOut->setTextWhenNothingSelected (juce::String()); sysexOut->setTextWhenNoChoicesAvailable (TRANS("(no choices)")); sysexOut->addListener (this); sysexOut->setBounds (104, 280, 224, 24); - sysexChl.reset (new Slider ("sysexChl")); + sysexChl.reset (new juce::Slider ("sysexChl")); addAndMakeVisible (sysexChl.get()); sysexChl->setRange (1, 16, 1); - sysexChl->setSliderStyle (Slider::RotaryVerticalDrag); - sysexChl->setTextBoxStyle (Slider::TextBoxLeft, false, 80, 20); + sysexChl->setSliderStyle (juce::Slider::RotaryVerticalDrag); + sysexChl->setTextBoxStyle (juce::Slider::TextBoxLeft, false, 80, 20); sysexChl->addListener (this); sysexChl->setBounds (264, 316, 72, 24); - engineReso.reset (new ComboBox ("new combo box")); + engineReso.reset (new juce::ComboBox ("new combo box")); addAndMakeVisible (engineReso.get()); engineReso->setEditableText (false); - engineReso->setJustificationType (Justification::centredLeft); - engineReso->setTextWhenNothingSelected (String()); + engineReso->setJustificationType (juce::Justification::centredLeft); + engineReso->setTextWhenNothingSelected (juce::String()); engineReso->setTextWhenNoChoicesAvailable (TRANS("(no choices)")); engineReso->addItem (TRANS("Modern (24-bit)"), 1); engineReso->addItem (TRANS("Mark I"), 2); @@ -95,153 +95,153 @@ ParamDialog::ParamDialog () showKeyboard.reset (new LightedToggleButton ("showKeyboard")); addAndMakeVisible (showKeyboard.get()); - showKeyboard->setButtonText (String()); + showKeyboard->setButtonText (juce::String()); showKeyboard->addListener (this); showKeyboard->setBounds (264, 96, 56, 24); - whlRange.reset (new Slider ("whlRange")); + whlRange.reset (new juce::Slider ("whlRange")); addAndMakeVisible (whlRange.get()); whlRange->setRange (0, 99, 1); - whlRange->setSliderStyle (Slider::RotaryVerticalDrag); - whlRange->setTextBoxStyle (Slider::TextBoxLeft, false, 80, 20); + whlRange->setSliderStyle (juce::Slider::RotaryVerticalDrag); + whlRange->setTextBoxStyle (juce::Slider::TextBoxLeft, false, 80, 20); whlRange->addListener (this); whlRange->setBounds (448, 16, 72, 24); - ftRange.reset (new Slider ("ftRange")); + ftRange.reset (new juce::Slider ("ftRange")); addAndMakeVisible (ftRange.get()); ftRange->setRange (0, 99, 1); - ftRange->setSliderStyle (Slider::RotaryVerticalDrag); - ftRange->setTextBoxStyle (Slider::TextBoxLeft, false, 80, 20); + ftRange->setSliderStyle (juce::Slider::RotaryVerticalDrag); + ftRange->setTextBoxStyle (juce::Slider::TextBoxLeft, false, 80, 20); ftRange->addListener (this); ftRange->setBounds (448, 56, 72, 24); - brRange.reset (new Slider ("brRange")); + brRange.reset (new juce::Slider ("brRange")); addAndMakeVisible (brRange.get()); brRange->setRange (0, 99, 1); - brRange->setSliderStyle (Slider::RotaryVerticalDrag); - brRange->setTextBoxStyle (Slider::TextBoxLeft, false, 80, 20); + brRange->setSliderStyle (juce::Slider::RotaryVerticalDrag); + brRange->setTextBoxStyle (juce::Slider::TextBoxLeft, false, 80, 20); brRange->addListener (this); brRange->setBounds (448, 96, 72, 24); - atRange.reset (new Slider ("atRange")); + atRange.reset (new juce::Slider ("atRange")); addAndMakeVisible (atRange.get()); atRange->setRange (0, 99, 1); - atRange->setSliderStyle (Slider::RotaryVerticalDrag); - atRange->setTextBoxStyle (Slider::TextBoxLeft, false, 80, 20); + atRange->setSliderStyle (juce::Slider::RotaryVerticalDrag); + atRange->setTextBoxStyle (juce::Slider::TextBoxLeft, false, 80, 20); atRange->addListener (this); atRange->setBounds (448, 136, 72, 24); whlEg.reset (new LightedToggleButton ("whlEg")); addAndMakeVisible (whlEg.get()); - whlEg->setButtonText (String()); + whlEg->setButtonText (juce::String()); whlEg->addListener (this); whlEg->setBounds (640, 16, 56, 24); ftEg.reset (new LightedToggleButton ("ftEg")); addAndMakeVisible (ftEg.get()); - ftEg->setButtonText (String()); + ftEg->setButtonText (juce::String()); ftEg->addListener (this); ftEg->setBounds (640, 56, 56, 24); brEg.reset (new LightedToggleButton ("brEg")); addAndMakeVisible (brEg.get()); - brEg->setButtonText (String()); + brEg->setButtonText (juce::String()); brEg->addListener (this); brEg->setBounds (640, 96, 56, 24); atEg.reset (new LightedToggleButton ("atEg")); addAndMakeVisible (atEg.get()); - atEg->setButtonText (String()); + atEg->setButtonText (juce::String()); atEg->addListener (this); atEg->setBounds (640, 136, 56, 24); whlAmp.reset (new LightedToggleButton ("whlAmp")); addAndMakeVisible (whlAmp.get()); - whlAmp->setButtonText (String()); + whlAmp->setButtonText (juce::String()); whlAmp->addListener (this); whlAmp->setBounds (584, 16, 56, 24); ftAmp.reset (new LightedToggleButton ("ftAmp")); addAndMakeVisible (ftAmp.get()); - ftAmp->setButtonText (String()); + ftAmp->setButtonText (juce::String()); ftAmp->addListener (this); ftAmp->setBounds (584, 56, 56, 24); brAmp.reset (new LightedToggleButton ("brAmp")); addAndMakeVisible (brAmp.get()); - brAmp->setButtonText (String()); + brAmp->setButtonText (juce::String()); brAmp->addListener (this); brAmp->setBounds (584, 96, 56, 24); atAmp.reset (new LightedToggleButton ("atAmp")); addAndMakeVisible (atAmp.get()); - atAmp->setButtonText (String()); + atAmp->setButtonText (juce::String()); atAmp->addListener (this); atAmp->setBounds (584, 136, 56, 24); whlPitch.reset (new LightedToggleButton ("whlPitch")); addAndMakeVisible (whlPitch.get()); - whlPitch->setButtonText (String()); + whlPitch->setButtonText (juce::String()); whlPitch->addListener (this); whlPitch->setBounds (528, 16, 56, 24); ftPitch.reset (new LightedToggleButton ("ftPitch")); addAndMakeVisible (ftPitch.get()); - ftPitch->setButtonText (String()); + ftPitch->setButtonText (juce::String()); ftPitch->addListener (this); ftPitch->setBounds (528, 56, 56, 24); brPitch.reset (new LightedToggleButton ("brPitch")); addAndMakeVisible (brPitch.get()); - brPitch->setButtonText (String()); + brPitch->setButtonText (juce::String()); brPitch->addListener (this); brPitch->setBounds (528, 96, 56, 24); atPitch.reset (new LightedToggleButton ("atPitch")); addAndMakeVisible (atPitch.get()); - atPitch->setButtonText (String()); + atPitch->setButtonText (juce::String()); atPitch->addListener (this); atPitch->setBounds (528, 136, 56, 24); - sclButton.reset (new TextButton ("scl button")); + sclButton.reset (new juce::TextButton ("scl button")); addAndMakeVisible (sclButton.get()); sclButton->setButtonText (TRANS("SCL")); sclButton->addListener (this); sclButton->setBounds (448, 205, 56, 30); - kbmButton.reset (new TextButton ("kbm button")); + kbmButton.reset (new juce::TextButton ("kbm button")); addAndMakeVisible (kbmButton.get()); kbmButton->setButtonText (TRANS("KBM")); kbmButton->addListener (this); kbmButton->setBounds (512, 205, 56, 30); - showTunButton.reset (new TextButton ("show tuning button")); + showTunButton.reset (new juce::TextButton ("show tuning button")); addAndMakeVisible (showTunButton.get()); showTunButton->setButtonText (TRANS("Show")); showTunButton->addListener (this); showTunButton->setBounds (576, 205, 48, 30); - resetTuningButton.reset (new TextButton ("reset tuning button")); + resetTuningButton.reset (new juce::TextButton ("reset tuning button")); addAndMakeVisible (resetTuningButton.get()); resetTuningButton->setButtonText (TRANS("Reset")); resetTuningButton->addListener (this); @@ -250,55 +250,57 @@ ParamDialog::ParamDialog () transposeScale.reset (new LightedToggleButton ("transposeScale")); addAndMakeVisible (transposeScale.get()); - transposeScale->setButtonText (String()); + transposeScale->setButtonText (juce::String()); transposeScale->addListener (this); transposeScale->setToggleState (true, dontSendNotification); transposeScale->setBounds (592, 240, 56, 30); - mpePBRange.reset (new Slider ("mpePBRange")); + mpePBRange.reset (new juce::Slider ("mpePBRange")); addAndMakeVisible (mpePBRange.get()); mpePBRange->setRange (0, 96, 1); - mpePBRange->setSliderStyle (Slider::RotaryVerticalDrag); - mpePBRange->setTextBoxStyle (Slider::TextBoxLeft, false, 80, 20); + mpePBRange->setSliderStyle (juce::Slider::RotaryVerticalDrag); + mpePBRange->setTextBoxStyle (juce::Slider::TextBoxLeft, false, 80, 20); mpePBRange->addListener (this); mpePBRange->setBounds (616, 291, 72, 24); mpeEnabled.reset (new LightedToggleButton ("mpeEnabled")); addAndMakeVisible (mpeEnabled.get()); - mpeEnabled->setButtonText (String()); + mpeEnabled->setButtonText (juce::String()); mpeEnabled->addListener (this); mpeEnabled->setBounds (448, 288, 56, 30); - transposeHelp.reset (new ImageButton ("new button")); + transposeHelp.reset (new juce::ImageButton ("new button")); addAndMakeVisible (transposeHelp.get()); transposeHelp->addListener (this); transposeHelp->setImages (false, true, true, - ImageCache::getFromMemory (BinaryData::HelpButton_png, BinaryData::HelpButton_pngSize), 1.000f, Colour (0x00000000), - Image(), 1.000f, Colour (0x00000000), - Image(), 1.000f, Colour (0x00000000)); + juce::ImageCache::getFromMemory (BinaryData::HelpButton_png, BinaryData::HelpButton_pngSize), 1.000f, juce::Colour (0x00000000), + juce::Image(), 1.000f, juce::Colour (0x00000000), + juce::Image(), 1.000f, juce::Colour (0x00000000)); transposeHelp->setBounds (500, 245, 20, 20); - pitchRangeUp.reset (new Slider ("pitchRangeUp")); + pitchRangeUp.reset (new juce::Slider ("pitchRangeUp")); addAndMakeVisible (pitchRangeUp.get()); pitchRangeUp->setRange (0, 48, 1); - pitchRangeUp->setSliderStyle (Slider::RotaryVerticalDrag); - pitchRangeUp->setTextBoxStyle (Slider::TextBoxLeft, false, 80, 20); + pitchRangeUp->setSliderStyle (juce::Slider::RotaryVerticalDrag); + pitchRangeUp->setTextBoxStyle (juce::Slider::TextBoxLeft, false, 80, 20); pitchRangeUp->addListener (this); pitchRangeUp->setBounds (168, 16, 72, 24); - scalingFactor.reset (new ComboBox ("scalingFactor")); + scalingFactor.reset (new juce::ComboBox ("scalingFactor")); addAndMakeVisible (scalingFactor.get()); scalingFactor->setEditableText (false); - scalingFactor->setJustificationType (Justification::centredLeft); - scalingFactor->setTextWhenNothingSelected (String()); + scalingFactor->setJustificationType (juce::Justification::centredLeft); + scalingFactor->setTextWhenNothingSelected (juce::String()); scalingFactor->setTextWhenNoChoicesAvailable (TRANS("(no choices)")); scalingFactor->addItem (TRANS("100 %"), 1); - scalingFactor->addItem (TRANS("150 %"), 2); + scalingFactor->addItem (TRANS("125 %"), 2); + scalingFactor->addItem (TRANS("150 %"), 3); + scalingFactor->addSeparator(); scalingFactor->addListener (this); scalingFactor->setBounds (236, 136, 90, 24); @@ -375,64 +377,64 @@ ParamDialog::~ParamDialog() } //============================================================================== -void ParamDialog::paint (Graphics& g) +void ParamDialog::paint (juce::Graphics& g) { //[UserPrePaint] Add your own custom painting code here.. //[/UserPrePaint] - g.fillAll (Colour (0xff3c322f)); + g.fillAll (juce::Colour (0xff3c322f)); { int x = 20, y = 16, width = 276, height = 23; - String text (TRANS("Pitch Bend Range")); - Colour fillColour = Colours::white; + juce::String text (TRANS("Pitch Bend Range")); + juce::Colour fillColour = juce::Colours::white; //[UserPaintCustomArguments] Customize the painting arguments here.. //[/UserPaintCustomArguments] g.setColour (fillColour); - g.setFont (Font (15.00f, Font::plain).withTypefaceStyle ("Regular")); + g.setFont (juce::Font (15.00f, juce::Font::plain).withTypefaceStyle ("Regular")); g.drawText (text, x, y, width, height, - Justification::centredLeft, true); + juce::Justification::centredLeft, true); } { int x = 20, y = 56, width = 276, height = 23; - String text (TRANS("Pitch Bend Step")); - Colour fillColour = Colours::white; + juce::String text (TRANS("Pitch Bend Step")); + juce::Colour fillColour = juce::Colours::white; //[UserPaintCustomArguments] Customize the painting arguments here.. //[/UserPaintCustomArguments] g.setColour (fillColour); - g.setFont (Font (15.00f, Font::plain).withTypefaceStyle ("Regular")); + g.setFont (juce::Font (15.00f, juce::Font::plain).withTypefaceStyle ("Regular")); g.drawText (text, x, y, width, height, - Justification::centredLeft, true); + juce::Justification::centredLeft, true); } { int x = 20, y = 318, width = 245, height = 23; - String text (TRANS("DX7 Channel")); - Colour fillColour = Colours::white; + juce::String text (TRANS("DX7 Channel")); + juce::Colour fillColour = juce::Colours::white; //[UserPaintCustomArguments] Customize the painting arguments here.. //[/UserPaintCustomArguments] g.setColour (fillColour); - g.setFont (Font (15.00f, Font::plain).withTypefaceStyle ("Regular")); + g.setFont (juce::Font (15.00f, juce::Font::plain).withTypefaceStyle ("Regular")); g.drawText (text, x, y, width, height, - Justification::centredLeft, true); + juce::Justification::centredLeft, true); } { int x = 20, y = 190, width = 276, height = 23; - String text (TRANS("Engine Resolution")); - Colour fillColour = Colours::white; + juce::String text (TRANS("Engine Resolution")); + juce::Colour fillColour = juce::Colours::white; //[UserPaintCustomArguments] Customize the painting arguments here.. //[/UserPaintCustomArguments] g.setColour (fillColour); - g.setFont (Font (15.00f, Font::plain).withTypefaceStyle ("Regular")); + g.setFont (juce::Font (15.00f, juce::Font::plain).withTypefaceStyle ("Regular")); g.drawText (text, x, y, width, height, - Justification::centredLeft, true); + juce::Justification::centredLeft, true); } { int x = 22, y = 174, width = 306, height = 1; - Colour fillColour = Colours::black; + juce::Colour fillColour = juce::Colours::black; //[UserPaintCustomArguments] Customize the painting arguments here.. //[/UserPaintCustomArguments] g.setColour (fillColour); @@ -441,7 +443,7 @@ void ParamDialog::paint (Graphics& g) { int x = 22, y = 227, width = 306, height = 1; - Colour fillColour = Colours::black; + juce::Colour fillColour = juce::Colours::black; //[UserPaintCustomArguments] Customize the painting arguments here.. //[/UserPaintCustomArguments] g.setColour (fillColour); @@ -450,19 +452,19 @@ void ParamDialog::paint (Graphics& g) { int x = 20, y = 96, width = 276, height = 23; - String text (TRANS("Show Keyboard")); - Colour fillColour = Colours::white; + juce::String text (TRANS("Show Keyboard")); + juce::Colour fillColour = juce::Colours::white; //[UserPaintCustomArguments] Customize the painting arguments here.. //[/UserPaintCustomArguments] g.setColour (fillColour); - g.setFont (Font (15.00f, Font::plain).withTypefaceStyle ("Regular")); + g.setFont (juce::Font (15.00f, juce::Font::plain).withTypefaceStyle ("Regular")); g.drawText (text, x, y, width, height, - Justification::centredLeft, true); + juce::Justification::centredLeft, true); } { int x = 352, y = 11, width = 1, height = 330; - Colour fillColour = Colours::black; + juce::Colour fillColour = juce::Colours::black; //[UserPaintCustomArguments] Customize the painting arguments here.. //[/UserPaintCustomArguments] g.setColour (fillColour); @@ -471,91 +473,91 @@ void ParamDialog::paint (Graphics& g) { int x = 368, y = 16, width = 276, height = 23; - String text (TRANS("Wheel")); - Colour fillColour = Colours::white; + juce::String text (TRANS("Wheel")); + juce::Colour fillColour = juce::Colours::white; //[UserPaintCustomArguments] Customize the painting arguments here.. //[/UserPaintCustomArguments] g.setColour (fillColour); - g.setFont (Font (15.00f, Font::plain).withTypefaceStyle ("Regular")); + g.setFont (juce::Font (15.00f, juce::Font::plain).withTypefaceStyle ("Regular")); g.drawText (text, x, y, width, height, - Justification::centredLeft, true); + juce::Justification::centredLeft, true); } { int x = 368, y = 96, width = 276, height = 23; - String text (TRANS("Breath")); - Colour fillColour = Colours::white; + juce::String text (TRANS("Breath")); + juce::Colour fillColour = juce::Colours::white; //[UserPaintCustomArguments] Customize the painting arguments here.. //[/UserPaintCustomArguments] g.setColour (fillColour); - g.setFont (Font (15.00f, Font::plain).withTypefaceStyle ("Regular")); + g.setFont (juce::Font (15.00f, juce::Font::plain).withTypefaceStyle ("Regular")); g.drawText (text, x, y, width, height, - Justification::centredLeft, true); + juce::Justification::centredLeft, true); } { int x = 368, y = 56, width = 276, height = 23; - String text (TRANS("Foot")); - Colour fillColour = Colours::white; + juce::String text (TRANS("Foot")); + juce::Colour fillColour = juce::Colours::white; //[UserPaintCustomArguments] Customize the painting arguments here.. //[/UserPaintCustomArguments] g.setColour (fillColour); - g.setFont (Font (15.00f, Font::plain).withTypefaceStyle ("Regular")); + g.setFont (juce::Font (15.00f, juce::Font::plain).withTypefaceStyle ("Regular")); g.drawText (text, x, y, width, height, - Justification::centredLeft, true); + juce::Justification::centredLeft, true); } { int x = 368, y = 136, width = 276, height = 23; - String text (TRANS("After Touch")); - Colour fillColour = Colours::white; + juce::String text (TRANS("After Touch")); + juce::Colour fillColour = juce::Colours::white; //[UserPaintCustomArguments] Customize the painting arguments here.. //[/UserPaintCustomArguments] g.setColour (fillColour); - g.setFont (Font (15.00f, Font::plain).withTypefaceStyle ("Regular")); + g.setFont (juce::Font (15.00f, juce::Font::plain).withTypefaceStyle ("Regular")); g.drawText (text, x, y, width, height, - Justification::centredLeft, true); + juce::Justification::centredLeft, true); } { int x = 528, y = 163, width = 48, height = 23; - String text (TRANS("PITCH")); - Colour fillColour = Colours::white; + juce::String text (TRANS("PITCH")); + juce::Colour fillColour = juce::Colours::white; //[UserPaintCustomArguments] Customize the painting arguments here.. //[/UserPaintCustomArguments] g.setColour (fillColour); - g.setFont (Font (15.00f, Font::plain).withTypefaceStyle ("Regular")); + g.setFont (juce::Font (15.00f, juce::Font::plain).withTypefaceStyle ("Regular")); g.drawText (text, x, y, width, height, - Justification::centred, true); + juce::Justification::centred, true); } { int x = 584, y = 163, width = 48, height = 23; - String text (TRANS("AMP")); - Colour fillColour = Colours::white; + juce::String text (TRANS("AMP")); + juce::Colour fillColour = juce::Colours::white; //[UserPaintCustomArguments] Customize the painting arguments here.. //[/UserPaintCustomArguments] g.setColour (fillColour); - g.setFont (Font (15.00f, Font::plain).withTypefaceStyle ("Regular")); + g.setFont (juce::Font (15.00f, juce::Font::plain).withTypefaceStyle ("Regular")); g.drawText (text, x, y, width, height, - Justification::centred, true); + juce::Justification::centred, true); } { int x = 640, y = 163, width = 48, height = 23; - String text (TRANS("EG BIAS")); - Colour fillColour = Colours::white; + juce::String text (TRANS("EG BIAS")); + juce::Colour fillColour = juce::Colours::white; //[UserPaintCustomArguments] Customize the painting arguments here.. //[/UserPaintCustomArguments] g.setColour (fillColour); - g.setFont (Font (15.00f, Font::plain).withTypefaceStyle ("Regular")); + g.setFont (juce::Font (15.00f, juce::Font::plain).withTypefaceStyle ("Regular")); g.drawText (text, x, y, width, height, - Justification::centred, true); + juce::Justification::centred, true); } { int x = 371, y = 194, width = 325, height = 1; - Colour fillColour = Colours::black; + juce::Colour fillColour = juce::Colours::black; //[UserPaintCustomArguments] Customize the painting arguments here.. //[/UserPaintCustomArguments] g.setColour (fillColour); @@ -564,31 +566,31 @@ void ParamDialog::paint (Graphics& g) { int x = 371, y = 208, width = 276, height = 25; - String text (TRANS("Tuning")); - Colour fillColour = Colours::white; + juce::String text (TRANS("Tuning")); + juce::Colour fillColour = juce::Colours::white; //[UserPaintCustomArguments] Customize the painting arguments here.. //[/UserPaintCustomArguments] g.setColour (fillColour); - g.setFont (Font (15.00f, Font::plain).withTypefaceStyle ("Regular")); + g.setFont (juce::Font (15.00f, juce::Font::plain).withTypefaceStyle ("Regular")); g.drawText (text, x, y, width, height, - Justification::centredLeft, true); + juce::Justification::centredLeft, true); } { int x = 371, y = 242, width = 157, height = 25; - String text (TRANS("Transposition 12 as:")); - Colour fillColour = Colours::white; + juce::String text (TRANS("Transposition 12 as:")); + juce::Colour fillColour = juce::Colours::white; //[UserPaintCustomArguments] Customize the painting arguments here.. //[/UserPaintCustomArguments] g.setColour (fillColour); - g.setFont (Font (15.00f, Font::plain).withTypefaceStyle ("Regular")); + g.setFont (juce::Font (15.00f, juce::Font::plain).withTypefaceStyle ("Regular")); g.drawText (text, x, y, width, height, - Justification::centredLeft, true); + juce::Justification::centredLeft, true); } { int x = 368, y = 280, width = 328, height = 1; - Colour fillColour = Colours::black; + juce::Colour fillColour = juce::Colours::black; //[UserPaintCustomArguments] Customize the painting arguments here.. //[/UserPaintCustomArguments] g.setColour (fillColour); @@ -597,86 +599,86 @@ void ParamDialog::paint (Graphics& g) { int x = 371, y = 290, width = 276, height = 27; - String text (TRANS("MPE")); - Colour fillColour = Colours::white; + juce::String text (TRANS("MPE")); + juce::Colour fillColour = juce::Colours::white; //[UserPaintCustomArguments] Customize the painting arguments here.. //[/UserPaintCustomArguments] g.setColour (fillColour); - g.setFont (Font (15.00f, Font::plain).withTypefaceStyle ("Regular")); + g.setFont (juce::Font (15.00f, juce::Font::plain).withTypefaceStyle ("Regular")); g.drawText (text, x, y, width, height, - Justification::centredLeft, true); + juce::Justification::centredLeft, true); } { int x = 528, y = 290, width = 119, height = 27; - String text (TRANS("Bend Range")); - Colour fillColour = Colours::white; + juce::String text (TRANS("Bend Range")); + juce::Colour fillColour = juce::Colours::white; //[UserPaintCustomArguments] Customize the painting arguments here.. //[/UserPaintCustomArguments] g.setColour (fillColour); - g.setFont (Font (15.00f, Font::plain).withTypefaceStyle ("Regular")); + g.setFont (juce::Font (15.00f, juce::Font::plain).withTypefaceStyle ("Regular")); g.drawText (text, x, y, width, height, - Justification::centredLeft, true); + juce::Justification::centredLeft, true); } { int x = 555, y = 242, width = 37, height = 25; - String text (TRANS("12")); - Colour fillColour = Colours::white; + juce::String text (TRANS("12")); + juce::Colour fillColour = juce::Colours::white; //[UserPaintCustomArguments] Customize the painting arguments here.. //[/UserPaintCustomArguments] g.setColour (fillColour); - g.setFont (Font (15.00f, Font::plain).withTypefaceStyle ("Regular")); + g.setFont (juce::Font (15.00f, juce::Font::plain).withTypefaceStyle ("Regular")); g.drawText (text, x, y, width, height, - Justification::centredLeft, true); + juce::Justification::centredLeft, true); } { int x = 659, y = 242, width = 45, height = 25; - String text (TRANS("SCL")); - Colour fillColour = Colours::white; + juce::String text (TRANS("SCL")); + juce::Colour fillColour = juce::Colours::white; //[UserPaintCustomArguments] Customize the painting arguments here.. //[/UserPaintCustomArguments] g.setColour (fillColour); - g.setFont (Font (15.00f, Font::plain).withTypefaceStyle ("Regular")); + g.setFont (juce::Font (15.00f, juce::Font::plain).withTypefaceStyle ("Regular")); g.drawText (text, x, y, width, height, - Justification::centredLeft, true); + juce::Justification::centredLeft, true); } { int x = 147, y = 16, width = 20, height = 23; - String text (TRANS("up")); - Colour fillColour = Colours::white; + juce::String text (TRANS("up")); + juce::Colour fillColour = juce::Colours::white; //[UserPaintCustomArguments] Customize the painting arguments here.. //[/UserPaintCustomArguments] g.setColour (fillColour); - g.setFont (Font (15.00f, Font::plain).withTypefaceStyle ("Regular")); + g.setFont (juce::Font (15.00f, juce::Font::plain).withTypefaceStyle ("Regular")); g.drawText (text, x, y, width, height, - Justification::centredLeft, true); + juce::Justification::centredLeft, true); } { int x = 240, y = 16, width = 20, height = 23; - String text (TRANS("dn")); - Colour fillColour = Colours::white; + juce::String text (TRANS("dn")); + juce::Colour fillColour = juce::Colours::white; //[UserPaintCustomArguments] Customize the painting arguments here.. //[/UserPaintCustomArguments] g.setColour (fillColour); - g.setFont (Font (15.00f, Font::plain).withTypefaceStyle ("Regular")); + g.setFont (juce::Font (15.00f, juce::Font::plain).withTypefaceStyle ("Regular")); g.drawText (text, x, y, width, height, - Justification::centredLeft, true); + juce::Justification::centredLeft, true); } { int x = 20, y = 136, width = 276, height = 23; - String text (TRANS("UI Scaling")); - Colour fillColour = Colours::white; + juce::String text (TRANS("UI Scaling")); + juce::Colour fillColour = juce::Colours::white; //[UserPaintCustomArguments] Customize the painting arguments here.. //[/UserPaintCustomArguments] g.setColour (fillColour); - g.setFont (Font (15.00f, Font::plain).withTypefaceStyle ("Regular")); + g.setFont (juce::Font (15.00f, juce::Font::plain).withTypefaceStyle ("Regular")); g.drawText (text, x, y, width, height, - Justification::centredLeft, true); + juce::Justification::centredLeft, true); } //[UserPaint] Add your own custom painting code here.. @@ -705,7 +707,7 @@ void ParamDialog::resized() //[/UserResized] } -void ParamDialog::sliderValueChanged (Slider* sliderThatWasMoved) +void ParamDialog::sliderValueChanged (juce::Slider* sliderThatWasMoved) { //[UsersliderValueChanged_Pre] bool handled = false; @@ -765,7 +767,7 @@ void ParamDialog::sliderValueChanged (Slider* sliderThatWasMoved) //[/UsersliderValueChanged_Post] } -void ParamDialog::comboBoxChanged (ComboBox* comboBoxThatHasChanged) +void ParamDialog::comboBoxChanged (juce::ComboBox* comboBoxThatHasChanged) { //[UsercomboBoxChanged_Pre] bool handled = false; @@ -798,7 +800,7 @@ void ParamDialog::comboBoxChanged (ComboBox* comboBoxThatHasChanged) //[/UsercomboBoxChanged_Post] } -void ParamDialog::buttonClicked (Button* buttonThatWasClicked) +void ParamDialog::buttonClicked (juce::Button* buttonThatWasClicked) { //[UserbuttonClicked_Pre] bool handled = false; @@ -998,10 +1000,13 @@ void ParamDialog::setDialogValues(Controllers &c, SysexComm &mgr, int reso, bool engineReso->setSelectedItemIndex(reso); showKeyboard->setToggleState(showKey, NotificationType::dontSendNotification); - if ( dpiScaleFactor == 1.5 ) + if ( dpiScaleFactor == 1.25 ) { scalingFactor->setSelectedItemIndex(1); - else + } else if ( dpiScaleFactor == 1.50 ) { + scalingFactor->setSelectedItemIndex(2); + } else { scalingFactor->setSelectedItemIndex(0); + } } bool ParamDialog::getDialogValues(Controllers &c, SysexComm &mgr, int *reso, bool *showKey, float *dpiScaleFactor) { @@ -1049,6 +1054,9 @@ bool ParamDialog::getDialogValues(Controllers &c, SysexComm &mgr, int *reso, boo switch(scalingFactor->getSelectedItemIndex()) { case 1 : + *dpiScaleFactor = 1.25; + break; + case 2 : *dpiScaleFactor = 1.5; break; default: @@ -1275,7 +1283,8 @@ BEGIN_JUCER_METADATA needsCallback="1"/> + layout="33" items="100 % 125 % 150 % " textWhenNonSelected="" + textWhenNoItems="(no choices)"/> END_JUCER_METADATA diff --git a/Source/ParamDialog.h b/Source/ParamDialog.h index 05c5c9fb..fe918af9 100644 --- a/Source/ParamDialog.h +++ b/Source/ParamDialog.h @@ -7,12 +7,12 @@ the "//[xyz]" and "//[/xyz]" sections will be retained when the file is loaded and re-saved. - Created with Projucer version: 5.4.7 + Created with Projucer version: 6.0.7 ------------------------------------------------------------------------------ The Projucer is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2020 - Raw Material Software Limited. ============================================================================== */ @@ -38,9 +38,9 @@ //[/Comments] */ class ParamDialog : public Component, - public Slider::Listener, - public ComboBox::Listener, - public Button::Listener + public juce::Slider::Listener, + public juce::ComboBox::Listener, + public juce::Button::Listener { public: //============================================================================== @@ -65,11 +65,11 @@ class ParamDialog : public Component, void setIsStandardTuning(bool s); //[/UserMethods] - void paint (Graphics& g) override; + void paint (juce::Graphics& g) override; void resized() override; - void sliderValueChanged (Slider* sliderThatWasMoved) override; - void comboBoxChanged (ComboBox* comboBoxThatHasChanged) override; - void buttonClicked (Button* buttonThatWasClicked) override; + void sliderValueChanged (juce::Slider* sliderThatWasMoved) override; + void comboBoxChanged (juce::ComboBox* comboBoxThatHasChanged) override; + void buttonClicked (juce::Button* buttonThatWasClicked) override; @@ -81,17 +81,17 @@ class ParamDialog : public Component, //[/UserVariables] //============================================================================== - std::unique_ptr pitchRangeDn; - std::unique_ptr pitchStep; - std::unique_ptr sysexIn; - std::unique_ptr sysexOut; - std::unique_ptr sysexChl; - std::unique_ptr engineReso; + std::unique_ptr pitchRangeDn; + std::unique_ptr pitchStep; + std::unique_ptr sysexIn; + std::unique_ptr sysexOut; + std::unique_ptr sysexChl; + std::unique_ptr engineReso; std::unique_ptr showKeyboard; - std::unique_ptr whlRange; - std::unique_ptr ftRange; - std::unique_ptr brRange; - std::unique_ptr atRange; + std::unique_ptr whlRange; + std::unique_ptr ftRange; + std::unique_ptr brRange; + std::unique_ptr atRange; std::unique_ptr whlEg; std::unique_ptr ftEg; std::unique_ptr brEg; @@ -104,16 +104,16 @@ class ParamDialog : public Component, std::unique_ptr ftPitch; std::unique_ptr brPitch; std::unique_ptr atPitch; - std::unique_ptr sclButton; - std::unique_ptr kbmButton; - std::unique_ptr showTunButton; - std::unique_ptr resetTuningButton; + std::unique_ptr sclButton; + std::unique_ptr kbmButton; + std::unique_ptr showTunButton; + std::unique_ptr resetTuningButton; std::unique_ptr transposeScale; - std::unique_ptr mpePBRange; + std::unique_ptr mpePBRange; std::unique_ptr mpeEnabled; - std::unique_ptr transposeHelp; - std::unique_ptr pitchRangeUp; - std::unique_ptr scalingFactor; + std::unique_ptr transposeHelp; + std::unique_ptr pitchRangeUp; + std::unique_ptr scalingFactor; //============================================================================== diff --git a/Source/PluginEditor.cpp b/Source/PluginEditor.cpp index c1c73b5d..d5095cca 100644 --- a/Source/PluginEditor.cpp +++ b/Source/PluginEditor.cpp @@ -1,441 +1,443 @@ -/** - * - * Copyright (c) 2013-2018 Pascal Gauthier. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#include "PluginProcessor.h" -#include "PluginEditor.h" -#include "GlobalEditor.h" -#include "ParamDialog.h" -#include "SysexComm.h" -#include "TuningShow.h" -#include "Dexed.h" -#include "math.h" -#include - -#include "msfa/fm_op_kernel.h" - -//============================================================================== -DexedAudioProcessorEditor::DexedAudioProcessorEditor (DexedAudioProcessor* ownerFilter) - : AudioProcessorEditor (ownerFilter), - midiKeyboard (ownerFilter->keyboardState, MidiKeyboardComponent::horizontalKeyboard), - cartManager(this) -{ - setSize(WINDOW_SIZE_X, (ownerFilter->showKeyboard ? WINDOW_SIZE_Y : 581)); - - processor = ownerFilter; - - lookAndFeel->setDefaultLookAndFeel(lookAndFeel); - background = lookAndFeel->background; - - // OPERATORS - addAndMakeVisible(&(operators[0])); - operators[0].setBounds(2, 1, 287, 218); - operators[0].bind(processor, 0); - - addAndMakeVisible(&(operators[1])); - operators[1].setBounds(290, 1, 287, 218); - operators[1].bind(processor, 1); - - addAndMakeVisible(&(operators[2])); - operators[2].setBounds(578, 1, 287, 218); - operators[2].bind(processor, 2); - - addAndMakeVisible(&(operators[3])); - operators[3].setBounds(2, 219, 287, 218); - operators[3].bind(processor, 3); - - addAndMakeVisible(&(operators[4])); - operators[4].setBounds(290, 219, 287, 218); - operators[4].bind(processor, 4); - - addAndMakeVisible(&(operators[5])); - operators[5].setBounds(578, 219, 287, 218); - operators[5].bind(processor, 5); - - // add the midi keyboard component.. - addAndMakeVisible (&midiKeyboard); - - // The DX7 is a badass on the bass, keep it that way - midiKeyboard.setLowestVisibleKey(24); - - midiKeyboard.setBounds(4, 581, getWidth() - 8, 90); - - addAndMakeVisible(&global); - global.setBounds(2,436,864,144); - global.bind(this); - - global.setMonoState(processor->isMonoMode()); - - rebuildProgramCombobox(); - global.programs->addListener(this); - - addChildComponent(&cartManager); - - updateUI(); - startTimer(100); -} - -DexedAudioProcessorEditor::~DexedAudioProcessorEditor() { - stopTimer(); - processor->unbindUI(); - setLookAndFeel(nullptr); -} - -//============================================================================== -void DexedAudioProcessorEditor::paint (Graphics& g) { - g.setColour(background); - g.fillRoundedRectangle(0.0f, 0.0f, (float) getWidth(), (float) getHeight(), 0); -} - -void DexedAudioProcessorEditor::cartShow() { - stopTimer(); - cartManager.resetActiveSysex(); - cartManager.setBounds(4, 2, 859, 576); - cartManager.setVisible(true); - cartManager.initialFocus(); -} - - -void DexedAudioProcessorEditor::loadCart(File file) { - Cartridge cart; - - int rc = cart.load(file); - - if ( rc < 0 ) { - AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, - "Error", - "Unable to open: " + file.getFullPathName()); - return; - } - - if ( rc != 0 ) { - rc = AlertWindow::showOkCancelBox(AlertWindow::QuestionIcon, "Unable to find DX7 sysex cartridge in file", - "This sysex file is not for the DX7 or it is corrupted. " - "Do you still want to load this file as random data ?"); - if ( rc == 0 ) - return; - } - - processor->loadCartridge(cart); - rebuildProgramCombobox(); - processor->setCurrentProgram(0); - global.programs->setSelectedId(processor->getCurrentProgram()+1, dontSendNotification); - processor->updateHostDisplay(); - - processor->activeFileCartridge = file; -} - -void DexedAudioProcessorEditor::saveCart() { - File startFileName = processor->activeFileCartridge.exists() ? processor->activeFileCartridge : processor->dexedCartDir; - - FileChooser fc ("Export DX sysex...", processor->dexedCartDir, "*.syx;*.SYX", 1); - if ( fc.browseForFileToSave(true) ) { - if ( ! processor->currentCart.saveVoice(fc.getResults().getReference(0)) ) { - AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, - "Error", - "Unable to write: " + fc.getResults().getReference(0).getFullPathName()); - } - } -} - -void DexedAudioProcessorEditor::tuningShow() { - auto te = new TuningShow(); - te->setTuning( processor->synthTuningState->getTuning() ); - - DialogWindow::LaunchOptions options; - options.content.setOwned(te); - options.dialogTitle = "Current Tuning"; - options.dialogBackgroundColour = Colour(0xFF323E44); - options.escapeKeyTriggersCloseButton = true; - options.useNativeTitleBar = false; - options.resizable = false; - - auto dialogwindow = options.launchAsync(); -} - -void DexedAudioProcessorEditor::parmShow() { - int tp = processor->getEngineType(); - DialogWindow::LaunchOptions options; - - auto param = new ParamDialog(); - param->setColour(AlertWindow::backgroundColourId, Colour(0xFF323E44)); - param->setDialogValues(processor->controllers, processor->sysexComm, tp, processor->showKeyboard, processor->dpiScaleFactor); - param->setIsStandardTuning(processor->synthTuningState->is_standard_tuning() ); - param->setTuningCallback([this](ParamDialog *p, ParamDialog::TuningAction which) { - switch(which) - { - case ParamDialog::LOAD_SCL: - this->processor->applySCLTuning(); - break; - case ParamDialog::LOAD_KBM: - this->processor->applyKBMMapping(); - break; - case ParamDialog::RESET_TUNING: - this->processor->retuneToStandard(); - break; - case ParamDialog::SHOW_TUNING: - // consider https://forum.juce.com/t/closing-a-modal-dialog-window/2961 - this->tuningShow(); - break; - } - p->setIsStandardTuning(this->processor->synthTuningState->is_standard_tuning() ); - } ); - - options.content.setOwned(param); - options.dialogTitle = "dexed Parameters"; - options.dialogBackgroundColour = Colour(0xFF323E44); - options.escapeKeyTriggersCloseButton = true; - options.useNativeTitleBar = false; - options.resizable = false; - - auto generalCallback = [this](ParamDialog *param) - { - int tpo; - bool ret = param->getDialogValues(this->processor->controllers, this->processor->sysexComm, &tpo, &this->processor->showKeyboard, &this->processor->dpiScaleFactor); - this->processor->setEngineType(tpo); - this->processor->savePreference(); - - this->setScaleFactor(this->processor->dpiScaleFactor); - this->setSize(WINDOW_SIZE_X, (processor->showKeyboard ? WINDOW_SIZE_Y : 581)); - this->midiKeyboard.repaint(); - - if ( ret == false ) { - AlertWindow::showMessageBoxAsync(AlertWindow::WarningIcon, "Midi Interface", "Error opening midi ports"); - } - }; - param->setGeneralCallback(generalCallback); - - auto dialogWindow = options.launchAsync(); -} - -void DexedAudioProcessorEditor::initProgram() { - processor->resetToInitVoice(); -} - -void DexedAudioProcessorEditor::comboBoxChanged (ComboBox* comboBoxThatHasChanged) { - processor->setCurrentProgram(global.programs->getSelectedId()-1); - processor->updateHostDisplay(); -} - -void DexedAudioProcessorEditor::timerCallback() { - if ( processor->forceRefreshUI ) { - processor->forceRefreshUI = false; - updateUI(); - } - - if ( ! processor->peekVoiceStatus() ) - return; - - for(int i=0;i<6;i++) { - operators[i].updateGain(sqrt(processor->voiceStatus.amp[5 - i]) / 8196); // TODO: FUGLY !!!! change this sqrt nonsense - operators[i].updateEnvPos(processor->voiceStatus.ampStep[5 - i]); - } - global.updatePitchPos(processor->voiceStatus.pitchStep); - global.updateVu(processor->vuSignal); -} - -void DexedAudioProcessorEditor::updateUI() { - for(int i=0;ictrl.size();i++) { - processor->ctrl[i]->updateComponent(); - } - for(int i=0;i<6;i++) { - operators[i].updateDisplay(); - } - rebuildProgramCombobox(); - global.updateDisplay(); -} - -void DexedAudioProcessorEditor::rebuildProgramCombobox() { - global.programs->clear(dontSendNotification); - - processor->currentCart.getProgramNames(processor->programNames); - - for(int i=0;igetNumPrograms();i++) { - String id; - id << (i+1) << ". " << processor->getProgramName(i); - global.programs->addItem(id, i+1); - } - - global.programs->setSelectedId(processor->getCurrentProgram()+1, dontSendNotification); - - String name = Cartridge::normalizePgmName((const char *) processor->data+145); - cartManager.setActiveProgram(processor->getCurrentProgram(), name); - if ( name != processor->getProgramName(processor->getCurrentProgram()) ) - global.programs->setText("**. " + name, dontSendNotification); - - cartManager.resetActiveSysex(); -} - -void DexedAudioProcessorEditor::storeProgram() { - String currentName = Cartridge::normalizePgmName((const char *) processor->data+145); - Cartridge destSysex = processor->currentCart; - File *externalFile = NULL; - - bool activeCartridgeFound = processor->activeFileCartridge.exists(); - - while (true) { - String msg; - - if ( externalFile == NULL ) { - if ( activeCartridgeFound ) - msg = "Store program to current (" + processor->activeFileCartridge.getFileName() + ") / new cartridge"; - else - msg = "Store program to current / new cartridge"; - } else { - msg = "Store program to " + externalFile->getFileName(); - } - - AlertWindow dialog("Store Program", msg, AlertWindow::NoIcon, this); - dialog.addTextEditor("Name", currentName, String("Name"), false); - // TODO: fix the name length to 10 - - StringArray programs; - destSysex.getProgramNames(programs); - dialog.addComboBox("Dest", programs, "Program Destination"); - - if ( externalFile == NULL ) { - StringArray saveAction; - saveAction.add("Store program to DAW plugin song state"); - saveAction.add("Store program and create a new copy of the .syx cartridge"); - if ( activeCartridgeFound ) - saveAction.add("Store program and overwrite current .syx cartridge"); - - dialog.addComboBox("SaveAction", saveAction, "Store Action"); - } - - dialog.addButton("OK", 0, KeyPress(KeyPress::returnKey)); - dialog.addButton("CANCEL", 1, KeyPress(KeyPress::escapeKey)); - dialog.addButton("EXTERNAL FILE", 2, KeyPress()); - int response = dialog.runModalLoop(); - - if ( response == 2 ) { - FileChooser fc("Destination Sysex", processor->dexedCartDir, "*.syx;*.SYX;*.*", 1); - - if ( fc.browseForFileToOpen() ) { - if ( externalFile != NULL ) - delete externalFile; - - externalFile = new File(fc.getResults().getReference(0)); - if ( destSysex.load(*externalFile) == 0 ) - continue; - AlertWindow::showMessageBoxAsync(AlertWindow::WarningIcon, "Read error", "Unable to read file"); - } - } - - if ( response == 0 ) { - TextEditor *name = dialog.getTextEditor("Name"); - ComboBox *dest = dialog.getComboBoxComponent("Dest"); - - int programNum = dest->getSelectedItemIndex(); - String programName(name->getText()); - if ( programName.length() > 10 ) { - int toStrip = programName.length() - 10; - programName = programName.dropLastCharacters(toStrip); - } - - if ( externalFile == NULL ) { - processor->currentCart.packProgram((uint8_t *) processor->data, programNum, programName, processor->controllers.opSwitch); - rebuildProgramCombobox(); - processor->setCurrentProgram(programNum); - processor->updateHostDisplay(); - - int action = dialog.getComboBoxComponent("SaveAction")->getSelectedItemIndex(); - if ( action > 0 ) { - File destination = processor->activeFileCartridge; - if ( action == 1 ) { - FileChooser fc("Destination Sysex", processor->dexedCartDir, "*.syx;*.SYX", 1); - if ( ! fc.browseForFileToSave(true) ) - break; - destination = fc.getResult(); - } - - processor->currentCart.saveVoice(destination); - processor->activeFileCartridge = destination; - } - } else { - destSysex.packProgram((uint8_t *) processor->data, programNum, programName, processor->controllers.opSwitch); - if ( ! destSysex.saveVoice(*externalFile)) { - AlertWindow::showMessageBoxAsync(AlertWindow::WarningIcon, "Write error", "Unable to write file"); - } - } - } - break; - } - - if ( externalFile != NULL ) - delete externalFile; - cartManager.resetActiveSysex(); -} - -class MidiCCListener: public AlertWindow, Value::Listener { - DexedAudioProcessorEditor *editor; - Ctrl *target; -public : - MidiCCListener(DexedAudioProcessorEditor *editor, Ctrl *target) : AlertWindow("","", AlertWindow::InfoIcon, editor) { - this->editor = editor; - this->target = target; - setMessage("Mapping: " + String(target->label) + ", waiting for midi controller change (CC) message..."); - addButton("CANCEL", -1); - editor->processor->lastCCUsed.setValue(-1); - editor->processor->lastCCUsed.addListener(this); - } - - ~MidiCCListener() { - editor->processor->lastCCUsed.removeListener(this); - } - - void valueChanged(Value &value) { - int cc = value.getValue(); - editor->processor->mappedMidiCC.remove(cc); - editor->processor->mappedMidiCC.set(cc, target); - editor->processor->savePreference(); - exitModalState(0); - } -}; - -void DexedAudioProcessorEditor::discoverMidiCC(Ctrl *ctrl) { - MidiCCListener ccListener(this, ctrl); - ccListener.runModalLoop(); -} - -bool DexedAudioProcessorEditor::isInterestedInFileDrag (const StringArray &files) -{ - if( files.size() != 1 ) return false; - - for( auto i = files.begin(); i != files.end(); ++i ) - { - if( i->endsWithIgnoreCase( ".scl" ) || i->endsWithIgnoreCase( ".kbm" ) ) - return true; - } - return false; -} - -void DexedAudioProcessorEditor::filesDropped (const StringArray &files, int x, int y ) -{ - if( files.size() != 1 ) return; - auto fn = files[0]; - if( fn.endsWithIgnoreCase( ".scl" ) ) - { - processor->applySCLTuning( File( fn ) ); - } - if( fn.endsWithIgnoreCase( ".kbm" ) ) - { - processor->applyKBMMapping( File( fn ) ); - } -} +/** + * + * Copyright (c) 2013-2018 Pascal Gauthier. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "PluginProcessor.h" +#include "PluginEditor.h" +#include "GlobalEditor.h" +#include "ParamDialog.h" +#include "SysexComm.h" +#include "TuningShow.h" +#include "Dexed.h" +#include "math.h" +#include + +#include "msfa/fm_op_kernel.h" + +//============================================================================== +DexedAudioProcessorEditor::DexedAudioProcessorEditor (DexedAudioProcessor* ownerFilter) + : AudioProcessorEditor (ownerFilter), + midiKeyboard (ownerFilter->keyboardState, MidiKeyboardComponent::horizontalKeyboard), + cartManager(this) +{ + setSize(WINDOW_SIZE_X, (ownerFilter->showKeyboard ? WINDOW_SIZE_Y : 581)); + + processor = ownerFilter; + + lookAndFeel->setDefaultLookAndFeel(lookAndFeel); + background = lookAndFeel->background; + + // OPERATORS + addAndMakeVisible(&(operators[0])); + operators[0].setBounds(2, 1, 287, 218); + operators[0].bind(processor, 0); + + addAndMakeVisible(&(operators[1])); + operators[1].setBounds(290, 1, 287, 218); + operators[1].bind(processor, 1); + + addAndMakeVisible(&(operators[2])); + operators[2].setBounds(578, 1, 287, 218); + operators[2].bind(processor, 2); + + addAndMakeVisible(&(operators[3])); + operators[3].setBounds(2, 219, 287, 218); + operators[3].bind(processor, 3); + + addAndMakeVisible(&(operators[4])); + operators[4].setBounds(290, 219, 287, 218); + operators[4].bind(processor, 4); + + addAndMakeVisible(&(operators[5])); + operators[5].setBounds(578, 219, 287, 218); + operators[5].bind(processor, 5); + + // add the midi keyboard component.. + addAndMakeVisible (&midiKeyboard); + + // The DX7 is a badass on the bass, keep it that way + midiKeyboard.setLowestVisibleKey(24); + + midiKeyboard.setBounds(4, 581, getWidth() - 8, 90); + + addAndMakeVisible(&global); + global.setBounds(2,436,864,144); + global.bind(this); + + global.setMonoState(processor->isMonoMode()); + + rebuildProgramCombobox(); + global.programs->addListener(this); + + addChildComponent(&cartManager); + + updateUI(); + startTimer(100); +} + +DexedAudioProcessorEditor::~DexedAudioProcessorEditor() { + stopTimer(); + processor->unbindUI(); + setLookAndFeel(nullptr); +} + +//============================================================================== +void DexedAudioProcessorEditor::paint (Graphics& g) { + g.setColour(background); + g.fillRoundedRectangle(0.0f, 0.0f, (float) getWidth(), (float) getHeight(), 0); +} + +void DexedAudioProcessorEditor::cartShow() { + stopTimer(); + cartManager.resetActiveSysex(); + cartManager.setBounds(4, 2, 859, 576); + cartManager.setVisible(true); + cartManager.initialFocus(); +} + + +void DexedAudioProcessorEditor::loadCart(File file) { + Cartridge cart; + + int rc = cart.load(file); + + if ( rc < 0 ) { + AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, + "Error", + "Unable to open: " + file.getFullPathName()); + return; + } + + if ( rc != 0 ) { + rc = AlertWindow::showOkCancelBox(AlertWindow::QuestionIcon, "Unable to find DX7 sysex cartridge in file", + "This sysex file is not for the DX7 or it is corrupted. " + "Do you still want to load this file as random data ?"); + if ( rc == 0 ) + return; + } + + processor->loadCartridge(cart); + rebuildProgramCombobox(); + processor->setCurrentProgram(0); + global.programs->setSelectedId(processor->getCurrentProgram()+1, dontSendNotification); + processor->updateHostDisplay(); + + processor->activeFileCartridge = file; +} + +void DexedAudioProcessorEditor::saveCart() { + File startFileName = processor->activeFileCartridge.exists() ? processor->activeFileCartridge : processor->dexedCartDir; + + FileChooser fc ("Export DX sysex...", processor->dexedCartDir, "*.syx;*.SYX", 1); + if ( fc.browseForFileToSave(true) ) { + if ( ! processor->currentCart.saveVoice(fc.getResults().getReference(0)) ) { + AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, + "Error", + "Unable to write: " + fc.getResults().getReference(0).getFullPathName()); + } + } +} + +void DexedAudioProcessorEditor::tuningShow() { + auto te = new TuningShow(); + te->setTuning( processor->synthTuningState->getTuning() ); + + DialogWindow::LaunchOptions options; + options.content.setOwned(te); + options.dialogTitle = "Current Tuning"; + options.dialogBackgroundColour = Colour(0xFF323E44); + options.escapeKeyTriggersCloseButton = true; + options.useNativeTitleBar = false; + options.resizable = false; + + auto dialogwindow = options.launchAsync(); +} + +void DexedAudioProcessorEditor::parmShow() { + int tp = processor->getEngineType(); + DialogWindow::LaunchOptions options; + + auto param = new ParamDialog(); + param->setColour(AlertWindow::backgroundColourId, Colour(0xFF323E44)); + param->setDialogValues(processor->controllers, processor->sysexComm, tp, processor->showKeyboard, processor->getDpiScaleFactor()); + param->setIsStandardTuning(processor->synthTuningState->is_standard_tuning() ); + param->setTuningCallback([this](ParamDialog *p, ParamDialog::TuningAction which) { + switch(which) + { + case ParamDialog::LOAD_SCL: + this->processor->applySCLTuning(); + break; + case ParamDialog::LOAD_KBM: + this->processor->applyKBMMapping(); + break; + case ParamDialog::RESET_TUNING: + this->processor->retuneToStandard(); + break; + case ParamDialog::SHOW_TUNING: + // consider https://forum.juce.com/t/closing-a-modal-dialog-window/2961 + this->tuningShow(); + break; + } + p->setIsStandardTuning(this->processor->synthTuningState->is_standard_tuning() ); + } ); + + options.content.setOwned(param); + options.dialogTitle = "dexed Parameters"; + options.dialogBackgroundColour = Colour(0xFF323E44); + options.escapeKeyTriggersCloseButton = true; + options.useNativeTitleBar = false; + options.resizable = false; + + auto generalCallback = [this](ParamDialog *param) + { + int tpo; + float scale = this->processor->getDpiScaleFactor(); + bool ret = param->getDialogValues(this->processor->controllers, this->processor->sysexComm, &tpo, &this->processor->showKeyboard, &scale); + this->processor->setEngineType(tpo); + this->processor->savePreference(); + + this->processor->setDpiScaleFactor(scale); + param->setSize(710, 355); + this->setSize(WINDOW_SIZE_X, (processor->showKeyboard ? WINDOW_SIZE_Y : 581)); + this->midiKeyboard.repaint(); + + if ( ret == false ) { + AlertWindow::showMessageBoxAsync(AlertWindow::WarningIcon, "Midi Interface", "Error opening midi ports"); + } + }; + param->setGeneralCallback(generalCallback); + + auto dialogWindow = options.launchAsync(); +} + +void DexedAudioProcessorEditor::initProgram() { + processor->resetToInitVoice(); +} + +void DexedAudioProcessorEditor::comboBoxChanged (ComboBox* comboBoxThatHasChanged) { + processor->setCurrentProgram(global.programs->getSelectedId()-1); + processor->updateHostDisplay(); +} + +void DexedAudioProcessorEditor::timerCallback() { + if ( processor->forceRefreshUI ) { + processor->forceRefreshUI = false; + updateUI(); + } + + if ( ! processor->peekVoiceStatus() ) + return; + + for(int i=0;i<6;i++) { + operators[i].updateGain(sqrt(processor->voiceStatus.amp[5 - i]) / 8196); // TODO: FUGLY !!!! change this sqrt nonsense + operators[i].updateEnvPos(processor->voiceStatus.ampStep[5 - i]); + } + global.updatePitchPos(processor->voiceStatus.pitchStep); + global.updateVu(processor->vuSignal); +} + +void DexedAudioProcessorEditor::updateUI() { + for(int i=0;ictrl.size();i++) { + processor->ctrl[i]->updateComponent(); + } + for(int i=0;i<6;i++) { + operators[i].updateDisplay(); + } + rebuildProgramCombobox(); + global.updateDisplay(); +} + +void DexedAudioProcessorEditor::rebuildProgramCombobox() { + global.programs->clear(dontSendNotification); + + processor->currentCart.getProgramNames(processor->programNames); + + for(int i=0;igetNumPrograms();i++) { + String id; + id << (i+1) << ". " << processor->getProgramName(i); + global.programs->addItem(id, i+1); + } + + global.programs->setSelectedId(processor->getCurrentProgram()+1, dontSendNotification); + + String name = Cartridge::normalizePgmName((const char *) processor->data+145); + cartManager.setActiveProgram(processor->getCurrentProgram(), name); + if ( name != processor->getProgramName(processor->getCurrentProgram()) ) + global.programs->setText("**. " + name, dontSendNotification); + + cartManager.resetActiveSysex(); +} + +void DexedAudioProcessorEditor::storeProgram() { + String currentName = Cartridge::normalizePgmName((const char *) processor->data+145); + Cartridge destSysex = processor->currentCart; + File *externalFile = NULL; + + bool activeCartridgeFound = processor->activeFileCartridge.exists(); + + while (true) { + String msg; + + if ( externalFile == NULL ) { + if ( activeCartridgeFound ) + msg = "Store program to current (" + processor->activeFileCartridge.getFileName() + ") / new cartridge"; + else + msg = "Store program to current / new cartridge"; + } else { + msg = "Store program to " + externalFile->getFileName(); + } + + AlertWindow dialog("Store Program", msg, AlertWindow::NoIcon, this); + dialog.addTextEditor("Name", currentName, String("Name"), false); + // TODO: fix the name length to 10 + + StringArray programs; + destSysex.getProgramNames(programs); + dialog.addComboBox("Dest", programs, "Program Destination"); + + if ( externalFile == NULL ) { + StringArray saveAction; + saveAction.add("Store program to DAW plugin song state"); + saveAction.add("Store program and create a new copy of the .syx cartridge"); + if ( activeCartridgeFound ) + saveAction.add("Store program and overwrite current .syx cartridge"); + + dialog.addComboBox("SaveAction", saveAction, "Store Action"); + } + + dialog.addButton("OK", 0, KeyPress(KeyPress::returnKey)); + dialog.addButton("CANCEL", 1, KeyPress(KeyPress::escapeKey)); + dialog.addButton("EXTERNAL FILE", 2, KeyPress()); + int response = dialog.runModalLoop(); + + if ( response == 2 ) { + FileChooser fc("Destination Sysex", processor->dexedCartDir, "*.syx;*.SYX;*.*", 1); + + if ( fc.browseForFileToOpen() ) { + if ( externalFile != NULL ) + delete externalFile; + + externalFile = new File(fc.getResults().getReference(0)); + if ( destSysex.load(*externalFile) == 0 ) + continue; + AlertWindow::showMessageBoxAsync(AlertWindow::WarningIcon, "Read error", "Unable to read file"); + } + } + + if ( response == 0 ) { + TextEditor *name = dialog.getTextEditor("Name"); + ComboBox *dest = dialog.getComboBoxComponent("Dest"); + + int programNum = dest->getSelectedItemIndex(); + String programName(name->getText()); + if ( programName.length() > 10 ) { + int toStrip = programName.length() - 10; + programName = programName.dropLastCharacters(toStrip); + } + + if ( externalFile == NULL ) { + processor->currentCart.packProgram((uint8_t *) processor->data, programNum, programName, processor->controllers.opSwitch); + rebuildProgramCombobox(); + processor->setCurrentProgram(programNum); + processor->updateHostDisplay(); + + int action = dialog.getComboBoxComponent("SaveAction")->getSelectedItemIndex(); + if ( action > 0 ) { + File destination = processor->activeFileCartridge; + if ( action == 1 ) { + FileChooser fc("Destination Sysex", processor->dexedCartDir, "*.syx;*.SYX", 1); + if ( ! fc.browseForFileToSave(true) ) + break; + destination = fc.getResult(); + } + + processor->currentCart.saveVoice(destination); + processor->activeFileCartridge = destination; + } + } else { + destSysex.packProgram((uint8_t *) processor->data, programNum, programName, processor->controllers.opSwitch); + if ( ! destSysex.saveVoice(*externalFile)) { + AlertWindow::showMessageBoxAsync(AlertWindow::WarningIcon, "Write error", "Unable to write file"); + } + } + } + break; + } + + if ( externalFile != NULL ) + delete externalFile; + cartManager.resetActiveSysex(); +} + +class MidiCCListener: public AlertWindow, Value::Listener { + DexedAudioProcessorEditor *editor; + Ctrl *target; +public : + MidiCCListener(DexedAudioProcessorEditor *editor, Ctrl *target) : AlertWindow("","", AlertWindow::InfoIcon, editor) { + this->editor = editor; + this->target = target; + setMessage("Mapping: " + String(target->label) + ", waiting for midi controller change (CC) message..."); + addButton("CANCEL", -1); + editor->processor->lastCCUsed.setValue(-1); + editor->processor->lastCCUsed.addListener(this); + } + + ~MidiCCListener() { + editor->processor->lastCCUsed.removeListener(this); + } + + void valueChanged(Value &value) { + int cc = value.getValue(); + editor->processor->mappedMidiCC.remove(cc); + editor->processor->mappedMidiCC.set(cc, target); + editor->processor->savePreference(); + exitModalState(0); + } +}; + +void DexedAudioProcessorEditor::discoverMidiCC(Ctrl *ctrl) { + MidiCCListener ccListener(this, ctrl); + ccListener.runModalLoop(); +} + +bool DexedAudioProcessorEditor::isInterestedInFileDrag (const StringArray &files) +{ + if( files.size() != 1 ) return false; + + for( auto i = files.begin(); i != files.end(); ++i ) + { + if( i->endsWithIgnoreCase( ".scl" ) || i->endsWithIgnoreCase( ".kbm" ) ) + return true; + } + return false; +} + +void DexedAudioProcessorEditor::filesDropped (const StringArray &files, int x, int y ) +{ + if( files.size() != 1 ) return; + auto fn = files[0]; + if( fn.endsWithIgnoreCase( ".scl" ) ) + { + processor->applySCLTuning( File( fn ) ); + } + if( fn.endsWithIgnoreCase( ".kbm" ) ) + { + processor->applyKBMMapping( File( fn ) ); + } +} diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index 66d8c1d4..f2c38177 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -1,910 +1,916 @@ -/** - * - * Copyright (c) 2013-2018 Pascal Gauthier. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#include -#include - -#include "PluginProcessor.h" -#include "PluginEditor.h" - -#include "Dexed.h" -#include "msfa/synth.h" -#include "msfa/freqlut.h" -#include "msfa/sin.h" -#include "msfa/exp2.h" -#include "msfa/env.h" -#include "msfa/pitchenv.h" -#include "msfa/aligned_buf.h" -#include "msfa/fm_op_kernel.h" - -#if JUCE_MSVC - #pragma comment (lib, "kernel32.lib") - #pragma comment (lib, "user32.lib") - #pragma comment (lib, "wininet.lib") - #pragma comment (lib, "advapi32.lib") - #pragma comment (lib, "ws2_32.lib") - #pragma comment (lib, "version.lib") - #pragma comment (lib, "shlwapi.lib") - #pragma comment (lib, "winmm.lib") - #pragma comment (lib, "DbgHelp.lib") - #pragma comment (lib, "Imm32.lib") - - #ifdef _NATIVE_WCHAR_T_DEFINED - #ifdef _DEBUG - #pragma comment (lib, "comsuppwd.lib") - #else - #pragma comment (lib, "comsuppw.lib") - #endif - #else - #ifdef _DEBUG - #pragma comment (lib, "comsuppd.lib") - #else - #pragma comment (lib, "comsupp.lib") - #endif - #endif - -#endif - -//============================================================================== -DexedAudioProcessor::DexedAudioProcessor() - : AudioProcessor(BusesProperties().withOutput("output", AudioChannelSet::stereo(), true)) { -#ifdef DEBUG - - // avoid creating the log file if it is in standalone mode - if ( !JUCEApplication::isStandaloneApp() ) { - Logger *tmp = Logger::getCurrentLogger(); - if ( tmp == NULL ) { - Logger::setCurrentLogger(FileLogger::createDateStampedLogger("Dexed", "DebugSession-", "log", "DexedAudioProcessor Created")); - } - } - TRACE("Hi"); -#endif - - Exp2::init(); - Tanh::init(); - Sin::init(); - - synthTuningState = createStandardTuning(); - - lastStateSave = 0; - currentNote = -1; - engineType = -1; - - vuSignal = 0; - monoMode = 0; - - resolvAppDir(); - - initCtrl(); - sendSysexChange = true; - normalizeDxVelocity = false; - sysexComm.listener = this; - showKeyboard = true; - - memset(&voiceStatus, 0, sizeof(VoiceStatus)); - setEngineType(DEXED_ENGINE_MARKI); - - controllers.values_[kControllerPitchRangeUp] = 3; - controllers.values_[kControllerPitchRangeDn] = 3; - controllers.values_[kControllerPitchStep] = 0; - controllers.masterTune = 0; - - loadPreference(); - - for (int note = 0; note < MAX_ACTIVE_NOTES; ++note) { - voices[note].dx7_note = NULL; - } - setCurrentProgram(0); - nextMidi = NULL; - midiMsg = NULL; - - clipboardContent = -1; - - mtsClient = NULL; - mtsClient = MTS_RegisterClient(); -} - -DexedAudioProcessor::~DexedAudioProcessor() { - Logger *tmp = Logger::getCurrentLogger(); - if ( tmp != NULL ) { - Logger::setCurrentLogger(NULL); - delete tmp; - } - TRACE("Bye"); - if (mtsClient) MTS_DeregisterClient(mtsClient); -} - -//============================================================================== -void DexedAudioProcessor::prepareToPlay(double sampleRate, int samplesPerBlock) { - Freqlut::init(sampleRate); - Lfo::init(sampleRate); - PitchEnv::init(sampleRate); - Env::init_sr(sampleRate); - fx.init(sampleRate); - - for (int note = 0; note < MAX_ACTIVE_NOTES; ++note) { - voices[note].dx7_note = new Dx7Note(synthTuningState, mtsClient); - voices[note].keydown = false; - voices[note].sustained = false; - voices[note].live = false; - } - - currentNote = 0; - controllers.values_[kControllerPitch] = 0x2000; - controllers.modwheel_cc = 0; - controllers.foot_cc = 0; - controllers.breath_cc = 0; - controllers.aftertouch_cc = 0; - controllers.refresh(); - - sustain = false; - extra_buf_size = 0; - - keyboardState.reset(); - - lfo.reset(data + 137); - - nextMidi = new MidiMessage(0xF0); - midiMsg = new MidiMessage(0xF0); -} - -void DexedAudioProcessor::releaseResources() { - currentNote = -1; - - for (int note = 0; note < MAX_ACTIVE_NOTES; ++note) { - if ( voices[note].dx7_note != NULL ) { - delete voices[note].dx7_note; - voices[note].dx7_note = NULL; - } - voices[note].keydown = false; - voices[note].sustained = false; - voices[note].live = false; - } - - keyboardState.reset(); - if ( nextMidi != NULL ) { - delete nextMidi; - nextMidi = NULL; - } - if ( midiMsg != NULL ) { - delete midiMsg; - midiMsg = NULL; - } -} - -void DexedAudioProcessor::processBlock(AudioSampleBuffer& buffer, MidiBuffer& midiMessages) { - - juce::ScopedNoDenormals noDenormals; - - int numSamples = buffer.getNumSamples(); - int i; - - if ( refreshVoice ) { - for(i=0;i < MAX_ACTIVE_NOTES;i++) { - if ( voices[i].live ) - voices[i].dx7_note->update(data, voices[i].midi_note, voices[i].velocity, voices[i].channel); - } - lfo.reset(data + 137); - refreshVoice = false; - } - - keyboardState.processNextMidiBuffer(midiMessages, 0, numSamples, true); - - MidiBuffer::Iterator it(midiMessages); - hasMidiMessage = it.getNextEvent(*nextMidi,midiEventPos); - - float *channelData = buffer.getWritePointer(0); - - // flush first events - for (i=0; i < numSamples && i < extra_buf_size; i++) { - channelData[i] = extra_buf[i]; - } - - // remaining buffer is still to be processed - if (extra_buf_size > numSamples) { - for (int j = 0; j < extra_buf_size - numSamples; j++) { - extra_buf[j] = extra_buf[j + numSamples]; - } - extra_buf_size -= numSamples; - - // flush the events, they will be process in the next cycle - while(getNextEvent(&it, numSamples)) { - processMidiMessage(midiMsg); - } - } else { - for (; i < numSamples; i += N) { - AlignedBuf audiobuf; - float sumbuf[N]; - - while(getNextEvent(&it, i)) { - processMidiMessage(midiMsg); - } - - for (int j = 0; j < N; ++j) { - audiobuf.get()[j] = 0; - sumbuf[j] = 0; - } - int32_t lfovalue = lfo.getsample(); - int32_t lfodelay = lfo.getdelay(); - - bool checkMTSESPRetuning = synthTuningState->is_standard_tuning() && - MTS_HasMaster(mtsClient); - - for (int note = 0; note < MAX_ACTIVE_NOTES; ++note) { - if (voices[note].live) { - - if (checkMTSESPRetuning) - voices[note].dx7_note->updateBasePitches(); - - voices[note].dx7_note->compute(audiobuf.get(), lfovalue, lfodelay, &controllers); - - for (int j=0; j < N; ++j) { - int32_t val = audiobuf.get()[j]; - - val = val >> 4; - int clip_val = val < -(1 << 24) ? 0x8000 : val >= (1 << 24) ? 0x7fff : val >> 9; - float f = ((float) clip_val) / (float) 0x8000; - if( f > 1 ) f = 1; - if( f < -1 ) f = -1; - sumbuf[j] += f; - audiobuf.get()[j] = 0; - } - } - } - - int jmax = numSamples - i; - for (int j = 0; j < N; ++j) { - if (j < jmax) { - channelData[i + j] = sumbuf[j]; - } else { - extra_buf[j - jmax] = sumbuf[j]; - } - } - } - extra_buf_size = i - numSamples; - } - - while(getNextEvent(&it, numSamples)) { - processMidiMessage(midiMsg); - } - - fx.process(channelData, numSamples); - for(i=0; i vuSignal) - vuSignal = s; - else if (vuSignal > 0.001f) - vuSignal *= decayFactor; - else - vuSignal = 0; - } - - // DX7 is a mono synth - buffer.copyFrom(1, 0, channelData, numSamples, 1); -} - - -//============================================================================== -// This creates new instances of the plugin.. -AudioProcessor* JUCE_CALLTYPE createPluginFilter() { - return new DexedAudioProcessor(); -} - -bool DexedAudioProcessor::getNextEvent(MidiBuffer::Iterator* iter,const int samplePos) { - if (hasMidiMessage && midiEventPos <= samplePos) { - *midiMsg = *nextMidi; - hasMidiMessage = iter->getNextEvent(*nextMidi, midiEventPos); - return true; - } - return false; -} - -void DexedAudioProcessor::processMidiMessage(const MidiMessage *msg) { - if ( msg->isSysEx() ) { - handleIncomingMidiMessage(NULL, *msg); - return; - } - - const uint8 *buf = msg->getRawData(); - uint8_t cmd = buf[0]; - uint8_t cf0 = cmd & 0xf0; - auto channel = msg->getChannel(); - - - if( controllers.mpeEnabled && channel != 1 && - ( - (cf0 == 0xb0 && buf[1] == 74 ) || //timbre - (cf0 == 0xd0 ) || // aftertouch - (cf0 == 0xe0 ) // pb - ) - ) - { - // OK so find my voice index - int voiceIndex = -1; - for( int i=0; i= 0 ) - { - int i = voiceIndex; - switch(cf0) { - case 0xb0: - voices[i].mpeTimbre = (int)buf[2]; - voices[i].dx7_note->mpeTimbre = (int)buf[2]; - break; - case 0xd0: - voices[i].mpePressure = (int)buf[1]; - voices[i].dx7_note->mpePressure = (int)buf[1]; - break; - case 0xe0: - voices[i].mpePitchBend = (int)( buf[1] | (buf[2] << 7) ); - voices[i].dx7_note->mpePitchBend = (int)( buf[1] | ( buf[2] << 7 ) ); - break; - } - } - } - else - { - switch(cmd & 0xf0) { - case 0x80 : - keyup(channel, buf[1], buf[2]); - return; - - case 0x90 : - if (!synthTuningState->is_standard_tuning() || !buf[2] || - !MTS_HasMaster(mtsClient) || !MTS_ShouldFilterNote(mtsClient, buf[1], channel - 1)) - keydown(channel, buf[1], buf[2]); - return; - - case 0xb0 : { - int ctrl = buf[1]; - int value = buf[2]; - - - switch(ctrl) { - case 1: - controllers.modwheel_cc = value; - controllers.refresh(); - break; - case 2: - controllers.breath_cc = value; - controllers.refresh(); - break; - case 4: - controllers.foot_cc = value; - controllers.refresh(); - break; - case 64: - sustain = value > 63; - if (!sustain) { - for (int note = 0; note < MAX_ACTIVE_NOTES; note++) { - if (voices[note].sustained && !voices[note].keydown) { - voices[note].dx7_note->keyup(); - voices[note].sustained = false; - } - } - } - break; - case 120: - panic(); - break; - case 123: - for (int note = 0; note < MAX_ACTIVE_NOTES; note++) { - if (voices[note].keydown) - keyup(channel, voices[note].midi_note, 0); - } - break; - default: - TRACE("handle CC %d %d", ctrl, value); - if ( mappedMidiCC.contains(ctrl) ) { - Ctrl *linkedCtrl = mappedMidiCC[ctrl]; - - // We are not publishing this in the DSP thread, moving that in the - // event thread - linkedCtrl->publishValueAsync((float) value / 127); - } - // this is used to notify the dialog that a CC value was received. - lastCCUsed.setValue(ctrl); - } - } - return; - - case 0xc0 : - setCurrentProgram(buf[1]); - return; - - case 0xd0 : - controllers.aftertouch_cc = buf[1]; - controllers.refresh(); - return; - - case 0xe0 : - controllers.values_[kControllerPitch] = buf[1] | (buf[2] << 7); - return; - } - } -} - -#define ACT(v) (v.keydown ? v.midi_note : -1) - -void DexedAudioProcessor::keydown(uint8_t channel, uint8_t pitch, uint8_t velo) { - if ( velo == 0 ) { - keyup(channel, pitch, velo); - return; - } - - pitch += tuningTranspositionShift(); - - if ( normalizeDxVelocity ) { - velo = ((float)velo) * 0.7874015; // 100/127 - } - - if( controllers.mpeEnabled ) - { - int note = currentNote; - for( int i=0; iinit(data, pitch, velo, channel); - if ( data[136] ) - voices[note].dx7_note->oscSync(); - break; - } - note = (note + 1) % MAX_ACTIVE_NOTES; - } - - if ( monoMode ) { - for(int i=0; itransferSignal(*voices[i].dx7_note); - break; - } - if ( voices[i].midi_note < pitch ) { - voices[i].live = false; - voices[note].dx7_note->transferState(*voices[i].dx7_note); - break; - } - return; - } - } - } - - voices[note].live = true; - //TRACE("activate %d [ %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d ]", pitch, ACT(voices[0]), ACT(voices[1]), ACT(voices[2]), ACT(voices[3]), ACT(voices[4]), ACT(voices[5]), ACT(voices[6]), ACT(voices[7]), ACT(voices[8]), ACT(voices[9]), ACT(voices[10]), ACT(voices[11]), ACT(voices[12]), ACT(voices[13]), ACT(voices[14]), ACT(voices[15])); -} - -void DexedAudioProcessor::keyup(uint8_t chan, uint8_t pitch, uint8_t velo) { - pitch += tuningTranspositionShift(); - - int note; - for (note=0; note= MAX_ACTIVE_NOTES ) { - TRACE("note found ??? %d [ %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d ]", pitch, ACT(voices[0]), ACT(voices[1]), ACT(voices[2]), ACT(voices[3]), ACT(voices[4]), ACT(voices[5]), ACT(voices[6]), ACT(voices[7]), ACT(voices[8]), ACT(voices[9]), ACT(voices[10]), ACT(voices[11]), ACT(voices[12]), ACT(voices[13]), ACT(voices[14]), ACT(voices[15])); - return; - } - - if ( monoMode ) { - int highNote = -1; - int target = 0; - for (int i=0; i highNote ) { - target = i; - highNote = voices[i].midi_note; - } - } - - if ( highNote != -1 && voices[note].live ) { - voices[note].live = false; - voices[target].live = true; - voices[target].dx7_note->transferState(*voices[note].dx7_note); - } - } - - if ( sustain ) { - voices[note].sustained = true; - } else { - voices[note].dx7_note->keyup(); - } -} - -int DexedAudioProcessor::tuningTranspositionShift() -{ - if( synthTuningState->is_standard_tuning() || ! controllers.transpose12AsScale ) - return data[144] - 24; - else - { - int d144 = data[144]; - if( d144 % 12 == 0 ) - { - int oct = (d144 - 24) / 12; - int res = oct * synthTuningState->scale_length(); - return res; - } - else - return data[144] - 24; - } -} - -void DexedAudioProcessor::panic() { - for(int i=0;ioscSync(); - } - } - keyboardState.reset(); -} - -void DexedAudioProcessor::handleIncomingMidiMessage(MidiInput* source, const MidiMessage& message) { - if ( message.isActiveSense() ) - return; - - sysexComm.inActivity = true; - - const uint8 *buf = message.getRawData(); - int sz = message.getRawDataSize(); - - //TRACE("%X %X %X %X %X %X", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]); - - if ( ! message.isSysEx() ) - return; - - // test if it is a Yamaha Sysex - if ( buf[1] != 0x43 ) { - TRACE("not a yamaha sysex %d", buf[0]); - return; - } - - int substatus = buf[2] >> 4; - - if ( substatus == 0 ) { - // single voice dump - if ( buf[3] == 0 ) { - if ( sz < 156 ) { - TRACE("wrong single voice datasize %d", sz); - return; - } - if ( updateProgramFromSysex(buf+6) ) - TRACE("bad checksum when updating program from sysex message"); - } - - // 32 voice dump - if ( buf[3] == 9 ) { - if ( sz < 4104 ) { - TRACE("wrong 32 voice dump data size %d", sz); - return; - } - - Cartridge received; - if ( received.load(buf, sz) == 0 ) { - loadCartridge(received); - setCurrentProgram(0); - } - } - } else if ( substatus == 1 ) { - // parameter change - if ( sz < 7 ) { - TRACE("wrong single voice datasize %d", sz); - return; - } - - uint8 offset = (buf[3] << 7) + buf[4]; - uint8 value = buf[5]; - - TRACE("parameter change message offset:%d value:%d", offset, value); - - if ( offset > 155 ) { - TRACE("wrong offset size"); - return; - } - - if ( offset == 155 ) { - unpackOpSwitch(value); - } else { - data[offset] = value; - } - } else { - TRACE("unknown sysex substatus: %d", substatus); - } - - updateHostDisplay(); - forceRefreshUI = true; -} - -int DexedAudioProcessor::getEngineType() { - return engineType; -} - -void DexedAudioProcessor::setEngineType(int tp) { - TRACE("settings engine %d", tp); - - switch (tp) { - case DEXED_ENGINE_MARKI: - controllers.core = &engineMkI; - break; - case DEXED_ENGINE_OPL: - controllers.core = &engineOpl; - break; - default: - controllers.core = &engineMsfa; - break; - } - engineType = tp; -} - -void DexedAudioProcessor::setMonoMode(bool mode) { - panic(); - monoMode = mode; -} - -// ==================================================================== -bool DexedAudioProcessor::peekVoiceStatus() { - if ( currentNote == -1 ) - return false; - - // we are trying to find the last "keydown" note - int note = currentNote; - for (int i = 0; i < MAX_ACTIVE_NOTES; i++) { - if (voices[note].keydown) { - voices[note].dx7_note->peekVoiceStatus(voiceStatus); - return true; - } - if ( --note < 0 ) - note = MAX_ACTIVE_NOTES-1; - } - - // not found; try a live note - note = currentNote; - for (int i = 0; i < MAX_ACTIVE_NOTES; i++) { - if (voices[note].live) { - voices[note].dx7_note->peekVoiceStatus(voiceStatus); - return true; - } - if ( --note < 0 ) - note = MAX_ACTIVE_NOTES-1; - } - - return true; -} - -const String DexedAudioProcessor::getInputChannelName (int channelIndex) const { - return String (channelIndex + 1); -} - -const String DexedAudioProcessor::getOutputChannelName (int channelIndex) const { - return String (channelIndex + 1); -} - -bool DexedAudioProcessor::isInputChannelStereoPair (int index) const { - return true; -} - -bool DexedAudioProcessor::isOutputChannelStereoPair (int index) const { - return true; -} - -bool DexedAudioProcessor::isBusesLayoutSupported(const BusesLayout &layouts) const { - return layouts.getMainOutputChannelSet() == AudioChannelSet::mono() - || layouts.getMainOutputChannelSet() == AudioChannelSet::stereo(); -} - -bool DexedAudioProcessor::acceptsMidi() const { - return true; -} - -bool DexedAudioProcessor::producesMidi() const { - return true; -} - -bool DexedAudioProcessor::silenceInProducesSilenceOut() const { - return false; -} - -double DexedAudioProcessor::getTailLengthSeconds() const { - return 0.0; -} - -const String DexedAudioProcessor::getName() const { - return JucePlugin_Name; -} - -//============================================================================== -bool DexedAudioProcessor::hasEditor() const { - return true; // (change this to false if you choose to not supply an editor) -} - -void DexedAudioProcessor::updateUI() { - // notify host something has changed - updateHostDisplay(); - - AudioProcessorEditor *editor = getActiveEditor(); - if ( editor == NULL ) { - return; - } - DexedAudioProcessorEditor *dexedEditor = (DexedAudioProcessorEditor *) editor; - dexedEditor->updateUI(); -} - -AudioProcessorEditor* DexedAudioProcessor::createEditor() { - static const uint8_t HIGH_DPI_THRESHOLD = 128; - - AudioProcessorEditor* editor = new DexedAudioProcessorEditor (this); - - if ( dpiScaleFactor == -1 ) { - if ( Desktop::getInstance().getDisplays().getPrimaryDisplay()->dpi > HIGH_DPI_THRESHOLD ) { - dpiScaleFactor = 1.5; - } else { - dpiScaleFactor = 1.0; - } - } - - const juce::Rectangle rect(DexedAudioProcessorEditor::WINDOW_SIZE_X * dpiScaleFactor,DexedAudioProcessorEditor::WINDOW_SIZE_Y * dpiScaleFactor); - bool displayFound = false; - - // validate if there is really a display that can show the complete plugin size - for (auto& display : Desktop::getInstance().getDisplays().displays) { - if ( display.userArea.getHeight() > rect.getHeight() && display.userArea.getWidth() > rect.getWidth() ) - displayFound = true; - } - - // no display found, scaling to default value - if ( ! displayFound ) - dpiScaleFactor = 1.0; - - // Currently the clap juce wrapper doesn't work with this deprecated scale factor direct set so - if ( is_clap ) - dpiScaleFactor = 1.0; - - // The scale factor needs to be done after object creation otherwise Bitwig, Live and REAPER can't render the - // plugin window. - editor->setScaleFactor(dpiScaleFactor); - - return editor; -} - -void DexedAudioProcessor::handleAsyncUpdate() { - updateUI(); -} - -void dexed_trace(const char *source, const char *fmt, ...) { - char output[4096]; - va_list argptr; - va_start(argptr, fmt); - vsnprintf(output, 4095, fmt, argptr); - va_end(argptr); - - String dest; - dest << source << " " << output; - Logger::writeToLog(dest); -} - -void DexedAudioProcessor::resetTuning(std::shared_ptr t) -{ - synthTuningState = t; - for( int i=0; ituning_state_ = synthTuningState; -} - -void DexedAudioProcessor::retuneToStandard() -{ - currentSCLData = ""; - currentKBMData = ""; - resetTuning(createStandardTuning()); -} - -void DexedAudioProcessor::applySCLTuning() { - FileChooser fc( "Please select an SCL File", File(), "*.scl" ); - if( fc.browseForFileToOpen() ) - { - auto s = fc.getResult(); - applySCLTuning(s); - } -} - -void DexedAudioProcessor::applySCLTuning(File s) { - std::string sclcontents = s.loadFileAsString().toStdString(); - applySCLTuning(sclcontents); -} - -void DexedAudioProcessor::applySCLTuning(std::string sclcontents) { - currentSCLData = sclcontents; - - if( currentKBMData.size() < 1 ) - { - auto t = createTuningFromSCLData( sclcontents ); - resetTuning(t); - } - else - { - auto t = createTuningFromSCLAndKBMData( sclcontents, currentKBMData ); - resetTuning(t); - } -} - -void DexedAudioProcessor::applyKBMMapping() { - FileChooser fc( "Please select an KBM File", File(), "*.kbm" ); - if( fc.browseForFileToOpen() ) - { - auto s = fc.getResult(); - applyKBMMapping(s); - } -} - -void DexedAudioProcessor::applyKBMMapping( File s ) -{ - std::string kbmcontents = s.loadFileAsString().toStdString(); - applyKBMMapping(kbmcontents); -} - -void DexedAudioProcessor::applyKBMMapping(std::string kbmcontents) { - currentKBMData = kbmcontents; - - if( currentSCLData.size() < 1 ) - { - auto t = createTuningFromKBMData( currentKBMData ); - resetTuning(t); - } - else - { - auto t = createTuningFromSCLAndKBMData( currentSCLData, currentKBMData ); - resetTuning(t); - } -} +/** + * + * Copyright (c) 2013-2018 Pascal Gauthier. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include +#include + +#include "PluginProcessor.h" +#include "PluginEditor.h" + +#include "Dexed.h" +#include "msfa/synth.h" +#include "msfa/freqlut.h" +#include "msfa/sin.h" +#include "msfa/exp2.h" +#include "msfa/env.h" +#include "msfa/pitchenv.h" +#include "msfa/aligned_buf.h" +#include "msfa/fm_op_kernel.h" + +#if JUCE_MSVC + #pragma comment (lib, "kernel32.lib") + #pragma comment (lib, "user32.lib") + #pragma comment (lib, "wininet.lib") + #pragma comment (lib, "advapi32.lib") + #pragma comment (lib, "ws2_32.lib") + #pragma comment (lib, "version.lib") + #pragma comment (lib, "shlwapi.lib") + #pragma comment (lib, "winmm.lib") + #pragma comment (lib, "DbgHelp.lib") + #pragma comment (lib, "Imm32.lib") + + #ifdef _NATIVE_WCHAR_T_DEFINED + #ifdef _DEBUG + #pragma comment (lib, "comsuppwd.lib") + #else + #pragma comment (lib, "comsuppw.lib") + #endif + #else + #ifdef _DEBUG + #pragma comment (lib, "comsuppd.lib") + #else + #pragma comment (lib, "comsupp.lib") + #endif + #endif + +#endif + +//============================================================================== +DexedAudioProcessor::DexedAudioProcessor() + : AudioProcessor(BusesProperties().withOutput("output", AudioChannelSet::stereo(), true)) { +#ifdef DEBUG + + // avoid creating the log file if it is in standalone mode + if ( !JUCEApplication::isStandaloneApp() ) { + Logger *tmp = Logger::getCurrentLogger(); + if ( tmp == NULL ) { + Logger::setCurrentLogger(FileLogger::createDateStampedLogger("Dexed", "DebugSession-", "log", "DexedAudioProcessor Created")); + } + } + TRACE("Hi"); +#endif + + Exp2::init(); + Tanh::init(); + Sin::init(); + + synthTuningState = createStandardTuning(); + + lastStateSave = 0; + currentNote = -1; + engineType = -1; + + vuSignal = 0; + monoMode = 0; + + resolvAppDir(); + + initCtrl(); + sendSysexChange = true; + normalizeDxVelocity = false; + sysexComm.listener = this; + showKeyboard = true; + + memset(&voiceStatus, 0, sizeof(VoiceStatus)); + setEngineType(DEXED_ENGINE_MARKI); + + controllers.values_[kControllerPitchRangeUp] = 3; + controllers.values_[kControllerPitchRangeDn] = 3; + controllers.values_[kControllerPitchStep] = 0; + controllers.masterTune = 0; + + loadPreference(); + + for (int note = 0; note < MAX_ACTIVE_NOTES; ++note) { + voices[note].dx7_note = NULL; + } + setCurrentProgram(0); + nextMidi = NULL; + midiMsg = NULL; + + clipboardContent = -1; + + mtsClient = NULL; + mtsClient = MTS_RegisterClient(); +} + +DexedAudioProcessor::~DexedAudioProcessor() { + Logger *tmp = Logger::getCurrentLogger(); + if ( tmp != NULL ) { + Logger::setCurrentLogger(NULL); + delete tmp; + } + TRACE("Bye"); + if (mtsClient) MTS_DeregisterClient(mtsClient); +} + +//============================================================================== +void DexedAudioProcessor::prepareToPlay(double sampleRate, int samplesPerBlock) { + Freqlut::init(sampleRate); + Lfo::init(sampleRate); + PitchEnv::init(sampleRate); + Env::init_sr(sampleRate); + fx.init(sampleRate); + + for (int note = 0; note < MAX_ACTIVE_NOTES; ++note) { + voices[note].dx7_note = new Dx7Note(synthTuningState, mtsClient); + voices[note].keydown = false; + voices[note].sustained = false; + voices[note].live = false; + } + + currentNote = 0; + controllers.values_[kControllerPitch] = 0x2000; + controllers.modwheel_cc = 0; + controllers.foot_cc = 0; + controllers.breath_cc = 0; + controllers.aftertouch_cc = 0; + controllers.refresh(); + + sustain = false; + extra_buf_size = 0; + + keyboardState.reset(); + + lfo.reset(data + 137); + + nextMidi = new MidiMessage(0xF0); + midiMsg = new MidiMessage(0xF0); +} + +void DexedAudioProcessor::releaseResources() { + currentNote = -1; + + for (int note = 0; note < MAX_ACTIVE_NOTES; ++note) { + if ( voices[note].dx7_note != NULL ) { + delete voices[note].dx7_note; + voices[note].dx7_note = NULL; + } + voices[note].keydown = false; + voices[note].sustained = false; + voices[note].live = false; + } + + keyboardState.reset(); + if ( nextMidi != NULL ) { + delete nextMidi; + nextMidi = NULL; + } + if ( midiMsg != NULL ) { + delete midiMsg; + midiMsg = NULL; + } +} + +void DexedAudioProcessor::processBlock(AudioSampleBuffer& buffer, MidiBuffer& midiMessages) { + + juce::ScopedNoDenormals noDenormals; + + int numSamples = buffer.getNumSamples(); + int i; + + if ( refreshVoice ) { + for(i=0;i < MAX_ACTIVE_NOTES;i++) { + if ( voices[i].live ) + voices[i].dx7_note->update(data, voices[i].midi_note, voices[i].velocity, voices[i].channel); + } + lfo.reset(data + 137); + refreshVoice = false; + } + + keyboardState.processNextMidiBuffer(midiMessages, 0, numSamples, true); + + MidiBuffer::Iterator it(midiMessages); + hasMidiMessage = it.getNextEvent(*nextMidi,midiEventPos); + + float *channelData = buffer.getWritePointer(0); + + // flush first events + for (i=0; i < numSamples && i < extra_buf_size; i++) { + channelData[i] = extra_buf[i]; + } + + // remaining buffer is still to be processed + if (extra_buf_size > numSamples) { + for (int j = 0; j < extra_buf_size - numSamples; j++) { + extra_buf[j] = extra_buf[j + numSamples]; + } + extra_buf_size -= numSamples; + + // flush the events, they will be process in the next cycle + while(getNextEvent(&it, numSamples)) { + processMidiMessage(midiMsg); + } + } else { + for (; i < numSamples; i += N) { + AlignedBuf audiobuf; + float sumbuf[N]; + + while(getNextEvent(&it, i)) { + processMidiMessage(midiMsg); + } + + for (int j = 0; j < N; ++j) { + audiobuf.get()[j] = 0; + sumbuf[j] = 0; + } + int32_t lfovalue = lfo.getsample(); + int32_t lfodelay = lfo.getdelay(); + + bool checkMTSESPRetuning = synthTuningState->is_standard_tuning() && + MTS_HasMaster(mtsClient); + + for (int note = 0; note < MAX_ACTIVE_NOTES; ++note) { + if (voices[note].live) { + + if (checkMTSESPRetuning) + voices[note].dx7_note->updateBasePitches(); + + voices[note].dx7_note->compute(audiobuf.get(), lfovalue, lfodelay, &controllers); + + for (int j=0; j < N; ++j) { + int32_t val = audiobuf.get()[j]; + + val = val >> 4; + int clip_val = val < -(1 << 24) ? 0x8000 : val >= (1 << 24) ? 0x7fff : val >> 9; + float f = ((float) clip_val) / (float) 0x8000; + if( f > 1 ) f = 1; + if( f < -1 ) f = -1; + sumbuf[j] += f; + audiobuf.get()[j] = 0; + } + } + } + + int jmax = numSamples - i; + for (int j = 0; j < N; ++j) { + if (j < jmax) { + channelData[i + j] = sumbuf[j]; + } else { + extra_buf[j - jmax] = sumbuf[j]; + } + } + } + extra_buf_size = i - numSamples; + } + + while(getNextEvent(&it, numSamples)) { + processMidiMessage(midiMsg); + } + + fx.process(channelData, numSamples); + for(i=0; i vuSignal) + vuSignal = s; + else if (vuSignal > 0.001f) + vuSignal *= decayFactor; + else + vuSignal = 0; + } + + // DX7 is a mono synth + buffer.copyFrom(1, 0, channelData, numSamples, 1); +} + + +//============================================================================== +// This creates new instances of the plugin.. +AudioProcessor* JUCE_CALLTYPE createPluginFilter() { + return new DexedAudioProcessor(); +} + +bool DexedAudioProcessor::getNextEvent(MidiBuffer::Iterator* iter,const int samplePos) { + if (hasMidiMessage && midiEventPos <= samplePos) { + *midiMsg = *nextMidi; + hasMidiMessage = iter->getNextEvent(*nextMidi, midiEventPos); + return true; + } + return false; +} + +void DexedAudioProcessor::processMidiMessage(const MidiMessage *msg) { + if ( msg->isSysEx() ) { + handleIncomingMidiMessage(NULL, *msg); + return; + } + + const uint8 *buf = msg->getRawData(); + uint8_t cmd = buf[0]; + uint8_t cf0 = cmd & 0xf0; + auto channel = msg->getChannel(); + + + if( controllers.mpeEnabled && channel != 1 && + ( + (cf0 == 0xb0 && buf[1] == 74 ) || //timbre + (cf0 == 0xd0 ) || // aftertouch + (cf0 == 0xe0 ) // pb + ) + ) + { + // OK so find my voice index + int voiceIndex = -1; + for( int i=0; i= 0 ) + { + int i = voiceIndex; + switch(cf0) { + case 0xb0: + voices[i].mpeTimbre = (int)buf[2]; + voices[i].dx7_note->mpeTimbre = (int)buf[2]; + break; + case 0xd0: + voices[i].mpePressure = (int)buf[1]; + voices[i].dx7_note->mpePressure = (int)buf[1]; + break; + case 0xe0: + voices[i].mpePitchBend = (int)( buf[1] | (buf[2] << 7) ); + voices[i].dx7_note->mpePitchBend = (int)( buf[1] | ( buf[2] << 7 ) ); + break; + } + } + } + else + { + switch(cmd & 0xf0) { + case 0x80 : + keyup(channel, buf[1], buf[2]); + return; + + case 0x90 : + if (!synthTuningState->is_standard_tuning() || !buf[2] || + !MTS_HasMaster(mtsClient) || !MTS_ShouldFilterNote(mtsClient, buf[1], channel - 1)) + keydown(channel, buf[1], buf[2]); + return; + + case 0xb0 : { + int ctrl = buf[1]; + int value = buf[2]; + + + switch(ctrl) { + case 1: + controllers.modwheel_cc = value; + controllers.refresh(); + break; + case 2: + controllers.breath_cc = value; + controllers.refresh(); + break; + case 4: + controllers.foot_cc = value; + controllers.refresh(); + break; + case 64: + sustain = value > 63; + if (!sustain) { + for (int note = 0; note < MAX_ACTIVE_NOTES; note++) { + if (voices[note].sustained && !voices[note].keydown) { + voices[note].dx7_note->keyup(); + voices[note].sustained = false; + } + } + } + break; + case 120: + panic(); + break; + case 123: + for (int note = 0; note < MAX_ACTIVE_NOTES; note++) { + if (voices[note].keydown) + keyup(channel, voices[note].midi_note, 0); + } + break; + default: + TRACE("handle CC %d %d", ctrl, value); + if ( mappedMidiCC.contains(ctrl) ) { + Ctrl *linkedCtrl = mappedMidiCC[ctrl]; + + // We are not publishing this in the DSP thread, moving that in the + // event thread + linkedCtrl->publishValueAsync((float) value / 127); + } + // this is used to notify the dialog that a CC value was received. + lastCCUsed.setValue(ctrl); + } + } + return; + + case 0xc0 : + setCurrentProgram(buf[1]); + return; + + case 0xd0 : + controllers.aftertouch_cc = buf[1]; + controllers.refresh(); + return; + + case 0xe0 : + controllers.values_[kControllerPitch] = buf[1] | (buf[2] << 7); + return; + } + } +} + +#define ACT(v) (v.keydown ? v.midi_note : -1) + +void DexedAudioProcessor::keydown(uint8_t channel, uint8_t pitch, uint8_t velo) { + if ( velo == 0 ) { + keyup(channel, pitch, velo); + return; + } + + pitch += tuningTranspositionShift(); + + if ( normalizeDxVelocity ) { + velo = ((float)velo) * 0.7874015; // 100/127 + } + + if( controllers.mpeEnabled ) + { + int note = currentNote; + for( int i=0; iinit(data, pitch, velo, channel); + if ( data[136] ) + voices[note].dx7_note->oscSync(); + break; + } + note = (note + 1) % MAX_ACTIVE_NOTES; + } + + if ( monoMode ) { + for(int i=0; itransferSignal(*voices[i].dx7_note); + break; + } + if ( voices[i].midi_note < pitch ) { + voices[i].live = false; + voices[note].dx7_note->transferState(*voices[i].dx7_note); + break; + } + return; + } + } + } + + voices[note].live = true; + //TRACE("activate %d [ %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d ]", pitch, ACT(voices[0]), ACT(voices[1]), ACT(voices[2]), ACT(voices[3]), ACT(voices[4]), ACT(voices[5]), ACT(voices[6]), ACT(voices[7]), ACT(voices[8]), ACT(voices[9]), ACT(voices[10]), ACT(voices[11]), ACT(voices[12]), ACT(voices[13]), ACT(voices[14]), ACT(voices[15])); +} + +void DexedAudioProcessor::keyup(uint8_t chan, uint8_t pitch, uint8_t velo) { + pitch += tuningTranspositionShift(); + + int note; + for (note=0; note= MAX_ACTIVE_NOTES ) { + TRACE("note found ??? %d [ %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d ]", pitch, ACT(voices[0]), ACT(voices[1]), ACT(voices[2]), ACT(voices[3]), ACT(voices[4]), ACT(voices[5]), ACT(voices[6]), ACT(voices[7]), ACT(voices[8]), ACT(voices[9]), ACT(voices[10]), ACT(voices[11]), ACT(voices[12]), ACT(voices[13]), ACT(voices[14]), ACT(voices[15])); + return; + } + + if ( monoMode ) { + int highNote = -1; + int target = 0; + for (int i=0; i highNote ) { + target = i; + highNote = voices[i].midi_note; + } + } + + if ( highNote != -1 && voices[note].live ) { + voices[note].live = false; + voices[target].live = true; + voices[target].dx7_note->transferState(*voices[note].dx7_note); + } + } + + if ( sustain ) { + voices[note].sustained = true; + } else { + voices[note].dx7_note->keyup(); + } +} + +int DexedAudioProcessor::tuningTranspositionShift() +{ + if( synthTuningState->is_standard_tuning() || ! controllers.transpose12AsScale ) + return data[144] - 24; + else + { + int d144 = data[144]; + if( d144 % 12 == 0 ) + { + int oct = (d144 - 24) / 12; + int res = oct * synthTuningState->scale_length(); + return res; + } + else + return data[144] - 24; + } +} + +void DexedAudioProcessor::panic() { + for(int i=0;ioscSync(); + } + } + keyboardState.reset(); +} + +void DexedAudioProcessor::handleIncomingMidiMessage(MidiInput* source, const MidiMessage& message) { + if ( message.isActiveSense() ) + return; + + sysexComm.inActivity = true; + + const uint8 *buf = message.getRawData(); + int sz = message.getRawDataSize(); + + //TRACE("%X %X %X %X %X %X", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]); + + if ( ! message.isSysEx() ) + return; + + // test if it is a Yamaha Sysex + if ( buf[1] != 0x43 ) { + TRACE("not a yamaha sysex %d", buf[0]); + return; + } + + int substatus = buf[2] >> 4; + + if ( substatus == 0 ) { + // single voice dump + if ( buf[3] == 0 ) { + if ( sz < 156 ) { + TRACE("wrong single voice datasize %d", sz); + return; + } + if ( updateProgramFromSysex(buf+6) ) + TRACE("bad checksum when updating program from sysex message"); + } + + // 32 voice dump + if ( buf[3] == 9 ) { + if ( sz < 4104 ) { + TRACE("wrong 32 voice dump data size %d", sz); + return; + } + + Cartridge received; + if ( received.load(buf, sz) == 0 ) { + loadCartridge(received); + setCurrentProgram(0); + } + } + } else if ( substatus == 1 ) { + // parameter change + if ( sz < 7 ) { + TRACE("wrong single voice datasize %d", sz); + return; + } + + uint8 offset = (buf[3] << 7) + buf[4]; + uint8 value = buf[5]; + + TRACE("parameter change message offset:%d value:%d", offset, value); + + if ( offset > 155 ) { + TRACE("wrong offset size"); + return; + } + + if ( offset == 155 ) { + unpackOpSwitch(value); + } else { + data[offset] = value; + } + } else { + TRACE("unknown sysex substatus: %d", substatus); + } + + updateHostDisplay(); + forceRefreshUI = true; +} + +int DexedAudioProcessor::getEngineType() { + return engineType; +} + +void DexedAudioProcessor::setEngineType(int tp) { + TRACE("settings engine %d", tp); + + switch (tp) { + case DEXED_ENGINE_MARKI: + controllers.core = &engineMkI; + break; + case DEXED_ENGINE_OPL: + controllers.core = &engineOpl; + break; + default: + controllers.core = &engineMsfa; + break; + } + engineType = tp; +} + +void DexedAudioProcessor::setMonoMode(bool mode) { + panic(); + monoMode = mode; +} + +// ==================================================================== +bool DexedAudioProcessor::peekVoiceStatus() { + if ( currentNote == -1 ) + return false; + + // we are trying to find the last "keydown" note + int note = currentNote; + for (int i = 0; i < MAX_ACTIVE_NOTES; i++) { + if (voices[note].keydown) { + voices[note].dx7_note->peekVoiceStatus(voiceStatus); + return true; + } + if ( --note < 0 ) + note = MAX_ACTIVE_NOTES-1; + } + + // not found; try a live note + note = currentNote; + for (int i = 0; i < MAX_ACTIVE_NOTES; i++) { + if (voices[note].live) { + voices[note].dx7_note->peekVoiceStatus(voiceStatus); + return true; + } + if ( --note < 0 ) + note = MAX_ACTIVE_NOTES-1; + } + + return true; +} + +const String DexedAudioProcessor::getInputChannelName (int channelIndex) const { + return String (channelIndex + 1); +} + +const String DexedAudioProcessor::getOutputChannelName (int channelIndex) const { + return String (channelIndex + 1); +} + +bool DexedAudioProcessor::isInputChannelStereoPair (int index) const { + return true; +} + +bool DexedAudioProcessor::isOutputChannelStereoPair (int index) const { + return true; +} + +bool DexedAudioProcessor::isBusesLayoutSupported(const BusesLayout &layouts) const { + return layouts.getMainOutputChannelSet() == AudioChannelSet::mono() + || layouts.getMainOutputChannelSet() == AudioChannelSet::stereo(); +} + +bool DexedAudioProcessor::acceptsMidi() const { + return true; +} + +bool DexedAudioProcessor::producesMidi() const { + return true; +} + +bool DexedAudioProcessor::silenceInProducesSilenceOut() const { + return false; +} + +double DexedAudioProcessor::getTailLengthSeconds() const { + return 0.0; +} + +const String DexedAudioProcessor::getName() const { + return JucePlugin_Name; +} + +//============================================================================== +bool DexedAudioProcessor::hasEditor() const { + return true; // (change this to false if you choose to not supply an editor) +} + +void DexedAudioProcessor::updateUI() { + // notify host something has changed + updateHostDisplay(); + + AudioProcessorEditor *editor = getActiveEditor(); + if ( editor == NULL ) { + return; + } + DexedAudioProcessorEditor *dexedEditor = (DexedAudioProcessorEditor *) editor; + dexedEditor->updateUI(); +} + +AudioProcessorEditor* DexedAudioProcessor::createEditor() { + static const uint8_t HIGH_DPI_THRESHOLD = 128; + AudioProcessorEditor* editor = new DexedAudioProcessorEditor (this); + float scaleFactor = getDpiScaleFactor(); + + if ( scaleFactor == -1 ) { + if ( Desktop::getInstance().getDisplays().getPrimaryDisplay()->dpi > HIGH_DPI_THRESHOLD ) { + scaleFactor = 1.5; + } else { + scaleFactor = 1.0; + } + } + + const juce::Rectangle rect(DexedAudioProcessorEditor::WINDOW_SIZE_X * scaleFactor,DexedAudioProcessorEditor::WINDOW_SIZE_Y * scaleFactor); + bool displayFound = false; + + // validate if there is really a display that can show the complete plugin size + for (auto& display : Desktop::getInstance().getDisplays().displays) { + if ( display.userArea.getHeight() > rect.getHeight() && display.userArea.getWidth() > rect.getWidth() ) + displayFound = true; + } + + // no display found, scaling to default value + if ( ! displayFound ) + setDpiScaleFactor(1.0); + else + setDpiScaleFactor(scaleFactor); + return editor; +} + +void DexedAudioProcessor::setDpiScaleFactor(float factor) { + // Currently the clap juce wrapper doesn't work with this deprecated scale factor direct set so + if ( is_clap ) { + dpiScaleFactor = 1.0; + return; + } + dpiScaleFactor = factor; + + // The scale factor needs to be done after object creation otherwise Bitwig, Live and REAPER can't render the + // plugin window. + Desktop::getInstance().setGlobalScaleFactor(dpiScaleFactor); +} + +void DexedAudioProcessor::handleAsyncUpdate() { + updateUI(); +} + +void dexed_trace(const char *source, const char *fmt, ...) { + char output[4096]; + va_list argptr; + va_start(argptr, fmt); + vsnprintf(output, 4095, fmt, argptr); + va_end(argptr); + + String dest; + dest << source << " " << output; + Logger::writeToLog(dest); +} + +void DexedAudioProcessor::resetTuning(std::shared_ptr t) +{ + synthTuningState = t; + for( int i=0; ituning_state_ = synthTuningState; +} + +void DexedAudioProcessor::retuneToStandard() +{ + currentSCLData = ""; + currentKBMData = ""; + resetTuning(createStandardTuning()); +} + +void DexedAudioProcessor::applySCLTuning() { + FileChooser fc( "Please select an SCL File", File(), "*.scl" ); + if( fc.browseForFileToOpen() ) + { + auto s = fc.getResult(); + applySCLTuning(s); + } +} + +void DexedAudioProcessor::applySCLTuning(File s) { + std::string sclcontents = s.loadFileAsString().toStdString(); + applySCLTuning(sclcontents); +} + +void DexedAudioProcessor::applySCLTuning(std::string sclcontents) { + currentSCLData = sclcontents; + + if( currentKBMData.size() < 1 ) + { + auto t = createTuningFromSCLData( sclcontents ); + resetTuning(t); + } + else + { + auto t = createTuningFromSCLAndKBMData( sclcontents, currentKBMData ); + resetTuning(t); + } +} + +void DexedAudioProcessor::applyKBMMapping() { + FileChooser fc( "Please select an KBM File", File(), "*.kbm" ); + if( fc.browseForFileToOpen() ) + { + auto s = fc.getResult(); + applyKBMMapping(s); + } +} + +void DexedAudioProcessor::applyKBMMapping( File s ) +{ + std::string kbmcontents = s.loadFileAsString().toStdString(); + applyKBMMapping(kbmcontents); +} + +void DexedAudioProcessor::applyKBMMapping(std::string kbmcontents) { + currentKBMData = kbmcontents; + + if( currentSCLData.size() < 1 ) + { + auto t = createTuningFromKBMData( currentKBMData ); + resetTuning(t); + } + else + { + auto t = createTuningFromSCLAndKBMData( currentSCLData, currentKBMData ); + resetTuning(t); + } +} diff --git a/Source/PluginProcessor.h b/Source/PluginProcessor.h index 4f923477..a95b170c 100644 --- a/Source/PluginProcessor.h +++ b/Source/PluginProcessor.h @@ -1,279 +1,283 @@ -/** - * - * Copyright (c) 2013-2017 Pascal Gauthier. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#ifndef PLUGINPROCESSOR_H_INCLUDED -#define PLUGINPROCESSOR_H_INCLUDED - -#include "../JuceLibraryCode/JuceHeader.h" - -#include "clap-juce-extensions/clap-juce-extensions.h" - -#include "msfa/controllers.h" -#include "msfa/dx7note.h" -#include "msfa/lfo.h" -#include "msfa/synth.h" -#include "msfa/fm_core.h" -#include "msfa/tuning.h" -#include "PluginParam.h" -#include "PluginData.h" -#include "PluginFx.h" -#include "SysexComm.h" -#include "EngineMkI.h" -#include "EngineOpl.h" - -struct ProcessorVoice { - int channel; - int midi_note; - int velocity; - bool keydown; - bool sustained; - bool live; - - int mpePitchBend; - int mpePressure; - int mpeTimbre; - - Dx7Note *dx7_note; -}; - -enum DexedEngineResolution { - DEXED_ENGINE_MODERN, - DEXED_ENGINE_MARKI, - DEXED_ENGINE_OPL -}; - -//============================================================================== -/** -*/ -class DexedAudioProcessor : public AudioProcessor, public AsyncUpdater, public MidiInputCallback, public clap_juce_extensions::clap_properties -{ - static const int MAX_ACTIVE_NOTES = 16; - ProcessorVoice voices[MAX_ACTIVE_NOTES]; - int currentNote; - - // The original DX7 had one single LFO. Later units had an LFO per note. - Lfo lfo; - - bool sustain; - bool monoMode; - - // Extra buffering for when GetSamples wants a buffer not a multiple of N - float extra_buf[N]; - int extra_buf_size; - - int currentProgram; - - /** - * The last time the state was save, to be able to bypass a VST host bug. - */ - long lastStateSave; - - /** - * Plugin fx (the filter) - */ - PluginFx fx; - - /** - * This flag is used in the audio thread to know if the voice has changed - * and needs to be updated. - */ - bool refreshVoice; - bool normalizeDxVelocity; - bool sendSysexChange; - - void processMidiMessage(const MidiMessage *msg); - void keydown(uint8_t chan, uint8_t pitch, uint8_t velo); - void keyup(uint8_t, uint8_t pitch, uint8_t velo); - - /** - * this is called from the Audio thread to tell - * to update the UI / hostdata - */ - void handleAsyncUpdate(); - void initCtrl(); - - MidiMessage* nextMidi,*midiMsg; - bool hasMidiMessage; - int midiEventPos; - bool getNextEvent(MidiBuffer::Iterator* iter,const int samplePos); - - void handleIncomingMidiMessage(MidiInput* source, const MidiMessage& message); - uint32_t engineType; - - FmCore engineMsfa; - EngineMkI engineMkI; - EngineOpl engineOpl; - - char clipboard[161]; - char clipboardContent; - - void resolvAppDir(); - - void unpackOpSwitch(char packOpValue); - void packOpSwitch(); - -public : - // in MIDI units (0x4000 is neutral) - Controllers controllers; - StringArray programNames; - Cartridge currentCart; - uint8_t data[161]; - - SysexComm sysexComm; - VoiceStatus voiceStatus; - File activeFileCartridge; - - bool forceRefreshUI; - float vuSignal; - bool showKeyboard; - int getEngineType(); - void setEngineType(int rs); - - HashMap mappedMidiCC; - - Array ctrl; - - OperatorCtrl opCtrl[6]; - std::unique_ptr pitchEgRate[4]; - std::unique_ptr pitchEgLevel[4]; - std::unique_ptr pitchModSens; - std::unique_ptr algo; - std::unique_ptr oscSync; - std::unique_ptr feedback; - std::unique_ptr lfoRate; - std::unique_ptr lfoDelay; - std::unique_ptr lfoAmpDepth; - std::unique_ptr lfoPitchDepth; - std::unique_ptr lfoWaveform; - std::unique_ptr lfoSync; - std::unique_ptr transpose; - - std::unique_ptr fxCutoff; - std::unique_ptr fxReso; - std::unique_ptr output; - std::unique_ptr tune; - - void loadCartridge(Cartridge &cart); - void setDxValue(int offset, int v); - - //============================================================================== - DexedAudioProcessor(); - ~DexedAudioProcessor(); - - //============================================================================== - void prepareToPlay (double sampleRate, int samplesPerBlock); - void releaseResources(); - void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages); - void panic(); - bool isMonoMode() { - return monoMode; - } - void setMonoMode(bool mode); - - void copyToClipboard(int srcOp); - void pasteOpFromClipboard(int destOp); - void pasteEnvFromClipboard(int destOp); - void sendCurrentSysexProgram(); - void sendCurrentSysexCartridge(); - void sendSysexCartridge(File cart); - bool hasClipboardContent(); - - //============================================================================== - AudioProcessorEditor* createEditor(); - bool hasEditor() const; - void updateUI(); - bool peekVoiceStatus(); - int updateProgramFromSysex(const uint8 *rawdata); - void setupStartupCart(); - - //============================================================================== - const String getName() const; - int getNumParameters(); - float getParameter (int index); - void setParameter (int index, float newValue); - const String getParameterName (int index); - const String getParameterText (int index); - String getParameterID (int index) override; - - const String getInputChannelName (int channelIndex) const; - const String getOutputChannelName (int channelIndex) const; - bool isInputChannelStereoPair (int index) const; - bool isOutputChannelStereoPair (int index) const; - bool isBusesLayoutSupported (const BusesLayout& layouts) const; - - bool acceptsMidi() const; - bool producesMidi() const; - bool silenceInProducesSilenceOut() const; - double getTailLengthSeconds() const; - - //============================================================================== - int getNumPrograms(); - int getCurrentProgram(); - void setCurrentProgram(int index); - const String getProgramName (int index); - void changeProgramName(int index, const String& newName); - void resetToInitVoice(); - - //============================================================================== - void getStateInformation (MemoryBlock& destData); - void setStateInformation (const void* data, int sizeInBytes); - - // this is kept up to date with the midi messages that arrive, and the UI component - // registers with it so it can represent the incoming messages - MidiKeyboardState keyboardState; - void unbindUI(); - - void loadPreference(); - void savePreference(); - - static File dexedAppDir; - static File dexedCartDir; - - Value lastCCUsed; - - MTSClient *mtsClient; - std::shared_ptr synthTuningState; - // Prompt for a file - void applySCLTuning(); - void applyKBMMapping(); - - // Load a file - void applySCLTuning(File sclf); - void applyKBMMapping(File kbmf); - - // Load from text - void applySCLTuning(std::string scld); - void applyKBMMapping(std::string kbmd); - - void retuneToStandard(); - void resetTuning(std::shared_ptr t); - int tuningTranspositionShift(); - - std::string currentSCLData = ""; - std::string currentKBMData = ""; - - float dpiScaleFactor = -1; -private: - //============================================================================== - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DexedAudioProcessor) - -}; - -#endif // PLUGINPROCESSOR_H_INCLUDED +/** + * + * Copyright (c) 2013-2017 Pascal Gauthier. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef PLUGINPROCESSOR_H_INCLUDED +#define PLUGINPROCESSOR_H_INCLUDED + +#include "../JuceLibraryCode/JuceHeader.h" + +#include "clap-juce-extensions/clap-juce-extensions.h" + +#include "msfa/controllers.h" +#include "msfa/dx7note.h" +#include "msfa/lfo.h" +#include "msfa/synth.h" +#include "msfa/fm_core.h" +#include "msfa/tuning.h" +#include "PluginParam.h" +#include "PluginData.h" +#include "PluginFx.h" +#include "SysexComm.h" +#include "EngineMkI.h" +#include "EngineOpl.h" + +struct ProcessorVoice { + int channel; + int midi_note; + int velocity; + bool keydown; + bool sustained; + bool live; + + int mpePitchBend; + int mpePressure; + int mpeTimbre; + + Dx7Note *dx7_note; +}; + +enum DexedEngineResolution { + DEXED_ENGINE_MODERN, + DEXED_ENGINE_MARKI, + DEXED_ENGINE_OPL +}; + +//============================================================================== +/** +*/ +class DexedAudioProcessor : public AudioProcessor, public AsyncUpdater, public MidiInputCallback, public clap_juce_extensions::clap_properties +{ + static const int MAX_ACTIVE_NOTES = 16; + ProcessorVoice voices[MAX_ACTIVE_NOTES]; + int currentNote; + + // The original DX7 had one single LFO. Later units had an LFO per note. + Lfo lfo; + + bool sustain; + bool monoMode; + + // Extra buffering for when GetSamples wants a buffer not a multiple of N + float extra_buf[N]; + int extra_buf_size; + + int currentProgram; + + /** + * The last time the state was save, to be able to bypass a VST host bug. + */ + long lastStateSave; + + /** + * Plugin fx (the filter) + */ + PluginFx fx; + + /** + * This flag is used in the audio thread to know if the voice has changed + * and needs to be updated. + */ + bool refreshVoice; + bool normalizeDxVelocity; + bool sendSysexChange; + + void processMidiMessage(const MidiMessage *msg); + void keydown(uint8_t chan, uint8_t pitch, uint8_t velo); + void keyup(uint8_t, uint8_t pitch, uint8_t velo); + + /** + * this is called from the Audio thread to tell + * to update the UI / hostdata + */ + void handleAsyncUpdate(); + void initCtrl(); + + MidiMessage* nextMidi,*midiMsg; + bool hasMidiMessage; + int midiEventPos; + bool getNextEvent(MidiBuffer::Iterator* iter,const int samplePos); + + void handleIncomingMidiMessage(MidiInput* source, const MidiMessage& message); + uint32_t engineType; + + FmCore engineMsfa; + EngineMkI engineMkI; + EngineOpl engineOpl; + + char clipboard[161]; + char clipboardContent; + + void resolvAppDir(); + + void unpackOpSwitch(char packOpValue); + void packOpSwitch(); + + float dpiScaleFactor = -1; + +public : + // in MIDI units (0x4000 is neutral) + Controllers controllers; + StringArray programNames; + Cartridge currentCart; + uint8_t data[161]; + + SysexComm sysexComm; + VoiceStatus voiceStatus; + File activeFileCartridge; + + bool forceRefreshUI; + float vuSignal; + bool showKeyboard; + int getEngineType(); + void setEngineType(int rs); + + HashMap mappedMidiCC; + + Array ctrl; + + OperatorCtrl opCtrl[6]; + std::unique_ptr pitchEgRate[4]; + std::unique_ptr pitchEgLevel[4]; + std::unique_ptr pitchModSens; + std::unique_ptr algo; + std::unique_ptr oscSync; + std::unique_ptr feedback; + std::unique_ptr lfoRate; + std::unique_ptr lfoDelay; + std::unique_ptr lfoAmpDepth; + std::unique_ptr lfoPitchDepth; + std::unique_ptr lfoWaveform; + std::unique_ptr lfoSync; + std::unique_ptr transpose; + + std::unique_ptr fxCutoff; + std::unique_ptr fxReso; + std::unique_ptr output; + std::unique_ptr tune; + + void loadCartridge(Cartridge &cart); + void setDxValue(int offset, int v); + + //============================================================================== + DexedAudioProcessor(); + ~DexedAudioProcessor(); + + //============================================================================== + void prepareToPlay (double sampleRate, int samplesPerBlock); + void releaseResources(); + void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages); + void panic(); + bool isMonoMode() { + return monoMode; + } + void setMonoMode(bool mode); + + void copyToClipboard(int srcOp); + void pasteOpFromClipboard(int destOp); + void pasteEnvFromClipboard(int destOp); + void sendCurrentSysexProgram(); + void sendCurrentSysexCartridge(); + void sendSysexCartridge(File cart); + bool hasClipboardContent(); + + //============================================================================== + AudioProcessorEditor* createEditor(); + bool hasEditor() const; + void updateUI(); + bool peekVoiceStatus(); + int updateProgramFromSysex(const uint8 *rawdata); + void setupStartupCart(); + + //============================================================================== + const String getName() const; + int getNumParameters(); + float getParameter (int index); + void setParameter (int index, float newValue); + const String getParameterName (int index); + const String getParameterText (int index); + String getParameterID (int index) override; + + const String getInputChannelName (int channelIndex) const; + const String getOutputChannelName (int channelIndex) const; + bool isInputChannelStereoPair (int index) const; + bool isOutputChannelStereoPair (int index) const; + bool isBusesLayoutSupported (const BusesLayout& layouts) const; + + bool acceptsMidi() const; + bool producesMidi() const; + bool silenceInProducesSilenceOut() const; + double getTailLengthSeconds() const; + + //============================================================================== + int getNumPrograms(); + int getCurrentProgram(); + void setCurrentProgram(int index); + const String getProgramName (int index); + void changeProgramName(int index, const String& newName); + void resetToInitVoice(); + + //============================================================================== + void getStateInformation (MemoryBlock& destData); + void setStateInformation (const void* data, int sizeInBytes); + + // this is kept up to date with the midi messages that arrive, and the UI component + // registers with it so it can represent the incoming messages + MidiKeyboardState keyboardState; + void unbindUI(); + + void loadPreference(); + void savePreference(); + + static File dexedAppDir; + static File dexedCartDir; + + Value lastCCUsed; + + MTSClient *mtsClient; + std::shared_ptr synthTuningState; + // Prompt for a file + void applySCLTuning(); + void applyKBMMapping(); + + // Load a file + void applySCLTuning(File sclf); + void applyKBMMapping(File kbmf); + + // Load from text + void applySCLTuning(std::string scld); + void applyKBMMapping(std::string kbmd); + + void retuneToStandard(); + void resetTuning(std::shared_ptr t); + int tuningTranspositionShift(); + + std::string currentSCLData = ""; + std::string currentKBMData = ""; + void setDpiScaleFactor(float factor); + float getDpiScaleFactor() { + return dpiScaleFactor; + } +private: + //============================================================================== + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DexedAudioProcessor) + +}; + +#endif // PLUGINPROCESSOR_H_INCLUDED