Extra "ports" of juce-based plugins using the distrho build system
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

431 lines
13KB

  1. /*
  2. ==============================================================================
  3. This file was auto-generated by the Introjucer!
  4. It contains the basic framework code for a JUCE plugin processor.
  5. ==============================================================================
  6. */
  7. #include "PluginProcessor.h"
  8. #include "MainComponent.h"
  9. bool PureDataAudioProcessor::otherInstanceAlreadyRunning;
  10. //==============================================================================
  11. PureDataAudioProcessor::PureDataAudioProcessor() : receiver(&parameterList, this)
  12. {
  13. for (int i=0; i<10; i++) {
  14. FloatParameter* p = new FloatParameter (0.5, ("Param" + (String) (i+1)).toStdString());
  15. parameterList.add(p);
  16. addParameter(p);
  17. }
  18. if(PureDataAudioProcessor::otherInstanceAlreadyRunning) {
  19. isInstanceLocked = true;
  20. }
  21. PureDataAudioProcessor::otherInstanceAlreadyRunning = true;
  22. cachedSampleRate = getSampleRate();
  23. }
  24. PureDataAudioProcessor::~PureDataAudioProcessor()
  25. {
  26. pd = nullptr;
  27. if (!isInstanceLocked) {
  28. PureDataAudioProcessor::otherInstanceAlreadyRunning = false;
  29. }
  30. }
  31. //==============================================================================
  32. void PureDataAudioProcessor::setParameterName(int index, String name)
  33. {
  34. FloatParameter* p = parameterList.getUnchecked(index);
  35. p->setName(name);
  36. }
  37. const String PureDataAudioProcessor::getName() const
  38. {
  39. return JucePlugin_Name;
  40. }
  41. const String PureDataAudioProcessor::getInputChannelName (int channelIndex) const
  42. {
  43. return String (channelIndex + 1);
  44. }
  45. const String PureDataAudioProcessor::getOutputChannelName (int channelIndex) const
  46. {
  47. return String (channelIndex + 1);
  48. }
  49. bool PureDataAudioProcessor::isInputChannelStereoPair (int index) const
  50. {
  51. return true;
  52. }
  53. bool PureDataAudioProcessor::isOutputChannelStereoPair (int index) const
  54. {
  55. return true;
  56. }
  57. bool PureDataAudioProcessor::acceptsMidi() const
  58. {
  59. #if JucePlugin_WantsMidiInput
  60. return true;
  61. #else
  62. return false;
  63. #endif
  64. }
  65. bool PureDataAudioProcessor::producesMidi() const
  66. {
  67. #if JucePlugin_ProducesMidiOutput
  68. return true;
  69. #else
  70. return false;
  71. #endif
  72. }
  73. bool PureDataAudioProcessor::silenceInProducesSilenceOut() const
  74. {
  75. return false;
  76. }
  77. double PureDataAudioProcessor::getTailLengthSeconds() const
  78. {
  79. return 0.0;
  80. }
  81. int PureDataAudioProcessor::getNumPrograms()
  82. {
  83. return 1; // NB: some hosts don't cope very well if you tell them there are 0 programs,
  84. // so this should be at least 1, even if you're not really implementing programs.
  85. }
  86. int PureDataAudioProcessor::getCurrentProgram()
  87. {
  88. return 0;
  89. }
  90. void PureDataAudioProcessor::setCurrentProgram (int index)
  91. {
  92. }
  93. const String PureDataAudioProcessor::getProgramName (int index)
  94. {
  95. return String();
  96. }
  97. void PureDataAudioProcessor::changeProgramName (int index, const String& newName)
  98. {
  99. }
  100. //==============================================================================
  101. void PureDataAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock)
  102. {
  103. // Use this method as the place to do any pre-playback
  104. // initialisation that you need..
  105. reloadPatch(sampleRate);
  106. pos = 0;
  107. }
  108. void PureDataAudioProcessor::releaseResources()
  109. {
  110. // When playback stops, you can use this as an opportunity to free up any
  111. // spare memory, etc.
  112. if (pd != nullptr)
  113. {
  114. pd->computeAudio (false);
  115. pd->closePatch (patch);
  116. }
  117. pd = nullptr;
  118. pdInBuffer.free();
  119. pdOutBuffer.free();
  120. }
  121. void PureDataAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
  122. {
  123. if (isInstanceLocked) {
  124. return;
  125. }
  126. pd->receiveMessages();
  127. // Send host info messages
  128. {
  129. AudioPlayHead::CurrentPositionInfo info;
  130. getPlayHead()->getCurrentPosition(info);
  131. if (positionInfo.isPlaying != info.isPlaying) {
  132. pd->sendMessage("hostIsPlaying", info.isPlaying ? "true" : "false");
  133. }
  134. if (positionInfo.bpm != info.bpm) {
  135. pd->sendFloat("hostBpm", (float) info.bpm);
  136. }
  137. positionInfo = info;
  138. }
  139. for (int i = getTotalNumInputChannels(); i < getTotalNumOutputChannels(); ++i) {
  140. buffer.clear (i, 0, buffer.getNumSamples());
  141. }
  142. int numChannels = jmin (getTotalNumInputChannels(), getTotalNumOutputChannels());
  143. int len = buffer.getNumSamples();
  144. int idx = 0;
  145. for (int i=0; i<parameterList.size(); i++) {
  146. FloatParameter* parameter = parameterList[i];
  147. pd->sendFloat(parameter->getName(300).toStdString(), parameter->getValue());
  148. }
  149. MidiMessage message;
  150. MidiBuffer::Iterator it (midiMessages);
  151. int samplePosition = buffer.getNumSamples();
  152. while (it.getNextEvent (message, samplePosition))
  153. {
  154. if (message.isNoteOn (true)) {
  155. pd->sendNoteOn (message.getChannel(), message.getNoteNumber(), message.getVelocity());
  156. }
  157. if (message.isNoteOff (true)) {
  158. pd->sendNoteOn (message.getChannel(), message.getNoteNumber(), 0);
  159. }
  160. }
  161. while (len > 0)
  162. {
  163. int max = jmin (len, pd->blockSize());
  164. /* interleave audio */
  165. {
  166. float* dstBuffer = pdInBuffer.getData();
  167. for (int i = 0; i < max; ++i)
  168. {
  169. for (int channelIndex = 0; channelIndex < numChannels; ++channelIndex)
  170. *dstBuffer++ = buffer.getReadPointer(channelIndex) [idx + i];
  171. }
  172. }
  173. pd->processFloat (1, pdInBuffer.getData(), pdOutBuffer.getData());
  174. /* write-back */
  175. {
  176. const float* srcBuffer = pdOutBuffer.getData();
  177. for (int i = 0; i < max; ++i)
  178. {
  179. for (int channelIndex = 0; channelIndex < numChannels; ++channelIndex)
  180. buffer.getWritePointer (channelIndex) [idx + i] = *srcBuffer++;
  181. }
  182. }
  183. idx += max;
  184. len -= max;
  185. }
  186. }
  187. //==============================================================================
  188. bool PureDataAudioProcessor::hasEditor() const
  189. {
  190. return true; // (change this to false if you choose to not supply an editor)
  191. }
  192. AudioProcessorEditor* PureDataAudioProcessor::createEditor()
  193. {
  194. return new MainComponent(*this);
  195. }
  196. //==============================================================================
  197. void PureDataAudioProcessor::getStateInformation (MemoryBlock& destData)
  198. {
  199. // You should use this method to store your parameters in the memory block.
  200. // You could do that either as raw data, or use the XML or ValueTree classes
  201. // as intermediaries to make it easy to save and load complex data.
  202. // STORE / SAVE
  203. XmlElement xml(getName().replace(" ", "-"));
  204. // patchfile
  205. XmlElement* patchfileElement = new XmlElement("patchfile");
  206. patchfileElement->setAttribute("path", patchfile.getParentDirectory().getFullPathName());
  207. patchfileElement->setAttribute("fullpath", patchfile.getFullPathName());
  208. patchfileElement->setAttribute("filename", patchfile.getFileName());
  209. xml.addChildElement(patchfileElement);
  210. // parameters
  211. XmlElement* parameterListElement = new XmlElement("parameterList");
  212. for(size_t i = 0; i < parameterList.size(); ++i) {
  213. XmlElement* parameterElement = new XmlElement("parameter");
  214. FloatParameter* parameter = parameterList[i];
  215. parameterElement->setAttribute("index", (int) parameter->getParameterIndex());
  216. parameterElement->setAttribute("name", parameter->getName(256));
  217. parameterElement->setAttribute("value", (double) parameter->getValue());
  218. parameterListElement->addChildElement(parameterElement);
  219. }
  220. xml.addChildElement(parameterListElement);
  221. MemoryOutputStream stream;
  222. xml.writeToStream(stream, "");
  223. //std::cout << "save [" << stream.toString() << "] " << std::endl;
  224. copyXmlToBinary(xml, destData);
  225. }
  226. void PureDataAudioProcessor::setStateInformation (const void* data, int sizeInBytes)
  227. {
  228. // You should use this method to restore your parameters from this memory block,
  229. // whose contents will have been created by the getStateInformation() call.
  230. // RESTORE / LOAD
  231. ScopedPointer<XmlElement> xml(getXmlFromBinary(data, sizeInBytes));
  232. if(xml != 0 && xml->hasTagName(getName().replace(" ", "-"))) {
  233. MemoryOutputStream stream;
  234. xml->writeToStream(stream, "<?xml version=\"1.0\"?>");
  235. //std::cout << "load [" << stream.toString() << "] " << std::endl;
  236. forEachXmlChildElement (*xml, child)
  237. {
  238. //std::cout << " - load : " << child->getTagName() << std::endl;
  239. if(child->hasTagName("patchfile")) {
  240. File path(child->getStringAttribute ("fullpath"));
  241. if (path.exists()) {
  242. patchfile = path; // creates a copy
  243. reloadPatch(0.0);
  244. }
  245. }
  246. if(child->hasTagName("parameterList")) {
  247. forEachXmlChildElement (*child, parameterElement) {
  248. //std::cout << "loading param " << parameterElement->getStringAttribute("name");
  249. //std::cout << "[" << parameterElement->getIntAttribute("index") << "]: ";
  250. //std::cout << parameterElement->getDoubleAttribute("value") << std::endl;
  251. setParameter(parameterElement->getIntAttribute("index"), (float) parameterElement->getDoubleAttribute("value"));
  252. setParameterName(parameterElement->getIntAttribute("index"), parameterElement->getStringAttribute("name"));
  253. }
  254. }
  255. }
  256. }
  257. }
  258. void PureDataAudioProcessor::setParameterDefaults()
  259. {
  260. for(size_t i = 0; i < parameterList.size(); i++) {
  261. SliderConfig* sc = getParameterList().getUnchecked(i)->getSliderConfig();
  262. setParameterNotifyingHost(i, sc->defaultValue);
  263. }
  264. }
  265. void PureDataAudioProcessor::reloadPatch (double sampleRate)
  266. {
  267. if (isInstanceLocked) {
  268. status = "Currently only one simultaneous instance of this plugin is allowed";
  269. return;
  270. }
  271. resetSliderConfigs();
  272. if (sampleRate) {
  273. cachedSampleRate = sampleRate;
  274. } else {
  275. sampleRate = cachedSampleRate;
  276. }
  277. if (pd) {
  278. pd->computeAudio(false);
  279. pd->closePatch(patch);
  280. }
  281. pd = new pd::PdBase;
  282. pd->init (getTotalNumInputChannels(), getTotalNumOutputChannels(), sampleRate);
  283. int numChannels = jmin (getTotalNumInputChannels(), getTotalNumOutputChannels());
  284. pdInBuffer.calloc (pd->blockSize() * numChannels);
  285. pdOutBuffer.calloc (pd->blockSize() * numChannels);
  286. // subscribe before openpatch, to be ready at loadbang time
  287. pd->setReceiver(&receiver);
  288. pd->unsubscribeAll();
  289. for (int i=1; i <= parameterList.size(); i++) {
  290. String identifier;
  291. identifier << receiver.paramIdentifier << i;
  292. pd->subscribe(identifier.toStdString());
  293. }
  294. if (!patchfile.exists()) {
  295. if (patchfile.getFullPathName().toStdString() != "") {
  296. status = "File does not exist";
  297. }
  298. // else keeps select patch message
  299. return;
  300. }
  301. if (patchfile.isDirectory()) {
  302. status = "You selected a directory";
  303. return;
  304. }
  305. patch = pd->openPatch (patchfile.getFileName().toStdString(), patchfile.getParentDirectory().getFullPathName().toStdString());
  306. if (patch.isValid()) {
  307. pd->computeAudio (true);
  308. if(!patchLoadError) {
  309. status = "Patch loaded successfully";
  310. }
  311. patchLoadError = false;
  312. } else {
  313. status = "Selected patch is not valid";
  314. }
  315. }
  316. void PureDataAudioProcessor::resetSliderConfigs()
  317. {
  318. SliderConfig def;
  319. for (int i=0; i < parameterList.size(); i++) {
  320. FloatParameter* p = parameterList.getUnchecked(i);
  321. SliderConfig* s = p->getSliderConfig();
  322. s->max = def.max;
  323. s->min = def.min;
  324. s->defaultValue = def.defaultValue;
  325. s->name = def.name + std::to_string(i+1);
  326. s->dirty = def.dirty;
  327. }
  328. }
  329. void PureDataAudioProcessor::setPatchFile(File file)
  330. {
  331. patchfile = file;
  332. }
  333. File PureDataAudioProcessor::getPatchFile()
  334. {
  335. return patchfile;
  336. }
  337. Array<FloatParameter*> PureDataAudioProcessor::getParameterList()
  338. {
  339. return parameterList;
  340. }
  341. //==============================================================================
  342. // This creates new instances of the plugin..
  343. AudioProcessor* JUCE_CALLTYPE createPluginFilter()
  344. {
  345. return new PureDataAudioProcessor();
  346. }