![]() |
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_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