Added some new source files

This commit is contained in:
xenakios 2018-02-27 01:59:22 +02:00
parent 51d40c6daf
commit 600f2d1251
4 changed files with 1015 additions and 0 deletions

View File

@ -0,0 +1,367 @@
#include "envelope_component.h"
EnvelopeComponent::EnvelopeComponent()
{
OnEnvelopeEdited = [](breakpoint_envelope*) {};
setWantsKeyboardFocus(true);
ValueFromNormalized = [](double x) { return x; };
TimeFromNormalized = [](double x) { return x; };
addChildComponent(&m_bubble);
setOpaque(true);
}
EnvelopeComponent::~EnvelopeComponent()
{
}
void EnvelopeComponent::show_bubble(int x, int y, const envelope_node& node)
{
double scaledtime = TimeFromNormalized(node.Time);
double scaledvalue = ValueFromNormalized(node.Value);
x -= 50;
if (x < 0)
x = 0;
if (x + 100 > getWidth())
x = getWidth() - 100;
if (y < 0)
y = 0;
if (y + 20 > getHeight())
y = getHeight() - 20;
AttributedString temp(String::formatted("%.2f %.2f", scaledtime, scaledvalue));
temp.setColour(Colours::white);
m_bubble.showAt({ x,y,100,20 }, temp , 5000);
}
void EnvelopeComponent::paint(Graphics& g)
{
if (!EnvelopeUnderlayDraw)
{
g.fillAll(Colours::black);
g.setColour(Colours::white.darker());
juce::Rectangle<int> rect(0, 0, getWidth(), getHeight());
g.setFont(15.0);
}
else
{
g.saveState();
EnvelopeUnderlayDraw(this, g);
g.restoreState();
}
if (m_envelope == nullptr)
{
g.drawText("No envelope set", 10, 10, getWidth(), getHeight(), Justification::centred);
return;
}
if (m_envelope.unique() == true)
{
g.drawText("Envelope is orphaned (may be a bug)", 10, 10, getWidth(), getHeight(), Justification::centred);
return;
}
String name = m_name;
if (name.isEmpty() == true)
name = "Untitled envelope";
g.drawText(name, 10, 10, getWidth(), getHeight(), Justification::topLeft);
const float linethickness = 1.0f;
for (int i = 0; i < m_envelope->GetNumNodes(); ++i)
{
const envelope_node& pt = m_envelope->GetNodeAtIndex(i);
double xcor = jmap(pt.Time, m_view_start_time, m_view_end_time, 0.0, (double)getWidth());
double ycor = (double)getHeight()-jmap(pt.Value, m_view_start_value, m_view_end_value, 0.0,(double)getHeight());
g.setColour(Colours::white);
if (pt.Status==0)
g.drawRect((float)xcor - 4.0f, (float)ycor - 4.0f, 8.0f, 8.0f, 1.0f);
else g.fillRect((float)xcor - 4.0f, (float)ycor - 4.0f, 8.0f, 8.0f);
m_envelope->resamplePointToLinearSegments(i,0.0, 1.0, 0.0, 1.0, [&g,linethickness,this](double pt_x0, double pt_y0, double pt_x1, double pt_y1)
{
double foo_x0 = jmap<double>(pt_x0, m_view_start_time, m_view_end_time, 0.0, getWidth());
double foo_x1 = jmap<double>(pt_x1, m_view_start_time, m_view_end_time, 0.0, getWidth());
double foo_y0 = (double)getHeight() - jmap<double>(pt_y0, m_view_start_value, m_view_end_value, 0.0, getHeight());
double foo_y1 = (double)getHeight() - jmap<double>(pt_y1, m_view_start_value, m_view_end_value, 0.0, getHeight());
g.setColour(m_env_color);
g.drawLine((float)foo_x0, (float)foo_y0, (float)foo_x1, (float)foo_y1, linethickness);
//g.setColour(Colours::white);
//g.drawLine(foo_x0, foo_y0 - 8.0, foo_x0, foo_y0 + 8.0, 1.0);
}, [this](double xdiff)
{
return std::max((int)(xdiff*getWidth() / 16), 8);
});
/*
envelope_node pt1;
if (i + 1 < m_envelope->GetNumNodes())
{
g.setColour(m_env_color);
pt1 = m_envelope->GetNodeAtIndex(i + 1);
double xcor1 = jmap(pt1.Time, m_view_start_time, m_view_end_time, 0.0, (double)getWidth());
double ycor1 = (double)getHeight() - jmap(pt1.Value, m_view_start_value, m_view_end_value, 0.0, (double)getHeight());
g.drawLine((float)xcor, (float)ycor, (float)xcor1, (float)ycor1, linethickness);
}
if (i == 0 && pt.Time >= 0.0)
{
g.setColour(m_env_color);
g.drawLine(0.0f, (float)ycor, (float)xcor, float(ycor), linethickness);
}
if (i == m_envelope->GetNumNodes()-1 && pt.Time < 1.0)
{
g.setColour(m_env_color);
g.drawLine((float)xcor, (float)ycor, (float)getWidth(), float(ycor), linethickness);
}
*/
}
#ifdef ENVELOPEDRAWDERIVATIVE
g.setColour(Colours::green);
//double prevderiv = derivative([this](double xx) { return m_envelope->GetInterpolatedNodeValue(xx); }, 0.0);
for (int i = 0; i < getWidth()/8; ++i)
{
double x = 1.0 / getWidth()*(i*8);
double derv = derivative([this](double xx) { return m_envelope->GetInterpolatedNodeValue(xx); }, x);
double y = getHeight()-jmap<double>(derv, -10.0, 10.0, 0.0, getHeight());
g.fillEllipse(i*8, y, 5.0f, 5.0f);
}
#endif
}
void EnvelopeComponent::changeListenerCallback(ChangeBroadcaster*)
{
repaint();
}
void EnvelopeComponent::timerCallback(int)
{
}
void EnvelopeComponent::set_envelope(std::shared_ptr<breakpoint_envelope> env, String name)
{
m_envelope = env;
m_name = name;
repaint();
}
void EnvelopeComponent::mouseDrag(const MouseEvent& ev)
{
if (m_envelope == nullptr)
return;
if (m_segment_drag_info.first >= 0 && ev.mods.isAltDown())
{
double dist = jmap<double>(ev.getDistanceFromDragStartX(), -300.0, 300.0, -1.0, 1.0);
m_envelope->performRelativeTransformation([dist, this](int index, envelope_node& point)
{
if (index == m_segment_drag_info.first)
{
point.ShapeParam1 += dist;
m_segment_drag_info.second = true;
}
});
repaint();
return;
}
if (m_segment_drag_info.first >= 0)
{
double dist = jmap<double>(ev.getDistanceFromDragStartY(), -getHeight(), getHeight(), -1.0, 1.0);
m_envelope->adjustEnvelopeSegmentValues(m_segment_drag_info.first, -dist);
repaint();
return;
}
if (m_node_to_drag >= 0)
{
//Logger::writeToLog("trying to move pt " + String(m_node_to_drag));
envelope_node& pt = m_envelope->GetNodeAtIndex(m_node_to_drag);
double left_bound = m_view_start_time;
double right_bound = m_view_end_time;
if (m_node_to_drag > 0 )
{
left_bound = m_envelope->GetNodeAtIndex(m_node_to_drag - 1).Time;
}
if (m_node_to_drag < m_envelope->GetNumNodes() - 1)
{
right_bound = m_envelope->GetNodeAtIndex(m_node_to_drag + 1).Time;
}
double normx = jmap((double)ev.x, 0.0, (double)getWidth(), m_view_start_time, m_view_end_time);
double normy = jmap((double)getHeight() - ev.y, 0.0, (double)getHeight(), m_view_start_value, m_view_end_value);
pt.Time=jlimit(left_bound+0.001, right_bound - 0.001, normx);
pt.Value=jlimit(0.0,1.0,normy);
m_last_tip = String(pt.Time, 2) + " " + String(pt.Value, 2);
show_bubble(ev.x, ev.y, pt);
m_node_that_was_dragged = m_node_to_drag;
repaint();
return;
}
}
void EnvelopeComponent::mouseMove(const MouseEvent & ev)
{
if (m_envelope == nullptr)
return;
m_node_to_drag = find_hot_envelope_point(ev.x, ev.y);
if (m_node_to_drag >= 0)
{
if (m_mouse_down == false)
{
show_bubble(ev.x, ev.y, m_envelope->GetNodeAtIndex(m_node_to_drag));
setMouseCursor(MouseCursor::PointingHandCursor);
}
}
else
{
setMouseCursor(MouseCursor::NormalCursor);
m_bubble.setVisible(false);
}
}
void EnvelopeComponent::mouseDown(const MouseEvent & ev)
{
if (m_envelope == nullptr)
return;
if (ev.mods.isRightButtonDown() == true)
{
PopupMenu menu;
menu.addItem(1, "Reset");
menu.addItem(2, "Invert");
int r = menu.show();
if (r == 1)
{
m_envelope->ResetEnvelope();
}
if (r == 2)
{
for (int i = 0; i < m_envelope->GetNumNodes(); ++i)
{
double val = 1.0 - m_envelope->GetNodeAtIndex(i).Value;
m_envelope->GetNodeAtIndex(i).Value = val;
}
}
repaint();
return;
}
m_node_to_drag = find_hot_envelope_point(ev.x, ev.y);
m_mouse_down = true;
m_segment_drag_info = { findHotEnvelopeSegment(ev.x, ev.y, true),false };
if (m_segment_drag_info.first >= 0)
{
m_envelope->beginRelativeTransformation();
return;
}
if (m_node_to_drag >= 0 && ev.mods.isAltDown() == true)
{
if (m_envelope->GetNumNodes() < 2)
{
m_bubble.showAt({ ev.x,ev.y, 0,0 }, AttributedString("Can't remove last node"), 3000, false, false);
return;
}
m_envelope->DeleteNode(m_node_to_drag);
m_node_to_drag = -1;
OnEnvelopeEdited(m_envelope.get());
repaint();
return;
}
if (m_node_to_drag >= 0 && ev.mods.isShiftDown()==true)
{
int oldstatus = m_envelope->GetNodeAtIndex(m_node_to_drag).Status;
if (oldstatus==0)
m_envelope->GetNodeAtIndex(m_node_to_drag).Status=1;
else m_envelope->GetNodeAtIndex(m_node_to_drag).Status=0;
repaint();
return;
}
if (m_node_to_drag == -1)
{
double normx = jmap((double)ev.x, 0.0, (double)getWidth(), m_view_start_time, m_view_end_time);
double normy = jmap((double)getHeight() - ev.y, 0.0, (double)getHeight(), m_view_start_value, m_view_end_value);
m_envelope->AddNode ({ normx,normy, 0.5});
m_envelope->SortNodes();
m_mouse_down = false;
OnEnvelopeEdited(m_envelope.get());
repaint();
}
}
void EnvelopeComponent::mouseUp(const MouseEvent &ev)
{
if (ev.mods == ModifierKeys::noModifiers)
m_bubble.setVisible(false);
if (m_node_that_was_dragged >= 0 || m_segment_drag_info.second==true)
{
OnEnvelopeEdited(m_envelope.get());
}
m_mouse_down = false;
m_node_that_was_dragged = -1;
m_node_to_drag = -1;
if (m_segment_drag_info.second == true)
{
m_segment_drag_info = { -1,false };
m_envelope->endRelativeTransformation();
}
}
bool EnvelopeComponent::keyPressed(const KeyPress & ev)
{
if (ev == KeyPress::deleteKey && m_envelope!=nullptr)
{
m_node_to_drag = -1;
//m_envelope->ClearAllNodes();
m_envelope->removePointsConditionally([](const envelope_node& pt) { return pt.Status == 1; });
if (m_envelope->GetNumNodes()==0)
m_envelope->AddNode({ 0.0,0.5 });
repaint();
OnEnvelopeEdited(m_envelope.get());
return true;
}
return false;
}
int EnvelopeComponent::find_hot_envelope_point(double xcor, double ycor)
{
if (m_envelope == nullptr)
return -1;
for (int i = 0; i < m_envelope->GetNumNodes(); ++i)
{
const envelope_node& pt = m_envelope->GetNodeAtIndex(i);
double ptxcor = jmap(pt.Time, m_view_start_time, m_view_end_time, 0.0, (double)getWidth());
double ptycor = (double)getHeight() - jmap(pt.Value, m_view_start_value, m_view_end_value, 0.0, (double)getHeight());
juce::Rectangle<double> target(ptxcor - 4.0, ptycor - 4.0, 8.0, 8.0);
if (target.contains(xcor, ycor) == true)
{
return i;
}
}
return -1;
}
int EnvelopeComponent::findHotEnvelopeSegment(double xcor, double ycor, bool detectsegment)
{
if (m_envelope == nullptr)
return -1;
for (int i = 0; i < m_envelope->GetNumNodes()-1; ++i)
{
const envelope_node& pt0 = m_envelope->GetNodeAtIndex(i);
const envelope_node& pt1 = m_envelope->GetNodeAtIndex(i+1);
float xcor0 = (float)jmap<double>(pt0.Time, m_view_start_time, m_view_end_time, 0.0, getWidth());
float xcor1 = (float)jmap<double>(pt1.Time, m_view_start_time, m_view_end_time, 0.0, getWidth());
float segwidth = xcor1 - xcor0;
juce::Rectangle<float> segrect(xcor0+8.0f, 0.0f, segwidth-16.0f, (float)getHeight());
if (segrect.contains((float)xcor, (float)ycor))
{
if (detectsegment == false)
return i;
else
{
double normx = jmap<double>(xcor, 0.0, getWidth(), m_view_start_time, m_view_end_time);
double yval = m_envelope->GetInterpolatedNodeValue(normx);
float ycor0 = (float)(getHeight()-jmap<double>(yval, 0.0, 1.0, 0.0, getHeight()));
juce::Rectangle<float> segrect2((float)(xcor - 20), (float)(ycor - 20), 40, 40);
if (segrect2.contains((float)xcor, ycor0))
return i;
}
}
}
return -1;
}

