2
0
Fork 0

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.
This commit is contained in:
Frits Talbot 2014-08-25 23:36:25 +02:00
parent 446486da6b
commit c75d2a60dd
6 changed files with 116 additions and 8 deletions

View file

@ -797,6 +797,11 @@ PluginGui::PluginGui (JuceOplvstiAudioProcessor* ownerFilter)
keyscaleAttenuationComboBox->addItem ("-6.0", 4); keyscaleAttenuationComboBox->addItem ("-6.0", 4);
keyscaleAttenuationComboBox->addListener (this); 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] //[UserPreSize]
frequencyComboBox->setColour (ComboBox::textColourId, Colour (COLOUR_MID)); frequencyComboBox->setColour (ComboBox::textColourId, Colour (COLOUR_MID));
@ -908,13 +913,29 @@ PluginGui::PluginGui (JuceOplvstiAudioProcessor* ownerFilter)
vibratoButton2->setColour(TextButton::buttonColourId, Colour(COLOUR_MID)); vibratoButton2->setColour(TextButton::buttonColourId, Colour(COLOUR_MID));
keyscaleEnvButton2->setColour(TextButton::buttonColourId, Colour(COLOUR_MID)); keyscaleEnvButton2->setColour(TextButton::buttonColourId, Colour(COLOUR_MID));
sustainButton2->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] //[/UserPreSize]
setSize (440, 810); setSize (440, 874);
//[Constructor] You can add your own custom stuff here.. //[Constructor] You can add your own custom stuff here..
processor = ownerFilter; processor = ownerFilter;
startTimer(1000/30);
//[/Constructor] //[/Constructor]
} }
@ -998,6 +1019,7 @@ PluginGui::~PluginGui()
dbLabel4 = nullptr; dbLabel4 = nullptr;
keyscaleAttenuationComboBox2 = nullptr; keyscaleAttenuationComboBox2 = nullptr;
keyscaleAttenuationComboBox = nullptr; keyscaleAttenuationComboBox = nullptr;
groupComponent4 = nullptr;
//[Destructor]. You can add your own custom destruction code here.. //[Destructor]. You can add your own custom destruction code here..
@ -1093,7 +1115,10 @@ void PluginGui::resized()
dbLabel4->setBounds (336, 440, 72, 16); dbLabel4->setBounds (336, 440, 72, 16);
keyscaleAttenuationComboBox2->setBounds (264, 432, 72, 24); keyscaleAttenuationComboBox2->setBounds (264, 432, 72, 24);
keyscaleAttenuationComboBox->setBounds (264, 88, 72, 24); keyscaleAttenuationComboBox->setBounds (264, 88, 72, 24);
groupComponent4->setBounds (16, 800, 408, 64);
//[UserResized] Add your own custom resize handling here.. //[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] //[/UserResized]
} }
@ -1431,6 +1456,12 @@ void PluginGui::buttonClicked (Button* buttonThatWasClicked)
processor->loadInstrumentFromFile(files[0]); 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] //[/MiscUserCode]
@ -1444,10 +1475,10 @@ void PluginGui::buttonClicked (Button* buttonThatWasClicked)
BEGIN_JUCER_METADATA BEGIN_JUCER_METADATA
<JUCER_COMPONENT documentType="Component" className="PluginGui" componentName="" <JUCER_COMPONENT documentType="Component" className="PluginGui" componentName=""
parentClasses="public AudioProcessorEditor, public FileDragAndDropTarget, public DragAndDropContainer" parentClasses="public AudioProcessorEditor, public FileDragAndDropTarget, public DragAndDropContainer, public Timer"
constructorParams="JuceOplvstiAudioProcessor* ownerFilter" variableInitialisers=" AudioProcessorEditor (ownerFilter)" constructorParams="JuceOplvstiAudioProcessor* ownerFilter" variableInitialisers=" AudioProcessorEditor (ownerFilter)"
snapPixels="8" snapActive="1" snapShown="1" overlayOpacity="0.33" snapPixels="8" snapActive="1" snapShown="1" overlayOpacity="0.33"
fixedSize="0" initialWidth="440" initialHeight="810"> fixedSize="0" initialWidth="440" initialHeight="874">
<BACKGROUND backgroundColour="ff000000"/> <BACKGROUND backgroundColour="ff000000"/>
<GROUPCOMPONENT name="new group" id="d2c7c07bf2d78c30" memberName="groupComponent" <GROUPCOMPONENT name="new group" id="d2c7c07bf2d78c30" memberName="groupComponent"
virtualName="" explicitFocusOrder="0" pos="16 8 408 336" outlinecol="ff007f00" virtualName="" explicitFocusOrder="0" pos="16 8 408 336" outlinecol="ff007f00"
@ -1829,6 +1860,9 @@ BEGIN_JUCER_METADATA
virtualName="" explicitFocusOrder="0" pos="264 88 72 24" editable="0" virtualName="" explicitFocusOrder="0" pos="264 88 72 24" editable="0"
layout="33" items="-0.0&#10;-3.0&#10;-1.5&#10;-6.0" textWhenNonSelected="" layout="33" items="-0.0&#10;-3.0&#10;-1.5&#10;-6.0" textWhenNonSelected=""
textWhenNoItems="(no choices)"/> textWhenNoItems="(no choices)"/>
<GROUPCOMPONENT name="new group" id="52f9803abb342980" memberName="groupComponent4"
virtualName="" explicitFocusOrder="0" pos="16 800 408 64" outlinecol="ff007f00"
textcol="ff007f00" title="Channels" textpos="33"/>
</JUCER_COMPONENT> </JUCER_COMPONENT>
END_JUCER_METADATA 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 char* PluginGui::logarithmic_saw_png = (const char*) resource_PluginGui_logarithmic_saw_png;
const int PluginGui::logarithmic_saw_pngSize = 206; 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] You can add extra defines here...
//[/EndFile] //[/EndFile]

