pl-nk v0.4.5
Plonk|Plink|Plank are a set of cross-platform C/C++ frameworks for audio software development
plonk_EnvelopeChannel.h
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 
 All Classes Functions Typedefs Enumerations Enumerator Properties