![]() |
pl-nk v0.4.5
Plonk|Plink|Plank are a set of cross-platform C/C++ frameworks for audio software development
|
00001 /* 00002 ------------------------------------------------------------------------------- 00003 This file is part of the Plink, Plonk, Plank libraries 00004 by Martin Robinson 00005 00006 http://code.google.com/p/pl-nk/ 00007 00008 Copyright University of the West of England, Bristol 2011-14 00009 All rights reserved. 00010 00011 Redistribution and use in source and binary forms, with or without 00012 modification, are permitted provided that the following conditions are met: 00013 00014 * Redistributions of source code must retain the above copyright 00015 notice, this list of conditions and the following disclaimer. 00016 * Redistributions in binary form must reproduce the above copyright 00017 notice, this list of conditions and the following disclaimer in the 00018 documentation and/or other materials provided with the distribution. 00019 * Neither the name of University of the West of England, Bristol nor 00020 the names of its contributors may be used to endorse or promote products 00021 derived from this software without specific prior written permission. 00022 00023 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 00024 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 00025 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 00026 DISCLAIMED. IN NO EVENT SHALL UNIVERSITY OF THE WEST OF ENGLAND, BRISTOL BE 00027 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 00028 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 00029 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 00030 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 00031 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 00032 OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00033 00034 This software makes use of third party libraries. For more information see: 00035 doc/license.txt included in the distribution. 00036 ------------------------------------------------------------------------------- 00037 */ 00038 00039 #ifndef PLONK_ENVELOPECHANNEL_H 00040 #define PLONK_ENVELOPECHANNEL_H 00041 00042 #include "../channel/plonk_ChannelInternalCore.h" 00043 #include "../plonk_GraphForwardDeclarations.h" 00044 00045 00046 template<class SampleType> class EnvelopeChannelInternal; 00047 00048 PLONK_CHANNELDATA_DECLARE(EnvelopeChannelInternal,SampleType) 00049 { 00050 ChannelInternalCore::Data base; 00051 00052 ShapeState<SampleType> shapeState; 00053 int targetPointIndex; 00054 00055 bool prevGate:1; 00056 bool done:1; 00057 bool deleteWhenDone:1; 00058 }; 00059 00060 00061 00063 template<class SampleType> 00064 class EnvelopeChannelInternal 00065 : public ChannelInternal<SampleType, PLONK_CHANNELDATA_NAME(EnvelopeChannelInternal,SampleType)> 00066 { 00067 public: 00068 typedef PLONK_CHANNELDATA_NAME(EnvelopeChannelInternal,SampleType) Data; 00069 typedef ChannelBase<SampleType> ChannelType; 00070 typedef EnvelopeChannelInternal<SampleType> EnvelopeInternal; 00071 typedef ChannelInternal<SampleType,Data> Internal; 00072 typedef ChannelInternalBase<SampleType> InternalBase; 00073 typedef UnitBase<SampleType> UnitType; 00074 typedef InputDictionary Inputs; 00075 typedef BreakpointsBase<SampleType> BreakpointsType; 00076 typedef BreakpointBase<SampleType> BreakpointType; 00077 00078 EnvelopeChannelInternal (Inputs const& inputs, 00079 Data const& data, 00080 BlockSize const& blockSize, 00081 SampleRate const& sampleRate) throw() 00082 : Internal (inputs, data, blockSize, sampleRate) 00083 { 00084 } 00085 00086 Text getName() const throw() 00087 { 00088 return "Envelope"; 00089 } 00090 00091 IntArray getInputKeys() const throw() 00092 { 00093 const IntArray keys (IOKey::Breakpoints, IOKey::Gate); 00094 return keys; 00095 } 00096 00097 InternalBase* getChannel (const int /*index*/) throw() 00098 { 00099 return this; 00100 } 00101 00102 void initChannel (const int channel) throw() 00103 { 00104 Data& data = this->getState(); 00105 const BreakpointsType& breakpoints = this->getInputAsBreakpoints (IOKey::Breakpoints); 00106 00107 // const UnitType& gate = this->getInputAsUnit (IOKey::Gate); 00108 plonk_assert (this->getInputAsUnit (IOKey::Gate).getOverlap (channel) == Math<DoubleVariable>::get1()); 00109 00110 data.shapeState.currentLevel = breakpoints.getStartLevel(); 00111 this->initValue (data.shapeState.currentLevel); 00112 00113 this->setTargetPoint (-1, 0); 00114 } 00115 00116 void setTargetPoint (const int index, const int samplesRemaining) throw() 00117 { 00118 Data& data = this->getState(); 00119 00120 if ((index != -1) && (index == data.targetPointIndex)) // ?? ugh can't be right 00121 { 00122 // sustain 00123 data.shapeState.grow.u.norm = 0; 00124 data.shapeState.stepsToTarget = samplesRemaining; 00125 } 00126 else 00127 { 00128 const BreakpointsType& breakpoints = this->getInputAsBreakpoints (IOKey::Breakpoints); 00129 00130 if (index < 0) 00131 { 00132 // start 00133 data.targetPointIndex = -1; 00134 data.shapeState.shapeType = Shape::Linear; 00135 data.shapeState.curve = 0.f; 00136 data.shapeState.grow.u.norm = SampleType (0); 00137 data.shapeState.stepsToTarget = samplesRemaining; 00138 } 00139 else if (index >= breakpoints.getNumBreakpoints()) 00140 { 00141 // finished... 00142 data.targetPointIndex = breakpoints.getNumBreakpoints(); 00143 Shape::sustain (data.shapeState); 00144 00145 data.done = true; 00146 00147 this->update (Text::getMessageDone(), Dynamic::getNull()); 00148 } 00149 else 00150 { 00151 // configure next transition 00152 data.targetPointIndex = index; 00153 const BreakpointType& targetBreakpoint = breakpoints.atUnchecked (index); 00154 00155 const Shape& shape = targetBreakpoint.getShape(); 00156 data.shapeState.shapeType = shape.getType(); 00157 data.shapeState.curve = shape.getCurve(); 00158 data.shapeState.targetLevel = targetBreakpoint.getTargetLevel(); 00159 data.shapeState.stepsToTarget = targetBreakpoint.getTargetTime() * data.base.sampleRate; 00160 Shape::initShape (data.shapeState); 00161 } 00162 } 00163 } 00164 00165 inline void nextTargetPoint (const bool gate, const int samplesRemaining) throw() 00166 { 00167 const Data& data = this->getState(); 00168 const BreakpointsType& breakpoints = this->getInputAsBreakpoints (IOKey::Breakpoints); 00169 00170 const int next = (data.targetPointIndex < 0) ? 0 : breakpoints.atUnchecked (data.targetPointIndex).getNext (gate); 00171 00172 if (next == BreakpointType::This) 00173 this->setTargetPoint (data.targetPointIndex, samplesRemaining); 00174 else if (next == BreakpointType::Next) 00175 this->setTargetPoint (data.targetPointIndex + 1, samplesRemaining); 00176 else 00177 this->setTargetPoint (next, samplesRemaining); 00178 } 00179 00180 void process (ProcessInfo& info, const int /*channel*/) throw() 00181 { 00182 Data& data = this->getState(); 00183 00184 SampleType* outputSamples = this->getOutputSamples(); 00185 const int outputBufferLength = this->getOutputBuffer().length(); 00186 00187 if (data.done) 00188 { 00189 NumericalArrayFiller<SampleType>::fill (outputSamples, data.shapeState.currentLevel, outputBufferLength); 00190 } 00191 else 00192 { 00193 UnitType& gate (this->getInputAsUnit (IOKey::Gate)); 00194 const Buffer& gateBuffer (gate.process (info, 0)); 00195 const SampleType* const gateSamples = gateBuffer.getArray(); 00196 const int gateBufferLength = gateBuffer.length(); 00197 00198 bool currGate; 00199 00200 if (gateBufferLength == 1) // most efficient 00201 { 00202 int samplesRemaining = outputBufferLength; 00203 currGate = gateSamples[0] >= SampleType (0.5); 00204 00205 if (currGate != data.prevGate) 00206 this->nextTargetPoint (currGate, samplesRemaining); 00207 00208 while (samplesRemaining > 0) 00209 { 00210 const int samplesThisTime = int (plonk::min (LongLong (samplesRemaining), data.shapeState.stepsToTarget)); 00211 00212 if (samplesThisTime > 0) 00213 { 00214 Shape::processShape (data.shapeState, outputSamples, samplesThisTime); 00215 00216 outputSamples += samplesThisTime; 00217 samplesRemaining -= samplesThisTime; 00218 } 00219 00220 if (data.shapeState.stepsToTarget == 0) 00221 this->nextTargetPoint (currGate, samplesRemaining); 00222 } 00223 00224 data.prevGate = currGate; 00225 } 00226 else if (gateBufferLength == outputBufferLength) 00227 { 00228 for (int i = 0; i < outputBufferLength; ++i) 00229 { 00230 currGate = gateSamples[i] >= SampleType (0.5); 00231 00232 if (currGate != data.prevGate) 00233 this->nextTargetPoint (currGate, 1); 00234 00235 Shape::processShape (data.shapeState, outputSamples++, 1); 00236 00237 if (data.shapeState.stepsToTarget == 0) 00238 this->nextTargetPoint (currGate, 1); 00239 00240 data.prevGate = currGate; 00241 } 00242 } 00243 else 00244 { 00245 double gatePosition = 0.0; 00246 const double gateIncrement = double (gateBufferLength) / double (outputBufferLength); 00247 00248 for (int i = 0; i < outputBufferLength; ++i) 00249 { 00250 currGate = gateSamples[int (gatePosition)] >= SampleType (0.5); 00251 00252 if (currGate != data.prevGate) 00253 this->nextTargetPoint (currGate, 1); 00254 00255 Shape::processShape (data.shapeState, outputSamples++, 1); 00256 00257 if (data.shapeState.stepsToTarget == 0) 00258 this->nextTargetPoint (currGate, 1); 00259 00260 data.prevGate = currGate; 00261 gatePosition += gateIncrement; 00262 } 00263 } 00264 00265 if (data.done && data.deleteWhenDone) 00266 info.setShouldDelete(); 00267 } 00268 } 00269 00270 private: 00271 }; 00272 00273 00274 00275 //------------------------------------------------------------------------------ 00276 00288 template<class SampleType> 00289 class EnvelopeUnit 00290 { 00291 public: 00292 typedef EnvelopeChannelInternal<SampleType> EnvelopeInternal; 00293 typedef typename EnvelopeInternal::Data Data; 00294 typedef ChannelBase<SampleType> ChannelType; 00295 typedef ChannelInternal<SampleType,Data> Internal; 00296 typedef ChannelInternalBase<SampleType> InternaBase; 00297 typedef UnitBase<SampleType> UnitType; 00298 typedef InputDictionary Inputs; 00299 typedef BreakpointsBase<SampleType> BreakpointsType; 00300 00301 static inline UnitInfos getInfo() throw() 00302 { 00303 const double blockSize = (double)BlockSize::getDefault().getValue(); 00304 const double sampleRate = SampleRate::getDefault().getValue(); 00305 00306 return UnitInfo ("Envelope", "Performs a series of breakpoints.", 00307 00308 // output 00309 1, 00310 IOKey::Generic, Measure::None, IOInfo::NoDefault, IOLimit::None, 00311 IOKey::End, 00312 00313 // inputs 00314 IOKey::Breakpoints, Measure::None, 00315 IOKey::Gate, Measure::NormalisedBipolar, 0.0, IOLimit::Clipped, Measure::NormalisedBipolar, -1.0, 1.0, 00316 IOKey::AutoDeleteFlag, Measure::Bool, IOInfo::True, IOLimit::None, 00317 IOKey::BlockSize, Measure::Samples, blockSize, IOLimit::Minimum, Measure::Samples, 1.0, 00318 IOKey::SampleRate, Measure::Hertz, sampleRate, IOLimit::Minimum, Measure::Hertz, 0.0, 00319 IOKey::End); 00320 } 00321 00323 static UnitType ar (BreakpointsType const& breakpoints, 00324 UnitType const& gate = SampleType (1), 00325 const bool deleteWhenDone = true, 00326 BlockSize const& preferredBlockSize = BlockSize::getDefault(), 00327 SampleRate const& preferredSampleRate = SampleRate::getDefault()) throw() 00328 { 00329 plonk_assert (gate.getNumChannels() == 1); 00330 00331 Inputs inputs; 00332 inputs.put (IOKey::Breakpoints, breakpoints); 00333 inputs.put (IOKey::Gate, gate); 00334 00335 Data data = { { -1.0, -1.0 }, 00336 { 0, 0, 0, 0, 00337 0, 0, 0, 0, // coeffs 00338 Shape::Linear, 0.f }, 00339 -1, 00340 false, false, deleteWhenDone }; 00341 00342 return UnitType::template createFromInputs<EnvelopeInternal> (inputs, 00343 data, 00344 preferredBlockSize, 00345 preferredSampleRate); 00346 } 00347 00349 static inline UnitType kr (BreakpointsType const& breakpoints, 00350 UnitType const& gate = SampleType (1), 00351 const bool deleteWhenDone = true) throw() 00352 { 00353 return ar (breakpoints, gate, deleteWhenDone, 00354 BlockSize::getControlRateBlockSize(), 00355 SampleRate::getControlRate()); 00356 } 00357 00358 }; 00359 00360 typedef EnvelopeUnit<PLONK_TYPE_DEFAULT> Envelope; 00361 00362 00363 00364 00365 #endif // PLONK_ENVELOPECHANNEL_H 00366 00367