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 3e1947e..4253be1 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/README.md b/README.md index 8fd81d3..2428309 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ Changelog * Modulation wheel support * Now using the [Obxd](https://obxd.wordpress.com) 4-pole lowpass filter implementation * Pitchbend range / step +* Output VU status Binary downloads ---------------- diff --git a/Source/DXComponents.cpp b/Source/DXComponents.cpp index 7602b35..e0676de 100644 --- a/Source/DXComponents.cpp +++ b/Source/DXComponents.cpp @@ -413,6 +413,10 @@ void PitchEnvDisplay::paint(Graphics &g) { } } +VuMeter::VuMeter() { + totalBlocks = 16; +} + void VuMeter::paint(Graphics &g) { // taken from the drawLevelMeter ; float width = getWidth(); @@ -420,22 +424,16 @@ void VuMeter::paint(Graphics &g) { g.setColour (Colours::black); g.fillRoundedRectangle (0.0f, 0.0f, (float) width, (float) height, 0); - /*g.setColour (Colours::black.withAlpha (0.2f)); - g.drawRoundedRectangle (1.0f, 1.0f, width - 2.0f, height - 2.0f, 3.0f, 1.0f);*/ - - const int totalBlocks = 16; + const int numBlocks = roundToInt (totalBlocks * v); const float h = (height - 6.0f) / (float) totalBlocks; for (int i = 0; i < totalBlocks; ++i) { - g.setColour (Colours::red); if (i >= numBlocks) g.setColour (Colours::red.withAlpha (0.2f)); else g.setColour (Colours::red); - //g.fillRoundedRectangle (3.0f + i * w + w * 0.1f, 3.0f, w * 0.8f, height - 6.0f, w * 0.4f); - - g.fillRoundedRectangle (3.0f, (height-3.0f) - (3.0f + i * h + h * 0.1f) , width - 6.0f, h * 0.8f, 0); + g.fillRoundedRectangle (3.0f, (height-4.0f) - (3.0f + i * h + h * 0.1f) , width - 6.0f, h * 0.8f, 0); } } diff --git a/Source/DXComponents.h b/Source/DXComponents.h index eda1895..63d29a2 100644 --- a/Source/DXComponents.h +++ b/Source/DXComponents.h @@ -50,8 +50,10 @@ public: class VuMeter: public Component { void paint(Graphics &g); - public : +public : + VuMeter(); float v; + int totalBlocks; }; class LcdDisplay : public Component { diff --git a/Source/GlobalEditor.cpp b/Source/GlobalEditor.cpp index 50cea36..65ac533 100644 --- a/Source/GlobalEditor.cpp +++ b/Source/GlobalEditor.cpp @@ -165,6 +165,15 @@ GlobalEditor::GlobalEditor () addAndMakeVisible (lcdDisplay = new LcdDisplay()); lcdDisplay->setName ("lcdDisplay"); + addAndMakeVisible (output = new Slider ("cutoff")); + output->setRange (0, 1, 0); + output->setSliderStyle (Slider::Rotary); + output->setTextBoxStyle (Slider::NoTextBox, false, 80, 20); + output->addListener (this); + + addAndMakeVisible (vuOutput = new VuMeter()); + vuOutput->setName ("vuOutput"); + //[UserPreSize] //[/UserPreSize] @@ -174,6 +183,7 @@ GlobalEditor::GlobalEditor () //[Constructor] You can add your own custom stuff here.. lcdDisplay->systemMsg << "*** DEXED FM synthesizer ***"; + vuOutput->totalBlocks = 6; //[/Constructor] } @@ -207,6 +217,8 @@ GlobalEditor::~GlobalEditor() feedback = nullptr; algo = nullptr; lcdDisplay = nullptr; + output = nullptr; + vuOutput = nullptr; //[Destructor]. You can add your own custom destruction code here.. @@ -219,6 +231,24 @@ void GlobalEditor::paint (Graphics& g) //[UserPrePaint] Add your own custom painting code here.. //[/UserPrePaint] + g.setColour (Colours::black); + g.setFont (Font (10.00f, Font::plain)); + g.drawText (TRANS("Volume"), + 27, 73, 45, 15, + Justification::centred, true); + + g.setColour (Colours::black); + g.setFont (Font (10.00f, Font::plain)); + g.drawText (TRANS("Cutoff"), + 75, 73, 45, 15, + Justification::centred, true); + + g.setColour (Colours::black); + g.setFont (Font (10.00f, Font::plain)); + g.drawText (TRANS("Resonance"), + 123, 73, 45, 15, + Justification::centred, true); + //[UserPaint] Add your own custom painting code here.. //[/UserPaint] @@ -231,8 +261,8 @@ void GlobalEditor::resized() lfoAmDepth->setBounds (672, 64, 24, 24); lfoPitchDepth->setBounds (648, 64, 24, 24); lfoDelay->setBounds (624, 64, 24, 24); - cutoff->setBounds (8, 40, 48, 48); - reso->setBounds (64, 40, 48, 48); + cutoff->setBounds (80, 40, 40, 40); + reso->setBounds (128, 40, 40, 40); pitchRate2->setBounds (752, 64, 32, 24); pitchRate3->setBounds (776, 64, 32, 24); pitchRate4->setBounds (800, 64, 32, 24); @@ -249,7 +279,9 @@ void GlobalEditor::resized() algoDisplay->setBounds (442, 8, 152, 74); feedback->setBounds (568, 32, 24, 24); algo->setBounds (568, 8, 24, 24); - lcdDisplay->setBounds (8, 0, 256, 32); + lcdDisplay->setBounds (8, 0, 232, 32); + output->setBounds (32, 40, 40, 40); + vuOutput->setBounds (8, 44, 16, 38); //[UserResized] Add your own custom resize handling here.. //[/UserResized] } @@ -372,6 +404,11 @@ void GlobalEditor::sliderValueChanged (Slider* sliderThatWasMoved) //[UserSliderCode_algo] -- add your slider handling code here.. //[/UserSliderCode_algo] } + else if (sliderThatWasMoved == output) + { + //[UserSliderCode_output] -- add your slider handling code here.. + //[/UserSliderCode_output] + } //[UsersliderValueChanged_Post] //[/UsersliderValueChanged_Post] @@ -403,6 +440,7 @@ void GlobalEditor::bind(DexedAudioProcessor *parent) { parent->pitchEgRate[3]->bind(pitchRate4); parent->fxCutoff->bind(cutoff); parent->fxReso->bind(reso); + parent->output->bind(output); algoDisplay->algo = &(parent->data[134]); pitchEnvDisplay->pvalues = &(parent->data[126]); processor = parent; @@ -429,6 +467,11 @@ void GlobalEditor::updatePitchPos(int pos) { pitchEnvDisplay->vPos = pos; pitchEnvDisplay->repaint(); } + +void GlobalEditor::updateVu(float f) { + vuOutput->v = f; + vuOutput->repaint(); +} //[/MiscUserCode] @@ -445,7 +488,14 @@ BEGIN_JUCER_METADATA parentClasses="public Component" constructorParams="" variableInitialisers="" snapPixels="8" snapActive="1" snapShown="1" overlayOpacity="0.330" fixedSize="1" initialWidth="855" initialHeight="90"> - + + + + + + + END_JUCER_METADATA diff --git a/Source/GlobalEditor.h b/Source/GlobalEditor.h index 597dd54..101f824 100644 --- a/Source/GlobalEditor.h +++ b/Source/GlobalEditor.h @@ -51,6 +51,7 @@ public: void setSystemMessage(String msg); void setParamMessage(String msg); void updatePitchPos(int pos); + void updateVu(float v); void updateDisplay(); //[/UserMethods] @@ -92,6 +93,8 @@ private: ScopedPointer feedback; ScopedPointer algo; ScopedPointer lcdDisplay; + ScopedPointer output; + ScopedPointer vuOutput; //============================================================================== diff --git a/Source/PluginData.cpp b/Source/PluginData.cpp index 841df50..023e7f6 100644 --- a/Source/PluginData.cpp +++ b/Source/PluginData.cpp @@ -230,6 +230,7 @@ void DexedAudioProcessor::getStateInformation(MemoryBlock& destData) { dexedState.setAttribute("cutoff", fx.uiCutoff); dexedState.setAttribute("reso", fx.uiReso); + dexedState.setAttribute("gain", fx.uiGain); dexedState.setAttribute("currentProgram", currentProgram); char sysex_blob[4104]; @@ -257,6 +258,7 @@ void DexedAudioProcessor::setStateInformation(const void* source, int sizeInByte fx.uiCutoff = root->getDoubleAttribute("cutoff"); fx.uiReso = root->getDoubleAttribute("reso"); + fx.uiGain = root->getDoubleAttribute("gain"); currentProgram = root->getIntAttribute("currentProgram"); XmlElement *dexedBlob = root->getChildByName("dexedBlob"); diff --git a/Source/PluginEditor.cpp b/Source/PluginEditor.cpp index 84fa972..7292760 100644 --- a/Source/PluginEditor.cpp +++ b/Source/PluginEditor.cpp @@ -278,6 +278,7 @@ void DexedAudioProcessorEditor::timerCallback() { operators[i].updateEnvPos(processor->voiceStatus.ampStep[5 - i]); } global.updatePitchPos(processor->voiceStatus.pitchStep); + global.updateVu(processor->vuSignal); } void DexedAudioProcessorEditor::updateUI() { diff --git a/Source/PluginFx.cpp b/Source/PluginFx.cpp index 4e6180f..149fe7c 100644 --- a/Source/PluginFx.cpp +++ b/Source/PluginFx.cpp @@ -75,9 +75,11 @@ void PluginFx::init(int sr) { uiCutoff = 1; uiReso = 0; + uiGain = 0.8; pCutoff = -1; pReso = -1; + pGain = -1; } inline float PluginFx::NR24(float sample,float g,float lpc) { @@ -95,66 +97,73 @@ inline float PluginFx::NR(float sample, float g) { void PluginFx::process(float *work, int sampleSize) { // don't apply the LPF if the cutoff is to maximum - if ( uiCutoff == 1 ) - return; - - if ( uiCutoff != pCutoff || uiReso != pReso ) { - rReso = (0.991-logsc(1-uiReso,0,0.991,40)); - R24 = 3.5 * rReso; + if ( uiCutoff != 1 ) { + if ( uiCutoff != pCutoff || uiReso != pReso ) { + rReso = (0.991-logsc(1-uiReso,0,0.991)); + R24 = 3.5 * rReso; - float cutoffNorm = logsc(uiCutoff,60,19000,30); - rCutoff = (float)tan(cutoffNorm * sampleRateInv * juce::float_Pi); + float cutoffNorm = logsc(uiCutoff,60,19000); + rCutoff = (float)tan(cutoffNorm * sampleRateInv * juce::float_Pi); + + pCutoff = uiCutoff; + pReso = uiReso; + + R = 1 - rReso; + } - pCutoff = uiCutoff; - pReso = uiReso; - - R = 1 - rReso; - } - - // THIS IS MY FAVORITE 4POLE OBXd filter - - // maybe smooth this value - float g = rCutoff; - float lpc = g / (1 + g); - - for(int i=0; i < sampleSize; i++ ) { - float s = work[i]; - s = s - 0.45*tptlpupw(c,s,15,sampleRateInv); - s = tptpc(d,s,bright); + // THIS IS MY FAVORITE 4POLE OBXd filter - float y0 = NR24(s,g,lpc); + // maybe smooth this value + float g = rCutoff; + float lpc = g / (1 + g); - //first low pass in cascade - double v = (y0 - s1) * lpc; - double res = v + s1; - s1 = res + v; + for(int i=0; i < sampleSize; i++ ) { + float s = work[i]; + s = s - 0.45*tptlpupw(c,s,15,sampleRateInv); + s = tptpc(d,s,bright); + + float y0 = NR24(s,g,lpc); + + //first low pass in cascade + double v = (y0 - s1) * lpc; + double res = v + s1; + s1 = res + v; + + //damping + s1 =atan(s1*rcor24)*rcor24Inv; + float y1= res; + float y2 = tptpc(s2,y1,g); + float y3 = tptpc(s3,y2,g); + float y4 = tptpc(s4,y3,g); + float mc; - //damping - s1 =atan(s1*rcor24)*rcor24Inv; - float y1= res; - float y2 = tptpc(s2,y1,g); - float y3 = tptpc(s3,y2,g); - float y4 = tptpc(s4,y3,g); - float mc; + switch(mmch) { + case 0: + mc = ((1 - mmt) * y4 + (mmt) * y3); + break; + case 1: + mc = ((1 - mmt) * y3 + (mmt) * y2); + break; + case 2: + mc = ((1 - mmt) * y2 + (mmt) * y1); + break; + case 3: + mc = y1; + break; + } - switch(mmch) { - case 0: - mc = ((1 - mmt) * y4 + (mmt) * y3); - break; - case 1: - mc = ((1 - mmt) * y3 + (mmt) * y2); - break; - case 2: - mc = ((1 - mmt) * y2 + (mmt) * y1); - break; - case 3: - mc = y1; - break; + //half volume comp + work[i] = mc * (1 + R24 * 0.45); } - - //half volume comp - work[i] = mc * (1 + R24 * 0.45); } + + if ( uiGain != pGain ) { + rGain = linsc(uiGain, 0, 1.25); + pGain = uiGain; + } + + for(int i=0; i < sampleSize; i++ ) + work[i] *= rGain; } /* diff --git a/Source/PluginFx.h b/Source/PluginFx.h index b100601..6f02ca4 100644 --- a/Source/PluginFx.h +++ b/Source/PluginFx.h @@ -39,11 +39,13 @@ class PluginFx { // preprocess values taken the UI float rCutoff; float rReso; + float rGain; // thread values; if these are different from the UI, // it needs to be recalculated. float pReso; float pCutoff; + float pGain; // I am still keeping the 2pole w/multimode filter inline float NR(float sample, float g); @@ -56,6 +58,7 @@ public: // this is set directly by the ui / parameter float uiCutoff; float uiReso; + float uiGain; void init(int sampleRate); void process(float *work, int sampleSize); diff --git a/Source/PluginParam.cpp b/Source/PluginParam.cpp index 74d6476..b777afa 100644 --- a/Source/PluginParam.cpp +++ b/Source/PluginParam.cpp @@ -215,6 +215,9 @@ void DexedAudioProcessor::initCtrl() { fxReso = new CtrlFloat("Resonance", &fx.uiReso); ctrl.add(fxReso); + output = new CtrlFloat("Output", &fx.uiGain); + ctrl.add(output); + algo = new CtrlDX("ALGORITHM", 32, 134, 1); ctrl.add(algo); diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index eb6fbdb..d8f6968 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -47,6 +47,7 @@ DexedAudioProcessor::DexedAudioProcessor() { currentNote = -1; workBlock = NULL; + vuSignal = 0; initCtrl(); setCurrentProgram(0); sendSysexChange = true; @@ -175,14 +176,24 @@ void DexedAudioProcessor::processBlock(AudioSampleBuffer& buffer, MidiBuffer& mi */ processSamples(block, workBlock); - for(int i = 0; i < block; i++ ) { + for(int i = 0; i < block; i++ ) channelData[i+samplePos] = workBlock[i]; - } samplePos += block; } fx.process(channelData, numSamples); + for(int i=0; i vuSignal) + vuSignal = s; + else if (vuSignal > 0.001f) + vuSignal *= decayFactor; + else + vuSignal = 0; + } // DX7 is a mono synth for (int channel = 1; channel < getNumInputChannels(); ++channel) { diff --git a/Source/PluginProcessor.h b/Source/PluginProcessor.h index 911c708..f26e7ef 100644 --- a/Source/PluginProcessor.h +++ b/Source/PluginProcessor.h @@ -114,6 +114,8 @@ public : bool forceRefreshUI; + float vuSignal; + Array ctrl; OperatorCtrl opCtrl[6]; @@ -133,6 +135,7 @@ public : ScopedPointer fxCutoff; ScopedPointer fxReso; + ScopedPointer output; int importSysex(const char *imported); void setDxValue(int offset, int v);