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->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&#10;-3.0&#10;-1.5&#10;-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]

View file

@ -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;
//==============================================================================

View file

@ -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()
@ -381,7 +384,7 @@ void JuceOplvstiAudioProcessor::applyPitchBend()
JuceOplvstiAudioProcessor::~JuceOplvstiAudioProcessor()
{
for (int i = 0; i < params.size(); ++i)
for (int i=0; i < params.size(); ++i)
delete params[i];
}
@ -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
@ -714,7 +743,7 @@ AudioProcessorEditor* JuceOplvstiAudioProcessor::createEditor()
template<class T>
void push(char *&cursor, T value)
{
*reinterpret_cast<T *>(cursor) = value;
*reinterpret_cast<T *>(cursor)=value;
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..
AudioProcessor* JUCE_CALLTYPE createPluginFilter()

View file

@ -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

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