diff --git a/.gitignore b/.gitignore
index 0f90feb2..0239b036 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,5 @@ JuceLibraryCode/
Builds/
assets/
products/
+
+.DS_Store
diff --git a/Dexed.jucer b/Dexed.jucer
index 790b744d..95cdea5f 100644
--- a/Dexed.jucer
+++ b/Dexed.jucer
@@ -79,6 +79,8 @@
+
+
@@ -140,7 +142,7 @@
+ vst3Folder="libs/vst3sdk" extraCompilerFlags="-Wno-macro-redefined -Wno-deprecated-declarations -Wno-shorten-64-to-32">
diff --git a/Source/GlobalEditor.cpp b/Source/GlobalEditor.cpp
index 9c132bd6..5964a653 100644
--- a/Source/GlobalEditor.cpp
+++ b/Source/GlobalEditor.cpp
@@ -644,8 +644,16 @@ void GlobalEditor::mouseDown(const MouseEvent &e) {
if ( e.mods.isPopupMenu()) {
PopupMenu popup;
popup.addItem(1, "Send current program to DX7");
- if ( popup.show() == 1 )
+
+ auto p = popup.show();
+ switch( p )
+ {
+ case 1:
processor->sendCurrentSysexProgram();
+ break;
+ default:
+ break;
+ }
}
}
//[/MiscUserCode]
diff --git a/Source/ParamDialog.cpp b/Source/ParamDialog.cpp
index 99ccb859..c8e926b6 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.2.0
+ Created with Projucer version: 5.4.5
------------------------------------------------------------------------------
- The Projucer is part of the JUCE library - "Jules' Utility Class Extensions"
- Copyright (c) 2015 - ROLI Ltd.
+ The Projucer is part of the JUCE library.
+ Copyright (c) 2017 - ROLI Ltd.
==============================================================================
*/
@@ -33,39 +33,55 @@ ParamDialog::ParamDialog ()
//[Constructor_pre] You can add your own custom stuff here..
//[/Constructor_pre]
- addAndMakeVisible (pitchRange = new Slider ("pitchRange"));
+ pitchRange.reset (new Slider ("pitchRange"));
+ addAndMakeVisible (pitchRange.get());
pitchRange->setRange (0, 12, 1);
pitchRange->setSliderStyle (Slider::RotaryVerticalDrag);
pitchRange->setTextBoxStyle (Slider::TextBoxLeft, false, 80, 20);
pitchRange->addListener (this);
- addAndMakeVisible (pitchStep = new Slider ("pitchStep"));
+ pitchRange->setBounds (264, 16, 72, 24);
+
+ pitchStep.reset (new Slider ("pitchStep"));
+ addAndMakeVisible (pitchStep.get());
pitchStep->setRange (0, 12, 1);
pitchStep->setSliderStyle (Slider::RotaryVerticalDrag);
pitchStep->setTextBoxStyle (Slider::TextBoxLeft, false, 80, 20);
pitchStep->addListener (this);
- addAndMakeVisible (sysexIn = new ComboBox ("sysexIn"));
+ pitchStep->setBounds (264, 56, 72, 24);
+
+ sysexIn.reset (new ComboBox ("sysexIn"));
+ addAndMakeVisible (sysexIn.get());
sysexIn->setEditableText (false);
sysexIn->setJustificationType (Justification::centredLeft);
sysexIn->setTextWhenNothingSelected (String());
sysexIn->setTextWhenNoChoicesAvailable (TRANS("(no choices)"));
sysexIn->addListener (this);
- addAndMakeVisible (sysexOut = new ComboBox ("sysexOut"));
+ sysexIn->setBounds (104, 224, 224, 24);
+
+ sysexOut.reset (new ComboBox ("sysexOut"));
+ addAndMakeVisible (sysexOut.get());
sysexOut->setEditableText (false);
sysexOut->setJustificationType (Justification::centredLeft);
sysexOut->setTextWhenNothingSelected (String());
sysexOut->setTextWhenNoChoicesAvailable (TRANS("(no choices)"));
sysexOut->addListener (this);
- addAndMakeVisible (sysexChl = new Slider ("sysexChl"));
+ sysexOut->setBounds (104, 264, 224, 24);
+
+ sysexChl.reset (new Slider ("sysexChl"));
+ addAndMakeVisible (sysexChl.get());
sysexChl->setRange (1, 16, 1);
sysexChl->setSliderStyle (Slider::RotaryVerticalDrag);
sysexChl->setTextBoxStyle (Slider::TextBoxLeft, false, 80, 20);
sysexChl->addListener (this);
- addAndMakeVisible (engineReso = new ComboBox ("new combo box"));
+ sysexChl->setBounds (264, 304, 72, 24);
+
+ engineReso.reset (new ComboBox ("new combo box"));
+ addAndMakeVisible (engineReso.get());
engineReso->setEditableText (false);
engineReso->setJustificationType (Justification::centredLeft);
engineReso->setTextWhenNothingSelected (String());
@@ -75,81 +91,170 @@ ParamDialog::ParamDialog ()
engineReso->addItem (TRANS("OPL Series"), 3);
engineReso->addListener (this);
- addAndMakeVisible (showKeyboard = new ToggleButton ("showKeyboard"));
+ engineReso->setBounds (160, 156, 168, 24);
+
+ showKeyboard.reset (new ToggleButton ("showKeyboard"));
+ addAndMakeVisible (showKeyboard.get());
showKeyboard->setButtonText (String());
+ showKeyboard->addListener (this);
- addAndMakeVisible (whlRange = new Slider ("whlRange"));
+ showKeyboard->setBounds (264, 96, 56, 24);
+
+ whlRange.reset (new Slider ("whlRange"));
+ addAndMakeVisible (whlRange.get());
whlRange->setRange (0, 99, 1);
whlRange->setSliderStyle (Slider::RotaryVerticalDrag);
whlRange->setTextBoxStyle (Slider::TextBoxLeft, false, 80, 20);
whlRange->addListener (this);
- addAndMakeVisible (ftRange = new Slider ("ftRange"));
+ whlRange->setBounds (448, 16, 72, 24);
+
+ ftRange.reset (new Slider ("ftRange"));
+ addAndMakeVisible (ftRange.get());
ftRange->setRange (0, 99, 1);
ftRange->setSliderStyle (Slider::RotaryVerticalDrag);
ftRange->setTextBoxStyle (Slider::TextBoxLeft, false, 80, 20);
ftRange->addListener (this);
- addAndMakeVisible (brRange = new Slider ("brRange"));
+ ftRange->setBounds (448, 56, 72, 24);
+
+ brRange.reset (new Slider ("brRange"));
+ addAndMakeVisible (brRange.get());
brRange->setRange (0, 99, 1);
brRange->setSliderStyle (Slider::RotaryVerticalDrag);
brRange->setTextBoxStyle (Slider::TextBoxLeft, false, 80, 20);
brRange->addListener (this);
- addAndMakeVisible (atRange = new Slider ("atRange"));
+ brRange->setBounds (448, 96, 72, 24);
+
+ atRange.reset (new Slider ("atRange"));
+ addAndMakeVisible (atRange.get());
atRange->setRange (0, 99, 1);
atRange->setSliderStyle (Slider::RotaryVerticalDrag);
atRange->setTextBoxStyle (Slider::TextBoxLeft, false, 80, 20);
atRange->addListener (this);
- addAndMakeVisible (whlEg = new ToggleButton ("whlEg"));
+ atRange->setBounds (448, 136, 72, 24);
+
+ whlEg.reset (new ToggleButton ("whlEg"));
+ addAndMakeVisible (whlEg.get());
whlEg->setButtonText (String());
whlEg->addListener (this);
- addAndMakeVisible (ftEg = new ToggleButton ("ftEg"));
+ whlEg->setBounds (640, 16, 56, 24);
+
+ ftEg.reset (new ToggleButton ("ftEg"));
+ addAndMakeVisible (ftEg.get());
ftEg->setButtonText (String());
ftEg->addListener (this);
- addAndMakeVisible (brEg = new ToggleButton ("brEg"));
+ ftEg->setBounds (640, 56, 56, 24);
+
+ brEg.reset (new ToggleButton ("brEg"));
+ addAndMakeVisible (brEg.get());
brEg->setButtonText (String());
brEg->addListener (this);
- addAndMakeVisible (atEg = new ToggleButton ("atEg"));
+ brEg->setBounds (640, 96, 56, 24);
+
+ atEg.reset (new ToggleButton ("atEg"));
+ addAndMakeVisible (atEg.get());
atEg->setButtonText (String());
atEg->addListener (this);
- addAndMakeVisible (whlAmp = new ToggleButton ("whlAmp"));
+ atEg->setBounds (640, 136, 56, 24);
+
+ whlAmp.reset (new ToggleButton ("whlAmp"));
+ addAndMakeVisible (whlAmp.get());
whlAmp->setButtonText (String());
whlAmp->addListener (this);
- addAndMakeVisible (ftAmp = new ToggleButton ("ftAmp"));
+ whlAmp->setBounds (584, 16, 56, 24);
+
+ ftAmp.reset (new ToggleButton ("ftAmp"));
+ addAndMakeVisible (ftAmp.get());
ftAmp->setButtonText (String());
ftAmp->addListener (this);
- addAndMakeVisible (brAmp = new ToggleButton ("brAmp"));
+ ftAmp->setBounds (584, 56, 56, 24);
+
+ brAmp.reset (new ToggleButton ("brAmp"));
+ addAndMakeVisible (brAmp.get());
brAmp->setButtonText (String());
brAmp->addListener (this);
- addAndMakeVisible (atAmp = new ToggleButton ("atAmp"));
+ brAmp->setBounds (584, 96, 56, 24);
+
+ atAmp.reset (new ToggleButton ("atAmp"));
+ addAndMakeVisible (atAmp.get());
atAmp->setButtonText (String());
atAmp->addListener (this);
- addAndMakeVisible (whlPitch = new ToggleButton ("whlPitch"));
+ atAmp->setBounds (584, 136, 56, 24);
+
+ whlPitch.reset (new ToggleButton ("whlPitch"));
+ addAndMakeVisible (whlPitch.get());
whlPitch->setButtonText (String());
whlPitch->addListener (this);
- addAndMakeVisible (ftPitch = new ToggleButton ("ftPitch"));
+ whlPitch->setBounds (528, 16, 56, 24);
+
+ ftPitch.reset (new ToggleButton ("ftPitch"));
+ addAndMakeVisible (ftPitch.get());
ftPitch->setButtonText (String());
ftPitch->addListener (this);
- addAndMakeVisible (brPitch = new ToggleButton ("brPitch"));
+ ftPitch->setBounds (528, 56, 56, 24);
+
+ brPitch.reset (new ToggleButton ("brPitch"));
+ addAndMakeVisible (brPitch.get());
brPitch->setButtonText (String());
brPitch->addListener (this);
- addAndMakeVisible (atPitch = new ToggleButton ("atPitch"));
+ brPitch->setBounds (528, 96, 56, 24);
+
+ atPitch.reset (new ToggleButton ("atPitch"));
+ addAndMakeVisible (atPitch.get());
atPitch->setButtonText (String());
atPitch->addListener (this);
+ atPitch->setBounds (528, 136, 56, 24);
+
+ sclButton.reset (new TextButton ("scl button"));
+ addAndMakeVisible (sclButton.get());
+ sclButton->setButtonText (TRANS("SCL"));
+ sclButton->addListener (this);
+
+ sclButton->setBounds (448, 208, 56, 24);
+
+ kbmButton.reset (new TextButton ("kbm button"));
+ addAndMakeVisible (kbmButton.get());
+ kbmButton->setButtonText (TRANS("KBM"));
+ kbmButton->addListener (this);
+
+ kbmButton->setBounds (512, 208, 56, 24);
+
+ showTunButton.reset (new TextButton ("show tuning button"));
+ addAndMakeVisible (showTunButton.get());
+ showTunButton->setButtonText (TRANS("Show"));
+ showTunButton->addListener (this);
+
+ showTunButton->setBounds (576, 208, 48, 24);
+
+ resetTuningButton.reset (new TextButton ("reset tuning button"));
+ addAndMakeVisible (resetTuningButton.get());
+ resetTuningButton->setButtonText (TRANS("Reset"));
+ resetTuningButton->addListener (this);
+
+ resetTuningButton->setBounds (632, 208, 48, 24);
+
+ transposeScale.reset (new ToggleButton ("transposeScale"));
+ addAndMakeVisible (transposeScale.get());
+ transposeScale->setButtonText (String());
+ transposeScale->addListener (this);
+
+ transposeScale->setBounds (576, 240, 56, 24);
+
//[UserPreSize]
//[/UserPreSize]
@@ -204,6 +309,11 @@ ParamDialog::~ParamDialog()
ftPitch = nullptr;
brPitch = nullptr;
atPitch = nullptr;
+ sclButton = nullptr;
+ kbmButton = nullptr;
+ showTunButton = nullptr;
+ resetTuningButton = nullptr;
+ transposeScale = nullptr;
//[Destructor]. You can add your own custom destruction code here..
@@ -319,7 +429,7 @@ void ParamDialog::paint (Graphics& g)
{
int x = 368, y = 96, width = 276, height = 23;
- String text (TRANS("Foot"));
+ String text (TRANS("Breath"));
Colour fillColour = Colours::white;
//[UserPaintCustomArguments] Customize the painting arguments here..
//[/UserPaintCustomArguments]
@@ -331,7 +441,7 @@ void ParamDialog::paint (Graphics& g)
{
int x = 368, y = 56, width = 276, height = 23;
- String text (TRANS("Breath"));
+ String text (TRANS("Foot"));
Colour fillColour = Colours::white;
//[UserPaintCustomArguments] Customize the painting arguments here..
//[/UserPaintCustomArguments]
@@ -389,6 +499,51 @@ void ParamDialog::paint (Graphics& g)
Justification::centredLeft, true);
}
+ {
+ int x = 371, y = 194, width = 325, height = 1;
+ Colour fillColour = Colours::black;
+ //[UserPaintCustomArguments] Customize the painting arguments here..
+ //[/UserPaintCustomArguments]
+ g.setColour (fillColour);
+ g.fillRect (x, y, width, height);
+ }
+
+ {
+ int x = 371, y = 210, width = 276, height = 25;
+ String text (TRANS("Tuning"));
+ Colour fillColour = Colours::white;
+ //[UserPaintCustomArguments] Customize the painting arguments here..
+ //[/UserPaintCustomArguments]
+ g.setColour (fillColour);
+ g.setFont (Font (15.00f, Font::plain).withTypefaceStyle ("Regular"));
+ g.drawText (text, x, y, width, height,
+ Justification::centredLeft, true);
+ }
+
+ {
+ int x = 459, y = 242, width = 125, height = 25;
+ String text (TRANS("Transp 12 as Scale"));
+ Colour fillColour = Colours::white;
+ //[UserPaintCustomArguments] Customize the painting arguments here..
+ //[/UserPaintCustomArguments]
+ g.setColour (fillColour);
+ g.setFont (Font (15.00f, Font::plain).withTypefaceStyle ("Regular"));
+ g.drawText (text, x, y, width, height,
+ Justification::centredLeft, true);
+ }
+
+ {
+ int x = 643, y = 242, width = 45, height = 25;
+ String text (TRANS("Value"));
+ Colour fillColour = Colours::white;
+ //[UserPaintCustomArguments] Customize the painting arguments here..
+ //[/UserPaintCustomArguments]
+ g.setColour (fillColour);
+ g.setFont (Font (15.00f, Font::plain).withTypefaceStyle ("Regular"));
+ g.drawText (text, x, y, width, height,
+ Justification::centredLeft, true);
+ }
+
//[UserPaint] Add your own custom painting code here..
if ( ! JUCEApplication::isStandaloneApp() ) {
g.setColour (Colours::white);
@@ -411,29 +566,6 @@ void ParamDialog::resized()
//[UserPreResize] Add your own custom resize code here..
//[/UserPreResize]
- pitchRange->setBounds (264, 16, 72, 24);
- pitchStep->setBounds (264, 56, 72, 24);
- sysexIn->setBounds (104, 224, 224, 24);
- sysexOut->setBounds (104, 264, 224, 24);
- sysexChl->setBounds (264, 304, 72, 24);
- engineReso->setBounds (160, 156, 168, 24);
- showKeyboard->setBounds (264, 96, 56, 24);
- whlRange->setBounds (448, 16, 72, 24);
- ftRange->setBounds (448, 56, 72, 24);
- brRange->setBounds (448, 96, 72, 24);
- atRange->setBounds (448, 136, 72, 24);
- whlEg->setBounds (640, 16, 56, 24);
- ftEg->setBounds (640, 56, 56, 24);
- brEg->setBounds (640, 96, 56, 24);
- atEg->setBounds (640, 136, 56, 24);
- whlAmp->setBounds (584, 16, 56, 24);
- ftAmp->setBounds (584, 56, 56, 24);
- brAmp->setBounds (584, 96, 56, 24);
- atAmp->setBounds (584, 136, 56, 24);
- whlPitch->setBounds (528, 16, 56, 24);
- ftPitch->setBounds (528, 56, 56, 24);
- brPitch->setBounds (528, 96, 56, 24);
- atPitch->setBounds (528, 136, 56, 24);
//[UserResized] Add your own custom resize handling here..
//[/UserResized]
}
@@ -441,141 +573,190 @@ void ParamDialog::resized()
void ParamDialog::sliderValueChanged (Slider* sliderThatWasMoved)
{
//[UsersliderValueChanged_Pre]
+ bool handled = false;
//[/UsersliderValueChanged_Pre]
- if (sliderThatWasMoved == pitchRange)
+ if (sliderThatWasMoved == pitchRange.get())
{
//[UserSliderCode_pitchRange] -- add your slider handling code here..
//[/UserSliderCode_pitchRange]
}
- else if (sliderThatWasMoved == pitchStep)
+ else if (sliderThatWasMoved == pitchStep.get())
{
//[UserSliderCode_pitchStep] -- add your slider handling code here..
pitchRange->setEnabled(pitchStep->getValue() == 0);
//[/UserSliderCode_pitchStep]
}
- else if (sliderThatWasMoved == sysexChl)
+ else if (sliderThatWasMoved == sysexChl.get())
{
//[UserSliderCode_sysexChl] -- add your slider handling code here..
//[/UserSliderCode_sysexChl]
}
- else if (sliderThatWasMoved == whlRange)
+ else if (sliderThatWasMoved == whlRange.get())
{
//[UserSliderCode_whlRange] -- add your slider handling code here..
//[/UserSliderCode_whlRange]
}
- else if (sliderThatWasMoved == ftRange)
+ else if (sliderThatWasMoved == ftRange.get())
{
//[UserSliderCode_ftRange] -- add your slider handling code here..
//[/UserSliderCode_ftRange]
}
- else if (sliderThatWasMoved == brRange)
+ else if (sliderThatWasMoved == brRange.get())
{
//[UserSliderCode_brRange] -- add your slider handling code here..
//[/UserSliderCode_brRange]
}
- else if (sliderThatWasMoved == atRange)
+ else if (sliderThatWasMoved == atRange.get())
{
//[UserSliderCode_atRange] -- add your slider handling code here..
//[/UserSliderCode_atRange]
}
//[UsersliderValueChanged_Post]
+ if( ! handled )
+ general_callback_(this);
//[/UsersliderValueChanged_Post]
}
void ParamDialog::comboBoxChanged (ComboBox* comboBoxThatHasChanged)
{
//[UsercomboBoxChanged_Pre]
+ bool handled = false;
//[/UsercomboBoxChanged_Pre]
- if (comboBoxThatHasChanged == sysexIn)
+ if (comboBoxThatHasChanged == sysexIn.get())
{
//[UserComboBoxCode_sysexIn] -- add your combo box handling code here..
//[/UserComboBoxCode_sysexIn]
}
- else if (comboBoxThatHasChanged == sysexOut)
+ else if (comboBoxThatHasChanged == sysexOut.get())
{
//[UserComboBoxCode_sysexOut] -- add your combo box handling code here..
//[/UserComboBoxCode_sysexOut]
}
- else if (comboBoxThatHasChanged == engineReso)
+ else if (comboBoxThatHasChanged == engineReso.get())
{
//[UserComboBoxCode_engineReso] -- add your combo box handling code here..
//[/UserComboBoxCode_engineReso]
}
//[UsercomboBoxChanged_Post]
+ if( ! handled )
+ general_callback_(this);
//[/UsercomboBoxChanged_Post]
}
void ParamDialog::buttonClicked (Button* buttonThatWasClicked)
{
//[UserbuttonClicked_Pre]
+ bool handled = false;
//[/UserbuttonClicked_Pre]
- if (buttonThatWasClicked == whlEg)
+ if (buttonThatWasClicked == showKeyboard.get())
+ {
+ //[UserButtonCode_showKeyboard] -- add your button handler code here..
+ //[/UserButtonCode_showKeyboard]
+ }
+ else if (buttonThatWasClicked == whlEg.get())
{
//[UserButtonCode_whlEg] -- add your button handler code here..
//[/UserButtonCode_whlEg]
}
- else if (buttonThatWasClicked == ftEg)
+ else if (buttonThatWasClicked == ftEg.get())
{
//[UserButtonCode_ftEg] -- add your button handler code here..
//[/UserButtonCode_ftEg]
}
- else if (buttonThatWasClicked == brEg)
+ else if (buttonThatWasClicked == brEg.get())
{
//[UserButtonCode_brEg] -- add your button handler code here..
//[/UserButtonCode_brEg]
}
- else if (buttonThatWasClicked == atEg)
+ else if (buttonThatWasClicked == atEg.get())
{
//[UserButtonCode_atEg] -- add your button handler code here..
//[/UserButtonCode_atEg]
}
- else if (buttonThatWasClicked == whlAmp)
+ else if (buttonThatWasClicked == whlAmp.get())
{
//[UserButtonCode_whlAmp] -- add your button handler code here..
//[/UserButtonCode_whlAmp]
}
- else if (buttonThatWasClicked == ftAmp)
+ else if (buttonThatWasClicked == ftAmp.get())
{
//[UserButtonCode_ftAmp] -- add your button handler code here..
//[/UserButtonCode_ftAmp]
}
- else if (buttonThatWasClicked == brAmp)
+ else if (buttonThatWasClicked == brAmp.get())
{
//[UserButtonCode_brAmp] -- add your button handler code here..
//[/UserButtonCode_brAmp]
}
- else if (buttonThatWasClicked == atAmp)
+ else if (buttonThatWasClicked == atAmp.get())
{
//[UserButtonCode_atAmp] -- add your button handler code here..
//[/UserButtonCode_atAmp]
}
- else if (buttonThatWasClicked == whlPitch)
+ else if (buttonThatWasClicked == whlPitch.get())
{
//[UserButtonCode_whlPitch] -- add your button handler code here..
//[/UserButtonCode_whlPitch]
}
- else if (buttonThatWasClicked == ftPitch)
+ else if (buttonThatWasClicked == ftPitch.get())
{
//[UserButtonCode_ftPitch] -- add your button handler code here..
//[/UserButtonCode_ftPitch]
}
- else if (buttonThatWasClicked == brPitch)
+ else if (buttonThatWasClicked == brPitch.get())
{
//[UserButtonCode_brPitch] -- add your button handler code here..
//[/UserButtonCode_brPitch]
}
- else if (buttonThatWasClicked == atPitch)
+ else if (buttonThatWasClicked == atPitch.get())
{
//[UserButtonCode_atPitch] -- add your button handler code here..
//[/UserButtonCode_atPitch]
}
+ else if (buttonThatWasClicked == sclButton.get())
+ {
+ //[UserButtonCode_sclButton] -- add your button handler code here..
+ tuning_callback_(this, TuningAction::LOAD_SCL);
+ handled = true;
+ //[/UserButtonCode_sclButton]
+ }
+ else if (buttonThatWasClicked == kbmButton.get())
+ {
+ //[UserButtonCode_kbmButton] -- add your button handler code here..
+ tuning_callback_(this, TuningAction::LOAD_KBM);
+ handled = true;
+ //[/UserButtonCode_kbmButton]
+ }
+ else if (buttonThatWasClicked == showTunButton.get())
+ {
+ //[UserButtonCode_showTunButton] -- add your button handler code here..
+ tuning_callback_(this, TuningAction::SHOW_TUNING);
+ handled = true;
+ //[/UserButtonCode_showTunButton]
+ }
+ else if (buttonThatWasClicked == resetTuningButton.get())
+ {
+ //[UserButtonCode_resetTuningButton] -- add your button handler code here..
+ tuning_callback_(this, TuningAction::RESET_TUNING);
+ handled = true;
+ //[/UserButtonCode_resetTuningButton]
+ }
+ else if (buttonThatWasClicked == transposeScale.get())
+ {
+ //[UserButtonCode_transposeScale] -- add your button handler code here..
+ //[/UserButtonCode_transposeScale]
+ }
//[UserbuttonClicked_Post]
+ if( ! handled )
+ {
+ general_callback_(this);
+ }
//[/UserbuttonClicked_Post]
}
@@ -609,6 +790,8 @@ void ParamDialog::setDialogValues(Controllers &c, SysexComm &mgr, int reso, bool
atAmp->setToggleState(c.at.amp, dontSendNotification);
atEg->setToggleState(c.at.eg, dontSendNotification);
+ transposeScale->setToggleState(c.transpose12AsScale ? 0 : 1, dontSendNotification );
+
StringArray inputs = MidiInput::getDevices();
int idx = inputs.indexOf(mgr.getInput());
idx = idx == -1 ? 0 : idx + 1;
@@ -649,6 +832,8 @@ bool ParamDialog::getDialogValues(Controllers &c, SysexComm &mgr, int *reso, boo
c.at.amp = atAmp->getToggleState();
c.at.eg = atEg->getToggleState();
+ c.transpose12AsScale = ! transposeScale->getToggleState();
+
c.refresh();
if ( ! JUCEApplication::isStandaloneApp() ) {
@@ -662,6 +847,15 @@ bool ParamDialog::getDialogValues(Controllers &c, SysexComm &mgr, int *reso, boo
return ret;
}
+void ParamDialog::setIsStandardTuning( bool b )
+{
+ is_standard_tuning_ = b;
+ if( showTunButton != nullptr )
+ showTunButton->setEnabled( ! b );
+ if( resetTuningButton != nullptr )
+ resetTuningButton->setEnabled( ! b );
+}
+
//[/MiscUserCode]
@@ -680,54 +874,64 @@ BEGIN_JUCER_METADATA
fixedSize="1" initialWidth="710" initialHeight="350">
+ fontname="Default font" fontsize="15.0" kerning="0.0" bold="0"
+ italic="0" justification="33"/>
+ fontname="Default font" fontsize="15.0" kerning="0.0" bold="0"
+ italic="0" justification="33"/>
+ fontname="Default font" fontsize="15.0" kerning="0.0" bold="0"
+ italic="0" justification="33"/>
+ fontname="Default font" fontsize="15.0" kerning="0.0" bold="0"
+ italic="0" justification="33"/>
+ fontname="Default font" fontsize="15.0" kerning="0.0" bold="0"
+ italic="0" justification="33"/>
-
-
+ fontname="Default font" fontsize="15.0" kerning="0.0" bold="0"
+ italic="0" justification="33"/>
+
+
+ fontname="Default font" fontsize="15.0" kerning="0.0" bold="0"
+ italic="0" justification="33"/>
+ fontname="Default font" fontsize="15.0" kerning="0.0" bold="0"
+ italic="0" justification="33"/>
+ fontname="Default font" fontsize="15.0" kerning="0.0" bold="0"
+ italic="0" justification="33"/>
+ fontname="Default font" fontsize="15.0" kerning="0.0" bold="0"
+ italic="0" justification="33"/>
+
+
+
+
+ explicitFocusOrder="0" pos="264 304 72 24" min="1.0" max="16.0"
+ int="1.0" style="RotaryVerticalDrag" textBoxPos="TextBoxLeft"
+ textBoxEditable="1" textBoxWidth="80" textBoxHeight="20" skewFactor="1.0"
+ needsCallback="1"/>
+ connectedEdges="0" needsCallback="1" radioGroupId="0" state="0"/>
+ explicitFocusOrder="0" pos="448 16 72 24" min="0.0" max="99.0"
+ int="1.0" style="RotaryVerticalDrag" textBoxPos="TextBoxLeft"
+ textBoxEditable="1" textBoxWidth="80" textBoxHeight="20" skewFactor="1.0"
+ needsCallback="1"/>
+ explicitFocusOrder="0" pos="448 56 72 24" min="0.0" max="99.0"
+ int="1.0" style="RotaryVerticalDrag" textBoxPos="TextBoxLeft"
+ textBoxEditable="1" textBoxWidth="80" textBoxHeight="20" skewFactor="1.0"
+ needsCallback="1"/>
+ explicitFocusOrder="0" pos="448 96 72 24" min="0.0" max="99.0"
+ int="1.0" style="RotaryVerticalDrag" textBoxPos="TextBoxLeft"
+ textBoxEditable="1" textBoxWidth="80" textBoxHeight="20" skewFactor="1.0"
+ needsCallback="1"/>
+ explicitFocusOrder="0" pos="448 136 72 24" min="0.0" max="99.0"
+ int="1.0" style="RotaryVerticalDrag" textBoxPos="TextBoxLeft"
+ textBoxEditable="1" textBoxWidth="80" textBoxHeight="20" skewFactor="1.0"
+ needsCallback="1"/>
@@ -798,6 +1007,21 @@ BEGIN_JUCER_METADATA
+
+
+
+
+
END_JUCER_METADATA
@@ -807,3 +1031,4 @@ END_JUCER_METADATA
//[EndFile] You can add extra defines here...
//[/EndFile]
+
diff --git a/Source/ParamDialog.h b/Source/ParamDialog.h
index 5732c7b1..cc02cc5d 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.2.0
+ Created with Projucer version: 5.4.5
------------------------------------------------------------------------------
- The Projucer is part of the JUCE library - "Jules' Utility Class Extensions"
- Copyright (c) 2015 - ROLI Ltd.
+ The Projucer is part of the JUCE library.
+ Copyright (c) 2017 - ROLI Ltd.
==============================================================================
*/
@@ -23,6 +23,7 @@
#include "JuceHeader.h"
#include "msfa/controllers.h"
#include "SysexComm.h"
+#include
//[/Headers]
@@ -49,6 +50,18 @@ class ParamDialog : public Component,
//[UserMethods] -- You can add your own custom methods in this section.
void setDialogValues(Controllers &c, SysexComm &mgr, int reso, bool showKeyboard);
bool getDialogValues(Controllers &c, SysexComm &mgr, int *reso, bool *showKeyboard);
+
+ typedef enum {
+ LOAD_SCL,
+ LOAD_KBM,
+ RESET_TUNING,
+ SHOW_TUNING
+ } TuningAction;
+
+ void setTuningCallback(std::function tc ) { tuning_callback_ = tc; }
+ void setGeneralCallback(std::function gc ) { general_callback_ = gc; }
+
+ void setIsStandardTuning(bool s);
//[/UserMethods]
void paint (Graphics& g) override;
@@ -61,32 +74,40 @@ class ParamDialog : public Component,
private:
//[UserVariables] -- You can add your own custom variables in this section.
+ std::function tuning_callback_ = [](ParamDialog *, ParamDialog::TuningAction i) {};
+ bool is_standard_tuning_;
+ std::function general_callback_ = [](ParamDialog *p) {};
//[/UserVariables]
//==============================================================================
- ScopedPointer pitchRange;
- ScopedPointer pitchStep;
- ScopedPointer sysexIn;
- ScopedPointer sysexOut;
- ScopedPointer sysexChl;
- ScopedPointer engineReso;
- ScopedPointer showKeyboard;
- ScopedPointer whlRange;
- ScopedPointer ftRange;
- ScopedPointer brRange;
- ScopedPointer atRange;
- ScopedPointer whlEg;
- ScopedPointer ftEg;
- ScopedPointer brEg;
- ScopedPointer atEg;
- ScopedPointer whlAmp;
- ScopedPointer ftAmp;
- ScopedPointer brAmp;
- ScopedPointer atAmp;
- ScopedPointer whlPitch;
- ScopedPointer ftPitch;
- ScopedPointer brPitch;
- ScopedPointer atPitch;
+ std::unique_ptr pitchRange;
+ 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 whlEg;
+ std::unique_ptr ftEg;
+ std::unique_ptr brEg;
+ std::unique_ptr atEg;
+ std::unique_ptr whlAmp;
+ std::unique_ptr ftAmp;
+ std::unique_ptr brAmp;
+ std::unique_ptr atAmp;
+ std::unique_ptr whlPitch;
+ 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 transposeScale;
//==============================================================================
@@ -95,3 +116,4 @@ class ParamDialog : public Component,
//[EndFile] You can add extra defines here...
//[/EndFile]
+
diff --git a/Source/PluginData.cpp b/Source/PluginData.cpp
index 9cc9e168..79c86cca 100644
--- a/Source/PluginData.cpp
+++ b/Source/PluginData.cpp
@@ -320,6 +320,7 @@ void DexedAudioProcessor::getStateInformation(MemoryBlock& destData) {
dexedState.setAttribute("masterTune", controllers.masterTune);
//TRACE("saving opswitch %s", controllers.opSwitch);
dexedState.setAttribute("opSwitch", controllers.opSwitch);
+ dexedState.setAttribute("transpose12AsScale", controllers.transpose12AsScale ? 1 : 0 );
char mod_cfg[15];
controllers.wheel.setConfig(mod_cfg);
@@ -330,6 +331,15 @@ void DexedAudioProcessor::getStateInformation(MemoryBlock& destData) {
dexedState.setAttribute("breathMod", mod_cfg);
controllers.at.setConfig(mod_cfg);
dexedState.setAttribute("aftertouchMod", mod_cfg);
+
+ if( currentSCLData.size() > 1 || currentKBMData.size() > 1 )
+ {
+ auto tuningx = dexedState.createNewChildElement("dexedTuning" );
+ auto sclx = tuningx->createNewChildElement("scl");
+ sclx->addTextElement(currentSCLData);
+ auto kbmx = tuningx->createNewChildElement("kbm");
+ kbmx->addTextElement(currentKBMData);
+ }
if ( activeFileCartridge.exists() )
dexedState.setAttribute("activeFileCartridge", activeFileCartridge.getFullPathName());
@@ -358,7 +368,7 @@ void DexedAudioProcessor::setStateInformation(const void* source, int sizeInByte
// used to LOAD plugin state
std::unique_ptr root(getXmlFromBinary(source, sizeInBytes));
-
+
if (root == nullptr) {
TRACE("unknown state format");
return;
@@ -387,10 +397,33 @@ void DexedAudioProcessor::setStateInformation(const void* source, int sizeInByte
setEngineType(root->getIntAttribute("engineType", 1));
monoMode = root->getIntAttribute("monoMode", 0);
controllers.masterTune = root->getIntAttribute("masterTune", 0);
+ controllers.transpose12AsScale = ( root->getIntAttribute("transpose12AsScale", 1) != 0 );
File possibleCartridge = File(root->getStringAttribute("activeFileCartridge"));
if ( possibleCartridge.exists() )
activeFileCartridge = possibleCartridge;
+
+ auto tuningParent = root->getChildByName( "dexedTuning" );
+ if( tuningParent )
+ {
+ auto sclx = tuningParent->getChildByName( "scl" );
+ auto kbmx = tuningParent->getChildByName( "kbm" );
+ std::string s = "";
+ if( sclx && sclx->getFirstChildElement() && sclx->getFirstChildElement()->isTextElement() )
+ {
+ s = sclx->getFirstChildElement()->getText().toStdString();
+ if( s.size() > 1 )
+ applySCLTuning(s);
+ }
+
+ std::string k = "";
+ if( kbmx && kbmx->getFirstChildElement() && kbmx->getFirstChildElement()->isTextElement() )
+ {
+ k = kbmx->getFirstChildElement()->getText().toStdString();
+ if( k.size() > 1 )
+ applyKBMMapping(k);
+ }
+ }
XmlElement *dexedBlob = root->getChildByName("dexedBlob");
if ( dexedBlob == NULL ) {
diff --git a/Source/PluginEditor.cpp b/Source/PluginEditor.cpp
index a5ff2e08..8968b487 100644
--- a/Source/PluginEditor.cpp
+++ b/Source/PluginEditor.cpp
@@ -153,30 +153,78 @@ void DexedAudioProcessorEditor::saveCart() {
}
}
+void DexedAudioProcessorEditor::tuningShow() {
+ auto te = new TextEditor();
+ te->setMultiLine(true);
+ te->setReadOnly(true);
+
+ te->setText( processor->synthTuningState->display_tuning_str().c_str() );
+ te->setSize( 500, 700 );
+
+ DialogWindow::LaunchOptions options;
+ options.content.setOwned(te);
+ options.dialogTitle = "Current Tuning";
+ options.dialogBackgroundColour = Colour(0x32FFFFFF);
+ options.escapeKeyTriggersCloseButton = true;
+ options.useNativeTitleBar = false;
+ options.resizable = false;
+
+ auto dialogwindow = options.launchAsync();
+}
+
void DexedAudioProcessorEditor::parmShow() {
int tp = processor->getEngineType();
- AlertWindow window("","", AlertWindow::NoIcon, this);
- ParamDialog param;
- param.setColour(AlertWindow::backgroundColourId, Colour(0x32FFFFFF));
- param.setDialogValues(processor->controllers, processor->sysexComm, tp, processor->showKeyboard);
-
- window.addCustomComponent(¶m);
- window.addButton("OK", 0);
- window.addButton("Cancel" ,1);
- if ( window.runModalLoop() != 0 )
- return;
-
- bool ret = param.getDialogValues(processor->controllers, processor->sysexComm, &tp, &processor->showKeyboard);
- processor->setEngineType(tp);
- processor->savePreference();
+ DialogWindow::LaunchOptions options;
- setSize(866, processor->showKeyboard ? 674 : 581);
- midiKeyboard.repaint();
-
- if ( ret == false ) {
- AlertWindow::showMessageBoxAsync(AlertWindow::WarningIcon, "Midi Interface", "Error opening midi ports");
- }
+ auto param = new ParamDialog();
+ param->setColour(AlertWindow::backgroundColourId, Colour(0x32FFFFFF));
+ param->setDialogValues(processor->controllers, processor->sysexComm, tp, processor->showKeyboard);
+ 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(0x32FFFFFF);
+ 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->setEngineType(tpo);
+ this->processor->savePreference();
+
+ this->setSize(866, this->processor->showKeyboard ? 674 : 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() {
@@ -369,3 +417,28 @@ void DexedAudioProcessorEditor::discoverMidiCC(Ctrl *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/PluginEditor.h b/Source/PluginEditor.h
index 6d242a35..7f1aad44 100644
--- a/Source/PluginEditor.h
+++ b/Source/PluginEditor.h
@@ -31,7 +31,8 @@
//==============================================================================
/**
*/
-class DexedAudioProcessorEditor : public AudioProcessorEditor, public ComboBox::Listener, public Timer {
+class DexedAudioProcessorEditor : public AudioProcessorEditor, public ComboBox::Listener, public Timer,
+ public FileDragAndDropTarget {
MidiKeyboardComponent midiKeyboard;
OperatorEditor operators[6];
Colour background;
@@ -44,10 +45,10 @@ class DexedAudioProcessorEditor : public AudioProcessorEditor, public ComboBox:
DexedAudioProcessorEditor (DexedAudioProcessor* ownerFilter);
~DexedAudioProcessorEditor();
- void timerCallback();
+ virtual void timerCallback() override;
- void paint (Graphics& g);
- void comboBoxChanged (ComboBox* comboBoxThatHasChanged);
+ virtual void paint (Graphics& g) override;
+ virtual void comboBoxChanged (ComboBox* comboBoxThatHasChanged) override;
void updateUI();
void rebuildProgramCombobox();
void loadCart(File file);
@@ -56,7 +57,11 @@ class DexedAudioProcessorEditor : public AudioProcessorEditor, public ComboBox:
void storeProgram();
void cartShow();
void parmShow();
+ void tuningShow();
void discoverMidiCC(Ctrl *ctrl);
+
+ virtual bool isInterestedInFileDrag (const StringArray &files) override;
+ virtual void filesDropped (const StringArray &files, int x, int y ) override;
};
diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp
index db24218e..0852e6af 100644
--- a/Source/PluginProcessor.cpp
+++ b/Source/PluginProcessor.cpp
@@ -80,6 +80,8 @@ DexedAudioProcessor::DexedAudioProcessor() {
Tanh::init();
Sin::init();
+ synthTuningState = createStandardTuning();
+
lastStateSave = 0;
currentNote = -1;
engineType = -1;
@@ -132,7 +134,7 @@ void DexedAudioProcessor::prepareToPlay(double sampleRate, int samplesPerBlock)
fx.init(sampleRate);
for (int note = 0; note < MAX_ACTIVE_NOTES; ++note) {
- voices[note].dx7_note = new Dx7Note;
+ voices[note].dx7_note = new Dx7Note(synthTuningState);
voices[note].keydown = false;
voices[note].sustained = false;
voices[note].live = false;
@@ -395,7 +397,7 @@ void DexedAudioProcessor::keydown(uint8_t pitch, uint8_t velo) {
return;
}
- pitch += data[144] - 24;
+ pitch += tuningTranspositionShift();
if ( normalizeDxVelocity ) {
velo = ((float)velo) * 0.7874015; // 100/127
@@ -442,7 +444,7 @@ void DexedAudioProcessor::keydown(uint8_t pitch, uint8_t velo) {
}
void DexedAudioProcessor::keyup(uint8_t pitch) {
- pitch += data[144] - 24;
+ pitch += tuningTranspositionShift();
int note;
for (note=0; noteis_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;i 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 0246a059..8c221553 100644
--- a/Source/PluginProcessor.h
+++ b/Source/PluginProcessor.h
@@ -28,6 +28,7 @@
#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"
@@ -236,6 +237,27 @@ public :
static File dexedCartDir;
Value lastCCUsed;
+
+ 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 = "";
+
private:
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DexedAudioProcessor)
diff --git a/Source/msfa/controllers.h b/Source/msfa/controllers.h
index 4a04931f..62a8482f 100644
--- a/Source/msfa/controllers.h
+++ b/Source/msfa/controllers.h
@@ -90,6 +90,8 @@ class Controllers {
int modwheel_cc;
int masterTune;
+
+ bool transpose12AsScale = true;
FmMod wheel;
FmMod foot;
diff --git a/Source/msfa/dx7note.cc b/Source/msfa/dx7note.cc
index e2a99867..43ad33ff 100644
--- a/Source/msfa/dx7note.cc
+++ b/Source/msfa/dx7note.cc
@@ -22,15 +22,10 @@
#include "exp2.h"
#include "controllers.h"
#include "dx7note.h"
+#include
const int FEEDBACK_BITDEPTH = 8;
-int32_t midinote_to_logfreq(int midinote) {
- const int base = 50857777; // (1 << 24) * (log(440) / log(2) - 69/12)
- const int step = (1 << 24) / 12;
- return base + step * midinote;
-}
-
const int32_t coarsemul[] = {
-16777216, 0, 16777216, 26591258, 33554432, 38955489, 43368474, 47099600,
50331648, 53182516, 55732705, 58039632, 60145690, 62083076, 63876816,
@@ -39,11 +34,11 @@ const int32_t coarsemul[] = {
81503396, 82323963, 83117622
};
-int32_t osc_freq(int midinote, int mode, int coarse, int fine, int detune) {
+int32_t Dx7Note::osc_freq(int midinote, int mode, int coarse, int fine, int detune) {
// TODO: pitch randomization
int32_t logfreq;
if (mode == 0) {
- logfreq = midinote_to_logfreq(midinote);
+ logfreq = tuning_state_->midinote_to_logfreq(midinote);
// could use more precision, closer enough for now. those numbers comes from my DX7
double detuneRatio = 0.0209 * exp(-0.396 * (((float)logfreq)/(1<<24))) / 7;
@@ -137,7 +132,7 @@ static const uint32_t ampmodsenstab[] = {
0, 4342338, 7171437, 16777216
};
-Dx7Note::Dx7Note() {
+Dx7Note::Dx7Note(std::shared_ptr ts) : tuning_state_(ts) {
for(int op=0;op<6;op++) {
params_[op].phase = 0;
params_[op].gain_out = 0;
diff --git a/Source/msfa/dx7note.h b/Source/msfa/dx7note.h
index 07d80f11..7e030eab 100644
--- a/Source/msfa/dx7note.h
+++ b/Source/msfa/dx7note.h
@@ -27,6 +27,8 @@
#include "env.h"
#include "pitchenv.h"
#include "fm_core.h"
+#include "tuning.h"
+#include
struct VoiceStatus {
uint32_t amp[6];
@@ -36,7 +38,7 @@ struct VoiceStatus {
class Dx7Note {
public:
- Dx7Note();
+ Dx7Note(std::shared_ptr ts);
void init(const uint8_t patch[156], int midinote, int velocity);
// Note: this _adds_ to the buffer. Interesting question whether it's
@@ -57,7 +59,11 @@ class Dx7Note {
void transferState(Dx7Note& src);
void transferSignal(Dx7Note &src);
void oscSync();
-
+
+ int32_t osc_freq(int midinote, int mode, int coarse, int fine, int detune);
+
+ std::shared_ptr tuning_state_;
+
private:
Env env_[6];
FmOpParams params_[6];
@@ -67,11 +73,12 @@ class Dx7Note {
int32_t fb_shift_;
int32_t ampmodsens_[6];
int32_t opMode[6];
-
+
int ampmoddepth_;
int algorithm_;
int pitchmoddepth_;
int pitchmodsens_;
+
};
#endif // SYNTH_DX7NOTE_H_
diff --git a/Source/msfa/tuning.cc b/Source/msfa/tuning.cc
new file mode 100644
index 00000000..3454ebc6
--- /dev/null
+++ b/Source/msfa/tuning.cc
@@ -0,0 +1,562 @@
+#include "tuning.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+struct StandardTuning : public TuningState {
+ StandardTuning() {
+ const int base = 50857777; // (1 << 24) * (log(440) / log(2) - 69/12)
+ const int step = (1 << 24) / 12;
+ for( int mn = 0; mn < 128; ++mn )
+ {
+ auto res = base + step * mn;
+ current_logfreq_table_[mn] = res;
+ }
+ }
+
+ virtual int32_t midinote_to_logfreq(int midinote) override {
+ return current_logfreq_table_[midinote];
+ }
+ int current_logfreq_table_[128];
+};
+
+
+std::shared_ptr createStandardTuning()
+{
+ return std::make_shared();
+}
+
+
+// This code is heavily based on the surge tuning implementation
+struct Tone
+{
+ typedef enum Type
+ {
+ kToneCents,
+ kToneRatio
+ } Type;
+
+ Type type;
+ float cents;
+ int ratio_d, ratio_n;
+ std::string stringRep;
+ float floatValue;
+
+ Tone() : type(kToneRatio), cents(0), ratio_d(1), ratio_n(1), stringRep("1/1"), floatValue(1.0)
+ {
+ }
+};
+
+struct Scale
+{
+ std::string name;
+ std::string description;
+ std::string rawText;
+ int count;
+ std::vector tones;
+
+ Scale() : name("empty scale"), description(""), rawText(""), count(0)
+ {
+ }
+
+ bool isValid() const;
+
+ static Scale evenTemperament12NoteScale();
+};
+
+struct KeyboardMapping
+{
+ bool isValid;
+ bool isStandardMapping;
+ int count;
+ int firstMidi, lastMidi;
+ int middleNote;
+ int tuningConstantNote;
+ float tuningFrequency;
+ int octaveDegrees;
+ std::vector keys; // rather than an 'x' we use a '-1' for skipped keys
+
+ std::string rawText;
+ std::string name;
+
+ KeyboardMapping() : isValid(true),
+ isStandardMapping(true),
+ count(12),
+ firstMidi(0),
+ lastMidi(127),
+ middleNote(60),
+ tuningConstantNote(60),
+ tuningFrequency(8.175798915 * 32),
+ octaveDegrees(12),
+ rawText( "" ),
+ name( "" )
+ {
+ for( int i=0; i<12; ++i )
+ keys.push_back(i);
+ }
+
+ static KeyboardMapping tuneA69To(double freq);
+};
+
+
+Scale parseSCLData(const std::string &sclContents);
+KeyboardMapping parseKBMData(const std::string &kbmContents);
+
+
+Scale scaleFromStream(std::istream &inf)
+{
+ std::string line;
+ const int read_header = 0, read_count = 1, read_note = 2;
+ int state = read_header;
+
+ Scale res;
+ std::ostringstream rawOSS;
+ while (std::getline(inf, line))
+ {
+ rawOSS << line << "\n";
+ if (line[0] == '!')
+ {
+ continue;
+ }
+ switch (state)
+ {
+ case read_header:
+ res.description = line;
+ state = read_count;
+ break;
+ case read_count:
+ res.count = atoi(line.c_str());
+ state = read_note;
+ break;
+ case read_note:
+ Tone t;
+ t.stringRep = line;
+ if (line.find(".") != std::string::npos)
+ {
+ t.type = Tone::kToneCents;
+ t.cents = atof(line.c_str());
+ }
+ else
+ {
+ t.type = Tone::kToneRatio;
+ auto slashPos = line.find("/");
+ if (slashPos == std::string::npos)
+ {
+ t.ratio_n = atoi(line.c_str());
+ t.ratio_d = 1;
+ }
+ else
+ {
+ t.ratio_n = atoi(line.substr(0, slashPos).c_str());
+ t.ratio_d = atoi(line.substr(slashPos + 1).c_str());
+ }
+
+ // 2^(cents/1200) = n/d
+ // cents = 1200 * log(n/d) / log(2)
+
+ t.cents = 1200 * log(1.0 * t.ratio_n/t.ratio_d) / log(2.0);
+ }
+ t.floatValue = t.cents / 1200.0 + 1.0;
+ res.tones.push_back(t);
+
+ break;
+ }
+ }
+
+ res.rawText = rawOSS.str();
+ return res;
+}
+
+Scale readSCLFile(std::string fname)
+{
+ std::ifstream inf;
+ inf.open(fname);
+ if (!inf.is_open())
+ {
+ return Scale();
+ }
+
+ auto res = scaleFromStream(inf);
+ res.name = fname;
+ return res;
+}
+
+Scale parseSCLData(const std::string &d)
+{
+ std::istringstream iss(d);
+ auto res = scaleFromStream(iss);
+ res.name = "Scale from Patch";
+ return res;
+}
+
+Scale Scale::evenTemperament12NoteScale()
+{
+ auto data = R"SCL(! even.scl
+!
+12 note even temprament
+ 12
+!
+ 100.0
+ 200.0
+ 300.0
+ 400.0
+ 500.0
+ 600.0
+ 700.0
+ 800.0
+ 900.0
+ 1000.0
+ 1100.0
+ 2/1
+)SCL";
+ return parseSCLData(data);
+}
+
+KeyboardMapping keyboardMappingFromStream(std::istream &inf)
+{
+ std::string line;
+
+ KeyboardMapping res;
+ std::ostringstream rawOSS;
+ res.isStandardMapping = false;
+ res.keys.clear();
+
+ enum parsePosition {
+ map_size = 0,
+ first_midi,
+ last_midi,
+ middle,
+ reference,
+ freq,
+ degree,
+ keys
+ };
+ parsePosition state = map_size;
+
+ while (std::getline(inf, line))
+ {
+ rawOSS << line << "\n";
+ if (line[0] == '!')
+ {
+ continue;
+ }
+
+ if( line == "x" ) line = "-1";
+
+ int i = std::atoi(line.c_str());
+ float v = std::atof(line.c_str());
+
+ switch (state)
+ {
+ case map_size:
+ res.count = i;
+ break;
+ case first_midi:
+ res.firstMidi = i;
+ break;
+ case last_midi:
+ res.lastMidi = i;
+ break;
+ case middle:
+ res.middleNote = i;
+ break;
+ case reference:
+ res.tuningConstantNote = i;
+ break;
+ case freq:
+ res.tuningFrequency = v;
+ break;
+ case degree:
+ res.octaveDegrees = i;
+ break;
+ case keys:
+ res.keys.push_back(i);
+ break;
+ }
+ if( state != keys ) state = (parsePosition)(state + 1);
+ }
+
+ res.rawText = rawOSS.str();
+ return res;
+}
+
+KeyboardMapping readKBMFile(std::string fname)
+{
+ std::ifstream inf;
+ inf.open(fname);
+ if (!inf.is_open())
+ {
+ return KeyboardMapping();
+ }
+
+ auto res = keyboardMappingFromStream(inf);
+ res.name = fname;
+ return res;
+}
+
+KeyboardMapping parseKBMData(const std::string &d)
+{
+ std::istringstream iss(d);
+ auto res = keyboardMappingFromStream(iss);
+ res.name = "Mapping from Patch";
+ return res;
+}
+
+bool Scale::isValid() const
+{
+ if (count <= 0)
+ return false;
+
+ // TODO check more things maybe...
+ return true;
+}
+
+KeyboardMapping KeyboardMapping::tuneA69To(double freq)
+{
+ // There's a couple of ways to do this but since I want it to stream I will syntheitcally create
+ // a KBM file
+ std::ostringstream oss;
+ oss << R"KBM(! Dexed Synthetic Keyboard Tuning to Retune A69
+!
+! Map Size
+0
+! First note
+0
+! Last note
+127
+! First mapping
+60
+! Reference Note
+69
+! Reference Freqency
+)KBM" << freq << R"KBM(
+! Scale Degree
+0
+! Mapping)KBM";
+ return parseKBMData( oss.str() );
+}
+
+struct SCLAndKBMTuningState : public TuningState {
+ virtual bool is_standard_tuning() override {
+ return false;
+ }
+
+ virtual int32_t midinote_to_logfreq(int midinote) override {
+ return current_logfreq_table_[midinote];
+ }
+
+ int current_logfreq_table_[128];
+ float current_freq_table_[128];
+
+ bool retuneTo(const Scale& s) {
+ return retuneTo( s, KeyboardMapping() );
+ }
+
+ bool retuneTo(const KeyboardMapping &k) {
+ return retuneTo( Scale(), k );
+ }
+
+ // Again this is basically the modified surge implementation
+ bool retuneTo( const Scale &s, const KeyboardMapping &k ) {
+ currentScale = s;
+ currentMapping = k;
+
+ if (!s.isValid())
+ return false;
+
+ float cp;
+ if( k.isStandardMapping )
+ cp = 32;
+ else
+ cp = k.tuningFrequency / 8.175798915;
+
+ float pitches[512];
+ int posPitch0 = 256 + k.tuningConstantNote;
+ int posScale0 = 256 + k.middleNote;
+ float pitchMod = log(cp)/log(2) - 1;
+
+ int scalePositionOfTuningNote = k.tuningConstantNote - k.middleNote;
+ if( k.count > 0 )
+ scalePositionOfTuningNote = k.keys[scalePositionOfTuningNote];
+
+ float tuningCenterPitchOffset;
+ if( scalePositionOfTuningNote == 0 )
+ tuningCenterPitchOffset = 0;
+ else
+ tuningCenterPitchOffset = s.tones[scalePositionOfTuningNote-1].floatValue - 1.0;
+
+ pitches[posPitch0] = 1.0;
+ float table_pitch[512];
+
+ for (int i=0; i<512; ++i)
+ {
+ // TODO: ScaleCenter and PitchCenter are now two different notes.
+ int distanceFromPitch0 = i - posPitch0;
+ int distanceFromScale0 = i - posScale0;
+
+ if( distanceFromPitch0 == 0 )
+ {
+ table_pitch[i] = pow( 2.0, pitches[i] + pitchMod );
+#if DEBUG_SCALES
+ if( i > 296 && i < 340 )
+ std::cout << "PITCH: i=" << i << " n=" << i - 256
+ << " p=" << pitches[i]
+ << " tp=" << table_pitch[i]
+ << " fr=" << table_pitch[i] * 8.175798915
+ << std::endl;
+#endif
+ }
+ else
+ {
+ /*
+ We used to have this which assumed 1-12
+ Now we have our note number, our distance from the
+ center note, and the key remapping
+ int rounds = (distanceFromScale0-1) / s.count;
+ int thisRound = (distanceFromScale0-1) % s.count;
+ */
+
+ int rounds;
+ int thisRound;
+ int disable = false;
+ if( k.isStandardMapping || ( k.count == 0 ) )
+ {
+ rounds = (distanceFromScale0-1) / s.count;
+ thisRound = (distanceFromScale0-1) % s.count;
+ }
+ else
+ {
+ /*
+ ** Now we have this situation. We are at note i so we
+ ** are m away from the center note which is distanceFromScale0
+ **
+ ** If we mod that by the mapping size we know which note we are on
+ */
+ int mappingKey = distanceFromScale0 % k.count;
+ if( mappingKey < 0 )
+ mappingKey += k.count;
+ int cm = k.keys[mappingKey];
+ int push = 0;
+ if( cm < 0 )
+ {
+ disable = true;
+ }
+ else
+ {
+ push = mappingKey - cm;
+ }
+ rounds = (distanceFromScale0 - push - 1) / s.count;
+ thisRound = (distanceFromScale0 - push - 1) % s.count;
+#ifdef DEBUG_SCALES
+ if( i > 296 && i < 340 )
+ std::cout << "MAPPING n=" << i - 256 << " pushes ds0=" << distanceFromScale0 << " cmc=" << k.count << " tr=" << thisRound << " r=" << rounds << " mk=" << mappingKey << " cm=" << cm << " push=" << push << " dis=" << disable << " mk-p-1=" << mappingKey - push - 1 << std::endl;
+#endif
+
+
+ }
+
+ if( thisRound < 0 )
+ {
+ thisRound += s.count;
+ rounds -= 1;
+ }
+#if DEBUG_SCALES
+ float mul = pow( s.tones[s.count-1].floatValue, rounds);
+ float otp = table_pitch[i];
+#endif
+ if( disable )
+ pitches[i] = 0;
+ else
+ pitches[i] = s.tones[thisRound].floatValue + rounds * (s.tones[s.count - 1].floatValue - 1.0) - tuningCenterPitchOffset;
+
+ table_pitch[i] = pow( 2.0, pitches[i] + pitchMod );
+
+#if DEBUG_SCALES
+ if( i > 296 && i < 340 )
+ std::cout << "PITCH: i=" << i << " n=" << i - 256
+ << " ds0=" << distanceFromScale0
+ << " dp0=" << distanceFromPitch0
+ << " r=" << rounds << " t=" << thisRound
+ << " p=" << pitches[i]
+ << " t=" << s.tones[thisRound].floatValue << " " << s.tones[thisRound ]
+ << " dis=" << disable
+ << " tp=" << table_pitch[i]
+ << " fr=" << table_pitch[i] * 8.175798915
+ << " otp=" << otp
+ << " tcpo=" << tuningCenterPitchOffset
+ << " diff=" << table_pitch[i] - otp
+
+ //<< " l2p=" << log(otp)/log(2.0)
+ //<< " l2p-p=" << log(otp)/log(2.0) - pitches[i] - rounds - 3
+ << std::endl;
+#endif
+ }
+ }
+
+ // OK so now table_pitch is the pitch where 1 -> 8.17 and 32 -> 261 and it doubles. It is also offset by 256. So
+ for( int i=0; i<128; ++i )
+ {
+ double tp = table_pitch[i + 256 ];
+ double freq = tp * 8.175798915;
+ current_freq_table_[i] = freq;
+ double flog = log(freq) / log(2.0);
+ int res = (int)( ( 1 << 24 ) * flog );
+
+ current_logfreq_table_[i] = res;
+ }
+
+ return true;
+ }
+
+ Scale currentScale;
+
+ virtual int scale_length() { return currentScale.count; }
+
+ KeyboardMapping currentMapping;
+
+ virtual std::string display_tuning_str() override {
+ std::ostringstream oss;
+ oss << "Current Tuning\n\n";
+ oss << "SCL File:\n" << currentScale.rawText << "\n\n KBM File:\n" << currentMapping.rawText << "\n\n";
+ oss << "Frequency by MidiNote\n";
+ oss << "Note | Freq\n";
+ for( int i=0; i<128; ++i )
+ {
+ oss << i << " | " << current_freq_table_[i] << " Hz\n";
+ }
+ return oss.str();
+ }
+
+
+};
+
+std::shared_ptr createTuningFromSCLData( const std::string &scl )
+{
+ auto k = parseSCLData(scl);
+ auto res = std::make_shared();
+ res->retuneTo(k);
+ return res;
+}
+
+std::shared_ptr createTuningFromKBMData( const std::string &kbm )
+{
+ auto k = parseKBMData(kbm);
+ auto res = std::make_shared();
+ res->retuneTo(Scale::evenTemperament12NoteScale(), k);
+ return res;
+}
+
+std::shared_ptr createTuningFromSCLAndKBMData( const std::string &sclData, const std::string &kbmData )
+{
+ auto s = parseSCLData(sclData);
+ auto k = parseKBMData(kbmData);
+ auto res = std::make_shared();
+ res->retuneTo(s,k);
+ return res;
+}
diff --git a/Source/msfa/tuning.h b/Source/msfa/tuning.h
new file mode 100644
index 00000000..d49b771a
--- /dev/null
+++ b/Source/msfa/tuning.h
@@ -0,0 +1,24 @@
+#ifndef __SYNTH_TUNING_H
+#define __SYNTH_TUNING_H
+
+#include "synth.h"
+#include
+#include
+
+class TuningState {
+public:
+ virtual ~TuningState() { }
+
+ virtual int32_t midinote_to_logfreq(int midinote) = 0;
+ virtual bool is_standard_tuning() { return true; }
+ virtual int scale_length() { return 12; }
+ virtual std::string display_tuning_str() { return "Standard Tuning"; }
+};
+
+std::shared_ptr createStandardTuning();
+
+std::shared_ptr createTuningFromSCLData( const std::string &sclData );
+std::shared_ptr createTuningFromKBMData( const std::string &kbmData );
+std::shared_ptr createTuningFromSCLAndKBMData( const std::string &sclData, const std::string &kbmData );
+
+#endif