pl-nk v0.4.5
Plonk|Plink|Plank are a set of cross-platform C/C++ frameworks for audio software development
plonk_PatchChannel.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_PATCHCHANNEL_H
00040 #define PLONK_PATCHCHANNEL_H
00041 
00042 #include "../channel/plonk_ChannelInternalCore.h"
00043 #include "../plonk_GraphForwardDeclarations.h"
00044 #include "plonk_Mixers.h"
00045 
00046 template<class SampleType> class PatchChannelInternal;
00047 
00048 PLONK_CHANNELDATA_DECLARE(PatchChannelInternal,SampleType)
00049 {    
00050     ChannelInternalCore::Data base;
00051     int preferredNumChannels;
00052     SampleType fadeIncrement;
00053     SampleType fadeLevel;
00054     bool allowAutoDelete:1;
00055 };      
00056 
00057 
00060 template<class SampleType>
00061 class PatchChannelInternal 
00062 :   public ProxyOwnerChannelInternal<SampleType, PLONK_CHANNELDATA_NAME(PatchChannelInternal,SampleType)> 
00063 {
00064 public:
00065     typedef PLONK_CHANNELDATA_NAME(PatchChannelInternal,SampleType)     Data;
00066     typedef ChannelBase<SampleType>                                     ChannelType;
00067     typedef ObjectArray<ChannelType>                                    ChannelArrayType;
00068     typedef PatchChannelInternal<SampleType>                            PatchChannelInternalType;
00069     typedef ProxyOwnerChannelInternal<SampleType,Data>                  Internal;
00070     typedef UnitBase<SampleType>                                        UnitType;
00071     typedef InputDictionary                                             Inputs;
00072     typedef NumericalArray<SampleType>                                  Buffer;
00073     typedef Variable<UnitType&>                                         UnitVariableType;
00074     
00075     typedef typename TypeUtility<SampleType>::IndexType                 DurationType;
00076     typedef UnitBase<DurationType>                                      DurationUnitType;
00077     typedef NumericalArray<DurationType>                                DurationBufferType;
00078 
00079     
00080     PatchChannelInternal (Inputs const& inputs, 
00081                           Data const& data, 
00082                           BlockSize const& blockSize,
00083                           SampleRate const& sampleRate,
00084                           ChannelArrayType& channels) throw()
00085     :   Internal (data.preferredNumChannels > 0 ? data.preferredNumChannels : numChannelsInSource (inputs), 
00086                   inputs, data, blockSize, sampleRate, channels)
00087     {
00088     }
00089         
00090     Text getName() const throw()
00091     {        
00092         return "Patch";
00093     }        
00094     
00095     IntArray getInputKeys() const throw()
00096     {
00097         const IntArray keys (IOKey::UnitVariable);
00098         return keys;
00099     }    
00100         
00101     void initChannel (const int channel) throw()
00102     {
00103         if ((channel % this->getNumChannels()) == 0)
00104         {
00105             UnitVariableType& var = ChannelInternalCore::getInputAs<UnitVariableType> (IOKey::UnitVariable);
00106             
00107             if (var.isValueNotNull())
00108                 var.swapValues (currentSource);
00109         }
00110         
00111         this->initProxyValue (channel, currentSource.getValue (channel)); 
00112     }    
00113     
00114     void process (ProcessInfo& info, const int /*channel*/) throw()
00115     {       
00116         Data& data = this->getState();
00117         updateSources (info);
00118                 
00119         if (data.fadeLevel > Math<SampleType>::get0())
00120             processFade (info);
00121         else
00122             processCopy (info);
00123     }
00124     
00125     void processFade (ProcessInfo& info)
00126     {
00127         Data& data = this->getState();
00128         const int numChannels = this->getNumChannels();
00129 
00130         const SampleType fadeIncrement = data.fadeIncrement;
00131         const SampleType fadeMax = TypeUtility<SampleType>::getTypePeak();
00132         const SampleType fadeMin = Math<SampleType>::get0();
00133         SampleType fadeOutLevel, fadeInLevel;
00134 
00135         for (int channel = 0; channel < numChannels; ++channel)
00136         {
00137             fadeOutLevel = data.fadeLevel;
00138             fadeInLevel = fadeMax - fadeOutLevel;
00139 
00140             Buffer& outputBuffer = this->getOutputBuffer (channel);
00141             SampleType* const outputSamples = outputBuffer.getArray();
00142             const int outputBufferLength = outputBuffer.length();        
00143             
00144             const Buffer fadeInSourceBuffer (currentSource.process (info, channel));
00145             const SampleType* const fadeInSourceSamples = fadeInSourceBuffer.getArray();
00146             const int fadeInSourceBufferLength = fadeInSourceBuffer.length();
00147             
00148             const Buffer fadeOutSourceBuffer (fadeSource.process (info, channel));
00149             const SampleType* const fadeOutSourceSamples = fadeOutSourceBuffer.getArray();
00150             const int fadeOutSourceBufferLength = fadeOutSourceBuffer.length();
00151 
00152             int i;
00153             
00154             if ((fadeInSourceBufferLength == outputBufferLength) &&
00155                 (fadeInSourceBufferLength) == fadeOutSourceBufferLength)
00156             {
00157                 for (i = 0; i < outputBufferLength; ++i)
00158                 {
00159                     outputSamples[i] = fadeInSourceSamples[i] * fadeInLevel + fadeOutSourceSamples[i] * fadeOutLevel;
00160                     fadeInLevel = plonk::min (fadeInLevel + fadeIncrement, fadeMax);
00161                     fadeOutLevel = plonk::max (fadeOutLevel - fadeIncrement, fadeMin);
00162                 }
00163             }
00164             else
00165             {
00166                 double fadeInSourcePosition = 0.0;
00167                 const double fadeInSourceIncrement = double (fadeInSourceBufferLength) / double (outputBufferLength);
00168                 double fadeOutSourcePosition = 0.0;
00169                 const double fadeOutSourceIncrement = double (fadeOutSourceBufferLength) / double (outputBufferLength);
00170                 
00171                 for (i = 0; i < outputBufferLength; ++i) 
00172                 {
00173                     outputSamples[i] = fadeInSourceSamples[int (fadeInSourcePosition)] * fadeInLevel + 
00174                                        fadeOutSourceSamples[int (fadeOutSourcePosition)] * fadeOutLevel;
00175 
00176                     fadeInLevel = plonk::min (fadeInLevel + fadeIncrement, fadeMax);
00177                     fadeOutLevel = plonk::max (fadeOutLevel - fadeIncrement, fadeMin);
00178 
00179                     fadeInSourcePosition += fadeInSourceIncrement;
00180                     fadeOutSourcePosition += fadeOutSourceIncrement;
00181                 }        
00182             }
00183             
00184             if (data.allowAutoDelete == false)
00185                 info.resetShouldDelete();
00186         }
00187         
00188         data.fadeLevel = fadeOutLevel;
00189         
00190         if (fadeOutLevel <= fadeMin)
00191         {
00192             this->update (Text::getMessagePatchEnd(), fadeSource);
00193             fadeSource = UnitType::getNull();
00194         }
00195     }
00196     
00197     void processCopy (ProcessInfo& info)
00198     {
00199         Data& data = this->getState();
00200         const int numChannels = this->getNumChannels();
00201 
00202         for (int channel = 0; channel < numChannels; ++channel)
00203         {
00204             Buffer& outputBuffer = this->getOutputBuffer (channel);
00205             SampleType* const outputSamples = outputBuffer.getArray();
00206             const int outputBufferLength = outputBuffer.length();        
00207             
00208             const Buffer sourceBuffer (currentSource.process (info, channel));
00209             const SampleType* const sourceSamples = sourceBuffer.getArray();
00210             const int sourceBufferLength = sourceBuffer.length();
00211             
00212             int i;
00213             
00214             if (sourceBufferLength == outputBufferLength)
00215             {
00216                 Buffer::copyData (outputSamples, sourceSamples, outputBufferLength);
00217             }
00218             else if (sourceBufferLength == 1)
00219             {
00220                 NumericalArrayFiller<SampleType>::fill (outputSamples, sourceSamples[0], outputBufferLength);
00221             }
00222             else
00223             {
00224                 double sourcePosition = 0.0;
00225                 const double sourceIncrement = double (sourceBufferLength) / double (outputBufferLength);
00226                 
00227                 for (i = 0; i < outputBufferLength; ++i) 
00228                 {
00229                     outputSamples[i] = sourceSamples[int (sourcePosition)];
00230                     sourcePosition += sourceIncrement;
00231                 }        
00232             }
00233             
00234             if (data.allowAutoDelete == false)
00235                 info.resetShouldDelete();
00236         }
00237     }
00238 
00239 private:
00240     UnitType currentSource;
00241     UnitType fadeSource;
00242     
00243     static inline int numChannelsInSource (Inputs const& inputs) throw()
00244     {
00245         const UnitVariableType& var = inputs[IOKey::UnitVariable].asUnchecked<UnitVariableType>();
00246         return var.getValue().getNumChannels();
00247     }
00248     
00249     inline void updateSources (ProcessInfo& info) throw()
00250     {
00251         UnitVariableType& var = ChannelInternalCore::getInputAs<UnitVariableType> (IOKey::UnitVariable);
00252         
00253         if (var.isValueNotNull())
00254         {
00255             fadeSource = currentSource;
00256             currentSource = UnitType::getNull();
00257             var.swapValues (currentSource);
00258             
00259             this->update (Text::getMessagePatchStart(), currentSource);
00260             
00261             Data& data = this->getState();
00262 
00263             DurationUnitType& durationUnit = ChannelInternalCore::getInputAs<DurationUnitType> (IOKey::Duration);
00264             plonk_assert (durationUnit.getNumChannels() == 1);
00265             const DurationBufferType& durationBuffer (durationUnit.process (info, 0));
00266             const DurationType duration = durationBuffer.atUnchecked (0);
00267             
00268             if (duration > Math<DurationType>::get0())
00269             {
00270                 const int fadeSamplesRemaining = int (duration * data.base.sampleRate + 0.5);
00271                 data.fadeIncrement = TypeUtility<SampleType>::getTypePeak() / SampleType (fadeSamplesRemaining);
00272                 data.fadeLevel = TypeUtility<SampleType>::getTypePeak();
00273             }
00274         }
00275     }
00276 };
00277 
00278 
00279 //------------------------------------------------------------------------------
00280 
00298 template<class SampleType>
00299 class PatchUnit
00300 {
00301 public:    
00302     typedef PatchChannelInternal<SampleType>                        PatchChannelInternalType;
00303     typedef typename PatchChannelInternalType::Data                 Data;
00304     typedef ChannelBase<SampleType>                                 ChannelType;
00305     typedef ChannelInternal<SampleType,Data>                        Internal;
00306     typedef ChannelInternalBase<SampleType>                         InternaBase;
00307     typedef UnitBase<SampleType>                                    UnitType;
00308     typedef InputDictionary                                         Inputs;
00309     typedef NumericalArray<SampleType>                              Buffer;
00310     typedef Variable<UnitType&>                                     UnitVariableType;
00311 
00312     typedef typename PatchChannelInternalType::DurationType         DurationType;
00313     typedef typename PatchChannelInternalType::DurationUnitType     DurationUnitType;
00314     typedef typename PatchChannelInternalType::DurationBufferType   DurationBufferType;
00315 
00316     static inline UnitInfos getInfo() throw()
00317     {
00318         const double blockSize = (double)BlockSize::getDefault().getValue();
00319         const double sampleRate = SampleRate::getDefault().getValue();
00320 
00321         return UnitInfo ("Patch", "Threadsafe repatching of signals.",
00322                          
00323                          // output
00324                          1, 
00325                          IOKey::Generic,            Measure::None,      IOInfo::NoDefault,      IOLimit::None, 
00326                          IOKey::End,
00327                          
00328                          // inputs
00329                          IOKey::UnitVariable,           Measure::None,      IOInfo::NoDefault,  IOLimit::None, 
00330                          IOKey::AutoDeleteFlag,         Measure::Bool,      IOInfo::True,       IOLimit::None,
00331                          IOKey::PreferredNumChannels,   Measure::Count,     0.0,                IOLimit::None,
00332                          IOKey::Duration,               Measure::Seconds,   0.1,                IOLimit::Minimum, Measure::Seconds,             0.0,
00333                          IOKey::BlockSize,              Measure::Samples,   blockSize,          IOLimit::Minimum, Measure::Samples,             1.0,
00334                          IOKey::SampleRate,             Measure::Hertz,     sampleRate,         IOLimit::Minimum, Measure::Hertz,               0.0,
00335                          IOKey::End);
00336     }    
00337     
00339     static UnitType ar (UnitVariableType const& initialSource,
00340                         const bool allowAutoDelete = true,
00341                         const int preferredNumChannels = 0, 
00342                         DurationUnitType const& fadeDuration = DurationType (0.1),
00343                         BlockSize const& preferredBlockSize = BlockSize::getDefault(),
00344                         SampleRate const& preferredSampleRate = SampleRate::getDefault()) throw()
00345     {        
00346         Inputs inputs;
00347         inputs.put (IOKey::Unknown, UnitType::getNull()); // ensure getNull() is called off the audio thread for the first time
00348         inputs.put (IOKey::UnitVariable, initialSource);
00349         inputs.put (IOKey::Duration, fadeDuration);
00350         
00351         Data data = { { -1.0, -1.0 }, preferredNumChannels, 0, 0, allowAutoDelete };
00352             
00353         return UnitType::template proxiesFromInputs<PatchChannelInternalType> (inputs, 
00354                                                                                data, 
00355                                                                                preferredBlockSize, 
00356                                                                                preferredSampleRate);
00357     }   
00358 };
00359 
00360 typedef PatchUnit<PLONK_TYPE_DEFAULT> Patch;
00361 
00362 
00363 #endif // PLONK_PATCHCHANNEL_H
00364 
00365 
 All Classes Functions Typedefs Enumerations Enumerator Properties