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.

2105 lines
81KB

  1. /*
  2. Copyright (C) 2009 Rory Walsh
  3. Cabbage is free software; you can redistribute it
  4. and/or modify it under the terms of the GNU Lesser General Public
  5. License as published by the Free Software Foundation; either
  6. version 2.1 of the License, or (at your option) any later version.
  7. Cabbage is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Lesser General Public License for more details.
  11. You should have received a copy of the GNU Lesser General Public
  12. License along with Csound; if not, write to the Free Software
  13. Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
  14. 02111-1307 USA
  15. */
  16. #include "CabbagePluginProcessor.h"
  17. #include "CabbagePluginEditor.h"
  18. #include <iostream>
  19. #define MAX_BUFFER_SIZE 1024
  20. //Csound functions like to return 0 when everything is ok..
  21. #define LOGGER 0
  22. //these two lines may need to be copied to top part of csound.h
  23. //#define int32 int
  24. //#define uint32 unsigned int
  25. CabbageLookAndFeel* lookAndFeel;
  26. CabbageLookAndFeelBasic* lookAndFeelBasic;
  27. juce_ImplementSingleton (IdentArray);
  28. #if defined(Cabbage_No_Csound)
  29. #define OK 0
  30. #endif
  31. //==============================================================================
  32. // There are two different CabbagePluginAudioProcessor constructors. One for the
  33. // standalone application and the other for the plugin library
  34. //==============================================================================
  35. //#ifdef Cabbage_Build_Standalone
  36. #if defined(Cabbage_Build_Standalone) || defined(Cabbage_Plugin_Host)
  37. //===========================================================
  38. // STANDALONE - CONSTRUCTOR
  39. //===========================================================
  40. CabbagePluginAudioProcessor::CabbagePluginAudioProcessor(String inputfile, bool guiOnOff, int _pluginType)
  41. :backgroundThread ("Audio Recorder Thread"),
  42. activeWriter (nullptr),
  43. csoundStatus(false),
  44. csdFile(File(inputfile)),
  45. showMIDI(false),
  46. csCompileResult(1),
  47. changeMessageType(""),
  48. guiON(false),
  49. currentLine(-99),
  50. noSteps(0),
  51. noPatterns(0),
  52. timeCounter(0),
  53. beat(0),
  54. bpm(120),
  55. patMatrixActive(0),
  56. masterCounter(0),
  57. xyAutosCreated(false),
  58. updateTable(false),
  59. yieldCallbackBool(false),
  60. yieldCounter(10),
  61. isNativeThreadRunning(false),
  62. scoreEvents(),
  63. nativePluginEditor(false),
  64. averageSampleIndex(0),
  65. pluginType(_pluginType),
  66. automationAmp(0),
  67. isAutomator(false),
  68. createLog(false),
  69. automationParamID(-1),
  70. debugMessage(""),
  71. guiRefreshRate(20),
  72. mouseY(0),
  73. mouseX(0),
  74. isWinXP(false),
  75. ksmpsOffset(0),
  76. breakCount(0),
  77. stopProcessing(false)
  78. {
  79. //suspendProcessing(true);
  80. codeEditor = nullptr;
  81. backgroundThread.startThread();
  82. setPlayConfigDetails(2, 2, 44100, 512);
  83. //set up file logger if needed..
  84. StringArray tmpArray;
  85. CabbageGUIClass cAttr;
  86. tmpArray.addLines(File(inputfile).loadFileAsString());
  87. for(int i=0; i<tmpArray.size() || tmpArray[i].contains("</Cabbage>"); i++)
  88. if(tmpArray[i].contains("logger("))
  89. {
  90. CabbageGUIClass cAttr(tmpArray[i], -99);
  91. createLog = cAttr.getNumProp(CabbageIDs::logger);
  92. if(createLog)
  93. {
  94. String logFileName = File(inputfile).getParentDirectory().getFullPathName()+String("/")+File(inputfile).getFileNameWithoutExtension()+String("_Log.txt");
  95. logFile = File(logFileName);
  96. fileLogger = new FileLogger(logFile, String("Cabbage Log.."));
  97. Logger::setCurrentLogger(fileLogger);
  98. }
  99. }
  100. #ifndef Cabbage_No_Csound
  101. //don't start of run Csound in edit mode
  102. setOpcodeDirEnv();
  103. csound = nullptr;
  104. #if !defined(AndroidBuild)
  105. csound = new Csound();
  106. #else
  107. AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
  108. "Uh-oh",
  109. "Pugin Constructor",
  110. "ok");
  111. csound = new AndroidCsound();
  112. csound->setOpenSlCallbacks(); // for android audio to work
  113. #endif
  114. #ifdef CSOUND6
  115. csound->SetHostImplementedMIDIIO(true);
  116. #endif
  117. csound->Reset();
  118. //Logger::writeToLog(csound->GetEnv("OPCODEDIR64"));
  119. #ifdef CSOUND5
  120. csound->PreCompile();
  121. #endif
  122. csound->SetHostData(this);
  123. csound->SetMessageCallback(CabbagePluginAudioProcessor::messageCallback);
  124. csound->SetExternalMidiInOpenCallback(OpenMidiInputDevice);
  125. csound->SetExternalMidiReadCallback(ReadMidiData);
  126. csound->SetExternalMidiOutOpenCallback(OpenMidiOutputDevice);
  127. csound->SetExternalMidiWriteCallback(WriteMidiData);
  128. csound->SetIsGraphable(0);
  129. #ifndef Cabbage_Plugin_Host
  130. if(!getPreference(appProperties, "UseCabbageIO"))
  131. {
  132. csoundPerfThread = new CsoundPerformanceThread(csound);
  133. csoundPerfThread->SetProcessCallback(CabbagePluginAudioProcessor::YieldCallback, (void*)this);
  134. }
  135. #endif
  136. if(pluginType==AUTOMATION_PLUGIN)
  137. isAutomator = true;
  138. csoundChanList = NULL;
  139. numCsoundChannels = 0;
  140. csndIndex = 32;
  141. //set up PVS struct
  142. dataout = new PVSDATEXT;
  143. if(inputfile.isNotEmpty())
  144. {
  145. File(inputfile).setAsCurrentWorkingDirectory();
  146. #ifdef CSOUND6
  147. csoundParams = new CSOUND_PARAMS();
  148. csoundParams->nchnls_override = this->getTotalNumOutputChannels();
  149. csoundParams->displays = 0;
  150. csound->SetParams(csoundParams);
  151. #endif
  152. //ftpp->ftable;
  153. StringArray lines, includeFiles;
  154. lines.addLines(File(inputfile).loadFileAsString());
  155. for(int i=0; i<lines.size(); i++)
  156. if(lines[i].contains("include("))
  157. {
  158. CabbageGUIClass cAttr(lines[i], -99);
  159. for(int y=0; y<cAttr.getStringArrayProp(CabbageIDs::include).size(); y++)
  160. {
  161. String infile = cAttr.getStringArrayPropValue(CabbageIDs::include, y);
  162. if(!File::isAbsolutePath(infile))
  163. {
  164. String file = returnFullPathForFile(infile, File(inputfile).getParentDirectory().getFullPathName());
  165. includeFiles.add(file);
  166. }
  167. else
  168. includeFiles.add(infile);
  169. }
  170. break;
  171. }
  172. includeFiles.removeDuplicates(0);
  173. csCompileResult = csound->Compile(const_cast<char*>(inputfile.toUTF8().getAddress()));
  174. Logger::writeToLog(inputfile);
  175. if(csCompileResult==OK)
  176. {
  177. //send root directory path to Csound.
  178. setPlayConfigDetails(getNumberCsoundOutChannels(),
  179. getNumberCsoundOutChannels(),
  180. getCsoundSamplingRate(),
  181. getCsoundKsmpsSize());
  182. //simple hack to allow tables to be set up correctly.
  183. csound->PerformKsmps();
  184. for(int i=0; i<includeFiles.size(); i++)
  185. {
  186. // csound->CompileOrc(File(includeFiles[i]).loadFileAsString().toUTF8().getAddress());
  187. // csound->InputMessage("i\"PROCESSOR\" 0 3600 1");
  188. }
  189. csound->SetScoreOffsetSeconds(0);
  190. csound->RewindScore();
  191. #ifdef WIN32
  192. csound->SetChannel("CSD_PATH", File(inputfile).getParentDirectory().getFullPathName().replace("\\", "\\\\").toUTF8().getAddress());
  193. #else
  194. csound->SetChannel("CSD_PATH", File(inputfile).getParentDirectory().getFullPathName().toUTF8().getAddress());
  195. #endif
  196. Logger::writeToLog("Csound compiled your file");
  197. //csound->SetYieldCallback(CabbagePluginAudioProcessor::yieldCallback);
  198. if(csound->GetSpout()==nullptr);
  199. CSspout = csound->GetSpout();
  200. CSspin = csound->GetSpin();
  201. // numCsoundChannels = csoundListChannels(csound->GetCsound(), &csoundChanList);
  202. csndIndex = csound->GetKsmps();
  203. csdKsmps = csound->GetKsmps();
  204. cs_scale = csound->Get0dBFS();
  205. csoundStatus = true;
  206. debugMessageArray.add(CABBAGE_VERSION);
  207. debugMessageArray.add(String("\n"));
  208. this->setLatencySamples(csound->GetKsmps());
  209. updateHostDisplay();
  210. }
  211. else
  212. {
  213. Logger::writeToLog("Csound couldn't compile your file");
  214. csoundStatus=false;
  215. //debugMessage = "Csound did not compile correctly. Check for snytax errors by compiling with WinXound";
  216. }
  217. }
  218. else
  219. Logger::writeToLog("Welcome to Cabbage, problems with input file...");
  220. if(SystemStats::getOperatingSystemType()!=SystemStats::WinXP)
  221. {
  222. isWinXP = true;
  223. String path = File(inputfile).getParentDirectory().getFullPathName();
  224. String fullFileName;
  225. #ifdef LINUX
  226. fullFileName = path+"/CabbageTemp.wav";
  227. #else
  228. fullFileName = path+"\\CabbageTemp.wav";
  229. #endif
  230. tempAudioFile = fullFileName;
  231. tempAudioFile.replaceWithData(0 ,0);
  232. }
  233. #endif
  234. }
  235. #else
  236. //===========================================================
  237. // PLUGIN - CONSTRUCTOR
  238. //===========================================================
  239. CabbagePluginAudioProcessor::CabbagePluginAudioProcessor():
  240. backgroundThread ("Audio Recorder Thread"),
  241. activeWriter (nullptr),
  242. csoundStatus(false),
  243. showMIDI(false),
  244. csCompileResult(1),
  245. changeMessageType(""),
  246. guiON(false),
  247. currentLine(-99),
  248. noSteps(0),
  249. noPatterns(0),
  250. timeCounter(0),
  251. beat(0),
  252. bpm(120),
  253. patMatrixActive(0),
  254. masterCounter(0),
  255. xyAutosCreated(false),
  256. createLog(false),
  257. updateTable(false),
  258. yieldCallbackBool(false),
  259. yieldCounter(10),
  260. nativePluginEditor(false),
  261. averageSampleIndex(0),
  262. stopProcessing(false)
  263. {
  264. //Cabbage plugins always try to load a csd file with the same name as the plugin library.
  265. //Therefore we need to find the name of the library and append a '.csd' to it.
  266. #ifdef MACOSX
  267. String osxCSD = File::getSpecialLocation(File::currentApplicationFile).getFullPathName()+String("/Contents/")+File::getSpecialLocation(File::currentApplicationFile).getFileName();
  268. File thisFile(osxCSD);
  269. Logger::writeToLog("MACOSX defined OK");
  270. csdFile = thisFile.withFileExtension(String(".csd")).getFullPathName();
  271. #elif defined(AndroidBuild)
  272. File inFile(File::getSpecialLocation(File::currentApplicationFile));
  273. ScopedPointer<InputStream> fileStream;
  274. fileStream = File(inFile.getFullPathName()).createInputStream();
  275. ZipFile zipFile (fileStream, false);
  276. ScopedPointer<InputStream> fileContents;
  277. fileContents = zipFile.createStreamForEntry(*zipFile.getEntry("assets/AndroidSimpleSynth.csd"));
  278. File thisFile("/sdcard/Cabbage.csd");
  279. thisFile.replaceWithText(fileContents->readEntireStreamAsString());
  280. csdFile = thisFile;
  281. #else
  282. File thisFile(File::getSpecialLocation(File::currentExecutableFile));
  283. csdFile = thisFile.withFileExtension(String(".csd")).getFullPathName();
  284. #endif
  285. //Logger::writeToLog(File::getSpecialLocation(File::currentExecutableFile).getFullPathName());
  286. if(csdFile.exists())
  287. {
  288. Logger::writeToLog("File exists:"+String(csdFile.getFullPathName()));
  289. }
  290. else
  291. {
  292. Logger::writeToLog("File doesn't exist"+String(csdFile.getFullPathName()));
  293. }
  294. File(csdFile.getFullPathName()).setAsCurrentWorkingDirectory();
  295. csdFile.setAsCurrentWorkingDirectory();
  296. StringArray tmpArray;
  297. CabbageGUIClass cAttr;
  298. tmpArray.addLines(csdFile.loadFileAsString());
  299. for(int i=0; i<tmpArray.size() || tmpArray[i].contains("</Cabbage>"); i++)
  300. if(tmpArray[i].contains("logger("))
  301. {
  302. CabbageGUIClass cAttr(tmpArray[i], -99);
  303. createLog = cAttr.getNumProp(CabbageIDs::logger);
  304. if(createLog)
  305. {
  306. String logFileName = csdFile.getParentDirectory().getFullPathName()+String("/")+csdFile.getFileNameWithoutExtension()+String("_Log.txt");
  307. logFile = File(logFileName);
  308. fileLogger = new FileLogger(logFile, String("Cabbage Log.."));
  309. Logger::setCurrentLogger(fileLogger);
  310. }
  311. }
  312. setOpcodeDirEnv();
  313. #ifndef Cabbage_No_Csound
  314. #if !defined(AndroidBuild)
  315. csound = new Csound();
  316. #else
  317. csound = new AndroidCsound();
  318. //csound->setOpenSlCallbacks(); // for android audio to work
  319. #endif
  320. csound->SetHostImplementedMIDIIO(true);
  321. //csound->Reset();
  322. //csound->PreCompile();
  323. csound->SetHostData(this);
  324. midiOutputBuffer.clear();
  325. //for host midi to get sent to Csound, don't need this for standalone
  326. //but might use it in the future for midi mapping to controls
  327. csound->SetMessageCallback(CabbagePluginAudioProcessor::messageCallback);
  328. csound->SetExternalMidiInOpenCallback(OpenMidiInputDevice);
  329. csound->SetExternalMidiReadCallback(ReadMidiData);
  330. csound->SetExternalMidiOutOpenCallback(OpenMidiOutputDevice);
  331. csound->SetExternalMidiWriteCallback(WriteMidiData);
  332. csoundChanList = NULL;
  333. numCsoundChannels = 0;
  334. csndIndex = 32;
  335. startTimer(20);
  336. #ifdef CSOUND6
  337. csoundParams = new CSOUND_PARAMS();
  338. csoundParams->nchnls_override = this->getTotalNumOutputChannels();
  339. csoundParams->displays = 0;
  340. csound->SetParams(csoundParams);
  341. #endif
  342. csCompileResult = csound->Compile(const_cast<char*>(csdFile.getFullPathName().toUTF8().getAddress()));
  343. //csoundSetBreakpointCallback(csound->GetCsound(), breakpointCallback, (void*)this);
  344. csdFile.setAsCurrentWorkingDirectory();
  345. if(csCompileResult==OK)
  346. {
  347. Logger::writeToLog("compiled Ok");
  348. keyboardState.allNotesOff(0);
  349. keyboardState.reset();
  350. //simple hack to allow tables to be set up correctly.
  351. csound->PerformKsmps();
  352. csound->SetScoreOffsetSeconds(0);
  353. csound->RewindScore();
  354. //set up PVS struct
  355. dataout = new PVSDATEXT;
  356. csdKsmps = csound->GetKsmps();
  357. if(csound->GetSpout()==nullptr);
  358. CSspout = csound->GetSpout();
  359. CSspin = csound->GetSpin();
  360. cs_scale = csound->Get0dBFS();
  361. //numCsoundChannels = csoundListChannels(csound->GetCsound(), &csoundChanList);
  362. csndIndex = csound->GetKsmps();
  363. this->setLatencySamples(csound->GetKsmps());
  364. updateHostDisplay();
  365. //soundFilerVector = new MYFLT[csdKsmps];
  366. csoundStatus = true;
  367. debugMessageArray.add(VERSION);
  368. debugMessageArray.add(String("\n"));
  369. #ifdef WIN32
  370. csound->SetChannel("CSD_PATH", File(csdFile).getParentDirectory().getFullPathName().replace("\\", "\\\\").toUTF8().getAddress());
  371. #else
  372. csound->SetChannel("CSD_PATH", File(csdFile).getParentDirectory().getFullPathName().toUTF8().getAddress());
  373. #endif
  374. //send host info before performance..
  375. if (getPlayHead() != 0 && getPlayHead()->getCurrentPosition (hostInfo))
  376. csound->SetChannel(CabbageIDs::hostbpm.toUTF8(), hostInfo.bpm);
  377. if (getPlayHead() != 0 && getPlayHead()->getCurrentPosition (hostInfo))
  378. csound->SetChannel(CabbageIDs::timeinseconds.toUTF8(), hostInfo.timeInSeconds);
  379. if (getPlayHead() != 0 && getPlayHead()->getCurrentPosition (hostInfo))
  380. csound->SetChannel(CabbageIDs::isplaying.toUTF8(), hostInfo.isPlaying);
  381. if (getPlayHead() != 0 && getPlayHead()->getCurrentPosition (hostInfo))
  382. csound->SetChannel(CabbageIDs::isrecording.toUTF8(), hostInfo.isRecording);
  383. if (getPlayHead() != 0 && getPlayHead()->getCurrentPosition (hostInfo))
  384. csound->SetChannel(CabbageIDs::hostppqpos.toUTF8(), hostInfo.ppqPosition);
  385. Logger::writeToLog("everything still good...");
  386. }
  387. else
  388. {
  389. Logger::writeToLog("Csound couldn't compile your file");
  390. csoundStatus=false;
  391. }
  392. #endif
  393. if(SystemStats::getOperatingSystemType()!=SystemStats::WinXP)
  394. {
  395. isWinXP = true;
  396. String path = csdFile.getParentDirectory().getFullPathName();
  397. String fullFileName;
  398. #ifdef LINUX
  399. fullFileName = path+"/CabbageTemp.wav";
  400. #else
  401. fullFileName = path+"\\CabbageTemp.wav";
  402. #endif
  403. tempAudioFile = fullFileName;
  404. tempAudioFile.replaceWithData(0 ,0);
  405. }
  406. createGUI(csdFile.loadFileAsString(), true);
  407. Logger::writeToLog("GUI has been created");
  408. }
  409. #endif
  410. //===========================================================
  411. // PLUGIN - DESTRUCTOR
  412. //===========================================================
  413. CabbagePluginAudioProcessor::~CabbagePluginAudioProcessor()
  414. {
  415. deleteAndZero(lookAndFeel);
  416. deleteAndZero(lookAndFeelBasic);
  417. Logger::setCurrentLogger (nullptr);
  418. stopProcessing = true;
  419. removeAllChangeListeners();
  420. #ifndef Cabbage_No_Csound
  421. xyAutomation.clear();
  422. IdentArray::deleteInstance();
  423. //const MessageManagerLock mmLock;
  424. if(csound)
  425. {
  426. if(csoundPerfThread)
  427. {
  428. csoundPerfThread->Stop();
  429. csoundPerfThread = nullptr;
  430. }
  431. //csound->SetHostImplementedMIDIIO(false);
  432. csound->DeleteChannelList(csoundChanList);
  433. Logger::writeToLog("about to cleanup Csound");
  434. //csoundDebugContinue(csound);
  435. //csound->Cleanup();
  436. //csound->SetHostImplementedMIDIIO(false);
  437. //csound->Reset();
  438. //csoundDebuggerClean(csound->GetCsound());
  439. csound = nullptr;
  440. Logger::writeToLog("Csound cleaned up");
  441. }
  442. #endif
  443. }
  444. int CabbagePluginAudioProcessor::performEntireScore()
  445. {
  446. #ifndef Cabbage_No_Csound
  447. if(!isNativeThreadRunning)
  448. {
  449. csoundPerfThread->Play();
  450. isNativeThreadRunning = true;
  451. }
  452. #endif
  453. return 1;
  454. }
  455. //this callback will be employed when users run with Csound audio IO rather than Cabbage
  456. void CabbagePluginAudioProcessor::YieldCallback(void* data)
  457. {
  458. CabbagePluginAudioProcessor *cabbage = (CabbagePluginAudioProcessor *)data;
  459. cabbage->sendOutgoingMessagesToCsound();
  460. cabbage->updateCabbageControls();
  461. }
  462. //============================================================================
  463. //RECOMPILE CSOUND. THIS IS CALLED FROM THE PLUGIN HOST WHEN UDPATES ARE MADE ON THE FLY
  464. //============================================================================
  465. void CabbagePluginAudioProcessor::reCompileCsound(File file)
  466. {
  467. #ifndef Cabbage_No_Csound
  468. stopProcessing = true;
  469. //getCallbackLock().enter();
  470. midiOutputBuffer.clear();
  471. //csound->DeleteChannelList(csoundChanList);
  472. csound->Reset();
  473. ksmpsOffset = 0;
  474. breakCount = 0;
  475. #ifdef CSOUND6
  476. csoundParams = new CSOUND_PARAMS();
  477. csoundParams->nchnls_override =2;
  478. csoundParams->displays = 0;
  479. csound->SetParams(csoundParams);
  480. #endif
  481. csound->SetHostImplementedMIDIIO(true);
  482. xyAutosCreated = false;
  483. //csound->SetMessageCallback(CabbagePluginAudioProcessor::messageCallback);
  484. numCsoundChannels = 0;
  485. //csound->SetParams(csoundParams);
  486. //csound->SetMessageCallback(CabbagePluginAudioProcessor::messageCallback);
  487. csound->SetExternalMidiInOpenCallback(OpenMidiInputDevice);
  488. csound->SetExternalMidiReadCallback(ReadMidiData);
  489. csound->SetExternalMidiOutOpenCallback(OpenMidiOutputDevice);
  490. csound->SetExternalMidiWriteCallback(WriteMidiData);
  491. CSspout = nullptr;
  492. CSspin = nullptr;
  493. csCompileResult = csound->Compile(const_cast<char*>(file.getFullPathName().toUTF8().getAddress()));
  494. #ifdef BUILD_DEBUGGER
  495. for(int i=0; i<breakpointInstruments.size(); i++)
  496. {
  497. getCallbackLock().enter();
  498. if(i==0)
  499. csoundDebuggerInit(csound->GetCsound());
  500. csoundSetBreakpointCallback(csound->GetCsound(), breakpointCallback, (void*)this);
  501. csoundSetInstrumentBreakpoint(csound->GetCsound(), breakpointInstruments[i], 0);
  502. getCallbackLock().exit();
  503. }
  504. #endif
  505. StringArray lines, includeFiles;
  506. lines.addLines(file.loadFileAsString());
  507. for(int i=0; i<lines.size(); i++)
  508. if(lines[i].contains("include("))
  509. {
  510. CabbageGUIClass cAttr(lines[i], -99);
  511. for(int y=0; y<cAttr.getStringArrayProp(CabbageIDs::include).size(); y++)
  512. {
  513. String infile = cAttr.getStringArrayPropValue(CabbageIDs::include, y);
  514. if(!File::isAbsolutePath(infile))
  515. {
  516. String ifile = returnFullPathForFile(infile, file.getParentDirectory().getFullPathName());
  517. includeFiles.add(ifile);
  518. }
  519. else
  520. includeFiles.add(infile);
  521. }
  522. break;
  523. }
  524. includeFiles.removeDuplicates(0);
  525. if(csCompileResult==OK)
  526. {
  527. keyboardState.allNotesOff(0);
  528. keyboardState.reset();
  529. //simple hack to allow tables to be set up correctly.
  530. csound->PerformKsmps();
  531. csound->SetScoreOffsetSeconds(0);
  532. csound->RewindScore();
  533. for(int i=0; i<includeFiles.size(); i++)
  534. {
  535. // csound->CompileOrc(File(includeFiles[i]).loadFileAsString().toUTF8().getAddress());
  536. // csound->InputMessage("i\"PROCESSOR\" 0 3600 1");
  537. }
  538. csndIndex = 0;
  539. CSspout = csound->GetSpout();
  540. CSspin = csound->GetSpin();
  541. csound->PerformKsmps();
  542. csound->SetScoreOffsetSeconds(0);
  543. csound->RewindScore();
  544. Logger::writeToLog("Csound compiled your file");
  545. //numCsoundChannels = csoundListChannels(csound->GetCsound(), &csoundChanList);
  546. cs_scale = csound->Get0dBFS();
  547. csoundStatus = true;
  548. debugMessageArray.add(CABBAGE_VERSION);
  549. debugMessageArray.add(String("\n"));
  550. //removeAllChangeListeners();
  551. getCallbackLock().exit();
  552. //init all channels with their init val
  553. for(int i=0; i<guiCtrls.size(); i++)
  554. {
  555. csound->SetChannel( guiCtrls.getReference(i).getStringProp(CabbageIDs::channel).toUTF8(),
  556. guiCtrls.getReference(i).getNumProp(CabbageIDs::value));
  557. }
  558. #ifdef WIN32
  559. csound->SetChannel("CSD_PATH", file.getParentDirectory().getFullPathName().replace("\\", "\\\\").toUTF8().getAddress());
  560. #else
  561. csound->SetChannel("CSD_PATH", file.getParentDirectory().getFullPathName().toUTF8().getAddress());
  562. #endif
  563. stopProcessing = false;
  564. return;
  565. }
  566. else
  567. {
  568. Logger::writeToLog("Csound couldn't compile your file");
  569. csoundStatus=false;
  570. }
  571. getCallbackLock().exit();
  572. #endif
  573. }
  574. //===========================================================
  575. // PARSE CSD FILE AND FILL GUI/GUI-LAYOUT VECTORs.
  576. // NO JUCE WIDGETS GET CREATED IN THIS CLASS. ALL
  577. // GUI OBJECTS ARE CREATED ON THE FLY IN THE CABBAGE PLUGIN
  578. // EDITOR FROM INFORMATION HELD IN THE GUICONTROLS VECTOR
  579. //===========================================================
  580. //maybe this should only be done at the end of a k-rate cycle..
  581. void CabbagePluginAudioProcessor::createGUI(String source, bool refresh)
  582. {
  583. //clear arrays if refresh is set
  584. if(refresh==true)
  585. {
  586. guiLayoutCtrls.clear();
  587. guiCtrls.clear();
  588. CabbagePluginAudioProcessorEditor* editor = dynamic_cast<CabbagePluginAudioProcessorEditor*>(this->getActiveEditor());
  589. if(editor)
  590. {
  591. editor->comps.clear();
  592. editor->layoutComps.clear();
  593. editor->subPatches.clear();
  594. editor->popupMenus.clear();
  595. }
  596. }
  597. int indexOfLastGUICtrl = guiCtrls.size();
  598. int indexOfLastLayoutCtrl = guiLayoutCtrls.size();
  599. int test=100;
  600. int checkGUI = isGuiEnabled();
  601. //setGuiEnabled((false));
  602. int guiID=0;
  603. StringArray csdText;
  604. int lines=1;
  605. String csdLine("");
  606. csdText.addLines(source);
  607. bool multiComment = false;
  608. bool multiLine = false;
  609. //check for minimal Cabbage GUI
  610. for(int i=0; i<csdText.size(); i++)
  611. {
  612. int csdLineNumber=0;
  613. #if defined(Cabbage_Build_Standalone) && !defined(AndroidBuild)
  614. if(!refresh)
  615. {
  616. StringArray fullText;
  617. if(codeEditor)
  618. fullText.addLines(codeEditor->getAllText());
  619. else
  620. fullText.addLines(csdFile.loadFileAsString());
  621. for(int u=0; u<fullText.size(); u++)
  622. if(csdText[i].equalsIgnoreCase(fullText[u]))
  623. {
  624. csdLineNumber = u;
  625. //Logger::writeToLog("LineNumber:"+String(csdLineNumber)+"\n"+csdText[i]);
  626. u=fullText.size();
  627. }
  628. }
  629. #endif
  630. if(csdText[i].indexOfWholeWordIgnoreCase(String("</Cabbage>"))==-1)
  631. {
  632. if(csdText[i].trim().isNotEmpty())
  633. {
  634. csdLine = csdText[i];
  635. if(refresh)
  636. csdLineNumber = i;
  637. //tidy up string
  638. csdLine = csdLine.trimStart();
  639. //csdLine = csdLine.removeCharacters(" \\");
  640. //csdLine = csdLine.removeCharacters(",\\");
  641. //Logger::writeToLog(csdLine);
  642. StringArray tokes;
  643. tokes.addTokens(csdLine.trimEnd(), ", ", "\"");
  644. if(tokes[0].containsIgnoreCase(String("/*")))
  645. {
  646. multiComment = true;
  647. }
  648. if(tokes[0].containsIgnoreCase(String("*\\")))
  649. {
  650. multiComment = false;
  651. }
  652. if(tokes[0].containsIgnoreCase(String(";")))
  653. {
  654. //allows for single line comments
  655. }
  656. else if(tokes[0].containsIgnoreCase(String("}")))
  657. {
  658. plantFlag = ""; //reset plantFlag when a closing bracket is found
  659. presetFlag = "";
  660. }
  661. if(!multiComment)
  662. //populate the guiLayoutCtrls vector with non-interactive widgets
  663. //the host widgets aren't GUI based but they can be added to this
  664. //vector too, as can the editor button.
  665. if(tokes[0].equalsIgnoreCase(String("form"))
  666. ||tokes[0].equalsIgnoreCase(String("image"))
  667. ||tokes[0].equalsIgnoreCase(String("keyboard"))
  668. ||tokes[0].equalsIgnoreCase(String("gentable"))
  669. ||tokes[0].equalsIgnoreCase(String("csoundoutput"))
  670. ||tokes[0].equalsIgnoreCase(String("textbox"))
  671. ||tokes[0].equalsIgnoreCase(String("line"))
  672. ||tokes[0].equalsIgnoreCase(String("recordbutton"))
  673. ||tokes[0].equalsIgnoreCase(String("label"))
  674. ||tokes[0].equalsIgnoreCase(String("hostbpm"))
  675. ||tokes[0].equalsIgnoreCase(String("hosttime"))
  676. ||tokes[0].equalsIgnoreCase(String("hostplaying"))
  677. ||tokes[0].equalsIgnoreCase(String("hostppqpos"))
  678. ||tokes[0].equalsIgnoreCase(String("vumeter"))
  679. ||tokes[0].equalsIgnoreCase(String("patmatrix"))
  680. ||tokes[0].equalsIgnoreCase(String("source"))
  681. ||tokes[0].equalsIgnoreCase(String("multitab"))
  682. ||tokes[0].equalsIgnoreCase(String("infobutton"))
  683. ||tokes[0].equalsIgnoreCase(String("filebutton"))
  684. ||tokes[0].equalsIgnoreCase(String("soundfiler"))
  685. ||tokes[0].equalsIgnoreCase(String("texteditor"))
  686. ||tokes[0].equalsIgnoreCase(String("popupmenu"))
  687. ||tokes[0].equalsIgnoreCase(String("snapshot"))
  688. ||tokes[0].equalsIgnoreCase(String("table"))
  689. ||tokes[0].equalsIgnoreCase(String("pvsview"))
  690. ||tokes[0].equalsIgnoreCase(String("hostrecording"))
  691. ||tokes[0].equalsIgnoreCase(String("directorylist"))
  692. ||tokes[0].equalsIgnoreCase(String("transport"))
  693. ||tokes[0].equalsIgnoreCase(String("groupbox")))
  694. {
  695. CabbageGUIClass cAttr(csdLine.trimEnd(), guiID);
  696. //showMessage(csdLine);
  697. cAttr.setNumProp(CabbageIDs::lineNumber, csdLineNumber);
  698. if(cAttr.getStringProp("native").length()>0)
  699. {
  700. //create generic plugin editor and break..
  701. setupNativePluginEditor();
  702. nativePluginEditor = true;
  703. return;
  704. }
  705. if(cAttr.getNumProp(CabbageIDs::guirefresh)>1)
  706. guiRefreshRate = cAttr.getNumProp(CabbageIDs::guirefresh);
  707. //showMessage(cAttr.getStringProp("type"));
  708. csdLine = "";
  709. //set up stuff for tables
  710. if(tokes[0].equalsIgnoreCase(String("table")))
  711. {
  712. /* if(cAttr.getStringArrayProp(CabbageIDs::channel).size()==0)
  713. for(int i=0; i<cAttr.getIntArrayProp("tablenumber").size(); i++)
  714. cAttr.addDummyChannel("dummy"+String(i));*/
  715. for(int i=0; i<cAttr.getStringArrayProp(CabbageIDs::channel).size(); i++)
  716. cAttr.addTableChannelValues();
  717. }
  718. //set up plant flag if needed for other widgets
  719. if(cAttr.getStringProp(String("plant")).isNotEmpty())
  720. {
  721. plantFlag = cAttr.getStringProp(String("plant"));
  722. }
  723. else if(cAttr.getStringProp(String("reltoplant")).equalsIgnoreCase(String("")))
  724. cAttr.setStringProp(String("reltoplant"), plantFlag);
  725. if(cAttr.getStringArrayProp(CabbageIDs::channelarray).size()>0){
  726. //showMessage(String(cAttr.getStringArrayProp(CabbageIDs::channelarray).joinIntoString(" ")));
  727. //showMessage(String(cAttr.getStringArrayProp(CabbageIDs::channelarray).size()));
  728. for(int i=0;i<cAttr.getStringArrayProp(CabbageIDs::channelarray).size();i++)
  729. {
  730. CabbageGUIClass copy = cAttr;
  731. copy.setStringProp(CabbageIDs::channel, cAttr.getStringArrayProp(CabbageIDs::channelarray).getReference(i));
  732. copy.setStringProp(CabbageIDs::identchannel, cAttr.getStringArrayProp(CabbageIDs::identchannelarray).getReference(i));
  733. //Logger::writeToLog(cAttr.getStringArrayProp(CabbageIDs::channelarray).getReference(i));
  734. guiLayoutCtrls.add(copy);
  735. guiID++;
  736. }
  737. }
  738. else{
  739. guiLayoutCtrls.add(cAttr);
  740. guiID++;
  741. }
  742. if(cAttr.getStringProp("type").containsIgnoreCase("form"))
  743. if(cAttr.getStringProp("text").length()>2)
  744. setPluginName(cAttr.getStringProp("text"));
  745. else if(cAttr.getStringProp("caption").length()>2)
  746. setPluginName(cAttr.getStringProp("caption"));
  747. else setPluginName("Untitled Cabbage Patch!");
  748. //StringArray log = logGUIAttributes(cAttr, String("Non-Interactive"));
  749. //debugMessageArray.addArray(logGUIAttributes(cAttr, String("Non-Interactive")));
  750. sendChangeMessage();
  751. //if instrument uses any of the host widgets, or an xypad, turn
  752. //on the timer
  753. if(tokes[0].equalsIgnoreCase(String("hostbpm"))
  754. ||tokes[0].equalsIgnoreCase(String("hosttime"))
  755. ||tokes[0].equalsIgnoreCase(String("hostplaying"))
  756. ||tokes[0].equalsIgnoreCase(String("hostppqpos"))
  757. ||tokes[0].equalsIgnoreCase(String("hostrecording")))
  758. startTimer(20);
  759. }
  760. //populate the guiCtrls vector with interactive widgets
  761. else if(tokes[0].equalsIgnoreCase(String("hslider"))
  762. ||tokes[0].equalsIgnoreCase(String("vslider"))
  763. ||tokes[0].equalsIgnoreCase(String("rslider"))
  764. ||tokes[0].equalsIgnoreCase(String("combobox"))
  765. ||tokes[0].equalsIgnoreCase(String("checkbox"))
  766. ||tokes[0].equalsIgnoreCase(String("numberbox"))
  767. ||tokes[0].equalsIgnoreCase(String("xypad"))
  768. ||tokes[0].equalsIgnoreCase(String("button")))
  769. {
  770. CabbageGUIClass cAttr(csdLine.trimEnd(), guiID);
  771. cAttr.setNumProp(CabbageIDs::lineNumber, csdLineNumber);
  772. //Logger::writeToLog(csdLine.trimEnd());
  773. csdLine = "";
  774. //Logger::writeToLog(tokes[0]);
  775. //attach widget to plant if need be
  776. if(cAttr.getStringProp(String("reltoplant")).equalsIgnoreCase(String("")))
  777. {
  778. //showMessage(cAttr.getStringProp(String("relToPlant")));
  779. cAttr.setStringProp(String("reltoplant"), plantFlag);
  780. //showMessage(String("presetFlag:")+presetFlag);
  781. //showMessage(cAttr.getStringProp("name"));
  782. if(cAttr.getStringProp("preset").length()<1)
  783. cAttr.setStringProp(String("preset"), presetFlag.trim());
  784. //showMessage(cAttr.getStringProp("preset"));
  785. }
  786. //xypad contain two control paramters, one for x axis and another for y. As such we add two
  787. //to our contorl vector so that plugin hosts display two sliders. We name one of the xypad pads
  788. // 'dummy' so that our editor doesn't display it. Our editor only needs to show one xypad.
  789. if(tokes[0].equalsIgnoreCase(String("xypad")))
  790. {
  791. cAttr.setStringProp(CabbageIDs::xychannel, String("X"));
  792. cAttr.setNumProp(CabbageIDs::range, cAttr.getNumProp(CabbageIDs::rangex));
  793. cAttr.setNumProp(CabbageIDs::min, cAttr.getNumProp(CabbageIDs::minx));
  794. cAttr.setNumProp(CabbageIDs::max, cAttr.getNumProp(CabbageIDs::maxx));
  795. cAttr.setNumProp(CabbageIDs::value, cAttr.getNumProp(CabbageIDs::valuex));
  796. cAttr.setStringProp(String(CabbageIDs::channel), cAttr.getStringProp(CabbageIDs::xchannel));
  797. guiCtrls.add(cAttr);
  798. cAttr.setStringProp(CabbageIDs::xychannel, String("Y"));
  799. cAttr.setNumProp(CabbageIDs::range, cAttr.getNumProp(CabbageIDs::rangey));
  800. cAttr.setNumProp(CabbageIDs::min, cAttr.getNumProp(CabbageIDs::miny));
  801. cAttr.setNumProp(CabbageIDs::max, cAttr.getNumProp(CabbageIDs::maxy));
  802. cAttr.setNumProp(CabbageIDs::value, cAttr.getNumProp(CabbageIDs::valuey));
  803. cAttr.setStringProp(String(CabbageIDs::channel), cAttr.getStringProp(CabbageIDs::ychannel));
  804. //append 'dummy' to name so the editor know not to display the
  805. //second xypad
  806. cAttr.setStringProp("name", cAttr.getStringProp(CabbageIDs::name)+String("dummy"));
  807. guiCtrls.add(cAttr);
  808. guiID++;
  809. startTimer(10);
  810. }
  811. else
  812. {
  813. //if an array of objects is to be set up enter.....
  814. if(cAttr.getStringArrayProp(CabbageIDs::channelarray).size()>0){
  815. //showMessage(String(cAttr.getStringArrayProp(CabbageIDs::channelarray).joinIntoString(" ")));
  816. //showMessage(String(cAttr.getStringArrayProp(CabbageIDs::channelarray).size()));
  817. for(int i=0;i<cAttr.getStringArrayProp(CabbageIDs::channelarray).size();i++)
  818. {
  819. CabbageGUIClass copy = cAttr;
  820. copy.setStringProp(CabbageIDs::channel, cAttr.getStringArrayProp(CabbageIDs::channelarray).getReference(i));
  821. copy.setStringProp(CabbageIDs::identchannel, cAttr.getStringArrayProp(CabbageIDs::identchannelarray).getReference(i));
  822. //Logger::writeToLog(cAttr.getStringArrayProp(CabbageIDs::channelarray).getReference(i));
  823. guiCtrls.add(copy);
  824. guiID++;
  825. }
  826. }
  827. else{
  828. guiCtrls.add(cAttr);
  829. guiID++;
  830. }
  831. }
  832. //debugMessageArray.addArray(logGUIAttributes(cAttr, String("Interactive")));
  833. sendChangeMessage();
  834. }
  835. }
  836. }
  837. else break;
  838. } //end of scan through entire csd text, control vectors are now populated
  839. //init all channels with their init val, and set parameters
  840. for(int i=0; i<guiCtrls.size(); i++)
  841. {
  842. // Logger::writeToLog(guiCtrls.getReference(i).getStringProp(CabbageIDs::channel)+": "+String(guiCtrls[i].getNumProp(CabbageIDs::value)));
  843. #ifndef Cabbage_No_Csound
  844. if(guiCtrls.getReference(i).getStringProp("channeltype")=="string")
  845. //deal with combobox strings..
  846. csound->SetChannel(guiCtrls.getReference(i).getStringProp(CabbageIDs::channel).toUTF8(), "");
  847. // guiCtrls.getReference(i).getStringArrayPropValue("text", guiCtrls[i].getNumProp(CabbageIDs::value)-1).toUTF8().getAddress());
  848. else
  849. csound->SetChannel( guiCtrls.getReference(i).getStringProp(CabbageIDs::channel).toUTF8(), guiCtrls[i].getNumProp(CabbageIDs::value));
  850. #endif
  851. }
  852. //init all channels with their init val, and set parameters
  853. for(int i=0; i<guiLayoutCtrls.size(); i++)
  854. {
  855. // Logger::writeToLog(guiCtrls.getReference(i).getStringProp(CabbageIDs::channel)+": "+String(guiCtrls[i].getNumProp(CabbageIDs::value)));
  856. #ifndef Cabbage_No_Csound
  857. if(guiLayoutCtrls.getReference(i).getStringProp(CabbageIDs::identchannel).isNotEmpty())
  858. //deal with combobox strings..
  859. csound->SetChannel(guiLayoutCtrls.getReference(i).getStringProp(CabbageIDs::identchannel).toUTF8(), "");
  860. // guiCtrls.getReference(i).getStringArrayPropValue("text", guiCtrls[i].getNumProp(CabbageIDs::value)-1).toUTF8().getAddress());
  861. #endif
  862. }
  863. #ifdef Cabbage_Build_Standalone
  864. if(this->getActiveEditor())
  865. {
  866. CabbagePluginAudioProcessorEditor* editor = dynamic_cast<CabbagePluginAudioProcessorEditor*>(this->getActiveEditor());
  867. if(refresh)
  868. {
  869. editor->comps.clear();
  870. editor->layoutComps.clear();
  871. editor->repaint();
  872. //((CabbagePluginAudioProcessorEditor*)getActiveEditor())->setEditMode(false);
  873. //editor->setEditMode(false);
  874. }
  875. //!this will not work as we are moving through our entire control vector
  876. for(int i=indexOfLastLayoutCtrl; i<guiLayoutCtrls.size(); i++)
  877. editor->InsertGUIControls(guiLayoutCtrls[i]);
  878. for(int i=indexOfLastGUICtrl; i<guiCtrls.size(); i++)
  879. editor->InsertGUIControls(guiCtrls[i]);
  880. if(!getPreference(appProperties, "ExternalEditor") && refresh)
  881. editor->setEditMode(checkGUI);
  882. }
  883. #endif
  884. }
  885. //============================================================================
  886. //dynamically remove components from editor window, used in EDIT mode
  887. //============================================================================
  888. void CabbagePluginAudioProcessor::removeGUIComponent(int index, String type)
  889. {
  890. //remove component struct from our abstract vector
  891. CabbagePluginAudioProcessorEditor* editor = dynamic_cast<CabbagePluginAudioProcessorEditor*>(this->getActiveEditor());
  892. if(type=="interactive")
  893. {
  894. //remove GUI abstract structure from vector
  895. guiCtrls.remove(index);
  896. }
  897. else
  898. {
  899. //remove GUI abstract structure from vector
  900. guiLayoutCtrls.remove(index);
  901. }
  902. editor->updateLayoutEditorFrames();
  903. editor->repaint();
  904. }
  905. //============================================================================
  906. // start/stop recording of file
  907. //============================================================================
  908. void CabbagePluginAudioProcessor::startRecording ()
  909. {
  910. if(isWinXP)
  911. if (sampleRate > 0)
  912. {
  913. // Create an OutputStream to write to our destination file...
  914. tempAudioFile.deleteFile();
  915. ScopedPointer<FileOutputStream> fileStream (tempAudioFile.createOutputStream());
  916. if (fileStream != nullptr)
  917. {
  918. // Now create a WAV writer object that writes to our output stream...
  919. WavAudioFormat wavFormat;
  920. AudioFormatWriter* writer = wavFormat.createWriterFor(fileStream, sampleRate, 2, 16, StringPairArray(), 0);
  921. if (writer != nullptr)
  922. {
  923. fileStream.release(); // (passes responsibility for deleting the stream to the writer object that is now using it)
  924. // Now we'll create one of these helper objects which will act as a FIFO buffer, and will
  925. // write the data to disk on our background thread.
  926. threadedWriter = new AudioFormatWriter::ThreadedWriter (writer, backgroundThread, 32768);
  927. // Reset our recording thumbnail
  928. //thumbnail.reset (writer->getTotalNumChannels(), writer->getSampleRate());
  929. nextSampleNum = 0;
  930. // And now, swap over our active writer pointer so that the audio callback will start using it..
  931. const ScopedLock sl (writerLock);
  932. activeWriter = threadedWriter;
  933. }
  934. }
  935. }
  936. }
  937. void CabbagePluginAudioProcessor::stopRecording()
  938. {
  939. if(isWinXP)
  940. {
  941. // First, clear this pointer to stop the audio callback from using our writer object..
  942. {
  943. const ScopedLock sl (writerLock);
  944. activeWriter = nullptr;
  945. }
  946. threadedWriter = nullptr;
  947. // Now we can delete the writer object. It's done in this order because the deletion could
  948. // take a little time while remaining data gets flushed to disk, so it's best to avoid blocking
  949. // the audio callback while this happens.
  950. #if !defined(AndroidBuild)
  951. FileChooser fc("Save", tempAudioFile.getFullPathName(), "*.wav", UseNativeDialogue);
  952. //determine whether to poen file or directory
  953. if(fc.browseForFileToSave(true))
  954. {
  955. File selectedFile = fc.getResult();
  956. tempAudioFile.moveFileTo(selectedFile);
  957. }
  958. #endif
  959. }
  960. }
  961. //============================================================================
  962. //SETS UP A GENERIC PLUGIN EDITOR
  963. //============================================================================
  964. void CabbagePluginAudioProcessor::setupNativePluginEditor()
  965. {
  966. /*
  967. //create a basic 'native' gui if specificed by the user.
  968. int guiID = 0;
  969. guiCtrls.clear();
  970. for(int i=0;i<numCsoundChannels;i++){
  971. const CsoundChannelListEntry& entry = csoundChanList[i];
  972. if (entry.type & CSOUND_CONTROL_CHANNEL && entry.type & CSOUND_INPUT_CHANNEL) {
  973. MYFLT ddefault, dmin, dmax;
  974. int value_type = getCsound()->GetControlChannelParams(entry.name, ddefault, dmin, dmax);
  975. String parameterInfo;
  976. float initVal = (ddefault<dmin ? dmin : ddefault);
  977. parameterInfo << "channel(\"" << entry.name << "\"), " << "range(" << String(dmin) << ", " << String(dmax) << ", " << String(initVal) << ")";
  978. Logger::writeToLog(parameterInfo);
  979. CabbageGUIClass cAttr(parameterInfo, guiID);
  980. cAttr.setNumProp("range", dmax-dmin);
  981. //cAttr.setStringProp(CabbageIDs::channel, entry.name);
  982. //cAttr.setNumProp("max", (dmax>0 ? dmax : 1));
  983. //cAttr.setNumProp("init", (ddefault<dmin ? dmin : ddefault));
  984. switch(value_type) {
  985. case CSOUND_CONTROL_CHANNEL_INT:
  986. cAttr.setNumProp("incr", 1);
  987. break;
  988. case CSOUND_CONTROL_CHANNEL_LIN:
  989. cAttr.setNumProp("incr", .01);
  990. break;
  991. case CSOUND_CONTROL_CHANNEL_EXP:
  992. cAttr.setNumProp("skew", .5);
  993. break;
  994. default:
  995. break;
  996. }
  997. guiCtrls.add(cAttr);
  998. setPluginName("Test Plugin");
  999. guiID++;
  1000. }
  1001. }
  1002. */
  1003. }
  1004. //===========================================================
  1005. // SHOW SOURCE EDITOR
  1006. //===========================================================
  1007. //void CabbagePluginAudioProcessor::createAndShowSourceEditor(LookAndFeel* looky)
  1008. //{
  1009. //if(!cabbageCsoundEditor){
  1010. //cabbageCsoundEditor = new CabbageEditorWindow(looky);
  1011. //cabbageCsoundEditor->setCsoundFile(csdFile);
  1012. //cabbageCsoundEditor->setCsoundOutputText(csoundOutput);
  1013. //}
  1014. //cabbageCsoundEditor->setVisible(true);
  1015. //}
  1016. //===========================================================
  1017. // CALLBACKS FOR STANDALONE
  1018. //===========================================================
  1019. #ifndef Cabbage_No_Csound
  1020. void CabbagePluginAudioProcessor::messageCallback(CSOUND* csound, int /*attr*/, const char* fmt, va_list args)
  1021. {
  1022. CabbagePluginAudioProcessor* ud = (CabbagePluginAudioProcessor *) csoundGetHostData(csound);
  1023. char msg[MAX_BUFFER_SIZE];
  1024. vsnprintf(msg, MAX_BUFFER_SIZE, fmt, args);
  1025. ud->debugMessage += String(msg); //We have to append the incoming msg
  1026. ud->csoundOutput += ud->debugMessage;
  1027. ud->debugMessageArray.add(ud->debugMessage);
  1028. if(ud->createLog)
  1029. Logger::writeToLog(String(msg).trim());
  1030. ud->sendChangeMessage();
  1031. ud->debugMessage = "";
  1032. ud = nullptr;
  1033. }
  1034. #endif
  1035. #if defined(BUILD_DEBUGGER) && !defined(Cabbage_No_Csound)
  1036. void CabbagePluginAudioProcessor::breakpointCallback(CSOUND *csound, debug_bkpt_info_t *bkpt_info, void *userdata)
  1037. {
  1038. #ifdef BUILD_DEBUGGER
  1039. CabbagePluginAudioProcessor* ud = (CabbagePluginAudioProcessor *) userdata;
  1040. ud->getCallbackLock().enter();
  1041. int ksmpOffset = ud->ksmpsOffset;;
  1042. //code based on Andres implementation in CsoundQT...
  1043. debug_instr_t *debug_instr = bkpt_info->breakpointInstr;
  1044. // Copy variable list
  1045. debug_variable_t* vp = bkpt_info->instrVarList;
  1046. if(ud->breakCount==0)
  1047. ud->breakCount= debug_instr->kcounter;
  1048. else ud->breakCount++;
  1049. String output;
  1050. output << "\nBreakpoint at instr " << debug_instr->p1 << "\tNumber of k-cycles into performance: " << ud->breakCount << "\n------------------------------------------------------";;
  1051. while (vp)
  1052. {
  1053. output << " \n";
  1054. if (vp->name[0] != '#')
  1055. {
  1056. output << " VarName:"<< vp->name << "\t";;
  1057. if (strcmp(vp->typeName, "i") == 0 || strcmp(vp->typeName, "k") == 0)
  1058. {
  1059. output << " value=" << *((MYFLT *) vp->data);
  1060. }
  1061. else if(strcmp(vp->typeName, "S") == 0)
  1062. {
  1063. output << " value=" << (char*)(vp->data);
  1064. }
  1065. else if (strcmp(vp->typeName, "a") == 0)
  1066. {
  1067. MYFLT *data = (MYFLT *) vp->data;
  1068. output << " value="<< *data;// << *(data + 1) << *(data + 2)<< *(data + 3);
  1069. }
  1070. output << " varType[" << vp->typeName << "]";
  1071. }
  1072. vp = vp->next;
  1073. }
  1074. ud->csoundDebuggerOutput = output;
  1075. csoundDebugFreeVariables(csound, vp);
  1076. //csoundDebugFreeInstrInstances(csound, debug_instr);
  1077. ud->getCallbackLock().exit();
  1078. #endif
  1079. }
  1080. #endif
  1081. //==============================================================================
  1082. #if defined(Cabbage_Build_Standalone) || defined(Cabbage_Plugin_Host)
  1083. CabbagePluginAudioProcessor* JUCE_CALLTYPE createCabbagePluginFilter(String inputfile, bool guiOnOff, int pluginType)
  1084. {
  1085. return new CabbagePluginAudioProcessor(inputfile, false, pluginType);
  1086. }
  1087. #else
  1088. AudioProcessor* JUCE_CALLTYPE createPluginFilter()
  1089. {
  1090. static bool needsInitialize = true;
  1091. if (needsInitialize)
  1092. {
  1093. csoundInitialize(CSOUNDINIT_NO_SIGNAL_HANDLER|CSOUNDINIT_NO_ATEXIT);
  1094. needsInitialize = false;
  1095. }
  1096. return new CabbagePluginAudioProcessor();
  1097. }
  1098. #endif
  1099. //==========================================================================
  1100. //action listener. Listen to messages being sent form xypad automations
  1101. //==========================================================================
  1102. void CabbagePluginAudioProcessor::changeListenerCallback(ChangeBroadcaster *source)
  1103. {
  1104. float xVal, yVal;
  1105. //is message coming from an xypad
  1106. XYPadAutomation* xyPad = dynamic_cast< XYPadAutomation*>(source);
  1107. if(xyPad)
  1108. {
  1109. #ifdef Cabbage_Build_Standalone
  1110. setParameterNotifyingHost(xyPad->paramIndex, xyPad->getXValue());
  1111. setParameterNotifyingHost(xyPad->paramIndex+1, xyPad->getYValue());
  1112. #else
  1113. if(xyPad->getMinimumXValue()>=0)
  1114. xVal = (xyPad->getXValue()/xyPad->getXRange())+(fabs(xyPad->getMinimumXValue())/xyPad->getXRange());
  1115. else
  1116. xVal = (xyPad->getXValue()/xyPad->getXRange())-(fabs(xyPad->getMinimumXValue())/xyPad->getXRange());
  1117. if(xyPad->getMinimumYValue()<=0)
  1118. yVal = (xyPad->getYValue()/xyPad->getYRange())+(fabs(xyPad->getMinimumYValue())/xyPad->getYRange());
  1119. else
  1120. yVal = (xyPad->getYValue()/xyPad->getYRange())-(fabs(xyPad->getMinimumYValue())/xyPad->getYRange());
  1121. //Logger::writeToLog("Param:"+String(xyPad->paramIndex)+" xyPadXVal:"+String(xVal));
  1122. //Logger::writeToLog("Param:"+String(xyPad->paramIndex+1)+" xyPadYVal:"+String(yVal));
  1123. setParameterNotifyingHost(xyPad->paramIndex, xVal);
  1124. setParameterNotifyingHost(xyPad->paramIndex+1, yVal);
  1125. #endif
  1126. }
  1127. }
  1128. //==============================================================================
  1129. // getTable data from Csound so table editor can draw table
  1130. //==============================================================================
  1131. StringArray CabbagePluginAudioProcessor::getTableStatement(int tableNum)
  1132. {
  1133. StringArray fdata;
  1134. fdata.add(String::empty);
  1135. #ifndef Cabbage_No_Csound
  1136. if(csCompileResult==OK)
  1137. {
  1138. MYFLT* argsPtr, *temp;
  1139. int noOfArgs = -1;//csoundGetTableArgs(csound->GetCsound(), &argsPtr, tableNum);
  1140. if(noOfArgs!=-1)
  1141. {
  1142. int tableSize = csound->GetTable(temp, tableNum);
  1143. fdata.add(String(tableNum));
  1144. fdata.add("0");
  1145. fdata.add(String(tableSize));
  1146. for(int i=0;i<noOfArgs;i++)
  1147. //Logger::writeToLog("TableNum:"+String(tableNum)+" ArgsCount:"+String(noOfArgs));
  1148. fdata.add(String(argsPtr[i]));
  1149. }
  1150. //int tableSize = csound->GetTable(temp, tableNum);
  1151. //if(tableSize>0)
  1152. //{
  1153. //uncomment with dev and modified Csound
  1154. /*EVTBLK* e = (EVTBLK*)csoundTableGetEvtblk(csound->GetCsound(), tableNum);
  1155. for(int i=0; i<=e->pcnt; i++)
  1156. fdata.add(String(e->p[i]));*/
  1157. //}
  1158. }
  1159. #endif
  1160. return fdata;
  1161. }
  1162. //==============================================================================
  1163. const Array<double, CriticalSection> CabbagePluginAudioProcessor::getTable(int tableNum)
  1164. {
  1165. Array<double, CriticalSection> points;
  1166. if(csCompileResult==OK)
  1167. {
  1168. int tableSize=0;
  1169. #ifndef Cabbage_No_Csound
  1170. MYFLT* temp;
  1171. tableSize = csound->GetTable(temp, tableNum);
  1172. #else
  1173. float *temp;
  1174. #endif
  1175. if(tableSize>0)
  1176. points = Array<double, CriticalSection>(temp, tableSize);
  1177. }
  1178. return points;
  1179. }
  1180. const Array<float, CriticalSection> CabbagePluginAudioProcessor::getTableFloats(int tableNum)
  1181. {
  1182. Array<float, CriticalSection> points;
  1183. if(csCompileResult==OK)
  1184. {
  1185. points.clear();
  1186. int tableSize=0;
  1187. #ifndef Cabbage_No_Csound
  1188. tableSize = csound->TableLength(tableNum);
  1189. temp.clear();
  1190. //not good if table size is -1!
  1191. if(tableSize<0)
  1192. return points;
  1193. temp.reserve(tableSize);
  1194. csound->TableCopyOut(tableNum, &temp[0]);
  1195. #else
  1196. float *temp;
  1197. #endif
  1198. if(tableSize>0)
  1199. points = Array<float, CriticalSection>(&temp[0], tableSize);
  1200. }
  1201. return points;
  1202. }
  1203. int CabbagePluginAudioProcessor::checkTable(int tableNum)
  1204. {
  1205. #ifndef Cabbage_No_Csound
  1206. return csound->TableLength(tableNum);
  1207. #else
  1208. return -1;
  1209. #endif
  1210. }
  1211. //=================================================================================
  1212. // Get and Set Parameter methods, called by our editor, and the plugin host...
  1213. //=================================================================================
  1214. float CabbagePluginAudioProcessor::getParameter (int index)
  1215. {
  1216. if(isPositiveAndBelow(index, getGUICtrlsSize()))
  1217. {
  1218. float range = getGUICtrls(index).getNumProp(CabbageIDs::range);
  1219. float min = getGUICtrls(index).getNumProp(CabbageIDs::min);
  1220. //Logger::writeToLog("parameterGet-"+String(index)+String("-Min:")+String(min)+" Range:"+String(range)+ " Val:"+String(getGUICtrls(index).getNumProp(CabbageIDs::value)));
  1221. //Logger::writeToLog("parameterGet:"+String(index)+String(":")+String(guiCtrls[index].getNumProp(CabbageIDs::value)));
  1222. /* this gets called at any time by our host or out GUI editor */
  1223. if(index<(int)guiCtrls.size()) //make sure index isn't out of range
  1224. {
  1225. #ifndef Cabbage_Build_Standalone
  1226. float val = (getGUICtrls(index).getNumProp(CabbageIDs::value)/range)-(min/range);
  1227. if(getGUICtrls(index).getStringProp(CabbageIDs::type)==CabbageIDs::combobox)
  1228. return (getGUICtrls(index).getNumProp(CabbageIDs::value)/getGUICtrls(index).getNumProp(CabbageIDs::comborange));
  1229. else if(getGUICtrls(index).getStringProp(CabbageIDs::type)==CabbageIDs::checkbox ||
  1230. getGUICtrls(index).getStringProp(CabbageIDs::type)==CabbageIDs::button)
  1231. return getGUICtrls(index).getNumProp(CabbageIDs::value);
  1232. else
  1233. return (getGUICtrls(index).getNumProp(CabbageIDs::value)/range)-(min/range);
  1234. #else
  1235. return guiCtrls[index].getNumProp(CabbageIDs::value);
  1236. #endif
  1237. }
  1238. else
  1239. return 0.0f;
  1240. }
  1241. else
  1242. return 0.0f;
  1243. }
  1244. void CabbagePluginAudioProcessor::setParameter (int index, float newValue)
  1245. {
  1246. String stringMessage;
  1247. #ifndef Cabbage_No_Csound
  1248. /* this will get called by the plugin GUI sliders or
  1249. by the host, via automation. The timer thread in the plugin's editor
  1250. will constantly update with the values that have been set here.
  1251. We don't actually change any parameters here, we simply add the messages
  1252. to a queue. See next method. The updates will only happen when it's safe to do. */
  1253. float range, min, max, comboRange;
  1254. //add index of control that was changed to dirty control vector
  1255. dirtyControls.addIfNotAlreadyThere(index);
  1256. //Logger::writeToLog("parameterSet:"+String(newValue));
  1257. if(index<(int)guiCtrls.size())//make sure index isn't out of range
  1258. {
  1259. #ifndef Cabbage_Build_Standalone
  1260. //scaling in here because incoming values in plugin mode range from 0-1
  1261. range = getGUICtrls(index).getNumProp(CabbageIDs::range);
  1262. comboRange = getGUICtrls(index).getNumProp(CabbageIDs::comborange);
  1263. //Logger::writeToLog("inValue:"+String(newValue));
  1264. min = getGUICtrls(index).getNumProp(CabbageIDs::min);
  1265. if(getGUICtrls(index).getStringProp(CabbageIDs::type)==CabbageIDs::xypad)
  1266. newValue = (jmax(0.f, newValue)*range)+min;
  1267. else if(getGUICtrls(index).getStringProp(CabbageIDs::type)==CabbageIDs::combobox)//combo box value need to be rounded...
  1268. newValue = (newValue*comboRange);
  1269. else if(getGUICtrls(index).getStringProp(CabbageIDs::type)==CabbageIDs::checkbox ||
  1270. getGUICtrls(index).getStringProp(CabbageIDs::type)==CabbageIDs::button)
  1271. range=1;
  1272. else
  1273. newValue = (newValue*range)+min;
  1274. #endif
  1275. if(getGUICtrls(index).getStringProp(CabbageIDs::type)==CabbageIDs::combobox &&
  1276. getGUICtrls(index).getStringProp(CabbageIDs::channeltype)==CabbageIDs::stringchannel)
  1277. {
  1278. stringMessage = getGUICtrls(index).getStringArrayPropValue(CabbageIDs::text, newValue-1);
  1279. Logger::writeToLog(stringMessage);
  1280. messageQueue.addOutgoingChannelMessageToQueue(guiCtrls.getReference(index).getStringProp(CabbageIDs::channel),
  1281. stringMessage,
  1282. CabbageIDs::stringchannel);
  1283. }
  1284. else
  1285. {
  1286. messageQueue.addOutgoingChannelMessageToQueue(guiCtrls.getReference(index).getStringProp(CabbageIDs::channel),
  1287. newValue,
  1288. guiCtrls.getReference(index).getStringProp(CabbageIDs::type));
  1289. }
  1290. guiCtrls.getReference(index).setNumProp(CabbageIDs::value, newValue);
  1291. }
  1292. #endif
  1293. }
  1294. //==============================================================================
  1295. //this method gets called after a performKsmps() to update our GUI controls
  1296. //with messages from Csound. For instance, a user might wish to change the value
  1297. //of a GUI slider from Csound by using a chnset opcode. The speed at which this is
  1298. //updated can be teaked, so as not to hog resources...
  1299. void CabbagePluginAudioProcessor::updateCabbageControls()
  1300. {
  1301. #ifndef Cabbage_No_Csound
  1302. String chanName, channelMessage;
  1303. if(csCompileResult==OK)
  1304. {
  1305. //update all control widgets
  1306. for(int index=0; index<getGUICtrlsSize(); index++)
  1307. {
  1308. if(guiCtrls[index].getStringProp(CabbageIDs::channeltype).equalsIgnoreCase(CabbageIDs::stringchannel))
  1309. {
  1310. //THIS NEEDS TO ALLOW COMBOBOXEX THAT CONTAIN SNAPSHOTS TO UPDATE..
  1311. }
  1312. else
  1313. {
  1314. float value = csound->GetChannel(guiCtrls[index].getStringProp(CabbageIDs::channel).getCharPointer());
  1315. //Logger::writeToLog("Channel:"+guiCtrls[index].getStringProp(CabbageIDs::channel));
  1316. //Logger::writeToLog("value:"+String(value));
  1317. if(value!=guiCtrls.getReference(index).getNumProp(CabbageIDs::value))
  1318. {
  1319. guiCtrls.getReference(index).setNumProp(CabbageIDs::value, value);
  1320. dirtyControls.addIfNotAlreadyThere(index);
  1321. }
  1322. }
  1323. if(guiCtrls[index].getStringProp(CabbageIDs::identchannel).isNotEmpty())
  1324. {
  1325. //if controls has an identifier channel send data from Csound to control
  1326. char string[1024] = {0};
  1327. csound->GetStringChannel(guiCtrls[index].getStringProp(CabbageIDs::identchannel).toUTF8().getAddress(), string);
  1328. channelMessage = String(string);
  1329. if(channelMessage!="")
  1330. {
  1331. guiCtrls.getReference(index).setStringProp(CabbageIDs::identchannelmessage, channelMessage.trim());
  1332. guiCtrls.getReference(index).parse(" "+channelMessage, "");
  1333. dirtyControls.addIfNotAlreadyThere(index);
  1334. }
  1335. //else
  1336. //guiCtrls.getReference(index).setStringProp(CabbageIDs::identchannelmessage, "");
  1337. //zero channel message so that we don't keep sending the same string
  1338. csound->SetChannel(guiCtrls[index].getStringProp(CabbageIDs::identchannel).toUTF8().getAddress(), "");
  1339. }
  1340. }
  1341. //update all layout control widgets
  1342. //currently this is only needed for table widgets as other layout controls
  1343. //don't use channel messages...
  1344. for(int index=0; index<getGUILayoutCtrlsSize(); index++)
  1345. {
  1346. if(guiLayoutCtrls[index].getStringProp(CabbageIDs::type)==CabbageIDs::table)
  1347. {
  1348. for(int y=0; y<guiLayoutCtrls[index].getStringArrayProp(CabbageIDs::channel).size(); y++)
  1349. {
  1350. //String test = getGUILayoutCtrls(index).getStringArrayPropValue(CabbageIDs::channel, y);
  1351. float value = csound->GetChannel(guiLayoutCtrls[index].getStringArrayPropValue(CabbageIDs::channel, y).getCharPointer());
  1352. guiLayoutCtrls[index].setTableChannelValues(y, value);
  1353. }
  1354. }
  1355. if(guiLayoutCtrls[index].getStringProp(CabbageIDs::identchannel).isNotEmpty())
  1356. {
  1357. char string[1024] = {0};
  1358. //guiLayoutCtrls[index].getStringProp(CabbageIDs::identchannel).toUTF8().getAddress()
  1359. csound->GetStringChannel(guiLayoutCtrls[index].getStringProp(CabbageIDs::identchannel).toUTF8().getAddress(), string);
  1360. channelMessage = String(string);
  1361. //Logger::writeToLog(guiLayoutCtrls[index].getStringProp(CabbageIDs::identchannel));
  1362. if(channelMessage!="")
  1363. {
  1364. guiLayoutCtrls.getReference(index).parse(" "+channelMessage, "");
  1365. guiLayoutCtrls.getReference(index).setStringProp(CabbageIDs::identchannelmessage, channelMessage.trim());
  1366. }
  1367. //else
  1368. // guiLayoutCtrls.getReference(index).setStringProp(CabbageIDs::identchannelmessage, "");
  1369. //zero channel message so that we don't keep sending the same string
  1370. csound->SetChannel(guiLayoutCtrls[index].getStringProp(CabbageIDs::identchannel).toUTF8().getAddress(), "");
  1371. }
  1372. }
  1373. }
  1374. sendChangeMessage();
  1375. #endif
  1376. }
  1377. //==============================================================================
  1378. //this method only gets called when it's safe to do so, i.e., between calls to performKsmps()
  1379. //this method sends any channel messages that are in the queue to from Cabbage to Csound
  1380. void CabbagePluginAudioProcessor::sendOutgoingMessagesToCsound()
  1381. {
  1382. #ifndef Cabbage_No_Csound
  1383. if(csCompileResult==OK)
  1384. {
  1385. #ifndef Cabbage_Build_Standalone
  1386. if (getPlayHead() != 0 && getPlayHead()->getCurrentPosition (hostInfo))
  1387. csound->SetChannel(CabbageIDs::hostbpm.toUTF8(), hostInfo.bpm);
  1388. if (getPlayHead() != 0 && getPlayHead()->getCurrentPosition (hostInfo))
  1389. csound->SetChannel(CabbageIDs::timeinseconds.toUTF8(), hostInfo.timeInSeconds);
  1390. if (getPlayHead() != 0 && getPlayHead()->getCurrentPosition (hostInfo))
  1391. csound->SetChannel(CabbageIDs::isplaying.toUTF8(), hostInfo.isPlaying);
  1392. if (getPlayHead() != 0 && getPlayHead()->getCurrentPosition (hostInfo))
  1393. csound->SetChannel(CabbageIDs::isrecording.toUTF8(), hostInfo.isRecording);
  1394. if (getPlayHead() != 0 && getPlayHead()->getCurrentPosition (hostInfo))
  1395. csound->SetChannel(CabbageIDs::hostppqpos.toUTF8(), hostInfo.ppqPosition);
  1396. #endif
  1397. for(int i=0; i<messageQueue.getNumberOfOutgoingChannelMessagesInQueue(); i++)
  1398. {
  1399. //Logger::writeToLog("MessageType:"+messageQueue.getOutgoingChannelMessageFromQueue(i).type);
  1400. if(messageQueue.getOutgoingChannelMessageFromQueue(i).type=="directoryList")
  1401. {
  1402. for(int y=0; y<scoreEvents.size(); y++)
  1403. csound->InputMessage(scoreEvents[y].toUTF8());
  1404. //scoreEvents.clear();
  1405. }
  1406. //update Csound function tables with values from table widget
  1407. else if(messageQueue.getOutgoingChannelMessageFromQueue(i).type=="updateTable")
  1408. {
  1409. //Logger::writeToLog(messageQueue.getOutgoingChannelMessageFromQueue(i).fStatement.toUTF8());
  1410. csound->InputMessage(messageQueue.getOutgoingChannelMessageFromQueue(i).fStatement.getCharPointer());
  1411. }
  1412. //catch string messags
  1413. else if(messageQueue.getOutgoingChannelMessageFromQueue(i).type==CabbageIDs::stringchannel)
  1414. {
  1415. Logger::writeToLog(messageQueue.getOutgoingChannelMessageFromQueue(i).channelName);
  1416. Logger::writeToLog(messageQueue.getOutgoingChannelMessageFromQueue(i).stringVal);
  1417. csound->SetChannel(messageQueue.getOutgoingChannelMessageFromQueue(i).channelName.getCharPointer(),
  1418. messageQueue.getOutgoingChannelMessageFromQueue(i).stringVal.toUTF8().getAddress());
  1419. }
  1420. else
  1421. csound->SetChannel(messageQueue.getOutgoingChannelMessageFromQueue(i).channelName.getCharPointer(),
  1422. messageQueue.getOutgoingChannelMessageFromQueue(i).value);
  1423. }
  1424. messageQueue.flushOutgoingChannelMessages();
  1425. }
  1426. #endif
  1427. }
  1428. //========================================================================
  1429. // Standard plugin methods, getName, getNumParameters, setParamterName, get ProgramName, etc....
  1430. //==============================================================================
  1431. const String CabbagePluginAudioProcessor::getName() const
  1432. {
  1433. return pluginName;
  1434. }
  1435. int CabbagePluginAudioProcessor::getNumParameters()
  1436. {
  1437. return guiCtrls.size();
  1438. }
  1439. const String CabbagePluginAudioProcessor::getParameterName (int index)
  1440. {
  1441. if(index<(int)guiCtrls.size())//make sure index isn't out of range
  1442. return guiCtrls.getReference(index).getStringProp(CabbageIDs::channel);
  1443. else return String::empty;
  1444. }
  1445. const String CabbagePluginAudioProcessor::getParameterText (int index)
  1446. {
  1447. if(index<(int)guiCtrls.size())//make sure index isn't out of range
  1448. return String (guiCtrls.getReference(index).getNumProp(CabbageIDs::value), 2);
  1449. else return String::empty;
  1450. }
  1451. const String CabbagePluginAudioProcessor::getInputChannelName (int channelIndex) const
  1452. {
  1453. if(channelIndex<(int)guiCtrls.size())//make sure index isn't out of range
  1454. return String (channelIndex + 1);
  1455. else return String::empty;
  1456. }
  1457. const String CabbagePluginAudioProcessor::getOutputChannelName (int channelIndex) const
  1458. {
  1459. if(channelIndex<(int)guiCtrls.size())//make sure index isn't out of range
  1460. return String (channelIndex + 1);
  1461. else return String::empty;
  1462. }
  1463. bool CabbagePluginAudioProcessor::isInputChannelStereoPair (int /*index*/) const
  1464. {
  1465. return true;
  1466. }
  1467. bool CabbagePluginAudioProcessor::isOutputChannelStereoPair (int /*index*/) const
  1468. {
  1469. return true;
  1470. }
  1471. bool CabbagePluginAudioProcessor::acceptsMidi() const
  1472. {
  1473. #if JucePlugin_WantsMidiInput
  1474. return true;
  1475. #else
  1476. return false;
  1477. #endif
  1478. }
  1479. bool CabbagePluginAudioProcessor::producesMidi() const
  1480. {
  1481. #if JucePlugin_ProducesMidiOutput
  1482. return true;
  1483. #else
  1484. return false;
  1485. #endif
  1486. }
  1487. void CabbagePluginAudioProcessor::setGuiEnabled(bool val)
  1488. {
  1489. #if defined(Cabbage_Build_Standalone) && !defined(AndroidBuild)
  1490. guiON = val;
  1491. CabbagePluginAudioProcessorEditor* editor = dynamic_cast< CabbagePluginAudioProcessorEditor*>(this->getActiveEditor());
  1492. if(editor)
  1493. {
  1494. if(val==false)
  1495. if(getPreference(appProperties, "ExternalEditor"))
  1496. csdFile.replaceWithText(codeEditor->getAllText());
  1497. }
  1498. #endif
  1499. }
  1500. int CabbagePluginAudioProcessor::getNumPrograms()
  1501. {
  1502. return 0;
  1503. }
  1504. int CabbagePluginAudioProcessor::getCurrentProgram()
  1505. {
  1506. return 0;
  1507. }
  1508. void CabbagePluginAudioProcessor::setCurrentProgram (int /*index*/)
  1509. {
  1510. }
  1511. const String CabbagePluginAudioProcessor::getProgramName (int /*index*/)
  1512. {
  1513. return String::empty;
  1514. }
  1515. void CabbagePluginAudioProcessor::changeProgramName (int /*index*/, const String& /*newName*/)
  1516. {
  1517. }
  1518. //==============================================================================
  1519. void CabbagePluginAudioProcessor::prepareToPlay (double sampRate, int samplesPerBlock)
  1520. {
  1521. // Use this method as the place to do any pre-playback
  1522. // initialisation that you need..
  1523. //showMessage(String(this->getTotalNumOutputChannels()));
  1524. keyboardState.reset();
  1525. sampleRate = sampRate;
  1526. }
  1527. //==============================================================================
  1528. void CabbagePluginAudioProcessor::releaseResources()
  1529. {
  1530. // When playback stops, you can use this as an opportunity to free up any
  1531. // spare memory, etc.
  1532. keyboardState.reset();
  1533. }
  1534. //==============================================================================
  1535. //this following callback only runs in plugin mode, and only when one of the
  1536. //host widgets are being used
  1537. void CabbagePluginAudioProcessor::timerCallback()
  1538. {
  1539. #ifndef Cabbage_No_Csound
  1540. for(int y=0; y<xyAutomation.size(); y++)
  1541. {
  1542. if(xyAutomation[y])
  1543. xyAutomation[y]->update();
  1544. }
  1545. #endif
  1546. }
  1547. //==============================================================================
  1548. void CabbagePluginAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
  1549. {
  1550. float* audioBuffer;
  1551. int numSamples = buffer.getNumSamples();
  1552. if(stopProcessing || isGuiEnabled())
  1553. {
  1554. buffer.clear();
  1555. }
  1556. else{
  1557. #ifndef Cabbage_No_Csound
  1558. if(csCompileResult==OK)
  1559. {
  1560. keyboardState.processNextMidiBuffer (midiMessages, 0, buffer.getNumSamples(), true);
  1561. midiBuffer = midiMessages;
  1562. ccBuffer = midiMessages;
  1563. //if no inputs are used clear buffer in case it's not empty..
  1564. if(getTotalNumInputChannels()==0)
  1565. buffer.clear();
  1566. #if JucePlugin_ProducesMidiOutput
  1567. if(!midiOutputBuffer.isEmpty())
  1568. midiMessages.swapWith(midiOutputBuffer);
  1569. #endif
  1570. for(int i=0; i<numSamples; i++, csndIndex++)
  1571. {
  1572. if(csndIndex == csound->GetKsmps())
  1573. {
  1574. getCallbackLock().enter();
  1575. //slow down calls to these functions, no need for them to be firing at k-rate
  1576. yieldCounter = (yieldCounter>guiRefreshRate) ? 0 : yieldCounter+1;
  1577. if(yieldCounter==0)
  1578. {
  1579. sendOutgoingMessagesToCsound();
  1580. updateCabbageControls();
  1581. }
  1582. csCompileResult = csound->PerformKsmps();
  1583. if(csCompileResult!=OK)
  1584. stopProcessing = true;
  1585. else
  1586. ksmpsOffset++;
  1587. getCallbackLock().exit();
  1588. csndIndex = 0;
  1589. }
  1590. if(csCompileResult==OK)
  1591. {
  1592. for(int channel = 0; channel < getTotalNumOutputChannels(); channel++ )
  1593. {
  1594. audioBuffer = buffer.getWritePointer(channel,0);
  1595. pos = csndIndex*getTotalNumOutputChannels();
  1596. CSspin[channel+pos] = audioBuffer[i]*cs_scale;
  1597. audioBuffer[i] = (CSspout[channel+pos]/cs_scale);
  1598. }
  1599. }
  1600. else
  1601. buffer.clear();
  1602. }
  1603. if (activeWriter != 0 && !isWinXP)
  1604. activeWriter->write (buffer.getArrayOfReadPointers(), buffer.getNumSamples());
  1605. }//if not compiled just mute output
  1606. else
  1607. {
  1608. for(int channel = 0; channel < getTotalNumInputChannels(); channel++)
  1609. {
  1610. audioBuffer = buffer.getWritePointer(channel,0);
  1611. for(int i=0; i<numSamples; i++, csndIndex++)
  1612. {
  1613. audioBuffer[i]=0;
  1614. }
  1615. }
  1616. }
  1617. #endif
  1618. }
  1619. #if JucePlugin_ProducesMidiOutput
  1620. if(!midiBuffer.isEmpty())
  1621. midiMessages.swapWith(midiOutputBuffer);
  1622. #endif
  1623. }
  1624. //==============================================================================
  1625. // breakpoint functions
  1626. //==============================================================================
  1627. void CabbagePluginAudioProcessor::setCsoundInstrumentBreakpoint(int instr, int line=0)
  1628. {
  1629. #if defined(BUILD_DEBUGGER) && !defined(Cabbage_No_Csound)
  1630. getCallbackLock().enter();
  1631. if(breakpointInstruments.size()==0)
  1632. csoundDebuggerInit(csound->GetCsound());
  1633. csoundSetBreakpointCallback(csound->GetCsound(), breakpointCallback, (void*)this);
  1634. csoundSetInstrumentBreakpoint(csound->GetCsound(), instr, 0);
  1635. breakpointInstruments.add(instr);
  1636. getCallbackLock().exit();
  1637. #endif
  1638. }
  1639. void CabbagePluginAudioProcessor::removeCsoundInstrumentBreakpoint(int instr)
  1640. {
  1641. #if defined(BUILD_DEBUGGER) && !defined(Cabbage_No_Csound)
  1642. getCallbackLock().enter();
  1643. breakpointInstruments.removeAllInstancesOf(instr);
  1644. if(breakpointInstruments.size()==0)
  1645. breakCount=0;
  1646. csoundDebuggerInit(csound->GetCsound());
  1647. csoundRemoveInstrumentBreakpoint(csound->GetCsound(), instr);
  1648. getCallbackLock().exit();
  1649. #endif
  1650. }
  1651. void CabbagePluginAudioProcessor::continueCsoundDebug()
  1652. {
  1653. #if defined(BUILD_DEBUGGER) && !defined(Cabbage_No_Csound)
  1654. if(breakpointInstruments.size()>0)
  1655. {
  1656. getCallbackLock().enter();
  1657. csoundDebugContinue(csound->GetCsound());
  1658. getCallbackLock().exit();
  1659. }
  1660. #endif
  1661. }
  1662. void CabbagePluginAudioProcessor::nextCsoundDebug()
  1663. {
  1664. #if defined(BUILD_DEBUGGER) && !defined(Cabbage_No_Csound)
  1665. //not yet implemented....
  1666. if(breakpointInstruments.size()>0)
  1667. {
  1668. getCallbackLock().enter();
  1669. //csoundDebugNext(csound->GetCsound());
  1670. getCallbackLock().exit();
  1671. }
  1672. #endif
  1673. }
  1674. void CabbagePluginAudioProcessor::cleanCsoundDebug()
  1675. {
  1676. #if defined(BUILD_DEBUGGER) && !defined(Cabbage_No_Csound)
  1677. csoundClearBreakpoints(csound->GetCsound());
  1678. #endif
  1679. }
  1680. //==============================================================================
  1681. // MIDI functions
  1682. //==============================================================================
  1683. #ifndef Cabbage_No_Csound
  1684. int CabbagePluginAudioProcessor::OpenMidiInputDevice(CSOUND * csound, void **userData, const char* /*devName*/)
  1685. {
  1686. *userData = csoundGetHostData(csound);
  1687. if(!userData)
  1688. cout << "\n\ncan't open midi in\n\n";
  1689. return 0;
  1690. }
  1691. //==============================================================================
  1692. // Reads MIDI input data from host, gets called every time there is MIDI input to our plugin
  1693. //==============================================================================
  1694. int CabbagePluginAudioProcessor::ReadMidiData(CSOUND* /*csound*/, void *userData,
  1695. unsigned char *mbuf, int nbytes)
  1696. {
  1697. CabbagePluginAudioProcessor *midiData = (CabbagePluginAudioProcessor *)userData;
  1698. if(!userData)
  1699. {
  1700. cout << "\n\nInvalid";
  1701. return 0;
  1702. }
  1703. int cnt=0;
  1704. if(!midiData->midiBuffer.isEmpty() && cnt <= (nbytes - 3))
  1705. {
  1706. MidiMessage message(0xf4, 0, 0, 0);
  1707. MidiBuffer::Iterator i (midiData->midiBuffer);
  1708. int messageFrameRelativeTothisProcess;
  1709. while (i.getNextEvent (message, messageFrameRelativeTothisProcess))
  1710. {
  1711. if(message.isNoteOn())
  1712. {
  1713. *mbuf++ = (unsigned char)0x90 + message.getChannel()-1;
  1714. *mbuf++ = (unsigned char)message.getNoteNumber();
  1715. *mbuf++ = (unsigned char)message.getVelocity();
  1716. cnt += 3;
  1717. }
  1718. else if(message.isNoteOff())
  1719. {
  1720. *mbuf++ = (unsigned char)0x80 + message.getChannel()-1;
  1721. *mbuf++ = (unsigned char)message.getNoteNumber();
  1722. *mbuf++ = (unsigned char)message.getVelocity();
  1723. cnt += 3;
  1724. }
  1725. else if(message.isAllSoundOff())
  1726. {
  1727. *mbuf++ = (unsigned char)0x7B + message.getChannel()-1;
  1728. *mbuf++ = (unsigned char)message.getNoteNumber();
  1729. *mbuf++ = (unsigned char)message.getVelocity();
  1730. cnt += 3;
  1731. }
  1732. else if(message.isController())
  1733. {
  1734. *mbuf++ = (unsigned char)0xB0 + message.getChannel()-1;
  1735. *mbuf++ = (unsigned char)message.getControllerNumber();
  1736. *mbuf++ = (unsigned char)message.getControllerValue();
  1737. cnt += 3;
  1738. }
  1739. else if(message.isProgramChange())
  1740. {
  1741. *mbuf++ = (unsigned char)0xC0 + message.getChannel()-1;
  1742. *mbuf++ = (unsigned char)message.getProgramChangeNumber();
  1743. cnt += 2;
  1744. }
  1745. else if(message.isPitchWheel())
  1746. {
  1747. *mbuf++ = (unsigned char)0xE0 + message.getChannel()-1;
  1748. *mbuf++ = (unsigned char)message.getPitchWheelValue();
  1749. cnt += 3;
  1750. }
  1751. }
  1752. midiData->midiBuffer.clear();
  1753. }
  1754. return cnt;
  1755. }
  1756. //==============================================================================
  1757. // Opens MIDI output device, adding -QN to your CsOptions will causes this method to be called
  1758. // as soon as your plugin loads
  1759. //==============================================================================
  1760. int CabbagePluginAudioProcessor::OpenMidiOutputDevice(CSOUND * csound, void **userData, const char* /*devName*/)
  1761. {
  1762. *userData = csoundGetHostData(csound);
  1763. if(!userData)
  1764. Logger::writeToLog("\n\ncan't open midi out\n\n");
  1765. return 0;
  1766. }
  1767. //==============================================================================
  1768. // Write MIDI data to plugin's MIDI output. Each time Csound outputs a midi message this
  1769. // method should be called. Note: you must have -Q set in your CsOptions
  1770. //==============================================================================
  1771. int CabbagePluginAudioProcessor::WriteMidiData(CSOUND* /*csound*/, void *_userData,
  1772. const unsigned char *mbuf, int nbytes)
  1773. {
  1774. CabbagePluginAudioProcessor *userData = (CabbagePluginAudioProcessor *)_userData;
  1775. if(!userData)
  1776. {
  1777. Logger::writeToLog("\n\nInvalid");
  1778. return 0;
  1779. }
  1780. MidiMessage message(mbuf, nbytes, 0);
  1781. //Logger::writeToLog(String(message.getNoteNumber()));
  1782. userData->midiOutputBuffer.addEvent(message, 0);
  1783. return nbytes;
  1784. }
  1785. #endif
  1786. //==============================================================================
  1787. bool CabbagePluginAudioProcessor::hasEditor() const
  1788. {
  1789. return true; // (change this to false if you choose to not supply an editor)
  1790. }
  1791. AudioProcessorEditor* CabbagePluginAudioProcessor::createEditor()
  1792. {
  1793. if(!nativePluginEditor)
  1794. return new CabbagePluginAudioProcessorEditor (this);
  1795. else
  1796. return new CabbageGenericAudioProcessorEditor (this);
  1797. }
  1798. //==============================================================================
  1799. void CabbagePluginAudioProcessor::getStateInformation (MemoryBlock& destData)
  1800. {
  1801. // You should use this method to store your parameters in the memory block.
  1802. // Here's an example of how you can use XML to make it easy and more robust:
  1803. // Create an outer XML element..
  1804. XmlElement xml ("CABBAGE_PLUGIN_SETTINGS");
  1805. for(int i=0; i<guiCtrls.size(); i++)
  1806. xml.setAttribute(guiCtrls[i].getStringProp(CabbageIDs::channel),getParameter(i));
  1807. for(int i=0; i<guiLayoutCtrls.size(); i++)
  1808. if(guiLayoutCtrls[i].getStringProp(CabbageIDs::type)==CabbageIDs::filebutton)
  1809. {
  1810. //save filebutton last opened file...
  1811. char string[1024] = {0};
  1812. csound->GetStringChannel(guiLayoutCtrls[i].getStringProp(CabbageIDs::channel).toUTF8().getAddress(), string);
  1813. xml.setAttribute ("filebutton_"+guiLayoutCtrls[i].getStringProp(CabbageIDs::channel), String(string));
  1814. Logger::writeToLog("filebutton_"+guiLayoutCtrls[i].getStringProp(CabbageIDs::channel));
  1815. Logger::writeToLog(String(string));
  1816. }
  1817. // then use this helper function to stuff it into the binary blob and return it..
  1818. copyXmlToBinary (xml, destData);
  1819. }
  1820. void CabbagePluginAudioProcessor::setStateInformation (const void* data, int sizeInBytes)
  1821. {
  1822. // You should use this method to restore your parameters from this memory block,
  1823. // whose contents will have been created by the getStateInformation() call.
  1824. // This getXmlFromBinary() helper function retrieves our XML from the binary blob..
  1825. ScopedPointer<XmlElement> xmlState (getXmlFromBinary (data, sizeInBytes));
  1826. if (xmlState != nullptr)
  1827. {
  1828. // make sure that it's actually our type of XML object..
  1829. if (xmlState->hasTagName ("CABBAGE_PLUGIN_SETTINGS"))
  1830. {
  1831. for(int i=0; i<this->getNumParameters(); i++)
  1832. {
  1833. this->setParameter(i, (float)xmlState->getDoubleAttribute(guiCtrls[i].getStringProp(CabbageIDs::channel)));
  1834. }
  1835. for(int i=0;i<xmlState->getNumAttributes();i++)
  1836. {
  1837. //if the instrument uses a filebutton, then retreive the last known
  1838. //file that was loaded with it
  1839. if(xmlState->getAttributeName(i).contains("filebutton_"))
  1840. {
  1841. String channel = xmlState->getAttributeName(i).substring(11);
  1842. //Logger::writeToLog(xmlState->getAttributeValue(i));
  1843. csound->SetChannel(channel.toUTF8().getAddress(), xmlState->getAttributeValue(i).toUTF8().getAddress());
  1844. }
  1845. }
  1846. }
  1847. }
  1848. }
  1849. //==============================================================================