// add the entire signal b to this signal, at the subpixel destination offset. // void MLSignal::add2D(const MLSignal& b, const Vec2& destOffset) { MLSignal& a = *this; Vec2 iDestOffset, fDestOffset; destOffset.getIntAndFracParts(iDestOffset, fDestOffset); int destX = iDestOffset[0]; int destY = iDestOffset[1]; float srcPosFX = fDestOffset[0]; float srcPosFY = fDestOffset[1]; MLRect srcRect(0, 0, b.getWidth() + 1, b.getHeight() + 1); // add (1, 1) for interpolation MLRect destRect = srcRect.translated(iDestOffset).intersect(getBoundsRect()); for(int j=destRect.top(); j<destRect.bottom(); ++j) { for(int i=destRect.left(); i<destRect.right(); ++i) { a(i, j) += b.getInterpolatedLinear(i - destX - srcPosFX, j - destY - srcPosFY); } } setConstant(false); }
void MLPanel::resizeWidget(const MLRect& b, const int) { Component* pC = getComponent(); if(pC) { // adapt vrect to juce rect Rectangle<int> c(b.left(), b.top(), b.width(), b.height()); pC->setBounds(c); } }
void MLDrawing::resizeWidget(const MLRect& b, const int u) { // adapt vrect to juce rect Component* pC = getComponent(); if(pC) { MLRect bb = b + mPixelOffset; if (bb.height() == 0) bb.setHeight(1); if (bb.width() == 0) bb.setWidth(1); // adapt vrect to juce rect Rectangle<int> c(bb.left(), bb.top(), bb.width(), bb.height()); pC->setBounds(c); } // iterate over points int size = mGridPoints.size(); for(int i = 0; i<size; ++i) { mTransformedPoints[i] = mGridPoints[i]*u; //debug() << "pt. " << i << ":" << mTransformedPoints[i] << " "; } //debug() << "\n"; }
void MLMultiSlider::paint (Graphics& g) { MLLookAndFeel* myLookAndFeel = (&(getRootViewResources(this).mLookAndFeel)); if (isOpaque()) myLookAndFeel->drawBackground(g, this); float outlineThickness = myLookAndFeel->getGridUnitSize() / 64.f; MLRect r = mPos.getLocalOutline(); const Colour outlineColor (findColour(MLLookAndFeel::outlineColor).withAlpha (isEnabled() ? 1.f : 0.5f)); // draw fills // vertical only Path full, empty; Colour fullColor, emptyColor; MLRect fullRect, emptyRect; float dialY; MLRange drawRange(mRange); drawRange.convertTo(MLRange(r.height(), 0.)); for (int i=0; i<mNumSliders; ++i) { MLRect sr = (mPos.getElementBounds(i)); dialY = drawRange(getFloatProperty(ml::textUtils::addFinalNumber(ml::Symbol("value"), i))); fullRect = sr; emptyRect = sr; fullRect.setTop(dialY); fullColor = findColour(trackFullDarkColor); emptyColor = findColour(trackEmptyDarkColor); // groups of 4 if (!(i&4)) { emptyColor = emptyColor.brighter(0.10f); fullColor = fullColor.brighter(0.20f); } empty.clear(); empty.addRectangle(MLToJuceRect(emptyRect)); g.setColour(emptyColor); g.fillPath(empty); full.clear(); full.addRectangle(MLToJuceRect(fullRect)); g.setColour(fullColor); g.fillPath(full); g.setColour(outlineColor); g.strokePath(empty, PathStrokeType (outlineThickness)); } }
// add the entire signal b to this signal, at the integer destination offset. // void MLSignal::add2D(const MLSignal& b, int destX, int destY) { MLSignal& a = *this; MLRect srcRect(0, 0, b.getWidth(), b.getHeight()); MLRect destRect = srcRect.translated(Vec2(destX, destY)).intersect(getBoundsRect()); for(int j=destRect.top(); j<destRect.bottom(); ++j) { for(int i=destRect.left(); i<destRect.right(); ++i) { a(i, j) += b(i - destX, j - destY); } } setConstant(false); }
void MLSignal::dump(std::ostream& s, const MLRect& b) const { const MLSignal& f = *this; { s << std::fixed << std::setprecision(3); for (int j=b.top(); j< b.bottom(); ++j) { s << j << " | "; for(int i=b.left(); i< b.right(); ++i) { s << f(i, j) << " "; } s << "\n"; } } }
void MLProgressBar::paint (Graphics& g) { const Colour fc = findColour(MLLookAndFeel::labelColor); const float progress = getFloatProperty("progress"); Path gbounds; const Rectangle<int> & boundsRect (getLocalBounds()); gbounds.addRectangle(boundsRect); MLRange xRange(0., 1., boundsRect.getX(), boundsRect.getRight()); MLRect progressRect = juceToMLRect(boundsRect); progressRect.setRight(xRange(progress)); Rectangle<int> fullRect = MLToJuceRectInt(progressRect); Path fullBounds; fullBounds.addRectangle(fullRect); g.setColour(fc); g.fillPath(fullBounds); g.strokePath(gbounds, PathStrokeType(1.0f)); }
void SoundplaneZoneView::renderZones() { if (!mpModel) return; const ScopedLock lock(*(mpModel->getZoneLock())); const std::vector<ZonePtr>& zoneList = mpModel->getZones(); int viewW = getBackingLayerWidth(); int viewH = getBackingLayerHeight(); int viewScale = getRenderingScale(); // float viewAspect = (float)viewW / (float)viewH; int gridWidth = 30; // Soundplane A TODO get from tracker int gridHeight = 5; int lineWidth = viewW / 200; int thinLineWidth = viewW / 400; // int margin = lineWidth*2; // put origin in lower left. MLGL::orthoView2(viewW, viewH); MLRange xRange(0, gridWidth, 1, viewW); MLRange yRange(0, gridHeight, 1, viewH); Vec4 lineColor; Vec4 darkBlue(0.3f, 0.3f, 0.5f, 1.f); Vec4 gray(0.6f, 0.6f, 0.6f, 1.f); Vec4 lightGray(0.9f, 0.9f, 0.9f, 1.f); Vec4 blue2(0.1f, 0.1f, 0.5f, 1.f); float smallDotSize = xRange(1.f); // float strokeWidth = viewW / 100; std::vector<ZonePtr>::const_iterator it; for(it = zoneList.begin(); it != zoneList.end(); ++it) { const Zone& zone = **it; int t = zone.getType(); MLRect zr = zone.getBounds(); const char * name = zone.getName().c_str(); int offset = zone.getOffset(); // affine transforms TODO for better syntax: MLRect zrd = zr.xform(gridToView); MLRect zoneRectInView(xRange.convert(zr.x()), yRange.convert(zr.y()), xRange.convert(zr.width()), yRange.convert(zr.height())); zoneRectInView.shrink(lineWidth); // color idx = type + port offset. Vec4 zoneStroke(MLGL::getIndicatorColor(t + offset)); Vec4 zoneFill(zoneStroke); zoneFill[3] = 0.1f; Vec4 activeFill(zoneStroke); activeFill[3] = 0.25f; Vec4 dotFill(zoneStroke); dotFill[3] = 0.5f; // draw box common to all kinds of zones glColor4fv(&zoneFill[0]); MLGL::fillRect(zoneRectInView); glColor4fv(&zoneStroke[0]); glLineWidth(lineWidth); MLGL::strokeRect(zoneRectInView, 2.0f*viewScale); glLineWidth(1); // draw name // all these rect calculations read upside-down here because view origin is at bottom MLGL::drawTextAt(zoneRectInView.left() + lineWidth, zoneRectInView.top() + lineWidth, 0.f, 0.1f, viewScale, name); // draw any zone-specific things float x, y; int toggle; switch(t) { case kNoteRow: for(int i = 0; i < kSoundplaneMaxTouches; ++i) { const ZoneTouch& uTouch = zone.getTouch(i); const ZoneTouch& touch = zone.touchToKeyPos(uTouch); if(touch.isActive()) { glColor4fv(&dotFill[0]); float dx = xRange(touch.pos.x()); float dy = yRange(touch.pos.y()); float dz = touch.pos.z(); MLGL::drawDot(Vec2(dx, dy), dz*smallDotSize); } } break; case kControllerX: x = xRange(zone.getXKeyPos()); glColor4fv(&zoneStroke[0]); glLineWidth(thinLineWidth); MLGL::strokeRect(MLRect(x, zoneRectInView.top(), 0., zoneRectInView.height()), viewScale); glColor4fv(&activeFill[0]); MLGL::fillRect(MLRect(zoneRectInView.left(), zoneRectInView.top(), x - zoneRectInView.left(), zoneRectInView.height())); break; case kControllerY: y = yRange(zone.getYKeyPos()); glColor4fv(&zoneStroke[0]); glLineWidth(thinLineWidth); MLGL::strokeRect(MLRect(zoneRectInView.left(), y, zoneRectInView.width(), 0.), viewScale); glColor4fv(&activeFill[0]); MLGL::fillRect(MLRect(zoneRectInView.left(), zoneRectInView.top(), zoneRectInView.width(), y - zoneRectInView.top())); break; case kControllerXY: x = xRange(zone.getXKeyPos()); y = yRange(zone.getYKeyPos()); glColor4fv(&zoneStroke[0]); glLineWidth(thinLineWidth); // cross-hairs centered on dot MLGL::strokeRect(MLRect(x, zoneRectInView.top(), 0., zoneRectInView.height()), viewScale); MLGL::strokeRect(MLRect(zoneRectInView.left(), y, zoneRectInView.width(), 0.), viewScale); glColor4fv(&dotFill[0]); MLGL::drawDot(Vec2(x, y), smallDotSize*0.25f); break; case kControllerZ: y = yRange(zone.mYRange(zone.getValue(0))); // look at z value over y range glColor4fv(&zoneStroke[0]); glLineWidth(thinLineWidth); MLGL::strokeRect(MLRect(zoneRectInView.left(), y, zoneRectInView.width(), 0.), viewScale); glColor4fv(&activeFill[0]); MLGL::fillRect(MLRect(zoneRectInView.left(), zoneRectInView.top(), zoneRectInView.width(), y - zoneRectInView.top())); break; case kToggle: toggle = zone.getToggleValue(); glColor4fv(&zoneStroke[0]); glLineWidth(thinLineWidth); if(toggle) { MLRect toggleFill = zoneRectInView; Vec2 zoneCenter = zoneRectInView.getCenter(); glColor4fv(&activeFill[0]); MLGL::fillRect(zoneRectInView); glColor4fv(&dotFill[0]); MLGL::drawDot(zoneCenter, smallDotSize*0.25f); } break; } } }
void MLPluginProcessor::getStateAsXML (XmlElement& xml) { if( !(mEngine.getCompileStatus() == MLProc::OK)) return; #if DEMO xml.setAttribute ("pluginVersion", JucePlugin_VersionCode); xml.setAttribute ("presetName", String("----")); #else const unsigned numParams = getNumParameters(); // TODO use string properties of model instead of these JUCE strings. // also move to JSON. xml.setAttribute ("pluginVersion", JucePlugin_VersionCode); xml.setAttribute ("presetName", String(getStringProperty("preset").c_str())); xml.setAttribute ("scaleName", String(getStringProperty("key_scale").c_str())); // store parameter values to xml as a bunch of attributes. // not XML best practice in general but takes fewer characters. for(unsigned i=0; i<numParams; ++i) { const String paramName = symbolToXMLAttr(getParameterAlias(i)); const float defaultVal = getParameterDefault(i); const float paramVal = getParameter(i); if (paramVal != defaultVal) { xml.setAttribute(paramName, paramVal); //debug() << "setting XML param " << paramName << " to " << paramVal << "\n"; } } // store patcher info to xml { MLProcList patchers = getPatcherList(); if (!patchers.empty()) { MLProcPatcher& firstPatcher = static_cast<MLProcPatcher&>(**patchers.begin()); const int inputs = firstPatcher.getParam("inputs"); const int outputs = firstPatcher.getParam("outputs"); String outStr; String patcherInput = "patcher_input_"; for(unsigned i=1; i<=inputs; ++i) { bool differentFromDefault = false; outStr = ""; for(unsigned j=1; j<=outputs; ++j) { if (firstPatcher.getConnection(i, j)) { outStr += "1"; differentFromDefault = true; } else { outStr += "0"; } } if(differentFromDefault) { String outNum (i); xml.setAttribute(patcherInput + outNum, outStr); } } } } // store editor state to XML if one exists MLPluginEditor* pEditor = static_cast<MLPluginEditor*>(getActiveEditor()); if(pEditor) { MLRect r = pEditor->getWindowBounds(); xml.setAttribute("editor_x", r.x()); xml.setAttribute("editor_y", r.y()); xml.setAttribute("editor_width", r.getWidth()); xml.setAttribute("editor_height", r.getHeight()); xml.setAttribute("editor_num", getFloatProperty("patch_num")); xml.setAttribute("editor_anim", getFloatProperty("patch_anim")); } // save blob as most recently saved state mpLatestStateLoaded = XmlElementPtr(new XmlElement(xml)); #endif }
void SoundplaneTouchGraphView::renderTouchBarGraphs() { if (!mpModel) return; if (!isShowing()) return; int viewW = getBackingLayerWidth(); int viewH = getBackingLayerHeight(); const MLSignal& currentTouch = mpModel->getTouchFrame(); const MLSignal& touchHistory = mpModel->getTouchHistory(); const int frames = mpModel->getFloatProperty("max_touches"); if (!frames) return; const Colour c = findColour(MLLookAndFeel::backgroundColor); float p = c.getBrightness(); int margin = viewH / 30; int numSize = margin*2; int left = margin*2 + numSize; int right = viewW - margin; int top = margin; int bottom = viewH - margin; int frameWidth = right - left; int frameOffset = (bottom - top)/frames; int frameHeight = frameOffset - margin; MLRect frameSize(0, 0, frameWidth, frameHeight); MLGL::orthoView(viewW, viewH); for(int j=0; j<frames; ++j) { // draw frames p = 0.9f; glColor4f(p, p, p, 1.0f); MLRect fr = frameSize.translated(Vec2(left, margin + j*frameOffset)); MLGL::fillRect(fr); p = 0.6f; glColor4f(p, p, p, 1.0f); MLGL::strokeRect(fr); // draw touch activity indicators at left glColor4fv(MLGL::getIndicatorColor(j)); MLRect r(0, 0, numSize, numSize); MLRect tr = r.translated(Vec2(margin, margin + j*frameOffset + (frameHeight - numSize)/2)); int age = currentTouch(4, j); if (age > 0) MLGL::fillRect(tr); else MLGL::strokeRect(tr); // draw history MLRange frameXRange(fr.left(), fr.right()); frameXRange.convertTo(MLRange(0, (float)kSoundplaneHistorySize)); MLRange frameYRange(0, 1); frameYRange.convertTo(MLRange(fr.bottom(), fr.top())); glBegin(GL_LINES); for(int i=fr.left() + 1; i<fr.right()-1; ++i) { int time = frameXRange(i); float force = touchHistory(2, j, time); // float d = touchHistory(3, j, time); // int age = touchHistory(4, j, time); float y = frameYRange.convert(force); // float drawY = (age > 0) ? y : 0.; // y = frameYRange.convert(d); // draw line glVertex2f(i, fr.bottom()); glVertex2f(i, y); } glEnd(); } }
void MLMultiSlider::mouseDrag(const MouseEvent& e) { MLRect r = mPos.getLocalOutline(); float w = r.width(); float h = r.height(); int mx = ml::clamp(e.x, (int)r.left() + 1, (int)(r.left() + w)); int my = ml::clamp(e.y, (int)r.top() + 1, (int)(r.top() + h)); int dials = getNumSliders(); int s = getSliderUnderPoint(Vec2(mx, my)); if (isEnabled()) { if (ml::within(s, 0, dials)) { const int mousePos = mVertical ? my : mx; float val; MLRange posRange; if (mVertical) { posRange.set(h, 1); } else { posRange.set(1, w); } posRange.convertTo(mRange); val = posRange(mousePos); // if dial changed in drag, interpolate, setting dials in between if((mCurrDragSlider >= 0) && (mCurrDragSlider != s)) { int span = (s - mCurrDragSlider); int dir = sign(span); float mix, mixedval; int startDrag = mCurrDragSlider + dir; int endDrag = s + dir; for(int i=startDrag; i != endDrag; i += dir) { mix = fabs(float(i - startDrag)/float(span)); mixedval = lerp(mCurrDragValue, val, mix); // finish old drag and switch to dragging new dial if (i != mCurrDragSlider) { mCurrDragSlider = i; } sendSliderAction(snapValue (mixedval, false), i); } } else if (mCurrDragSlider == s) // set current drag dial { sendSliderAction(snapValue (val, false), s); } if (s != mSliderUnderMouse) { mSliderUnderMouse = s; repaint(); } mCurrDragSlider = s; mCurrDragValue = val; } } }