/* Copyright (C) 2009 Rory Walsh Cabbage is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. Cabbage is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with Csound; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "CabbagePluginProcessor.h" #include "CabbagePluginEditor.h" #include #define MAX_BUFFER_SIZE 1024 #define LOGGER 0 //these two lines may need to be copied to top part of csound.h //#define int32 int //#define uint32 unsigned int CabbageLookAndFeel* lookAndFeel; CabbageLookAndFeelBasic* lookAndFeelBasic; //============================================================================== // There are two different CabbagePluginAudioProcessor constructors. One for the // standalone application and the other for the plugin library //============================================================================== //#ifdef Cabbage_Build_Standalone #if defined(Cabbage_Build_Standalone) || defined(Cabbage_Plugin_Host) //=========================================================== // STANDALONE - CONSTRUCTOR //=========================================================== CabbagePluginAudioProcessor::CabbagePluginAudioProcessor(String inputfile, bool guiOnOff, int _pluginType) :csoundStatus(false), csdFile(File(inputfile)), showMIDI(false), csCompileResult(1), changeMessageType(""), guiON(false), currentLine(-99), noSteps(0), noPatterns(0), timeCounter(0), beat(0), bpm(120), patMatrixActive(0), masterCounter(0), xyAutosCreated(false), updateTable(false), yieldCallbackBool(false), yieldCounter(10), isNativeThreadRunning(false), soundFileIndex(0), scoreEvents(), nativePluginEditor(false), averageSampleIndex(0), pluginType(_pluginType), automationAmp(0), isAutomator(false), automationParamID(-1), debugMessage(""), guiRefreshRate(20) { //suspendProcessing(true); codeEditor = nullptr; #ifdef Cabbage_Logger logFile = File((appProperties->getCommonSettings(true)->getFile().getParentDirectory().getFullPathName()+"/CabbageLog.txt")); fileLogger = new FileLogger(logFile, String("Cabbage Log..")); Logger::setCurrentLogger(fileLogger); #endif //reset patMatrix. If this has more than one we know that //pattern matrix object is being used patStepMatrix.clear(); patPfieldMatrix.clear(); setPlayConfigDetails(2, 2, 44100, 512); #ifndef Cabbage_No_Csound //don't start of run Csound in edit mode setOpcodeDirEnv(); csound = new Csound(); #ifdef CSOUND6 csound->SetHostImplementedMIDIIO(true); #endif csound->Reset(); //Logger::writeToLog(csound->GetEnv("OPCODEDIR64")); #ifdef CSOUND5 csound->PreCompile(); #endif csound->SetHostData(this); csound->SetMessageCallback(CabbagePluginAudioProcessor::messageCallback); csound->SetExternalMidiInOpenCallback(OpenMidiInputDevice); csound->SetExternalMidiReadCallback(ReadMidiData); csound->SetExternalMidiOutOpenCallback(OpenMidiOutputDevice); csound->SetExternalMidiWriteCallback(WriteMidiData); #ifndef Cabbage_Plugin_Host if(!getPreference(appProperties, "UseCabbageIO")){ csoundPerfThread = new CsoundPerformanceThread(csound); csoundPerfThread->SetProcessCallback(CabbagePluginAudioProcessor::YieldCallback, (void*)this); } #endif if(pluginType==AUTOMATION_PLUGIN) isAutomator = true; csoundChanList = NULL; numCsoundChannels = 0; csndIndex = 32; //set up PVS struct dataout = new PVSDATEXT; if(inputfile.isNotEmpty()){ File(inputfile).setAsCurrentWorkingDirectory(); #ifdef CSOUND6 csoundParams = new CSOUND_PARAMS(); csoundParams->nchnls_override =2; csound->SetParams(csoundParams); #endif csCompileResult = csound->Compile(const_cast(inputfile.toUTF8().getAddress())); if(csCompileResult==0){ //send root directory path to Csound. setPlayConfigDetails(getNumberCsoundOutChannels(), getNumberCsoundOutChannels(), getCsoundSamplingRate(), getCsoundKsmpsSize()); //simple hack to allow tables to be set up correctly. csound->PerformKsmps(); csound->SetScoreOffsetSeconds(0); csound->RewindScore(); #ifdef WIN32 csound->SetChannel("CSD_PATH", File(inputfile).getParentDirectory().getFullPathName().replace("\\", "\\\\").toUTF8().getAddress()); #else csound->SetChannel("CSD_PATH", File(inputfile).getParentDirectory().getFullPathName().toUTF8().getAddress()); #endif Logger::writeToLog("Csound compiled your file"); //csound->SetYieldCallback(CabbagePluginAudioProcessor::yieldCallback); if(csound->GetSpout()==nullptr); CSspout = csound->GetSpout(); CSspin = csound->GetSpin(); numCsoundChannels = csoundListChannels(csound->GetCsound(), &csoundChanList); csndIndex = csound->GetKsmps(); csdKsmps = csound->GetKsmps(); soundFilerTempVector = new MYFLT[csdKsmps]; cs_scale = csound->Get0dBFS(); csoundStatus = true; debugMessageArray.add(CABBAGE_VERSION); debugMessageArray.add(String("\n")); this->setLatencySamples(csound->GetKsmps()); updateHostDisplay(); } else{ Logger::writeToLog("Csound couldn't compile your file"); csoundStatus=false; //debugMessage = "Csound did not compile correctly. Check for snytax errors by compiling with WinXound"; } } else Logger::writeToLog("Welcome to Cabbage"); #endif lookAndFeel = new CabbageLookAndFeel(); lookAndFeelBasic = new CabbageLookAndFeelBasic(); } #else //=========================================================== // PLUGIN - CONSTRUCTOR //=========================================================== CabbagePluginAudioProcessor::CabbagePluginAudioProcessor(): csoundStatus(false), showMIDI(false), csCompileResult(1), changeMessageType(""), guiON(false), currentLine(-99), noSteps(0), noPatterns(0), timeCounter(0), beat(0), bpm(120), patMatrixActive(0), masterCounter(0), xyAutosCreated(false), updateTable(false), yieldCallbackBool(false), yieldCounter(10), soundFileIndex(0), nativePluginEditor(false), averageSampleIndex(0) { //Cabbage plugins always try to load a csd file with the same name as the plugin library. //Therefore we need to find the name of the library and append a '.csd' to it. #ifdef MACOSX String osxCSD = File::getSpecialLocation(File::currentApplicationFile).getFullPathName()+String("/Contents/")+File::getSpecialLocation(File::currentApplicationFile).getFileName(); File thisFile(osxCSD); Logger::writeToLog("MACOSX defined OK"); #else File thisFile(File::getSpecialLocation(File::currentExecutableFile)); #endif csdFile = thisFile.withFileExtension(String(".csd")).getFullPathName(); Logger::writeToLog(File::getSpecialLocation(File::currentExecutableFile).getFullPathName()); if(csdFile.exists()) Logger::writeToLog("File exists:"+String(csdFile.getFullPathName())); else Logger::writeToLog("File doesn't exist"+String(csdFile.getFullPathName())); File(csdFile.getFullPathName()).setAsCurrentWorkingDirectory(); //set logger #ifdef Cabbage_Logger logFile = File(File(csdFile.getFullPathName()).getParentDirectory().getFullPathName()+"/CabbageLog.txt"); fileLogger = new FileLogger(logFile, String("Cabbage Log..")); Logger::setCurrentLogger(fileLogger); #endif setOpcodeDirEnv(); #ifndef Cabbage_No_Csound csound = new Csound(); csound->SetHostImplementedMIDIIO(true); //csound->Reset(); //csound->PreCompile(); csound->SetHostData(this); midiOutputBuffer.clear(); //for host midi to get sent to Csound, don't need this for standalone //but might use it in the future for midi mapping to controls csound->SetMessageCallback(CabbagePluginAudioProcessor::messageCallback); csound->SetExternalMidiInOpenCallback(OpenMidiInputDevice); csound->SetExternalMidiReadCallback(ReadMidiData); csound->SetExternalMidiOutOpenCallback(OpenMidiOutputDevice); csound->SetExternalMidiWriteCallback(WriteMidiData); patStepMatrix.clear(); patternNames.clear(); patPfieldMatrix.clear(); csoundChanList = NULL; numCsoundChannels = 0; csndIndex = 32; startTimer(20); #ifdef CSOUND6 csoundParams = new CSOUND_PARAMS(); csoundParams->nchnls_override =2; csound->SetParams(csoundParams); #endif csdFile.setAsCurrentWorkingDirectory(); csCompileResult = csound->Compile(const_cast(csdFile.getFullPathName().toUTF8().getAddress())); csdFile.setAsCurrentWorkingDirectory(); if(csCompileResult==0){ Logger::writeToLog("compiled Ok"); keyboardState.allNotesOff(0); keyboardState.reset(); //simple hack to allow tables to be set up correctly. csound->PerformKsmps(); csound->SetScoreOffsetSeconds(0); csound->RewindScore(); //set up PVS struct dataout = new PVSDATEXT; csdKsmps = csound->GetKsmps(); soundFilerTempVector = new MYFLT[csdKsmps]; if(csound->GetSpout()==nullptr); CSspout = csound->GetSpout(); CSspin = csound->GetSpin(); cs_scale = csound->Get0dBFS(); numCsoundChannels = csoundListChannels(csound->GetCsound(), &csoundChanList); csndIndex = csound->GetKsmps(); this->setLatencySamples(csound->GetKsmps()); updateHostDisplay(); //soundFilerVector = new MYFLT[csdKsmps]; csoundStatus = true; debugMessageArray.add(VERSION); debugMessageArray.add(String("\n")); #ifdef WIN32 csound->SetChannel("CSD_PATH", File(csdFile).getParentDirectory().getFullPathName().replace("\\", "\\\\").toUTF8().getAddress()); #else csound->SetChannel("CSD_PATH", File(csdFile).getParentDirectory().getFullPathName().toUTF8().getAddress()); #endif //send host info before performance.. if (getPlayHead() != 0 && getPlayHead()->getCurrentPosition (hostInfo)) csound->SetChannel(CabbageIDs::hostbpm.toUTF8(), hostInfo.bpm); if (getPlayHead() != 0 && getPlayHead()->getCurrentPosition (hostInfo)) csound->SetChannel(CabbageIDs::timeinseconds.toUTF8(), hostInfo.timeInSeconds); if (getPlayHead() != 0 && getPlayHead()->getCurrentPosition (hostInfo)) csound->SetChannel(CabbageIDs::isplaying.toUTF8(), hostInfo.isPlaying); if (getPlayHead() != 0 && getPlayHead()->getCurrentPosition (hostInfo)) csound->SetChannel(CabbageIDs::isrecording.toUTF8(), hostInfo.isRecording); if (getPlayHead() != 0 && getPlayHead()->getCurrentPosition (hostInfo)) csound->SetChannel(CabbageIDs::hostppqpos.toUTF8(), hostInfo.ppqPosition); } else{ Logger::writeToLog("Csound couldn't compile your file"); csoundStatus=false; } #endif createGUI(csdFile.loadFileAsString(), true); } #endif //=========================================================== // PLUGIN - DESTRUCTOR //=========================================================== CabbagePluginAudioProcessor::~CabbagePluginAudioProcessor() { deleteAndZero(lookAndFeel); deleteAndZero(lookAndFeelBasic); Logger::writeToLog("~CabbagePluginAudioProcessor()"); Logger::setCurrentLogger (nullptr); suspendProcessing(true); removeAllChangeListeners(); #ifndef Cabbage_No_Csound patStepMatrix.clear(); patternNames.clear(); patPfieldMatrix.clear(); xyAutomation.clear(); //const MessageManagerLock mmLock; if(csound){ if(csoundPerfThread){ csoundPerfThread->Stop(); csoundPerfThread = nullptr; } //csound->SetHostImplementedMIDIIO(false); csound->DeleteChannelList(csoundChanList); Logger::writeToLog("about to cleanup Csound"); //csound->Cleanup(); //csound->SetHostImplementedMIDIIO(false); csound->Reset(); csound = nullptr; Logger::writeToLog("Csound cleaned up"); if(audioSourcesArray.size()>0){ for(int i=0;isourceChannelInfo.buffer = nullptr; audioSourcesArray[i]->audioSourceBuffer = nullptr; } audioSourcesArray.clear(); } } soundFilerTempVector = nullptr; #endif } int CabbagePluginAudioProcessor::performEntireScore(){ #ifndef Cabbage_No_Csound if(!isNativeThreadRunning){ csoundPerfThread->Play(); isNativeThreadRunning = true; } #endif return 1; } //this callback will be employed when users run with Csound audio IO rather than Cabbage void CabbagePluginAudioProcessor::YieldCallback(void* data){ CabbagePluginAudioProcessor *cabbage = (CabbagePluginAudioProcessor *)data; cabbage->sendOutgoingMessagesToCsound(); cabbage->updateCabbageControls(); } //============================================================================ //RECOMPILE CSOUND. THIS IS CALLED FROM THE PLUGIN HOST WHEN UDPATES ARE MADE ON THE FLY //============================================================================ void CabbagePluginAudioProcessor::reCompileCsound(File file) { #ifndef Cabbage_No_Csound suspendProcessing(true); soundFileIndex = 0; midiOutputBuffer.clear(); getCallbackLock().enter(); csound->DeleteChannelList(csoundChanList); csound->SetHostImplementedMIDIIO(true); csound->Reset(); xyAutosCreated = false; //csound->SetMessageCallback(CabbagePluginAudioProcessor::messageCallback); numCsoundChannels = 0; //csound->SetParams(csoundParams); //csound->SetMessageCallback(CabbagePluginAudioProcessor::messageCallback); csound->SetExternalMidiInOpenCallback(OpenMidiInputDevice); csound->SetExternalMidiReadCallback(ReadMidiData); csound->SetExternalMidiOutOpenCallback(OpenMidiOutputDevice); csound->SetExternalMidiWriteCallback(WriteMidiData); CSspout = nullptr; CSspin = nullptr; csCompileResult = csound->Compile(const_cast(file.getFullPathName().toUTF8().getAddress())); if(csCompileResult==0){ //simple hack to allow tables to be set up correctly. keyboardState.allNotesOff(0); keyboardState.reset(); csndIndex = 0; CSspout = csound->GetSpout(); CSspin = csound->GetSpin(); csound->PerformKsmps(); csound->SetScoreOffsetSeconds(0); csound->RewindScore(); Logger::writeToLog("Csound compiled your file"); numCsoundChannels = csoundListChannels(csound->GetCsound(), &csoundChanList); cs_scale = csound->Get0dBFS(); csoundStatus = true; debugMessageArray.add(CABBAGE_VERSION); debugMessageArray.add(String("\n")); //removeAllChangeListeners(); getCallbackLock().exit(); //init all channels with their init val for(int i=0;iSetChannel( guiCtrls.getReference(i).getStringProp(CabbageIDs::channel).toUTF8(), guiCtrls.getReference(i).getNumProp(CabbageIDs::value)); } #ifdef WIN32 csound->SetChannel("CSD_PATH", file.getParentDirectory().getFullPathName().replace("\\", "\\\\").toUTF8().getAddress()); #else csound->SetChannel("CSD_PATH", file.getParentDirectory().getFullPathName().toUTF8().getAddress()); #endif this->suspendProcessing(false); return; } else{ Logger::writeToLog("Csound couldn't compile your file"); csoundStatus=false; } getCallbackLock().exit(); #endif } //=========================================================== // PARSE CSD FILE AND FILL GUI/GUI-LAYOUT VECTORs. // NO JUCE WIDGETS GET CREATED IN THIS CLASS. ALL // GUI OBJECTS ARE CREATED ON THE FLY IN THE CABBAGE PLUGIN // EDITOR FROM INFORMATION HELD IN THE GUICONTROLS VECTOR //=========================================================== //maybe this should only be done at the end of a k-rate cycle.. void CabbagePluginAudioProcessor::createGUI(String source, bool refresh) { //clear arrays if refresh is set if(refresh==true){ guiLayoutCtrls.clear(); guiCtrls.clear(); CabbagePluginAudioProcessorEditor* editor = dynamic_cast(this->getActiveEditor()); if(editor){ editor->comps.clear(); editor->layoutComps.clear(); } //if rebuilding the entire plugin, reset soundfilers, if present if(audioSourcesArray.size()>0){ for(int i=0;isourceChannelInfo.buffer = nullptr; audioSourcesArray[i]->audioSourceBuffer = nullptr; } audioSourcesArray.clear(); } } int indexOfLastGUICtrl = guiCtrls.size(); int indexOfLastLayoutCtrl = guiLayoutCtrls.size(); int test=100; int checkGUI = isGuiEnabled(); //setGuiEnabled((false)); int guiID=0; StringArray csdText; int lines=1; String csdLine(""); csdText.addLines(source); bool multiComment = false; bool multiLine = false; //check for minimal Cabbage GUI for(int i=0;i"))==-1) { if(!csdText[i].contains("multitab "))//we don't enter for multitab, plants need to be created first if(csdText[i].trim().isNotEmpty()){ if(csdText[i].contains("), \\")|| csdText[i].contains("),\\")|| csdText[i].contains(") \\")){ multiLine = true; csdLine=""; lines=0; while(multiLine){ if(csdText[i+lines].contains("), \\")|| csdText[i+lines].contains("),\\")|| csdText[i+lines].contains(") \\")) lines++; else multiLine=false; } for(int y=0;y<=lines;y++) csdLine = csdLine + " "+ csdText[i+y].trim()+" "; i=i+lines; } else csdLine = csdText[i]; //tidy up string csdLine = csdLine.trimStart(); //csdLine = csdLine.removeCharacters(" \\"); //csdLine = csdLine.removeCharacters(",\\"); //Logger::writeToLog(csdLine); StringArray tokes; tokes.addTokens(csdLine.trimEnd(), ", ", "\""); if(tokes[0].containsIgnoreCase(String("/*"))){ multiComment = true; } if(tokes[0].containsIgnoreCase(String("*\\"))){ multiComment = false; } if(tokes[0].containsIgnoreCase(String(";"))){ //allows for single line comments } else if(tokes[0].containsIgnoreCase(String("}"))){ plantFlag = ""; //reset plantFlag when a closing bracket is found presetFlag = ""; } if(!multiComment) //populate the guiLayoutCtrls vector with non-interactive widgets //the host widgets aren't GUI based but they can be added to this //vector too, as can the editor button. if(tokes[0].equalsIgnoreCase(String("form")) ||tokes[0].equalsIgnoreCase(String("image")) ||tokes[0].equalsIgnoreCase(String("keyboard")) ||tokes[0].equalsIgnoreCase(String("csoundoutput")) ||tokes[0].equalsIgnoreCase(String("line")) ||tokes[0].equalsIgnoreCase(String("label")) ||tokes[0].equalsIgnoreCase(String("hostbpm")) ||tokes[0].equalsIgnoreCase(String("hosttime")) ||tokes[0].equalsIgnoreCase(String("hostplaying")) ||tokes[0].equalsIgnoreCase(String("hostppqpos")) ||tokes[0].equalsIgnoreCase(String("vumeter")) ||tokes[0].equalsIgnoreCase(String("patmatrix")) ||tokes[0].equalsIgnoreCase(String("source")) ||tokes[0].equalsIgnoreCase(String("multitab")) ||tokes[0].equalsIgnoreCase(String("infobutton")) ||tokes[0].equalsIgnoreCase(String("filebutton")) ||tokes[0].equalsIgnoreCase(String("soundfiler")) ||tokes[0].equalsIgnoreCase(String("snapshot")) ||tokes[0].equalsIgnoreCase(String("table")) ||tokes[0].equalsIgnoreCase(String("pvsview")) ||tokes[0].equalsIgnoreCase(String("hostrecording")) ||tokes[0].equalsIgnoreCase(String("directorylist")) ||tokes[0].equalsIgnoreCase(String("transport")) ||tokes[0].equalsIgnoreCase(String("groupbox"))) { CabbageGUIClass cAttr(csdLine.trimEnd(), guiID); if(cAttr.getStringProp("native").length()>0){ //create generic plugin editor and break.. setupNativePluginEditor(); nativePluginEditor = true; return; } if(cAttr.getNumProp(CabbageIDs::guirefresh)>1) guiRefreshRate = cAttr.getNumProp(CabbageIDs::guirefresh); //showMessage(cAttr.getStringProp("type")); csdLine = ""; //add soundfiler buffering sources if(tokes[0].equalsIgnoreCase(String("soundfiler"))){ addSoundfilerSource(cAttr.getStringProp(("file")), cAttr.getChannels()); Logger::writeToLog("createGUI, soundfiler size:"+String(audioSourcesArray.size()-1)); cAttr.setNumProp("soundfilerIndex", audioSourcesArray.size()-1); } //set up stuff for tables if(tokes[0].equalsIgnoreCase(String("table"))){ if(cAttr.getStringArrayProp(CabbageIDs::channel).size()==0) for(int i=0;i2) setPluginName(cAttr.getStringProp("text")); else if(cAttr.getStringProp("caption").length()>2) setPluginName(cAttr.getStringProp("caption")); else setPluginName("Untitled Cabbage Patch!"); //StringArray log = logGUIAttributes(cAttr, String("Non-Interactive")); //debugMessageArray.addArray(logGUIAttributes(cAttr, String("Non-Interactive"))); sendChangeMessage(); //if instrument uses any of the host widgets, or an xypad, turn //on the timer if(tokes[0].equalsIgnoreCase(String("hostbpm")) ||tokes[0].equalsIgnoreCase(String("hosttime")) ||tokes[0].equalsIgnoreCase(String("hostplaying")) ||tokes[0].equalsIgnoreCase(String("hostppqpos")) ||tokes[0].equalsIgnoreCase(String("hostrecording"))) startTimer(20); } //populate the guiCtrls vector with interactive widgets else if(tokes[0].equalsIgnoreCase(String("hslider")) ||tokes[0].equalsIgnoreCase(String("vslider")) ||tokes[0].equalsIgnoreCase(String("rslider")) ||tokes[0].equalsIgnoreCase(String("combobox")) ||tokes[0].equalsIgnoreCase(String("checkbox")) ||tokes[0].equalsIgnoreCase(String("xypad")) ||tokes[0].equalsIgnoreCase(String("button"))){ CabbageGUIClass cAttr(csdLine.trimEnd(), guiID); //Logger::writeToLog(csdLine.trimEnd()); csdLine = ""; //Logger::writeToLog(tokes[0]); //attach widget to plant if need be if(cAttr.getStringProp(String("reltoplant")).equalsIgnoreCase(String(""))){ //showMessage(cAttr.getStringProp(String("relToPlant"))); cAttr.setStringProp(String("reltoplant"), plantFlag); //showMessage(String("presetFlag:")+presetFlag); //showMessage(cAttr.getStringProp("name")); if(cAttr.getStringProp("preset").length()<1) cAttr.setStringProp(String("preset"), presetFlag.trim()); //showMessage(cAttr.getStringProp("preset")); } //xypad contain two control paramters, one for x axis and another for y. As such we add two //to our contorl vector so that plugin hosts display two sliders. We name one of the xypad pads // 'dummy' so that our editor doesn't display it. Our editor only needs to show one xypad. if(tokes[0].equalsIgnoreCase(String("xypad"))){ cAttr.setStringProp(CabbageIDs::xychannel, String("X")); cAttr.setNumProp(CabbageIDs::range, cAttr.getNumProp(CabbageIDs::rangex)); cAttr.setNumProp(CabbageIDs::min, cAttr.getNumProp(CabbageIDs::minx)); cAttr.setNumProp(CabbageIDs::max, cAttr.getNumProp(CabbageIDs::maxx)); cAttr.setNumProp(CabbageIDs::value, cAttr.getNumProp(CabbageIDs::valuex)); cAttr.setStringProp(String(CabbageIDs::channel), cAttr.getStringProp(CabbageIDs::xchannel)); guiCtrls.add(cAttr); cAttr.setStringProp(CabbageIDs::xychannel, String("Y")); cAttr.setNumProp(CabbageIDs::range, cAttr.getNumProp(CabbageIDs::rangey)); cAttr.setNumProp(CabbageIDs::min, cAttr.getNumProp(CabbageIDs::miny)); cAttr.setNumProp(CabbageIDs::max, cAttr.getNumProp(CabbageIDs::maxy)); cAttr.setNumProp(CabbageIDs::value, cAttr.getNumProp(CabbageIDs::valuey)); cAttr.setStringProp(String(CabbageIDs::channel), cAttr.getStringProp(CabbageIDs::ychannel)); //append 'dummy' to name so the editor know not to display the //second xypad cAttr.setStringProp("name", cAttr.getStringProp(CabbageIDs::name)+String("dummy")); guiCtrls.add(cAttr); guiID++; startTimer(10); } else{ //Logger::writeToLog("Value:"+String(cAttr.getNumProp(CabbageIDs::value))); guiCtrls.add(cAttr); guiID++; } //debugMessageArray.addArray(logGUIAttributes(cAttr, String("Interactive"))); sendChangeMessage(); } } } else break; } //end of scan through entire csd text, control vectors are now populated //create multitabs now that plants have been inserted to control vector.. for(int i=0;iSetChannel(guiCtrls.getReference(i).getStringProp(CabbageIDs::channel).toUTF8(), ""); // guiCtrls.getReference(i).getStringArrayPropValue("text", guiCtrls[i].getNumProp(CabbageIDs::value)-1).toUTF8().getAddress()); else csound->SetChannel( guiCtrls.getReference(i).getStringProp(CabbageIDs::channel).toUTF8(), guiCtrls[i].getNumProp(CabbageIDs::value)); #endif } #ifdef Cabbage_Build_Standalone if(this->getActiveEditor()){ CabbagePluginAudioProcessorEditor* editor = dynamic_cast(this->getActiveEditor()); if(refresh){ editor->comps.clear(); editor->layoutComps.clear(); editor->repaint(); //((CabbagePluginAudioProcessorEditor*)getActiveEditor())->setEditMode(false); //editor->setEditMode(false); } //!this will not work as we are moving through our entire control vector for(int i=indexOfLastLayoutCtrl;iInsertGUIControls(guiLayoutCtrls[i]); for(int i=indexOfLastGUICtrl;iInsertGUIControls(guiCtrls[i]); if(refresh) editor->setEditMode(checkGUI); } #endif } //============================================================================ //dynamically remove components from editor window, used in EDIT mode //============================================================================ void CabbagePluginAudioProcessor::removeGUIComponent(int index, String type){ //remove component struct from our abstract vector CabbagePluginAudioProcessorEditor* editor = dynamic_cast(this->getActiveEditor()); if(type=="interactive"){ //remove GUI abstract structure from vector guiCtrls.remove(index); } else{ //remove GUI abstract structure from vector guiLayoutCtrls.remove(index); } editor->updateLayoutEditorFrames(); editor->repaint(); } //============================================================================ //SETS UP A GENERIC PLUGIN EDITOR //============================================================================ void CabbagePluginAudioProcessor::setupNativePluginEditor() { /* //create a basic 'native' gui if specificed by the user. int guiID = 0; guiCtrls.clear(); for(int i=0;iGetControlChannelParams(entry.name, ddefault, dmin, dmax); String parameterInfo; float initVal = (ddefault0 ? dmax : 1)); //cAttr.setNumProp("init", (ddefaultsetCsoundFile(csdFile); //cabbageCsoundEditor->setCsoundOutputText(csoundOutput); //} //cabbageCsoundEditor->setVisible(true); //} //=========================================================== // CALLBACKS FOR STANDALONE //=========================================================== #ifndef Cabbage_No_Csound void CabbagePluginAudioProcessor::messageCallback(CSOUND* csound, int /*attr*/, const char* fmt, va_list args) { CabbagePluginAudioProcessor* ud = (CabbagePluginAudioProcessor *) csoundGetHostData(csound); char msg[MAX_BUFFER_SIZE]; vsnprintf(msg, MAX_BUFFER_SIZE, fmt, args); // MOD - Stefano Bonetti ud->debugMessage += String(msg); //We have to append the incoming msg ud->csoundOutput += ud->debugMessage; ud->debugMessageArray.add(ud->debugMessage); //Logger::writeToLog(String(msg).trim()); ud->sendChangeMessage(); // MOD - End ud->debugMessage = ""; ud = nullptr; } #endif //============================================================================== #if defined(Cabbage_Build_Standalone) || defined(Cabbage_Plugin_Host) CabbagePluginAudioProcessor* JUCE_CALLTYPE createCabbagePluginFilter(String inputfile, bool guiOnOff, int pluginType) { return new CabbagePluginAudioProcessor(inputfile, false, pluginType); } #else AudioProcessor* JUCE_CALLTYPE createPluginFilter() { return new CabbagePluginAudioProcessor(); } #endif //========================================================================== //action listener. Listen to messages being sent form xypad automations //========================================================================== void CabbagePluginAudioProcessor::changeListenerCallback(ChangeBroadcaster *source) { float xVal, yVal; //is message coming from an xypad XYPadAutomation* xyPad = dynamic_cast< XYPadAutomation*>(source); if(xyPad){ #ifdef Cabbage_Build_Standalone setParameterNotifyingHost(xyPad->paramIndex, xyPad->getXValue()); setParameterNotifyingHost(xyPad->paramIndex+1, xyPad->getYValue()); #else if(xyPad->getMinimumXValue()>=0) xVal = (xyPad->getXValue()/xyPad->getXRange())+(fabs(xyPad->getMinimumXValue())/xyPad->getXRange()); else xVal = (xyPad->getXValue()/xyPad->getXRange())-(fabs(xyPad->getMinimumXValue())/xyPad->getXRange()); if(xyPad->getMinimumYValue()<=0) yVal = (xyPad->getYValue()/xyPad->getYRange())+(fabs(xyPad->getMinimumYValue())/xyPad->getYRange()); else yVal = (xyPad->getYValue()/xyPad->getYRange())-(fabs(xyPad->getMinimumYValue())/xyPad->getYRange()); //Logger::writeToLog("Param:"+String(xyPad->paramIndex)+" xyPadXVal:"+String(xVal)); //Logger::writeToLog("Param:"+String(xyPad->paramIndex+1)+" xyPadYVal:"+String(yVal)); setParameterNotifyingHost(xyPad->paramIndex, xVal); setParameterNotifyingHost(xyPad->paramIndex+1, yVal); #endif } } //============================================================================== // getTable data from Csound so table editor can draw table //============================================================================== const Array CabbagePluginAudioProcessor::getTable(int tableNum){ Array points; int tableSize=0; #ifndef Cabbage_No_Csound MYFLT* temp; tableSize = csound->GetTable(temp, tableNum); #else float *temp; #endif if(tableSize>0) points = Array(temp, tableSize); return points; } //================================================================================= // Get and Set Parameter methods, called by our editor, and the plugin host... //================================================================================= float CabbagePluginAudioProcessor::getParameter (int index) { float range = getGUICtrls(index).getNumProp(CabbageIDs::range); float min = getGUICtrls(index).getNumProp(CabbageIDs::min); //Logger::writeToLog("parameterGet-"+String(index)+String("-Min:")+String(min)+" Range:"+String(range)+ " Val:"+String(getGUICtrls(index).getNumProp(CabbageIDs::value))); //Logger::writeToLog("parameterGet:"+String(index)+String(":")+String(guiCtrls[index].getNumProp(CabbageIDs::value))); /* this gets called at any time by our host or out GUI editor */ if(index<(int)guiCtrls.size()){//make sure index isn't out of range #ifndef Cabbage_Build_Standalone float val = (getGUICtrls(index).getNumProp(CabbageIDs::value)/range)-(min/range); if(getGUICtrls(index).getStringProp(CabbageIDs::type)==CabbageIDs::combobox) return (getGUICtrls(index).getNumProp(CabbageIDs::value)/getGUICtrls(index).getNumProp(CabbageIDs::comborange)); else if(getGUICtrls(index).getStringProp(CabbageIDs::type)==CabbageIDs::checkbox || getGUICtrls(index).getStringProp(CabbageIDs::type)==CabbageIDs::button) return getGUICtrls(index).getNumProp(CabbageIDs::value); else return (getGUICtrls(index).getNumProp(CabbageIDs::value)/range)-(min/range); #else return guiCtrls[index].getNumProp(CabbageIDs::value); #endif } else return 0.0f; } void CabbagePluginAudioProcessor::setParameter (int index, float newValue) { String stringMessage; #ifndef Cabbage_No_Csound /* this will get called by the plugin GUI sliders or by the host, via automation. The timer thread in the plugin's editor will constantly update with the values that have been set here. We don't actually change any parameters here, we simply add the messages to a queue. See next method. The updates will only happen when it's safe to do. */ float range, min, max, comboRange; //Logger::writeToLog("parameterSet:"+String(newValue)); if(index<(int)guiCtrls.size())//make sure index isn't out of range { #ifndef Cabbage_Build_Standalone //scaling in here because incoming values in plugin mode range from 0-1 range = getGUICtrls(index).getNumProp(CabbageIDs::range); comboRange = getGUICtrls(index).getNumProp(CabbageIDs::comborange); //Logger::writeToLog("inValue:"+String(newValue)); min = getGUICtrls(index).getNumProp(CabbageIDs::min); if(getGUICtrls(index).getStringProp(CabbageIDs::type)==CabbageIDs::xypad) newValue = (jmax(0.f, newValue)*range)+min; else if(getGUICtrls(index).getStringProp(CabbageIDs::type)==CabbageIDs::combobox)//combo box value need to be rounded... newValue = (newValue*comboRange); else if(getGUICtrls(index).getStringProp(CabbageIDs::type)==CabbageIDs::checkbox || getGUICtrls(index).getStringProp(CabbageIDs::type)==CabbageIDs::button) range=1; else newValue = (newValue*range)+min; //guiCtrls.getReference(index).setNumProp(CabbageIDs::value, newValue); //messageQueue.addOutgoingChannelMessageToQueue(guiCtrls.getReference(index).getStringProp(CabbageIDs::channel).toUTF8(), newValue, //guiCtrls.getReference(index).getStringProp("type")); //Logger::writeToLog(String("parameterSet:"+String(newValue))); #endif //Logger::writeToLog(String("parameterSet:"+String(newValue))); //no need to scale here when in standalone mode if(getGUICtrls(index).getStringProp(CabbageIDs::type)==CabbageIDs::combobox && getGUICtrls(index).getStringProp(CabbageIDs::channeltype)==CabbageIDs::stringchannel) { stringMessage = getGUICtrls(index).getStringArrayPropValue(CabbageIDs::text, newValue-1); //Logger::writeToLog(stringMessage); messageQueue.addOutgoingChannelMessageToQueue(guiCtrls.getReference(index).getStringProp(CabbageIDs::channel), stringMessage, CabbageIDs::stringchannel); } else messageQueue.addOutgoingChannelMessageToQueue(guiCtrls.getReference(index).getStringProp(CabbageIDs::channel), newValue, guiCtrls.getReference(index).getStringProp(CabbageIDs::type)); guiCtrls.getReference(index).setNumProp(CabbageIDs::value, newValue); } #endif } //============================================================================== //this method gets called after a performKsmps() to update our GUI controls //with messages from Csound. For instance, a user might wish to change the position //of a GUI slider from Csound by using a chnset opcode. The speed at which this is //updated can be teaked, so as not to hog resources. It might be worth allowing users //the option of setting how fast this update... void CabbagePluginAudioProcessor::updateCabbageControls() { #ifndef Cabbage_No_Csound String chanName; if(!CSCompResult) { MYFLT* val=0; //update all control widgets for(int index=0;indexGetChannel(guiCtrls[index].getStringProp(CabbageIDs::channel).getCharPointer()); //Logger::writeToLog("Channel:"+guiCtrls[index].getStringProp(CabbageIDs::channel)); //Logger::writeToLog("value:"+String(value)); guiCtrls.getReference(index).setNumProp(CabbageIDs::value, value); } } //update all layout control widgets //currently this is only needed for table widgets as other layout controls //don't use channel messages... for(int index=0;indexGetChannel(guiLayoutCtrls[index].getStringArrayPropValue(CabbageIDs::channel, y).getCharPointer()); guiLayoutCtrls[index].setTableChannelValues(y, value); } } } } sendChangeMessage(); #endif } //============================================================================== //this method only gets called when it's safe to do so, i.e., between calls to performKsmps() //this method sends any channel messages that are in the queue to from Cabbage to Csound void CabbagePluginAudioProcessor::sendOutgoingMessagesToCsound() { #ifndef Cabbage_No_Csound if(!csCompileResult){ #ifndef Cabbage_Build_Standalone if (getPlayHead() != 0 && getPlayHead()->getCurrentPosition (hostInfo)) csound->SetChannel(CabbageIDs::hostbpm.toUTF8(), hostInfo.bpm); if (getPlayHead() != 0 && getPlayHead()->getCurrentPosition (hostInfo)) csound->SetChannel(CabbageIDs::timeinseconds.toUTF8(), hostInfo.timeInSeconds); if (getPlayHead() != 0 && getPlayHead()->getCurrentPosition (hostInfo)) csound->SetChannel(CabbageIDs::isplaying.toUTF8(), hostInfo.isPlaying); if (getPlayHead() != 0 && getPlayHead()->getCurrentPosition (hostInfo)) csound->SetChannel(CabbageIDs::isrecording.toUTF8(), hostInfo.isRecording); if (getPlayHead() != 0 && getPlayHead()->getCurrentPosition (hostInfo)) csound->SetChannel(CabbageIDs::hostppqpos.toUTF8(), hostInfo.ppqPosition); #endif for(int i=0;iInputMessage(scoreEvents[y].toUTF8()); //scoreEvents.clear(); } //update Csound function tables with values from table widget else if(messageQueue.getOutgoingChannelMessageFromQueue(i).type=="updateTable"){ //Logger::writeToLog(messageQueue.getOutgoingChannelMessageFromQueue(i).fStatement.toUTF8()); csound->InputMessage(messageQueue.getOutgoingChannelMessageFromQueue(i).fStatement.getCharPointer()); } //catch string messags else if(messageQueue.getOutgoingChannelMessageFromQueue(i).type==CabbageIDs::stringchannel){ csound->SetChannel(messageQueue.getOutgoingChannelMessageFromQueue(i).channelName.getCharPointer(), messageQueue.getOutgoingChannelMessageFromQueue(i).stringVal.toUTF8().getAddress()); } else csound->SetChannel(messageQueue.getOutgoingChannelMessageFromQueue(i).channelName.getCharPointer(), messageQueue.getOutgoingChannelMessageFromQueue(i).value); } messageQueue.flushOutgoingChannelMessages(); if(isAutomator){ //sendChangeMessage(); //sendActionMessage("update automation:"+String(automationParamID)+"|"+String(automationAmp)); //Logger::writeToLog("update automation:"+String(automationAmp)); } } #endif } //============================================================================== //set up buffered audio source for each sound file object. The method below this one //fills Csound channels with sampler from our soundfiler controls.. void CabbagePluginAudioProcessor::addSoundfilerSource(String filename, StringArray channels) { #ifndef Cabbage_No_Csound audioSourcesArray.add(new CabbageAudioSource(filename, csound->GetKsmps())); Logger::writeToLog("Number of soundfilers:"+String(audioSourcesArray.size())); audioSourcesArray[audioSourcesArray.size()-1]->channels = channels; if(File(filename).exists())Logger::writeToLog("File exists"); else{ Logger::writeToLog("Soundfiler can't find file"); //set default sampling rate in case of no file name given audioSourcesArray[audioSourcesArray.size()-1]->isValidFile = false; audioSourcesArray[audioSourcesArray.size()-1]->sampleRate = 44100; } #endif } //gets sample data from any soundfiler controls and passes it to Csound void CabbagePluginAudioProcessor::sendAudioToCsoundFromSoundFilers(int numSamples) { #ifndef Cabbage_No_Csound if(this->isSuspended()==false){ for(int i=0;isourceChannelInfo.buffer = &output; audioSourcesArray[i]->sourceChannelInfo.startSample = 0; audioSourcesArray[i]->sourceChannelInfo.numSamples = output.getNumSamples(); if(audioSourcesArray[i]->isSourcePlaying && audioSourcesArray[i]->isValidFile) audioSourcesArray[i]->audioSourceBuffer->getNextAudioBlock(audioSourcesArray[i]->sourceChannelInfo); else output.clear(); for(int index=0;indexchannels.size();index++) { float* samples = output.getSampleData(index); for(int y=0;ychannels[index]); if(csoundGetChannelPtr(csound->GetCsound(), &soundFilerTempVector, audioSourcesArray[i]->channels[index].toUTF8(), CSOUND_INPUT_CHANNEL | CSOUND_AUDIO_CHANNEL) != 0) Logger::writeToLog("error sending audio to Csound"); } } } #endif } //======================================================================== // Standard plugin methods, getName, getNumParameters, setParamterName, get ProgramName, etc.... //============================================================================== const String CabbagePluginAudioProcessor::getName() const { return pluginName; } int CabbagePluginAudioProcessor::getNumParameters() { return guiCtrls.size(); } const String CabbagePluginAudioProcessor::getParameterName (int index) { if(index<(int)guiCtrls.size())//make sure index isn't out of range return guiCtrls.getReference(index).getStringProp(CabbageIDs::channel); else return String::empty; } const String CabbagePluginAudioProcessor::getParameterText (int index) { if(index<(int)guiCtrls.size())//make sure index isn't out of range return String (guiCtrls.getReference(index).getNumProp(CabbageIDs::value), 2); else return String::empty; } const String CabbagePluginAudioProcessor::getInputChannelName (int channelIndex) const { if(channelIndex<(int)guiCtrls.size())//make sure index isn't out of range return String (channelIndex + 1); else return String::empty; } const String CabbagePluginAudioProcessor::getOutputChannelName (int channelIndex) const { if(channelIndex<(int)guiCtrls.size())//make sure index isn't out of range return String (channelIndex + 1); else return String::empty; } bool CabbagePluginAudioProcessor::isInputChannelStereoPair (int /*index*/) const { return true; } bool CabbagePluginAudioProcessor::isOutputChannelStereoPair (int /*index*/) const { return true; } bool CabbagePluginAudioProcessor::acceptsMidi() const { #if JucePlugin_WantsMidiInput return true; #else return false; #endif } bool CabbagePluginAudioProcessor::producesMidi() const { #if JucePlugin_ProducesMidiOutput return true; #else return false; #endif } void CabbagePluginAudioProcessor::setGuiEnabled(bool val){ #ifdef Cabbage_Build_Standalone guiON = val; CabbagePluginAudioProcessorEditor* editor = dynamic_cast< CabbagePluginAudioProcessorEditor*>(this->getActiveEditor()); if(editor){ if(val==false){ int val = getPreference(appProperties, "ExternalEditor"); if(val) csdFile.replaceWithText(codeEditor->getAllText()); //editor->resizer->setVisible(false); //editor->propsWindow->setVisible(false); } else{ //editor->resizer->setVisible(true); } } #endif } int CabbagePluginAudioProcessor::getNumPrograms() { return 0; } int CabbagePluginAudioProcessor::getCurrentProgram() { return 0; } void CabbagePluginAudioProcessor::setCurrentProgram (int /*index*/) { } const String CabbagePluginAudioProcessor::getProgramName (int /*index*/) { return String::empty; } void CabbagePluginAudioProcessor::changeProgramName (int /*index*/, const String& /*newName*/) { } //============================================================================== void CabbagePluginAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock) { // Use this method as the place to do any pre-playback // initialisation that you need.. keyboardState.reset(); for(int i=0;iisValidFile) audioSourcesArray[i]->audioSourceBuffer->prepareToPlay(samplesPerBlock, sampleRate); } //============================================================================== void CabbagePluginAudioProcessor::releaseResources() { // When playback stops, you can use this as an opportunity to free up any // spare memory, etc. keyboardState.reset(); } //============================================================================== //this following callback only runs in plugin mode, and only when one of the //host widgets are being used void CabbagePluginAudioProcessor::timerCallback(){ #ifndef Cabbage_No_Csound for(int y=0;yupdate(); } #endif } //============================================================================== void CabbagePluginAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) { if(!isSuspended() && !isGuiEnabled()){ float* audioBuffer; float lastOutputAmp; #ifndef Cabbage_No_Csound int numSamples = buffer.getNumSamples(); if(csCompileResult==0){ keyboardState.processNextMidiBuffer (midiMessages, 0, buffer.getNumSamples(), true); midiBuffer = midiMessages; ccBuffer = midiMessages; //if no inputs are used clear buffer in case it's not empty.. if(getNumInputChannels()==0) buffer.clear(); #if JucePlugin_ProducesMidiOutput if(!midiOutputBuffer.isEmpty()) midiMessages.swapWith(midiOutputBuffer); #endif for(int i=0;iGetKsmps()) { getCallbackLock().enter(); //slow down calls to these functions, no need for them to be firing at k-rate yieldCounter = (yieldCounter>guiRefreshRate) ? 0 : yieldCounter+1; if(yieldCounter==0){ sendOutgoingMessagesToCsound(); updateCabbageControls(); } if(audioSourcesArray.size()>0) sendAudioToCsoundFromSoundFilers(csound->GetKsmps()); csCompileResult = csound->PerformKsmps(); if(csCompileResult!=0) suspendProcessing(true); getCallbackLock().exit(); csndIndex = 0; } if(!csCompileResult) { for(int channel = 0; channel < getNumOutputChannels(); channel++ ) { audioBuffer = buffer.getSampleData(channel,0); pos = csndIndex*getNumOutputChannels(); CSspin[channel+pos] = audioBuffer[i]*cs_scale; audioBuffer[i] = (CSspout[channel+pos]/cs_scale); } } else buffer.clear(); } }//if not compiled just mute output else{ for(int channel = 0; channel < getNumInputChannels(); channel++) { audioBuffer = buffer.getSampleData(channel,0); for(int i=0; imidiBuffer.isEmpty() && cnt <= (nbytes - 3)){ MidiMessage message(0xf4, 0, 0, 0); MidiBuffer::Iterator i (midiData->midiBuffer); int messageFrameRelativeTothisProcess; while (i.getNextEvent (message, messageFrameRelativeTothisProcess)) { if(message.isNoteOn()){ *mbuf++ = (unsigned char)0x90 + message.getChannel(); *mbuf++ = (unsigned char)message.getNoteNumber(); *mbuf++ = (unsigned char)message.getVelocity(); cnt += 3; } else if(message.isNoteOff()){ *mbuf++ = (unsigned char)0x80 + message.getChannel(); *mbuf++ = (unsigned char)message.getNoteNumber(); *mbuf++ = (unsigned char)message.getVelocity(); cnt += 3; } else if(message.isAllSoundOff()){ *mbuf++ = (unsigned char)0x7B + message.getChannel(); *mbuf++ = (unsigned char)message.getNoteNumber(); *mbuf++ = (unsigned char)message.getVelocity(); cnt += 3; } else if(message.isController()){ *mbuf++ = (unsigned char)0xB0 + message.getChannel()-1; *mbuf++ = (unsigned char)message.getControllerNumber(); *mbuf++ = (unsigned char)message.getControllerValue(); cnt += 3; } } midiData->midiBuffer.clear(); } return cnt; } //============================================================================== // Opens MIDI output device, adding -QN to your CsOptions will causes this method to be called // as soon as your plugin loads //============================================================================== int CabbagePluginAudioProcessor::OpenMidiOutputDevice(CSOUND * csound, void **userData, const char* /*devName*/) { *userData = csoundGetHostData(csound); if(!userData) Logger::writeToLog("\n\ncan't open midi out\n\n"); return 0; } //============================================================================== // Write MIDI data to plugin's MIDI output. Each time Csound outputs a midi message this // method should be called. Note: you must have -Q set in your CsOptions //============================================================================== int CabbagePluginAudioProcessor::WriteMidiData(CSOUND* /*csound*/, void *_userData, const unsigned char *mbuf, int nbytes) { CabbagePluginAudioProcessor *userData = (CabbagePluginAudioProcessor *)_userData; if(!userData){ Logger::writeToLog("\n\nInvalid"); return 0; } MidiMessage message(mbuf, nbytes, 0); //Logger::writeToLog(String(message.getNoteNumber())); userData->midiOutputBuffer.addEvent(message, 0); return nbytes; } #endif //============================================================================== bool CabbagePluginAudioProcessor::hasEditor() const { return true; // (change this to false if you choose to not supply an editor) } AudioProcessorEditor* CabbagePluginAudioProcessor::createEditor() { if(!nativePluginEditor) return new CabbagePluginAudioProcessorEditor (this); else return new CabbageGenericAudioProcessorEditor (this); } //============================================================================== void CabbagePluginAudioProcessor::getStateInformation (MemoryBlock& destData) { // You should use this method to store your parameters in the memory block. // Here's an example of how you can use XML to make it easy and more robust: // Create an outer XML element.. XmlElement xml ("CABBAGE_PLUGIN_SETTINGS"); for(int i=0;i xmlState (getXmlFromBinary (data, sizeInBytes)); if (xmlState != nullptr) { // make sure that it's actually our type of XML object.. if (xmlState->hasTagName ("CABBAGE_PLUGIN_SETTINGS")) { for(int i=0;igetNumParameters();i++) this->setParameter(i, (float)xmlState->getDoubleAttribute(guiCtrls[i].getStringProp(CabbageIDs::channel))); } } } //==============================================================================