pl-nk v0.4.5
Plonk|Plink|Plank are a set of cross-platform C/C++ frameworks for audio software development
plonk_Bus.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_BUS_H
00040 #define PLONK_BUS_H
00041 
00042 #include "../plonk_GraphForwardDeclarations.h"
00043 #include "../../containers/plonk_DynamicContainer.h"
00044 
00045 #include "../utility/plonk_ProcessInfo.h"
00046 #include "../utility/plonk_BlockSize.h"
00047 #include "../../core/plonk_SmartPointer.h"
00048 #include "../utility/plonk_TimeStamp.h"
00049 
00050 template<class SampleType>
00051 class BusBufferInternal : public SmartPointer,
00052                           public BlockSize::Receiver // what about samplerate changes?
00053 {
00054 public:    
00055     typedef NumericalArray<SampleType>                                          Buffer;
00056     typedef BusBuffer<SampleType>                                               BusType;
00057     typedef Dictionary<BusType>                                                 BusDictionary;
00058     typedef typename BinaryOpFunctionsHelper<SampleType>::BinaryOpFunctionsType BinaryOpFunctionsType;    
00059 
00060     BusBufferInternal() throw()
00061     {
00062         this->bufferSize.addReceiver(this);
00063         this->writeBlockSize.addReceiver(this);
00064     }
00065     
00066     BusBufferInternal(BlockSize const& bufferSizeToUse,
00067                       BlockSize const& writeBlockSizeToUse,
00068                       SampleRate const& sampleRateToUse) throw()
00069     :   bufferSize (bufferSizeToUse),
00070         writeBlockSize (writeBlockSizeToUse),
00071         sampleRate (sampleRateToUse),
00072         buffer (Buffer::newClear (bufferSize.getValue())),
00073         bufferStartTime (TimeStamp::getZero()),
00074         bufferEndTime (bufferStartTime + TimeStamp::fromSamples (bufferSize.getValue(), sampleRate)),
00075         latestValidTime (TimeStamp::getZero()),
00076         readDiff (0.0),
00077         firstWriteSize (0)
00078     {
00079         this->bufferSize.addReceiver(this);
00080         this->writeBlockSize.addReceiver(this);
00081     } 
00082     
00083     ~BusBufferInternal()
00084     {
00085         this->bufferSize.removeReceiver(this);
00086         this->writeBlockSize.removeReceiver(this);
00087     }
00088     
00089     void changed (BlockSize::Sender const& source, Text const& message, Dynamic const& payload) throw()
00090     {
00091         (void)message;
00092         (void)payload;
00093         
00094         BlockSize blockSizeSource = static_cast<BlockSize> (source);
00095         
00096         if (blockSizeSource == bufferSize)
00097         {
00098             plonk_assert (bufferSize.getValue() > buffer.length()); // code assumes the buffer will only grow at the moment
00099             
00100             buffer.setSize (bufferSize.getValue(), true); // copies existing data and zeros extra space too
00101             bufferEndTime = bufferStartTime + TimeStamp::fromSamples (bufferSize.getValue(), sampleRate);
00102         }
00103         else if (blockSizeSource == writeBlockSize)
00104         {
00105             // nothing
00106         }
00107     }
00108     
00109     inline void growBufferSize() throw()                            { bufferSize.setValue (bufferSize.getValue() * 2); }
00110     inline BlockSize getBufferSize() const throw()                  { return bufferSize; }
00111     inline BlockSize getWriteBlockSize() const throw()              { return writeBlockSize; }
00112     inline SampleRate getSampleRate() const throw()                 { return sampleRate; }
00113     inline double getDuration() const throw()                       { return (bufferEndTime - bufferStartTime).getValue(); }
00114     inline const TimeStamp& getLatestValidTime() const throw()      { return latestValidTime; }
00115     inline const TimeStamp getEarliestValidTime() const throw()     { return latestValidTime - this->getDuration(); }
00116     Text getLabel() const throw()                                   { return identifier; }
00117     void setLabel (Text const& newId) throw()                       { identifier = newId; }
00118 
00119     SampleType getPeak() const throw()
00120     {
00121         return buffer.findMaximumAbs();
00122     }
00123     
00124     SampleType getMean() const throw()
00125     {
00126         return buffer.findMeanAbs();
00127     }
00128     
00129     SampleType getRMS() const throw()
00130     {
00131         return buffer.findRMS();
00132     }
00133     
00134     void write (TimeStamp writeStartTime,
00135                 const int numWriteSamples,
00136                 const SampleType* sourceData) throw()
00137     {
00138         const int currentBufferSize = bufferSize.getValue();
00139         const double currentSampleRate = sampleRate.getValue();
00140         
00141         SampleType* const bufferSamples = buffer.getArray();
00142         
00143         int numSamplesRemaining = numWriteSamples;
00144         
00145         bool shouldMix = false;
00146         
00147         if (latestValidTime - TimeStamp::fromSamples (numWriteSamples,
00148                                                       currentSampleRate) >= writeStartTime)
00149         {
00150             shouldMix = true;
00151             
00152             // if mixing on a bus the write size must match the write size set for this bus on this block
00153             plonk_assert (numWriteSamples == firstWriteSize);
00154         }
00155         else
00156         {
00157             // keep a record of the size used to write the first block at this time
00158             firstWriteSize = writeBlockSize.getValue();
00159         }
00160         
00161         while (numSamplesRemaining > 0)
00162         {            
00163             if (writeStartTime >= bufferEndTime)
00164             {
00165                 bufferStartTime = bufferEndTime;
00166                 bufferEndTime = bufferStartTime + TimeStamp::fromSamples (currentBufferSize,
00167                                                                           currentSampleRate);
00168             }
00169 
00170             const TimeStamp bufferOffsetTime = writeStartTime - bufferStartTime;
00171             int bufferOffsetSamples = int (bufferOffsetTime.toSamples (currentSampleRate) + 0.5);
00172             
00173             if (bufferOffsetSamples >= currentBufferSize)
00174                 bufferOffsetSamples -= currentBufferSize;
00175             
00176             if (bufferOffsetSamples >= currentBufferSize)
00177             {
00178                 bufferStartTime = writeStartTime;
00179                 bufferEndTime = bufferStartTime + TimeStamp::fromSamples (currentBufferSize,
00180                                                                           currentSampleRate);
00181                 bufferOffsetSamples = 0;
00182                 buffer.zero();
00183             }
00184             
00185             const int bufferSamplesRemaining = currentBufferSize - bufferOffsetSamples;
00186             const int samplesThisTime = plonk::min (numSamplesRemaining,
00187                                                     bufferSamplesRemaining);
00188             
00189             if (shouldMix)
00190             {
00191                 NumericalArrayBinaryOp<SampleType, BinaryOpFunctionsType::addop>::calcNN (bufferSamples + bufferOffsetSamples,
00192                                                                                           bufferSamples + bufferOffsetSamples,
00193                                                                                           sourceData,
00194                                                                                           samplesThisTime);
00195             }
00196             else
00197             {
00198                 NumericalArray<SampleType>::copyData (bufferSamples + bufferOffsetSamples,
00199                                                       sourceData,
00200                                                       samplesThisTime);
00201             }
00202             
00203             numSamplesRemaining -= samplesThisTime;
00204             sourceData += samplesThisTime;
00205             
00206             const double timestampOffset = TimeStamp::fromSamples (samplesThisTime,
00207                                                                    currentSampleRate);
00208             writeStartTime += timestampOffset;
00209             latestValidTime = writeStartTime;
00210         }
00211     }
00212 
00213     void read (TimeStamp& readStartTime,
00214                const int numReadSamples, 
00215                SampleType* destData) throw()
00216     {                                
00217         const int currentBufferSize = bufferSize.getValue();
00218         const double currentSampleRate = sampleRate.getValue();
00219         
00220         const TimeStamp duration = TimeStamp::fromSamples (numReadSamples, currentSampleRate);
00221         TimeStamp readEndTime;
00222         
00223         if (readStartTime < TimeStamp::getZero())
00224         {
00225             readEndTime = latestValidTime;
00226             readStartTime = readEndTime - duration;
00227         }
00228         else
00229         {
00230             readEndTime = readStartTime + duration;
00231         }
00232 
00233         const TimeStamp earliestValidTime = this->getEarliestValidTime();
00234         const double diff = (latestValidTime - readEndTime).getValue();
00235         
00236         if ((readStartTime >= earliestValidTime) && 
00237             (readStartTime < latestValidTime) && 
00238             (readEndTime <= latestValidTime))
00239         {
00240             int bufferOffsetSamples = int ((readStartTime - bufferStartTime).toSamples (currentSampleRate) + 0.5);
00241             
00242             if (bufferOffsetSamples < 0)
00243                 bufferOffsetSamples += currentBufferSize - 1; // -1 due to being rounded in the wrong direction above when negative
00244             
00245             plonk_assert (bufferOffsetSamples < currentBufferSize);
00246             
00247             SampleType* const bufferSamples = buffer.getArray();
00248             
00249             int numSamplesRemaining = numReadSamples;
00250             
00251             while (numSamplesRemaining > 0)
00252             {
00253                 const int bufferSamplesRemaining = currentBufferSize - bufferOffsetSamples;
00254                 const int samplesThisTime = plonk::min (numSamplesRemaining, 
00255                                                         bufferSamplesRemaining);
00256                 
00257                 NumericalArray<SampleType>::copyData (destData, bufferSamples + bufferOffsetSamples, samplesThisTime);                    
00258                 numSamplesRemaining -= samplesThisTime;
00259                 
00260                 if (numSamplesRemaining > 0)
00261                 {
00262                     destData += samplesThisTime;
00263                     bufferOffsetSamples = 0;
00264                 }
00265             }                                  
00266             
00267             readStartTime = readEndTime; // for return
00268         }
00269         else
00270         {
00271             // zero this time and check if we need to enlarge the buffer
00272             
00273             if ((diff > 0.0) && (diff >= readDiff))
00274                 this->growBufferSize();
00275             
00276             NumericalArray<SampleType>::zeroData (destData, numReadSamples);
00277         }
00278 
00279         readDiff = diff; // keep track of the read difference to determine if we're actually getting anywhere
00280     }
00281         
00282 
00283 private:
00284     BlockSize bufferSize;       // size of the circular buffer
00285     BlockSize writeBlockSize;   // estimated size of the write operations
00286     SampleRate sampleRate;      // sample rate of the audio
00287     Buffer buffer;              // the circular buffer
00288     TimeStamp bufferStartTime;  // timestamp of the first sample in the buffer
00289     TimeStamp bufferEndTime;    // calculated timestamp of the sample past the end of the buffer but this may not be where we have written to yet
00290     TimeStamp latestValidTime;  // the latest time that has been written to the bus
00291     double readDiff;
00292     int firstWriteSize;         // size of the actual first write
00293     Text identifier;            // named ID for the buffer
00294 };
00295 
00296 
00303 template<class SampleType>
00304 class BusBuffer : public SmartPointerContainer<BusBufferInternal<SampleType> >
00305 {
00306 public:    
00307     typedef BusBufferInternal<SampleType>   Internal;
00308     typedef SmartPointerContainer<Internal> Base;
00309     typedef NumericalArray<SampleType>      Buffer;    
00310     typedef BusBuffer<SampleType>           BusType;
00311     typedef Dictionary<BusType>             BusDictionary;
00312     
00313     BusBuffer() throw()
00314     :   Base (new Internal())
00315     {
00316     }            
00317         
00318     explicit BusBuffer (Text const& name) throw()
00319     :   Base (getBus (name))
00320     {
00321     }
00322     
00323     explicit BusBuffer (const int number) throw()
00324     :   Base (getBus (Text (number)))
00325     {
00326     }
00327     
00328     explicit BusBuffer (const char* name) throw()
00329     :   Base (getBus (Text (name)))
00330     {
00331     }
00332     
00333     BusBuffer (BlockSize const& bufferSize, 
00334                BlockSize const& writeBlockSize, 
00335                SampleRate const& sampleRate) throw()
00336     :   Base (new Internal (bufferSize, writeBlockSize, sampleRate))
00337     {
00338     }        
00339     
00340     explicit BusBuffer (Internal* internal) throw()
00341     :   Base (internal)
00342     {
00343     }
00344     
00345     BusBuffer (BusBuffer const& copy) throw()
00346         :       Base (static_cast<Base const&> (copy))
00347         {
00348         }        
00349             
00350     static const BusBuffer& getNull() throw()
00351         {
00352                 static BusBuffer null;
00353                 return null;
00354         }                           
00355     
00356     inline void growBufferSize() throw()                            { this->getInternal()->growBufferSize(); }
00357     inline const BlockSize getBufferSize() const throw()            { return this->getInternal()->getBufferSize(); }
00358     inline BlockSize getBufferSize() throw()                        { return this->getInternal()->getBufferSize(); }
00359     inline const BlockSize getWriteBlockSize() const throw()        { return this->getInternal()->getWriteBlockSize(); }
00360     inline BlockSize getWriteBlockSize() throw()                    { return this->getInternal()->getWriteBlockSize(); }
00361     inline const SampleRate getSampleRate() const throw()           { return this->getInternal()->getSampleRate(); }
00362     inline SampleRate getSampleRate() throw()                       { return this->getInternal()->getSampleRate(); }
00363     inline double getDuration() throw()                             { return this->getInternal()->getDuration(); }
00364     inline const TimeStamp& getLatestValidTime() const throw()      { return this->getInternal()->getLatestValidTime(); }
00365     inline const TimeStamp  getEarliestValidTime() const throw()    { return this->getInternal()->getEarliestValidTime(); }
00366     inline Text getLabel() const throw()                            { return this->getInternal()->getLabel(); }
00367     inline void setLabel(Text const& newId) throw()                 { this->getInternal()->setLabel (newId); }
00368 
00370     inline void write (TimeStamp const& timeStamp, const int numSamples, const SampleType* sourceData) throw()
00371     {
00372         this->getInternal()->write (timeStamp, numSamples, sourceData);
00373     }
00374     
00376     inline void read (TimeStamp& timeStamp, const int numSamples, SampleType* destData) throw()
00377     {
00378         this->getInternal()->read (timeStamp, numSamples, destData);
00379     }
00380     
00381     inline SampleType getPeak() const throw()
00382     {
00383         return this->getInternal()->getPeak();
00384     }
00385     
00386     inline SampleType getMean() const throw()
00387     {
00388         return this->getInternal()->getMean();
00389     }
00390     
00391     inline SampleType getRMS() const throw()
00392     {
00393         return this->getInternal()->getRMS();
00394     }
00395     
00396     Text getName() const throw()
00397     {
00398         return getBusDictionary().keyForValue (*this);
00399     }
00400 
00401     static BusBuffer getBus (Text const& name) throw()
00402     {
00403         BusDictionary& dictionary = getBusDictionary();
00404         
00405         if (dictionary.getKeys().contains (name) == false)
00406         {
00407             BusBuffer newBusBuffer (BusBuffer::getDefaultBufferSize(), 
00408                                     BlockSize::getDefault(), 
00409                                     SampleRate::getDefault());
00410             dictionary.put (name, newBusBuffer);  
00411             return newBusBuffer;
00412         }        
00413         
00414         return dictionary[name];
00415     }
00416     
00417     static void addBus (Text const& name, 
00418                         BlockSize const& bufferSize = BusBuffer::getDefaultBufferSize(),
00419                         BlockSize const& writeBlockSize = BlockSize::getDefault(),
00420                         SampleRate const& sampleRate = SampleRate::getDefault()) throw()
00421     {
00422         BusDictionary& dictionary = getBusDictionary();
00423         
00424         plonk_assert (dictionary.getKeys().contains (name) == false);
00425 
00426         BusBuffer newBusBuffer (bufferSize, writeBlockSize, sampleRate);
00427         dictionary.put (name, newBusBuffer);
00428     }
00429     
00430     static void removeBus (Text const& name) throw()
00431     {
00432         getBusDictionary().remove (name);
00433     }
00434         
00435     static BlockSize getDefaultBufferSize() throw()
00436     {
00437         static BlockSize defaultBufferSize (4096);
00438         return defaultBufferSize;
00439     }    
00440     
00441     int getTypeCode() const throw()
00442     {
00443         return TypeUtility<BusBuffer>::getTypeCode();
00444     }
00445     
00446     int getSampleTypeCode() const throw()
00447     {
00448         return TypeUtility<SampleType>::getTypeCode();
00449     }            
00450     
00451     PLONK_OBJECTARROWOPERATOR(BusBuffer);
00452 
00453 private:    
00454     static BusDictionary& getBusDictionary() throw()
00455     {
00456         static BusDictionary dictionary;
00457         return dictionary;
00458     }    
00459     
00460     BusBuffer (const double) throw();
00461     BusBuffer (const float) throw();
00462 };
00463 
00464 
00465 
00466 
00467 #endif // PLONK_BUS_H
00468 
 All Classes Functions Typedefs Enumerations Enumerator Properties