/* ============================================================================== This file was auto-generated by the Introjucer! It contains the basic framework code for a JUCE plugin processor. ============================================================================== */ #include "PluginProcessor.h" #include "MainComponent.h" bool PureDataAudioProcessor::otherInstanceAlreadyRunning; //============================================================================== PureDataAudioProcessor::PureDataAudioProcessor() : receiver(¶meterList, this) { for (int i=0; i<10; i++) { FloatParameter* p = new FloatParameter (0.5, ("Param" + (String) (i+1)).toStdString()); parameterList.add(p); addParameter(p); } if(PureDataAudioProcessor::otherInstanceAlreadyRunning) { isInstanceLocked = true; } PureDataAudioProcessor::otherInstanceAlreadyRunning = true; cachedSampleRate = getSampleRate(); } PureDataAudioProcessor::~PureDataAudioProcessor() { pd = nullptr; if (!isInstanceLocked) { PureDataAudioProcessor::otherInstanceAlreadyRunning = false; } } //============================================================================== void PureDataAudioProcessor::setParameterName(int index, String name) { FloatParameter* p = parameterList.getUnchecked(index); p->setName(name); } const String PureDataAudioProcessor::getName() const { return JucePlugin_Name; } const String PureDataAudioProcessor::getInputChannelName (int channelIndex) const { return String (channelIndex + 1); } const String PureDataAudioProcessor::getOutputChannelName (int channelIndex) const { return String (channelIndex + 1); } bool PureDataAudioProcessor::isInputChannelStereoPair (int index) const { return true; } bool PureDataAudioProcessor::isOutputChannelStereoPair (int index) const { return true; } bool PureDataAudioProcessor::acceptsMidi() const { #if JucePlugin_WantsMidiInput return true; #else return false; #endif } bool PureDataAudioProcessor::producesMidi() const { #if JucePlugin_ProducesMidiOutput return true; #else return false; #endif } bool PureDataAudioProcessor::silenceInProducesSilenceOut() const { return false; } double PureDataAudioProcessor::getTailLengthSeconds() const { return 0.0; } int PureDataAudioProcessor::getNumPrograms() { return 1; // NB: some hosts don't cope very well if you tell them there are 0 programs, // so this should be at least 1, even if you're not really implementing programs. } int PureDataAudioProcessor::getCurrentProgram() { return 0; } void PureDataAudioProcessor::setCurrentProgram (int index) { } const String PureDataAudioProcessor::getProgramName (int index) { return String(); } void PureDataAudioProcessor::changeProgramName (int index, const String& newName) { } //============================================================================== void PureDataAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock) { // Use this method as the place to do any pre-playback // initialisation that you need.. reloadPatch(sampleRate); pos = 0; } void PureDataAudioProcessor::releaseResources() { // When playback stops, you can use this as an opportunity to free up any // spare memory, etc. if (pd != nullptr) { pd->computeAudio (false); pd->closePatch (patch); } pd = nullptr; pdInBuffer.free(); pdOutBuffer.free(); } void PureDataAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) { if (isInstanceLocked) { return; } 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(); int idx = 0; for (int i=0; isendFloat(parameter->getName(300).toStdString(), parameter->getValue()); } MidiMessage message; MidiBuffer::Iterator it (midiMessages); int samplePosition = buffer.getNumSamples(); while (it.getNextEvent (message, samplePosition)) { if (message.isNoteOn (true)) { pd->sendNoteOn (message.getChannel(), message.getNoteNumber(), message.getVelocity()); } if (message.isNoteOff (true)) { pd->sendNoteOn (message.getChannel(), message.getNoteNumber(), 0); } } while (len > 0) { int max = jmin (len, pd->blockSize()); /* interleave audio */ { float* dstBuffer = pdInBuffer.getData(); for (int i = 0; i < max; ++i) { for (int channelIndex = 0; channelIndex < numChannels; ++channelIndex) *dstBuffer++ = buffer.getReadPointer(channelIndex) [idx + i]; } } pd->processFloat (1, pdInBuffer.getData(), pdOutBuffer.getData()); /* write-back */ { const float* srcBuffer = pdOutBuffer.getData(); for (int i = 0; i < max; ++i) { for (int channelIndex = 0; channelIndex < numChannels; ++channelIndex) buffer.getWritePointer (channelIndex) [idx + i] = *srcBuffer++; } } idx += max; len -= max; } } //============================================================================== bool PureDataAudioProcessor::hasEditor() const { return true; // (change this to false if you choose to not supply an editor) } AudioProcessorEditor* PureDataAudioProcessor::createEditor() { return new MainComponent(*this); } //============================================================================== void PureDataAudioProcessor::getStateInformation (MemoryBlock& destData) { // You should use this method to store your parameters in the memory block. // You could do that either as raw data, or use the XML or ValueTree classes // as intermediaries to make it easy to save and load complex data. // STORE / SAVE XmlElement xml(getName().replace(" ", "-")); // patchfile XmlElement* patchfileElement = new XmlElement("patchfile"); patchfileElement->setAttribute("path", patchfile.getParentDirectory().getFullPathName()); patchfileElement->setAttribute("fullpath", patchfile.getFullPathName()); patchfileElement->setAttribute("filename", patchfile.getFileName()); xml.addChildElement(patchfileElement); // parameters XmlElement* parameterListElement = new XmlElement("parameterList"); for(size_t i = 0; i < parameterList.size(); ++i) { XmlElement* parameterElement = new XmlElement("parameter"); FloatParameter* parameter = parameterList[i]; parameterElement->setAttribute("index", (int) parameter->getParameterIndex()); parameterElement->setAttribute("name", parameter->getName(256)); parameterElement->setAttribute("value", (double) parameter->getValue()); parameterListElement->addChildElement(parameterElement); } xml.addChildElement(parameterListElement); MemoryOutputStream stream; xml.writeToStream(stream, ""); //std::cout << "save [" << stream.toString() << "] " << std::endl; copyXmlToBinary(xml, destData); } void PureDataAudioProcessor::setStateInformation (const void* data, int sizeInBytes) { // You should use this method to restore your parameters from this memory block, // whose contents will have been created by the getStateInformation() call. // RESTORE / LOAD ScopedPointer xml(getXmlFromBinary(data, sizeInBytes)); if(xml != 0 && xml->hasTagName(getName().replace(" ", "-"))) { MemoryOutputStream stream; xml->writeToStream(stream, ""); //std::cout << "load [" << stream.toString() << "] " << std::endl; forEachXmlChildElement (*xml, child) { //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(0.0); } } if(child->hasTagName("parameterList")) { forEachXmlChildElement (*child, parameterElement) { //std::cout << "loading param " << parameterElement->getStringAttribute("name"); //std::cout << "[" << parameterElement->getIntAttribute("index") << "]: "; //std::cout << parameterElement->getDoubleAttribute("value") << std::endl; setParameter(parameterElement->getIntAttribute("index"), (float) parameterElement->getDoubleAttribute("value")); setParameterName(parameterElement->getIntAttribute("index"), parameterElement->getStringAttribute("name")); } } } } } 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 { sampleRate = cachedSampleRate; } if (pd) { pd->computeAudio(false); pd->closePatch(patch); } pd = new pd::PdBase; pd->init (getTotalNumInputChannels(), getTotalNumOutputChannels(), sampleRate); int numChannels = jmin (getTotalNumInputChannels(), getTotalNumOutputChannels()); 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; } if (patchfile.isDirectory()) { status = "You selected a directory"; return; } patch = pd->openPatch (patchfile.getFileName().toStdString(), patchfile.getParentDirectory().getFullPathName().toStdString()); if (patch.isValid()) { pd->computeAudio (true); if(!patchLoadError) { status = "Patch loaded successfully"; } patchLoadError = false; } else { 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; } File PureDataAudioProcessor::getPatchFile() { return patchfile; } Array PureDataAudioProcessor::getParameterList() { return parameterList; } //============================================================================== // This creates new instances of the plugin.. AudioProcessor* JUCE_CALLTYPE createPluginFilter() { return new PureDataAudioProcessor(); }