MLPublishedParam::MLPublishedParam(const MLPath & procPath, const MLSymbol name, const MLSymbol alias, const MLSymbol type, int idx) : mPublishedAlias(alias), mIndex(idx), mAutomatable(true), mFlip(false) { setRange(0.f, 1.f, 0.01f, false, 0.f); mUnit = kJucePluginParam_Generic; mWarpMode = kJucePluginParam_Linear; mParamValue = 0.f; mDefault = 0.f; mZeroThreshold = 0.f - (MLParamValue)(2 << 16); mGroupIndex = -1; addAddress(procPath, name); // default type is float if(type == MLSymbol()) { mType = MLSymbol("float"); } else { mType = type; } }
// add a number n to the end of a symbol after removing any final number. // n must be >= 0. MLSymbol MLSymbol::withFinalNumber(int n) const { // *not* static because different threads could collide accesing a single buffer. char tempBuf[kMLMaxSymbolLength + kMLMaxNumberLength] = {0}; const std::string& str = getString(); const char* p = str.c_str(); int i, j; int l = 0; // set l to length while(p[l]) { l++; } // find last non-number character for (i = l-1; i >= 0; --i) { if (!isDigit(p[i])) break; } // copy symbol without number to temp for(j=0; j<=i; ++j) { tempBuf[j] = p[j]; } naturalNumberToDigits(n, tempBuf+j); //debug() << "in: " << n << ", char*: " << tempBuf << ", Symbol " << MLSymbol(tempBuf) << "\n"; return MLSymbol(tempBuf); }
// remove any final number. MLSymbol MLSymbol::withoutFinalNumber() const { // *not* static because different threads could collide accessing a single buffer. char tempBuf[kMLMaxSymbolLength] = {0}; const std::string& str = getString(); const char* p = str.c_str(); int i, j; int l = 0; // set l to length while(p[l]) { l++; } // find last non-number character for (i = l-1; i >= 0; --i) { if (!isDigit(p[i])) break; } // copy symbol without number to temp for(j=0; j<=i; ++j) { tempBuf[j] = p[j]; } tempBuf[i+1] = 0; return MLSymbol(tempBuf); }
// base-26 arithmetic with letters produces A, B, ... Z, AA, AB ... const MLSymbol MLNameMaker::nextName() { std::string nameStr; const int base = 26; const char baseChar = 'A'; int a, m, d, rem; std::list<int> digits; a = index++; if (!a) { digits.push_front(0); } else while(a) { d = a / base; m = d * base; rem = a - m; digits.push_front(rem); a = d; } while(digits.size()) { d = digits.front(); digits.pop_front(); nameStr += (char)d + baseChar; } return MLSymbol(nameStr.c_str()); }
// replace wild card with the given number. MLSymbol MLSymbol::withWildCardNumber(int n) const { const std::string& inStr = getString(); const char* p = inStr.c_str(); int l = 0; int m = 0; char c; char tempBuf[kMLMaxSymbolLength] = {0}; while(p[l] && (m < kMLMaxSymbolLength-1)) { c = p[l]; if (c == '*') { const char* d = positiveIntToDigits(n); int j = 0; while(d[j] && (m < kMLMaxSymbolLength-1)) { tempBuf[m] = d[j]; j++; m++; } m--; } else { tempBuf[m] = c; } l++; m++; } tempBuf[m] = 0; return MLSymbol(tempBuf); }
// parse an input string into our representation: an array of MLSymbols. MLPath::MLPath(const char * str) : mStart(0), mEnd(0), mCopy(0) { memset(mpData, '\0', kMLPathMaxSymbols*sizeof(MLSymbol)); unsigned symStart = 0; unsigned symEnd = 0; // get token up to each delimiter int endPos = 0; while(str[endPos]) { for(endPos = symStart; str[endPos]; endPos++) { if (str[endPos] == '/') { break; } } symEnd = endPos - 1; // make symbol addSymbol(MLSymbol(str+symStart, symEnd - symStart + 1)); symStart = symEnd + 2; } }
void MLSignalView::setupSignalView (MLDSPEngine* pEng, const MLSymbol sigName, int voices) { mViewingSignal = true; mpEngine = pEng; mSignalName = sigName; if(voices != mVoices) { mpWidget->setProperty(MLSymbol("voices"), voices); mVoices = voices; } }
MLSymbol MLProc::getOutputName(int index) { if (procInfo().hasVariableOutputs()) { return MLSymbol("out").withFinalNumber(index); } else if (index <= (int)mOutputs.size()) { MLSymbolMap& map = procInfo().getOutputMap(); for (MLSymbolMap::MLSymbolMapIter i = map.begin(); i != map.end(); i++) { if ((*i).second == index) { return ((*i).first); } } } return MLSymbol(); }
void MLPluginController::processFileFromCollection (MLSymbol action, const MLFile fileToProcess, const MLFileCollection& collection, int idx, int size) { MLSymbol collectionName(collection.getName()); if(action == "process") { if(collectionName.beginsWith(MLSymbol("convert_presets"))) { // add file action to queue FileAction f(action, fileToProcess, &collection, idx, size); PaUtil_WriteRingBuffer( &mFileActionQueue, &f, 1 ); } } }
void SoundplaneModel::setAllPropertiesToDefaults() { // parameter defaults and creation setProperty("max_touches", 4); setProperty("lopass", 100.); setProperty("z_thresh", 0.01); setProperty("z_max", 0.05); setProperty("z_curve", 0.25); setProperty("display_scale", 1.); setProperty("quantize", 1.); setProperty("lock", 0.); setProperty("abs_rel", 0.); setProperty("snap", 250.); setProperty("vibrato", 0.5); setProperty("t_thresh", 0.2); setProperty("midi_active", 0); setProperty("midi_multi_chan", 1); setProperty("midi_start_chan", 1); setProperty("data_freq_midi", 250.); setProperty("kyma_poll", 1); setProperty("osc_active", 1); setProperty("osc_raw", 0); setProperty("data_freq_osc", 250.); setProperty("bend_range", 48); setProperty("transpose", 0); setProperty("bg_filter", 0.05); setProperty("hysteresis", 0.5); // menu param defaults setProperty("viewmode", "calibrated"); // preset menu defaults (TODO use first choices?) setProperty("zone_preset", "continuous pitch x"); setProperty("touch_preset", "touch default"); setProperty("view_page", 0); for(int i=0; i<32; ++i) { setProperty(MLSymbol("carrier_toggle").withFinalNumber(i), 1); } }
// setup info pertaining to the plugin format we are controlling // void MLPluginController::initialize() { AudioProcessor::WrapperType w = getProcessor()->wrapperType; std::string regStr, bitsStr, pluginType; switch(w) { case AudioProcessor::wrapperType_VST: pluginType = "VST"; break; case AudioProcessor::wrapperType_AudioUnit: pluginType = "AU"; break; case AudioProcessor::wrapperType_Standalone: pluginType = "App"; break; default: pluginType = "?"; break; } #if (__LP64__) || (_WIN64) bitsStr = ".64"; #else bitsStr = ".32"; #endif mVersionString = std::string("version "); mVersionString += (std::string(MLProjectInfo::versionString)); mVersionString += " (" + pluginType + bitsStr + ")"; regStr = mVersionString; #if DEMO regStr += " DEMO\n"; #else regStr += ", licensed to:\n"; #endif MLAppView* myView = getView(); if(myView) { MLLabel* regLabel = static_cast<MLLabel*>(myView->getWidget("reg")); if(regLabel) { regLabel->setPropertyImmediate(MLSymbol("text"), regStr); } } startTimer(kControllerTimerRate); }
void MLPluginProcessor::loadPluginDescription(const char* desc) { mpPluginDoc = new XmlDocument(String(desc)); if (mpPluginDoc.get()) { ScopedPointer<XmlElement> doc (mpPluginDoc->getDocumentElement(true)); if (doc) // true = quick scan header { mEngine.scanDoc(&*mpPluginDoc, &mNumParameters); debug() << "loaded " << JucePlugin_Name << " plugin description, " << mNumParameters << " parameters.\n"; } else { MLError() << "MLPluginProcessor: error loading plugin description!\n"; } } else { MLError() << "MLPluginProcessor: couldn't load plugin description!\n"; return; } // get plugin parameters and initial values and create corresponding model properties. int params = getNumParameters(); for(int i=0; i<params; ++i) { MLPublishedParamPtr p = getParameterPtr(i); MLPublishedParam* param = &(*p); if(param) { MLSymbol type = param->getType(); if((type == "float") || (type == MLSymbol())) { debug() << param->getAlias() << " is a float type \n"; setProperty(param->getAlias(), param->getDefault()); } else { debug() << param->getAlias() << " is a non-float type \n"; } } } }
const String MLPluginProcessor::getParameterName (int index) { MLSymbol nameSym; const int p = mEngine.getPublishedParams(); if (index < mNumParameters) { if (p == 0) // doc has been scanned but not built { nameSym = MLSymbol("param").withFinalNumber(index); } else { // graph has been built nameSym = mEngine.getParamPtr(index)->getAlias(); //debug() << "getParameterName: " << index << " is " << nameSym.getString().c_str() << ".\n"; } } return (String(nameSym.getString().c_str())); }
const MLSymbol MLPluginProcessor::XMLAttrToSymbol(const String& str) { std::string strCopy(str.toUTF8()); unsigned len = strCopy.length(); for(unsigned c = 0; c < len; ++c) { unsigned char k = strCopy[c]; if(k == ':') { strCopy[c] = '#'; } else if(k == 0xB7) { strCopy[c] = '*'; } } return (MLSymbol(strCopy.c_str())); }
const MLSymbol MLNameMaker::nextName() { return MLSymbol(nextNameAsString().c_str()); }
MLEnvelope* MLPluginView::addEnvelope(const MLRect & r, const MLSymbol paramName) { MLEnvelope * pE = new MLEnvelope(); const std::string paramStr = paramName.getString(); addPropertyView(MLSymbol(paramStr + "_delay"), pE, MLSymbol("delay")); addPropertyView(MLSymbol(paramStr + "_attack"), pE, MLSymbol("attack")); addPropertyView(MLSymbol(paramStr + "_decay"), pE, MLSymbol("decay")); addPropertyView(MLSymbol(paramStr + "_sustain"), pE, MLSymbol("sustain")); addPropertyView(MLSymbol(paramStr + "_release"), pE, MLSymbol("release")); addPropertyView(MLSymbol(paramStr + "_repeat"), pE, MLSymbol("repeat")); addWidgetToView(pE, r, paramName); return(pE); }
void SoundplaneModel::doPropertyChangeAction(MLSymbol p, const MLProperty & newVal) { // debug() << "SoundplaneModel::doPropertyChangeAction: " << p << " -> " << newVal << "\n"; int propertyType = newVal.getType(); switch(propertyType) { case MLProperty::kFloatProperty: { float v = newVal.getFloatValue(); if (p.withoutFinalNumber() == MLSymbol("carrier_toggle")) { // toggles changed -- mute carriers unsigned long mask = 0; for(int i=0; i<32; ++i) { MLSymbol tSym = MLSymbol("carrier_toggle").withFinalNumber(i); bool on = (int)(getFloatProperty(tSym)); mask = mask | (on << i); } mCarriersMask = mask; mCarrierMaskDirty = true; // trigger carriers set in a second or so } else if (p == "all_toggle") { bool on = (bool)(v); for(int i=0; i<32; ++i) { MLSymbol tSym = MLSymbol("carrier_toggle").withFinalNumber(i); setProperty(tSym, on); } mCarriersMask = on ? ~0 : 0; mCarrierMaskDirty = true; // trigger carriers set in a second or so } else if (p == "max_touches") { mTracker.setMaxTouches(v); mMIDIOutput.setMaxTouches(v); mOSCOutput.setMaxTouches(v); } else if (p == "lopass") { mTracker.setLopass(v); } else if (p == "z_thresh") { mTracker.setThresh(v); } else if (p == "z_max") { mTracker.setMaxForce(v); } else if (p == "z_curve") { mTracker.setForceCurve(v); } else if (p == "snap") { sendParametersToZones(); } else if (p == "vibrato") { sendParametersToZones(); } else if (p == "lock") { sendParametersToZones(); } else if (p == "data_freq_midi") { // TODO attribute mMIDIOutput.setDataFreq(v); } else if (p == "data_freq_osc") { // TODO attribute mOSCOutput.setDataFreq(v); } else if (p == "midi_active") { mMIDIOutput.setActive(bool(v)); } else if (p == "midi_multi_chan") { mMIDIOutput.setMultiChannel(bool(v)); } else if (p == "midi_start_chan") { mMIDIOutput.setStartChannel(int(v)); } else if (p == "midi_pressure_active") { mMIDIOutput.setPressureActive(bool(v)); } else if (p == "osc_active") { bool b = v; mOSCOutput.setActive(b); listenToOSC(b ? kDefaultUDPReceivePort : 0); } else if (p == "osc_send_matrix") { bool b = v; mSendMatrixData = b; } else if (p == "t_thresh") { mTracker.setTemplateThresh(v); } else if (p == "bg_filter") { mTracker.setBackgroundFilter(v); } else if (p == "quantize") { bool b = v; mTracker.setQuantize(b); sendParametersToZones(); } else if (p == "rotate") { bool b = v; mTracker.setRotate(b); } else if (p == "retrig") { mMIDIOutput.setRetrig(bool(v)); sendParametersToZones(); } else if (p == "hysteresis") { mMIDIOutput.setHysteresis(v); sendParametersToZones(); } else if (p == "transpose") { sendParametersToZones(); } else if (p == "bend_range") { mMIDIOutput.setBendRange(v); sendParametersToZones(); } else if (p == "debug_pause") { debug().setActive(!bool(v)); } else if (p == "kyma_poll") { mMIDIOutput.setKymaPoll(bool(v)); } } break; case MLProperty::kStringProperty: { const std::string& str = newVal.getStringValue(); if (p == "viewmode") { // nothing to do for Model } else if (p == "midi_device") { mMIDIOutput.setDevice(str); } else if (p == "zone_JSON") { loadZonesFromString(str); } } break; case MLProperty::kSignalProperty: { const MLSignal& sig = newVal.getSignalValue(); if(p == MLSymbol("carriers")) { // get carriers from signal assert(sig.getSize() == kSoundplaneSensorWidth); for(int i=0; i<kSoundplaneSensorWidth; ++i) { mCarriers[i] = sig[i]; } mNeedsCarriersSet = true; } if(p == MLSymbol("tracker_calibration")) { mTracker.setCalibration(sig); } if(p == MLSymbol("tracker_normalize")) { mTracker.setNormalizeMap(sig); } } break; default: break; } }
void MLPluginProcessor::setStateFromXML(const XmlElement& xmlState, bool setViewAttributes) { if (!(xmlState.hasTagName (JucePlugin_Name))) return; if (!(mEngine.getCompileStatus() == MLProc::OK)) return; // TODO revisit need to compile first // getCallbackLock() is in juce_AudioProcessor // process lock is a quick fix. it is here to prevent doParams() from getting called in // process() methods and thereby setting mParamsChanged to false before the real changes take place. // A better alternative would be a lock-free queue of parameter changes. const ScopedLock sl (getCallbackLock()); // only the differences between default parameters and the program state are saved in a program, // so the first step is to set the default parameters. setDefaultParameters(); // get program version of saved state unsigned blobVersion = xmlState.getIntAttribute ("pluginVersion"); unsigned pluginVersion = JucePlugin_VersionCode; if (blobVersion > pluginVersion) { // TODO show error to user MLError() << "MLPluginProcessor::setStateFromXML: saved program version is newer than plugin version!\n"; return; } // try to load scale if a scale attribute exists // TODO auto save all state including this const String scaleDir = xmlState.getStringAttribute ("scaleDir"); // look for old-style dir attribute const String scaleName = xmlState.getStringAttribute ("scaleName"); String fullName; if(scaleName != String::empty) { fullName = scaleName; if(scaleDir != String::empty) { fullName = scaleDir + String("/") + fullName + ".scl"; } } else { fullName = "12-equal"; } std::string fullScaleName(fullName.toUTF8()); setProperty("key_scale", fullScaleName); bool loaded = false; // look for scale under full name with path if(fullScaleName != std::string()) { const MLFilePtr f = mScaleFiles->getFileByName(fullScaleName); if(f != MLFilePtr()) { loadScale(f->mFile); loaded = true; } } if(!loaded) { loadDefaultScale(); } // get preset name saved in blob. when saving from AU host, name will also be set from RestoreState(). const String presetName = xmlState.getStringAttribute ("presetName"); setProperty("preset", std::string(presetName.toUTF8())); /* debug() << "MLPluginProcessor: setStateFromXML: loading program " << presetName << ", version " << std::hex << blobVersion << std::dec << "\n"; MemoryOutputStream myStream; xmlState->writeToStream (myStream, ""); debug() << myStream.toString(); */ /* setCurrentPresetName(presetName.toUTF8()); setCurrentPresetDir(presetDir.toUTF8()); */ // get plugin-specific translation table for updating older versions of data std::map<MLSymbol, MLSymbol> translationTable; // TODO move this into Aalto! // make translation tables based on program version. // if (blobVersion <= 0x00010120) { // translate seq parameters for(unsigned n=0; n<16; ++n) { std::stringstream pName; std::stringstream pName2; pName << "seq_value" << n; pName2 << "seq_pulse" << n; MLSymbol oldSym(pName.str()); MLSymbol newSym = MLSymbol("seq_value#").withFinalNumber(n); MLSymbol oldSym2(pName2.str()); MLSymbol newSym2 = MLSymbol("seq_pulse#").withFinalNumber(n); translationTable[oldSym] = newSym; translationTable[oldSym2] = newSym2; } } if (blobVersion <= 0x00010200) { MLSymbol oldSym = MLSymbol("seq_value"); MLSymbol newSym = MLSymbol("seq_value").withFinalNumber(0); MLSymbol oldSym2 = MLSymbol("seq_pulse"); MLSymbol newSym2 = MLSymbol("seq_pulse").withFinalNumber(0); translationTable[oldSym] = newSym; translationTable[oldSym2] = newSym2; // translate seq parameters for(unsigned n=1; n<16; ++n) { oldSym = MLSymbol("seq_value#").withFinalNumber(n); newSym = MLSymbol("seq_value").withFinalNumber(n); oldSym2 = MLSymbol("seq_pulse#").withFinalNumber(n); newSym2 = MLSymbol("seq_pulse").withFinalNumber(n); translationTable[oldSym] = newSym; translationTable[oldSym2] = newSym2; } } // get params from xml const unsigned numAttrs = xmlState.getNumAttributes(); String patcherInputStr ("patcher_input_"); for(unsigned i=0; i<numAttrs; ++i) { // get name / value pair. const String& attrName = xmlState.getAttributeName(i); const MLParamValue paramVal = xmlState.getDoubleAttribute(attrName); // if not a patcher input setting, if (!attrName.contains(patcherInputStr)) { // see if we have this named parameter in our engine. MLSymbol paramSym = XMLAttrToSymbol(attrName); const int pIdx = getParameterIndex(paramSym); if (pIdx >= 0) { // debug() << "setStateFromXML: <" << paramSym << " = " << paramVal << ">\n"; setPropertyImmediate(paramSym, paramVal); } else // try finding a match through translation table. { //debug() << "Looking for parameter " << paramSym << " in table...\n"; std::map<MLSymbol, MLSymbol>::iterator it; it = translationTable.find(paramSym); if (it != translationTable.end()) { const MLSymbol newSym = translationTable[paramSym]; const int pNewIdx = getParameterIndex(newSym); if (pNewIdx >= 0) { //debug() << "translated parameter to " << newSym << " .\n"; setPropertyImmediate(newSym, paramVal); } else { MLError() << "MLPluginProcessor::setStateFromXML: no such parameter! \n"; } } else { // fail silently on unfound params, because we have deprecated some but they may still // be around in old presets. //debug() << "MLPluginProcessor::setStateFromXML: parameter " << paramSym << " not found!\n"; } } } } // get editor state from XML if(setViewAttributes) { int x = xmlState.getIntAttribute("editor_x"); int y = xmlState.getIntAttribute("editor_y"); int width = xmlState.getIntAttribute("editor_width"); int height = xmlState.getIntAttribute("editor_height"); mEditorRect = MLRect(x, y, width, height); mEditorNumbersOn = xmlState.getIntAttribute("editor_num", 1); mEditorAnimationsOn = xmlState.getIntAttribute("editor_anim", 1); } }