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 4253be1..750d638 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 2428309..ebe6d29 100644 --- a/README.md +++ b/README.md @@ -24,17 +24,18 @@ Features Changelog --------- -#### Version 0.4.0 (current sprint) +#### Version 0.4.0 * Modulation wheel support * Now using the [Obxd](https://obxd.wordpress.com) 4-pole lowpass filter implementation * Pitchbend range / step -* Output VU status +* Output/Volume VU status Binary downloads ---------------- Dexed is not a finished product but it is stable enough to be used in a DAW environment: in normal operation it shouldn't crash and the VST state saving works. +* Version 0.4.0 [vst win32](http://le-son666.com/software/dexed/dexed-0.4.0-win32.zip) - [vst win64](http://le-son666.com/software/dexed/dexed-0.4.0-win64.zip) - [vst os x](http://le-son666.com/software/dexed/dexed-0.4.0-osx.vst.zip) * Version 0.3.0 [vst win32](http://le-son666.com/software/dexed/dexed-0.3.0-win32.zip) - [vst win64](http://le-son666.com/software/dexed/dexed-0.3.0-win64.zip) - [vst os x](http://le-son666.com/software/dexed/dexed-0.3.0-osx.vst.zip) Using as a DX7 editor @@ -57,6 +58,12 @@ digital circuit bending feel. Saving those corrupt/random sysex data will regenerate a 32 bulk program sysex dump with a valid checksum for your DX7 keyboard. I'm in now way responsible if this breaks your DX7 keyboard. +FAQ (possibly) +-------------- +* Some programs can generate distortion : This is because the voice summing still needs some tuning. You can simply lower the volume on those programs. +* Some sysex seems to be corrupted : Even if the sysex checksum doesn't match, Dexed will try to load it (this is a kind of randomize feature). Right now Dexed supports only original DX7 sysex, other DX family sysex (like the DX21) is considered as random data. +* Dexed doesn't receive/send parameter data from/to my DX7 : Most DX7 parameter change are done via sysex and very few VST host actually implements sysex. I'm planning to do a standalone executable to handle this issue. + Credits & thanks ---------------- * DX Synth engine : Raph Levien and the [msfa](https://code.google.com/p/music-synthesizer-for-android) team @@ -70,6 +77,7 @@ TODO - Dexed * Implement a better DX look and feel (amp, pitch, algo) * Various code cleanup * Standalone executable (for full support of the sysex editor) +* Midi sample resolution on DAW block size TODO - msfa ----------- diff --git a/Source/DXComponents.cpp b/Source/DXComponents.cpp index e0676de..9dcc162 100644 --- a/Source/DXComponents.cpp +++ b/Source/DXComponents.cpp @@ -437,6 +437,25 @@ void VuMeter::paint(Graphics &g) { } } +LcdDisplay::LcdDisplay() { + systemMsg << "*** DEXED FM synthesizer ***"; +} + +void LcdDisplay::timerCallback() { + systemMsg = "*** DEXED FM synthesizer ***"; + repaint(); +} + +void LcdDisplay::setSystemMsg(String msg) { + systemMsg = msg; + triggerAsyncUpdate(); +} + +void LcdDisplay::handleAsyncUpdate() { + repaint(); + startTimer(5000); +} + void LcdDisplay::paint(Graphics &g) { g.setColour(Colours::black.withAlpha(0.4f)); g.fillRoundedRectangle (0.0f, 0.0f, (float) getWidth(), (float) getHeight(), 1.0f); diff --git a/Source/DXComponents.h b/Source/DXComponents.h index 63d29a2..e4a22da 100644 --- a/Source/DXComponents.h +++ b/Source/DXComponents.h @@ -56,8 +56,12 @@ public : int totalBlocks; }; -class LcdDisplay : public Component { +class LcdDisplay : public Component, public Timer, public AsyncUpdater { + void timerCallback(); + void handleAsyncUpdate(); public: + LcdDisplay(); + void setSystemMsg(String msg); String systemMsg; String paramMsg; void paint(Graphics &g); diff --git a/Source/GlobalEditor.cpp b/Source/GlobalEditor.cpp index 65ac533..104903a 100644 --- a/Source/GlobalEditor.cpp +++ b/Source/GlobalEditor.cpp @@ -182,7 +182,6 @@ GlobalEditor::GlobalEditor () //[Constructor] You can add your own custom stuff here.. - lcdDisplay->systemMsg << "*** DEXED FM synthesizer ***"; vuOutput->totalBlocks = 6; //[/Constructor] @@ -448,8 +447,7 @@ void GlobalEditor::bind(DexedAudioProcessor *parent) { } void GlobalEditor::setSystemMessage(String msg) { - lcdDisplay->systemMsg = msg; - repaint(); + lcdDisplay->setSystemMsg(msg); } void GlobalEditor::setParamMessage(String msg) { diff --git a/Source/ParamDialog.cpp b/Source/ParamDialog.cpp index c9189ca..5ce2500 100644 --- a/Source/ParamDialog.cpp +++ b/Source/ParamDialog.cpp @@ -49,6 +49,7 @@ ParamDialog::ParamDialog () //[Constructor] You can add your own custom stuff here.. + pitchRange->setEnabled(pitchStep->getValue() == 0); //[/Constructor] } @@ -110,6 +111,7 @@ void ParamDialog::sliderValueChanged (Slider* sliderThatWasMoved) else if (sliderThatWasMoved == pitchStep) { //[UserSliderCode_pitchStep] -- add your slider handling code here.. + pitchRange->setEnabled(pitchStep->getValue() == 0); //[/UserSliderCode_pitchStep] } diff --git a/Source/PluginEditor.cpp b/Source/PluginEditor.cpp index 7292760..380e336 100644 --- a/Source/PluginEditor.cpp +++ b/Source/PluginEditor.cpp @@ -175,7 +175,6 @@ void DexedAudioProcessorEditor::buttonClicked(Button *buttonThatWasClicked) { return; } - if (buttonThatWasClicked == loadButton) { FileChooser fc ("Import original DX sysex...", File::nonexistent, "*.syx;*.SYX;*.*", 1); @@ -191,7 +190,9 @@ void DexedAudioProcessorEditor::buttonClicked(Button *buttonThatWasClicked) { } fp_in.read((char *)syx_data, 4104); fp_in.close(); - processor->importSysex((char *) &syx_data); + if ( processor->importSysex((char *) &syx_data) ) { + global.setSystemMessage(String("Unkown sysex format !?")); + } processor->setCurrentProgram(0); rebuildProgramCombobox(); programs.setSelectedId(processor->getCurrentProgram()+1, NotificationType::dontSendNotification); @@ -248,9 +249,6 @@ void DexedAudioProcessorEditor::buttonClicked(Button *buttonThatWasClicked) { } if (buttonThatWasClicked == aboutButton) { - /*AlertWindow::showMessageBoxAsync(AlertWindow::NoIcon, "DEXED - DX Emulator 0.4", "https://github.com/asb2m10/dexed\n" - "(c) 2013-2014 Pascal Gauthier\nUnder the GPL v2\n\n" - "Based on Music Synthesizer for Android\nhttps://code.google.com/p/music-synthesizer-for-android");*/ AboutBox about(this); about.runModalLoop(); return; diff --git a/Source/PluginFx.cpp b/Source/PluginFx.cpp index 149fe7c..4d9690d 100644 --- a/Source/PluginFx.cpp +++ b/Source/PluginFx.cpp @@ -75,11 +75,10 @@ void PluginFx::init(int sr) { uiCutoff = 1; uiReso = 0; - uiGain = 0.8; + uiGain = 1; pCutoff = -1; pReso = -1; - pGain = -1; } inline float PluginFx::NR24(float sample,float g,float lpc) { @@ -96,74 +95,72 @@ inline float PluginFx::NR(float sample, float g) { } void PluginFx::process(float *work, int sampleSize) { + if ( uiGain != 1 ) { + for(int i=0; i < sampleSize; i++ ) + work[i] *= uiGain; + } + // don't apply the LPF if the cutoff is to maximum - if ( uiCutoff != 1 ) { - if ( uiCutoff != pCutoff || uiReso != pReso ) { - rReso = (0.991-logsc(1-uiReso,0,0.991)); - R24 = 3.5 * rReso; + if ( uiCutoff == 1 ) + return; + + if ( uiCutoff != pCutoff || uiReso != pReso ) { + rReso = (0.991-logsc(1-uiReso,0,0.991)); + R24 = 3.5 * rReso; - float cutoffNorm = logsc(uiCutoff,60,19000); - rCutoff = (float)tan(cutoffNorm * sampleRateInv * juce::float_Pi); - - pCutoff = uiCutoff; - pReso = uiReso; + float cutoffNorm = logsc(uiCutoff,60,19000); + rCutoff = (float)tan(cutoffNorm * sampleRateInv * juce::float_Pi); - R = 1 - rReso; - } + pCutoff = uiCutoff; + pReso = uiReso; - // THIS IS MY FAVORITE 4POLE OBXd filter + R = 1 - rReso; + } - // maybe smooth this value - float g = rCutoff; - float lpc = g / (1 + g); + // THIS IS MY FAVORITE 4POLE OBXd filter - 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; + // 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); - 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; - } + float y0 = NR24(s,g,lpc); - //half volume comp - work[i] = mc * (1 + R24 * 0.45); - } - } + //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; - if ( uiGain != pGain ) { - rGain = linsc(uiGain, 0, 1.25); - pGain = uiGain; + 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); } - - for(int i=0; i < sampleSize; i++ ) - work[i] *= rGain; } /* diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index d8f6968..4cef115 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -63,7 +63,6 @@ DexedAudioProcessor::DexedAudioProcessor() { controllers.values_[kControllerPitchRange] = 3; controllers.values_[kControllerPitchStep] = 0; loadPreference(); - } DexedAudioProcessor::~DexedAudioProcessor() { @@ -87,7 +86,6 @@ void DexedAudioProcessor::prepareToPlay(double sampleRate, int samplesPerBlock) currentNote = 0; controllers.values_[kControllerPitch] = 0x2000; controllers.values_[kControllerModWheel] = 0; - sustain = false; extra_buf_size = 0; diff --git a/Source/msfa/dx7note.cc b/Source/msfa/dx7note.cc index 1926193..796f572 100755 --- a/Source/msfa/dx7note.cc +++ b/Source/msfa/dx7note.cc @@ -26,6 +26,14 @@ using namespace std; +void dexed_trace(const char *source, const char *fmt, ...); + +#ifdef _MSC_VER +#define TRACE(fmt, ...) dexed_trace(__FUNCTION__,fmt,##__VA_ARGS__) +#else +#define TRACE(fmt, ...) dexed_trace(__PRETTY_FUNCTION__,fmt,##__VA_ARGS__) +#endif + 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; @@ -191,16 +199,18 @@ void Dx7Note::compute(int32_t *buf, int32_t lfo_val, int32_t lfo_delay, pitchmod += (((int64_t)pmd) * (int64_t)senslfo) >> 39; int pitchbend = ctrls->values_[kControllerPitch]; - int32_t pb; + int32_t pb = (pitchbend - 0x2000); - if ( ctrls->values_[kControllerPitchStep] == 0 ) { - pb = ((float)((pitchbend - 0x2000) << 11)) * ((float)ctrls->values_[kControllerPitchRange]) / 12.0; - } else { - int stp = 12 / ctrls->values_[kControllerPitchStep]; - pb = (pitchbend - 0x2000) / stp; - pb = (pb * stp) << 11; + if ( pb != 0 ) { + if ( ctrls->values_[kControllerPitchStep] == 0 ) { + pb = ((float)(pb << 11)) * ((float)ctrls->values_[kControllerPitchRange]) / 12.0; + } else { + int stp = 12 / ctrls->values_[kControllerPitchStep]; + pb = pb * stp / 8191; + pb = (pb * (8191/stp)) << 11; + } } - + pitchmod += pb; for (int op = 0; op < 6; op++) { params_[op].gain[0] = params_[op].gain[1];