View File

@ -0,0 +1,56 @@
#pragma once
#include "../JuceLibraryCode/JuceHeader.h"
#include <memory>
#include <functional>
#include <future>
#include "jcdp_envelope.h"
class EnvelopeComponent : public Component,
public ChangeListener,
public MultiTimer
//public TooltipClient
{
public:
EnvelopeComponent();
~EnvelopeComponent();
void paint(Graphics& g) override;
void set_envelope(std::shared_ptr<breakpoint_envelope> env, String name = String());
std::shared_ptr<breakpoint_envelope> get_envelope() { return m_envelope; }
String get_name() { return m_name; }
void mouseMove(const MouseEvent& ev) override;
void mouseDown(const MouseEvent& ev) override;
void mouseDrag(const MouseEvent& ev) override;
void mouseUp(const MouseEvent& ev) override;
bool keyPressed(const KeyPress& ev) override;
double get_view_start_time() const { return m_view_start_time; }
double get_view_end_time() const { return m_view_end_time; }
void set_view_start_time(double t) { m_view_start_time = t; repaint(); }
void set_view_end_time(double t) { m_view_end_time = t; repaint(); }
std::function<void(EnvelopeComponent*, Graphics&)> EnvelopeUnderlayDraw;
std::function<void(EnvelopeComponent*, Graphics&)> EnvelopeOverlayDraw;
std::function<void(breakpoint_envelope*)> OnEnvelopeEdited;
std::function<double(double)> ValueFromNormalized;
std::function<double(double)> TimeFromNormalized;
void changeListenerCallback(ChangeBroadcaster*) override;
void timerCallback(int id) override;
//String getTooltip() override;
private:
std::shared_ptr<breakpoint_envelope> m_envelope;
String m_name;
Colour m_env_color{ Colours::yellow };
double m_view_start_time = 0.0;
double m_view_end_time = 1.0;
double m_view_start_value = 0.0;
double m_view_end_value = 1.0;
int find_hot_envelope_point(double xcor, double ycor);
int findHotEnvelopeSegment(double xcor, double ycor, bool detectsegment);
bool m_mouse_down = false;
int m_node_to_drag = -1;
std::pair<int, bool> m_segment_drag_info{ -1,false };
int m_node_that_was_dragged = -1;
String m_last_tip;
BubbleMessageComponent m_bubble;
void show_bubble(int x, int y, const envelope_node &node);
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(EnvelopeComponent)
};