View file

@ -21,6 +21,7 @@
#define __JUCE_HEADER_450C07F5C14097B8__ #define __JUCE_HEADER_450C07F5C14097B8__
//[Headers] -- You can add your own extra header files here -- //[Headers] -- You can add your own extra header files here --
#include <array>
#include "JuceHeader.h" #include "JuceHeader.h"
#include "PluginProcessor.h" #include "PluginProcessor.h"
//[/Headers] //[/Headers]
@ -36,6 +37,7 @@
class PluginGui : public AudioProcessorEditor, class PluginGui : public AudioProcessorEditor,
public FileDragAndDropTarget, public FileDragAndDropTarget,
public DragAndDropContainer, public DragAndDropContainer,
public Timer,
public ComboBoxListener, public ComboBoxListener,
public SliderListener, public SliderListener,
public ButtonListener public ButtonListener
@ -53,6 +55,7 @@ public:
void fileDragMove (const StringArray& files, int x, int y); void fileDragMove (const StringArray& files, int x, int y);
void fileDragExit (const StringArray& files); void fileDragExit (const StringArray& files);
void filesDropped (const StringArray& files, int x, int y); void filesDropped (const StringArray& files, int x, int y);
void timerCallback();
//[/UserMethods] //[/UserMethods]
void paint (Graphics& g); void paint (Graphics& g);
@ -78,12 +81,17 @@ public:
static const int square_pngSize; static const int square_pngSize;
static const char* logarithmic_saw_png; static const char* logarithmic_saw_png;
static const int logarithmic_saw_pngSize; 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: private:
//[UserVariables] -- You can add your own custom variables in this section. //[UserVariables] -- You can add your own custom variables in this section.
static const uint32 COLOUR_MID = 0xff007f00; static const uint32 COLOUR_MID = 0xff007f00;
JuceOplvstiAudioProcessor* processor; JuceOplvstiAudioProcessor* processor;
std::array<ScopedPointer<ImageButton>, Hiopl::CHANNELS> channels;
//[/UserVariables] //[/UserVariables]
//============================================================================== //==============================================================================
@ -162,6 +170,7 @@ private:
ScopedPointer<Label> dbLabel4; ScopedPointer<Label> dbLabel4;
ScopedPointer<ComboBox> keyscaleAttenuationComboBox2; ScopedPointer<ComboBox> keyscaleAttenuationComboBox2;
ScopedPointer<ComboBox> keyscaleAttenuationComboBox; ScopedPointer<ComboBox> keyscaleAttenuationComboBox;
ScopedPointer<GroupComponent> groupComponent4;
//============================================================================== //==============================================================================

View file

