kopia lustrzana https://github.com/jameshball/osci-render
293 wiersze
7.2 KiB
C++
293 wiersze
7.2 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(std::vector<double> levels,
|
||
|
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() 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];
|
||
|
|
||
|
EnvCurve curve = getCurves()[stageIndex-1];
|
||
|
EnvCurve::CurveType type = curve.getType();
|
||
|
float curveValue = curve.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,
|
||
|
EnvCurve const& curve) throw()
|
||
|
{
|
||
|
return Env({ 0.0, level, (level * sustainLevel), 0.0 },
|
||
|
{ attackTime, decayTime, releaseTime },
|
||
|
curve, 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
|