![]() |
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_FILEPLAY_H 00040 #define PLONK_FILEPLAY_H 00041 00042 #include "../channel/plonk_ChannelInternalCore.h" 00043 #include "../plonk_GraphForwardDeclarations.h" 00044 00045 template<class SampleType> class FilePlayChannelInternal; 00046 00047 PLONK_CHANNELDATA_DECLARE(FilePlayChannelInternal,SampleType) 00048 { 00049 ChannelInternalCore::Data base; 00050 int numChannels; 00051 int cueIndex; 00052 00053 bool done:1; 00054 bool deleteWhenDone:1; 00055 }; 00056 00057 //------------------------------------------------------------------------------ 00058 00060 template<class SampleType> 00061 class FilePlayChannelInternal 00062 : public ProxyOwnerChannelInternal<SampleType, PLONK_CHANNELDATA_NAME(FilePlayChannelInternal,SampleType)> 00063 { 00064 public: 00065 typedef PLONK_CHANNELDATA_NAME(FilePlayChannelInternal,SampleType) Data; 00066 typedef ChannelBase<SampleType> ChannelType; 00067 typedef ObjectArray<ChannelType> ChannelArrayType; 00068 typedef FilePlayChannelInternal<SampleType> FilePlayInternal; 00069 typedef ProxyOwnerChannelInternal<SampleType,Data> Internal; 00070 typedef UnitBase<SampleType> UnitType; 00071 typedef InputDictionary Inputs; 00072 typedef NumericalArray<SampleType> Buffer; 00073 00074 FilePlayChannelInternal (Inputs const& inputs, 00075 Data const& data, 00076 BlockSize const& blockSize, 00077 SampleRate const& sampleRate, 00078 ChannelArrayType& channels) throw() 00079 : Internal (decideNumChannels (inputs, data), 00080 inputs, data, blockSize, sampleRate, 00081 channels), 00082 zero (0) 00083 { 00084 // AudioFileReader& file = this->getInputAsAudioFileReader (IOKey::AudioFileReader); 00085 // file.setOwner (this); 00086 } 00087 00088 ~FilePlayChannelInternal() 00089 { 00090 // AudioFileReader& file = this->getInputAsAudioFileReader (IOKey::AudioFileReader); 00091 // file.setOwner (0); 00092 } 00093 00094 Text getName() const throw() 00095 { 00096 return "File Play"; 00097 } 00098 00099 IntArray getInputKeys() const throw() 00100 { 00101 const IntArray keys (IOKey::AudioFileReader, IOKey::Loop); 00102 return keys; 00103 } 00104 00105 void initChannel (const int channel) throw() 00106 { 00107 if ((channel % this->getNumChannels()) == 0) 00108 { 00109 const AudioFileReader& file = this->getInputAsAudioFileReader (IOKey::AudioFileReader); 00110 00111 double fileSampleRate = file.getSampleRate(); 00112 00113 if (fileSampleRate <= 0.0) 00114 fileSampleRate = file.getDefaultSampleRate(); 00115 00116 this->setSampleRate (SampleRate::decide (fileSampleRate, this->getSampleRate())); 00117 buffer.setSize (this->getBlockSize().getValue() * file.getNumChannels(), false); 00118 } 00119 00120 this->initProxyValue (channel, 0); 00121 } 00122 00123 void process (ProcessInfo& info, const int /*channel*/) throw() 00124 { 00125 Data& data = this->getState(); 00126 00127 IntVariable& loopCount (this->template getInputAs<IntVariable> (IOKey::LoopCount)); 00128 int channel; 00129 int offset = 0; 00130 const int blockSize = this->getBlockSize().getValue(); 00131 int blockRemain = blockSize; 00132 00133 while (offset < blockSize) 00134 { 00135 AudioFileReader& file = this->getInputAsAudioFileReader (IOKey::AudioFileReader); 00136 00137 const int numChannels = this->getNumChannels(); 00138 const int fileNumChannels = file.getNumChannels(); 00139 int bufferSize = blockRemain * fileNumChannels; 00140 00141 const LongLong numFrames = file.getNumFrames(); 00142 LongLong filePosition = file.getFramePosition(); 00143 bool willHitEOF = false; 00144 00145 if (numFrames > 0) 00146 { 00147 LongLong framesRemaining = numFrames - filePosition; 00148 00149 if (framesRemaining == 0) 00150 { 00151 if ((loopCount.getValue() == 0) || (loopCount.getValue() > 1)) 00152 { 00153 file.resetFramePosition(); 00154 data.cueIndex = 0; 00155 00156 if (loopCount.getValue() > 1) 00157 loopCount.setValue (loopCount.getValue() - 1); 00158 } 00159 00160 filePosition = file.getFramePosition(); 00161 framesRemaining = numFrames - filePosition; 00162 } 00163 00164 if (framesRemaining <= LongLong (blockRemain)) 00165 { 00166 bufferSize = int (plonk::min (LongLong (bufferSize), framesRemaining * fileNumChannels)); 00167 willHitEOF = true; 00168 } 00169 } 00170 00171 const AudioFileMetaData metaData = file.getMetaData(); 00172 00173 if (metaData.isNotNull()) 00174 { 00175 const AudioFileCuePointArray cuePoints = metaData.getCuePoints(); 00176 AudioFileCuePoint cue = cuePoints[data.cueIndex]; 00177 00178 bool renderToNextCue = false; 00179 00180 if (cue.isNotNull()) 00181 { 00182 if (cue.getFramePosition (file.getSampleRate()) == filePosition) 00183 { 00184 this->update (Text::getMessageCuePoint(), Text (cue.getLabel())); 00185 00186 ++data.cueIndex; 00187 cue = cuePoints[data.cueIndex]; 00188 00189 if (cue.isNotNull()) 00190 renderToNextCue = true; 00191 } 00192 else 00193 { 00194 renderToNextCue = true; 00195 } 00196 00197 if (renderToNextCue) 00198 { 00199 const LongLong framesToNextCue = cue.getFramePosition (file.getSampleRate()) - filePosition; 00200 00201 if (framesToNextCue < LongLong (bufferSize / fileNumChannels)) 00202 { 00203 bufferSize = int (plonk::min (LongLong (bufferSize), framesToNextCue * fileNumChannels)); 00204 willHitEOF = false; 00205 } 00206 } 00207 } 00208 } 00209 00210 buffer.setSize (bufferSize, false); 00211 file.readFrames (buffer, zero); 00212 const bool changedNumChannels = file.didNumChannelsChange(); 00213 const bool audioFileChanged = file.didAudioFileChange(); 00214 const bool hitEOF = file.didHitEOF(); 00215 const int bufferAvailable = buffer.length(); 00216 00217 if ((bufferAvailable == 0) || data.done) 00218 { 00219 for (channel = 0; channel < numChannels; ++channel) 00220 { 00221 Buffer& outputBuffer = this->getOutputBuffer (channel); 00222 SampleType* const outputSamples = outputBuffer.getArray() + offset; 00223 const int outputBufferLength = outputBuffer.length() - offset; 00224 00225 for (int i = 0; i < outputBufferLength; ++i) 00226 outputSamples[i] = SampleType (0); 00227 } 00228 00229 offset = blockSize; 00230 blockRemain = 0; 00231 } 00232 else if (willHitEOF || hitEOF) 00233 { 00234 const int bufferFramesAvailable = bufferAvailable / fileNumChannels; 00235 00236 for (channel = 0; channel < numChannels; ++channel) 00237 { 00238 Buffer& outputBuffer = this->getOutputBuffer (channel); 00239 SampleType* const outputSamples = outputBuffer.getArray() + offset; 00240 const int outputBufferLength = outputBuffer.length() - offset; 00241 const int outputLengthToWrite = plonk::min (bufferFramesAvailable, outputBufferLength); 00242 00243 const SampleType* bufferSamples = buffer.getArray() + ((unsigned int)channel % (unsigned int)fileNumChannels); 00244 00245 for (int i = 0; i < outputLengthToWrite; ++i, bufferSamples += fileNumChannels) 00246 outputSamples[i] = *bufferSamples; 00247 } 00248 00249 if ((loopCount.getValue() == 0) || (loopCount.getValue() > 1)) 00250 { 00251 file.resetFramePosition(); 00252 data.cueIndex = 0; 00253 00254 if (loopCount.getValue() > 1) 00255 loopCount.setValue (loopCount.getValue() - 1); 00256 } 00257 else if (!data.done) 00258 { 00259 data.done = true; 00260 this->update (Text::getMessageDone(), Dynamic::getNull()); 00261 } 00262 00263 offset += bufferFramesAvailable; 00264 blockRemain -= bufferFramesAvailable; 00265 } 00266 else 00267 { 00268 const int bufferFramesAvailable = bufferAvailable / fileNumChannels; 00269 00270 for (channel = 0; channel < numChannels; ++channel) 00271 { 00272 Buffer& outputBuffer = this->getOutputBuffer (channel); 00273 SampleType* const outputSamples = outputBuffer.getArray() + offset; 00274 const int outputBufferLength = outputBuffer.length() - offset; 00275 const int outputLengthToWrite = plonk::min (bufferFramesAvailable, outputBufferLength); 00276 00277 const SampleType* bufferSamples = buffer.getArray() + ((unsigned int)channel % (unsigned int)fileNumChannels); 00278 00279 for (int i = 0; i < outputLengthToWrite; ++i, bufferSamples += fileNumChannels) 00280 outputSamples[i] = *bufferSamples; 00281 } 00282 00283 offset += bufferFramesAvailable; 00284 blockRemain -= bufferFramesAvailable; 00285 } 00286 00287 if (audioFileChanged) 00288 this->update (Text::getMessageAudioFileChanged(), file); 00289 00290 if (changedNumChannels) 00291 this->update (Text::getMessageNumChannelsChanged(), IntVariable (fileNumChannels)); 00292 } 00293 00294 if (data.done && data.deleteWhenDone) 00295 info.setShouldDelete(); 00296 } 00297 00298 00299 // void process (ProcessInfo& info, const int /*channel*/) throw() 00300 // { 00301 // Data& data = this->getState(); 00302 // 00303 // IntVariable& loopCount (this->template getInputAs<IntVariable> (IOKey::LoopCount)); 00304 // int channel; 00305 // int offset = 0; 00306 // bool done = false; 00307 // 00308 // while (!done) 00309 // { 00310 // AudioFileReader& file = this->getInputAsAudioFileReader (IOKey::AudioFileReader); 00311 // 00312 // const int numChannels = this->getNumChannels(); 00313 // const int fileNumChannels = file.getNumChannels(); 00314 // const int blockSize = this->getBlockSize().getValue(); 00315 // int bufferSize = blockSize * fileNumChannels; 00316 // 00317 // const LongLong numFrames = file.getNumFrames(); 00318 // const LongLong filePosition = file.getFramePosition(); 00319 // bool willHitEOF = false; 00320 // 00321 // if (numFrames > 0) 00322 // { 00323 // const LongLong framesRemaining = numFrames - filePosition; 00324 // 00325 // if (framesRemaining <= (LongLong)blockSize) 00326 // { 00327 // bufferSize = plonk::min (blockSize, (int)framesRemaining * fileNumChannels); 00328 // willHitEOF = true; 00329 // } 00330 // } 00331 // 00332 // const AudioFileMetaData metaData = file.getMetaData(); 00333 // 00334 // if (metaData.isNotNull()) 00335 // { 00336 // const AudioFileCuePointArray cuePoints = metaData.getCuePoints(); 00337 // AudioFileCuePoint cue = cuePoints[data.cueIndex]; 00338 // 00339 // bool renderToNextCue = false; 00340 // 00341 // if (cue.isNotNull()) 00342 // { 00343 // if (cue.getPosition() == filePosition) 00344 // { 00345 // this->update (Text::getMessageCuePoint(), Text (cue.getLabel())); 00346 // 00347 // ++data.cueIndex; 00348 // cue = cuePoints[data.cueIndex]; 00349 // 00350 // if (cue.isNotNull()) 00351 // renderToNextCue = true; 00352 // } 00353 // else 00354 // { 00355 // renderToNextCue = true; 00356 // } 00357 // 00358 // if (renderToNextCue) 00359 // { 00360 // const LongLong framesToNextCue = cue.getPosition() - filePosition; 00361 // 00362 // if (framesToNextCue < (LongLong)blockSize) 00363 // bufferSize = plonk::min (bufferSize, (int)framesToNextCue * fileNumChannels); 00364 // } 00365 // } 00366 // } 00367 // 00368 // buffer.setSize (bufferSize, false); 00369 // file.readFrames (buffer, zero); 00370 // const bool changedNumChannels = file.didNumChannelsChange(); 00371 // const int bufferAvailable = buffer.length(); 00372 // 00373 // if (bufferAvailable == 0) 00374 // { 00375 // for (channel = 0; channel < numChannels; ++channel) 00376 // { 00377 // Buffer& outputBuffer = this->getOutputBuffer (channel); 00378 // SampleType* const outputSamples = outputBuffer.getArray() + offset; 00379 // const int outputBufferLength = outputBuffer.length() - offset; 00380 // 00381 // for (int i = 0; i < outputBufferLength; ++i) 00382 // outputSamples[i] = SampleType (0); 00383 // } 00384 // 00385 // done = true; 00386 // } 00387 // else if (willHitEOF) 00388 // { 00389 // const int bufferFramesAvailable = bufferAvailable / fileNumChannels; 00390 // 00391 // for (channel = 0; channel < numChannels; ++channel) 00392 // { 00393 // Buffer& outputBuffer = this->getOutputBuffer (channel); 00394 // SampleType* const outputSamples = outputBuffer.getArray() + offset; 00395 // const int outputBufferLength = outputBuffer.length() - offset; 00396 // const int outputLengthToWrite = plonk::min (bufferFramesAvailable, outputBufferLength); 00397 // 00398 // const SampleType* bufferSamples = buffer.getArray() + ((unsigned int)channel % (unsigned int)fileNumChannels); 00399 // 00400 // for (int i = 0; i < outputLengthToWrite; ++i, bufferSamples += fileNumChannels) 00401 // outputSamples[i] = *bufferSamples; 00402 // } 00403 // 00404 // if ((loopCount.getValue() == 0) || (loopCount.getValue() > 1)) 00405 // { 00406 // file.resetFramePosition(); 00407 // data.cueIndex = 0; 00408 // 00409 // if (loopCount.getValue() > 1) 00410 // loopCount.setValue (loopCount.getValue() - 1); 00411 // } 00412 // else if (!data.done) 00413 // { 00414 // data.done = true; 00415 // this->update (Text::getMessageDone(), Dynamic::getNull()); 00416 // } 00417 // 00418 // offset += bufferFramesAvailable; 00419 // done = false; 00420 // } 00421 // else if (changedNumChannels) 00422 // { 00423 // this->update (Text::getMessageNumChannelsChanged(), IntVariable (fileNumChannels)); 00424 // 00425 // const int bufferFramesAvailable = bufferAvailable / fileNumChannels; 00426 // 00427 // for (channel = 0; channel < numChannels; ++channel) 00428 // { 00429 // Buffer& outputBuffer = this->getOutputBuffer (channel); 00430 // SampleType* const outputSamples = outputBuffer.getArray() + offset; 00431 // const int outputBufferLength = outputBuffer.length() - offset; 00432 // const int outputLengthToWrite = plonk::min (bufferFramesAvailable, outputBufferLength); 00433 // 00434 // const SampleType* bufferSamples = buffer.getArray() + ((unsigned int)channel % (unsigned int)fileNumChannels); 00435 // 00436 // for (int i = 0; i < outputLengthToWrite; ++i, bufferSamples += fileNumChannels) 00437 // outputSamples[i] = *bufferSamples; 00438 // } 00439 // 00440 // offset += bufferFramesAvailable; 00441 // done = false; 00442 // } 00443 // else 00444 // { 00445 // for (channel = 0; channel < numChannels; ++channel) 00446 // { 00447 // Buffer& outputBuffer = this->getOutputBuffer (channel); 00448 // SampleType* const outputSamples = outputBuffer.getArray() + offset; 00449 // const int outputBufferLength = outputBuffer.length() - offset; 00450 // 00451 // const SampleType* bufferSamples = buffer.getArray() + ((unsigned int)channel % (unsigned int)fileNumChannels); 00452 // 00453 // for (int i = 0; i < outputBufferLength; ++i, bufferSamples += fileNumChannels) 00454 // outputSamples[i] = *bufferSamples; 00455 // } 00456 // 00457 // done = true; 00458 // } 00459 // } 00460 // 00461 // if (data.done && data.deleteWhenDone) 00462 // info.setShouldDelete(); 00463 // } 00464 00465 00466 private: 00467 Buffer buffer; // might need to use a signal... 00468 IntVariable zero; 00469 00470 static const int decideNumChannels (Inputs const& inputs, Data const& data) throw() 00471 { 00472 if (data.numChannels > 0) 00473 { 00474 return data.numChannels; 00475 } 00476 else 00477 { 00478 const AudioFileReader& file = inputs[IOKey::AudioFileReader].asUnchecked<AudioFileReader>(); 00479 return file.getNumChannels(); 00480 } 00481 } 00482 }; 00483 00484 //------------------------------------------------------------------------------ 00485 00509 template<class SampleType> 00510 class FilePlayUnit 00511 { 00512 public: 00513 typedef FilePlayChannelInternal<SampleType> FilePlayInternal; 00514 typedef typename FilePlayInternal::Data Data; 00515 typedef ChannelBase<SampleType> ChannelType; 00516 typedef ChannelInternal<SampleType,Data> Internal; 00517 typedef UnitBase<SampleType> UnitType; 00518 typedef InputDictionary Inputs; 00519 00520 00521 static inline UnitInfos getInfo() throw() 00522 { 00523 const double blockSize = (double)BlockSize::getDefault().getValue(); 00524 const double sampleRate = SampleRate::getDefault().getValue(); 00525 00526 return UnitInfo ("FilePlay", "A signal player generator.", 00527 00528 // output 00529 ChannelCount::VariableChannelCount, 00530 IOKey::Generic, Measure::None, 0.0, IOLimit::None, 00531 IOKey::End, 00532 00533 // inputs 00534 IOKey::AudioFileReader, Measure::None, 00535 IOKey::LoopCount, Measure::Count, 0.0, IOLimit::Minimum, Measure::Count, 0.0, 00536 IOKey::Multiply, Measure::Factor, 1.0, IOLimit::None, 00537 IOKey::Add, Measure::None, 0.0, IOLimit::None, 00538 IOKey::AutoDeleteFlag, Measure::Bool, IOInfo::True, IOLimit::None, 00539 IOKey::BlockSize, Measure::Samples, blockSize, IOLimit::Minimum, Measure::Samples, 1.0, 00540 IOKey::SampleRate, Measure::Hertz, sampleRate, IOLimit::Minimum, Measure::Hertz, 0.0, 00541 IOKey::End); 00542 } 00543 00545 static UnitType ar (AudioFileReader const& file, 00546 IntVariable const& loopCount = 0, 00547 UnitType const& mul = SampleType (1), 00548 UnitType const& add = SampleType (0), 00549 const bool deleteWhenDone = true, 00550 BlockSize const& preferredBlockSize = BlockSize::getDefault(), 00551 SampleRate const& preferredSampleRate = SampleRate::noPreference()) throw() 00552 { 00553 if (file.isReady() && !file.isOwned()) 00554 { 00555 Inputs inputs; 00556 inputs.put (IOKey::AudioFileReader, file); 00557 inputs.put (IOKey::LoopCount, loopCount); 00558 inputs.put (IOKey::Multiply, mul); 00559 inputs.put (IOKey::Add, add); 00560 00561 Data data = { { -1.0, -1.0 }, file.getDefaultNumChannels(), 0, false, deleteWhenDone }; 00562 00563 return UnitType::template proxiesFromInputs<FilePlayInternal> (inputs, 00564 data, 00565 preferredBlockSize, 00566 preferredSampleRate); 00567 } 00568 else return UnitType::getNull(); 00569 } 00570 00575 class Simple 00576 { 00577 public: 00578 typedef InputTaskUnit<SampleType,Interp::Linear> TaskType; 00579 typedef ResampleUnit<SampleType,Interp::Linear> ResampleType; 00580 typedef typename ResampleType::RateType RateType; 00581 typedef typename ResampleType::RateUnitType RateUnitType; 00582 00583 static UnitType ar (AudioFileReader const& file, 00584 RateUnitType const& rate = Math<RateUnitType>::get1(), 00585 IntVariable const& loopCount = 0, 00586 const int blockSizeMultiplier = 0, 00587 const int numBuffers = 4) 00588 { 00589 double fileSampleRate = file.getSampleRate(); 00590 00591 if (fileSampleRate <= 0.0) 00592 fileSampleRate = file.getDefaultSampleRate(); 00593 00594 const DoubleVariable multiplier = blockSizeMultiplier <= 0 ? 00595 (DoubleVariable (fileSampleRate) / SampleRate::getDefault()).ceil() * 2.0 : 00596 DoubleVariable (blockSizeMultiplier); 00597 00598 UnitType play = FilePlayUnit::ar (file, loopCount, 00599 SampleType (1), SampleType (0), 00600 false, 00601 BlockSize::getMultipleOfDefault (multiplier)); 00602 00603 UnitType task = TaskType::ar (play, numBuffers); 00604 00605 return ResampleType::ar (task, rate); 00606 } 00607 00608 class HQ 00609 { 00610 public: 00611 typedef InputTaskUnit<SampleType,Interp::Lagrange3> TaskType; 00612 typedef ResampleUnit<SampleType,Interp::Lagrange3> ResampleType; 00613 typedef typename ResampleType::RateType RateType; 00614 typedef typename ResampleType::RateUnitType RateUnitType; 00615 00616 static UnitType ar (AudioFileReader const& file, 00617 RateUnitType const& rate = Math<RateUnitType>::get1(), 00618 IntVariable const& loopCount = 0, 00619 const int blockSizeMultiplier = 0, 00620 const int numBuffers = 4) 00621 { 00622 double fileSampleRate = file.getSampleRate(); 00623 00624 if (fileSampleRate <= 0.0) 00625 fileSampleRate = file.getDefaultSampleRate(); 00626 00627 const DoubleVariable multiplier = blockSizeMultiplier <= 0 ? 00628 (DoubleVariable (fileSampleRate) / SampleRate::getDefault()).ceil() * 2.0 : 00629 DoubleVariable (blockSizeMultiplier); 00630 00631 UnitType play = FilePlayUnit::ar (file, loopCount, 00632 SampleType (1), SampleType (0), 00633 false, 00634 BlockSize::getMultipleOfDefault (multiplier)); 00635 00636 UnitType task = TaskType::ar (play, numBuffers); 00637 00638 return ResampleType::ar (task, rate); 00639 } 00640 }; 00641 00642 }; 00643 }; 00644 00645 typedef FilePlayUnit<PLONK_TYPE_DEFAULT> FilePlay; 00646 00647 00648 #endif // PLONK_FILEPLAY_H 00649