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:
parent
446486da6b
commit
c75d2a60dd
6 changed files with 116 additions and 8 deletions
|
@ -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
|
||||
|
||||
<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)"
|
||||
snapPixels="8" snapActive="1" snapShown="1" overlayOpacity="0.33"
|
||||
fixedSize="0" initialWidth="440" initialHeight="810">
|
||||
fixedSize="0" initialWidth="440" initialHeight="874">
|
||||
<BACKGROUND backgroundColour="ff000000"/>
|
||||
<GROUPCOMPONENT name="new group" id="d2c7c07bf2d78c30" memberName="groupComponent"
|
||||
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"
|
||||
layout="33" items="-0.0 -3.0 -1.5 -6.0" textWhenNonSelected=""
|
||||
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>
|
||||
|
||||
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]
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#define __JUCE_HEADER_450C07F5C14097B8__
|
||||
|
||||
//[Headers] -- You can add your own extra header files here --
|
||||
#include <array>
|
||||
#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<ScopedPointer<ImageButton>, Hiopl::CHANNELS> channels;
|
||||
//[/UserVariables]
|
||||
|
||||
//==============================================================================
|
||||
|
@ -162,6 +170,7 @@ private:
|
|||
ScopedPointer<Label> dbLabel4;
|
||||
ScopedPointer<ComboBox> keyscaleAttenuationComboBox2;
|
||||
ScopedPointer<ComboBox> keyscaleAttenuationComboBox;
|
||||
ScopedPointer<GroupComponent> groupComponent4;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
|
|
|
@ -122,6 +122,9 @@ JuceOplvstiAudioProcessor::JuceOplvstiAudioProcessor()
|
|||
active_notes[i] = NO_NOTE;
|
||||
}
|
||||
currentScaledBend = 1.0f;
|
||||
|
||||
for (int i = 1; i <= Hiopl::CHANNELS; ++i)
|
||||
available_channels.push_back(i);
|
||||
}
|
||||
|
||||
void JuceOplvstiAudioProcessor::initPrograms()
|
||||
|
@ -648,10 +651,22 @@ void JuceOplvstiAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuf
|
|||
//the beginning of the current buffer
|
||||
int n = midi_message.getNoteNumber();
|
||||
float noteHz = (float)MidiMessage::getMidiNoteInHertz(n);
|
||||
int ch = 1;
|
||||
while (ch <= Hiopl::CHANNELS && NO_NOTE != active_notes[ch]) {
|
||||
ch += 1;
|
||||
int ch;
|
||||
|
||||
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")) {
|
||||
case 0:
|
||||
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]) {
|
||||
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);
|
||||
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
|
||||
|
@ -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..
|
||||
AudioProcessor* JUCE_CALLTYPE createPluginFilter()
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#ifndef PLUGINPROCESSOR_H_INCLUDED
|
||||
#define PLUGINPROCESSOR_H_INCLUDED
|
||||
|
||||
#include <deque>
|
||||
#include "../JuceLibraryCode/JuceHeader.h"
|
||||
#include "hiopl.h"
|
||||
#include "FloatParameter.h"
|
||||
|
@ -53,6 +54,8 @@ public:
|
|||
void loadInstrumentFromFile(String filename);
|
||||
void setParametersByRegister(int register_base, int op, uint8 value);
|
||||
|
||||
int isChannelActive(int idx) const;
|
||||
|
||||
void updateGuiIfPresent();
|
||||
|
||||
const String getParameterName (int index);
|
||||
|
@ -90,6 +93,8 @@ private:
|
|||
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
|
||||
std::deque<int> available_channels; // most recently freed at end
|
||||
std::deque<int> used_channels; // most recently used at end
|
||||
float currentScaledBend;
|
||||
|
||||
//==============================================================================
|
||||
|
|
BIN
img/channeloff.png
Normal file
BIN
img/channeloff.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 414 B |
BIN
img/channelon.png
Normal file
BIN
img/channelon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 326 B |
Loading…
Reference in a new issue