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.

1567 lines
58KB

  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. #define LOGGER 0
  21. //these two lines may need to be copied to top part of csound.h
  22. //#define int32 int
  23. //#define uint32 unsigned int
  24. CabbageLookAndFeel* lookAndFeel;
  25. CabbageLookAndFeelBasic* lookAndFeelBasic;
  26. //==============================================================================
  27. // There are two different CabbagePluginAudioProcessor constructors. One for the
  28. // standalone application and the other for the plugin library
  29. //==============================================================================
  30. //#ifdef Cabbage_Build_Standalone
  31. #if defined(Cabbage_Build_Standalone) || defined(Cabbage_Plugin_Host)
  32. //===========================================================
  33. // STANDALONE - CONSTRUCTOR
  34. //===========================================================
  35. CabbagePluginAudioProcessor::CabbagePluginAudioProcessor(String inputfile, bool guiOnOff, int _pluginType)
  36. :csoundStatus(false),
  37. csdFile(File(inputfile)),
  38. showMIDI(false),
  39. csCompileResult(1),
  40. changeMessageType(""),
  41. guiON(false),
  42. currentLine(-99),
  43. noSteps(0),
  44. noPatterns(0),
  45. timeCounter(0),
  46. beat(0),
  47. bpm(120),
  48. patMatrixActive(0),
  49. masterCounter(0),
  50. xyAutosCreated(false),
  51. updateTable(false),
  52. yieldCallbackBool(false),
  53. yieldCounter(10),
  54. isNativeThreadRunning(false),
  55. soundFileIndex(0),
  56. scoreEvents(),
  57. nativePluginEditor(false),
  58. averageSampleIndex(0),
  59. pluginType(_pluginType),
  60. automationAmp(0),
  61. isAutomator(false),
  62. automationParamID(-1),
  63. debugMessage(""),
  64. guiRefreshRate(20)
  65. {
  66. //suspendProcessing(true);
  67. codeEditor = nullptr;
  68. #ifdef Cabbage_Logger
  69. logFile = File((appProperties->getCommonSettings(true)->getFile().getParentDirectory().getFullPathName()+"/CabbageLog.txt"));
  70. fileLogger = new FileLogger(logFile, String("Cabbage Log.."));
  71. Logger::setCurrentLogger(fileLogger);
  72. #endif
  73. //reset patMatrix. If this has more than one we know that
  74. //pattern matrix object is being used
  75. patStepMatrix.clear();
  76. patPfieldMatrix.clear();
  77. setPlayConfigDetails(2, 2, 44100, 512);
  78. #ifndef Cabbage_No_Csound
  79. //don't start of run Csound in edit mode
  80. setOpcodeDirEnv();
  81. csound = new Csound();
  82. #ifdef CSOUND6
  83. csound->SetHostImplementedMIDIIO(true);
  84. #endif
  85. csound->Reset();
  86. //Logger::writeToLog(csound->GetEnv("OPCODEDIR64"));
  87. #ifdef CSOUND5
  88. csound->PreCompile();
  89. #endif
  90. csound->SetHostData(this);
  91. csound->SetMessageCallback(CabbagePluginAudioProcessor::messageCallback);
  92. csound->SetExternalMidiInOpenCallback(OpenMidiInputDevice);
  93. csound->SetExternalMidiReadCallback(ReadMidiData);
  94. csound->SetExternalMidiOutOpenCallback(OpenMidiOutputDevice);
  95. csound->SetExternalMidiWriteCallback(WriteMidiData);
  96. #ifndef Cabbage_Plugin_Host
  97. if(!getPreference(appProperties, "UseCabbageIO")){
  98. csoundPerfThread = new CsoundPerformanceThread(csound);
  99. csoundPerfThread->SetProcessCallback(CabbagePluginAudioProcessor::YieldCallback, (void*)this);
  100. }
  101. #endif
  102. if(pluginType==AUTOMATION_PLUGIN)
  103. isAutomator = true;
  104. csoundChanList = NULL;
  105. numCsoundChannels = 0;
  106. csndIndex = 32;
  107. //set up PVS struct
  108. dataout = new PVSDATEXT;
  109. if(inputfile.isNotEmpty()){
  110. File(inputfile).setAsCurrentWorkingDirectory();
  111. #ifdef CSOUND6
  112. csoundParams = new CSOUND_PARAMS();
  113. csoundParams->nchnls_override =2;
  114. csound->SetParams(csoundParams);
  115. #endif
  116. csCompileResult = csound->Compile(const_cast<char*>(inputfile.toUTF8().getAddress()));
  117. if(csCompileResult==0){
  118. //send root directory path to Csound.
  119. setPlayConfigDetails(getNumberCsoundOutChannels(),
  120. getNumberCsoundOutChannels(),
  121. getCsoundSamplingRate(),
  122. getCsoundKsmpsSize());
  123. //simple hack to allow tables to be set up correctly.
  124. csound->PerformKsmps();
  125. csound->SetScoreOffsetSeconds(0);
  126. csound->RewindScore();
  127. #ifdef WIN32
  128. csound->SetChannel("CSD_PATH", File(inputfile).getParentDirectory().getFullPathName().replace("\\", "\\\\").toUTF8().getAddress());
  129. #else
  130. csound->SetChannel("CSD_PATH", File(inputfile).getParentDirectory().getFullPathName().toUTF8().getAddress());
  131. #endif
  132. Logger::writeToLog("Csound compiled your file");
  133. //csound->SetYieldCallback(CabbagePluginAudioProcessor::yieldCallback);
  134. if(csound->GetSpout()==nullptr);
  135. CSspout = csound->GetSpout();
  136. CSspin = csound->GetSpin();
  137. numCsoundChannels = csoundListChannels(csound->GetCsound(), &csoundChanList);
  138. csndIndex = csound->GetKsmps();
  139. csdKsmps = csound->GetKsmps();
  140. soundFilerTempVector = new MYFLT[csdKsmps];
  141. cs_scale = csound->Get0dBFS();
  142. csoundStatus = true;
  143. debugMessageArray.add(CABBAGE_VERSION);
  144. debugMessageArray.add(String("\n"));
  145. this->setLatencySamples(csound->GetKsmps());
  146. updateHostDisplay();
  147. }
  148. else{
  149. Logger::writeToLog("Csound couldn't compile your file");
  150. csoundStatus=false;
  151. //debugMessage = "Csound did not compile correctly. Check for snytax errors by compiling with WinXound";
  152. }
  153. }
  154. else
  155. Logger::writeToLog("Welcome to Cabbage");
  156. #endif
  157. lookAndFeel = new CabbageLookAndFeel();
  158. lookAndFeelBasic = new CabbageLookAndFeelBasic();
  159. }
  160. #else
  161. //===========================================================
  162. // PLUGIN - CONSTRUCTOR
  163. //===========================================================
  164. CabbagePluginAudioProcessor::CabbagePluginAudioProcessor():
  165. csoundStatus(false),
  166. showMIDI(false),
  167. csCompileResult(1),
  168. changeMessageType(""),
  169. guiON(false),
  170. currentLine(-99),
  171. noSteps(0),
  172. noPatterns(0),
  173. timeCounter(0),
  174. beat(0),
  175. bpm(120),
  176. patMatrixActive(0),
  177. masterCounter(0),
  178. xyAutosCreated(false),
  179. updateTable(false),
  180. yieldCallbackBool(false),
  181. yieldCounter(10),
  182. soundFileIndex(0),
  183. nativePluginEditor(false),
  184. averageSampleIndex(0)
  185. {
  186. //Cabbage plugins always try to load a csd file with the same name as the plugin library.
  187. //Therefore we need to find the name of the library and append a '.csd' to it.
  188. #ifdef MACOSX
  189. String osxCSD = File::getSpecialLocation(File::currentApplicationFile).getFullPathName()+String("/Contents/")+File::getSpecialLocation(File::currentApplicationFile).getFileName();
  190. File thisFile(osxCSD);
  191. Logger::writeToLog("MACOSX defined OK");
  192. #else
  193. File thisFile(File::getSpecialLocation(File::currentExecutableFile));
  194. #endif
  195. csdFile = thisFile.withFileExtension(String(".csd")).getFullPathName();
  196. Logger::writeToLog(File::getSpecialLocation(File::currentExecutableFile).getFullPathName());
  197. if(csdFile.exists())
  198. Logger::writeToLog("File exists:"+String(csdFile.getFullPathName()));
  199. else
  200. Logger::writeToLog("File doesn't exist"+String(csdFile.getFullPathName()));
  201. File(csdFile.getFullPathName()).setAsCurrentWorkingDirectory();
  202. //set logger
  203. #ifdef Cabbage_Logger
  204. logFile = File(File(csdFile.getFullPathName()).getParentDirectory().getFullPathName()+"/CabbageLog.txt");
  205. fileLogger = new FileLogger(logFile, String("Cabbage Log.."));
  206. Logger::setCurrentLogger(fileLogger);
  207. #endif
  208. setOpcodeDirEnv();
  209. #ifndef Cabbage_No_Csound
  210. csound = new Csound();
  211. csound->SetHostImplementedMIDIIO(true);
  212. //csound->Reset();
  213. //csound->PreCompile();
  214. csound->SetHostData(this);
  215. midiOutputBuffer.clear();
  216. //for host midi to get sent to Csound, don't need this for standalone
  217. //but might use it in the future for midi mapping to controls
  218. csound->SetMessageCallback(CabbagePluginAudioProcessor::messageCallback);
  219. csound->SetExternalMidiInOpenCallback(OpenMidiInputDevice);
  220. csound->SetExternalMidiReadCallback(ReadMidiData);
  221. csound->SetExternalMidiOutOpenCallback(OpenMidiOutputDevice);
  222. csound->SetExternalMidiWriteCallback(WriteMidiData);
  223. patStepMatrix.clear();
  224. patternNames.clear();
  225. patPfieldMatrix.clear();
  226. csoundChanList = NULL;
  227. numCsoundChannels = 0;
  228. csndIndex = 32;
  229. startTimer(20);
  230. #ifdef CSOUND6
  231. csoundParams = new CSOUND_PARAMS();
  232. csoundParams->nchnls_override =2;
  233. csound->SetParams(csoundParams);
  234. #endif
  235. csdFile.setAsCurrentWorkingDirectory();
  236. csCompileResult = csound->Compile(const_cast<char*>(csdFile.getFullPathName().toUTF8().getAddress()));
  237. csdFile.setAsCurrentWorkingDirectory();
  238. if(csCompileResult==0){
  239. Logger::writeToLog("compiled Ok");
  240. keyboardState.allNotesOff(0);
  241. keyboardState.reset();
  242. //simple hack to allow tables to be set up correctly.
  243. csound->PerformKsmps();
  244. csound->SetScoreOffsetSeconds(0);
  245. csound->RewindScore();
  246. //set up PVS struct
  247. dataout = new PVSDATEXT;
  248. csdKsmps = csound->GetKsmps();
  249. soundFilerTempVector = new MYFLT[csdKsmps];
  250. if(csound->GetSpout()==nullptr);
  251. CSspout = csound->GetSpout();
  252. CSspin = csound->GetSpin();
  253. cs_scale = csound->Get0dBFS();
  254. numCsoundChannels = csoundListChannels(csound->GetCsound(), &csoundChanList);
  255. csndIndex = csound->GetKsmps();
  256. this->setLatencySamples(csound->GetKsmps());
  257. updateHostDisplay();
  258. //soundFilerVector = new MYFLT[csdKsmps];
  259. csoundStatus = true;
  260. debugMessageArray.add(VERSION);
  261. debugMessageArray.add(String("\n"));
  262. #ifdef WIN32
  263. csound->SetChannel("CSD_PATH", File(csdFile).getParentDirectory().getFullPathName().replace("\\", "\\\\").toUTF8().getAddress());
  264. #else
  265. csound->SetChannel("CSD_PATH", File(csdFile).getParentDirectory().getFullPathName().toUTF8().getAddress());
  266. #endif
  267. //send host info before performance..
  268. if (getPlayHead() != 0 && getPlayHead()->getCurrentPosition (hostInfo))
  269. csound->SetChannel(CabbageIDs::hostbpm.toUTF8(), hostInfo.bpm);
  270. if (getPlayHead() != 0 && getPlayHead()->getCurrentPosition (hostInfo))
  271. csound->SetChannel(CabbageIDs::timeinseconds.toUTF8(), hostInfo.timeInSeconds);
  272. if (getPlayHead() != 0 && getPlayHead()->getCurrentPosition (hostInfo))
  273. csound->SetChannel(CabbageIDs::isplaying.toUTF8(), hostInfo.isPlaying);
  274. if (getPlayHead() != 0 && getPlayHead()->getCurrentPosition (hostInfo))
  275. csound->SetChannel(CabbageIDs::isrecording.toUTF8(), hostInfo.isRecording);
  276. if (getPlayHead() != 0 && getPlayHead()->getCurrentPosition (hostInfo))
  277. csound->SetChannel(CabbageIDs::hostppqpos.toUTF8(), hostInfo.ppqPosition);
  278. }
  279. else{
  280. Logger::writeToLog("Csound couldn't compile your file");
  281. csoundStatus=false;
  282. }
  283. #endif
  284. createGUI(csdFile.loadFileAsString(), true);
  285. }
  286. #endif
  287. //===========================================================
  288. // PLUGIN - DESTRUCTOR
  289. //===========================================================
  290. CabbagePluginAudioProcessor::~CabbagePluginAudioProcessor()
  291. {
  292. deleteAndZero(lookAndFeel);
  293. deleteAndZero(lookAndFeelBasic);
  294. Logger::writeToLog("~CabbagePluginAudioProcessor()");
  295. Logger::setCurrentLogger (nullptr);
  296. suspendProcessing(true);
  297. removeAllChangeListeners();
  298. #ifndef Cabbage_No_Csound
  299. patStepMatrix.clear();
  300. patternNames.clear();
  301. patPfieldMatrix.clear();
  302. xyAutomation.clear();
  303. //const MessageManagerLock mmLock;
  304. if(csound){
  305. if(csoundPerfThread){
  306. csoundPerfThread->Stop();
  307. csoundPerfThread = nullptr;
  308. }
  309. //csound->SetHostImplementedMIDIIO(false);
  310. csound->DeleteChannelList(csoundChanList);
  311. Logger::writeToLog("about to cleanup Csound");
  312. //csound->Cleanup();
  313. //csound->SetHostImplementedMIDIIO(false);
  314. csound->Reset();
  315. csound = nullptr;
  316. Logger::writeToLog("Csound cleaned up");
  317. if(audioSourcesArray.size()>0){
  318. for(int i=0;i<audioSourcesArray.size();i++){
  319. audioSourcesArray[i]->sourceChannelInfo.buffer = nullptr;
  320. audioSourcesArray[i]->audioSourceBuffer = nullptr;
  321. }
  322. audioSourcesArray.clear();
  323. }
  324. }
  325. soundFilerTempVector = nullptr;
  326. #endif
  327. }
  328. int CabbagePluginAudioProcessor::performEntireScore(){
  329. #ifndef Cabbage_No_Csound
  330. if(!isNativeThreadRunning){
  331. csoundPerfThread->Play();
  332. isNativeThreadRunning = true;
  333. }
  334. #endif
  335. return 1;
  336. }
  337. //this callback will be employed when users run with Csound audio IO rather than Cabbage
  338. void CabbagePluginAudioProcessor::YieldCallback(void* data){
  339. CabbagePluginAudioProcessor *cabbage = (CabbagePluginAudioProcessor *)data;
  340. cabbage->sendOutgoingMessagesToCsound();
  341. cabbage->updateCabbageControls();
  342. }
  343. //============================================================================
  344. //RECOMPILE CSOUND. THIS IS CALLED FROM THE PLUGIN HOST WHEN UDPATES ARE MADE ON THE FLY
  345. //============================================================================
  346. void CabbagePluginAudioProcessor::reCompileCsound(File file)
  347. {
  348. #ifndef Cabbage_No_Csound
  349. suspendProcessing(true);
  350. soundFileIndex = 0;
  351. midiOutputBuffer.clear();
  352. getCallbackLock().enter();
  353. csound->DeleteChannelList(csoundChanList);
  354. csound->SetHostImplementedMIDIIO(true);
  355. csound->Reset();
  356. xyAutosCreated = false;
  357. //csound->SetMessageCallback(CabbagePluginAudioProcessor::messageCallback);
  358. numCsoundChannels = 0;
  359. //csound->SetParams(csoundParams);
  360. //csound->SetMessageCallback(CabbagePluginAudioProcessor::messageCallback);
  361. csound->SetExternalMidiInOpenCallback(OpenMidiInputDevice);
  362. csound->SetExternalMidiReadCallback(ReadMidiData);
  363. csound->SetExternalMidiOutOpenCallback(OpenMidiOutputDevice);
  364. csound->SetExternalMidiWriteCallback(WriteMidiData);
  365. CSspout = nullptr;
  366. CSspin = nullptr;
  367. csCompileResult = csound->Compile(const_cast<char*>(file.getFullPathName().toUTF8().getAddress()));
  368. if(csCompileResult==0){
  369. //simple hack to allow tables to be set up correctly.
  370. keyboardState.allNotesOff(0);
  371. keyboardState.reset();
  372. csndIndex = 0;
  373. CSspout = csound->GetSpout();
  374. CSspin = csound->GetSpin();
  375. csound->PerformKsmps();
  376. csound->SetScoreOffsetSeconds(0);
  377. csound->RewindScore();
  378. Logger::writeToLog("Csound compiled your file");
  379. numCsoundChannels = csoundListChannels(csound->GetCsound(), &csoundChanList);
  380. cs_scale = csound->Get0dBFS();
  381. csoundStatus = true;
  382. debugMessageArray.add(CABBAGE_VERSION);
  383. debugMessageArray.add(String("\n"));
  384. //removeAllChangeListeners();
  385. getCallbackLock().exit();
  386. //init all channels with their init val
  387. for(int i=0;i<guiCtrls.size();i++)
  388. {
  389. csound->SetChannel( guiCtrls.getReference(i).getStringProp(CabbageIDs::channel).toUTF8(),
  390. guiCtrls.getReference(i).getNumProp(CabbageIDs::value));
  391. }
  392. #ifdef WIN32
  393. csound->SetChannel("CSD_PATH", file.getParentDirectory().getFullPathName().replace("\\", "\\\\").toUTF8().getAddress());
  394. #else
  395. csound->SetChannel("CSD_PATH", file.getParentDirectory().getFullPathName().toUTF8().getAddress());
  396. #endif
  397. this->suspendProcessing(false);
  398. return;
  399. }
  400. else{
  401. Logger::writeToLog("Csound couldn't compile your file");
  402. csoundStatus=false;
  403. }
  404. getCallbackLock().exit();
  405. #endif
  406. }
  407. //===========================================================
  408. // PARSE CSD FILE AND FILL GUI/GUI-LAYOUT VECTORs.
  409. // NO JUCE WIDGETS GET CREATED IN THIS CLASS. ALL
  410. // GUI OBJECTS ARE CREATED ON THE FLY IN THE CABBAGE PLUGIN
  411. // EDITOR FROM INFORMATION HELD IN THE GUICONTROLS VECTOR
  412. //===========================================================
  413. //maybe this should only be done at the end of a k-rate cycle..
  414. void CabbagePluginAudioProcessor::createGUI(String source, bool refresh)
  415. {
  416. //clear arrays if refresh is set
  417. if(refresh==true){
  418. guiLayoutCtrls.clear();
  419. guiCtrls.clear();
  420. CabbagePluginAudioProcessorEditor* editor = dynamic_cast<CabbagePluginAudioProcessorEditor*>(this->getActiveEditor());
  421. if(editor){
  422. editor->comps.clear();
  423. editor->layoutComps.clear();
  424. }
  425. //if rebuilding the entire plugin, reset soundfilers, if present
  426. if(audioSourcesArray.size()>0){
  427. for(int i=0;i<audioSourcesArray.size();i++){
  428. audioSourcesArray[i]->sourceChannelInfo.buffer = nullptr;
  429. audioSourcesArray[i]->audioSourceBuffer = nullptr;
  430. }
  431. audioSourcesArray.clear();
  432. }
  433. }
  434. int indexOfLastGUICtrl = guiCtrls.size();
  435. int indexOfLastLayoutCtrl = guiLayoutCtrls.size();
  436. int test=100;
  437. int checkGUI = isGuiEnabled();
  438. //setGuiEnabled((false));
  439. int guiID=0;
  440. StringArray csdText;
  441. int lines=1;
  442. String csdLine("");
  443. csdText.addLines(source);
  444. bool multiComment = false;
  445. bool multiLine = false;
  446. //check for minimal Cabbage GUI
  447. for(int i=0;i<csdText.size();i++)
  448. {
  449. if(csdText[i].indexOfWholeWordIgnoreCase(String("</Cabbage>"))==-1)
  450. {
  451. if(!csdText[i].contains("multitab "))//we don't enter for multitab, plants need to be created first
  452. if(csdText[i].trim().isNotEmpty()){
  453. if(csdText[i].contains("), \\")||
  454. csdText[i].contains("),\\")||
  455. csdText[i].contains(") \\")){
  456. multiLine = true;
  457. csdLine="";
  458. lines=0;
  459. while(multiLine){
  460. if(csdText[i+lines].contains("), \\")||
  461. csdText[i+lines].contains("),\\")||
  462. csdText[i+lines].contains(") \\"))
  463. lines++;
  464. else multiLine=false;
  465. }
  466. for(int y=0;y<=lines;y++)
  467. csdLine = csdLine + " "+ csdText[i+y].trim()+" ";
  468. i=i+lines;
  469. }
  470. else
  471. csdLine = csdText[i];
  472. //tidy up string
  473. csdLine = csdLine.trimStart();
  474. //csdLine = csdLine.removeCharacters(" \\");
  475. //csdLine = csdLine.removeCharacters(",\\");
  476. //Logger::writeToLog(csdLine);
  477. StringArray tokes;
  478. tokes.addTokens(csdLine.trimEnd(), ", ", "\"");
  479. if(tokes[0].containsIgnoreCase(String("/*"))){
  480. multiComment = true;
  481. }
  482. if(tokes[0].containsIgnoreCase(String("*\\"))){
  483. multiComment = false;
  484. }
  485. if(tokes[0].containsIgnoreCase(String(";"))){
  486. //allows for single line comments
  487. }
  488. else if(tokes[0].containsIgnoreCase(String("}"))){
  489. plantFlag = ""; //reset plantFlag when a closing bracket is found
  490. presetFlag = "";
  491. }
  492. if(!multiComment)
  493. //populate the guiLayoutCtrls vector with non-interactive widgets
  494. //the host widgets aren't GUI based but they can be added to this
  495. //vector too, as can the editor button.
  496. if(tokes[0].equalsIgnoreCase(String("form"))
  497. ||tokes[0].equalsIgnoreCase(String("image"))
  498. ||tokes[0].equalsIgnoreCase(String("keyboard"))
  499. ||tokes[0].equalsIgnoreCase(String("csoundoutput"))
  500. ||tokes[0].equalsIgnoreCase(String("line"))
  501. ||tokes[0].equalsIgnoreCase(String("label"))
  502. ||tokes[0].equalsIgnoreCase(String("hostbpm"))
  503. ||tokes[0].equalsIgnoreCase(String("hosttime"))
  504. ||tokes[0].equalsIgnoreCase(String("hostplaying"))
  505. ||tokes[0].equalsIgnoreCase(String("hostppqpos"))
  506. ||tokes[0].equalsIgnoreCase(String("vumeter"))
  507. ||tokes[0].equalsIgnoreCase(String("patmatrix"))
  508. ||tokes[0].equalsIgnoreCase(String("source"))
  509. ||tokes[0].equalsIgnoreCase(String("multitab"))
  510. ||tokes[0].equalsIgnoreCase(String("infobutton"))
  511. ||tokes[0].equalsIgnoreCase(String("filebutton"))
  512. ||tokes[0].equalsIgnoreCase(String("soundfiler"))
  513. ||tokes[0].equalsIgnoreCase(String("snapshot"))
  514. ||tokes[0].equalsIgnoreCase(String("table"))
  515. ||tokes[0].equalsIgnoreCase(String("pvsview"))
  516. ||tokes[0].equalsIgnoreCase(String("hostrecording"))
  517. ||tokes[0].equalsIgnoreCase(String("directorylist"))
  518. ||tokes[0].equalsIgnoreCase(String("transport"))
  519. ||tokes[0].equalsIgnoreCase(String("groupbox")))
  520. {
  521. CabbageGUIClass cAttr(csdLine.trimEnd(), guiID);
  522. if(cAttr.getStringProp("native").length()>0){
  523. //create generic plugin editor and break..
  524. setupNativePluginEditor();
  525. nativePluginEditor = true;
  526. return;
  527. }
  528. if(cAttr.getNumProp(CabbageIDs::guirefresh)>1)
  529. guiRefreshRate = cAttr.getNumProp(CabbageIDs::guirefresh);
  530. //showMessage(cAttr.getStringProp("type"));
  531. csdLine = "";
  532. //add soundfiler buffering sources
  533. if(tokes[0].equalsIgnoreCase(String("soundfiler"))){
  534. addSoundfilerSource(cAttr.getStringProp(("file")), cAttr.getChannels());
  535. Logger::writeToLog("createGUI, soundfiler size:"+String(audioSourcesArray.size()-1));
  536. cAttr.setNumProp("soundfilerIndex", audioSourcesArray.size()-1);
  537. }
  538. //set up stuff for tables
  539. if(tokes[0].equalsIgnoreCase(String("table"))){
  540. if(cAttr.getStringArrayProp(CabbageIDs::channel).size()==0)
  541. for(int i=0;i<cAttr.getIntArrayProp("tablenumber").size();i++)
  542. cAttr.addDummyChannel("dummy"+String(i));
  543. for(int i=0;i<cAttr.getStringArrayProp(CabbageIDs::channel).size();i++)
  544. cAttr.addTableChannelValues();
  545. }
  546. //set up plant flag if needed for other widgets
  547. if(cAttr.getStringProp(String("plant")).isNotEmpty()){
  548. plantFlag = cAttr.getStringProp(String("plant"));
  549. presetFlag = cAttr.getStringProp(String("preset"));
  550. }
  551. else if(cAttr.getStringProp(String("reltoplant")).equalsIgnoreCase(String("")))
  552. cAttr.setStringProp(String("reltoplant"), plantFlag);
  553. guiLayoutCtrls.add(cAttr);
  554. guiID++;
  555. if(cAttr.getStringProp("type").containsIgnoreCase("form"))
  556. if(cAttr.getStringProp("text").length()>2)
  557. setPluginName(cAttr.getStringProp("text"));
  558. else if(cAttr.getStringProp("caption").length()>2)
  559. setPluginName(cAttr.getStringProp("caption"));
  560. else setPluginName("Untitled Cabbage Patch!");
  561. //StringArray log = logGUIAttributes(cAttr, String("Non-Interactive"));
  562. //debugMessageArray.addArray(logGUIAttributes(cAttr, String("Non-Interactive")));
  563. sendChangeMessage();
  564. //if instrument uses any of the host widgets, or an xypad, turn
  565. //on the timer
  566. if(tokes[0].equalsIgnoreCase(String("hostbpm"))
  567. ||tokes[0].equalsIgnoreCase(String("hosttime"))
  568. ||tokes[0].equalsIgnoreCase(String("hostplaying"))
  569. ||tokes[0].equalsIgnoreCase(String("hostppqpos"))
  570. ||tokes[0].equalsIgnoreCase(String("hostrecording")))
  571. startTimer(20);
  572. }
  573. //populate the guiCtrls vector with interactive widgets
  574. else if(tokes[0].equalsIgnoreCase(String("hslider"))
  575. ||tokes[0].equalsIgnoreCase(String("vslider"))
  576. ||tokes[0].equalsIgnoreCase(String("rslider"))
  577. ||tokes[0].equalsIgnoreCase(String("combobox"))
  578. ||tokes[0].equalsIgnoreCase(String("checkbox"))
  579. ||tokes[0].equalsIgnoreCase(String("xypad"))
  580. ||tokes[0].equalsIgnoreCase(String("button"))){
  581. CabbageGUIClass cAttr(csdLine.trimEnd(), guiID);
  582. //Logger::writeToLog(csdLine.trimEnd());
  583. csdLine = "";
  584. //Logger::writeToLog(tokes[0]);
  585. //attach widget to plant if need be
  586. if(cAttr.getStringProp(String("reltoplant")).equalsIgnoreCase(String(""))){
  587. //showMessage(cAttr.getStringProp(String("relToPlant")));
  588. cAttr.setStringProp(String("reltoplant"), plantFlag);
  589. //showMessage(String("presetFlag:")+presetFlag);
  590. //showMessage(cAttr.getStringProp("name"));
  591. if(cAttr.getStringProp("preset").length()<1)
  592. cAttr.setStringProp(String("preset"), presetFlag.trim());
  593. //showMessage(cAttr.getStringProp("preset"));
  594. }
  595. //xypad contain two control paramters, one for x axis and another for y. As such we add two
  596. //to our contorl vector so that plugin hosts display two sliders. We name one of the xypad pads
  597. // 'dummy' so that our editor doesn't display it. Our editor only needs to show one xypad.
  598. if(tokes[0].equalsIgnoreCase(String("xypad"))){
  599. cAttr.setStringProp(CabbageIDs::xychannel, String("X"));
  600. cAttr.setNumProp(CabbageIDs::range, cAttr.getNumProp(CabbageIDs::rangex));
  601. cAttr.setNumProp(CabbageIDs::min, cAttr.getNumProp(CabbageIDs::minx));
  602. cAttr.setNumProp(CabbageIDs::max, cAttr.getNumProp(CabbageIDs::maxx));
  603. cAttr.setNumProp(CabbageIDs::value, cAttr.getNumProp(CabbageIDs::valuex));
  604. cAttr.setStringProp(String(CabbageIDs::channel), cAttr.getStringProp(CabbageIDs::xchannel));
  605. guiCtrls.add(cAttr);
  606. cAttr.setStringProp(CabbageIDs::xychannel, String("Y"));
  607. cAttr.setNumProp(CabbageIDs::range, cAttr.getNumProp(CabbageIDs::rangey));
  608. cAttr.setNumProp(CabbageIDs::min, cAttr.getNumProp(CabbageIDs::miny));
  609. cAttr.setNumProp(CabbageIDs::max, cAttr.getNumProp(CabbageIDs::maxy));
  610. cAttr.setNumProp(CabbageIDs::value, cAttr.getNumProp(CabbageIDs::valuey));
  611. cAttr.setStringProp(String(CabbageIDs::channel), cAttr.getStringProp(CabbageIDs::ychannel));
  612. //append 'dummy' to name so the editor know not to display the
  613. //second xypad
  614. cAttr.setStringProp("name", cAttr.getStringProp(CabbageIDs::name)+String("dummy"));
  615. guiCtrls.add(cAttr);
  616. guiID++;
  617. startTimer(10);
  618. }
  619. else{
  620. //Logger::writeToLog("Value:"+String(cAttr.getNumProp(CabbageIDs::value)));
  621. guiCtrls.add(cAttr);
  622. guiID++;
  623. }
  624. //debugMessageArray.addArray(logGUIAttributes(cAttr, String("Interactive")));
  625. sendChangeMessage();
  626. }
  627. }
  628. }
  629. else break;
  630. } //end of scan through entire csd text, control vectors are now populated
  631. //create multitabs now that plants have been inserted to control vector..
  632. for(int i=0;i<csdText.size();i++)
  633. {
  634. if(csdText[i].contains("multitab ") && !csdText[i].contains(";"))
  635. {
  636. csdLine = csdText[i];
  637. csdLine = csdLine.trimStart();
  638. StringArray tokes;
  639. tokes.addTokens(csdLine.trimEnd(), ", ", "\"");
  640. if(tokes[0].equalsIgnoreCase(String("multitab"))){
  641. CabbageGUIClass cAttr(csdLine.trimEnd(), guiID);
  642. //showMessage(cAttr.getStringProp("type"));
  643. csdLine = "";
  644. //set up plant flag if needed for other widgets
  645. if(cAttr.getStringProp(String("plant")).isNotEmpty()){
  646. plantFlag = cAttr.getStringProp(String("plant"));
  647. presetFlag = cAttr.getStringProp(String("preset"));
  648. }
  649. else if(cAttr.getStringProp(String("reltoplant")).equalsIgnoreCase(String("")))
  650. cAttr.setStringProp(String("reltoplant"), plantFlag);
  651. guiLayoutCtrls.add(cAttr);
  652. guiID++;
  653. }
  654. }
  655. }//end of multitab check
  656. //init all channels with their init val, and set parameters
  657. for(int i=0;i<guiCtrls.size();i++)
  658. {
  659. // Logger::writeToLog(guiCtrls.getReference(i).getStringProp(CabbageIDs::channel)+": "+String(guiCtrls[i].getNumProp(CabbageIDs::value)));
  660. #ifndef Cabbage_No_Csound
  661. if(guiCtrls.getReference(i).getStringProp("channeltype")=="string")
  662. //deal with combobox strings..
  663. csound->SetChannel(guiCtrls.getReference(i).getStringProp(CabbageIDs::channel).toUTF8(), "");
  664. // guiCtrls.getReference(i).getStringArrayPropValue("text", guiCtrls[i].getNumProp(CabbageIDs::value)-1).toUTF8().getAddress());
  665. else
  666. csound->SetChannel( guiCtrls.getReference(i).getStringProp(CabbageIDs::channel).toUTF8(), guiCtrls[i].getNumProp(CabbageIDs::value));
  667. #endif
  668. }
  669. #ifdef Cabbage_Build_Standalone
  670. if(this->getActiveEditor()){
  671. CabbagePluginAudioProcessorEditor* editor = dynamic_cast<CabbagePluginAudioProcessorEditor*>(this->getActiveEditor());
  672. if(refresh){
  673. editor->comps.clear();
  674. editor->layoutComps.clear();
  675. editor->repaint();
  676. //((CabbagePluginAudioProcessorEditor*)getActiveEditor())->setEditMode(false);
  677. //editor->setEditMode(false);
  678. }
  679. //!this will not work as we are moving through our entire control vector
  680. for(int i=indexOfLastLayoutCtrl;i<guiLayoutCtrls.size();i++)
  681. editor->InsertGUIControls(guiLayoutCtrls[i]);
  682. for(int i=indexOfLastGUICtrl;i<guiCtrls.size();i++)
  683. editor->InsertGUIControls(guiCtrls[i]);
  684. if(refresh)
  685. editor->setEditMode(checkGUI);
  686. }
  687. #endif
  688. }
  689. //============================================================================
  690. //dynamically remove components from editor window, used in EDIT mode
  691. //============================================================================
  692. void CabbagePluginAudioProcessor::removeGUIComponent(int index, String type){
  693. //remove component struct from our abstract vector
  694. CabbagePluginAudioProcessorEditor* editor = dynamic_cast<CabbagePluginAudioProcessorEditor*>(this->getActiveEditor());
  695. if(type=="interactive"){
  696. //remove GUI abstract structure from vector
  697. guiCtrls.remove(index);
  698. }
  699. else{
  700. //remove GUI abstract structure from vector
  701. guiLayoutCtrls.remove(index);
  702. }
  703. editor->updateLayoutEditorFrames();
  704. editor->repaint();
  705. }
  706. //============================================================================
  707. //SETS UP A GENERIC PLUGIN EDITOR
  708. //============================================================================
  709. void CabbagePluginAudioProcessor::setupNativePluginEditor()
  710. {
  711. /*
  712. //create a basic 'native' gui if specificed by the user.
  713. int guiID = 0;
  714. guiCtrls.clear();
  715. for(int i=0;i<numCsoundChannels;i++){
  716. const CsoundChannelListEntry& entry = csoundChanList[i];
  717. if (entry.type & CSOUND_CONTROL_CHANNEL && entry.type & CSOUND_INPUT_CHANNEL) {
  718. MYFLT ddefault, dmin, dmax;
  719. int value_type = getCsound()->GetControlChannelParams(entry.name, ddefault, dmin, dmax);
  720. String parameterInfo;
  721. float initVal = (ddefault<dmin ? dmin : ddefault);
  722. parameterInfo << "channel(\"" << entry.name << "\"), " << "range(" << String(dmin) << ", " << String(dmax) << ", " << String(initVal) << ")";
  723. Logger::writeToLog(parameterInfo);
  724. CabbageGUIClass cAttr(parameterInfo, guiID);
  725. cAttr.setNumProp("range", dmax-dmin);
  726. //cAttr.setStringProp(CabbageIDs::channel, entry.name);
  727. //cAttr.setNumProp("max", (dmax>0 ? dmax : 1));
  728. //cAttr.setNumProp("init", (ddefault<dmin ? dmin : ddefault));
  729. switch(value_type) {
  730. case CSOUND_CONTROL_CHANNEL_INT:
  731. cAttr.setNumProp("incr", 1);
  732. break;
  733. case CSOUND_CONTROL_CHANNEL_LIN:
  734. cAttr.setNumProp("incr", .01);
  735. break;
  736. case CSOUND_CONTROL_CHANNEL_EXP:
  737. cAttr.setNumProp("skew", .5);
  738. break;
  739. default:
  740. break;
  741. }
  742. guiCtrls.add(cAttr);
  743. setPluginName("Test Plugin");
  744. guiID++;
  745. }
  746. }
  747. */
  748. }
  749. //===========================================================
  750. // SHOW SOURCE EDITOR
  751. //===========================================================
  752. //void CabbagePluginAudioProcessor::createAndShowSourceEditor(LookAndFeel* looky)
  753. //{
  754. //if(!cabbageCsoundEditor){
  755. //cabbageCsoundEditor = new CabbageEditorWindow(looky);
  756. //cabbageCsoundEditor->setCsoundFile(csdFile);
  757. //cabbageCsoundEditor->setCsoundOutputText(csoundOutput);
  758. //}
  759. //cabbageCsoundEditor->setVisible(true);
  760. //}
  761. //===========================================================
  762. // CALLBACKS FOR STANDALONE
  763. //===========================================================
  764. #ifndef Cabbage_No_Csound
  765. void CabbagePluginAudioProcessor::messageCallback(CSOUND* csound, int /*attr*/, const char* fmt, va_list args)
  766. {
  767. CabbagePluginAudioProcessor* ud = (CabbagePluginAudioProcessor *) csoundGetHostData(csound);
  768. char msg[MAX_BUFFER_SIZE];
  769. vsnprintf(msg, MAX_BUFFER_SIZE, fmt, args);
  770. // MOD - Stefano Bonetti
  771. ud->debugMessage += String(msg); //We have to append the incoming msg
  772. ud->csoundOutput += ud->debugMessage;
  773. ud->debugMessageArray.add(ud->debugMessage);
  774. //Logger::writeToLog(String(msg).trim());
  775. ud->sendChangeMessage();
  776. // MOD - End
  777. ud->debugMessage = "";
  778. ud = nullptr;
  779. }
  780. #endif
  781. //==============================================================================
  782. #if defined(Cabbage_Build_Standalone) || defined(Cabbage_Plugin_Host)
  783. CabbagePluginAudioProcessor* JUCE_CALLTYPE createCabbagePluginFilter(String inputfile, bool guiOnOff, int pluginType)
  784. {
  785. return new CabbagePluginAudioProcessor(inputfile, false, pluginType);
  786. }
  787. #else
  788. AudioProcessor* JUCE_CALLTYPE createPluginFilter()
  789. {
  790. return new CabbagePluginAudioProcessor();
  791. }
  792. #endif
  793. //==========================================================================
  794. //action listener. Listen to messages being sent form xypad automations
  795. //==========================================================================
  796. void CabbagePluginAudioProcessor::changeListenerCallback(ChangeBroadcaster *source)
  797. {
  798. float xVal, yVal;
  799. //is message coming from an xypad
  800. XYPadAutomation* xyPad = dynamic_cast< XYPadAutomation*>(source);
  801. if(xyPad){
  802. #ifdef Cabbage_Build_Standalone
  803. setParameterNotifyingHost(xyPad->paramIndex, xyPad->getXValue());
  804. setParameterNotifyingHost(xyPad->paramIndex+1, xyPad->getYValue());
  805. #else
  806. if(xyPad->getMinimumXValue()>=0)
  807. xVal = (xyPad->getXValue()/xyPad->getXRange())+(fabs(xyPad->getMinimumXValue())/xyPad->getXRange());
  808. else
  809. xVal = (xyPad->getXValue()/xyPad->getXRange())-(fabs(xyPad->getMinimumXValue())/xyPad->getXRange());
  810. if(xyPad->getMinimumYValue()<=0)
  811. yVal = (xyPad->getYValue()/xyPad->getYRange())+(fabs(xyPad->getMinimumYValue())/xyPad->getYRange());
  812. else
  813. yVal = (xyPad->getYValue()/xyPad->getYRange())-(fabs(xyPad->getMinimumYValue())/xyPad->getYRange());
  814. //Logger::writeToLog("Param:"+String(xyPad->paramIndex)+" xyPadXVal:"+String(xVal));
  815. //Logger::writeToLog("Param:"+String(xyPad->paramIndex+1)+" xyPadYVal:"+String(yVal));
  816. setParameterNotifyingHost(xyPad->paramIndex, xVal);
  817. setParameterNotifyingHost(xyPad->paramIndex+1, yVal);
  818. #endif
  819. }
  820. }
  821. //==============================================================================
  822. // getTable data from Csound so table editor can draw table
  823. //==============================================================================
  824. const Array<double, CriticalSection> CabbagePluginAudioProcessor::getTable(int tableNum){
  825. Array<double, CriticalSection> points;
  826. int tableSize=0;
  827. #ifndef Cabbage_No_Csound
  828. MYFLT* temp;
  829. tableSize = csound->GetTable(temp, tableNum);
  830. #else
  831. float *temp;
  832. #endif
  833. if(tableSize>0)
  834. points = Array<double, CriticalSection>(temp, tableSize);
  835. return points;
  836. }
  837. //=================================================================================
  838. // Get and Set Parameter methods, called by our editor, and the plugin host...
  839. //=================================================================================
  840. float CabbagePluginAudioProcessor::getParameter (int index)
  841. {
  842. float range = getGUICtrls(index).getNumProp(CabbageIDs::range);
  843. float min = getGUICtrls(index).getNumProp(CabbageIDs::min);
  844. //Logger::writeToLog("parameterGet-"+String(index)+String("-Min:")+String(min)+" Range:"+String(range)+ " Val:"+String(getGUICtrls(index).getNumProp(CabbageIDs::value)));
  845. //Logger::writeToLog("parameterGet:"+String(index)+String(":")+String(guiCtrls[index].getNumProp(CabbageIDs::value)));
  846. /* this gets called at any time by our host or out GUI editor */
  847. if(index<(int)guiCtrls.size()){//make sure index isn't out of range
  848. #ifndef Cabbage_Build_Standalone
  849. float val = (getGUICtrls(index).getNumProp(CabbageIDs::value)/range)-(min/range);
  850. if(getGUICtrls(index).getStringProp(CabbageIDs::type)==CabbageIDs::combobox)
  851. return (getGUICtrls(index).getNumProp(CabbageIDs::value)/getGUICtrls(index).getNumProp(CabbageIDs::comborange));
  852. else if(getGUICtrls(index).getStringProp(CabbageIDs::type)==CabbageIDs::checkbox ||
  853. getGUICtrls(index).getStringProp(CabbageIDs::type)==CabbageIDs::button)
  854. return getGUICtrls(index).getNumProp(CabbageIDs::value);
  855. else
  856. return (getGUICtrls(index).getNumProp(CabbageIDs::value)/range)-(min/range);
  857. #else
  858. return guiCtrls[index].getNumProp(CabbageIDs::value);
  859. #endif
  860. }
  861. else
  862. return 0.0f;
  863. }
  864. void CabbagePluginAudioProcessor::setParameter (int index, float newValue)
  865. {
  866. String stringMessage;
  867. #ifndef Cabbage_No_Csound
  868. /* this will get called by the plugin GUI sliders or
  869. by the host, via automation. The timer thread in the plugin's editor
  870. will constantly update with the values that have been set here.
  871. We don't actually change any parameters here, we simply add the messages
  872. to a queue. See next method. The updates will only happen when it's safe to do. */
  873. float range, min, max, comboRange;
  874. //Logger::writeToLog("parameterSet:"+String(newValue));
  875. if(index<(int)guiCtrls.size())//make sure index isn't out of range
  876. {
  877. #ifndef Cabbage_Build_Standalone
  878. //scaling in here because incoming values in plugin mode range from 0-1
  879. range = getGUICtrls(index).getNumProp(CabbageIDs::range);
  880. comboRange = getGUICtrls(index).getNumProp(CabbageIDs::comborange);
  881. //Logger::writeToLog("inValue:"+String(newValue));
  882. min = getGUICtrls(index).getNumProp(CabbageIDs::min);
  883. if(getGUICtrls(index).getStringProp(CabbageIDs::type)==CabbageIDs::xypad)
  884. newValue = (jmax(0.f, newValue)*range)+min;
  885. else if(getGUICtrls(index).getStringProp(CabbageIDs::type)==CabbageIDs::combobox)//combo box value need to be rounded...
  886. newValue = (newValue*comboRange);
  887. else if(getGUICtrls(index).getStringProp(CabbageIDs::type)==CabbageIDs::checkbox ||
  888. getGUICtrls(index).getStringProp(CabbageIDs::type)==CabbageIDs::button)
  889. range=1;
  890. else
  891. newValue = (newValue*range)+min;
  892. //guiCtrls.getReference(index).setNumProp(CabbageIDs::value, newValue);
  893. //messageQueue.addOutgoingChannelMessageToQueue(guiCtrls.getReference(index).getStringProp(CabbageIDs::channel).toUTF8(), newValue,
  894. //guiCtrls.getReference(index).getStringProp("type"));
  895. //Logger::writeToLog(String("parameterSet:"+String(newValue)));
  896. #endif
  897. //Logger::writeToLog(String("parameterSet:"+String(newValue)));
  898. //no need to scale here when in standalone mode
  899. if(getGUICtrls(index).getStringProp(CabbageIDs::type)==CabbageIDs::combobox &&
  900. getGUICtrls(index).getStringProp(CabbageIDs::channeltype)==CabbageIDs::stringchannel)
  901. {
  902. stringMessage = getGUICtrls(index).getStringArrayPropValue(CabbageIDs::text, newValue-1);
  903. //Logger::writeToLog(stringMessage);
  904. messageQueue.addOutgoingChannelMessageToQueue(guiCtrls.getReference(index).getStringProp(CabbageIDs::channel),
  905. stringMessage,
  906. CabbageIDs::stringchannel);
  907. }
  908. else
  909. messageQueue.addOutgoingChannelMessageToQueue(guiCtrls.getReference(index).getStringProp(CabbageIDs::channel),
  910. newValue,
  911. guiCtrls.getReference(index).getStringProp(CabbageIDs::type));
  912. guiCtrls.getReference(index).setNumProp(CabbageIDs::value, newValue);
  913. }
  914. #endif
  915. }
  916. //==============================================================================
  917. //this method gets called after a performKsmps() to update our GUI controls
  918. //with messages from Csound. For instance, a user might wish to change the position
  919. //of a GUI slider from Csound by using a chnset opcode. The speed at which this is
  920. //updated can be teaked, so as not to hog resources. It might be worth allowing users
  921. //the option of setting how fast this update...
  922. void CabbagePluginAudioProcessor::updateCabbageControls()
  923. {
  924. #ifndef Cabbage_No_Csound
  925. String chanName;
  926. if(!CSCompResult)
  927. {
  928. MYFLT* val=0;
  929. //update all control widgets
  930. for(int index=0;index<getGUICtrlsSize();index++)
  931. {
  932. if(guiCtrls[index].getStringProp(CabbageIDs::channeltype).equalsIgnoreCase(CabbageIDs::stringchannel)){
  933. //argghhh!! THIS NEEDS TO ALLOW COMBOBOXEX THAT CONTAIN SNAPSHOTS TO UPDATE!
  934. }
  935. else{
  936. float value = csound->GetChannel(guiCtrls[index].getStringProp(CabbageIDs::channel).getCharPointer());
  937. //Logger::writeToLog("Channel:"+guiCtrls[index].getStringProp(CabbageIDs::channel));
  938. //Logger::writeToLog("value:"+String(value));
  939. guiCtrls.getReference(index).setNumProp(CabbageIDs::value, value);
  940. }
  941. }
  942. //update all layout control widgets
  943. //currently this is only needed for table widgets as other layout controls
  944. //don't use channel messages...
  945. for(int index=0;index<getGUILayoutCtrlsSize();index++)
  946. {
  947. if(guiLayoutCtrls[index].getStringProp(CabbageIDs::type)==CabbageIDs::table)
  948. {
  949. for(int y=0;y<guiLayoutCtrls[index].getStringArrayProp(CabbageIDs::channel).size();y++){
  950. //String test = getGUILayoutCtrls(index).getStringArrayPropValue(CabbageIDs::channel, y);
  951. float value = csound->GetChannel(guiLayoutCtrls[index].getStringArrayPropValue(CabbageIDs::channel, y).getCharPointer());
  952. guiLayoutCtrls[index].setTableChannelValues(y, value);
  953. }
  954. }
  955. }
  956. }
  957. sendChangeMessage();
  958. #endif
  959. }
  960. //==============================================================================
  961. //this method only gets called when it's safe to do so, i.e., between calls to performKsmps()
  962. //this method sends any channel messages that are in the queue to from Cabbage to Csound
  963. void CabbagePluginAudioProcessor::sendOutgoingMessagesToCsound()
  964. {
  965. #ifndef Cabbage_No_Csound
  966. if(!csCompileResult){
  967. #ifndef Cabbage_Build_Standalone
  968. if (getPlayHead() != 0 && getPlayHead()->getCurrentPosition (hostInfo))
  969. csound->SetChannel(CabbageIDs::hostbpm.toUTF8(), hostInfo.bpm);
  970. if (getPlayHead() != 0 && getPlayHead()->getCurrentPosition (hostInfo))
  971. csound->SetChannel(CabbageIDs::timeinseconds.toUTF8(), hostInfo.timeInSeconds);
  972. if (getPlayHead() != 0 && getPlayHead()->getCurrentPosition (hostInfo))
  973. csound->SetChannel(CabbageIDs::isplaying.toUTF8(), hostInfo.isPlaying);
  974. if (getPlayHead() != 0 && getPlayHead()->getCurrentPosition (hostInfo))
  975. csound->SetChannel(CabbageIDs::isrecording.toUTF8(), hostInfo.isRecording);
  976. if (getPlayHead() != 0 && getPlayHead()->getCurrentPosition (hostInfo))
  977. csound->SetChannel(CabbageIDs::hostppqpos.toUTF8(), hostInfo.ppqPosition);
  978. #endif
  979. for(int i=0;i<messageQueue.getNumberOfOutgoingChannelMessagesInQueue();i++)
  980. {
  981. //Logger::writeToLog("MessageType:"+messageQueue.getOutgoingChannelMessageFromQueue(i).type);
  982. if(messageQueue.getOutgoingChannelMessageFromQueue(i).type=="directoryList"){
  983. for(int y=0;y<scoreEvents.size();y++)
  984. csound->InputMessage(scoreEvents[y].toUTF8());
  985. //scoreEvents.clear();
  986. }
  987. //update Csound function tables with values from table widget
  988. else if(messageQueue.getOutgoingChannelMessageFromQueue(i).type=="updateTable"){
  989. //Logger::writeToLog(messageQueue.getOutgoingChannelMessageFromQueue(i).fStatement.toUTF8());
  990. csound->InputMessage(messageQueue.getOutgoingChannelMessageFromQueue(i).fStatement.getCharPointer());
  991. }
  992. //catch string messags
  993. else if(messageQueue.getOutgoingChannelMessageFromQueue(i).type==CabbageIDs::stringchannel){
  994. csound->SetChannel(messageQueue.getOutgoingChannelMessageFromQueue(i).channelName.getCharPointer(),
  995. messageQueue.getOutgoingChannelMessageFromQueue(i).stringVal.toUTF8().getAddress());
  996. }
  997. else
  998. csound->SetChannel(messageQueue.getOutgoingChannelMessageFromQueue(i).channelName.getCharPointer(),
  999. messageQueue.getOutgoingChannelMessageFromQueue(i).value);
  1000. }
  1001. messageQueue.flushOutgoingChannelMessages();
  1002. if(isAutomator){
  1003. //sendChangeMessage();
  1004. //sendActionMessage("update automation:"+String(automationParamID)+"|"+String(automationAmp));
  1005. //Logger::writeToLog("update automation:"+String(automationAmp));
  1006. }
  1007. }
  1008. #endif
  1009. }
  1010. //==============================================================================
  1011. //set up buffered audio source for each sound file object. The method below this one
  1012. //fills Csound channels with sampler from our soundfiler controls..
  1013. void CabbagePluginAudioProcessor::addSoundfilerSource(String filename, StringArray channels)
  1014. {
  1015. #ifndef Cabbage_No_Csound
  1016. audioSourcesArray.add(new CabbageAudioSource(filename, csound->GetKsmps()));
  1017. Logger::writeToLog("Number of soundfilers:"+String(audioSourcesArray.size()));
  1018. audioSourcesArray[audioSourcesArray.size()-1]->channels = channels;
  1019. if(File(filename).exists())Logger::writeToLog("File exists");
  1020. else{
  1021. Logger::writeToLog("Soundfiler can't find file");
  1022. //set default sampling rate in case of no file name given
  1023. audioSourcesArray[audioSourcesArray.size()-1]->isValidFile = false;
  1024. audioSourcesArray[audioSourcesArray.size()-1]->sampleRate = 44100;
  1025. }
  1026. #endif
  1027. }
  1028. //gets sample data from any soundfiler controls and passes it to Csound
  1029. void CabbagePluginAudioProcessor::sendAudioToCsoundFromSoundFilers(int numSamples)
  1030. {
  1031. #ifndef Cabbage_No_Csound
  1032. if(this->isSuspended()==false){
  1033. for(int i=0;i<audioSourcesArray.size();i++){
  1034. AudioSampleBuffer output (2, numSamples);
  1035. audioSourcesArray[i]->sourceChannelInfo.buffer = &output;
  1036. audioSourcesArray[i]->sourceChannelInfo.startSample = 0;
  1037. audioSourcesArray[i]->sourceChannelInfo.numSamples = output.getNumSamples();
  1038. if(audioSourcesArray[i]->isSourcePlaying && audioSourcesArray[i]->isValidFile)
  1039. audioSourcesArray[i]->audioSourceBuffer->getNextAudioBlock(audioSourcesArray[i]->sourceChannelInfo);
  1040. else
  1041. output.clear();
  1042. for(int index=0;index<audioSourcesArray[i]->channels.size();index++)
  1043. {
  1044. float* samples = output.getSampleData(index);
  1045. for(int y=0;y<numSamples;y+=2)
  1046. soundFilerTempVector[y] = samples[y];
  1047. //Logger::writeToLog(audioSourcesArray[i]->channels[index]);
  1048. if(csoundGetChannelPtr(csound->GetCsound(), &soundFilerTempVector, audioSourcesArray[i]->channels[index].toUTF8(),
  1049. CSOUND_INPUT_CHANNEL | CSOUND_AUDIO_CHANNEL) != 0)
  1050. Logger::writeToLog("error sending audio to Csound");
  1051. }
  1052. }
  1053. }
  1054. #endif
  1055. }
  1056. //========================================================================
  1057. // Standard plugin methods, getName, getNumParameters, setParamterName, get ProgramName, etc....
  1058. //==============================================================================
  1059. const String CabbagePluginAudioProcessor::getName() const
  1060. {
  1061. return pluginName;
  1062. }
  1063. int CabbagePluginAudioProcessor::getNumParameters()
  1064. {
  1065. return guiCtrls.size();
  1066. }
  1067. const String CabbagePluginAudioProcessor::getParameterName (int index)
  1068. {
  1069. if(index<(int)guiCtrls.size())//make sure index isn't out of range
  1070. return guiCtrls.getReference(index).getStringProp(CabbageIDs::channel);
  1071. else return String::empty;
  1072. }
  1073. const String CabbagePluginAudioProcessor::getParameterText (int index)
  1074. {
  1075. if(index<(int)guiCtrls.size())//make sure index isn't out of range
  1076. return String (guiCtrls.getReference(index).getNumProp(CabbageIDs::value), 2);
  1077. else return String::empty;
  1078. }
  1079. const String CabbagePluginAudioProcessor::getInputChannelName (int channelIndex) const
  1080. {
  1081. if(channelIndex<(int)guiCtrls.size())//make sure index isn't out of range
  1082. return String (channelIndex + 1);
  1083. else return String::empty;
  1084. }
  1085. const String CabbagePluginAudioProcessor::getOutputChannelName (int channelIndex) const
  1086. {
  1087. if(channelIndex<(int)guiCtrls.size())//make sure index isn't out of range
  1088. return String (channelIndex + 1);
  1089. else return String::empty;
  1090. }
  1091. bool CabbagePluginAudioProcessor::isInputChannelStereoPair (int /*index*/) const
  1092. {
  1093. return true;
  1094. }
  1095. bool CabbagePluginAudioProcessor::isOutputChannelStereoPair (int /*index*/) const
  1096. {
  1097. return true;
  1098. }
  1099. bool CabbagePluginAudioProcessor::acceptsMidi() const
  1100. {
  1101. #if JucePlugin_WantsMidiInput
  1102. return true;
  1103. #else
  1104. return false;
  1105. #endif
  1106. }
  1107. bool CabbagePluginAudioProcessor::producesMidi() const
  1108. {
  1109. #if JucePlugin_ProducesMidiOutput
  1110. return true;
  1111. #else
  1112. return false;
  1113. #endif
  1114. }
  1115. void CabbagePluginAudioProcessor::setGuiEnabled(bool val){
  1116. #ifdef Cabbage_Build_Standalone
  1117. guiON = val;
  1118. CabbagePluginAudioProcessorEditor* editor = dynamic_cast< CabbagePluginAudioProcessorEditor*>(this->getActiveEditor());
  1119. if(editor){
  1120. if(val==false){
  1121. int val = getPreference(appProperties, "ExternalEditor");
  1122. if(val)
  1123. csdFile.replaceWithText(codeEditor->getAllText());
  1124. //editor->resizer->setVisible(false);
  1125. //editor->propsWindow->setVisible(false);
  1126. }
  1127. else{
  1128. //editor->resizer->setVisible(true);
  1129. }
  1130. }
  1131. #endif
  1132. }
  1133. int CabbagePluginAudioProcessor::getNumPrograms()
  1134. {
  1135. return 0;
  1136. }
  1137. int CabbagePluginAudioProcessor::getCurrentProgram()
  1138. {
  1139. return 0;
  1140. }
  1141. void CabbagePluginAudioProcessor::setCurrentProgram (int /*index*/)
  1142. {
  1143. }
  1144. const String CabbagePluginAudioProcessor::getProgramName (int /*index*/)
  1145. {
  1146. return String::empty;
  1147. }
  1148. void CabbagePluginAudioProcessor::changeProgramName (int /*index*/, const String& /*newName*/)
  1149. {
  1150. }
  1151. //==============================================================================
  1152. void CabbagePluginAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock)
  1153. {
  1154. // Use this method as the place to do any pre-playback
  1155. // initialisation that you need..
  1156. keyboardState.reset();
  1157. for(int i=0;i<audioSourcesArray.size();i++)
  1158. if(audioSourcesArray[i]->isValidFile)
  1159. audioSourcesArray[i]->audioSourceBuffer->prepareToPlay(samplesPerBlock, sampleRate);
  1160. }
  1161. //==============================================================================
  1162. void CabbagePluginAudioProcessor::releaseResources()
  1163. {
  1164. // When playback stops, you can use this as an opportunity to free up any
  1165. // spare memory, etc.
  1166. keyboardState.reset();
  1167. }
  1168. //==============================================================================
  1169. //this following callback only runs in plugin mode, and only when one of the
  1170. //host widgets are being used
  1171. void CabbagePluginAudioProcessor::timerCallback(){
  1172. #ifndef Cabbage_No_Csound
  1173. for(int y=0;y<xyAutomation.size();y++){
  1174. if(xyAutomation[y])
  1175. xyAutomation[y]->update();
  1176. }
  1177. #endif
  1178. }
  1179. //==============================================================================
  1180. void CabbagePluginAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
  1181. {
  1182. if(!isSuspended() && !isGuiEnabled()){
  1183. float* audioBuffer;
  1184. float lastOutputAmp;
  1185. #ifndef Cabbage_No_Csound
  1186. int numSamples = buffer.getNumSamples();
  1187. if(csCompileResult==0){
  1188. keyboardState.processNextMidiBuffer (midiMessages, 0, buffer.getNumSamples(), true);
  1189. midiBuffer = midiMessages;
  1190. ccBuffer = midiMessages;
  1191. //if no inputs are used clear buffer in case it's not empty..
  1192. if(getNumInputChannels()==0)
  1193. buffer.clear();
  1194. #if JucePlugin_ProducesMidiOutput
  1195. if(!midiOutputBuffer.isEmpty())
  1196. midiMessages.swapWith(midiOutputBuffer);
  1197. #endif
  1198. for(int i=0;i<numSamples;i++, csndIndex++)
  1199. {
  1200. if(csndIndex == csound->GetKsmps())
  1201. {
  1202. getCallbackLock().enter();
  1203. //slow down calls to these functions, no need for them to be firing at k-rate
  1204. yieldCounter = (yieldCounter>guiRefreshRate) ? 0 : yieldCounter+1;
  1205. if(yieldCounter==0){
  1206. sendOutgoingMessagesToCsound();
  1207. updateCabbageControls();
  1208. }
  1209. if(audioSourcesArray.size()>0)
  1210. sendAudioToCsoundFromSoundFilers(csound->GetKsmps());
  1211. csCompileResult = csound->PerformKsmps();
  1212. if(csCompileResult!=0)
  1213. suspendProcessing(true);
  1214. getCallbackLock().exit();
  1215. csndIndex = 0;
  1216. }
  1217. if(!csCompileResult)
  1218. {
  1219. for(int channel = 0; channel < getNumOutputChannels(); channel++ )
  1220. {
  1221. audioBuffer = buffer.getSampleData(channel,0);
  1222. pos = csndIndex*getNumOutputChannels();
  1223. CSspin[channel+pos] = audioBuffer[i]*cs_scale;
  1224. audioBuffer[i] = (CSspout[channel+pos]/cs_scale);
  1225. }
  1226. }
  1227. else
  1228. buffer.clear();
  1229. }
  1230. }//if not compiled just mute output
  1231. else{
  1232. for(int channel = 0; channel < getNumInputChannels(); channel++)
  1233. {
  1234. audioBuffer = buffer.getSampleData(channel,0);
  1235. for(int i=0; i<numSamples;i++, csndIndex++)
  1236. {
  1237. audioBuffer[i]=0;
  1238. }
  1239. }
  1240. }
  1241. #endif
  1242. }
  1243. #if JucePlugin_ProducesMidiOutput
  1244. if(!midiBuffer.isEmpty())
  1245. midiMessages.swapWith(midiOutputBuffer);
  1246. #endif
  1247. }
  1248. //==============================================================================
  1249. // MIDI functions
  1250. //==============================================================================
  1251. #ifndef Cabbage_No_Csound
  1252. int CabbagePluginAudioProcessor::OpenMidiInputDevice(CSOUND * csound, void **userData, const char* /*devName*/)
  1253. {
  1254. *userData = csoundGetHostData(csound);
  1255. if(!userData)
  1256. cout << "\n\ncan't open midi in\n\n";
  1257. return 0;
  1258. }
  1259. //==============================================================================
  1260. // Reads MIDI input data from host, gets called every time there is MIDI input to our plugin
  1261. //==============================================================================
  1262. int CabbagePluginAudioProcessor::ReadMidiData(CSOUND* /*csound*/, void *userData,
  1263. unsigned char *mbuf, int nbytes)
  1264. {
  1265. CabbagePluginAudioProcessor *midiData = (CabbagePluginAudioProcessor *)userData;
  1266. if(!userData){
  1267. cout << "\n\nInvalid";
  1268. return 0;
  1269. }
  1270. int cnt=0;
  1271. if(!midiData->midiBuffer.isEmpty() && cnt <= (nbytes - 3)){
  1272. MidiMessage message(0xf4, 0, 0, 0);
  1273. MidiBuffer::Iterator i (midiData->midiBuffer);
  1274. int messageFrameRelativeTothisProcess;
  1275. while (i.getNextEvent (message, messageFrameRelativeTothisProcess))
  1276. {
  1277. if(message.isNoteOn()){
  1278. *mbuf++ = (unsigned char)0x90 + message.getChannel();
  1279. *mbuf++ = (unsigned char)message.getNoteNumber();
  1280. *mbuf++ = (unsigned char)message.getVelocity();
  1281. cnt += 3;
  1282. }
  1283. else if(message.isNoteOff()){
  1284. *mbuf++ = (unsigned char)0x80 + message.getChannel();
  1285. *mbuf++ = (unsigned char)message.getNoteNumber();
  1286. *mbuf++ = (unsigned char)message.getVelocity();
  1287. cnt += 3;
  1288. }
  1289. else if(message.isAllSoundOff()){
  1290. *mbuf++ = (unsigned char)0x7B + message.getChannel();
  1291. *mbuf++ = (unsigned char)message.getNoteNumber();
  1292. *mbuf++ = (unsigned char)message.getVelocity();
  1293. cnt += 3;
  1294. }
  1295. else if(message.isController()){
  1296. *mbuf++ = (unsigned char)0xB0 + message.getChannel()-1;
  1297. *mbuf++ = (unsigned char)message.getControllerNumber();
  1298. *mbuf++ = (unsigned char)message.getControllerValue();
  1299. cnt += 3;
  1300. }
  1301. }
  1302. midiData->midiBuffer.clear();
  1303. }
  1304. return cnt;
  1305. }
  1306. //==============================================================================
  1307. // Opens MIDI output device, adding -QN to your CsOptions will causes this method to be called
  1308. // as soon as your plugin loads
  1309. //==============================================================================
  1310. int CabbagePluginAudioProcessor::OpenMidiOutputDevice(CSOUND * csound, void **userData, const char* /*devName*/)
  1311. {
  1312. *userData = csoundGetHostData(csound);
  1313. if(!userData)
  1314. Logger::writeToLog("\n\ncan't open midi out\n\n");
  1315. return 0;
  1316. }
  1317. //==============================================================================
  1318. // Write MIDI data to plugin's MIDI output. Each time Csound outputs a midi message this
  1319. // method should be called. Note: you must have -Q set in your CsOptions
  1320. //==============================================================================
  1321. int CabbagePluginAudioProcessor::WriteMidiData(CSOUND* /*csound*/, void *_userData,
  1322. const unsigned char *mbuf, int nbytes)
  1323. {
  1324. CabbagePluginAudioProcessor *userData = (CabbagePluginAudioProcessor *)_userData;
  1325. if(!userData){
  1326. Logger::writeToLog("\n\nInvalid");
  1327. return 0;
  1328. }
  1329. MidiMessage message(mbuf, nbytes, 0);
  1330. //Logger::writeToLog(String(message.getNoteNumber()));
  1331. userData->midiOutputBuffer.addEvent(message, 0);
  1332. return nbytes;
  1333. }
  1334. #endif
  1335. //==============================================================================
  1336. bool CabbagePluginAudioProcessor::hasEditor() const
  1337. {
  1338. return true; // (change this to false if you choose to not supply an editor)
  1339. }
  1340. AudioProcessorEditor* CabbagePluginAudioProcessor::createEditor()
  1341. {
  1342. if(!nativePluginEditor)
  1343. return new CabbagePluginAudioProcessorEditor (this);
  1344. else
  1345. return new CabbageGenericAudioProcessorEditor (this);
  1346. }
  1347. //==============================================================================
  1348. void CabbagePluginAudioProcessor::getStateInformation (MemoryBlock& destData)
  1349. {
  1350. // You should use this method to store your parameters in the memory block.
  1351. // Here's an example of how you can use XML to make it easy and more robust:
  1352. // Create an outer XML element..
  1353. XmlElement xml ("CABBAGE_PLUGIN_SETTINGS");
  1354. for(int i=0;i<guiCtrls.size();i++)
  1355. xml.setAttribute (guiCtrls[i].getStringProp(CabbageIDs::channel), guiCtrls[i].getNumProp(CabbageIDs::value));
  1356. // then use this helper function to stuff it into the binary blob and return it..
  1357. copyXmlToBinary (xml, destData);
  1358. }
  1359. void CabbagePluginAudioProcessor::setStateInformation (const void* data, int sizeInBytes)
  1360. {
  1361. // You should use this method to restore your parameters from this memory block,
  1362. // whose contents will have been created by the getStateInformation() call.
  1363. // This getXmlFromBinary() helper function retrieves our XML from the binary blob..
  1364. ScopedPointer<XmlElement> xmlState (getXmlFromBinary (data, sizeInBytes));
  1365. if (xmlState != nullptr)
  1366. {
  1367. // make sure that it's actually our type of XML object..
  1368. if (xmlState->hasTagName ("CABBAGE_PLUGIN_SETTINGS"))
  1369. {
  1370. for(int i=0;i<this->getNumParameters();i++)
  1371. this->setParameter(i, (float)xmlState->getDoubleAttribute(guiCtrls[i].getStringProp(CabbageIDs::channel)));
  1372. }
  1373. }
  1374. }
  1375. //==============================================================================