2
0
Fork 0

Merge pull request #17 from trylle/master

Issue #15 workaround and miscellaneous improvements
This commit is contained in:
Bruce Sutherland 2014-08-30 11:32:33 +09:00
commit f4336c5e1e
6 changed files with 186 additions and 15 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

@ -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,9 +701,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
@ -705,23 +742,84 @@ AudioProcessorEditor* JuceOplvstiAudioProcessor::createEditor()
}
//==============================================================================
// 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)
{
return s.replaceCharacters(" ", "_");
}
void JuceOplvstiAudioProcessor::getStateInformation(MemoryBlock& destData)
{
destData.ensureSize(sizeof(float) * getNumParameters());
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;
}
//==============================================================================
// 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);
@ -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

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