83 changes: 69 additions & 14 deletions ports/PdPulp/Source/PluginProcessor.cpp
Expand Up @@ -13,7 +13,7 @@
bool PureDataAudioProcessor::otherInstanceAlreadyRunning;

//==============================================================================
PureDataAudioProcessor::PureDataAudioProcessor()
PureDataAudioProcessor::PureDataAudioProcessor() : receiver(&parameterList, this)
{
for (int i=0; i<10; i++) {
FloatParameter* p = new FloatParameter (0.5, ("Param" + (String) (i+1)).toStdString());
Expand All @@ -25,6 +25,7 @@ PureDataAudioProcessor::PureDataAudioProcessor()
isInstanceLocked = true;
}
PureDataAudioProcessor::otherInstanceAlreadyRunning = true;
cachedSampleRate = getSampleRate();
}

PureDataAudioProcessor::~PureDataAudioProcessor()
Expand Down Expand Up @@ -153,10 +154,24 @@ void PureDataAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer
return;
}

// In case we have more outputs than inputs, this code clears any output channels that didn't contain input data, (because these aren't guaranteed to be empty - they may contain garbage).
// I've added this to avoid people getting screaming feedback when they first compile the plugin, but obviously you don't need to this code if your algorithm already fills all the output channels.
for (int i = getTotalNumInputChannels(); i < getTotalNumOutputChannels(); ++i)
pd->receiveMessages();

// Send host info messages
{
AudioPlayHead::CurrentPositionInfo info;
getPlayHead()->getCurrentPosition(info);
if (positionInfo.isPlaying != info.isPlaying) {
pd->sendMessage("hostIsPlaying", info.isPlaying ? "true" : "false");
}
if (positionInfo.bpm != info.bpm) {
pd->sendFloat("hostBpm", (float) info.bpm);
}
positionInfo = info;
}

for (int i = getTotalNumInputChannels(); i < getTotalNumOutputChannels(); ++i) {
buffer.clear (i, 0, buffer.getNumSamples());
}

int numChannels = jmin (getTotalNumInputChannels(), getTotalNumOutputChannels());
int len = buffer.getNumSamples();
Expand Down Expand Up @@ -232,7 +247,7 @@ void PureDataAudioProcessor::getStateInformation (MemoryBlock& destData)

// STORE / SAVE

XmlElement xml(getName());
XmlElement xml(getName().replace(" ", "-"));

