osci-render/Source/UGen/Env.cpp

337 wiersze
8.3 KiB
C++

// $Id$
// $HeadURL$
/*
==============================================================================
This file is part of the UGEN++ library
Copyright 2008-11 The University of the West of England.
by Martin Robinson
------------------------------------------------------------------------------
UGEN++ can be redistributed and/or modified 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.
UGEN++ 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 UGEN++; if not, visit www.gnu.org/licenses or write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
Boston, MA 02111-1307 USA
The idea for this project and code in the UGen implementations is
derived from SuperCollider which is also released under the
GNU General Public License:
SuperCollider real time audio synthesis system
Copyright (c) 2002 James McCartney. All rights reserved.
http://www.audiosynth.com
==============================================================================
*/
#ifndef UGEN_NOEXTGPL
#include "Env.h"
Env::Env(
const std::vector<double>& levels,
const std::vector<double>& times,
EnvCurveList const& curves,
const int releaseNode,
const int loopNode) throw()
: levels_(levels),
times_(times),
curves_(curves),
releaseNode_(releaseNode),
loopNode_(loopNode)
{
}
Env& Env::operator=(Env const& other) throw() {
if (this != &other) {
levels_ = other.levels_;
times_ = other.times_;
curves_ = other.curves_;
releaseNode_ = other.releaseNode_;
loopNode_ = other.loopNode_;
}
return *this;
}
bool Env::operator==(const Env& other) const throw() {
// approximate comparison
bool levelsEqual = true;
for (int i = 0; i < levels_.size(); i++) {
if (std::abs(levels_[i] - other.levels_[i]) > 0.001) {
levelsEqual = false;
break;
}
}
// approximate comparison
bool timesEqual = true;
for (int i = 0; i < times_.size(); i++) {
if (std::abs(times_[i] - other.times_[i]) > 0.001) {
timesEqual = false;
break;
}
}
// approximate comparison
bool curvesEqual = true;
for (int i = 0; i < curves_.size(); i++) {
if (std::abs(curves_[i].getCurve() - other.curves_[i].getCurve()) > 0.001) {
curvesEqual = false;
break;
}
}
return levelsEqual && timesEqual && curvesEqual;
}
Env::~Env() throw() {}
double Env::duration() const throw() {
return std::reduce(times_.begin(), times_.end());
}
Env Env::levelScale(const double scale) const throw()
{
std::vector<double> newLevels;
for (auto level : levels_) {
newLevels.push_back(level * scale);
}
return Env(newLevels,
times_,
curves_,
releaseNode_,
loopNode_);
}
Env Env::levelBias(const double bias) const throw()
{
std::vector<double> newLevels;
for (auto level : levels_) {
newLevels.push_back(level + bias);
}
return Env(newLevels,
times_,
curves_,
releaseNode_,
loopNode_);
}
Env Env::timeScale(const double scale) const throw()
{
std::vector<double> newTimes;
for (auto time : times_) {
newTimes.push_back(time * scale);
}
return Env(levels_,
newTimes,
curves_,
releaseNode_,
loopNode_);
}
inline double linlin(const double input,
const double inLow, const double inHigh,
const double outLow, const double outHigh) throw()
{
double inRange = inHigh - inLow;
double outRange = outHigh - outLow;
return (input - inLow) * outRange / inRange + outLow;
}
inline double linexp(const double input,
const double inLow, const double inHigh,
const double outLow, const double outHigh) throw()
{
double outRatio = outHigh / outLow;
double reciprocalInRange = 1.0 / (inHigh - inLow);
double negInLowOverInRange = reciprocalInRange * -inLow;
return outLow * pow(outRatio, input * reciprocalInRange + negInLowOverInRange);
}
inline double linsin(const double input,
const double inLow, const double inHigh,
const double outLow, const double outHigh) throw()
{
double inRange = inHigh - inLow;
double outRange = outHigh - outLow;
double inPhase = (input - inLow) * std::numbers::pi / inRange + std::numbers::pi;
#ifndef UGEN_ANDROID
double cosInPhase = std::cos(inPhase) * 0.5 + 0.5;
#else
double cosInPhase = cos(inPhase) * 0.5 + 0.5;
#endif
return cosInPhase * outRange + outLow;
}
inline float linwelch(const float input,
const float inLow, const float inHigh,
const float outLow, const float outHigh) throw()
{
float inRange = inHigh - inLow;
float outRange = outHigh - outLow;
float inPos = (input - inLow) / inRange;
#ifndef UGEN_ANDROID
if (outLow < outHigh)
return outLow + outRange * std::sin(std::numbers::pi * inPos / 2);
else
return outHigh - outRange * std::sin(std::numbers::pi / 2 - std::numbers::pi * inPos / 2);
#else
if (outLow < outHigh)
return outLow + outRange * sinf(piOverTwo * inPos);
else
return outHigh - outRange * sinf(piOverTwo - piOverTwo * inPos);
#endif
}
float Env::lookup(float time) const throw()
{
const int numTimes = times_.size();
const int numLevels = levels_.size();
const int lastLevel = numLevels-1;
if(numLevels < 1) return 0.f;
if(time <= 0.f || numTimes == 0) return levels_[0];
float lastTime = 0.f;
float stageTime = 0.f;
int stageIndex = 0;
while(stageTime < time && stageIndex < numTimes)
{
lastTime = stageTime;
stageTime += times_[stageIndex];
stageIndex++;
}
if(stageIndex > numTimes) return levels_[lastLevel];
float level0 = levels_[stageIndex-1];
float level1 = levels_[stageIndex];
int curveIndex = (stageIndex - 1) % curves_.size();
EnvCurve::CurveType type = curves_.data[curveIndex].getType();
float curveValue = curves_.data[curveIndex].getCurve();
if((lastTime - stageTime)==0.f)
{
return level1;
}
else if(type == EnvCurve::Linear)
{
return linlin(time, lastTime, stageTime, level0, level1);
}
else if(type == EnvCurve::Numerical)
{
if(abs(curveValue) <= 0.001)
{
return linlin(time, lastTime, stageTime, level0, level1);
}
else
{
float pos = (time-lastTime) / (stageTime-lastTime);
#ifndef UGEN_ANDROID
float denom = 1.f - std::exp(curveValue);
float numer = 1.f - std::exp(pos * curveValue);
#else
// android
float denom = 1.f - expf(curveValue);
float numer = 1.f - expf(pos * curveValue);
#endif
return level0 + (level1 - level0) * (numer/denom);
}
}
else if(type == EnvCurve::Sine)
{
return linsin(time, lastTime, stageTime, level0, level1);
}
else if(type == EnvCurve::Exponential)
{
return linexp(time, lastTime, stageTime, level0, level1);
}
else if(type == EnvCurve::Welch)
{
return linwelch(time, lastTime, stageTime, level0, level1);
}
else
{
// Empty or Step
return level1;
}
}
Env Env::linen(const double attackTime,
const double sustainTime,
const double releaseTime,
const double sustainLevel,
EnvCurve const& curve) throw()
{
return Env({ 0.0, sustainLevel, sustainLevel, 0.0 },
{ attackTime, sustainTime, releaseTime },
curve);
}
Env Env::triangle(const double duration,
const double level) throw() {
const double durationHalved = duration * 0.5;
return Env({ 0.0, level, 0.0 },
{ durationHalved, durationHalved });
}
Env Env::sine(const double duration,
const double level) throw()
{
const double durationHalved = duration * 0.5;
return Env({ 0.0, level, 0.0 },
{ durationHalved, durationHalved },
EnvCurve::Sine);
}
Env Env::perc(const double attackTime,
const double releaseTime,
const double level,
EnvCurve const& curve) throw()
{
return Env({ 0.0, level, 0.0 },
{ attackTime, releaseTime },
curve);
}
Env Env::adsr(const double attackTime,
const double decayTime,
const double sustainLevel,
const double releaseTime,
const double level,
EnvCurveList const& curves) throw()
{
return Env({ 0.0, level, (level * sustainLevel), 0.0 },
{ attackTime, decayTime, releaseTime },
curves, 2);
}
Env Env::asr(const double attackTime,
const double sustainLevel,
const double releaseTime,
const double level,
EnvCurve const& curve) throw()
{
return Env({ 0.0, (level * sustainLevel), 0.0 },
{ attackTime, releaseTime },
curve, 1);
}
#endif // gpl