diff --git a/Source/Audio/AudioComponent.cpp b/Source/Audio/AudioComponent.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4209abb8974a0662205f536de20ff9ae34f9ab6e --- /dev/null +++ b/Source/Audio/AudioComponent.cpp @@ -0,0 +1,103 @@ +/* + ============================================================================== + + AudioComponent.cpp + Created: 7 May 2011 1:35:05pm + Author: jsiegle + + ============================================================================== +*/ + +#include "AudioComponent.h" +#include <stdio.h> + +AudioComponent::AudioComponent() : isPlaying(false) +{ + + initialise(0, // numInputChannelsNeeded + 2, // numOutputChannelsNeeded + 0, // *savedState (XmlElement) + true, // selectDefaultDeviceOnFailure + String::empty, // preferred device + 0); // preferred device setup options + + AudioIODevice* aIOd = getCurrentAudioDevice(); + + std::cout << "Got audio device." << std::endl; + + String devType = getCurrentAudioDeviceType(); + String devName = aIOd->getName(); + + std::cout << std::endl << "Audio device name: " << devName << std::endl; + + AudioDeviceManager::AudioDeviceSetup setup; + getAudioDeviceSetup(setup); + + setup.bufferSize = 2048; /// larger buffer = fewer empty blocks, but longer latencies + setup.useDefaultInputChannels = false; + setup.inputChannels = 0; + setup.useDefaultOutputChannels = true; + setup.outputChannels = 2; + setup.sampleRate = 44100.0; + + String msg = setAudioDeviceSetup(setup, false); + + devType = getCurrentAudioDeviceType(); + std::cout << "Audio device type: " << devType << std::endl; + + float sr = setup.sampleRate; + int buffSize = setup.bufferSize; + String oDN = setup.outputDeviceName; + BigInteger oC = setup.outputChannels; + + std::cout << "Audio output channels: " << oC.toInteger() << std::endl; + std::cout << "Audio device sample rate: " << sr << std::endl; + std::cout << "Audio device buffer size: " << buffSize << std::endl << std::endl; + + graphPlayer = new AudioProcessorPlayer(); + +} + +AudioComponent::~AudioComponent() { + + if (callbacksAreActive()) + endCallbacks(); + + deleteAndZero(graphPlayer); + +} + +void AudioComponent::connectToProcessorGraph(AudioProcessorGraph* processorGraph) +{ + + graphPlayer->setProcessor(processorGraph); + +} + +void AudioComponent::disconnectProcessorGraph() +{ + + graphPlayer->setProcessor(0); + +} + +bool AudioComponent::callbacksAreActive() { + return isPlaying; +} + +void AudioComponent::beginCallbacks() { + + std::cout << std::endl << "Adding audio callback." << std::endl; + addAudioCallback(graphPlayer); + isPlaying = true; + +} + +void AudioComponent::endCallbacks() { + + std::cout << std::endl << "Removing audio callback." << std::endl; + removeAudioCallback(graphPlayer); + isPlaying = false; + +} + diff --git a/Source/Audio/AudioComponent.h b/Source/Audio/AudioComponent.h new file mode 100644 index 0000000000000000000000000000000000000000..15a6296ed414a7ddea222beca527e0f9de64c1a2 --- /dev/null +++ b/Source/Audio/AudioComponent.h @@ -0,0 +1,50 @@ +/* + ============================================================================== + + AudioComponent.h + Created: 7 May 2011 1:35:05pm + Author: jsiegle + + ============================================================================== +*/ + +#ifndef __AUDIOCOMPONENT_H_D97C73CF__ +#define __AUDIOCOMPONENT_H_D97C73CF__ + +#include "../../JuceLibraryCode/JuceHeader.h" + +class AudioComponent : public AudioDeviceManager { + +public: + AudioComponent(); + ~AudioComponent(); + + void beginCallbacks(); + void endCallbacks(); + + void connectToProcessorGraph(AudioProcessorGraph* processorGraph); + void disconnectProcessorGraph(); + + bool callbacksAreActive(); + +private: + + bool isPlaying; + + AudioProcessorPlayer* graphPlayer; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioComponent); + +}; + + + + + + + + + + +#endif + diff --git a/Source/Dsp/Bessel.cpp b/Source/Dsp/Bessel.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ed2036ab7391776745c2f7b8d69accbc04c04dd4 --- /dev/null +++ b/Source/Dsp/Bessel.cpp @@ -0,0 +1,224 @@ +/******************************************************************************* + +"A Collection of Useful C++ Classes for Digital Signal Processing" + By Vincent Falco + +Official project location: +http://code.google.com/p/dspfilterscpp/ + +See Documentation.cpp for contact information, notes, and bibliography. + +-------------------------------------------------------------------------------- + +License: MIT License (http://www.opensource.org/licenses/mit-license.php) +Copyright (c) 2009 by Vincent Falco + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*******************************************************************************/ + +#include "Common.h" +#include "Bessel.h" +#include "RootFinder.h" + +namespace Dsp { + +namespace Bessel { + +// returns fact(n) = n! +static double fact (int n) +{ + if (n == 0) + return 1; + + double y = n; + for (double m = n; --m;) + y *= m; + + return y; +} + +// returns the k-th zero based coefficient of the reverse bessel polynomial of degree n +static double reversebessel (int k, int n) +{ + return fact (2 * n - k) / + ((fact (n - k) * fact(k)) * pow(2., n - k)); +} + +//------------------------------------------------------------------------------ + +AnalogLowPass::AnalogLowPass () + : m_numPoles (-1) +{ + setNormal (0, 1); +} + +void AnalogLowPass::design (int numPoles, + WorkspaceBase* w) +{ + if (m_numPoles != numPoles) + { + m_numPoles = numPoles; + + reset (); + + RootFinderBase& solver (w->roots); + for (int i = 0; i < numPoles + 1; ++i) + solver.coef()[i] = reversebessel (i, numPoles); + solver.solve (numPoles); + + const int pairs = numPoles / 2; + for (int i = 0; i < pairs; ++i) + { + complex_t c = solver.root()[i]; + addPoleZeroConjugatePairs (c, infinity()); + } + + if (numPoles & 1) + add (solver.root()[pairs].real(), infinity()); + } +} + +//------------------------------------------------------------------------------ + +AnalogLowShelf::AnalogLowShelf () + : m_numPoles (-1) +{ + setNormal (doublePi, 1); +} + +void AnalogLowShelf::design (int numPoles, + double gainDb, + WorkspaceBase* w) +{ + if (m_numPoles != numPoles || + m_gainDb != gainDb) + { + m_numPoles = numPoles; + m_gainDb = gainDb; + + reset (); + + const double G = pow (10., gainDb / 20) - 1; + + RootFinderBase& poles (w->roots); + for (int i = 0; i < numPoles + 1; ++i) + poles.coef()[i] = reversebessel (i, numPoles); + poles.solve (numPoles); + + RootFinder<50> zeros; + for (int i = 0; i < numPoles + 1; ++i) + zeros.coef()[i] = reversebessel (i, numPoles); + double a0 = reversebessel (0, numPoles); + zeros.coef()[0] += G * a0; + zeros.solve (numPoles); + + const int pairs = numPoles / 2; + for (int i = 0; i < pairs; ++i) + { + complex_t p = poles.root()[i]; + complex_t z = zeros.root()[i]; + addPoleZeroConjugatePairs (p, z); + } + + if (numPoles & 1) + add (poles.root()[pairs].real(), zeros.root()[pairs].real()); + } +} + +//------------------------------------------------------------------------------ + +void LowPassBase::setup (int order, + double sampleRate, + double cutoffFrequency, + WorkspaceBase* w) +{ + m_analogProto.design (order, w); + + LowPassTransform (cutoffFrequency / sampleRate, + m_digitalProto, + m_analogProto); + + Cascade::setLayout (m_digitalProto); +} + +void HighPassBase::setup (int order, + double sampleRate, + double cutoffFrequency, + WorkspaceBase* w) +{ + m_analogProto.design (order, w); + + HighPassTransform (cutoffFrequency / sampleRate, + m_digitalProto, + m_analogProto); + + Cascade::setLayout (m_digitalProto); +} + +void BandPassBase::setup (int order, + double sampleRate, + double centerFrequency, + double widthFrequency, + WorkspaceBase* w) +{ + m_analogProto.design (order, w); + + BandPassTransform (centerFrequency / sampleRate, + widthFrequency / sampleRate, + m_digitalProto, + m_analogProto); + + Cascade::setLayout (m_digitalProto); +} + +void BandStopBase::setup (int order, + double sampleRate, + double centerFrequency, + double widthFrequency, + WorkspaceBase* w) +{ + m_analogProto.design (order, w); + + BandStopTransform (centerFrequency / sampleRate, + widthFrequency / sampleRate, + m_digitalProto, + m_analogProto); + + Cascade::setLayout (m_digitalProto); +} + +void LowShelfBase::setup (int order, + double sampleRate, + double cutoffFrequency, + double gainDb, + WorkspaceBase* w) +{ + m_analogProto.design (order, gainDb, w); + + LowPassTransform (cutoffFrequency / sampleRate, + m_digitalProto, + m_analogProto); + + Cascade::setLayout (m_digitalProto); +} + +} + +} diff --git a/Source/Dsp/Bessel.h b/Source/Dsp/Bessel.h new file mode 100644 index 0000000000000000000000000000000000000000..e133cd72c124a5d63d7d0d0d98b4d3dce6e64969 --- /dev/null +++ b/Source/Dsp/Bessel.h @@ -0,0 +1,485 @@ +/******************************************************************************* + +"A Collection of Useful C++ Classes for Digital Signal Processing" + By Vincent Falco + +Official project location: +http://code.google.com/p/dspfilterscpp/ + +See Documentation.cpp for contact information, notes, and bibliography. + +-------------------------------------------------------------------------------- + +License: MIT License (http://www.opensource.org/licenses/mit-license.php) +Copyright (c) 2009 by Vincent Falco + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*******************************************************************************/ + +#ifndef DSPFILTERS_BESSEL_H +#define DSPFILTERS_BESSEL_H + +#include "Common.h" +#include "Cascade.h" +#include "Design.h" +#include "Filter.h" +#include "PoleFilter.h" +#include "RootFinder.h" + +namespace Dsp { + +/* + * Filters with Bessel response characteristics + * + */ + +namespace Bessel { + +// A Workspace is necessary to find roots + +struct WorkspaceBase +{ + WorkspaceBase (RootFinderBase* rootsBase) + : roots (*rootsBase) + { + } + + RootFinderBase& roots; + +private: + WorkspaceBase (WorkspaceBase&); + WorkspaceBase& operator= (WorkspaceBase&); +}; + +template <int MaxOrder> +struct Workspace : WorkspaceBase +{ + Workspace () + : WorkspaceBase (&m_roots) + { + } + +private: + RootFinder <MaxOrder> m_roots; +}; + +//------------------------------------------------------------------------------ + +// Half-band analog prototypes (s-plane) + +class AnalogLowPass : public LayoutBase +{ +public: + AnalogLowPass (); + + void design (const int numPoles, + WorkspaceBase* w); + +private: + int m_numPoles; +}; + +//------------------------------------------------------------------------------ + +class AnalogLowShelf : public LayoutBase +{ +public: + AnalogLowShelf (); + + void design (int numPoles, + double gainDb, + WorkspaceBase* w); + +private: + int m_numPoles; + double m_gainDb; +}; + +//------------------------------------------------------------------------------ + +// Factored implementations to reduce template instantiations + +struct LowPassBase : PoleFilterBase <AnalogLowPass> +{ + void setup (int order, + double sampleRate, + double cutoffFrequency, + WorkspaceBase* w); +}; + +struct HighPassBase : PoleFilterBase <AnalogLowPass> +{ + void setup (int order, + double sampleRate, + double cutoffFrequency, + WorkspaceBase* w); +}; + +struct BandPassBase : PoleFilterBase <AnalogLowPass> +{ + void setup (int order, + double sampleRate, + double centerFrequency, + double widthFrequency, + WorkspaceBase* w); +}; + +struct BandStopBase : PoleFilterBase <AnalogLowPass> +{ + void setup (int order, + double sampleRate, + double centerFrequency, + double widthFrequency, + WorkspaceBase* w); +}; + +struct LowShelfBase : PoleFilterBase <AnalogLowShelf> +{ + void setup (int order, + double sampleRate, + double cutoffFrequency, + double gainDb, + WorkspaceBase* w); +}; + +//------------------------------------------------------------------------------ + +// +// Raw filters +// + +template <int MaxOrder> +struct LowPass : PoleFilter <LowPassBase, MaxOrder> +{ + void setup (int order, + double sampleRate, + double cutoffFrequency) + { + Workspace <MaxOrder> w; + LowPassBase::setup (order, + sampleRate, + cutoffFrequency, + &w); + } +}; + +template <int MaxOrder> +struct HighPass : PoleFilter <HighPassBase, MaxOrder> +{ + void setup (int order, + double sampleRate, + double cutoffFrequency) + { + Workspace <MaxOrder> w; + HighPassBase::setup (order, + sampleRate, + cutoffFrequency, + &w); + } +}; + +template <int MaxOrder> +struct BandPass : PoleFilter <BandPassBase, MaxOrder, MaxOrder*2> +{ + void setup (int order, + double sampleRate, + double centerFrequency, + double widthFrequency) + { + Workspace <MaxOrder> w; + BandPassBase::setup (order, + sampleRate, + centerFrequency, + widthFrequency, + &w); + } +}; + +template <int MaxOrder> +struct BandStop : PoleFilter <BandStopBase, MaxOrder, MaxOrder*2> +{ + void setup (int order, + double sampleRate, + double centerFrequency, + double widthFrequency) + { + Workspace <MaxOrder> w; + BandStopBase::setup (order, + sampleRate, + centerFrequency, + widthFrequency, + &w); + } +}; + +template <int MaxOrder> +struct LowShelf : PoleFilter <LowShelfBase, MaxOrder, MaxOrder*2> +{ + void setup (int order, + double sampleRate, + double cutoffFrequency, + double gainDb) + { + Workspace <MaxOrder> w; + LowShelfBase::setup (order, + sampleRate, + cutoffFrequency, + gainDb, + &w); + } +}; + +//------------------------------------------------------------------------------ + +// +// Gui-friendly Design layer +// + +namespace Design { + +struct TypeIBase : DesignBase +{ + enum + { + NumParams = 3 + }; + + static int getNumParams () + { + return 3; + } + + static const ParamInfo getParamInfo_2 () + { + return ParamInfo::defaultCutoffFrequencyParam (); + } +}; + +template <class FilterClass> +struct TypeI : TypeIBase, FilterClass +{ + void setParams (const Params& params) + { + FilterClass::setup (int(params[1]), params[0], params[2]); + } +}; + +struct TypeIIBase : DesignBase +{ + enum + { + NumParams = 4 + }; + + static int getNumParams () + { + return 4; + } + + static const ParamInfo getParamInfo_2 () + { + return ParamInfo::defaultCenterFrequencyParam (); + } + + static const ParamInfo getParamInfo_3 () + { + return ParamInfo::defaultBandwidthHzParam (); + } +}; + +template <class FilterClass> +struct TypeII : TypeIIBase, FilterClass +{ + void setParams (const Params& params) + { + FilterClass::setup (int(params[1]), params[0], params[2], params[3]); + } +}; + +struct TypeIIIBase : DesignBase +{ + enum + { + NumParams = 4 + }; + + static int getNumParams () + { + return 4; + } + + static const ParamInfo getParamInfo_2 () + { + return ParamInfo::defaultCutoffFrequencyParam (); + } + + static const ParamInfo getParamInfo_3 () + { + return ParamInfo::defaultGainParam (); + } +}; + +template <class FilterClass> +struct TypeIII : TypeIIIBase, FilterClass +{ + void setParams (const Params& params) + { + FilterClass::setup (int(params[1]), + params[0], + params[2], + params[3]); + } +}; + +struct TypeIVBase : DesignBase +{ + enum + { + NumParams = 5 + }; + + static int getNumParams () + { + return 5; + } + + static const ParamInfo getParamInfo_2 () + { + return ParamInfo::defaultCenterFrequencyParam (); + } + + static const ParamInfo getParamInfo_3 () + { + return ParamInfo::defaultBandwidthHzParam (); + } + + static const ParamInfo getParamInfo_4 () + { + return ParamInfo::defaultGainParam (); + } +}; + +template <class FilterClass> +struct TypeIV : TypeIVBase, FilterClass +{ + void setParams (const Params& params) + { + FilterClass::setup (int(params[1]), params[0], params[2], params[3], params[4]); + } +}; + +// Factored kind and name + +struct LowPassDescription +{ + static Kind getKind () { return kindLowPass; } + static const char* getName() { return "Bessel Low Pass"; } +}; + +struct HighPassDescription +{ + static Kind getKind () { return kindHighPass; } + static const char* getName() { return "Bessel High Pass"; } +}; + +struct BandPassDescription +{ + static Kind getKind () { return kindHighPass; } + static const char* getName() { return "Bessel Band Pass"; } +}; + +struct BandStopDescription +{ + static Kind getKind () { return kindHighPass; } + static const char* getName() { return "Bessel Band Stop"; } +}; + +struct LowShelfDescription +{ + static Kind getKind () { return kindLowShelf; } + static const char* getName() { return "Bessel Low Shelf"; } +}; + +// This glues on the Order parameter +template <int MaxOrder, + template <class> class TypeClass, + template <int> class FilterClass> +struct OrderBase : TypeClass <FilterClass <MaxOrder> > +{ + const ParamInfo getParamInfo_1 () const + { + return ParamInfo (idOrder, "Order", "Order", + 1, MaxOrder, 2, + &ParamInfo::Int_toControlValue, + &ParamInfo::Int_toNativeValue, + &ParamInfo::Int_toString); + + } +}; + +//------------------------------------------------------------------------------ + +// +// Gui-friendly Design layer +// + +template <int MaxOrder> +struct LowPass : OrderBase <MaxOrder, TypeI, Bessel::LowPass>, + LowPassDescription +{ +}; + +template <int MaxOrder> +struct HighPass : OrderBase <MaxOrder, TypeI, Bessel::HighPass>, + HighPassDescription +{ +}; + +template <int MaxOrder> +struct BandPass : OrderBase <MaxOrder, TypeII, Bessel::BandPass>, + BandPassDescription +{ +}; + +template <int MaxOrder> +struct BandStop : OrderBase <MaxOrder, TypeII, Bessel::BandStop>, + BandStopDescription +{ +}; + +/* + * NOT IMPLEMENTED + * + */ +template <int MaxOrder> +struct LowShelf : OrderBase <MaxOrder, TypeIII, Bessel::LowShelf>, + LowShelfDescription +{ +}; + +} + +} + +} + +#endif + +/* This is a test of svn:external */ diff --git a/Source/Dsp/Biquad.cpp b/Source/Dsp/Biquad.cpp new file mode 100644 index 0000000000000000000000000000000000000000..16b2b8f77296db2ad5e42de5c18add99c3232974 --- /dev/null +++ b/Source/Dsp/Biquad.cpp @@ -0,0 +1,236 @@ +/******************************************************************************* + +"A Collection of Useful C++ Classes for Digital Signal Processing" + By Vincent Falco + +Official project location: +http://code.google.com/p/dspfilterscpp/ + +See Documentation.cpp for contact information, notes, and bibliography. + +-------------------------------------------------------------------------------- + +License: MIT License (http://www.opensource.org/licenses/mit-license.php) +Copyright (c) 2009 by Vincent Falco + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*******************************************************************************/ + +#include "Common.h" +#include "MathSupplement.h" +#include "Biquad.h" + +namespace Dsp { + +BiquadPoleState::BiquadPoleState (const BiquadBase& s) +{ + const double a0 = s.getA0 (); + const double a1 = s.getA1 (); + const double a2 = s.getA2 (); + const double b0 = s.getB0 (); + const double b1 = s.getB1 (); + const double b2 = s.getB2 (); + + if (a2 == 0 && b2 == 0) + { + // single pole + poles.first = -a1; + zeros.first = -b0 / b1; + poles.second = 0; + zeros.second = 0; + } + else + { + { + const complex_t c = sqrt (complex_t (a1 * a1 - 4 * a0 * a2, 0)); + double d = 2. * a0; + poles.first = -(a1 + c) / d; + poles.second = (c - a1) / d; + assert (!poles.is_nan()); + } + + { + const complex_t c = sqrt (complex_t ( + b1 * b1 - 4 * b0 * b2, 0)); + double d = 2. * b0; + zeros.first = -(b1 + c) / d; + zeros.second = (c - b1) / d; + assert (!zeros.is_nan()); + } + } + + gain = b0 / a0; +} + +//------------------------------------------------------------------------------ + +complex_t BiquadBase::response (double normalizedFrequency) const +{ + const double a0 = getA0 (); + const double a1 = getA1 (); + const double a2 = getA2 (); + const double b0 = getB0 (); + const double b1 = getB1 (); + const double b2 = getB2 (); + + const double w = 2 * doublePi * normalizedFrequency; + const complex_t czn1 = std::polar (1., -w); + const complex_t czn2 = std::polar (1., -2 * w); + complex_t ch (1); + complex_t cbot (1); + + complex_t ct (b0/a0); + complex_t cb (1); + ct = addmul (ct, b1/a0, czn1); + ct = addmul (ct, b2/a0, czn2); + cb = addmul (cb, a1/a0, czn1); + cb = addmul (cb, a2/a0, czn2); + ch *= ct; + cbot *= cb; + + return ch / cbot; +} + +std::vector<PoleZeroPair> BiquadBase::getPoleZeros () const +{ + std::vector<PoleZeroPair> vpz; + BiquadPoleState bps (*this); + vpz.push_back (bps); + return vpz; +} + +void BiquadBase::setCoefficients (double a0, double a1, double a2, + double b0, double b1, double b2) +{ + assert (!Dsp::is_nan (a0) && !Dsp::is_nan (a1) && !Dsp::is_nan (a2) && + !Dsp::is_nan (b0) && !Dsp::is_nan (b1) && !Dsp::is_nan (b2)); + + m_a0 = a0; + m_a1 = a1/a0; + m_a2 = a2/a0; + m_b0 = b0/a0; + m_b1 = b1/a0; + m_b2 = b2/a0; +} + +void BiquadBase::setOnePole (complex_t pole, complex_t zero) +{ +#if 0 + pole = adjust_imag (pole); + zero = adjust_imag (zero); +#else + assert (pole.imag() == 0); + assert (zero.imag() == 0); +#endif + + const double a0 = 1; + const double a1 = -pole.real(); + const double a2 = 0; + const double b0 = -zero.real(); + const double b1 = 1; + const double b2 = 0; + + setCoefficients (a0, a1, a2, b0, b1, b2); +} + +void BiquadBase::setTwoPole (complex_t pole1, complex_t zero1, + complex_t pole2, complex_t zero2) +{ +#if 0 + pole1 = adjust_imag (pole1); + pole2 = adjust_imag (pole2); + zero1 = adjust_imag (zero1); + zero2 = adjust_imag (zero2); +#endif + + const double a0 = 1; + double a1; + double a2; + + if (pole1.imag() != 0) + { + assert (pole2 == std::conj (pole1)); + + a1 = -2 * pole1.real(); + a2 = std::norm (pole1); + } + else + { + assert (pole2.imag() == 0); + + a1 = -(pole1.real() + pole2.real()); + a2 = pole1.real() * pole2.real(); + } + + const double b0 = 1; + double b1; + double b2; + + if (zero1.imag() != 0) + { + assert (zero2 == std::conj (zero1)); + + b1 = -2 * zero1.real(); + b2 = std::norm (zero1); + } + else + { + assert (zero2.imag() == 0); + + b1 = -(zero1.real() + zero2.real()); + b2 = zero1.real() * zero2.real(); + } + + setCoefficients (a0, a1, a2, b0, b1, b2); +} + +void BiquadBase::setPoleZeroForm (const BiquadPoleState& bps) +{ + setPoleZeroPair (bps); + applyScale (bps.gain); +} + +void BiquadBase::setIdentity () +{ + setCoefficients (1, 0, 0, 1, 0, 0); +} + +void BiquadBase::applyScale (double scale) +{ + m_b0 *= scale; + m_b1 *= scale; + m_b2 *= scale; +} + +//------------------------------------------------------------------------------ + +Biquad::Biquad () +{ +} + +// Construct a second order section from a pair of poles and zeroes +Biquad::Biquad (const BiquadPoleState& bps) +{ + setPoleZeroForm (bps); +} + +//------------------------------------------------------------------------------ + +} diff --git a/Source/Dsp/Biquad.h b/Source/Dsp/Biquad.h new file mode 100644 index 0000000000000000000000000000000000000000..5946b5fe331b8490b850c6ef23925567ba6ab078 --- /dev/null +++ b/Source/Dsp/Biquad.h @@ -0,0 +1,232 @@ +/******************************************************************************* + +"A Collection of Useful C++ Classes for Digital Signal Processing" + By Vincent Falco + +Official project location: +http://code.google.com/p/dspfilterscpp/ + +See Documentation.cpp for contact information, notes, and bibliography. + +-------------------------------------------------------------------------------- + +License: MIT License (http://www.opensource.org/licenses/mit-license.php) +Copyright (c) 2009 by Vincent Falco + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*******************************************************************************/ + +#ifndef DSPFILTERS_BIQUAD_H +#define DSPFILTERS_BIQUAD_H + +#include "Common.h" +#include "MathSupplement.h" +#include "Types.h" + +namespace Dsp { + +struct BiquadPoleState; + +/* + * Holds coefficients for a second order Infinite Impulse Response + * digital filter. This is the building block for all IIR filters. + * + */ + +// Factored interface to prevent outsiders from fiddling +class BiquadBase +{ +public: + template <class StateType> + struct State : StateType, private DenormalPrevention + { + template <typename Sample> + inline Sample process (const Sample in, const BiquadBase& b) + { + return static_cast<Sample> (StateType::process1 (in, b, ac())); + } + }; + +public: + // Calculate filter response at the given normalized frequency. + complex_t response (double normalizedFrequency) const; + + std::vector<PoleZeroPair> getPoleZeros () const; + + double getA0 () const { return m_a0; } + double getA1 () const { return m_a1*m_a0; } + double getA2 () const { return m_a2*m_a0; } + double getB0 () const { return m_b0*m_a0; } + double getB1 () const { return m_b1*m_a0; } + double getB2 () const { return m_b2*m_a0; } + + // Process a block of samples in the given form + template <class StateType, typename Sample> + void process (int numSamples, Sample* dest, StateType& state) const + { + while (--numSamples >= 0) + *dest++ = state.process (*dest, *this); + } + +protected: + // + // These are protected so you can't mess with RBJ biquads + // + + void setCoefficients (double a0, double a1, double a2, + double b0, double b1, double b2); + + void setOnePole (complex_t pole, complex_t zero); + + void setTwoPole (complex_t pole1, complex_t zero1, + complex_t pole2, complex_t zero2); + + void setPoleZeroPair (const PoleZeroPair& pair) + { + if (pair.isSinglePole ()) + setOnePole (pair.poles.first, pair.zeros.first); + else + setTwoPole (pair.poles.first, pair.zeros.first, + pair.poles.second, pair.zeros.second); + } + + void setPoleZeroForm (const BiquadPoleState& bps); + + void setIdentity (); + + void applyScale (double scale); + +public: + double m_a0; + double m_a1; + double m_a2; + double m_b1; + double m_b2; + double m_b0; +}; + +//------------------------------------------------------------------------------ + +// Expresses a biquad as a pair of pole/zeros, with gain +// values so that the coefficients can be reconstructed precisely. +struct BiquadPoleState : PoleZeroPair +{ + BiquadPoleState () { } + + explicit BiquadPoleState (const BiquadBase& s); + + double gain; +}; + +// More permissive interface for fooling around +class Biquad : public BiquadBase +{ +public: + Biquad (); + + explicit Biquad (const BiquadPoleState& bps); + +public: + // Process a block of samples, interpolating from the old section's coefficients + // to this section's coefficients, over numSamples. This implements smooth + // parameter changes. + + template <class StateType, typename Sample> + void smoothProcess1 (int numSamples, + Sample* dest, + StateType& state, + Biquad sectionPrev) const + { + double t = 1. / numSamples; + double da1 = (m_a1 - sectionPrev.m_a1) * t; + double da2 = (m_a2 - sectionPrev.m_a2) * t; + double db0 = (m_b0 - sectionPrev.m_b0) * t; + double db1 = (m_b1 - sectionPrev.m_b1) * t; + double db2 = (m_b2 - sectionPrev.m_b2) * t; + + while (--numSamples >= 0) + { + sectionPrev.m_a1 += da1; + sectionPrev.m_a2 += da2; + sectionPrev.m_b0 += db0; + sectionPrev.m_b1 += db1; + sectionPrev.m_b2 += db2; + + *dest++ = state.process (*dest, sectionPrev); + } + } + + // Process a block of samples, interpolating from the old section's pole/zeros + // to this section's pole/zeros, over numSamples. The interpolation is done + // in the z-plane using polar coordinates. + template <class StateType, typename Sample> + void smoothProcess2 (int numSamples, + Sample* dest, + StateType& state, + BiquadPoleState zPrev) const + { + BiquadPoleState z (*this); + double t = 1. / numSamples; + complex_t dp0 = (z.poles.first - zPrev.poles.first) * t; + complex_t dp1 = (z.poles.second - zPrev.poles.second) * t; + complex_t dz0 = (z.zeros.first - zPrev.zeros.first) * t; + complex_t dz1 = (z.zeros.second - zPrev.zeros.second) * t; + double dg = (z.gain - zPrev.gain) * t; + + while (--numSamples >= 0) + { + zPrev.poles.first += dp0; + zPrev.poles.second += dp1; + zPrev.zeros.first += dz0; + zPrev.zeros.second += dz1; + zPrev.gain += dg; + + *dest++ = state.process (*dest, Biquad (zPrev)); + } + } + +public: + // Export these as public + + void setOnePole (complex_t pole, complex_t zero) + { + BiquadBase::setOnePole (pole, zero); + } + + void setTwoPole (complex_t pole1, complex_t zero1, + complex_t pole2, complex_t zero2) + { + BiquadBase::setTwoPole (pole1, zero1, pole2, zero2); + } + + void setPoleZeroPair (const PoleZeroPair& pair) + { + BiquadBase::setPoleZeroPair (pair); + } + + void applyScale (double scale) + { + BiquadBase::applyScale (scale); + } +}; + +} + +#endif diff --git a/Source/Dsp/Butterworth.cpp b/Source/Dsp/Butterworth.cpp new file mode 100644 index 0000000000000000000000000000000000000000..26113a8b461f6597d277ffaea97bb6b4c377ecca --- /dev/null +++ b/Source/Dsp/Butterworth.cpp @@ -0,0 +1,212 @@ +/******************************************************************************* + +"A Collection of Useful C++ Classes for Digital Signal Processing" + By Vincent Falco + +Official project location: +http://code.google.com/p/dspfilterscpp/ + +See Documentation.cpp for contact information, notes, and bibliography. + +-------------------------------------------------------------------------------- + +License: MIT License (http://www.opensource.org/licenses/mit-license.php) +Copyright (c) 2009 by Vincent Falco + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*******************************************************************************/ + +#include "Common.h" +#include "Butterworth.h" + +namespace Dsp { + +namespace Butterworth { + +AnalogLowPass::AnalogLowPass () + : m_numPoles (-1) +{ + setNormal (0, 1); +} + +void AnalogLowPass::design (int numPoles) +{ + if (m_numPoles != numPoles) + { + m_numPoles = numPoles; + + reset (); + + const double n2 = 2 * numPoles; + const int pairs = numPoles / 2; + for (int i = 0; i < pairs; ++i) + { + complex_t c = std::polar (1., doublePi_2 + (2 * i + 1) * doublePi / n2); + addPoleZeroConjugatePairs (c, infinity()); + } + + if (numPoles & 1) + add (-1, infinity()); + } +} + +//------------------------------------------------------------------------------ + +AnalogLowShelf::AnalogLowShelf () + : m_numPoles (-1) +{ + setNormal (doublePi, 1); +} + +void AnalogLowShelf::design (int numPoles, double gainDb) +{ + if (m_numPoles != numPoles || + m_gainDb != gainDb) + { + m_numPoles = numPoles; + m_gainDb = gainDb; + + reset (); + + const double n2 = numPoles * 2; + const double g = pow (pow (10., gainDb/20), 1. / n2); + const double gp = -1. / g; + const double gz = -g; + + const int pairs = numPoles / 2; + for (int i = 1; i <= pairs; ++i) + { + const double theta = doublePi * (0.5 - (2 * i - 1) / n2); + addPoleZeroConjugatePairs (std::polar (gp, theta), std::polar (gz, theta)); + } + + if (numPoles & 1) + add (gp, gz); + } +} + +//------------------------------------------------------------------------------ + +void LowPassBase::setup (int order, + double sampleRate, + double cutoffFrequency) +{ + m_analogProto.design (order); + + LowPassTransform (cutoffFrequency / sampleRate, + m_digitalProto, + m_analogProto); + + Cascade::setLayout (m_digitalProto); +} + +void HighPassBase::setup (int order, + double sampleRate, + double cutoffFrequency) +{ + m_analogProto.design (order); + + HighPassTransform (cutoffFrequency / sampleRate, + m_digitalProto, + m_analogProto); + + Cascade::setLayout (m_digitalProto); +} + +void BandPassBase::setup (int order, + double sampleRate, + double centerFrequency, + double widthFrequency) +{ + m_analogProto.design (order); + + BandPassTransform (centerFrequency / sampleRate, + widthFrequency / sampleRate, + m_digitalProto, + m_analogProto); + + Cascade::setLayout (m_digitalProto); +} + +void BandStopBase::setup (int order, + double sampleRate, + double centerFrequency, + double widthFrequency) +{ + m_analogProto.design (order); + + BandStopTransform (centerFrequency / sampleRate, + widthFrequency / sampleRate, + m_digitalProto, + m_analogProto); + + Cascade::setLayout (m_digitalProto); +} + +void LowShelfBase::setup (int order, + double sampleRate, + double cutoffFrequency, + double gainDb) +{ + m_analogProto.design (order, gainDb); + + LowPassTransform (cutoffFrequency / sampleRate, + m_digitalProto, + m_analogProto); + + Cascade::setLayout (m_digitalProto); +} + +void HighShelfBase::setup (int order, + double sampleRate, + double cutoffFrequency, + double gainDb) +{ + m_analogProto.design (order, gainDb); + + HighPassTransform (cutoffFrequency / sampleRate, + m_digitalProto, + m_analogProto); + + Cascade::setLayout (m_digitalProto); +} + +void BandShelfBase::setup (int order, + double sampleRate, + double centerFrequency, + double widthFrequency, + double gainDb) +{ + m_analogProto.design (order, gainDb); + + BandPassTransform (centerFrequency / sampleRate, + widthFrequency / sampleRate, + m_digitalProto, + m_analogProto); + + // HACK! + m_digitalProto.setNormal (((centerFrequency/sampleRate) < 0.25) ? doublePi : 0, 1); + + Cascade::setLayout (m_digitalProto); +} + +} + +} diff --git a/Source/Dsp/Butterworth.h b/Source/Dsp/Butterworth.h new file mode 100644 index 0000000000000000000000000000000000000000..da222eae1251df02c45db24c58a5470d75092ac7 --- /dev/null +++ b/Source/Dsp/Butterworth.h @@ -0,0 +1,436 @@ +/******************************************************************************* + +"A Collection of Useful C++ Classes for Digital Signal Processing" + By Vincent Falco + +Official project location: +http://code.google.com/p/dspfilterscpp/ + +See Documentation.cpp for contact information, notes, and bibliography. + +-------------------------------------------------------------------------------- + +License: MIT License (http://www.opensource.org/licenses/mit-license.php) +Copyright (c) 2009 by Vincent Falco + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*******************************************************************************/ + +#ifndef DSPFILTERS_BUTTERWORTH_H +#define DSPFILTERS_BUTTERWORTH_H + +#include "Common.h" +#include "Cascade.h" +#include "Design.h" +#include "Filter.h" +#include "PoleFilter.h" + +namespace Dsp { + +/* + * Filters with Butterworth response characteristics + * + */ + +namespace Butterworth { + +// Half-band analog prototypes (s-plane) + +class AnalogLowPass : public LayoutBase +{ +public: + AnalogLowPass (); + + void design (const int numPoles); + +private: + int m_numPoles; +}; + +//------------------------------------------------------------------------------ + +class AnalogLowShelf : public LayoutBase +{ +public: + AnalogLowShelf (); + + void design (int numPoles, double gainDb); + +private: + int m_numPoles; + double m_gainDb; +}; + +//------------------------------------------------------------------------------ + +// Factored implementations to reduce template instantiations + +struct LowPassBase : PoleFilterBase <AnalogLowPass> +{ + void setup (int order, + double sampleRate, + double cutoffFrequency); +}; + +struct HighPassBase : PoleFilterBase <AnalogLowPass> +{ + void setup (int order, + double sampleRate, + double cutoffFrequency); +}; + +struct BandPassBase : PoleFilterBase <AnalogLowPass> +{ + void setup (int order, + double sampleRate, + double centerFrequency, + double widthFrequency); +}; + +struct BandStopBase : PoleFilterBase <AnalogLowPass> +{ + void setup (int order, + double sampleRate, + double centerFrequency, + double widthFrequency); +}; + +struct LowShelfBase : PoleFilterBase <AnalogLowShelf> +{ + void setup (int order, + double sampleRate, + double cutoffFrequency, + double gainDb); +}; + +struct HighShelfBase : PoleFilterBase <AnalogLowShelf> +{ + void setup (int order, + double sampleRate, + double cutoffFrequency, + double gainDb); +}; + +struct BandShelfBase : PoleFilterBase <AnalogLowShelf> +{ + void setup (int order, + double sampleRate, + double centerFrequency, + double widthFrequency, + double gainDb); +}; + +//------------------------------------------------------------------------------ + +// +// Raw filters +// + +template <int MaxOrder> +struct LowPass : PoleFilter <LowPassBase, MaxOrder> +{ +}; + +template <int MaxOrder> +struct HighPass : PoleFilter <HighPassBase, MaxOrder> +{ +}; + +template <int MaxOrder> +struct BandPass : PoleFilter <BandPassBase, MaxOrder, MaxOrder*2> +{ +}; + +template <int MaxOrder> +struct BandStop : PoleFilter <BandStopBase, MaxOrder, MaxOrder*2> +{ +}; + +template <int MaxOrder> +struct LowShelf : PoleFilter <LowShelfBase, MaxOrder> +{ +}; + +template <int MaxOrder> +struct HighShelf : PoleFilter <HighShelfBase, MaxOrder> +{ +}; + +template <int MaxOrder> +struct BandShelf : PoleFilter <BandShelfBase, MaxOrder, MaxOrder*2> +{ +}; + +//------------------------------------------------------------------------------ + +// +// Gui-friendly Design layer +// + +namespace Design { + +struct TypeIBase : DesignBase +{ + enum + { + NumParams = 3 + }; + + static int getNumParams () + { + return 3; + } + + static const ParamInfo getParamInfo_2 () + { + return ParamInfo::defaultCutoffFrequencyParam (); + } +}; + +template <class FilterClass> +struct TypeI : TypeIBase, FilterClass +{ + void setParams (const Params& params) + { + FilterClass::setup (int(params[1]), params[0], params[2]); + } +}; + +struct TypeIIBase : DesignBase +{ + enum + { + NumParams = 4 + }; + + static int getNumParams () + { + return 4; + } + + static const ParamInfo getParamInfo_2 () + { + return ParamInfo::defaultCenterFrequencyParam (); + } + + static const ParamInfo getParamInfo_3 () + { + return ParamInfo::defaultBandwidthHzParam (); + } +}; + +template <class FilterClass> +struct TypeII : TypeIIBase, FilterClass +{ + void setParams (const Params& params) + { + FilterClass::setup (int(params[1]), params[0], params[2], params[3]); + } +}; + +struct TypeIIIBase : DesignBase +{ + enum + { + NumParams = 4 + }; + + static int getNumParams () + { + return 4; + } + + static const ParamInfo getParamInfo_2 () + { + return ParamInfo::defaultCutoffFrequencyParam (); + } + + static const ParamInfo getParamInfo_3 () + { + return ParamInfo::defaultGainParam (); + } +}; + +template <class FilterClass> +struct TypeIII : TypeIIIBase, FilterClass +{ + void setParams (const Params& params) + { + FilterClass::setup (int(params[1]), + params[0], + params[2], + params[3]); + } +}; + +struct TypeIVBase : DesignBase +{ + enum + { + NumParams = 5 + }; + + static int getNumParams () + { + return 5; + } + + static const ParamInfo getParamInfo_2 () + { + return ParamInfo::defaultCenterFrequencyParam (); + } + + static const ParamInfo getParamInfo_3 () + { + return ParamInfo::defaultBandwidthHzParam (); + } + + static const ParamInfo getParamInfo_4 () + { + return ParamInfo::defaultGainParam (); + } +}; + +template <class FilterClass> +struct TypeIV : TypeIVBase, FilterClass +{ + void setParams (const Params& params) + { + FilterClass::setup (int(params[1]), params[0], params[2], params[3], params[4]); + } +}; + +// Factored kind and name + +struct LowPassDescription +{ + static Kind getKind () { return kindLowPass; } + static const char* getName() { return "Butterworth Low Pass"; } +}; + +struct HighPassDescription +{ + static Kind getKind () { return kindHighPass; } + static const char* getName() { return "Butterworth High Pass"; } +}; + +struct BandPassDescription +{ + static Kind getKind () { return kindHighPass; } + static const char* getName() { return "Butterworth Band Pass"; } +}; + +struct BandStopDescription +{ + static Kind getKind () { return kindHighPass; } + static const char* getName() { return "Butterworth Band Stop"; } +}; + +struct LowShelfDescription +{ + static Kind getKind () { return kindLowShelf; } + static const char* getName() { return "Butterworth Low Shelf"; } +}; + +struct HighShelfDescription +{ + static Kind getKind () { return kindHighShelf; } + static const char* getName() { return "Butterworth High Shelf"; } +}; + +struct BandShelfDescription +{ + static Kind getKind () { return kindBandShelf; } + static const char* getName() { return "Butterworth Band Shelf"; } +}; + +// This glues on the Order parameter +template <int MaxOrder, + template <class> class TypeClass, + template <int> class FilterClass> +struct OrderBase : TypeClass <FilterClass <MaxOrder> > +{ + const ParamInfo getParamInfo_1 () const + { + return ParamInfo (idOrder, "Order", "Order", + 1, MaxOrder, 2, + &ParamInfo::Int_toControlValue, + &ParamInfo::Int_toNativeValue, + &ParamInfo::Int_toString); + + } +}; + +//------------------------------------------------------------------------------ + +// +// Design filters +// + +template <int MaxOrder> +struct LowPass : OrderBase <MaxOrder, TypeI, Butterworth::LowPass>, + LowPassDescription +{ +}; + +template <int MaxOrder> +struct HighPass : OrderBase <MaxOrder, TypeI, Butterworth::HighPass>, + HighPassDescription +{ +}; + +template <int MaxOrder> +struct BandPass : OrderBase <MaxOrder, TypeII, Butterworth::BandPass>, + BandPassDescription +{ +}; + +template <int MaxOrder> +struct BandStop : OrderBase <MaxOrder, TypeII, Butterworth::BandStop>, + BandStopDescription +{ +}; + +template <int MaxOrder> +struct LowShelf : OrderBase <MaxOrder, TypeIII, Butterworth::LowShelf>, + LowShelfDescription +{ +}; + +template <int MaxOrder> +struct HighShelf : OrderBase <MaxOrder, TypeIII, Butterworth::HighShelf>, + HighShelfDescription +{ +}; + +template <int MaxOrder> +struct BandShelf : OrderBase <MaxOrder, TypeIV, Butterworth::BandShelf>, + BandShelfDescription +{ +}; + +} + +} + +} + +#endif + diff --git a/Source/Dsp/Cascade.cpp b/Source/Dsp/Cascade.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1efa7ecc982eb9819524637296522989b6c3dc2a --- /dev/null +++ b/Source/Dsp/Cascade.cpp @@ -0,0 +1,118 @@ +/******************************************************************************* + +"A Collection of Useful C++ Classes for Digital Signal Processing" + By Vincent Falco + +Official project location: +http://code.google.com/p/dspfilterscpp/ + +See Documentation.cpp for contact information, notes, and bibliography. + +-------------------------------------------------------------------------------- + +License: MIT License (http://www.opensource.org/licenses/mit-license.php) +Copyright (c) 2009 by Vincent Falco + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*******************************************************************************/ + +#include "Common.h" +#include "Cascade.h" + +namespace Dsp { + +Cascade::Cascade () + : m_numStages (0) + , m_maxStages (0) + , m_stageArray (0) +{ +} + +void Cascade::setCascadeStorage (const Storage& storage) +{ + m_numStages = 0; + m_maxStages = storage.maxStages; + m_stageArray = storage.stageArray; +} + +complex_t Cascade::response (double normalizedFrequency) const +{ + double w = 2 * doublePi * normalizedFrequency; + const complex_t czn1 = std::polar (1., -w); + const complex_t czn2 = std::polar (1., -2 * w); + complex_t ch (1); + complex_t cbot (1); + + const Biquad* stage = m_stageArray; + for (int i = m_numStages; --i >=0; ++stage) + { + complex_t cb (1); + complex_t ct (stage->getB0()/stage->getA0()); + ct = addmul (ct, stage->getB1()/stage->getA0(), czn1); + ct = addmul (ct, stage->getB2()/stage->getA0(), czn2); + cb = addmul (cb, stage->getA1()/stage->getA0(), czn1); + cb = addmul (cb, stage->getA2()/stage->getA0(), czn2); + ch *= ct; + cbot *= cb; + } + + return ch / cbot; +} + +std::vector<PoleZeroPair> Cascade::getPoleZeros () const +{ + std::vector<PoleZeroPair> vpz; + vpz.reserve (m_numStages); + + const Stage* stage = m_stageArray; + for (int i = m_numStages; --i >=0;) + { + BiquadPoleState bps (*stage++); + assert (!bps.isSinglePole() || i == 0); + vpz.push_back (bps); + } + + return vpz; +} + +void Cascade::applyScale (double scale) +{ + // For higher order filters it might be helpful + // to spread this factor between all the stages. + assert (m_numStages > 0); + m_stageArray->applyScale (scale); +} + +void Cascade::setLayout (const LayoutBase& proto) +{ + const int numPoles = proto.getNumPoles(); + m_numStages = (numPoles + 1)/ 2; + assert (m_numStages <= m_maxStages); + + Biquad* stage = m_stageArray; + for (int i = 0; i < m_numStages; ++i, ++stage) + stage->setPoleZeroPair (proto[i]); + + applyScale (proto.getNormalGain() / + std::abs (response (proto.getNormalW() / (2 * doublePi)))); +} + +} + diff --git a/Source/Dsp/Cascade.h b/Source/Dsp/Cascade.h new file mode 100644 index 0000000000000000000000000000000000000000..9f44ded539d5c278415c740f635b33246c4b4227 --- /dev/null +++ b/Source/Dsp/Cascade.h @@ -0,0 +1,181 @@ +/******************************************************************************* + +"A Collection of Useful C++ Classes for Digital Signal Processing" + By Vincent Falco + +Official project location: +http://code.google.com/p/dspfilterscpp/ + +See Documentation.cpp for contact information, notes, and bibliography. + +-------------------------------------------------------------------------------- + +License: MIT License (http://www.opensource.org/licenses/mit-license.php) +Copyright (c) 2009 by Vincent Falco + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*******************************************************************************/ + +#ifndef DSPFILTERS_CASCADE_H +#define DSPFILTERS_CASCADE_H + +#include "Common.h" +#include "Biquad.h" +#include "Filter.h" +#include "Layout.h" +#include "MathSupplement.h" + +namespace Dsp { + +/* + * Holds coefficients for a cascade of second order sections. + * + */ + +// Factored implementation to reduce template instantiations +class Cascade +{ +public: + template <class StateType> + class StateBase : private DenormalPrevention + { + public: + template <typename Sample> + inline Sample process (const Sample in, const Cascade& c) + { + double out = in; + StateType* state = m_stateArray; + Biquad const* stage = c.m_stageArray; + const double vsa = ac(); + int i = c.m_numStages - 1; + out = (state++)->process1 (out, *stage++, vsa); + for (; --i >= 0;) + out = (state++)->process1 (out, *stage++, 0); + //for (int i = c.m_numStages; --i >= 0; ++state, ++stage) + // out = state->process1 (out, *stage, vsa); + return static_cast<Sample> (out); + } + + protected: + StateBase (StateType* stateArray) + : m_stateArray (stateArray) + { + } + + protected: + StateType* m_stateArray; + }; + + struct Stage : Biquad + { + }; + + struct Storage + { + Storage (int maxStages_, Stage* stageArray_) + : maxStages (maxStages_) + , stageArray (stageArray_) + { + } + + int maxStages; + Stage* stageArray; + }; + + int getNumStages () const + { + return m_numStages; + } + + const Stage& operator[] (int index) + { + assert (index >= 0 && index <= m_numStages); + return m_stageArray[index]; + } + +public: + // Calculate filter response at the given normalized frequency. + complex_t response (double normalizedFrequency) const; + + std::vector<PoleZeroPair> getPoleZeros () const; + + // Process a block of samples in the given form + template <class StateType, typename Sample> + void process (int numSamples, Sample* dest, StateType& state) const + { + while (--numSamples >= 0) + *dest++ = state.process (*dest, *this); + } + +protected: + Cascade (); + + void setCascadeStorage (const Storage& storage); + + void applyScale (double scale); + void setLayout (const LayoutBase& proto); + +private: + int m_numStages; + int m_maxStages; + Stage* m_stageArray; +}; + +//------------------------------------------------------------------------------ + +// Storage for Cascade +template <int MaxStages> +class CascadeStages +{ +public: + template <class StateType> + class State : public Cascade::StateBase <StateType> + { + public: + State() : Cascade::StateBase <StateType> (m_states) + { + Cascade::StateBase <StateType>::m_stateArray = m_states; + reset (); + } + + void reset () + { + StateType* state = m_states; + for (int i = MaxStages; --i >= 0; ++state) + state->reset(); + } + + private: + StateType m_states[MaxStages]; + }; + + /*@Internal*/ + Cascade::Storage getCascadeStorage() + { + return Cascade::Storage (MaxStages, m_stages); + } + +private: + Cascade::Stage m_stages[MaxStages]; +}; + +} + +#endif diff --git a/Source/Dsp/ChebyshevI.cpp b/Source/Dsp/ChebyshevI.cpp new file mode 100644 index 0000000000000000000000000000000000000000..06fb2b454f491181ab7941fb08aaf33fde505e11 --- /dev/null +++ b/Source/Dsp/ChebyshevI.cpp @@ -0,0 +1,272 @@ +/******************************************************************************* + +"A Collection of Useful C++ Classes for Digital Signal Processing" + By Vincent Falco + +Official project location: +http://code.google.com/p/dspfilterscpp/ + +See Documentation.cpp for contact information, notes, and bibliography. + +-------------------------------------------------------------------------------- + +License: MIT License (http://www.opensource.org/licenses/mit-license.php) +Copyright (c) 2009 by Vincent Falco + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*******************************************************************************/ + +#include "Common.h" +#include "ChebyshevI.h" + +namespace Dsp { + +namespace ChebyshevI { + +AnalogLowPass::AnalogLowPass () + : m_numPoles (-1) +{ +} + +void AnalogLowPass::design (int numPoles, + double rippleDb) +{ + if (m_numPoles != numPoles || + m_rippleDb != rippleDb) + { + m_numPoles = numPoles; + m_rippleDb = rippleDb; + + reset (); + + const double eps = std::sqrt (1. / std::exp (-rippleDb * 0.1 * doubleLn10) - 1); + const double v0 = asinh (1 / eps) / numPoles; + const double sinh_v0 = -sinh (v0); + const double cosh_v0 = cosh (v0); + + const double n2 = 2 * numPoles; + const int pairs = numPoles / 2; + for (int i = 0; i < pairs; ++i) + { + const int k = 2 * i + 1 - numPoles; + double a = sinh_v0 * cos (k * doublePi / n2); + double b = cosh_v0 * sin (k * doublePi / n2); + + //addPoleZero (complex_t (a, b), infinity()); + //addPoleZero (complex_t (a, -b), infinity()); + addPoleZeroConjugatePairs (complex_t (a, b), infinity()); + } + + if (numPoles & 1) + { + add (complex_t (sinh_v0, 0), infinity()); + setNormal (0, 1); + } + else + { + setNormal (0, pow (10, -rippleDb/20.)); + } + } +} + +//------------------------------------------------------------------------------ + +// +// Chebyshev Type I low pass shelf prototype +// From "High-Order Digital Parametric Equalizer Design" +// Sophocles J. Orfanidis +// http://www.ece.rutgers.edu/~orfanidi/ece521/hpeq.pdf +// + +AnalogLowShelf::AnalogLowShelf () +{ + setNormal (doublePi, 1); +} + +void AnalogLowShelf::design (int numPoles, + double gainDb, + double rippleDb) +{ + if (m_numPoles != numPoles || + m_rippleDb != rippleDb || + m_gainDb != gainDb) + { + m_numPoles = numPoles; + m_rippleDb = rippleDb; + m_gainDb = gainDb; + + reset (); + + gainDb = -gainDb; + + if (rippleDb >= abs(gainDb)) + rippleDb = abs (gainDb); + if (gainDb<0) + rippleDb = -rippleDb; + + const double G = std::pow (10., gainDb / 20.0 ); + const double Gb = std::pow (10., (gainDb - rippleDb) / 20.0); + const double G0 = 1; + const double g0 = pow (G0 , 1. / numPoles); + + double eps; + if (Gb != G0 ) + eps = sqrt((G*G-Gb*Gb)/(Gb*Gb-G0*G0)); + else + eps = G-1; // This is surely wrong + + const double b = pow (G/eps+Gb*sqrt(1+1/(eps*eps)), 1./numPoles); + const double u = log (b / g0); + const double v = log (pow (1. / eps+sqrt(1+1/(eps*eps)),1./numPoles)); + + const double sinh_u = sinh (u); + const double sinh_v = sinh (v); + const double cosh_u = cosh (u); + const double cosh_v = cosh (v); + const double n2 = 2 * numPoles; + const int pairs = numPoles / 2; + for (int i = 1; i <= pairs; ++i) + { + const double a = doublePi * (2 * i - 1) / n2; + const double sn = sin (a); + const double cs = cos (a); + addPoleZeroConjugatePairs (complex_t (-sn * sinh_u, cs * cosh_u), + complex_t (-sn * sinh_v, cs * cosh_v)); + } + + if (numPoles & 1) + add (-sinh_u, -sinh_v); + } +} + +//------------------------------------------------------------------------------ + +void LowPassBase::setup (int order, + double sampleRate, + double cutoffFrequency, + double rippleDb) +{ + m_analogProto.design (order, rippleDb); + + LowPassTransform (cutoffFrequency / sampleRate, + m_digitalProto, + m_analogProto); + + Cascade::setLayout (m_digitalProto); +} + +void HighPassBase::setup (int order, + double sampleRate, + double cutoffFrequency, + double rippleDb) +{ + m_analogProto.design (order, rippleDb); + + HighPassTransform (cutoffFrequency / sampleRate, + m_digitalProto, + m_analogProto); + + Cascade::setLayout (m_digitalProto); +} + +void BandPassBase::setup (int order, + double sampleRate, + double centerFrequency, + double widthFrequency, + double rippleDb) +{ + m_analogProto.design (order, rippleDb); + + BandPassTransform (centerFrequency / sampleRate, + widthFrequency / sampleRate, + m_digitalProto, + m_analogProto); + + Cascade::setLayout (m_digitalProto); +} + +void BandStopBase::setup (int order, + double sampleRate, + double centerFrequency, + double widthFrequency, + double rippleDb) +{ + m_analogProto.design (order, rippleDb); + + BandStopTransform (centerFrequency / sampleRate, + widthFrequency / sampleRate, + m_digitalProto, + m_analogProto); + + Cascade::setLayout (m_digitalProto); +} + +void LowShelfBase::setup (int order, + double sampleRate, + double cutoffFrequency, + double gainDb, + double rippleDb) +{ + m_analogProto.design (order, gainDb, rippleDb); + + LowPassTransform (cutoffFrequency / sampleRate, + m_digitalProto, + m_analogProto); + + Cascade::setLayout (m_digitalProto); +} + +void HighShelfBase::setup (int order, + double sampleRate, + double cutoffFrequency, + double gainDb, + double rippleDb) +{ + m_analogProto.design (order, gainDb, rippleDb); + + HighPassTransform (cutoffFrequency / sampleRate, + m_digitalProto, + m_analogProto); + + Cascade::setLayout (m_digitalProto); +} + +void BandShelfBase::setup (int order, + double sampleRate, + double centerFrequency, + double widthFrequency, + double gainDb, + double rippleDb) +{ + m_analogProto.design (order, gainDb, rippleDb); + + BandPassTransform (centerFrequency / sampleRate, + widthFrequency / sampleRate, + m_digitalProto, + m_analogProto); + + m_digitalProto.setNormal (((centerFrequency/sampleRate) < 0.25) ? doublePi : 0, 1); + + Cascade::setLayout (m_digitalProto); +} + +} + +} diff --git a/Source/Dsp/ChebyshevI.h b/Source/Dsp/ChebyshevI.h new file mode 100644 index 0000000000000000000000000000000000000000..791105ca21bfa21da06b697e025be933cea8e80e --- /dev/null +++ b/Source/Dsp/ChebyshevI.h @@ -0,0 +1,465 @@ +/******************************************************************************* + +"A Collection of Useful C++ Classes for Digital Signal Processing" + By Vincent Falco + +Official project location: +http://code.google.com/p/dspfilterscpp/ + +See Documentation.cpp for contact information, notes, and bibliography. + +-------------------------------------------------------------------------------- + +License: MIT License (http://www.opensource.org/licenses/mit-license.php) +Copyright (c) 2009 by Vincent Falco + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*******************************************************************************/ + +#ifndef DSPFILTERS_CHEBYSHEVI_H +#define DSPFILTERS_CHEBYSHEVI_H + +#include "Common.h" +#include "Cascade.h" +#include "Design.h" +#include "Filter.h" +#include "PoleFilter.h" + +namespace Dsp { + +/* + * Filters with Chebyshev response characteristics + * + */ + +namespace ChebyshevI { + +// Half-band analog prototypes (s-plane) + +class AnalogLowPass : public LayoutBase +{ +public: + AnalogLowPass (); + + void design (const int numPoles, + double rippleDb); + +private: + int m_numPoles; + double m_rippleDb; +}; + +//------------------------------------------------------------------------------ + +class AnalogLowShelf : public LayoutBase +{ +public: + AnalogLowShelf (); + + void design (int numPoles, + double gainDb, + double rippleDb); + +private: + int m_numPoles; + double m_rippleDb; + double m_gainDb; +}; + +//------------------------------------------------------------------------------ + +// Factored implementations to reduce template instantiations + +struct LowPassBase : PoleFilterBase <AnalogLowPass> +{ + void setup (int order, + double sampleRate, + double cutoffFrequency, + double rippleDb); +}; + +struct HighPassBase : PoleFilterBase <AnalogLowPass> +{ + void setup (int order, + double sampleRate, + double cutoffFrequency, + double rippleDb); +}; + +struct BandPassBase : PoleFilterBase <AnalogLowPass> +{ + void setup (int order, + double sampleRate, + double centerFrequency, + double widthFrequency, + double rippleDb); +}; + +struct BandStopBase : PoleFilterBase <AnalogLowPass> +{ + void setup (int order, + double sampleRate, + double centerFrequency, + double widthFrequency, + double rippleDb); +}; + +struct LowShelfBase : PoleFilterBase <AnalogLowShelf> +{ + void setup (int order, + double sampleRate, + double cutoffFrequency, + double gainDb, + double rippleDb); +}; + +struct HighShelfBase : PoleFilterBase <AnalogLowShelf> +{ + void setup (int order, + double sampleRate, + double cutoffFrequency, + double gainDb, + double rippleDb); +}; + +struct BandShelfBase : PoleFilterBase <AnalogLowShelf> +{ + void setup (int order, + double sampleRate, + double centerFrequency, + double widthFrequency, + double gainDb, + double rippleDb); +}; + +//------------------------------------------------------------------------------ + +// +// Raw filters +// + +template <int MaxOrder> +struct LowPass : PoleFilter <LowPassBase, MaxOrder> +{ +}; + +template <int MaxOrder> +struct HighPass : PoleFilter <HighPassBase, MaxOrder> +{ +}; + +template <int MaxOrder> +struct BandPass : PoleFilter <BandPassBase, MaxOrder, MaxOrder*2> +{ +}; + +template <int MaxOrder> +struct BandStop : PoleFilter <BandStopBase, MaxOrder, MaxOrder*2> +{ +}; + +template <int MaxOrder> +struct LowShelf : PoleFilter <LowShelfBase, MaxOrder> +{ +}; + +template <int MaxOrder> +struct HighShelf : PoleFilter <HighShelfBase, MaxOrder> +{ +}; + +template <int MaxOrder> +struct BandShelf : PoleFilter <BandShelfBase, MaxOrder, MaxOrder*2> +{ +}; + +//------------------------------------------------------------------------------ + +// +// Gui-friendly Design layer +// + +namespace Design { + +struct TypeIBase : DesignBase +{ + enum + { + NumParams = 4 + }; + + static int getNumParams () + { + return 4; + } + + static const ParamInfo getParamInfo_2 () + { + return ParamInfo::defaultCutoffFrequencyParam (); + } + + static const ParamInfo getParamInfo_3 () + { + return ParamInfo::defaultRippleDbParam (); + } +}; + +template <class FilterClass> +struct TypeI : TypeIBase, FilterClass +{ + void setParams (const Params& params) + { + FilterClass::setup (int(params[1]), params[0], params[2], params[3]); + } +}; + +struct TypeIIBase : DesignBase +{ + enum + { + NumParams = 5 + }; + + static int getNumParams () + { + return 5; + } + + static const ParamInfo getParamInfo_2 () + { + return ParamInfo::defaultCenterFrequencyParam (); + } + + static const ParamInfo getParamInfo_3 () + { + return ParamInfo::defaultBandwidthHzParam (); + } + + static const ParamInfo getParamInfo_4 () + { + return ParamInfo::defaultRippleDbParam (); + } +}; + +template <class FilterClass> +struct TypeII : TypeIIBase, FilterClass +{ + void setParams (const Params& params) + { + FilterClass::setup (int(params[1]), params[0], params[2], params[3], params[4]); + } +}; + +struct TypeIIIBase : DesignBase +{ + enum + { + NumParams = 5 + }; + + static int getNumParams () + { + return 5; + } + + static const ParamInfo getParamInfo_2 () + { + return ParamInfo::defaultCutoffFrequencyParam (); + } + + static const ParamInfo getParamInfo_3 () + { + return ParamInfo::defaultGainParam (); + } + + static const ParamInfo getParamInfo_4 () + { + return ParamInfo::defaultRippleDbParam (); + } +}; + +template <class FilterClass> +struct TypeIII : TypeIIIBase, FilterClass +{ + void setParams (const Params& params) + { + FilterClass::setup (int(params[1]), params[0], params[2], params[3], params[4]); + } +}; + +struct TypeIVBase : DesignBase +{ + enum + { + NumParams = 6 + }; + + static int getNumParams () + { + return 6; + } + + static const ParamInfo getParamInfo_2 () + { + return ParamInfo::defaultCenterFrequencyParam (); + } + + static const ParamInfo getParamInfo_3 () + { + return ParamInfo::defaultBandwidthHzParam (); + } + + static const ParamInfo getParamInfo_4 () + { + return ParamInfo::defaultGainParam (); + } + + static const ParamInfo getParamInfo_5 () + { + return ParamInfo::defaultRippleDbParam (); + } +}; + +template <class FilterClass> +struct TypeIV : TypeIVBase, FilterClass +{ + void setParams (const Params& params) + { + FilterClass::setup (int(params[1]), params[0], params[2], params[3], params[4], params[5]); + } +}; + +// Factored kind and name + +struct LowPassDescription +{ + static Kind getKind () { return kindLowPass; } + static const char* getName() { return "Chebyshev I Low Pass"; } +}; + +struct HighPassDescription +{ + static Kind getKind () { return kindHighPass; } + static const char* getName() { return "Chebyshev I High Pass"; } +}; + +struct BandPassDescription +{ + static Kind getKind () { return kindHighPass; } + static const char* getName() { return "Chebyshev I Band Pass"; } +}; + +struct BandStopDescription +{ + static Kind getKind () { return kindHighPass; } + static const char* getName() { return "Chebyshev I Band Stop"; } +}; + +struct LowShelfDescription +{ + static Kind getKind () { return kindLowShelf; } + static const char* getName() { return "Chebyshev I Low Shelf"; } +}; + +struct HighShelfDescription +{ + static Kind getKind () { return kindHighShelf; } + static const char* getName() { return "Chebyshev I High Shelf"; } +}; + +struct BandShelfDescription +{ + static Kind getKind () { return kindBandShelf; } + static const char* getName() { return "Chebyshev I Band Shelf"; } +}; + +// This glues on the Order parameter +template <int MaxOrder, + template <class> class TypeClass, + template <int> class FilterClass> +struct OrderBase : TypeClass <FilterClass <MaxOrder> > +{ + const ParamInfo getParamInfo_1 () const + { + return ParamInfo (idOrder, "Order", "Order", + 1, MaxOrder, 2, + &ParamInfo::Int_toControlValue, + &ParamInfo::Int_toNativeValue, + &ParamInfo::Int_toString); + + } +}; + +//------------------------------------------------------------------------------ + +// +// Design filters +// + +template <int MaxOrder> +struct LowPass : OrderBase <MaxOrder, TypeI, ChebyshevI::LowPass>, + LowPassDescription +{ +}; + +template <int MaxOrder> +struct HighPass : OrderBase <MaxOrder, TypeI, ChebyshevI::HighPass>, + HighPassDescription +{ +}; + +template <int MaxOrder> +struct BandPass : OrderBase <MaxOrder, TypeII, ChebyshevI::BandPass>, + BandPassDescription +{ +}; + +template <int MaxOrder> +struct BandStop : OrderBase <MaxOrder, TypeII, ChebyshevI::BandStop>, + BandStopDescription +{ +}; + +template <int MaxOrder> +struct LowShelf : OrderBase <MaxOrder, TypeIII, ChebyshevI::LowShelf>, + LowShelfDescription +{ +}; + +template <int MaxOrder> +struct HighShelf : OrderBase <MaxOrder, TypeIII, ChebyshevI::HighShelf>, + HighShelfDescription +{ +}; + +template <int MaxOrder> +struct BandShelf : OrderBase <MaxOrder, TypeIV, ChebyshevI::BandShelf>, + BandShelfDescription +{ +}; + +} + +} + +} + +#endif + diff --git a/Source/Dsp/ChebyshevII.cpp b/Source/Dsp/ChebyshevII.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fc482ccd80f6f2bf91a8e6a643c8b2ecbe09e0e9 --- /dev/null +++ b/Source/Dsp/ChebyshevII.cpp @@ -0,0 +1,271 @@ +/******************************************************************************* + +"A Collection of Useful C++ Classes for Digital Signal Processing" + By Vincent Falco + +Official project location: +http://code.google.com/p/dspfilterscpp/ + +See Documentation.cpp for contact information, notes, and bibliography. + +-------------------------------------------------------------------------------- + +License: MIT License (http://www.opensource.org/licenses/mit-license.php) +Copyright (c) 2009 by Vincent Falco + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*******************************************************************************/ + +#include "Common.h" +#include "ChebyshevII.h" + +namespace Dsp { + +namespace ChebyshevII { + +// "Chebyshev Filter Properties" +// http://cnx.org/content/m16906/latest/ + +AnalogLowPass::AnalogLowPass () + : m_numPoles (-1) +{ + setNormal (0, 1); +} + +void AnalogLowPass::design (int numPoles, + double stopBandDb) +{ + if (m_numPoles != numPoles || + m_stopBandDb != stopBandDb) + { + m_numPoles = numPoles; + m_stopBandDb = stopBandDb; + + reset (); + + const double eps = std::sqrt (1. / (std::exp (stopBandDb * 0.1 * doubleLn10) - 1)); + const double v0 = asinh (1 / eps) / numPoles; + const double sinh_v0 = -sinh (v0); + const double cosh_v0 = cosh (v0); + const double fn = doublePi / (2 * numPoles); + + int k = 1; + for (int i = numPoles / 2; --i >= 0; k+=2) + { + const double a = sinh_v0 * cos ((k - numPoles) * fn); + const double b = cosh_v0 * sin ((k - numPoles) * fn); + const double d2 = a * a + b * b; + const double im = 1 / cos (k * fn); + addPoleZeroConjugatePairs (complex_t (a / d2, b / d2), + complex_t (0, im)); + } + + if (numPoles & 1) + { + add (1 / sinh_v0, infinity()); + } + } +} + +//------------------------------------------------------------------------------ + +// +// Chebyshev Type I low pass shelf prototype +// From "High-Order Digital Parametric Equalizer Design" +// Sophocles J. Orfanidis +// http://www.ece.rutgers.edu/~orfanidi/ece521/hpeq.pdf +// + +AnalogLowShelf::AnalogLowShelf () + : m_numPoles (-1) +{ + setNormal (doublePi, 1); +} + +void AnalogLowShelf::design (int numPoles, + double gainDb, + double stopBandDb) +{ + if (m_numPoles != numPoles || + m_stopBandDb != stopBandDb || + m_gainDb != gainDb) + { + m_numPoles = numPoles; + m_stopBandDb = stopBandDb; + m_gainDb = gainDb; + + reset (); + + gainDb = -gainDb; + + if (stopBandDb >= abs(gainDb)) + stopBandDb = abs (gainDb); + if (gainDb<0) + stopBandDb = -stopBandDb; + + const double G = std::pow (10., gainDb / 20.0 ); + const double Gb = std::pow (10., (gainDb - stopBandDb) / 20.0); + const double G0 = 1; + const double g0 = pow (G0 , 1. / numPoles); + + double eps; + if (Gb != G0 ) + eps = sqrt((G*G-Gb*Gb)/(Gb*Gb-G0*G0)); + else + eps = G-1; // This is surely wrong + + const double b = pow (G/eps+Gb*sqrt(1+1/(eps*eps)), 1./numPoles); + const double u = log (b / g0); + const double v = log (pow (1. / eps+sqrt(1+1/(eps*eps)),1./numPoles)); + + const double sinh_u = sinh (u); + const double sinh_v = sinh (v); + const double cosh_u = cosh (u); + const double cosh_v = cosh (v); + const double n2 = 2 * numPoles; + const int pairs = numPoles / 2; + for (int i = 1; i <= pairs; ++i) + { + const double a = doublePi * (2 * i - 1) / n2; + const double sn = sin (a); + const double cs = cos (a); + addPoleZeroConjugatePairs (complex_t (-sn * sinh_u, cs * cosh_u), + complex_t (-sn * sinh_v, cs * cosh_v)); + } + + if (numPoles & 1) + add (-sinh_u, -sinh_v); + } +} + +//------------------------------------------------------------------------------ + +void LowPassBase::setup (int order, + double sampleRate, + double cutoffFrequency, + double stopBandDb) +{ + m_analogProto.design (order, stopBandDb); + + LowPassTransform (cutoffFrequency / sampleRate, + m_digitalProto, + m_analogProto); + + Cascade::setLayout (m_digitalProto); +} + +void HighPassBase::setup (int order, + double sampleRate, + double cutoffFrequency, + double stopBandDb) +{ + m_analogProto.design (order, stopBandDb); + + HighPassTransform (cutoffFrequency / sampleRate, + m_digitalProto, + m_analogProto); + + Cascade::setLayout (m_digitalProto); +} + +void BandPassBase::setup (int order, + double sampleRate, + double centerFrequency, + double widthFrequency, + double stopBandDb) +{ + m_analogProto.design (order, stopBandDb); + + BandPassTransform (centerFrequency / sampleRate, + widthFrequency / sampleRate, + m_digitalProto, + m_analogProto); + + Cascade::setLayout (m_digitalProto); +} + +void BandStopBase::setup (int order, + double sampleRate, + double centerFrequency, + double widthFrequency, + double stopBandDb) +{ + m_analogProto.design (order, stopBandDb); + + BandStopTransform (centerFrequency / sampleRate, + widthFrequency / sampleRate, + m_digitalProto, + m_analogProto); + + Cascade::setLayout (m_digitalProto); +} + +void LowShelfBase::setup (int order, + double sampleRate, + double cutoffFrequency, + double gainDb, + double stopBandDb) +{ + m_analogProto.design (order, gainDb, stopBandDb); + + LowPassTransform (cutoffFrequency / sampleRate, + m_digitalProto, + m_analogProto); + + Cascade::setLayout (m_digitalProto); +} + +void HighShelfBase::setup (int order, + double sampleRate, + double cutoffFrequency, + double gainDb, + double stopBandDb) +{ + m_analogProto.design (order, gainDb, stopBandDb); + + HighPassTransform (cutoffFrequency / sampleRate, + m_digitalProto, + m_analogProto); + + Cascade::setLayout (m_digitalProto); +} + +void BandShelfBase::setup (int order, + double sampleRate, + double centerFrequency, + double widthFrequency, + double gainDb, + double stopBandDb) +{ + m_analogProto.design (order, gainDb, stopBandDb); + + BandPassTransform (centerFrequency / sampleRate, + widthFrequency / sampleRate, + m_digitalProto, + m_analogProto); + + m_digitalProto.setNormal (((centerFrequency/sampleRate) < 0.25) ? doublePi : 0, 1); + + Cascade::setLayout (m_digitalProto); +} + +} + +} diff --git a/Source/Dsp/ChebyshevII.h b/Source/Dsp/ChebyshevII.h new file mode 100644 index 0000000000000000000000000000000000000000..62fbcba86a2931d3da8c9f026a7ab120733b90c4 --- /dev/null +++ b/Source/Dsp/ChebyshevII.h @@ -0,0 +1,466 @@ +/******************************************************************************* + +"A Collection of Useful C++ Classes for Digital Signal Processing" + By Vincent Falco + +Official project location: +http://code.google.com/p/dspfilterscpp/ + +See Documentation.cpp for contact information, notes, and bibliography. + +-------------------------------------------------------------------------------- + +License: MIT License (http://www.opensource.org/licenses/mit-license.php) +Copyright (c) 2009 by Vincent Falco + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*******************************************************************************/ + +#ifndef DSPFILTERS_CHEBYSHEVII_H +#define DSPFILTERS_CHEBYSHEVII_H + +#include "Common.h" +#include "Cascade.h" +#include "Design.h" +#include "Filter.h" +#include "PoleFilter.h" + +namespace Dsp { + +/* + * Filters with Inverse Chebyshev response characteristics + * + */ + +namespace ChebyshevII { + +// Half-band analog prototypes (s-plane) + +class AnalogLowPass : public LayoutBase +{ +public: + AnalogLowPass (); + + void design (const int numPoles, + double stopBandDb); + +private: + int m_numPoles; + double m_stopBandDb; +}; + +//------------------------------------------------------------------------------ + +class AnalogLowShelf : public LayoutBase +{ +public: + AnalogLowShelf (); + + void design (int numPoles, + double gainDb, + double stopBandDb); + +private: + int m_numPoles; + double m_stopBandDb; + double m_gainDb; +}; + +//------------------------------------------------------------------------------ + +// Factored implementations to reduce template instantiations + +struct LowPassBase : PoleFilterBase <AnalogLowPass> +{ + void setup (int order, + double sampleRate, + double cutoffFrequency, + double stopBandDb); +}; + +struct HighPassBase : PoleFilterBase <AnalogLowPass> +{ + void setup (int order, + double sampleRate, + double cutoffFrequency, + double stopBandDb); +}; + +struct BandPassBase : PoleFilterBase <AnalogLowPass> +{ + void setup (int order, + double sampleRate, + double centerFrequency, + double widthFrequency, + double stopBandDb); +}; + +struct BandStopBase : PoleFilterBase <AnalogLowPass> +{ + void setup (int order, + double sampleRate, + double centerFrequency, + double widthFrequency, + double stopBandDb); +}; + +struct LowShelfBase : PoleFilterBase <AnalogLowShelf> +{ + void setup (int order, + double sampleRate, + double cutoffFrequency, + double gainDb, + double stopBandDb); +}; + +struct HighShelfBase : PoleFilterBase <AnalogLowShelf> +{ + void setup (int order, + double sampleRate, + double cutoffFrequency, + double gainDb, + double stopBandDb); +}; + +struct BandShelfBase : PoleFilterBase <AnalogLowShelf> +{ + void setup (int order, + double sampleRate, + double centerFrequency, + double widthFrequency, + double gainDb, + double stopBandDb); +}; + +//------------------------------------------------------------------------------ + +// +// Raw filters +// + +template <int MaxOrder> +struct LowPass : PoleFilter <LowPassBase, MaxOrder> +{ +}; + +template <int MaxOrder> +struct HighPass : PoleFilter <HighPassBase, MaxOrder> +{ +}; + +template <int MaxOrder> +struct BandPass : PoleFilter <BandPassBase, MaxOrder, MaxOrder*2> +{ +}; + +template <int MaxOrder> +struct BandStop : PoleFilter <BandStopBase, MaxOrder, MaxOrder*2> +{ +}; + +template <int MaxOrder> +struct LowShelf : PoleFilter <LowShelfBase, MaxOrder> +{ +}; + +template <int MaxOrder> +struct HighShelf : PoleFilter <HighShelfBase, MaxOrder> +{ +}; + +template <int MaxOrder> +struct BandShelf : PoleFilter <BandShelfBase, MaxOrder, MaxOrder*2> +{ +}; + +//------------------------------------------------------------------------------ + +// +// Gui-friendly Design layer +// + +namespace Design { + +struct TypeIBase : DesignBase +{ + enum + { + NumParams = 4 + }; + + static int getNumParams () + { + return 4; + } + + static const ParamInfo getParamInfo_2 () + { + return ParamInfo::defaultCutoffFrequencyParam (); + } + + static const ParamInfo getParamInfo_3 () + { + return ParamInfo::defaultStopDbParam (); + } +}; + +template <class FilterClass> +struct TypeI : TypeIBase, FilterClass +{ + void setParams (const Params& params) + { + FilterClass::setup (int(params[1]), params[0], params[2], params[3]); + } +}; + +struct TypeIIBase : DesignBase +{ + enum + { + NumParams = 5 + }; + + static int getNumParams () + { + return 5; + } + + static const ParamInfo getParamInfo_2 () + { + return ParamInfo::defaultCenterFrequencyParam (); + } + + static const ParamInfo getParamInfo_3 () + { + return ParamInfo::defaultBandwidthHzParam (); + } + + static const ParamInfo getParamInfo_4 () + { + return ParamInfo::defaultStopDbParam (); + } +}; + +template <class FilterClass> +struct TypeII : TypeIIBase, FilterClass +{ + void setParams (const Params& params) + { + FilterClass::setup (int(params[1]), params[0], params[2], params[3], params[4]); + } +}; + +struct TypeIIIBase : DesignBase +{ + enum + { + NumParams = 5 + }; + + static int getNumParams () + { + return 5; + } + + static const ParamInfo getParamInfo_2 () + { + return ParamInfo::defaultCutoffFrequencyParam (); + } + + static const ParamInfo getParamInfo_3 () + { + return ParamInfo::defaultGainParam (); + } + + static const ParamInfo getParamInfo_4 () + { + return ParamInfo::defaultStopDbParam (); + } +}; + +template <class FilterClass> +struct TypeIII : TypeIIIBase, FilterClass +{ + void setParams (const Params& params) + { + FilterClass::setup (int(params[1]), params[0], params[2], params[3], params[4]); + } +}; + +struct TypeIVBase : DesignBase +{ + enum + { + NumParams = 6 + }; + + static int getNumParams () + { + return 6; + } + + static const ParamInfo getParamInfo_2 () + { + return ParamInfo::defaultCenterFrequencyParam (); + } + + static const ParamInfo getParamInfo_3 () + { + return ParamInfo::defaultBandwidthHzParam (); + } + + static const ParamInfo getParamInfo_4 () + { + return ParamInfo::defaultGainParam (); + } + + static const ParamInfo getParamInfo_5 () + { + return ParamInfo::defaultStopDbParam (); + } +}; + +template <class FilterClass> +struct TypeIV : TypeIVBase, FilterClass +{ + void setParams (const Params& params) + { + FilterClass::setup (int(params[1]), params[0], params[2], params[3], params[4], params[5]); + } +}; + +// Factored kind and name + +struct LowPassDescription +{ + static Kind getKind () { return kindLowPass; } + static const char* getName() { return "Chebyshev II Low Pass"; } +}; + +struct HighPassDescription +{ + static Kind getKind () { return kindHighPass; } + static const char* getName() { return "Chebyshev II High Pass"; } +}; + +struct BandPassDescription +{ + static Kind getKind () { return kindHighPass; } + static const char* getName() { return "Chebyshev II Band Pass"; } +}; + +struct BandStopDescription +{ + static Kind getKind () { return kindHighPass; } + static const char* getName() { return "Chebyshev II Band Stop"; } +}; + +struct LowShelfDescription +{ + static Kind getKind () { return kindLowShelf; } + static const char* getName() { return "Chebyshev II Low Shelf"; } +}; + +struct HighShelfDescription +{ + static Kind getKind () { return kindHighShelf; } + static const char* getName() { return "Chebyshev II High Shelf"; } +}; + +struct BandShelfDescription +{ + static Kind getKind () { return kindBandShelf; } + static const char* getName() { return "Chebyshev II Band Shelf"; } +}; + +// This glues on the Order parameter +template <int MaxOrder, + template <class> class TypeClass, + template <int> class FilterClass> +struct OrderBase : TypeClass <FilterClass <MaxOrder> > +{ + const ParamInfo getParamInfo_1 () const + { + return ParamInfo (idOrder, "Order", "Order", + 1, MaxOrder, 2, + &ParamInfo::Int_toControlValue, + &ParamInfo::Int_toNativeValue, + &ParamInfo::Int_toString); + + } +}; + +//------------------------------------------------------------------------------ + +// +// Design Filters +// + +template <int MaxOrder> +struct LowPass : OrderBase <MaxOrder, TypeI, ChebyshevII::LowPass>, + LowPassDescription +{ +}; + +template <int MaxOrder> +struct HighPass : OrderBase <MaxOrder, TypeI, ChebyshevII::HighPass>, + HighPassDescription +{ +}; + +template <int MaxOrder> +struct BandPass : OrderBase <MaxOrder, TypeII, ChebyshevII::BandPass>, + BandPassDescription +{ +}; + +template <int MaxOrder> +struct BandStop : OrderBase <MaxOrder, TypeII, ChebyshevII::BandStop>, + BandStopDescription +{ +}; + +template <int MaxOrder> +struct LowShelf : OrderBase <MaxOrder, TypeIII, ChebyshevII::LowShelf>, + LowShelfDescription +{ +}; + +template <int MaxOrder> +struct HighShelf : OrderBase <MaxOrder, TypeIII, ChebyshevII::HighShelf>, + HighShelfDescription +{ +}; + +template <int MaxOrder> +struct BandShelf : OrderBase <MaxOrder, TypeIV, ChebyshevII::BandShelf>, + BandShelfDescription +{ +}; + + +} + +} + +} + +#endif + diff --git a/Source/Dsp/Common.h b/Source/Dsp/Common.h new file mode 100644 index 0000000000000000000000000000000000000000..738197481bfd58b8564e9354855ecbe64af30eba --- /dev/null +++ b/Source/Dsp/Common.h @@ -0,0 +1,66 @@ +/******************************************************************************* + +"A Collection of Useful C++ Classes for Digital Signal Processing" + By Vincent Falco + +Official project location: +http://code.google.com/p/dspfilterscpp/ + +See Documentation.cpp for contact information, notes, and bibliography. + +-------------------------------------------------------------------------------- + +License: MIT License (http://www.opensource.org/licenses/mit-license.php) +Copyright (c) 2009 by Vincent Falco + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*******************************************************************************/ + +#ifndef DSPFILTERS_COMMON_H +#define DSPFILTERS_COMMON_H + +// +// This must be the first file included in every DspFilters header and source +// + +#ifdef _MSC_VER +# pragma warning (disable: 4100) +#endif + +//#include <assert.h> +#include <stdlib.h> + +#include <cassert> +#include <cfloat> +#include <cmath> +#include <complex> +#include <cstring> +#include <string> +#include <limits> +#include <vector> + +#ifdef _MSC_VER +namespace tr1 = std::tr1; +#else +namespace tr1 = std; +#endif + + +#endif diff --git a/Source/Dsp/Custom.cpp b/Source/Dsp/Custom.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f3139e5dd8661a87f6879d09a58d5847383c16c3 --- /dev/null +++ b/Source/Dsp/Custom.cpp @@ -0,0 +1,66 @@ +/******************************************************************************* + +"A Collection of Useful C++ Classes for Digital Signal Processing" + By Vincent Falco + +Official project location: +http://code.google.com/p/dspfilterscpp/ + +See Documentation.cpp for contact information, notes, and bibliography. + +-------------------------------------------------------------------------------- + +License: MIT License (http://www.opensource.org/licenses/mit-license.php) +Copyright (c) 2009 by Vincent Falco + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*******************************************************************************/ + +#include "Common.h" +#include "Custom.h" + +namespace Dsp { + +namespace Custom { + +void OnePole::setup (double scale, + double pole, + double zero) +{ + setOnePole (pole, zero); + applyScale (scale); +} + +void TwoPole::setup (double scale, + double poleRho, + double poleTheta, + double zeroRho, + double zeroTheta) +{ + complex_t pole = std::polar (poleRho, poleTheta); + complex_t zero = std::polar (zeroRho, zeroTheta); + + setTwoPole (pole, zero, std::conj(pole), std::conj(zero)); + applyScale (scale); +} + +} + +} diff --git a/Source/Dsp/Custom.h b/Source/Dsp/Custom.h new file mode 100644 index 0000000000000000000000000000000000000000..24d4ade000a58706cce28121d0e0bac73c3573e9 --- /dev/null +++ b/Source/Dsp/Custom.h @@ -0,0 +1,177 @@ +/******************************************************************************* + +"A Collection of Useful C++ Classes for Digital Signal Processing" + By Vincent Falco + +Official project location: +http://code.google.com/p/dspfilterscpp/ + +See Documentation.cpp for contact information, notes, and bibliography. + +-------------------------------------------------------------------------------- + +License: MIT License (http://www.opensource.org/licenses/mit-license.php) +Copyright (c) 2009 by Vincent Falco + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*******************************************************************************/ + +#ifndef DSPFILTERS_CUSTOM_H +#define DSPFILTERS_CUSTOM_H + +#include "Common.h" +#include "Biquad.h" +#include "Design.h" +#include "Filter.h" + +namespace Dsp { + +/* + * Single pole and Biquad with parameters allowing + * for directly setting the poles and zeros + * + */ + +namespace Custom { + +// +// Raw filters +// + +struct OnePole : Biquad +{ + void setup (double scale, + double pole, + double zero); +}; + +struct TwoPole : Biquad +{ + void setup (double scale, + double poleRho, + double poleTheta, + double zeroRho, + double zeroTheta); +}; + +//------------------------------------------------------------------------------ + +// +// Gui-friendly Design layer +// + +namespace Design { + +struct OnePole : DesignBase, Custom::OnePole +{ + enum + { + NumParams = 4 + }; + + static int getNumParams () + { + return 4; + } + + static const ParamInfo getParamInfo_1 () + { + return ParamInfo::defaultGainParam (); + } + + static const ParamInfo getParamInfo_2 () + { + return ParamInfo::defaultPoleRealParam (); + } + + static const ParamInfo getParamInfo_3 () + { + return ParamInfo::defaultZeroRealParam (); + } + + static Kind getKind () { return kindOther; } + static const char* getName() { return "Custom One-Pole"; } + + void setParams (const Params& params) + { + setup (pow (10., params[1]/20), + params[2], + params[3]); + } +}; + +struct TwoPole : DesignBase, Custom::TwoPole +{ + enum + { + NumParams = 6 + }; + + static int getNumParams () + { + return 6; + } + + static const ParamInfo getParamInfo_1 () + { + return ParamInfo::defaultGainParam (); + } + + static const ParamInfo getParamInfo_2 () + { + return ParamInfo::defaultPoleRhoParam (); + } + + static const ParamInfo getParamInfo_3 () + { + return ParamInfo::defaultPoleThetaParam (); + } + + static const ParamInfo getParamInfo_4 () + { + return ParamInfo::defaultZeroRhoParam (); + } + + static const ParamInfo getParamInfo_5 () + { + return ParamInfo::defaultZeroThetaParam (); + } + + + static Kind getKind () { return kindOther; } + static const char* getName() { return "Custom Two-Pole"; } + + void setParams (const Params& params) + { + setup (pow (10., params[1]/20), + params[2], + params[3], + params[4], + params[5]); + } +}; + +} + +} + +} + +#endif diff --git a/Source/Dsp/Design.cpp b/Source/Dsp/Design.cpp new file mode 100644 index 0000000000000000000000000000000000000000..af4263f3a15d248254269ebb7cb30964d2a0e308 --- /dev/null +++ b/Source/Dsp/Design.cpp @@ -0,0 +1,41 @@ +/******************************************************************************* + +"A Collection of Useful C++ Classes for Digital Signal Processing" + By Vincent Falco + +Official project location: +http://code.google.com/p/dspfilterscpp/ + +See Documentation.cpp for contact information, notes, and bibliography. + +-------------------------------------------------------------------------------- + +License: MIT License (http://www.opensource.org/licenses/mit-license.php) +Copyright (c) 2009 by Vincent Falco + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*******************************************************************************/ + +#include "Common.h" +#include "Design.h" + +namespace Dsp { + +} diff --git a/Source/Dsp/Design.h b/Source/Dsp/Design.h new file mode 100644 index 0000000000000000000000000000000000000000..e7dcf09f0a7b0c2a970835532a8ab9433d268bbb --- /dev/null +++ b/Source/Dsp/Design.h @@ -0,0 +1,64 @@ +/******************************************************************************* + +"A Collection of Useful C++ Classes for Digital Signal Processing" + By Vincent Falco + +Official project location: +http://code.google.com/p/dspfilterscpp/ + +See Documentation.cpp for contact information, notes, and bibliography. + +-------------------------------------------------------------------------------- + +License: MIT License (http://www.opensource.org/licenses/mit-license.php) +Copyright (c) 2009 by Vincent Falco + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*******************************************************************************/ + +#ifndef DSPFILTERS_DESIGN_H +#define DSPFILTERS_DESIGN_H + +#include "Common.h" +#include "Params.h" + +namespace Dsp { + +struct DesignBase +{ + // Sampling rate is the first param for every Design filter + static const ParamInfo getParamInfo_0 () + { + return ParamInfo::defaultSampleRateParam (); + } + + // These should never get called + static const ParamInfo getParamInfo_1 () { return ParamInfo(); } + static const ParamInfo getParamInfo_2 () { return ParamInfo(); } + static const ParamInfo getParamInfo_3 () { return ParamInfo(); } + static const ParamInfo getParamInfo_4 () { return ParamInfo(); } + static const ParamInfo getParamInfo_5 () { return ParamInfo(); } + static const ParamInfo getParamInfo_6 () { return ParamInfo(); } + static const ParamInfo getParamInfo_7 () { return ParamInfo(); } +}; + +} + +#endif diff --git a/Source/Dsp/Documentation.cpp b/Source/Dsp/Documentation.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d4b2da77a13843793162bbb730c7daba646c7f4f --- /dev/null +++ b/Source/Dsp/Documentation.cpp @@ -0,0 +1,456 @@ +/******************************************************************************* + +"A Collection of Useful C++ Classes for Digital Signal Processing" + By Vincent Falco + +Official project location: +http://code.google.com/p/dspfilterscpp/ + +See Documentation.h for contact information, notes, and bibliography. + +-------------------------------------------------------------------------------- + +License: MIT License (http://www.opensource.org/licenses/mit-license.php) +Copyright (c) 2009 by Vincent Falco + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +******************************************************************************** + +="A Collection of Useful C++ Classes for Digital Signal Processing"= +_By Vincent Falco_ + +_"Techniques for digital signal processing are well guarded and held +close to the chest, as they have valuable applications for multimedia +content. The black art of Infinite Impulse Response ("IIR") filtering +has remained shrouded in secrecy with little publicly available source +code....until now."_ + +---- +Building on the work of cherished luminaries such as Sophocles Orfanidis, Andreas Antoniou, Martin Holters, and Udo Zolzer, this library harnesses the power of C++ templates to solve a useful problem in digital signal processing: the realization of multichannel IIR filters of arbitrary order and prescribed specifications with various properties such as Butterworth, Chebyshev, Elliptic, and Optimum-L (Legendre) responses. The library is provided under the MIT license and is therefore fully compatible with proprietary usage. + +Classes are designed as independent re-usable building blocks. Use some or all of the provided features, or extend the functionality by writing your own objects that plug into the robust framework. Only the code that you need will get linked into your application. Here's a list of features: + + * Exclusive focus on IIR filters instead of boring FIR filters + * Complete implementation of all "RBJ Biquad" Cookbook filter formulas + * Butterworth, Chebyshev, Elliptic, Bessel, Legendre designs + * Low Pass, High Pass, Band Pass, Band Stop transformations + * Low, High, and Band Shelf filter implementations for most types + * Smooth interpolation of filter settings, pole/zeros, and biquad coefficients to achieve seamless parameter changes + * Representation of digital filters using poles and zeros + * Realization using Direct Form I, Direct Form II, or user provided class + * Fully factored to minimize template instantiations + * "Design" layer provides runtime introspection into a filter + * Utility template functions for manipulating buffers of sample data + * No calls to malloc or new, great for embedded systems + * No external dependencies, just the standard C++ library! + +Using these filters is easy: + +{{{ + // Create a Chebyshev type I Band Stop filter of order 3 + // with state for processing 2 channels of audio. + Dsp::SimpleFilter <Dsp::ChebyshevI::BandStop <3>, 2> f; + f.setup (3, // order + 44100,// sample rate + 4000, // center frequency + 880, // band width + 1); // ripple dB + f.process (numSamples, arrayOfChannels); +}}} + +An accompanying demonstration program that works on most popular platforms by using the separately licensed Juce application framework (included), exercises all the functionality of the library, including these features: + + * Dynamic interface creates itself using filter introspection capabilities + * Audio playback with real time application of a selected filter + * Live time stretching and amplitude modulation without clicks or popping + * Charts to show magnitude, phase response and pole/zero placement + * Thread safety "best practices" for audio applications + +Here's a screenshot of the DspFilters Demo + +http://dspfilterscpp.googlecode.com/files/dspfiltersdemo.png + +If you've been searching in futility on the Internet for some source code for implementing high order filters, then look no further because this is it! Whether you are a student of C++ or digital signal processing, a writer of audio plugins, or even a VST synthesizer coder, "A Collection of Useful C++ Classes for Digital Signal Processing" might have something for you! + +******************************************************************************** + +Notes: + + Please direct all comments this DSP and Plug-in Development forum: + + http://www.kvraudio.com/forum/viewforum.php?f=33 + +Credits + + All of this code was written by the author Vincent Falco except where marked. + + Some filter ideas are based on a java applet (http://www.falstad.com/dfilter/) + developed by Paul Falstad. + +Bibliography + + "High-Order Digital Parametric Equalizer Design" + Sophocles J. Orfanidis + (Journal of the Audio Engineering Society, vol 53. pp 1026-1046) + + http://crca.ucsd.edu/~msp/techniques/v0.08/book-html/node1.html + + "Spectral Transformations for digital filters" + A. G. Constantinides, B.Sc.(Eng.) Ph.D. + (Proceedings of the IEEE, vol. 117, pp. 1585-1590, August 1970) + +******************************************************************************** + +DOCUMENTATION + +All symbols are in the Dsp namespace. + +class Filter + + This is an abstract polymorphic interface that supports any filter. The + parameters to the filter are passed in the Params structure, which is + essentially an array of floating point numbers with a hard coded size + limit (maxParameters). Each filter makes use of the Params as it sees fit. + + Filter::getKind () + Filter::getName () + Filter::getNumParams () + Filter::getParamInfo () + + Through the use of these functions, the caller can determine the meaning + of each indexed filter parameter at run-time. The ParamInfo structure + contains methods that describe information about an individual parameter, + including convenience functions to map a filter parameter to a "control + value" in the range 0...1, suitable for presentation by a GUI element such + as a knob or scrollbar. + + Filter::getDefaultParams () + Filter::getParams () + Filter::getParam () + Filter::setParam () + Filter::findParamId () + Filter::setParamById () + Filter::setParams () + Filter::copyParamsFrom () + + These methods allow the caller to inspect the values of the parameters, + and set the filter parameters in various ways. When parameters are changed + they take effect on the filter immediately. + + Filter::getPoleZeros () + Filter::response () + + For analysis, these routines provide insight into the pole/zero arrangement + in the z-plane, and the complex valued response at a given normalized + frequency in the range (0..nyquist = 0.5]. From the complex number the + magnitude and phase can be calculated. + + Filter::getNumChannels() + Filter::reset() + Filter::process() + + These functions are for applying the filter to channels of data. If the + filter was not created with channel state (i.e. Channels==0 in the derived + class template) then they will throw an exception. + + To create a Filter object, use operator new on a subclass template with + appropriate parameters based on the type of filter you want. Here are the + subclasses. + + + +template <class DesignClass, int Channels = 0, class StateType = DirectFormII> +class FilterDesign : public Filter + + This subclass of Filter takes a DesignClass (explained below) representing + a filter, an optional parameter for the number of channels of data to + process, and an optional customizable choice of which state realization + to use for processing samples. Channels may be zero, in which case the + object can only be used for analysis. + + Because the DesignClass is a member and not inherited, it is in general + not possible to call members of the DesignClass directly. You must go + through the Filter interface. + + + +template <class DesignClass, int Channels, class StateType = DirectFormII> +class SmoothedFilterDesign : public Filter + + This subclass of FilterDesign implements a filter of the given DesignClass, + and also performs smoothing of parameters over time. Specifically, when + one or more filter parameters (such as cutoff frequency) are changed, the + class creates a transition over a given number of samples from the original + values to the new values. This process is invisible and seamless to the + caller, except that the constructor takes an additional parameter that + indicates the duration of transitions when parameters change. + + + +template <class FilterClass, int Channels = 0, class StateType = DirectFormII> +class SimpleFilter : public FilterClass + + This is a simple wrapper around a given raw FilterClass (explained below). + It uses inheritance so all of the members of the FilterClass are available + to instances of this object. The simple wrapper provides state information + for processing channels in the given form. + + The wrapper does not support introspection, parameter smoothing, or the + Params style of applying filter settings. Instead, it uses the interface + of the given FilterClass, which is typically a function called setup() + that takes a list of arguments representing the parameters. + + The use of this class bypasses the virtual function overhead of going + through a Filter object. It is not practical to change filter parameters + of a SimpleFilter, unless you are re-using the filter for a brand new + stream of data in which case reset() should be called immediately before + or after changing parameters, to clear the state and prevent audible + artifacts. + + + +Filter family namespaces + + Each family of filters is given its own namespace. Currently these namespaces + include: + + RBJ: Filters from the RBJ Cookbook + Butterworth: Filters with Butterworth response + ChebyshevI: Filters using Chebyshev polynomials (ripple in the passband) + ChebyshevII: "Inverse Chebyshev" filters (ripple in the stopband) + Elliptic: Filters with ripple in both the passband and stopband + Bessel: Uses Bessel polynomials, theoretically with linear phase + Legendre: "Optimum-L" filters with steepest transition and monotonic passband. + Custom: Simple filters that allow poles and zeros to be specified directly + +<class FilterClass> + + Within each namespace we have a set of "raw filters" (each one is an example + of a FilterClass). For example, the raw filters in the Butterworth namespace are: + + Butterworth::LowPass + Butterworth::HighPass + Butterworth::BandPass + Butterworth::BandStop + Butterworth::LowShelf + Butterworth::HighShelf + Butterworth::BandShelf + + When a class template (such as SimpleFilter) requires a FilterClass, it is + expecting an identifier of a raw filter. For example, Legendre::LowPass. The + raw filters do not have any support for introspection or the Params style of + changing filter settings. All they offer is a setup() function for updating + the IIR coefficients to a given set of parameters. + +<class DesignClass> + + Each filter family namespace also has the nested namespace "Design". Inside + this namespace we have all of the raw filter names repeated, except that + these classes additional provide the Design interface, which adds + introspection, polymorphism, the Params style of changing filter settings, + and in general all of the features necessary to interoperate with the Filter + virtual base class and its derived classes. For example, the design filters + from the Butterworth namespace are: + + Butterworth::Design::LowPass + Butterworth::Design::HighPass + Butterworth::Design::BandPass + Butterworth::Design::BandStop + Butterworth::Design::LowShelf + Butterworth::Design::HighShelf + Butterworth::Design::BandShelf + + For any class template that expects a DesignClass, you must pass a suitable + object from the Design namespace of the desired filter family. For example, + ChebyshevI::Design::BandPass. + +*******************************************************************************/ + +// +// Usage Examples +// +// This shows you how to operate the filters +// + +// This is the only include you need +#include "Dsp.h" + +#include <sstream> +#include <iostream> +#include <iomanip> + +namespace { + +void UsageExamples () +{ + // create a two channel audio buffer + int numSamples = 2000; + float* audioData[2]; + audioData[0] = new float[numSamples]; + audioData[1] = new float[numSamples]; + + // create a 2-channel RBJ Low Pass with parameter smoothing + // and apply it to the audio data + { + // "1024" is the number of samples over which to fade parameter changes + Dsp::Filter* f = new Dsp::SmoothedFilterDesign + <Dsp::RBJ::Design::LowPass, 2> (1024); + Dsp::Params params; + params[0] = 44100; // sample rate + params[1] = 4000; // cutoff frequency + params[2] = 1.25; // Q + f->setParams (params); + f->process (numSamples, audioData); + } + + // set up a 2-channel RBJ High Pass with parameter smoothing, + // but bypass virtual function overhead + { + // the difference here is that we don't go through a pointer. + Dsp::SmoothedFilterDesign <Dsp::RBJ::Design::LowPass, 2> f (1024); + Dsp::Params params; + params[0] = 44100; // sample rate + params[1] = 4000; // cutoff frequency + params[2] = 1.25; // Q + f.setParams (params); + f.process (numSamples, audioData); + } + + // create a 2-channel Butterworth Band Pass of order 4, + // with parameter smoothing and apply it to the audio data. + // Output samples are generated using Direct Form II realization. + { + Dsp::Filter* f = new Dsp::SmoothedFilterDesign + <Dsp::Butterworth::Design::BandPass <4>, 2, Dsp::DirectFormII> (1024); + Dsp::Params params; + params[0] = 44100; // sample rate + params[1] = 4; // order + params[2] = 4000; // center frequency + params[3] = 880; // band width + f->setParams (params); + f->process (numSamples, audioData); + } + + // create a 2-channel Inverse Chebyshev Low Shelf of order 5 + // and passband ripple 0.1dB, without parameter smoothing and apply it. + { + Dsp::Filter* f = new Dsp::FilterDesign + <Dsp::ChebyshevII::Design::LowShelf <5>, 2>; + Dsp::Params params; + params[0] = 44100; // sample rate + params[1] = 5; // order + params[2] = 4000; // corner frequency + params[3] = 6; // shelf gain + params[4] = 0.1; // passband ripple + f->setParams (params); + f->process (numSamples, audioData); + } + + // create an abstract Butterworth High Pass of order 4. + // This one can't process channels, it can only be used for analysis + // (i.e. extract poles and zeros). + { + Dsp::Filter* f = new Dsp::FilterDesign + <Dsp::Butterworth::Design::HighPass <4> >; + Dsp::Params params; + params[0] = 44100; // sample rate + params[1] = 4; // order + params[2] = 4000; // cutoff frequency + f->setParams (params); + // this will cause a runtime assertion + f->process (numSamples, audioData); + } + + // Use the simple filter API to create a Chebyshev Band Stop of order 3 + // and 1dB ripple in the passband. The simle API has a smaller + // footprint, but no introspection or smoothing. + { + // Note we use the raw filter instead of the one + // from the Design namespace. + Dsp::SimpleFilter <Dsp::ChebyshevI::BandStop <3>, 2> f; + f.setup (3, // order + 44100,// sample rate + 4000, // center frequency + 880, // band width + 1); // ripple dB + f.process (numSamples, audioData); + } + + // Set up a filter, extract the coefficients and print them to standard + // output. Note that this filter is not capable of processing samples, + // as it has no state. It only has coefficients. + { + Dsp::SimpleFilter <Dsp::RBJ::LowPass> f; + f.setup (44100, // sample rate Hz + 440, // cutoff frequency Hz + 1); // "Q" (resonance) + + std::ostringstream os; + + os << "a0 = " << f.getA0 () << "\n" + << "a1 = " << f.getA1 () << "\n" + << "a2 = " << f.getA2 () << "\n" + << "b0 = " << f.getB0 () << "\n" + << "b1 = " << f.getB1 () << "\n" + << "b2 = " << f.getB2 () << "\n"; + ; + + std::cout << os.str(); + } + + // Create an instance of a raw filter. This is as low as it gets, any + // lower and we will just have either a Biquad or a Cascade, and you'll + // be setting the coefficients manually. + { + // This is basically like eating uncooked food + Dsp::RBJ::LowPass f; + f.setup (44100, 440, 1); + + // calculate response at frequency 440 Hz + Dsp::complex_t response = f.response (440./44100); + } + + // Extract coefficients from a Cascade + { + Dsp::SimpleFilter <Dsp::Butterworth::HighPass <3> > f; + f.setup (3, 44100, 2000); + + std::ostringstream os; + + os << "numStages = " << f.getNumStages() << "\n" + << "a0[0] = " << f[0].getA0 () << "\n" + << "a1[0] = " << f[0].getA1 () << "\n" + << "a2[0] = " << f[0].getA2 () << "\n" + << "b0[0] = " << f[0].getB0 () << "\n" + << "b1[0] = " << f[0].getB1 () << "\n" + << "b2[0] = " << f[0].getB2 () << "\n" + << "a0[1] = " << f[1].getA0 () << "\n" + << "a1[1] = " << f[1].getA1 () << "\n" + << "a2[1] = " << f[1].getA2 () << "\n" + << "b0[1] = " << f[1].getB0 () << "\n" + << "b1[1] = " << f[1].getB1 () << "\n" + << "b2[1] = " << f[1].getB2 () << "\n" + ; + + std::cout << os.str(); + } +} + +} diff --git a/Source/Dsp/Dsp.h b/Source/Dsp/Dsp.h new file mode 100644 index 0000000000000000000000000000000000000000..2d3abf2622f7df1e55d341a8f7761acf6d9d0e7c --- /dev/null +++ b/Source/Dsp/Dsp.h @@ -0,0 +1,62 @@ +/******************************************************************************* + +"A Collection of Useful C++ Classes for Digital Signal Processing" + By Vincent Falco + +Official project location: +http://code.google.com/p/dspfilterscpp/ + +See Documentation.cpp for contact information, notes, and bibliography. + +-------------------------------------------------------------------------------- + +License: MIT License (http://www.opensource.org/licenses/mit-license.php) +Copyright (c) 2009 by Vincent Falco + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*******************************************************************************/ + +#ifndef DSPFILTERS_DSP_H +#define DSPFILTERS_DSP_H + +// +// Include this file in your application to get everything +// + +#include "Common.h" + +#include "Biquad.h" +#include "Cascade.h" +#include "Filter.h" +#include "PoleFilter.h" +#include "SmoothedFilter.h" +#include "State.h" +#include "Utilities.h" + +#include "Bessel.h" +#include "Butterworth.h" +#include "ChebyshevI.h" +#include "ChebyshevII.h" +#include "Custom.h" +#include "Elliptic.h" +#include "Legendre.h" +#include "RBJ.h" + +#endif diff --git a/Source/Dsp/Elliptic.cpp b/Source/Dsp/Elliptic.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7b2ff44ddaaa9317c82269232dca58bf0e86859b --- /dev/null +++ b/Source/Dsp/Elliptic.cpp @@ -0,0 +1,384 @@ +/******************************************************************************* + +"A Collection of Useful C++ Classes for Digital Signal Processing" + By Vincent Falco + +Official project location: +http://code.google.com/p/dspfilterscpp/ + +See Documentation.cpp for contact information, notes, and bibliography. + +-------------------------------------------------------------------------------- + +License: MIT License (http://www.opensource.org/licenses/mit-license.php) +Copyright (c) 2009 by Vincent Falco + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*******************************************************************************/ + +#include "Common.h" +#include "Elliptic.h" + +namespace Dsp { + +namespace Elliptic { + +// shit ton of math in here + +// approximation to complete elliptic integral of the first kind. +// fast convergence, peak error less than 2e-16. +double Solver::ellipticK (double k) +{ + double m = k*k; + double a = 1; + double b = sqrt (1 - m); + double c = a - b; + double co; + do + { + co = c; + c = (a - b) / 2; + double ao = (a + b) / 2; + b = sqrt (a*b); + a = ao; + } + while (c<co); + + return doublePi / (a + a); +} + +//------------------------------------------------------------------------------ + +AnalogLowPass::AnalogLowPass () + : m_numPoles (-1) +{ + setNormal (0, 1); +} + +void AnalogLowPass::design (int numPoles, + double rippleDb, + double rolloff) +{ + if (m_numPoles != numPoles || + m_rippleDb != rippleDb || + m_rolloff != rolloff) + { + m_numPoles = numPoles; + m_rippleDb = rippleDb; + m_rolloff = rolloff; + + reset (); + + // calculate + //const double ep = rippleDb; // passband ripple + + const int n = numPoles; + + double e2=pow(10.,rippleDb/10)-1; + //double xi = rolloff + 1; + double xi = 5 * exp (rolloff - 1) + 1; + + m_K = Solver::ellipticK(1/xi); + m_Kprime = Solver::ellipticK(sqrt(1-1/(xi*xi))); + + int ni = ((n & 1) == 1) ? 0 : 1; + int i; + double f[100]; // HACK!!! + for (i = 1; i <= n/2; i++) + { + double u = (2*i-ni)*m_K/n; + double sn = calcsn(u); + sn *= 2*doublePi/m_K; + f[i] = m_zeros[i-1] = 1/sn; + } + m_zeros[n/2] = std::numeric_limits<double>::infinity(); + double fb = 1/(2*doublePi); + m_nin = n % 2; + m_n2 = n/2; + for (i = 1; i <= m_n2; i++) + { + double x = f[m_n2+1-i]; + m_z1[i] = sqrt(1-1/(x*x)); + } + double ee = e2;//pow(10., rippleDb/20)-1; + m_e = sqrt(ee); + double fbb = fb*fb; + m_m = m_nin+2*m_n2; + m_em = 2*(m_m/2); + double tp = 2*doublePi; + calcfz(); + calcqz(); + if (m_m > m_em) + m_c1[2*m_m] = 0; + for (i = 0; i <= 2*m_m; i += 2) + m_a1[m_m-i/2] = m_c1[i] + m_d1[i]; + double a0 = findfact(m_m); + int r = 0; + while (r < m_em/2) + { + r++; + m_p[r] /= 10; + m_q1[r] /= 100; + double d = 1+m_p[r]+m_q1[r]; + m_b1[r] = (1+m_p[r]/2)*fbb/d; + m_zf1[r] = fb/pow(d, .25); + m_zq1[r] = 1/sqrt(abs(2*(1-m_b1[r]/(m_zf1[r]*m_zf1[r])))); + m_zw1[r] = tp*m_zf1[r]; + + m_rootR[r] = -.5*m_zw1[r]/m_zq1[r]; + m_rootR[r+m_em/2] = m_rootR[r]; + m_rootI[r] = .5*sqrt(abs(m_zw1[r]*m_zw1[r]/(m_zq1[r]*m_zq1[r]) - 4*m_zw1[r]*m_zw1[r])); + m_rootI[r+m_em/2] = -m_rootI[r]; + + complex_t pole ( + -.5*m_zw1[r]/m_zq1[r], + .5*sqrt(abs(m_zw1[r]*m_zw1[r]/(m_zq1[r]*m_zq1[r]) - 4*m_zw1[r]*m_zw1[r]))); + + complex_t zero (0, m_zeros[r-1]); + + addPoleZeroConjugatePairs (pole, zero); + } + + if (a0 != 0) + { + m_rootR[r+1+m_em/2] = -sqrt(fbb/(.1*a0-1))*tp; + m_rootI[r+1+m_em/2] = 0; + + add (-sqrt(fbb/(.1*a0-1))*tp, infinity()); + } + + setNormal (0, (numPoles&1) ? 1. : pow (10., -rippleDb / 20.0)); + } +} + +// generate the product of (z+s1[i]) for i = 1 .. sn and store it in b1[] +// (i.e. f[z] = b1[0] + b1[1] z + b1[2] z^2 + ... b1[sn] z^sn) +void AnalogLowPass::prodpoly( int sn ) +{ + m_b1[0] = m_s1[1]; + m_b1[1] = 1; + int i, j; + for (j = 2; j <= sn; j++) + { + m_a1[0] = m_s1[j]*m_b1[0]; + for (i = 1; i <= j-1; i++) + m_a1[i] = m_b1[i-1]+m_s1[j]*m_b1[i]; + for (i = 0; i != j; i++) + m_b1[i] = m_a1[i]; + m_b1[j] = 1; + } +} + +// determine f(z)^2 +void AnalogLowPass::calcfz2( int i ) +{ + int ji = 0; + int jf = 0; + if (i < m_em+2) + { + ji = 0; + jf = i; + } + if (i > m_em) + { + ji = i-m_em; + jf = m_em; + } + m_c1[i] = 0; + int j; + for(j = ji; j <= jf; j += 2) + m_c1[i] += m_a1[j]*(m_a1[i-j]*pow(10., m_m-i/2)); +} + +// calculate f(z) +void AnalogLowPass::calcfz( void ) +{ + int i = 1; + if( m_nin == 1 ) + m_s1[i++] = 1; + for (; i <= m_nin+m_n2; i++) + m_s1[i] = m_s1[i+m_n2] = m_z1[i-m_nin]; + prodpoly(m_nin+2*m_n2); + for (i = 0; i <= m_em; i += 2) + m_a1[i] = m_e*m_b1[i]; + for (i = 0; i <= 2*m_em; i += 2) + calcfz2(i); +} + +// determine q(z) +void AnalogLowPass::calcqz( void ) +{ + int i; + for (i = 1; i <= m_nin; i++) + m_s1[i] = -10; + for (; i <= m_nin+m_n2; i++) + m_s1[i] = -10*m_z1[i-m_nin]*m_z1[i-m_nin]; + for (; i <= m_nin+2*m_n2; i++) + m_s1[i] = m_s1[i-m_n2]; + prodpoly(m_m); + int dd = ((m_nin & 1) == 1) ? -1 : 1; + for (i = 0; i <= 2*m_m; i += 2) + m_d1[i] = dd*m_b1[i/2]; +} + +// compute factors +double AnalogLowPass::findfact(int t) +{ + int i; + double a = 0; + for (i = 1; i <= t; i++) + m_a1[i] /= m_a1[0]; + m_a1[0] = m_b1[0] = m_c1[0] = 1; + int i1 = 0; + for(;;) + { + if (t <= 2) + break; + double p0 = 0, q0 = 0; + i1++; + for(;;) + { + m_b1[1] = m_a1[1] - p0; + m_c1[1] = m_b1[1] - p0; + for (i = 2; i <= t; i++) + m_b1[i] = m_a1[i]-p0*m_b1[i-1]-q0*m_b1[i-2]; + for (i = 2; i < t; i++) + m_c1[i] = m_b1[i]-p0*m_c1[i-1]-q0*m_c1[i-2]; + int x1 = t-1; + int x2 = t-2; + int x3 = t-3; + double x4 = m_c1[x2]*m_c1[x2]+m_c1[x3]*(m_b1[x1]-m_c1[x1]); + if (x4 == 0) + x4 = 1e-3; + double ddp = (m_b1[x1]*m_c1[x2]-m_b1[t]*m_c1[x3])/x4; + p0 += ddp; + double dq = (m_b1[t]*m_c1[x2]-m_b1[x1]*(m_c1[x1]-m_b1[x1]))/x4; + q0 += dq; + if (abs(ddp+dq) < 1e-6) + break; + } + m_p[i1] = p0; + m_q1[i1] = q0; + m_a1[1] = m_a1[1]-p0; + t -= 2; + for (i = 2; i <= t; i++) + m_a1[i] -= p0*m_a1[i-1]+q0*m_a1[i-2]; + if (t <= 2) + break; + } + + if (t == 2) + { + i1++; + m_p[i1] = m_a1[1]; + m_q1[i1] = m_a1[2]; + } + if (t == 1) + a = -m_a1[1]; + + return a; +} + +double AnalogLowPass::calcsn(double u) +{ + double sn = 0; + int j; + // q = modular constant + double q = exp(-doublePi*m_Kprime/m_K); + double v = doublePi*.5*u/m_K; + for (j = 0; ; j++) + { + double w = pow(q, j+.5); + sn += w*sin((2*j+1)*v)/(1-w*w); + if (w < 1e-7) + break; + } + return sn; +} + +//------------------------------------------------------------------------------ + +void LowPassBase::setup (int order, + double sampleRate, + double cutoffFrequency, + double rippleDb, + double rolloff) +{ + m_analogProto.design (order, rippleDb, rolloff); + + LowPassTransform (cutoffFrequency / sampleRate, + m_digitalProto, + m_analogProto); + + Cascade::setLayout (m_digitalProto); +} + +void HighPassBase::setup (int order, + double sampleRate, + double cutoffFrequency, + double rippleDb, + double rolloff) +{ + m_analogProto.design (order, rippleDb, rolloff); + + HighPassTransform (cutoffFrequency / sampleRate, + m_digitalProto, + m_analogProto); + + Cascade::setLayout (m_digitalProto); +} + +void BandPassBase::setup (int order, + double sampleRate, + double centerFrequency, + double widthFrequency, + double rippleDb, + double rolloff) +{ + m_analogProto.design (order, rippleDb, rolloff); + + BandPassTransform (centerFrequency / sampleRate, + widthFrequency / sampleRate, + m_digitalProto, + m_analogProto); + + Cascade::setLayout (m_digitalProto); +} + +void BandStopBase::setup (int order, + double sampleRate, + double centerFrequency, + double widthFrequency, + double rippleDb, + double rolloff) +{ + m_analogProto.design (order, rippleDb, rolloff); + + BandStopTransform (centerFrequency / sampleRate, + widthFrequency / sampleRate, + m_digitalProto, + m_analogProto); + + Cascade::setLayout (m_digitalProto); +} + +} + +} diff --git a/Source/Dsp/Elliptic.h b/Source/Dsp/Elliptic.h new file mode 100644 index 0000000000000000000000000000000000000000..86e8d9cfcc5d460401fc60a9225c193a65ab66a4 --- /dev/null +++ b/Source/Dsp/Elliptic.h @@ -0,0 +1,359 @@ +/******************************************************************************* + +"A Collection of Useful C++ Classes for Digital Signal Processing" + By Vincent Falco + +Official project location: +http://code.google.com/p/dspfilterscpp/ + +See Documentation.cpp for contact information, notes, and bibliography. + +-------------------------------------------------------------------------------- + +License: MIT License (http://www.opensource.org/licenses/mit-license.php) +Copyright (c) 2009 by Vincent Falco + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*******************************************************************************/ + +#ifndef DSPFILTERS_ELLIPTIC_H +#define DSPFILTERS_ELLIPTIC_H + +#include "Common.h" +#include "Cascade.h" +#include "Design.h" +#include "Filter.h" +#include "PoleFilter.h" + +namespace Dsp { + +/* + * Filters with Elliptic response characteristics + * + */ + +namespace Elliptic { + +// Solves for Jacobi elliptics +class Solver +{ +public: + static double ellipticK (double k); +}; + +// Half-band analog prototype (s-plane) + +class AnalogLowPass : public LayoutBase +{ +public: + AnalogLowPass (); + + void design (const int numPoles, + double rippleDb, + double rolloff); + +private: + void prodpoly (int sn); + void calcfz2 (int i); + void calcfz (); + void calcqz (); + double findfact (int t); + double calcsn (double u); + +#if 0 + template<int n> + struct CalcArray + { + double& operator[](size_t index) + { + assert( index<n ); + return m_a[index]; + } + private: + double m_a[n]; + }; +#else +#endif + + double m_p0; + double m_q; + double m_K; + double m_Kprime; + double m_e; + int m_nin; + int m_m; + int m_n2; + int m_em; + double m_zeros[100]; + double m_c1[100]; + double m_b1[100]; + double m_a1[100]; + double m_d1[100]; + double m_q1[100]; + double m_z1[100]; + double m_f1[100]; + double m_s1[100]; + double m_p [100]; + double m_zw1[100]; + double m_zf1[100]; + double m_zq1[100]; + double m_rootR[100]; + double m_rootI[100]; + + int m_numPoles; + double m_rippleDb; + double m_rolloff; +}; + +//------------------------------------------------------------------------------ + +// Factored implementations to reduce template instantiations + +struct LowPassBase : PoleFilterBase <AnalogLowPass> +{ + void setup (int order, + double sampleRate, + double cutoffFrequency, + double rippleDb, + double rolloff); +}; + +struct HighPassBase : PoleFilterBase <AnalogLowPass> +{ + void setup (int order, + double sampleRate, + double cutoffFrequency, + double rippleDb, + double rolloff); +}; + +struct BandPassBase : PoleFilterBase <AnalogLowPass> +{ + void setup (int order, + double sampleRate, + double centerFrequency, + double widthFrequency, + double rippleDb, + double rolloff); +}; + +struct BandStopBase : PoleFilterBase <AnalogLowPass> +{ + void setup (int order, + double sampleRate, + double centerFrequency, + double widthFrequency, + double rippleDb, + double rolloff); +}; + +//------------------------------------------------------------------------------ + +// +// Raw filters +// + +template <int MaxOrder> +struct LowPass : PoleFilter <LowPassBase, MaxOrder> +{ +}; + +template <int MaxOrder> +struct HighPass : PoleFilter <HighPassBase, MaxOrder> +{ +}; + +template <int MaxOrder> +struct BandPass : PoleFilter <BandPassBase, MaxOrder, MaxOrder*2> +{ +}; + +template <int MaxOrder> +struct BandStop : PoleFilter <BandStopBase, MaxOrder, MaxOrder*2> +{ +}; + +//------------------------------------------------------------------------------ + +// +// Gui-friendly Design layer +// + +namespace Design { + +struct TypeIBase : DesignBase +{ + enum + { + NumParams = 5 + }; + + static int getNumParams () + { + return 5; + } + + static const ParamInfo getParamInfo_2 () + { + return ParamInfo::defaultCutoffFrequencyParam (); + } + + static const ParamInfo getParamInfo_3 () + { + return ParamInfo::defaultRippleDbParam (); + } + + static const ParamInfo getParamInfo_4 () + { + return ParamInfo::defaultRolloffParam (); + } +}; + +template <class FilterClass> +struct TypeI : TypeIBase, FilterClass +{ + void setParams (const Params& params) + { + FilterClass::setup (int(params[1]), params[0], params[2], params[3], params[4]); + } +}; + +struct TypeIIBase : DesignBase +{ + enum + { + NumParams = 6 + }; + + static int getNumParams () + { + return 6; + } + + static const ParamInfo getParamInfo_2 () + { + return ParamInfo::defaultCenterFrequencyParam (); + } + + static const ParamInfo getParamInfo_3 () + { + return ParamInfo::defaultBandwidthHzParam (); + } + + static const ParamInfo getParamInfo_4 () + { + return ParamInfo::defaultRippleDbParam (); + } + + static const ParamInfo getParamInfo_5 () + { + return ParamInfo::defaultRolloffParam (); + } +}; + +template <class FilterClass> +struct TypeII : TypeIIBase, FilterClass +{ + void setParams (const Params& params) + { + FilterClass::setup (int(params[1]), params[0], params[2], params[3], params[4], params[5]); + } +}; + +// Factored kind and name + +struct LowPassDescription +{ + static Kind getKind () { return kindLowPass; } + static const char* getName() { return "Elliptic Low Pass"; } +}; + +struct HighPassDescription +{ + static Kind getKind () { return kindHighPass; } + static const char* getName() { return "Elliptic High Pass"; } +}; + +struct BandPassDescription +{ + static Kind getKind () { return kindHighPass; } + static const char* getName() { return "Elliptic Band Pass"; } +}; + +struct BandStopDescription +{ + static Kind getKind () { return kindHighPass; } + static const char* getName() { return "Elliptic Band Stop"; } +}; + +// This glues on the Order parameter +template <int MaxOrder, + template <class> class TypeClass, + template <int> class FilterClass> +struct OrderBase : TypeClass <FilterClass <MaxOrder> > +{ + const ParamInfo getParamInfo_1 () const + { + return ParamInfo (idOrder, "Order", "Order", + 1, MaxOrder, 2, + &ParamInfo::Int_toControlValue, + &ParamInfo::Int_toNativeValue, + &ParamInfo::Int_toString); + + } +}; +//------------------------------------------------------------------------------ + +// +// Design filters +// + +template <int MaxOrder> +struct LowPass : OrderBase <MaxOrder, TypeI, Elliptic::LowPass>, + LowPassDescription +{ +}; + +template <int MaxOrder> +struct HighPass : OrderBase <MaxOrder, TypeI, Elliptic::HighPass>, + HighPassDescription +{ +}; + +template <int MaxOrder> +struct BandPass : OrderBase <MaxOrder, TypeII, Elliptic::BandPass>, + BandPassDescription +{ +}; + +template <int MaxOrder> +struct BandStop : OrderBase <MaxOrder, TypeII, Elliptic::BandStop>, + BandStopDescription +{ +}; + +} + +} + +} + +#endif + diff --git a/Source/Dsp/Filter.cpp b/Source/Dsp/Filter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..40e7a01b129fe91603d59ebe5f27ed191ba28a79 --- /dev/null +++ b/Source/Dsp/Filter.cpp @@ -0,0 +1,117 @@ +/******************************************************************************* + +"A Collection of Useful C++ Classes for Digital Signal Processing" + By Vincent Falco + +Official project location: +http://code.google.com/p/dspfilterscpp/ + +See Documentation.cpp for contact information, notes, and bibliography. + +-------------------------------------------------------------------------------- + +License: MIT License (http://www.opensource.org/licenses/mit-license.php) +Copyright (c) 2009 by Vincent Falco + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*******************************************************************************/ + +#include "Common.h" +#include "Filter.h" + +namespace Dsp { + +Params Filter::getDefaultParams() const +{ + Params params; + + params.clear(); + + for (int i = 0; i < getNumParams(); ++i) + params[i] = getParamInfo(i).getDefaultValue(); + + return params; +} + +Filter::~Filter() +{ +} + +int Filter::findParamId (int paramId) +{ + int index = -1; + + for (int i = getNumParams(); --i >= 0;) + { + if (getParamInfo (i).getId () == paramId) + { + index = i; + break; + } + } + + return index; +} + +void Filter::setParamById (int paramId, double nativeValue) +{ + for (int i = getNumParams(); --i >= 0;) + { + if (getParamInfo (i).getId () == paramId) + { + setParam (i, nativeValue); + return; + } + } + + assert (0); +} + +void Filter::copyParamsFrom (Dsp::Filter const* other) +{ + // first, set reasonable defaults + m_params = getDefaultParams (); + + if (other) + { + // now loop + for (int i = 0; i < getNumParams (); ++i) + { + const ParamInfo& paramInfo = getParamInfo (i); + + // find a match + for (int j = 0; j < other->getNumParams(); ++j) + { + const ParamInfo& otherParamInfo = other->getParamInfo (j); + + if (paramInfo.getId() == otherParamInfo.getId()) + { + // match! + m_params [i] = paramInfo.clamp (other->getParam (j)); + break; + } + } + } + } + + doSetParams (m_params); +} + +} diff --git a/Source/Dsp/Filter.h b/Source/Dsp/Filter.h new file mode 100644 index 0000000000000000000000000000000000000000..9f3be4bd46af38147519f45108dd3abc4b5dc035 --- /dev/null +++ b/Source/Dsp/Filter.h @@ -0,0 +1,269 @@ +/******************************************************************************* + +"A Collection of Useful C++ Classes for Digital Signal Processing" + By Vincent Falco + +Official project location: +http://code.google.com/p/dspfilterscpp/ + +See Documentation.cpp for contact information, notes, and bibliography. + +-------------------------------------------------------------------------------- + +License: MIT License (http://www.opensource.org/licenses/mit-license.php) +Copyright (c) 2009 by Vincent Falco + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*******************************************************************************/ + +#ifndef DSPFILTERS_FILTER_H +#define DSPFILTERS_FILTER_H + +#include "Common.h" +#include "MathSupplement.h" +#include "Params.h" +#include "State.h" +#include "Types.h" + +namespace Dsp { + +/* + * Filter + * + * Full abstraction of a digital IIR filter. + * Supports run-time introspection and modulation of filter + * parameters. + * + */ +class Filter +{ +public: + virtual ~Filter(); + + virtual Kind getKind () const = 0; + + virtual const std::string getName () const = 0; + + virtual int getNumParams () const = 0; + + virtual ParamInfo getParamInfo (int index) const = 0; + + Params getDefaultParams() const; + + const Params& getParams() const + { + return m_params; + } + + double getParam (int paramIndex) const + { + assert (paramIndex >= 0 && paramIndex <= getNumParams()); + return m_params[paramIndex]; + } + + void setParam (int paramIndex, double nativeValue) + { + assert (paramIndex >= 0 && paramIndex <= getNumParams()); + m_params[paramIndex] = nativeValue; + doSetParams (m_params); + } + + int findParamId (int paramId); + + void setParamById (int paramId, double nativeValue); + + void setParams (const Params& parameters) + { + m_params = parameters; + doSetParams (parameters); + } + + // This makes a best-effort to pick up the values + // of matching parameters from another set. It uses + // the ParamID information to make the match. + void copyParamsFrom (Dsp::Filter const* other); + + virtual std::vector<PoleZeroPair> getPoleZeros() const = 0; + + virtual complex_t response (double normalizedFrequency) const = 0; + + virtual int getNumChannels() = 0; + virtual void reset () = 0; + virtual void process (int numSamples, float* const* arrayOfChannels) = 0; + virtual void process (int numSamples, double* const* arrayOfChannels) = 0; + +protected: + virtual void doSetParams (const Params& parameters) = 0; + +private: + Params m_params; +}; + +//------------------------------------------------------------------------------ + +/* + * FilterDesign + * + * This container holds a filter Design (Gui-friendly layer) and + * optionally combines it with the necessary state information to + * process channel data. + * + */ + +// Factored to reduce template instantiations +template <class DesignClass> +class FilterDesignBase : public Filter +{ +public: + Kind getKind () const + { + return m_design.getKind (); + } + + const std::string getName () const + { + return m_design.getName (); + } + + int getNumParams () const + { + return DesignClass::NumParams; + } + + Params getDefaultParams() const + { + return m_design.getDefaultParams(); + } + + ParamInfo getParamInfo (int index) const + { + switch (index) + { + case 0: return m_design.getParamInfo_0 (); + case 1: return m_design.getParamInfo_1 (); + case 2: return m_design.getParamInfo_2 (); + case 3: return m_design.getParamInfo_3 (); + case 4: return m_design.getParamInfo_4 (); + case 5: return m_design.getParamInfo_5 (); + case 6: return m_design.getParamInfo_6 (); + case 7: return m_design.getParamInfo_7 (); + }; + + return ParamInfo(); + } + + std::vector<PoleZeroPair> getPoleZeros() const + { + return m_design.getPoleZeros(); + } + + complex_t response (double normalizedFrequency) const + { + return m_design.response (normalizedFrequency); + } + +protected: + void doSetParams (const Params& parameters) + { + m_design.setParams (parameters); + } + +protected: + DesignClass m_design; +}; + + + +template <class DesignClass, + int Channels = 0, + class StateType = DirectFormII> +class FilterDesign : public FilterDesignBase <DesignClass> +{ +public: + FilterDesign () + { + } + + int getNumChannels() + { + return Channels; + } + + void reset () + { + m_state.reset(); + } + + void process (int numSamples, float* const* arrayOfChannels) + { + m_state.process (numSamples, arrayOfChannels, + FilterDesignBase<DesignClass>::m_design); + } + + void process (int numSamples, double* const* arrayOfChannels) + { + m_state.process (numSamples, arrayOfChannels, + FilterDesignBase<DesignClass>::m_design); + } + +protected: + ChannelsState <Channels, + typename DesignClass::template State <StateType> > m_state; +}; + +//------------------------------------------------------------------------------ + +/* + * This container combines a raw filter with state information + * so it can process channels. In order to set up the filter you + * must call a setup function directly. Smooth changes are + * not supported, but this class has a smaller footprint. + * + */ +template <class FilterClass, + int Channels = 0, + class StateType = DirectFormII> +class SimpleFilter : public FilterClass +{ +public: + int getNumChannels() + { + return Channels; + } + + void reset () + { + m_state.reset(); + } + + template <typename Sample> + void process (int numSamples, Sample* const* arrayOfChannels) + { + m_state.process (numSamples, arrayOfChannels, *((FilterClass*)this)); + } + +protected: + ChannelsState <Channels, + typename FilterClass::template State <StateType> > m_state; +}; + +} + +#endif diff --git a/Source/Dsp/Layout.h b/Source/Dsp/Layout.h new file mode 100644 index 0000000000000000000000000000000000000000..57fc5d4fcfded0ac63c276a60e8010dd6f3b2799 --- /dev/null +++ b/Source/Dsp/Layout.h @@ -0,0 +1,170 @@ +/******************************************************************************* + +"A Collection of Useful C++ Classes for Digital Signal Processing" + By Vincent Falco + +Official project location: +http://code.google.com/p/dspfilterscpp/ + +See Documentation.cpp for contact information, notes, and bibliography. + +-------------------------------------------------------------------------------- + +License: MIT License (http://www.opensource.org/licenses/mit-license.php) +Copyright (c) 2009 by Vincent Falco + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*******************************************************************************/ + +#ifndef DSPFILTERS_LAYOUT_H +#define DSPFILTERS_LAYOUT_H + +#include "Common.h" +#include "MathSupplement.h" + +namespace Dsp { + +// +// Describes a filter as a collection of poles and zeros along with +// normalization information to achieve a specified gain at a specified +// frequency. The poles and zeros may lie either in the s or the z plane. +// + +// Base uses pointers to reduce template instantiations +class LayoutBase +{ +public: + LayoutBase () + : m_numPoles (0) + , m_maxPoles (0) + { + } + + LayoutBase (int maxPoles, PoleZeroPair* pairs) + : m_numPoles (0) + , m_maxPoles (maxPoles) + , m_pair (pairs) + { + } + + void setStorage (const LayoutBase& other) + { + m_numPoles = 0; + m_maxPoles = other.m_maxPoles; + m_pair = other.m_pair; + } + + void reset () + { + m_numPoles = 0; + } + + int getNumPoles () const + { + return m_numPoles; + } + + int getMaxPoles () const + { + return m_maxPoles; + } + + void add (const complex_t& pole, const complex_t& zero) + { + assert (!(m_numPoles&1)); // single comes last + assert (!Dsp::is_nan (pole)); + m_pair[m_numPoles/2] = PoleZeroPair (pole, zero); + ++m_numPoles; + } + + void addPoleZeroConjugatePairs (const complex_t pole, + const complex_t zero) + { + assert (!(m_numPoles&1)); // single comes last + assert (!Dsp::is_nan (pole)); + m_pair[m_numPoles/2] = PoleZeroPair ( + pole, zero, std::conj (pole), std::conj (zero)); + m_numPoles += 2; + } + + void add (const ComplexPair& poles, const ComplexPair& zeros) + { + assert (!(m_numPoles&1)); // single comes last + assert (poles.isMatchedPair ()); + assert (zeros.isMatchedPair ()); + m_pair[m_numPoles/2] = PoleZeroPair (poles.first, zeros.first, + poles.second, zeros.second); + m_numPoles += 2; + } + + const PoleZeroPair& getPair (int pairIndex) const + { + assert (pairIndex >= 0 && pairIndex < (m_numPoles+1)/2); + return m_pair[pairIndex]; + } + + const PoleZeroPair& operator[] (int pairIndex) const + { + return getPair (pairIndex); + } + + double getNormalW () const + { + return m_normalW; + } + + double getNormalGain () const + { + return m_normalGain; + } + + void setNormal (double w, double g) + { + m_normalW = w; + m_normalGain = g; + } + +private: + int m_numPoles; + int m_maxPoles; + PoleZeroPair* m_pair; + double m_normalW; + double m_normalGain; +}; + +//------------------------------------------------------------------------------ + +// Storage for Layout +template <int MaxPoles> +class Layout +{ +public: + operator LayoutBase () + { + return LayoutBase (MaxPoles, m_pairs); + } + +private: + PoleZeroPair m_pairs[(MaxPoles+1)/2]; +}; + +} + +#endif diff --git a/Source/Dsp/Legendre.cpp b/Source/Dsp/Legendre.cpp new file mode 100644 index 0000000000000000000000000000000000000000..627182d6f351420497411f66ccf23e5705ed1d37 --- /dev/null +++ b/Source/Dsp/Legendre.cpp @@ -0,0 +1,344 @@ +/******************************************************************************* + +"A Collection of Useful C++ Classes for Digital Signal Processing" + By Vincent Falco + +Official project location: +http://code.google.com/p/dspfilterscpp/ + +See Documentation.cpp for contact information, notes, and bibliography. + +-------------------------------------------------------------------------------- + +License: MIT License (http://www.opensource.org/licenses/mit-license.php) +Copyright (c) 2009 by Vincent Falco + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*******************************************************************************/ + +#include "Common.h" +#include "Legendre.h" +#include "RootFinder.h" + +#include <sstream> +#include <iostream> +#include <iomanip> + +namespace Dsp { + +namespace Legendre { + +static inline double m_sqrt2 () +{ + return 1.41421356237309504880; +} + +// Optimum 'L' Filter algorithm. +// (C) 2004, C. Bond. +// +// Based on discussion in Kuo, "Network Analysis and Synthesis", +// pp. 379-383. Original method due to A.Papoulis."On Monotonic +// Response Filters", Proc. IRE, 47, Feb. 1959. +// +// Rewritten by Vincent Falco to change the way temporary +// storage is allocated +// + +// +// This routine calculates the coefficients of the Legendre polynomial +// of the 1st kind. It uses a recursion relation. The first few polynomials +// are hard coded and the rest are found by recursion. +// +// (n+1)Pn+1 = (2n+1)xPn - nPn-1 Recursion relation. +// +void PolynomialFinderBase::legendre (double *p, int n) +{ + int i,j; + + if (n == 0) { + p[0] = 1.0; + return; + } + if (n == 1) { + p[0] = 0.0; + p[1] = 1.0; + return; + } + p[0] = -0.5; + p[1] = 0.0; + p[2] = 1.5; + + if (n == 2) return; + + for (i=0;i<=n;i++) { + m_aa[i] = m_bb[i] = 0.0; + } + m_bb[1] = 1.0; + + for (i=3;i<=n;i++) { + for (j=0;j<=i;j++) { + m_aa[j] = m_bb[j]; + m_bb[j] = p[j]; + p[j] = 0.0; + } + for (j=i-2;j>=0;j-=2) { + p[j] -= (i-1)*m_aa[j]/i; + } + for (j=i-1;j>=0;j-=2) { + p[j+1] += (2*i-1)*m_bb[j]/i; + } + } +} + +// +// +// In the following routine n = 2k + 1 for odd 'n' and n = 2k + 2 for +// even 'n'. +// +// +// n k +// ----- +// 1 0 +// 2 0 +// 3 1 +// 4 1 +// 5 2 +// 6 2 +// + +void PolynomialFinderBase::solve (int n) +{ + assert (n <= m_maxN); + + double c0,c1; + int i,j,k; + + k = (n-1)/2; + // + // form vector of 'a' constants + // + if (n & 1) { // odd + for (i=0;i<=k;i++) { + m_a[i] = (2.0*i+1.0)/(m_sqrt2()*(k+1.0)); + } + } // even + else { + for (i=0;i<k+1;i++) { + m_a[i] = 0.0; + } + if (k & 1) { + for (i=1;i<=k;i+=2) { + m_a[i] = (2*i+1)/sqrt(double((k+1)*(k+2))); + } + } + else { + for (i=0;i<=k;i+=2) { + m_a[i] = (2*i+1)/sqrt(double((k+1)*(k+2))); + } + } + } + for (i=0;i<=n;i++){ + m_s[i] = 0.0; + m_w[i] = 0.0; + } + // + // form s[] = sum of a[i]*P[i] + // + m_s[0] = m_a[0]; + m_s[1] = m_a[1]; + for (i=2;i<=k;i++) { + legendre(m_p,i); + for (j=0;j<=i;j++) { + m_s[j] += m_a[i]*m_p[j]; + } + } + // + // form v[] = square of s[] + // + for (i=0;i<=2*k+2;i++) { + m_v[i] = 0.0; + } + for (i=0;i<=k;i++) { + for (j=0;j<=k;j++) { + m_v[i+j] += m_s[i]*m_s[j]; + } + } + // + // modify integrand for even 'n' + // + m_v[2*k+1] = 0.0; + if ((n & 1) == 0) { + for (i=n;i>=0;i--) { + m_v[i+1] += m_v[i]; + } + } + // + // form integral of v[] + // + for (i=n+1;i>=0;i--) { + m_v[i+1] = m_v[i]/(double)(i+1.0); + } + m_v[0] = 0.0; + // + // clear s[] for use in computing definite integral + // + for (i=0;i<(n+2);i++){ + m_s[i] = 0.0; + } + m_s[0] = -1.0; + m_s[1] = 2.0; + // + // calculate definite integral + // + for (i=1;i<=n;i++) { + if (i > 1) { + c0 = -m_s[0]; + for (j=1;j<i+1;j++) { + c1 = -m_s[j] + 2.0*m_s[j-1]; + m_s[j-1] = c0; + c0 = c1; + } + c1 = 2.0*m_s[i]; + m_s[i] = c0; + m_s[i+1] = c1; + } + for (j=i;j>0;j--) { + m_w[j] += (m_v[i]*m_s[j]); + } + } + if ((n & 1) == 0) m_w[1] = 0.0; +} + +//------------------------------------------------------------------------------ + +AnalogLowPass::AnalogLowPass () + : m_numPoles (-1) +{ + setNormal (0, 1); +} + +void AnalogLowPass::design (int numPoles, + WorkspaceBase* w) +{ + if (m_numPoles != numPoles) + { + m_numPoles = numPoles; + + reset (); + + PolynomialFinderBase& poly (w->poly); + RootFinderBase& poles (w->roots); + + poly.solve (numPoles); + int degree = numPoles * 2; + + poles.coef()[0] = 1 + poly.coef()[0]; + poles.coef()[1] = 0; + for (int i = 1; i <= degree; ++i) + { + poles.coef()[2*i] = poly.coef()[i] * ((i & 1) ? -1 : 1); + poles.coef()[2*i+1] = 0; + } + poles.solve (degree); + + int j = 0; + for (int i = 0; i < degree; ++i) + if (poles.root()[i].real() <= 0) + poles.root()[j++] = poles.root()[i]; + // sort descending imag() and cut degree in half + poles.sort (degree/2); + + const int pairs = numPoles / 2; + for (int i = 0; i < pairs; ++i) + { + complex_t c = poles.root()[i]; + addPoleZeroConjugatePairs (c, infinity()); + } + + if (numPoles & 1) + add (poles.root()[pairs].real(), infinity()); + } +} + +//------------------------------------------------------------------------------ + +void LowPassBase::setup (int order, + double sampleRate, + double cutoffFrequency, + WorkspaceBase* w) +{ + m_analogProto.design (order, w); + + LowPassTransform (cutoffFrequency / sampleRate, + m_digitalProto, + m_analogProto); + + Cascade::setLayout (m_digitalProto); +} + +void HighPassBase::setup (int order, + double sampleRate, + double cutoffFrequency, + WorkspaceBase* w) +{ + m_analogProto.design (order, w); + + HighPassTransform (cutoffFrequency / sampleRate, + m_digitalProto, + m_analogProto); + + Cascade::setLayout (m_digitalProto); +} + +void BandPassBase::setup (int order, + double sampleRate, + double centerFrequency, + double widthFrequency, + WorkspaceBase* w) +{ + m_analogProto.design (order, w); + + BandPassTransform (centerFrequency / sampleRate, + widthFrequency / sampleRate, + m_digitalProto, + m_analogProto); + + Cascade::setLayout (m_digitalProto); +} + +void BandStopBase::setup (int order, + double sampleRate, + double centerFrequency, + double widthFrequency, + WorkspaceBase* w) +{ + m_analogProto.design (order, w); + + BandStopTransform (centerFrequency / sampleRate, + widthFrequency / sampleRate, + m_digitalProto, + m_analogProto); + + Cascade::setLayout (m_digitalProto); +} + +} + +} diff --git a/Source/Dsp/Legendre.h b/Source/Dsp/Legendre.h new file mode 100644 index 0000000000000000000000000000000000000000..ecd577361564cb163bb0262e3dbaef1efcd8da62 --- /dev/null +++ b/Source/Dsp/Legendre.h @@ -0,0 +1,417 @@ +/******************************************************************************* + +"A Collection of Useful C++ Classes for Digital Signal Processing" + By Vincent Falco + +Official project location: +http://code.google.com/p/dspfilterscpp/ + +See Documentation.cpp for contact information, notes, and bibliography. + +-------------------------------------------------------------------------------- + +License: MIT License (http://www.opensource.org/licenses/mit-license.php) +Copyright (c) 2009 by Vincent Falco + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*******************************************************************************/ + +#ifndef DSPFILTERS_LEGENDRE_H +#define DSPFILTERS_LEGENDRE_H + +#include "Common.h" +#include "Cascade.h" +#include "Design.h" +#include "Filter.h" +#include "PoleFilter.h" +#include "RootFinder.h" + +namespace Dsp { + +/* + * Filters with Legendre / "Optimum-L" response characteristics + * + */ + +namespace Legendre { + +// Numerical computation of Legendre "Optimum-L" polynomials + +class PolynomialFinderBase +{ +public: + void solve (int n); + + double* coef() + { + return m_w; + } + +private: + void legendre (double* p, int n); + +protected: + int m_maxN; + double* m_w; + double* m_a; + double* m_p; + double* m_s; + double* m_v; + double* m_aa; + double* m_bb; +}; + +template <int maxN> +class PolynomialFinder : public PolynomialFinderBase +{ +public: + PolynomialFinder () + { + m_maxN = maxN; + m_w = m_ws; + m_a = m_as; + m_p = m_ps; + m_s = m_ss; + m_v = m_vs; + m_aa = m_aas; + m_bb = m_bbs; + } + + void solve (int n) + { + assert (n <= maxN); + PolynomialFinderBase::solve (n); + } + +private: + double m_ws [2 * maxN + 1]; + double m_as [ maxN + 1]; + double m_ps [2 * maxN + 1]; + double m_ss [2 * maxN + 1]; + double m_vs [2 * maxN + 4]; + double m_aas [ maxN + 1]; + double m_bbs [ maxN + 1]; +}; + +//------------------------------------------------------------------------------ + +// A Workspace is necessary to construct the polynomial and find its roots + +struct WorkspaceBase +{ + WorkspaceBase (PolynomialFinderBase* polyBase, + RootFinderBase* rootsBase) + : poly (*polyBase) + , roots (*rootsBase) + { + } + + PolynomialFinderBase& poly; + RootFinderBase& roots; + +private: + WorkspaceBase (WorkspaceBase&); + WorkspaceBase& operator= (WorkspaceBase&); +}; + +template <int MaxOrder> +struct Workspace : WorkspaceBase +{ + Workspace () + : WorkspaceBase (&m_poly, &m_roots) + { + } + +private: + PolynomialFinder <MaxOrder> m_poly; + RootFinder <MaxOrder * 2> m_roots; +}; + +//------------------------------------------------------------------------------ + +// Half-band analog prototypes (s-plane) + +class AnalogLowPass : public LayoutBase +{ +public: + AnalogLowPass (); + + void design (const int numPoles, WorkspaceBase* w); + +private: + int m_numPoles; +}; + +//------------------------------------------------------------------------------ + +// Factored implementations to reduce template instantiations + +struct LowPassBase : PoleFilterBase <AnalogLowPass> +{ + void setup (int order, + double sampleRate, + double cutoffFrequency, + WorkspaceBase* w); +}; + +struct HighPassBase : PoleFilterBase <AnalogLowPass> +{ + void setup (int order, + double sampleRate, + double cutoffFrequency, + WorkspaceBase* w); +}; + +struct BandPassBase : PoleFilterBase <AnalogLowPass> +{ + void setup (int order, + double sampleRate, + double centerFrequency, + double widthFrequency, + WorkspaceBase* w); +}; + +struct BandStopBase : PoleFilterBase <AnalogLowPass> +{ + void setup (int order, + double sampleRate, + double centerFrequency, + double widthFrequency, + WorkspaceBase* w); +}; + +//------------------------------------------------------------------------------ + +// +// Raw filters +// + +template <int MaxOrder> +struct LowPass : PoleFilter <LowPassBase, MaxOrder> +{ + void setup (int order, + double sampleRate, + double cutoffFrequency) + { + Workspace <MaxOrder> w; + LowPassBase::setup (order, + sampleRate, + cutoffFrequency, + &w); + } +}; + +template <int MaxOrder> +struct HighPass : PoleFilter <HighPassBase, MaxOrder> +{ + void setup (int order, + double sampleRate, + double cutoffFrequency) + { + Workspace <MaxOrder> w; + HighPassBase::setup (order, + sampleRate, + cutoffFrequency, + &w); + } +}; + +template <int MaxOrder> +struct BandPass : PoleFilter <BandPassBase, MaxOrder, MaxOrder*2> +{ + void setup (int order, + double sampleRate, + double centerFrequency, + double widthFrequency) + { + Workspace <MaxOrder> w; + BandPassBase::setup (order, + sampleRate, + centerFrequency, + widthFrequency, + &w); + } +}; + +template <int MaxOrder> +struct BandStop : PoleFilter <BandStopBase, MaxOrder, MaxOrder*2> +{ + void setup (int order, + double sampleRate, + double centerFrequency, + double widthFrequency) + { + Workspace <MaxOrder> w; + BandStopBase::setup (order, + sampleRate, + centerFrequency, + widthFrequency, + &w); + } +}; + +//------------------------------------------------------------------------------ + +// +// Gui-friendly Design layer +// + +namespace Design { + +struct TypeIBase : DesignBase +{ + enum + { + NumParams = 3 + }; + + static int getNumParams () + { + return 3; + } + + static const ParamInfo getParamInfo_2 () + { + return ParamInfo::defaultCutoffFrequencyParam (); + } +}; + +template <class FilterClass> +struct TypeI : TypeIBase, FilterClass +{ + void setParams (const Params& params) + { + FilterClass::setup (int(params[1]), params[0], params[2]); + } +}; + +struct TypeIIBase : DesignBase +{ + enum + { + NumParams = 4 + }; + + static int getNumParams () + { + return 4; + } + + static const ParamInfo getParamInfo_2 () + { + return ParamInfo::defaultCenterFrequencyParam (); + } + + static const ParamInfo getParamInfo_3 () + { + return ParamInfo::defaultBandwidthHzParam (); + } +}; + +template <class FilterClass> +struct TypeII : TypeIIBase, FilterClass +{ + void setParams (const Params& params) + { + FilterClass::setup (int(params[1]), params[0], params[2], params[3]); + } +}; + +// Factored kind and name + +struct LowPassDescription +{ + static Kind getKind () { return kindLowPass; } + static const char* getName() { return "Legendre Low Pass"; } +}; + +struct HighPassDescription +{ + static Kind getKind () { return kindHighPass; } + static const char* getName() { return "Legendre High Pass"; } +}; + +struct BandPassDescription +{ + static Kind getKind () { return kindHighPass; } + static const char* getName() { return "Legendre Band Pass"; } +}; + +struct BandStopDescription +{ + static Kind getKind () { return kindHighPass; } + static const char* getName() { return "Legendre Band Stop"; } +}; + +// This glues on the Order parameter +template <int MaxOrder, + template <class> class TypeClass, + template <int> class FilterClass> +struct OrderBase : TypeClass <FilterClass <MaxOrder> > +{ + const ParamInfo getParamInfo_1 () const + { + return ParamInfo (idOrder, "Order", "Order", + 1, MaxOrder, 2, + &ParamInfo::Int_toControlValue, + &ParamInfo::Int_toNativeValue, + &ParamInfo::Int_toString); + + } +}; + +//------------------------------------------------------------------------------ + +// +// Design filters +// + +template <int MaxOrder> +struct LowPass : OrderBase <MaxOrder, TypeI, Legendre::LowPass>, + LowPassDescription +{ +}; + +template <int MaxOrder> +struct HighPass : OrderBase <MaxOrder, TypeI, Legendre::HighPass>, + HighPassDescription +{ +}; + +template <int MaxOrder> +struct BandPass : OrderBase <MaxOrder, TypeII, Legendre::BandPass>, + BandPassDescription +{ +}; + +template <int MaxOrder> +struct BandStop : OrderBase <MaxOrder, TypeII, Legendre::BandStop>, + BandStopDescription +{ +}; + +} + +} + +} + +#endif + diff --git a/Source/Dsp/MathSupplement.h b/Source/Dsp/MathSupplement.h new file mode 100644 index 0000000000000000000000000000000000000000..59bbc7973907be2d1d467f5dba47a2312ef7152a --- /dev/null +++ b/Source/Dsp/MathSupplement.h @@ -0,0 +1,154 @@ +/******************************************************************************* + +"A Collection of Useful C++ Classes for Digital Signal Processing" + By Vincent Falco + +Official project location: +http://code.google.com/p/dspfilterscpp/ + +See Documentation.cpp for contact information, notes, and bibliography. + +-------------------------------------------------------------------------------- + +License: MIT License (http://www.opensource.org/licenses/mit-license.php) +Copyright (c) 2009 by Vincent Falco + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*******************************************************************************/ + +#ifndef DSPFILTERS_MATHSUPPLEMENT_H +#define DSPFILTERS_MATHSUPPLEMENT_H + +#include "Common.h" + +namespace Dsp { + +const double doublePi =3.1415926535897932384626433832795028841971; +const double doublePi_2 =1.5707963267948966192313216916397514420986; +const double doubleLn2 =0.69314718055994530941723212145818;//????? +const double doubleLn10 =2.3025850929940456840179914546844;//?????? + +typedef std::complex<double> complex_t; +typedef std::pair<complex_t, complex_t> complex_pair_t; + +template<typename Real> +inline std::complex<Real> solve_quadratic_1 (Real a, Real b, Real c) +{ + return (-b + sqrt (std::complex<Real> (b*b - 4*a*c, 0))) / (2. * a); +} + +template<typename Real> +inline std::complex<Real> solve_quadratic_2 (Real a, Real b, Real c) +{ + return (-b - sqrt (std::complex<Real> (b*b - 4*a*c, 0))) / (2. * a); +} + +inline const complex_t infinity() +{ + return complex_t (std::numeric_limits<double>::infinity()); +} + +inline const complex_t adjust_imag (const complex_t& c) +{ + if (fabs (c.imag()) < 1e-30) + return complex_t (c.real(), 0); + else + return c; +} + +template <typename Ty, typename To> +inline std::complex<Ty> addmul (const std::complex<Ty>& c, + Ty v, + const std::complex<To>& c1) +{ + return std::complex <Ty> ( + c.real() + v * c1.real(), c.imag() + v * c1.imag()); +} + +template <typename Ty> +inline std::complex<Ty> recip (const std::complex<Ty>& c) +{ + Ty n = 1.0 / std::norm (c); + + return std::complex<Ty> (n * c.real(), n * c.imag()); +} + +template <typename Ty> +inline Ty asinh (Ty x) +{ + return log (x + std::sqrt (x * x + 1 )); +} + +template <typename Ty> +inline Ty acosh (Ty x) +{ + return log (x + std::sqrt (x * x - 1)); +} + +template <typename Ty> +inline bool is_nan (Ty v) +{ + return !(v == v); +} + +template <> +inline bool is_nan<complex_t> (complex_t v) +{ + return Dsp::is_nan (v.real()) || Dsp::is_nan (v.imag()); +} + +//------------------------------------------------------------------------------ + +/* + * Hack to prevent denormals + * + */ + +//const double anti_denormal_vsa = 1e-16; // doesn't prevent denormals +//const double anti_denormal_vsa = 0; +const double anti_denormal_vsa = 1e-8; + +class DenormalPrevention +{ +public: + DenormalPrevention () + : m_v (anti_denormal_vsa) + { + } + + // small alternating current + inline double ac () + { + return m_v = -m_v; + } + + // small direct current + static inline double dc () + { + return anti_denormal_vsa; + } + +private: + double m_v; +}; + +} + +#endif diff --git a/Source/Dsp/Param.cpp b/Source/Dsp/Param.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cc376c65141707c7fc673c0c5b7496db2db78bcf --- /dev/null +++ b/Source/Dsp/Param.cpp @@ -0,0 +1,309 @@ +/******************************************************************************* + +"A Collection of Useful C++ Classes for Digital Signal Processing" + By Vincent Falco + +Official project location: +http://code.google.com/p/dspfilterscpp/ + +See Documentation.cpp for contact information, notes, and bibliography. + +-------------------------------------------------------------------------------- + +License: MIT License (http://www.opensource.org/licenses/mit-license.php) +Copyright (c) 2009 by Vincent Falco + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*******************************************************************************/ + +#include "Common.h" +#include "Design.h" + +#include <stdexcept> +#include <sstream> +#include <iostream> +#include <iomanip> + +namespace Dsp { + +ParamInfo::ParamInfo () +{ + throw std::logic_error ("invalid usage of ParamInfo"); +} + +double ParamInfo::clamp (double nativeValue) const +{ + const double minValue = toNativeValue (0); + const double maxValue = toNativeValue (1); + if (nativeValue < minValue) + nativeValue = minValue; + else if (nativeValue > maxValue) + nativeValue = maxValue; + return nativeValue; +} + +//------------------------------------------------------------------------------ + +double ParamInfo::Int_toControlValue (double nativeValue) const +{ + return (nativeValue - m_arg1) / (m_arg2 - m_arg1); +} + +double ParamInfo::Int_toNativeValue (double controlValue) const +{ + return std::floor (m_arg1 + controlValue * (m_arg2 - m_arg1) + 0.5); +} + +//------------------------------------------------------------------------------ + +double ParamInfo::Real_toControlValue (double nativeValue) const +{ + return (nativeValue - m_arg1) / (m_arg2 - m_arg1); +} + +double ParamInfo::Real_toNativeValue (double controlValue) const +{ + return m_arg1 + controlValue * (m_arg2 - m_arg1); +} + +//------------------------------------------------------------------------------ + +double ParamInfo::Log_toControlValue (double nativeValue) const +{ + const double base = 1.5; + double l0 = log (m_arg1) / log (base); + double l1 = log (m_arg2) / log (base); + return (log (nativeValue) / log(base) - l0) / (l1 - l0); +} + +double ParamInfo::Log_toNativeValue (double controlValue) const +{ + const double base = 1.5; + double l0 = log (m_arg1) / log (base); + double l1 = log (m_arg2) / log (base); + return pow (base, l0 + controlValue * (l1 - l0)); +} + +//------------------------------------------------------------------------------ + +double ParamInfo::Pow2_toControlValue (double nativeValue) const +{ + return ((log (nativeValue) / log (2.)) - m_arg1) / (m_arg2 - m_arg1); +} + +double ParamInfo::Pow2_toNativeValue (double controlValue) const +{ + return pow (2., (controlValue * (m_arg2 - m_arg1)) + m_arg1); +} + +//------------------------------------------------------------------------------ + +std::string ParamInfo::Int_toString (double nativeValue) const +{ + std::ostringstream os; + os << int (nativeValue); + return os.str(); +} + +std::string ParamInfo::Hz_toString (double nativeValue) const +{ + std::ostringstream os; + os << int (nativeValue) << " Hz"; + return os.str(); +} + +std::string ParamInfo::Real_toString (double nativeValue) const +{ + std::ostringstream os; + os << std::fixed << std::setprecision(3) << nativeValue; + return os.str(); +} + +std::string ParamInfo::Db_toString (double nativeValue) const +{ + const double af = fabs (nativeValue); + int prec; + if (af < 1) + prec = 3; + else if (af < 10) + prec = 2; + else + prec = 1; + std::ostringstream os; + os << std::fixed << std::setprecision (prec) << nativeValue << " dB"; + return os.str(); +} + +//------------------------------------------------------------------------------ + +ParamInfo ParamInfo::defaultSampleRateParam () +{ + return ParamInfo (idSampleRate, "Fs", "Sample Rate", + 11025, 192000, 44100, + &ParamInfo::Real_toControlValue, + &ParamInfo::Real_toNativeValue, + &ParamInfo::Hz_toString); +} + +ParamInfo ParamInfo::defaultCutoffFrequencyParam () +{ + return ParamInfo (idFrequency, "Fc", "Cutoff Frequency", + 10, 22040, 2000, + &ParamInfo::Log_toControlValue, + &ParamInfo::Log_toNativeValue, + &ParamInfo::Hz_toString); +} + +ParamInfo ParamInfo::defaultCenterFrequencyParam () +{ + return ParamInfo (idFrequency, "Fc", "Center Frequency", + 10, 22040, 2000, + &ParamInfo::Log_toControlValue, + &ParamInfo::Log_toNativeValue, + &ParamInfo::Hz_toString); +} + +ParamInfo ParamInfo::defaultQParam () +{ + return ParamInfo (idQ, "Q", "Resonance", + -4, 4, 1, + &ParamInfo::Pow2_toControlValue, + &ParamInfo::Pow2_toNativeValue, + &ParamInfo::Real_toString); +} + +ParamInfo ParamInfo::defaultBandwidthParam () +{ + return ParamInfo (idBandwidth, "BW", "Bandwidth (Octaves)", + -4, 4, 1, + &ParamInfo::Pow2_toControlValue, + &ParamInfo::Pow2_toNativeValue, + &ParamInfo::Real_toString); +} + +ParamInfo ParamInfo::defaultBandwidthHzParam () +{ + return ParamInfo (idBandwidthHz, "BW", "Bandwidth (Hz)", + 10, 22040, 1720, + &ParamInfo::Log_toControlValue, + &ParamInfo::Log_toNativeValue, + &ParamInfo::Hz_toString); +} + +ParamInfo ParamInfo::defaultGainParam () +{ + return ParamInfo (idGain, "Gain", "Gain", + -24, 24, -6, + &ParamInfo::Real_toControlValue, + &ParamInfo::Real_toNativeValue, + &ParamInfo::Db_toString); +} + +ParamInfo ParamInfo::defaultSlopeParam () +{ + return ParamInfo (idSlope, "Slope", "Slope", + -2, 2, 1, + &ParamInfo::Pow2_toControlValue, + &ParamInfo::Pow2_toNativeValue, + &ParamInfo::Real_toString); +} + +ParamInfo ParamInfo::defaultRippleDbParam () +{ + return ParamInfo (idRippleDb, "Ripple", "Ripple dB", + 0.001, 12, 0.01, + &ParamInfo::Real_toControlValue, + &ParamInfo::Real_toNativeValue, + &ParamInfo::Db_toString); +} + +ParamInfo ParamInfo::defaultStopDbParam () +{ + return ParamInfo (idStopDb, "Stop", "Stopband dB", + 3, 60, 48, + &ParamInfo::Real_toControlValue, + &ParamInfo::Real_toNativeValue, + &ParamInfo::Db_toString); +} + +ParamInfo ParamInfo::defaultRolloffParam () +{ + return ParamInfo (idRolloff, "W", "Transition Width", + -16, 4, 0, + &ParamInfo::Real_toControlValue, + &ParamInfo::Real_toNativeValue, + &ParamInfo::Real_toString); +} + +ParamInfo ParamInfo::defaultPoleRhoParam () +{ + return ParamInfo (idPoleRho, "Pd", "Pole Distance", + 0, 1, 0.5, + &ParamInfo::Real_toControlValue, + &ParamInfo::Real_toNativeValue, + &ParamInfo::Real_toString); +} + +ParamInfo ParamInfo::defaultPoleThetaParam () +{ + return ParamInfo (idPoleTheta, "Pa", "Pole Angle", + 0, doublePi, doublePi/2, + &ParamInfo::Real_toControlValue, + &ParamInfo::Real_toNativeValue, + &ParamInfo::Real_toString); +} + +ParamInfo ParamInfo::defaultZeroRhoParam () +{ + return ParamInfo (idZeroRho, "Pd", "Zero Distance", + 0, 1, 0.5, + &ParamInfo::Real_toControlValue, + &ParamInfo::Real_toNativeValue, + &ParamInfo::Real_toString); +} + +ParamInfo ParamInfo::defaultZeroThetaParam () +{ + return ParamInfo (idZeroTheta, "Pa", "Zero Angle", + 0, doublePi, doublePi/2, + &ParamInfo::Real_toControlValue, + &ParamInfo::Real_toNativeValue, + &ParamInfo::Real_toString); +} + +ParamInfo ParamInfo::defaultPoleRealParam () +{ + return ParamInfo (idPoleReal, "A1", "Pole Real", + -1, 1, 0.25, + &ParamInfo::Real_toControlValue, + &ParamInfo::Real_toNativeValue, + &ParamInfo::Real_toString); +} + +ParamInfo ParamInfo::defaultZeroRealParam () +{ + return ParamInfo (idZeroReal, "B1", "Zero Real", + -1, 1, -0.25, + &ParamInfo::Real_toControlValue, + &ParamInfo::Real_toNativeValue, + &ParamInfo::Real_toString); +} +} + diff --git a/Source/Dsp/Params.h b/Source/Dsp/Params.h new file mode 100644 index 0000000000000000000000000000000000000000..882e554c23b8860f79fe6824eef999a7d217a346 --- /dev/null +++ b/Source/Dsp/Params.h @@ -0,0 +1,244 @@ +/******************************************************************************* + +"A Collection of Useful C++ Classes for Digital Signal Processing" + By Vincent Falco + +Official project location: +http://code.google.com/p/dspfilterscpp/ + +See Documentation.cpp for contact information, notes, and bibliography. + +-------------------------------------------------------------------------------- + +License: MIT License (http://www.opensource.org/licenses/mit-license.php) +Copyright (c) 2009 by Vincent Falco + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*******************************************************************************/ + +#ifndef DSPFILTERS_PARAMS_H +#define DSPFILTERS_PARAMS_H + +#include "Common.h" +#include "Types.h" + +namespace Dsp { + +/* + * System for abstracting parameterizable filter specifications. + * + * This provides a "GUI-friendly" layer to the filters. Note that + * it is not necessary to use this layer, it is possible to instantiate + * the filters and their associated processing state directly, + * and bypass the overhead for this API if it is not needed. + * + */ + +// Unique IDs to help identify parameters +enum ParamID +{ + idSampleRate, + idFrequency, + idQ, + idBandwidth, + idBandwidthHz, + idGain, + idSlope, + idOrder, + idRippleDb, + idStopDb, + idRolloff, + + idPoleRho, + idPoleTheta, + idZeroRho, + idZeroTheta, + + idPoleReal, + idZeroReal +}; + +enum +{ + maxParameters = 8 +}; + +struct Params +{ + void clear () + { + for (int i = 0; i < maxParameters; ++i) + value[i] = 0; + } + + double& operator[] (int index) + { + return value[index]; + } + + const double& operator[] (int index) const + { + return value[index]; + } + + double value[maxParameters]; +}; + +// +// Provides meta-information about a filter parameter +// to achieve run-time introspection. +// +class ParamInfo +{ +public: + typedef double (ParamInfo::*toControlValue_t) (double) const; + typedef double (ParamInfo::*toNativeValue_t) (double) const; + typedef std::string (ParamInfo::*toString_t) (double) const; + + // dont use this one + ParamInfo (); // throws std::logic_error + + ParamInfo (ParamID id, + const char* szLabel, + const char* szName, + double arg1, + double arg2, + double defaultNativeValue, + toControlValue_t toControlValue_proc, + toNativeValue_t toNativeValue_proc, + toString_t toString_proc) + : m_id (id) + , m_szLabel (szLabel) + , m_szName (szName) + , m_arg1 (arg1) + , m_arg2 (arg2) + , m_defaultNativeValue (defaultNativeValue) + , m_toControlValue (toControlValue_proc) + , m_toNativeValue (toNativeValue_proc) + , m_toString (toString_proc) + { + } + + // Used to identify well-known parameters (like cutoff frequency) + ParamID getId () const + { + return m_id; + } + + // Returns a short label suitable for placement on a control + const char* getLabel () const + { + return m_szLabel; + } + + // Returns the full name + const char* getName () const + { + return m_szName; + } + + double getDefaultValue () const + { + return m_defaultNativeValue; + } + + // + // Control value is always in the range [0..1] + // + double toControlValue (double nativeValue) const + { + return (this->*m_toControlValue) (nativeValue); + } + + // + // Native value is in filter-specific units. For example, + // cutoff frequency would probably be in Hertz. + // + double toNativeValue (double controlValue) const + { + return (this->*m_toNativeValue) (controlValue); + } + + std::string toString (double nativeValue) const + { + return (this->*m_toString) (nativeValue); + } + + double clamp (double nativeValue) const; + + // + // These routines are used as function pointers when + // constructing the various ParamInfo used by filters + // + + double Int_toControlValue (double nativeValue) const; + double Int_toNativeValue (double controlValue) const; + + double Real_toControlValue (double nativeValue) const; + double Real_toNativeValue (double controlValue) const; + + double Log_toControlValue (double nativeValue) const; + double Log_toNativeValue (double controlValue) const; + + double Pow2_toControlValue (double nativeValue) const; + double Pow2_toNativeValue (double controlValue) const; + + std::string Int_toString (double nativeValue) const; + std::string Hz_toString (double nativeValue) const; + std::string Real_toString (double nativeValue) const; + std::string Db_toString (double nativeValue) const; + + // + // Creates the specified ParamInfo + // + + static ParamInfo defaultSampleRateParam (); + static ParamInfo defaultCutoffFrequencyParam (); + static ParamInfo defaultCenterFrequencyParam (); + static ParamInfo defaultQParam (); + static ParamInfo defaultBandwidthParam (); + static ParamInfo defaultBandwidthHzParam (); + static ParamInfo defaultGainParam (); + static ParamInfo defaultSlopeParam (); + static ParamInfo defaultRippleDbParam (); + static ParamInfo defaultStopDbParam (); + static ParamInfo defaultRolloffParam (); + static ParamInfo defaultPoleRhoParam (); + static ParamInfo defaultPoleThetaParam (); + static ParamInfo defaultZeroRhoParam (); + static ParamInfo defaultZeroThetaParam (); + static ParamInfo defaultPoleRealParam (); + static ParamInfo defaultZeroRealParam (); + +private: + ParamID m_id; + const char* m_szLabel; + const char* m_szName; + double m_arg1; + double m_arg2; + double m_defaultNativeValue; + toControlValue_t m_toControlValue; + toNativeValue_t m_toNativeValue; + toString_t m_toString; +}; + +} + +#endif diff --git a/Source/Dsp/PoleFilter.cpp b/Source/Dsp/PoleFilter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6794554df70a36b153372501b8f7dd4232327db7 --- /dev/null +++ b/Source/Dsp/PoleFilter.cpp @@ -0,0 +1,337 @@ +/******************************************************************************* + +"A Collection of Useful C++ Classes for Digital Signal Processing" + By Vincent Falco + +Official project location: +http://code.google.com/p/dspfilterscpp/ + +See Documentation.cpp for contact information, notes, and bibliography. + +-------------------------------------------------------------------------------- + +License: MIT License (http://www.opensource.org/licenses/mit-license.php) +Copyright (c) 2009 by Vincent Falco + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*******************************************************************************/ + +#include "Common.h" +#include "PoleFilter.h" + +namespace Dsp { + +//------------------------------------------------------------------------------ + +complex_t LowPassTransform::transform (complex_t c) +{ + if (c == infinity()) + return complex_t (-1, 0); + + // frequency transform + c = f * c; + + // bilinear low pass transform + return (1. + c) / (1. - c); +} + +LowPassTransform::LowPassTransform (double fc, + LayoutBase& digital, + LayoutBase const& analog) +{ + digital.reset (); + + // prewarp + f = tan (doublePi * fc); + + const int numPoles = analog.getNumPoles (); + const int pairs = numPoles / 2; + for (int i = 0; i < pairs; ++i) + { + const PoleZeroPair& pair = analog[i]; + digital.addPoleZeroConjugatePairs (transform (pair.poles.first), + transform (pair.zeros.first)); + } + + if (numPoles & 1) + { + const PoleZeroPair& pair = analog[pairs]; + digital.add (transform (pair.poles.first), + transform (pair.zeros.first)); + } + + digital.setNormal (analog.getNormalW(), + analog.getNormalGain()); +} + +//------------------------------------------------------------------------------ + +complex_t HighPassTransform::transform (complex_t c) +{ + if (c == infinity()) + return complex_t (1, 0); + + // frequency transform + c = f * c; + + // bilinear high pass transform + return - (1. + c) / (1. - c); +} + +HighPassTransform::HighPassTransform (double fc, + LayoutBase& digital, + LayoutBase const& analog) +{ + digital.reset (); + + // prewarp + f = 1. / tan (doublePi * fc); + + const int numPoles = analog.getNumPoles (); + const int pairs = numPoles / 2; + for (int i = 0; i < pairs; ++i) + { + const PoleZeroPair& pair = analog[i]; + digital.addPoleZeroConjugatePairs (transform (pair.poles.first), + transform (pair.zeros.first)); + } + + if (numPoles & 1) + { + const PoleZeroPair& pair = analog[pairs]; + digital.add (transform (pair.poles.first), + transform (pair.zeros.first)); + } + + digital.setNormal (doublePi - analog.getNormalW(), + analog.getNormalGain()); +} + +//------------------------------------------------------------------------------ + +BandPassTransform::BandPassTransform (double fc, + double fw, + LayoutBase& digital, + LayoutBase const& analog) +{ + // handle degenerate cases efficiently + // THIS DOESNT WORK because the cascade states won't match +#if 0 + const double fw_2 = fw / 2; + if (fc - fw_2 < 0) + { + LowPassTransform::transform (fc + fw_2, digital, analog); + } + else if (fc + fw_2 >= 0.5) + { + HighPassTransform::transform (fc - fw_2, digital, analog); + } + else +#endif + + digital.reset (); + + const double ww = 2 * doublePi * fw; + + // pre-calcs + wc2 = 2 * doublePi * fc - (ww / 2); + wc = wc2 + ww; + + // what is this crap? + if (wc2 < 1e-8) + wc2 = 1e-8; + if (wc > doublePi-1e-8) + wc = doublePi-1e-8; + + a = cos ((wc + wc2) * 0.5) / + cos ((wc - wc2) * 0.5); + b = 1 / tan ((wc - wc2) * 0.5); + a2 = a * a; + b2 = b * b; + ab = a * b; + ab_2 = 2 * ab; + + const int numPoles = analog.getNumPoles (); + const int pairs = numPoles / 2; + for (int i = 0; i < pairs; ++i) + { + const PoleZeroPair& pair = analog[i]; + ComplexPair p1 = transform (pair.poles.first); + ComplexPair z1 = transform (pair.zeros.first); + + // + // Optimize out the calculations for conjugates for Release builds + // +#ifndef NDEBUG + ComplexPair p2 = transform (pair.poles.second); + ComplexPair z2 = transform (pair.zeros.second); + assert (p2.first == std::conj (p1.first)); + assert (p2.second == std::conj (p1.second)); +#endif + + digital.addPoleZeroConjugatePairs (p1.first, z1.first); + digital.addPoleZeroConjugatePairs (p1.second, z1.second); + } + + if (numPoles & 1) + { + ComplexPair poles = transform (analog[pairs].poles.first); + ComplexPair zeros = transform (analog[pairs].zeros.first); + + digital.add (poles, zeros); + } + + double wn = analog.getNormalW(); + digital.setNormal (2 * atan (sqrt (tan ((wc + wn)* 0.5) * tan((wc2 + wn)* 0.5))), + analog.getNormalGain()); +} + +ComplexPair BandPassTransform::transform (complex_t c) +{ + if (c == infinity()) + return ComplexPair (-1, 1); + + c = (1. + c) / (1. - c); // bilinear + + complex_t v = 0; + v = addmul (v, 4 * (b2 * (a2 - 1) + 1), c); + v += 8 * (b2 * (a2 - 1) - 1); + v *= c; + v += 4 * (b2 * (a2 - 1) + 1); + v = std::sqrt (v); + + complex_t u = -v; + u = addmul (u, ab_2, c); + u += ab_2; + + v = addmul (v, ab_2, c); + v += ab_2; + + complex_t d = 0; + d = addmul (d, 2 * (b - 1), c) + 2 * (1 + b); + + return ComplexPair (u/d, v/d); +} + +//------------------------------------------------------------------------------ + +BandStopTransform::BandStopTransform (double fc, + double fw, + LayoutBase& digital, + LayoutBase const& analog) +{ + digital.reset (); + + const double ww = 2 * doublePi * fw; + + wc2 = 2 * doublePi * fc - (ww / 2); + wc = wc2 + ww; + + // this is crap + if (wc2 < 1e-8) + wc2 = 1e-8; + if (wc > doublePi-1e-8) + wc = doublePi-1e-8; + + a = cos ((wc + wc2) * .5) / + cos ((wc - wc2) * .5); + b = tan ((wc - wc2) * .5); + a2 = a * a; + b2 = b * b; + + const int numPoles = analog.getNumPoles (); + const int pairs = numPoles / 2; + for (int i = 0; i < pairs; ++i) + { + const PoleZeroPair& pair = analog[i]; + ComplexPair p = transform (pair.poles.first); + ComplexPair z = transform (pair.zeros.first); + + // + // Optimize out the calculations for conjugates for Release builds + // +#ifdef NDEBUG + // trick to get the conjugate + if (z.second == z.first) + z.second = std::conj (z.first); + +#else + // Do the full calculation to verify correctness + ComplexPair pc = transform (analog[i].poles.second); + ComplexPair zc = transform (analog[i].zeros.second); + + // get the conjugates into pc and zc + if (zc.first == z.first) + std::swap (zc.first, zc.second); + + assert (pc.first == std::conj (p.first)); + assert (pc.second == std::conj (p.second)); + assert (zc.first == std::conj (z.first)); + assert (zc.second == std::conj (z.second)); + +#endif + + digital.addPoleZeroConjugatePairs (p.first, z.first); + digital.addPoleZeroConjugatePairs (p.second, z.second); + } + + if (numPoles & 1) + { + ComplexPair poles = transform (analog[pairs].poles.first); + ComplexPair zeros = transform (analog[pairs].zeros.first); + + digital.add (poles, zeros); + } + + if (fc < 0.25) + digital.setNormal (doublePi, analog.getNormalGain()); + else + digital.setNormal (0, analog.getNormalGain()); +} + +ComplexPair BandStopTransform::transform (complex_t c) +{ + if (c == infinity()) + c = -1; + else + c = (1. + c) / (1. - c); // bilinear + + complex_t u (0); + u = addmul (u, 4 * (b2 + a2 - 1), c); + u += 8 * (b2 - a2 + 1); + u *= c; + u += 4 * (a2 + b2 - 1); + u = std::sqrt (u); + + complex_t v = u * -.5; + v += a; + v = addmul (v, -a, c); + + u *= .5; + u += a; + u = addmul (u, -a, c); + + complex_t d (b + 1); + d = addmul (d, b-1, c); + + return ComplexPair (u/d, v/d); +} + +} diff --git a/Source/Dsp/PoleFilter.h b/Source/Dsp/PoleFilter.h new file mode 100644 index 0000000000000000000000000000000000000000..76eeb6eff77e9cb5ca75552898104bdab06aa2ef --- /dev/null +++ b/Source/Dsp/PoleFilter.h @@ -0,0 +1,217 @@ +/******************************************************************************* + +"A Collection of Useful C++ Classes for Digital Signal Processing" + By Vincent Falco + +Official project location: +http://code.google.com/p/dspfilterscpp/ + +See Documentation.cpp for contact information, notes, and bibliography. + +-------------------------------------------------------------------------------- + +License: MIT License (http://www.opensource.org/licenses/mit-license.php) +Copyright (c) 2009 by Vincent Falco + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*******************************************************************************/ + +#ifndef DSPFILTERS_POLEFILTER_H +#define DSPFILTERS_POLEFILTER_H + +#include "Common.h" +#include "MathSupplement.h" +#include "Cascade.h" + +namespace Dsp { + +/* + * Base for filters designed via algorithmic placement of poles and zeros. + * + * Typically, the filter is first designed as a half-band low pass or + * low shelf analog filter (s-plane). Then, using a transformation such + * as the ones from Constantinides, the poles and zeros of the analog filter + * are calculated in the z-plane. + * + */ + +// Factored implementations to reduce template instantiations + +class PoleFilterBase2 : public Cascade +{ +public: + // This gets the poles/zeros directly from the digital + // prototype. It is used to double check the correctness + // of the recovery of pole/zeros from biquad coefficients. + // + // It can also be used to accelerate the interpolation + // of pole/zeros for parameter modulation, since a pole + // filter already has them calculated + +#if 1 + // Commenting this out will pass the call to the Cascade, + // which tries to compute the poles and zeros from the biquad + // coefficients. + std::vector<PoleZeroPair> getPoleZeros () const + { + std::vector<PoleZeroPair> vpz; + const int pairs = (m_digitalProto.getNumPoles () + 1) / 2; + for (int i = 0; i < pairs; ++i) + vpz.push_back (m_digitalProto[i]); + return vpz; + } +#endif + +protected: + LayoutBase m_digitalProto; +}; + +// Serves a container to hold the analog prototype +// and the digital pole/zero layout. +template <class AnalogPrototype> +class PoleFilterBase : public PoleFilterBase2 +{ +protected: + void setPrototypeStorage (const LayoutBase& analogStorage, + const LayoutBase& digitalStorage) + { + m_analogProto.setStorage (analogStorage); + m_digitalProto = digitalStorage; + } + +protected: + AnalogPrototype m_analogProto; +}; + +//------------------------------------------------------------------------------ + +// Storage for pole filters +template <class BaseClass, + int MaxAnalogPoles, + int MaxDigitalPoles = MaxAnalogPoles> +struct PoleFilter : BaseClass + , CascadeStages <(MaxDigitalPoles + 1) / 2> +{ + PoleFilter () + { + // This glues together the factored base classes + // with the templatized storage classes. + BaseClass::setCascadeStorage (this->getCascadeStorage()); + BaseClass::setPrototypeStorage (m_analogStorage, m_digitalStorage); + } + +private: + Layout <MaxAnalogPoles> m_analogStorage; + Layout <MaxDigitalPoles> m_digitalStorage; +}; + +//------------------------------------------------------------------------------ + +/* + * s-plane to z-plane transforms + * + * For pole filters, an analog prototype is created via placement of + * poles and zeros in the s-plane. The analog prototype is either + * a halfband low pass or a halfband low shelf. The poles, zeros, + * and normalization parameters are transformed into the z-plane + * using variants of the bilinear transformation. + * + */ + +// low pass to low pass +class LowPassTransform +{ +public: + LowPassTransform (double fc, + LayoutBase& digital, + LayoutBase const& analog); + +private: + complex_t transform (complex_t c); + + double f; +}; + +//------------------------------------------------------------------------------ + +// low pass to high pass +class HighPassTransform +{ +public: + HighPassTransform (double fc, + LayoutBase& digital, + LayoutBase const& analog); + +private: + complex_t transform (complex_t c); + + double f; +}; + +//------------------------------------------------------------------------------ + +// low pass to band pass transform +class BandPassTransform +{ + +public: + BandPassTransform (double fc, + double fw, + LayoutBase& digital, + LayoutBase const& analog); + +private: + ComplexPair transform (complex_t c); + + double wc; + double wc2; + double a; + double b; + double a2; + double b2; + double ab; + double ab_2; +}; + +//------------------------------------------------------------------------------ + +// low pass to band stop transform +class BandStopTransform +{ +public: + BandStopTransform (double fc, + double fw, + LayoutBase& digital, + LayoutBase const& analog); + +private: + ComplexPair transform (complex_t c); + + double wc; + double wc2; + double a; + double b; + double a2; + double b2; +}; + +} + +#endif diff --git a/Source/Dsp/RBJ.cpp b/Source/Dsp/RBJ.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2a71b98af86c1bb5d0cec3fbf0b5710eb5fdbcc3 --- /dev/null +++ b/Source/Dsp/RBJ.cpp @@ -0,0 +1,207 @@ +/******************************************************************************* + +"A Collection of Useful C++ Classes for Digital Signal Processing" + By Vincent Falco + +Official project location: +http://code.google.com/p/dspfilterscpp/ + +See Documentation.cpp for contact information, notes, and bibliography. + +-------------------------------------------------------------------------------- + +License: MIT License (http://www.opensource.org/licenses/mit-license.php) +Copyright (c) 2009 by Vincent Falco + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*******************************************************************************/ + +#include "Common.h" +#include "RBJ.h" + +namespace Dsp { + +namespace RBJ { + +void LowPass::setup (double sampleRate, + double cutoffFrequency, + double q) +{ + double w0 = 2 * doublePi * cutoffFrequency / sampleRate; + double cs = cos (w0); + double sn = sin (w0); + double AL = sn / (2 * q); + double b0 = (1 - cs) / 2; + double b1 = 1 - cs; + double b2 = (1 - cs) / 2; + double a0 = 1 + AL; + double a1 = -2 * cs; + double a2 = 1 - AL; + setCoefficients (a0, a1, a2, b0, b1, b2); +} + +void HighPass::setup (double sampleRate, + double cutoffFrequency, + double q) +{ + double w0 = 2 * doublePi * cutoffFrequency / sampleRate; + double cs = cos (w0); + double sn = sin (w0); + double AL = sn / ( 2 * q ); + double b0 = (1 + cs) / 2; + double b1 = -(1 + cs); + double b2 = (1 + cs) / 2; + double a0 = 1 + AL; + double a1 = -2 * cs; + double a2 = 1 - AL; + setCoefficients (a0, a1, a2, b0, b1, b2); +} + +void BandPass1::setup (double sampleRate, + double centerFrequency, + double bandWidth) +{ + double w0 = 2 * doublePi * centerFrequency / sampleRate; + double cs = cos (w0); + double sn = sin (w0); + double AL = sn / ( 2 * bandWidth ); + double b0 = bandWidth * AL;// sn / 2; + double b1 = 0; + double b2 = -bandWidth * AL;//-sn / 2; + double a0 = 1 + AL; + double a1 = -2 * cs; + double a2 = 1 - AL; + setCoefficients (a0, a1, a2, b0, b1, b2); +} + +void BandPass2::setup (double sampleRate, + double centerFrequency, + double bandWidth) +{ + double w0 = 2 * doublePi * centerFrequency / sampleRate; + double cs = cos (w0); + double sn = sin (w0); + double AL = sn / ( 2 * bandWidth ); + double b0 = AL; + double b1 = 0; + double b2 = -AL; + double a0 = 1 + AL; + double a1 = -2 * cs; + double a2 = 1 - AL; + setCoefficients (a0, a1, a2, b0, b1, b2); +} + +void BandStop::setup (double sampleRate, + double centerFrequency, + double bandWidth) +{ + double w0 = 2 * doublePi * centerFrequency / sampleRate; + double cs = cos (w0); + double sn = sin (w0); + double AL = sn / ( 2 * bandWidth ); + double b0 = 1; + double b1 = -2 * cs; + double b2 = 1; + double a0 = 1 + AL; + double a1 = -2 * cs; + double a2 = 1 - AL; + setCoefficients (a0, a1, a2, b0, b1, b2); +} + +void LowShelf::setup (double sampleRate, + double cutoffFrequency, + double gainDb, + double shelfSlope) +{ + double A = pow (10, gainDb/40); + double w0 = 2 * doublePi * cutoffFrequency / sampleRate; + double cs = cos (w0); + double sn = sin (w0); + double AL = sn / 2 * ::std::sqrt ((A + 1/A) * (1/shelfSlope - 1) + 2); + double sq = 2 * sqrt(A) * AL; + double b0 = A*( (A+1) - (A-1)*cs + sq ); + double b1 = 2*A*( (A-1) - (A+1)*cs ); + double b2 = A*( (A+1) - (A-1)*cs - sq ); + double a0 = (A+1) + (A-1)*cs + sq; + double a1 = -2*( (A-1) + (A+1)*cs ); + double a2 = (A+1) + (A-1)*cs - sq; + setCoefficients (a0, a1, a2, b0, b1, b2); +} + +void HighShelf::setup (double sampleRate, + double cutoffFrequency, + double gainDb, + double shelfSlope) +{ + double A = pow (10, gainDb/40); + double w0 = 2 * doublePi * cutoffFrequency / sampleRate; + double cs = cos (w0); + double sn = sin (w0); + double AL = sn / 2 * ::std::sqrt ((A + 1/A) * (1/shelfSlope - 1) + 2); + double sq = 2 * sqrt(A) * AL; + double b0 = A*( (A+1) - (A-1)*cs + sq ); + double b1 = -2*A*( (A-1) - (A+1)*cs ); + double b2 = A*( (A+1) - (A-1)*cs - sq ); + double a0 = (A+1) + (A-1)*cs + sq; + double a1 = 2*( (A-1) + (A+1)*cs ); + double a2 = (A+1) + (A-1)*cs - sq; + setCoefficients (a0, a1, a2, b0, b1, b2); +} + +void BandShelf::setup (double sampleRate, + double centerFrequency, + double gainDb, + double bandWidth) +{ + double A = pow (10, gainDb/40); + double w0 = 2 * doublePi * centerFrequency / sampleRate; + double cs = cos(w0); + double sn = sin(w0); + double AL = sn * sinh( doubleLn2/2 * bandWidth * w0/sn ); + assert (!Dsp::is_nan (AL)); + double b0 = 1 + AL * A; + double b1 = -2 * cs; + double b2 = 1 - AL * A; + double a0 = 1 + AL / A; + double a1 = -2 * cs; + double a2 = 1 - AL / A; + setCoefficients (a0, a1, a2, b0, b1, b2); +} + +void AllPass::setup (double sampleRate, + double phaseFrequency, + double q) +{ + double w0 = 2 * doublePi * phaseFrequency / sampleRate; + double cs = cos (w0); + double sn = sin (w0); + double AL = sn / ( 2 * q ); + double b0 = 1 - AL; + double b1 = -2 * cs; + double b2 = 1 + AL; + double a0 = 1 + AL; + double a1 = -2 * cs; + double a2 = 1 - AL; + setCoefficients (a0, a1, a2, b0, b1, b2); +} + +} + +} diff --git a/Source/Dsp/RBJ.h b/Source/Dsp/RBJ.h new file mode 100644 index 0000000000000000000000000000000000000000..35899522e6a715c258bf4d1b56c715308225ff40 --- /dev/null +++ b/Source/Dsp/RBJ.h @@ -0,0 +1,335 @@ +/******************************************************************************* + +"A Collection of Useful C++ Classes for Digital Signal Processing" + By Vincent Falco + +Official project location: +http://code.google.com/p/dspfilterscpp/ + +See Documentation.cpp for contact information, notes, and bibliography. + +-------------------------------------------------------------------------------- + +License: MIT License (http://www.opensource.org/licenses/mit-license.php) +Copyright (c) 2009 by Vincent Falco + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*******************************************************************************/ + +#ifndef DSPFILTERS_RBJ_H +#define DSPFILTERS_RBJ_H + +#include "Common.h" +#include "Biquad.h" +#include "Design.h" +#include "Filter.h" + +namespace Dsp { + +/* + * Filter realizations based on Robert Bristol-Johnson formulae: + * + * http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt + * + */ + +namespace RBJ { + +// +// Raw filters +// + +struct LowPass : BiquadBase +{ + void setup (double sampleRate, + double cutoffFrequency, + double q); +}; + +struct HighPass : BiquadBase +{ + void setup (double sampleRate, + double cutoffFrequency, + double q); +}; + +struct BandPass1 : BiquadBase +{ + // (constant skirt gain, peak gain = Q) + void setup (double sampleRate, + double centerFrequency, + double bandWidth); +}; + +struct BandPass2 : BiquadBase +{ + // (constant 0 dB peak gain) + void setup (double sampleRate, + double centerFrequency, + double bandWidth); +}; + +struct BandStop : BiquadBase +{ + void setup (double sampleRate, + double centerFrequency, + double bandWidth); +}; + +struct LowShelf : BiquadBase +{ + void setup (double sampleRate, + double cutoffFrequency, + double gainDb, + double shelfSlope); +}; + +struct HighShelf : BiquadBase +{ + void setup (double sampleRate, + double cutoffFrequency, + double gainDb, + double shelfSlope); +}; + +struct BandShelf : BiquadBase +{ + void setup (double sampleRate, + double centerFrequency, + double gainDb, + double bandWidth); +}; + +struct AllPass : BiquadBase +{ + void setup (double sampleRate, + double phaseFrequency, + double q); +}; + +//------------------------------------------------------------------------------ + +// +// Gui-friendly Design layer +// + +namespace Design { + +struct TypeIBase : DesignBase +{ + enum + { + NumParams = 3 + }; + + static int getNumParams () + { + return 3; + } + + static const ParamInfo getParamInfo_1 () + { + return ParamInfo::defaultCutoffFrequencyParam (); + } + + static const ParamInfo getParamInfo_2 () + { + return ParamInfo::defaultQParam (); + } +}; + +template <class FilterClass> +struct TypeI : TypeIBase, FilterClass +{ + void setParams (const Params& params) + { + FilterClass::setup (params[0], params[1], params[2]); + } +}; + +struct TypeIIBase : DesignBase +{ + enum + { + NumParams = 3 + }; + + static int getNumParams () + { + return 3; + } + + static const ParamInfo getParamInfo_1 () + { + return ParamInfo::defaultCenterFrequencyParam (); + } + + static const ParamInfo getParamInfo_2 () + { + return ParamInfo::defaultBandwidthParam (); + } +}; + +template <class FilterClass> +struct TypeII : TypeIIBase, FilterClass +{ + void setParams (const Params& params) + { + FilterClass::setup (params[0], params[1], params[2]); + } +}; + +struct TypeIIIBase : DesignBase +{ + enum + { + NumParams = 4 + }; + + static int getNumParams () + { + return 4; + } + + static const ParamInfo getParamInfo_1 () + { + return ParamInfo::defaultCutoffFrequencyParam (); + } + + static const ParamInfo getParamInfo_2 () + { + return ParamInfo::defaultGainParam (); + } + + static const ParamInfo getParamInfo_3 () + { + return ParamInfo::defaultSlopeParam (); + } +}; + +template <class FilterClass> +struct TypeIII : TypeIIIBase, FilterClass +{ + void setParams (const Params& params) + { + FilterClass::setup (params[0], params[1], params[2], params[3]); + } +}; + +struct TypeIVBase : DesignBase +{ + enum + { + NumParams = 4 + }; + + static int getNumParams () + { + return 4; + } + + static const ParamInfo getParamInfo_1 () + { + return ParamInfo::defaultCenterFrequencyParam (); + } + + static const ParamInfo getParamInfo_2 () + { + return ParamInfo::defaultGainParam (); + } + + static const ParamInfo getParamInfo_3 () + { + return ParamInfo::defaultBandwidthParam (); + } +}; + +template <class FilterClass> +struct TypeIV : TypeIVBase , FilterClass +{ + void setParams (const Params& params) + { + FilterClass::setup (params[0], params[1], params[2], params[3]); + } +}; + +//------------------------------------------------------------------------------ + +struct LowPass : TypeI <RBJ::LowPass> +{ + static Kind getKind () { return kindLowPass; } + static const char* getName() { return "RBJ Low Pass"; } +}; + +struct HighPass : TypeI <RBJ::HighPass> +{ + static Kind getKind () { return kindHighPass; } + static const char* getName() { return "RBJ High Pass"; } +}; + +struct BandPass1 : TypeII <RBJ::BandPass1> +{ + static Kind getKind () { return kindBandPass; } + static const char* getName() { return "RBJ Band Pass 1"; } +}; + +struct BandPass2 : TypeII <RBJ::BandPass2> +{ + static Kind getKind () { return kindBandPass; } + static const char* getName() { return "RBJ Band Pass 2"; } +}; + +struct BandStop : TypeII <RBJ::BandStop> +{ + static Kind getKind () { return kindBandStop; } + static const char* getName() { return "RBJ Band Stop"; } +}; + +struct LowShelf : TypeIII <RBJ::LowShelf> +{ + static Kind getKind () { return kindLowShelf; } + static const char* getName() { return "RBJ Low Shelf"; } +}; + +struct HighShelf : TypeIII <RBJ::HighShelf> +{ + static Kind getKind () { return kindHighShelf; } + static const char* getName() { return "RBJ High Shelf"; } +}; + +struct BandShelf : TypeIV <RBJ::BandShelf> +{ + static Kind getKind () { return kindBandShelf; } + static const char* getName() { return "RBJ Band Shelf"; } +}; + +struct AllPass : TypeI <RBJ::AllPass> +{ + static Kind getKind () { return kindOther; } + static const char* getName() { return "RBJ All Pass"; } +}; + +} + +} + +} + +#endif diff --git a/Source/Dsp/RootFinder.cpp b/Source/Dsp/RootFinder.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f00cca087d49d99f6b18f198558efcbe1a89656e --- /dev/null +++ b/Source/Dsp/RootFinder.cpp @@ -0,0 +1,187 @@ +/******************************************************************************* + +"A Collection of Useful C++ Classes for Digital Signal Processing" + By Vincent Falco + +Official project location: +http://code.google.com/p/dspfilterscpp/ + +See Documentation.cpp for contact information, notes, and bibliography. + +-------------------------------------------------------------------------------- + +License: MIT License (http://www.opensource.org/licenses/mit-license.php) +Copyright (c) 2009 by Vincent Falco + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*******************************************************************************/ + +#include "Common.h" +#include "RootFinder.h" +#include <stdexcept> + +namespace Dsp { + +void RootFinderBase::solve (int degree, + bool polish, + bool doSort) +{ + assert (degree <= m_maxdegree); + + const double EPS = 1.0e-30; + + int its; + complex_t x, b, c; + + int m = degree; + + // copy coefficients + for (int j = 0; j <= m; ++j) + m_ad[j] = m_a[j]; + + // for each root + for (int j = m - 1; j >= 0; --j) + { + // initial guess at 0 + x = 0.0; + laguerre (j + 1, m_ad, x, its); + + if (fabs (std::imag(x)) <= 2.0 * EPS * fabs (std::real(x))) + x = complex_t (std::real(x), 0.0); + + m_root[j] = x; + + // deflate + b = m_ad[j+1]; + for (int jj = j; jj >= 0; --jj) + { + c = m_ad[jj]; + m_ad[jj] = b; + b = x * b + c; + } + } + + if (polish) + for (int j = 0; j < m; ++j) + laguerre (degree, m_a, m_root[j], its); + + if (doSort) + sort (degree); +} + +void RootFinderBase::sort (int degree) +{ + for (int j = 1; j < degree; ++j) + { + complex_t x = m_root[j]; + + int i; + for (i = j - 1; i >= 0; --i ) + { + if (m_root[i].imag() >= x.imag()) + break; + + m_root[i+1] = m_root[i]; + } + + m_root[i+1] = x; + } +} + +//------------------------------------------------------------------------------ + +void RootFinderBase::laguerre (int degree, + complex_t a[], + complex_t& x, + int& its) +{ + const int MR = 8, MT = 10, MAXIT = MT * MR; + const double EPS = std::numeric_limits<double>::epsilon(); + + static const double frac[MR + 1] = + {0.0, 0.5, 0.25, 0.75, 0.13, 0.38, 0.62, 0.88, 1.0}; + + complex_t dx, x1, b, d, f, g, h, sq, gp, gm, g2; + + int m = degree; + for (int iter = 1; iter <= MAXIT; ++iter) + { + its = iter; + b = a[m]; + double err = std::abs(b); + d = f = 0.0; + double abx = std::abs(x); + for (int j = m - 1; j >= 0; --j) + { + f = x * f + d; + d = x * d + b; + b = x * b + a[j]; + err = std::abs(b) + abx * err; + } + err *= EPS; + if (std::abs(b) <= err) + return; + g = d / b; + g2 = g * g; + h = g2 - 2.0 * f / b; + + sq = sqrt (double(m - 1) * (double(m) * h - g2)); + gp = g + sq; + gm = g - sq; + + double abp = std::abs (gp); + double abm = std::abs (gm); + if (abp < abm) + gp = gm; + dx = std::max(abp, abm) > 0.0 ? double(m) / gp : std::polar (1 + abx, double(iter)); + x1 = x - dx; + if (x == x1) + return; + if (iter % MT != 0) + x = x1; + else + x -= frac[iter / MT] * dx; + } + + throw std::logic_error ("laguerre failed"); +} + +//------------------------------------------------------------------------------ + +complex_t RootFinderBase::eval (int degree, + const complex_t& x ) +{ + complex_t y; + + if (x != 0.) + { + for (int i = 0; i <= degree; ++i) + y += m_a[i] * pow (x, double(i)); + } + else + { + y = m_a[0]; + } + + return y; +} + + +} diff --git a/Source/Dsp/RootFinder.h b/Source/Dsp/RootFinder.h new file mode 100644 index 0000000000000000000000000000000000000000..2202ba98eb4e0bd9d1f10533021eea1a22010da5 --- /dev/null +++ b/Source/Dsp/RootFinder.h @@ -0,0 +1,129 @@ +/******************************************************************************* + +"A Collection of Useful C++ Classes for Digital Signal Processing" + By Vincent Falco + +Official project location: +http://code.google.com/p/dspfilterscpp/ + +See Documentation.cpp for contact information, notes, and bibliography. + +-------------------------------------------------------------------------------- + +License: MIT License (http://www.opensource.org/licenses/mit-license.php) +Copyright (c) 2009 by Vincent Falco + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*******************************************************************************/ + +#ifndef DSPFILTERS_ROOTFINDER_H +#define DSPFILTERS_ROOTFINDER_H + +#include "Common.h" +#include "Types.h" + +namespace Dsp { + +// +// Finds the complex roots of the given polynomial with +// complex-valued coefficients using a numerical method. +// + +class RootFinderBase +{ +public: + struct Array + { + Array (int max, complex_t* values) + // : m_max (max) + // , m_values (values) + { + } + + //complex_t& operator[] (int index) + //{ + //}; + }; + + // + // Find roots of polynomial f(x)=a[0]+a[1]*x+a[2]*x^2...+a[degree]*x^degree + // The input coefficients are set using coef()[]. + // The solutions are placed in roots. + // + void solve (int degree, + bool polish = true, + bool doSort = true); + + // Evaluates the polynomial at x + complex_t eval (int degree, + const complex_t& x); + + // Direct access to the input coefficient array of size degree+1. + complex_t* coef() + { + return m_a; + } + + // Direct access to the resulting roots array of size degree + complex_t* root() + { + return m_root; + } + + // sort the roots by descending imaginary part + void sort (int degree); + +private: + // Improves x as a root using Laguerre's method. + // The input coefficient array has degree+1 elements. + void laguerre (int degree, + complex_t a[], + complex_t& x, + int& its); + +protected: + int m_maxdegree; + complex_t* m_a; // input coefficients (m_maxdegree+1 elements) + complex_t* m_ad; // copy of deflating coefficients + complex_t* m_root; // array of roots (maxdegree elements) +}; + +//------------------------------------------------------------------------------ + +template<int maxdegree> +struct RootFinder : RootFinderBase +{ + RootFinder() + { + m_maxdegree = maxdegree; + m_a = m_a0; + m_ad = m_ad0; + m_root = m_r; + } + +private: + complex_t m_a0 [maxdegree+1]; + complex_t m_ad0[maxdegree+1]; + complex_t m_r [maxdegree]; +}; + +} + +#endif diff --git a/Source/Dsp/SmoothedFilter.h b/Source/Dsp/SmoothedFilter.h new file mode 100644 index 0000000000000000000000000000000000000000..72e089d1691a8048b98e576614b0aa3cf5ff43e8 --- /dev/null +++ b/Source/Dsp/SmoothedFilter.h @@ -0,0 +1,154 @@ +/******************************************************************************* + +"A Collection of Useful C++ Classes for Digital Signal Processing" + By Vincent Falco + +Official project location: +http://code.google.com/p/dspfilterscpp/ + +See Documentation.cpp for contact information, notes, and bibliography. + +-------------------------------------------------------------------------------- + +License: MIT License (http://www.opensource.org/licenses/mit-license.php) +Copyright (c) 2009 by Vincent Falco + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*******************************************************************************/ + +#ifndef DSPFILTERS_SMOOTHEDFILTER_H +#define DSPFILTERS_SMOOTHEDFILTER_H + +#include "Common.h" +#include "Filter.h" + +namespace Dsp { + +/* + * Implements smooth modulation of time-varying filter parameters + * + */ +template <class DesignClass, + int Channels, + class StateType = DirectFormII> +class SmoothedFilterDesign + : public FilterDesign <DesignClass, + Channels, + StateType> +{ +public: + typedef FilterDesign <DesignClass, Channels, StateType> filter_type_t; + + SmoothedFilterDesign (int transitionSamples) + : m_transitionSamples (transitionSamples) + , m_remainingSamples (-1) // first time flag + { + } + + // Process a block of samples. + template <typename Sample> + void processBlock (int numSamples, + Sample* const* destChannelArray) + { + const int numChannels = this->getNumChannels(); + + // If this goes off it means setup() was never called + assert (m_remainingSamples >= 0); + + // first handle any transition samples + int remainingSamples = std::min (m_remainingSamples, numSamples); + + if (remainingSamples > 0) + { + // interpolate parameters for each sample + const double t = 1. / m_remainingSamples; + double dp[maxParameters]; + for (int i = 0; i < DesignClass::NumParams; ++i) + dp[i] = (this->getParams()[i] - m_transitionParams[i]) * t; + + for (int n = 0; n < remainingSamples; ++n) + { + for (int i = DesignClass::NumParams; --i >=0;) + m_transitionParams[i] += dp[i]; + + m_transitionFilter.setParams (m_transitionParams); + + for (int i = numChannels; --i >= 0;) + { + Sample* dest = destChannelArray[i]+n; + *dest = this->m_state[i].process (*dest, m_transitionFilter); + } + } + + m_remainingSamples -= remainingSamples; + + if (m_remainingSamples == 0) + m_transitionParams = this->getParams(); + } + + // do what's left + if (numSamples - remainingSamples > 0) + { + // no transition + for (int i = 0; i < numChannels; ++i) + this->m_design.process (numSamples - remainingSamples, + destChannelArray[i] + remainingSamples, + this->m_state[i]); + } + } + + void process (int numSamples, float* const* arrayOfChannels) + { + processBlock (numSamples, arrayOfChannels); + } + + void process (int numSamples, double* const* arrayOfChannels) + { + processBlock (numSamples, arrayOfChannels); + } + +protected: + void doSetParams (const Params& parameters) + { + if (m_remainingSamples >= 0) + { + m_remainingSamples = m_transitionSamples; + } + else + { + // first time + m_remainingSamples = 0; + m_transitionParams = parameters; + } + + filter_type_t::doSetParams (parameters); + } + +protected: + Params m_transitionParams; + DesignClass m_transitionFilter; + int m_transitionSamples; + + int m_remainingSamples; // remaining transition samples +}; + +} + +#endif diff --git a/Source/Dsp/State.cpp b/Source/Dsp/State.cpp new file mode 100644 index 0000000000000000000000000000000000000000..61c07cb3020ccccdf08dcd984decb497798f8411 --- /dev/null +++ b/Source/Dsp/State.cpp @@ -0,0 +1,43 @@ +/******************************************************************************* + +"A Collection of Useful C++ Classes for Digital Signal Processing" + By Vincent Falco + +Official project location: +http://code.google.com/p/dspfilterscpp/ + +See Documentation.cpp for contact information, notes, and bibliography. + +-------------------------------------------------------------------------------- + +License: MIT License (http://www.opensource.org/licenses/mit-license.php) +Copyright (c) 2009 by Vincent Falco + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*******************************************************************************/ + +#include "Common.h" +#include "State.h" + +namespace Dsp { + +//------------------------------------------------------------------------------ + +} diff --git a/Source/Dsp/State.h b/Source/Dsp/State.h new file mode 100644 index 0000000000000000000000000000000000000000..eed4edb934052c631f96b65b38ba4cb36e37ab85 --- /dev/null +++ b/Source/Dsp/State.h @@ -0,0 +1,323 @@ +/******************************************************************************* + +"A Collection of Useful C++ Classes for Digital Signal Processing" + By Vincent Falco + +Official project location: +http://code.google.com/p/dspfilterscpp/ + +See Documentation.cpp for contact information, notes, and bibliography. + +-------------------------------------------------------------------------------- + +License: MIT License (http://www.opensource.org/licenses/mit-license.php) +Copyright (c) 2009 by Vincent Falco + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*******************************************************************************/ + +#ifndef DSPFILTERS_STATE_H +#define DSPFILTERS_STATE_H + +#include "Common.h" +#include "Biquad.h" + +#include <stdexcept> + +namespace Dsp { + +/* + * Various forms of state information required to + * process channels of actual sample data. + * + */ + +//------------------------------------------------------------------------------ + +/* + * State for applying a second order section to a sample using Direct Form I + * + * Difference equation: + * + * y[n] = (b0/a0)*x[n] + (b1/a0)*x[n-1] + (b2/a0)*x[n-2] + * - (a1/a0)*y[n-1] - (a2/a0)*y[n-2] + */ +class DirectFormI +{ +public: + DirectFormI () + { + reset(); + } + + void reset () + { + m_x1 = 0; + m_x2 = 0; + m_y1 = 0; + m_y2 = 0; + } + + template <typename Sample> + inline Sample process1 (const Sample in, + const BiquadBase& s, + const double vsa) // very small amount + { + double out = s.m_b0*in + s.m_b1*m_x1 + s.m_b2*m_x2 + - s.m_a1*m_y1 - s.m_a2*m_y2 + + vsa; + m_x2 = m_x1; + m_y2 = m_y1; + m_x1 = in; + m_y1 = out; + + return static_cast<Sample> (out); + } + +protected: + double m_x2; // x[n-2] + double m_y2; // y[n-2] + double m_x1; // x[n-1] + double m_y1; // y[n-1] +}; + +//------------------------------------------------------------------------------ + +/* + * State for applying a second order section to a sample using Direct Form II + * + * Difference equation: + * + * v[n] = x[n] - (a1/a0)*v[n-1] - (a2/a0)*v[n-2] + * y(n) = (b0/a0)*v[n] + (b1/a0)*v[n-1] + (b2/a0)*v[n-2] + * + */ +class DirectFormII +{ +public: + DirectFormII () + { + reset (); + } + + void reset () + { + m_v1 = 0; + m_v2 = 0; + } + + template <typename Sample> + Sample process1 (const Sample in, + const BiquadBase& s, + const double vsa) + { + double w = in - s.m_a1*m_v1 - s.m_a2*m_v2 + vsa; + double out = s.m_b0*w + s.m_b1*m_v1 + s.m_b2*m_v2; + + m_v2 = m_v1; + m_v1 = w; + + return static_cast<Sample> (out); + } + +private: + double m_v1; // v[-1] + double m_v2; // v[-2] +}; + +//------------------------------------------------------------------------------ + +/* + * Transposed Direct Form I and II + * by lubomir i. ivanov (neolit123 [at] gmail) + * + * Reference: + * http://www.kvraudio.com/forum/viewtopic.php?p=4430351 + * + */ + +// I think this one is broken +class TransposedDirectFormI +{ +public: + TransposedDirectFormI () + { + reset (); + } + + void reset () + { + m_v = 0; + m_s1 = 0; + m_s1_1 = 0; + m_s2 = 0; + m_s2_1 = 0; + m_s3 = 0; + m_s3_1 = 0; + m_s4 = 0; + m_s4_1 = 0; + } + + template <typename Sample> + inline Sample process1 (const Sample in, + const BiquadBase& s, + const double vsa) + { + double out; + + // can be: in += m_s1_1; + m_v = in + m_s1_1; + out = s.m_b0*m_v + m_s3_1; + m_s1 = m_s2_1 - s.m_a1*m_v; + m_s2 = -s.m_a2*m_v; + m_s3 = s.m_b1*m_v + m_s4_1; + m_s4 = s.m_b2*m_v; + + m_s4_1 = m_s4; + m_s3_1 = m_s3; + m_s2_1 = m_s2; + m_s1_1 = m_s1; + + return static_cast<Sample> (out); + } + +private: + double m_v; + double m_s1; + double m_s1_1; + double m_s2; + double m_s2_1; + double m_s3; + double m_s3_1; + double m_s4; + double m_s4_1; +}; + +//------------------------------------------------------------------------------ + +class TransposedDirectFormII +{ +public: + TransposedDirectFormII () + { + reset (); + } + + void reset () + { + m_s1 = 0; + m_s1_1 = 0; + m_s2 = 0; + m_s2_1 = 0; + } + + template <typename Sample> + inline Sample process1 (const Sample in, + const BiquadBase& s, + const double vsa) + { + double out; + + out = m_s1_1 + s.m_b0*in + vsa; + m_s1 = m_s2_1 + s.m_b1*in - s.m_a1*out; + m_s2 = s.m_b2*in - s.m_a2*out; + m_s1_1 = m_s1; + m_s2_1 = m_s2; + + return static_cast<Sample> (out); + } + +private: + double m_s1; + double m_s1_1; + double m_s2; + double m_s2_1; +}; + +//------------------------------------------------------------------------------ + +// Holds an array of states suitable for multi-channel processing +template <int Channels, class StateType> +class ChannelsState +{ +public: + ChannelsState () + { + } + + const int getNumChannels() const + { + return Channels; + } + + void reset () + { + for (int i = 0; i < Channels; ++i) + m_state[i].reset(); + } + + StateType& operator[] (int index) + { + assert (index >= 0 && index < Channels); + return m_state[index]; + } + + template <class Filter, typename Sample> + void process (int numSamples, + Sample* const* arrayOfChannels, + Filter& filter) + { + for (int i = 0; i < Channels; ++i) + filter.process (numSamples, arrayOfChannels[i], m_state[i]); + } + +private: + StateType m_state[Channels]; +}; + +// Empty state, can't process anything +template <class StateType> +class ChannelsState <0, StateType> +{ +public: + const int getNumChannels() const + { + return 0; + } + + void reset () + { + throw std::logic_error ("attempt to reset empty ChannelState"); + } + + template <class FilterDesign, typename Sample> + void process (int numSamples, + Sample* const* arrayOfChannels, + FilterDesign& filter) + { + throw std::logic_error ("attempt to process empty ChannelState"); + } +}; + +//------------------------------------------------------------------------------ + +} + +#endif diff --git a/Source/Dsp/Types.h b/Source/Dsp/Types.h new file mode 100644 index 0000000000000000000000000000000000000000..c9445a7eb5abc833703d6ab309f124a7c040edb8 --- /dev/null +++ b/Source/Dsp/Types.h @@ -0,0 +1,139 @@ +/******************************************************************************* + +"A Collection of Useful C++ Classes for Digital Signal Processing" + By Vincent Falco + +Official project location: +http://code.google.com/p/dspfilterscpp/ + +See Documentation.cpp for contact information, notes, and bibliography. + +-------------------------------------------------------------------------------- + +License: MIT License (http://www.opensource.org/licenses/mit-license.php) +Copyright (c) 2009 by Vincent Falco + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*******************************************************************************/ + +#ifndef DSPFILTERS_TYPES_H +#define DSPFILTERS_TYPES_H + +#include "Common.h" +#include "MathSupplement.h" + +namespace Dsp { + +// A conjugate or real pair +struct ComplexPair : complex_pair_t +{ + ComplexPair () + { + } + + explicit ComplexPair (const complex_t& c1) + : complex_pair_t (c1, 0.) + { + assert (isReal()); + } + + ComplexPair (const complex_t& c1, + const complex_t& c2) + : complex_pair_t (c1, c2) + { + } + + bool isConjugate () const + { + return second == std::conj (first); + } + + bool isReal () const + { + return first.imag() == 0 && second.imag() == 0; + } + + // Returns true if this is either a conjugate pair, + // or a pair of reals where neither is zero. + bool isMatchedPair () const + { + if (first.imag() != 0) + return second == std::conj (first); + else + return second.imag () == 0 && + second.real () != 0 && + first.real () != 0; + } + + bool is_nan () const + { + return Dsp::is_nan (first) || Dsp::is_nan (second); + } +}; + +// A pair of pole/zeros. This fits in a biquad (but is missing the gain) +struct PoleZeroPair +{ + ComplexPair poles; + ComplexPair zeros; + + PoleZeroPair () { } + + // single pole/zero + PoleZeroPair (const complex_t& p, const complex_t& z) + : poles (p), zeros (z) + { + } + + // pole/zero pair + PoleZeroPair (const complex_t& p1, const complex_t& z1, + const complex_t& p2, const complex_t& z2) + : poles (p1, p2) + , zeros (z1, z2) + { + } + + bool isSinglePole () const + { + return poles.second == 0. && zeros.second == 0.; + } + + bool is_nan () const + { + return poles.is_nan() || zeros.is_nan(); + } +}; + +// Identifies the general class of filter +enum Kind +{ + kindLowPass, + kindHighPass, + kindBandPass, + kindBandStop, + kindLowShelf, + kindHighShelf, + kindBandShelf, + kindOther +}; + +} + +#endif diff --git a/Source/Dsp/Utilities.h b/Source/Dsp/Utilities.h new file mode 100644 index 0000000000000000000000000000000000000000..d0d3612bf9593933d403dd1859338ce3de356d7f --- /dev/null +++ b/Source/Dsp/Utilities.h @@ -0,0 +1,699 @@ +/******************************************************************************* + +"A Collection of Useful C++ Classes for Digital Signal Processing" + By Vincent Falco + +Official project location: +http://code.google.com/p/dspfilterscpp/ + +See Documentation.cpp for contact information, notes, and bibliography. + +-------------------------------------------------------------------------------- + +License: MIT License (http://www.opensource.org/licenses/mit-license.php) +Copyright (c) 2009 by Vincent Falco + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*******************************************************************************/ + +#ifndef DSPFILTERS_UTILITIES_H +#define DSPFILTERS_UTILITIES_H + +#include "Common.h" + +namespace Dsp { + +/* + * Utilities + * + * These routines are handy for manipulating buffers of samples. + * + */ + +//------------------------------------------------------------------------------ + +// Add src samples to dest, without clip or overflow checking. +template <class Td, + class Ts> +void add (int samples, + Td* dest, + Ts const* src, + int destSkip = 0, + int srcSkip = 0) +{ + if (srcSkip !=0 || destSkip != 0) + { + ++srcSkip; + ++destSkip; + while (--samples >= 0) + { + *dest = static_cast<Td>(*src); + dest += destSkip; + src += srcSkip; + } + } + else + { + while (--samples >= 0) + *dest++ += static_cast<Td>(*src++); + } +} + +// Multichannel add +template <typename Td, + typename Ts> +void add (int channels, + int samples, + Td* const* dest, + Ts const* const* src) +{ + for (int i = channels; --i >= 0;) + add (samples, dest[i], src[i]); +} + +//-------------------------------------------------------------------------- + +// Copy samples from src to dest, which may not overlap. Performs an implicit +// type conversion if Ts and Td are different (for example, float to double). +template <typename Td, + typename Ts> +void copy (int samples, + Td* dest, + Ts const* src, + int destSkip = 0, + int srcSkip = 0) +{ + if (srcSkip != 0) + { + if (destSkip != 0) + { + ++srcSkip; + ++destSkip; + while (--samples >= 0) + { + *dest++ = *src++; + dest += destSkip; + src += srcSkip; + } + } + else + { + ++srcSkip; + while (--samples >= 0) + { + *dest++ = *src++; + src += srcSkip; + } + } + } + else if (destSkip != 0) + { + ++destSkip; + while (--samples >= 0) + { + *dest = *src++; + dest += destSkip; + } + } + else + { + while (--samples >= 0) + *dest++ = *src++; + } +} + +// Wrapper that uses memcpy if there is no skip and the types are the same +template <typename Ty> +void copy (int samples, + Ty* dest, + Ty const* src, + int destSkip = 0, + int srcSkip = 0) +{ + if (destSkip != 0 || srcSkip != 0) + copy<Ty,Ty> (samples, dest, src, destSkip, srcSkip); + else + ::memcpy (dest, src, samples * sizeof(src[0])); +} + +// Copy a set of channels from src to dest, with implicit type conversion. +template <typename Td, + typename Ts> +void copy (int channels, + int samples, + Td* const* dest, + Ts const* const* src, + int destSkip = 0, + int srcSkip = 0) +{ + for (int i = channels; --i >= 0;) + copy (samples, dest[i], src[i], destSkip, srcSkip); +} + +//-------------------------------------------------------------------------- + +// Deinterleave channels. Performs implicit type conversion. +template <typename Td, typename Ts> +void deinterleave (int channels, + int samples, + Td* const* dest, + Ts const* src) +{ + assert (channels > 1); + + switch (channels) + { + case 2: + { + Td* l = dest[0]; + Td* r = dest[1]; + int n = (samples + 7) / 8; + switch (samples % 8) + { + case 0: do + { + *l++ = *src++; *r++ = *src++; + case 7: *l++ = *src++; *r++ = *src++; + case 6: *l++ = *src++; *r++ = *src++; + case 5: *l++ = *src++; *r++ = *src++; + case 4: *l++ = *src++; *r++ = *src++; + case 3: *l++ = *src++; *r++ = *src++; + case 2: *l++ = *src++; *r++ = *src++; + case 1: *l++ = *src++; *r++ = *src++; + } + while (--n > 0); + } + } + break; + + default: + { + for (int i = channels; --i >= 0;) + copy (samples, dest[i], src + i, 0, channels - 1); + } + break; + }; +} + +// Convenience for a stereo pair of channels +template <typename Td, + typename Ts> +void deinterleave (int samples, + Td* left, + Td* right, + Ts const* src) +{ + Td* dest[2]; + dest[0] = left; + dest[1] = right; + deinterleave (2, samples, dest, src); +} + +//-------------------------------------------------------------------------- + +// Fade src into dest +template <typename Td, + typename Ts, + typename Ty> +void fade (int samples, + Td* dest, + Ts const* src, + Ty start = 0, + Ty end = 1) +{ + Ty t = start; + Ty dt = (end - start) / samples; + + while (--samples >= 0) + { + *dest++ = static_cast<Td>(*dest + t * (*src++ - *dest)); + t += dt; + } +} + +// Fade src channels into dest channels +template <typename Td, + typename Ts, + typename Ty> +void fade (int channels, + int samples, + Td* const* dest, + Ts const* const* src, + Ty start = 0, + Ty end = 1) +{ + for (int i = channels; --i >= 0;) + fade (samples, dest[i], src[i], start, end); +} + +//-------------------------------------------------------------------------- + +// Interleave separate channels from source pointers to destination +// (Destination requires channels*frames samples of storage). Performs +// implicit type conversion. +template <typename Td, + typename Ts> +void interleave (int channels, + size_t samples, + Td* dest, + Ts const* const* src) +{ + assert (channels>1); + + if (samples==0) + return; + + switch (channels) + { + case 2: + { + const Ts* l = src[0]; + const Ts* r = src[1]; + + // note that Duff's Device only works when samples>0 + int n = (samples + 7) / 8; + switch (samples % 8) + { + case 0: do + { + *dest++ = *l++; *dest++ = *r++; + case 7: *dest++ = *l++; *dest++ = *r++; + case 6: *dest++ = *l++; *dest++ = *r++; + case 5: *dest++ = *l++; *dest++ = *r++; + case 4: *dest++ = *l++; *dest++ = *r++; + case 3: *dest++ = *l++; *dest++ = *r++; + case 2: *dest++ = *l++; *dest++ = *r++; + case 1: *dest++ = *l++; *dest++ = *r++; + } + while (--n > 0); + } + } + break; + + default: + { + for (int i = channels; --i >= 0;) + copy (samples, dest + i, src[i], channels - 1, 0); + } + break; + }; +} + +//-------------------------------------------------------------------------- + +// Convenience for a stereo channel pair +template <typename Td, + typename Ts> +void interleave (int samples, + Td* dest, + Ts const* left, + Ts const* right) +{ + const Ts* src[2]; + src[0] = left; + src[1] = right; + interleave (2, samples, dest, src); +} + +//-------------------------------------------------------------------------- + +// Multiply samples by a constant, without clip or overflow checking. +template <typename Td, + typename Ty> +void multiply (int samples, + Td* dest, + Ty factor, + int destSkip = 0) +{ + if (destSkip != 0) + { + ++destSkip; + while (--samples >= 0) + { + *dest = static_cast<Td>(*dest * factor); + dest += destSkip; + } + } + else + { + while (--samples >= 0) + *dest++ = static_cast<Td>(*dest * factor); + } +} + +// Multiply a set of channels by a constant. +template <typename Td, + typename Ty> +void multiply (int channels, + int samples, + Td* const* dest, + Ty factor, + int destSkip = 0) +{ + for (int i = channels; --i >= 0;) + multiply (samples, dest[i], factor, destSkip); +} + +//-------------------------------------------------------------------------- + +// Copy samples from src to dest in reversed order. Performs implicit +// type conversion. src and dest may not overlap. +template <typename Td, + typename Ts> +void reverse (int samples, + Td* dest, + Ts const* src, + int destSkip = 0, + int srcSkip = 0 ) +{ + src += (srcSkip + 1) * samples; + + if (srcSkip != 0 || destSkip == 0) + { + ++srcSkip; + ++destSkip; + while (--samples >= 0) + { + src -= srcSkip; + *dest = *src; + dest += destSkip; + } + } + else + { + while (--samples >= 0) + *dest++ = *--src; + } +} + +template <typename Td, typename Ts> +void reverse (int channels, size_t frames, Td* const* dest, const Ts* const* src) +{ + for (int i = channels; --i >= 0;) + reverse (frames, dest[i], src[i]); +} + +//-------------------------------------------------------------------------- + +template <typename Tn> +void to_mono (int samples, Tn* dest, Tn const* left, Tn const* right) +{ +#if 1 + while (samples-- > 0) + *dest++ = (*left++ + *right++) * Tn(0.70710678118654752440084436210485); +#else + while (samples-- > 0) + *dest++ = (*left++ + *right++) * Tn(0.5); +#endif +} + +//-------------------------------------------------------------------------- + +template <typename T> +void validate (int numChannels, int numSamples, T const* const* src) +{ + for (int i = 0; i < numChannels; ++i) + { + T const* p = src [i]; + for (int j = numSamples; j > 0; --j) + { + T v = *p++; + assert (v < 2 && v > -2); + } + } +} + +//-------------------------------------------------------------------------- + +#if 0 +/* + * this stuff all depends on is_pod which is not always available + * + */ +namespace detail { + +template <typename Ty, + bool isPod> +struct zero +{ + static void process (int samples, + Ty* dest, + int destSkip) + { + if (destSkip != 0) + { + ++destSkip; + while (--samples >= 0) + { + *dest = Ty(); + dest += destSkip; + } + } + else + { + std::fill (dest, dest + samples, Ty()); + } + } +}; + +template <typename Ty> +struct zero<Ty, true> +{ + static void process (int samples, + Ty* dest, + int destSkip) + { + if (destSkip != 0) + zero<Ty,false>::process (samples, dest, destSkip); + else + ::memset (dest, 0, samples * sizeof(dest[0])); + } +}; + +} + +// Fill a channel with zeros. This works even if Ty is not a basic type. +template <typename Ty> +void zero (int samples, + Ty* dest, + int destSkip = 0) +{ + detail::zero<Ty, tr1::is_pod<Ty>::value>::process (samples, dest, destSkip ); +} + +#else +// Fill a channel with zeros. This works even if Ty is not a basic type. +template <typename Ty> +void zero (int samples, + Ty* dest, + int destSkip = 0) +{ + if (destSkip != 0) + { + ++destSkip; + while (--samples >= 0) + { + *dest = Ty(); + dest += destSkip; + } + } + else + { + std::fill (dest, dest + samples, Ty()); + } +} + +#endif + +// Fill a set of channels with zero. +template <typename Ty> +void zero (int channels, + int samples, + Ty* const* dest, + int destSkip = 0) +{ + for (int i = channels; --i >= 0;) + zero (samples, dest[i], destSkip); +} + +//------------------------------------------------------------------------------ + +// Implementation of Brent's Method provided by +// John D. Cook (http://www.johndcook.com/) +// The return value of Minimize is the minimum of the function f. +// The location where f takes its minimum is returned in the variable minLoc. +// Notation and implementation based on Chapter 5 of Richard Brent's book +// "Algorithms for Minimization Without Derivatives". +// +// Reference: +// http://www.codeproject.com/KB/recipes/one_variable_optimize.aspx?msg=2779038 + +template <class TFunction> +double BrentMinimize +( + TFunction& f, // [in] objective function to minimize + double leftEnd, // [in] smaller value of bracketing interval + double rightEnd, // [in] larger value of bracketing interval + double epsilon, // [in] stopping tolerance + double& minLoc // [out] location of minimum +) +{ + double d, e, m, p, q, r, tol, t2, u, v, w, fu, fv, fw, fx; + static const double c = 0.5*(3.0 - ::std::sqrt(5.0)); + static const double SQRT_DBL_EPSILON = ::std::sqrt(DBL_EPSILON); + + double& a = leftEnd; + double& b = rightEnd; + double& x = minLoc; + + v = w = x = a + c*(b - a); + d = e = 0.0; + fv = fw = fx = f(x); + int counter = 0; +loop: + counter++; + m = 0.5*(a + b); + tol = SQRT_DBL_EPSILON*::fabs(x) + epsilon; + t2 = 2.0*tol; + // Check stopping criteria + if (::fabs(x - m) > t2 - 0.5*(b - a)) + { + p = q = r = 0.0; + if (::fabs(e) > tol) + { + // fit parabola + r = (x - w)*(fx - fv); + q = (x - v)*(fx - fw); + p = (x - v)*q - (x - w)*r; + q = 2.0*(q - r); + (q > 0.0) ? p = -p : q = -q; + r = e; + e = d; + } + if (::fabs(p) < ::fabs(0.5*q*r) && p < q*(a - x) && p < q*(b - x)) + { + // A parabolic interpolation step + d = p/q; + u = x + d; + // f must not be evaluated too close to a or b + if (u - a < t2 || b - u < t2) + d = (x < m) ? tol : -tol; + } + else + { + // A golden section step + e = (x < m) ? b : a; + e -= x; + d = c*e; + } + // f must not be evaluated too close to x + if (::fabs(d) >= tol) + u = x + d; + else if (d > 0.0) + u = x + tol; + else + u = x - tol; + fu = f(u); + // Update a, b, v, w, and x + if (fu <= fx) + { + (u < x) ? b = x : a = x; + v = w; + fv = fw; + w = x; + fw = fx; + x = u; + fx = fu; + } + else + { + (u < x) ? a = u : b = u; + if (fu <= fw || w == x) + { + v = w; + fv = fw; + w = u; + fw = fu; + } + else if (fu <= fv || v == x || v == w) + { + v = u; + fv = fu; + } + } + goto loop; // Yes, the dreaded goto statement. But the code + // here is faithful to Brent's orginal pseudocode. + } + return fx; +} + +//------------------------------------------------------------------------------ + +// Tracks the peaks in the signal stream using the attack and release parameters +template <int Channels=2, typename Value=float> +class EnvelopeFollower +{ +public: + EnvelopeFollower() + { + for (int i = 0; i < Channels; i++) + m_env[i]=0; + } + + Value operator[] (int channel) const + { + return m_env[channel]; + } + + void Setup (int sampleRate, double attackMs, double releaseMs) + { + m_a = pow (0.01, 1.0 / (attackMs * sampleRate * 0.001)); + m_r = pow (0.01, 1.0 / (releaseMs * sampleRate * 0.001)); + } + + void Process (size_t samples, const Value** src) + { + for( int i=0; i<Channels; i++ ) + { + const Value* cur = src[i]; + + double e = m_env[i]; + for (int n = samples; n; n--) + { + double v = ::fabs(*cur++); + if (v > e) + e = m_a * (e - v) + v; + else + e = m_r * (e - v) + v; + } + m_env[i]=e; + } + } + + double m_env[Channels]; + +protected: + double m_a; + double m_r; +}; + +} + +#endif diff --git a/Source/Main.cpp b/Source/Main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d5b5bd513958b1ab976b9bc9af340f206b2fe48f --- /dev/null +++ b/Source/Main.cpp @@ -0,0 +1,59 @@ +/* + ============================================================================== + + This file was auto-generated by the Jucer! + + It contains the basic startup code for a Juce application. + + ============================================================================== +*/ + +#include "../JuceLibraryCode/JuceHeader.h" +#include "MainWindow.h" +#include "UI/CustomLookAndFeel.h" + + +//============================================================================== +class OpenEphysApplication : public JUCEApplication +{ +public: + //============================================================================== + OpenEphysApplication() {} + + ~OpenEphysApplication() {} + + //============================================================================== + void initialise (const String& commandLine) + { + mainWindow = new MainWindow(); + mainWindow->setUsingNativeTitleBar (true); + + customLookAndFeel = new CustomLookAndFeel(); + LookAndFeel::setDefaultLookAndFeel(customLookAndFeel); + } + + void shutdown() + { + mainWindow = 0; + customLookAndFeel = 0; + } + + //============================================================================== + void systemRequestedQuit() + {quit();} + + //============================================================================== + const String getApplicationName() { return "Open Ephys Data Acquisition Software";} + const String getApplicationVersion() {return ProjectInfo::versionString;} + bool moreThanOneInstanceAllowed() {return true;} + void anotherInstanceStarted (const String& commandLine) + {} + +private: + ScopedPointer <MainWindow> mainWindow; + ScopedPointer <CustomLookAndFeel> customLookAndFeel; +}; + +//============================================================================== +// This macro generates the main() routine that starts the app. +START_JUCE_APPLICATION(OpenEphysApplication) diff --git a/Source/MainWindow.cpp b/Source/MainWindow.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0fdd019e62235e3007bd39a2300f3df9e3bb6598 --- /dev/null +++ b/Source/MainWindow.cpp @@ -0,0 +1,134 @@ +/* + ============================================================================== + + This file was auto-generated by the Jucer! + + It contains the basic outline for a simple desktop window. + + ============================================================================== +*/ + +#include "MainWindow.h" +#include <stdio.h> + + +//============================================================================== +MainWindow::MainWindow() + : DocumentWindow (JUCEApplication::getInstance()->getApplicationName(), + Colour(70,70,75), + DocumentWindow::allButtons) +{ + centreWithSize (500, 400); + //setBounds(0,0,500,400); + setResizable (true, false); + + + + // constraining size doesn't seem to work: + //setResizeLimits(500, 400, 10000, 10000); + //ComponentBoundsConstrainer* cbc = getConstrainer(); + //cbc->setMinimumWidth(300); + //cbc->setMinimumHeight(200); + + // Create ProcessorGraph and AudioComponent, and connect them. + // Callbacks will be set by the play button in the control panel + processorGraph = new ProcessorGraph(); + audioComponent = new AudioComponent(); + audioComponent->connectToProcessorGraph(processorGraph); + + setContentComponent (new UIComponent(processorGraph, audioComponent), true, true); + + loadWindowBounds(); + setVisible (true); + +} + +MainWindow::~MainWindow() +{ + + saveWindowBounds(); + processorGraph->saveState(); + + audioComponent->disconnectProcessorGraph(); + + deleteAndZero(processorGraph); + deleteAndZero(audioComponent); + + setContentComponent (0); + +} + +void MainWindow::closeButtonPressed() +{ + if (audioComponent->callbacksAreActive()) { + audioComponent->endCallbacks(); + processorGraph->disableProcessors(); + } + + JUCEApplication::getInstance()->systemRequestedQuit(); + +} + +void MainWindow::saveWindowBounds() +{ + + std::cout << "Saving window bounds." << std::endl; + + File file = File("./windowState.xml"); + + XmlElement* xml = new XmlElement("MAINWINDOW"); + + XmlElement* bounds = new XmlElement("BOUNDS"); + bounds->setAttribute("x",getScreenX()); + bounds->setAttribute("y",getScreenY()); + bounds->setAttribute("w",getWidth()); + bounds->setAttribute("h",getHeight()); + bounds->setAttribute("fullscreen",isFullScreen()); + + xml->addChildElement(bounds); + + String error; + + if (! xml->writeToFile (file, String::empty)) + error = "Couldn't write to file"; + + delete xml; +} + +void MainWindow::loadWindowBounds() +{ + + std::cout << "Loading window bounds." << std::endl; + + File file = File("./windowState.xml"); + + XmlDocument doc (file); + XmlElement* xml = doc.getDocumentElement(); + + // if (xml == 0 || ! xml->hasTagName (T("MAINWINDOW"))) + // { + // delete xml; + // // return "Not a valid file."; + // } + + String description;// = T(" "); + + forEachXmlChildElement (*xml, e) + { + + int x = e->getIntAttribute("x"); + int y = e->getIntAttribute("y"); + int w = e->getIntAttribute("w"); + int h = e->getIntAttribute("h"); + + bool fs = e->getBoolAttribute("fullscreen"); + + setTopLeftPosition(x,y); + getContentComponent()->setBounds(0,0,w,h); + //setFullScreen(fs); + + } + + delete xml; + // return "Everything went ok."; +} \ No newline at end of file diff --git a/Source/MainWindow.h b/Source/MainWindow.h new file mode 100644 index 0000000000000000000000000000000000000000..5796053dc428fbbfd7edea464632a629ac35b6a0 --- /dev/null +++ b/Source/MainWindow.h @@ -0,0 +1,44 @@ +/* + ============================================================================== + + This file was auto-generated by the Jucer! + + It contains the basic outline for a simple desktop window. + + ============================================================================== +*/ + +#ifndef __MAINWINDOW_H_BA75E17__ +#define __MAINWINDOW_H_BA75E17__ + +#include "../JuceLibraryCode/JuceHeader.h" +#include "UI/UIComponent.h" +#include "Audio/AudioComponent.h" +#include "Processors/ProcessorGraph.h" + +//============================================================================== +class MainWindow : public DocumentWindow +{ +public: + //============================================================================== + MainWindow(); + ~MainWindow(); + + void closeButtonPressed(); + +private: + //============================================================================== + + void saveWindowBounds(); + void loadWindowBounds(); + + // ScopedPointer <UIComponent> uiComponent; + AudioComponent* audioComponent; + ProcessorGraph* processorGraph; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainWindow) + +}; + + +#endif // __MAINWINDOW_H_BA75E17__ diff --git a/Source/Network/datapacket.cpp b/Source/Network/datapacket.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d25c33f6bf56ee157cf6e13446f41738ce9031d1 --- /dev/null +++ b/Source/Network/datapacket.cpp @@ -0,0 +1,264 @@ +#include <iostream> +#include "datapacket.h" +#include <stdio.h> +#include <string.h> + +void printBuff(char* buff, int blen){ + char val; + for (int i=0; i<blen; i++){ + val = *(buff+i); + std::cout<<"\\"<<(int)val; + } + std::cout<<std::endl; +} + + +/*------------- TIME ------------*/ +void tsToBuff(timestamp_t* t, char* buff, int blen){ +// if (blen<6) +// std::cout<<"ERROR: Buffer is too short"<<std::endl; + timestamp_t ts = hton32(*t); + memcpy(buff+2, &ts, 4); +} + +timestamp_t buffToTs(char *buff, int blen){ + +// if (blen<6) +// std::cout<<"Error buffer is too short"<<std::endl; + + timestamp_t s; + memcpy(&s, buff+2, 4); + s = ntoh32(s); + return s; +} +/*------------- SPIKE ------------*/ +void spikeToBuff(spike_net_t* s, char* buff, int *buff_len){ + //std::cout<<"spikeToBuff(), isn't checking the buffer size, implement this!"<<std::endl; + + buff[0] = typeToChar(NETCOM_UDP_SPIKE); + buff[3] = 0; + + uint32_t ts_tx = hton32(s->ts); + uint16_t name_tx = hton16(s->name); + uint16_t n_chans_tx = hton16(s->n_chans); + uint16_t n_samps_per_chan_tx = hton16(s->n_samps_per_chan); + uint16_t samp_n_bytes_tx = hton16(s->samp_n_bytes); + rdata_t data_tx[MAX_FILTERED_BUFFER_TOTAL_SAMPLE_COUNT]; + int16_t gains_tx[MAX_FILTERED_BUFFER_N_CHANS]; + rdata_t thresh_tx[MAX_FILTERED_BUFFER_N_CHANS]; + uint16_t trig_ind_tx = hton32(s->trig_ind); + + for(int c = 0; c < s->n_chans * s->n_samps_per_chan; c++){ + // ASSUMES 16-bit data! + data_tx[c] = hton16(s->data[c]); + } + for(int c = 0; c < s->n_chans; c++){ + gains_tx[c] = hton16(s->data[c]); + thresh_tx[c] = hton16(s->thresh[c]); + } + + uint16_t cursor = 4; // start offset 4 bytes (first is packet type, 2nd and 3rd are buff_size 4th is \0 + uint16_t cursor_tx; + + memcpy(buff+cursor, &ts_tx, 4); + cursor += 4; + memcpy(buff+cursor, &name_tx, 2); + cursor += 2; + memcpy(buff+cursor, &n_chans_tx, 2); + cursor += 2; + memcpy(buff+cursor, &n_samps_per_chan_tx, 2); + cursor += 2; + memcpy(buff+cursor, &samp_n_bytes_tx, 2); + cursor += 2; + memcpy(buff+cursor, &data_tx, (s->n_chans * s->n_samps_per_chan * s->samp_n_bytes)); + cursor += (s->n_chans * s->n_samps_per_chan * s->samp_n_bytes); + memcpy(buff+cursor, &gains_tx, (2 * s->n_chans)); + cursor += 2*s->n_chans; + memcpy(buff+cursor, &thresh_tx, (2 * s->n_chans)); + cursor += 2*s->n_chans; + memcpy(buff+cursor, &trig_ind_tx, 2); + cursor += 2; + + cursor_tx = hton16(cursor); + memcpy(buff+1, &cursor_tx, 2); + + buff[cursor] = '\0'; + *buff_len = cursor; + +} + +void buffToSpike( spike_net_t *s, char* buff, int blen){ + + int cursor = 4; + + memcpy( &(s->ts), buff+cursor, 4); + s->ts = ntoh32(s->ts); + cursor += 4; + + memcpy( &(s->name), buff+cursor, 2); + s->name = ntoh16(s->name); + cursor += 2; + + memcpy( &(s->n_chans), buff+cursor, 2); + s->n_chans = ntoh16(s->n_chans); + cursor += 2; + + memcpy( &(s->n_samps_per_chan), buff+cursor, 2); + s->n_samps_per_chan = ntoh16(s->n_samps_per_chan); + cursor += 2; + + memcpy( &(s->samp_n_bytes), buff+cursor, 2); + s->samp_n_bytes = ntoh16(s->samp_n_bytes); + cursor += 2; + + int n_total_samps = s->n_chans * s->n_samps_per_chan; + int data_bytes = n_total_samps * s->samp_n_bytes; + int gain_bytes = s->n_chans * 2; // 2 byte datatype for gain: int16 + int thresh_bytes = s->n_chans * 2; // 2 byte datatype for thresh: rdata_t + + memcpy( &(s->data), buff+cursor, data_bytes); + for(int n = 0; n < n_total_samps; n++) + s->data[n] = ntoh16(s->data[n]); + cursor += data_bytes; + + memcpy( &(s->gains), buff+cursor, gain_bytes); + cursor += gain_bytes; + memcpy( &(s->thresh), buff+cursor, thresh_bytes); + cursor += thresh_bytes; + + for(int n = 0; n < s->n_chans; n++){ + s->gains[n] = ntoh16(s->gains[n]); + s->thresh[n] = ntoh16(s->thresh[n]); + } + + memcpy( &(s->trig_ind), buff+cursor, 2); + s->trig_ind = ntoh16(s->trig_ind); + cursor += 2; + +} + +/*------------- WAVE ------------*/ +void waveToBuff(lfp_bank_net_t* lfp, char* buff, int *blen){ + //TODO IMPLEMENT + //printf("waveToBuff\n"); + + int s; + + //old + //int cursor = 2; + //NEW + uint16_t cursor = 4; + uint16_t cursor_tx; + + int16_t name_tx = hton16(lfp->name); + uint32_t ts_tx = htonl(lfp->ts); + uint16_t n_chans_tx = hton16(lfp->n_chans); + uint16_t n_samps_per_chan_tx = hton16(lfp->n_samps_per_chan); + uint16_t bytes_per_samp_tx = hton16(lfp->samp_n_bytes); + rdata_t data_tx[MAX_FILTERED_BUFFER_TOTAL_SAMPLE_COUNT]; // find out the right minimum size for this array + int16_t gain_tx[MAX_FILTERED_BUFFER_N_CHANS]; + + // flip all the elements in data + // THIS ASSUMES 16 BIT SAMPLES + for(s = 0; s < lfp->n_chans * lfp->n_samps_per_chan; s++){ + data_tx[s] = hton16(lfp->data[s]); + } + for(s = 0; s < lfp->n_chans; s++){ + gain_tx[s] = hton16(lfp->gains[s]); + } + + buff[0] = typeToChar(NETCOM_UDP_LFP); + buff[3] = '\0'; + + memcpy(buff+cursor, &ts_tx , 4); + cursor += 4; + memcpy(buff+cursor, &name_tx , 2); + cursor += 2; + memcpy(buff+cursor, &n_chans_tx , 2); + cursor += 2; + memcpy(buff+cursor, &n_samps_per_chan_tx , 2); + cursor += 2; + memcpy(buff+cursor, &bytes_per_samp_tx, 2); + cursor += 2; + // is this next line right? &data_tx or data_tx ??? + memcpy(buff+cursor, &data_tx , (s=(lfp->n_chans * lfp->n_samps_per_chan * lfp->samp_n_bytes))); + cursor += s; + memcpy(buff+cursor, &gain_tx , (s=(lfp->n_chans * 2))); + cursor += s; + + // NEW + cursor_tx = hton16(cursor); + memcpy(buff+1, &cursor_tx, 2); + + buff[cursor] = '\0'; + *blen = cursor; + + if(false){ + printf("incoming ts: %d. ts after swap: %d",lfp->ts, ts_tx); + } + + if(false){ + printf("buffer being written: "); + for(int n = 0; n < *blen; n++) + printf("%d:%c\n ",n,buff[n]); + } +} + +void buffToWave(lfp_bank_net_t *lfp, char* buff){ + //TODO IMPLEMENT + int cursor = 4; // skip over the packet_type field + + + //printf("1"); + //fflush(stdout); + memcpy( &(lfp->ts), buff+cursor, 4); + lfp->ts = ntoh32(lfp->ts); + cursor += 4; + //printf("2"); + //fflush(stdout); + memcpy( &(lfp->name), buff+cursor, 2); + lfp->name = ntoh16(lfp->name); + cursor +=2; + //printf("3"); + //fflush(stdout); + memcpy( &(lfp->n_chans), buff+cursor, 2); + lfp->n_chans = ntoh16(lfp->n_chans); + cursor +=2; + //printf("4"); + //fflush(stdout); + memcpy( &(lfp->n_samps_per_chan), buff+cursor, 2); + lfp->n_samps_per_chan = ntoh16(lfp->n_samps_per_chan); + cursor += 2; + //printf("5"); + //fflush(stdout); + memcpy( &(lfp->samp_n_bytes), buff+cursor, 2); + lfp->samp_n_bytes = ntoh16(lfp->samp_n_bytes); + cursor += 2; + //printf("6"); + //fflush(stdout); + int n_total_samps = lfp->n_chans * lfp->n_samps_per_chan; + int data_bytes = lfp->n_chans * lfp->n_samps_per_chan * lfp->samp_n_bytes; + int gain_bytes = lfp->n_chans * lfp->samp_n_bytes; + //printf("7"); + //fflush(stdout); + memcpy( &(lfp->data), buff+cursor, data_bytes); + for(int n = 0; n < n_total_samps; n++){ + //printf("about to do data[%d]\n",n); + //fflush(stdout); + lfp->data[n] = ntoh16(lfp->data[n]); + } + cursor += data_bytes; + //printf("8"); + //fflush(stdout); + memcpy( &(lfp->gains), buff+cursor, gain_bytes); + //printf("finished memcopy for gains\n"); + for(int n = 0; n < lfp->n_chans; n++){ + //printf("about to do gain[%d] \n",n); + //fflush(stdout); + lfp->gains[n] = ntoh16(lfp->gains[n]); + } + cursor += gain_bytes; + //printf("9"); + //fflush(stdout); +} + diff --git a/Source/Network/datapacket.h b/Source/Network/datapacket.h new file mode 100644 index 0000000000000000000000000000000000000000..c12a64d6326636884f51a98298120b108ea1c733 --- /dev/null +++ b/Source/Network/datapacket.h @@ -0,0 +1,131 @@ +#ifndef NETCOM_DATAPACKET_H +#define NETCOM_DATAPACKET_H + +#include <stdint.h> +#include <vector> +#include <byteswap.h> +#include <netinet/in.h> +#include "global_defs.h" + +#define ntoh64(x) bswap_64(x) +#define hton64(x) bswap_64(x) + +#define ntoh32(x) bswap_32(x) +#define hton32(x) bswap_32(x) + +#define ntoh16(x) bswap_16(x) +#define hton16(x) bswap_16(x) + +#define MAX_BUF_LEN 2048 +void printBuff(char* buff, int blen); + +// Each buffer has a 2 byte header, with the first byte corresponding to +// the type of data contained in the buffer, the second byte is set to 0 +// but this isn't currently required as maybe in the future we will see +// a need to add more information to the buffer header +// +// ??should we explicity test to see if the 2nd byte is set to zero as a +// sanity test on the buffer? Not sure we should discuss this +// +// Unless otherwise specified byts in each packet are arranged +// such that the first var in the type def corresponds to +// the first set of bytes in the buffer, the next var is found in the +// directly after the bytes allocated for the previous var and so on. +// Each var uses exactly the number of bytes in the buffer that uses in +// the struct, for Example: +// +// if I had struct data_t{ +// uint8_t v1; +// uint8_t v2; +// uint16_t v3; +// }; +// +// the data in the packed buffer would look like: +// [BYTE0 | BYTE1 | BYTE2 | BYTE3 | BYTE4 | BYTE5 ] +// [DTYPE | 0 | VAR1 | VAR2 | VAR3 | VAR3 ] +// + + +typedef uint32_t timestamp_t; + +struct spike_t{ + timestamp_t ts; + uint8_t src; + uint8_t filt; + uint8_t nChan; + uint8_t nSamp; + std::vector<uint16_t> gain; + std::vector<uint16_t> thold; + std::vector<uint16_t> data; +}; + +// Greg's alternative for a spike pre-packet struct +struct spike_net_t{ + timestamp_t ts; // bytes 2:5 + uint16_t name; // bytes 6:7 + uint16_t n_chans; // bytes 8:9 + uint16_t n_samps_per_chan; // bytes 10:11 + uint16_t samp_n_bytes; // bytes 11:12 + rdata_t data[MAX_FILTERED_BUFFER_TOTAL_SAMPLE_COUNT]; // bytes 13: 13+( c * s * b ) + int16_t gains[MAX_FILTERED_BUFFER_N_CHANS]; // the next 2*c bytes + rdata_t thresh[MAX_FILTERED_BUFFER_N_CHANS]; // the next b*c bytes + uint16_t trig_ind; // the next 2 bytes +}; + +struct wave_t{ + timestamp_t ts; + uint8_t src; + uint8_t filt; + std::vector<uint16_t> gain; + std::vector<uint16_t> nSamp; + std::vector<uint16_t> data; +}; + +// Greg's alternative for a lfp_bank pre-packet struct +struct lfp_bank_net_t{ + timestamp_t ts; // bytes 2:5 + uint16_t name; // bytes 6:7 does this field exist for lfp_banks? + uint16_t n_chans; // bytes 8:9 + uint16_t n_samps_per_chan; // bytes 10:11 + uint16_t samp_n_bytes; // bytes 11:12 + int16_t data[MAX_FILTERED_BUFFER_TOTAL_SAMPLE_COUNT]; // bytes 13 : 13*s*c*b + rdata_t gains[MAX_FILTERED_BUFFER_N_CHANS]; // the next 2*c bytes +}; + +// The xxToBuff functions add the appropriate buffer headers, the user +// does not have to worry about adding the headers by hand + +void tsToBuff(timestamp_t* s,char* buff, int blen); +timestamp_t buffToTs(char* buff, int blen); + +void spikeToBuff(spike_t* s, char* buff, int *blen); +void buffToSpike(spike_net_t *s, char *buff); + +void waveToBuff(lfp_bank_net_t* lfp, char* buff, int *blen); +void buffToWave(lfp_bank_net_t *lfp, char *buff); + +enum packetType_t {NETCOM_UDP_SPIKE = 65, + NETCOM_UDP_LFP = 66, + NETCOM_UDP_TIME = 67, + NETCOM_UNDEFINED=-1}; + +inline char typeToChar(packetType_t x){ + switch (x){ + case NETCOM_UDP_SPIKE: return 65; + case NETCOM_UDP_LFP: return 66; + case NETCOM_UDP_TIME: return 67; + default: return -1; + } +} + +inline packetType_t charToType(char x){ + switch (x){ + case 65: return NETCOM_UDP_SPIKE; + case 66: return NETCOM_UDP_LFP; + case 67: return NETCOM_UDP_TIME; + default:return NETCOM_UNDEFINED; + } +} + + +#endif diff --git a/Source/Network/global_defs.h b/Source/Network/global_defs.h new file mode 100644 index 0000000000000000000000000000000000000000..e12a38ff4749bfb61ed95dbcae5aebf00e5e5cee --- /dev/null +++ b/Source/Network/global_defs.h @@ -0,0 +1,110 @@ +#ifndef GLOBAL_DEFS_H_ +#define GLOBAL_DEFS_H_ + +//#define INT16_MAX 190000 +//#define INT16_MIN -190000 + +#include <string> +#include <iostream> +#include <stdio.h> +#include <map> +#include <stdint.h> +#include <limits.h> + +#define MAX_THREADS 32 +#define THREADED_ + +#include <pthread.h> + +typedef int16_t rdata_t; +const rdata_t RDATA_MIN = INT16_MIN; +const rdata_t RDATA_MAX = INT16_MAX; + +extern pthread_t my_threads[MAX_THREADS]; + +const int MAX_NAME_STRING_LEN = 64; +const int MAX_EVENT_STRING_LEN = 256; + + +//typedef float64 rdata_t; +typedef char name_string_t [MAX_NAME_STRING_LEN]; +typedef char event_string_t [MAX_EVENT_STRING_LEN]; + +const int MAX_NEURAL_DAQS = 16; +const int MAX_TRODES = 64; +const int MAX_LFP_BANKS = 16; +const int MAX_FILTERED_BUFFERS = MAX_TRODES + MAX_LFP_BANKS; + +const std::string default_setup_config_filename ("/home/greghale/arte-ephys/conf/arte_setup_default.conf"); +const std::string default_session_config_filename ("/home/greghale/arte-ephys/conf/arte_session_default.conf"); + +const int MAX_NEURAL_DAQ_N_CHANS = 32; +const int MAX_NEURAL_DAQ_N_SAMPS_PER_CHAN = 320; +const int MAX_NEURAL_DAQ_BUFFER = MAX_NEURAL_DAQ_N_CHANS * MAX_NEURAL_DAQ_N_SAMPS_PER_CHAN; + + +/******* These are all properties of a filtered_buffer now ******/ +/* const int MAX_TRODE_N_CHANS = 32; // 32 chanels */ +/* const int MAX_TRODE_BUFFER_LEN = 320; // 3200 samps = about 0.01 seconds */ +/* const int MAX_N_INTERMEDIATE_BUFFERS = 10; */ +/* const int MAX_TRODE_BUFFER = MAX_TRODE_N_CHANS * MAX_TRODE_BUFFER_LEN * (MAX_N_INTERMEDIATE_BUFFERS + 2); */ + +/* const int MAX_LFP_BANK_N_CHANS = 32; */ +/* const int MAX_LFP_BANK_BUFFER_LEN = 320; */ +/* const int MAX_LFP_BANK_N_INTERMEDIATE_BUFFERS = 10; */ +/* const int MAX_LFP_BANK_BUFFER = MAX_LFP_BANK_N_CHANS * MAX_LFP_BANK_BUFFER_LEN * MAX_LFP_BANK_N_INTERMEDIATE_BUFFERS; */ + +const int MAX_FILTERED_BUFFER_N_CHANS = 32; +const int MAX_FILTERED_BUFFER_LEN = 320; // +const int MAX_FILTERED_BUFFER_N_INTERMEDIATE_BUFFERS = 10; +const int MAX_FILTERED_BUFFER_TOTAL_SAMPLE_COUNT_MULTI = + MAX_FILTERED_BUFFER_N_CHANS * + MAX_FILTERED_BUFFER_LEN * MAX_FILTERED_BUFFER_N_INTERMEDIATE_BUFFERS; +const int MAX_FILTERED_BUFFER_TOTAL_SAMPLE_COUNT = MAX_FILTERED_BUFFER_N_CHANS * + MAX_FILTERED_BUFFER_LEN; + + +const double NEURAL_DAQ_V_MAX = 10.0; +const double NEURAL_DAQ_V_MIN = -10.0; + +const int MAX_FILT_COEFS = MAX_FILTERED_BUFFER_LEN; + +const int MAX_SPIKE_N_SAMPS_PER_CHAN = 128; + +struct neural_daq{ + int id; + name_string_t dev_name; + uint16_t n_samps_per_buffer; + uint16_t n_chans; + name_string_t in_filename; + FILE *in_file; + name_string_t raw_dump_filename; + FILE *out_file; + rdata_t data_buffer[MAX_NEURAL_DAQ_BUFFER]; + rdata_t *data_ptr; + uint32_t buffer_timestamp; + //TaskHandle task_handle; + int total_samp_count; + int size_bytes; + int status; + double buffer_time_interval; // inter-buffer-interval (sec) + uint32_t daq_buffer_count; + uint32_t this_buffer; +}; + +extern bool daqs_reading; // Daq's read in unison. No simultaneous mixing between file and card in allowed. +extern bool daqs_writing; // <-- prob won't be used. Some daq's may write while others don't. + +extern uint32_t buffer_count; + +extern std::map <int, neural_daq> neural_daq_map; + +// hackish enumeration +typedef uint8_t recordtype_t; +const recordtype_t LFP_BANK_RECORD = 0; +const recordtype_t EVENT_STRING_RECORD = 1; +const recordtype_t SPIKE_RECORD = 2; +const recordtype_t THRESHOLD_FRAME_RECORD = 3; + + +#endif diff --git a/Source/Network/netcom.cpp b/Source/Network/netcom.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3905f7cff1b415e7c18a7a05b2fbfff69d03c68e --- /dev/null +++ b/Source/Network/netcom.cpp @@ -0,0 +1,211 @@ +#include "netcom.h" +#include "datapacket.h" + +NetComDat NetCom::initUdpTx(char host[], char port[]){ + + int sockfd; + sockaddr_in addr; + hostent *he; + int numbytes; + int broadcast = 1; + + if ((he=gethostbyname(host))==NULL){ + perror("gethostname"); + printf("gethostbyname error.\n"); + fflush(stdout); + exit(1); + } + if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1){ + perror("socket"); + printf("socket error.\n"); + fflush(stdout); + exit(1); + } + if (setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof broadcast) == -1) { + perror("setsockopt (SO_BROADCAST)"); + printf("setsockopt error\n"); + fflush(stdout); + exit(1); + } + + addr.sin_family = AF_INET; + // addr.sin_port = htons(port); + addr.sin_addr = *((struct in_addr *)he->h_addr); + + memset(addr.sin_zero, '\0', sizeof addr.sin_zero); + NetComDat net; + + net.sockfd = sockfd; + net.addr_in = addr; + return net; +} + +NetComDat NetCom::initUdpRx(char host[], char port[]){ + + //std::stringstream ss; + // ss<<portIn; + // const char * port = ss.str().c_str(); + + int sockfd; + struct addrinfo hints, *servinfo, *p; + int rv; + struct sockaddr_storage their_addr; + socklen_t addr_len; + char s[INET6_ADDRSTRLEN]; + + NetComDat net; + + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; // set to AF_INET to force IPv4 + hints.ai_socktype = SOCK_DGRAM; + hints.ai_flags = AI_PASSIVE; // use my IP + + std::cout<<"Listening to port:"<<port<<" from IP:"<<host<<std::endl; + if ((rv = getaddrinfo(NULL, port, &hints, &servinfo)) != 0) { + fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv)); + return net; + } + + // loop through all the results and bind to the first we can + for(p = servinfo; p != NULL; p = p->ai_next) { +// std::cout<<"\t"<< p->ai_canonname<<"----"<<std::endl; +// std::cout<<"\t"<< p->ai_addr<<"----"<<std::endl; + if ((sockfd = socket(p->ai_family, p->ai_socktype, + p->ai_protocol)) == -1) { + perror("listener: socket"); + continue; + } + + if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) { + close(sockfd); + perror("listener: bind"); + continue; + } + + break; + } + + if (p == NULL) { + fprintf(stderr, "listener: failed to bind socket\n"); + return net; + } + + freeaddrinfo(servinfo); + + net.sockfd = sockfd; + net.their_addr = their_addr; + + return net; +} + +int NetCom::txTs(NetComDat net, timestamp_t count, int nTx){ + std::cout<<"NetCom::txTs "<<count<<std::endl; + + //char* buff = new char[6]; + char buff[6]; // this is much faster than using new + *buff = typeToChar(NETCOM_UDP_TIME); + + //TEMPORARY commented by Greg because of error + //tsToBuff(&count, buff, 6); + + std::cout<<"Buffer Out:"; + //TEMPORARY commented by Greg because of error + //printBuff(buff,6); + + for (int i=0; i<nTx; i++) + sendto(net.sockfd, buff, 6, 0, (struct sockaddr *)&net.addr_in, sizeof net.addr_in); + // delete buff; +} + +timestamp_t NetCom::rxTs(NetComDat net){ + + char s[INET6_ADDRSTRLEN]; + char buff[6] = {'\0'};; + + int numbytes; + sockaddr_storage their_addr = net.their_addr; + socklen_t addr_len = sizeof (their_addr); + + sockaddr_in sa = *(struct sockaddr_in *)&their_addr; + + if ((numbytes = recvfrom(net.sockfd, &buff, 20 , 0, + (struct sockaddr *)&their_addr, &addr_len)) == -1) { + perror("recvfrom"); + exit(1); + } + + std::cout<<"Buffer In:"; + // TEMPORARY commented by Greg because of error + // printBuff(buff, 6); + std::cout<<"Buffer Type:"<<charToType(*buff)<<std::endl; + /// Below this line is undeeded + + printf("listener: got packet from %s\n", + inet_ntop(their_addr.ss_family,get_in_addr((struct sockaddr *)&their_addr), s, + sizeof s)); + + //TEMPORARY commented by Greg because of error + //timestamp_t ts = buffToTs(buff, 6); + //TEMPORARY commented by Greg because of error + //std::cout<<"Recovered Timestamp:" << ts << std::endl; +// printf("listener: packet is %d bytes long\n", numbytes); +// buff[10] = '\0'; +// printf("listener: packet contains \"%s\"\n", buff); + + return 0; +} + +void NetCom::txBuff(NetComDat net, char *buff, int buff_len){ + + if(false){ // this thing sets the buff line to all 'a'. Kinda useful for testing listeners. + printf("buff len: %d content: ",buff_len); + for(int c = 0; c < buff_len; c++){ + buff[c] = 'a'; + printf("%d: %c\n",c, buff[c]); + } + buff[buff_len-1] = '\0'; + } + + sendto( net.sockfd, buff, buff_len, 0, (sockaddr*) &net.addr_in, sizeof( net.addr_in ) ); + +} + +void NetCom::rxBuff(NetComDat net, char *buff, int *buff_len){ + + char s[INET6_ADDRSTRLEN]; + int numbytes; + sockaddr_storage their_addr = net.their_addr; + socklen_t addr_len = sizeof(their_addr); + + sockaddr_in sa = *(sockaddr_in*)&their_addr; + + if ( (numbytes = recvfrom(net.sockfd, buff, BUFFSIZE-1, 0, (SA*)&their_addr, &addr_len)) == -1){ + printf("recvfrom error from rxBuff.\n"); + } + + *buff_len = numbytes; + +} + +void NetCom::rxWave(NetComDat net, lfp_bank_net_t *lfp){ + + char buff[BUFFSIZE-1]; + int buff_len = 0; + //printf("before rxbuff\n"); + //fflush(stdout); + rxBuff(net, buff, &buff_len); + //printf("after rxbuff, before bufftowave\n"); + + buffToWave(lfp, buff); + //printf("after bufftowave\n"); +} + +void *get_in_addr(struct sockaddr *sa){ + if (sa->sa_family == AF_INET) { + return &(((struct sockaddr_in*)sa)->sin_addr); + } + + return &(((struct sockaddr_in6*)sa)->sin6_addr); +} + + diff --git a/Source/Network/netcom.h b/Source/Network/netcom.h new file mode 100644 index 0000000000000000000000000000000000000000..c5732885e8a70276418878478cc5de0522cac2a0 --- /dev/null +++ b/Source/Network/netcom.h @@ -0,0 +1,49 @@ +#ifndef NETCOM_H +#define NETCOM_H + +#include <stdio.h> +#include <iostream> +#include <sstream> +#include <stdint.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#include "datapacket.h" +#include "unp.h" + +struct NetComDat{ + int sockfd; + struct sockaddr_in addr_in; + struct sockaddr_storage their_addr; +}; + +class NetCom{ + public: + static NetComDat initUdpTx(char host[], char port[]); + static NetComDat initUdpRx(char host[], char port[]); + + static int txTs(NetComDat net, timestamp_t count, int nTx); + static timestamp_t rxTs(NetComDat net); + + static int txSpike(NetComDat net, spike_t, int nTx); + static spike_t rxSpike(NetComDat net); + + static int txWave(NetComDat net, wave_t w, int nTx); + static void rxWave(NetComDat net, lfp_bank_net_t *lfp); + + static void txBuff(NetComDat net, char * buff, int buff_len); + static void rxBuff(NetComDat net, char * buff, int *buff_len); + +}; + +void *get_in_addr(struct sockaddr *sa); + + +#endif + diff --git a/Source/Network/unp.h b/Source/Network/unp.h new file mode 100644 index 0000000000000000000000000000000000000000..bac95caa174b42c06b15f066cd4680c3a243b80d --- /dev/null +++ b/Source/Network/unp.h @@ -0,0 +1,194 @@ +// Unix network programming unc.h standard header +#ifndef __unp_h +#define __unp_h + +// Skipping this line until we have a good makefile +// include "../config.h" + +#include <sys/types.h> // basic system datatypes +#include <sys/socket.h> // basic socket definitions +#include <sys/time.h> // timeval{} for select() +#include <time.h> // timespec{} for pselect() +#include <netinet/in.h> // sockaddy_in{} and other Internet defns +#include <arpa/inet.h> // inet(3) functions +#include <errno.h> +#include <fcntl.h> // for nonblocking +#include <netdb.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> // for S_xxx file mode constants +#include <sys/uio.h> // for iovec{} and readv/writev +#include <unistd.h> +#include <sys/wait.h> // for unix domain sockets +#include <sys/un.h> + +#ifdef HAVE_SYS_SELECT_H +#include <sys/select.h> // for convenience +#endif + +#ifdef HAVE_POLL_H +#include <poll.h> // for convenience +#endif + +#ifdef HAVE_STRINGS_H +#include <strings.h> // for convenience +#endif + +// Three headers are normally neeed for socket/file ioctl's: +// <sys/ioctl.h>, <sys/filio.h>, and <sys/sockio.h> +#ifdef HAVE_SYS_IOCTL_H +#include <sys/ioctl.h> +#endif +#ifdef HAVE_SYS_FILIO_H +#include <sys/filio.h> +#endif +#ifdef HAVE_SYS_SOCKIO_H +#include <sys/sockio.h> +#endif + +// 110skipping this because nidaqmx is finicky about pthread +// ifdef HAVE_PTHREAD_H +// include <pthread.h> +// endif + + + + #ifdef __osf__ + #undef recv + #udef send + #define recv(a,b,c,d) recvfrom(a,b,c,d,0,0) + #define send(a,b,c,d) sendto(a,b,c,d,0,0) + #endif + + #ifndef INADDR_NONE + #define INADDR_NONE 0xffffffff // should have been in <netinet/in.h> + #endif + + #ifndef SHUT_RD // these three Posix.1g names are quite new + #define SHUT_RD 0 // shutdown for reading + #define SHUT_WR 1 // shutdown for writing + #define SHUT_RDWR 2 // shutdown for reading and writing + #endif + #ifndef INET_ADDRSTRLEN + #define INET_ADDRSTRLEN 16 // "ddd.ddd.ddd.ddd\0" + #endif + + // define following even if IPv6 not supported, so we can + // always allocate an adequately-sized buffer, + // without #ifdefs in the code + #ifndef INET6_ADDRSTRLEN + #define INET6_ADDRSTRLEN 46 + #endif + + // define bzero() as a macro if it's not in standard C library + #ifndef HAVE_BZERO + #define bzero(ptr,n) memset(ptr,0,n) + #endif + + // older resolvers don't have gethostbyname2() + #ifndef HAVE_GETHOSTBYNAME2 + #define gethostbyname2(host,family) gethostbyname((host)) + #endif + + // The structure returned by recvfrom_flags() + //struct in_pktinfo{ + // struct in_addr ipi_addr; // dst IPv4 address + // int ipi_ifindex; // received interface index + //}; + + // We need the newer CMSG_LEN() and CMSG_SPACE() macros, but few + // implementations support them today. These two macros really + // need an ASIGN() macro, but each implementation does this differently + #ifndef CMSG_LEN + #define CMSG_LEN(size) (sizeof(struct cmsghdr) + (size)) + #endif + #ifndef CMSG_SPACE + #define CMSG_SPACE(size) (sizeof(struct cmsghdr) + (size)) + #endif + + // Posix.1g requires the SUN_LEN() macro but not all implementations define + // it (yet). Note that this 4.4BSD macro works regardless whether there is + // a length field or not + #ifndef SUN_LEN + #define SUN_LEN(su) (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path)) + #endif + + // Posix.1g renames "Unix domain" as "local IPC" + // But not all systems define AF_LOCAL and PF_LOCAL (yet) + #ifndef AF_LOCAL + #define AF_LOCAL AF_UNIX + #endif + #ifndef PF_LOCAL + #define PF_LOCAL PF_UNIX + #endif + + + + + // Posix.1g requires that an #include of <poll.h> define INFTIM, but many + // systems still define it in <sys/stropts.h> We don't want to include + // all the streams stuff if it's not needed, so we just define INFTIM here. + // This is the standard value, but there's no guarentee it is -1 + #ifndef INFTIM + #define INFTIM (-1) // infinite poll timeout + #ifdef HAVE_POLL_H + #define INFTIM_UNPH // tell unpxti.h we defined it + #endif + #endif + + // Following could be derived from SOMAXCONN in <sys/socket.h>, but many + // kernels still #define it as 5, while actually supporting many more + #define LISTENQ 1024 + + // Miscillaneous constants + #define MAXLINE 4096 // max text line length + #define MAXSOCKADDR 128 // max socket address structure size + #define BUFFSIZE 8192 // buffer size for reads and writes + + + + + // Define some port number that can be used for client-servers + #define SERV_PORT 9877 // TCP and UDP client-servers + #define SERV_PORT_STR "9877" // TCP and UDP client-servers + #define UNIXSTR_PATH "/tmp/unix.str" // Unix domain stream cli-serv + #define UNIXDG_PATH "/tmp/unix.dg" // Unix domain datagram cli-serv + + // Following shortens all the type casts of pointer arguments + #define SA struct sockaddr + + #define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) + // default file access permissions for new files + #define DIR_MODE (FILE_MODE | S_IXUSR | S_IXGRP | S_IXOTH) + + typedef void Sigfunc (int); // for signal handlers + +// Commented b/c these guys break the compile + #define MIN(a,b) ((a) < (b) ? (a) : (b)) + #define MAX(a,b) ((a) > (b) ? (a) : (b)) + +/* // will this work? */ +/* //#ifndef HAVE_ADDRINFO_STRUCT */ +/* //#include "../lib/addrinfo.h" */ +/* //#endif */ + + #ifndef HAVE_IF_NAMEINDEX_STRUCT + struct if_nameindex { + unsigned int if_index; // 1, 2, ... + char *if_name; // sull terminated name: "1e0", ... + }; + #endif + + + + +//#ifndef HAVE_TIMESPEC_STRUCT +//struct timespec{ +// time_t tv_sec; // seconds +// long nv_nset; // and nanoseconds +//}; +//#endif + +#endif diff --git a/Source/Processors/AudioNode.cpp b/Source/Processors/AudioNode.cpp new file mode 100644 index 0000000000000000000000000000000000000000..41a9538874221de04caaab24a5b114a02fa29fca --- /dev/null +++ b/Source/Processors/AudioNode.cpp @@ -0,0 +1,91 @@ +/* + ============================================================================== + + AudioNode.cpp + Created: 14 Jul 2011 6:04:39pm + Author: jsiegle + + ============================================================================== +*/ + + +#include "AudioNode.h" + +AudioNode::AudioNode() + : GenericProcessor("Audio Node") +{ + + // 64 inputs, 2 outputs (left and right channel) + setPlayConfigDetails(64,2,44100.0,128); + + leftChan.add(0); + rightChan.add(1); +} + + +AudioNode::~AudioNode() { + +} + +AudioProcessorEditor* AudioNode::createEditor() +{ + + AudioEditor* editor = new AudioEditor(this); + + setEditor(editor); + + return editor; + +} + + +void AudioNode::setParameter (int parameterIndex, float newValue) +{ + // change left channel, right channel, or volume + if (parameterIndex == 1) // volume level + volume = newValue; + +} + + +void AudioNode::prepareToPlay (double sampleRate_, int estimatedSamplesPerBlock) +{ + +} + +void AudioNode::releaseResources() +{ + +} + + +void AudioNode::process(AudioSampleBuffer &buffer, + MidiBuffer &midiMessages, + int& nSamples) +{ + + buffer.clear(0,0,buffer.getNumSamples()); + buffer.clear(1,0,buffer.getNumSamples()); + + for (int n = 0; n < leftChan.size(); n++) { + buffer.addFrom(0, // destination channel + 0, // destination start sample + buffer, // source + leftChan[n]+2, // source channel + 0, // source start sample + buffer.getNumSamples(), // number of samples + volume // gain to apply + ); + } + + for (int n = 0; n < rightChan.size(); n++) { + buffer.addFrom(1, // destination channel + 0, // destination start sample + buffer, // source + rightChan[n]+2, // source channel + 0, // source start sample + buffer.getNumSamples(), // number of samples + volume // gain to apply + ); + } +} diff --git a/Source/Processors/AudioNode.h b/Source/Processors/AudioNode.h new file mode 100644 index 0000000000000000000000000000000000000000..7270d5824390db92aea90ec1367ebfd08c680386 --- /dev/null +++ b/Source/Processors/AudioNode.h @@ -0,0 +1,51 @@ +/* + ============================================================================== + + AudioNode.h + Created: 14 Jul 2011 6:04:39pm + Author: jsiegle + + ============================================================================== +*/ + +#ifndef __AUDIONODE_H_AF61F3C5__ +#define __AUDIONODE_H_AF61F3C5__ + + +#include "../../JuceLibraryCode/JuceHeader.h" +#include <stdio.h> + +#include "GenericProcessor.h" +#include "Editors/AudioEditor.h" + +class AudioNode : public GenericProcessor +{ +public: + + // real member functions: + AudioNode(); + ~AudioNode(); + + void prepareToPlay (double sampleRate, int estimatedSamplesPerBlock); + void releaseResources(); + void process(AudioSampleBuffer &buffer, MidiBuffer &midiMessages, int& nSamples); + + void setParameter (int parameterIndex, float newValue); + + AudioProcessorEditor* createEditor(); + +private: + + Array<int> leftChan; + Array<int> rightChan; + float volume; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioNode); + +}; + + + + + +#endif // __AUDIONODE_H_AF61F3C5__ diff --git a/Source/Processors/DataThreads/DataBuffer.cpp b/Source/Processors/DataThreads/DataBuffer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3258c2bf75e37fb0df7c9554228fbed357cce970 --- /dev/null +++ b/Source/Processors/DataThreads/DataBuffer.cpp @@ -0,0 +1,79 @@ +/* + ============================================================================== + + DataBuffer.cpp + Created: 27 May 2011 2:13:42pm + Author: jsiegle + + ============================================================================== +*/ + +#include "DataBuffer.h" + +DataBuffer::DataBuffer(int chans, int size) + : abstractFifo (size), buffer(chans, size), numChans(chans) {} + + +DataBuffer::~DataBuffer() {} + +void DataBuffer::clear() { + buffer.clear(); +} + +void DataBuffer::addToBuffer(float* data, int numItems) { + // writes one sample for all channels + int startIndex1, blockSize1, startIndex2, blockSize2; + abstractFifo.prepareToWrite(numItems, startIndex1, blockSize1, startIndex2, blockSize2); + + for (int chan = 0; chan < numChans; chan++) { + + buffer.copyFrom(chan, // int destChannel + startIndex1, // int destStartSample + data + chan, // const float* source + 1); // int num samples + } + abstractFifo.finishedWrite(numItems); +} + +int DataBuffer::getNumSamples() { + return abstractFifo.getNumReady(); +} + + +int DataBuffer::readAllFromBuffer (AudioSampleBuffer& data, int maxSize) +{ + // check to see if the maximum size is smaller than the total number of available ints + int numItems = (maxSize < abstractFifo.getNumReady()) ? + maxSize : abstractFifo.getNumReady(); + + int startIndex1, blockSize1, startIndex2, blockSize2; + abstractFifo.prepareToRead(numItems, startIndex1, blockSize1, startIndex2, blockSize2); + + if (blockSize1 > 0) { + for (int chan = 0; chan < data.getNumChannels(); chan++) { + data.copyFrom(chan, // destChan + 0, // destStartSample + buffer, // source + chan, // sourceChannel + startIndex1, // sourceStartSample + blockSize1); // numSamples + } + } + + if (blockSize2 > 0) { + + for (int chan = 0; chan < data.getNumChannels(); chan++) { + data.copyFrom(chan, // destChan + blockSize1, // destStartSample + buffer, // source + chan, // sourceChannel + startIndex2, // sourceStartSample + blockSize2); // numSamples + } + } + + abstractFifo.finishedRead(numItems); + + return numItems; + +} \ No newline at end of file diff --git a/Source/Processors/DataThreads/DataBuffer.h b/Source/Processors/DataThreads/DataBuffer.h new file mode 100644 index 0000000000000000000000000000000000000000..2695ab61d314e6ebe38189f3c9d550bcf34e4886 --- /dev/null +++ b/Source/Processors/DataThreads/DataBuffer.h @@ -0,0 +1,34 @@ +/* + ============================================================================== + + DataBuffer.h + Created: 27 May 2011 2:13:42pm + Author: jsiegle + + ============================================================================== +*/ + +#ifndef __DATABUFFER_H_11C6C591__ +#define __DATABUFFER_H_11C6C591__ + +#include "../../../JuceLibraryCode/JuceHeader.h" + +class DataBuffer +{ + +public: + DataBuffer(int chans, int size); + ~DataBuffer(); + void clear(); + void addToBuffer(float* data, int numItems); + int getNumSamples(); + int readAllFromBuffer(AudioSampleBuffer& data, int maxSize); + +private: + AbstractFifo abstractFifo; + AudioSampleBuffer buffer; + int numChans; +}; + + +#endif // __DATABUFFER_H_11C6C591__ diff --git a/Source/Processors/DataThreads/DataThread.cpp b/Source/Processors/DataThreads/DataThread.cpp new file mode 100644 index 0000000000000000000000000000000000000000..69cd98626ba43b4966c57250b4dcb3646b55934b --- /dev/null +++ b/Source/Processors/DataThreads/DataThread.cpp @@ -0,0 +1,31 @@ +/* + ============================================================================== + + DataThread.cpp + Created: 9 Jun 2011 1:31:49pm + Author: jsiegle + + ============================================================================== +*/ + +#include "DataThread.h" + +DataThread::DataThread() : Thread ("Data Thread"), dataBuffer(0) {} + +DataThread::~DataThread() {} + +void DataThread::run() { + + while (! threadShouldExit()) + { + const MessageManagerLock mml (Thread::getCurrentThread()); + if (! mml.lockWasGained()) + return; + updateBuffer(); + } +} + +DataBuffer* DataThread::getBufferAddress() { + return dataBuffer; +} + diff --git a/Source/Processors/DataThreads/DataThread.h b/Source/Processors/DataThreads/DataThread.h new file mode 100644 index 0000000000000000000000000000000000000000..5393cc9bed8fe83a8f90d5c9a7904eac674d8957 --- /dev/null +++ b/Source/Processors/DataThreads/DataThread.h @@ -0,0 +1,39 @@ +/* + ============================================================================== + + DataThread.h + Created: 9 Jun 2011 1:31:49pm + Author: jsiegle + + ============================================================================== +*/ + +#ifndef __DATATHREAD_H_C454F4DB__ +#define __DATATHREAD_H_C454F4DB__ + +#include "../../../JuceLibraryCode/JuceHeader.h" +#include <stdio.h> +#include "DataBuffer.h" + +class DataThread : public Thread +{ + +public: + + DataThread(); + ~DataThread(); + + void run(); + + DataBuffer* getBufferAddress(); + + virtual void updateBuffer() = 0; + + DataBuffer* dataBuffer; + + virtual bool threadStarted() {return true;} + +}; + + +#endif // __DATATHREAD_H_C454F4DB__ diff --git a/Source/Processors/DataThreads/FPGAThread.cpp b/Source/Processors/DataThreads/FPGAThread.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7e1b9e45bee8da1d8d78da2b418e138fd49b6487 --- /dev/null +++ b/Source/Processors/DataThreads/FPGAThread.cpp @@ -0,0 +1,149 @@ +/* + ============================================================================== + + FPGAThread.cpp + Created: 9 Jun 2011 2:08:11pm + Author: jsiegle + + ============================================================================== +*/ + +#include "FPGAThread.h" + +FPGAThread::FPGAThread() : DataThread(), + isRunning(false), + numchannels(32), + m_u32SegmentSize(1048576) + +{ + + // Initialize the FPGA with our configuration bitfile. + printf("New device created.\n"); + const char* bitfilename = "./pipetest.bit"; + + printf("---- Opal Kelly ---- PipeTest Application v1.0 ----\n"); + + if (FALSE == okFrontPanelDLL_LoadLib(NULL)) { + printf("FrontPanel DLL could not be loaded.\n"); + } + + okFrontPanelDLL_GetVersion(dll_date, dll_time); + printf("FrontPanel DLL loaded. Built: %s %s\n", dll_date, dll_time); + + dev = new okCFrontPanel; + + strncpy(bitfile, bitfilename, 128); + + if (!initializeFPGA(dev, bitfile)) { + printf("FPGA could not be initialized.\n"); + } + + Ndatabytes = numchannels*3; + + std::cout << "FPGA interface initialized." << std::endl; + + dataBuffer = new DataBuffer(32, 10000); + + startThread(); + +} + + +FPGAThread::~FPGAThread() { + + stopThread(500); + + std::cout << "FPGA interface destroyed." << std::endl; + + // probably not the best way to do this: + delete dataBuffer; + delete dev; + dev = 0; + dataBuffer = 0; + +} + +void FPGAThread::updateBuffer() { + + dev->ReadFromPipeOut(0xA0, sizeof(pBuffer), pBuffer); + + int j = 0; + + while (j < sizeof(pBuffer)) + { + // look for timecode block (6 bytes) + if ( (pBuffer[j] & 1) && (pBuffer[j+1] & 1) && (pBuffer[j+2] & 1) && (pBuffer[j+3] & 1) && (pBuffer[j+4] & 1) && (pBuffer[j+5] & 1) && (j+5+Ndatabytes <= sizeof(pBuffer)) ) // indicated by last bit being 1 + { //read 6 bytes, assemble to 6*7 = 42 bits, arranged in 6 bytes + char timecode[6]; // 1st byte throw out last bit of each byte and just concatenate the other bytes in ascending order + timecode[0] = (pBuffer[j] >> 1) | ((pBuffer[j+1] >> 1) << 7); // 2nd byte + timecode[1] = (pBuffer[j+1] >> 2) | ((pBuffer[j+2] >> 1) << 6); // 3rd byte + timecode[2] = (pBuffer[j+2] >> 3) | ((pBuffer[j+3] >> 1) << 5); // 4th byte + timecode[3] = (pBuffer[j+3] >> 4) | ((pBuffer[j+4] >> 1) << 4); // 5th byte + timecode[4] = (pBuffer[j+4] >> 5) | ((pBuffer[j+5] >> 1) << 3); // 6th byte + timecode[5] = (pBuffer[j+5] >> 6); + + j += 6; //move cursor to 1st data byte + + // loop through sample data and condense from 3 bytes to 2 bytes + char hi; char lo; + for (int n = 0; n < numchannels ; n++) + { + // last bit of first 2 is zero, replace with bits 1 and 2 from 3rd byte + hi = (pBuffer[j]) | ((( pBuffer[j+2] >> 2) & ~(1<<6)) & ~(1<<7)) ; + lo = (pBuffer[j+1]) | ((( pBuffer[j+2] >> 1) & ~(1<<1)) & ~(1<<7)) ; + j += 3; + + //thisSample[n] = float( hi - 256)/256; + thisSample[n] = float(float(hi)*256 + lo - 32768)/32768; + + } + dataBuffer->addToBuffer(thisSample,1); + } + j++; // keep scanning for timecodes + } +} + + + + +bool FPGAThread::initializeFPGA(okCFrontPanel *dev, char *bitfile) +{ + if (okCFrontPanel::NoError != dev->OpenBySerial()) { + delete dev; + printf("Device could not be opened. Is one connected?\n"); + return(NULL); + } + + printf("Found a device: %s\n", dev->GetBoardModelString(dev->GetBoardModel()).c_str()); + + dev->LoadDefaultPLLConfiguration(); + + // Get some general information about the XEM. + std::string str; + printf("Device firmware version: %d.%d\n", dev->GetDeviceMajorVersion(), dev->GetDeviceMinorVersion()); + str = dev->GetSerialNumber(); + printf("Device serial number: %s\n", str.c_str()); + str = dev->GetDeviceID(); + printf("Device device ID: %s\n", str.c_str()); + + // Download the configuration file. + if (okCFrontPanel::NoError != dev->ConfigureFPGA(bitfile)) { + printf("FPGA configuration failed.\n"); + return(false); + } + + // Check for FrontPanel support in the FPGA configuration. + if (dev->IsFrontPanelEnabled()) + printf("FrontPanel support is enabled.\n"); + else + printf("FrontPanel support is not enabled.\n"); + + return(true); + + dev->SetWireInValue(0x00, 1<<2); // set reset bit in cmd wire to 1 and back to 0 + dev->UpdateWireIns(); + dev->SetWireInValue(0x00, 0<<2); + dev->UpdateWireIns(); + +} + diff --git a/Source/Processors/DataThreads/FPGAThread.h b/Source/Processors/DataThreads/FPGAThread.h new file mode 100644 index 0000000000000000000000000000000000000000..7dcb7182e28fb68413987b316c5b4fb96ea490e6 --- /dev/null +++ b/Source/Processors/DataThreads/FPGAThread.h @@ -0,0 +1,60 @@ +/* + ============================================================================== + + FPGAThread.h + Created: 9 Jun 2011 2:08:11pm + Author: jsiegle + + ============================================================================== +*/ + +#ifndef __FPGATHREAD_H_FBB22A45__ +#define __FPGATHREAD_H_FBB22A45__ + + +#include <stdio.h> +#include <string.h> +#include <iostream> +#include <time.h> + +#include "../../../JuceLibraryCode/JuceHeader.h" + +#include "okFrontPanelDLL.h" +#include "DataThread.h" + +class FPGAThread : public DataThread + +{ +public: + FPGAThread(); + ~FPGAThread(); + +private: + + okCFrontPanel* dev; + char bitfile[128]; + char dll_date[32], dll_time[32]; + UINT32 i; + + int m_u32SegmentSize; + + unsigned char pBuffer[500000]; // request a 1MB block of data + + bool isRunning; + + DataBuffer* dataBuffer; + + float thisSample[32]; + + int numchannels; + int Ndatabytes; + + void updateBuffer(); + bool initializeFPGA(okCFrontPanel*, char*); + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FPGAThread); +}; + + + +#endif // __FPGATHREAD_H_FBB22A45__ diff --git a/Source/Processors/DataThreads/FileReaderThread.cpp b/Source/Processors/DataThreads/FileReaderThread.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e1e75a8ffa906a144f3c923fd1ac8c9acd7e24e6 --- /dev/null +++ b/Source/Processors/DataThreads/FileReaderThread.cpp @@ -0,0 +1,62 @@ +/* + ============================================================================== + + FileReaderThread.cpp + Created: 6 Sep 2011 11:05:57am + Author: jsiegle + + ============================================================================== +*/ + + +#include "FileReaderThread.h" + +FileReaderThread::FileReaderThread() : DataThread(), + sampleRate(40000.0), + numChannels(16), + samplesPerBlock(1024) + +{ + File file = File("./data_stream_16ch"); + input = file.createInputStream(); + + dataBuffer = new DataBuffer(16, 4096); + + std::cout << "File Reader Thread initialized." << std::endl; + + startThread(); +} + +FileReaderThread::~FileReaderThread() { + + stopThread(500); + + std::cout << "File reader received disable signal." << std::endl; + + deleteAndZero(input); + + delete dataBuffer; + dataBuffer = 0; +} + + +void FileReaderThread::updateBuffer() +{ + + while (dataBuffer->getNumSamples() < 4096) + { + + for (int ch = 0; ch < numChannels; ch++) { + + if (input->isExhausted()) + input->setPosition(0); + + thisSample[ch%numChannels] = float(input->readShort()); + + } + + dataBuffer->addToBuffer(thisSample,1); + + } + +} diff --git a/Source/Processors/DataThreads/FileReaderThread.h b/Source/Processors/DataThreads/FileReaderThread.h new file mode 100644 index 0000000000000000000000000000000000000000..bf4309fb548dee20a5e342496597b853203c7d49 --- /dev/null +++ b/Source/Processors/DataThreads/FileReaderThread.h @@ -0,0 +1,44 @@ +/* + ============================================================================== + + FileReaderThread.h + Created: 6 Sep 2011 11:05:57am + Author: jsiegle + + ============================================================================== +*/ + +#ifndef __FILEREADERTHREAD_H_82594504__ +#define __FILEREADERTHREAD_H_82594504__ + + +#include "../../../JuceLibraryCode/JuceHeader.h" +#include <ftdi.h> +#include <stdio.h> +#include "DataThread.h" + +class FileReaderThread : public DataThread + +{ +public: + FileReaderThread(); + ~FileReaderThread(); + +private: + + float sampleRate; + int numChannels; + int samplesPerBlock; + + FileInputStream* input; + + float thisSample[16]; + + void updateBuffer(); + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileReaderThread); +}; + + + +#endif // __FILEREADERTHREAD_H_82594504__ diff --git a/Source/Processors/DataThreads/IntanThread.cpp b/Source/Processors/DataThreads/IntanThread.cpp new file mode 100644 index 0000000000000000000000000000000000000000..71d5cb8840775700a1dbeecf0bf70d2786be646d --- /dev/null +++ b/Source/Processors/DataThreads/IntanThread.cpp @@ -0,0 +1,131 @@ +/* + ============================================================================== + + IntanThread.cpp + Created: 9 Jun 2011 1:34:16pm + Author: jsiegle + + ============================================================================== +*/ + +#include "IntanThread.h" + +IntanThread::IntanThread() : DataThread(), + vendorID(0x0403), + productID(0x6010), + baudrate(115200), + startCode(83), + stopCode(115), + ch(-1), + isTransmitting(false) + +{ + + dataBuffer = new DataBuffer(16,4096); + + if (initializeUSB()) + { + std::cout << "FTDI interface initialized." << std::endl; + ftdi_write_data(&ftdic, &startCode, 1); + startThread(); + isTransmitting = true; + } + + +} + +IntanThread::~IntanThread() { + + stopThread(500); + + if (isTransmitting) { + ftdi_write_data(&ftdic, &stopCode, 1); + unsigned char buf[4097]; // has to be bigger than the on-chip buffer + ftdi_read_data(&ftdic, buf, sizeof(buf)); + + ftdi_usb_close(&ftdic); + ftdi_deinit(&ftdic); + std::cout << "FTDI interface destroyed." << std::endl; + } + + deleteAndZero(dataBuffer); +} + +bool IntanThread::initializeUSB() +{ + int return_value; + + // Step 1: initialise the ftdi_context: + if (ftdi_init(&ftdic) < 0) {// -1 = couldn't allocate read buffer + // -2 = couldn't allocate struct buffer + fprintf(stderr, "ftdi_init failed\n"); + return false; + } else { + std::cout << "FTDI context initialized." << std::endl; + } + + // Step 2: open USB device + // -3 = device not found + // -8 = wrong permissions + if ((return_value = ftdi_usb_open(&ftdic, vendorID, productID)) < 0) + { + fprintf(stderr, "unable to open FTDI device: %d (%s)\n", + return_value, + ftdi_get_error_string(&ftdic)); + return false; + } else { + std::cout << "USB connection opened." << std::endl; + } + + // Step 3: set the baud rate + if ((return_value = ftdi_set_baudrate(&ftdic, baudrate)) < 0) + { + fprintf(stderr, "unable to set baud rate: %d (%s)\n", + return_value, + ftdi_get_error_string(&ftdic)); + return false; + } else { + std::cout << "Baud rate set to 115200" << std::endl; + } + + + + return true; + +} + + +void IntanThread::updateBuffer() +{ + + // Step 1: update buffer + ftdi_read_data(&ftdic, buffer, sizeof(buffer)); + + + // Step 2: sort data + int TTLval, channelVal; + + for (int index = 0; index < sizeof(buffer); index += 3) { + + ++ch; + + for (int n = 0; n < 1; n++) { // + + thisSample[ch%16+n*16] = float((buffer[index] & 127) + + ((buffer[index+1] & 127) << 7) + + ((buffer[index+2] & 3) << 14) - 32768)/32768; + + } + + + TTLval = (buffer[index+2] & 4) >> 2; // extract TTL value (bit 3) + channelVal = buffer[index+2] & 60; // extract channel value + + if (channelVal == 60) { + dataBuffer->addToBuffer(thisSample,1); + ch = -1; + } + + } +} + diff --git a/Source/Processors/DataThreads/IntanThread.h b/Source/Processors/DataThreads/IntanThread.h new file mode 100644 index 0000000000000000000000000000000000000000..14a8c16e070edc17769f54df6477fd8a266ef180 --- /dev/null +++ b/Source/Processors/DataThreads/IntanThread.h @@ -0,0 +1,51 @@ +/* + ============================================================================== + + IntanThread.h + Created: 9 Jun 2011 1:34:16pm + Author: jsiegle + + ============================================================================== +*/ + +#ifndef __INTANTHREAD_H_D9135C03__ +#define __INTANTHREAD_H_D9135C03__ + +#include "../../../JuceLibraryCode/JuceHeader.h" +#include <ftdi.h> +#include <stdio.h> +#include "DataThread.h" + +class IntanThread : public DataThread + +{ +public: + IntanThread(); + ~IntanThread(); + + bool threadStarted() {return isTransmitting;} + +private: + + struct ftdi_context ftdic; + int vendorID, productID; + int baudrate; + bool isTransmitting; + + bool initializeUSB(); + + unsigned char startCode, stopCode; + unsigned char buffer[240]; // should be 5 samples per channel + + float thisSample[16]; + //float thisSample[64]; + + int ch; + + void updateBuffer(); + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (IntanThread); +}; + + +#endif // __INTANTHREAD_H_D9135C03__ diff --git a/Source/Processors/DataThreads/NetworkThread.cpp b/Source/Processors/DataThreads/NetworkThread.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1591ad729016aead818fca40d18a147069032b21 --- /dev/null +++ b/Source/Processors/DataThreads/NetworkThread.cpp @@ -0,0 +1,51 @@ +/* + ============================================================================== + + NetworkThread.cpp + Created: 9 Jun 2011 2:08:29pm + Author: jsiegle + + ============================================================================== +*/ + +#include "NetworkThread.h" + +NetworkThread::NetworkThread() : DataThread() +{ + char host[] = "10.121.43.47"; + char port[] = "5227"; + + my_netcomdat = my_netcom.initUdpRx(host, port); + + dataBuffer = new DataBuffer(8, 4096); + + startThread(); + + std::cout << "Network interface created." << std::endl; +} + +NetworkThread::~NetworkThread() { + stopThread(500); + close(my_netcomdat.sockfd); + + // need to close socket in order to reopen + close(my_netcomdat.sockfd); + + std::cout << "Network interface destroyed." << std::endl; + + delete dataBuffer; + dataBuffer = 0; +} + + +void NetworkThread::updateBuffer(){ + + NetCom::rxWave (my_netcomdat, &lfp); + + for (int s = 0; s < lfp.n_samps_per_chan; s++) { + for (int c = 0; c < lfp.n_chans; c++) { + thisSample[c] = float(lfp.data[s*lfp.n_chans + c])/500.0f; + } + dataBuffer->addToBuffer(thisSample,1); + } +} \ No newline at end of file diff --git a/Source/Processors/DataThreads/NetworkThread.h b/Source/Processors/DataThreads/NetworkThread.h new file mode 100644 index 0000000000000000000000000000000000000000..838e03ae01a30dce797c0428886a5a9194535f3a --- /dev/null +++ b/Source/Processors/DataThreads/NetworkThread.h @@ -0,0 +1,50 @@ +/* + ============================================================================== + + NetworkThread.h + Created: 9 Jun 2011 2:08:29pm + Author: jsiegle + + ============================================================================== +*/ + +#ifndef __NETWORKTHREAD_H_DD31EB15__ +#define __NETWORKTHREAD_H_DD31EB15__ + +#include <stdio.h> + +#include "../../../JuceLibraryCode/JuceHeader.h" + +#include "../../Network/unp.h" +#include "../../Network/netcom.h" +#include "../../Network/datapacket.h" + +#include "DataThread.h" + +class NetworkThread : public DataThread +{ +public: + + NetworkThread(); + ~NetworkThread(); + +private: + + NetCom my_netcom; + NetComDat my_netcomdat; + + lfp_bank_net_t lfp; + + DataBuffer* dataBuffer; + + float thisSample[8]; + + void updateBuffer(); + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NetworkThread); + +}; + + + +#endif // __NETWORKTHREAD_H_DD31EB15__ diff --git a/Source/Processors/DataThreads/okFrontPanelDLL.cpp b/Source/Processors/DataThreads/okFrontPanelDLL.cpp new file mode 100644 index 0000000000000000000000000000000000000000..519f5c0ba3ba624678162fb7db67b2c88e704ee2 --- /dev/null +++ b/Source/Processors/DataThreads/okFrontPanelDLL.cpp @@ -0,0 +1,1500 @@ +//------------------------------------------------------------------------ +// okFrontPanelDLL.c +// +// This is the import source for the FrontPanel API DLL. If you are +// building an application using the DLL, this source should be included +// within your C++/C project. It includes methods that will +// automatically load the DLL and map function calls to the DLL entry +// points. +// +// This library is not necessary when you call the DLL methods from +// another application or language such as LabVIEW or VisualBasic. +// +// This methods in this DLL correspond closely with the C++ API. +// Therefore, the C++ API documentation serves as the documentation for +// this DLL. +// +// +// NOTE: Before any API function calls are made, you MUST call: +// okFrontPanelDLL_LoadLib +// +// When you are finished using the API methods, you should call: +// okFrontPanelDLL_FreeLib +// +// The current DLL version can be retrieved by calling: +// okFrontPanelDLL_GetVersionString +// +//------------------------------------------------------------------------ +// Copyright (c) 2005-2009 Opal Kelly Incorporated +// $Rev: 925 $ $Date: 2011-02-25 17:09:05 -0800 (Fri, 25 Feb 2011) $ +//------------------------------------------------------------------------ + +#include <stdio.h> +#include <stdlib.h> + + +#include "okFrontPanelDLL.h" + +#if defined(_WIN32) + #if !defined(okLIB_NAME) + #define okLIB_NAME "okFrontPanel.dll" + #endif +#elif defined(__APPLE__) + #include <dlfcn.h> + #define okLIB_NAME "libokFrontPanel.dylib" +#elif defined(__linux__) + #include <dlfcn.h> + #define okLIB_NAME "./libokFrontPanel.so" +#endif + +typedef void DLL; +static DLL *hLib = NULL; +static DLL_EP dll_entrypoint(DLL *dll, const char *name); +static DLL *dll_load(const char *libname); +static void dll_unload(DLL *dll); +static char VERSION_STRING[32]; + + +#ifdef __cplusplus +#include <string> +//------------------------------------------------------------------------ +// okCPLL22150 C++ wrapper class +//------------------------------------------------------------------------ +bool okCPLL22150::to_bool(Bool x) + { return( (x==TRUE)?(true):(false) ); } +Bool okCPLL22150::from_bool(bool x) + { return( (x==true)?(TRUE):(FALSE) ); } +okCPLL22150::okCPLL22150() + { h=okPLL22150_Construct(); } +void okCPLL22150::SetCrystalLoad(double capload) + { okPLL22150_SetCrystalLoad(h, capload); } +void okCPLL22150::SetReference(double freq, bool extosc) + { okPLL22150_SetReference(h, freq, from_bool(extosc)); } +double okCPLL22150::GetReference() + { return(okPLL22150_GetReference(h)); } +bool okCPLL22150::SetVCOParameters(int p, int q) + { return(to_bool(okPLL22150_SetVCOParameters(h,p,q))); } +int okCPLL22150::GetVCOP() + { return(okPLL22150_GetVCOP(h)); } +int okCPLL22150::GetVCOQ() + { return(okPLL22150_GetVCOQ(h)); } +double okCPLL22150::GetVCOFrequency() + { return(okPLL22150_GetVCOFrequency(h)); } +void okCPLL22150::SetDiv1(DividerSource divsrc, int n) + { okPLL22150_SetDiv1(h, (ok_DividerSource)divsrc, n); } +void okCPLL22150::SetDiv2(DividerSource divsrc, int n) + { okPLL22150_SetDiv2(h, (ok_DividerSource)divsrc, n); } +okCPLL22150::DividerSource okCPLL22150::GetDiv1Source() + { return((DividerSource) okPLL22150_GetDiv1Source(h)); } +okCPLL22150::DividerSource okCPLL22150::GetDiv2Source() + { return((DividerSource) okPLL22150_GetDiv2Source(h)); } +int okCPLL22150::GetDiv1Divider() + { return(okPLL22150_GetDiv1Divider(h)); } +int okCPLL22150::GetDiv2Divider() + { return(okPLL22150_GetDiv2Divider(h)); } +void okCPLL22150::SetOutputSource(int output, okCPLL22150::ClockSource clksrc) + { okPLL22150_SetOutputSource(h, output, (ok_ClockSource_22150)clksrc); } +void okCPLL22150::SetOutputEnable(int output, bool enable) + { okPLL22150_SetOutputEnable(h, output, to_bool(enable)); } +okCPLL22150::ClockSource okCPLL22150::GetOutputSource(int output) + { return( (ClockSource)okPLL22150_GetOutputSource(h, output)); } +double okCPLL22150::GetOutputFrequency(int output) + { return(okPLL22150_GetOutputFrequency(h, output)); } +bool okCPLL22150::IsOutputEnabled(int output) + { return(to_bool(okPLL22150_IsOutputEnabled(h, output))); } +void okCPLL22150::InitFromProgrammingInfo(unsigned char *buf) + { okPLL22150_InitFromProgrammingInfo(h, buf); } +void okCPLL22150::GetProgrammingInfo(unsigned char *buf) + { okPLL22150_GetProgrammingInfo(h, buf); } + +//------------------------------------------------------------------------ +// okCPLL22393 C++ wrapper class +//------------------------------------------------------------------------ +bool okCPLL22393::to_bool(Bool x) + { return( (x==TRUE)?(true):(false) ); } +Bool okCPLL22393::from_bool(bool x) + { return( (x==true)?(TRUE):(FALSE) ); } +okCPLL22393::okCPLL22393() + { h=okPLL22393_Construct(); } +void okCPLL22393::SetCrystalLoad(double capload) + { okPLL22393_SetCrystalLoad(h, capload); } +void okCPLL22393::SetReference(double freq) + { okPLL22393_SetReference(h, freq); } +double okCPLL22393::GetReference() + { return(okPLL22393_GetReference(h)); } +bool okCPLL22393::SetPLLParameters(int n, int p, int q, bool enable) + { return(to_bool(okPLL22393_SetPLLParameters(h, n, p, q, from_bool(enable)))); } +bool okCPLL22393::SetPLLLF(int n, int lf) + { return(to_bool(okPLL22393_SetPLLLF(h, n, lf))); } +bool okCPLL22393::SetOutputDivider(int n, int div) + { return(to_bool(okPLL22393_SetOutputDivider(h, n, div))); } +bool okCPLL22393::SetOutputSource(int n, okCPLL22393::ClockSource clksrc) + { return(to_bool(okPLL22393_SetOutputSource(h, n, (ok_ClockSource_22393)clksrc))); } +void okCPLL22393::SetOutputEnable(int n, bool enable) + { okPLL22393_SetOutputEnable(h, n, from_bool(enable)); } +int okCPLL22393::GetPLLP(int n) + { return(okPLL22393_GetPLLP(h, n)); } +int okCPLL22393::GetPLLQ(int n) + { return(okPLL22393_GetPLLQ(h, n)); } +double okCPLL22393::GetPLLFrequency(int n) + { return(okPLL22393_GetPLLFrequency(h, n)); } +int okCPLL22393::GetOutputDivider(int n) + { return(okPLL22393_GetOutputDivider(h, n)); } +okCPLL22393::ClockSource okCPLL22393::GetOutputSource(int n) + { return((ClockSource) okPLL22393_GetOutputSource(h, n)); } +double okCPLL22393::GetOutputFrequency(int n) + { return(okPLL22393_GetOutputFrequency(h, n)); } +bool okCPLL22393::IsOutputEnabled(int n) + { return(to_bool(okPLL22393_IsOutputEnabled(h, n))); } +bool okCPLL22393::IsPLLEnabled(int n) + { return(to_bool(okPLL22393_IsPLLEnabled(h, n))); } +void okCPLL22393::InitFromProgrammingInfo(unsigned char *buf) + { okPLL22393_InitFromProgrammingInfo(h, buf); } +void okCPLL22393::GetProgrammingInfo(unsigned char *buf) + { okPLL22393_GetProgrammingInfo(h, buf); } + +//------------------------------------------------------------------------ +// okCFrontPanel C++ wrapper class +//------------------------------------------------------------------------ +bool okCFrontPanel::to_bool(Bool x) + { return( (x==TRUE)?(true):(false) ); } +Bool okCFrontPanel::from_bool(bool x) + { return( (x==true)?(TRUE):(FALSE) ); } +okCFrontPanel::okCFrontPanel() + { h=okFrontPanel_Construct(); } +okCFrontPanel::~okCFrontPanel() + { okFrontPanel_Destruct(h); } +int okCFrontPanel::GetHostInterfaceWidth() + { return(okFrontPanel_GetHostInterfaceWidth(h)); } +bool okCFrontPanel::IsHighSpeed() + { return(to_bool(okFrontPanel_IsHighSpeed(h))); } +okCFrontPanel::BoardModel okCFrontPanel::GetBoardModel() + { return((okCFrontPanel::BoardModel)okFrontPanel_GetBoardModel(h)); } +std::string okCFrontPanel::GetBoardModelString(okCFrontPanel::BoardModel m) + { + char str[MAX_BOARDMODELSTRING_LENGTH]; + okFrontPanel_GetBoardModelString(h, (ok_BoardModel)m, str); + return(std::string(str)); + } +int okCFrontPanel::GetDeviceCount() + { return(okFrontPanel_GetDeviceCount(h)); } +okCFrontPanel::BoardModel okCFrontPanel::GetDeviceListModel(int num) + { return((okCFrontPanel::BoardModel)okFrontPanel_GetDeviceListModel(h, num)); } +std::string okCFrontPanel::GetDeviceListSerial(int num) + { + char str[MAX_SERIALNUMBER_LENGTH+1]; + okFrontPanel_GetDeviceListSerial(h, num, str); + return(std::string(str)); + } +void okCFrontPanel::EnableAsynchronousTransfers(bool enable) + { okFrontPanel_EnableAsynchronousTransfers(h, to_bool(enable)); } +okCFrontPanel::ErrorCode okCFrontPanel::OpenBySerial(std::string str) + { return((okCFrontPanel::ErrorCode) okFrontPanel_OpenBySerial(h, str.c_str())); } +bool okCFrontPanel::IsOpen() + { return(to_bool(okFrontPanel_IsOpen(h))); } +int okCFrontPanel::GetDeviceMajorVersion() + { return(okFrontPanel_GetDeviceMajorVersion(h)); } +int okCFrontPanel::GetDeviceMinorVersion() + { return(okFrontPanel_GetDeviceMinorVersion(h)); } +std::string okCFrontPanel::GetSerialNumber() + { + char str[MAX_SERIALNUMBER_LENGTH+1]; + okFrontPanel_GetSerialNumber(h, str); + return(std::string(str)); + } +std::string okCFrontPanel::GetDeviceID() + { + char str[MAX_DEVICEID_LENGTH+1]; + okFrontPanel_GetDeviceID(h, str); + return(std::string(str)); + } +void okCFrontPanel::SetDeviceID(const std::string str) + { okFrontPanel_SetDeviceID(h, str.c_str()); } +okCFrontPanel::ErrorCode okCFrontPanel::SetBTPipePollingInterval(int interval) + { return((okCFrontPanel::ErrorCode) okFrontPanel_SetBTPipePollingInterval(h, interval)); } +void okCFrontPanel::SetTimeout(int timeout) + { okFrontPanel_SetTimeout(h, timeout); } +okCFrontPanel::ErrorCode okCFrontPanel::ResetFPGA() + { return((okCFrontPanel::ErrorCode) okFrontPanel_ResetFPGA(h)); } +okCFrontPanel::ErrorCode okCFrontPanel::ConfigureFPGAFromMemory(unsigned char *data, const unsigned long length, void(*callback)(int, int, void *), void *arg) + { return((okCFrontPanel::ErrorCode) okFrontPanel_ConfigureFPGAFromMemory(h, data, length)); } +okCFrontPanel::ErrorCode okCFrontPanel::ConfigureFPGA(const std::string strFilename, void (*callback)(int, int, void *), void *arg) + { return((okCFrontPanel::ErrorCode) okFrontPanel_ConfigureFPGA(h, strFilename.c_str())); } +okCFrontPanel::ErrorCode okCFrontPanel::WriteI2C(const int addr, int length, unsigned char *data) + { return((okCFrontPanel::ErrorCode) okFrontPanel_WriteI2C(h, addr, length, data)); } +okCFrontPanel::ErrorCode okCFrontPanel::ReadI2C(const int addr, int length, unsigned char *data) + { return((okCFrontPanel::ErrorCode) okFrontPanel_ReadI2C(h, addr, length, data)); } +okCFrontPanel::ErrorCode okCFrontPanel::GetPLL22150Configuration(okCPLL22150& pll) + { return((okCFrontPanel::ErrorCode) okFrontPanel_GetPLL22150Configuration(h, pll.h)); } +okCFrontPanel::ErrorCode okCFrontPanel::SetPLL22150Configuration(okCPLL22150& pll) + { return((okCFrontPanel::ErrorCode) okFrontPanel_SetPLL22150Configuration(h, pll.h)); } +okCFrontPanel::ErrorCode okCFrontPanel::GetEepromPLL22150Configuration(okCPLL22150& pll) + { return((okCFrontPanel::ErrorCode) okFrontPanel_GetEepromPLL22150Configuration(h, pll.h)); } +okCFrontPanel::ErrorCode okCFrontPanel::SetEepromPLL22150Configuration(okCPLL22150& pll) + { return((okCFrontPanel::ErrorCode) okFrontPanel_SetEepromPLL22150Configuration(h, pll.h)); } +okCFrontPanel::ErrorCode okCFrontPanel::GetPLL22393Configuration(okCPLL22393& pll) + { return((okCFrontPanel::ErrorCode) okFrontPanel_GetPLL22393Configuration(h, pll.h)); } +okCFrontPanel::ErrorCode okCFrontPanel::SetPLL22393Configuration(okCPLL22393& pll) + { return((okCFrontPanel::ErrorCode) okFrontPanel_SetPLL22393Configuration(h, pll.h)); } +okCFrontPanel::ErrorCode okCFrontPanel::GetEepromPLL22393Configuration(okCPLL22393& pll) + { return((okCFrontPanel::ErrorCode) okFrontPanel_GetEepromPLL22393Configuration(h, pll.h)); } +okCFrontPanel::ErrorCode okCFrontPanel::SetEepromPLL22393Configuration(okCPLL22393& pll) + { return((okCFrontPanel::ErrorCode) okFrontPanel_SetEepromPLL22393Configuration(h, pll.h)); } +okCFrontPanel::ErrorCode okCFrontPanel::LoadDefaultPLLConfiguration() + { return((okCFrontPanel::ErrorCode) okFrontPanel_LoadDefaultPLLConfiguration(h)); } +bool okCFrontPanel::IsFrontPanelEnabled() + { return(to_bool(okFrontPanel_IsFrontPanelEnabled(h))); } +bool okCFrontPanel::IsFrontPanel3Supported() + { return(to_bool(okFrontPanel_IsFrontPanel3Supported(h))); } +// void UnregisterAll(); +// void AddEventHandler(okCEventHandler *handler); +void okCFrontPanel::UpdateWireIns() + { okFrontPanel_UpdateWireIns(h); } +okCFrontPanel::ErrorCode okCFrontPanel::SetWireInValue(int ep, UINT32 val, UINT32 mask) + { return((okCFrontPanel::ErrorCode) okFrontPanel_SetWireInValue(h, ep, val, mask)); } +void okCFrontPanel::UpdateWireOuts() + { okFrontPanel_UpdateWireOuts(h); } +UINT32 okCFrontPanel::GetWireOutValue(int epAddr) + { return(okFrontPanel_GetWireOutValue(h, epAddr)); } +okCFrontPanel::ErrorCode okCFrontPanel::ActivateTriggerIn(int epAddr, int bit) + { return((okCFrontPanel::ErrorCode) okFrontPanel_ActivateTriggerIn(h, epAddr, bit)); } +void okCFrontPanel::UpdateTriggerOuts() + { okFrontPanel_UpdateTriggerOuts(h); } +bool okCFrontPanel::IsTriggered(int epAddr, UINT32 mask) + { return(to_bool(okFrontPanel_IsTriggered(h, epAddr, mask))); } +long okCFrontPanel::GetLastTransferLength() + { return(okFrontPanel_GetLastTransferLength(h)); } +long okCFrontPanel::WriteToPipeIn(int epAddr, long length, unsigned char *data) + { return(okFrontPanel_WriteToPipeIn(h, epAddr, length, data)); } +long okCFrontPanel::ReadFromPipeOut(int epAddr, long length, unsigned char *data) + { return(okFrontPanel_ReadFromPipeOut(h, epAddr, length, data)); } +long okCFrontPanel::WriteToBlockPipeIn(int epAddr, int blockSize, long length, unsigned char *data) + { return(okFrontPanel_WriteToBlockPipeIn(h, epAddr, blockSize, length, data)); } +long okCFrontPanel::ReadFromBlockPipeOut(int epAddr, int blockSize, long length, unsigned char *data) + { return(okFrontPanel_ReadFromBlockPipeOut(h, epAddr, blockSize, length, data)); } + +#endif // __cplusplus + + +//------------------------------------------------------------------------ +// Function prototypes +//------------------------------------------------------------------------ +typedef void (DLL_ENTRY *OKFRONTPANELDLL_GETVERSION_FN) (char *, char *); + +typedef okPLL22150_HANDLE (DLL_ENTRY *OKPLL22150_CONSTRUCT_FN) (void); +typedef void (DLL_ENTRY *OKPLL22150_DESTRUCT_FN) (okPLL22150_HANDLE); +typedef void (DLL_ENTRY *OKPLL22150_SETCRYSTALLOAD_FN) (okPLL22150_HANDLE, double); +typedef void (DLL_ENTRY *OKPLL22150_SETREFERENCE_FN) (okPLL22150_HANDLE, double, Bool); +typedef double (DLL_ENTRY *OKPLL22150_GETREFERENCE_FN) (okPLL22150_HANDLE); +typedef Bool (DLL_ENTRY *OKPLL22150_SETVCOPARAMETERS_FN) (okPLL22150_HANDLE, int, int); +typedef int (DLL_ENTRY *OKPLL22150_GETVCOP_FN) (okPLL22150_HANDLE); +typedef int (DLL_ENTRY *OKPLL22150_GETVCOQ_FN) (okPLL22150_HANDLE); +typedef double (DLL_ENTRY *OKPLL22150_GETVCOFREQUENCY_FN) (okPLL22150_HANDLE); +typedef void (DLL_ENTRY *OKPLL22150_SETDIV1_FN) (okPLL22150_HANDLE, ok_DividerSource, int); +typedef void (DLL_ENTRY *OKPLL22150_SETDIV2_FN) (okPLL22150_HANDLE, ok_DividerSource, int); +typedef ok_DividerSource (DLL_ENTRY *OKPLL22150_GETDIV1SOURCE_FN) (okPLL22150_HANDLE); +typedef ok_DividerSource (DLL_ENTRY *OKPLL22150_GETDIV2SOURCE_FN) (okPLL22150_HANDLE); +typedef int (DLL_ENTRY *OKPLL22150_GETDIV1DIVIDER_FN) (okPLL22150_HANDLE); +typedef int (DLL_ENTRY *OKPLL22150_GETDIV2DIVIDER_FN) (okPLL22150_HANDLE); +typedef void (DLL_ENTRY *OKPLL22150_SETOUTPUTSOURCE_FN) (okPLL22150_HANDLE, int, ok_ClockSource_22150); +typedef void (DLL_ENTRY *OKPLL22150_SETOUTPUTENABLE_FN) (okPLL22150_HANDLE, int, Bool); +typedef ok_ClockSource_22150 (DLL_ENTRY *OKPLL22150_GETOUTPUTSOURCE_FN) (okPLL22150_HANDLE, int); +typedef double (DLL_ENTRY *OKPLL22150_GETOUTPUTFREQUENCY_FN) (okPLL22150_HANDLE, int); +typedef Bool (DLL_ENTRY *OKPLL22150_ISOUTPUTENABLED_FN) (okPLL22150_HANDLE, int); +typedef void (DLL_ENTRY *OKPLL22150_INITFROMPROGRAMMINGINFO_FN) (okPLL22150_HANDLE, unsigned char *); +typedef void (DLL_ENTRY *OKPLL22150_GETPROGRAMMINGINFO_FN) (okPLL22150_HANDLE, unsigned char *); + +typedef okPLL22393_HANDLE (DLL_ENTRY *OKPLL22393_CONSTRUCT_FN) (void); +typedef void (DLL_ENTRY *OKPLL22393_DESTRUCT_FN) (okPLL22393_HANDLE); +typedef void (DLL_ENTRY *OKPLL22393_SETCRYSTALLOAD_FN) (okPLL22393_HANDLE, double); +typedef void (DLL_ENTRY *OKPLL22393_SETREFERENCE_FN) (okPLL22393_HANDLE, double); +typedef double (DLL_ENTRY *OKPLL22393_GETREFERENCE_FN) (okPLL22393_HANDLE); +typedef Bool (DLL_ENTRY *OKPLL22393_SETPLLPARAMETERS_FN) (okPLL22393_HANDLE, int, int, int, Bool); +typedef Bool (DLL_ENTRY *OKPLL22393_SETPLLLF_FN) (okPLL22393_HANDLE, int, int); +typedef Bool (DLL_ENTRY *OKPLL22393_SETOUTPUTDIVIDER_FN) (okPLL22393_HANDLE, int, int); +typedef Bool (DLL_ENTRY *OKPLL22393_SETOUTPUTSOURCE_FN) (okPLL22393_HANDLE, int, ok_ClockSource_22393); +typedef void (DLL_ENTRY *OKPLL22393_SETOUTPUTENABLE_FN) (okPLL22393_HANDLE, int, Bool); +typedef int (DLL_ENTRY *OKPLL22393_GETPLLP_FN) (okPLL22393_HANDLE, int); +typedef int (DLL_ENTRY *OKPLL22393_GETPLLQ_FN) (okPLL22393_HANDLE, int); +typedef double (DLL_ENTRY *OKPLL22393_GETPLLFREQUENCY_FN) (okPLL22393_HANDLE, int); +typedef int (DLL_ENTRY *OKPLL22393_GETOUTPUTDIVIDER_FN) (okPLL22393_HANDLE, int); +typedef ok_ClockSource_22393 (DLL_ENTRY *OKPLL22393_GETOUTPUTSOURCE_FN) (okPLL22393_HANDLE, int); +typedef double (DLL_ENTRY *OKPLL22393_GETOUTPUTFREQUENCY_FN) (okPLL22393_HANDLE, int); +typedef Bool (DLL_ENTRY *OKPLL22393_ISOUTPUTENABLED_FN) (okPLL22393_HANDLE, int); +typedef Bool (DLL_ENTRY *OKPLL22393_ISPLLENABLED_FN) (okPLL22393_HANDLE, int); +typedef void (DLL_ENTRY *OKPLL22393_INITFROMPROGRAMMINGINFO_FN) (okPLL22393_HANDLE, unsigned char *); +typedef void (DLL_ENTRY *OKPLL22393_GETPROGRAMMINGINFO_FN) (okPLL22393_HANDLE, unsigned char *); + +typedef okFrontPanel_HANDLE (DLL_ENTRY *okFrontPanel_CONSTRUCT_FN) (void); +typedef void (DLL_ENTRY *okFrontPanel_DESTRUCT_FN) (okFrontPanel_HANDLE); +typedef int (DLL_ENTRY *okFrontPanel_GETHOSTINTERFACEWIDTH_FN) (okFrontPanel_HANDLE); +typedef Bool (DLL_ENTRY *okFrontPanel_ISHIGHSPEED_FN) (okFrontPanel_HANDLE); +typedef ok_BoardModel (DLL_ENTRY *okFrontPanel_GETBOARDMODEL_FN) (okFrontPanel_HANDLE); +typedef void (DLL_ENTRY *okFrontPanel_GETBOARDMODELSTRING_FN) (okFrontPanel_HANDLE, ok_BoardModel, char *); +typedef ok_ErrorCode (DLL_ENTRY *okFrontPanel_WRITEI2C_FN) (okFrontPanel_HANDLE, const int, int, unsigned char *); +typedef ok_ErrorCode (DLL_ENTRY *okFrontPanel_READI2C_FN) (okFrontPanel_HANDLE, const int, int, unsigned char *); +typedef int (DLL_ENTRY *okFrontPanel_GETDEVICECOUNT_FN) (okFrontPanel_HANDLE); +typedef ok_BoardModel (DLL_ENTRY *okFrontPanel_GETDEVICELISTMODEL_FN) (okFrontPanel_HANDLE, int); +typedef void (DLL_ENTRY *okFrontPanel_GETDEVICELISTSERIAL_FN) (okFrontPanel_HANDLE, int, char *); +typedef ok_ErrorCode (DLL_ENTRY *okFrontPanel_OPENBYSERIAL_FN) (okFrontPanel_HANDLE, const char *); +typedef Bool (DLL_ENTRY *okFrontPanel_ISOPEN_FN) (okFrontPanel_HANDLE); +typedef void (DLL_ENTRY *okFrontPanel_ENABLEASYNCHRONOUSTRANSFERS_FN) (okFrontPanel_HANDLE, Bool); +typedef ok_ErrorCode (DLL_ENTRY *okFrontPanel_SETBTPIPEPOLLINGINTERVAL_FN) (okFrontPanel_HANDLE, int); +typedef void (DLL_ENTRY *okFrontPanel_SETTIMEOUT_FN) (okFrontPanel_HANDLE, int); +typedef int (DLL_ENTRY *okFrontPanel_GETDEVICEMAJORVERSION_FN) (okFrontPanel_HANDLE); +typedef int (DLL_ENTRY *okFrontPanel_GETDEVICEMINORVERSION_FN) (okFrontPanel_HANDLE); +typedef ok_ErrorCode (DLL_ENTRY *okFrontPanel_RESETFPGA_FN) (okFrontPanel_HANDLE); +typedef void (DLL_ENTRY *okFrontPanel_GETSERIALNUMBER_FN) (okFrontPanel_HANDLE, char *); +typedef void (DLL_ENTRY *okFrontPanel_GETDEVICEID_FN) (okFrontPanel_HANDLE, char *); +typedef void (DLL_ENTRY *okFrontPanel_SETDEVICEID_FN) (okFrontPanel_HANDLE, const char *); +typedef ok_ErrorCode (DLL_ENTRY *okFrontPanel_CONFIGUREFPGA_FN) (okFrontPanel_HANDLE, const char *); +typedef ok_ErrorCode (DLL_ENTRY *okFrontPanel_CONFIGUREFPGAFROMMEMORY_FN) (okFrontPanel_HANDLE, unsigned char *, unsigned long); +typedef ok_ErrorCode (DLL_ENTRY *okFrontPanel_GETPLL22150CONFIGURATION_FN) (okFrontPanel_HANDLE, okPLL22150_HANDLE); +typedef ok_ErrorCode (DLL_ENTRY *okFrontPanel_SETPLL22150CONFIGURATION_FN) (okFrontPanel_HANDLE, okPLL22150_HANDLE); +typedef ok_ErrorCode (DLL_ENTRY *okFrontPanel_GETEEPROMPLL22150CONFIGURATION_FN) (okFrontPanel_HANDLE, okPLL22150_HANDLE); +typedef ok_ErrorCode (DLL_ENTRY *okFrontPanel_SETEEPROMPLL22150CONFIGURATION_FN) (okFrontPanel_HANDLE, okPLL22150_HANDLE); +typedef ok_ErrorCode (DLL_ENTRY *okFrontPanel_GETPLL22393CONFIGURATION_FN) (okFrontPanel_HANDLE, okPLL22393_HANDLE); +typedef ok_ErrorCode (DLL_ENTRY *okFrontPanel_SETPLL22393CONFIGURATION_FN) (okFrontPanel_HANDLE, okPLL22393_HANDLE); +typedef ok_ErrorCode (DLL_ENTRY *okFrontPanel_GETEEPROMPLL22393CONFIGURATION_FN) (okFrontPanel_HANDLE, okPLL22393_HANDLE); +typedef ok_ErrorCode (DLL_ENTRY *okFrontPanel_SETEEPROMPLL22393CONFIGURATION_FN) (okFrontPanel_HANDLE, okPLL22393_HANDLE); +typedef ok_ErrorCode (DLL_ENTRY *okFrontPanel_LOADDEFAULTPLLCONFIGURATION_FN) (okFrontPanel_HANDLE); +typedef Bool (DLL_ENTRY *okFrontPanel_ISFRONTPANELENABLED_FN) (okFrontPanel_HANDLE); +typedef Bool (DLL_ENTRY *okFrontPanel_ISFRONTPANEL3SUPPORTED_FN) (okFrontPanel_HANDLE); +typedef void (DLL_ENTRY *okFrontPanel_UPDATEWIREINS_FN) (okFrontPanel_HANDLE); +typedef ok_ErrorCode (DLL_ENTRY *okFrontPanel_SETWIREINVALUE_FN) (okFrontPanel_HANDLE, int, UINT32, UINT32); +typedef void (DLL_ENTRY *okFrontPanel_UPDATEWIREOUTS_FN) (okFrontPanel_HANDLE); +typedef UINT32 (DLL_ENTRY *okFrontPanel_GETWIREOUTVALUE_FN) (okFrontPanel_HANDLE, int); +typedef ok_ErrorCode (DLL_ENTRY *okFrontPanel_ACTIVATETRIGGERIN_FN) (okFrontPanel_HANDLE, int, int); +typedef void (DLL_ENTRY *okFrontPanel_UPDATETRIGGEROUTS_FN) (okFrontPanel_HANDLE); +typedef Bool (DLL_ENTRY *okFrontPanel_ISTRIGGERED_FN) (okFrontPanel_HANDLE, int, UINT32); +typedef long (DLL_ENTRY *okFrontPanel_GETLASTTRANSFERLENGTH_FN) (okFrontPanel_HANDLE); +typedef long (DLL_ENTRY *okFrontPanel_WRITETOPIPEIN_FN) (okFrontPanel_HANDLE, int, long, unsigned char *); +typedef long (DLL_ENTRY *okFrontPanel_WRITETOBLOCKPIPEIN_FN) (okFrontPanel_HANDLE, int, long, int, unsigned char *); +typedef long (DLL_ENTRY *okFrontPanel_READFROMPIPEOUT_FN) (okFrontPanel_HANDLE, int, long, unsigned char *); +typedef long (DLL_ENTRY *okFrontPanel_READFROMBLOCKPIPEOUT_FN) (okFrontPanel_HANDLE, int, long, int, unsigned char *); + +//------------------------------------------------------------------------ +// Function pointers +//------------------------------------------------------------------------ +OKFRONTPANELDLL_GETVERSION_FN _okFrontPanelDLL_GetVersion = NULL; + +OKPLL22393_CONSTRUCT_FN _okPLL22393_Construct = NULL; +OKPLL22393_DESTRUCT_FN _okPLL22393_Destruct = NULL; +OKPLL22393_SETCRYSTALLOAD_FN _okPLL22393_SetCrystalLoad = NULL; +OKPLL22393_SETREFERENCE_FN _okPLL22393_SetReference = NULL; +OKPLL22393_GETREFERENCE_FN _okPLL22393_GetReference = NULL; +OKPLL22393_SETPLLPARAMETERS_FN _okPLL22393_SetPLLParameters = NULL; +OKPLL22393_SETPLLLF_FN _okPLL22393_SetPLLLF = NULL; +OKPLL22393_SETOUTPUTDIVIDER_FN _okPLL22393_SetOutputDivider = NULL; +OKPLL22393_SETOUTPUTSOURCE_FN _okPLL22393_SetOutputSource = NULL; +OKPLL22393_SETOUTPUTENABLE_FN _okPLL22393_SetOutputEnable = NULL; +OKPLL22393_GETPLLP_FN _okPLL22393_GetPLLP = NULL; +OKPLL22393_GETPLLQ_FN _okPLL22393_GetPLLQ = NULL; +OKPLL22393_GETPLLFREQUENCY_FN _okPLL22393_GetPLLFrequency = NULL; +OKPLL22393_GETOUTPUTDIVIDER_FN _okPLL22393_GetOutputDivider = NULL; +OKPLL22393_GETOUTPUTSOURCE_FN _okPLL22393_GetOutputSource = NULL; +OKPLL22393_GETOUTPUTFREQUENCY_FN _okPLL22393_GetOutputFrequency = NULL; +OKPLL22393_ISOUTPUTENABLED_FN _okPLL22393_IsOutputEnabled = NULL; +OKPLL22393_ISPLLENABLED_FN _okPLL22393_IsPLLEnabled = NULL; +OKPLL22393_INITFROMPROGRAMMINGINFO_FN _okPLL22393_InitFromProgrammingInfo = NULL; +OKPLL22393_GETPROGRAMMINGINFO_FN _okPLL22393_GetProgrammingInfo = NULL; + +OKPLL22150_CONSTRUCT_FN _okPLL22150_Construct = NULL; +OKPLL22150_DESTRUCT_FN _okPLL22150_Destruct = NULL; +OKPLL22150_SETCRYSTALLOAD_FN _okPLL22150_SetCrystalLoad = NULL; +OKPLL22150_SETREFERENCE_FN _okPLL22150_SetReference = NULL; +OKPLL22150_GETREFERENCE_FN _okPLL22150_GetReference = NULL; +OKPLL22150_SETVCOPARAMETERS_FN _okPLL22150_SetVCOParameters = NULL; +OKPLL22150_GETVCOP_FN _okPLL22150_GetVCOP = NULL; +OKPLL22150_GETVCOQ_FN _okPLL22150_GetVCOQ = NULL; +OKPLL22150_GETVCOFREQUENCY_FN _okPLL22150_GetVCOFrequency = NULL; +OKPLL22150_SETDIV1_FN _okPLL22150_SetDiv1 = NULL; +OKPLL22150_SETDIV2_FN _okPLL22150_SetDiv2 = NULL; +OKPLL22150_GETDIV1SOURCE_FN _okPLL22150_GetDiv1Source = NULL; +OKPLL22150_GETDIV2SOURCE_FN _okPLL22150_GetDiv2Source = NULL; +OKPLL22150_GETDIV1DIVIDER_FN _okPLL22150_GetDiv1Divider = NULL; +OKPLL22150_GETDIV2DIVIDER_FN _okPLL22150_GetDiv2Divider = NULL; +OKPLL22150_SETOUTPUTSOURCE_FN _okPLL22150_SetOutputSource = NULL; +OKPLL22150_SETOUTPUTENABLE_FN _okPLL22150_SetOutputEnable = NULL; +OKPLL22150_GETOUTPUTSOURCE_FN _okPLL22150_GetOutputSource = NULL; +OKPLL22150_GETOUTPUTFREQUENCY_FN _okPLL22150_GetOutputFrequency = NULL; +OKPLL22150_ISOUTPUTENABLED_FN _okPLL22150_IsOutputEnabled = NULL; +OKPLL22150_INITFROMPROGRAMMINGINFO_FN _okPLL22150_InitFromProgrammingInfo = NULL; +OKPLL22150_GETPROGRAMMINGINFO_FN _okPLL22150_GetProgrammingInfo = NULL; + +okFrontPanel_CONSTRUCT_FN _okFrontPanel_Construct = NULL; +okFrontPanel_DESTRUCT_FN _okFrontPanel_Destruct = NULL; +okFrontPanel_GETHOSTINTERFACEWIDTH_FN _okFrontPanel_GetHostInterfaceWidth = NULL; +okFrontPanel_ISHIGHSPEED_FN _okFrontPanel_IsHighSpeed = NULL; +okFrontPanel_GETBOARDMODEL_FN _okFrontPanel_GetBoardModel = NULL; +okFrontPanel_GETBOARDMODELSTRING_FN _okFrontPanel_GetBoardModelString = NULL; +okFrontPanel_WRITEI2C_FN _okFrontPanel_WriteI2C = NULL; +okFrontPanel_READI2C_FN _okFrontPanel_ReadI2C = NULL; +okFrontPanel_GETDEVICECOUNT_FN _okFrontPanel_GetDeviceCount = NULL; +okFrontPanel_GETDEVICELISTMODEL_FN _okFrontPanel_GetDeviceListModel = NULL; +okFrontPanel_GETDEVICELISTSERIAL_FN _okFrontPanel_GetDeviceListSerial = NULL; +okFrontPanel_OPENBYSERIAL_FN _okFrontPanel_OpenBySerial = NULL; +okFrontPanel_ISOPEN_FN _okFrontPanel_IsOpen = NULL; +okFrontPanel_ENABLEASYNCHRONOUSTRANSFERS_FN _okFrontPanel_EnableAsynchronousTransfers = NULL; +okFrontPanel_SETBTPIPEPOLLINGINTERVAL_FN _okFrontPanel_SetBTPipePollingInterval = NULL; +okFrontPanel_SETTIMEOUT_FN _okFrontPanel_SetTimeout = NULL; +okFrontPanel_GETDEVICEMAJORVERSION_FN _okFrontPanel_GetDeviceMajorVersion = NULL; +okFrontPanel_GETDEVICEMINORVERSION_FN _okFrontPanel_GetDeviceMinorVersion = NULL; +okFrontPanel_RESETFPGA_FN _okFrontPanel_ResetFPGA = NULL; +okFrontPanel_GETSERIALNUMBER_FN _okFrontPanel_GetSerialNumber = NULL; +okFrontPanel_GETDEVICEID_FN _okFrontPanel_GetDeviceID = NULL; +okFrontPanel_SETDEVICEID_FN _okFrontPanel_SetDeviceID = NULL; +okFrontPanel_CONFIGUREFPGA_FN _okFrontPanel_ConfigureFPGA = NULL; +okFrontPanel_CONFIGUREFPGAFROMMEMORY_FN _okFrontPanel_ConfigureFPGAFromMemory = NULL; +okFrontPanel_GETPLL22150CONFIGURATION_FN _okFrontPanel_GetPLL22150Configuration = NULL; +okFrontPanel_SETPLL22150CONFIGURATION_FN _okFrontPanel_SetPLL22150Configuration = NULL; +okFrontPanel_GETEEPROMPLL22150CONFIGURATION_FN _okFrontPanel_GetEepromPLL22150Configuration = NULL; +okFrontPanel_SETEEPROMPLL22150CONFIGURATION_FN _okFrontPanel_SetEepromPLL22150Configuration = NULL; +okFrontPanel_GETPLL22393CONFIGURATION_FN _okFrontPanel_GetPLL22393Configuration = NULL; +okFrontPanel_SETPLL22393CONFIGURATION_FN _okFrontPanel_SetPLL22393Configuration = NULL; +okFrontPanel_GETEEPROMPLL22393CONFIGURATION_FN _okFrontPanel_GetEepromPLL22393Configuration = NULL; +okFrontPanel_SETEEPROMPLL22393CONFIGURATION_FN _okFrontPanel_SetEepromPLL22393Configuration = NULL; +okFrontPanel_LOADDEFAULTPLLCONFIGURATION_FN _okFrontPanel_LoadDefaultPLLConfiguration = NULL; +okFrontPanel_ISFRONTPANELENABLED_FN _okFrontPanel_IsFrontPanelEnabled = NULL; +okFrontPanel_ISFRONTPANEL3SUPPORTED_FN _okFrontPanel_IsFrontPanel3Supported = NULL; +okFrontPanel_UPDATEWIREINS_FN _okFrontPanel_UpdateWireIns = NULL; +okFrontPanel_SETWIREINVALUE_FN _okFrontPanel_SetWireInValue = NULL; +okFrontPanel_UPDATEWIREOUTS_FN _okFrontPanel_UpdateWireOuts = NULL; +okFrontPanel_GETWIREOUTVALUE_FN _okFrontPanel_GetWireOutValue = NULL; +okFrontPanel_ACTIVATETRIGGERIN_FN _okFrontPanel_ActivateTriggerIn = NULL; +okFrontPanel_UPDATETRIGGEROUTS_FN _okFrontPanel_UpdateTriggerOuts = NULL; +okFrontPanel_ISTRIGGERED_FN _okFrontPanel_IsTriggered = NULL; +okFrontPanel_GETLASTTRANSFERLENGTH_FN _okFrontPanel_GetLastTransferLength = NULL; +okFrontPanel_WRITETOPIPEIN_FN _okFrontPanel_WriteToPipeIn = NULL; +okFrontPanel_WRITETOBLOCKPIPEIN_FN _okFrontPanel_WriteToBlockPipeIn = NULL; +okFrontPanel_READFROMPIPEOUT_FN _okFrontPanel_ReadFromPipeOut = NULL; +okFrontPanel_READFROMBLOCKPIPEOUT_FN _okFrontPanel_ReadFromBlockPipeOut = NULL; + + +//------------------------------------------------------------------------ + +/// Returns the version number of the DLL. +const char * +okFrontPanelDLL_GetVersionString() +{ + return(VERSION_STRING); +} + + +/// Loads the FrontPanel API DLL. This function returns False if the +/// DLL did not load for some reason, True otherwise. +Bool +okFrontPanelDLL_LoadLib(const char *libname) +{ + // Return TRUE if the DLL is already loaded. + if (hLib) + return(TRUE); + + if (NULL == libname) + hLib = dll_load(okLIB_NAME); + else + hLib = dll_load(libname); + + if (hLib) { + _okFrontPanelDLL_GetVersion = ( OKFRONTPANELDLL_GETVERSION_FN ) dll_entrypoint( hLib, "okFrontPanelDLL_GetVersion" ); + + _okPLL22150_Construct = ( OKPLL22150_CONSTRUCT_FN ) dll_entrypoint( hLib, "okPLL22150_Construct" ); + _okPLL22150_Destruct = ( OKPLL22150_DESTRUCT_FN ) dll_entrypoint( hLib, "okPLL22150_Destruct" ); + _okPLL22150_SetCrystalLoad = ( OKPLL22150_SETCRYSTALLOAD_FN ) dll_entrypoint( hLib, "okPLL22150_SetCrystalLoad" ); + _okPLL22150_SetReference = ( OKPLL22150_SETREFERENCE_FN ) dll_entrypoint( hLib, "okPLL22150_SetReference" ); + _okPLL22150_GetReference = ( OKPLL22150_GETREFERENCE_FN ) dll_entrypoint( hLib, "okPLL22150_GetReference" ); + _okPLL22150_SetVCOParameters = ( OKPLL22150_SETVCOPARAMETERS_FN ) dll_entrypoint( hLib, "okPLL22150_SetVCOParameters" ); + _okPLL22150_GetVCOP = ( OKPLL22150_GETVCOP_FN ) dll_entrypoint( hLib, "okPLL22150_GetVCOP" ); + _okPLL22150_GetVCOQ = ( OKPLL22150_GETVCOQ_FN ) dll_entrypoint( hLib, "okPLL22150_GetVCOQ" ); + _okPLL22150_GetVCOFrequency = ( OKPLL22150_GETVCOFREQUENCY_FN ) dll_entrypoint( hLib, "okPLL22150_GetVCOFrequency" ); + _okPLL22150_SetDiv1 = ( OKPLL22150_SETDIV1_FN ) dll_entrypoint( hLib, "okPLL22150_SetDiv1" ); + _okPLL22150_SetDiv2 = ( OKPLL22150_SETDIV2_FN ) dll_entrypoint( hLib, "okPLL22150_SetDiv2" ); + _okPLL22150_GetDiv1Source = ( OKPLL22150_GETDIV1SOURCE_FN ) dll_entrypoint( hLib, "okPLL22150_GetDiv1Source" ); + _okPLL22150_GetDiv2Source = ( OKPLL22150_GETDIV2SOURCE_FN ) dll_entrypoint( hLib, "okPLL22150_GetDiv2Source" ); + _okPLL22150_GetDiv1Divider = ( OKPLL22150_GETDIV1DIVIDER_FN ) dll_entrypoint( hLib, "okPLL22150_GetDiv1Divider" ); + _okPLL22150_GetDiv2Divider = ( OKPLL22150_GETDIV2DIVIDER_FN ) dll_entrypoint( hLib, "okPLL22150_GetDiv2Divider" ); + _okPLL22150_SetOutputSource = ( OKPLL22150_SETOUTPUTSOURCE_FN ) dll_entrypoint( hLib, "okPLL22150_SetOutputSource" ); + _okPLL22150_SetOutputEnable = ( OKPLL22150_SETOUTPUTENABLE_FN ) dll_entrypoint( hLib, "okPLL22150_SetOutputEnable" ); + _okPLL22150_GetOutputSource = ( OKPLL22150_GETOUTPUTSOURCE_FN ) dll_entrypoint( hLib, "okPLL22150_GetOutputSource" ); + _okPLL22150_GetOutputFrequency = ( OKPLL22150_GETOUTPUTFREQUENCY_FN ) dll_entrypoint( hLib, "okPLL22150_GetOutputFrequency" ); + _okPLL22150_IsOutputEnabled = ( OKPLL22150_ISOUTPUTENABLED_FN ) dll_entrypoint( hLib, "okPLL22150_IsOutputEnabled" ); + _okPLL22150_InitFromProgrammingInfo = ( OKPLL22150_INITFROMPROGRAMMINGINFO_FN ) dll_entrypoint( hLib, "okPLL22150_InitFromProgrammingInfo" ); + _okPLL22150_GetProgrammingInfo = ( OKPLL22150_GETPROGRAMMINGINFO_FN ) dll_entrypoint( hLib, "okPLL22150_GetProgrammingInfo" ); + + _okPLL22393_Construct = ( OKPLL22393_CONSTRUCT_FN ) dll_entrypoint( hLib, "okPLL22393_Construct" ); + _okPLL22393_Destruct = ( OKPLL22393_DESTRUCT_FN ) dll_entrypoint( hLib, "okPLL22393_Destruct" ); + _okPLL22393_SetCrystalLoad = ( OKPLL22393_SETCRYSTALLOAD_FN ) dll_entrypoint( hLib, "okPLL22393_SetCrystalLoad" ); + _okPLL22393_SetReference = ( OKPLL22393_SETREFERENCE_FN ) dll_entrypoint( hLib, "okPLL22393_SetReference" ); + _okPLL22393_GetReference = ( OKPLL22393_GETREFERENCE_FN ) dll_entrypoint( hLib, "okPLL22393_GetReference" ); + _okPLL22393_SetPLLParameters = ( OKPLL22393_SETPLLPARAMETERS_FN ) dll_entrypoint( hLib, "okPLL22393_SetPLLParameters" ); + _okPLL22393_SetPLLLF = ( OKPLL22393_SETPLLLF_FN ) dll_entrypoint( hLib, "okPLL22393_SetPLLLF" ); + _okPLL22393_SetOutputDivider = ( OKPLL22393_SETOUTPUTDIVIDER_FN ) dll_entrypoint( hLib, "okPLL22393_SetOutputDivider" ); + _okPLL22393_SetOutputSource = ( OKPLL22393_SETOUTPUTSOURCE_FN ) dll_entrypoint( hLib, "okPLL22393_SetOutputSource" ); + _okPLL22393_SetOutputEnable = ( OKPLL22393_SETOUTPUTENABLE_FN ) dll_entrypoint( hLib, "okPLL22393_SetOutputEnable" ); + _okPLL22393_GetPLLP = ( OKPLL22393_GETPLLP_FN ) dll_entrypoint( hLib, "okPLL22393_GetPLLP" ); + _okPLL22393_GetPLLQ = ( OKPLL22393_GETPLLQ_FN ) dll_entrypoint( hLib, "okPLL22393_GetPLLQ" ); + _okPLL22393_GetPLLFrequency = ( OKPLL22393_GETPLLFREQUENCY_FN ) dll_entrypoint( hLib, "okPLL22393_GetPLLFrequency" ); + _okPLL22393_GetOutputDivider = ( OKPLL22393_GETOUTPUTDIVIDER_FN ) dll_entrypoint( hLib, "okPLL22393_GetOutputDivider" ); + _okPLL22393_GetOutputSource = ( OKPLL22393_GETOUTPUTSOURCE_FN ) dll_entrypoint( hLib, "okPLL22393_GetOutputSource" ); + _okPLL22393_GetOutputFrequency = ( OKPLL22393_GETOUTPUTFREQUENCY_FN ) dll_entrypoint( hLib, "okPLL22393_GetOutputFrequency" ); + _okPLL22393_IsOutputEnabled = ( OKPLL22393_ISOUTPUTENABLED_FN ) dll_entrypoint( hLib, "okPLL22393_IsOutputEnabled" ); + _okPLL22393_IsPLLEnabled = ( OKPLL22393_ISPLLENABLED_FN ) dll_entrypoint( hLib, "okPLL22393_IsPLLEnabled" ); + _okPLL22393_InitFromProgrammingInfo = ( OKPLL22393_INITFROMPROGRAMMINGINFO_FN ) dll_entrypoint( hLib, "okPLL22393_InitFromProgrammingInfo" ); + _okPLL22393_GetProgrammingInfo = ( OKPLL22393_GETPROGRAMMINGINFO_FN ) dll_entrypoint( hLib, "okPLL22393_GetProgrammingInfo" ); + + _okFrontPanel_Construct = ( okFrontPanel_CONSTRUCT_FN ) dll_entrypoint( hLib, "okFrontPanel_Construct" ); + _okFrontPanel_Destruct = ( okFrontPanel_DESTRUCT_FN ) dll_entrypoint( hLib, "okFrontPanel_Destruct" ); + _okFrontPanel_GetHostInterfaceWidth = ( okFrontPanel_GETHOSTINTERFACEWIDTH_FN ) dll_entrypoint( hLib, "okFrontPanel_GetHostInterfaceWidth" ); + _okFrontPanel_IsHighSpeed = ( okFrontPanel_ISHIGHSPEED_FN ) dll_entrypoint( hLib, "okFrontPanel_IsHighSpeed" ); + _okFrontPanel_GetBoardModel = ( okFrontPanel_GETBOARDMODEL_FN ) dll_entrypoint( hLib, "okFrontPanel_GetBoardModel" ); + _okFrontPanel_GetBoardModelString = ( okFrontPanel_GETBOARDMODELSTRING_FN ) dll_entrypoint( hLib, "okFrontPanel_GetBoardModelString" ); + _okFrontPanel_WriteI2C = ( okFrontPanel_WRITEI2C_FN ) dll_entrypoint( hLib, "okFrontPanel_WriteI2C" ); + _okFrontPanel_ReadI2C = ( okFrontPanel_READI2C_FN ) dll_entrypoint( hLib, "okFrontPanel_ReadI2C" ); + _okFrontPanel_GetDeviceCount = ( okFrontPanel_GETDEVICECOUNT_FN ) dll_entrypoint( hLib, "okFrontPanel_GetDeviceCount" ); + _okFrontPanel_GetDeviceListModel = ( okFrontPanel_GETDEVICELISTMODEL_FN ) dll_entrypoint( hLib, "okFrontPanel_GetDeviceListModel" ); + _okFrontPanel_GetDeviceListSerial = ( okFrontPanel_GETDEVICELISTSERIAL_FN ) dll_entrypoint( hLib, "okFrontPanel_GetDeviceListSerial" ); + _okFrontPanel_OpenBySerial = ( okFrontPanel_OPENBYSERIAL_FN ) dll_entrypoint( hLib, "okFrontPanel_OpenBySerial" ); + _okFrontPanel_IsOpen = ( okFrontPanel_ISOPEN_FN ) dll_entrypoint( hLib, "okFrontPanel_IsOpen" ); + _okFrontPanel_SetBTPipePollingInterval = ( okFrontPanel_SETBTPIPEPOLLINGINTERVAL_FN ) dll_entrypoint( hLib, "okFrontPanel_SetBTPipePollingInterval" ); + _okFrontPanel_SetTimeout = ( okFrontPanel_SETTIMEOUT_FN ) dll_entrypoint( hLib, "okFrontPanel_SetTimeout" ); + _okFrontPanel_EnableAsynchronousTransfers = ( okFrontPanel_ENABLEASYNCHRONOUSTRANSFERS_FN ) dll_entrypoint( hLib, "okFrontPanel_EnableAsynchronousTransfers" ); + _okFrontPanel_GetDeviceMajorVersion = ( okFrontPanel_GETDEVICEMAJORVERSION_FN ) dll_entrypoint( hLib, "okFrontPanel_GetDeviceMajorVersion" ); + _okFrontPanel_GetDeviceMinorVersion = ( okFrontPanel_GETDEVICEMINORVERSION_FN ) dll_entrypoint( hLib, "okFrontPanel_GetDeviceMinorVersion" ); + _okFrontPanel_ResetFPGA = ( okFrontPanel_RESETFPGA_FN ) dll_entrypoint( hLib, "okFrontPanel_ResetFPGA" ); + _okFrontPanel_GetSerialNumber = ( okFrontPanel_GETSERIALNUMBER_FN ) dll_entrypoint( hLib, "okFrontPanel_GetSerialNumber" ); + _okFrontPanel_GetDeviceID = ( okFrontPanel_GETDEVICEID_FN ) dll_entrypoint( hLib, "okFrontPanel_GetDeviceID" ); + _okFrontPanel_SetDeviceID = ( okFrontPanel_SETDEVICEID_FN ) dll_entrypoint( hLib, "okFrontPanel_SetDeviceID" ); + _okFrontPanel_ConfigureFPGA = ( okFrontPanel_CONFIGUREFPGA_FN ) dll_entrypoint( hLib, "okFrontPanel_ConfigureFPGA" ); + _okFrontPanel_ConfigureFPGAFromMemory = ( okFrontPanel_CONFIGUREFPGAFROMMEMORY_FN ) dll_entrypoint( hLib, "okFrontPanel_ConfigureFPGAFromMemory" ); + _okFrontPanel_GetPLL22150Configuration = ( okFrontPanel_GETPLL22150CONFIGURATION_FN ) dll_entrypoint( hLib, "okFrontPanel_GetPLL22150Configuration" ); + _okFrontPanel_SetPLL22150Configuration = ( okFrontPanel_SETPLL22150CONFIGURATION_FN ) dll_entrypoint( hLib, "okFrontPanel_SetPLL22150Configuration" ); + _okFrontPanel_GetEepromPLL22150Configuration = ( okFrontPanel_GETEEPROMPLL22150CONFIGURATION_FN ) dll_entrypoint( hLib, "okFrontPanel_GetEepromPLL22150Configuration" ); + _okFrontPanel_SetEepromPLL22150Configuration = ( okFrontPanel_SETEEPROMPLL22150CONFIGURATION_FN ) dll_entrypoint( hLib, "okFrontPanel_SetEepromPLL22150Configuration" ); + _okFrontPanel_GetPLL22393Configuration = ( okFrontPanel_GETPLL22393CONFIGURATION_FN ) dll_entrypoint( hLib, "okFrontPanel_GetPLL22393Configuration" ); + _okFrontPanel_SetPLL22393Configuration = ( okFrontPanel_SETPLL22393CONFIGURATION_FN ) dll_entrypoint( hLib, "okFrontPanel_SetPLL22393Configuration" ); + _okFrontPanel_GetEepromPLL22393Configuration = ( okFrontPanel_GETEEPROMPLL22393CONFIGURATION_FN ) dll_entrypoint( hLib, "okFrontPanel_GetEepromPLL22393Configuration" ); + _okFrontPanel_SetEepromPLL22393Configuration = ( okFrontPanel_SETEEPROMPLL22393CONFIGURATION_FN ) dll_entrypoint( hLib, "okFrontPanel_SetEepromPLL22393Configuration" ); + _okFrontPanel_LoadDefaultPLLConfiguration = ( okFrontPanel_LOADDEFAULTPLLCONFIGURATION_FN ) dll_entrypoint( hLib, "okFrontPanel_LoadDefaultPLLConfiguration" ); + _okFrontPanel_IsFrontPanelEnabled = ( okFrontPanel_ISFRONTPANELENABLED_FN ) dll_entrypoint( hLib, "okFrontPanel_IsFrontPanelEnabled" ); + _okFrontPanel_IsFrontPanel3Supported = ( okFrontPanel_ISFRONTPANEL3SUPPORTED_FN ) dll_entrypoint( hLib, "okFrontPanel_IsFrontPanel3Supported" ); + _okFrontPanel_UpdateWireIns = ( okFrontPanel_UPDATEWIREINS_FN ) dll_entrypoint( hLib, "okFrontPanel_UpdateWireIns" ); + _okFrontPanel_SetWireInValue = ( okFrontPanel_SETWIREINVALUE_FN ) dll_entrypoint( hLib, "okFrontPanel_SetWireInValue" ); + _okFrontPanel_UpdateWireOuts = ( okFrontPanel_UPDATEWIREOUTS_FN ) dll_entrypoint( hLib, "okFrontPanel_UpdateWireOuts" ); + _okFrontPanel_GetWireOutValue = ( okFrontPanel_GETWIREOUTVALUE_FN ) dll_entrypoint( hLib, "okFrontPanel_GetWireOutValue" ); + _okFrontPanel_ActivateTriggerIn = ( okFrontPanel_ACTIVATETRIGGERIN_FN ) dll_entrypoint( hLib, "okFrontPanel_ActivateTriggerIn" ); + _okFrontPanel_UpdateTriggerOuts = ( okFrontPanel_UPDATETRIGGEROUTS_FN ) dll_entrypoint( hLib, "okFrontPanel_UpdateTriggerOuts" ); + _okFrontPanel_IsTriggered = ( okFrontPanel_ISTRIGGERED_FN ) dll_entrypoint( hLib, "okFrontPanel_IsTriggered" ); + _okFrontPanel_GetLastTransferLength = ( okFrontPanel_GETLASTTRANSFERLENGTH_FN ) dll_entrypoint( hLib, "okFrontPanel_GetLastTransferLength" ); + _okFrontPanel_WriteToPipeIn = ( okFrontPanel_WRITETOPIPEIN_FN ) dll_entrypoint( hLib, "okFrontPanel_WriteToPipeIn" ); + _okFrontPanel_WriteToBlockPipeIn = ( okFrontPanel_WRITETOBLOCKPIPEIN_FN ) dll_entrypoint( hLib, "okFrontPanel_WriteToBlockPipeIn" ); + _okFrontPanel_ReadFromPipeOut = ( okFrontPanel_READFROMPIPEOUT_FN ) dll_entrypoint( hLib, "okFrontPanel_ReadFromPipeOut" ); + _okFrontPanel_ReadFromBlockPipeOut = ( okFrontPanel_READFROMBLOCKPIPEOUT_FN ) dll_entrypoint( hLib, "okFrontPanel_ReadFromBlockPipeOut" ); + } + + if (NULL == hLib) { + return(FALSE); + } + + return(TRUE); +} + + +void +okFrontPanelDLL_FreeLib(void) +{ + _okFrontPanelDLL_GetVersion = NULL; + + _okFrontPanel_Construct = NULL; + _okFrontPanel_Destruct = NULL; + _okFrontPanel_GetHostInterfaceWidth = NULL; + _okFrontPanel_IsHighSpeed = NULL; + _okFrontPanel_GetBoardModel = NULL; + _okFrontPanel_GetBoardModelString = NULL; + _okFrontPanel_WriteI2C = NULL; + _okFrontPanel_ReadI2C = NULL; + _okFrontPanel_GetDeviceCount = NULL; + _okFrontPanel_GetDeviceListModel = NULL; + _okFrontPanel_GetDeviceListSerial = NULL; + _okFrontPanel_OpenBySerial = NULL; + _okFrontPanel_IsOpen = NULL; + _okFrontPanel_SetBTPipePollingInterval = NULL; + _okFrontPanel_SetTimeout = NULL; + _okFrontPanel_EnableAsynchronousTransfers = NULL; + _okFrontPanel_GetDeviceMajorVersion = NULL; + _okFrontPanel_GetDeviceMinorVersion = NULL; + _okFrontPanel_ResetFPGA = NULL; + _okFrontPanel_GetSerialNumber = NULL; + _okFrontPanel_GetDeviceID = NULL; + _okFrontPanel_SetDeviceID = NULL; + _okFrontPanel_ConfigureFPGA = NULL; + _okFrontPanel_ConfigureFPGAFromMemory = NULL; + _okFrontPanel_GetPLL22150Configuration = NULL; + _okFrontPanel_SetPLL22150Configuration = NULL; + _okFrontPanel_GetEepromPLL22150Configuration = NULL; + _okFrontPanel_SetEepromPLL22150Configuration = NULL; + _okFrontPanel_GetPLL22393Configuration = NULL; + _okFrontPanel_SetPLL22393Configuration = NULL; + _okFrontPanel_GetEepromPLL22393Configuration = NULL; + _okFrontPanel_SetEepromPLL22393Configuration = NULL; + _okFrontPanel_IsFrontPanelEnabled = NULL; + _okFrontPanel_IsFrontPanel3Supported = NULL; + _okFrontPanel_UpdateWireIns = NULL; + _okFrontPanel_SetWireInValue = NULL; + _okFrontPanel_UpdateWireOuts = NULL; + _okFrontPanel_GetWireOutValue = NULL; + _okFrontPanel_ActivateTriggerIn = NULL; + _okFrontPanel_UpdateTriggerOuts = NULL; + _okFrontPanel_IsTriggered = NULL; + _okFrontPanel_GetLastTransferLength = NULL; + _okFrontPanel_WriteToPipeIn = NULL; + _okFrontPanel_WriteToBlockPipeIn = NULL; + _okFrontPanel_ReadFromPipeOut = NULL; + _okFrontPanel_ReadFromBlockPipeOut = NULL; + + _okPLL22393_Construct = NULL; + _okPLL22393_Destruct = NULL; + _okPLL22393_SetCrystalLoad = NULL; + _okPLL22393_SetReference = NULL; + _okPLL22393_GetReference = NULL; + _okPLL22393_SetPLLParameters = NULL; + _okPLL22393_SetPLLLF = NULL; + _okPLL22393_SetOutputDivider = NULL; + _okPLL22393_SetOutputSource = NULL; + _okPLL22393_SetOutputEnable = NULL; + _okPLL22393_GetPLLP = NULL; + _okPLL22393_GetPLLQ = NULL; + _okPLL22393_GetPLLFrequency = NULL; + _okPLL22393_GetOutputDivider = NULL; + _okPLL22393_GetOutputSource = NULL; + _okPLL22393_GetOutputFrequency = NULL; + _okPLL22393_IsOutputEnabled = NULL; + _okPLL22393_IsPLLEnabled = NULL; + _okPLL22393_InitFromProgrammingInfo = NULL; + _okPLL22393_GetProgrammingInfo = NULL; + + _okPLL22150_Construct = NULL; + _okPLL22150_Destruct = NULL; + _okPLL22150_SetCrystalLoad = NULL; + _okPLL22150_SetReference = NULL; + _okPLL22150_GetReference = NULL; + _okPLL22150_SetVCOParameters = NULL; + _okPLL22150_GetVCOP = NULL; + _okPLL22150_GetVCOQ = NULL; + _okPLL22150_GetVCOFrequency = NULL; + _okPLL22150_SetDiv1 = NULL; + _okPLL22150_SetDiv2 = NULL; + _okPLL22150_GetDiv1Source = NULL; + _okPLL22150_GetDiv2Source = NULL; + _okPLL22150_GetDiv1Divider = NULL; + _okPLL22150_GetDiv2Divider = NULL; + _okPLL22150_SetOutputSource = NULL; + _okPLL22150_SetOutputEnable = NULL; + _okPLL22150_GetOutputSource = NULL; + _okPLL22150_GetOutputFrequency = NULL; + _okPLL22150_IsOutputEnabled = NULL; + _okPLL22150_InitFromProgrammingInfo = NULL; + _okPLL22150_GetProgrammingInfo = NULL; + + if (hLib) { + dll_unload(hLib); + hLib = NULL; + } +} + + +static DLL_EP +dll_entrypoint(DLL *dll, const char *name) +{ +#if defined(_WIN32) + FARPROC proc; + proc = GetProcAddress((HMODULE) dll, (LPCSTR) name); + if (NULL == proc) { + printf( "Failed to load %s. Error code %d\n", name, GetLastError() ); + } + return((DLL_EP)proc); +#else + void *handle = (void *)dll; + DLL_EP ep; + ep = (DLL_EP)dlsym(handle, name); + return( (dlerror()==0) ? (ep) : ((DLL_EP)NULL) ); +#endif +} + + +static DLL * +dll_load(const char *libname) +{ +#if defined(_WIN32) + return((DLL *) LoadLibrary(libname)); +#else + DLL *dll; + dll = dlopen(libname, RTLD_NOW); + if (!dll) + printf("%s\n", (char *)dlerror()); + return(dll); +#endif +} + + +static void +dll_unload(DLL *dll) +{ +#if defined(_WIN32) + HINSTANCE hInst = (HINSTANCE) dll; + FreeLibrary(hInst); +#else + void *handle = (void *)dll; + dlclose(handle); +#endif +} + + +//------------------------------------------------------------------------ +// Function calls - General +//------------------------------------------------------------------------ +okDLLEXPORT void DLL_ENTRY +okFrontPanelDLL_GetVersion(char *date, char *time) { + + if (_okFrontPanelDLL_GetVersion) + (*_okFrontPanelDLL_GetVersion)(date, time); +} + +//------------------------------------------------------------------------ +// Function calls - okCPLL22393 +//------------------------------------------------------------------------ +okDLLEXPORT okPLL22393_HANDLE DLL_ENTRY +okPLL22393_Construct() { + if (_okPLL22393_Construct) + return((*_okPLL22393_Construct)()); + + return(NULL); +} + +okDLLEXPORT void DLL_ENTRY +okPLL22393_Destruct(okPLL22393_HANDLE pll) { + if (_okPLL22393_Destruct) + (*_okPLL22393_Destruct)(pll); +} + +okDLLEXPORT void DLL_ENTRY +okPLL22393_SetCrystalLoad(okPLL22393_HANDLE pll, double capload) { + if (_okPLL22393_SetCrystalLoad) + (*_okPLL22393_SetCrystalLoad)(pll, capload); +} + +okDLLEXPORT void DLL_ENTRY +okPLL22393_SetReference(okPLL22393_HANDLE pll, double freq) { + if (_okPLL22393_SetReference) + (*_okPLL22393_SetReference)(pll, freq); +} + +okDLLEXPORT double DLL_ENTRY +okPLL22393_GetReference(okPLL22393_HANDLE pll) { + if (_okPLL22393_GetReference) + return((*_okPLL22393_GetReference)(pll)); + return(0.0); +} + +okDLLEXPORT Bool DLL_ENTRY +okPLL22393_SetPLLParameters(okPLL22393_HANDLE pll, int n, int p, int q, Bool enable) { + if (_okPLL22393_SetPLLParameters) + return((*_okPLL22393_SetPLLParameters)(pll, n, p, q, enable)); + return(FALSE); +} + +okDLLEXPORT Bool DLL_ENTRY +okPLL22393_SetPLLLF(okPLL22393_HANDLE pll, int n, int lf) { + if (_okPLL22393_SetPLLLF) + return((*_okPLL22393_SetPLLLF)(pll, n, lf)); + return(FALSE); +} + +okDLLEXPORT Bool DLL_ENTRY +okPLL22393_SetOutputDivider(okPLL22393_HANDLE pll, int n, int div) { + if (_okPLL22393_SetOutputDivider) + return((*_okPLL22393_SetOutputDivider)(pll, n, div)); + return(FALSE); +} + +okDLLEXPORT Bool DLL_ENTRY +okPLL22393_SetOutputSource(okPLL22393_HANDLE pll, int n, ok_ClockSource_22393 clksrc) { + if (_okPLL22393_SetOutputSource) + return((*_okPLL22393_SetOutputSource)(pll, n, clksrc)); + return(FALSE); +} + +okDLLEXPORT void DLL_ENTRY +okPLL22393_SetOutputEnable(okPLL22393_HANDLE pll, int n, Bool enable) { + if (_okPLL22393_SetOutputEnable) + (*_okPLL22393_SetOutputEnable)(pll, n, enable); +} + +okDLLEXPORT int DLL_ENTRY +okPLL22393_GetPLLP(okPLL22393_HANDLE pll, int n) { + if (_okPLL22393_GetPLLP) + return((*_okPLL22393_GetPLLP)(pll, n)); + return(0); +} + +okDLLEXPORT int DLL_ENTRY +okPLL22393_GetPLLQ(okPLL22393_HANDLE pll, int n) { + if (_okPLL22393_GetPLLQ) + return((*_okPLL22393_GetPLLQ)(pll, n)); + return(0); +} + +okDLLEXPORT double DLL_ENTRY +okPLL22393_GetPLLFrequency(okPLL22393_HANDLE pll, int n) { + if (_okPLL22393_GetPLLFrequency) + return((*_okPLL22393_GetPLLFrequency)(pll, n)); + return(0.0); +} + +okDLLEXPORT int DLL_ENTRY +okPLL22393_GetOutputDivider(okPLL22393_HANDLE pll, int n) { + if (_okPLL22393_GetOutputDivider) + return((*_okPLL22393_GetOutputDivider)(pll, n)); + return(0); +} + +okDLLEXPORT ok_ClockSource_22393 DLL_ENTRY +okPLL22393_GetOutputSource(okPLL22393_HANDLE pll, int n) { + if (_okPLL22393_GetOutputSource) + return((*_okPLL22393_GetOutputSource)(pll, n)); + return(ok_ClkSrc22393_Ref); +} + +okDLLEXPORT double DLL_ENTRY +okPLL22393_GetOutputFrequency(okPLL22393_HANDLE pll, int n) { + if (_okPLL22393_GetOutputFrequency) + return((*_okPLL22393_GetOutputFrequency)(pll, n)); + return(0.0); +} + +okDLLEXPORT Bool DLL_ENTRY +okPLL22393_IsOutputEnabled(okPLL22393_HANDLE pll, int n) { + if (_okPLL22393_IsOutputEnabled) + return((*_okPLL22393_IsOutputEnabled)(pll, n)); + return(FALSE); +} + +okDLLEXPORT Bool DLL_ENTRY +okPLL22393_IsPLLEnabled(okPLL22393_HANDLE pll, int n) { + if (_okPLL22393_IsPLLEnabled) + return((*_okPLL22393_IsPLLEnabled)(pll, n)); + return(FALSE); +} + +okDLLEXPORT void DLL_ENTRY +okPLL22393_InitFromProgrammingInfo(okPLL22393_HANDLE pll, unsigned char *buf) { + if (_okPLL22393_InitFromProgrammingInfo) + (*_okPLL22393_InitFromProgrammingInfo)(pll, buf); +} + +okDLLEXPORT void DLL_ENTRY +okPLL22393_GetProgrammingInfo(okPLL22393_HANDLE pll, unsigned char *buf) { + if (_okPLL22393_GetProgrammingInfo) + (*_okPLL22393_GetProgrammingInfo)(pll, buf); +} + + +//------------------------------------------------------------------------ +// Function calls - okCPLL22150 +//------------------------------------------------------------------------ +okDLLEXPORT okPLL22150_HANDLE DLL_ENTRY +okPLL22150_Construct() +{ + if (_okPLL22150_Construct) + return((*_okPLL22150_Construct)()); + + return(NULL); +} + +okDLLEXPORT void DLL_ENTRY +okPLL22150_Destruct(okPLL22150_HANDLE pll) +{ + if (_okPLL22150_Destruct) + (*_okPLL22150_Destruct)(pll); +} + +okDLLEXPORT void DLL_ENTRY +okPLL22150_SetCrystalLoad(okPLL22150_HANDLE pll, double capload) +{ + if (_okPLL22150_SetCrystalLoad) + (*_okPLL22150_SetCrystalLoad)(pll, capload); +} + +okDLLEXPORT void DLL_ENTRY +okPLL22150_SetReference(okPLL22150_HANDLE pll, double freq, Bool extosc) +{ + if (_okPLL22150_SetReference) + (*_okPLL22150_SetReference)(pll, freq, extosc); +} + +okDLLEXPORT double DLL_ENTRY +okPLL22150_GetReference(okPLL22150_HANDLE pll) +{ + if (_okPLL22150_GetReference) + return((*_okPLL22150_GetReference)(pll)); + + return(0.0); +} + +okDLLEXPORT Bool DLL_ENTRY +okPLL22150_SetVCOParameters(okPLL22150_HANDLE pll, int p, int q) +{ + if (_okPLL22150_SetVCOParameters) + return((*_okPLL22150_SetVCOParameters)(pll, p, q)); + + return(FALSE); +} + +okDLLEXPORT int DLL_ENTRY +okPLL22150_GetVCOP(okPLL22150_HANDLE pll) +{ + if (_okPLL22150_GetVCOP) + return((*_okPLL22150_GetVCOP)(pll)); + + return(0); +} + +okDLLEXPORT int DLL_ENTRY +okPLL22150_GetVCOQ(okPLL22150_HANDLE pll) +{ + if (_okPLL22150_GetVCOQ) + return((*_okPLL22150_GetVCOQ)(pll)); + + return(0); +} + +okDLLEXPORT double DLL_ENTRY +okPLL22150_GetVCOFrequency(okPLL22150_HANDLE pll) +{ + if (_okPLL22150_GetVCOFrequency) + return((*_okPLL22150_GetVCOFrequency)(pll)); + + return(0.0); +} + +okDLLEXPORT void DLL_ENTRY +okPLL22150_SetDiv1(okPLL22150_HANDLE pll, ok_DividerSource divsrc, int n) +{ + if (_okPLL22150_SetDiv1) + (*_okPLL22150_SetDiv1)(pll, divsrc, n); +} + +okDLLEXPORT void DLL_ENTRY +okPLL22150_SetDiv2(okPLL22150_HANDLE pll, ok_DividerSource divsrc, int n) +{ + if (_okPLL22150_SetDiv2) + (*_okPLL22150_SetDiv2)(pll, divsrc, n); +} + +okDLLEXPORT ok_DividerSource DLL_ENTRY +okPLL22150_GetDiv1Source(okPLL22150_HANDLE pll) +{ + if (_okPLL22150_GetDiv1Source) + return((*_okPLL22150_GetDiv1Source)(pll)); + + return(ok_DivSrc_Ref); +} + +okDLLEXPORT ok_DividerSource DLL_ENTRY +okPLL22150_GetDiv2Source(okPLL22150_HANDLE pll) +{ + if (_okPLL22150_GetDiv2Source) + return((*_okPLL22150_GetDiv2Source)(pll)); + + return(ok_DivSrc_Ref); +} + +okDLLEXPORT int DLL_ENTRY +okPLL22150_GetDiv1Divider(okPLL22150_HANDLE pll) +{ + if (_okPLL22150_GetDiv1Divider) + return((*_okPLL22150_GetDiv1Divider)(pll)); + + return(0); +} + +okDLLEXPORT int DLL_ENTRY +okPLL22150_GetDiv2Divider(okPLL22150_HANDLE pll) +{ + if (_okPLL22150_GetDiv2Divider) + return((*_okPLL22150_GetDiv2Divider)(pll)); + + return(0); +} + +okDLLEXPORT void DLL_ENTRY +okPLL22150_SetOutputSource(okPLL22150_HANDLE pll, int output, ok_ClockSource_22150 clksrc) +{ + if (_okPLL22150_SetOutputSource) + (*_okPLL22150_SetOutputSource)(pll, output, clksrc); +} + +okDLLEXPORT void DLL_ENTRY +okPLL22150_SetOutputEnable(okPLL22150_HANDLE pll, int output, Bool enable) +{ + if (_okPLL22150_SetOutputEnable) + (*_okPLL22150_SetOutputEnable)(pll, output, enable); +} + +okDLLEXPORT ok_ClockSource_22150 DLL_ENTRY +okPLL22150_GetOutputSource(okPLL22150_HANDLE pll, int output) +{ + if (_okPLL22150_GetOutputSource) + return((*_okPLL22150_GetOutputSource)(pll, output)); + + return(ok_ClkSrc22150_Ref); +} + +okDLLEXPORT double DLL_ENTRY +okPLL22150_GetOutputFrequency(okPLL22150_HANDLE pll, int output) +{ + if (_okPLL22150_GetOutputFrequency) + return((*_okPLL22150_GetOutputFrequency)(pll, output)); + + return(0.0); +} + +okDLLEXPORT Bool DLL_ENTRY +okPLL22150_IsOutputEnabled(okPLL22150_HANDLE pll, int output) +{ + if (_okPLL22150_IsOutputEnabled) + return((*_okPLL22150_IsOutputEnabled)(pll, output)); + + return(FALSE); +} + +okDLLEXPORT void DLL_ENTRY +okPLL22150_InitFromProgrammingInfo(okPLL22150_HANDLE pll, unsigned char *buf) +{ + if (_okPLL22150_InitFromProgrammingInfo) + (*_okPLL22150_InitFromProgrammingInfo)(pll, buf); +} + +okDLLEXPORT void DLL_ENTRY +okPLL22150_GetProgrammingInfo(okPLL22150_HANDLE pll, unsigned char *buf) +{ + if (_okPLL22150_GetProgrammingInfo) + (*_okPLL22150_GetProgrammingInfo)(pll, buf); +} + + +//------------------------------------------------------------------------ +// Function calls - okCFrontPanel +//------------------------------------------------------------------------ +okDLLEXPORT okFrontPanel_HANDLE DLL_ENTRY +okFrontPanel_Construct() +{ + if (_okFrontPanel_Construct) + return((*_okFrontPanel_Construct)()); + + return(NULL); +} + + +okDLLEXPORT void DLL_ENTRY +okFrontPanel_Destruct(okFrontPanel_HANDLE hnd) +{ + if (_okFrontPanel_Destruct) + (*_okFrontPanel_Destruct)(hnd); +} + + +okDLLEXPORT int DLL_ENTRY +okFrontPanel_GetHostInterfaceWidth(okFrontPanel_HANDLE hnd) +{ + if (_okFrontPanel_GetHostInterfaceWidth) + return((*_okFrontPanel_GetHostInterfaceWidth)(hnd)); + + return(FALSE); +} + + +okDLLEXPORT Bool DLL_ENTRY +okFrontPanel_IsHighSpeed(okFrontPanel_HANDLE hnd) +{ + if (_okFrontPanel_IsHighSpeed) + return((*_okFrontPanel_IsHighSpeed)(hnd)); + + return(FALSE); +} + + +okDLLEXPORT ok_BoardModel DLL_ENTRY +okFrontPanel_GetBoardModel(okFrontPanel_HANDLE hnd) +{ + if (_okFrontPanel_GetBoardModel) + return((*_okFrontPanel_GetBoardModel)(hnd)); + + return(ok_brdUnknown); +} + + +okDLLEXPORT void DLL_ENTRY +okFrontPanel_GetBoardModelString(okFrontPanel_HANDLE hnd, ok_BoardModel m, char *str) +{ + if (_okFrontPanel_GetBoardModelString) + (*_okFrontPanel_GetBoardModelString)(hnd, m, str); +} + + +okDLLEXPORT ok_ErrorCode DLL_ENTRY +okFrontPanel_WriteI2C(okFrontPanel_HANDLE hnd, const int addr, int length, unsigned char *data) +{ + if (_okFrontPanel_WriteI2C) + return((*_okFrontPanel_WriteI2C)(hnd, addr, length, data)); + + return(ok_UnsupportedFeature); +} + + +okDLLEXPORT ok_ErrorCode DLL_ENTRY +okFrontPanel_ReadI2C(okFrontPanel_HANDLE hnd, const int addr, int length, unsigned char *data) +{ + if (_okFrontPanel_ReadI2C) + return((*_okFrontPanel_ReadI2C)(hnd, addr, length, data)); + + return(ok_UnsupportedFeature); +} + + +okDLLEXPORT int DLL_ENTRY +okFrontPanel_GetDeviceCount(okFrontPanel_HANDLE hnd) +{ + if (_okFrontPanel_GetDeviceCount) + return((*_okFrontPanel_GetDeviceCount)(hnd)); + + return(0); +} + + +okDLLEXPORT ok_BoardModel DLL_ENTRY +okFrontPanel_GetDeviceListModel(okFrontPanel_HANDLE hnd, int num) +{ + if (_okFrontPanel_GetDeviceListModel) + return((*_okFrontPanel_GetDeviceListModel)(hnd, num)); + + return(ok_brdUnknown); +} + + +okDLLEXPORT void DLL_ENTRY +okFrontPanel_GetDeviceListSerial(okFrontPanel_HANDLE hnd, int num, char *str) +{ + if (_okFrontPanel_GetDeviceListSerial) + (*_okFrontPanel_GetDeviceListSerial)(hnd, num, str); +} + + +okDLLEXPORT ok_ErrorCode DLL_ENTRY +okFrontPanel_OpenBySerial(okFrontPanel_HANDLE hnd, const char *serial) +{ + if (_okFrontPanel_OpenBySerial) + return((*_okFrontPanel_OpenBySerial)(hnd, serial)); + + return(ok_UnsupportedFeature); +} + +okDLLEXPORT Bool DLL_ENTRY +okFrontPanel_IsOpen(okFrontPanel_HANDLE hnd) +{ + if (_okFrontPanel_IsOpen) + return((*_okFrontPanel_IsOpen)(hnd)); + + return(FALSE); +} + +okDLLEXPORT void DLL_ENTRY +okFrontPanel_EnableAsynchronousTransfers(okFrontPanel_HANDLE hnd, Bool enable) +{ + if (_okFrontPanel_EnableAsynchronousTransfers) + (*_okFrontPanel_EnableAsynchronousTransfers)(hnd, enable); +} + +okDLLEXPORT ok_ErrorCode DLL_ENTRY +okFrontPanel_SetBTPipePollingInterval(okFrontPanel_HANDLE hnd, int interval) +{ + if (_okFrontPanel_SetBTPipePollingInterval) + return((*_okFrontPanel_SetBTPipePollingInterval)(hnd, interval)); + + return(ok_UnsupportedFeature); +} + +okDLLEXPORT void DLL_ENTRY +okFrontPanel_SetTimeout(okFrontPanel_HANDLE hnd, int timeout) +{ + if (_okFrontPanel_SetTimeout) + (*_okFrontPanel_SetTimeout)(hnd, timeout); +} + +okDLLEXPORT int DLL_ENTRY +okFrontPanel_GetDeviceMajorVersion(okFrontPanel_HANDLE hnd) +{ + if (_okFrontPanel_GetDeviceMajorVersion) + return((*_okFrontPanel_GetDeviceMajorVersion)(hnd)); + + return(0); +} + +okDLLEXPORT int DLL_ENTRY +okFrontPanel_GetDeviceMinorVersion(okFrontPanel_HANDLE hnd) +{ + if (_okFrontPanel_GetDeviceMinorVersion) + return((*_okFrontPanel_GetDeviceMinorVersion)(hnd)); + + return(0); +} + +okDLLEXPORT ok_ErrorCode DLL_ENTRY +okFrontPanel_ResetFPGA(okFrontPanel_HANDLE hnd) +{ + if (_okFrontPanel_ResetFPGA) + return((*_okFrontPanel_ResetFPGA)(hnd)); + + return(ok_UnsupportedFeature); +} + +okDLLEXPORT void DLL_ENTRY +okFrontPanel_GetSerialNumber(okFrontPanel_HANDLE hnd, char *buf) +{ + if (_okFrontPanel_GetSerialNumber) + (*_okFrontPanel_GetSerialNumber)(hnd, buf); +} + +okDLLEXPORT void DLL_ENTRY +okFrontPanel_GetDeviceID(okFrontPanel_HANDLE hnd, char *buf) +{ + if (_okFrontPanel_GetDeviceID) + (*_okFrontPanel_GetDeviceID)(hnd, buf); +} + +okDLLEXPORT void DLL_ENTRY +okFrontPanel_SetDeviceID(okFrontPanel_HANDLE hnd, const char *strID) +{ + if (_okFrontPanel_SetDeviceID) + (*_okFrontPanel_SetDeviceID)(hnd, strID); +} + +okDLLEXPORT ok_ErrorCode DLL_ENTRY +okFrontPanel_ConfigureFPGA(okFrontPanel_HANDLE hnd, const char *strFilename) +{ + if (_okFrontPanel_ConfigureFPGA) + return((*_okFrontPanel_ConfigureFPGA)(hnd, strFilename)); + + return(ok_UnsupportedFeature); +} + +okDLLEXPORT ok_ErrorCode DLL_ENTRY +okFrontPanel_ConfigureFPGAFromMemory(okFrontPanel_HANDLE hnd, unsigned char *data, unsigned long length) +{ + if (_okFrontPanel_ConfigureFPGAFromMemory) + return((*_okFrontPanel_ConfigureFPGAFromMemory)(hnd, data, length)); + + return(ok_UnsupportedFeature); +} + +okDLLEXPORT ok_ErrorCode DLL_ENTRY +okFrontPanel_GetPLL22150Configuration(okFrontPanel_HANDLE hnd, okPLL22150_HANDLE pll) +{ + if (_okFrontPanel_GetPLL22150Configuration) + return((*_okFrontPanel_GetPLL22150Configuration)(hnd, pll)); + + return(ok_UnsupportedFeature); +} + +okDLLEXPORT ok_ErrorCode DLL_ENTRY +okFrontPanel_SetPLL22150Configuration(okFrontPanel_HANDLE hnd, okPLL22150_HANDLE pll) +{ + if (_okFrontPanel_SetPLL22150Configuration) + return((*_okFrontPanel_SetPLL22150Configuration)(hnd, pll)); + + return(ok_UnsupportedFeature); +} + +okDLLEXPORT ok_ErrorCode DLL_ENTRY +okFrontPanel_GetEepromPLL22150Configuration(okFrontPanel_HANDLE hnd, okPLL22150_HANDLE pll) +{ + if (_okFrontPanel_GetEepromPLL22150Configuration) + return((*_okFrontPanel_GetEepromPLL22150Configuration)(hnd, pll)); + + return(ok_UnsupportedFeature); +} + +okDLLEXPORT ok_ErrorCode DLL_ENTRY +okFrontPanel_SetEepromPLL22150Configuration(okFrontPanel_HANDLE hnd, okPLL22150_HANDLE pll) +{ + if (_okFrontPanel_SetEepromPLL22150Configuration) + return((*_okFrontPanel_SetEepromPLL22150Configuration)(hnd, pll)); + + return(ok_UnsupportedFeature); +} + +okDLLEXPORT ok_ErrorCode DLL_ENTRY +okFrontPanel_GetPLL22393Configuration(okFrontPanel_HANDLE hnd, okPLL22393_HANDLE pll) +{ + if (_okFrontPanel_GetPLL22393Configuration) + return((*_okFrontPanel_GetPLL22393Configuration)(hnd, pll)); + + return(ok_UnsupportedFeature); +} + +okDLLEXPORT ok_ErrorCode DLL_ENTRY +okFrontPanel_SetPLL22393Configuration(okFrontPanel_HANDLE hnd, okPLL22393_HANDLE pll) +{ + if (_okFrontPanel_SetPLL22393Configuration) + return((*_okFrontPanel_SetPLL22393Configuration)(hnd, pll)); + + return(ok_UnsupportedFeature); +} + +okDLLEXPORT ok_ErrorCode DLL_ENTRY +okFrontPanel_GetEepromPLL22393Configuration(okFrontPanel_HANDLE hnd, okPLL22393_HANDLE pll) +{ + if (_okFrontPanel_GetEepromPLL22393Configuration) + return((*_okFrontPanel_GetEepromPLL22393Configuration)(hnd, pll)); + + return(ok_UnsupportedFeature); +} + +okDLLEXPORT ok_ErrorCode DLL_ENTRY +okFrontPanel_SetEepromPLL22393Configuration(okFrontPanel_HANDLE hnd, okPLL22393_HANDLE pll) +{ + if (_okFrontPanel_SetEepromPLL22393Configuration) + return((*_okFrontPanel_SetEepromPLL22393Configuration)(hnd, pll)); + + return(ok_UnsupportedFeature); +} + +okDLLEXPORT ok_ErrorCode DLL_ENTRY +okFrontPanel_LoadDefaultPLLConfiguration(okFrontPanel_HANDLE hnd) +{ + if (_okFrontPanel_LoadDefaultPLLConfiguration) + return((*_okFrontPanel_LoadDefaultPLLConfiguration)(hnd)); + + return(ok_UnsupportedFeature); +} + +okDLLEXPORT Bool DLL_ENTRY +okFrontPanel_IsFrontPanelEnabled(okFrontPanel_HANDLE hnd) +{ + if (_okFrontPanel_IsFrontPanelEnabled) + return((*_okFrontPanel_IsFrontPanelEnabled)(hnd)); + + return(ok_UnsupportedFeature); +} + +okDLLEXPORT Bool DLL_ENTRY +okFrontPanel_IsFrontPanel3Supported(okFrontPanel_HANDLE hnd) +{ + if (_okFrontPanel_IsFrontPanel3Supported) + return((*_okFrontPanel_IsFrontPanel3Supported)(hnd)); + + return(FALSE); +} + +okDLLEXPORT void DLL_ENTRY +okFrontPanel_UpdateWireIns(okFrontPanel_HANDLE hnd) +{ + if (_okFrontPanel_UpdateWireIns) + (*_okFrontPanel_UpdateWireIns)(hnd); +} + +okDLLEXPORT ok_ErrorCode DLL_ENTRY +okFrontPanel_SetWireInValue(okFrontPanel_HANDLE hnd, int ep, UINT32 val, UINT32 mask) +{ + if (_okFrontPanel_SetWireInValue) + return((*_okFrontPanel_SetWireInValue)(hnd, ep, val, mask)); + + return(ok_UnsupportedFeature); +} + +okDLLEXPORT void DLL_ENTRY +okFrontPanel_UpdateWireOuts(okFrontPanel_HANDLE hnd) +{ + if (_okFrontPanel_UpdateWireOuts) + (*_okFrontPanel_UpdateWireOuts)(hnd); +} + +okDLLEXPORT UINT32 DLL_ENTRY +okFrontPanel_GetWireOutValue(okFrontPanel_HANDLE hnd, int epAddr) +{ + if (_okFrontPanel_GetWireOutValue) + return((*_okFrontPanel_GetWireOutValue)(hnd, epAddr)); + + return(0); +} + +okDLLEXPORT ok_ErrorCode DLL_ENTRY +okFrontPanel_ActivateTriggerIn(okFrontPanel_HANDLE hnd, int epAddr, int bit) +{ + if (_okFrontPanel_ActivateTriggerIn) + return((*_okFrontPanel_ActivateTriggerIn)(hnd, epAddr, bit)); + + return(ok_UnsupportedFeature); +} + +okDLLEXPORT void DLL_ENTRY +okFrontPanel_UpdateTriggerOuts(okFrontPanel_HANDLE hnd) +{ + if (_okFrontPanel_UpdateTriggerOuts) + (*_okFrontPanel_UpdateTriggerOuts)(hnd); +} + +okDLLEXPORT Bool DLL_ENTRY +okFrontPanel_IsTriggered(okFrontPanel_HANDLE hnd, int epAddr, UINT32 mask) +{ + if (_okFrontPanel_IsTriggered) + return((*_okFrontPanel_IsTriggered)(hnd, epAddr, mask)); + + return(FALSE); +} + +okDLLEXPORT long DLL_ENTRY +okFrontPanel_GetLastTransferLength(okFrontPanel_HANDLE hnd) +{ + if (_okFrontPanel_GetLastTransferLength) + return((*_okFrontPanel_GetLastTransferLength)(hnd)); + + return(0); +} + +okDLLEXPORT long DLL_ENTRY +okFrontPanel_WriteToPipeIn(okFrontPanel_HANDLE hnd, int epAddr, long length, unsigned char *data) +{ + if (_okFrontPanel_WriteToPipeIn) + return((*_okFrontPanel_WriteToPipeIn)(hnd, epAddr, length, data)); + + return(0); +} + +okDLLEXPORT long DLL_ENTRY +okFrontPanel_WriteToBlockPipeIn(okFrontPanel_HANDLE hnd, int epAddr, int blocksize, long length, unsigned char *data) +{ + if (_okFrontPanel_WriteToBlockPipeIn) + return((*_okFrontPanel_WriteToBlockPipeIn)(hnd, epAddr, blocksize, length, data)); + + return(0); +} + +okDLLEXPORT long DLL_ENTRY +okFrontPanel_ReadFromPipeOut(okFrontPanel_HANDLE hnd, int epAddr, long length, unsigned char *data) +{ + if (_okFrontPanel_ReadFromPipeOut) + return((*_okFrontPanel_ReadFromPipeOut)(hnd, epAddr, length, data)); + + return(0); +} + +okDLLEXPORT long DLL_ENTRY +okFrontPanel_ReadFromBlockPipeOut(okFrontPanel_HANDLE hnd, int epAddr, int blocksize, long length, unsigned char *data) +{ + if (_okFrontPanel_ReadFromBlockPipeOut) + return((*_okFrontPanel_ReadFromBlockPipeOut)(hnd, epAddr, blocksize, length, data)); + + return(0); +} + + diff --git a/Source/Processors/DataThreads/okFrontPanelDLL.h b/Source/Processors/DataThreads/okFrontPanelDLL.h new file mode 100644 index 0000000000000000000000000000000000000000..3df445dc46272cf1001e12334d95590c75de0173 --- /dev/null +++ b/Source/Processors/DataThreads/okFrontPanelDLL.h @@ -0,0 +1,440 @@ +///------------------------------------------------------------------------ +// okFrontPanelDLL.h +// +// This is the header file for C/C++ compilation of the FrontPanel DLL +// import library. This file must be included within any C/C++ source +// which references the FrontPanel DLL methods. +// +//------------------------------------------------------------------------ +// Copyright (c) 2005-2010 Opal Kelly Incorporated +// $Rev: 925 $ $Date: 2011-02-25 17:09:05 -0800 (Fri, 25 Feb 2011) $ +//------------------------------------------------------------------------ + +#ifndef __okFrontPanelDLL_h__ +#define __okFrontPanelDLL_h__ + +#if defined(_WIN32) + #include <windows.h> +#elif defined(__APPLE__) + typedef unsigned char UINT8; + typedef unsigned short UINT16; + typedef unsigned int UINT32; +#elif defined(__linux__) + typedef unsigned char UINT8; + typedef unsigned short UINT16; + typedef unsigned int UINT32; +#endif + +// The following ifdef block is the standard way of creating macros which make exporting +// from a DLL simpler. All files within this DLL are compiled with the FRONTPANELDLL_EXPORTS +// symbol defined on the command line. This symbol should not be defined on any project +// that uses this DLL. +#if defined(_WIN32) + #if defined(FRONTPANELDLL_EXPORTS) + #define okDLLEXPORT __declspec(dllexport) + #define DLL_ENTRY __stdcall + #else + #define okDLLEXPORT + #define DLL_ENTRY __stdcall + #endif +#elif defined(__linux__) || defined(__APPLE__) + #define okDLLEXPORT + #define DLL_ENTRY +#endif + + +typedef void (* DLL_EP)(void); + +#ifdef __cplusplus +#include <string> +extern "C" { +#endif // __cplusplus + +typedef void* okPLL22150_HANDLE; +typedef void* okPLL22393_HANDLE; +typedef void* okFrontPanel_HANDLE; +typedef int Bool; + +#define MAX_SERIALNUMBER_LENGTH 10 // 10 characters + Does NOT include termination NULL. +#define MAX_DEVICEID_LENGTH 32 // 32 characters + Does NOT include termination NULL. +#define MAX_BOARDMODELSTRING_LENGTH 64 // 64 characters including termination NULL. + +#ifndef TRUE + #define TRUE 1 + #define FALSE 0 +#endif + +typedef enum { + ok_ClkSrc22150_Ref=0, + ok_ClkSrc22150_Div1ByN=1, + ok_ClkSrc22150_Div1By2=2, + ok_ClkSrc22150_Div1By3=3, + ok_ClkSrc22150_Div2ByN=4, + ok_ClkSrc22150_Div2By2=5, + ok_ClkSrc22150_Div2By4=6 +} ok_ClockSource_22150; + +typedef enum { + ok_ClkSrc22393_Ref=0, + ok_ClkSrc22393_PLL0_0=2, + ok_ClkSrc22393_PLL0_180=3, + ok_ClkSrc22393_PLL1_0=4, + ok_ClkSrc22393_PLL1_180=5, + ok_ClkSrc22393_PLL2_0=6, + ok_ClkSrc22393_PLL2_180=7 +} ok_ClockSource_22393; + +typedef enum { + ok_DivSrc_Ref = 0, + ok_DivSrc_VCO = 1 +} ok_DividerSource; + +typedef enum { + ok_brdUnknown = 0, + ok_brdXEM3001v1 = 1, + ok_brdXEM3001v2 = 2, + ok_brdXEM3010 = 3, + ok_brdXEM3005 = 4, + ok_brdXEM3001CL = 5, + ok_brdXEM3020 = 6, + ok_brdXEM3050 = 7, + ok_brdXEM9002 = 8, + ok_brdXEM3001RB = 9, + ok_brdXEM5010 = 10, + ok_brdXEM6110LX45 = 11, + ok_brdXEM6110LX150 = 15, + ok_brdXEM6001 = 12, + ok_brdXEM6010LX45 = 13, + ok_brdXEM6010LX150 = 14 +} ok_BoardModel; + +typedef enum { + ok_NoError = 0, + ok_Failed = -1, + ok_Timeout = -2, + ok_DoneNotHigh = -3, + ok_TransferError = -4, + ok_CommunicationError = -5, + ok_InvalidBitstream = -6, + ok_FileError = -7, + ok_DeviceNotOpen = -8, + ok_InvalidEndpoint = -9, + ok_InvalidBlockSize = -10, + ok_I2CRestrictedAddress = -11, + ok_I2CBitError = -12, + ok_I2CNack = -13, + ok_I2CUnknownStatus = -14, + ok_UnsupportedFeature = -15, + ok_FIFOUnderflow = -16, + ok_FIFOOverflow = -17, + ok_DataAlignmentError = -18 +} ok_ErrorCode; + +// +// Define the LoadLib and FreeLib methods for the IMPORT side. +// +#ifndef FRONTPANELDLL_EXPORTS + Bool okFrontPanelDLL_LoadLib(const char *libname); + void okFrontPanelDLL_FreeLib(void); +#endif + +// +// General +// +okDLLEXPORT void DLL_ENTRY okFrontPanelDLL_GetVersion(char *date, char *time); + +// +// okPLL22393 +// +okDLLEXPORT okPLL22393_HANDLE DLL_ENTRY okPLL22393_Construct(); +okDLLEXPORT void DLL_ENTRY okPLL22393_Destruct(okPLL22393_HANDLE pll); +okDLLEXPORT void DLL_ENTRY okPLL22393_SetCrystalLoad(okPLL22393_HANDLE pll, double capload); +okDLLEXPORT void DLL_ENTRY okPLL22393_SetReference(okPLL22393_HANDLE pll, double freq); +okDLLEXPORT double DLL_ENTRY okPLL22393_GetReference(okPLL22393_HANDLE pll); +okDLLEXPORT Bool DLL_ENTRY okPLL22393_SetPLLParameters(okPLL22393_HANDLE pll, int n, int p, int q, Bool enable); +okDLLEXPORT Bool DLL_ENTRY okPLL22393_SetPLLLF(okPLL22393_HANDLE pll, int n, int lf); +okDLLEXPORT Bool DLL_ENTRY okPLL22393_SetOutputDivider(okPLL22393_HANDLE pll, int n, int div); +okDLLEXPORT Bool DLL_ENTRY okPLL22393_SetOutputSource(okPLL22393_HANDLE pll, int n, ok_ClockSource_22393 clksrc); +okDLLEXPORT void DLL_ENTRY okPLL22393_SetOutputEnable(okPLL22393_HANDLE pll, int n, Bool enable); +okDLLEXPORT int DLL_ENTRY okPLL22393_GetPLLP(okPLL22393_HANDLE pll, int n); +okDLLEXPORT int DLL_ENTRY okPLL22393_GetPLLQ(okPLL22393_HANDLE pll, int n); +okDLLEXPORT double DLL_ENTRY okPLL22393_GetPLLFrequency(okPLL22393_HANDLE pll, int n); +okDLLEXPORT int DLL_ENTRY okPLL22393_GetOutputDivider(okPLL22393_HANDLE pll, int n); +okDLLEXPORT ok_ClockSource_22393 DLL_ENTRY okPLL22393_GetOutputSource(okPLL22393_HANDLE pll, int n); +okDLLEXPORT double DLL_ENTRY okPLL22393_GetOutputFrequency(okPLL22393_HANDLE pll, int n); +okDLLEXPORT Bool DLL_ENTRY okPLL22393_IsOutputEnabled(okPLL22393_HANDLE pll, int n); +okDLLEXPORT Bool DLL_ENTRY okPLL22393_IsPLLEnabled(okPLL22393_HANDLE pll, int n); +okDLLEXPORT void DLL_ENTRY okPLL22393_InitFromProgrammingInfo(okPLL22393_HANDLE pll, unsigned char *buf); +okDLLEXPORT void DLL_ENTRY okPLL22393_GetProgrammingInfo(okPLL22393_HANDLE pll, unsigned char *buf); + + +// +// okPLL22150 +// +okDLLEXPORT okPLL22150_HANDLE DLL_ENTRY okPLL22150_Construct(); +okDLLEXPORT void DLL_ENTRY okPLL22150_Destruct(okPLL22150_HANDLE pll); +okDLLEXPORT void DLL_ENTRY okPLL22150_SetCrystalLoad(okPLL22150_HANDLE pll, double capload); +okDLLEXPORT void DLL_ENTRY okPLL22150_SetReference(okPLL22150_HANDLE pll, double freq, Bool extosc); +okDLLEXPORT double DLL_ENTRY okPLL22150_GetReference(okPLL22150_HANDLE pll); +okDLLEXPORT Bool DLL_ENTRY okPLL22150_SetVCOParameters(okPLL22150_HANDLE pll, int p, int q); +okDLLEXPORT int DLL_ENTRY okPLL22150_GetVCOP(okPLL22150_HANDLE pll); +okDLLEXPORT int DLL_ENTRY okPLL22150_GetVCOQ(okPLL22150_HANDLE pll); +okDLLEXPORT double DLL_ENTRY okPLL22150_GetVCOFrequency(okPLL22150_HANDLE pll); +okDLLEXPORT void DLL_ENTRY okPLL22150_SetDiv1(okPLL22150_HANDLE pll, ok_DividerSource divsrc, int n); +okDLLEXPORT void DLL_ENTRY okPLL22150_SetDiv2(okPLL22150_HANDLE pll, ok_DividerSource divsrc, int n); +okDLLEXPORT ok_DividerSource DLL_ENTRY okPLL22150_GetDiv1Source(okPLL22150_HANDLE pll); +okDLLEXPORT ok_DividerSource DLL_ENTRY okPLL22150_GetDiv2Source(okPLL22150_HANDLE pll); +okDLLEXPORT int DLL_ENTRY okPLL22150_GetDiv1Divider(okPLL22150_HANDLE pll); +okDLLEXPORT int DLL_ENTRY okPLL22150_GetDiv2Divider(okPLL22150_HANDLE pll); +okDLLEXPORT void DLL_ENTRY okPLL22150_SetOutputSource(okPLL22150_HANDLE pll, int output, ok_ClockSource_22150 clksrc); +okDLLEXPORT void DLL_ENTRY okPLL22150_SetOutputEnable(okPLL22150_HANDLE pll, int output, Bool enable); +okDLLEXPORT ok_ClockSource_22150 DLL_ENTRY okPLL22150_GetOutputSource(okPLL22150_HANDLE pll, int output); +okDLLEXPORT double DLL_ENTRY okPLL22150_GetOutputFrequency(okPLL22150_HANDLE pll, int output); +okDLLEXPORT Bool DLL_ENTRY okPLL22150_IsOutputEnabled(okPLL22150_HANDLE pll, int output); +okDLLEXPORT void DLL_ENTRY okPLL22150_InitFromProgrammingInfo(okPLL22150_HANDLE pll, unsigned char *buf); +okDLLEXPORT void DLL_ENTRY okPLL22150_GetProgrammingInfo(okPLL22150_HANDLE pll, unsigned char *buf); + + +// +// okFrontPanel +// +okDLLEXPORT okFrontPanel_HANDLE DLL_ENTRY okFrontPanel_Construct(); +okDLLEXPORT void DLL_ENTRY okFrontPanel_Destruct(okFrontPanel_HANDLE hnd); +okDLLEXPORT ok_ErrorCode DLL_ENTRY okFrontPanel_WriteI2C(okFrontPanel_HANDLE hnd, const int addr, int length, unsigned char *data); +okDLLEXPORT ok_ErrorCode DLL_ENTRY okFrontPanel_ReadI2C(okFrontPanel_HANDLE hnd, const int addr, int length, unsigned char *data); +okDLLEXPORT int DLL_ENTRY okFrontPanel_GetHostInterfaceWidth(okFrontPanel_HANDLE hnd); +okDLLEXPORT Bool DLL_ENTRY okFrontPanel_IsHighSpeed(okFrontPanel_HANDLE hnd); +okDLLEXPORT ok_BoardModel DLL_ENTRY okFrontPanel_GetBoardModel(okFrontPanel_HANDLE hnd); +okDLLEXPORT void DLL_ENTRY okFrontPanel_GetBoardModelString(okFrontPanel_HANDLE hnd, ok_BoardModel m, char *buf); +okDLLEXPORT int DLL_ENTRY okFrontPanel_GetDeviceCount(okFrontPanel_HANDLE hnd); +okDLLEXPORT ok_BoardModel DLL_ENTRY okFrontPanel_GetDeviceListModel(okFrontPanel_HANDLE hnd, int num); +okDLLEXPORT void DLL_ENTRY okFrontPanel_GetDeviceListSerial(okFrontPanel_HANDLE hnd, int num, char *buf); +okDLLEXPORT ok_ErrorCode DLL_ENTRY okFrontPanel_OpenBySerial(okFrontPanel_HANDLE hnd, const char *serial); +okDLLEXPORT Bool DLL_ENTRY okFrontPanel_IsOpen(okFrontPanel_HANDLE hnd); +okDLLEXPORT void DLL_ENTRY okFrontPanel_EnableAsynchronousTransfers(okFrontPanel_HANDLE hnd, Bool enable); +okDLLEXPORT ok_ErrorCode DLL_ENTRY okFrontPanel_SetBTPipePollingInterval(okFrontPanel_HANDLE hnd, int interval); +okDLLEXPORT void DLL_ENTRY okFrontPanel_SetTimeout(okFrontPanel_HANDLE hnd, int timeout); +okDLLEXPORT int DLL_ENTRY okFrontPanel_GetDeviceMajorVersion(okFrontPanel_HANDLE hnd); +okDLLEXPORT int DLL_ENTRY okFrontPanel_GetDeviceMinorVersion(okFrontPanel_HANDLE hnd); +okDLLEXPORT ok_ErrorCode DLL_ENTRY okFrontPanel_ResetFPGA(okFrontPanel_HANDLE hnd); +okDLLEXPORT void DLL_ENTRY okFrontPanel_GetSerialNumber(okFrontPanel_HANDLE hnd, char *buf); +okDLLEXPORT void DLL_ENTRY okFrontPanel_GetDeviceID(okFrontPanel_HANDLE hnd, char *buf); +okDLLEXPORT void DLL_ENTRY okFrontPanel_SetDeviceID(okFrontPanel_HANDLE hnd, const char *strID); +okDLLEXPORT ok_ErrorCode DLL_ENTRY okFrontPanel_ConfigureFPGA(okFrontPanel_HANDLE hnd, const char *strFilename); +okDLLEXPORT ok_ErrorCode DLL_ENTRY okFrontPanel_ConfigureFPGAFromMemory(okFrontPanel_HANDLE hnd, unsigned char *data, unsigned long length); +okDLLEXPORT ok_ErrorCode DLL_ENTRY okFrontPanel_GetPLL22150Configuration(okFrontPanel_HANDLE hnd, okPLL22150_HANDLE pll); +okDLLEXPORT ok_ErrorCode DLL_ENTRY okFrontPanel_SetPLL22150Configuration(okFrontPanel_HANDLE hnd, okPLL22150_HANDLE pll); +okDLLEXPORT ok_ErrorCode DLL_ENTRY okFrontPanel_GetEepromPLL22150Configuration(okFrontPanel_HANDLE hnd, okPLL22150_HANDLE pll); +okDLLEXPORT ok_ErrorCode DLL_ENTRY okFrontPanel_SetEepromPLL22150Configuration(okFrontPanel_HANDLE hnd, okPLL22150_HANDLE pll); +okDLLEXPORT ok_ErrorCode DLL_ENTRY okFrontPanel_GetPLL22393Configuration(okFrontPanel_HANDLE hnd, okPLL22393_HANDLE pll); +okDLLEXPORT ok_ErrorCode DLL_ENTRY okFrontPanel_SetPLL22393Configuration(okFrontPanel_HANDLE hnd, okPLL22393_HANDLE pll); +okDLLEXPORT ok_ErrorCode DLL_ENTRY okFrontPanel_GetEepromPLL22393Configuration(okFrontPanel_HANDLE hnd, okPLL22393_HANDLE pll); +okDLLEXPORT ok_ErrorCode DLL_ENTRY okFrontPanel_SetEepromPLL22393Configuration(okFrontPanel_HANDLE hnd, okPLL22393_HANDLE pll); +okDLLEXPORT ok_ErrorCode DLL_ENTRY okFrontPanel_LoadDefaultPLLConfiguration(okFrontPanel_HANDLE hnd); +okDLLEXPORT Bool DLL_ENTRY okFrontPanel_IsFrontPanelEnabled(okFrontPanel_HANDLE hnd); +okDLLEXPORT Bool DLL_ENTRY okFrontPanel_IsFrontPanel3Supported(okFrontPanel_HANDLE hnd); +okDLLEXPORT void DLL_ENTRY okFrontPanel_UpdateWireIns(okFrontPanel_HANDLE hnd); +okDLLEXPORT ok_ErrorCode DLL_ENTRY okFrontPanel_SetWireInValue(okFrontPanel_HANDLE hnd, int ep, UINT32 val, UINT32 mask); +okDLLEXPORT void DLL_ENTRY okFrontPanel_UpdateWireOuts(okFrontPanel_HANDLE hnd); +okDLLEXPORT UINT32 DLL_ENTRY okFrontPanel_GetWireOutValue(okFrontPanel_HANDLE hnd, int epAddr); +okDLLEXPORT ok_ErrorCode DLL_ENTRY okFrontPanel_ActivateTriggerIn(okFrontPanel_HANDLE hnd, int epAddr, int bit); +okDLLEXPORT void DLL_ENTRY okFrontPanel_UpdateTriggerOuts(okFrontPanel_HANDLE hnd); +okDLLEXPORT Bool DLL_ENTRY okFrontPanel_IsTriggered(okFrontPanel_HANDLE hnd, int epAddr, UINT32 mask); +okDLLEXPORT long DLL_ENTRY okFrontPanel_GetLastTransferLength(okFrontPanel_HANDLE hnd); +okDLLEXPORT long DLL_ENTRY okFrontPanel_WriteToPipeIn(okFrontPanel_HANDLE hnd, int epAddr, long length, unsigned char *data); +okDLLEXPORT long DLL_ENTRY okFrontPanel_ReadFromPipeOut(okFrontPanel_HANDLE hnd, int epAddr, long length, unsigned char *data); +okDLLEXPORT long DLL_ENTRY okFrontPanel_WriteToBlockPipeIn(okFrontPanel_HANDLE hnd, int epAddr, int blockSize, long length, unsigned char *data); +okDLLEXPORT long DLL_ENTRY okFrontPanel_ReadFromBlockPipeOut(okFrontPanel_HANDLE hnd, int epAddr, int blockSize, long length, unsigned char *data); + + +#ifdef __cplusplus +#if !defined(FRONTPANELDLL_EXPORTS) +//------------------------------------------------------------------------ +// okCPLL22150 C++ wrapper class +//------------------------------------------------------------------------ +class okCPLL22150 +{ +public: + okPLL22150_HANDLE h; + enum ClockSource { + ClkSrc_Ref=0, + ClkSrc_Div1ByN=1, + ClkSrc_Div1By2=2, + ClkSrc_Div1By3=3, + ClkSrc_Div2ByN=4, + ClkSrc_Div2By2=5, + ClkSrc_Div2By4=6 }; + + enum DividerSource { + DivSrc_Ref = 0, + DivSrc_VCO = 1 }; +private: + bool to_bool(Bool x); + Bool from_bool(bool x); +public: + okCPLL22150(); + void SetCrystalLoad(double capload); + void SetReference(double freq, bool extosc); + double GetReference(); + bool SetVCOParameters(int p, int q); + int GetVCOP(); + int GetVCOQ(); + double GetVCOFrequency(); + void SetDiv1(DividerSource divsrc, int n); + void SetDiv2(DividerSource divsrc, int n); + DividerSource GetDiv1Source(); + DividerSource GetDiv2Source(); + int GetDiv1Divider(); + int GetDiv2Divider(); + void SetOutputSource(int output, ClockSource clksrc); + void SetOutputEnable(int output, bool enable); + ClockSource GetOutputSource(int output); + double GetOutputFrequency(int output); + bool IsOutputEnabled(int output); + void InitFromProgrammingInfo(unsigned char *buf); + void GetProgrammingInfo(unsigned char *buf); +}; + +//------------------------------------------------------------------------ +// okCPLL22150 C++ wrapper class +//------------------------------------------------------------------------ +class okCPLL22393 +{ +public: + okPLL22393_HANDLE h; + enum ClockSource { + ClkSrc_Ref=0, + ClkSrc_PLL0_0=2, + ClkSrc_PLL0_180=3, + ClkSrc_PLL1_0=4, + ClkSrc_PLL1_180=5, + ClkSrc_PLL2_0=6, + ClkSrc_PLL2_180=7 }; +private: + bool to_bool(Bool x); + Bool from_bool(bool x); +public: + okCPLL22393(); + void SetCrystalLoad(double capload); + void SetReference(double freq); + double GetReference(); + bool SetPLLParameters(int n, int p, int q, bool enable=true); + bool SetPLLLF(int n, int lf); + bool SetOutputDivider(int n, int div); + bool SetOutputSource(int n, ClockSource clksrc); + void SetOutputEnable(int n, bool enable); + int GetPLLP(int n); + int GetPLLQ(int n); + double GetPLLFrequency(int n); + int GetOutputDivider(int n); + ClockSource GetOutputSource(int n); + double GetOutputFrequency(int n); + bool IsOutputEnabled(int n); + bool IsPLLEnabled(int n); + void InitFromProgrammingInfo(unsigned char *buf); + void GetProgrammingInfo(unsigned char *buf); +}; + +//------------------------------------------------------------------------ +// okCFrontPanel C++ wrapper class +//------------------------------------------------------------------------ +class okCFrontPanel +{ +public: + okFrontPanel_HANDLE h; + enum BoardModel { + brdUnknown=0, + brdXEM3001v1=1, + brdXEM3001v2=2, + brdXEM3010=3, + brdXEM3005=4, + brdXEM3001CL=5, + brdXEM3020=6, + brdXEM3050=7, + brdXEM9002=8, + brdXEM3001RB=9, + brdXEM5010=10, + brdXEM6110LX45=11, + brdXEM6110LX150=15, + brdXEM6001=12, + brdXEM6010LX45=13, + brdXEM6010LX150=14 + }; + enum ErrorCode { + NoError = 0, + Failed = -1, + Timeout = -2, + DoneNotHigh = -3, + TransferError = -4, + CommunicationError = -5, + InvalidBitstream = -6, + FileError = -7, + DeviceNotOpen = -8, + InvalidEndpoint = -9, + InvalidBlockSize = -10, + I2CRestrictedAddress = -11, + I2CBitError = -12, + I2CNack = -13, + I2CUnknownStatus = -14, + UnsupportedFeature = -15 + }; +private: + bool to_bool(Bool x); + Bool from_bool(bool x); +public: + okCFrontPanel(); + ~okCFrontPanel(); + int GetHostInterfaceWidth(); + BoardModel GetBoardModel(); + std::string GetBoardModelString(BoardModel m); + int GetDeviceCount(); + BoardModel GetDeviceListModel(int num); + std::string GetDeviceListSerial(int num); + void EnableAsynchronousTransfers(bool enable); + ErrorCode OpenBySerial(std::string str = ""); + bool IsOpen(); + int GetDeviceMajorVersion(); + int GetDeviceMinorVersion(); + std::string GetSerialNumber(); + std::string GetDeviceID(); + void SetDeviceID(const std::string str); + ErrorCode SetBTPipePollingInterval(int interval); + void SetTimeout(int timeout); + ErrorCode ResetFPGA(); + ErrorCode ConfigureFPGAFromMemory(unsigned char *data, const unsigned long length, + void(*callback)(int, int, void *) = NULL, void *arg = NULL); + ErrorCode ConfigureFPGA(const std::string strFilename, + void (*callback)(int, int, void *) = NULL, void *arg = NULL); + ErrorCode WriteI2C(const int addr, int length, unsigned char *data); + ErrorCode ReadI2C(const int addr, int length, unsigned char *data); + ErrorCode GetPLL22150Configuration(okCPLL22150& pll); + ErrorCode SetPLL22150Configuration(okCPLL22150& pll); + ErrorCode GetEepromPLL22150Configuration(okCPLL22150& pll); + ErrorCode SetEepromPLL22150Configuration(okCPLL22150& pll); + ErrorCode GetPLL22393Configuration(okCPLL22393& pll); + ErrorCode SetPLL22393Configuration(okCPLL22393& pll); + ErrorCode GetEepromPLL22393Configuration(okCPLL22393& pll); + ErrorCode SetEepromPLL22393Configuration(okCPLL22393& pll); + ErrorCode LoadDefaultPLLConfiguration(); + bool IsHighSpeed(); + bool IsFrontPanelEnabled(); + bool IsFrontPanel3Supported(); + void UpdateWireIns(); + ErrorCode SetWireInValue(int ep, UINT32 val, UINT32 mask = 0xffffffff); + void UpdateWireOuts(); + UINT32 GetWireOutValue(int epAddr); + ErrorCode ActivateTriggerIn(int epAddr, int bit); + void UpdateTriggerOuts(); + bool IsTriggered(int epAddr, UINT32 mask); + long GetLastTransferLength(); + long WriteToPipeIn(int epAddr, long length, unsigned char *data); + long ReadFromPipeOut(int epAddr, long length, unsigned char *data); + long WriteToBlockPipeIn(int epAddr, int blockSize, long length, unsigned char *data); + long ReadFromBlockPipeOut(int epAddr, int blockSize, long length, unsigned char *data); +}; +#endif // !defined(FRONTPANELDLL_EXPORTS) + +} +#endif // __cplusplus + +#endif // __okFrontPanelDLL_h__ diff --git a/Source/Processors/DisplayNode.cpp b/Source/Processors/DisplayNode.cpp new file mode 100644 index 0000000000000000000000000000000000000000..19ff8f9812ae5d16fa1bde870c65d15671541b88 --- /dev/null +++ b/Source/Processors/DisplayNode.cpp @@ -0,0 +1,44 @@ +/* + ============================================================================== + + DisplayNode.cpp + Created: 7 May 2011 5:07:43pm + Author: jsiegle + + ============================================================================== +*/ + + +#include "DisplayNode.h" +#include "ResamplingNode.h" +#include <stdio.h> + +DisplayNode::DisplayNode() + : GenericProcessor("Display Node") + +{ + +} + +DisplayNode::~DisplayNode() +{ +} + +AudioProcessorEditor* DisplayNode::createEditor() +{ + + Visualizer* visualizer = new Visualizer(this, viewport, dataViewport); + + GenericProcessor* source = (GenericProcessor*) getSourceNode(); + + + visualizer->setBuffers(source->getContinuousBuffer(),source->getEventBuffer()); + visualizer->setUIComponent(getUIComponent()); + visualizer->setConfiguration(config); + + setEditor(visualizer); + + std::cout << "Creating visualizer." << std::endl; + return visualizer; + +} diff --git a/Source/Processors/DisplayNode.h b/Source/Processors/DisplayNode.h new file mode 100644 index 0000000000000000000000000000000000000000..031700d4b72d01beec2ccc3fecb01e95014b9af3 --- /dev/null +++ b/Source/Processors/DisplayNode.h @@ -0,0 +1,48 @@ +/* + ============================================================================== + + DisplayNode.h + Created: 7 May 2011 5:07:43pm + Author: jsiegle + + ============================================================================== +*/ + +#ifndef __DISPLAYNODE_H_C7B28CA4__ +#define __DISPLAYNODE_H_C7B28CA4__ + + +#include "../../JuceLibraryCode/JuceHeader.h" +#include "Editors/Visualizer.h" +#include "GenericProcessor.h" + +class DataViewport; + +class DisplayNode : public GenericProcessor + +{ +public: + // real member functions: + DisplayNode(); + ~DisplayNode(); + + AudioProcessorEditor* createEditor(); + + bool isSink() {return true;} + + void setDestNode(GenericProcessor* sn) {destNode = 0;} + + void process(AudioSampleBuffer &buffer, MidiBuffer &midiMessages, int& nSamples) {} + + +private: + + DataViewport* dataViewport; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DisplayNode); + +}; + + + +#endif // __DISPLAYNODE_H_C7B28CA4__ diff --git a/Source/Processors/Editors/AudioEditor.cpp b/Source/Processors/Editors/AudioEditor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7580fb03d75a718bc079f42721d5ff3f72c65b6a --- /dev/null +++ b/Source/Processors/Editors/AudioEditor.cpp @@ -0,0 +1,153 @@ +/* + ============================================================================== + + AudioEditor.cpp + Created: 31 Jul 2011 10:28:36pm + Author: jsiegle + + ============================================================================== +*/ + + +#include "AudioEditor.h" + + +MuteButton::MuteButton() + : DrawableButton (T("Mute button"), DrawableButton::ImageFitted) +{ + DrawablePath normal, over, down; + + Path p; + p.addEllipse (0.0,0.0,20.0,20.0); + normal.setPath (p); + normal.setFill (Colours::lightgrey); + normal.setStrokeThickness (0.0f); + + over.setPath (p); + over.setFill (Colours::black); + over.setStrokeFill (Colours::black); + over.setStrokeThickness (5.0f); + + setImages (&normal, &over, &over); + setBackgroundColours(Colours::darkgrey, Colours::purple); + setClickingTogglesState (true); + setTooltip ("Toggle a state."); + + + +} + +MuteButton::~MuteButton() +{ +} + +AudioEditor::AudioEditor (AudioNode* owner) + : AudioProcessorEditor (owner), isSelected(false), + desiredWidth(150) + +{ + name = getAudioProcessor()->getName(); + + nodeId = owner->getNodeId(); + + backgroundColor = Colours::lightgrey.withAlpha(0.5f); + + muteButton = new MuteButton(); + muteButton->addListener(this); + muteButton->setBounds(95,5,15,15); + muteButton->setToggleState(false,false); + addAndMakeVisible(muteButton); + +} + +AudioEditor::~AudioEditor() +{ + //std::cout << " Generic editor for " << getName() << " being deleted with " << getNumChildComponents() << " children. " << std::endl; + deleteAllChildren(); + //delete titleFont; +} + +//void GenericEditor::setTabbedComponent(TabbedComponent* tc) { + +// tabComponent = tc; + +//} + +bool AudioEditor::keyPressed (const KeyPress& key) +{ + //std::cout << name << " received " << key.getKeyCode() << std::endl; +} + +void AudioEditor::switchSelectedState() +{ + //std::cout << "Switching selected state" << std::endl; + isSelected = !isSelected; + repaint(); +} + +void AudioEditor::select() +{ + isSelected = true; + repaint(); + setWantsKeyboardFocus(true); + grabKeyboardFocus(); +} + +bool AudioEditor::getSelectionState() { + return isSelected; +} + +void AudioEditor::deselect() +{ + isSelected = false; + repaint(); + setWantsKeyboardFocus(false); +} + +void AudioEditor::buttonClicked(Button* button) +{ + if (button == muteButton) + { + + if(muteButton->getToggleState()) { + getAudioProcessor()->setParameter(1,0.0f); + std::cout << "Mute on." << std::endl; + } else { + getAudioProcessor()->setParameter(1,1.0f); + std::cout << "Mute off." << std::endl; + } + } + +} + +void AudioEditor::paint (Graphics& g) +{ + + //g.addTransform(AffineTransform::rotation( double_Pi/20)); + + // g.setColour(Colours::black); + // g.fillRoundedRectangle(0,0,getWidth(),getHeight(),10.0); + + // if (isSelected) { + g.setColour(backgroundColor); + // } else { + // g.setColour(Colours::lightgrey); + // } + g.fillRoundedRectangle(1,1,getWidth()-2,getHeight()-2,9.0); + + // g.setColour(Colours::grey); + // g.fillRoundedRectangle(4,15,getWidth()-8, getHeight()-19,8.0); + // g.fillRect(4,15,getWidth()-8, 20); + + + + g.setColour(Colours::black); + + Font titleFont = Font(14.0, Font::plain); + + // //titleFont.setTypefaceName(T("Miso")); + + g.setFont(titleFont); + g.drawText("Mute ON/OFF", 15, 10, 100, 7, Justification::left, false); + +} diff --git a/Source/Processors/Editors/AudioEditor.h b/Source/Processors/Editors/AudioEditor.h new file mode 100644 index 0000000000000000000000000000000000000000..e718ae3595b279df98918f468c1ee9cee73be2af --- /dev/null +++ b/Source/Processors/Editors/AudioEditor.h @@ -0,0 +1,69 @@ +/* + ============================================================================== + + AudioEditor.h + Created: 31 Jul 2011 10:28:36pm + Author: jsiegle + + ============================================================================== +*/ + +#ifndef __AUDIOEDITOR_H_9D6F1FC3__ +#define __AUDIOEDITOR_H_9D6F1FC3__ + + +#include "../../../JuceLibraryCode/JuceHeader.h" +#include "../AudioNode.h" +#include <stdio.h> + +class AudioNode; + +class MuteButton : public DrawableButton +{ + public: + MuteButton(); + ~MuteButton(); +}; + +class AudioEditor : public AudioProcessorEditor, + public Button::Listener + +{ +public: + AudioEditor (AudioNode* owner); + ~AudioEditor(); + + void paint (Graphics& g); + + bool keyPressed (const KeyPress& key); + + void switchSelectedState(); + void select(); + void deselect(); + bool getSelectionState(); + + String getName() {return name;} + + int desiredWidth; + int nodeId; + + AudioProcessor* getProcessor() const {return getAudioProcessor();} + +private: + + void buttonClicked (Button* button); + + Colour backgroundColor; + + MuteButton* muteButton; + + bool isSelected; + String name; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioEditor); + +}; + + + +#endif // __AUDIOEDITOR_H_9D6F1FC3__ diff --git a/Source/Processors/Editors/FilterEditor.cpp b/Source/Processors/Editors/FilterEditor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cde3bbf5d7f3523ffa6f6abcc6c71d66800694c5 --- /dev/null +++ b/Source/Processors/Editors/FilterEditor.cpp @@ -0,0 +1,49 @@ +/* + ============================================================================== + + FilterEditor.cpp + Created: 12 Jun 2011 5:04:08pm + Author: jsiegle + + ============================================================================== +*/ + +#include "FilterEditor.h" +#include "../FilterNode.h" +#include <stdio.h> + + +FilterEditor::FilterEditor (GenericProcessor* parentNode, FilterViewport* vp) + : GenericEditor(parentNode, vp), lowSlider(0), highSlider(0) + +{ + desiredWidth = 250; + + lowSlider = new Slider (T("Low-Cut Slider")); + lowSlider->setBounds(25,20,200,40); + lowSlider->setRange(10,600,10); + lowSlider->addListener(this); + addAndMakeVisible(lowSlider); + + highSlider = new Slider (T("High-Cut Slider")); + highSlider->setBounds(25,65,200,40); + highSlider->setRange(1000,10000,500); + highSlider->addListener(this); + addAndMakeVisible(highSlider); + +} + +FilterEditor::~FilterEditor() +{ + deleteAllChildren(); +} + +void FilterEditor::sliderValueChanged (Slider* slider) +{ + + if (slider == lowSlider) + getAudioProcessor()->setParameter(0,slider->getValue()); + else + getAudioProcessor()->setParameter(1,slider->getValue()); + +} \ No newline at end of file diff --git a/Source/Processors/Editors/FilterEditor.h b/Source/Processors/Editors/FilterEditor.h new file mode 100644 index 0000000000000000000000000000000000000000..46881778ced7f6f39a248e2a00ceb0b7ad8d7de4 --- /dev/null +++ b/Source/Processors/Editors/FilterEditor.h @@ -0,0 +1,38 @@ +/* + ============================================================================== + + FilterEditor.h + Created: 12 Jun 2011 5:04:08pm + Author: jsiegle + + ============================================================================== +*/ + +#ifndef __FILTEREDITOR_H_969BDB5__ +#define __FILTEREDITOR_H_969BDB5__ + + +#include "../../../JuceLibraryCode/JuceHeader.h" +#include "GenericEditor.h" + +class FilterViewport; + +class FilterEditor : public GenericEditor, + public Slider::Listener +{ +public: + FilterEditor (GenericProcessor* parentNode, FilterViewport* vp); + virtual ~FilterEditor(); + void sliderValueChanged (Slider* slider); + +private: + Slider* lowSlider; + Slider* highSlider; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FilterEditor); + +}; + + + +#endif // __FILTEREDITOR_H_969BDB5__ diff --git a/Source/Processors/Editors/GenericEditor.cpp b/Source/Processors/Editors/GenericEditor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fa21f87a93dc3e8a92117522160b8ee7958c246f --- /dev/null +++ b/Source/Processors/Editors/GenericEditor.cpp @@ -0,0 +1,175 @@ +/* + ============================================================================== + + GenericEditor.cpp + Created: 7 Jun 2011 11:37:12am + Author: jsiegle + + ============================================================================== +*/ + +#include "GenericEditor.h" + +GenericEditor::GenericEditor (GenericProcessor* owner, FilterViewport* vp) + : AudioProcessorEditor (owner), isSelected(false), viewport(vp), + desiredWidth(150), tNum(-1), isEnabled(true) + +{ + name = getAudioProcessor()->getName(); + + nodeId = owner->getNodeId(); + + Random r = Random(99); + r.setSeedRandomly(); + + //titleFont = new Font(14.0, Font::plain); + + //titleFont->setTypefaceName(T("Miso")); + + backgroundColor = Colour(3, 143, 255); + +} + +GenericEditor::~GenericEditor() +{ + //std::cout << " Generic editor for " << getName() << " being deleted with " << getNumChildComponents() << " children. " << std::endl; + deleteAllChildren(); + //delete titleFont; +} + +void GenericEditor::setViewport(FilterViewport* vp) { + + viewport = vp; + +} + +//void GenericEditor::setTabbedComponent(TabbedComponent* tc) { + +// tabComponent = tc; + +//} + +bool GenericEditor::keyPressed (const KeyPress& key) +{ + //std::cout << name << " received " << key.getKeyCode() << std::endl; + + if (key.getKeyCode() == key.deleteKey || key.getKeyCode() == key.backspaceKey) { + + //std::cout << name << " should be deleted." << std::endl; + viewport->deleteNode(this); + + } else if (key.getKeyCode() == key.leftKey || key.getKeyCode() == key.rightKey) { + + viewport->moveSelection(key); + + } +} + +void GenericEditor::switchSelectedState() +{ + //std::cout << "Switching selected state" << std::endl; + isSelected = !isSelected; + repaint(); +} + +void GenericEditor::select() +{ + isSelected = true; + repaint(); + setWantsKeyboardFocus(true); + grabKeyboardFocus(); +} + +bool GenericEditor::getSelectionState() { + return isSelected; +} + +void GenericEditor::deselect() +{ + isSelected = false; + repaint(); + setWantsKeyboardFocus(false); +} + +void GenericEditor::enable() +{ + isEnabled = true; + GenericProcessor* p = (GenericProcessor*) getProcessor(); + p->enabledState(true); +} + +void GenericEditor::disable() +{ + isEnabled = false; + GenericProcessor* p = (GenericProcessor*) getProcessor(); + p->enabledState(false); +} + +bool GenericEditor::getEnabledState() +{ + return isEnabled; +} + +void GenericEditor::setEnabledState(bool t) +{ + isEnabled = t; + GenericProcessor* p = (GenericProcessor*) getProcessor(); + p->enabledState(isEnabled); +} + +void GenericEditor::paint (Graphics& g) +{ + int offset = 0; + + GenericProcessor* p = (GenericProcessor*) getProcessor(); + + g.setColour(Colour(127,137,147)); + g.fillAll(); + + if (isSelected) + g.setColour(Colours::yellow); + else + g.setColour(Colours::darkgrey); + + + g.fillRoundedRectangle(0,0,getWidth()-offset,getHeight(),7.0); + + + if (isEnabled) + g.setColour(backgroundColor); + else + g.setColour(Colours::lightgrey); + + // if (p->isSource()) { + // g.setColour(Colours::red); + // } else if (p->isSink()) { + // g.setColour(Colours::blue); + // } else if (p->isSplitter() || p->isMerger()) + // { + // g.setColour(Colours::darkgrey); + // } else { + // g.setColour(Colours::red); + // } + + g.fillRoundedRectangle(1,1,getWidth()-(2+offset),getHeight()-2,6.0); + + g.setColour(Colours::grey); + g.fillRoundedRectangle(4,15,getWidth()-(8+offset), getHeight()-19,5.0); + g.fillRect(4,15,getWidth()-(8+offset), 20); + + Font titleFont = Font(14.0, Font::plain); + + //titleFont.setTypefaceName(T("Miso")); + + g.setFont(titleFont); + + if (isEnabled) + { + g.setColour(Colours::black); + } else { + g.setColour(Colours::grey); + } + + g.drawText(name, 8, 4, 100, 7, Justification::left, false); + +} diff --git a/Source/Processors/Editors/GenericEditor.h b/Source/Processors/Editors/GenericEditor.h new file mode 100644 index 0000000000000000000000000000000000000000..6897b7ddd5d648e5a335b867a433939c5344c8e1 --- /dev/null +++ b/Source/Processors/Editors/GenericEditor.h @@ -0,0 +1,81 @@ +/* + ============================================================================== + + GenericEditor.h + Created: 7 Jun 2011 11:37:12am + Author: jsiegle + + ============================================================================== +*/ + +#ifndef __GENERICEDITOR_H_DD406E71__ +#define __GENERICEDITOR_H_DD406E71__ + + +#include "../../../JuceLibraryCode/JuceHeader.h" +#include "../GenericProcessor.h" +#include "../../UI/FilterViewport.h" +#include "../../UI/Configuration.h" +#include <stdio.h> + +class GenericProcessor; +class FilterViewport; + +class GenericEditor : public AudioProcessorEditor + +{ +public: + GenericEditor (GenericProcessor* owner, FilterViewport* vp); + virtual ~GenericEditor(); + + void paint (Graphics& g); + void setViewport(FilterViewport*); + //void setTabbedComponent(TabbedComponent*); + + bool keyPressed (const KeyPress& key); + + void switchSelectedState(); + void select(); + void deselect(); + bool getSelectionState(); + + void enable(); + void disable(); + bool getEnabledState(); + void setEnabledState(bool); + + String getName() {return name;} + + int desiredWidth; + int nodeId; + + void tabNumber(int t) {tNum = t;} + int tabNumber() {return tNum;} + + FilterViewport* viewport; + Configuration* config; + + void setConfiguration(Configuration* cf) {config = cf;} + Configuration* getConfiguration() {return config;} + + AudioProcessor* getProcessor() const {return getAudioProcessor();} + +private: + + Colour backgroundColor; + + bool isSelected; + bool isEnabled; + + int tNum; + + String name; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GenericEditor); + +}; + + + + +#endif // __GENERICEDITOR_H_DD406E71__ diff --git a/Source/Processors/Editors/LfpDisplayEditor.cpp b/Source/Processors/Editors/LfpDisplayEditor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..18078c400227965201256e453d19d4144459af33 --- /dev/null +++ b/Source/Processors/Editors/LfpDisplayEditor.cpp @@ -0,0 +1,228 @@ +/* + ============================================================================== + + LfpDisplayEditor.cpp + Created: 8 Feb 2012 12:56:43pm + Author: jsiegle + + ============================================================================== +*/ + + +#include "LfpDisplayEditor.h" +#include "../Visualization/LfpDisplayCanvas.h" + +SelectorButton::SelectorButton() + : DrawableButton (T("Selector"), DrawableButton::ImageFitted) +{ + DrawablePath normal, over, down; + + Path p; + p.addEllipse (0.0,0.0,20.0,20.0); + normal.setPath (p); + normal.setFill (Colours::lightgrey); + normal.setStrokeThickness (0.0f); + + over.setPath (p); + over.setFill (Colours::black); + over.setStrokeFill (Colours::black); + over.setStrokeThickness (5.0f); + + setImages (&normal, &over, &over); + setBackgroundColours(Colours::darkgrey, Colours::green); + setClickingTogglesState (true); + setTooltip ("Toggle a state."); + +} + +SelectorButton::~SelectorButton() +{ +} + + +LfpDisplayEditor::LfpDisplayEditor (GenericProcessor* parentNode, + FilterViewport* vp, + DataViewport* dv) + : GenericEditor(parentNode, vp), dataViewport(dv), + tabIndex(-1), dataWindow(0), + streamBuffer(0), eventBuffer(0), canvas(0), + isPlaying(false) + +{ + desiredWidth = 275; + + timebaseSlider = new Slider (T("Time Base Slider")); + timebaseSlider->setBounds(60,20,200,40); + timebaseSlider->setRange(500,10000,500); + timebaseSlider->addListener(this); + addAndMakeVisible(timebaseSlider); + + displayGainSlider = new Slider (T("Display Gain Slider")); + displayGainSlider->setBounds(60,65,200,40); + displayGainSlider->setRange(1,8,1); + displayGainSlider->addListener(this); + addAndMakeVisible(displayGainSlider); + + windowSelector = new SelectorButton(); + windowSelector->addListener(this); + windowSelector->setBounds(25,25,20,20); + windowSelector->setToggleState(false,false); + addAndMakeVisible(windowSelector); + + tabSelector = new SelectorButton(); + tabSelector->addListener(this); + tabSelector->setBounds(25,50,20,20); + + addAndMakeVisible(tabSelector); + tabSelector->setToggleState(false,false); + + +} + +LfpDisplayEditor::~LfpDisplayEditor() +{ + + if (tabIndex > -1) + { + dataViewport->removeTab(tabIndex); + } + + deleteAllChildren(); + +} + +void LfpDisplayEditor::enable() +{ + if (canvas != 0) + canvas->beginAnimation(); + + isPlaying = true; +} + +void LfpDisplayEditor::disable() +{ + if (canvas != 0) + canvas->endAnimation(); + + isPlaying = false; +} + +void LfpDisplayEditor::updateNumInputs(int n) +{ + if (canvas != 0) + canvas->updateNumInputs(n); +} + +void LfpDisplayEditor::updateSampleRate(float r) +{ + if (canvas != 0) + canvas->updateSampleRate(r); +} + +void LfpDisplayEditor::setBuffers(AudioSampleBuffer* asb, MidiBuffer* mb) +{ + std::cout << "Buffers are set!" << std::endl; + streamBuffer = asb; + eventBuffer = mb; + + std::cout << streamBuffer << std::endl; + std::cout << eventBuffer << std::endl; +} + +void LfpDisplayEditor::buttonClicked(Button* button) +{ + + if (canvas == 0) { + canvas = new LfpDisplayCanvas((LfpDisplayNode*) getProcessor()); + if (isPlaying) + canvas->beginAnimation(); + } + + if (button == windowSelector) + { + if (dataWindow == 0) { + + dataWindow = new DataWindow(windowSelector); + + //if (canvas == 0) + // canvas = new LfpDisplayCanvas((LfpDisplayNode*) getProcessor()); + + //dataWindow->setContentComponent(new LfpDisplayCanvas(streamBuffer,eventBuffer,getConfiguration(), this)); + + if (tabSelector->getToggleState()) + { + tabSelector->setToggleState(false, false); + dataViewport->removeTab(tabIndex); + tabIndex = -1; + } + + //LfpDisplayNode* p = (LfpDisplayNode*) getProcessor(); + + dataWindow->setContentNonOwned(canvas, false); + //p->isVisible = true; + + //getProcessor()->parentComponentChanged(); + dataWindow->setVisible(true); + + } else { + + if (tabSelector->getToggleState()) + { + tabSelector->setToggleState(false, false); + dataViewport->removeTab(tabIndex); + tabIndex = -1; + } + + dataWindow->setVisible(windowSelector->getToggleState()); + + //LfpDisplayNode* p = (LfpDisplayNode*) getProcessor(); + //p->isVisible = windowSelector->getToggleState(); + //getProcessor()->parentComponentChanged(); + } + + } else if (button == tabSelector) + { + if (tabSelector->getToggleState() && tabIndex < 0) + { + + //std::cout << "Editor data viewport: " << dataViewport << std::endl; + + if (windowSelector->getToggleState()) + { + windowSelector->setToggleState(false, false); + dataWindow->setVisible(false); + } + + //tabIndex = dataViewport->addTabToDataViewport("LFP",new LfpDisplayCanvas(streamBuffer,eventBuffer,getConfiguration(), this)); + //Component* p = (Component*) getProcessor(); + + //LfpDisplayNode* p = (LfpDisplayNode*) getProcessor(); + tabIndex = dataViewport->addTabToDataViewport("LFP",canvas); + //p->isVisible = true; + + } else if (!tabSelector->getToggleState() && tabIndex > -1) + { + dataViewport->removeTab(tabIndex); + tabIndex = -1; + //LfpDisplayNode* p = (LfpDisplayNode*) getProcessor(); + //p->isVisible = false; + } + } +} + +void LfpDisplayEditor::sliderValueChanged (Slider* slider) +{ + + if (canvas != 0) + { + if (slider == timebaseSlider) + canvas->setParameter(0,slider->getValue()); + else + canvas->setParameter(1,slider->getValue()); + } + + + //else + // getAudioProcessor()->setParameter(1,slider->getValue()); + +} \ No newline at end of file diff --git a/Source/Processors/Editors/LfpDisplayEditor.h b/Source/Processors/Editors/LfpDisplayEditor.h new file mode 100644 index 0000000000000000000000000000000000000000..549e9e2cbbb5cd341b1be87e8c31e4aed31352ee --- /dev/null +++ b/Source/Processors/Editors/LfpDisplayEditor.h @@ -0,0 +1,79 @@ +/* + ============================================================================== + + LfpDisplayEditor.h + Created: 8 Feb 2012 12:56:43pm + Author: jsiegle + + ============================================================================== +*/ + +#ifndef __LFPDISPLAYEDITOR_H_3438800D__ +#define __LFPDISPLAYEDITOR_H_3438800D__ + +#include "../../../JuceLibraryCode/JuceHeader.h" +#include "GenericEditor.h" +#include "../../UI/UIComponent.h" +#include "../../UI/DataViewport.h" +#include "../Visualization/DataWindow.h" +#include "../LfpDisplayNode.h" +#include "../Visualization/LfpDisplayCanvas.h" + +class FilterViewport; +class DataViewport; +class DataWindow; +class LfpDisplayCanvas; + +class SelectorButton : public DrawableButton +{ + public: + SelectorButton(); + ~SelectorButton(); +}; + +class LfpDisplayEditor : public GenericEditor, + public Button::Listener, + public Slider::Listener +{ +public: + LfpDisplayEditor (GenericProcessor*, FilterViewport*, DataViewport*); + ~LfpDisplayEditor(); + + void buttonClicked (Button* button); + void setBuffers (AudioSampleBuffer*, MidiBuffer*); + void setUIComponent (UIComponent* ui) {UI = ui;} + + void sliderValueChanged (Slider* slider); + + void enable(); + void disable(); + + void updateNumInputs(int); + void updateSampleRate(float); + +private: + + bool isPlaying; + + ScopedPointer <DataWindow> dataWindow; + + Slider* timebaseSlider; + Slider* displayGainSlider; + + SelectorButton* windowSelector; + SelectorButton* tabSelector; + + ScopedPointer <LfpDisplayCanvas> canvas; + + AudioSampleBuffer* streamBuffer; + MidiBuffer* eventBuffer; + UIComponent* UI; + DataViewport* dataViewport; + + int tabIndex; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LfpDisplayEditor); + +}; + +#endif // __LFPDISPLAYEDITOR_H_3438800D__ diff --git a/Source/Processors/Editors/SignalGeneratorEditor.cpp b/Source/Processors/Editors/SignalGeneratorEditor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5d72f28a7b4a91b052bc0136b93d0414930f404b --- /dev/null +++ b/Source/Processors/Editors/SignalGeneratorEditor.cpp @@ -0,0 +1,50 @@ +/* + ============================================================================== + + SignalGeneratorEditor.cpp + Created: 10 Feb 2012 1:28:45pm + Author: jsiegle + + ============================================================================== +*/ + + +#include "SignalGeneratorEditor.h" +#include "../SignalGenerator.h" +#include <stdio.h> + + +SignalGeneratorEditor::SignalGeneratorEditor (GenericProcessor* parentNode, FilterViewport* vp) + : GenericEditor(parentNode, vp), amplitudeSlider(0), frequencySlider(0) + +{ + desiredWidth = 250; + + amplitudeSlider = new Slider (T("Amplitude Slider")); + amplitudeSlider->setBounds(25,20,200,40); + amplitudeSlider->setRange(0.005,0.05,0.005); + amplitudeSlider->addListener(this); + addAndMakeVisible(amplitudeSlider); + + frequencySlider = new Slider (T("High-Cut Slider")); + frequencySlider->setBounds(25,65,200,40); + frequencySlider->setRange(1,100,1); + frequencySlider->addListener(this); + addAndMakeVisible(frequencySlider); + +} + +SignalGeneratorEditor::~SignalGeneratorEditor() +{ + deleteAllChildren(); +} + +void SignalGeneratorEditor::sliderValueChanged (Slider* slider) +{ + + if (slider == amplitudeSlider) + getAudioProcessor()->setParameter(0,slider->getValue()); + else + getAudioProcessor()->setParameter(1,slider->getValue()); + +} \ No newline at end of file diff --git a/Source/Processors/Editors/SignalGeneratorEditor.h b/Source/Processors/Editors/SignalGeneratorEditor.h new file mode 100644 index 0000000000000000000000000000000000000000..1d495ca1af6632ca2d35fd996dfff1012bb38d74 --- /dev/null +++ b/Source/Processors/Editors/SignalGeneratorEditor.h @@ -0,0 +1,36 @@ +/* + ============================================================================== + + SignalGeneratorEditor.h + Created: 10 Feb 2012 1:28:45pm + Author: jsiegle + + ============================================================================== +*/ + +#ifndef __SIGNALGENERATOREDITOR_H_841A7078__ +#define __SIGNALGENERATOREDITOR_H_841A7078__ + +#include "../../../JuceLibraryCode/JuceHeader.h" +#include "GenericEditor.h" + +class FilterViewport; + +class SignalGeneratorEditor : public GenericEditor, + public Slider::Listener +{ +public: + SignalGeneratorEditor (GenericProcessor* parentNode, FilterViewport* vp); + virtual ~SignalGeneratorEditor(); + void sliderValueChanged (Slider* slider); + +private: + Slider* amplitudeSlider; + Slider* frequencySlider; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SignalGeneratorEditor); + +}; + + +#endif // __SIGNALGENERATOREDITOR_H_841A7078__ diff --git a/Source/Processors/Editors/SourceNodeEditor.cpp b/Source/Processors/Editors/SourceNodeEditor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e39ff44cf7425c270c009c5939398bb2eeab037e --- /dev/null +++ b/Source/Processors/Editors/SourceNodeEditor.cpp @@ -0,0 +1,48 @@ +/* + ============================================================================== + + SourceNodeEditor.cpp + Created: 7 Sep 2011 5:08:31pm + Author: jsiegle + + ============================================================================== +*/ + + +#include "SourceNodeEditor.h" +#include "../SourceNode.h" +#include <stdio.h> + + +SourceNodeEditor::SourceNodeEditor (GenericProcessor* parentNode, FilterViewport* vp) + : GenericEditor(parentNode, vp) + +{ + desiredWidth = 125; + + Image im; + + std::cout << getName() << std::endl; + + if (getName().equalsIgnoreCase("Data Sources/Intan Demo Board")) + { + im = ImageCache::getFromMemory (BinaryData::IntanIcon_png, + BinaryData::IntanIcon_pngSize); + } else if (getName().equalsIgnoreCase("Data Sources/File Reader")) { + im = ImageCache::getFromMemory (BinaryData::FileReaderIcon_png, + BinaryData::FileReaderIcon_pngSize); + } else { + im = ImageCache::getFromMemory (BinaryData::DefaultDataSource_png, + BinaryData::DefaultDataSource_pngSize); + } + + icon = new ImageIcon(im); + icon->setBounds(30,25,70,70); + addAndMakeVisible(icon); + +} + +SourceNodeEditor::~SourceNodeEditor() +{ + deleteAllChildren(); +} diff --git a/Source/Processors/Editors/SourceNodeEditor.h b/Source/Processors/Editors/SourceNodeEditor.h new file mode 100644 index 0000000000000000000000000000000000000000..46b7368acf9fa7e55cca379ab7e7577b2245e8db --- /dev/null +++ b/Source/Processors/Editors/SourceNodeEditor.h @@ -0,0 +1,59 @@ +/* + ============================================================================== + + SourceNodeEditor.h + Created: 7 Sep 2011 5:08:31pm + Author: jsiegle + + ============================================================================== +*/ + +#ifndef __SOURCENODEEDITOR_H_A1B19E1E__ +#define __SOURCENODEEDITOR_H_A1B19E1E__ + +#include "../../../JuceLibraryCode/JuceHeader.h" +#include "GenericEditor.h" + +class FilterViewport; + +class ImageIcon : public Component +{ +public: + ImageIcon (Image& image_) { image = image_;} + ~ImageIcon () {} + + +private: + + void paint (Graphics& g) + { + g.drawImageWithin(image, // image& + 0, // destX + 0, // destY + getWidth(), // destWidth + getHeight(), // destHeight + RectanglePlacement::xLeft); + } + + Image image; + +}; + +class SourceNodeEditor : public GenericEditor + +{ +public: + SourceNodeEditor (GenericProcessor* parentNode, FilterViewport* vp); + virtual ~SourceNodeEditor(); + +private: + + ImageIcon* icon; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SourceNodeEditor); + +}; + + + +#endif // __SOURCENODEEDITOR_H_A1B19E1E__ diff --git a/Source/Processors/Editors/SpikeDetectorEditor.cpp b/Source/Processors/Editors/SpikeDetectorEditor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9dccfe89c76feac679d799e6ea133ac49a4e320e --- /dev/null +++ b/Source/Processors/Editors/SpikeDetectorEditor.cpp @@ -0,0 +1,44 @@ +/* + ============================================================================== + + SpikeDetectorEditor.cpp + Created: 14 Aug 2011 6:27:19pm + Author: jsiegle + + ============================================================================== +*/ + + +#include "SpikeDetectorEditor.h" +#include "../SpikeDetector.h" +#include <stdio.h> + + +SpikeDetectorEditor::SpikeDetectorEditor (GenericProcessor* parentNode, FilterViewport* vp) + : GenericEditor(parentNode, vp), threshSlider(0) + +{ + desiredWidth = 200; + + threshSlider = new Slider (T("Threshold Slider")); + threshSlider->setBounds(25,20,150,40); + threshSlider->setRange(1000,20000,1000); + threshSlider->addListener(this); + addAndMakeVisible(threshSlider); + +} + +SpikeDetectorEditor::~SpikeDetectorEditor() +{ + + deleteAllChildren(); + +} + +void SpikeDetectorEditor::sliderValueChanged (Slider* slider) +{ + + if (slider == threshSlider) + getAudioProcessor()->setParameter(0,slider->getValue()); + +} \ No newline at end of file diff --git a/Source/Processors/Editors/SpikeDetectorEditor.h b/Source/Processors/Editors/SpikeDetectorEditor.h new file mode 100644 index 0000000000000000000000000000000000000000..7946040b9b32b1cb8c62c175f6066c6a874a0c5f --- /dev/null +++ b/Source/Processors/Editors/SpikeDetectorEditor.h @@ -0,0 +1,41 @@ +/* + ============================================================================== + + SpikeDetectorEditor.h + Created: 14 Aug 2011 6:27:19pm + Author: jsiegle + + ============================================================================== +*/ + +#ifndef __SPIKEDETECTOREDITOR_H_F0BD2DD9__ +#define __SPIKEDETECTOREDITOR_H_F0BD2DD9__ + + +#include "../../../JuceLibraryCode/JuceHeader.h" +#include "GenericEditor.h" + +class FilterViewport; + +class SpikeDetectorEditor : public GenericEditor, + public Slider::Listener +{ +public: + SpikeDetectorEditor (GenericProcessor* parentNode, FilterViewport* vp); + virtual ~SpikeDetectorEditor(); + void sliderValueChanged (Slider* slider); + +private: + Slider* threshSlider; + DocumentWindow* docWindow; + + //int tabIndex; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SpikeDetectorEditor); + +}; + + + + +#endif // __SPIKEDETECTOREDITOR_H_F0BD2DD9__ diff --git a/Source/Processors/Editors/SplitterEditor.cpp b/Source/Processors/Editors/SplitterEditor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d1bc5d652c66d00bd06cc5d551895bc93c8164b4 --- /dev/null +++ b/Source/Processors/Editors/SplitterEditor.cpp @@ -0,0 +1,101 @@ +/* + ============================================================================== + + SplitterEditor.cpp + Created: 5 Sep 2011 2:18:10pm + Author: jsiegle + + ============================================================================== +*/ + +#include "SplitterEditor.h" +#include "../Utilities/Splitter.h" + +// PipelineSelectorButton::PipelineSelectorButton() +// : DrawableButton (T("Selector"), DrawableButton::ImageFitted) +// { +// DrawablePath normal, over, down; + +// Path p; +// p.addTriangle (0.0f, 0.0f, 0.0f, 20.0f, 18.0f, 10.0f); +// normal.setPath (p); +// normal.setFill (Colours::lightgrey); +// normal.setStrokeThickness (0.0f); + +// over.setPath (p); +// over.setFill (Colours::black); +// over.setStrokeFill (Colours::black); +// over.setStrokeThickness (5.0f); + +// setImages (&normal, &over, &over); +// setBackgroundColours(Colours::darkgrey, Colours::purple); +// setClickingTogglesState (true); +// setTooltip ("Toggle a state."); + +// } + +// PipelineSelectorButton::~PipelineSelectorButton() +// { +// } + +SplitterEditor::SplitterEditor (GenericProcessor* parentNode, FilterViewport* vp) + : GenericEditor(parentNode, vp) + +{ + desiredWidth = 90; + + pipelineSelectorA = new ImageButton("Pipeline A"); + + Image normalImageA = ImageCache::getFromMemory (BinaryData::PipelineB01_png, BinaryData::PipelineB01_pngSize); + Image downImageA = ImageCache::getFromMemory (BinaryData::PipelineA01_png, BinaryData::PipelineA01_pngSize); + Image normalImageB = ImageCache::getFromMemory (BinaryData::PipelineA02_png, BinaryData::PipelineA02_pngSize); + Image downImageB = ImageCache::getFromMemory (BinaryData::PipelineB02_png, BinaryData::PipelineB02_pngSize); + + pipelineSelectorA->setImages(true, true, true, + normalImageA, 1.0f, Colours::white.withAlpha(0.0f), + normalImageA, 1.0f, Colours::black.withAlpha(0.0f), + downImageA, 1.0f, Colours::white.withAlpha(0.0f)); + + + pipelineSelectorA->addListener(this); + pipelineSelectorA->setBounds(-10,15,95,50); + pipelineSelectorA->setToggleState(true,false); + addAndMakeVisible(pipelineSelectorA); + + pipelineSelectorB = new ImageButton("Pipeline B"); + + pipelineSelectorB->setImages(true, true, true, + normalImageB, 1.0f, Colours::white.withAlpha(0.0f), + normalImageB, 1.0f, Colours::black.withAlpha(0.0f), + downImageB, 1.0f, Colours::white.withAlpha(0.0f)); + + pipelineSelectorB->addListener(this); + pipelineSelectorB->setBounds(-10,65,95,50); + pipelineSelectorB->setToggleState(false,false); + addAndMakeVisible(pipelineSelectorB); + +} + +SplitterEditor::~SplitterEditor() +{ + deleteAllChildren(); +} + +void SplitterEditor::buttonClicked(Button* button) +{ + if (button == pipelineSelectorA) + { + pipelineSelectorA->setToggleState(true,false); + pipelineSelectorB->setToggleState(false,false); + Splitter* processor = (Splitter*) getProcessor(); + processor->switchDest(0); + + } else if (button == pipelineSelectorB) + { + pipelineSelectorB->setToggleState(true,false); + pipelineSelectorA->setToggleState(false,false); + Splitter* processor = (Splitter*) getProcessor(); + processor->switchDest(1); + + } +} \ No newline at end of file diff --git a/Source/Processors/Editors/SplitterEditor.h b/Source/Processors/Editors/SplitterEditor.h new file mode 100644 index 0000000000000000000000000000000000000000..b4e9d8e4b0fc895f789df8d006deac46541ae08a --- /dev/null +++ b/Source/Processors/Editors/SplitterEditor.h @@ -0,0 +1,46 @@ +/* + ============================================================================== + + SplitterEditor.h + Created: 5 Sep 2011 2:18:10pm + Author: jsiegle + + ============================================================================== +*/ + +#ifndef __SPLITTEREDITOR_H_33F644A8__ +#define __SPLITTEREDITOR_H_33F644A8__ + + +#include "../../../JuceLibraryCode/JuceHeader.h" +#include "GenericEditor.h" + +class FilterViewport; + +// class PipelineSelectorButton : public DrawableButton +// { +// public: +// PipelineSelectorButton(); +// ~PipelineSelectorButton(); +// }; + +class SplitterEditor : public GenericEditor, + public Button::Listener +{ +public: + SplitterEditor (GenericProcessor* parentNode, FilterViewport* vp); + virtual ~SplitterEditor(); + + void buttonClicked (Button* button); + +private: + + ImageButton* pipelineSelectorA; + ImageButton* pipelineSelectorB; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SplitterEditor); + +}; + + +#endif // __SPLITTEREDITOR_H_33F644A8__ diff --git a/Source/Processors/Editors/Visualizer.cpp b/Source/Processors/Editors/Visualizer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e464a89a6623729899ff7f4cb5e8b5f05731516f --- /dev/null +++ b/Source/Processors/Editors/Visualizer.cpp @@ -0,0 +1,168 @@ + +// ============================================================================== + +// Visualizer.cpp +// Created: 15 Jul 2011 8:42:01pm +// Author: jsiegle + +// ============================================================================== + + +// #include "Visualizer.h" +// #include "../Visualization/SpikeViewer.h" +// #include "../Visualization/LfpViewer.h" + +// SelectorButton::SelectorButton() +// : DrawableButton (T("Selector"), DrawableButton::ImageFitted) +// { +// DrawablePath normal, over, down; + +// Path p; +// p.addEllipse (0.0,0.0,20.0,20.0); +// normal.setPath (p); +// normal.setFill (Colours::lightgrey); +// normal.setStrokeThickness (0.0f); + +// over.setPath (p); +// over.setFill (Colours::black); +// over.setStrokeFill (Colours::black); +// over.setStrokeThickness (5.0f); + +// setImages (&normal, &over, &over); +// setBackgroundColours(Colours::darkgrey, Colours::green); +// setClickingTogglesState (true); +// setTooltip ("Toggle a state."); + +// } + +// SelectorButton::~SelectorButton() +// { +// } + + +// Visualizer::Visualizer (GenericProcessor* parentNode, +// FilterViewport* vp, +// DataViewport* dv) +// : GenericEditor(parentNode, vp), dataViewport(dv), +// tabIndex(-1), dataWindow(0), +// streamBuffer(0), eventBuffer(0) + +// { +// desiredWidth = 210; + +// windowSelector = new SelectorButton(); +// windowSelector->addListener(this); +// windowSelector->setBounds(25,25,20,20); +// windowSelector->setToggleState(false,false); +// addAndMakeVisible(windowSelector); + +// tabSelector = new SelectorButton(); +// tabSelector->addListener(this); +// tabSelector->setBounds(25,50,20,20); +// tabSelector->setToggleState(false,false); +// addAndMakeVisible(tabSelector); + +// } + +// Visualizer::~Visualizer() +// { + +// if (tabIndex > -1) +// { +// dataViewport->removeTab(tabIndex); +// } + +// deleteAllChildren(); + +// } + +// void Visualizer::setBuffers(AudioSampleBuffer* asb, MidiBuffer* mb) +// { +// std::cout << "Buffers are set!" << std::endl; +// streamBuffer = asb; +// eventBuffer = mb; + + +// std::cout << streamBuffer << std::endl; +// std::cout << eventBuffer << std::endl; +// } + +// void Visualizer::buttonClicked(Button* button) +// { +// if (button == windowSelector) +// { +// if (dataWindow == 0) { +// dataWindow = new DataWindow(windowSelector); + +// if (getName().equalsIgnoreCase("Visualizers/LFP Viewer")) +// dataWindow->setContentComponent(new LfpViewer(streamBuffer,eventBuffer,UI)); +// else if (getName().equalsIgnoreCase("Visualizers/Spike Viewer")) +// dataWindow->setContentComponent(new SpikeViewer(streamBuffer,eventBuffer,UI)); + +// dataWindow->setVisible(true); + +// } else { +// dataWindow->setVisible(windowSelector->getToggleState()); +// } + +// } else if (button == tabSelector) +// { +// if (tabSelector->getToggleState() && tabIndex < 0) +// { +// if (getName().equalsIgnoreCase("Visualizers/LFP Viewer")) +// tabIndex = dataViewport->addTabToDataViewport("LFP",new LfpViewer(streamBuffer,eventBuffer,UI)); +// else if (getName().equalsIgnoreCase("Visualizers/Spike Viewer")) +// tabIndex = dataViewport->addTabToDataViewport("Spikes",new SpikeViewer(streamBuffer,eventBuffer,UI)); + +// } else if (!tabSelector->getToggleState() && tabIndex > -1) +// { +// dataViewport->removeTab(tabIndex); +// tabIndex = -1; +// } +// } +// } + +// //=================================================== + +// Renderer::Renderer(AudioSampleBuffer* sBuffer, MidiBuffer* eBuffer, UIComponent* ui) +// : streamBuffer(sBuffer), eventBuffer(eBuffer) +// { +// //ui->addActionListener(this); +// config = ui->getConfiguration(); +// } + +// Renderer::~Renderer() { } + +// void Renderer::actionListenerCallback(const String & msg) +// { +// repaint(); +// } + + +// DataWindow::DataWindow(Button* cButton) +// : DocumentWindow ("Stream Window", +// Colours::black, +// DocumentWindow::allButtons), +// controlButton(cButton) + +// { +// centreWithSize(300,200); +// setUsingNativeTitleBar(true); +// setResizable(true,true); +// setTitleBarHeight(40); +// } + +// DataWindow::~DataWindow() +// { +// //deleteAllChildren(); +// setContentComponent (0); + +// } + +// void DataWindow::closeButtonPressed() +// { +// setVisible(false); +// controlButton->setToggleState(false,false); +// //viewport->removeTab(0); + +// } \ No newline at end of file diff --git a/Source/Processors/Editors/Visualizer.h b/Source/Processors/Editors/Visualizer.h new file mode 100644 index 0000000000000000000000000000000000000000..a44f3df489f1a564958e2119b5ab95e8807aa582 --- /dev/null +++ b/Source/Processors/Editors/Visualizer.h @@ -0,0 +1,122 @@ + +// ============================================================================== + +// Visualizer.h +// Created: 15 Jul 2011 8:42:01pm +// Author: jsiegle + +// ============================================================================== + + +// #ifndef __VISUALIZER_H_5573CACE__ +// #define __VISUALIZER_H_5573CACE__ + + +// #include "../../../JuceLibraryCode/JuceHeader.h" +// #include "GenericEditor.h" +// #include "../../UI/UIComponent.h" +// #include "../../UI/DataViewport.h" + +// #ifdef _WIN32 +// #include <windows.h> +// #endif + +// #if JUCE_WINDOWS +// #include <gl/gl.h> +// #include <gl/glu.h> +// #elif JUCE_LINUX +// #include <GL/gl.h> +// #include <GL/glut.h> +// #undef KeyPress +// #elif JUCE_IPHONE +// #include <OpenGLES/ES1/gl.h> +// #include <OpenGLES/ES1/glext.h> +// #elif JUCE_MAC +// #include <GLUT/glut.h> +// #endif + +// #ifndef GL_BGRA_EXT +// #define GL_BGRA_EXT 0x80e1 +// #endif + + +// class FilterViewport; +// class DataViewport; + +// class DataWindow : public DocumentWindow +// { +// public: +// DataWindow(Button* button); +// ~DataWindow(); + +// void closeButtonPressed(); + +// private: +// JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DataWindow); + +// Button* controlButton; + +// }; + +// class SelectorButton : public DrawableButton +// { +// public: +// SelectorButton(); +// ~SelectorButton(); +// }; + +// class Visualizer : public GenericEditor, +// public Button::Listener +// { +// public: +// Visualizer (GenericProcessor*, FilterViewport*, DataViewport*); +// ~Visualizer(); + +// void buttonClicked (Button* button); +// void setBuffers (AudioSampleBuffer*, MidiBuffer*); +// void setUIComponent (UIComponent* ui) {UI = ui;} + +// private: +// //Slider* slider; +// ScopedPointer <DataWindow> dataWindow; + +// SelectorButton* windowSelector; +// SelectorButton* tabSelector; + +// AudioSampleBuffer* streamBuffer; +// MidiBuffer* eventBuffer; +// UIComponent* UI; +// DataViewport* dataViewport; + +// int tabIndex; + +// JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Visualizer); + +// }; + +// class Renderer : public OpenGLComponent, +// public ActionListener + +// { +// public: +// Renderer(AudioSampleBuffer* streamBuffer, MidiBuffer* eventBuffer, UIComponent* ui); +// ~Renderer(); +// virtual void newOpenGLContextCreated() = 0; +// virtual void renderOpenGL() = 0; + +// AudioSampleBuffer* streamBuffer; +// MidiBuffer* eventBuffer; + +// Configuration* config; + +// private: + +// void actionListenerCallback(const String& msg); + + +// }; + + + + +// #endif // __VISUALIZER_H_5573CACE__ diff --git a/Source/Processors/EventNode.cpp b/Source/Processors/EventNode.cpp new file mode 100644 index 0000000000000000000000000000000000000000..11f8c39a6f231362bc83f129127c25791465a048 --- /dev/null +++ b/Source/Processors/EventNode.cpp @@ -0,0 +1,164 @@ +/* + ============================================================================== + + EventNode.cpp + Created: 13 Jun 2011 10:42:26am + Author: jsiegle + + ============================================================================== +*/ + + +#include <stdio.h> +#include "EventNode.h" +//#include "FilterEditor.h" + +EventNode::EventNode() + : GenericProcessor("Event Node") +{ + +} + +EventNode::~EventNode() +{ + +} + +// AudioProcessorEditor* EventNode::createEditor() +// { +// FilterEditor* filterEditor = new FilterEditor(this, viewport); + +// std::cout << "Creating editor." << std::endl; +// //filterEditor = new FilterEditor(this); +// return filterEditor; + +// //return 0; +// } + +//AudioProcessorEditor* FilterNode::createEditor(AudioProcessorEditor* const editor) +//{ + +// return editor; +//} +void EventNode::setParameter (int parameterIndex, float newValue) +{ + +} + + +void EventNode::prepareToPlay (double sampleRate_, int estimatedSamplesPerBlock) +{ + //std::cout << "Filter node preparing." << std::endl; +} + +//void EventNode::enable() +//{ + //prepareToPlay(); +//} + + +//void EventNode::disable() +//{ + //releaseResources(); +//} + +void EventNode::releaseResources() +{ +} + +void EventNode::process(AudioSampleBuffer &buffer, + MidiBuffer &midiMessages, + int& nSamples) +{ + accumulator++; + + if (!isSource) { + + if (midiMessages.getNumEvents() > 0) { + + std::cout << "Events received by node " << getNodeId() << std::endl; + + MidiBuffer::Iterator i (midiMessages); + MidiMessage message(0xf4); + + int samplePosition; + i.setNextSamplePosition(samplePosition); + + while (i.getNextEvent (message, samplePosition)) { + + //message.getChannel(); + + //MidiMessage msgCopy = MidiMessage(message); + int numbytes = message.getRawDataSize(); + uint8* dataptr = message.getRawData(); + + + + std::cout << " Bytes received: " << numbytes << std::endl; + std::cout << " Message timestamp = " << message.getTimeStamp() << std::endl; + + //std::cout << sizeof(int) << " " << sizeof(uint16) << std::endl; + + std::cout << " "; + for (int n = 0; n < numbytes; n++) { + std::cout << String(*dataptr++) << " "; + } + + std::cout << std::endl << std::endl; + //std::cout << " Event on channel " << message.getRawData() << std::endl; //<< message.getRawDataSize() << std::endl; + + } + + + // accumulator = 0; + }//MidiBuffer::Iterator = midiMessages. + + //midiMessages.clear(); + + } else { + + if (accumulator > 20) { + + uint8 data[95]; + + for (int n = 0; n < sizeof(data); n++) { + data[n] = 1; + } + + //MidiMessage event = MidiMessage::noteOn(2,1,10.0f); + MidiMessage event = MidiMessage(data, // spike data (float) + sizeof(data), // number of bytes to use + 1000.0 // timestamp (64-bit) + ); + + //event.setChannel(1); + + midiMessages.addEvent(data, sizeof(data), 5); + //midiMessages.addEvent(event, 1); + + for (int n = 0; n < sizeof(data); n++) { + data[n] = 2; + } + + midiMessages.addEvent(data, sizeof(data), 10); + + for (int n = 0; n < sizeof(data); n++) { + data[n] = 3; + } + + midiMessages.addEvent(data, sizeof(data), 15); + + //midiMessages.addEvent(event, 5); + + //std::cout << "Midi buffer contains " << midiMessages.getNumEvents() << " events." << std::endl; + + accumulator = 0; + } + + + } + + + + +} diff --git a/Source/Processors/EventNode.h b/Source/Processors/EventNode.h new file mode 100644 index 0000000000000000000000000000000000000000..8b2b3ed6ddf9fef0bf99e1b73305a3f6ab413ad3 --- /dev/null +++ b/Source/Processors/EventNode.h @@ -0,0 +1,52 @@ +/* + ============================================================================== + + EventNode.h + Created: 13 Jun 2011 10:42:26am + Author: jsiegle + + ============================================================================== +*/ + +#ifndef __EVENTNODE_H_9B67A789__ +#define __EVENTNODE_H_9B67A789__ + +#include "../../JuceLibraryCode/JuceHeader.h" +#include "GenericProcessor.h" +//#include "Editors/FilterEditor.h" + +class FilterViewport; + +class EventNode : public GenericProcessor + +{ +public: + + EventNode(); + ~EventNode(); + + void prepareToPlay (double sampleRate, int estimatedSamplesPerBlock); + void releaseResources(); + void process(AudioSampleBuffer &buffer, MidiBuffer &midiMessages, int& nSamples); + void setParameter (int parameterIndex, float newValue); + +// bool enable(); + //bool disable(); + + // AudioProcessorEditor* createEditor(); + +private: + + int accumulator; + bool isSource; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (EventNode); + +}; + + + + + + +#endif // __EVENTNODE_H_9B67A789__ diff --git a/Source/Processors/FileReader.cpp b/Source/Processors/FileReader.cpp new file mode 100644 index 0000000000000000000000000000000000000000..486ebe3cf5b4f9baa263663d212f4df520fcd37a --- /dev/null +++ b/Source/Processors/FileReader.cpp @@ -0,0 +1,109 @@ +/* + ============================================================================== + + FileReader.cpp + Created: 13 Aug 2011 7:18:22pm + Author: jsiegle + + ============================================================================== +*/ + +#include "FileReader.h" + + +FileReader::FileReader() + : GenericProcessor("File Reader"), + sampleRate (40000.0), + numChannels(16), + samplesPerBlock(1024) +{ + setNumOutputs(numChannels); + setNumInputs(0); +} + +FileReader::~FileReader() +{ +} + + +//AudioProcessorEditor* FileReader::createEditor( ) +//{ + //filterEditor = new FilterEditor(this); + +// std::cout << "Creating editor." << std::endl; +// sourceEditor = new SourceNodeEditor(this); +// return sourceEditor; +//} + +//AudioProcessorEditor* FilterNode::createEditor(AudioProcessorEditor* const editor) +//{ + +// return editor; +//} +void FileReader::setParameter (int parameterIndex, float newValue) +{ + //std::cout << "Message received." << std::endl; + + +} + +void FileReader::prepareToPlay (double sampleRate_, int estimatedSamplesPerBlock) +{ + + samplesPerBlock = int(float(estimatedSamplesPerBlock)/sampleRate*sampleRate); + + std::cout << "Samples per block = " << samplesPerBlock << std::endl; + + +} + +bool FileReader::enable () { + + File file = File("./data_stream_16ch"); + input = file.createInputStream(); + + + std::cout << "File Reader received enable signal." << std::endl; + + return true; + +} + +bool FileReader::disable() { + + deleteAndZero(input); + + std::cout << "File reader received disable signal." << std::endl; + + return true; + +} + +void FileReader::releaseResources() +{ + +} + +void FileReader::process(AudioSampleBuffer &buffer, + MidiBuffer &midiMessages, + int& nSamples) +{ + + nSamples = samplesPerBlock; + //std::cout << buffer.getNumChannels() << std::endl; + + for (int i = 0; i < samplesPerBlock; ++i) + { + + for (int j = 0; j < numChannels; j++) { + + if (input->isExhausted()) + input->setPosition(0); + + const float sample = float(input->readShort()); + + *buffer.getSampleData (j, i) = sample; + } + + } +} \ No newline at end of file diff --git a/Source/Processors/FileReader.h b/Source/Processors/FileReader.h new file mode 100644 index 0000000000000000000000000000000000000000..e5a187570cb95db5b64846d65309df1d4395ab8f --- /dev/null +++ b/Source/Processors/FileReader.h @@ -0,0 +1,61 @@ +/* + ============================================================================== + + FileReader.h + Created: 13 Aug 2011 7:18:22pm + Author: jsiegle + + ============================================================================== +*/ + +#ifndef __FILEREADER_H_605BF4A__ +#define __FILEREADER_H_605BF4A__ + + +#include "../../JuceLibraryCode/JuceHeader.h" +#include "GenericProcessor.h" + +//class FileReaderEditor; + +class FileReader : public GenericProcessor + +{ +public: + + // real member functions: + FileReader(); + ~FileReader(); + + void prepareToPlay (double sampleRate, int estimatedSamplesPerBlock); + void releaseResources(); + void process(AudioSampleBuffer &buffer, MidiBuffer &midiMessages, int& nSamples); + + void setParameter (int parameterIndex, float newValue); + + //AudioProcessorEditor* createEditor(); + bool hasEditor() const {return true;} + + bool enable(); + bool disable(); + + bool isSource() {return true;} + +private: + + float sampleRate; + int numChannels; + int samplesPerBlock; + + FileInputStream* input; + + //SourceNodeEditor* sourceEditor; + + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileReader); + +}; + + + + +#endif // __FILEREADER_H_605BF4A__ diff --git a/Source/Processors/FilterNode.cpp b/Source/Processors/FilterNode.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2659697723d8700f50b0b8cf49f16261f0610bc2 --- /dev/null +++ b/Source/Processors/FilterNode.cpp @@ -0,0 +1,215 @@ +/* + ============================================================================== + + FilterNode.cpp + Created: 7 May 2011 5:07:28pm + Author: jsiegle + + ============================================================================== +*/ + +#include <stdio.h> +#include "FilterNode.h" +//#include "FilterEditor.h" + +FilterNode::FilterNode() + : GenericProcessor("Bandpass Filter"), filter(0), + highCut(6000.0), lowCut(600.0) + +{ + setNumInputs(10); + setSampleRate(20000.0); + // set up default configuration + setPlayConfigDetails(16, 16, 44100.0, 128); + + // each family of filters is given its own namespace + // RBJ: filters from the RBJ cookbook + // Butterworth + // ChebyshevI: ripple in the passband + // ChebyshevII: ripple in the stop band + // Elliptic: ripple in both the passband and stopband + // Bessel: theoretically with linear phase + // Legendre: "Optimum-L" filters with steepest transition and monotonic passband + // Custom: Simple filters that allow poles and zeros to be specified directly + + // within each namespace exists a set of "raw filters" + // Butterworth::LowPass + // HighPass + // BandPass + // BandStop + // LowShelf + // HighShelf + // BandShelf + // + // class templates (such as SimpleFilter) which require FilterClasses + // expect an identifier of a raw filter + // raw filters do not support introspection, or the Params style of changing + // filter settings; they only offer a setup() function for updating the IIR + // coefficients to a given set of parameters + // + + // each filter family namespace also has the nested namespace "Design" + // here we have all of the raw filter names repeated, except these classes + // also provide the Design interface, which adds introspection, polymorphism, + // the Params style of changing filter settings, and in general all fo the features + // necessary to interoperate with the Filter virtual base class and its derived classes + + + + + +} + +FilterNode::~FilterNode() +{ + filter = 0; +} + +AudioProcessorEditor* FilterNode::createEditor() +{ + FilterEditor* filterEditor = new FilterEditor(this, viewport); + setEditor(filterEditor); + + std::cout << "Creating editor." << std::endl; + //filterEditor = new FilterEditor(this); + return filterEditor; + + //return 0; +} + +// void FilterNode::setSourceNode(GenericProcessor* sn) +// { +// sourceNode = sn; +// setNumInputs(sourceNode->getNumOutputs()); + +// if (destNode != 0) +// { +// destNode->setNumInputs(getNumOutputs()); +// } + +// } + +// void FilterNode::setDestNode(GenericProcessor* dn) +// { +// if (dn != 0) { +// if (!dn->isSource()) +// { +// destNode = dn; +// destNode->setSourceNode(this); +// } +// } +// } + + +void FilterNode::setNumInputs(int inputs) +{ + + numInputs = inputs; + setNumOutputs(inputs); + + if (filter != 0) + { + delete filter; + filter = 0; + } + + const int nChans = inputs; + + if (nChans == 16) { + + filter = new Dsp::SmoothedFilterDesign + <Dsp::Butterworth::Design::BandPass // design type + <4>, // order + 16, // number of channels (must be const) + Dsp::DirectFormII> // realization + (1024); // number of samples over which to fade + + } else if (nChans == 32) { + + filter = new Dsp::SmoothedFilterDesign + <Dsp::Butterworth::Design::BandPass // design type + <4>, // order + 32 , // number of channels (must be const) + Dsp::DirectFormII> // realization + (1024); // number of samples over which to fade + // parameter changes + + } else { + // send a message saying this is not implemented + } + + //std::cout << "Filter created with " << getNumInputs() << " channels." << std::endl; + + + setFilterParameters(); + + setPlayConfigDetails(getNumInputs(), getNumOutputs(), 44100.0, 128); + + +} + +//AudioProcessorEditor* FilterNode::createEditor(AudioProcessorEditor* const editor) +//{ + +// return editor; +//} + +void FilterNode::setSampleRate(float r) +{ + sampleRate = r; + setFilterParameters(); +} + +void FilterNode::setFilterParameters() +{ + + Dsp::Params params; + params[0] = getSampleRate(); // sample rate + params[1] = 4; // order + params[2] = (highCut + lowCut)/2; // center frequency + params[3] = highCut - lowCut; // bandwidth + + if (filter != 0) + filter->setParams (params); + +} + +void FilterNode::setParameter (int parameterIndex, float newValue) +{ + //std::cout << "Message received." << std::endl; + + if (parameterIndex == 0) { + lowCut = newValue; + } else { + highCut = newValue; + } + + setFilterParameters(); + +} + + +void FilterNode::prepareToPlay (double sampleRate_, int estimatedSamplesPerBlock) +{ + //std::cout << "Filter node preparing." << std::endl; +} + +void FilterNode::releaseResources() +{ +} + +void FilterNode::process(AudioSampleBuffer &buffer, + MidiBuffer &midiMessages, + int& nSamples) +{ + //std::cout << "Filter node processing." << std::endl; + //std::cout << buffer.getNumChannels() << std::endl; + //::cout << buffer.getNumSamples() << std::endl; + + //int nSamps = getNumSamples(midiMessages); + //std::cout << nSamples << std::endl; + filter->process (nSamples, buffer.getArrayOfChannels()); + + //std::cout << "Filter node:" << *buffer.getSampleData(0,0); + +} diff --git a/Source/Processors/FilterNode.h b/Source/Processors/FilterNode.h new file mode 100644 index 0000000000000000000000000000000000000000..9a00a411ac998d931cd90f7d13e7854f2afec977 --- /dev/null +++ b/Source/Processors/FilterNode.h @@ -0,0 +1,59 @@ +/* + ============================================================================== + + FilterNode.h + Created: 7 May 2011 5:07:28pm + Author: jsiegle + + ============================================================================== +*/ + +#ifndef __FILTERNODE_H_CED428E__ +#define __FILTERNODE_H_CED428E__ + +#include "../../JuceLibraryCode/JuceHeader.h" +#include "../Dsp/Dsp.h" +#include "GenericProcessor.h" +#include "Editors/FilterEditor.h" + +class FilterEditor; +class FilterViewport; + +class FilterNode : public GenericProcessor + +{ +public: + + FilterNode(); + ~FilterNode(); + + void prepareToPlay (double sampleRate, int estimatedSamplesPerBlock); + void releaseResources(); + void process(AudioSampleBuffer &buffer, MidiBuffer &midiMessages, int& nSamples); + void setParameter (int parameterIndex, float newValue); + + AudioProcessorEditor* createEditor(); + + bool hasEditor() const {return true;} + + // void setSourceNode(GenericProcessor* sn); + // void setDestNode(GenericProcessor* dn); + + void setNumInputs(int); + void setSampleRate(float); + +private: + double lowCut, highCut; + Dsp::Filter* filter; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FilterNode); + + void setFilterParameters(); + +}; + + + + + +#endif // __FILTERNODE_H_CED428E__ diff --git a/Source/Processors/GenericProcessor.cpp b/Source/Processors/GenericProcessor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0fea89e39ea55472c2ef76c97d98cda52cc99e30 --- /dev/null +++ b/Source/Processors/GenericProcessor.cpp @@ -0,0 +1,325 @@ +/* + ============================================================================== + + GenericProcessor.cpp + Created: 7 May 2011 2:26:54pm + Author: jsiegle + + ============================================================================== +*/ + +#include "GenericProcessor.h" +#include "../UI/UIComponent.h" + +GenericProcessor::GenericProcessor(const String& name_) : name(name_), + sourceNode(0), destNode(0), editor(0) + +{ + + //name = "Generic Processor"; + //setSourceNode(source_); + //setDestNode(dest_); + + //setNumInputs(); + //setNumOutputs(); + + //setPlayConfigDetails(getNumInputs(),getNumOutputs(),1024); + +} + +GenericProcessor::~GenericProcessor() +{ + //deleteAllChildren(); + //std::cout << name << " deleting editor." << std::endl; + + if (editor != 0) + { + delete(editor); + editor = 0; + } +} + +AudioProcessorEditor* GenericProcessor::createEditor() +{ + editor = new GenericEditor (this, viewport); + return editor; +} + + +void GenericProcessor::setParameter (int parameterIndex, float newValue) +{ + + +} + +GenericProcessor* GenericProcessor::getOriginalSourceNode() +{ + if (isSource()) + { + return this; + } else { + + GenericProcessor* source = getSourceNode(); + + if (source != 0) + { + while (!source->isSource() && source != 0) + { + source = source->getSourceNode(); + } + + return source; + + } else { + return 0; + } + } +} + +// void GenericProcessor::setViewport(FilterViewport* vp) { + +// viewport = vp; +// } + +void GenericProcessor::setDataViewport(DataViewport* dv) + +{ + std::cout << "Processor data viewport: " << dv << std::endl; + dataViewport = dv; +} + +void GenericProcessor::prepareToPlay (double sampleRate_, int estimatedSamplesPerBlock) +{ + //std::cout << "Preparing to play." << std::endl; + +} + +void GenericProcessor::releaseResources() +{ +} + + +// void GenericProcessor::sendMessage(const String& msg) +// { +// std::cout << "Message: "; +// std::cout << msg << "...." << std::endl; +// UI->transmitMessage(msg); +// } + + +void GenericProcessor::setNumSamples(MidiBuffer& midiMessages, int numberToAdd) { + //lock.enter(); + //*numSamplesInThisBuffer = n; + //lock.exit(); + + uint8 data[2]; + + data[0] = numberToAdd >> 8; // most-significant byte + data[1] = numberToAdd & 0xFF; // least-significant byte + + midiMessages.addEvent(data, // spike data + sizeof(data), // total bytes + 0); // sample index + + +} + +int GenericProcessor::getNumSamples(MidiBuffer& midiMessages) { + //lock.enter(); + //int numRead = *numSamplesInThisBuffer; + //lock.exit(); + int numRead = 0; + + if (midiMessages.getNumEvents() > 0) + { + + int m = midiMessages.getNumEvents(); + + // if (m == 1) + // std::cout << m << " event received by node " << getNodeId() << std::endl; + // else + // std::cout << m << " events received by node " << getNodeId() << std::endl; + + MidiBuffer::Iterator i (midiMessages); + MidiMessage message(0xf4); + + int samplePosition = 0; + //i.setNextSamplePosition(samplePosition); + + while (i.getNextEvent (message, samplePosition)) { + + int numbytes = message.getRawDataSize(); + uint8* dataptr = message.getRawData(); + + //std::cout << " Bytes received: " << numbytes << std::endl; + //std::cout << " Message timestamp = " << message.getTimeStamp() << std::endl; + + numRead = (*dataptr<<8) + *(dataptr+1); + //std::cout << " " << numRead << std::endl; + } + + } + + return numRead; +} + +void GenericProcessor::setSourceNode(GenericProcessor* sn) +{ + if (!isSource()) + { + if (sn != 0) + { + if (!sn->isSink()) + { + if (sourceNode != sn) { + sourceNode = sn; + sn->setDestNode(this); + setNumInputs(sn->getNumOutputs()); + setSampleRate(sn->getSampleRate()); + } + } else { + sourceNode = 0; + } + } else { + sourceNode = 0; + } + } else { + if (sn != 0) + sn->setDestNode(this); + } +} + + +void GenericProcessor::setDestNode(GenericProcessor* dn) +{ + if (!isSink()) + { + if (dn != 0) + { + if (!dn->isSource()) + { + if (destNode != dn) + { + destNode = dn; + dn->setSourceNode(this); + } + } else { + destNode = 0; + } + } else { + destNode = 0; + } + } else { + if (dn != 0) + dn->setSourceNode(this); + } +} + +// void GenericProcessor::setSourceNode(GenericProcessor* sn) +// { +// if (!isSource()) +// sourceNode = sn; +// else +// sourceNode = 0; +// } + +// void GenericProcessor::setDestNode(GenericProcessor* dn) +// { +// if (!isSink()) +// destNode = dn +// else +// destNode = 0; +// } +int GenericProcessor::getNumInputs() +{ + return numInputs; +} + +int GenericProcessor::getNumOutputs() +{ + return numOutputs; +} + +void GenericProcessor::setNumInputs(int n) { + numInputs = n; + //setPlayConfigDetails(numInputs,numOutputs,44100.0,1024); +} + +void GenericProcessor::setNumInputs() { + + int n = getSourceNode()->getNumOutputs(); + setNumInputs(n); +} + +void GenericProcessor::setNumOutputs() +{ + setNumOutputs(getNumInputs()); +} + +void GenericProcessor::setNumOutputs(int n) { + numOutputs = n; + //setPlayConfigDetails(numInputs,numOutputs,44100.0,1024); +} + +float GenericProcessor::getSampleRate() +{ + return sampleRate; +} +void GenericProcessor::setSampleRate(float sr) +{ + sampleRate = sr; +} + +void GenericProcessor::checkForMidiEvents(MidiBuffer& midiMessages) +{ + + if (midiMessages.getNumEvents() > 0) + { + + int m = midiMessages.getNumEvents(); + std::cout << m << "events received by node " << getNodeId() << std::endl; + + MidiBuffer::Iterator i (midiMessages); + MidiMessage message(0xf4); + + int samplePosition; + i.setNextSamplePosition(samplePosition); + + while (i.getNextEvent (message, samplePosition)) { + + int numbytes = message.getRawDataSize(); + uint8* dataptr = message.getRawData(); + + std::cout << " Bytes received: " << numbytes << std::endl; + std::cout << " Message timestamp = " << message.getTimeStamp() << std::endl; + + int value = (*dataptr<<8) + *(dataptr+1); + std::cout << " " << value << std::endl; + } + + } +} + +void GenericProcessor::addMidiEvent(MidiBuffer& midiMessages, int numberToAdd) +{ + uint8 data[2]; + + data[0] = numberToAdd >> 8; // most-significant byte + data[1] = numberToAdd & 0xFF; // least-significant byte + + midiMessages.addEvent(data, // spike data + sizeof(data), // total bytes + 0); // sample index +} + +void GenericProcessor::processBlock (AudioSampleBuffer &buffer, MidiBuffer &midiMessages) +{ + + int nSamples = getNumSamples(midiMessages); // removes first value from midimessages + + process(buffer, midiMessages, nSamples); + + setNumSamples(midiMessages, nSamples); // adds it back, + // even if it's unchanged + +} diff --git a/Source/Processors/GenericProcessor.h b/Source/Processors/GenericProcessor.h new file mode 100644 index 0000000000000000000000000000000000000000..158eef84a1218c9388748b9358175a0e29ffe88f --- /dev/null +++ b/Source/Processors/GenericProcessor.h @@ -0,0 +1,176 @@ +/* + ============================================================================== + + GenericProcessor.h + Created: 7 May 2011 2:26:54pm + Author: jsiegle + + ============================================================================== +*/ + +#ifndef __GENERICPROCESSOR_H_1F469DAF__ +#define __GENERICPROCESSOR_H_1F469DAF__ + + +#include "../../JuceLibraryCode/JuceHeader.h" +#include "Editors/GenericEditor.h" +#include "../UI/Configuration.h" +#include <time.h> +#include <stdio.h> + +class FilterViewport; +class DataViewport; +class UIComponent; + +class GenericProcessor : public AudioProcessor, + public ActionBroadcaster + +{ +public: + + GenericProcessor(const String& name_); + virtual ~GenericProcessor(); + + const String getName() const {return name;} + //virtual void setName(const String& name_) {} + + void prepareToPlay (double sampleRate, int estimatedSamplesPerBlock); + void releaseResources(); + + void setParameter (int parameterIndex, float newValue); + + virtual AudioProcessorEditor* createEditor(); + bool hasEditor() const {return true;} + + void reset() {} + void setCurrentProgramStateInformation(const void* data, int sizeInBytes) {} + void setStateInformation(const void* data, int sizeInBytes) {} + void getCurrentProgramStateInformation(MemoryBlock &destData) {} + void getStateInformation (MemoryBlock &destData) {} + void changeProgramName (int index, const String &newName) {} + void setCurrentProgram (int index) {} + + const String getInputChannelName (int channelIndex) const {return T(" ");} + const String getOutputChannelName (int channelIndex) const {return T(" ");} + const String getParameterName (int parameterIndex) {return T(" ");} + const String getParameterText (int parameterIndex) {return T(" ");} + const String getProgramName (int index) {return T(" ");} + + bool isInputChannelStereoPair (int index) const {return true;} + bool isOutputChannelStereoPair (int index) const {return true;} + bool acceptsMidi () const {return true;} + bool producesMidi () const {return true;} + + bool isParameterAutomatable(int parameterIndex) {return false;} + bool isMetaParameter(int parameterIndex) {return false;} + + int getNumParameters() {return 0;} + int getNumPrograms() {return 0;} + int getCurrentProgram() {return 0;} + + float getParameter (int parameterIndex) {return 1.0;} + + // custom methods: + + // pure virtual function + virtual void process(AudioSampleBuffer& /*buffer*/, + MidiBuffer& /*buffer*/, + int& /*nSamples*/) = 0; + + const String name; + //int* numSamplesInThisBuffer; + //const CriticalSection& lock; + int nodeId; + + GenericProcessor* sourceNode; + GenericProcessor* destNode; + + FilterViewport* viewport; + DataViewport* dataViewport; + + Configuration* config; + + AudioProcessorEditor* editor; + + int numInputs; + int numOutputs; + + float sampleRate; + + UIComponent* UI; + + //void sendMessage(const String& msg); + + virtual float getSampleRate(); + virtual void setSampleRate(float sr); + + virtual int getNumInputs(); + virtual void setNumInputs(int); + virtual void setNumInputs(); + + virtual int getNumOutputs(); + virtual void setNumOutputs(int); + virtual void setNumOutputs(); + + int getNodeId() {return nodeId;} + void setNodeId(int id) {nodeId = id;} + + // get/set source node functions + GenericProcessor* getSourceNode() {return sourceNode;} + GenericProcessor* getDestNode() {return destNode;} + GenericProcessor* getOriginalSourceNode(); + + virtual void setSourceNode(GenericProcessor* sn); + virtual void setDestNode(GenericProcessor* dn); + + virtual bool isSource() {return false;} + virtual bool isSink() {return false;} + virtual bool isSplitter() {return false;} + virtual bool isMerger() {return false;} + + virtual bool canSendSignalTo(GenericProcessor*) {return true;} + + virtual bool enable() {return true;} + virtual bool disable() {return true;} + + bool enabledState() {return isEnabled;} + void enabledState(bool t) {isEnabled = t;} + + virtual AudioSampleBuffer* getContinuousBuffer() {return 0;} + virtual MidiBuffer* getEventBuffer() {return 0;} + + AudioProcessorEditor* getEditor() {return editor;} + void setEditor(AudioProcessorEditor* e) {editor = e;} + + void setUIComponent(UIComponent* ui) {UI = ui;} + UIComponent* getUIComponent() {return UI;} + + virtual void setConfiguration(Configuration* cf) {config = cf;} + Configuration* getConfiguration() {return config;} + + void setFilterViewport(FilterViewport* vp) {viewport = vp;} + void setDataViewport(DataViewport* dv); + DataViewport* getDataViewport() {return dataViewport;} + + void checkForMidiEvents(MidiBuffer& mb); + void addMidiEvent(MidiBuffer& mb, int a); + +private: + + void processBlock (AudioSampleBuffer &buffer, MidiBuffer &midiMessages); + + + + bool isEnabled; + + int getNumSamples(MidiBuffer&); + void setNumSamples(MidiBuffer&, int); + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GenericProcessor); + +}; + + + + +#endif // __GENERICPROCESSOR_H_1F469DAF__ diff --git a/Source/Processors/LfpDisplayNode.cpp b/Source/Processors/LfpDisplayNode.cpp new file mode 100644 index 0000000000000000000000000000000000000000..51f6ab0d1e8f22d44260bea9d509d0d0a54d2280 --- /dev/null +++ b/Source/Processors/LfpDisplayNode.cpp @@ -0,0 +1,406 @@ +/* + ============================================================================== + + LfpDisplayNode.cpp + Created: 8 Feb 2012 12:31:32pm + Author: jsiegle + + ============================================================================== +*/ + +#include "LfpDisplayNode.h" +#include <stdio.h> + +LfpDisplayNode::LfpDisplayNode() + : GenericProcessor("LFP Viewer"), + timebase(1000), displayGain(1), parameterChanged(true), isVisible(false), + xBuffer(10), yBuffer(10), + plotHeight(60), selectedChan(-1), + displayBufferIndex(0),// screenBufferIndex(0), + repaintInterval(10), repaintCounter(0) + +{ + + + lock = new ReadWriteLock(); + + displayBuffer = 0; + //screenBuffer = 0; //new AudioSampleBuffer(16, 10000); + + //setNumInputs(16); + //setSampleRate(10000.0); + setPlayConfigDetails(16,0,44100.0,128); + + eventBuffer = new MidiBuffer(); +} + +LfpDisplayNode::~LfpDisplayNode() +{ + if (displayBuffer != 0) + deleteAndZero(displayBuffer); + + //if (screenBuffer != 0) + // deleteAndZero(screenBuffer); + + deleteAndZero(eventBuffer); + deleteAndZero(lock); +} + +AudioProcessorEditor* LfpDisplayNode::createEditor() +{ + + std::cout << "Processor data viewport: " << getDataViewport() << std::endl; + + LfpDisplayEditor* editor = new LfpDisplayEditor(this, viewport, getDataViewport()); + + editor->setBuffers(displayBuffer,eventBuffer); + editor->setUIComponent(getUIComponent()); + editor->setConfiguration(config); + + setEditor(editor); + + std::cout << "Creating LFP Display Editor." << std::endl; + return editor; + +} + +void LfpDisplayNode::setNumInputs(int inputs) +{ + numInputs = inputs; + setNumOutputs(0); + + int nSamples = (int) sampleRate*10.0f; + int nInputs = getNumInputs(); + std::cout << "Setting inputs. Samples: " << nSamples << ", Inputs: " << nInputs << std::endl; + + setPlayConfigDetails(getNumInputs(), 0, 44100.0, 128); + + if (nSamples > 0 && nInputs > 0) + resizeBuffer(); + + LfpDisplayEditor* editor = (LfpDisplayEditor*) getEditor(); + editor->updateNumInputs(inputs); +} + +void LfpDisplayNode::setSampleRate(float r) +{ + sampleRate = r; + int nSamples = (int) sampleRate*10.0f; + int nInputs = getNumInputs(); + std::cout << "Setting sample rate. Samples: " << nSamples << ", Inputs: " << nInputs << std::endl; + + resizeBuffer(); + + LfpDisplayEditor* editor = (LfpDisplayEditor*) getEditor(); + editor->updateSampleRate(r); +} + +void LfpDisplayNode::resizeBuffer() +{ + int nSamples = (int) sampleRate*10.0f; + int nInputs = getNumInputs(); + + std::cout << "Resizing buffer. Samples: " << nSamples << ", Inputs: " << nInputs << std::endl; + + if (displayBuffer != 0) + deleteAndZero(displayBuffer); + + //if (screenBuffer != 0) + //deleteAndZero(screenBuffer); + + displayBuffer = new AudioSampleBuffer(nInputs, nSamples); + //screenBuffer = new AudioSampleBuffer(nInputs, 10000); + +} + +bool LfpDisplayNode::enable() +{ + LfpDisplayEditor* editor = (LfpDisplayEditor*) getEditor(); + editor->enable(); + return true; +} + +bool LfpDisplayNode::disable() +{ + LfpDisplayEditor* editor = (LfpDisplayEditor*) getEditor(); + editor->disable(); + return true; +} + +void LfpDisplayNode::setParameter (int parameterIndex, float newValue) +{ + //std::cout << "Message received." << std::endl; + + // if (parameterIndex == 0) { + // timebase = newValue; + // //screenBuffer->clear(); + // //screenBufferIndex = 0; + // } else { + // displayGain = newValue; + // screenBuffer->clear(); + // screenBufferIndex = 0; + // } + + // parameterChanged = true; + +} + +void LfpDisplayNode::prepareToPlay (double, int) +{ + if (displayBuffer == 0) + displayBuffer = new AudioSampleBuffer(16, 100000); +} + + +void LfpDisplayNode::process(AudioSampleBuffer &buffer, MidiBuffer &midiMessages, int& nSamples) +{ + // 1. place any new samples into the displayBuffer + int samplesLeft = displayBuffer->getNumSamples() - displayBufferIndex; + + //lock->enterWrite(); + + if (nSamples < samplesLeft) + { + + for (int chan = 0; chan < buffer.getNumChannels(); chan++) + { + displayBuffer->copyFrom(chan, // destChannel + displayBufferIndex, // destStartSample + buffer, // source + chan, // source channel + 0, // source start sample + nSamples); // numSamples + + } + displayBufferIndex += nSamples; + + } else { + + int extraSamples = nSamples - samplesLeft; + + for (int chan = 0; chan < buffer.getNumChannels(); chan++) + { + displayBuffer->copyFrom(chan, // destChannel + displayBufferIndex, // destStartSample + buffer, // source + chan, // source channel + 0, // source start sample + samplesLeft); // numSamples + + displayBuffer->copyFrom(chan, + 0, + buffer, + chan, + samplesLeft, + extraSamples); + } + + displayBufferIndex = extraSamples; + } + + //lock->exitWrite(); + + //std::cout << displayBufferIndex << std::endl; + + // 2. update the screenBuffer + // if (true) + // { + // float nPixels = (float) getWidth(); + // float ratio = sampleRate * timebase / 1000.0f / nPixels; + // float subSampleOffset = 0.0f; + + // int valuesNeeded = nSamples / (int) ratio; + + // for (int index = screenBufferIndex; index < valuesNeeded; index++) + // { + + // for (int channel = 0; channel < buffer.getNumChannels(); ++channel) { + + // screenBuffer->copyFrom(channel, // destChannel + // index, // destSampleOffset + // buffer, // source + // channel, // sourceChannel + // (int) subSampleOffset,// sourceSampleOffset + // 1); // number of samples + + // subSampleOffset += ratio; + // subSampleOffset = jmin(subSampleOffset, (float) buffer.getNumSamples()); + // } + + // } + // } + + // repaintCounter++; + // // 3. if it's time, repaint the display + // if (false && repaintCounter >= repaintInterval) + // { + // repaint(); + + // } +} + + +// void LfpDisplayNode::newOpenGLContextCreated() +// { + +// setUp2DCanvas(); +// activateAntiAliasing(); + +// glClearColor (0.8, 0.4, 0.9, 1.0); +// resized(); + +// } + +// void LfpDisplayNode::renderOpenGL() +// { + +// repaintCounter = 0; + +// glClear(GL_COLOR_BUFFER_BIT); // clear buffers to preset values + +// for (int i = 0; i < getNumInputs(); i++) +// { +// bool isSelected = false; + +// if (selectedChan == i) +// isSelected = true; + +// if (checkBounds(i)) { +// setViewport(i); +// drawBorder(isSelected); +// drawChannelInfo(i,isSelected); +// //drawWaveform(i,isSelected); +// } +// } +// drawScrollBars(); +// } + +// void LfpDisplayNode::drawWaveform(int chan, bool isSelected) +// { +// // draw the screen buffer for a given channel + +// glBegin(GL_LINE_STRIP); + +// for (int i = 0; i < getWidth(); i++) +// { +// glVertex2f(i,0.5);//*screenBuffer->getSampleData(chan, i)+0.5); +// } + +// glEnd(); +// } + + +// void LfpDisplayNode::drawTicks() +// { + +// glViewport(0,0,getWidth(),getHeight()); + +// glColor4f(1.0f, 1.0f, 1.0f, 0.25f); + +// for (int i = 0; i < 10; i++) +// { +// if (i == 5) +// glLineWidth(3.0); +// else if (i == 1 || i == 3 || i == 7 || i == 9) +// glLineWidth(2.0); +// else +// glLineWidth(1.0); + +// glBegin(GL_LINE_STRIP); +// glVertex2f(0.1*i,0); +// glVertex2f(0.1*i,1); +// glEnd(); +// } +// } + + +// bool LfpDisplayNode::checkBounds(int chan) +// { +// bool isVisible; + +// int lowerBound = (chan+1)*(plotHeight+yBuffer); +// int upperBound = chan*(plotHeight+yBuffer); + +// if (getScrollAmount() < lowerBound && getScrollAmount() + getHeight() > upperBound) +// isVisible = true; +// else +// isVisible = false; + +// return isVisible; + +// } + +// void LfpDisplayNode::setViewport(int chan) +// { +// glViewport(xBuffer, +// getHeight()-(chan+1)*(plotHeight+yBuffer)+getScrollAmount(), +// getWidth()-2*xBuffer, +// plotHeight); +// } + +// void LfpDisplayNode::drawBorder(bool isSelected) +// { +// float alpha = 0.5f; + +// if (isSelected) +// alpha = 1.0f; + +// glColor4f(0.0f, 0.0f, 0.0f, alpha); +// glBegin(GL_LINE_STRIP); +// glVertex2f(0.0f, 0.0f); +// glVertex2f(1.0f, 0.0f); +// glVertex2f(1.0f, 1.0f); +// glVertex2f(0.0f, 1.0f); +// glVertex2f(0.0f, 0.0f); +// glEnd(); + +// } + +// void LfpDisplayNode::drawChannelInfo(int chan, bool isSelected) +// { +// float alpha = 0.5f; + +// if (isSelected) +// alpha = 1.0f; + +// glColor4f(0.0f,0.0f,0.0f,alpha); +// glRasterPos2f(5.0f/getWidth(),0.3); +// String s = String("Channel "); +// s += (chan+1); + +// getFont(String("miso-regular"))->FaceSize(16); +// getFont(String("miso-regular"))->Render(s); +// } + +// int LfpDisplayNode::getTotalHeight() +// { +// return (plotHeight+yBuffer)*getNumInputs() + yBuffer; +// } + + +// void LfpDisplayNode::resized() + +// { + +// canvasWasResized(); +// // glClear(GL_COLOR_BUFFER_BIT); + +// // int h, w; + +// // if (inWindow) +// // { +// // h = getParentComponent()->getHeight(); +// // w = getParentComponent()->getWidth(); +// // } else { +// // h = getHeight(); +// // w = getWidth(); +// // } + +// // if (getScrollAmount() + h > getTotalHeight() && getTotalHeight() > h) +// // setScrollAmount(getTotalHeight() - h); +// // else +// // setScrollAmount(0); + +// // showScrollBars(); + +// } diff --git a/Source/Processors/LfpDisplayNode.h b/Source/Processors/LfpDisplayNode.h new file mode 100644 index 0000000000000000000000000000000000000000..fbc3ee4a07fdde01bd99c2963ab0181091802757 --- /dev/null +++ b/Source/Processors/LfpDisplayNode.h @@ -0,0 +1,100 @@ +/* + ============================================================================== + + LfpDisplayNode.h + Created: 8 Feb 2012 12:31:32pm + Author: jsiegle + + ============================================================================== +*/ + +#ifndef __LFPDISPLAYNODE_H_D969A379__ +#define __LFPDISPLAYNODE_H_D969A379__ + +#include "../../JuceLibraryCode/JuceHeader.h" +#include "Editors/LfpDisplayEditor.h" +#include "GenericProcessor.h" +//#include "Visualization/OpenGLCanvas.h" + +class DataViewport; + +class LfpDisplayNode : public GenericProcessor + ///public OpenGLCanvas, + + +{ +public: + + LfpDisplayNode(); + ~LfpDisplayNode(); + + // void newOpenGLContextCreated(); + // void renderOpenGL(); + // int getTotalHeight(); + + AudioProcessorEditor* createEditor(); + + bool isSink() {return true;} + + void process(AudioSampleBuffer &buffer, MidiBuffer &midiMessages, int& nSamples); + + void setParameter(int, float); + + void setNumInputs(int inputs); + void setSampleRate(float r); + + bool enable(); + bool disable(); + + void prepareToPlay(double, int); + + AudioSampleBuffer* getDisplayBufferAddress() {return displayBuffer;} + int getDisplayBufferIndex() {return displayBufferIndex;} + ReadWriteLock* getLock() {return lock;} + + bool isVisible; + + ReadWriteLock* lock; + +private: + + DataViewport* dataViewport; + + AudioSampleBuffer* displayBuffer; + //AudioSampleBuffer* screenBuffer; + MidiBuffer* eventBuffer; + + int displayBufferIndex;//, screenBufferIndex; + + int repaintInterval, repaintCounter; + + // void setViewport(int chan); + // void drawBorder(bool isSelected); + // void drawChannelInfo(int chan, bool isSelected); + // void drawWaveform(int chan, bool isSelected); + + // void drawTicks(); + + // bool checkBounds(int chan); + + int selectedChan; + + float timebase; // ms + float displayGain; // + + int xBuffer, yBuffer, plotHeight, totalHeight; + + bool parameterChanged; + + void resizeBuffer(); + + void resized(); + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LfpDisplayNode); + +}; + + + + +#endif // __LFPDISPLAYNODE_H_D969A379__ diff --git a/Source/Processors/ProcessorGraph.cpp b/Source/Processors/ProcessorGraph.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b478fa9d986731b2445749a3de070738361135a7 --- /dev/null +++ b/Source/Processors/ProcessorGraph.cpp @@ -0,0 +1,520 @@ +/* + ============================================================================== + + ProcessorGraph.cpp + Created: 30 Apr 2011 8:36:35pm + Author: jsiegle + + ============================================================================== +*/ + +#include <stdio.h> + +#include "ProcessorGraph.h" + +#include "AudioNode.h" +#include "LfpDisplayNode.h" +#include "EventNode.h" +#include "FileReader.h" +#include "FilterNode.h" +#include "GenericProcessor.h" +#include "RecordNode.h" +#include "ResamplingNode.h" +#include "SignalGenerator.h" +#include "SourceNode.h" +#include "SpikeDetector.h" +#include "Utilities/Splitter.h" +#include "../UI/UIComponent.h" +#include "../UI/Configuration.h" +#include "../UI/FilterViewport.h" + +ProcessorGraph::ProcessorGraph() : + currentNodeId(100), + RECORD_NODE_ID(199), + AUDIO_NODE_ID(200), + OUTPUT_NODE_ID(201), + RESAMPLING_NODE_ID(202), + totalAudioConnections(0), + totalRecordConnections(0) + + { + + // ProcessorGraph will always have 0 inputs (all content is generated within graph) + // but it will have N outputs, where N is the number of channels for the audio monitor + setPlayConfigDetails(0, // number of inputs + 2, // number of outputs + 44100.0, // sampleRate + 128); // blockSize + + createDefaultNodes(); + +} + +ProcessorGraph::~ProcessorGraph() { } + + +void ProcessorGraph::createDefaultNodes() +{ + + // add output node -- sends output to the audio card + AudioProcessorGraph::AudioGraphIOProcessor* on = + new AudioProcessorGraph::AudioGraphIOProcessor(AudioProcessorGraph::AudioGraphIOProcessor::audioOutputNode); + + // add record node -- sends output to disk + RecordNode* recn = new RecordNode(); + recn->setNodeId(RECORD_NODE_ID); + //recn->setConfiguration(config); + + // add audio node -- takes all inputs and selects those to be used for audio monitoring + AudioNode* an = new AudioNode(); + recn->setNodeId(AUDIO_NODE_ID); + //an->setConfiguration(config); + + // add resampling node -- resamples continuous signals to 44.1kHz + ResamplingNode* rn = new ResamplingNode(true); + rn->setNodeId(RESAMPLING_NODE_ID); + + addNode(on,OUTPUT_NODE_ID); + addNode(recn,RECORD_NODE_ID); + addNode(an, AUDIO_NODE_ID); + addNode(rn, RESAMPLING_NODE_ID); + + // connect audio network + for (int n = 0; n < 2; n++) { + + addConnection(AUDIO_NODE_ID, n, + RESAMPLING_NODE_ID, n); + + addConnection(RESAMPLING_NODE_ID, n, + OUTPUT_NODE_ID, n); + + } + + std::cout << "Default nodes created." << std::endl; + +} + + +void* ProcessorGraph::createNewProcessor(String& description)//, + // GenericProcessor* source, + // GenericProcessor* dest) +{ + + GenericProcessor* processor = createProcessorFromDescription(description); + + int id = currentNodeId++; + + if (processor != 0) { + + processor->setNodeId(id); // identifier within processor graph + + std::cout << " Adding node to graph with ID number " << id << std::endl; + + processor->setFilterViewport(filterViewport); + processor->setConfiguration(config); + processor->addActionListener(messageCenter); + + addNode(processor,id); // have to add it so it can be deleted by the graph + + return processor->createEditor(); + + } else { + + sendActionMessage("Not a valid processor type."); + + return 0; + } + +} + +void ProcessorGraph::clearConnections() +{ + + for (int i = 0; i < getNumConnections(); i++) + { + const Connection* connection = getConnection(i); + + if (connection->destNodeId == RESAMPLING_NODE_ID || + connection->destNodeId == OUTPUT_NODE_ID) + { + ; // leave it + } else { + removeConnection(i); + } + } + + // std::cout << "Clearing nodes..." << std::endl; + + // for (int i = 0; i < getNumNodes(); i++) + // { + // Node* node = getNode(i); + + // int id = node->nodeId; + + // if (!(id == RECORD_NODE_ID || id == AUDIO_NODE_ID || + // id == OUTPUT_NODE_ID || id == RESAMPLING_NODE_ID)) + // { + // removeNode(id); + // } + + // } + + // std::cout << "Remaining nodes: " << std::endl; + // for (int i = 0; i < getNumNodes(); i++) + // { + // Node* node = getNode(i); + // std::cout << " " << node->getProcessor()->getName() << std::endl; + // } + + // std::cout << std::endl; +} + +void ProcessorGraph::updateConnections(Array<SignalChainTabButton*, CriticalSection> tabs) +{ + clearConnections(); // clear processor graph + //createDefaultNodes(); // add audio and record nodes + std::cout << "Updating connections:" << std::endl; + + for (int n = 0; n < tabs.size(); n++) + { + std::cout << "Signal chain " << n << std::endl; + //if (tabs[n]->hasNewConnections()) + //{ + + GenericEditor* sourceEditor = (GenericEditor*) tabs[n]->getEditor(); + GenericProcessor* source = (GenericProcessor*) sourceEditor->getProcessor(); + + while (source != 0)// && destEditor->isEnabled()) + { + std::cout << "Source node: " << source->getName() << ", "; + GenericProcessor* dest = (GenericProcessor*) source->getDestNode(); + if (dest != 0) + { + std::cout << "Dest node: " << dest->getName() << std::endl; + } else { + std::cout << "no dest node." << std::endl; + } + + if (source->enabledState()) + { + + // add the source to the graph if it doesn't already exist + //Node* node = getNodeForId(source->getNodeId()); + //if (node == 0) + // addNode(source, source->getNodeId()); + + // add the connections to audio and record nodes if necessary + if (!(source->isSink() || source->isSource() || + source->isSplitter() || source->isMerger())) + { + std::cout << " Connecting to audio and record nodes." << std::endl; + + for (int chan = 0; chan < source->getNumOutputs(); chan++) { + + addConnection(source->getNodeId(), // sourceNodeID + chan, // sourceNodeChannelIndex + AUDIO_NODE_ID, // destNodeID + getNextFreeAudioChannel()); // destNodeChannelIndex + + addConnection(source->getNodeId(), // sourceNodeID + chan, // sourceNodeChannelIndex + RECORD_NODE_ID, // destNodeID + getNextFreeRecordChannel()); // destNodeChannelIndex + } + } + + if (dest != 0) { + + if (dest->enabledState()) + std::cout << "OK." << std::endl; + else + std::cout << "Not OK." << std::endl; + + if (dest->enabledState()) + { + + // add dest node to graph if it doesn't already exist + //node = getNodeForId(dest->getNodeId()); + //if (node == 0) + // addNode(dest, dest->getNodeId()); + + std::cout << " Connecting " << source->getName() << " channel "; + + for (int chan = 0; chan < source->getNumOutputs(); chan++) + { + + // eventually need to account for splitter and mergers + + std::cout << chan << " "; + + + addConnection(source->getNodeId(), // sourceNodeID + chan, // sourceNodeChannelIndex + dest->getNodeId(), // destNodeID + chan); // destNodeChannelIndex + } + + std::cout << " to " << dest->getName() << std::endl; + + std::cout << " Connecting " << source->getName() << + " event channel to " << + dest->getName() << std::endl; + // connect event channel + addConnection(source->getNodeId(), // sourceNodeID + midiChannelIndex, // sourceNodeChannelIndex + dest->getNodeId(), // destNodeID + midiChannelIndex); // destNodeChannelIndex + + } + + } + } + + source = dest; // switch source and dest + } // end while source != 0 + } // end "tabs" for loop +} // end method + +int ProcessorGraph::getNextFreeAudioChannel() +{ + return totalAudioConnections++; +} + +int ProcessorGraph::getNextFreeRecordChannel() +{ + return totalRecordConnections++; +} + +GenericProcessor* ProcessorGraph::createProcessorFromDescription(String& description) +{ + int splitPoint = description.indexOf("/"); + String processorType = description.substring(0,splitPoint); + String subProcessorType = description.substring(splitPoint+1); + + std::cout << processorType << "::" << subProcessorType << std::endl; + + GenericProcessor* processor = 0; + + if (processorType.equalsIgnoreCase("Sources")) { + + if (subProcessorType.equalsIgnoreCase("Intan Demo Board")) { + processor = new SourceNode(subProcessorType); + std::cout << "Creating a new data source." << std::endl; + } else if (subProcessorType.equalsIgnoreCase("Signal Generator")) + { + processor = new SignalGenerator(); + std::cout << "Creating a new signal generator." << std::endl; + } + + + sendActionMessage("New source node created."); + + + } else if (processorType.equalsIgnoreCase("Filters")) { + + if (subProcessorType.equalsIgnoreCase("Bandpass Filter")) { + std::cout << "Creating a new filter." << std::endl; + processor = new FilterNode(); + + } else if (subProcessorType.equalsIgnoreCase("Resampler")) { + std::cout << "Creating a new resampler." << std::endl; + processor = new ResamplingNode(false); + + } else if (subProcessorType.equalsIgnoreCase("Spike Detector")) { + std::cout << "Creating a new spike detector." << std::endl; + processor = new SpikeDetector(); + } + + sendActionMessage("New filter node created."); + + } else if (processorType.equalsIgnoreCase("Utilities")) { + + if (subProcessorType.equalsIgnoreCase("Splitter")) { + + std::cout << "Creating a new splitter." << std::endl; + processor = new Splitter(); + + sendActionMessage("New splitter created."); + + } + + } else if (processorType.equalsIgnoreCase("Sinks")) { + + //if (subProcessorType.equalsIgnoreCase("Stream Viewer")) { + + if (subProcessorType.equalsIgnoreCase("LFP Viewer")) { + std::cout << "Creating a display node." << std::endl; + processor = new LfpDisplayNode(); + + std::cout << "Graph data viewport: " << UI->getDataViewport() << std::endl; + processor->setDataViewport(UI->getDataViewport()); + processor->setUIComponent(UI); + } + //} + + sendActionMessage("New visualizer created."); + } + + return processor; +} + + +void ProcessorGraph::removeProcessor(GenericProcessor* processor) { + + std::cout << "Removing processor with ID " << processor->getNodeId() << std::endl; + + // GenericProcessor* source = processor->getSourceNode(); + // GenericProcessor* dest = processor->getDestNode(); + // int numInputs = processor->getNumInputs(); + + // std::cout << " Source " << source << std::endl; + // std::cout << " Dest " << dest << std::endl; + + removeNode(processor->getNodeId()); + + // eliminate connections for now + /*if (dest !=0 && source !=0) { + + std::cout << " Making new connections...." << std::endl; + + // connect source and dest + for (int chan = 0; chan < numInputs; chan++) { + + addConnection(source->getNodeId(), + chan, + dest->getNodeId(), + chan); + } + + }*/ + + // if (dest != 0) + // dest->setSourceNode(source); + + // if (source != 0) + // source->setDestNode(dest); + +} + +void ProcessorGraph::setUIComponent(UIComponent* ui) +{ + UI = ui; + //config = ui->getConfiguration(); +} + +void ProcessorGraph::setFilterViewport(FilterViewport* fv) +{ + filterViewport = fv; +} + +void ProcessorGraph::setMessageCenter(MessageCenter* mc) +{ + messageCenter = mc; +} + +void ProcessorGraph::setConfiguration(Configuration* c) +{ + config = c; +} + + +bool ProcessorGraph::enableProcessors() { + + updateConnections(filterViewport->requestSignalChain()); + + std::cout << "Enabling processors..." << std::endl; + + bool allClear; + + for (int i = 0; i < getNumNodes(); i++) + { + + Node* node = getNode(i); + + if (node->nodeId != OUTPUT_NODE_ID) + { + GenericProcessor* p = (GenericProcessor*) node->getProcessor(); + allClear = p->enable(); + + if (!allClear) { + sendActionMessage("Could not initialize acquisition. Is the Intan Board plugged in?"); + return false; + } + } + } + + filterViewport->signalChainCanBeEdited(false); + + sendActionMessage("Acquisition started."); + + return true; +} + +bool ProcessorGraph::disableProcessors() { + + std::cout << "Disabling processors..." << std::endl; + + bool allClear; + + for (int i = 0; i < getNumNodes(); i++) + { + Node* node = getNode(i); + if (node->nodeId != OUTPUT_NODE_ID) + { + GenericProcessor* p = (GenericProcessor*) node->getProcessor(); + allClear = p->disable(); + + if (!allClear) { + sendActionMessage("Could not stop acquisition."); + return false; + } + } + } + + filterViewport->signalChainCanBeEdited(true); + + sendActionMessage("Acquisition ended."); + + return true; +} + + +AudioNode* ProcessorGraph::getAudioNode() { + + Node* node = getNodeForId(AUDIO_NODE_ID); + return (AudioNode*) node->getProcessor(); + +} + +RecordNode* ProcessorGraph::getRecordNode() { + + Node* node = getNodeForId(RECORD_NODE_ID); + return (RecordNode*) node->getProcessor(); + +} + +GenericProcessor* ProcessorGraph::getSourceNode(int snID) { + + std::cout << "Requested ID: " << snID << std::endl; + //if (snID != 0) { + Node* node = getNodeForId(snID); + + if (node != 0) { + return (GenericProcessor*) node->getProcessor(); + } else { + return 0; + } + +} + +void ProcessorGraph::saveState() +{ + File file = File("./savedState.xml"); + filterViewport->saveState(file); +} + +void ProcessorGraph::loadState() +{ + File file = File("./savedState.xml"); + filterViewport->loadState(file); +} \ No newline at end of file diff --git a/Source/Processors/ProcessorGraph.h b/Source/Processors/ProcessorGraph.h new file mode 100644 index 0000000000000000000000000000000000000000..00697264e8664c295ab977c487078d4f17c1ddbb --- /dev/null +++ b/Source/Processors/ProcessorGraph.h @@ -0,0 +1,89 @@ +/* + ============================================================================== + + ProcessorGraph.h + Created: 30 Apr 2011 8:36:35pm + Author: jsiegle + + ============================================================================== +*/ + +#ifndef __PROCESSORGRAPH_H_124F8B50__ +#define __PROCESSORGRAPH_H_124F8B50__ + +#include "../../JuceLibraryCode/JuceHeader.h" + +class GenericProcessor; +class RecordNode; +class SourceNode; +class FilterViewport; +class SignalChainTabButton; +class AudioNode; +class UIComponent; +class Configuration; +class MessageCenter; + +class ProcessorGraph : public AudioProcessorGraph, + public ActionBroadcaster +{ +public: + ProcessorGraph(); + ~ProcessorGraph(); + + void* createNewProcessor(String& description); + GenericProcessor* createProcessorFromDescription(String& description); + + void removeProcessor(GenericProcessor* processor); + + bool enableProcessors(); + bool disableProcessors(); + + RecordNode* getRecordNode(); + GenericProcessor* getSourceNode(int snID); + AudioNode* getAudioNode(); + + void setUIComponent(UIComponent* ui); + void setFilterViewport(FilterViewport *fv); + void setMessageCenter(MessageCenter* mc); + void setConfiguration(Configuration* config); + + void updateConnections(Array<SignalChainTabButton*, CriticalSection>); + + + void saveState(); + void loadState(); + //const String saveState(const File& file); + //const String loadState(const File& file); + + //XmlElement* createNodeXml(GenericProcessor*); + + int getNextFreeAudioChannel(); + int getNextFreeRecordChannel(); + +private: + + int currentNodeId; + + Array<int> source_node_IDs; + + const int RECORD_NODE_ID; + const int AUDIO_NODE_ID; + const int OUTPUT_NODE_ID; + const int RESAMPLING_NODE_ID; + + void createDefaultNodes(); + void clearConnections(); + + UIComponent* UI; + FilterViewport* filterViewport; + Configuration* config; + MessageCenter* messageCenter; + + int totalAudioConnections; + int totalRecordConnections; + +}; + + + +#endif // __PROCESSORGRAPH_H_124F8B50__ diff --git a/Source/Processors/RecordNode.cpp b/Source/Processors/RecordNode.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8975e459cd257d99bcf85c299891780fef4595de --- /dev/null +++ b/Source/Processors/RecordNode.cpp @@ -0,0 +1,84 @@ +/* + ============================================================================== + + RecordNode.cpp + Created: 10 May 2011 7:17:09pm + Author: jsiegle + + ============================================================================== +*/ + +#include "RecordNode.h" + +RecordNode::RecordNode() + : GenericProcessor("Record Node"), isRecording(false) +{ + + // need to update this: + setPlayConfigDetails(64,0,44100.0,128); + + outputFile = File("./data"); // create output file + outputStream = 0; + +} + + +RecordNode::~RecordNode() { + +} + + +void RecordNode::setParameter (int parameterIndex, float newValue) +{ + if (parameterIndex == 1) { + isRecording = true; + } else { + isRecording = false; + } +} + + +void RecordNode::prepareToPlay (double sampleRate_, int estimatedSamplesPerBlock) +{ + + outputStream = outputFile.createOutputStream(); +} + +void RecordNode::releaseResources() +{ + + if (outputStream != 0) { + outputStream->flush(); + + delete(outputStream); + outputStream = 0; + } +} + +float RecordNode::getFreeSpace() +{ + return (1.0f-float(outputFile.getBytesFreeOnVolume())/float(outputFile.getVolumeTotalSize())); +} + +void RecordNode::process(AudioSampleBuffer &buffer, + MidiBuffer &midiMessages, + int& nSamples) +{ + + //std::cout << "Record node processing block." << std::endl; + //std::cout << "Num channels: " << buffer.getNumChannels() << std::endl; + + if (isRecording) { + + //int nSamps = getNumSamples(midiMessages); + + for (int n = 0; n < nSamples; n++) { + + float* sample = buffer.getSampleData(1,n); + outputStream->writeFloat(*sample); + //AudioDataConverters::convertFloatToInt16BE(&sample) + //); + } + } + +} diff --git a/Source/Processors/RecordNode.h b/Source/Processors/RecordNode.h new file mode 100644 index 0000000000000000000000000000000000000000..325ee2b41704f48f6f797c95cfc189840d5a9198 --- /dev/null +++ b/Source/Processors/RecordNode.h @@ -0,0 +1,50 @@ +/* + ============================================================================== + + RecordNode.h + Created: 10 May 2011 7:17:09pm + Author: jsiegle + + ============================================================================== +*/ + +#ifndef __RECORDNODE_H_FB9B1CA7__ +#define __RECORDNODE_H_FB9B1CA7__ + + +#include "../../JuceLibraryCode/JuceHeader.h" +#include <stdio.h> + +#include "GenericProcessor.h" + +class RecordNode : public GenericProcessor +{ +public: + + // real member functions: + RecordNode(); + ~RecordNode(); + + + void prepareToPlay (double sampleRate, int estimatedSamplesPerBlock); + void releaseResources(); + void process(AudioSampleBuffer &buffer, MidiBuffer &midiMessages, int& nSamples); + + void setParameter (int parameterIndex, float newValue); + + float getFreeSpace(); + +private: + + File outputFile; + FileOutputStream* outputStream; + + bool isRecording; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RecordNode); + +}; + + + +#endif // __RECORDNODE_H_FB9B1CA7__ diff --git a/Source/Processors/ResamplingNode.cpp b/Source/Processors/ResamplingNode.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cf1c80b56155321ddfceccc3cc510018679aa47d --- /dev/null +++ b/Source/Processors/ResamplingNode.cpp @@ -0,0 +1,328 @@ +/* + ============================================================================== + + ResamplingNode.cpp + Created: 7 May 2011 5:07:58pm + Author: jsiegle + + ============================================================================== +*/ + +#include "ResamplingNode.h" +#include <stdio.h> + +ResamplingNode::ResamplingNode(bool destBufferType) + : GenericProcessor("Resampling Node"), + ratio (1.0), lastRatio (1.0), + destBufferPos(0), destBufferIsTempBuffer(destBufferType), + destBufferSampleRate(44100.0), sourceBufferSampleRate(40000.0), + destBuffer(0), tempBuffer(0), isTransmitting(false) + +{ + + setNumInputs(2); + setNumOutputs(2); + + setPlayConfigDetails(2, // number of inputs + 2, // number of outputs + 44100.0, // sampleRate + 128); // blockSize + + filter = new Dsp::SmoothedFilterDesign + <Dsp::RBJ::Design::LowPass, 1> (1024); + + if (destBufferIsTempBuffer) + destBufferWidth = 1024; + else + destBufferWidth = 1000; + + destBufferTimebaseSecs = 1.0; + + destBuffer = new AudioSampleBuffer(16, destBufferWidth); + tempBuffer = new AudioSampleBuffer(16, destBufferWidth); + + + + // filter->getKind() + // filter->getName() + // filter->getNumParams() + // filter->getParamInfo() + // filter->getDefaultParams() + // filter->getParams() + // filter->getParam() + + // filter->setParam() + // filter->findParamId() + // filter->setParamById() + // filter->setParams() + // filter->copyParamsFrom() + + // filter->getPoleZeros() + // filter->response() + // filter->getNumChannels() + // filter->reset() + // filter->process() + + // Filter families: + // RBJ: from RBJ cookbook (audio-specific) + // Butterworth + // ChebyshevI: ripple in the passband + // ChebyshevII: ripple in the stopband + // Elliptic: ripple in passband and stopband + // Bessel: theoretically with linear phase + // Legendre: steepest transition and monotonic passband + // Custom: poles and zeros can be specified directly + + // Filter classes: + // vary by filter family +} + +ResamplingNode::~ResamplingNode() +{ + filter = 0; + deleteAndZero(destBuffer); + deleteAndZero(tempBuffer); + //filterEditor = 0; +} + +//AudioProcessorEditor* ResamplingNode::createEditor( ) +//{ + //filterEditor = new FilterEditor(this); + + //std::cout << "Creating editor." << std::endl; + //filterEditor = new FilterEditor(this); + //return filterEditor; + +// return 0; +//} + + +void ResamplingNode::setParameter (int parameterIndex, float newValue) +{ + + switch (parameterIndex) { + + case 0: destBufferTimebaseSecs = newValue; break; + case 1: destBufferWidth = roundToInt(newValue); + + } + + // reset to zero and clear if timebase or width has changed. + destBufferPos = 0; + destBuffer->clear(); + +} + + +void ResamplingNode::prepareToPlay (double sampleRate_, int estimatedSamplesPerBlock) +{ + + //std::cout << "ResamplingNode preparing to play." << std::endl; + + if (destBufferIsTempBuffer) { + destBufferSampleRate = sampleRate_; + tempBuffer->setSize(getNumInputs(), estimatedSamplesPerBlock); + } + else { + destBufferSampleRate = float(destBufferWidth) / destBufferTimebaseSecs; + destBuffer->setSize(getNumInputs(), destBufferWidth); + } + + destBuffer->clear(); + tempBuffer->clear(); + + destBufferPos = 0; + + updateFilter(); + +} + +void ResamplingNode::updateFilter() { + + double cutoffFreq = (ratio > 1.0) ? 2 * destBufferSampleRate // downsample + : destBufferSampleRate / 2; // upsample + + double sampleFreq = (ratio > 1.0) ? sourceBufferSampleRate // downsample + : destBufferSampleRate; // upsample + + Dsp::Params params; + params[0] = sampleFreq; // sample rate + params[1] = cutoffFreq; // cutoff frequency + params[2] = 1.25; //Q // + + filter->setParams (params); + +} + +void ResamplingNode::releaseResources() +{ + + // if (destBuffer != 0) { + // deleteAndZero(destBuffer); + // } + // if (tempBuffer != 0) { + // deleteAndZero(tempBuffer); + // } +} + +void ResamplingNode::process(AudioSampleBuffer &buffer, + MidiBuffer &midiMessages, + int& nSamples) +{ + + //std::cout << "Resampling node sample count: " << buffer.getNumSamples() << std::endl; + + int nSamps = nSamples; + int valuesNeeded; + + //std::cout << "END OF OLD BUFFER." << std::endl; + + if (destBufferIsTempBuffer) { + ratio = float(nSamps) / float(buffer.getNumSamples()); + valuesNeeded = tempBuffer->getNumSamples(); + } else { + ratio = sourceBufferSampleRate / destBufferSampleRate; + valuesNeeded = (int) buffer.getNumSamples() / ratio; + //std::cout << std::endl; + //std::cout << "Ratio: " << ratio << std::endl; + //std::cout << "Values needed: " << valuesNeeded << std::endl; + } + + + + if (lastRatio != ratio) { + updateFilter(); + lastRatio = ratio; + } + + + if (ratio > 1.0001) { + // pre-apply filter before downsampling + filter->process (nSamps, buffer.getArrayOfChannels()); + } + + + // initialize variables + tempBuffer->clear(); + int sourceBufferPos = 0; + int sourceBufferSize = buffer.getNumSamples(); + float subSampleOffset = 0.0; + int nextPos = (sourceBufferPos + 1) % sourceBufferSize; + + int tempBufferPos; + //int totalSamples = 0; + + // code modified from "juce_ResamplingAudioSource.cpp": + + for (tempBufferPos = 0; tempBufferPos < valuesNeeded; tempBufferPos++) + { + float gain = 1.0; + float alpha = (float) subSampleOffset; + float invAlpha = 1.0f - alpha; + + for (int channel = 0; channel < buffer.getNumChannels(); ++channel) { + + tempBuffer->addFrom(channel, // destChannel + tempBufferPos, // destSampleOffset + buffer, // source + channel, // sourceChannel + sourceBufferPos,// sourceSampleOffset + 1, // number of samples + invAlpha*gain); // gain to apply to source + + tempBuffer->addFrom(channel, // destChannel + tempBufferPos, // destSampleOffset + buffer, // source + channel, // sourceChannel + nextPos, // sourceSampleOffset + 1, // number of samples + alpha*gain); // gain to apply to source + + } + + subSampleOffset += ratio; + + while (subSampleOffset >= 1.0) + { + if (++sourceBufferPos >= sourceBufferSize) + sourceBufferPos = 0; + + nextPos = (sourceBufferPos + 1) % sourceBufferSize; + subSampleOffset -= 1.0; + } + } + + + if (ratio < 0.9999) { + + filter->process (tempBufferPos, tempBuffer->getArrayOfChannels()); + // apply the filter after upsampling + ///////filter->process (totalSamples, buffer.getArrayOfChannels()); + } else if (ratio <= 1.0001) { + + // no resampling is being applied, no need to filter, BUT... + // keep the filter stoked with samples to avoid discontinuities + + } + + if (destBufferIsTempBuffer) { + + // copy the temp buffer into the original buffer + buffer = *tempBuffer; + + //buffer.setSize(2,0,true,false,true); + + //for (int n = 0; n < buffer.getNumSamples(); n+= 10) + //std::cout << buffer.getRMSLevel(1,0,buffer.getNumSamples()) << " "; + + //std::cout << "END OF NEW BUFFER." << std::endl; + + } else { + + //std::cout << "Copying into dest buffer..." << std::endl; + + // copy the temp buffer into the destination buffer + + int pos = 0; + + while (*tempBuffer->getSampleData(0,pos) != 0) + pos++; + + int spaceAvailable = destBufferWidth - destBufferPos; + int blockSize1 = (spaceAvailable > pos) ? pos : spaceAvailable; + int blockSize2 = (spaceAvailable > pos) ? 0 : (pos - spaceAvailable); + + for (int channel = 0; channel < destBuffer->getNumChannels(); channel++) { + + // copy first block + destBuffer->copyFrom(channel, //destChannel + destBufferPos, //destStartSample + *tempBuffer, //source + channel, //sourceChannel + 0, //sourceStartSample + blockSize1 //numSamples + ); + + // copy second block + destBuffer->copyFrom(channel, //destChannel + 0, //destStartSample + *tempBuffer, //source + channel, //sourceChannel + blockSize1, //sourceStartSample + blockSize2 //numSamples + ); + + } + + //destBufferPos = (spaceAvailable > tempBufferPos) ? destBufferPos + + destBufferPos += pos; + destBufferPos %= destBufferWidth; + + //std::cout << "Temp buffer position: " << tempBufferPos << std::endl; + //std::cout << "Resampling node value:" << *destBuffer->getSampleData(0,0) << std::endl; + + } + + +} \ No newline at end of file diff --git a/Source/Processors/ResamplingNode.h b/Source/Processors/ResamplingNode.h new file mode 100644 index 0000000000000000000000000000000000000000..332f435942fa430e623bb395a08b96bebaec3e6f --- /dev/null +++ b/Source/Processors/ResamplingNode.h @@ -0,0 +1,68 @@ +/* + ============================================================================== + + ResamplingNode.h + Created: 7 May 2011 5:07:58pm + Author: jsiegle + + ============================================================================== +*/ + +#ifndef __RESAMPLINGNODE_H_79663B0__ +#define __RESAMPLINGNODE_H_79663B0__ + + +#include "../../JuceLibraryCode/JuceHeader.h" +#include "../Dsp/Dsp.h" +#include "GenericProcessor.h" + +//class ResamplingNode; + +class ResamplingNode : public GenericProcessor + +{ +public: + + // real member functions: + ResamplingNode(bool destBufferIsTempBuffer); + ~ResamplingNode(); + + AudioSampleBuffer* getBufferAddress() { return destBuffer; } + void updateFilter(); + + void prepareToPlay (double sampleRate, int estimatedSamplesPerBlock); + void releaseResources(); + void process(AudioSampleBuffer &buffer, MidiBuffer &midiMessages, int& nSamples); + void setParameter (int parameterIndex, float newValue); + + AudioSampleBuffer* getContinuousBuffer() {return destBuffer;} + + +private: + + // sample rate, timebase, and ratio info: + double sourceBufferSampleRate, destBufferSampleRate; + double ratio, lastRatio; + double destBufferTimebaseSecs; + int destBufferWidth; + + // major objects: + Dsp::Filter* filter; + AudioSampleBuffer* destBuffer; + AudioSampleBuffer* tempBuffer; + + // is the destBuffer a temp buffer or not? + bool destBufferIsTempBuffer; + bool isTransmitting; + + // indexing objects that persist between rounds: + int destBufferPos; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ResamplingNode); + +}; + + + + +#endif // __RESAMPLINGNODE_H_79663B0__ diff --git a/Source/Processors/SignalGenerator.cpp b/Source/Processors/SignalGenerator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..772b32803d23d226c4940721af96dc26561a5d78 --- /dev/null +++ b/Source/Processors/SignalGenerator.cpp @@ -0,0 +1,117 @@ +/* + ============================================================================== + + SignalGenerator.cpp + Created: 9 May 2011 8:11:41pm + Author: jsiegle + + ============================================================================== +*/ + + +#include "SignalGenerator.h" +//#include "SourceNodeEditor.h" +#include <stdio.h> + +SignalGenerator::SignalGenerator() + : GenericProcessor("Signal Generator"), + + frequency(10.0), + sampleRate (44100.0), + currentPhase (0.0), + phasePerSample (0.0), + amplitude (0.02f) + +{ + + setNumOutputs(16); + setNumInputs(0); + + + setPlayConfigDetails(getNumInputs(), getNumOutputs(), 44100.0, 128); + + +} + +SignalGenerator::~SignalGenerator() +{ + config->removeDataSource(this); +} + + +void SignalGenerator::setConfiguration(Configuration* cf) +{ + config = cf; + + DataSource* d = new DataSource(this, config); + + // add a new data source to this configuration + config->addDataSource(d); + +} + +AudioProcessorEditor* SignalGenerator::createEditor( ) +{ + SignalGeneratorEditor* ed = new SignalGeneratorEditor(this, viewport); + setEditor(ed); + + std::cout << "Creating editor." << std::endl; + //filterEditor = new FilterEditor(this); + return ed; +} + + +void SignalGenerator::setParameter (int parameterIndex, float newValue) +{ + //std::cout << "Message received." << std::endl; + + if (parameterIndex == 0) + amplitude = newValue; + else + frequency = newValue; + + phasePerSample = double_Pi * 2.0 / (sampleRate / frequency); + +} + +void SignalGenerator::prepareToPlay (double sampleRate_, int estimatedSamplesPerBlock) +{ + sampleRate = sampleRate_; + phasePerSample = double_Pi * 2.0 / (sampleRate / frequency); + //std::cout << "Prepare to play: " << std::endl; +} + +bool SignalGenerator::enable () { + + std::cout << "Signal generator received enable signal." << std::endl; + return true; +} + +bool SignalGenerator::disable() { + + std::cout << "Signal generator received disable signal." << std::endl; + return true; +} + +void SignalGenerator::releaseResources() +{ +} + +void SignalGenerator::process(AudioSampleBuffer &buffer, + MidiBuffer &midiMessages, + int& nSamps) +{ + + //std::cout << buffer.getNumChannels() << std::endl; + nSamps = buffer.getNumSamples(); + + for (int i = 0; i < nSamps; ++i) + { + const float sample = amplitude * (float) std::sin (currentPhase); + currentPhase += phasePerSample; + + for (int j = buffer.getNumChannels(); --j >= 0;) + // dereference pointer to one of the buffer's samples + *buffer.getSampleData (j, i) = sample; + } +} diff --git a/Source/Processors/SignalGenerator.h b/Source/Processors/SignalGenerator.h new file mode 100644 index 0000000000000000000000000000000000000000..1f39e93cade65e7248bf874f35748a2e31f45353 --- /dev/null +++ b/Source/Processors/SignalGenerator.h @@ -0,0 +1,63 @@ +/* + ============================================================================== + + SignalGenerator.h + Created: 9 May 2011 8:11:41pm + Author: jsiegle + + ============================================================================== +*/ + +#ifndef __SIGNALGENERATOR_H_EAA44B0B__ +#define __SIGNALGENERATOR_H_EAA44B0B__ + + +#include "../../JuceLibraryCode/JuceHeader.h" +#include "GenericProcessor.h" +#include "Editors/SignalGeneratorEditor.h" + +//class SourceNodeEditor; + +class SignalGenerator : public GenericProcessor + +{ +public: + + // real member functions: + SignalGenerator(); + ~SignalGenerator(); + + void prepareToPlay (double sampleRate, int estimatedSamplesPerBlock); + void releaseResources(); + void process(AudioSampleBuffer &buffer, MidiBuffer &midiMessages, int& nSamples); + + void setParameter (int parameterIndex, float newValue); + + void setConfiguration(Configuration* cf); + + float getSampleRate() {return 44100.0;} + + AudioProcessorEditor* createEditor(); + bool hasEditor() const {return true;} + + bool enable(); + bool disable(); + + bool isSource() {return true;} + +private: + double frequency, sampleRate; + double currentPhase, phasePerSample; + float amplitude; + //SourceNodeEditor* sourceEditor; + + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SignalGenerator); + +}; + + + + + +#endif // __SIGNALGENERATOR_H_EAA44B0B__ diff --git a/Source/Processors/SourceNode.cpp b/Source/Processors/SourceNode.cpp new file mode 100644 index 0000000000000000000000000000000000000000..67ca7a1b0f07d0ea35f3445225e350eb046eede4 --- /dev/null +++ b/Source/Processors/SourceNode.cpp @@ -0,0 +1,190 @@ +/* + ============================================================================== + + SourceNode.cpp + Created: 7 May 2011 5:07:14pm + Author: jsiegle + + ============================================================================== +*/ + + +#include "SourceNode.h" +#include "Editors/SourceNodeEditor.h" +#include <stdio.h> + +SourceNode::SourceNode(const String& name_) + : GenericProcessor(name_), + dataThread(0) +{ + if (getName().equalsIgnoreCase("Intan Demo Board")) { + setNumOutputs(16); + setNumInputs(0); + } else if (getName().equalsIgnoreCase("Custom FPGA")) { + setNumOutputs(32); + setNumInputs(0); + } else if (getName().equalsIgnoreCase("File Reader")) { + setNumOutputs(16); + setNumInputs(0); + } + + setPlayConfigDetails(getNumInputs(), getNumOutputs(), 44100.0, 128); + + //sendActionMessage("Intan Demo Board source created."); + //sendMessage("Intan Demo Board source created."); + +} + +SourceNode::~SourceNode() +{ + config->removeDataSource(this); +} + +float SourceNode::getSampleRate() +{ + if (getName().equalsIgnoreCase("Intan Demo Board")) { + return 25000.0; + } else if (getName().equalsIgnoreCase("Custom FPGA")) { + return 25000.0; + } else if (getName().equalsIgnoreCase("File Reader")) { + return 40000.0; + } else { + return 44100.0; + } + +} + +// void SourceNode::setName(const String name_) +// { +// name = name_; + +// // Source node type determines configuration info + +void SourceNode::setConfiguration(Configuration* cf) +{ + config = cf; + + DataSource* d = new DataSource(this, config); + + // // add tetrodes -- should really be doing this dynamically + d->addTrode(4, "TT1"); + d->addTrode(4, "TT2"); + d->addTrode(4, "TT3"); + d->addTrode(4, "TT4"); + + for (int n = 0; n < d->numTetrodes(); n++) + { + std::cout << d->getTetrode(n)->getName(); + } + std::cout << std::endl; + + // // add a new data source to this configuration + config->addDataSource(d); + +} + + +void SourceNode::setParameter (int parameterIndex, float newValue) +{ + //std::cout << "Got parameter change notification"; +} + +void SourceNode::prepareToPlay (double sampleRate_, int estimatedSamplesPerBlock) +{ + // + // We take care of thread creation and destruction in separate enable/disable function + // + // prepareToPlay is called whenever the graph is edited, not only when callbacks are + // about to begin + // +} + +void SourceNode::releaseResources() {} + + +AudioProcessorEditor* SourceNode::createEditor() +{ + SourceNodeEditor* ed = new SourceNodeEditor(this, viewport); + setEditor(ed); + + std::cout << "Creating editor." << std::endl; + //filterEditor = new FilterEditor(this); + return ed; + + //return 0; +} + +// void SourceNode::setSourceNode(GenericProcessor* sn) +// { +// sourceNode = 0; +// } + +// void SourceNode::setDestNode(GenericProcessor* dn) +// { +// destNode = dn; +// if (dn != 0) +// dn->setSourceNode(this); +// } + +//void SourceNode::createEditor() { + +//} + +bool SourceNode::enable() { + + std::cout << "Source node received enable signal" << std::endl; + + bool return_code = true; + + if (getName().equalsIgnoreCase("Intan Demo Board")) { + + dataThread = new IntanThread(); + inputBuffer = dataThread->getBufferAddress(); + return_code = dataThread->threadStarted(); + + if (!return_code) + deleteAndZero(dataThread); + + } else if (getName().equalsIgnoreCase("Custom FPGA")) { + dataThread = new FPGAThread(); + inputBuffer = dataThread->getBufferAddress(); + } else if (getName().equalsIgnoreCase("File Reader")) { + dataThread = new FileReaderThread(); + inputBuffer = dataThread->getBufferAddress(); + } + + return return_code; + +} + +bool SourceNode::disable() { + + std::cout << "Source node received disable signal" << std::endl; + + if (dataThread != 0) { + delete dataThread; + dataThread = 0; + } + + return true; +} + + +void SourceNode::process(AudioSampleBuffer &outputBuffer, + MidiBuffer &midiMessages, + int& nSamples) +{ + + //std::cout << "Source node processing." << std::endl; + //std::cout << outputBuffer.getNumChannels() << " " << outputBuffer.getNumSamples() << std::endl; + + + outputBuffer.clear(); + nSamples = inputBuffer->readAllFromBuffer(outputBuffer,outputBuffer.getNumSamples()); + // //setNumSamples(numRead); // write the total number of samples + // setNumSamples(midiMessages, numRead); + //std::cout << numRead << std::endl; +} + + + diff --git a/Source/Processors/SourceNode.h b/Source/Processors/SourceNode.h new file mode 100644 index 0000000000000000000000000000000000000000..89f0f7227fa3525531614a1fbae851913f013192 --- /dev/null +++ b/Source/Processors/SourceNode.h @@ -0,0 +1,70 @@ +/* + ============================================================================== + + SourceNode.h + Created: 7 May 2011 5:07:14pm + Author: jsiegle + + ============================================================================== +*/ + +#ifndef __SOURCENODE_H_DCE798F1__ +#define __SOURCENODE_H_DCE798F1__ + +#include "../../JuceLibraryCode/JuceHeader.h" +#include <ftdi.h> +#include <stdio.h> +#include "DataThreads/DataBuffer.h" +#include "DataThreads/IntanThread.h" +#include "DataThreads/FPGAThread.h" +#include "DataThreads/FileReaderThread.h" +#include "GenericProcessor.h" + +class SourceNode : public GenericProcessor + +{ +public: + + // real member functions: + SourceNode(const String& name); + ~SourceNode(); + + //void setName(const String name_); + + void prepareToPlay (double sampleRate, int estimatedSamplesPerBlock); + void releaseResources(); + void process(AudioSampleBuffer &buffer, MidiBuffer &midiMessages, int& nSamples); + + void setParameter (int parameterIndex, float newValue); + + void setConfiguration(Configuration* cf); + + float getSampleRate(); + + // void setSourceNode(GenericProcessor* sn); + // void setDestNode(GenericProcessor* dn); + + AudioProcessorEditor* createEditor(); + bool hasEditor() const {return true;} + + bool enable(); + bool disable(); + + bool isSource() {return true;} + +private: + + //const String name; + + DataThread* dataThread; + DataBuffer* inputBuffer; + + int* numSamplesInThisBuffer; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SourceNode); + +}; + + +#endif // __SOURCENODE_H_DCE798F1__ + diff --git a/Source/Processors/SpikeDetector.cpp b/Source/Processors/SpikeDetector.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2139e68b6a29d5b12a6464772d0cb82bd4f99752 --- /dev/null +++ b/Source/Processors/SpikeDetector.cpp @@ -0,0 +1,170 @@ +/* + ============================================================================== + + SpikeDetector.cpp + Created: 14 Aug 2011 3:36:00pm + Author: jsiegle + + ============================================================================== +*/ + +#include <stdio.h> +#include "SpikeDetector.h" + +SpikeDetector::SpikeDetector() + : GenericProcessor("Spike Detector"), + sampleRate (40000.0), threshold(5000.0), prePeakMs(0.2), postPeakMs(0.6), + accumulator(0) + +{ + + spikeBuffer = new MidiBuffer(); + +} + +SpikeDetector::~SpikeDetector() +{ + deleteAndZero(spikeBuffer); +} + + +AudioProcessorEditor* SpikeDetector::createEditor() +{ + + SpikeDetectorEditor* editor = new SpikeDetectorEditor(this, viewport); + + std::cout << "Creating editor." << std::endl; + + setEditor(editor); + + return editor; +} + +void SpikeDetector::setParameter (int parameterIndex, float newValue) +{ + //std::cout << "Message received." << std::endl; + if (parameterIndex == 0) { + for (int n = 0; n < getNumOutputs(); n++) + { + thresh.set(n,newValue); + } + } + +} + + +void SpikeDetector::prepareToPlay (double sampleRate_, int estimatedSamplesPerBlock) +{ + //std::cout << "SpikeDetector node preparing." << std::endl; + prePeakSamples = int((prePeakMs / 1000.0f) / (1/sampleRate)); + postPeakSamples = int((postPeakMs / 1000.0f) / (1/sampleRate)); + + thresh.ensureStorageAllocated(getNumOutputs()); + channels.ensureStorageAllocated(getNumOutputs()); + nChans.ensureStorageAllocated(getNumOutputs()); + isActive.ensureStorageAllocated(getNumOutputs()); + lastSpike.ensureStorageAllocated(getNumOutputs()); + + for (int n = 0; n < getNumOutputs(); n++) + { + isActive.set(n,false); + lastSpike.set(n,-40); + } + + // check configuration + for (int ds = 0; ds < config->numDataSources(); ds++) + { + for (int tt = 0; tt < config->getSource(ds)->numTetrodes(); tt++) + { + + Trode* t = config->getSource(ds)->getTetrode(tt); + + for (int ch = 0; ch < t->numChannels(); ch++) + { + thresh.set(t->getChannel(ch),t->getThreshold(ch)); + channels.set(t->getChannel(ch),t->getRawDataPointer()); + nChans.set(t->getChannel(ch),t->numChannels()); + isActive.set(t->getChannel(ch),t->getState(ch)); + } + } + } + +} + +void SpikeDetector::releaseResources() +{ + thresh.clear(); + channels.clear(); + nChans.clear(); + isActive.clear(); + lastSpike.clear(); +} + +void SpikeDetector::process(AudioSampleBuffer &buffer, + MidiBuffer &midiMessages, + int& nSamples) +{ + + int maxSamples = nSamples;//getNumSamples(midiMessages); + int spikeSize = 2 + prePeakSamples*2 + postPeakSamples*2; + + spikeBuffer->clear(); + + for (int sample = prePeakSamples + 1; sample < maxSamples - postPeakSamples - 1; sample++) + { + for (int chan = 0; chan < getNumOutputs(); chan++) + { + if (isActive[chan] && lastSpike[chan]+spikeSize < sample) // channel is active + { + if (*buffer.getSampleData(chan,sample) > thresh[chan]) + { + // if thresh cross is detected on one channel + // save a waveform on all of them + // std::cout << "Thresh cross on channel " << chan << ": " << thresh[chan] << std::endl; + // std::cout << " Capturing spikes on " << nChans[chan] << " channels." << std::endl; + + for (int wire = 0; wire < nChans[chan]; wire++) + { + int* firstChan = channels[chan]; + int channelNum = *(firstChan+wire); + + //std::cout << " Found spike on channel " << channelNum << std::endl; + + uint8 data[spikeSize]; + data[0] = channelNum >> 8; // channel most-significant byte + data[1] = channelNum & 0xFF; // channel least-significant byte + + // not currently looking for peak, just centering on thresh cross + uint8* dataptr = data+2; + + for (int s = -prePeakSamples; s < postPeakSamples; s++) { + + uint16 sampleValue = uint16(*buffer.getSampleData(channelNum, sample+s) + 32768); + + *dataptr++ = uint8(sampleValue >> 8); + *dataptr++ = uint8(sampleValue & 255); + + } + + // broadcast spike + spikeBuffer->addEvent(data, // spike data + sizeof(data), // total bytes + sample); // sample index + + // keep track of last spike + lastSpike.set(channelNum, sample-prePeakSamples); + + } + } + } + } + } + + // reset lastSpike at the end of each buffer + for (int n = 0; n < getNumOutputs(); n++) + { + lastSpike.set(n,-40); + } + + +} diff --git a/Source/Processors/SpikeDetector.h b/Source/Processors/SpikeDetector.h new file mode 100644 index 0000000000000000000000000000000000000000..187d4c85de2803bf9b40169b3cfd674368f3bdc6 --- /dev/null +++ b/Source/Processors/SpikeDetector.h @@ -0,0 +1,63 @@ +/* + ============================================================================== + + SpikeDetector.h + Created: 14 Aug 2011 3:36:00pm + Author: jsiegle + + ============================================================================== +*/ + +#ifndef __SPIKEDETECTOR_H_3F920F95__ +#define __SPIKEDETECTOR_H_3F920F95__ + +#include "../../JuceLibraryCode/JuceHeader.h" +#include "GenericProcessor.h" +#include "Editors/SpikeDetectorEditor.h" +#include "../UI/Configuration.h" + +class SpikeDetectorEditor; +class FilterViewport; + +class SpikeDetector : public GenericProcessor + +{ +public: + + SpikeDetector(); + ~SpikeDetector(); + + void prepareToPlay (double sampleRate, int estimatedSamplesPerBlock); + void releaseResources(); + void process(AudioSampleBuffer &buffer, MidiBuffer &midiMessages, int& nSamples); + void setParameter (int parameterIndex, float newValue); + + MidiBuffer* getEventBuffer() {return spikeBuffer;} + + AudioProcessorEditor* createEditor(); + + bool hasEditor() const {return true;} + +private: + double sampleRate, threshold; + double prePeakMs, postPeakMs; + int prePeakSamples, postPeakSamples; + int accumulator; + + Array<float> thresh; + Array<int*> channels; + Array<int> nChans; + Array<bool> isActive; + Array<int> lastSpike; + + MidiBuffer* spikeBuffer; + + //AudioData::ConverterInstance<AudioData::Float32, AudioData::Int16> converter; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SpikeDetector); + +}; + + + +#endif // __SPIKEDETECTOR_H_3F920F95__ diff --git a/Source/Processors/Utilities/Splitter.cpp b/Source/Processors/Utilities/Splitter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0826971518eb675f016bf7a847d59d5a80c1a3a9 --- /dev/null +++ b/Source/Processors/Utilities/Splitter.cpp @@ -0,0 +1,65 @@ +/* + ============================================================================== + + Splitter.cpp + Created: 17 Aug 2011 1:02:57am + Author: jsiegle + + ============================================================================== +*/ + + +#include "Splitter.h" +#include "../Editors/SplitterEditor.h" + +Splitter::Splitter() + : GenericProcessor("Splitter"), + destNodeA(0), destNodeB(0), activePath(0) + { + + } + +Splitter::~Splitter() +{ + +} + +AudioProcessorEditor* Splitter::createEditor() +{ + SplitterEditor* editor = new SplitterEditor(this, viewport); + setEditor(editor); + + std::cout << "Creating editor." << std::endl; + return editor; +} + +void Splitter::setDestNode(GenericProcessor* dn) +{ + + destNode = dn; + + if (activePath == 0) { + std::cout << "Setting destination node A." << std::endl; + destNodeA = dn; + } else { + destNodeB = dn; + std::cout << "Setting destination node B." << std::endl; + + } +} + +void Splitter::switchDest(int destNum) { + + activePath = destNum; + + if (destNum == 0) + { + setDestNode(destNodeA); + } else + { + setDestNode(destNodeB); + } + + //viewport->setActiveEditor((GenericEditor*) getEditor()); + //viewport->updateVisibleEditors(); +} \ No newline at end of file diff --git a/Source/Processors/Utilities/Splitter.h b/Source/Processors/Utilities/Splitter.h new file mode 100644 index 0000000000000000000000000000000000000000..055c144f857c978396e32f2845ade321cb579900 --- /dev/null +++ b/Source/Processors/Utilities/Splitter.h @@ -0,0 +1,44 @@ +/* + ============================================================================== + + Splitter.h + Created: 17 Aug 2011 1:02:57am + Author: jsiegle + + ============================================================================== +*/ + +#ifndef __SPLITTER_H_A75239F7__ +#define __SPLITTER_H_A75239F7__ + +#include "../../../JuceLibraryCode/JuceHeader.h" +#include "../GenericProcessor.h" + +#include <stdio.h> + +class Splitter : public GenericProcessor +{ +public: + + Splitter(); + ~Splitter(); + + AudioProcessorEditor* createEditor(); + + void process(AudioSampleBuffer &buffer, MidiBuffer &midiMessages, int& nSamples) {} + + bool isSplitter() {return true;} + + void switchDest(int); + void setDestNode(GenericProcessor* dn); + +private: + + GenericProcessor* destNodeA; + GenericProcessor* destNodeB; + int activePath; + +}; + + +#endif // __SPLITTER_H_A75239F7__ diff --git a/Source/Processors/Visualization/DataWindow.cpp b/Source/Processors/Visualization/DataWindow.cpp new file mode 100644 index 0000000000000000000000000000000000000000..087b7e9e6bc1a58e4a00ccd5641207ebdd57d338 --- /dev/null +++ b/Source/Processors/Visualization/DataWindow.cpp @@ -0,0 +1,36 @@ +/* + ============================================================================== + + DataWindow.cpp + Created: 8 Feb 2012 1:10:13pm + Author: jsiegle + + ============================================================================== +*/ + +#include "DataWindow.h" + + +DataWindow::DataWindow(Button* cButton) + : DocumentWindow ("Stream Window", + Colours::black, + DocumentWindow::allButtons), + controlButton(cButton) + +{ + centreWithSize(500,500); + setUsingNativeTitleBar(true); + setResizable(true,false); + //setTitleBarHeight(40); +} + +DataWindow::~DataWindow() +{ + //setContentComponent (0); +} + +void DataWindow::closeButtonPressed() +{ + setVisible(false); + controlButton->setToggleState(false,false); +} \ No newline at end of file diff --git a/Source/Processors/Visualization/DataWindow.h b/Source/Processors/Visualization/DataWindow.h new file mode 100644 index 0000000000000000000000000000000000000000..84bd6176f1364941c483435f248434fcdd96f2eb --- /dev/null +++ b/Source/Processors/Visualization/DataWindow.h @@ -0,0 +1,32 @@ +/* + ============================================================================== + + DataWindow.h + Created: 8 Feb 2012 1:10:13pm + Author: jsiegle + + ============================================================================== +*/ + +#ifndef __DATAWINDOW_H_FDDAB8D0__ +#define __DATAWINDOW_H_FDDAB8D0__ + +#include "../../../JuceLibraryCode/JuceHeader.h" + +class DataWindow : public DocumentWindow +{ +public: + DataWindow(Button* button); + ~DataWindow(); + + void closeButtonPressed(); + +private: + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DataWindow); + + Button* controlButton; + +}; + + +#endif // __DATAWINDOW_H_FDDAB8D0__ diff --git a/Source/Processors/Visualization/LfpDisplayCanvas.cpp b/Source/Processors/Visualization/LfpDisplayCanvas.cpp new file mode 100644 index 0000000000000000000000000000000000000000..119381d5409508127ce3de61e26dce24659e1753 --- /dev/null +++ b/Source/Processors/Visualization/LfpDisplayCanvas.cpp @@ -0,0 +1,348 @@ +/* + ============================================================================== + + LfpDisplayCanvas.cpp + Created: 8 Feb 2012 1:06:53pm + Author: jsiegle + + ============================================================================== +*/ + +#include "LfpDisplayCanvas.h" + +LfpDisplayCanvas::LfpDisplayCanvas(LfpDisplayNode* n) : processor(n), + xBuffer(10), yBuffer(10), + plotHeight(60), selectedChan(-1), screenBufferIndex(0), + timebase(1.0f), displayGain(5.0f), displayBufferIndex(0) +{ + + //GenericProcessor* gp = (GenericProcessor*) editor->getProcessor(); + + nChans = processor->getNumInputs(); + sampleRate = processor->getSampleRate(); + + displayBuffer = processor->getDisplayBufferAddress(); + displayBufferSize = displayBuffer->getNumSamples(); + + lock = processor->getLock(); + + //screenBuffer = new AudioSampleBuffer(nChans, 10000); + + //setBounds(0,0,700,400); + + totalHeight = (plotHeight+yBuffer)*nChans + yBuffer; + +} + +LfpDisplayCanvas::~LfpDisplayCanvas() +{ +} + + +void LfpDisplayCanvas::newOpenGLContextCreated() +{ + + setUp2DCanvas(); + activateAntiAliasing(); + + glClearColor (0.667, 0.698, 0.9, 1.0); + resized(); + + screenBuffer = new AudioSampleBuffer(nChans, 10000); + + //startTimer(50); + +} + +void LfpDisplayCanvas::beginAnimation() +{ + std::cout << "Beginning animation." << std::endl; + + displayBufferIndex = 0; + screenBufferIndex = 0; + + startCallbacks(); +} + +void LfpDisplayCanvas::endAnimation() +{ + std::cout << "Ending animation." << std::endl; + stopCallbacks(); +} + +void LfpDisplayCanvas::updateNumInputs(int n) +{ + nChans = n; + //sampleRate = processor->getSampleRate(); +} + +void LfpDisplayCanvas::updateSampleRate(float r) +{ + sampleRate = r; + displayBufferSize = displayBuffer->getNumSamples(); + std::cout << "Display canvas updating sample rate to " << r << std::endl; +} + +void LfpDisplayCanvas::setParameter(int param, float val) +{ + if (param == 0) + timebase = val; + else + displayGain = val; + +} + +void LfpDisplayCanvas::updateScreenBuffer() +{ + // copy new samples from the displayBuffer into the screenBuffer + int maxSamples = getWidth(); + + int index = processor->getDisplayBufferIndex(); + + int nSamples = index - displayBufferIndex; + + if (nSamples < 0) + { + nSamples = (displayBufferSize - displayBufferIndex) + index; + } + + float ratio = sampleRate * timebase / float(getWidth()); + + // this number is crucial: this method should be updated: + int valuesNeeded = nSamples / int(ratio) - 1; + + //lock->enterRead(); + + if (valuesNeeded > 0) { +// + int sourceSampleNumber; + int destSampleNumber; + + int valuesTaken = 0; + + //float subSampleOffset = 0.0; + + for (int i = 0; i < valuesNeeded; i++) + { + + sourceSampleNumber = (displayBufferIndex + int(float(i)*ratio) ) % displayBufferSize; + destSampleNumber = (screenBufferIndex + i) % maxSamples; + + if (sourceSampleNumber >= 0 && destSampleNumber >= 0 && destSampleNumber < displayBufferSize) { + + valuesTaken++; + + //std::cout << " " << " " << sourceSampleNumber << " " << + //destSampleNumber << std::endl; + + for (int channel = 0; channel < displayBuffer->getNumChannels(); channel++) { + + + screenBuffer->copyFrom(channel, // destChannel + destSampleNumber, // destSampleOffset + //*displayBuffer, + //channel, + //sourceSampleNumber, + // 1); + displayBuffer->getSampleData(channel,sourceSampleNumber), // source + 1, // sourceChannel + displayGain); // number of samples + + + } + } + } + + //std::cout << screenBufferIndex << " " << displayBufferIndex << " " << + // valuesNeeded << " " << valuesTaken << " " << nSamples << " " << ratio << std::endl; + + //if (sourceSampleNumber >= 0 && destSampleNumber >= 0) { + screenBufferIndex += valuesTaken; + screenBufferIndex %= maxSamples; + + displayBufferIndex = index; + + } + //} + //lock->exitRead(); + + //std::cout << n << std::endl; +} + +void LfpDisplayCanvas::renderOpenGL() +{ + + glClear(GL_COLOR_BUFFER_BIT); // clear buffers to preset values + + //drawTicks(); + + updateScreenBuffer(); + + for (int i = 0; i < nChans; i++) + { + bool isSelected = false; + + if (selectedChan == i) + isSelected = true; + + if (checkBounds(i)) { + setViewport(i); + drawBorder(isSelected); + drawChannelInfo(i,isSelected); + drawWaveform(i,isSelected); + } + } + drawScrollBars(); +} + +void LfpDisplayCanvas::drawWaveform(int chan, bool isSelected) +{ + // draw the screen buffer for a given channel + + float w = float(getWidth()); + + glBegin(GL_LINE_STRIP); + + for (float i = 0; i < float(getWidth()); i++) + { + glVertex2f(i/w,*screenBuffer->getSampleData(chan, int(i))+0.5); + } + + glEnd(); + + glColor4f(1.0, 1.0, 0.1, 1.0); + glBegin(GL_LINE_STRIP); + glVertex2f(float(screenBufferIndex)/w,0); + glVertex2f(float(screenBufferIndex)/w,1); + glEnd(); + +} + + +void LfpDisplayCanvas::drawTicks() +{ + + glViewport(0,0,getWidth(),getHeight()); + + glColor4f(1.0f, 1.0f, 1.0f, 0.25f); + + for (int i = 0; i < 10; i++) + { + if (i == 5) + glLineWidth(3.0); + else if (i == 1 || i == 3 || i == 7 || i == 9) + glLineWidth(2.0); + else + glLineWidth(1.0); + + glBegin(GL_LINE_STRIP); + glVertex2f(0.1*i,0); + glVertex2f(0.1*i,1); + glEnd(); + } +} + + +bool LfpDisplayCanvas::checkBounds(int chan) +{ + bool isVisible; + + int lowerBound = (chan+1)*(plotHeight+yBuffer); + int upperBound = chan*(plotHeight+yBuffer); + + if (getScrollAmount() < lowerBound && getScrollAmount() + getHeight() > upperBound) + isVisible = true; + else + isVisible = false; + + return isVisible; + +} + +void LfpDisplayCanvas::setViewport(int chan) +{ + glViewport(xBuffer, + getHeight()-(chan+1)*(plotHeight+yBuffer)+getScrollAmount(), + getWidth()-2*xBuffer, + plotHeight); +} + +void LfpDisplayCanvas::drawBorder(bool isSelected) +{ + float alpha = 0.5f; + + if (isSelected) + alpha = 1.0f; + + glColor4f(0.0f, 0.0f, 0.0f, alpha); + glBegin(GL_LINE_STRIP); + glVertex2f(0.0f, 0.0f); + glVertex2f(1.0f, 0.0f); + glVertex2f(1.0f, 1.0f); + glVertex2f(0.0f, 1.0f); + glVertex2f(0.0f, 0.0f); + glEnd(); + +} + +void LfpDisplayCanvas::drawChannelInfo(int chan, bool isSelected) +{ + float alpha = 0.5f; + + if (isSelected) + alpha = 1.0f; + + glColor4f(0.0f,0.0f,0.0f,alpha); + glRasterPos2f(5.0f/getWidth(),0.3); + String s = String("Channel "); + s += (chan+1); + + getFont(String("miso-regular"))->FaceSize(16); + getFont(String("miso-regular"))->Render(s); +} + +int LfpDisplayCanvas::getTotalHeight() +{ + return totalHeight; +} + + +// void LfpDisplayCanvas::mouseDown(const MouseEvent& e) +// { + +// Point<int> pos = e.getPosition(); +// int xcoord = pos.getX(); + +// if (xcoord < getWidth()-getScrollBarWidth()) +// { +// int chan = (e.getMouseDownY() + getScrollAmount())/(yBuffer+plotHeight); + +// selectedChan = chan; + +// repaint(); +// } + +// mouseDownInCanvas(e); +// } + +// void LfpDisplayCanvas::mouseDrag(const MouseEvent& e) {mouseDragInCanvas(e);} +// void LfpDisplayCanvas::mouseMove(const MouseEvent& e) {mouseMoveInCanvas(e);} +// void LfpDisplayCanvas::mouseUp(const MouseEvent& e) {mouseUpInCanvas(e);} +// void LfpDisplayCanvas::mouseWheelMove(const MouseEvent& e, float a, float b) {mouseWheelMoveInCanvas(e,a,b);} + +// void LfpDisplayCanvas::resized() +// { +// //screenBuffer = new AudioSampleBuffer(nChans, getWidth()); + +// // glClear(GL_COLOR_BUFFER_BIT); + +// // //int h = getParentComponent()->getHeight(); + +// // if (scrollPix + getHeight() > getTotalHeight() && getTotalHeight() > getHeight()) +// // scrollPix = getTotalHeight() - getHeight(); +// // else +// // scrollPix = 0; + +// // showScrollBars(); +// canvasWasResized(); +// } \ No newline at end of file diff --git a/Source/Processors/Visualization/LfpDisplayCanvas.h b/Source/Processors/Visualization/LfpDisplayCanvas.h new file mode 100644 index 0000000000000000000000000000000000000000..5ed80dab59119135924088b66a8f26c56752da65 --- /dev/null +++ b/Source/Processors/Visualization/LfpDisplayCanvas.h @@ -0,0 +1,89 @@ +/* + ============================================================================== + + LfpDisplayCanvas.h + Created: 8 Feb 2012 1:06:53pm + Author: jsiegle + + ============================================================================== +*/ + +#ifndef __LFPDISPLAYCANVAS_H_B711873A__ +#define __LFPDISPLAYCANVAS_H_B711873A__ + + +#include "../../../JuceLibraryCode/JuceHeader.h" +#include "OpenGLCanvas.h" +#include "../../UI/Configuration.h" +#include "../LfpDisplayNode.h" + +class LfpDisplayNode; + +class LfpDisplayCanvas : public OpenGLCanvas + +{ +public: + LfpDisplayCanvas(LfpDisplayNode* n); + ~LfpDisplayCanvas(); + void newOpenGLContextCreated(); + void renderOpenGL(); + + void beginAnimation(); + void endAnimation(); + + void updateNumInputs(int); + void updateSampleRate(float); + + void setParameter(int, float); + +private: + + ReadWriteLock* lock; + + int xBuffer, yBuffer; + + float sampleRate; + float timebase; + float displayGain; + //float ratio; + + LfpDisplayNode* processor; + AudioSampleBuffer* displayBuffer; + ScopedPointer<AudioSampleBuffer> screenBuffer; + MidiBuffer* eventBuffer; + + void setViewport(int chan); + void drawBorder(bool isSelected); + void drawChannelInfo(int chan, bool isSelected); + void drawWaveform(int chan, bool isSelected); + + void drawTicks(); + + bool checkBounds(int chan); + + + + void updateScreenBuffer(); + int screenBufferIndex; + int displayBufferIndex; + int displayBufferSize; + + int nChans, plotHeight, totalHeight; + int selectedChan; + + int getTotalHeight(); + + // void resized(); + // void mouseDown(const MouseEvent& e); + // void mouseDrag(const MouseEvent& e); + // void mouseMove(const MouseEvent& e); + // void mouseUp(const MouseEvent& e); + // void mouseWheelMove(const MouseEvent&, float, float); + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LfpDisplayCanvas); + +}; + + + +#endif // __LFPDISPLAYCANVAS_H_B711873A__ diff --git a/Source/Processors/Visualization/LfpViewer.cpp b/Source/Processors/Visualization/LfpViewer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b6112977fb0e6b23a0d2aa26cf7c6261fedab54c --- /dev/null +++ b/Source/Processors/Visualization/LfpViewer.cpp @@ -0,0 +1,109 @@ +/* + ============================================================================== + + LfpViewer.cpp + Created: 16 Aug 2011 8:46:57pm + Author: jsiegle + + ============================================================================== +*/ + +#include "LfpViewer.h" + + +LfpViewer::LfpViewer(AudioSampleBuffer* sBuffer, MidiBuffer* eBuffer, UIComponent* ui) + : Renderer(sBuffer, eBuffer, ui) +{ + addMouseListener (this, true); +} + +LfpViewer::~LfpViewer() {} + +void LfpViewer::newOpenGLContextCreated() +{ + glClearColor(0.6f, 0.6f, 0.4f, 1.0f); + glClearDepth (1.0); + + glMatrixMode (GL_PROJECTION); + glLoadIdentity (); + glOrtho(0, 1, 1, 0, 0, 1); + glMatrixMode (GL_MODELVIEW); + + + //glDepthFunc (GL_LESS); + //glEnable (GL_DEPTH_TEST); + //glEnable (GL_TEXTURE_2D); + glEnable (GL_BLEND); + glShadeModel(GL_FLAT); + + glHint (GL_LINE_SMOOTH_HINT, GL_NICEST); + glHint (GL_POINT_SMOOTH_HINT, GL_NICEST); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glColor3f(1,1,1); + + glFlush(); + +} + +void LfpViewer::renderOpenGL() +{ + + //std::cout << "Painting..." << std::endl; + + glClear(GL_COLOR_BUFFER_BIT); + + //glMatrixMode (GL_PROJECTION); + //glLoadIdentity (); + //glOrtho(0, 1, 1, 0, 0, 1); + //glMatrixMode (GL_MODELVIEW); + + + int skip = 1; + + int nSamples = streamBuffer->getNumSamples(); + + + for (int chan = 0; chan < streamBuffer->getNumChannels(); chan++) { + + glBegin(GL_LINE_STRIP); + + //std::cout << "Message Received." << std::endl; + if (chan % 4 == 0) + glColor3f(0.0,0.0,0);//1.0*chan/16,1.0*chan/16,1.0*chan/16); + else if (chan % 4 == 1) + glColor3f(0,0.0,0); + else if (chan % 4 == 2) + glColor3f(0.0,0.0,0); + else + glColor3f(0.0,0.0,0.0); + + + for (int n = 0; n < nSamples-skip; n+= skip ) + { + glVertex2f(float(n)/nSamples,*streamBuffer->getSampleData(chan,n)/0.5+0.03+chan*0.06); + glVertex2f(float(n+skip)/nSamples,*streamBuffer->getSampleData(chan,n+skip)/0.5+0.03+chan*0.06); + } + + //std::cout << *streamBuffer->getSampleData(0,0) << std::endl; + + glEnd(); + + } + + + + glFlush(); + + +} + +void LfpViewer::mouseDown (const MouseEvent& e) +{ + + // std::cout << "Mouse click at " << + // e.getMouseDownX() << + // ", " << + // e.getMouseDownY() << std::endl; + +} \ No newline at end of file diff --git a/Source/Processors/Visualization/LfpViewer.h b/Source/Processors/Visualization/LfpViewer.h new file mode 100644 index 0000000000000000000000000000000000000000..a01b6c26b38cf6d63c8c061dd4245f5eaa35fdf5 --- /dev/null +++ b/Source/Processors/Visualization/LfpViewer.h @@ -0,0 +1,52 @@ +/* + ============================================================================== + + LfpViewer.h + Created: 16 Aug 2011 8:46:57pm + Author: jsiegle + + ============================================================================== +*/ + +#ifndef __LFPVIEWER_H_7FEACF46__ +#define __LFPVIEWER_H_7FEACF46__ + +#include "../../../JuceLibraryCode/JuceHeader.h" +#include "../Editors/Visualizer.h" + +#ifdef _WIN32 +#include <windows.h> +#endif + +#if JUCE_WINDOWS +#include <gl/gl.h> +#include <gl/glu.h> +#elif JUCE_LINUX +#include <GL/gl.h> +#include <GL/glut.h> +#undef KeyPress +#elif JUCE_IPHONE +#include <OpenGLES/ES1/gl.h> +#include <OpenGLES/ES1/glext.h> +#elif JUCE_MAC +#include <GLUT/glut.h> +#endif + +#ifndef GL_BGRA_EXT +#define GL_BGRA_EXT 0x80e1 +#endif + + + +class LfpViewer : public Renderer +{ +public: + LfpViewer(AudioSampleBuffer* streamBuffer, MidiBuffer* eventBuffer, UIComponent* ui); + ~LfpViewer(); + void renderOpenGL(); + void newOpenGLContextCreated(); + void mouseDown(const MouseEvent &e); +}; + + +#endif // __LFPVIEWER_H_7FEACF46__ diff --git a/Source/Processors/Visualization/OpenGLCanvas.cpp b/Source/Processors/Visualization/OpenGLCanvas.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6dacf5a227cf0bcd920e78a2d878c5457cd4fa0f --- /dev/null +++ b/Source/Processors/Visualization/OpenGLCanvas.cpp @@ -0,0 +1,404 @@ +/* + ============================================================================== + + OpenGLCanvas.cpp + Created: 27 Jan 2012 4:23:10pm + Author: jsiegle + + ============================================================================== +*/ + +#include "OpenGLCanvas.h" +#include <stdio.h> +#include <math.h> + +OpenGLCanvas::OpenGLCanvas() : //OpenGLComponent(OpenGLComponent::OpenGLType::openGLDefault, true), + scrollPix(0), scrollTime(0), scrollDiff(0), originalScrollPix(0), + scrollBarWidth(15), PI(3.1415926), showScrollTrack(true), + animationIsActive(false), refreshMs(60) +{ + + loadFonts(); + + timer = new Time(); + +} + +OpenGLCanvas::~OpenGLCanvas() +{ + +} + +void OpenGLCanvas::setUp2DCanvas() +{ + glMatrixMode (GL_PROJECTION); + + glLoadIdentity(); + glOrtho (0, 1, 1, 0, 0, 1); + glMatrixMode (GL_MODELVIEW); + + glEnable(GL_TEXTURE_2D); +} + +void OpenGLCanvas::activateAntiAliasing() +{ + glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); + glHint(GL_POINT_SMOOTH_HINT, GL_NICEST); + glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST); + + glEnable(GL_LINE_SMOOTH); + glEnable(GL_POINT_SMOOTH); + glEnable(GL_POLYGON_SMOOTH); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); +} + + +void OpenGLCanvas::loadFonts() +{ + + const unsigned char* buffer = reinterpret_cast<const unsigned char*>(BinaryData::misoregular_ttf); + size_t bufferSize = BinaryData::misoregular_ttfSize; + + FTPixmapFont* font1 = new FTPixmapFont(buffer, bufferSize); + + fontList.add(font1); + + buffer = reinterpret_cast<const unsigned char*>(BinaryData::misobold_ttf); + bufferSize = BinaryData::misobold_ttfSize; + + FTPixmapFont* font2 = new FTPixmapFont(buffer, bufferSize); + + fontList.add(font2); + + buffer = reinterpret_cast<const unsigned char*>(BinaryData::misolight_ttf); + bufferSize = BinaryData::misolight_ttfSize; + + FTPixmapFont* font3 = new FTPixmapFont(buffer, bufferSize); + + fontList.add(font3); + +} + +FTPixmapFont* OpenGLCanvas::getFont(String fontName) +{ + + if (fontName.equalsIgnoreCase("miso-regular")) + return fontList[0]; + else if (fontName.equalsIgnoreCase("miso-bold")) + return fontList[1]; + else if (fontName.equalsIgnoreCase("miso-light")) + return fontList[2]; + else + return fontList[0]; // miso-regular is default font + +} + +void OpenGLCanvas::startCallbacks() +{ + startTimer(refreshMs); + animationIsActive = true; +} + +void OpenGLCanvas::stopCallbacks() +{ + stopTimer(); + animationIsActive = false; +} + + +void OpenGLCanvas::drawScrollBars() +{ + float scrollBarY = float(getHeight())/float(getTotalHeight()); + float timeSinceScroll = timer->getMillisecondCounter()-scrollTime; + + if (scrollBarY < 1.0f && timeSinceScroll < 1300) + { + float alpha; + + if (timeSinceScroll < 1000) + alpha = 1.0f; + else + alpha = 1.0f*(1-float(timeSinceScroll-1000)/300.0f); + + float Yoffset = float(scrollPix)/float(getTotalHeight()); + + if (showScrollTrack) + drawScrollBar(0.995f, 2.0f/getHeight(), alpha*0.2f); + + scrollBarBottom = scrollBarY + Yoffset - 2.0f/getHeight(); + scrollBarTop = Yoffset + 2.0f/getHeight(); + + drawScrollBar(scrollBarBottom, scrollBarTop, alpha*0.5f); + + } else { + if (!animationIsActive) { + stopTimer(); + } + showScrollTrack = false; + } +} + +void OpenGLCanvas::drawScrollBar(float y1, float y2, float alpha) +{ + glViewport(0,0,getWidth(),getHeight()); + + float x1 = (getWidth()-8.0f)/getWidth(); + float x2 = (getWidth()-2.0f)/getWidth(); + //float px2 = 2.0f/getWidth(); + + glColor4f(0.0f, 0.0f, 0.0f, alpha); + + glBegin(GL_POLYGON); + + glVertex2f(x1,y1); + //glVertex2f(x1+px2,y1+px2); + ///glVertex2f(x1+px2*2,y1+px2); + glVertex2f(x2,y1); + glVertex2f(x2,y2); + //glVertex2f(x2-px2,y2-px2); + //glVertex2f(x2-px2*2,y2-px2); + glVertex2f(x1,y2); + + glEnd(); + +} + +void OpenGLCanvas::showScrollBars() +{ + scrollTime = timer->getMillisecondCounter(); + startTimer(refreshMs); +} + +void OpenGLCanvas::drawRoundedRect(float x, + float y, + float w, + float h, + float r, + int n) +{ + //glLineWidth(3.0); + GLint* params = new GLint[4]; + + glGetIntegerv(GL_VIEWPORT, params); + + //std::cout << params[0] << " " << params[1] << " " << params[2] << " " + // << params[3] << std::endl; + + float ratio = float(params[3])/float(params[2]); + + //std::cout << ratio << std::endl; + + glBegin(GL_LINE_LOOP); + + for (int side = 0; side < 4; side++) + { + float x0[2]; + float y0[2]; + float origin[2]; + float angle[2]; + + switch (side) { + case 0: + x0[0] = 0; x0[1] = 0; + y0[0] = r; y0[1] = h-r; + origin[0] = r*ratio; origin[1] = h-r; + angle[0] = PI/2; angle[1] = PI; + break; + + case 1: + x0[0] = r*ratio; x0[1] = w-r*ratio; + y0[0] = h; y0[1] = h; + origin[0] = w-r*ratio; origin[1] = h-r; + angle[0] = 0; angle[1] = PI/2; + break; + + case 2: + x0[0] = w; x0[1] = w; + y0[0] = h-r; y0[1] = r; + origin[0] = w-r*ratio; origin[1] = r; + angle[0] = 3*PI/2; angle[1] = 2*PI; + break; + + case 3: + x0[0] = w-r*ratio; x0[1] = r*ratio; + y0[0] = 0; y0[1] = 0; + origin[0] = r*ratio; origin[1] = r; + angle[0] = PI; angle[1] = 3*PI/2; + break; + + default: + break; + } + + //glLineWidth(2.0); + glVertex2f(x0[0]+x,y0[1]+y); + glVertex2f(x0[1]+x,y0[1]+y); + + //glLineWidth(1.0); + for (float a = angle[1]; a > angle[0]; a -= (PI/2)/n) + { + glVertex2f(cos(a)*r*ratio+origin[0]+x, + sin(a)*r+origin[1]+y); + } + + } + + glEnd(); + +} + +void OpenGLCanvas::mouseMoveInCanvas(const MouseEvent& e) +{ + if (getTotalHeight() > getHeight()) { + Point<int> pos = e.getPosition(); + int xcoord = pos.getX(); + if (xcoord > getWidth() - scrollBarWidth) + { + showScrollTrack = true; showScrollBars(); + } + } +} + +void OpenGLCanvas::mouseDownInCanvas(const MouseEvent& e) +{ + + if (getTotalHeight() > getHeight()) { + + Point<int> pos = e.getPosition(); + int xcoord = pos.getX(); + + if (xcoord > getWidth()-scrollBarWidth) { + + int ycoord = pos.getY(); + + float targetPoint = float(ycoord)/float(getHeight()); + + if (targetPoint < scrollBarTop && targetPoint < scrollBarTop) + { + + scrollPix = int(float(ycoord)/float(getHeight())*float(getTotalHeight())); + + } else if (targetPoint > scrollBarBottom && targetPoint > scrollBarBottom) { + + scrollPix = int(float(ycoord)/float(getHeight())*float(getTotalHeight())) - + (scrollBarBottom-scrollBarTop)*float(getTotalHeight()); + + } + + showScrollTrack = true; + showScrollBars(); + } + } +} + +void OpenGLCanvas::mouseDragInCanvas(const MouseEvent& e) +{ + + if (getTotalHeight() > getHeight()) { + if (e.getMouseDownX() > getWidth()-scrollBarWidth) + { + + if (float(e.getMouseDownY()/float(getHeight())) > scrollBarTop && + float(e.getMouseDownY()/float(getHeight())) < scrollBarBottom) + { + + if (scrollDiff == 0) + { + originalScrollPix = scrollPix; + scrollDiff = 1; + } + + } + + if (scrollDiff == 1) + { + scrollPix = originalScrollPix + + float(e.getDistanceFromDragStartY())/float(getHeight()) + * float(getTotalHeight()); + + if (scrollPix < 0) + scrollPix = 0; + + if (scrollPix + getHeight() > getTotalHeight()) + scrollPix = getTotalHeight() - getHeight(); + + scrollTime = timer->getMillisecondCounter(); + showScrollTrack = true; + repaint(); + } + } + } +} + +void OpenGLCanvas::mouseUpInCanvas(const MouseEvent& e) +{ + scrollDiff = 0; +} + +void OpenGLCanvas::mouseWheelMoveInCanvas(const MouseEvent&e, + float wheelIncrementX, float wheelIncrementY) + +{ + if (getTotalHeight() > getHeight()) { + if (wheelIncrementY > 0) + { + if (scrollPix + getHeight() < getTotalHeight()) + { + scrollPix += int(100.0f*wheelIncrementY); + if (scrollPix + getHeight() > getTotalHeight()) + scrollPix = getTotalHeight() - getHeight(); + } + } else if (wheelIncrementY < 0) + { + if (scrollPix > 0) + { + scrollPix += int(100.0f*wheelIncrementY); + if (scrollPix < 0) + scrollPix = 0; + } + } + + repaint(); + + showScrollBars(); + + } + +} + +void OpenGLCanvas::canvasWasResized() +{ + glClear(GL_COLOR_BUFFER_BIT); + + if (scrollPix + getHeight() > getTotalHeight() && getTotalHeight() > getHeight()) + scrollPix = getTotalHeight() - getHeight(); + else + scrollPix = 0; + + showScrollBars(); + +} + +void OpenGLCanvas::timerCallback() +{ + repaint(); +} + + + +void OpenGLCanvas::mouseDown(const MouseEvent& e) +{ + mouseDownInCanvas(e); +} + +void OpenGLCanvas::mouseDrag(const MouseEvent& e) {mouseDragInCanvas(e);} +void OpenGLCanvas::mouseMove(const MouseEvent& e) {mouseMoveInCanvas(e);} +void OpenGLCanvas::mouseUp(const MouseEvent& e) {mouseUpInCanvas(e);} +void OpenGLCanvas::mouseWheelMove(const MouseEvent& e, float a, float b) {mouseWheelMoveInCanvas(e,a,b);} + +void OpenGLCanvas::resized() +{ + canvasWasResized(); +} \ No newline at end of file diff --git a/Source/Processors/Visualization/OpenGLCanvas.h b/Source/Processors/Visualization/OpenGLCanvas.h new file mode 100644 index 0000000000000000000000000000000000000000..9484d9fac9046dc9919d4db7017ddbfac8a0ae0d --- /dev/null +++ b/Source/Processors/Visualization/OpenGLCanvas.h @@ -0,0 +1,110 @@ +/* + ============================================================================== + + OpenGLCanvas.h + Created: 27 Jan 2012 4:23:10pm + Author: jsiegle + + ============================================================================== +*/ + +#ifndef __OPENGLCANVAS_H_98F0C13D__ +#define __OPENGLCANVAS_H_98F0C13D__ + +#include "../../../JuceLibraryCode/JuceHeader.h" + +#ifdef _WIN32 +#include <windows.h> +#endif + +#if JUCE_WINDOWS +#include <gl/gl.h> +#include <gl/glu.h> +#elif JUCE_LINUX +#include <GL/gl.h> +#include <GL/glut.h> +#undef KeyPress +#elif JUCE_IPHONE +#include <OpenGLES/ES1/gl.h> +#include <OpenGLES/ES1/glext.h> +#elif JUCE_MAC +#include <GLUT/glut.h> +#endif + +#ifndef GL_BGRA_EXT +#define GL_BGRA_EXT 0x80e1 +#endif + +#include <FTGL/ftgl.h> + +class OpenGLCanvas : public OpenGLComponent, Timer + +{ +public: + OpenGLCanvas(); + ~OpenGLCanvas(); + + void setUp2DCanvas(); + void activateAntiAliasing(); + + void mouseDownInCanvas(const MouseEvent& e); + void mouseDragInCanvas(const MouseEvent& e); + void mouseMoveInCanvas(const MouseEvent& e); + void mouseUpInCanvas(const MouseEvent& e); + void mouseWheelMoveInCanvas(const MouseEvent&, float, float); + + virtual void resized(); + virtual void mouseDown(const MouseEvent& e); + virtual void mouseDrag(const MouseEvent& e); + virtual void mouseMove(const MouseEvent& e); + virtual void mouseUp(const MouseEvent& e); + virtual void mouseWheelMove(const MouseEvent&, float, float); + + void startCallbacks(); + void stopCallbacks(); + + void canvasWasResized(); + + int getScrollAmount() {return scrollPix;}; + int getScrollBarWidth() {return scrollBarWidth;} + void drawScrollBars(); + + void drawRoundedRect(float x, float y, float w, float h, float r, int n); + + FTGLPixmapFont* getFont(String fontName); + +protected: + + virtual int getTotalHeight() = 0; + +private: + + bool animationIsActive; + + int refreshMs; + + void loadFonts(); + + void drawScrollBar(float y1, float y2, float alpha); + + void showScrollBars(); + + int scrollBarWidth, scrollPix, scrollDiff, originalScrollPix; + int scrollTime; + bool showScrollTrack; + + Time* timer; + void timerCallback(); + + float scrollBarTop, scrollBarBottom; + + OwnedArray<FTGLPixmapFont> fontList; + + const float PI; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenGLCanvas); + +}; + + +#endif // __OPENGLCANVAS_H_98F0C13D__ diff --git a/Source/Processors/Visualization/SpikeViewer.cpp b/Source/Processors/Visualization/SpikeViewer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a3b62f66e390c04ecd0db115d46d099f61d96666 --- /dev/null +++ b/Source/Processors/Visualization/SpikeViewer.cpp @@ -0,0 +1,327 @@ +/* + ============================================================================== + + SpikeViewer.cpp + Created: 16 Aug 2011 8:34:41pm + Author: jsiegle + + ============================================================================== +*/ + +#include "SpikeViewer.h" + + +SpikeViewer::SpikeViewer(AudioSampleBuffer* sBuffer, MidiBuffer* eBuffer, UIComponent* ui) + : Renderer(sBuffer, eBuffer, ui) +{ + +} + + + +SpikeViewer::~SpikeViewer() {} + +void SpikeViewer::resized() +{ + + xBox = getWidth()/4; + yBox = getHeight()/2; + yPadding = 5; + xPadding = 5; + + glClear(GL_COLOR_BUFFER_BIT); + + clearWaveforms(); + clearProjections(); + +} + +void SpikeViewer::newOpenGLContextCreated() +{ + + glClearDepth (1.0); + + glMatrixMode (GL_PROJECTION); + glLoadIdentity (); + glOrtho(0, 1, 1, 0, 0, 1); + glMatrixMode (GL_MODELVIEW); + + glClearColor(0.2f, 0.2f, 0.2f, 1.0f); + + resized(); + + glFlush(); + +} + +void SpikeViewer::clearWaveforms() +{ + + for (int n = 0; n < 4; n++) + { + setViewportForWaveN(n); + drawBorder(); + } + +} + +void SpikeViewer::clearProjections() +{ + for (int n = 0; n < 6; n++) + { + setViewportForProjectionN(n); + drawBorder(); + } + +} + +void SpikeViewer::renderOpenGL() +{ + + if (eventBuffer->getNumEvents() > 0) { + + glRasterPos2f(0.1,0.1); + + //const char* str = "i"; + // void* font = GLUT_BITMAP_8_BY_13; + + // glutBitmapCharacter(font,54); + // drawBorder(); + + //std::cout << "Events received by Spike Viewer." << std::endl; + + MidiBuffer::Iterator i (*eventBuffer); + MidiMessage message(0xf4); + + int samplePosition; + i.setNextSamplePosition(samplePosition); + + //Array<int> peaks; + + + clearWaveforms(); + + while (i.getNextEvent (message, samplePosition)) { + + int numbytes = message.getRawDataSize(); + int numSamples = (numbytes-2)/2; + uint8* dataptr = message.getRawData(); + + int chan = (*dataptr<<8) + *(dataptr+1); + int electrode = config->getSource(0)->getElectrodeNumberForChannel(chan); + + //std::cout << chan << "::" << electrode << std::endl; + + dataptr += 2; + + //glViewport(0,0,getWidth()/2,getHeight()); + + if (electrode == 0) + { + //for (int n = 0; n < 4; n++) { + setViewportForWaveN(chan); + float peak = drawWaveform(dataptr, numSamples); + + peaks.set(chan,peak*1.25); + //peaks.set(chan,peak); + + } + + if (peaks.size() == 4) + { + drawProjections(); + peaks.clear(); + } + + //std::cout << " Bytes received: " << numbytes << std::endl; + //std::cout << " Message timestamp = " << message.getTimeStamp() << std::endl; + //std::cout << " Message channel: " << chan << std::endl; + + //std::cout << " "; + + //AudioDataConverters::convertInt16BEToFloat ( dataptr, // source + // spikeData, // dest + // numSamples, // numSamples + // 2 ); // destBytesPerSample = 2 + + //for (int n = 0; n < numSamples; n++) { + // std::cout << String(spikeData[n]) << " "; + //} + + //std::cout << std::endl << std::endl; + } + + // for (int ch = 0; ch < 4; ch++) + // { + + // } + + //eventBuffer->clear(); + + } + + //glOrtho(0, 0.5, 0.5, 0, 0, 1); + glFlush(); + +} + +void SpikeViewer::drawProjections() +{ + + for (int i = 0; i < 6; i++) + { + setViewportForProjectionN(i); + + int d1,d2; + + if (i == 0){ + d1 = 0; + d2 = 1; + } else if (i == 1) { + d1 = 0; + d2 = 2; + } else if (i == 2) { + d1 = 0; + d2 = 3; + } else if (i == 3) { + d1 = 1; + d2 = 2; + } else if (i == 4) { + d1 = 1; + d2 = 3; + } else if (i == 5) { + d1 = 2; + d2 = 3; + } + + glColor3f(0.0, 0.0, 0.0); + glBegin(GL_POINTS); + glVertex2f(1-peaks[d1],peaks[d2]); + glEnd(); + + } + +} + +float SpikeViewer::drawWaveform(uint8* dataptr, int numSamples) +{ + + glColor3f(0,0.0,0); + + glBegin(GL_LINE_STRIP); + + float maxVal = 0; + + for (int n = 0; n < numSamples; n++) + { + uint16 sampleValue = (*dataptr << 8) + *(dataptr+1); + + float sampleVal = -float(sampleValue - 32768)/32768 + 0.6; + + (sampleVal > maxVal) ? maxVal = sampleVal : maxVal = maxVal; + + glVertex2f(float(n)/numSamples + 0.01, + sampleVal); + dataptr += 2; + + } + + glEnd(); + + return maxVal; + +} + +void SpikeViewer::drawBorder() +{ + + glColor3f(0.9,0.7,0.2); + + glRectf(0.01f,0.01f,0.99f,0.99f); + + // glBegin(GL_LINE_STRIP); + // glVertex2f(0.01f, 0.01f); + // glVertex2f(0.99f, 0.01f); + // glVertex2f(0.99f, 0.99f); + // glVertex2f(0.01f, 0.99f); + // glVertex2f(0.01f, 0.01f); + // glEnd(); +} + +void SpikeViewer::setViewportForProjectionN(int n) +{ + // std::cout<<"Setting viewport on projection:"<<n<<std::endl; + float viewDX = xBox; + float viewDY = yBox; + float viewX,viewY; + + switch (n){ + case 0: + viewX=xBox; + viewY=yBox; + break; + case 1: + viewX = xBox*2; + viewY = yBox; + break; + case 2: + viewX=xBox*3; + viewY=yBox; + break; + case 3: + viewX = xBox; + viewY = 0; + break; + case 4: + viewX = xBox*2; + viewY = 0; + break; + case 5: + viewX = xBox*3; + viewY = 0; + break; + default: + std::cout<<"drawing of more than 4 channels is not supported, returning! Requested:"<<n<<std::endl; + return; + } + viewX = viewX + xPadding; + viewY = viewY + yPadding; + viewDX = viewDX - 2*xPadding; + viewDY = viewDY - 2*yPadding; + + glViewport(viewX, viewY, viewDX, viewDY); + +} + + +void SpikeViewer::setViewportForWaveN(int n){ + float viewDX = xBox/2; + float viewDY = yBox; + float viewX,viewY; + switch (n){ + case 0: + viewX=0; + viewY=yBox; + break; + case 1: + viewX = xBox/2; + viewY = yBox; + break; + case 2: + viewX=0; + viewY=0; + break; + case 3: + viewX = xBox/2; + viewY = 0; + break; + default: + std::cout<<"drawing of more than 4 channels is not supported, returning! Requested:"<<n<<std::endl; + return; + } + viewX = viewX + xPadding; + viewY = viewY + yPadding; + viewDX = viewDX - 2*xPadding; + viewDY = viewDY - 2*yPadding; + glViewport(viewX,viewY,viewDX,viewDY); +} \ No newline at end of file diff --git a/Source/Processors/Visualization/SpikeViewer.h b/Source/Processors/Visualization/SpikeViewer.h new file mode 100644 index 0000000000000000000000000000000000000000..3c1ace07ac180bd3134947b746879e29b1beb40b --- /dev/null +++ b/Source/Processors/Visualization/SpikeViewer.h @@ -0,0 +1,73 @@ +/* + ============================================================================== + + SpikeViewer.h + Created: 16 Aug 2011 8:34:41pm + Author: jsiegle + + ============================================================================== +*/ + +#ifndef __SPIKEVIEWER_H_AF442416__ +#define __SPIKEVIEWER_H_AF442416__ + +#include "../../../JuceLibraryCode/JuceHeader.h" +#include "../Editors/Visualizer.h" + +#ifdef _WIN32 +#include <windows.h> +#endif + +#if JUCE_WINDOWS +#include <gl/gl.h> +#include <gl/glu.h> +#elif JUCE_LINUX +#include <GL/gl.h> +#include <GL/glut.h> +#undef KeyPress +#elif JUCE_IPHONE +#include <OpenGLES/ES1/gl.h> +#include <OpenGLES/ES1/glext.h> +#elif JUCE_MAC +#include <GLUT/glut.h> +#endif + +#ifndef GL_BGRA_EXT +#define GL_BGRA_EXT 0x80e1 +#endif + + + +class SpikeViewer : public Renderer +{ +public: + SpikeViewer(AudioSampleBuffer* streamBuffer, MidiBuffer* eventBuffer, UIComponent* ui); + ~SpikeViewer(); + void renderOpenGL(); + void newOpenGLContextCreated(); + + //void setViewportForProjection +private: + int nTrodes; + + Array<float> peaks; + + float xBox, yBox, xPadding, yPadding; + + void drawBorder(); + + void clearWaveforms(); + void clearProjections(); + + void drawProjections(); + float drawWaveform(uint8* dataptr, int numSamples); + + void setViewportForWaveN(int n); + void setViewportForProjectionN(int n); + + void resized(); + +}; + + +#endif // __SPIKEVIEWER_H_AF442416__ diff --git a/Source/UI/Configuration.cpp b/Source/UI/Configuration.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0da9152030fcaf7654d7c29a5b7aec882c65f849 --- /dev/null +++ b/Source/UI/Configuration.cpp @@ -0,0 +1,52 @@ +/* + ============================================================================== + + Configuration.cpp + Created: 6 Sep 2011 10:24:09pm + Author: jsiegle + + ============================================================================== +*/ + +#include "Configuration.h" + + +DataSource::DataSource(GenericProcessor* p, Configuration* c) +{ + + processor = p; + + name = p->getName(); + id = p->getNodeId(); + + firstChan = 0; + + for (int n = 0; n < c->numDataSources(); n++) + { + firstChan += c->getSource(n)->getNumChans(); + } + + numChans = p->getNumOutputs(); + + nextAvailableChan = firstChan; + + channelMapping.ensureStorageAllocated(numChans); + + totalElectrodes = 0; + +} + + +void Configuration::removeDataSource(GenericProcessor* p) +{ + std::cout << "Removing data source from configuration!" << std::endl; + + for (int n = 0; n < numDataSources(); n++) + { + GenericProcessor* gp = (GenericProcessor*) getSource(n)->getProcessor(); + + if (gp == p) + dataSources.remove(n); + } + +} \ No newline at end of file diff --git a/Source/UI/Configuration.h b/Source/UI/Configuration.h new file mode 100644 index 0000000000000000000000000000000000000000..214e7ae2fee97092309ed4a1f76e4c7dead3b6d6 --- /dev/null +++ b/Source/UI/Configuration.h @@ -0,0 +1,211 @@ +/* + ============================================================================== + + Configuration.h + Created: 6 Sep 2011 10:24:09pm + Author: jsiegle + + ============================================================================== +*/ + +#ifndef __CONFIGURATION_H_9DEA9372__ +#define __CONFIGURATION_H_9DEA9372__ + +#include "../../JuceLibraryCode/JuceHeader.h" +#include "../Processors/GenericProcessor.h" + +class GenericProcessor; +class Configuration; + +class Trode +{ +public: + Trode(int nchans, int startChan, String name_) + { + name = name_; + + for (int n = 0; n < nchans; n++) + { + channelMapping.add(startChan++); + inputRange.add(10000.0); + threshold.add(5000.0); + isActive.add(true); + } + } + + ~Trode() {} + + int numChannels() {return channelMapping.size();} + + void setChannel(int wire, int chan) + { + if (checkWire(wire)) + channelMapping.set(wire,chan); + } + + void setThreshold(int wire, float thresh) + { + if (checkWire(wire)) + threshold.set(wire,thresh); + } + + void setInputRange(int wire, float inRange) + { + if (checkWire(wire)) + inputRange.set(wire,inRange); + } + + void setState(int wire, bool state) + { + if (checkWire(wire)) + isActive.set(wire,state); + } + + int getChannel(int wire) + { + if (checkWire(wire)) + return channelMapping[wire]; + else + return -1; + } + + float getThreshold(int wire) + { + if (checkWire(wire)) + return threshold[wire]; + else + return -1; + } + + float getInputRange(int wire) + { + if (checkWire(wire)) + return inputRange[wire]; + else + return -1; + } + + bool getState(int wire) + { + if (checkWire(wire)) + return isActive[wire]; + else + return false; + } + + int* getRawDataPointer() + { + return channelMapping.getRawDataPointer(); + } + + String getName() {return name;} + +private: + Array<int, CriticalSection> channelMapping; + Array<float, CriticalSection> threshold; + Array<float, CriticalSection> inputRange; + Array<bool, CriticalSection> isActive; + + String name; + + bool checkWire(int wire) + { + if (wire < channelMapping.size() && wire > -1) + return true; + else + return false; + } + +}; + +class DataSource +{ +public: + DataSource(GenericProcessor* p, Configuration* c); + + ~DataSource() {} + + GenericProcessor* getProcessor() {return processor;} + + int numTetrodes() {return tetrodes.size();} + int numStereotrodes() {return stereotrodes.size();} + int numSingleWires() {return singleWires.size();} + int getElectrodeNumberForChannel(int chan) + { + return channelMapping[chan]; + } + + Trode* getTetrode(int n) {return tetrodes[n];} + Trode* getStereotrode(int n) {return stereotrodes[n];} + Trode* getSingleWire(int n) {return singleWires[n];} + + void addTrode(int numChannels, String name_) + { + Trode* t = new Trode(numChannels, nextAvailableChan, name_); + + for (int n = 0; n < numChannels; n++) + { + channelMapping.set(nextAvailableChan++, totalElectrodes); + } + + totalElectrodes++; + + if (t->numChannels() == 1) + singleWires.add(t); + else if (t->numChannels() == 2) + stereotrodes.add(t); + else if (t->numChannels() == 4) + tetrodes.add(t); + } + + String getName() {return name;} + + int getNumChans() {return numChans;} + int getFirstChan() {return firstChan;} + + int id; + +private: + + String name; + + OwnedArray<Trode, CriticalSection> tetrodes; + OwnedArray<Trode, CriticalSection> stereotrodes; + OwnedArray<Trode, CriticalSection> singleWires; + Array<int, CriticalSection> channelMapping; + + int totalElectrodes; + int firstChan; + int numChans; + int nextAvailableChan; + + GenericProcessor* processor; + +}; + +class Configuration +{ +public: + Configuration() {}; + ~Configuration() {}; + + void removeDataSource(GenericProcessor*); + + int numDataSources() {return dataSources.size();} + DataSource* getSource(int sourceNum) {return dataSources[sourceNum];} + + void addDataSource(DataSource* d) + { + std::cout << "New data source added." << std::endl; + dataSources.add(d); + } + +private: + + OwnedArray<DataSource, CriticalSection> dataSources; +}; + + + + +#endif // __CONFIGURATION_H_9DEA9372__ diff --git a/Source/UI/ControlPanel.cpp b/Source/UI/ControlPanel.cpp new file mode 100644 index 0000000000000000000000000000000000000000..70167f3ae72e7ca8b1c45ce5ab2dfa5fa63fd0c8 --- /dev/null +++ b/Source/UI/ControlPanel.cpp @@ -0,0 +1,299 @@ +/* + ============================================================================== + + ControlPanel.cpp + Created: 1 May 2011 2:57:48pm + Author: jsiegle + + ============================================================================== +*/ + +#include "ControlPanel.h" +#include <stdio.h> + + +PlayButton::PlayButton() + : DrawableButton (T("PlayButton"), DrawableButton::ImageFitted) +{ + + DrawablePath normal, over, down; + + Path p; + p.addTriangle (0.0f, 0.0f, 0.0f, 20.0f, 18.0f, 10.0f); + normal.setPath (p); + normal.setFill (Colours::lightgrey); + normal.setStrokeThickness (0.0f); + + over.setPath (p); + over.setFill (Colours::black); + over.setStrokeFill (Colours::black); + over.setStrokeThickness (5.0f); + + down.setPath (p); + down.setFill (Colours::pink); + down.setStrokeFill (Colours::pink); + down.setStrokeThickness (5.0f); + + setImages (&normal, &over, &over); + setBackgroundColours(Colours::darkgrey, Colours::yellow); + setClickingTogglesState (true); + setTooltip ("Start/stop acquisition"); +} + +PlayButton::~PlayButton() +{ +} + +RecordButton::RecordButton() + : DrawableButton (T("RecordButton"), DrawableButton::ImageFitted) +{ + + DrawablePath normal, over, down; + + Path p; + p.addEllipse (0.0,0.0,20.0,20.0); + normal.setPath (p); + normal.setFill (Colours::lightgrey); + normal.setStrokeThickness (0.0f); + + over.setPath (p); + over.setFill (Colours::black); + over.setStrokeFill (Colours::black); + over.setStrokeThickness (5.0f); + + setImages (&normal, &over, &over); + setBackgroundColours(Colours::darkgrey, Colours::red); + setClickingTogglesState (true); + setTooltip ("Start/stop writing to disk"); +} + +RecordButton::~RecordButton() +{ +} + + +CPUMeter::CPUMeter() : Label(T("CPU Meter"),"0.0"), cpu(0.0f), lastCpu(0.0f) +{ + //startTimer(100); + //repaint(); +} + +CPUMeter::~CPUMeter() +{ +} + +void CPUMeter::updateCPU(float usage) { + lastCpu = cpu; + cpu = usage; +} + +void CPUMeter::paint(Graphics& g) +{ + g.fillAll(Colours::grey); + + g.setColour(Colours::yellow); + g.fillRect(0.0f,0.0f,getWidth()*cpu,float(getHeight())); + + g.setColour(Colours::black); + g.drawRect(0,0,getWidth(),getHeight(),1); + +} + + +DiskSpaceMeter::DiskSpaceMeter() + +{ + //graph = g; + //updateDiskSpace(graph->getRecordNode()->getFreeSpace()); + //repaint(); + //startTimer(10000); // refresh every 10 seconds +} + + +DiskSpaceMeter::~DiskSpaceMeter() +{ +} + +void DiskSpaceMeter::updateDiskSpace(float percent) +{ + diskFree = percent; +} + +void DiskSpaceMeter::paint(Graphics& g) +{ + + g.fillAll(Colours::grey); + + g.setColour(Colours::pink); + g.fillRect(0.0f,0.0f,getWidth()*diskFree,float(getHeight())); + + g.setColour(Colours::black); + g.drawRect(0,0,getWidth(),getHeight(),1); + +} + +Clock::Clock() : Label(T("Clock"),"00:00.00") +{ + setColour(Label::textColourId, Colours::white); +} + +Clock::~Clock() +{ +} + +ControlPanel::ControlPanel(ProcessorGraph* graph_, AudioComponent* audio_) : + graph (graph_), audio(audio_) +{ + + audioEditor = (AudioEditor*) graph->getAudioNode()->createEditor(); + addChildComponent(audioEditor); + + playButton = new PlayButton(); + playButton->addListener (this); + addAndMakeVisible(playButton); + + recordButton = new RecordButton(); + recordButton->addListener (this); + addAndMakeVisible(recordButton); + + masterClock = new Clock(); + addAndMakeVisible(masterClock); + + cpuMeter = new CPUMeter(); + addAndMakeVisible(cpuMeter); + + diskMeter = new DiskSpaceMeter(); + addAndMakeVisible(diskMeter); + + startTimer(100); + + if (1) { + MemoryInputStream mis(BinaryData::misoserialized, BinaryData::misoserializedSize, false); + Typeface::Ptr typeface = new CustomTypeface(mis); + font = Font(typeface); + } +} + +ControlPanel::~ControlPanel() +{ + //deleteAllChildren() -> if this is used, audioEditor will be deleted + deleteAndZero(playButton); + deleteAndZero(recordButton); + deleteAndZero(masterClock); + deleteAndZero(cpuMeter); + deleteAndZero(diskMeter); + //audioEditor will delete itself + + graph = 0; +} + +void ControlPanel::paint(Graphics& g) +{ + g.setColour(Colour(40,40,40)); + g.fillRoundedRectangle(0,0,getWidth(),getHeight(),8.0f); + + g.setFont(font); + g.setColour(Colours::white); + g.drawText("CONTROL PANEL",getWidth()/2,0,getWidth(),getHeight(),Justification::left,true); +} + +void ControlPanel::resized() +{ + int w = getWidth(); + int h = getHeight(); + + if (playButton != 0) + playButton->setBounds(w-h*5,5,h-5,h-10); + + if (recordButton != 0) + recordButton->setBounds(w-h*4,5,h-5,h-10); + + if (masterClock != 0) + masterClock->setBounds(w-h*3,0,h*2,h); + + if (cpuMeter != 0) + cpuMeter->setBounds(20,h/4,h*4,h/2); + + if (diskMeter != 0) + diskMeter->setBounds(150,h/4,h*4,h/2); + + if (audioEditor != 0) + audioEditor->setBounds(w-h*12,5,h*5,h-10); +} + +void ControlPanel::buttonClicked(Button* button) + +{ + if (button == recordButton) + { + std::cout << "Record button pressed." << std::endl; + if (recordButton->getToggleState()) + { + playButton->setToggleState(true,true); + graph->getRecordNode()->setParameter(1,10.0f); // turn on recording + + } else { + graph->getRecordNode()->setParameter(0,10.0f); // turn off recording + + } + + } else if (button == playButton) { + std::cout << "Play button pressed." << std::endl; + if (!playButton->getToggleState()) + { + recordButton->setToggleState(false,true); + } + + } + + if (playButton->getToggleState()) + { + + if (!audio->callbacksAreActive()) { + if (graph->enableProcessors()) + audio->beginCallbacks(); + else + playButton->setToggleState(false, false); + } + + } else { + + if (audio->callbacksAreActive()) { + audio->endCallbacks(); + graph->disableProcessors(); + cpuMeter->updateCPU(0.0f); + } + + } + +} + +void ControlPanel::actionListenerCallback(const String & msg) +{ + //std::cout << "Message Received." << std::endl; + if (playButton->getToggleState()) { + cpuMeter->updateCPU(audio->getCpuUsage()); + } + + cpuMeter->repaint(); + + diskMeter->updateDiskSpace(graph->getRecordNode()->getFreeSpace()); + diskMeter->repaint(); + + +} + +void ControlPanel::timerCallback() +{ + //std::cout << "Message Received." << std::endl; + if (playButton->getToggleState()) { + cpuMeter->updateCPU(audio->getCpuUsage()); + } + + cpuMeter->repaint(); + + diskMeter->updateDiskSpace(graph->getRecordNode()->getFreeSpace()); + diskMeter->repaint(); + + +} \ No newline at end of file diff --git a/Source/UI/ControlPanel.h b/Source/UI/ControlPanel.h new file mode 100644 index 0000000000000000000000000000000000000000..32c37a627d0b0fc9c026283e51a9ae053b25c965 --- /dev/null +++ b/Source/UI/ControlPanel.h @@ -0,0 +1,113 @@ +/* + ============================================================================== + + ControlPanel.h + Created: 1 May 2011 2:57:48pm + Author: jsiegle + + ============================================================================== +*/ + +#ifndef __CONTROLPANEL_H_AD81E528__ +#define __CONTROLPANEL_H_AD81E528__ + +#include "../../JuceLibraryCode/JuceHeader.h" +#include "../Audio/AudioComponent.h" +#include "../Processors/Editors/AudioEditor.h" +#include "../Processors/ProcessorGraph.h" +#include "../Processors/RecordNode.h" +#include "CustomLookAndFeel.h" + +class PlayButton : public DrawableButton +{ + public: + PlayButton(); + ~PlayButton(); +}; + +class RecordButton : public DrawableButton +{ + public: + RecordButton(); + ~RecordButton(); +}; + +class CPUMeter : public Label//, public Timer //Component +{ + public: + CPUMeter(); + ~CPUMeter(); + + void updateCPU(float usage); + + void paint (Graphics& g); + + private: + float cpu; + float lastCpu; + + //void timerCallback() {repaint();} + +}; + +class DiskSpaceMeter : public Component//, public Timer +{ +public: + DiskSpaceMeter(); + ~DiskSpaceMeter(); + + void updateDiskSpace(float percent); + + void paint (Graphics& g); + +private: + float diskFree; + ProcessorGraph* graph; + //void timerCallback() {repaint();} + +}; + +class Clock : public Label +{ + public: + Clock(); + ~Clock(); +}; + + + +class ControlPanel : public Component, + public Button::Listener, + public ActionListener, + public Timer + +{ +public: + ControlPanel(ProcessorGraph* graph, AudioComponent* audio); + ~ControlPanel(); + +private: + PlayButton* playButton; + RecordButton* recordButton; + Clock* masterClock; + CPUMeter* cpuMeter; + DiskSpaceMeter* diskMeter; + AudioComponent* audio; + ProcessorGraph* graph; + AudioEditor* audioEditor; + + void paint(Graphics& g); + + void resized(); + void buttonClicked(Button* button); + + void actionListenerCallback(const String& msg); + + void timerCallback(); + + Font font; + +}; + + +#endif // __CONTROLPANEL_H_AD81E528__ diff --git a/Source/UI/CustomLookAndFeel.cpp b/Source/UI/CustomLookAndFeel.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c4cd2c3e44496492b0d8b87ffdb4b5598f708a13 --- /dev/null +++ b/Source/UI/CustomLookAndFeel.cpp @@ -0,0 +1,514 @@ +/* + ============================================================================== + + CustomLookAndFeel.cpp + Created: 26 Jan 2012 8:42:01pm + Author: jsiegle + + ============================================================================== +*/ + +#include "CustomLookAndFeel.h" + +CustomLookAndFeel::CustomLookAndFeel() +{ + MemoryInputStream mis(BinaryData::misoserialized, BinaryData::misoserializedSize, false); + Miso = new CustomTypeface(mis); +} + +CustomLookAndFeel::~CustomLookAndFeel() {} + +// =============== +const Typeface::Ptr CustomLookAndFeel::getTypefaceForFont (const Font& font) +{ + + return Miso; + +} + + +//============================================================================== +int CustomLookAndFeel::getTabButtonOverlap (int tabDepth) +{ + return 0; //1 + tabDepth / 4; +} + +int CustomLookAndFeel::getTabButtonSpaceAroundImage() +{ + return 6; +} + +void CustomLookAndFeel::drawTabButton (Graphics& g, + int w, int h, + const Colour& preferredColour, + int tabIndex, + const String& text, + Button& button, + TabbedButtonBar::Orientation orientation, + const bool isMouseOver, + const bool isMouseDown, + const bool isFrontTab) +{ + int length = w; + int depth = h; + + if (orientation == TabbedButtonBar::TabsAtLeft + || orientation == TabbedButtonBar::TabsAtRight) + { + swapVariables (length, depth); + } + + int borderSize = 3; // border between adjacent tabs + int innerBorderSize = 3; + int gapSize = 6; // gap between tab and tabbedComponent + float cornerSize = 5.0f; // how rounded should the corners be? + + g.setColour(Colour(103,116,140)); + + if (orientation == TabbedButtonBar::TabsAtRight) + { + g.fillRoundedRectangle(5,5,w-5,h-5,5); + + if (isFrontTab) + g.fillRect(0,5,10,h-5); + } + else if (orientation == TabbedButtonBar::TabsAtTop) + { + g.fillRoundedRectangle(borderSize,0,w-2*borderSize,h-gapSize,cornerSize-2.0); + //g.setColour(Colour(170,178,183)); + //g.fillRect(borderSize+innerBorderSize,innerBorderSize + // ,w-2*borderSize-2*innerBorderSize,h-gapSize-2*innerBorderSize); + + if (isFrontTab) { + g.setColour(Colour(103,116,140)); + g.fillRect(borderSize,h-2*gapSize,w-2*borderSize,2*gapSize); + g.fillRect(0,h-gapSize,w,gapSize); + + //g.setColour(Colour(170,178,183)); + //g.fillRect(borderSize+innerBorderSize,innerBorderSize + // ,w-2*borderSize-2*innerBorderSize,h-gapSize-2*innerBorderSize); + + g.setColour(Colour(70,70,75)); + g.fillRoundedRectangle(-borderSize,0,2*borderSize,h,cornerSize); + g.fillRoundedRectangle(w-borderSize,0,2*borderSize,h,cornerSize); + } + } + else if (orientation == TabbedButtonBar::TabsAtBottom) + { + g.fillRoundedRectangle(5,5,w-5,h-5,5); + + if (isFrontTab) + g.fillRect(5,10,w-5,20); + } + else if (orientation == TabbedButtonBar::TabsAtLeft) + { + g.fillRoundedRectangle(5,5,w-5,h-5,5); + + if (isFrontTab) + g.fillRect(5,10,w-5,20); + } + + const int indent = getTabButtonOverlap (depth); + int x = 0, y = 0; + + if (orientation == TabbedButtonBar::TabsAtLeft + || orientation == TabbedButtonBar::TabsAtRight) + { + y += indent; + h -= indent * 2; + } + else + { + x += indent; + w -= indent * 2; + } + + drawTabButtonText (g, x, y, w, h, preferredColour, + tabIndex, text, button, orientation, + isMouseOver, isMouseDown, isFrontTab); +} + + +void CustomLookAndFeel::drawTabButtonText (Graphics& g, + int x, int y, int w, int h, + const Colour& preferredBackgroundColour, + int /*tabIndex*/, + const String& text, + Button& button, + TabbedButtonBar::Orientation orientation, + const bool isMouseOver, + const bool isMouseDown, + const bool isFrontTab) +{ + int length = w; + int depth = h; + + if (orientation == TabbedButtonBar::TabsAtLeft + || orientation == TabbedButtonBar::TabsAtRight) + { + swapVariables (length, depth); + } + + Font font (depth * 0.6f); + font.setUnderline (button.hasKeyboardFocus (false)); + + GlyphArrangement textLayout; + textLayout.addFittedText (font, text.trim(), + 0.0f, 0.0f, (float) length, (float) depth, + Justification::centred, + jmax (1, depth / 12)); + + AffineTransform transform; + + if (orientation == TabbedButtonBar::TabsAtLeft) + { + transform = transform.rotated (float_Pi * -0.5f) + .translated ((float) x, (float) (y + h)); + } + else if (orientation == TabbedButtonBar::TabsAtRight) + { + transform = transform.rotated (float_Pi * 0.5f) + .translated ((float) (x + w), (float) y); + } + else if (orientation == TabbedButtonBar::TabsAtTop) + { + transform = transform.translated ((float) x, (float) y - 3.0f); + } + else + { + transform = transform.translated ((float) x, (float) y + 3.0f); + } + + if (isFrontTab && (button.isColourSpecified (TabbedButtonBar::frontTextColourId) || isColourSpecified (TabbedButtonBar::frontTextColourId))) + g.setColour (findColour (TabbedButtonBar::frontTextColourId)); + else if (button.isColourSpecified (TabbedButtonBar::tabTextColourId) || isColourSpecified (TabbedButtonBar::tabTextColourId)) + g.setColour (findColour (TabbedButtonBar::tabTextColourId)); + else + g.setColour (preferredBackgroundColour.contrasting()); + + if (! (isMouseOver || isMouseDown)) + g.setOpacity (0.8f); + + if (! button.isEnabled()) + g.setOpacity (0.3f); + + textLayout.draw (g, transform); +} + +int CustomLookAndFeel::getTabButtonBestWidth (int /*tabIndex*/, + const String& text, + int tabDepth, + Button&) +{ + Font f (tabDepth * 0.6f); + return f.getStringWidth (text.trim()) + getTabButtonOverlap (tabDepth) * 2 + 40; +} + + + +void CustomLookAndFeel::drawTabAreaBehindFrontButton (Graphics& g, + int w, int h, + TabbedButtonBar& tabBar, + TabbedButtonBar::Orientation orientation) +{ + +} + +//================================================================== +// SCROLL BAR METHODS : +//================================================================== + +void CustomLookAndFeel::drawScrollbarButton (Graphics& g, + ScrollBar& scrollbar, + int width, int height, + int buttonDirection, + bool isScrollBarVertical, + bool isMouseOverButton, + bool isButtonDown) +{ + + Path p; + + float w1 = 0.25f; + float w2 = 0.75f; + + if (buttonDirection == 0) + p.addTriangle (width * 0.5f, height * 0.2f, + width * w1, height * 0.7f, + width * w2, height * 0.7f); + else if (buttonDirection == 1) + p.addTriangle (width * 0.8f, height * 0.5f, + width * 0.3f, height * w1, + width * 0.3f, height * w2); + else if (buttonDirection == 2) + p.addTriangle (width * 0.5f, height * 0.8f, + width * w1, height * 0.3f, + width * w2, height * 0.3f); + else if (buttonDirection == 3) + p.addTriangle (width * 0.2f, height * 0.5f, + width * 0.7f, height * w1, + width * 0.7f, height * w2); + + if (isButtonDown) + g.setColour (Colours::white); + else + g.setColour (Colours::darkgrey); + + g.fillPath (p); + + if (isMouseOverButton) + g.strokePath (p, PathStrokeType (1.0f)); + + +} + +void CustomLookAndFeel::drawScrollbar (Graphics& g, + ScrollBar& scrollbar, + int x, int y, + int width, int height, + bool isScrollbarVertical, + int thumbStartPosition, + int thumbSize, + bool isMouseOver, + bool isMouseDown) + + { + + Path thumbPath; + + const float slotIndent = jmin (width, height) > 15 ? 1.0f : 0.0f; + const float thumbIndent = slotIndent + 4.0f; + const float thumbIndentx2 = thumbIndent * 2.0f; + + if (isScrollbarVertical) + { + + if (thumbSize > 0) + thumbPath.addRoundedRectangle (x + thumbIndent, + thumbStartPosition + thumbIndent, + width - thumbIndentx2, + thumbSize - thumbIndentx2, + (width - thumbIndentx2) * 0.3f); + } + else + { + + if (thumbSize > 0) + thumbPath.addRoundedRectangle (thumbStartPosition + thumbIndent, + y + thumbIndent, + thumbSize - thumbIndentx2, + height - thumbIndentx2, + (height - thumbIndentx2) * 0.3f); + + } + + g.setColour (Colours::darkgrey); + g.fillPath (thumbPath); + + } + +//================================================================== +// SLIDER METHODS : +//================================================================== + + +void CustomLookAndFeel::drawLinearSliderThumb (Graphics& g, + int x, int y, + int width, int height, + float sliderPos, + float minSliderPos, + float maxSliderPos, + const Slider::SliderStyle style, + Slider& slider) +{ + const float sliderRadius = (float) (getSliderThumbRadius (slider) - 2); + + Colour knobColour (Colours::darkgrey);//LookAndFeelHelpers::createBaseColour (slider.findColour (Slider::thumbColourId), + // slider.hasKeyboardFocus (false) && slider.isEnabled(), + // slider.isMouseOverOrDragging() && slider.isEnabled(), + // slider.isMouseButtonDown() && slider.isEnabled())); + + const float outlineThickness = slider.isEnabled() ? 2.0f : 0.5f; + + if (style == Slider::LinearHorizontal || style == Slider::LinearVertical) + { + float kx, ky; + + if (style == Slider::LinearVertical) + { + kx = x + width * 0.5f; + ky = sliderPos; + } + else + { + kx = sliderPos; + ky = y + height * 0.5f; + } + + drawSliderKnob (g, + kx - sliderRadius, + ky - sliderRadius, + sliderRadius * 2.0f, + knobColour, outlineThickness); + } + else + { + if (style == Slider::ThreeValueVertical) + { + drawSliderKnob (g, x + width * 0.5f - sliderRadius, + sliderPos - sliderRadius, + sliderRadius * 2.0f, + knobColour, outlineThickness); + } + else if (style == Slider::ThreeValueHorizontal) + { + drawSliderKnob (g,sliderPos - sliderRadius, + y + height * 0.5f - sliderRadius, + sliderRadius * 2.0f, + knobColour, outlineThickness); + } + + if (style == Slider::TwoValueVertical || style == Slider::ThreeValueVertical) + { + const float sr = jmin (sliderRadius, width * 0.4f); + + drawGlassPointer (g, jmax (0.0f, x + width * 0.5f - sliderRadius * 2.0f), + minSliderPos - sliderRadius, + sliderRadius * 2.0f, knobColour, outlineThickness, 1); + + drawGlassPointer (g, jmin (x + width - sliderRadius * 2.0f, x + width * 0.5f), maxSliderPos - sr, + sliderRadius * 2.0f, knobColour, outlineThickness, 3); + } + else if (style == Slider::TwoValueHorizontal || style == Slider::ThreeValueHorizontal) + { + const float sr = jmin (sliderRadius, height * 0.4f); + + drawGlassPointer (g, minSliderPos - sr, + jmax (0.0f, y + height * 0.5f - sliderRadius * 2.0f), + sliderRadius * 2.0f, knobColour, outlineThickness, 2); + + drawGlassPointer (g, maxSliderPos - sliderRadius, + jmin (y + height - sliderRadius * 2.0f, y + height * 0.5f), + sliderRadius * 2.0f, knobColour, outlineThickness, 4); + } + } +} + +void CustomLookAndFeel::drawLinearSliderBackground (Graphics& g, + int x, int y, + int width, int height, + float sliderPos, + float minSliderPos, + float maxSliderPos, + const Slider::SliderStyle /*style*/, + Slider& slider) +{ + const float sliderRadius = (float) (getSliderThumbRadius (slider) - 2); + + Path indent; + // Path backgroundPath; + + if (slider.isHorizontal()) + { + const float iy = y + height * 0.5f - sliderRadius * 0.5f; + const float ih = sliderRadius; + + indent.addRoundedRectangle (x - sliderRadius * 0.5f, iy, + width + sliderRadius, ih, + 5.0f); + + // backgroundPath.addRoundedRectangle (x - sliderRadius * 0.5f, iy, + // (width + sliderRadius)*minSliderPos, ih, + // 5.0f); + + // g.setColour(Colours::orange); + // g.fillPath (backgroundPath); + } + else + { + const float ix = x + width * 0.5f - sliderRadius * 0.5f; + const float iw = sliderRadius; + indent.addRoundedRectangle (ix, y - sliderRadius * 0.5f, + iw, height + sliderRadius, + 5.0f); + + // backgroundPath.addRoundedRectangle (ix, y - sliderRadius * 0.5f, + // iw, (height + sliderRadius)*sliderPos, + // 5.0f); + + // g.setColour(Colours::orange); + // g.fillPath (backgroundPath); + //g.fillPath (indent); + } + + g.setColour (Colours::darkgrey); + g.strokePath (indent, PathStrokeType (0.5f)); +} + +int CustomLookAndFeel::getSliderThumbRadius (Slider& slider) +{ + return jmin (7, + slider.getHeight() / 2, + slider.getWidth() / 2) + 2; +} + +void CustomLookAndFeel::drawSliderKnob (Graphics& g, + const float x, const float y, + const float diameter, + const Colour& colour, + const float outlineThickness) throw() +{ + if (diameter <= outlineThickness) + return; + + g.setColour(Colours::darkgrey); + + g.fillEllipse (x, y, diameter, diameter); + + g.setColour(Colours::black); + g.drawEllipse (x, y, diameter, diameter, outlineThickness); +} + +void CustomLookAndFeel::drawGlassPointer (Graphics& g, + const float x, const float y, + const float diameter, + const Colour& colour, const float outlineThickness, + const int direction) throw() +{ + if (diameter <= outlineThickness) + return; + + Path p; + p.startNewSubPath (x + diameter * 0.5f, y); + p.lineTo (x + diameter, y + diameter * 0.6f); + p.lineTo (x + diameter, y + diameter); + p.lineTo (x, y + diameter); + p.lineTo (x, y + diameter * 0.6f); + p.closeSubPath(); + + p.applyTransform (AffineTransform::rotation (direction * (float_Pi * 0.5f), x + diameter * 0.5f, y + diameter * 0.5f)); + + { + ColourGradient cg (Colours::white.overlaidWith (colour.withMultipliedAlpha (0.3f)), 0, y, + Colours::white.overlaidWith (colour.withMultipliedAlpha (0.3f)), 0, y + diameter, false); + + cg.addColour (0.4, Colours::white.overlaidWith (colour)); + + g.setGradientFill (cg); + g.fillPath (p); + } + + ColourGradient cg (Colours::transparentBlack, + x + diameter * 0.5f, y + diameter * 0.5f, + Colours::black.withAlpha (0.5f * outlineThickness * colour.getFloatAlpha()), + x - diameter * 0.2f, y + diameter * 0.5f, true); + + cg.addColour (0.5, Colours::transparentBlack); + cg.addColour (0.7, Colours::black.withAlpha (0.07f * outlineThickness)); + + g.setGradientFill (cg); + g.fillPath (p); + + g.setColour (Colours::black.withAlpha (0.5f * colour.getFloatAlpha())); + g.strokePath (p, PathStrokeType (outlineThickness)); +} \ No newline at end of file diff --git a/Source/UI/CustomLookAndFeel.h b/Source/UI/CustomLookAndFeel.h new file mode 100644 index 0000000000000000000000000000000000000000..971fdea3ccad858b57167ae7fe6fa89c1cacd92e --- /dev/null +++ b/Source/UI/CustomLookAndFeel.h @@ -0,0 +1,129 @@ +/* + ============================================================================== + + CustomLookAndFeel.h + Created: 26 Jan 2012 8:42:00pm + Author: jsiegle + + ============================================================================== +*/ + +#ifndef __CUSTOMLOOKANDFEEL_H_6B021009__ +#define __CUSTOMLOOKANDFEEL_H_6B021009__ + +#include "../../JuceLibraryCode/JuceHeader.h" + + +class CustomLookAndFeel : public LookAndFeel + +{ +public: + CustomLookAndFeel(); + ~CustomLookAndFeel(); + + // ======== custom fonts ================ + Typeface::Ptr Miso; + + // ======== custom tab methods: ============================= + + void drawTabButton (Graphics & g, + int w, int h, + const Colour& preferredColour, + int tabIndex, const String& text, + Button& button, + TabbedButtonBar::Orientation, + bool isMouseOver, + bool isMouseDown, + bool isFrontTab); + + void drawTabButtonText (Graphics& g, + int x, int y, int w, int h, + const Colour& preferredBackgroundColour, + int tabIndex, + const String& text, + Button& button, + TabbedButtonBar::Orientation o, + bool isMouseOver, + bool isMouseDown, + bool isFrontTab); + + int getTabButtonBestWidth (int tabIndex, + const String& text, + int tabDepth, + Button& button); + + int getTabButtonSpaceAroundImage (); + + void drawTabAreaBehindFrontButton (Graphics& g, + int w, int h, + TabbedButtonBar& tabBar, + TabbedButtonBar::Orientation o); + + int getTabButtonOverlap (int tabDepth); + + // ======== custom scroll bar methods: ============================= + + void drawScrollbarButton (Graphics& g, + ScrollBar& scrollbar, + int width, int height, + int buttonDirection, + bool isScrollBarVertical, + bool isMouseOverButton, + bool isButtonDown); + + void drawScrollbar (Graphics& g, + ScrollBar& scrollbar, + int x, int y, + int width, int height, + bool isScrollbarVertical, + int thumbStartPosition, + int thumbSize, + bool isMouseOver, + bool isMouseDown); + + + // ======== custom slider methods: ============================= + + void drawLinearSliderThumb (Graphics& g, + int x, int y, + int width, int height, + float sliderPos, + float minSliderPos, + float maxSliderPos, + const Slider::SliderStyle style, + Slider& slider); + + void drawLinearSliderBackground (Graphics& g, + int x, int y, + int width, int height, + float /*sliderPos*/, + float /*minSliderPos*/, + float /*maxSliderPos*/, + const Slider::SliderStyle /*style*/, + Slider& slider); + + + int getSliderThumbRadius (Slider& slider); + + void drawSliderKnob (Graphics& g, + const float x, const float y, + const float diameter, + const Colour& colour, + const float outlineThickness) throw(); + + void drawGlassPointer (Graphics& g, + const float x, const float y, + const float diameter, + const Colour& colour, const float outlineThickness, + const int direction) throw(); + + const Typeface::Ptr getTypefaceForFont (const Font& font); + + +private: + + +}; + + +#endif // __CUSTOMLOOKANDFEEL_H_6B021009__ diff --git a/Source/UI/DataViewport.cpp b/Source/UI/DataViewport.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6e5c0a72f2d41ea728024c277850711a4f97922c --- /dev/null +++ b/Source/UI/DataViewport.cpp @@ -0,0 +1,99 @@ +/* + ============================================================================== + + DataViewport.cpp + Created: 26 Jan 2012 12:38:09pm + Author: jsiegle + + ============================================================================== +*/ + +#include "DataViewport.h" + +DataViewport::DataViewport() : + TabbedComponent(TabbedButtonBar::TabsAtTop), + tabDepth(32) +{ + + tabArray = new Array<int>; + + setTabBarDepth(tabDepth); + setIndent(8); // gap to leave around the edge + // of the content component + setColour(TabbedComponent::outlineColourId, + Colours::darkgrey); + setColour(TabbedComponent::backgroundColourId, + Colours::darkgrey); + +} + +DataViewport::~DataViewport() +{ + deleteAndZero(tabArray); +} + + int DataViewport::addTabToDataViewport(String name, Component* component) { + + if (tabArray->size() == 0) + setVisible(true); + + int tabIndex = getTabbedButtonBar().getNumTabs(); + addTab(name, Colours::lightgrey, component, false, tabIndex); + getTabbedButtonBar().setCurrentTabIndex(tabIndex); + + setOutline(0); + + tabArray->add(tabIndex); + + return tabIndex; + + } + + void DataViewport::removeTab(int index) { + + int newIndex = tabArray->indexOf(index); + tabArray->remove(newIndex); + + //Component* p = getTabContentComponent(newIndex); + //removeChildComponent(p); + + getTabbedButtonBar().removeTab(newIndex); + + if (tabArray->size() == 0) + setVisible(false); + + } + +void DataViewport::paint(Graphics& g) +{ + + const TabbedButtonBar::Orientation o = getOrientation(); + + int x = 0; + int y = 0; + int r = getWidth(); + int b = getHeight(); + + if (o == TabbedButtonBar::TabsAtTop) + y += tabDepth; + else if (o == TabbedButtonBar::TabsAtBottom) + b -= tabDepth; + else if (o == TabbedButtonBar::TabsAtLeft) + x += tabDepth; + else if (o == TabbedButtonBar::TabsAtRight) + r -= tabDepth; + + //g.setColour(Colour(103,116,140)); + + Colour c1 (103, 116, 140); + Colour c2 (120, 130, 155); + + g.setGradientFill (ColourGradient (c1, + 0.0f, 0.0f, + c2, + 0.0f, (float) getHeight(), + false)); + + g.fillRoundedRectangle(x,y,r-x,b-y,8); + +} \ No newline at end of file diff --git a/Source/UI/DataViewport.h b/Source/UI/DataViewport.h new file mode 100644 index 0000000000000000000000000000000000000000..4aefe2f4db67aa8582305cfa185ec0b1b9c34cb5 --- /dev/null +++ b/Source/UI/DataViewport.h @@ -0,0 +1,41 @@ +/* + ============================================================================== + + DataViewport.h + Created: 26 Jan 2012 12:38:09pm + Author: jsiegle + + ============================================================================== +*/ + +#ifndef __DATAVIEWPORT_H_B38FE628__ +#define __DATAVIEWPORT_H_B38FE628__ + +#include "../../JuceLibraryCode/JuceHeader.h" + + +class DataViewport : public TabbedComponent + +{ +public: + DataViewport(); + ~DataViewport(); + + int addTabToDataViewport(String tabName, Component* componentToAdd); + void removeTab(int); + +private: + + Array<int>* tabArray; + + void paint(Graphics& g); + + int tabDepth; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DataViewport); + +}; + + + +#endif // __DATAVIEWPORT_H_B38FE628__ diff --git a/Source/UI/FilterList.cpp b/Source/UI/FilterList.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e5b595ee73df0235bf292f34f640651f8d7d98b5 --- /dev/null +++ b/Source/UI/FilterList.cpp @@ -0,0 +1,146 @@ +/* + ============================================================================== + + FilterList.cpp + Created: 1 May 2011 4:01:50pm + Author: jsiegle + + ============================================================================== +*/ + + +#include "FilterList.h" + + FilterList::FilterList() : treeView(0) + { + rootItem = new ListItem("Processors","none",true); + rootItem->setOpen(true); + + addAndMakeVisible (treeView = new TreeView()); + treeView->setRootItem (rootItem); + treeView->setMultiSelectEnabled(false); + treeView->setBounds(10,10,200,600); + treeView->setDefaultOpenness(true); + treeView->setRootItemVisible(false); + + //button = new Button + +} + +FilterList::~FilterList() +{ + treeView->deleteRootItem(); + deleteAllChildren(); + +} + +void FilterList::paint (Graphics& g) +{ + //g.setColour (Colour(103,116,140)); + + Colour c1 (95, 106, 130); + Colour c2 (120, 130, 155); + + g.setGradientFill (ColourGradient (c1, + 0.0f, 0.0f, + c2, + 0.0f, (float) getHeight(), + false)); + g.fillRoundedRectangle (0, 0, getWidth(), getHeight(), 8); + + //g.setColour (Colour(170,178,183)); + g.setGradientFill (ColourGradient (c1, + 0.0f, (float) getHeight(), + c2, + 0.0f, 0.0f, + false)); + g.fillRect(6,6,getWidth()-12,getHeight()-12); + //g.setColour (Colours::black); + // g.drawRoundedRectangle(0, 0, getWidth(), getHeight(), 10, 3); + // g.setColour (Colours::black); + //g.drawRoundedRectangle(5, 5, getWidth()-10, getHeight()-10, 8, 2.2); + +} + + +void FilterList::resized() +{ + if (treeView != 0) + treeView->setBoundsInset (BorderSize(10,10,10,10)); +} + + +ListItem::ListItem(const String name_, const String parentName_, bool containsSubItems_) + : name(name_), parentName(parentName_), containsSubItems(containsSubItems_) { + + if (name.equalsIgnoreCase("Processors")) { + addSubItem (new ListItem ("Sources",name,true)); + addSubItem (new ListItem ("Filters",name,true)); + addSubItem (new ListItem ("Sinks",name,true)); + //addSubItem (new ListItem ("Utilities",name,true)); + } else if (name.equalsIgnoreCase("Sources")) { + addSubItem (new ListItem ("Intan Demo Board",name,false)); + //addSubItem (new ListItem ("Custom FPGA",name,false)); + //addSubItem (new ListItem ("Network Stream",name,false)); + addSubItem (new ListItem ("Signal Generator",name,false)); + // addSubItem (new ListItem ("File Reader",name,false)); + } else if (name.equalsIgnoreCase("Filters")) { + addSubItem (new ListItem ("Bandpass Filter",name,false)); + //addSubItem (new ListItem ("Resampler",name,false)); + //addSubItem (new ListItem ("Spike Detector",name,false)); + } else if (name.equalsIgnoreCase("Sinks")) { + addSubItem (new ListItem ("LFP Viewer",name,false)); + //addSubItem (new ListItem ("Spike Viewer",name,false)); + } else if (name.equalsIgnoreCase("Utilities")) { + addSubItem (new ListItem ("Splitter",name,false)); + addSubItem (new ListItem ("Merger",name,false)); + } + +} + +ListItem::~ListItem() {}//clearSubItems();} + +void ListItem::paintItem(Graphics& g, int width, int height) { + + //if (isSelected()) + // g.fillAll (Colour(249,210,14)); + //else + // g.fillAll (Colour(170,178,183)); + + if (isSelected()) + g.setColour(Colours::yellow); + else + g.setColour(Colours::black); + + g.setFont( height*0.7f); + g.drawText (getUniqueName(),4, 0, width-4, height, Justification::centredLeft, true); +} + +const String ListItem::getDragSourceDescription() +{ + //String parentName = getParentItem()->getUniqueName(); + //std::cout << parentName << std::endl; + return parentName + "/" + name; +} + +// void ListItem::paintOpenCloseButton (Graphics &g, int width, int height, bool isMouseOver) +// { +// g.setColour(Colours::black); + +// if (isOpen()) { + +// g.drawLine(width/4, height/2, width*3/4, height/2, 1.0f); + +// } else { +// g.drawEllipse(0, 0, height/2, height/2, 1.0f); +// } +// } + +bool ListItem::mightContainSubItems() { + return containsSubItems; +} + +const String ListItem::getUniqueName() { + return name; +} + diff --git a/Source/UI/FilterList.h b/Source/UI/FilterList.h new file mode 100644 index 0000000000000000000000000000000000000000..fc89f4c2b53edc3714c99e882d2afb2ebe165b01 --- /dev/null +++ b/Source/UI/FilterList.h @@ -0,0 +1,65 @@ +/* + ============================================================================== + + FilterList.h + Created: 1 May 2011 4:01:50pm + Author: jsiegle + + ============================================================================== +*/ + +#ifndef __FILTERLIST_H_1D6290B7__ +#define __FILTERLIST_H_1D6290B7__ + + + +#include "../../JuceLibraryCode/JuceHeader.h" + +//============================================================================== +// this is the listbox containing the draggable source components + + + + +class FilterList : public Component//, + //public Button::Listener +{ +public: + FilterList(); + ~FilterList(); + + void paint (Graphics& g); +private: + TreeView* treeView; + TreeViewItem* rootItem; + + void resized(); + //Button* showHideButton; + +}; + +class ListItem : public TreeViewItem +{ +public: + ListItem(const String, const String, bool); + ~ListItem(); + + void paintItem(Graphics&, int, int); + //void paintOpenCloseButton (Graphics &g, int width, int height, bool isMouseOver); + + bool mightContainSubItems(); + const String getUniqueName(); + const String getDragSourceDescription(); + + + +private: + bool containsSubItems; + const String name; + const String parentName; + +}; + + + +#endif // __FILTERLIST_H_1D6290B7__ diff --git a/Source/UI/FilterViewport.cpp b/Source/UI/FilterViewport.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6c7a94e2097d78e45f00ed3fd03a217b7414b2f1 --- /dev/null +++ b/Source/UI/FilterViewport.cpp @@ -0,0 +1,957 @@ +/* +============================================================================== + +FilterViewport.cpp +Created: 1 May 2011 4:13:45pm +Author: jsiegle + +============================================================================== +*/ + +#include "FilterViewport.h" + +FilterViewport::FilterViewport(ProcessorGraph* pgraph, DataViewport* tcomp) + : message ("Drag-and-drop some rows from the top-left box onto this component!"), + somethingIsBeingDraggedOver (false), graph(pgraph), tabComponent(tcomp), shiftDown(false), + insertionPoint(0), componentWantsToMove(false), indexOfMovingComponent(-1), + borderSize(6), tabSize(20), tabButtonSize(15), canEdit(true)//, signalChainNeedsSource(true) +{ + + addMouseListener(this, true); + + //File file = File("./savedState.xml"); + //loadState(file); + +} + +FilterViewport::~FilterViewport() +{ + deleteAllChildren(); +} + +void FilterViewport::signalChainCanBeEdited(bool t) +{ + canEdit = t; + if (!canEdit) + std::cout << "Filter Viewport disabled."; + else + std::cout << "Filter Viewport enabled."; + +} + +void FilterViewport::paint (Graphics& g) +{ + + if (somethingIsBeingDraggedOver) + { + g.setColour (Colours::magenta); + + } else { + g.setColour (Colour(127,137,147)); + } + + g.fillRoundedRectangle (tabSize, 0, getWidth(), getHeight(), 8); + + g.setColour (Colour(170,178,183)); + g.fillRect (tabSize+borderSize,borderSize, + getWidth()-borderSize*2-tabSize, + getHeight()-borderSize*2); + + if (somethingIsBeingDraggedOver) + { + float insertionX = (float) (borderSize) * 2.5 + (float) tabSize; + + int n; + for (n = 0; n < insertionPoint; n++) + { + insertionX += editorArray[n]->getWidth(); + + } + + if (n > 1) + insertionX += borderSize*(n-1); + + g.setColour(Colours::orange); + g.drawLine(insertionX, (float) borderSize, + insertionX, (float) getHeight()-(float) borderSize, 3.0f); + + } + + //if (signalChainNeedsSource) + // { + // draw the signal chain reminders + // if (!(somethingIsBeingDraggedOver && insertionPoint == 0)) + float insertionX = (float) tabSize + (float) borderSize; + g.setColour(Colours::teal); + //g.drawLine(insertionX, (float) borderSize, + // insertionX, (float) getHeight()-(float) borderSize, 3.0f); + g.drawLine(insertionX*1.1, (float) (borderSize+10), + insertionX*1.1, (float) getHeight()-(float) (borderSize+10), 3.0f); + // } + +} + +bool FilterViewport::isInterestedInDragSource (const String& description, Component* component) +{ + + if (canEdit && description.startsWith("Processors")) { + return false; + } else { + return true; + } + +} + +void FilterViewport::itemDragEnter (const String& /*sourceDescription*/, Component* /*sourceComponent*/, int /*x*/, int /*y*/) +{ + if (canEdit) { + somethingIsBeingDraggedOver = true; + repaint(); + } +} + +void FilterViewport::itemDragMove (const String& /*sourceDescription*/, Component* /*sourceComponent*/, int x, int /*y*/) +{ + + if (canEdit) { + bool foundInsertionPoint = false; + + int lastCenterPoint = -1; + int leftEdge; + int centerPoint; + + for (int n = 0; n < editorArray.size(); n++) + { + leftEdge = editorArray[n]->getX(); + centerPoint = leftEdge + (editorArray[n]->getWidth())/2; + + if (x < centerPoint && x > lastCenterPoint) { + insertionPoint = n; + //std::cout << insertionPoint << std::endl; + foundInsertionPoint = true; + } + + lastCenterPoint = centerPoint; + } + + if (!foundInsertionPoint) { + insertionPoint = editorArray.size(); + } + + repaint(); + refreshEditors(); + } + +} + +void FilterViewport::itemDragExit (const String& /*sourceDescription*/, Component* /*sourceComponent*/) +{ + somethingIsBeingDraggedOver = false; + + repaint(); + + refreshEditors(); + +} + +void FilterViewport::itemDropped (const String& sourceDescription, Component* /*sourceComponent*/, int /*x*/, int /*y*/) +{ + + if (canEdit) { + + message = "last filter dropped: " + sourceDescription; + + std::cout << "Item dropped at insertion point " << insertionPoint << std::endl; + + /// needed to remove const cast --> should be a better way to do this + String description = sourceDescription.substring(0); + + GenericEditor* activeEditor = (GenericEditor*) graph->createNewProcessor(description);//, source, dest); + + std::cout << "Active editor: " << activeEditor << std::endl; + + if (activeEditor != 0) + { + addChildComponent(activeEditor); + + updateVisibleEditors(activeEditor, 1); + + } + + somethingIsBeingDraggedOver = false; + + repaint(); + } +} + + + +void FilterViewport::addEditor (GenericEditor* editor) +{ + + + +} + +void FilterViewport::deleteNode (GenericEditor* editor) { + + if (canEdit) { + indexOfMovingComponent = editorArray.indexOf(editor); + editor->setVisible(false); + + updateVisibleEditors(editor, 3); + + graph->removeProcessor((GenericProcessor*) editor->getProcessor()); + } + +} + +void FilterViewport::createNewTab(GenericEditor* editor) +{ + + int index = signalChainArray.size(); + + SignalChainTabButton* t = new SignalChainTabButton(); + t->setEditor(editor); + + t->setBounds(0,(tabButtonSize+5)*(index), + tabButtonSize,tabButtonSize); + addAndMakeVisible(t); + signalChainArray.add(t); + editor->tabNumber(signalChainArray.size()-1); + t->setToggleState(true,false); + t->setNumber(index); + +} + +void FilterViewport::removeTab(int tabIndex) +{ + SignalChainTabButton* t = signalChainArray.remove(tabIndex); + deleteAndZero(t); + + for (int n = 0; n < signalChainArray.size(); n++) + { + signalChainArray[n]->setBounds(0,(tabButtonSize+5)*n, + tabButtonSize,tabButtonSize); + + int tNum = signalChainArray[n]->getEditor()->tabNumber(); + + if (tNum > tabIndex) { + signalChainArray[n]->getEditor()->tabNumber(tNum-1); + signalChainArray[n]->setNumber(tNum-1); + } + + } + +} + +void FilterViewport::updateVisibleEditors(GenericEditor* activeEditor, int action) + +{ + // 1 = add + // 2 = move + // 3 = remove + + // Step 1: update the editor array + if (action == 1) /// add + { + std::cout << " Adding editor." << std::endl; + editorArray.insert(insertionPoint, activeEditor); + //activeEditor->select(); + } else if (action == 2) { /// move + std::cout << " Moving editors." << std::endl; + if (insertionPoint < indexOfMovingComponent) + editorArray.move(indexOfMovingComponent, insertionPoint); + else if (insertionPoint > indexOfMovingComponent) + editorArray.move(indexOfMovingComponent, insertionPoint-1); + + //activeEditor->select(); + } else if (action == 3) {/// remove + std::cout << " Removing editor." << std::endl; + + editorArray.remove(indexOfMovingComponent); + + int t = activeEditor->tabNumber(); + + // std::cout << editorArray.size() << " " << t << std::endl; + + if (editorArray.size() > 0) // if there are still editors in this chain + { + if (t > -1) {// pass on tab + // std::cout << "passing on the tab." << std::endl; + int nextEditor = jmax(0,0);//indexOfMovingComponent-1); + editorArray[nextEditor]->tabNumber(t); + signalChainArray[t]->setEditor(editorArray[nextEditor]); + } + + // int nextEditor; + // if (indexOfMovingComponent > editorArray.size()) + // nextEditor = indexOfMovingComponent -1; + // else if (indexOfMovingComponent == editorArray.size()) + // nextEditor = + + int nextEditor = jmin(indexOfMovingComponent,editorArray.size()-1); + activeEditor = editorArray[nextEditor]; + activeEditor->select(); + + } else { + + removeTab(t); + + if (signalChainArray.size() > 0) // if there are other chains + { + int nextTab = jmin(t,signalChainArray.size()-1); + activeEditor = signalChainArray[nextTab]->getEditor(); + activeEditor->select(); + signalChainArray[nextTab]->setToggleState(true,false); // send it back to update connections + } else { + activeEditor = 0; // nothing is active + // signalChainNeedsSource = true; + } + } + + } else { //no change + ; + } + + // Step 2: update connections + if (action < 4 && editorArray.size() > 0) { + + GenericProcessor* source = 0; + GenericProcessor* dest = (GenericProcessor*) editorArray[0]->getProcessor(); + + dest->setSourceNode(source); // set first source as 0 + + // std::cout << " " << dest->getName() << "::"; + + for (int n = 1; n < editorArray.size(); n++) + { + + dest = (GenericProcessor*) editorArray[n]->getProcessor(); + source = (GenericProcessor*) editorArray[n-1]->getProcessor(); + + dest->setSourceNode(source); + + // std::cout << dest->getName() << "::"; + } + + dest->setDestNode(0); // set last dest as 0 + + // std::cout << std::endl; + }// + + + // Step 3: check for new tabs + if (action < 4) { + + for (int n = 0; n < editorArray.size(); n++) + { + GenericProcessor* p = (GenericProcessor*) editorArray[n]->getProcessor(); + + // std::cout << editorArray[n]->tabNumber() << std::endl; + + if (p->getSourceNode() == 0)// && editorArray[n]->tabNumber() == -1) + { + if (editorArray[n]->tabNumber() == -1) + { + createNewTab(editorArray[n]); + } + + } else { + if (editorArray[n]->tabNumber() > -1) + { + removeTab(editorArray[n]->tabNumber()); + } + + editorArray[n]->tabNumber(-1); // reset tab status + } + } + } + + + + + // Step 4: Refresh editors in editor array, based on active editor + for (int n = 0; n < editorArray.size(); n++) + { + editorArray[n]->setVisible(false); + } + + editorArray.clear(); + + GenericEditor* editorToAdd = activeEditor; + + while (editorToAdd != 0) + { + + editorArray.insert(0,editorToAdd); + GenericProcessor* currentProcessor = (GenericProcessor*) editorToAdd->getProcessor(); + GenericProcessor* source = currentProcessor->getSourceNode(); + + if (source != 0) + { + // std::cout << "Source: " << source->getName() << std::endl; + editorToAdd = (GenericEditor*) source->getEditor(); + } else { + + // std::cout << "No source found." << std::endl; + editorToAdd = 0; + } + } + + editorToAdd = activeEditor; + + while (editorToAdd != 0) + { + + GenericProcessor* currentProcessor = (GenericProcessor*) editorToAdd->getProcessor(); + GenericProcessor* dest = currentProcessor->getDestNode(); + + if (dest != 0) + { + // std::cout << "Destination: " << dest->getName() << std::endl; + editorToAdd = (GenericEditor*) dest->getEditor(); + editorArray.add(editorToAdd); + + } else { + // std::cout << "No dest found." << std::endl; + editorToAdd = 0; + } + } + + //std::cout << "OK1." << std::endl; + + // Step 5: check the validity of the signal chain + bool enable = true; + + if (editorArray.size() == 1) { + + GenericProcessor* source = (GenericProcessor*) editorArray[0]->getProcessor(); + if (source->isSource()) + editorArray[0]->setEnabledState(true); + else + editorArray[0]->setEnabledState(false); + + } else { + + //bool sourceIsInChain = true; + + for (int n = 0; n < editorArray.size()-1; n++) + { + GenericProcessor* source = (GenericProcessor*) editorArray[n]->getProcessor(); + GenericProcessor* dest = (GenericProcessor*) editorArray[n+1]->getProcessor(); + + if (n == 0 && !source->isSource()) + enable = false; + + editorArray[n]->setEnabledState(enable); + + if (source->canSendSignalTo(dest) && source->enabledState()) + enable = true; + else + enable = false; + + if (enable) + std::cout << "Enabling node." << std::endl; + else + std::cout << "Not enabling node." << std::endl; + + editorArray[n+1]->setEnabledState(enable); + + } + } + + // Step 6: inform the tabs that something has changed + for (int n = 0; n < signalChainArray.size(); n++) + { + if (signalChainArray[n]->getToggleState()) + { + signalChainArray[n]->hasNewConnections(true); + } + } + + // std::cout << "OK2." << std::endl; + + // Step 7: make sure all editors are visible, and refresh + for (int n = 0; n < editorArray.size(); n++) + { + // std::cout << "Editor " << n << ": " << editorArray[n]->getName() << std::endl; + editorArray[n]->setVisible(true); + } + + insertionPoint = -1; // make sure all editors are left-justified + indexOfMovingComponent = -1; + + // std::cout << "OK3." << std::endl; + refreshEditors(); + + // std::cout << "OK4." << std::endl; + grabKeyboardFocus(); + // std::cout << "OK5." << std::endl; + +} + +void FilterViewport::refreshEditors () { + + int lastBound = borderSize+tabSize; + + for (int n = 0; n < editorArray.size(); n++) + { + if (n == 0) + { + if (!editorArray[n]->getEnabledState()) + { + lastBound += borderSize*3; + // signalChainNeedsSource = true; + } else { + // signalChainNeedsSource = false; + } + } + + if (somethingIsBeingDraggedOver && n == insertionPoint) + { + if (indexOfMovingComponent > -1) + { + if (n != indexOfMovingComponent && n != indexOfMovingComponent+1) + { + if (n == 0) + lastBound += borderSize*3; + else + lastBound += borderSize*2; + } + } else { + if (n == 0) + lastBound += borderSize*3; + else + lastBound += borderSize*2; + } + + } + + int componentWidth = editorArray[n]->desiredWidth; + editorArray[n]->setBounds(lastBound, borderSize, componentWidth, getHeight()-borderSize*2); + lastBound+=(componentWidth + borderSize); + } + +} + +void FilterViewport::moveSelection (const KeyPress &key) { + + if (key.getKeyCode() == key.leftKey) { + + for (int i = 0; i < editorArray.size(); i++) { + + if (editorArray[i]->getSelectionState() && i > 0) { + + editorArray[i-1]->select(); + editorArray[i]->deselect(); + break; + } + } + } else if (key.getKeyCode() == key.rightKey) { + + for (int i = 0; i < editorArray.size(); i++) { + + if (editorArray[i]->getSelectionState()) { + + if (i == editorArray.size()-1) + { + editorArray[i]->deselect(); + break; + } else { + editorArray[i+1]->select(); + editorArray[i]->deselect(); + break; + } + } + } + } +} + +bool FilterViewport::keyPressed (const KeyPress &key) { + + //std::cout << key.getKeyCode() << std::endl; + + if (canEdit) { + + if (key.getKeyCode() == key.deleteKey || key.getKeyCode() == key.backspaceKey) { + + for (int i = 0; i < editorArray.size(); i++) { + + if (editorArray[i]->getSelectionState()) { + deleteNode(editorArray[i]); + break; + } + } + + } else if (key.getKeyCode() == key.leftKey || key.getKeyCode() == key.rightKey) { + + moveSelection(key); + + } + } + +} + +//void FilterViewport::modifierKeysChanged (const ModifierKeys & modifiers) { + +/* if (modifiers.isShiftDown()) { + + std::cout << "Shift key pressed." << std::endl; + shiftDown = true; + + } else { + + + std::cout << "Shift key released." << std::endl; + shiftDown = false; + }*/ + +//} + +void FilterViewport::selectEditor(GenericEditor* editor) +{ + for (int i = 0; i < editorArray.size(); i++) { + + if (editor == editorArray[i] + || editor->getParentComponent() == editorArray[i]) { + editorArray[i]->select(); + } else { + editorArray[i]->deselect(); + } + } +} + +void FilterViewport::mouseDown(const MouseEvent &e) { + + for (int i = 0; i < editorArray.size(); i++) { + + if (e.eventComponent == editorArray[i] + || e.eventComponent->getParentComponent() == editorArray[i]) { + editorArray[i]->select(); + } else { + editorArray[i]->deselect(); + } + } + + // selectEditor((GenericEditor*) e.eventComponent); + +} + +void FilterViewport::mouseDrag(const MouseEvent &e) { + + + if (editorArray.contains((GenericEditor*) e.originalComponent) + && e.y < 15 + && canEdit + && editorArray.size() > 1) { + + componentWantsToMove = true; + indexOfMovingComponent = editorArray.indexOf((GenericEditor*) e.originalComponent); + + } + + if (componentWantsToMove) + { + + somethingIsBeingDraggedOver = true; + + bool foundInsertionPoint = false; + + int lastCenterPoint = 0; + int leftEdge; + int centerPoint; + + const MouseEvent event = e.getEventRelativeTo(this); + + for (int n = 0; n < editorArray.size(); n++) + { + leftEdge = editorArray[n]->getX(); + centerPoint = leftEdge + (editorArray[n]->getWidth())/2; + + if (event.x < centerPoint && event.x > lastCenterPoint) + { + insertionPoint = n; + foundInsertionPoint = true; + } + + lastCenterPoint = centerPoint; + } + + if (!foundInsertionPoint && indexOfMovingComponent != editorArray.size()-1) { + insertionPoint = editorArray.size(); + } + + refreshEditors(); + repaint(); + } + +} + +void FilterViewport::mouseUp(const MouseEvent &e) { + + + if (componentWantsToMove) { + + somethingIsBeingDraggedOver = false; + componentWantsToMove = false; + + GenericEditor* editor = editorArray[indexOfMovingComponent]; + + updateVisibleEditors(editor, 2); + refreshEditors(); + repaint(); + + } + + +} + +void FilterViewport::mouseExit(const MouseEvent &e) { + + if (componentWantsToMove) { + + somethingIsBeingDraggedOver = false; + componentWantsToMove = false; + + repaint(); + refreshEditors(); + + } + + +} + +SignalChainTabButton::SignalChainTabButton() : Button("Name"), + configurationChanged(true) + { + setRadioGroupId(99); + //setToggleState(false,true); + setClickingTogglesState(true); + + MemoryInputStream mis(BinaryData::misoserialized, BinaryData::misoserializedSize, false); + Typeface::Ptr typeface = new CustomTypeface(mis); + buttonFont = Font(typeface); + buttonFont.setHeight(14); + } + + +void SignalChainTabButton::clicked() +{ + { + + //std::cout << "Button clicked: " << firstEditor->getName() << std::endl; + FilterViewport* fv = (FilterViewport*) getParentComponent(); + + fv->updateVisibleEditors(firstEditor,4); + } + +} + +void SignalChainTabButton::paintButton(Graphics &g, bool isMouseOver, bool isButtonDown) +{ + if (getToggleState() == true) + g.setColour(Colours::teal); + else + g.setColour(Colours::darkgrey); + + if (isMouseOver) + g.setColour(Colours::white); + + g.fillEllipse(0,0,getWidth(),getHeight()); + + g.setFont(buttonFont); + g.setColour(Colours::black); + + String n; + + if (num == 0) + n = "A"; + else if (num == 1) + n = "B"; + else if (num == 2) + n = "C"; + else if (num == 3) + n = "D"; + else if (num == 4) + n = "E"; + else if (num == 5) + n = "F"; + else if (num == 6) + n = "G"; + else if (num == 7) + n = "H"; + else if (num == 8) + n = "I"; + else + n = "-"; + + g.drawText(n,0,0,getWidth(),getHeight(),Justification::centred,true); + } + + + +// how about some loading and saving? + +// void FilterViewport::loadSignalChain() +// { +// insertionPoint = 0; + +// itemDropped ("Sources/Intan Demo Board", 0, 0, 0); + +// insertionPoint = 1; + +// itemDropped ("Filters/Bandpass Filter", 0, 0, 0); +// } + +// void FilterViewport::saveSignalChain() +// { + + + +// } + + + + +XmlElement* FilterViewport::createNodeXml (GenericEditor* editor, + int insertionPt) +{ + + // if (editor == 0) + // return 0; + + XmlElement* e = new XmlElement("PROCESSOR"); + + GenericProcessor* source = (GenericProcessor*) editor->getProcessor(); + + String name = ""; + + if (source->isSource()) + name += "Sources/"; + else if (source->isSink()) + name += "Sinks/"; + else if (source->isSplitter() || source->isMerger()) + name += "Utilities/"; + else + name += "Filters/"; + + name += editor->getName(); + + std::cout << name << std::endl; + + e->setAttribute (T("name"), name); + e->setAttribute (T("insertionPoint"), insertionPt); + + GenericProcessor* dest = (GenericProcessor*) source->getDestNode(); + + //if (dest != 0) + // editor = (GenericEditor*) dest->getEditor(); + //else + // editor = 0; +// + return e; + + // if (dest != 0) + // return (GenericEditor*) dest->getEditor(); + // else + // return 0; + + // int sourceId = -1; + // int destId = -1; + + // if (processor->getSourceNode() != 0) + // sourceId = processor->getSourceNode()->getNodeId(); + + // if (processor->getDestNode() != 0) + // destId = processor->getDestNode()->getNodeId(); + + // e->setAttribute (T("dest"), destId); + // e->setAttribute (T("source"), sourceId); + + // XmlElement* state = new XmlElement ("STATE"); + + // MemoryBlock m; + // node->getProcessor()->getStateInformation (m); + // state->addTextElement (m.toBase64Encoding()); + // e->addChildElement (state); + + /// return e; + +} + +const String FilterViewport::saveState(const File& file) +{ + + XmlElement* xml = new XmlElement("PROCESSORGRAPH"); + + for (int n = 0; n < signalChainArray.size(); n++) + { + + XmlElement* signalChain = new XmlElement("SIGNALCHAIN"); + + GenericEditor* editor = signalChainArray[n]->getEditor(); + + int insertionPt = 0; + + while (editor != 0) + { + + signalChain->addChildElement(createNodeXml(editor, insertionPt)); + + GenericProcessor* source = (GenericProcessor*) editor->getProcessor(); + GenericProcessor* dest = (GenericProcessor*) source->getDestNode(); + + if (dest != 0) + editor = (GenericEditor*) dest->getEditor(); + else + editor = 0; + + insertionPt++; + } + + xml->addChildElement(signalChain); + } + + String error; + + std::cout << "Saving processor graph." << std::endl; + + if (! xml->writeToFile (file, String::empty)) + error = "Couldn't write to file"; + + delete xml; + + return error; +} + +const String FilterViewport::loadState(const File& file) +{ + std::cout << "Loading processor graph." << std::endl; + + XmlDocument doc (file); + XmlElement* xml = doc.getDocumentElement(); + + if (xml == 0 || ! xml->hasTagName (T("PROCESSORGRAPH"))) + { + delete xml; + return "Not a valid file."; + } + + String description;// = T(" "); + + forEachXmlChildElement (*xml, signalChain) + { + forEachXmlChildElement(*signalChain, processor) + { + insertionPoint = processor->getIntAttribute("insertionPoint"); + + itemDropped(processor->getStringAttribute("name"),0,0,0); + } + + } + + delete xml; + return "Everything went ok."; + +} \ No newline at end of file diff --git a/Source/UI/FilterViewport.h b/Source/UI/FilterViewport.h new file mode 100644 index 0000000000000000000000000000000000000000..7873208d6f7fa398c4edd33b5e3da4f51abd67f8 --- /dev/null +++ b/Source/UI/FilterViewport.h @@ -0,0 +1,134 @@ +/* + ============================================================================== + + FilterViewport.h + Created: 1 May 2011 4:13:45pm + Author: jsiegle + + ============================================================================== +*/ + +#ifndef __FILTERVIEWPORT_H_80260F3F__ +#define __FILTERVIEWPORT_H_80260F3F__ + +#include "../../JuceLibraryCode/JuceHeader.h" +#include "../Processors/ProcessorGraph.h" +#include "../Processors/Editors/GenericEditor.h" +#include "DataViewport.h" + +class GenericEditor; +class SignalChainTabButton; + +class FilterViewport : public Component, + public DragAndDropTarget//, + //public KeyListener +{ +public: + + FilterViewport(ProcessorGraph* pgraph, DataViewport* tabComp); + ~FilterViewport(); + + void paint (Graphics& g); + + // Creating and deleting editors: + void deleteNode(GenericEditor* editor); + void addEditor (GenericEditor*); + void updateVisibleEditors(GenericEditor* activeEditor, int action); + void selectEditor(GenericEditor* e); + // void setActiveEditor(GenericEditor* e) {activeEditor = e; updateVisibleEditors();} + + void signalChainCanBeEdited(bool t); + + // DragAndDropTarget methods: + bool isInterestedInDragSource (const String& /*sourceDescription*/, Component* /*sourceComponent*/); + void itemDragEnter (const String& /*sourceDescription*/, Component* /*sourceComponent*/, int /*x*/, int /*y*/); + void itemDragMove (const String& /*sourceDescription*/, Component* /*sourceComponent*/, int /*x*/, int /*y*/); + void itemDragExit (const String& /*sourceDescription*/, Component* /*sourceComponent*/); + void itemDropped (const String& sourceDescription, Component* /*sourceComponent*/, int /*x*/, int /*y*/); + + // mouse and keypress methods: + void mouseDown(const MouseEvent &e); + void mouseDrag(const MouseEvent &e); + void mouseUp(const MouseEvent &e); + void mouseExit(const MouseEvent &e); + //void mouseEnter(const MouseEvent &e); + //void mouseExit + //void modifierKeysChanged (const ModifierKeys & modifiers); + bool keyPressed (const KeyPress &key); + void moveSelection( const KeyPress &key); + + Array<SignalChainTabButton*, CriticalSection> requestSignalChain() {return signalChainArray;} + + // loading and saving! + const String saveState(const File& file); + const String loadState(const File& file); + + XmlElement* createNodeXml(GenericEditor*, int); + +private: + + String message; + bool somethingIsBeingDraggedOver; + bool shiftDown; + + bool canEdit; + + ProcessorGraph* graph; + DataViewport* tabComponent; + + Array<GenericEditor*, CriticalSection> editorArray; + Array<SignalChainTabButton*, CriticalSection> signalChainArray; + // GenericEditor* activeEditor; + + + // int activeTab; + + void refreshEditors(); + void createNewTab(GenericEditor* editor); + void removeTab(int tabIndex); + //void drawTabs(); + + int borderSize, tabSize, tabButtonSize; + + int insertionPoint; + bool componentWantsToMove; + int indexOfMovingComponent; + + //bool signalChainNeedsSource; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FilterViewport); + +}; + +class SignalChainTabButton : public Button +{ +public: + SignalChainTabButton();// : Button("Name") {configurationChanged = true;} + ~SignalChainTabButton() {} + + void setEditor(GenericEditor* p) {firstEditor = p;} + GenericEditor* getEditor() {return firstEditor;} + + void setNumber(int n) {num = n;} + + bool hasNewConnections() {return configurationChanged;} + void hasNewConnections(bool t) {configurationChanged = t;} + + +private: + + GenericEditor* firstEditor; + + void paintButton(Graphics &g, bool isMouseOver, bool isButtonDown); + + void clicked(); + + int num; + bool configurationChanged; + + Font buttonFont; + + +}; + +#endif // __FILTERVIEWPORT_H_80260F3F__ diff --git a/Source/UI/InfoLabel.cpp b/Source/UI/InfoLabel.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ed30a39c207d49295c132f960b6e7a53a6a684ed --- /dev/null +++ b/Source/UI/InfoLabel.cpp @@ -0,0 +1,139 @@ +/* + ============================================================================== + + InfoLabel.cpp + Created: 26 Jan 2012 12:52:07pm + Author: jsiegle + + ============================================================================== +*/ + +#include "InfoLabel.h" + +InfoLabel::InfoLabel() : xBuffer(10), yBuffer(10) +{ + +} + +InfoLabel::~InfoLabel() +{ + +} + + +void InfoLabel::newOpenGLContextCreated() +{ + + setUp2DCanvas(); + activateAntiAliasing(); + + glClearColor (0.667, 0.698, 0.718, 1.0); + resized(); + + +} + +void InfoLabel::renderOpenGL() +{ + //makeCurrentContextActive(); + + glClear(GL_COLOR_BUFFER_BIT); // clear buffers to preset values + + drawLabel(); + + drawScrollBars(); + + //makeCurrentContextInactive(); +} + + +void InfoLabel::drawLabel() +{ + + glViewport(xBuffer, + yBuffer-getScrollAmount(), + getWidth()-2*xBuffer, + jmax(getHeight(),getTotalHeight())-2*yBuffer); + + float mult = 1/float(getWidth()); + + glColor4f(0.5,0.5,0.5,0.6); + + glBegin(GL_LINE_STRIP); + glVertex2f(0.1,0); + glVertex2f(0.1,1.0); + glEnd(); + + glBegin(GL_LINE_STRIP); + glVertex2f(0,0.1); + glVertex2f(1.0,0.1); + glEnd(); + + glBegin(GL_LINE_STRIP); + glVertex2f(0.9,0); + glVertex2f(0.9,1.0); + glEnd(); + + glBegin(GL_LINE_STRIP); + glVertex2f(0,0.9); + glVertex2f(1.0,0.9); + glEnd(); + + glColor4f(0.5,0.5,0.5,0.8); + + getFont(String("miso-regular"))->FaceSize(12.0f); + + for (float x = 0.1f; x < 1.0f; x += 0.8f) + { + for (float y = 0.1f; y < 1.0f; y += 0.8f) + { + glRasterPos2f(x+0.005f,y+0.025f); + String s = String("(0."); + s += int(x*10); + s += String(", 0."); + s += int(y*10); + s += String(")"); + getFont(String("miso-regular"))->Render(s); + } + } +} + +void InfoLabel::resized() +{ + + //std::cout << getWidth() << " " << getHeight() + // << std::endl; + + canvasWasResized(); + +} + +int InfoLabel::getTotalHeight() +{ + return 300; +} + +void InfoLabel::mouseDown(const MouseEvent& e) +{ + // Point<int> pos = e.getPosition(); + // int xcoord = pos.getX(); + + // if (xcoord < getWidth()-getScrollBarWidth()) + // { + // int chan = (e.getMouseDownY() + getScrollAmount())/(yBuffer+plotHeight); + + // //if (chan == selectedChan) + // // selectedChan = -1; + // //else + // selectedChan = chan; + + // repaint(); + // } + mouseDownInCanvas(e); +} + +void InfoLabel::mouseDrag(const MouseEvent& e) {mouseDragInCanvas(e);} +void InfoLabel::mouseMove(const MouseEvent& e) {mouseMoveInCanvas(e);} +void InfoLabel::mouseUp(const MouseEvent& e) {mouseUpInCanvas(e);} +void InfoLabel::mouseWheelMove(const MouseEvent& e, float a, float b) {mouseWheelMoveInCanvas(e,a,b);} + diff --git a/Source/UI/InfoLabel.h b/Source/UI/InfoLabel.h new file mode 100644 index 0000000000000000000000000000000000000000..af6b7ca55c62ea7eb9bc02bf867cd20559da6668 --- /dev/null +++ b/Source/UI/InfoLabel.h @@ -0,0 +1,48 @@ +/* + ============================================================================== + + InfoLabel.h + Created: 26 Jan 2012 12:52:07pm + Author: jsiegle + + ============================================================================== +*/ + +#ifndef __INFOLABEL_H_14DA9A62__ +#define __INFOLABEL_H_14DA9A62__ + +#include "../../JuceLibraryCode/JuceHeader.h" +#include "../Processors/Visualization/OpenGLCanvas.h" + +class InfoLabel : public OpenGLCanvas + +{ +public: + InfoLabel(); + ~InfoLabel(); + void newOpenGLContextCreated(); + void renderOpenGL(); + +private: + + int xBuffer, yBuffer; + + void drawLabel(); + + int getTotalHeight(); + + void resized(); + void mouseDown(const MouseEvent& e); + void mouseDrag(const MouseEvent& e); + void mouseMove(const MouseEvent& e); + void mouseUp(const MouseEvent& e); + void mouseWheelMove(const MouseEvent&, float, float); + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (InfoLabel); + +}; + + + + +#endif // __INFOLABEL_H_14DA9A62__ diff --git a/Source/UI/MessageCenter.cpp b/Source/UI/MessageCenter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b51fedcedb28c27c986067e153488e907a43dcf2 --- /dev/null +++ b/Source/UI/MessageCenter.cpp @@ -0,0 +1,59 @@ +/* + ============================================================================== + + MessageCenter.cpp + Created: 31 Jul 2011 3:31:01pm + Author: jsiegle + + ============================================================================== +*/ + +#include "MessageCenter.h" + + +MessageCenter::MessageCenter() : + messageBackground(Colours::grey.withAlpha(0.5f)) { + + messageDisplayArea = new Label("Message Display Area","No new messages."); + + addAndMakeVisible(messageDisplayArea); + +} + +MessageCenter::~MessageCenter() { + + + deleteAllChildren(); + +} + +void MessageCenter::paint(Graphics& g) +{ + + g.setColour (Colour(103,116,140)); + + g.fillRoundedRectangle (0, 0, getWidth(), getHeight(), 5); + + g.setColour (messageBackground); + + g.fillRect (5, 5, getWidth()-10, getHeight()-10); + +} + +void MessageCenter::resized() +{ + if (messageDisplayArea != 0) + messageDisplayArea->setBounds(5,0,getWidth(),getHeight()); + +} + +void MessageCenter::actionListenerCallback(const String& message) +{ + + messageDisplayArea->setText(message,false); + + messageBackground = Colours::orange; + + repaint(); + +} \ No newline at end of file diff --git a/Source/UI/MessageCenter.h b/Source/UI/MessageCenter.h new file mode 100644 index 0000000000000000000000000000000000000000..fb68fdae02ea752289f0015e5e9c7d6e11b4d5b6 --- /dev/null +++ b/Source/UI/MessageCenter.h @@ -0,0 +1,41 @@ +/* + ============================================================================== + + MessageCenter.h + Created: 31 Jul 2011 3:31:01pm + Author: jsiegle + + ============================================================================== +*/ + +#ifndef __MESSAGECENTER_H_2695FC38__ +#define __MESSAGECENTER_H_2695FC38__ + + +#include "../../JuceLibraryCode/JuceHeader.h" + + +class MessageCenter : public Component, + public ActionListener + +{ +public: + MessageCenter(); + ~MessageCenter(); + + void paint (Graphics& g); + +private: + Label* messageDisplayArea; + + void resized(); + + void actionListenerCallback(const String& message); + + Colour messageBackground; + +}; + + + +#endif // __MESSAGECENTER_H_2695FC38__ diff --git a/Source/UI/UIComponent.cpp b/Source/UI/UIComponent.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8e0259b9bf465108ed20568e4efffa9fbcc6b3b3 --- /dev/null +++ b/Source/UI/UIComponent.cpp @@ -0,0 +1,103 @@ +/* + ============================================================================== + + UIComponent.cpp + Created: 30 Apr 2011 8:33:05pm + Author: jsiegle + + ============================================================================== +*/ + +#include "UIComponent.h" +#include <stdio.h> + +UIComponent::UIComponent (ProcessorGraph* pgraph, AudioComponent* audio_) + : processorGraph(pgraph), audio(audio_) + +{ + + processorGraph->setUIComponent(this); + + infoLabel = new InfoLabel(); + + dataViewport = new DataViewport (); + addChildComponent(dataViewport); + dataViewport->addTabToDataViewport("Info",infoLabel); + + std::cout << "Created data viewport." << std::endl; + + filterViewport = new FilterViewport(processorGraph, dataViewport); + processorGraph->setFilterViewport(filterViewport); + + addAndMakeVisible(filterViewport); + + std::cout << "Created filter viewport." << std::endl; + + controlPanel = new ControlPanel(processorGraph, audio); + addAndMakeVisible(controlPanel); + + std::cout << "Created control panel." << std::endl; + + filterList = new FilterList(); + addAndMakeVisible(filterList); + + std::cout << "Created filter list." << std::endl; + + messageCenter = new MessageCenter(); + processorGraph->addActionListener(messageCenter); + addAndMakeVisible(messageCenter); + + std::cout << "Created message center." << std::endl; + + config = new Configuration(); + processorGraph->setConfiguration(config); + + std::cout << "Created configuration object." << std::endl; + + setBounds(0,0,500,400); + + std::cout << "Component width = " << getWidth() << std::endl; + std::cout << "Component height = " << getHeight() << std::endl; + + std::cout << "Finished UI stuff." << std::endl; + + std::cout << "UI component data viewport: " << dataViewport << std::endl; + + processorGraph->loadState(); + +} + +UIComponent::~UIComponent() +{ + deleteAllChildren(); + + deleteAndZero(config); + deleteAndZero(infoLabel); + + processorGraph = 0; + audio = 0; +} + +void UIComponent::resized() +{ + + int w = getWidth(); + int h = getHeight(); + + if (dataViewport != 0) { + dataViewport->setBounds(212,45,w-230,h-230); + } + + if (filterViewport != 0) + filterViewport->setBounds(10,h-175,w-20,125); + + if (controlPanel != 0) + controlPanel->setBounds(10,10,w-20,30); + + if (filterList != 0) + filterList->setBounds(10,50,192,h-235); + + if (messageCenter != 0) + messageCenter->setBounds(40,h-40,w-160,30); + +} \ No newline at end of file diff --git a/Source/UI/UIComponent.h b/Source/UI/UIComponent.h new file mode 100644 index 0000000000000000000000000000000000000000..2a9ecf1e1ad6b735f745044b355ef7fe0893b435 --- /dev/null +++ b/Source/UI/UIComponent.h @@ -0,0 +1,63 @@ +/* + ============================================================================== + + UIComponent.h + Created: 30 Apr 2011 8:33:05pm + Author: jsiegle + + ============================================================================== +*/ + +#ifndef __UICOMPONENT_H_D97C73CF__ +#define __UICOMPONENT_H_D97C73CF__ + +#include "../../JuceLibraryCode/JuceHeader.h" +#include "InfoLabel.h" +#include "ControlPanel.h" +#include "FilterList.h" +#include "FilterViewport.h" +#include "DataViewport.h" +#include "MessageCenter.h" +#include "Configuration.h" +#include "../Processors/DisplayNode.h" +#include "../Processors/ProcessorGraph.h" +#include "../Audio/AudioComponent.h" + + +class UIComponent : public Component, + //public ActionBroadcaster, + public DragAndDropContainer // required for + // drag-and-drop + // internal components + +{ +public: + UIComponent(ProcessorGraph* pgraph, AudioComponent* audio); + ~UIComponent(); + + FilterViewport* getFilterViewport() {return filterViewport;} + DataViewport* getDataViewport() {return dataViewport;} + Configuration* getConfiguration() {return config;} + + //void transmitMessage(const String& message); + +private: + + DataViewport* dataViewport; + FilterViewport* filterViewport; + FilterList* filterList; + ControlPanel* controlPanel; + MessageCenter* messageCenter; + Configuration* config; + InfoLabel* infoLabel; + + ProcessorGraph* processorGraph; + AudioComponent* audio; + + void resized(); + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UIComponent); + +}; + +#endif // __UICOMPONENT_H_D97C73CF__