@ -122,6 +122,9 @@ JuceOplvstiAudioProcessor::JuceOplvstiAudioProcessor()
active_notes[i] = NO_NOTE; active_notes[i] = NO_NOTE;
} }
currentScaledBend = 1.0f; currentScaledBend = 1.0f;
for (int i = 1; i <= Hiopl::CHANNELS; ++i)
available_channels.push_back(i);
} }
void JuceOplvstiAudioProcessor::initPrograms() void JuceOplvstiAudioProcessor::initPrograms()
@ -381,7 +384,7 @@ void JuceOplvstiAudioProcessor::applyPitchBend()
JuceOplvstiAudioProcessor::~JuceOplvstiAudioProcessor() JuceOplvstiAudioProcessor::~JuceOplvstiAudioProcessor()
{ {
for (int i = 0; i < params.size(); ++i) for (int i=0; i < params.size(); ++i)
delete params[i]; delete params[i];
} }
@ -648,10 +651,22 @@ void JuceOplvstiAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuf
//the beginning of the current buffer //the beginning of the current buffer
int n = midi_message.getNoteNumber(); int n = midi_message.getNoteNumber();
float noteHz = (float)MidiMessage::getMidiNoteInHertz(n); float noteHz = (float)MidiMessage::getMidiNoteInHertz(n);
int ch = 1; int ch;
while (ch <= Hiopl::CHANNELS && NO_NOTE != active_notes[ch]) {
ch += 1; if (!available_channels.empty())
{
ch = available_channels.front();
available_channels.pop_front();
} }
else
{
ch = used_channels.back(); // steal earliest/longest running active channel if out of free channels
used_channels.pop_back();
Opl->KeyOff(ch);
}
used_channels.push_front(ch);
switch (getEnumParameter("Carrier Velocity Sensitivity")) { switch (getEnumParameter("Carrier Velocity Sensitivity")) {
case 0: case 0:
Opl->SetAttenuation(ch, 2, getEnumParameter("Carrier Attenuation")); Opl->SetAttenuation(ch, 2, getEnumParameter("Carrier Attenuation"));
@ -684,9 +699,23 @@ void JuceOplvstiAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuf
while (ch <= Hiopl::CHANNELS && n != active_notes[ch]) { while (ch <= Hiopl::CHANNELS && n != active_notes[ch]) {
ch += 1; ch += 1;
} }
if (ch <= Hiopl::CHANNELS)
{
for (auto i = used_channels.begin(); i != used_channels.end(); ++i)
{
if (*i == ch)
{
used_channels.erase(i);
available_channels.push_back(ch);
break;
}
}
Opl->KeyOff(ch); Opl->KeyOff(ch);
active_notes[ch]=NO_NOTE; active_notes[ch]=NO_NOTE;
} }
}
else if (midi_message.isPitchWheel()) { else if (midi_message.isPitchWheel()) {
int bend = midi_message.getPitchWheelValue() - 0x2000; // range -8192 to 8191 int bend = midi_message.getPitchWheelValue() - 0x2000; // range -8192 to 8191
// 1.05946309436 == (2^(1/1200))^100 == 1 semitone == 100 cents // 1.05946309436 == (2^(1/1200))^100 == 1 semitone == 100 cents
@ -714,7 +743,7 @@ AudioProcessorEditor* JuceOplvstiAudioProcessor::createEditor()
template<class T> template<class T>
void push(char *&cursor, T value) void push(char *&cursor, T value)
{ {
*reinterpret_cast<T *>(cursor) = value; *reinterpret_cast<T *>(cursor)=value;
cursor+=sizeof(T); cursor+=sizeof(T);
} }
@ -777,6 +806,13 @@ void JuceOplvstiAudioProcessor::setStateInformation (const void* data, int sizeI
} }
} }
// @param idx 1-based channel index
// @note since this is just reading off pod, "safe" to access without a mutex by other threads such as GUI
int JuceOplvstiAudioProcessor::isChannelActive(int idx) const
{
return active_notes[idx] != NO_NOTE;
}
//============================================================================== //==============================================================================
// This creates new instances of the plugin.. // This creates new instances of the plugin..
AudioProcessor* JUCE_CALLTYPE createPluginFilter() AudioProcessor* JUCE_CALLTYPE createPluginFilter()

View file

@ -10,6 +10,7 @@
#ifndef PLUGINPROCESSOR_H_INCLUDED #ifndef PLUGINPROCESSOR_H_INCLUDED
#define PLUGINPROCESSOR_H_INCLUDED #define PLUGINPROCESSOR_H_INCLUDED
#include <deque>
#include "../JuceLibraryCode/JuceHeader.h" #include "../JuceLibraryCode/JuceHeader.h"
#include "hiopl.h" #include "hiopl.h"
#include "FloatParameter.h" #include "FloatParameter.h"
@ -53,6 +54,8 @@ public:
void loadInstrumentFromFile(String filename); void loadInstrumentFromFile(String filename);
void setParametersByRegister(int register_base, int op, uint8 value); void setParametersByRegister(int register_base, int op, uint8 value);
int isChannelActive(int idx) const;
void updateGuiIfPresent(); void updateGuiIfPresent();
const String getParameterName (int index); const String getParameterName (int index);
@ -90,6 +93,8 @@ private:
static const int NO_NOTE=-1; static const int NO_NOTE=-1;
static const int CURRENT_VERSION = 0x09720100; // OPL2 v01.00 magic static const int CURRENT_VERSION = 0x09720100; // OPL2 v01.00 magic
int active_notes[Hiopl::CHANNELS+1]; // keyed by channel int active_notes[Hiopl::CHANNELS+1]; // keyed by channel
std::deque<int> available_channels; // most recently freed at end
std::deque<int> used_channels; // most recently used at end
float currentScaledBend; float currentScaledBend;
//============================================================================== //==============================================================================

BIN
img/channeloff.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 414 B

BIN
img/channelon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 B