588
Source/jcdp_envelope.h Normal file
View File

@ -0,0 +1,588 @@
/*
This file is part of CDP Front-end.
CDP front-end is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
CDP front-end 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with CDP front-end. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef JCDP_ENVELOPE_H
#define JCDP_ENVELOPE_H
#include <vector>
#include <algorithm>
#include "../JuceLibraryCode/JuceHeader.h"
#include "PS_Source/globals.h"
struct envelope_node
{
envelope_node()
: Time(0.0), Value(0.0), ShapeParam1(0.5), ShapeParam2(0.5) {}
envelope_node(double x, double y, double p1=0.5, double p2=0.5)
: Time(x), Value(y),ShapeParam1(p1),ShapeParam2(p2) {}
double Time;
double Value;
int Shape = 0;
double ShapeParam1;
double ShapeParam2;
int Status = 0;
size_t get_hash() const
{
size_t seed = 0;
seed ^= std::hash<double>()(Time) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
seed ^= std::hash<double>()(Value) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
seed ^= std::hash<int>()(Shape) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
seed ^= std::hash<double>()(ShapeParam1) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
seed ^= std::hash<double>()(ShapeParam2) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
return seed;
}
};
inline bool operator<(const envelope_node& a, const envelope_node& b)
{
return a.Time<b.Time;
}
template<typename T>
inline void appendToMemoryBlock(MemoryBlock& mb, T x)
{
T temp(x);
mb.append((void*)&temp, sizeof(temp));
}
struct grid_entry
{
grid_entry(double v) : m_value(v) {}
double m_value=0.0;
bool m_foo=false;
};
inline double grid_value(const grid_entry& ge)
{
return ge.m_value;
}
inline bool operator<(const grid_entry& a, const grid_entry& b)
{
return a.m_value<b.m_value;
}
using grid_t=std::vector<grid_entry>;
//#define BEZIER_EXPERIMENT
inline double get_shaped_value(double x, int, double p1, double)
{
#ifndef BEZIER_EXPERIMENT
if (p1<0.5)
{
double foo=1.0-(p1*2.0);
return 1.0-pow(1.0-x,1.0+foo*4.0);
}
double foo=(p1-0.5)*2.0;
return pow(x,1.0+foo*4.0);
#else
/*
double pt0=-2.0*p1;
double pt1=2.0*p2;
double pt2=1.0;
return pow(1-x,2.0)*pt0+2*(1-x)*x*pt1+pow(x,2)*pt2;
*/
if (p2<=0.5)
{
if (p1<0.5)
{
double foo=1.0-(p1*2.0);
return 1.0-pow(1.0-x,1.0+foo*4.0);
}
double foo=(p1-0.5)*2.0;
return pow(x,1.0+foo*4.0);
} else
{
if (p1<0.5)
{
if (x<0.5)
{
x*=2.0;
p1*=2.0;
return 0.5*pow(x,p1*4.0);
} else
{
x-=0.5;
x*=2.0;
p1*=2.0;
return 1.0-0.5*pow(1.0-x,p1*4.0);
}
} else
{
if (x<0.5)
{
x*=2.0;
p1-=0.5;
p1*=2.0;
return 0.5-0.5*pow(1.0-x,p1*4.0);
} else
{
x-=0.5;
x*=2.0;
p1-=0.5;
p1*=2.0;
return 0.5+0.5*pow(x,p1*4.0);
}
}
}
return x;
#endif
}
using nodes_t=std::vector<envelope_node>;
inline double GetInterpolatedNodeValue(const nodes_t& m_nodes, double atime, double m_defvalue=0.5)
{
int maxnodeind=(int)m_nodes.size()-1;
if (m_nodes.size()==0) return m_defvalue;
if (m_nodes.size()==1) return m_nodes[0].Value;
if (atime<=m_nodes[0].Time)
return m_nodes[0].Value;
if (atime>m_nodes[maxnodeind].Time)
return m_nodes[maxnodeind].Value;
const envelope_node to_search(atime,0.0);
//to_search.Time=atime;
auto it=std::lower_bound(m_nodes.begin(),m_nodes.end(),to_search,
[](const envelope_node& a, const envelope_node& b)
{ return a.Time<b.Time; } );
if (it==m_nodes.end())
{
return m_defvalue;
}
--it; // lower_bound has returned iterator to point one too far
double t1=it->Time;
double v1=it->Value;
double p1=it->ShapeParam1;
double p2=it->ShapeParam2;
++it; // next envelope point
double tdelta=it->Time-t1;
if (tdelta<0.00001)
tdelta=0.00001;
double vdelta=it->Value-v1;
return v1+vdelta*get_shaped_value(((1.0/tdelta*(atime-t1))),0,p1,p2);
}
inline double interpolate_foo(double atime,double t0, double v0, double t1, double v1, double p1, double p2)
{
double tdelta=t1-t0;
if (tdelta<0.00001)
tdelta=0.00001;
double vdelta=v1-v0;
return v0+vdelta*get_shaped_value(((1.0/tdelta*(atime-t0))),0,p1,p2);
}
class breakpoint_envelope
{
public:
breakpoint_envelope() : m_name("Untitled") {}
breakpoint_envelope(String name, double minv=0.0, double maxv=1.0)
: m_minvalue(minv), m_maxvalue(maxv), m_name(name)
{
m_defshape=0;
//m_color=RGB(0,255,255);
m_defvalue=0.5;
m_updateopinprogress=false;
m_value_grid={0.0,0.25,0.5,0.75,1.0};
}
void SetName(String Name) { m_name=Name; }
const String& GetName() const { return m_name; }
double GetDefValue() { return m_defvalue; }
void SetDefValue(double value) { m_defvalue=value; }
int GetDefShape() { return m_defshape; }
ValueTree saveState()
{
ValueTree result("envelope");
for (int i = 0; i < m_nodes.size(); ++i)
{
ValueTree pt_tree("pt");
storeToTreeProperties(pt_tree, nullptr,
"x", m_nodes[i].Time, "y", m_nodes[i].Value, "p1", m_nodes[i].ShapeParam1, "p2", m_nodes[i].ShapeParam2);
result.addChild(pt_tree, -1, nullptr);
}
return result;
}
void restoreState(ValueTree state)
{
int numnodes = state.getNumChildren();
if (numnodes > 0)
{
m_nodes.clear();
for (int i = 0; i < numnodes; ++i)
{
ValueTree pt_tree = state.getChild(i);
double x, y = 0.0;
double p1, p2 = 0.5;
getFromTreeProperties(pt_tree, "x", x, "y", y, "p1", p1, "p2", p2);
m_nodes.emplace_back(x, y, p1,p2);
}
SortNodes();
}
}
MD5 getHash() const
{
MemoryBlock mb;
for (int i = 0; i < m_nodes.size(); ++i)
{
appendToMemoryBlock(mb, m_nodes[i].Time);
appendToMemoryBlock(mb, m_nodes[i].Value);
appendToMemoryBlock(mb, m_nodes[i].ShapeParam1);
appendToMemoryBlock(mb, m_nodes[i].ShapeParam2);
}
return MD5(mb);
}
int GetNumNodes() const { return (int)m_nodes.size(); }
void SetDefShape(int value) { m_defshape=value; }
double getNodeLeftBound(int index, double margin=0.01) const noexcept
{
if (m_nodes.size() == 0)
return 0.0;
if (index == 0)
return 0.0;
return m_nodes[index - 1].Time + margin;
}
double getNodeRightBound(int index, double margin = 0.01) const noexcept
{
if (m_nodes.size() == 0)
return 1.0;
if (index == m_nodes.size()-1)
return 1.0;
return m_nodes[index + 1].Time - margin;
}
const std::vector<envelope_node>& get_all_nodes() const { return m_nodes; }
void set_all_nodes(nodes_t nds) { m_nodes=std::move(nds); }
void set_reset_nodes(const std::vector<envelope_node>& nodes, bool convertvalues=false)
{
if (convertvalues==false)
m_reset_nodes=nodes;
else
{
if (scaled_to_normalized_func)
{
m_nodes.clear();
for (int i=0;i<nodes.size();++i)
{
envelope_node node=nodes[i];
node.Value=scaled_to_normalized_func(node.Value);
m_nodes.push_back(node);
}
}
}
}
void ResetEnvelope()
{
m_nodes=m_reset_nodes;
m_playoffset=0.0;
}
Colour GetColor()
{
return m_colour;
}
void SetColor(Colour colour)
{
m_colour=colour;
}
void BeginUpdate() // used for doing larger update operations, so can avoid needlessly sorting etc
{
m_updateopinprogress=true;
}
void EndUpdate()
{
m_updateopinprogress=false;
SortNodes();
}
void AddNode(envelope_node newnode)
{
m_nodes.push_back(newnode);
if (!m_updateopinprogress)
SortNodes();
}
void ClearAllNodes()
{
m_nodes.clear();
}
void DeleteNode(int indx)
{
if (indx<0 || indx>m_nodes.size()-1)
return;
m_nodes.erase(m_nodes.begin()+indx);
}
void delete_nodes_in_time_range(double t0, double t1)
{
m_nodes.erase(std::remove_if(std::begin(m_nodes),
std::end(m_nodes),
[t0,t1](const envelope_node& a) { return a.Time>=t0 && a.Time<=t1; } ),
std::end(m_nodes) );
}
template<typename F>
void removePointsConditionally(F predicate)
{
m_nodes.erase(std::remove_if(m_nodes.begin(), m_nodes.end(), predicate), m_nodes.end());
}
envelope_node& GetNodeAtIndex(int indx)
{
if (m_nodes.size()==0)
{
throw(std::length_error("Empty envelope accessed"));
}
if (indx<0)
indx=0;
if (indx>=(int)m_nodes.size())
indx=(int)m_nodes.size()-1;
return m_nodes[indx];
}
const envelope_node& GetNodeAtIndex(int indx) const
{
if (m_nodes.size()==0)
{
throw(std::length_error("Empty envelope accessed"));
}
if (indx<0)
indx=0;
if (indx>=(int)m_nodes.size())
indx=(int)m_nodes.size()-1;
return m_nodes[indx];
}
void SetNodeStatus(int indx, int nstatus)
{
int i=indx;
if (indx<0) i=0;
if (indx>(int)m_nodes.size()-1) i=(int)m_nodes.size()-1;
m_nodes[i].Status=nstatus;
}
void SetNode(int indx, envelope_node anode)
{
int i=indx;
if (indx<0) i=0;
if (indx>(int)m_nodes.size()-1) i=(int)m_nodes.size()-1;
m_nodes[i]=anode;
}
void SetNodeTimeValue(int indx,bool setTime,bool setValue,double atime,double avalue)
{
int i=indx;
if (indx<0) i=0;
if (indx>(int)m_nodes.size()-1) i=(int)m_nodes.size()-1;
if (setTime) m_nodes[i].Time=atime;
if (setValue) m_nodes[i].Value=avalue;
}
double GetInterpolatedNodeValue(double atime)
{
double t0=0.0;
double t1=0.0;
double v0=0.0;
double v1=0.0;
double p1=0.0;
double p2=0.0;
const int maxnodeind=(int)m_nodes.size()-1;
if (m_nodes.size()==0)
return m_defvalue;
if (m_nodes.size()==1)
return m_nodes[0].Value;
if (atime<=m_nodes[0].Time)
{
#ifdef INTERPOLATING_ENVELOPE_BORDERS
t1=m_nodes[0].Time;
t0=0.0-(1.0-m_nodes[maxnodeind].Time);
v0=m_nodes[maxnodeind].Value;
p1=m_nodes[maxnodeind].ShapeParam1;
p2=m_nodes[maxnodeind].ShapeParam2;
v1=m_nodes[0].Value;
return interpolate_foo(atime,t0,v0,t1,v1,p1,p2);
#else
return m_nodes[0].Value;
#endif
}
if (atime>m_nodes[maxnodeind].Time)
{
#ifdef INTERPOLATING_ENVELOPE_BORDERS
t0=m_nodes[maxnodeind].Time;
t1=1.0+(m_nodes[0].Time);
v0=m_nodes[maxnodeind].Value;
v1=m_nodes[0].Value;
p1=m_nodes[maxnodeind].ShapeParam1;
p2=m_nodes[maxnodeind].ShapeParam2;
return interpolate_foo(atime,t0,v0,t1,v1,p1,p2);
#else
return m_nodes.back().Value;
#endif
}
const envelope_node to_search(atime,0.0);
//to_search.Time=atime;
auto it=std::lower_bound(m_nodes.begin(),m_nodes.end(),to_search,
[](const envelope_node& a, const envelope_node& b)
{ return a.Time<b.Time; } );
if (it==m_nodes.end())
{
return m_defvalue;
}
--it; // lower_bound has returned iterator to point one too far
t0=it->Time;
v0=it->Value;
p1=it->ShapeParam1;
p2=it->ShapeParam2;
++it; // next envelope point
t1=it->Time;
v1=it->Value;
return interpolate_foo(atime,t0,v0,t1,v1,p1,p2);
}
bool IsSorted() const
{
return std::is_sorted(m_nodes.begin(), m_nodes.end(), []
(const envelope_node& lhs, const envelope_node& rhs)
{
return lhs.Time<rhs.Time;
});
}
void SortNodes()
{
stable_sort(m_nodes.begin(),m_nodes.end(),
[](const envelope_node& a, const envelope_node& b){ return a.Time<b.Time; } );
}
double minimum_value() const { return m_minvalue; }
double maximum_value() const { return m_maxvalue; }
void set_minimum_value(double v) { m_minvalue=v; }
void set_maximum_value(double v) { m_maxvalue=v; }
std::function<double(double)> normalized_to_scaled_func;
std::function<double(double)> scaled_to_normalized_func;
void beginRelativeTransformation()
{
m_old_nodes=m_nodes;
}
void endRelativeTransformation()
{
m_old_nodes.clear();
}
nodes_t& getRelativeTransformBaseNodes()
{
return m_old_nodes;
}
template<typename F>
inline void performRelativeTransformation(F&& f)
{
for (int i = 0; i < m_old_nodes.size(); ++i)
{
envelope_node node = m_old_nodes[i];
f(i, node);
node.ShapeParam1 = jlimit(0.0, 1.0, node.ShapeParam1);
m_nodes[i] = node;
}
}
void adjustEnvelopeSegmentValues(int index, double amount)
{
if (index >= m_old_nodes.size())
{
m_nodes.back().Value = jlimit(0.0,1.0,m_old_nodes.back().Value+amount);
return;
}
m_nodes[index].Value = jlimit(0.0, 1.0, m_old_nodes[index].Value + amount);
m_nodes[index+1].Value = jlimit(0.0, 1.0, m_old_nodes[index+1].Value + amount);
}
const nodes_t& repeater_nodes() const
{
return m_repeater_nodes;
}
void store_repeater_nodes()
{
m_repeater_nodes.clear();
for (int i=0;i<m_nodes.size();++i)
{
if (m_nodes[i].Time>=m_playoffset && m_nodes[i].Time<=m_playoffset+1.0)
{
envelope_node temp=m_nodes[i];
temp.Time-=m_playoffset;
m_repeater_nodes.push_back(temp);
}
}
}
double get_play_offset() const { return m_playoffset; }
//void set_play_offset(double x) { m_playoffset=bound_value(m_mintime,x,m_maxtime); }
//time_range get_play_offset_range() const { return std::make_pair(m_mintime,m_maxtime); }
const grid_t& get_value_grid() const { return m_value_grid; }
void set_value_grid(grid_t g) { m_value_grid=std::move(g); }
template<typename F>
void manipulate(F&& f)
{
nodes_t backup=m_nodes;
if (f(backup)==true)
{
std::swap(backup,m_nodes);
SortNodes();
}
}
template<typename F0, typename F1>
inline void resamplePointToLinearSegments(int point_index,double /*xmin*/, double /*xmax*/, double /*ymin*/, double /*ymax*/,
F0&& handlesegmentfunc, F1&& numsegmentsfunc)
{
if (m_nodes.size() == 0)
return;
envelope_node pt0 = GetNodeAtIndex(point_index);
envelope_node pt1 = GetNodeAtIndex(point_index+1);
double xdiff = pt1.Time - pt0.Time;
if (xdiff > 0.0)
{
int numsegments = numsegmentsfunc(xdiff);
for (int j=0;j<numsegments;++j)
{
double cb_x0 = pt0.Time + xdiff / (numsegments)*j;
double cb_y0 = GetInterpolatedNodeValue(cb_x0);
double cb_x1 = pt0.Time + xdiff / (numsegments)*(j+1);
double cb_y1 = GetInterpolatedNodeValue(cb_x1);
handlesegmentfunc(cb_x0, cb_y0,cb_x1,cb_y1);
}
}
}
String m_script;
private:
nodes_t m_nodes;
double m_playoffset=0.0;
double m_minvalue=0.0;
double m_maxvalue=1.0;
double m_mintime=-2.0;
double m_maxtime=2.0;
int m_defshape;
Colour m_colour;
String m_name;
bool m_updateopinprogress;
double m_defvalue; // "neutral" value to be used for resets and stuff
nodes_t m_reset_nodes;
nodes_t m_old_nodes;
nodes_t m_repeater_nodes;
grid_t m_value_grid;
};
template<typename F, typename... Args>
inline double derivative(const F& f, double x, const Args&... func_args)
{
const double epsilon = std::numeric_limits<double>::epsilon() * 100;
//const double epsilon=0.000001;
return (f(x + epsilon, func_args...) - f(x, func_args...)) / epsilon;
}
#endif // JCDP_ENVELOPE_H

View File

@ -15,6 +15,10 @@
headerPath="&#10;&#10;">
<MAINGROUP id="nozXHl" name="PaulXStretch">
<GROUP id="{03DA6B32-F666-FF60-F168-4385D0847058}" name="Source">
<FILE id="Mz5aVb" name="envelope_component.cpp" compile="1" resource="0"
file="Source/envelope_component.cpp"/>
<FILE id="apM6W6" name="envelope_component.h" compile="0" resource="0"
file="Source/envelope_component.h"/>
<FILE id="TDOHpE" name="resample.cpp" compile="1" resource="0" file="Source/WDL/resample.cpp"/>
<GROUP id="{3B6D1AF9-E53E-2F78-24A5-D12A34009E6A}" name="PS_Source">
<FILE id="bnWZA4" name="BinauralBeats.cpp" compile="1" resource="0"