Skip to content

Instantly share code, notes, and snippets.

@jpcima
Last active June 15, 2020 17:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jpcima/3f8272bf70e19db24850e5dca18c888f to your computer and use it in GitHub Desktop.
Save jpcima/3f8272bf70e19db24850e5dca18c888f to your computer and use it in GitHub Desktop.
// SPDX-License-Identifier: BSD-2-Clause
// This code is part of the sfizz library and is licensed under a BSD 2-clause
// license. You should have receive a LICENSE.md file along with the code.
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
#pragma once
#include <elements.hpp>
#include <infra/support.hpp>
namespace el = cycfi::elements;
/**
* @brief Common base of ranged elements
*/
class range_element_base : public el::receiver<double> {
public:
virtual ~range_element_base() {}
typedef std::pair<double, double> range_type;
explicit range_element_base(range_type r)
: range_(r)
{
}
range_type range() const
{
return range_;
}
void range(range_type r)
{
double v = value();
range_ = r;
value(v);
}
std::function<void(double)> on_change;
protected:
double clamp(double x) const
{
return std::max(range_.first, std::min(range_.second, x));
}
double normalize(double x) const
{
return (x - range_.first) / (range_.second - range_.first);
}
double denormalize(double x) const
{
return range_.first + x * (range_.second - range_.first);
}
private:
range_type range_ { 0.0, 1.0 };
};
/**
* @brief Specialized base of ranged elements
*/
template <class Subject, class Base = range_element_base>
class basic_range_element : public Base, public el::proxy<Subject> {
public:
using range_type = typename Base::range_type;
basic_range_element(Subject&& subject_, range_type init_range)
: Base(init_range), el::proxy<Subject>(std::move(subject_))
{
auto& e = static_cast<Subject&>(this->subject());
e.on_change = [this](double v) {
if (this->on_change)
this->on_change(this->denormalize(v));
};
}
double value() const override
{
auto& e = static_cast<const Subject&>(this->subject());
return this->denormalize(e.value());
}
void value(double v) override
{
auto& e = static_cast<Subject&>(this->subject());
e.value(this->normalize(v));
}
};
/**
* @brief Create a ranged proxy for a valued element,
*
* @param element the element to wrap.
* @param init_value the initial value. (optional, default 0)
* @param init_range the initial range. (optional, default 0-1)
*/
template <class Element>
inline basic_range_element<cycfi::remove_cvref_t<Element>>
ranged(Element&& element, double init_value = 0.0, std::pair<double, double> init_range = { 0.0, 1.0 })
{
basic_range_element<cycfi::remove_cvref_t<Element>> e {
std::forward<Element>(element), init_range
};
e.value(init_value);
return e;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment