![]() |
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_SAW_H 00040 #define PLONK_SAW_H 00041 00042 #include "../channel/plonk_ChannelInternalCore.h" 00043 #include "../plonk_GraphForwardDeclarations.h" 00044 00045 template<class SampleType> class SawChannelInternal; 00046 00047 PLONK_CHANNELDATA_DECLARE(SawChannelInternal,SampleType) 00048 { 00049 typedef typename TypeUtility<SampleType>::IndexType FrequencyType; 00050 00051 ChannelInternalCore::Data base; 00052 00053 FrequencyType currentValue; 00054 LongLong peak; 00055 LongLong peak2peak; 00056 }; 00057 00058 PLONK_CHANNELDATA_SPECIAL(SawChannelInternal,float) 00059 { 00060 ChannelInternalCore::Data base; 00061 00062 float currentValue; 00063 float peak; 00064 float peak2peak; 00065 }; 00066 00067 PLONK_CHANNELDATA_SPECIAL(SawChannelInternal,double) 00068 { 00069 ChannelInternalCore::Data base; 00070 00071 double currentValue; 00072 double peak; 00073 double peak2peak; 00074 }; 00075 00076 PLONK_CHANNELDATA_SPECIAL(SawChannelInternal,short) 00077 { 00078 typedef TypeUtility<short>::IndexType FrequencyType; 00079 00080 ChannelInternalCore::Data base; 00081 00082 FrequencyType currentValue; 00083 short peak; 00084 int peak2peak; 00085 }; 00086 00087 PLONK_CHANNELDATA_SPECIAL(SawChannelInternal,int) 00088 { 00089 typedef TypeUtility<int>::IndexType FrequencyType; 00090 00091 ChannelInternalCore::Data base; 00092 00093 FrequencyType currentValue; 00094 int peak; 00095 LongLong peak2peak; 00096 }; 00097 00098 00099 00100 //------------------------------------------------------------------------------ 00101 00103 template<class SampleType> 00104 class SawChannelInternal 00105 : public ChannelInternal<SampleType, PLONK_CHANNELDATA_NAME(SawChannelInternal,SampleType)> 00106 { 00107 public: 00108 typedef PLONK_CHANNELDATA_NAME(SawChannelInternal,SampleType) Data; 00109 typedef InputDictionary Inputs; 00110 00111 typedef ChannelBase<SampleType> ChannelType; 00112 typedef SawChannelInternal<SampleType> SawInternal; 00113 typedef ChannelInternal<SampleType,Data> Internal; 00114 typedef ChannelInternalBase<SampleType> InternalBase; 00115 typedef UnitBase<SampleType> UnitType; 00116 00117 typedef typename TypeUtility<SampleType>::IndexType FrequencyType; 00118 typedef UnitBase<FrequencyType> FrequencyUnitType; 00119 typedef NumericalArray<FrequencyType> FrequencyBufferType; 00120 00121 SawChannelInternal (Inputs const& inputs, 00122 Data const& data, 00123 BlockSize const& blockSize, 00124 SampleRate const& sampleRate) throw() 00125 : Internal (inputs, data, blockSize, sampleRate) 00126 { 00127 } 00128 00129 Text getName() const throw() 00130 { 00131 return "Saw"; 00132 } 00133 00134 IntArray getInputKeys() const throw() 00135 { 00136 const IntArray keys (IOKey::Frequency); 00137 return keys; 00138 } 00139 00140 InternalBase* getChannel (const int index) throw() 00141 { 00142 const Inputs channelInputs = this->getInputs().getChannel (index); 00143 return new SawInternal (channelInputs, 00144 this->getState(), 00145 this->getBlockSize(), 00146 this->getSampleRate()); 00147 } 00148 00149 void initChannel (const int channel) throw() 00150 { 00151 const FrequencyUnitType& frequencyUnit = ChannelInternalCore::getInputAs<FrequencyUnitType> (IOKey::Frequency); 00152 00153 this->setBlockSize (BlockSize::decide (frequencyUnit.getBlockSize (channel), 00154 this->getBlockSize())); 00155 this->setSampleRate (SampleRate::decide (frequencyUnit.getSampleRate (channel), 00156 this->getSampleRate())); 00157 00158 this->setOverlap (frequencyUnit.getOverlap (channel)); 00159 00160 this->initValue (this->getState().currentValue); 00161 } 00162 00163 void process (ProcessInfo& info, const int channel) throw() 00164 { 00165 Data& data = this->getState(); 00166 const double sampleDuration = data.base.sampleDuration; 00167 const double factor = data.peak2peak * sampleDuration; 00168 00169 FrequencyUnitType& frequencyUnit = ChannelInternalCore::getInputAs<FrequencyUnitType> (IOKey::Frequency); 00170 00171 const FrequencyBufferType& frequencyBuffer (frequencyUnit.process (info, channel)); 00172 const FrequencyType* const frequencySamples = frequencyBuffer.getArray(); 00173 const int frequencyBufferLength = frequencyBuffer.length(); 00174 00175 SampleType* const outputSamples = this->getOutputSamples(); 00176 const int outputBufferLength = this->getOutputBuffer().length(); 00177 00178 int i; 00179 00180 if (frequencyBufferLength == outputBufferLength) 00181 { 00182 for (i = 0; i < outputBufferLength; ++i) 00183 { 00184 outputSamples[i] = SampleType (data.currentValue); 00185 data.currentValue += frequencySamples[i] * factor; 00186 00187 if (data.currentValue >= data.peak) 00188 data.currentValue -= data.peak2peak; 00189 else if (data.currentValue < -data.peak) 00190 data.currentValue += data.peak2peak; 00191 } 00192 } 00193 else if (frequencyBufferLength == 1) 00194 { 00195 const FrequencyType valueIncrement (frequencySamples[0] * factor); 00196 00197 for (i = 0; i < outputBufferLength; ++i) 00198 { 00199 outputSamples[i] = SampleType (data.currentValue); 00200 data.currentValue += valueIncrement; 00201 00202 if (data.currentValue >= data.peak) 00203 data.currentValue -= data.peak2peak; 00204 else if (data.currentValue < -data.peak) 00205 data.currentValue += data.peak2peak; 00206 } 00207 } 00208 else 00209 { 00210 double frequencyPosition = 0.0; 00211 const double frequencyIncrement = double (frequencyBufferLength) / double (outputBufferLength); 00212 00213 for (i = 0; i < outputBufferLength; ++i) 00214 { 00215 outputSamples[i] = SampleType (data.currentValue); 00216 data.currentValue += frequencySamples[int (frequencyPosition)] * factor; 00217 00218 if (data.currentValue >= data.peak) 00219 data.currentValue -= data.peak2peak; 00220 else if (data.currentValue < -data.peak) 00221 data.currentValue += data.peak2peak; 00222 00223 frequencyPosition += frequencyIncrement; 00224 } 00225 } 00226 } 00227 00228 private: 00229 }; 00230 00231 //------------------------------------------------------------------------------ 00232 00233 #ifdef PLONK_USEPLINK 00234 00235 template<> 00236 class SawChannelInternal<float> : public ChannelInternal<float, SawProcessStateF> 00237 { 00238 public: 00239 typedef SawProcessStateF Data; 00240 typedef InputDictionary Inputs; 00241 typedef ChannelBase<float> ChannelType; 00242 typedef SawChannelInternal<float> SawInternal; 00243 typedef ChannelInternal<float,Data> Internal; 00244 typedef ChannelInternalBase<float> InternalBase; 00245 typedef UnitBase<float> UnitType; 00246 00247 typedef float FrequencyType; 00248 typedef UnitBase<float> FrequencyUnitType; 00249 typedef NumericalArray<float> FrequencyBufferType; 00250 00251 enum Outputs { Output, NumOutputs }; 00252 enum InputIndices { Frequency, NumInputs }; 00253 enum Buffers { OutputBuffer, FrequencyBuffer, NumBuffers }; 00254 00255 typedef PlinkProcess<NumBuffers> Process; 00256 00257 SawChannelInternal (Inputs const& inputs, 00258 Data const& data, 00259 BlockSize const& blockSize, 00260 SampleRate const& sampleRate) throw() 00261 : Internal (inputs, data, blockSize, sampleRate) 00262 { 00263 plonk_staticassert (NumBuffers == (NumInputs + NumOutputs)); 00264 00265 Process::init (&p, this, NumOutputs, NumInputs); 00266 } 00267 00268 Text getName() const throw() 00269 { 00270 return "Saw"; 00271 } 00272 00273 IntArray getInputKeys() const throw() 00274 { 00275 const IntArray keys (IOKey::Frequency); 00276 return keys; 00277 } 00278 00279 InternalBase* getChannel (const int index) throw() 00280 { 00281 const Inputs channelInputs = this->getInputs().getChannel (index); 00282 return new SawInternal (channelInputs, 00283 this->getState(), 00284 this->getBlockSize(), 00285 this->getSampleRate()); 00286 } 00287 00288 void initChannel (const int channel) throw() 00289 { 00290 const FrequencyUnitType& frequencyUnit = ChannelInternalCore::getInputAs<FrequencyUnitType> (IOKey::Frequency); 00291 00292 this->setBlockSize (BlockSize::decide (frequencyUnit.getBlockSize (channel), 00293 this->getBlockSize())); 00294 this->setSampleRate (SampleRate::decide (frequencyUnit.getSampleRate (channel), 00295 this->getSampleRate())); 00296 00297 this->initValue (this->getState().currentValue); 00298 } 00299 00300 void process (ProcessInfo& info, const int channel) throw() 00301 { 00302 FrequencyUnitType& frequencyUnit = ChannelInternalCore::getInputAs<FrequencyUnitType> (IOKey::Frequency); 00303 const FrequencyBufferType& frequencyBuffer (frequencyUnit.process (info, channel)); 00304 00305 p.buffers[0].bufferSize = this->getOutputBuffer().length();; 00306 p.buffers[0].buffer = this->getOutputSamples(); 00307 p.buffers[1].bufferSize = frequencyBuffer.length(); 00308 p.buffers[1].buffer = frequencyBuffer.getArray(); 00309 00310 plink_SawProcessF (&p, &this->getState()); 00311 } 00312 00313 private: 00314 Process p; 00315 }; 00316 00317 #endif 00318 00319 //------------------------------------------------------------------------------ 00320 00335 template<class SampleType> 00336 class SawUnit 00337 { 00338 public: 00339 typedef SawChannelInternal<SampleType> SawInternal; 00340 typedef typename SawInternal::Data Data; 00341 typedef InputDictionary Inputs; 00342 typedef ChannelBase<SampleType> ChannelType; 00343 typedef ChannelInternal<SampleType,Data> Internal; 00344 typedef UnitBase<SampleType> UnitType; 00345 00346 typedef typename SawInternal::FrequencyType FrequencyType; 00347 typedef typename SawInternal::FrequencyUnitType FrequencyUnitType; 00348 typedef typename SawInternal::FrequencyBufferType FrequencyBufferType; 00349 00350 00351 static inline UnitInfos getInfo() throw() 00352 { 00353 const double blockSize = (double)BlockSize::getDefault().getValue(); 00354 const double sampleRate = SampleRate::getDefault().getValue(); 00355 const double peak = (double)TypeUtility<SampleType>::getTypePeak(); // will be innaccurate for LongLong 00356 00357 return UnitInfo ("Saw", "A non-band limited sawtooth wave oscillator.", 00358 00359 // output 00360 ChannelCount::VariableChannelCount, 00361 IOKey::Generic, Measure::None, 0.0, IOLimit::Clipped, Measure::NormalisedBipolar, -peak, peak, 00362 IOKey::End, 00363 00364 // inputs 00365 IOKey::Frequency, Measure::Hertz, 440.0, IOLimit::Clipped, Measure::SampleRateRatio, -0.5, 0.5, 00366 IOKey::Multiply, Measure::Factor, 1.0, IOLimit::None, 00367 IOKey::Add, Measure::None, 0.0, IOLimit::None, 00368 IOKey::BlockSize, Measure::Samples, blockSize, IOLimit::Minimum, Measure::Samples, 1.0, 00369 IOKey::SampleRate, Measure::Hertz, sampleRate, IOLimit::Minimum, Measure::Hertz, 0.0, 00370 IOKey::End); 00371 } 00372 00374 static UnitType ar (FrequencyUnitType const& frequency = FrequencyType (440), 00375 UnitType const& mul = SampleType (1), 00376 UnitType const& add = SampleType (0), 00377 BlockSize const& preferredBlockSize = BlockSize::getDefault(), 00378 SampleRate const& preferredSampleRate = SampleRate::getDefault()) throw() 00379 { 00380 Inputs inputs; 00381 inputs.put (IOKey::Frequency, frequency); 00382 inputs.put (IOKey::Multiply, mul); 00383 inputs.put (IOKey::Add, add); 00384 00385 const LongLong peak = SampleType (TypeUtility<SampleType>::getTypePeak()); 00386 00387 Data data = { { -1.0, -1.0 }, 0, peak, peak * 2 }; 00388 00389 return UnitType::template createFromInputs<SawInternal> (inputs, 00390 data, 00391 preferredBlockSize, 00392 preferredSampleRate); 00393 } 00394 00396 static UnitType kr (FrequencyUnitType const& frequency, 00397 UnitType const& mul = SampleType (1), 00398 UnitType const& add = SampleType (0)) throw() 00399 { 00400 return ar (frequency, mul, add, 00401 BlockSize::getControlRateBlockSize(), 00402 SampleRate::getControlRate()); 00403 } 00404 }; 00405 00406 typedef SawUnit<PLONK_TYPE_DEFAULT> Saw; 00407 00408 00409 00410 00411 #endif // PLONK_SAW_H 00412