diff --git a/Builds/MacOSX/Dexed.xcodeproj/project.pbxproj b/Builds/MacOSX/Dexed.xcodeproj/project.pbxproj index 82b1763..25f654c 100644 --- a/Builds/MacOSX/Dexed.xcodeproj/project.pbxproj +++ b/Builds/MacOSX/Dexed.xcodeproj/project.pbxproj @@ -32,6 +32,7 @@ 1CBFE444B43C89E749602B7F = { isa = PBXBuildFile; fileRef = 45ADFD8885BE76C7F7D999AC; }; 478699B4EF80A1A1C0D34C10 = { isa = PBXBuildFile; fileRef = 018D0FB9E97B68D2EB3E3F72; }; 623AFA9E78826CA0136251DF = { isa = PBXBuildFile; fileRef = 59D15F780D468B587F9C7E78; }; + B18C192606DF6079E7B9AEB9 = { isa = PBXBuildFile; fileRef = 8BADEB7BF1A65E83A7A1736D; }; 1C87DA0B69D67481FC6CF4FD = { isa = PBXBuildFile; fileRef = 54363D0B39B88D43447C0123; }; E3FA856CA4DB2009BB0F13E7 = { isa = PBXBuildFile; fileRef = 505852570B48EB3F18076B03; }; 7E6DF76EEA834AC1E96470CE = { isa = PBXBuildFile; fileRef = 3254CAE4C282C4432214B016; }; @@ -487,6 +488,7 @@ 8B160A62F6A6EF21379A8EA7 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_win32_Direct2DGraphicsContext.cpp"; path = "../../JuceLibraryCode/modules/juce_graphics/native/juce_win32_Direct2DGraphicsContext.cpp"; sourceTree = "SOURCE_ROOT"; }; 8B86ECF3351C9D029821C621 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_ImageComponent.h"; path = "../../JuceLibraryCode/modules/juce_gui_basics/widgets/juce_ImageComponent.h"; sourceTree = "SOURCE_ROOT"; }; 8B87D00DA619D83BA6926E28 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_ScopedLock.h"; path = "../../JuceLibraryCode/modules/juce_core/threads/juce_ScopedLock.h"; sourceTree = "SOURCE_ROOT"; }; + 8BADEB7BF1A65E83A7A1736D = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = PluginFx.cpp; path = ../../Source/PluginFx.cpp; sourceTree = "SOURCE_ROOT"; }; 8BBE375590444CD57A311DD2 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_Time.h"; path = "../../JuceLibraryCode/modules/juce_core/time/juce_Time.h"; sourceTree = "SOURCE_ROOT"; }; 8C4A3CBB322357AF0CA3C2CD = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_FileListComponent.h"; path = "../../JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileListComponent.h"; sourceTree = "SOURCE_ROOT"; }; 8CA96F07FD8390BD5EEC909B = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_DirectoryContentsDisplayComponent.h"; path = "../../JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_DirectoryContentsDisplayComponent.h"; sourceTree = "SOURCE_ROOT"; }; @@ -733,6 +735,7 @@ DB3B5F6FE49267EE53089A05 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_linux_WebBrowserComponent.cpp"; path = "../../JuceLibraryCode/modules/juce_gui_extra/native/juce_linux_WebBrowserComponent.cpp"; sourceTree = "SOURCE_ROOT"; }; DB9F89D1872247982F4D0913 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_TableHeaderComponent.h"; path = "../../JuceLibraryCode/modules/juce_gui_basics/widgets/juce_TableHeaderComponent.h"; sourceTree = "SOURCE_ROOT"; }; DBEA78ADC148399D593D22A9 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_AudioProcessorEditor.h"; path = "../../JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessorEditor.h"; sourceTree = "SOURCE_ROOT"; }; + DC75DFCDFCDB425927B11EC0 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = PluginFx.h; path = ../../Source/PluginFx.h; sourceTree = "SOURCE_ROOT"; }; DD1BF932ABF0CEC81A8B8A3A = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_DynamicObject.h"; path = "../../JuceLibraryCode/modules/juce_core/containers/juce_DynamicObject.h"; sourceTree = "SOURCE_ROOT"; }; DD683E5793B347EEB07FB199 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_ArrayAllocationBase.h"; path = "../../JuceLibraryCode/modules/juce_core/containers/juce_ArrayAllocationBase.h"; sourceTree = "SOURCE_ROOT"; }; DD9B0DAF1119ECB6FDC0A3AE = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_Path.cpp"; path = "../../JuceLibraryCode/modules/juce_graphics/geometry/juce_Path.cpp"; sourceTree = "SOURCE_ROOT"; }; @@ -889,7 +892,9 @@ 018D0FB9E97B68D2EB3E3F72, BD25F7AC4728A7875B6AE9D5, 59D15F780D468B587F9C7E78, - EA6FC4F1F0D6EAE661569905 ); name = Source; sourceTree = ""; }; + EA6FC4F1F0D6EAE661569905, + 8BADEB7BF1A65E83A7A1736D, + DC75DFCDFCDB425927B11EC0 ); name = Source; sourceTree = ""; }; C21CEF3B473FDC99270B7623 = { isa = PBXGroup; children = ( F472964B0FFEE5615B72CE3D, 29B923E6EB12F97B68585AFC ); name = Dexed; sourceTree = ""; }; @@ -1942,6 +1947,7 @@ 1CBFE444B43C89E749602B7F, 478699B4EF80A1A1C0D34C10, 623AFA9E78826CA0136251DF, + B18C192606DF6079E7B9AEB9, 1C87DA0B69D67481FC6CF4FD, E3FA856CA4DB2009BB0F13E7, 7E6DF76EEA834AC1E96470CE, diff --git a/Builds/MacOSX/Dexed.xcodeproj/project.xcworkspace/xcuserdata/asb2m10.xcuserdatad/UserInterfaceState.xcuserstate b/Builds/MacOSX/Dexed.xcodeproj/project.xcworkspace/xcuserdata/asb2m10.xcuserdatad/UserInterfaceState.xcuserstate index 6f095f4..7e65e0d 100644 Binary files a/Builds/MacOSX/Dexed.xcodeproj/project.xcworkspace/xcuserdata/asb2m10.xcuserdatad/UserInterfaceState.xcuserstate and b/Builds/MacOSX/Dexed.xcodeproj/project.xcworkspace/xcuserdata/asb2m10.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/Builds/VisualStudio2012/Dexed.vcxproj b/Builds/VisualStudio2012/Dexed.vcxproj index cd99dff..a2a39c8 100644 --- a/Builds/VisualStudio2012/Dexed.vcxproj +++ b/Builds/VisualStudio2012/Dexed.vcxproj @@ -148,6 +148,7 @@ + true @@ -1129,6 +1130,7 @@ + diff --git a/Builds/VisualStudio2012/Dexed.vcxproj.filters b/Builds/VisualStudio2012/Dexed.vcxproj.filters index 8a6e036..529789d 100644 --- a/Builds/VisualStudio2012/Dexed.vcxproj.filters +++ b/Builds/VisualStudio2012/Dexed.vcxproj.filters @@ -313,6 +313,9 @@ Dexed\Source + + Dexed\Source + Juce Modules\juce_audio_basics\buffers @@ -1437,6 +1440,9 @@ Dexed\Source + + Dexed\Source + Juce Modules\juce_audio_basics\buffers diff --git a/Dexed.jucer b/Dexed.jucer index d958335..9ae120a 100644 --- a/Dexed.jucer +++ b/Dexed.jucer @@ -57,6 +57,8 @@ + + diff --git a/README.md b/README.md index 0f69ad5..100711b 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ Dexed DX7 Software Emulator =========================== -Dexed is a multiplatform, multiformat plugin synth that is closely modeled on the Yamaha DX7. +Dexed is a multi platform, multiformat plugin synth that is closely modeled on the Yamaha DX7. Under the hood, it uses [music-synthesizer-for-android](https://code.google.com/p/music-synthesizer-for-android) for the synth engine and [JUCE](http://wwww.juce.com) as a plugin wrapper. @@ -13,10 +13,21 @@ Binary downloads It is far from finish but for those who want to try the "music-synthesizer-for-android" project on a PC/Mac, you can download it [here](http://le-son666.com/software/dexed) +(New) Features +-------------- +* Multi platform (OS X, Windows, Linux) and multi format (VST, AU); by using JUCE. +* The sound engine [music-synthesizer-for-android](https://code.google.com/p/music-synthesizer-for-android) is closely modeled on the original DX7 characteristics +* All of the 144 DX7 parameters are available from one single panel +* Fully supports DX7 input and output Sysex messages; including controller change. This means that you can use this with a native DX7/TX7 as a patch editor +* Each operator have a realtime VU meter to know wich one is active +* Can load any DX7/TX7 sysex programs [see this](http://www.synprez.com/SynprezFM/) for great collection of DX7 patches (SynprezFM-II-builtins.tgz) + TODO - Dexed ------------ -* Make all the knobs actually work -* Implement the DX look and feel +* Implement a better DX look and feel +* Better implementation of the LPF filter +* Better display of the amplitude envelope +* Implement a display of the algo TODO - msfa ----------- diff --git a/Source/GlobalEditor.cpp b/Source/GlobalEditor.cpp index f014ee6..8d8b4c0 100644 --- a/Source/GlobalEditor.cpp +++ b/Source/GlobalEditor.cpp @@ -50,7 +50,7 @@ GlobalEditor::GlobalEditor () addAndMakeVisible (lfoSpeed = new Slider ("lfoSpeed")); lfoSpeed->setRange (0, 99, 1); - lfoSpeed->setSliderStyle (Slider::LinearVertical); + lfoSpeed->setSliderStyle (Slider::LinearHorizontal); lfoSpeed->setTextBoxStyle (Slider::NoTextBox, false, 80, 20); lfoSpeed->addListener (this); @@ -68,18 +68,18 @@ GlobalEditor::GlobalEditor () addAndMakeVisible (lfoDelay = new Slider ("lfoDelay")); lfoDelay->setRange (0, 99, 1); - lfoDelay->setSliderStyle (Slider::LinearVertical); + lfoDelay->setSliderStyle (Slider::Rotary); lfoDelay->setTextBoxStyle (Slider::NoTextBox, false, 80, 20); lfoDelay->addListener (this); addAndMakeVisible (cutoff = new Slider ("cutoff")); - cutoff->setRange (0, 10, 0); + cutoff->setRange (0, 1, 0); cutoff->setSliderStyle (Slider::Rotary); cutoff->setTextBoxStyle (Slider::NoTextBox, false, 80, 20); cutoff->addListener (this); addAndMakeVisible (reso = new Slider ("reso")); - reso->setRange (0, 10, 0); + reso->setRange (0, 1, 0); reso->setSliderStyle (Slider::Rotary); reso->setTextBoxStyle (Slider::NoTextBox, false, 80, 20); reso->addListener (this); @@ -87,6 +87,78 @@ GlobalEditor::GlobalEditor () addAndMakeVisible (algoDisplay = new AlgoDisplay()); algoDisplay->setName ("algoDisplay"); + addAndMakeVisible (pitchRate2 = new Slider ("pitchRate2")); + pitchRate2->setRange (0, 99, 1); + pitchRate2->setSliderStyle (Slider::Rotary); + pitchRate2->setTextBoxStyle (Slider::NoTextBox, false, 80, 20); + pitchRate2->addListener (this); + + addAndMakeVisible (pitchRate3 = new Slider ("pitchRate3")); + pitchRate3->setRange (0, 99, 1); + pitchRate3->setSliderStyle (Slider::Rotary); + pitchRate3->setTextBoxStyle (Slider::NoTextBox, false, 80, 20); + pitchRate3->addListener (this); + + addAndMakeVisible (pitchRate4 = new Slider ("pitchRate4")); + pitchRate4->setRange (0, 99, 1); + pitchRate4->setSliderStyle (Slider::Rotary); + pitchRate4->setTextBoxStyle (Slider::NoTextBox, false, 80, 20); + pitchRate4->addListener (this); + + addAndMakeVisible (pitchRate1 = new Slider ("pitchRate1")); + pitchRate1->setRange (0, 99, 1); + pitchRate1->setSliderStyle (Slider::Rotary); + pitchRate1->setTextBoxStyle (Slider::NoTextBox, false, 80, 20); + pitchRate1->addListener (this); + + addAndMakeVisible (pitchLevel2 = new Slider ("pitchLevel2")); + pitchLevel2->setRange (0, 99, 1); + pitchLevel2->setSliderStyle (Slider::Rotary); + pitchLevel2->setTextBoxStyle (Slider::NoTextBox, false, 80, 20); + pitchLevel2->addListener (this); + + addAndMakeVisible (pitchLevel3 = new Slider ("pitchLevel3")); + pitchLevel3->setRange (0, 99, 1); + pitchLevel3->setSliderStyle (Slider::Rotary); + pitchLevel3->setTextBoxStyle (Slider::NoTextBox, false, 80, 20); + pitchLevel3->addListener (this); + + addAndMakeVisible (pitchLevel4 = new Slider ("pitchLevel4")); + pitchLevel4->setRange (0, 99, 1); + pitchLevel4->setSliderStyle (Slider::Rotary); + pitchLevel4->setTextBoxStyle (Slider::NoTextBox, false, 80, 20); + pitchLevel4->addListener (this); + + addAndMakeVisible (pitchLevel1 = new Slider ("pitchLevel1")); + pitchLevel1->setRange (0, 99, 1); + pitchLevel1->setSliderStyle (Slider::Rotary); + pitchLevel1->setTextBoxStyle (Slider::NoTextBox, false, 80, 20); + pitchLevel1->addListener (this); + + addAndMakeVisible (feedback = new Slider ("feedback")); + feedback->setRange (0, 7, 1); + feedback->setSliderStyle (Slider::Rotary); + feedback->setTextBoxStyle (Slider::NoTextBox, false, 80, 20); + feedback->addListener (this); + + addAndMakeVisible (transpose = new Slider ("transpose")); + transpose->setRange (0, 49, 0); + transpose->setSliderStyle (Slider::LinearVertical); + transpose->setTextBoxStyle (Slider::NoTextBox, false, 80, 20); + transpose->addListener (this); + + addAndMakeVisible (oscSync = new ToggleButton ("oscSync")); + oscSync->setButtonText (String::empty); + + addAndMakeVisible (pitchModSens = new Slider ("pitchModSens")); + pitchModSens->setRange (0, 7, 0); + pitchModSens->setSliderStyle (Slider::Rotary); + pitchModSens->setTextBoxStyle (Slider::NoTextBox, false, 80, 20); + pitchModSens->addListener (this); + + addAndMakeVisible (lfoSync = new ToggleButton ("lfoSync")); + lfoSync->setButtonText (String::empty); + //[UserPreSize] //[/UserPreSize] @@ -95,6 +167,8 @@ GlobalEditor::GlobalEditor () //[Constructor] You can add your own custom stuff here.. + systemMsg << "* DEXED DX synthesizer *"; + //[/Constructor] } @@ -112,6 +186,19 @@ GlobalEditor::~GlobalEditor() cutoff = nullptr; reso = nullptr; algoDisplay = nullptr; + pitchRate2 = nullptr; + pitchRate3 = nullptr; + pitchRate4 = nullptr; + pitchRate1 = nullptr; + pitchLevel2 = nullptr; + pitchLevel3 = nullptr; + pitchLevel4 = nullptr; + pitchLevel1 = nullptr; + feedback = nullptr; + transpose = nullptr; + oscSync = nullptr; + pitchModSens = nullptr; + lfoSync = nullptr; //[Destructor]. You can add your own custom destruction code here.. @@ -125,20 +212,44 @@ void GlobalEditor::paint (Graphics& g) //[/UserPrePaint] //[UserPaint] Add your own custom painting code here.. + g.setColour (Colours::black); + g.setFont (Font (Font::getDefaultMonospacedFontName(), 15.00f, Font::plain)); + g.drawText (systemMsg, + 11, 8, 300, 8, + Justification::centredLeft, true); + + g.setColour (Colours::black); + g.setFont (Font (Font::getDefaultMonospacedFontName(), 15.00f, Font::plain)); + g.drawText (paramMsg, + 11, 24, 300, 8, + Justification::centredLeft, true); //[/UserPaint] } void GlobalEditor::resized() { - algo->setBounds (784, 48, 64, 24); - lfoType->setBounds (768, 8, 80, 16); - lfoSpeed->setBounds (712, 0, 23, 80); - lfoAmDepth->setBounds (816, 24, 32, 24); - lfoPitchDepth->setBounds (792, 24, 32, 24); - lfoDelay->setBounds (736, 0, 23, 80); - cutoff->setBounds (176, 40, 48, 48); - reso->setBounds (232, 40, 48, 48); - algoDisplay->setBounds (8, 8, 150, 72); + algo->setBounds (480, 64, 80, 24); + lfoType->setBounds (624, 40, 96, 16); + lfoSpeed->setBounds (624, 16, 96, 16); + lfoAmDepth->setBounds (696, 64, 24, 24); + lfoPitchDepth->setBounds (648, 64, 24, 24); + lfoDelay->setBounds (672, 64, 24, 24); + cutoff->setBounds (8, 40, 48, 48); + reso->setBounds (64, 40, 48, 48); + algoDisplay->setBounds (480, 8, 126, 56); + pitchRate2->setBounds (776, 64, 32, 24); + pitchRate3->setBounds (800, 64, 32, 24); + pitchRate4->setBounds (824, 64, 32, 24); + pitchRate1->setBounds (752, 64, 32, 24); + pitchLevel2->setBounds (776, 40, 32, 24); + pitchLevel3->setBounds (800, 40, 32, 24); + pitchLevel4->setBounds (824, 40, 32, 24); + pitchLevel1->setBounds (752, 40, 32, 24); + feedback->setBounds (560, 64, 24, 24); + transpose->setBounds (728, 0, 24, 56); + oscSync->setBounds (584, 64, 24, 24); + pitchModSens->setBounds (728, 64, 24, 24); + lfoSync->setBounds (624, 64, 24, 24); //[UserResized] Add your own custom resize handling here.. //[/UserResized] } @@ -183,6 +294,61 @@ void GlobalEditor::sliderValueChanged (Slider* sliderThatWasMoved) //[UserSliderCode_reso] -- add your slider handling code here.. //[/UserSliderCode_reso] } + else if (sliderThatWasMoved == pitchRate2) + { + //[UserSliderCode_pitchRate2] -- add your slider handling code here.. + //[/UserSliderCode_pitchRate2] + } + else if (sliderThatWasMoved == pitchRate3) + { + //[UserSliderCode_pitchRate3] -- add your slider handling code here.. + //[/UserSliderCode_pitchRate3] + } + else if (sliderThatWasMoved == pitchRate4) + { + //[UserSliderCode_pitchRate4] -- add your slider handling code here.. + //[/UserSliderCode_pitchRate4] + } + else if (sliderThatWasMoved == pitchRate1) + { + //[UserSliderCode_pitchRate1] -- add your slider handling code here.. + //[/UserSliderCode_pitchRate1] + } + else if (sliderThatWasMoved == pitchLevel2) + { + //[UserSliderCode_pitchLevel2] -- add your slider handling code here.. + //[/UserSliderCode_pitchLevel2] + } + else if (sliderThatWasMoved == pitchLevel3) + { + //[UserSliderCode_pitchLevel3] -- add your slider handling code here.. + //[/UserSliderCode_pitchLevel3] + } + else if (sliderThatWasMoved == pitchLevel4) + { + //[UserSliderCode_pitchLevel4] -- add your slider handling code here.. + //[/UserSliderCode_pitchLevel4] + } + else if (sliderThatWasMoved == pitchLevel1) + { + //[UserSliderCode_pitchLevel1] -- add your slider handling code here.. + //[/UserSliderCode_pitchLevel1] + } + else if (sliderThatWasMoved == feedback) + { + //[UserSliderCode_feedback] -- add your slider handling code here.. + //[/UserSliderCode_feedback] + } + else if (sliderThatWasMoved == transpose) + { + //[UserSliderCode_transpose] -- add your slider handling code here.. + //[/UserSliderCode_transpose] + } + else if (sliderThatWasMoved == pitchModSens) + { + //[UserSliderCode_pitchModSens] -- add your slider handling code here.. + //[/UserSliderCode_pitchModSens] + } //[UsersliderValueChanged_Post] //[/UsersliderValueChanged_Post] @@ -214,7 +380,32 @@ void GlobalEditor::bind(DexedAudioProcessor *parent) { parent->lfoWaveform->bind(lfoType); parent->lfoAmpDepth->bind(lfoAmDepth); parent->lfoPitchDepth->bind(lfoPitchDepth); - processor = parent; + parent->lfoSync->bind(lfoSync); + parent->oscSync->bind(oscSync); + parent->transpose->bind(transpose); + parent->feedback->bind(feedback); + parent->pitchModSens->bind(pitchModSens); + parent->pitchEgLevel[0]->bind(pitchLevel1); + parent->pitchEgLevel[1]->bind(pitchLevel2); + parent->pitchEgLevel[2]->bind(pitchLevel3); + parent->pitchEgLevel[3]->bind(pitchLevel4); + parent->pitchEgRate[0]->bind(pitchRate1); + parent->pitchEgRate[1]->bind(pitchRate2); + parent->pitchEgRate[2]->bind(pitchRate3); + parent->pitchEgRate[3]->bind(pitchRate4); + parent->fxCutoff->bind(cutoff); + parent->fxReso->bind(reso); + processor = parent; +} + +void GlobalEditor::setSystemMessage(String msg) { + systemMsg = msg; + repaint(); +} + +void GlobalEditor::setParamMessage(String msg) { + paramMsg = msg; + repaint(); } //[/MiscUserCode] @@ -235,40 +426,90 @@ BEGIN_JUCER_METADATA fixedSize="1" initialWidth="855" initialHeight="90"> + explicitFocusOrder="0" pos="672 64 24 24" min="0" max="99" int="1" + style="Rotary" textBoxPos="NoTextBox" textBoxEditable="1" textBoxWidth="80" + textBoxHeight="20" skewFactor="1"/> + + + + + + + + + + + + + END_JUCER_METADATA diff --git a/Source/GlobalEditor.h b/Source/GlobalEditor.h index 2fb0c2f..86215e9 100644 --- a/Source/GlobalEditor.h +++ b/Source/GlobalEditor.h @@ -47,8 +47,9 @@ public: //============================================================================== //[UserMethods] -- You can add your own custom methods in this section. - void bind(DexedAudioProcessor *processor); + void setSystemMessage(String msg); + void setParamMessage(String msg); //[/UserMethods] void paint (Graphics& g); @@ -61,6 +62,8 @@ public: private: //[UserVariables] -- You can add your own custom variables in this section. DexedAudioProcessor *processor; + String systemMsg; + String paramMsg; //[/UserVariables] //============================================================================== @@ -73,6 +76,19 @@ private: ScopedPointer cutoff; ScopedPointer reso; ScopedPointer algoDisplay; + ScopedPointer pitchRate2; + ScopedPointer pitchRate3; + ScopedPointer pitchRate4; + ScopedPointer pitchRate1; + ScopedPointer pitchLevel2; + ScopedPointer pitchLevel3; + ScopedPointer pitchLevel4; + ScopedPointer pitchLevel1; + ScopedPointer feedback; + ScopedPointer transpose; + ScopedPointer oscSync; + ScopedPointer pitchModSens; + ScopedPointer lfoSync; //============================================================================== diff --git a/Source/OperatorEditor.cpp b/Source/OperatorEditor.cpp index 6bd0e20..cd01e05 100644 --- a/Source/OperatorEditor.cpp +++ b/Source/OperatorEditor.cpp @@ -462,6 +462,7 @@ void OperatorEditor::bind(DexedAudioProcessor *parent, int op) { parent->opCtrl[op].sclRightCurve->bind(kbdRightCurve); parent->opCtrl[op].sclLeftDepth->bind(sclLeftLevel); parent->opCtrl[op].sclRightDepth->bind(sclRightLevel); + parent->opCtrl[op].sclRate->bind(sclRateScaling); } diff --git a/Source/PluginFx.cpp b/Source/PluginFx.cpp new file mode 100644 index 0000000..36386ab --- /dev/null +++ b/Source/PluginFx.cpp @@ -0,0 +1,86 @@ +/* + ============================================================================== + + PluginFx.cpp + Created: 26 Dec 2013 7:13:29pm + Author: Pascal Gauthier + + The LPF is taken from the PD moog~ object. (David Lowenfels) + Code is based off of Tim Stilson's moog filter code + + ============================================================================== +*/ + +#include "math.h" +#include "PluginFx.h" + +static float gaintable[199] = { + 0.999969, 0.990082, 0.980347, 0.970764, 0.961304, 0.951996, 0.94281, 0.933777, 0.924866, 0.916077, 0.90741, 0.898865, 0.890442, + 0.882141, 0.873962, 0.865906, 0.857941, 0.850067, 0.842346, 0.834686, 0.827148, 0.819733, 0.812378, 0.805145, 0.798004, 0.790955, + 0.783997, 0.77713, 0.770355, 0.763672, 0.75708 , 0.75058, 0.744141, 0.737793, 0.731537, 0.725342, 0.719238, 0.713196, 0.707245, + 0.701355, 0.695557, 0.689819, 0.684174, 0.678558, 0.673035, 0.667572, 0.66217, 0.65686, 0.651581, 0.646393, 0.641235, 0.636169, + 0.631134, 0.62619, 0.621277, 0.616425, 0.611633, 0.606903, 0.602234, 0.597626, 0.593048, 0.588531, 0.584045, 0.579651, 0.575287, + 0.570953, 0.566681, 0.562469, 0.558289, 0.554169, 0.550079, 0.546051, 0.542053, 0.538116, 0.53421, 0.530334, 0.52652, 0.522736, + 0.518982, 0.515289, 0.511627, 0.507996 , 0.504425, 0.500885, 0.497375, 0.493896, 0.490448, 0.487061, 0.483704, 0.480377, 0.477081, + 0.473816, 0.470581, 0.467377, 0.464203, 0.46109, 0.457977, 0.454926, 0.451874, 0.448883, 0.445892, 0.442932, 0.440033, 0.437134, + 0.434265, 0.431427, 0.428619, 0.425842, 0.423096, 0.42038, 0.417664, 0.415009, 0.412354, 0.409729, 0.407135, 0.404572, 0.402008, + 0.399506, 0.397003, 0.394501, 0.392059, 0.389618, 0.387207, 0.384827, 0.382477, 0.380127, 0.377808, 0.375488, 0.37323, 0.370972, + 0.368713, 0.366516, 0.364319, 0.362122, 0.359985, 0.357849, 0.355713, 0.353607, 0.351532, 0.349457, 0.347412, 0.345398, 0.343384, + 0.34137, 0.339417, 0.337463, 0.33551, 0.333588, 0.331665, 0.329773, 0.327911, 0.32605, 0.324188, 0.322357, 0.320557, 0.318756, + 0.316986, 0.315216, 0.313446, 0.311707, 0.309998, 0.308289, 0.30658, 0.304901, 0.303223, 0.301575, 0.299927, 0.298309, 0.296692, + 0.295074, 0.293488, 0.291931, 0.290375, 0.288818, 0.287262, 0.285736, 0.284241, 0.282715, 0.28125, 0.279755, 0.27829, 0.276825, + 0.275391, 0.273956, 0.272552, 0.271118, 0.269745, 0.268341, 0.266968, 0.265594, 0.264252, 0.262909, 0.261566, 0.260223, 0.258911, + 0.257599, 0.256317, 0.255035, 0.25375 +}; + +static inline float saturate(float input) { //clamp without branching +#define _limit 0.95 + float x1 = fabsf( input + _limit ); + float x2 = fabsf( input - _limit ); + return 0.5 * (x1 - x2); +} + +static inline float crossfade(float amount, float a, float b) { + return (1-amount) * a + amount * b; +} + +void PluginFx::init(int sampleRate) { + srate = sampleRate; + output = 0; + for(int i=0;i<4;i++) + state[i] = 0; +} + +void PluginFx::process(float *work, int sampleSize) { + // the UI values haved changed + if ( uiCutoff != pCutoff || uiReso != pReso) { + // calc cutoff + float freqCutoff = uiCutoff * 22000; + float fc = 2 * freqCutoff / srate; + float x2 = fc*fc; + float x3 = fc*x2; + p = -0.69346 * x3 - 0.59515 * x2 + 3.2937 * fc - 1.0072; //cubic fit + + // calc reso + float ix = p * 99; + int ixint = floor( ix ); + float ixfrac = ix - ixint; + Q = uiReso * crossfade( ixfrac, gaintable[ ixint + 99 ], gaintable[ ixint + 100 ] ); + + pCutoff = uiCutoff; + pReso = uiReso; + } + + for (int i=0; i < sampleSize; i++ ) { + output = 0.25 * ( work[i] - output ); //negative feedback + for(int pole=0; pole < 4; pole++) { + float temp = state[pole]; + output = saturate( output + p * (output - temp)); + state[pole] = output; + output = saturate( output + temp ); + } + work[i] = output; + output *= Q; //scale the feedback + } +} + diff --git a/Source/PluginFx.h b/Source/PluginFx.h new file mode 100644 index 0000000..42976ae --- /dev/null +++ b/Source/PluginFx.h @@ -0,0 +1,34 @@ +/* + ============================================================================== + + PluginFx.h + Created: 26 Dec 2013 7:13:29pm + Author: Pascal Gauthier + + ============================================================================== +*/ + +#ifndef PLUGINFX_H_INCLUDED +#define PLUGINFX_H_INCLUDED + +class PluginFx { + /** + * Used for the 4pole LFP + */ + // process and ui values + float pReso; + float pCutoff; + float p, Q; + int srate; + float state[4]; + float output = 0; +public: + + float uiCutoff = 0; + float uiReso = 0; + + void init(int sampleRate); + void process(float *work, int sampleSize); +}; + +#endif // PLUGINFX_H_INCLUDED diff --git a/Source/PluginParam.cpp b/Source/PluginParam.cpp index 9ab8bad..065b044 100755 --- a/Source/PluginParam.cpp +++ b/Source/PluginParam.cpp @@ -20,6 +20,7 @@ #include "PluginParam.h" #include "PluginProcessor.h" +#include "PluginEditor.h" // ************************************************************************ // @@ -65,21 +66,64 @@ void Ctrl::unbind() { } } +void Ctrl::publishValue(float value) { + parent->beginParameterChangeGesture(idx); + parent->setParameterNotifyingHost(idx, value); + parent->endParameterChangeGesture(idx); +} + +void Ctrl::sliderValueChanged(Slider* moved) { + publishValue(moved->getValue()); +} + +void Ctrl::buttonClicked(Button* clicked) { + publishValue(clicked->getToggleStateValue() == 1 ? 1 : 0); +} + +void Ctrl::comboBoxChanged(ComboBox* combo) { + publishValue((combo->getSelectedId() - 1) / combo->getNumItems()); +} + +// ************************************************************************ +// CtrlDX - control DX mapping +CtrlFloat::CtrlFloat(String name, float *storageValue) : Ctrl(name) { + vPointer = storageValue; +} + +float CtrlFloat::getValueHost() { + return *vPointer; +} + +void CtrlFloat::setValueHost(float v) { + *vPointer = v; +} + +String CtrlFloat::getValueDisplay() { + String display; + display << *vPointer; + return display; +} + +void CtrlFloat::updateComponent() { + if (slider != NULL) { + slider->setValue(*vPointer, NotificationType::dontSendNotification); + } +} + // ************************************************************************ -// CtrlInt ================================================================ -CtrlDX::CtrlDX(String name, int steps, int offset, bool starts1) : - Ctrl(name) { +// CtrlDX - control DX mapping +CtrlDX::CtrlDX(String name, int steps, int offset, bool starts1) : Ctrl(name) { add1 = starts1 == 1; this->steps = steps; - value = 0; + dxValue = 0; dxOffset = offset; } -float CtrlDX::getValuePlugin() { - return value / steps; +float CtrlDX::getValueHost() { + return dxValue / steps; } -void CtrlDX::setValuePlugin(float f) { +void CtrlDX::setValueHost(float f) { setValue((f * steps)); } @@ -88,7 +132,7 @@ void CtrlDX::setValue(int v) { TRACE("WARNING: value too big %s : %d", label.toRawUTF8(), v); v = steps - 1; } - value = v; + dxValue = v; if (dxOffset >= 0) { if (parent != NULL) parent->setDxValue(dxOffset, v); @@ -97,36 +141,36 @@ void CtrlDX::setValue(int v) { int CtrlDX::getValue() { if (dxOffset >= 0) - value = parent->data[dxOffset]; - return value; + dxValue = parent->data[dxOffset]; + return dxValue; } String CtrlDX::getValueDisplay() { String ret; - ret << getValue(); + ret << ( getValue() + add1 ); return ret; } -void CtrlDX::publishValue(int value) { - parent->beginParameterChangeGesture(idx); - parent->setParameterNotifyingHost(idx, (((float) value) / steps)); - parent->endParameterChangeGesture(idx); +void CtrlDX::publishValue(float value) { + Ctrl::publishValue(value / steps); + + DexedAudioProcessorEditor *editor = (DexedAudioProcessorEditor *) parent->getActiveEditor(); + if ( editor == NULL ) + return; + String msg; + msg << label << " = " << getValueDisplay(); + editor->global.setParamMessage(msg); } void CtrlDX::sliderValueChanged(Slider* moved) { publishValue(((int) moved->getValue() - add1)); } -void CtrlDX::buttonClicked(Button* clicked) { - publishValue(clicked->getToggleStateValue() == 1 ? 1 : 0); -} - void CtrlDX::comboBoxChanged(ComboBox* combo) { publishValue(combo->getSelectedId() - 1); } void CtrlDX::updateComponent() { - //TRACE("setting for %s %d", label.toRawUTF8(), getValue()); if (slider != NULL) { slider->setValue(getValue() + add1, NotificationType::dontSendNotification); @@ -151,11 +195,18 @@ void CtrlDX::updateComponent() { } } -// ************************************************************************ -// Patcher ================================================================ +/*************************************************************** + * + */ void DexedAudioProcessor::initCtrl() { importSysex(BinaryData::startup_syx); + fxCutoff = new CtrlFloat("Cutoff", &fx.uiCutoff); + ctrl.add(fxCutoff); + + fxReso = new CtrlFloat("Resonance", &fx.uiReso); + ctrl.add(fxReso); + // fill operator values; for (int i = 0; i < 6; i++) { //// In the Sysex, OP6 comes first, then OP5... @@ -167,107 +218,129 @@ void DexedAudioProcessor::initCtrl() { for (int j = 0; j < 4; j++) { String opRate; - opRate << opName << "-EGR" << (j + 1); + opRate << opName << " EGR" << (j + 1); opCtrl[opVal].egRate[j] = new CtrlDX(opRate, 100, opTarget + j); ctrl.add(opCtrl[opVal].egRate[j]); String opLevel; - opLevel << opName << "-EGL" << (j + 1); - opCtrl[opVal].egLevel[j] = new CtrlDX(opLevel, 100, - opTarget + j + 4); + opLevel << opName << " EGL" << (j + 1); + opCtrl[opVal].egLevel[j] = new CtrlDX(opLevel, 100, opTarget + j + 4); ctrl.add(opCtrl[opVal].egLevel[j]); } String opVol; - opVol << opName << "-LEVEL"; + opVol << opName << " OUTPUT LEVEL"; opCtrl[opVal].level = new CtrlDX(opVol, 100, opTarget + 16); ctrl.add(opCtrl[opVal].level); String opMode; - opMode << opName << "-MODE"; + opMode << opName << " MODE"; opCtrl[opVal].opMode = new CtrlDX(opMode, 1, opTarget + 17); ctrl.add(opCtrl[opVal].opMode); String coarse; - coarse << opName << "-COARSE"; + coarse << opName << " F COARSE"; opCtrl[opVal].coarse = new CtrlDX(coarse, 32, opTarget + 18); ctrl.add(opCtrl[opVal].coarse); String fine; - fine << opName << "-FINE"; + fine << opName << " F FINE"; opCtrl[opVal].fine = new CtrlDX(fine, 100, opTarget + 19); ctrl.add(opCtrl[opVal].fine); String detune; - detune << opName << "-DETUNE"; + detune << opName << " OSC DETUNE"; opCtrl[opVal].detune = new CtrlDX(detune, 15, opTarget + 20); ctrl.add(opCtrl[opVal].detune); String sclBrkPt; - sclBrkPt << opName << "-SCL_BRK_PNT"; + sclBrkPt << opName << " BREAK POINT"; opCtrl[opVal].sclBrkPt = new CtrlDX(sclBrkPt, 100, opTarget + 8); ctrl.add(opCtrl[opVal].sclBrkPt); String sclLeftDepth; - sclLeftDepth << opName << "-SCL_LFT_DEPTH"; + sclLeftDepth << opName << " L SCALE DEPTH"; opCtrl[opVal].sclLeftDepth = new CtrlDX(sclLeftDepth, 100, opTarget + 9); ctrl.add(opCtrl[opVal].sclLeftDepth); String sclRightDepth; - sclRightDepth << opName << "-SCL_RHT_DEPTH"; + sclRightDepth << opName << " R SCALE DEPTH"; opCtrl[opVal].sclRightDepth = new CtrlDX(sclRightDepth, 100, opTarget + 10); ctrl.add(opCtrl[opVal].sclRightDepth); String sclLeftCurve; - sclLeftCurve << opName << "-SCL_LFT_CURVE"; + sclLeftCurve << opName << " L KEY SCALE"; opCtrl[opVal].sclLeftCurve = new CtrlDX(sclLeftCurve, 4, opTarget + 11); ctrl.add(opCtrl[opVal].sclLeftCurve); String sclRightCurve; - sclRightCurve << opName << "-SCL_RHT_CURVE"; + sclRightCurve << opName << " R KEY SCALE"; opCtrl[opVal].sclRightCurve = new CtrlDX(sclRightCurve, 4, opTarget + 12); ctrl.add(opCtrl[opVal].sclRightCurve); String sclRate; - sclRate << opName << "-SCL_RATE"; + sclRate << opName << " RATE SCALING"; opCtrl[opVal].sclRate = new CtrlDX(sclRate, 7, opTarget + 13); ctrl.add(opCtrl[opVal].sclRate); String ampModSens; - ampModSens << opName << "-AMP_MODSENS"; + ampModSens << opName << " A MOD SENS."; opCtrl[opVal].ampModSens = new CtrlDX(ampModSens, 3, opTarget + 14); ctrl.add(opCtrl[opVal].ampModSens); String velModSens; - velModSens << opName << "-VEL_MODSENS"; + velModSens << opName << " KEY VELOCITY"; opCtrl[opVal].velModSens = new CtrlDX(velModSens, 8, opTarget + 15); ctrl.add(opCtrl[opVal].velModSens); } - algo = new CtrlDX("Algorithm", 32, 134, true); + algo = new CtrlDX("ALGORITHM", 32, 134, true); ctrl.add(algo); - lfoRate = new CtrlDX("LFO-Rate", 100, 137); + feedback = new CtrlDX("FEEDBACK", 8, 135); + ctrl.add(feedback); + + oscSync = new CtrlDX("OSC KEY SYNC", 2, 136); + ctrl.add(oscSync); + + lfoRate = new CtrlDX("LFO SPEED", 100, 137); ctrl.add(lfoRate); - lfoDelay = new CtrlDX("LFO-Delay", 100, 138); + lfoDelay = new CtrlDX("LFO DELAY", 100, 138); ctrl.add(lfoDelay); - lfoPitchDepth = new CtrlDX("LFO-PitchDepth", 100, 139); + lfoPitchDepth = new CtrlDX("LFO PM DEPTH", 100, 139); ctrl.add(lfoPitchDepth); - lfoAmpDepth = new CtrlDX("LFO-AmpDepth", 100, 140); + lfoAmpDepth = new CtrlDX("LFO AM DEPTH", 100, 140); ctrl.add(lfoAmpDepth); - lfoSync = new CtrlDX("LFO-Sync", 2, 141); + lfoSync = new CtrlDX("LFO KEY SYNC", 2, 141); ctrl.add(lfoSync); - lfoWaveform = new CtrlDX("LFO-Waveform", 5, 142); + lfoWaveform = new CtrlDX("LFO WAVE", 5, 142); ctrl.add(lfoWaveform); - - for (int i = 0; i < ctrl.size(); i++) { + + transpose = new CtrlDX("MIDDLE C", 49, 144); + ctrl.add(transpose); + + pitchModSens = new CtrlDX("P MODE SENS.", 8, 143); + ctrl.add(pitchModSens); + + for (int i=0;i<4;i++) { + String rate; + rate << "PITCH EGR" << (i+1); + String level; + level << "PITCH EGL" << (i+1); + pitchEgRate[i] = new CtrlDX(rate, 99, 126+i); + ctrl.add(pitchEgRate[i]); + pitchEgLevel[i] = new CtrlDX(level, 99, 130+i); + ctrl.add(pitchEgLevel[i]); + } + + for (int i=0; i < ctrl.size(); i++) { ctrl[i]->idx = i; ctrl[i]->parent = this; } @@ -370,11 +443,11 @@ int DexedAudioProcessor::getNumParameters() { } float DexedAudioProcessor::getParameter(int index) { - return ctrl[index]->getValuePlugin(); + return ctrl[index]->getValueHost(); } void DexedAudioProcessor::setParameter(int index, float newValue) { - ctrl[index]->setValuePlugin(newValue); + ctrl[index]->setValueHost(newValue); } int DexedAudioProcessor::getNumPrograms() { diff --git a/Source/PluginParam.h b/Source/PluginParam.h index 0262017..c8018ac 100755 --- a/Source/PluginParam.h +++ b/Source/PluginParam.h @@ -42,12 +42,22 @@ public: void bind(Button *b); void bind(ComboBox *c); void unbind(); + + // use this to signal a parameter change to the host + void publishValue(float value); - virtual void setValuePlugin(float f) = 0; - virtual float getValuePlugin() = 0; + /** + * Host value is related 0.0 to 1.0 values + */ + virtual void setValueHost(float f) = 0; + virtual float getValueHost() = 0; virtual String getValueDisplay() = 0; virtual void updateComponent() = 0; + void comboBoxChanged (ComboBox* combo); + void sliderValueChanged (Slider* moved); + void buttonClicked (Button* buttonThatWasClicked); + /** * Index of this parameter */ @@ -55,24 +65,35 @@ public: DexedAudioProcessor *parent; }; +class CtrlFloat : public Ctrl { + float *vPointer; +public: + + CtrlFloat(String name, float *storageValue); + void setValueHost(float f); + float getValueHost(); + String getValueDisplay(); + void updateComponent(); +}; + // CtrlDX is a controller that is related to DX parameters class CtrlDX : public Ctrl { - int value; + int dxValue; int steps; int add1; int dxOffset; public: CtrlDX(String name, int steps, int offset = -1, bool starts1 = false); - void setValuePlugin(float f); - float getValuePlugin(); - void publishValue(int value); + void setValueHost(float f); + float getValueHost(); + void publishValue(float value); void setValue(int value); int getValue(); String getValueDisplay(); + void sliderValueChanged (Slider* moved); - void buttonClicked (Button* buttonThatWasClicked); void comboBoxChanged (ComboBox* combo); void updateComponent(); }; diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index fa405ae..cdc5812 100755 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -133,6 +133,8 @@ void DexedAudioProcessor::processBlock(AudioSampleBuffer& buffer, MidiBuffer& mi channelData[i] = (double) f; } + //fx.process(channelData, numSamples); + // DX7 is a mono synth for (int channel = 1; channel < getNumInputChannels(); ++channel) { buffer.copyFrom(channel, 0, channelData, numSamples, 1); @@ -384,13 +386,14 @@ bool DexedAudioProcessor::hasEditor() const { return true; // (change this to false if you choose to not supply an editor) } + void DexedAudioProcessor::updateUI() { AudioProcessorEditor *editor = getActiveEditor(); if ( editor == NULL ) { TRACE("no editor found!?"); return; } - DexedAudioProcessorEditor *dexedEditor = (DexedAudioProcessorEditor *) editor; + DexedAudioProcessorEditor *dexedEditor = (DexedAudioProcessorEditor *) editor; dexedEditor->updateUI(); } diff --git a/Source/PluginProcessor.h b/Source/PluginProcessor.h index 9c1cb33..a40d6e2 100755 --- a/Source/PluginProcessor.h +++ b/Source/PluginProcessor.h @@ -28,6 +28,7 @@ #include "msfa/lfo.h" #include "msfa/synth.h" #include "PluginParam.h" +#include "PluginFx.h" struct ProcessorVoice { int midi_note; @@ -68,7 +69,12 @@ class DexedAudioProcessor : public AudioProcessor void updateProgramFromSysex(const uint8 *rawdata); /** - * This flag is usd in the audio thread to know if the voice has changed + * PlugFX + */ + PluginFx fx; + + /** + * This flag is used in the audio thread to know if the voice has changed * and needs to be updated. */ bool refreshVoice; @@ -93,13 +99,20 @@ public : OperatorCtrl opCtrl[6]; ScopedPointer pitchEgRate[4]; ScopedPointer pitchEgLevel[4]; + ScopedPointer pitchModSens; ScopedPointer algo; + ScopedPointer oscSync; + ScopedPointer feedback; ScopedPointer lfoRate; ScopedPointer lfoDelay; ScopedPointer lfoAmpDepth; ScopedPointer lfoPitchDepth; ScopedPointer lfoWaveform; ScopedPointer lfoSync; + ScopedPointer transpose; + + ScopedPointer fxCutoff; + ScopedPointer fxReso; int importSysex(const char *imported); void setDxValue(int offset, int v);