// patchfile
XmlElement* patchfileElement = new XmlElement("patchfile");
Expand Down Expand Up @@ -271,23 +286,20 @@ void PureDataAudioProcessor::setStateInformation (const void* data, int sizeInBy
// RESTORE / LOAD

ScopedPointer<XmlElement> xml(getXmlFromBinary(data, sizeInBytes));
if(xml != 0 && xml->hasTagName(getName())) {
if(xml != 0 && xml->hasTagName(getName().replace(" ", "-"))) {

MemoryOutputStream stream;
xml->writeToStream(stream, "<?xml version=\"1.0\"?>");
//std::cout << "load [" << stream.toString() << "] " << std::endl;

forEachXmlChildElement (*xml, child)
{
std::cout << " - load : " << child->getTagName() << std::endl;
//std::cout << " - load : " << child->getTagName() << std::endl;
if(child->hasTagName("patchfile")) {
File path(child->getStringAttribute ("fullpath"));
if (path.exists()) {
patchfile = path; // creates a copy
reloadPatch(NULL);
} else {
// Todo add exclamation mark or something
std::cout << "cant find " << child->getStringAttribute("fullpath") << std::endl;
}
}

Expand All @@ -306,13 +318,23 @@ void PureDataAudioProcessor::setStateInformation (const void* data, int sizeInBy
}
}

void PureDataAudioProcessor::setParameterDefaults()
{
for(size_t i = 0; i < parameterList.size(); i++) {
SliderConfig* sc = getParameterList().getUnchecked(i)->getSliderConfig();
setParameterNotifyingHost(i, sc->defaultValue);
}
}

void PureDataAudioProcessor::reloadPatch (double sampleRate)
{
if (isInstanceLocked) {
status = "Currently only one simultaneous instance of this plugin is allowed";
return;
}

resetSliderConfigs();

if (sampleRate) {
cachedSampleRate = sampleRate;
} else {
Expand All @@ -331,13 +353,22 @@ void PureDataAudioProcessor::reloadPatch (double sampleRate)
pdInBuffer.calloc (pd->blockSize() * numChannels);
pdOutBuffer.calloc (pd->blockSize() * numChannels);

// subscribe before openpatch, to be ready at loadbang time
pd->setReceiver(&receiver);
pd->unsubscribeAll();

for (int i=1; i <= parameterList.size(); i++) {
String identifier;
identifier << receiver.paramIdentifier << i;
pd->subscribe(identifier.toStdString());
}


if (!patchfile.exists()) {

if (patchfile.getFullPathName().toStdString() != "") {
status = "File does not exist";
}

// else keeps select patch message
return;
}

Expand All @@ -350,13 +381,32 @@ void PureDataAudioProcessor::reloadPatch (double sampleRate)

if (patch.isValid()) {
pd->computeAudio (true);
status = "Patch loaded successfully";
if(!patchLoadError) {
status = "Patch loaded successfully";
}
patchLoadError = false;
} else {
status = "Selected patch is not valid, sorry";
status = "Selected patch is not valid";
}

}

void PureDataAudioProcessor::resetSliderConfigs()
{
SliderConfig def;

for (int i=0; i < parameterList.size(); i++) {
FloatParameter* p = parameterList.getUnchecked(i);
SliderConfig* s = p->getSliderConfig();

s->max = def.max;
s->min = def.min;
s->defaultValue = def.defaultValue;
s->name = def.name + std::to_string(i+1);
s->dirty = def.dirty;
}
}

void PureDataAudioProcessor::setPatchFile(File file)
{
patchfile = file;
Expand All @@ -367,6 +417,11 @@ File PureDataAudioProcessor::getPatchFile()
return patchfile;
}

Array<FloatParameter*> PureDataAudioProcessor::getParameterList()
{
return parameterList;
}

//==============================================================================
// This creates new instances of the plugin..
AudioProcessor* JUCE_CALLTYPE createPluginFilter()
Expand Down
19 changes: 10 additions & 9 deletions ports/PdPulp/Source/PluginProcessor.h
Expand Up @@ -12,6 +12,7 @@
#define PLUGINPROCESSOR_H_INCLUDED

#include "PdBase.hpp"
#include "Receiver.h"
#include "../JuceLibraryCode/JuceHeader.h"
#include "FloatParameter.h"

Expand Down Expand Up @@ -65,24 +66,24 @@ class PureDataAudioProcessor : public AudioProcessor
void reloadPatch(double sampleRate);
void setPatchFile(File file);
File getPatchFile();
void setParameterDefaults();

Array<FloatParameter*> getParameterList();


String status = "Select a pure data patch file...";
static bool otherInstanceAlreadyRunning;
bool isInstanceLocked = false;
Receiver receiver;
bool patchLoadError = false;

private:
void resetSliderConfigs();
ScopedPointer<pd::PdBase> pd;
int pos;

Array<FloatParameter*> parameterList;

AudioProcessorParameter* freq;
AudioProcessorParameter* volume;
AudioProcessorParameter* del_delay;
AudioProcessorParameter* del_feedback;
AudioProcessorParameter* del_mode_rate;
AudioProcessorParameter* del_mode_depth;

Array<FloatParameter*> parameterList; // no owned array because values are already owned by parent processor
AudioPlayHead::CurrentPositionInfo positionInfo;
File patchfile;

pd::Patch patch;
Expand Down
98 changes: 98 additions & 0 deletions ports/PdPulp/Source/Receiver.cpp
@@ -0,0 +1,98 @@
/*
==============================================================================
Receiver.cpp
Created: 21 Aug 2015 3:45:37pm
Author: Karl
==============================================================================
*/

#include "Receiver.h"
#include "PluginProcessor.h"

Receiver::Receiver(Array<FloatParameter*>* pList, PureDataAudioProcessor* processor) : parameterList(pList), processor(processor)
{
}

Receiver::~Receiver()
{
}

void Receiver::setErrorMessage(std::string msg)
{
processor->patchLoadError = true;
processor->status = msg;
}

void Receiver::receiveMessage(const std::string& dest, const std::string& msg, const pd::List& list)
{
// Note: only pdpulp_p1 through pdpulp_p10 messages have been subscribed to

String id = dest;
id = id.replace(paramIdentifier, "");
int index = std::stoi(id.toStdString());
index--; // remap param number

FloatParameter* p = parameterList->getUnchecked(index);
SliderConfig* s = p->getSliderConfig();

bool isDirty = false;

if (msg.length() > 0) {
s->name = msg;
isDirty = true;
}

SliderConfig defaults;

float def = defaults.defaultValue,
max = defaults.max,
min = defaults.min,
step = defaults.stepSize;

for (int i=0; i<list.len(); i++) {
if (list.isFloat(i)) {
float v = list.getFloat(i);
switch (i) {

case 0:
def = v;
isDirty = true;
break;

case 1:
max = v;
isDirty = true;
break;

case 2:
min = v;
isDirty = true;
break;

case 3:
step = v;
isDirty = true;
break;
}
}
}

// validate
if (!(min < max)) {
setErrorMessage("Param Error: min value must be higher than max value (" + dest + ", " + msg + ")");
return;
}

if (!(def < max) && !(def > min)) {
setErrorMessage("Param Error: default value must be between min and max value (" + dest + ", " + msg + ")");
return;
}

s->stepSize = step;
s->defaultValue = def;
s->min = min;
s->max = max;
s->dirty = isDirty;
}
44 changes: 44 additions & 0 deletions ports/PdPulp/Source/Receiver.h
@@ -0,0 +1,44 @@
/*
==============================================================================
Receiver.h
Created: 21 Aug 2015 3:45:37pm
Author: Karl
==============================================================================
*/

#ifndef RECEIVER_H_INCLUDED
#define RECEIVER_H_INCLUDED

#include <iostream>
#include "PdReceiver.hpp"
#include "PdTypes.hpp"
#include "../JuceLibraryCode/JuceHeader.h"
#include "FloatParameter.h"

//resolve circular dependency
class PureDataAudioProcessor;



class Receiver : public pd::PdReceiver {

public:

Receiver(Array<FloatParameter*>* pList, PureDataAudioProcessor* processor);
virtual ~Receiver();

void receiveMessage(const std::string& dest, const std::string& msg, const pd::List& list);
const String paramIdentifier = "pdpulp_p";

private:
Array<FloatParameter*>* parameterList;
PureDataAudioProcessor* processor;

void setErrorMessage(std::string msg);
};


#endif // RECEIVER_H_INCLUDED

44 changes: 34 additions & 10 deletions ports/PdPulp/Source/SendSlider.cpp
Expand Up @@ -7,12 +7,12 @@
the "//[xyz]" and "//[/xyz]" sections will be retained when the file is loaded
and re-saved.
Created with Introjucer version: 3.1.1
Created with Introjucer version: 4.1.0
------------------------------------------------------------------------------
The Introjucer is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-13 by Raw Material Software Ltd.
Copyright (c) 2015 - ROLI Ltd.
==============================================================================
*/
Expand All @@ -28,7 +28,7 @@
//[/MiscUserDefs]

//==============================================================================
SendSlider::SendSlider (int index, AudioProcessor& processor)
SendSlider::SendSlider (int index, PureDataAudioProcessor& processor)
: index(index),
processor(processor)
{
Expand All @@ -42,7 +42,10 @@ SendSlider::SendSlider (int index, AudioProcessor& processor)
slider->setColour (Slider::thumbColourId, Colour (0xff5c5c5c));
slider->setColour (Slider::rotarySliderFillColourId, Colour (0x7fdddddd));
slider->setColour (Slider::rotarySliderOutlineColourId, Colour (0x66e6e6e6));
slider->setColour (Slider::textBoxHighlightColourId, Colour (0x40a6a6a6));
slider->setColour (Slider::textBoxTextColourId, Colour (0xffc1c1c1));
slider->setColour (Slider::textBoxBackgroundColourId, Colour (0x1effffff));
slider->setColour (Slider::textBoxHighlightColourId, Colour (0x4ba6a6a6));
slider->setColour (Slider::textBoxOutlineColourId, Colour (0x00808080));
slider->addListener (this);

addAndMakeVisible (label = new Label ("label",
Expand All @@ -55,6 +58,7 @@ SendSlider::SendSlider (int index, AudioProcessor& processor)
label->setColour (Label::textColourId, Colour (0xffc4c4c4));
label->setColour (TextEditor::textColourId, Colour (0xffe9e9e9));
label->setColour (TextEditor::backgroundColourId, Colour (0x00343434));
label->setColour (TextEditor::highlightColourId, Colour (0x61ffffff));
label->addListener (this);


Expand All @@ -68,7 +72,12 @@ SendSlider::SendSlider (int index, AudioProcessor& processor)
PureDataAudioProcessor& p = (PureDataAudioProcessor&) processor;
String labelText(p.getParameterName(index-1));
label->setText(labelText, dontSendNotification);


SliderConfig* sc = processor.getParameterList().getUnchecked(index-1)->getSliderConfig();
slider->setDoubleClickReturnValue(true, sc->defaultValue);
slider->setRange(sc->min, sc->max, sc->stepSize);
slider->setValue(sc->defaultValue);

startTimer(25);
//[/Constructor]
}
Expand Down Expand Up @@ -146,7 +155,21 @@ void SendSlider::labelTextChanged (Label* labelThatHasChanged)
void SendSlider::timerCallback()
{
slider->setValue(processor.getParameter(index-1), NotificationType::dontSendNotification);

SliderConfig* sc = processor.getParameterList().getUnchecked(index-1)->getSliderConfig();

if (sc->dirty) {
slider->setRange(sc->min, sc->max, sc->stepSize);
slider->setName(sc->name);
slider->setValue(sc->defaultValue, NotificationType::dontSendNotification);
slider->setDoubleClickReturnValue(true, sc->defaultValue);
label->setText(sc->name, NotificationType::dontSendNotification);
processor.setParameterName(index-1, sc->name);

sc->dirty = false;
}
}

//[/MiscUserCode]


Expand All @@ -160,22 +183,23 @@ void SendSlider::timerCallback()
BEGIN_JUCER_METADATA

<JUCER_COMPONENT documentType="Component" className="SendSlider" componentName=""
parentClasses="public Component, public Timer" constructorParams="int index, AudioProcessor&amp; processor"
parentClasses="public Component, public Timer" constructorParams="int index, PureDataAudioProcessor&amp; processor"
variableInitialisers="index(index),&#10;processor(processor)"
snapPixels="8" snapActive="1" snapShown="1" overlayOpacity="0.330"
fixedSize="1" initialWidth="100" initialHeight="130">
<BACKGROUND backgroundColour="ffffff"/>
<SLIDER name="slider" id="a814494a82a416fe" memberName="slider" virtualName=""
explicitFocusOrder="0" pos="14 35 71 80" thumbcol="ff5c5c5c"
rotarysliderfill="7fdddddd" rotaryslideroutline="66e6e6e6" textboxhighlight="40a6a6a6"
rotarysliderfill="7fdddddd" rotaryslideroutline="66e6e6e6" textboxtext="ffc1c1c1"
textboxbkgd="1effffff" textboxhighlight="4ba6a6a6" textboxoutline="808080"
min="0" max="1" int="0" style="RotaryVerticalDrag" textBoxPos="TextBoxBelow"
textBoxEditable="1" textBoxWidth="80" textBoxHeight="20" skewFactor="1"/>
<LABEL name="label" id="6f4900c890043ea2" memberName="label" virtualName=""
explicitFocusOrder="0" pos="-4 5 104 24" tooltip="Click to set the name of the corresponding Pure Data receive port."
bkgCol="404040" textCol="ffc4c4c4" edTextCol="ffe9e9e9" edBkgCol="343434"
labelText="Label" editableSingleClick="1" editableDoubleClick="1"
focusDiscardsChanges="0" fontname="Default font" fontsize="15"
bold="0" italic="0" justification="36"/>
hiliteCol="61ffffff" labelText="Label" editableSingleClick="1"
editableDoubleClick="1" focusDiscardsChanges="0" fontname="Default font"
fontsize="15" bold="0" italic="0" justification="36"/>
</JUCER_COMPONENT>

END_JUCER_METADATA
Expand Down
11 changes: 6 additions & 5 deletions ports/PdPulp/Source/SendSlider.h
Expand Up @@ -7,12 +7,12 @@
the "//[xyz]" and "//[/xyz]" sections will be retained when the file is loaded
and re-saved.
Created with Introjucer version: 3.1.1
Created with Introjucer version: 3.2.0
------------------------------------------------------------------------------
The Introjucer is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-13 by Raw Material Software Ltd.
Copyright (c) 2015 - ROLI Ltd.
==============================================================================
*/
Expand All @@ -22,6 +22,7 @@

//[Headers] -- You can add your own extra header files here --
#include "JuceHeader.h"
#include "PluginProcessor.h"
//[/Headers]


Expand All @@ -41,12 +42,12 @@ class SendSlider : public Component,
{
public:
//==============================================================================
SendSlider (int index, AudioProcessor& processor);
SendSlider (int index, PureDataAudioProcessor& processor);
~SendSlider();

//==============================================================================
//[UserMethods] -- You can add your own custom methods in this section.
void timerCallback();
void timerCallback() override;
//[/UserMethods]

void paint (Graphics& g);
Expand All @@ -59,7 +60,7 @@ class SendSlider : public Component,
private:
//[UserVariables] -- You can add your own custom variables in this section.
int index;
AudioProcessor& processor;
PureDataAudioProcessor& processor;
//[/UserVariables]

//==============================================================================
Expand Down
30 changes: 30 additions & 0 deletions ports/PdPulp/Source/SliderConfig.h
@@ -0,0 +1,30 @@
/*
==============================================================================
SliderConfigMessage.h
Created: 22 Aug 2015 3:06:59pm
Author: Karl
==============================================================================
*/

#ifndef SLIDERCONFIGMESSAGE_H_INCLUDED
#define SLIDERCONFIGMESSAGE_H_INCLUDED

#include "../JuceLibraryCode/JuceHeader.h"

class SliderConfig {

public:

std::string name = "Param";
float defaultValue = 0.5;
float max = 1;
float min = 0;
float stepSize = 0.0;
bool dirty = false;
};



#endif // SLIDERCONFIGMESSAGE_H_INCLUDED
1,308 changes: 0 additions & 1,308 deletions ports/PdPulp/Source/mono-tape-echo.pd

This file was deleted.

92 changes: 0 additions & 92 deletions ports/PdPulp/Source/sawsynth.pd

This file was deleted.

34 changes: 0 additions & 34 deletions ports/PdPulp/Source/test2.pd

This file was deleted.

16 changes: 0 additions & 16 deletions ports/PdPulp/Source/test3.pd

This file was deleted.

57 changes: 0 additions & 57 deletions ports/PdPulp/Source/test4.pd

This file was deleted.