Merge pull request #17 from trylle/master
Issue #15 workaround and miscellaneous improvements
This commit is contained in:
commit
f4336c5e1e
6 changed files with 186 additions and 15 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;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
|
|
|
@ -4,8 +4,11 @@
|
|||
#include "IntFloatParameter.h"
|
||||
#include "SbiLoader.h"
|
||||
|
||||
const char *JuceOplvstiAudioProcessor::PROGRAM_INDEX = "Program Index";
|
||||
|
||||
//==============================================================================
|
||||
JuceOplvstiAudioProcessor::JuceOplvstiAudioProcessor()
|
||||
: i_program(-1)
|
||||
{
|
||||
// Initalize OPL
|
||||
velocity = false;
|
||||
|
@ -121,6 +124,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()
|
||||
|
@ -380,6 +386,8 @@ void JuceOplvstiAudioProcessor::applyPitchBend()
|
|||
|
||||
JuceOplvstiAudioProcessor::~JuceOplvstiAudioProcessor()
|
||||
{
|
||||
for (int i=0; i < params.size(); ++i)
|
||||
delete params[i];
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
@ -598,6 +606,9 @@ void JuceOplvstiAudioProcessor::updateGuiIfPresent()
|
|||
|
||||
void JuceOplvstiAudioProcessor::setCurrentProgram (int index)
|
||||
{
|
||||
if (i_program==index)
|
||||
return;
|
||||
|
||||
i_program = index;
|
||||
std::vector<float> &v_params = programs[getProgramName(index)];
|
||||
for (unsigned int i = 0; i < params.size() && i < v_params.size(); i++) {
|
||||
|
@ -634,7 +645,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()) {
|
||||
|
@ -642,10 +653,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"));
|
||||
|
@ -678,8 +701,22 @@ void JuceOplvstiAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuf
|
|||
while (ch <= Hiopl::CHANNELS && n != active_notes[ch]) {
|
||||
ch += 1;
|
||||
}
|
||||
Opl->KeyOff(ch);
|
||||
active_notes[ch] = NO_NOTE;
|
||||
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
|
||||
|
@ -705,21 +742,82 @@ AudioProcessorEditor* JuceOplvstiAudioProcessor::createEditor()
|
|||
}
|
||||
|
||||
//==============================================================================
|
||||
void JuceOplvstiAudioProcessor::getStateInformation (MemoryBlock& destData)
|
||||
|
||||
// JUCE requires that JSON names are valid Identifiers (restricted character set) even though the JSON standard allows any valid string.
|
||||
// Per http://www.juce.com/forum/topic/problem-spaces-json-identifier jules allowed Strings to bypass the assertion check
|
||||
// but a regression occured in 4317f60 re-enabling this check, so we need to sanitize the names.
|
||||
// Technically, the code still works without this, but it will trigger a gargantuan number of assertions hindering effective debugging.
|
||||
Identifier stringToIdentifier(const String &s)
|
||||
{
|
||||
destData.ensureSize(sizeof(float) * getNumParameters());
|
||||
return s.replaceCharacters(" ", "_");
|
||||
}
|
||||
|
||||
void JuceOplvstiAudioProcessor::getStateInformation(MemoryBlock& destData)
|
||||
{
|
||||
ReferenceCountedObjectPtr<DynamicObject> v(new DynamicObject);
|
||||
|
||||
v->setProperty(stringToIdentifier(PROGRAM_INDEX), i_program);
|
||||
|
||||
for (int i = 0; i < getNumParameters(); i++) {
|
||||
float p = getParameter(i);
|
||||
destData.copyFrom((void*)&p, i*sizeof(float), sizeof(float));
|
||||
double p = getParameter(i);
|
||||
|
||||
v->setProperty(stringToIdentifier(getParameterName(i)), p);
|
||||
}
|
||||
|
||||
String s = JSON::toString(v.get());
|
||||
|
||||
destData.setSize(s.length());
|
||||
destData.copyFrom(s.getCharPointer(), 0, destData.getSize());
|
||||
}
|
||||
|
||||
void JuceOplvstiAudioProcessor::setStateInformation (const void* data, int sizeInBytes)
|
||||
{
|
||||
if (sizeInBytes < 1)
|
||||
return;
|
||||
|
||||
const char *first = static_cast<const char *>(data);
|
||||
const char *last = first + sizeInBytes - 1;
|
||||
|
||||
// simple check for JSON data - assume always object
|
||||
if (*first=='{' && *last=='}')
|
||||
{
|
||||
// json format
|
||||
String s(first, sizeInBytes);
|
||||
var v = JSON::fromString(s);
|
||||
|
||||
{
|
||||
var program = v[stringToIdentifier(PROGRAM_INDEX)];
|
||||
|
||||
if (!program.isVoid())
|
||||
i_program = program;
|
||||
}
|
||||
|
||||
for (int i=0; i<getNumParameters(); ++i)
|
||||
{
|
||||
var param = v[stringToIdentifier(getParameterName(i))];
|
||||
|
||||
if (!param.isVoid())
|
||||
setParameter(i, param);
|
||||
}
|
||||
|
||||
updateGuiIfPresent();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
float* fdata = (float*)data;
|
||||
for (unsigned int i = 0; i < sizeInBytes / sizeof(float); i++) {
|
||||
const int parametersToLoad = std::min<int>(sizeInBytes / sizeof(float), getNumParameters());
|
||||
|
||||
for (int i = 0; i < parametersToLoad; i++) {
|
||||
setParameter(i, fdata[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// @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;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
|
|
@ -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);
|
||||
|
@ -88,7 +91,10 @@ private:
|
|||
int i_program;
|
||||
bool velocity;
|
||||
static const int NO_NOTE=-1;
|
||||
static const char *PROGRAM_INDEX;
|
||||
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