From 446486da6b37c52a89ec991f92d4826ce14c8be8 Mon Sep 17 00:00:00 2001 From: Frits Talbot Date: Mon, 25 Aug 2014 23:09:39 +0200 Subject: [PATCH 1/3] Delete leaking params that cause JUCE assertions to fail. Fix bad MidiMessage construction (JUCE assertion failure). Workaround for Cubase 7.5 invoking setCurrentProgram after setStateInformation and overwriting saved state with hardcoded preset. Add rudimentary versioning to get-/setStateInformation. --- Source/PluginProcessor.cpp | 71 +++++++++++++++++++++++++++++++++----- Source/PluginProcessor.h | 1 + 2 files changed, 64 insertions(+), 8 deletions(-) diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index ea71510..91f5b7e 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -6,6 +6,7 @@ //============================================================================== JuceOplvstiAudioProcessor::JuceOplvstiAudioProcessor() + : i_program(-1) { // Initalize OPL velocity = false; @@ -380,6 +381,8 @@ void JuceOplvstiAudioProcessor::applyPitchBend() JuceOplvstiAudioProcessor::~JuceOplvstiAudioProcessor() { + for (int i = 0; i < params.size(); ++i) + delete params[i]; } //============================================================================== @@ -598,6 +601,9 @@ void JuceOplvstiAudioProcessor::updateGuiIfPresent() void JuceOplvstiAudioProcessor::setCurrentProgram (int index) { + if (i_program==index) + return; + i_program = index; std::vector &v_params = programs[getProgramName(index)]; for (unsigned int i = 0; i < params.size() && i < v_params.size(); i++) { @@ -634,7 +640,7 @@ void JuceOplvstiAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuf buffer.clear(0, 0, buffer.getNumSamples()); MidiBuffer::Iterator midi_buffer_iterator(midiMessages); - MidiMessage midi_message(0); + MidiMessage midi_message; int sample_number; while (midi_buffer_iterator.getNextEvent(midi_message,sample_number)) { if (midi_message.isNoteOn()) { @@ -678,9 +684,9 @@ void JuceOplvstiAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuf while (ch <= Hiopl::CHANNELS && n != active_notes[ch]) { ch += 1; } - Opl->KeyOff(ch); - active_notes[ch] = NO_NOTE; - } + Opl->KeyOff(ch); + active_notes[ch]=NO_NOTE; + } else if (midi_message.isPitchWheel()) { int bend = midi_message.getPitchWheelValue() - 0x2000; // range -8192 to 8191 // 1.05946309436 == (2^(1/1200))^100 == 1 semitone == 100 cents @@ -704,22 +710,71 @@ AudioProcessorEditor* JuceOplvstiAudioProcessor::createEditor() return gui; } +// FIXME: these two funcs and get-/setStateInformation should be replaced by a proper cross-platform archiving API, e.g. eos portable archive or boost text archive +template +void push(char *&cursor, T value) +{ + *reinterpret_cast(cursor) = value; + cursor+=sizeof(T); +} + +template +void pop(const char *&cursor, T &value) +{ + value=*reinterpret_cast(cursor); + cursor+=sizeof(T); +} + //============================================================================== void JuceOplvstiAudioProcessor::getStateInformation (MemoryBlock& destData) { - destData.ensureSize(sizeof(float) * getNumParameters()); + destData.ensureSize(sizeof(CURRENT_VERSION)+sizeof(i_program)+sizeof(float) * getNumParameters()); + + char *cursor = static_cast(destData.getData()); + + push(cursor, CURRENT_VERSION); + push(cursor, i_program); + for (int i = 0; i < getNumParameters(); i++) { float p = getParameter(i); - destData.copyFrom((void*)&p, i*sizeof(float), sizeof(float)); + push(cursor, p); } } void JuceOplvstiAudioProcessor::setStateInformation (const void* data, int sizeInBytes) { + if (sizeInBytes < sizeof(CURRENT_VERSION)) + return; + + const char *cursor = static_cast(data); + const char *end = cursor+sizeInBytes; + int version; + + pop(cursor, version); + + if (version == CURRENT_VERSION) + { + pop(cursor, i_program); + + const int parametersToLoad = std::min(static_cast(std::distance(cursor, end)) / sizeof(float), getNumParameters()); + + for (int i = 0; i < parametersToLoad; i++) + { + float p; + + pop(cursor, p); + setParameter(i, p); + } + + return; + } + float* fdata = (float*)data; - for (unsigned int i = 0; i < sizeInBytes / sizeof(float); i++) { + const int parametersToLoad = std::min(sizeInBytes / sizeof(float), getNumParameters()); + + for (int i = 0; i < parametersToLoad; i++) { setParameter(i, fdata[i]); - } + } } //============================================================================== diff --git a/Source/PluginProcessor.h b/Source/PluginProcessor.h index e3cac2f..c7a95b2 100644 --- a/Source/PluginProcessor.h +++ b/Source/PluginProcessor.h @@ -88,6 +88,7 @@ private: int i_program; bool velocity; static const int NO_NOTE=-1; + static const int CURRENT_VERSION = 0x09720100; // OPL2 v01.00 magic int active_notes[Hiopl::CHANNELS+1]; // keyed by channel float currentScaledBend; From c75d2a60ddddf4f4b244fcba35e1ec49f9a8a95f Mon Sep 17 00:00:00 2001 From: Frits Talbot Date: Mon, 25 Aug 2014 23:36:25 +0200 Subject: [PATCH 2/3] Fix channel reuse does not take into account release after keyOff and needlessly interrupts still audible notes. Now always selecting oldest free channel. Add channel visualization to GUI. --- Source/PluginGui.cpp | 64 +++++++++++++++++++++++++++++++++++-- Source/PluginGui.h | 9 ++++++ Source/PluginProcessor.cpp | 46 +++++++++++++++++++++++--- Source/PluginProcessor.h | 5 +++ img/channeloff.png | Bin 0 -> 414 bytes img/channelon.png | Bin 0 -> 326 bytes 6 files changed, 116 insertions(+), 8 deletions(-) create mode 100644 img/channeloff.png create mode 100644 img/channelon.png diff --git a/Source/PluginGui.cpp b/Source/PluginGui.cpp index d96caa0..d82703f 100644 --- a/Source/PluginGui.cpp +++ b/Source/PluginGui.cpp @@ -797,6 +797,11 @@ PluginGui::PluginGui (JuceOplvstiAudioProcessor* ownerFilter) keyscaleAttenuationComboBox->addItem ("-6.0", 4); keyscaleAttenuationComboBox->addListener (this); + addAndMakeVisible (groupComponent4 = new GroupComponent ("new group", + "Channels")); + groupComponent4->setTextLabelPosition (Justification::centredLeft); + groupComponent4->setColour (GroupComponent::outlineColourId, Colour (0xff007f00)); + groupComponent4->setColour (GroupComponent::textColourId, Colour (0xff007f00)); //[UserPreSize] frequencyComboBox->setColour (ComboBox::textColourId, Colour (COLOUR_MID)); @@ -908,13 +913,29 @@ PluginGui::PluginGui (JuceOplvstiAudioProcessor* ownerFilter) vibratoButton2->setColour(TextButton::buttonColourId, Colour(COLOUR_MID)); keyscaleEnvButton2->setColour(TextButton::buttonColourId, Colour(COLOUR_MID)); sustainButton2->setColour(TextButton::buttonColourId, Colour(COLOUR_MID)); + + for (int i = 0; i < channels.size(); ++i) + { + ImageButton *channel=new ImageButton("new button"); + + addAndMakeVisible(channel); + channel->addListener(this); + + channel->setImages(false, true, true, + ImageCache::getFromMemory(channeloff_png, channeloff_pngSize), 1.000f, Colour(0x00000000), + Image(), 1.000f, Colour(0x00000000), + ImageCache::getFromMemory(channelon_png, channelon_pngSize), 1.000f, Colour(0x00000000)); + + channels[i]=channel; + } //[/UserPreSize] - setSize (440, 810); + setSize (440, 874); //[Constructor] You can add your own custom stuff here.. processor = ownerFilter; + startTimer(1000/30); //[/Constructor] } @@ -998,6 +1019,7 @@ PluginGui::~PluginGui() dbLabel4 = nullptr; keyscaleAttenuationComboBox2 = nullptr; keyscaleAttenuationComboBox = nullptr; + groupComponent4 = nullptr; //[Destructor]. You can add your own custom destruction code here.. @@ -1093,7 +1115,10 @@ void PluginGui::resized() dbLabel4->setBounds (336, 440, 72, 16); keyscaleAttenuationComboBox2->setBounds (264, 432, 72, 24); keyscaleAttenuationComboBox->setBounds (264, 88, 72, 24); + groupComponent4->setBounds (16, 800, 408, 64); //[UserResized] Add your own custom resize handling here.. + for (int i = 0; i < channels.size(); ++i) + channels[i]->setBounds(32+24*i+4, 824+4, 16, 16); //[/UserResized] } @@ -1431,6 +1456,12 @@ void PluginGui::buttonClicked (Button* buttonThatWasClicked) processor->loadInstrumentFromFile(files[0]); } } + + void PluginGui::timerCallback() + { + for (int i = 0; i < Hiopl::CHANNELS; ++i) + channels[i]->setState(processor->isChannelActive(i+1) ? Button::buttonDown : Button::buttonNormal); + } //[/MiscUserCode] @@ -1444,10 +1475,10 @@ void PluginGui::buttonClicked (Button* buttonThatWasClicked) BEGIN_JUCER_METADATA + fixedSize="0" initialWidth="440" initialHeight="874"> + END_JUCER_METADATA @@ -1910,6 +1944,30 @@ static const unsigned char resource_PluginGui_logarithmic_saw_png[] = { 137,80,7 const char* PluginGui::logarithmic_saw_png = (const char*) resource_PluginGui_logarithmic_saw_png; const int PluginGui::logarithmic_saw_pngSize = 206; +// JUCER_RESOURCE: channeloff_png, 414, "../img/channeloff.png" +static const unsigned char resource_PluginGui_channeloff_png[] = { 137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,97,0,0,0,6,98,75,71,68,0,0,0,0,0,0,249,67,187,127, +0,0,0,9,112,72,89,115,0,0,11,19,0,0,11,19,1,0,154,156,24,0,0,0,7,116,73,77,69,7,222,8,25,20,6,56,156,246,144,159,0,0,0,29,105,84,88,116,67,111,109,109,101,110,116,0,0,0,0,0,67,114,101,97,116,101,100,32, +119,105,116,104,32,71,73,77,80,100,46,101,7,0,0,1,2,73,68,65,84,56,203,165,211,61,78,66,65,20,5,224,143,193,82,119,224,62,104,40,166,196,194,202,10,66,65,137,149,75,120,121,193,21,216,104,108,164,32,178, +128,183,1,94,66,67,66,172,172,140,27,176,178,192,202,70,139,55,26,2,138,2,39,153,102,230,220,159,185,231,220,154,85,100,14,113,138,46,154,233,118,138,17,10,185,183,101,122,88,9,110,99,129,11,20,104,164, +83,164,187,69,226,252,128,204,64,230,67,166,239,55,100,250,137,51,176,86,185,122,104,249,11,153,86,226,182,161,150,254,188,192,185,220,141,255,160,234,242,26,71,117,209,25,142,229,27,90,95,69,105,46,58, +193,107,72,211,30,218,30,67,116,67,146,106,178,67,130,9,154,193,158,8,201,36,113,135,216,136,105,72,14,235,237,144,160,135,81,248,118,92,182,133,10,21,183,129,162,174,244,46,122,194,157,104,166,244,252, +167,145,24,163,35,247,80,79,186,62,138,14,112,43,122,81,154,111,168,60,198,165,220,85,229,196,245,101,186,199,44,233,60,89,26,88,47,181,221,145,27,127,133,212,246,93,231,79,44,229,73,181,37,137,229,213, +0,0,0,0,73,69,78,68,174,66,96,130,0,0}; + +const char* PluginGui::channeloff_png = (const char*) resource_PluginGui_channeloff_png; +const int PluginGui::channeloff_pngSize = 414; + +// JUCER_RESOURCE: channelon_png, 326, "../img/channelon.png" +static const unsigned char resource_PluginGui_channelon_png[] = { 137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,97,0,0,0,6,98,75,71,68,0,0,0,0,0,0,249,67,187,127, +0,0,0,9,112,72,89,115,0,0,11,19,0,0,11,19,1,0,154,156,24,0,0,0,7,116,73,77,69,7,222,8,25,20,6,39,17,254,157,106,0,0,0,29,105,84,88,116,67,111,109,109,101,110,116,0,0,0,0,0,67,114,101,97,116,101,100,32, +119,105,116,104,32,71,73,77,80,100,46,101,7,0,0,0,170,73,68,65,84,56,203,173,147,177,13,194,48,16,69,159,143,148,176,81,20,101,129,84,84,68,217,5,89,176,0,204,16,193,0,30,0,69,202,8,169,88,131,12,64,145, +139,176,82,64,116,230,117,182,252,206,242,249,159,99,201,145,45,80,1,13,144,235,110,15,180,64,192,51,198,199,221,66,62,0,55,190,83,227,185,207,139,77,36,159,128,11,191,217,83,146,209,241,248,20,152,110, +94,35,207,20,148,60,233,24,156,190,249,133,141,157,104,195,172,84,162,221,182,210,72,244,85,22,114,33,17,209,144,88,233,69,19,102,165,21,32,36,20,8,162,217,174,13,114,141,103,156,146,216,49,80,146,1,197, +74,249,140,231,250,151,97,114,169,227,252,6,230,208,38,246,228,75,209,233,0,0,0,0,73,69,78,68,174,66,96,130,0,0}; + +const char* PluginGui::channelon_png = (const char*) resource_PluginGui_channelon_png; +const int PluginGui::channelon_pngSize = 326; + //[EndFile] You can add extra defines here... //[/EndFile] diff --git a/Source/PluginGui.h b/Source/PluginGui.h index 6d7cc25..f53c958 100644 --- a/Source/PluginGui.h +++ b/Source/PluginGui.h @@ -21,6 +21,7 @@ #define __JUCE_HEADER_450C07F5C14097B8__ //[Headers] -- You can add your own extra header files here -- +#include #include "JuceHeader.h" #include "PluginProcessor.h" //[/Headers] @@ -36,6 +37,7 @@ class PluginGui : public AudioProcessorEditor, public FileDragAndDropTarget, public DragAndDropContainer, + public Timer, public ComboBoxListener, public SliderListener, public ButtonListener @@ -53,6 +55,7 @@ public: void fileDragMove (const StringArray& files, int x, int y); void fileDragExit (const StringArray& files); void filesDropped (const StringArray& files, int x, int y); + void timerCallback(); //[/UserMethods] void paint (Graphics& g); @@ -78,12 +81,17 @@ public: static const int square_pngSize; static const char* logarithmic_saw_png; static const int logarithmic_saw_pngSize; + static const char* channeloff_png; + static const int channeloff_pngSize; + static const char* channelon_png; + static const int channelon_pngSize; private: //[UserVariables] -- You can add your own custom variables in this section. static const uint32 COLOUR_MID = 0xff007f00; JuceOplvstiAudioProcessor* processor; + std::array, Hiopl::CHANNELS> channels; //[/UserVariables] //============================================================================== @@ -162,6 +170,7 @@ private: ScopedPointer