pl-nk v0.4.5
Plonk|Plink|Plank are a set of cross-platform C/C++ frameworks for audio software development
plonk_TriggerChannel.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_TRIGGERCHANNEL_H
00040 #define PLONK_TRIGGERCHANNEL_H
00041 
00042 #include "../channel/plonk_ChannelInternalCore.h"
00043 #include "../plonk_GraphForwardDeclarations.h"
00044 
00045 template<class SampleType> class TriggerChannelInternal;
00046 
00047 PLONK_CHANNELDATA_DECLARE(TriggerChannelInternal,SampleType)
00048 {
00049     ChannelInternalCore::Data base;
00050     
00051     int durationRemainingInSamples;
00052     SampleType prevValue;
00053 };
00054 
00056 template<class SampleType>
00057 class TriggerChannelInternal
00058 :   public ChannelInternal<SampleType, PLONK_CHANNELDATA_NAME(TriggerChannelInternal,SampleType)>
00059 {
00060 public:
00061     typedef PLONK_CHANNELDATA_NAME(TriggerChannelInternal,SampleType)   Data;
00062     typedef ChannelBase<SampleType>                                     ChannelType;
00063     typedef TriggerChannelInternal<SampleType>                          TriggerInternal;
00064     typedef ChannelInternal<SampleType,Data>                            Internal;
00065     typedef ChannelInternalBase<SampleType>                             InternalBase;
00066     typedef UnitBase<SampleType>                                        UnitType;
00067     typedef InputDictionary                                             Inputs;
00068     typedef NumericalArray<SampleType>                                  Buffer;
00069     typedef typename TypeUtility<SampleType>::IndexType                 IndexType;
00070     
00071     typedef typename TypeUtility<SampleType>::IndexType                 DurationType;
00072     typedef UnitBase<DurationType>                                      DurationUnitType;
00073     typedef NumericalArray<DurationType>                                DurationBufferType;
00074         
00075     TriggerChannelInternal (Inputs const& inputs, 
00076                             Data const& data,
00077                             BlockSize const& blockSize,
00078                             SampleRate const& sampleRate) throw()
00079     :   Internal (inputs, data, blockSize, sampleRate)
00080     {
00081     }
00082     
00083     Text getName() const throw()
00084     {
00085         return "Trigger";
00086     }        
00087     
00088     IntArray getInputKeys() const throw()
00089     {
00090         const IntArray keys (IOKey::Generic, IOKey::Duration);
00091         return keys;
00092     }
00093     
00094     InternalBase* getChannel (const int index) throw()
00095     {
00096         const Inputs channelInputs = this->getInputs().getChannel (index);
00097         return new TriggerInternal (channelInputs, 
00098                                     this->getState(), 
00099                                     this->getBlockSize(),
00100                                     this->getSampleRate());
00101     }    
00102     
00103     void initChannel (const int channel) throw()
00104     {        
00105         const UnitType& input = this->getInputAsUnit (IOKey::Generic);
00106 
00107         this->setBlockSize (BlockSize::decide (input.getBlockSize (channel),
00108                                                this->getBlockSize()));
00109         this->setSampleRate (SampleRate::decide (input.getSampleRate (channel),
00110                                                  this->getSampleRate()));
00111         this->setOverlap (input.getOverlap (channel));
00112     
00113         this->initValue (0);
00114     }    
00115         
00116     void process (ProcessInfo& info, const int channel) throw()
00117     {
00118         Data& data = this->getState();
00119         
00120         UnitType& inputUnit (this->getInputAsUnit (IOKey::Generic));
00121         const Buffer& inputBuffer (inputUnit.process (info, channel));
00122         const SampleType* inputSamples = inputBuffer.getArray();
00123         const int inputBufferLength = inputBuffer.length();
00124 
00125         DurationUnitType& durationUnit = ChannelInternalCore::getInputAs<DurationUnitType> (IOKey::Duration);
00126         const DurationBufferType& durationBuffer (durationUnit.process (info, channel));
00127         const DurationType* durationSamples = durationBuffer.getArray();
00128         const int durationBufferLength = durationBuffer.length();
00129 
00130         SampleType* outputSamples = this->getOutputSamples();
00131         const int outputBufferLength = this->getOutputBuffer().length();
00132         
00133         SampleType prevValue = data.prevValue;
00134         DurationType durationRemainingInSamples = data.durationRemainingInSamples;
00135         
00136         const SampleType sampleZero = Math<SampleType>::get0();
00137         const SampleType sampleOne = Math<SampleType>::get1();
00138 
00139         int i;
00140         
00141         if (inputBufferLength == outputBufferLength)
00142         {
00143             if (inputBufferLength == durationBufferLength)
00144             {
00145                 for (i = 0; i < outputBufferLength; ++i)
00146                 {
00147                     const SampleType currValue = inputSamples[i];
00148                     
00149                     if (prevValue <= sampleZero && currValue > sampleZero)
00150                     {
00151                         durationRemainingInSamples = plonk::max (1, int (durationSamples[i] * data.base.sampleRate));
00152                         this->update (Text::getMessageTrigger(), Dynamic::getNull());
00153                     }
00154                     
00155                     outputSamples[i] = durationRemainingInSamples > 0 ? sampleOne : sampleZero;
00156                         
00157                     --durationRemainingInSamples;
00158                     prevValue = currValue;
00159                 }
00160             }
00161             else // if (durationBufferLength == 1)
00162             {
00163                 const DurationType durationInSamples = plonk::max (1, int (durationSamples[0] * data.base.sampleRate));
00164                 
00165                 for (i = 0; i < outputBufferLength; ++i)
00166                 {
00167                     const SampleType currValue = inputSamples[i];
00168                     
00169                     if (prevValue <= sampleZero && currValue > sampleZero)
00170                     {
00171                         durationRemainingInSamples = durationInSamples;
00172                         this->update (Text::getMessageTrigger(), Dynamic::getNull());
00173                     }
00174                     
00175                     outputSamples[i] = durationRemainingInSamples > 0 ? sampleOne : sampleZero;
00176                     
00177                     --durationRemainingInSamples;
00178                     prevValue = currValue;
00179                 }
00180             }
00181         }
00182         else
00183         {
00184             double inputPosition = 0.0;
00185             const double inputIncrement = double (inputBufferLength) / double (outputBufferLength);
00186             
00187             double durationPosition = 0.0;
00188             const double durationIncrement = double (durationBufferLength) / double (outputBufferLength);
00189             
00190             for (i = 0; i < outputBufferLength; ++i)
00191             {
00192                 const SampleType currValue = inputSamples[int (inputPosition)];
00193                 
00194                 if (prevValue <= sampleZero && currValue > sampleZero)
00195                 {
00196                     durationRemainingInSamples = plonk::max (1, int (durationSamples[int (durationPosition)] * data.base.sampleRate));
00197                     this->update (Text::getMessageTrigger(), Dynamic::getNull());
00198                 }
00199                 
00200                 outputSamples[i] = durationRemainingInSamples > 0 ? sampleOne : sampleZero;
00201                     
00202                 --durationRemainingInSamples;
00203                 inputPosition += inputIncrement;
00204                 durationPosition += durationIncrement;
00205                 prevValue = currValue;
00206             }
00207 
00208         }
00209         
00210         // prevent overflow
00211         if (durationRemainingInSamples < 0)
00212             durationRemainingInSamples = 0;
00213         
00214         data.prevValue = prevValue;
00215         data.durationRemainingInSamples = durationRemainingInSamples;
00216     }
00217 };
00218 
00219 //------------------------------------------------------------------------------
00220 
00237 template<class SampleType>
00238 class TriggerUnit
00239 {
00240 public:    
00241     typedef TriggerChannelInternal<SampleType>              TriggerInternal;
00242     typedef typename TriggerInternal::Data                  Data;
00243     typedef ChannelBase<SampleType>                         ChannelType;
00244     typedef ChannelInternal<SampleType,Data>                Internal;
00245     typedef UnitBase<SampleType>                            UnitType;
00246     typedef InputDictionary                                 Inputs;
00247 
00248     typedef typename TriggerInternal::DurationType          DurationType;
00249     typedef typename TriggerInternal::DurationUnitType      DurationUnitType;
00250     typedef typename TriggerInternal::DurationBufferType    DurationBufferType;
00251     
00252     static inline UnitInfos getInfo() throw()
00253     {
00254         const double blockSize = (double)BlockSize::getDefault().getValue();
00255         const double sampleRate = SampleRate::getDefault().getValue();
00256 
00257         return UnitInfo ("Trigger", "Trigger detects signal transitions from zero or less to greater than zero.",
00258                          
00259                          // output
00260                          ChannelCount::VariableChannelCount, 
00261                          IOKey::Generic,    Measure::None,     IOInfo::NoDefault,   IOLimit::None,      IOKey::End,
00262 
00263                          // inputs
00264                          IOKey::Generic,    Measure::None,     IOInfo::NoDefault,   IOLimit::None,
00265                          IOKey::Duration,   Measure::Seconds,  0.005,               IOLimit::Minimum,   Measure::Seconds,   0.0,
00266                          IOKey::BlockSize,  Measure::Samples,  blockSize,           IOLimit::Minimum,   Measure::Samples,   1.0,
00267                          IOKey::SampleRate, Measure::Hertz,    sampleRate,          IOLimit::Minimum,   Measure::Hertz,     0.0,
00268                          IOKey::End);
00269     }    
00270     
00272     static UnitType ar (UnitType const& input,
00273                         DurationUnitType const& duration = DurationType (0.005),
00274                         BlockSize const& preferredBlockSize = BlockSize::getDefault(),
00275                         SampleRate const& preferredSampleRate = SampleRate::getDefault()) throw()
00276     {                                
00277         Inputs inputs;
00278         inputs.put (IOKey::Generic, input);
00279         inputs.put (IOKey::Duration, duration);
00280         
00281         Data data = { { -1.0, -1.0 }, 0, 0 };
00282                 
00283         return UnitType::template createFromInputs<TriggerInternal> (inputs, 
00284                                                                      data,
00285                                                                      preferredBlockSize,
00286                                                                      preferredSampleRate);
00287     }
00288     
00289     static inline UnitType kr (UnitType const& input,
00290                                DurationUnitType const& duration = DurationType (0.005)) throw()
00291     {
00292         return ar (input,
00293                    duration,
00294                    BlockSize::getControlRateBlockSize(), 
00295                    SampleRate::getControlRate());
00296     }
00297     
00298 };
00299 
00300 typedef TriggerUnit<PLONK_TYPE_DEFAULT> Trigger;
00301 
00302 
00303 
00304 #endif // PLONK_TRIGGERCHANNEL_H
00305 
00306 
 All Classes Functions Typedefs Enumerations Enumerator Properties