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_TRIGGERCHANNEL_H 00040 #define PLONK_TRIGGERCHANNEL_H 00041 00042 #include "../channel/plonk_ChannelInternalCore.h" 00043 #include "../plonk_GraphForwardDeclarations.h" 00044 00045 template<class SampleType> class TriggerChannelInternal; 00046 00047 PLONK_CHANNELDATA_DECLARE(TriggerChannelInternal,SampleType) 00048 { 00049 ChannelInternalCore::Data base; 00050 00051 int durationRemainingInSamples; 00052 SampleType prevValue; 00053 }; 00054 00056 template<class SampleType> 00057 class TriggerChannelInternal 00058 : public ChannelInternal<SampleType, PLONK_CHANNELDATA_NAME(TriggerChannelInternal,SampleType)> 00059 { 00060 public: 00061 typedef PLONK_CHANNELDATA_NAME(TriggerChannelInternal,SampleType) Data; 00062 typedef ChannelBase<SampleType> ChannelType; 00063 typedef TriggerChannelInternal<SampleType> TriggerInternal; 00064 typedef ChannelInternal<SampleType,Data> Internal; 00065 typedef ChannelInternalBase<SampleType> InternalBase; 00066 typedef UnitBase<SampleType> UnitType; 00067 typedef InputDictionary Inputs; 00068 typedef NumericalArray<SampleType> Buffer; 00069 typedef typename TypeUtility<SampleType>::IndexType IndexType; 00070 00071 typedef typename TypeUtility<SampleType>::IndexType DurationType; 00072 typedef UnitBase<DurationType> DurationUnitType; 00073 typedef NumericalArray<DurationType> DurationBufferType; 00074 00075 TriggerChannelInternal (Inputs const& inputs, 00076 Data const& data, 00077 BlockSize const& blockSize, 00078 SampleRate const& sampleRate) throw() 00079 : Internal (inputs, data, blockSize, sampleRate) 00080 { 00081 } 00082 00083 Text getName() const throw() 00084 { 00085 return "Trigger"; 00086 } 00087 00088 IntArray getInputKeys() const throw() 00089 { 00090 const IntArray keys (IOKey::Generic, IOKey::Duration); 00091 return keys; 00092 } 00093 00094 InternalBase* getChannel (const int index) throw() 00095 { 00096 const Inputs channelInputs = this->getInputs().getChannel (index); 00097 return new TriggerInternal (channelInputs, 00098 this->getState(), 00099 this->getBlockSize(), 00100 this->getSampleRate()); 00101 } 00102 00103 void initChannel (const int channel) throw() 00104 { 00105 const UnitType& input = this->getInputAsUnit (IOKey::Generic); 00106 00107 this->setBlockSize (BlockSize::decide (input.getBlockSize (channel), 00108 this->getBlockSize())); 00109 this->setSampleRate (SampleRate::decide (input.getSampleRate (channel), 00110 this->getSampleRate())); 00111 this->setOverlap (input.getOverlap (channel)); 00112 00113 this->initValue (0); 00114 } 00115 00116 void process (ProcessInfo& info, const int channel) throw() 00117 { 00118 Data& data = this->getState(); 00119 00120 UnitType& inputUnit (this->getInputAsUnit (IOKey::Generic)); 00121 const Buffer& inputBuffer (inputUnit.process (info, channel)); 00122 const SampleType* inputSamples = inputBuffer.getArray(); 00123 const int inputBufferLength = inputBuffer.length(); 00124 00125 DurationUnitType& durationUnit = ChannelInternalCore::getInputAs<DurationUnitType> (IOKey::Duration); 00126 const DurationBufferType& durationBuffer (durationUnit.process (info, channel)); 00127 const DurationType* durationSamples = durationBuffer.getArray(); 00128 const int durationBufferLength = durationBuffer.length(); 00129 00130 SampleType* outputSamples = this->getOutputSamples(); 00131 const int outputBufferLength = this->getOutputBuffer().length(); 00132 00133 SampleType prevValue = data.prevValue; 00134 DurationType durationRemainingInSamples = data.durationRemainingInSamples; 00135 00136 const SampleType sampleZero = Math<SampleType>::get0(); 00137 const SampleType sampleOne = Math<SampleType>::get1(); 00138 00139 int i; 00140 00141 if (inputBufferLength == outputBufferLength) 00142 { 00143 if (inputBufferLength == durationBufferLength) 00144 { 00145 for (i = 0; i < outputBufferLength; ++i) 00146 { 00147 const SampleType currValue = inputSamples[i]; 00148 00149 if (prevValue <= sampleZero && currValue > sampleZero) 00150 { 00151 durationRemainingInSamples = plonk::max (1, int (durationSamples[i] * data.base.sampleRate)); 00152 this->update (Text::getMessageTrigger(), Dynamic::getNull()); 00153 } 00154 00155 outputSamples[i] = durationRemainingInSamples > 0 ? sampleOne : sampleZero; 00156 00157 --durationRemainingInSamples; 00158 prevValue = currValue; 00159 } 00160 } 00161 else // if (durationBufferLength == 1) 00162 { 00163 const DurationType durationInSamples = plonk::max (1, int (durationSamples[0] * data.base.sampleRate)); 00164 00165 for (i = 0; i < outputBufferLength; ++i) 00166 { 00167 const SampleType currValue = inputSamples[i]; 00168 00169 if (prevValue <= sampleZero && currValue > sampleZero) 00170 { 00171 durationRemainingInSamples = durationInSamples; 00172 this->update (Text::getMessageTrigger(), Dynamic::getNull()); 00173 } 00174 00175 outputSamples[i] = durationRemainingInSamples > 0 ? sampleOne : sampleZero; 00176 00177 --durationRemainingInSamples; 00178 prevValue = currValue; 00179 } 00180 } 00181 } 00182 else 00183 { 00184 double inputPosition = 0.0; 00185 const double inputIncrement = double (inputBufferLength) / double (outputBufferLength); 00186 00187 double durationPosition = 0.0; 00188 const double durationIncrement = double (durationBufferLength) / double (outputBufferLength); 00189 00190 for (i = 0; i < outputBufferLength; ++i) 00191 { 00192 const SampleType currValue = inputSamples[int (inputPosition)]; 00193 00194 if (prevValue <= sampleZero && currValue > sampleZero) 00195 { 00196 durationRemainingInSamples = plonk::max (1, int (durationSamples[int (durationPosition)] * data.base.sampleRate)); 00197 this->update (Text::getMessageTrigger(), Dynamic::getNull()); 00198 } 00199 00200 outputSamples[i] = durationRemainingInSamples > 0 ? sampleOne : sampleZero; 00201 00202 --durationRemainingInSamples; 00203 inputPosition += inputIncrement; 00204 durationPosition += durationIncrement; 00205 prevValue = currValue; 00206 } 00207 00208 } 00209 00210 // prevent overflow 00211 if (durationRemainingInSamples < 0) 00212 durationRemainingInSamples = 0; 00213 00214 data.prevValue = prevValue; 00215 data.durationRemainingInSamples = durationRemainingInSamples; 00216 } 00217 }; 00218 00219 //------------------------------------------------------------------------------ 00220 00237 template<class SampleType> 00238 class TriggerUnit 00239 { 00240 public: 00241 typedef TriggerChannelInternal<SampleType> TriggerInternal; 00242 typedef typename TriggerInternal::Data Data; 00243 typedef ChannelBase<SampleType> ChannelType; 00244 typedef ChannelInternal<SampleType,Data> Internal; 00245 typedef UnitBase<SampleType> UnitType; 00246 typedef InputDictionary Inputs; 00247 00248 typedef typename TriggerInternal::DurationType DurationType; 00249 typedef typename TriggerInternal::DurationUnitType DurationUnitType; 00250 typedef typename TriggerInternal::DurationBufferType DurationBufferType; 00251 00252 static inline UnitInfos getInfo() throw() 00253 { 00254 const double blockSize = (double)BlockSize::getDefault().getValue(); 00255 const double sampleRate = SampleRate::getDefault().getValue(); 00256 00257 return UnitInfo ("Trigger", "Trigger detects signal transitions from zero or less to greater than zero.", 00258 00259 // output 00260 ChannelCount::VariableChannelCount, 00261 IOKey::Generic, Measure::None, IOInfo::NoDefault, IOLimit::None, IOKey::End, 00262 00263 // inputs 00264 IOKey::Generic, Measure::None, IOInfo::NoDefault, IOLimit::None, 00265 IOKey::Duration, Measure::Seconds, 0.005, IOLimit::Minimum, Measure::Seconds, 0.0, 00266 IOKey::BlockSize, Measure::Samples, blockSize, IOLimit::Minimum, Measure::Samples, 1.0, 00267 IOKey::SampleRate, Measure::Hertz, sampleRate, IOLimit::Minimum, Measure::Hertz, 0.0, 00268 IOKey::End); 00269 } 00270 00272 static UnitType ar (UnitType const& input, 00273 DurationUnitType const& duration = DurationType (0.005), 00274 BlockSize const& preferredBlockSize = BlockSize::getDefault(), 00275 SampleRate const& preferredSampleRate = SampleRate::getDefault()) throw() 00276 { 00277 Inputs inputs; 00278 inputs.put (IOKey::Generic, input); 00279 inputs.put (IOKey::Duration, duration); 00280 00281 Data data = { { -1.0, -1.0 }, 0, 0 }; 00282 00283 return UnitType::template createFromInputs<TriggerInternal> (inputs, 00284 data, 00285 preferredBlockSize, 00286 preferredSampleRate); 00287 } 00288 00289 static inline UnitType kr (UnitType const& input, 00290 DurationUnitType const& duration = DurationType (0.005)) throw() 00291 { 00292 return ar (input, 00293 duration, 00294 BlockSize::getControlRateBlockSize(), 00295 SampleRate::getControlRate()); 00296 } 00297 00298 }; 00299 00300 typedef TriggerUnit<PLONK_TYPE_DEFAULT> Trigger; 00301 00302 00303 00304 #endif // PLONK_TRIGGERCHANNEL_H 00305 00306