From ffa1b92c1e60ae6319fd03cc94f0db1af111509f Mon Sep 17 00:00:00 2001 From: Luca Bondi Date: Sat, 2 May 2020 16:18:14 +0200 Subject: [PATCH 1/4] Huge refractoring and re-organization --- Source/{FIR.cpp => AudioBufferFFT.cpp} | 144 +++--- Source/AudioBufferFFT.h | 55 +++ Source/AudioComponents.cpp | 216 --------- Source/AudioComponents.h | 206 -------- Source/AudioParts.cpp | 67 --- Source/AudioParts.h | 31 -- Source/Beamformer.cpp | 207 ++++---- Source/Beamformer.h | 178 ++++--- Source/BeamformingAlgorithm.h | 45 -- Source/BeamformingAlgorithms.cpp | 73 +++ Source/BeamformingAlgorithms.h | 112 +++++ Source/CpuLoadComp.cpp | 35 +- Source/CpuLoadComp.h | 48 +- Source/DAS.cpp | 78 --- Source/DAS.h | 81 ---- Source/FIR.h | 48 -- Source/MeterComp.cpp | 193 ++++++++ Source/MeterComp.h | 104 ++++ Source/MeterDecay.cpp | 70 +++ Source/MeterDecay.h | 45 ++ Source/MidiCC.h | 44 ++ Source/MidiComp.cpp | 85 ++-- Source/MidiComp.h | 179 +++++-- Source/PluginEditor.cpp | 198 ++++---- Source/PluginEditor.h | 96 ++-- Source/PluginProcessor.cpp | 469 ++++++++++--------- Source/PluginProcessor.h | 225 ++++----- Source/SceneComp.cpp | 250 ++++++++++ Source/SceneComp.h | 161 +++++++ Source/SceneComponent.cpp | 286 ----------- Source/SceneComponent.h | 138 ------ Source/{SigProc.cpp => SignalProcessing.cpp} | 44 +- Source/{SigProc.h => SignalProcessing.h} | 14 +- Source/ebeamerDefs.h | 77 +++ eBeamer.jucer | 67 ++- 35 files changed, 2215 insertions(+), 2154 deletions(-) rename Source/{FIR.cpp => AudioBufferFFT.cpp} (52%) create mode 100644 Source/AudioBufferFFT.h delete mode 100644 Source/AudioComponents.cpp delete mode 100644 Source/AudioComponents.h delete mode 100644 Source/AudioParts.cpp delete mode 100644 Source/AudioParts.h delete mode 100644 Source/BeamformingAlgorithm.h create mode 100644 Source/BeamformingAlgorithms.cpp create mode 100644 Source/BeamformingAlgorithms.h delete mode 100644 Source/DAS.cpp delete mode 100644 Source/DAS.h delete mode 100644 Source/FIR.h create mode 100644 Source/MeterComp.cpp create mode 100644 Source/MeterComp.h create mode 100644 Source/MeterDecay.cpp create mode 100644 Source/MeterDecay.h create mode 100644 Source/MidiCC.h create mode 100644 Source/SceneComp.cpp create mode 100644 Source/SceneComp.h delete mode 100644 Source/SceneComponent.cpp delete mode 100644 Source/SceneComponent.h rename Source/{SigProc.cpp => SignalProcessing.cpp} (53%) rename Source/{SigProc.h => SignalProcessing.h} (72%) create mode 100644 Source/ebeamerDefs.h diff --git a/Source/FIR.cpp b/Source/AudioBufferFFT.cpp similarity index 52% rename from Source/FIR.cpp rename to Source/AudioBufferFFT.cpp index 7b616a2..16a1476 100644 --- a/Source/FIR.cpp +++ b/Source/AudioBufferFFT.cpp @@ -1,167 +1,159 @@ /* - ============================================================================== + Audio Buffer in FFT domain - vFIR.cpp - Created: 27 Apr 2019 9:45:28am - Author: Luca Bondi - - ============================================================================== - */ - -#include "FIR.h" + Authors: + Luca Bondi (luca.bondi@polimi.it) +*/ +#include "AudioBufferFFT.h" -namespace FIR{ -//========== copied from juce_Convolution.cpp ============ - -/** After each FFT, this function is called to allow convolution to be performed with only 4 SIMD functions calls. */ -void AudioBufferFFT::prepareForConvolution (float *samples, int fftSize) noexcept -{ +/** After each FFT, this function is called to allow convolution to be performed with only 4 SIMD functions calls. + Credits to juce_Convolution.cpp + */ +void AudioBufferFFT::prepareForConvolution(float *samples, int fftSize) const { int FFTSizeDiv2 = fftSize / 2; - + for (size_t i = 0; i < FFTSizeDiv2; i++) samples[i] = samples[2 * i]; - + samples[FFTSizeDiv2] = 0; - + for (size_t i = 1; i < FFTSizeDiv2; i++) samples[i + FFTSizeDiv2] = -samples[2 * (fftSize - i) + 1]; } -/** Does the convolution operation itself only on half of the frequency domain samples. */ -void AudioBufferFFT::convolutionProcessingAndAccumulate (const float *input, const float *impulse, float *output, int fftSize) -{ +/** Does the convolution operation itself only on half of the frequency domain samples. + Credits to juce_Convolution.cpp*/ +void AudioBufferFFT::convolutionProcessingAndAccumulate(const float *input, const float *impulse, float *output, + int fftSize) const { int FFTSizeDiv2 = fftSize / 2; - - FloatVectorOperations::addWithMultiply (output, input, impulse, FFTSizeDiv2); - FloatVectorOperations::subtractWithMultiply (output, &(input[FFTSizeDiv2]), &(impulse[FFTSizeDiv2]), FFTSizeDiv2); - - FloatVectorOperations::addWithMultiply (&(output[FFTSizeDiv2]), input, &(impulse[FFTSizeDiv2]), FFTSizeDiv2); - FloatVectorOperations::addWithMultiply (&(output[FFTSizeDiv2]), &(input[FFTSizeDiv2]), impulse, FFTSizeDiv2); - + + FloatVectorOperations::addWithMultiply(output, input, impulse, FFTSizeDiv2); + FloatVectorOperations::subtractWithMultiply(output, &(input[FFTSizeDiv2]), &(impulse[FFTSizeDiv2]), FFTSizeDiv2); + + FloatVectorOperations::addWithMultiply(&(output[FFTSizeDiv2]), input, &(impulse[FFTSizeDiv2]), FFTSizeDiv2); + FloatVectorOperations::addWithMultiply(&(output[FFTSizeDiv2]), &(input[FFTSizeDiv2]), impulse, FFTSizeDiv2); + output[fftSize] += input[fftSize] * impulse[fftSize]; } /** Undo the re-organization of samples from the function prepareForConvolution. - Then, takes the conjugate of the frequency domain first half of samples, to fill the - second half, so that the inverse transform will return real samples in the time domain. + Then, takes the conjugate of the frequency domain first half of samples, to fill the + second half, so that the inverse transform will return real samples in the time domain. + Credits to juce_Convolution.cpp */ -void AudioBufferFFT::updateSymmetricFrequencyDomainData (float* samples, int fftSize) noexcept -{ +void AudioBufferFFT::updateSymmetricFrequencyDomainData(float *samples, int fftSize) const { auto FFTSizeDiv2 = fftSize / 2; - - for (size_t i = 1; i < FFTSizeDiv2; i++) - { + + for (size_t i = 1; i < FFTSizeDiv2; i++) { samples[2 * (fftSize - i)] = samples[i]; samples[2 * (fftSize - i) + 1] = -samples[FFTSizeDiv2 + i]; } - + samples[1] = 0.f; - - for (size_t i = 1; i < FFTSizeDiv2; i++) - { + + for (size_t i = 1; i < FFTSizeDiv2; i++) { samples[2 * i] = samples[2 * (fftSize - i)]; samples[2 * i + 1] = -samples[2 * (fftSize - i) + 1]; } } -AudioBufferFFT::AudioBufferFFT(int numChannels,std::shared_ptr&fft_){ +AudioBufferFFT::AudioBufferFFT(int numChannels, std::shared_ptr &fft_) { fft = fft_; - convBuffer = AudioBuffer(1,fft->getSize()*2); - setSize(numChannels, fft->getSize()*2); + convBuffer = AudioBuffer(1, fft->getSize() * 2); + setSize(numChannels, fft->getSize() * 2); } -AudioBufferFFT::AudioBufferFFT(const AudioBuffer& in_,std::shared_ptr&fft_){ +AudioBufferFFT::AudioBufferFFT(const AudioBuffer &in_, std::shared_ptr &fft_) { fft = fft_; - convBuffer = AudioBuffer(1,fft->getSize()*2); - setSize(in_.getNumChannels(), fft->getSize()*2); + convBuffer = AudioBuffer(1, fft->getSize() * 2); + setSize(in_.getNumChannels(), fft->getSize() * 2); setTimeSeries(in_); } -void AudioBufferFFT::reset(){ +void AudioBufferFFT::reset() { clear(); - readyForConvolution=false; + readyForConvolution = false; } -void AudioBufferFFT::setTimeSeries(const AudioBuffer & in_){ +void AudioBufferFFT::setTimeSeries(const AudioBuffer &in_) { jassert(fft->getSize() >= in_.getNumSamples()); - + clear(); - for (int channelIdx = 0; channelIdx < jmin(getNumChannels(),in_.getNumChannels()); ++channelIdx){ + for (int channelIdx = 0; channelIdx < jmin(getNumChannels(), in_.getNumChannels()); ++channelIdx) { copyFrom(channelIdx, 0, in_, channelIdx, 0, in_.getNumSamples()); } // perform FFT - for (int channelIdx = 0; channelIdx < jmin(getNumChannels(),in_.getNumChannels()); ++channelIdx){ + for (int channelIdx = 0; channelIdx < jmin(getNumChannels(), in_.getNumChannels()); ++channelIdx) { fft->performRealOnlyForwardTransform(getWritePointer(channelIdx)); } - + readyForConvolution = false; } void AudioBufferFFT::updateSymmetricFrequency() { - if (readyForConvolution){ - for (int channelIdx = 0; channelIdx < getNumChannels(); ++channelIdx){ + if (readyForConvolution) { + for (int channelIdx = 0; channelIdx < getNumChannels(); ++channelIdx) { updateSymmetricFrequencyDomainData(getWritePointer(channelIdx), fft->getSize()); } readyForConvolution = false; } } -void AudioBufferFFT::getTimeSeries(AudioBuffer& out){ +void AudioBufferFFT::copyToTimeSeries(AudioBuffer &out) { updateSymmetricFrequency(); - for (int channelIdx = 0; channelIdx < getNumChannels(); ++channelIdx){ - convBuffer.copyFrom(0, 0, *(this), channelIdx, 0, fft->getSize()*2); + for (int channelIdx = 0; channelIdx < getNumChannels(); ++channelIdx) { + convBuffer.copyFrom(0, 0, *(this), channelIdx, 0, fft->getSize() * 2); fft->performRealOnlyInverseTransform(convBuffer.getWritePointer(0)); out.copyFrom(channelIdx, 0, convBuffer, 0, 0, fft->getSize()); } } -void AudioBufferFFT::addTimeSeries(AudioBuffer& out){ +void AudioBufferFFT::addToTimeSeries(AudioBuffer &out) { updateSymmetricFrequency(); - for (int channelIdx = 0; channelIdx < getNumChannels(); ++channelIdx){ - convBuffer.copyFrom(0, 0, *(this), channelIdx, 0, fft->getSize()*2); + for (int channelIdx = 0; channelIdx < getNumChannels(); ++channelIdx) { + convBuffer.copyFrom(0, 0, *(this), channelIdx, 0, fft->getSize() * 2); fft->performRealOnlyInverseTransform(convBuffer.getWritePointer(0)); out.addFrom(channelIdx, 0, convBuffer, 0, 0, fft->getSize()); } } -void AudioBufferFFT::getTimeSeries(int sourceCh, AudioBuffer& dest,int destCh){ +void AudioBufferFFT::copyToTimeSeries(int sourceCh, AudioBuffer &dest, int destCh) { updateSymmetricFrequency(); - convBuffer.copyFrom(0, 0, *(this), sourceCh, 0, fft->getSize()*2); + convBuffer.copyFrom(0, 0, *(this), sourceCh, 0, fft->getSize() * 2); fft->performRealOnlyInverseTransform(convBuffer.getWritePointer(0)); dest.copyFrom(destCh, 0, convBuffer, 0, 0, fft->getSize()); } -void AudioBufferFFT::addTimeSeries(int sourceCh, AudioBuffer& dest,int destCh){ +void AudioBufferFFT::addToTimeSeries(int sourceCh, AudioBuffer &dest, int destCh) { updateSymmetricFrequency(); - convBuffer.copyFrom(0, 0, *(this), sourceCh, 0, fft->getSize()*2); + convBuffer.copyFrom(0, 0, *(this), sourceCh, 0, fft->getSize() * 2); fft->performRealOnlyInverseTransform(convBuffer.getWritePointer(0)); dest.addFrom(destCh, 0, convBuffer, 0, 0, fft->getSize()); } void AudioBufferFFT::prepareForConvolution() { - if (! readyForConvolution){ - for (int channelIdx = 0; channelIdx < getNumChannels(); ++channelIdx){ + if (!readyForConvolution) { + for (int channelIdx = 0; channelIdx < getNumChannels(); ++channelIdx) { prepareForConvolution(getWritePointer(channelIdx), fft->getSize()); } readyForConvolution = true; - }else{ + } else { // prepareForConvolution should not be called if jassertfalse; } } -void AudioBufferFFT::convolve(int outputChannel, const AudioBufferFFT& in_, int inChannel, AudioBufferFFT& filter_, int filterChannel ){ - +void AudioBufferFFT::convolve(int outputChannel, const AudioBufferFFT &in_, int inChannel, AudioBufferFFT &filter_, + int filterChannel) { + jassert(in_.isReadyForConvolution()); jassert(filter_.isReadyForConvolution()); - - clear(outputChannel,0,getNumSamples()); - convolutionProcessingAndAccumulate(in_.getReadPointer(inChannel),filter_.getReadPointer(filterChannel),getWritePointer(outputChannel),fft->getSize()); - - readyForConvolution = true; -} -} // End namespace vFIR + clear(outputChannel, 0, getNumSamples()); + convolutionProcessingAndAccumulate(in_.getReadPointer(inChannel), filter_.getReadPointer(filterChannel), + getWritePointer(outputChannel), fft->getSize()); + readyForConvolution = true; +} diff --git a/Source/AudioBufferFFT.h b/Source/AudioBufferFFT.h new file mode 100644 index 0000000..98e4049 --- /dev/null +++ b/Source/AudioBufferFFT.h @@ -0,0 +1,55 @@ +/* + Audio Buffer in FFT domain + + Authors: + Luca Bondi (luca.bondi@polimi.it) +*/ + +#pragma once + +#include "../JuceLibraryCode/JuceHeader.h" + +class AudioBufferFFT : public AudioBuffer { + +public: + AudioBufferFFT() {}; + + AudioBufferFFT(int numChannels, std::shared_ptr &); + + AudioBufferFFT(const AudioBuffer &, std::shared_ptr &); + + void reset(); + + void setTimeSeries(const AudioBuffer &); + + void copyToTimeSeries(AudioBuffer &); + + void copyToTimeSeries(int sourceCh, AudioBuffer &dest, int destCh); + + void addToTimeSeries(AudioBuffer &); + + void addToTimeSeries(int sourceCh, AudioBuffer &dest, int destCh); + + void + convolve(int outputChannel, const AudioBufferFFT &in_, int inChannel, AudioBufferFFT &filter_, int filterChannel); + + void prepareForConvolution(); + + bool isReadyForConvolution() const { return readyForConvolution; }; + +private: + AudioBuffer convBuffer; + std::shared_ptr fft; + + void prepareForConvolution(float *samples, int fftSize) const; + + void convolutionProcessingAndAccumulate(const float *input, const float *impulse, float *output, int fftSize) const; + + void updateSymmetricFrequencyDomainData(float *samples, int fftSize) const; + + void updateSymmetricFrequency(); + + bool readyForConvolution = false; + +}; + diff --git a/Source/AudioComponents.cpp b/Source/AudioComponents.cpp deleted file mode 100644 index 9522082..0000000 --- a/Source/AudioComponents.cpp +++ /dev/null @@ -1,216 +0,0 @@ -/* - ============================================================================== - - AudioComponents.cpp - Created: 16 Mar 2019 11:44:28am - Author: Luca Bondi - - ============================================================================== - */ - -#include "AudioComponents.h" - - - -void RoundLed::paint(Graphics& g){ - - Rectangle area = getLocalBounds().toFloat(); - auto side = area.getHeight() > area.getWidth() ? area.getWidth() : area.getHeight(); - auto ctr = area.getCentre(); - area = Rectangle(side,side); - area.setCentre(ctr); - - g.setColour(colour); - g.fillEllipse(area); -} - -void RoundLed::resized(){ - -} - - -void MultiChannelLedBar::makeLayout() -{ - removeAllChildren(); - leds.clear(); - if (source == nullptr){ - return; - } - auto num = source->size(); - for (auto ledIdx = 0; ledIdx < num; ++ledIdx) - { - leds.push_back(std::make_unique()); - leds[ledIdx]->colour = Colours::grey; - addAndMakeVisible(leds[ledIdx].get()); - } - resized(); -} - -void MultiChannelLedBar::paint(Graphics&){ - -} - -void MultiChannelLedBar::resized(){ - - if (source == nullptr){ - return; - } - auto num = source->size(); - Rectangle area = getLocalBounds(); - - int step = isHorizontal ? floor(area.getWidth()/num) : floor(area.getHeight()/num); - int otherDim = isHorizontal ? area.getHeight() : area.getWidth(); - otherDim = jmin(otherDim,step-1); - - const auto areaCtr = area.getCentre(); - - // Re-center the area - if (isHorizontal){ - area.setWidth((int)(step*num)); - area.setHeight(otherDim); - }else{ - area.setHeight((int)(step*num)); - area.setWidth(otherDim); - } - area.setCentre(areaCtr); - - for (auto ledIdx = 0; ledIdx < num; ++ledIdx){ - Rectangle ledArea = isHorizontal ? area.removeFromLeft(step) : area.removeFromTop(step); - leds[ledIdx]->setBounds(ledArea); - } - -} - -void MultiChannelLedBar::timerCallback() -{ - - if (source == nullptr) - return; - - lock->enter(); - std::vector values = *source; - lock->exit(); - - if (values.size() != leds.size()) - { - makeLayout(); - } - - for (auto ledIdx = 0; ledIdx < leds.size(); ++ledIdx) - { - auto value = values.at(ledIdx); - Colour col; - if (value > Decibels::decibelsToGain(RED_LT)) - { - col = Colours::red; - }else if(value > Decibels::decibelsToGain(YELLOW_LT)) - { - col = Colours::yellow; - } - else if (value > Decibels::decibelsToGain(GREEN_LT)) - { - col = Colours::lightgreen; - } - else - { - col = Colours::grey; - } - leds[ledIdx]->colour = col; - } - - repaint(); -} - -void MultiChannelLedBar::setSource(const std::vector &newSource,SpinLock &newLock) -{ - source = &newSource; - lock = &newLock; - if (source->size() > 0){ - makeLayout(); - } -} - - -SingleChannelLedBar::SingleChannelLedBar(size_t numLeds, bool isHorizontal){ - jassert(numLeds > 4); - - this->isHorizontal = isHorizontal; - - const float ledStep = 3; //dB - - leds.clear(); - th.clear(); - for (auto ledIdx = 0; ledIdx < numLeds; ++ledIdx) - { - leds.push_back(std::make_unique()); - - auto ledThDb = ledIdx == (numLeds-1) ? RED_LT : -((numLeds - 1 - ledIdx) *ledStep); - th.push_back(ledThDb); - leds[ledIdx]->colour = thToColour(ledThDb,false); - - addAndMakeVisible(leds[ledIdx].get()); - } -} - -void SingleChannelLedBar::setSource(const std::vector &newSource,int newCh, SpinLock &newLock){ - source = &newSource; - ch = newCh; - lock = &newLock; -} - -void SingleChannelLedBar::paint(Graphics&){ - -} - -void SingleChannelLedBar::resized(){ - - Rectangle area = getLocalBounds().toFloat(); - auto num = leds.size(); - float step = isHorizontal ? area.getWidth()/num : area.getHeight()/num; - for (auto ledIdx = 0; ledIdx < num; ++ledIdx) - { - Rectangle ledArea = isHorizontal ? area.removeFromLeft(step) : area.removeFromBottom(step); - leds[ledIdx]->setBounds(ledArea.toNearestInt()); - } - -} - -void SingleChannelLedBar::timerCallback() -{ - lock->enter(); - auto valueDb = Decibels::gainToDecibels(source->at(ch)); - lock->exit(); - for (auto ledIdx = 0; ledIdx < leds.size(); ++ledIdx) - leds[ledIdx]->colour = thToColour(th[ledIdx], valueDb > th[ledIdx]); - - repaint(); -} - -Colour SingleChannelLedBar::thToColour(float th, bool active) -{ - if (th >= RED_LT) - { - if (active) - return Colours::red; - else - return Colours::darkred; - } - else if (th >= YELLOW_LT) - { - if (active) - return Colours::yellow; - else - return Colours::darkgoldenrod; - } - else - { - if (active) - return Colours::lightgreen; - else - return Colours::darkgreen; - } -} - -MuteButton::MuteButton(){ - setClickingTogglesState (true); -} diff --git a/Source/AudioComponents.h b/Source/AudioComponents.h deleted file mode 100644 index 06ab068..0000000 --- a/Source/AudioComponents.h +++ /dev/null @@ -1,206 +0,0 @@ -/* - ============================================================================== - - AudioComponents.h - Created: 16 Mar 2019 11:44:28am - Author: Luca Bondi - - ============================================================================== -*/ - -#pragma once -#define DB_MINUS_INF -100.0 - -#define RED_LT -0.5f //dB -#define YELLOW_LT -6.0f //dB -#define GREEN_LT -20.0f //dB - -#include "../JuceLibraryCode/JuceHeader.h" -#include "MidiComp.h" - -//============================================================================== -class DecibelSlider : public SliderCC -{ -public: - - DecibelSlider(){}; - - double getValueFromText (const String& text) override - { - - auto decibelText = text.upToFirstOccurrenceOf ("dB", false, false).trim(); - - return decibelText.equalsIgnoreCase ("-INF") ? DB_MINUS_INF - : decibelText.getDoubleValue(); // [2] - } - - String getTextFromValue (double value) override - { - return Decibels::toString (value,1,DB_MINUS_INF); - } - -private: - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DecibelSlider) -}; - -//============================================================================== - -class FrequencySlider : public SliderCC -{ -public: - - FrequencySlider(){}; - - double getValueFromText (const String& text) override - { - - auto hzText = text.upToFirstOccurrenceOf ("Hz", false, false).trim(); - auto hzVal = roundToInt(hzText.getDoubleValue()); - return hzVal; - } - - String getTextFromValue (double value) override - { - std::ostringstream valueString; - valueString << roundToInt(value) << " Hz"; - return valueString.str(); - } - -private: - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FrequencySlider) -}; - -//============================================================================== - -class PanSlider : public SliderCC -{ -public: - - PanSlider(){}; - - double getValueFromText (const String& text) override - { - - if (text.startsWithIgnoreCase("C") || text.startsWithIgnoreCase("0")) - { - return 0; - } - - float sign = 1; - if (text.endsWithIgnoreCase("L")){ - sign = -1; - } - - String valueText = text.upToFirstOccurrenceOf ("%", false, false).trim(); - double val = valueText.getDoubleValue()/100.; - return sign*val; - } - - String getTextFromValue (double value) override - { - jassert(value >= -1 && value <= 1); - std::ostringstream valueString; - if (value == 0){ - valueString << "C"; - } - else if (value < 0) - { - valueString << roundToInt(abs(value)*100) << " % L"; - } - else - { - valueString << roundToInt((value)*100) << " % R"; - } - - return valueString.str(); - } - -private: - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PanSlider) -}; - -//============================================================================== - -class RoundLed : public Component -{ -public: - - RoundLed(){}; - - Colour colour; - - void paint(Graphics&) override; - void resized() override; - -private: - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RoundLed) -}; - -//============================================================================== -class MultiChannelLedBar : public Component, public Timer -{ -public: - - MultiChannelLedBar(){}; - ~MultiChannelLedBar(){}; - void paint(Graphics&) override; - void resized() override; - - void setSource(const std::vector &source,SpinLock &lock); - void setHorizontal(int numRows=1){isHorizontal = true;}; - void setVertical(int numCols=1){isHorizontal = false;}; - - -private: - - bool isHorizontal = true; - const std::vector *source = nullptr; - SpinLock *lock = nullptr; - std::vector> leds; - - void timerCallback() override; - - void makeLayout(); - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MultiChannelLedBar) -}; - -//============================================================================== -class SingleChannelLedBar : public Component, public Timer -{ -public: - - SingleChannelLedBar(size_t numLeds = 7, bool isHorizontal = false); - ~SingleChannelLedBar(){}; - - void setSource(const std::vector &source,int ch, SpinLock &lock); - void paint(Graphics&) override; - void resized() override; - - static Colour thToColour(float th, bool active); - -private: - - bool isHorizontal; - const std::vector *source = nullptr; - int ch = 0; - SpinLock *lock = nullptr; - - std::vector th; - std::vector> leds; - - - - void timerCallback() override; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SingleChannelLedBar) -}; - -//============================================================================== -class MuteButton: public TextButtonCC { -public: - - MuteButton(); - -}; diff --git a/Source/AudioParts.cpp b/Source/AudioParts.cpp deleted file mode 100644 index 67f4846..0000000 --- a/Source/AudioParts.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/* - ============================================================================== - - AudioParts.cpp - Created: 22 Mar 2019 8:48:06am - Author: Luca Bondi - - ============================================================================== -*/ - -#include "AudioParts.h" - - -MeterDecay::MeterDecay(float fs, float duration, float blockSize, int numChannels) -{ - - int numBlocks = (int)ceil(duration*fs/blockSize); - - minMaxCircularBuffer.resize(numChannels); - idxs.resize(numChannels); - for (auto channelIdx = 0; channelIdx < numChannels; ++channelIdx) - { - idxs[channelIdx] = 0; - minMaxCircularBuffer[channelIdx].resize(numBlocks); - } - -} - -void MeterDecay::push(const AudioBuffer& signal) -{ - for (auto channelIdx = 0; channelIdx < signal.getNumChannels(); ++channelIdx) - { - Range minMax = FloatVectorOperations::findMinAndMax(signal.getReadPointer(channelIdx),signal.getNumSamples()); - float maxAbs = jmax(abs(minMax.getStart()),abs(minMax.getEnd())); - minMaxCircularBuffer[channelIdx][idxs[channelIdx]++] = maxAbs; - if (idxs[channelIdx] >= minMaxCircularBuffer[channelIdx].size()){ - idxs[channelIdx] = 0; - } - } -} - -std::vector MeterDecay::get() -{ - std::vector values(minMaxCircularBuffer.size()); - for (auto channelIdx = 0; channelIdx < minMaxCircularBuffer.size(); ++channelIdx) - { - float maxVal = 0; - for (auto idx = 0; idx < minMaxCircularBuffer[channelIdx].size(); ++idx) - { - maxVal = jmax(maxVal,minMaxCircularBuffer[channelIdx][idx]); - } - values[channelIdx] = maxVal; - } - return values; -} - -float panToLinearGain(float gain, bool isLeftChannel) { - const float db_at0 = -4.5; //How many dB at each channel when pan is centered (0) - jassert(gain >= -1); - jassert(gain <= 1); - float alpha = std::pow(10.,(db_at0/20.)); - if (isLeftChannel){ - gain = -gain; - } - float y = (0.5-alpha)*std::pow(gain,2.)+0.5*gain+alpha; - return y; -} diff --git a/Source/AudioParts.h b/Source/AudioParts.h deleted file mode 100644 index 7063708..0000000 --- a/Source/AudioParts.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - ============================================================================== - - AudioParts.h - Created: 22 Mar 2019 8:48:06am - Author: Luca Bondi - - ============================================================================== -*/ - -#pragma once -#include "../JuceLibraryCode/JuceHeader.h" - -class MeterDecay -{ - -public: - - MeterDecay(float fs, float duration, float blockSize, int numChannels); - void push(const AudioBuffer& signal); - std::vector get(); - -private: - - std::vector idxs; - std::vector> minMaxCircularBuffer; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MeterDecay) -}; - -float panToLinearGain(float gain, bool isLeftChannel) ; diff --git a/Source/Beamformer.cpp b/Source/Beamformer.cpp index 9d029a2..44e64d6 100644 --- a/Source/Beamformer.cpp +++ b/Source/Beamformer.cpp @@ -1,104 +1,102 @@ /* - ============================================================================== + Beamforming processing class - Beamformer.cpp - Created: 14 Apr 2020 9:12:12am - Author: Luca Bondi - - ============================================================================== - */ + Authors: + Luca Bondi (luca.bondi@polimi.it) +*/ #include "Beamformer.h" -BeamformerDoa::BeamformerDoa(Beamformer& b, - int numDoas_, - float sampleRate_, - int numActiveInputChannels, - int firLen, - std::shared_ptr fft_):beamformer(b){ - +BeamformerDoa::BeamformerDoa(Beamformer &b, + int numDoas_, + float sampleRate_, + int numActiveInputChannels, + int firLen, + std::shared_ptr fft_) : beamformer(b) { + numDoas = numDoas_; fft = fft_; sampleRate = sampleRate_; - + /** Initialize levels and FIR */ - doaLevels.resize(numDoas,-100); + doaLevels.resize(numDoas, -100); doaFirFFT.resize(numDoas); - + /** Allocate inputBuffer */ - inputBuffer = FIR::AudioBufferFFT(numActiveInputChannels,fft); - + inputBuffer = AudioBufferFFT(numActiveInputChannels, fft); + /** Allocate convolution buffer */ - convolutionBuffer = FIR::AudioBufferFFT(1, fft); - + convolutionBuffer = AudioBufferFFT(1, fft); + /** Allocate DOA beam */ doaBeam.setSize(1, fft->getSize()); - + /** Compute FIR for DOA estimation */ - AudioBuffer tmpFir(numActiveInputChannels,firLen); - BeamParameters tmpBeamParams{0,0}; - for (auto dirIdx = 0;dirIdx < numDoas;dirIdx++){ - tmpBeamParams.doa = -1+(2./(numDoas-1)*dirIdx); - b.getFir(tmpFir, tmpBeamParams,1); - doaFirFFT[dirIdx] = FIR::AudioBufferFFT(numActiveInputChannels,fft); + AudioBuffer tmpFir(numActiveInputChannels, firLen); + BeamParameters tmpBeamParams{0, 0}; + for (auto dirIdx = 0; dirIdx < numDoas; dirIdx++) { + tmpBeamParams.doa = -1 + (2. / (numDoas - 1) * dirIdx); + b.getFir(tmpFir, tmpBeamParams, 1); + doaFirFFT[dirIdx] = AudioBufferFFT(numActiveInputChannels, fft); doaFirFFT[dirIdx].setTimeSeries(tmpFir); doaFirFFT[dirIdx].prepareForConvolution(); } } -BeamformerDoa::~BeamformerDoa(){ +BeamformerDoa::~BeamformerDoa() { } -void BeamformerDoa::timerCallback(){ - +void BeamformerDoa::timerCallback() { + beamformer.getDoaInputBuffer(inputBuffer); - + /** Compute DOA levels */ - for (auto dirIdx = 0; dirIdx < numDoas; dirIdx++){ + for (auto dirIdx = 0; dirIdx < numDoas; dirIdx++) { doaBeam.clear(); - for (auto inCh=0;inCh minMax = FloatVectorOperations::findMinAndMax(doaBeam.getReadPointer(0), doaBeam.getNumSamples()); - const float dirEnergy = jmax(abs(minMax.getStart()),abs(minMax.getEnd())); + convolutionBuffer.addToTimeSeries(0, doaBeam, 0); + } + + const Range minMax = FloatVectorOperations::findMinAndMax(doaBeam.getReadPointer(0), + doaBeam.getNumSamples()); + const float dirEnergy = jmax(abs(minMax.getStart()), abs(minMax.getEnd())); const float dirEnergyDb = Decibels::gainToDecibels(dirEnergy); doaLevels[dirIdx] = dirEnergyDb; } - + beamformer.setDoaEnergy(doaLevels); } // ============================================================================== -Beamformer::Beamformer(int numBeams_, int numDoas_){ - +Beamformer::Beamformer(int numBeams_, int numDoas_) { + numBeams = numBeams_; numDoas = numDoas_; - + firIR.resize(numBeams); firFFT.resize(numBeams); } -Beamformer::~Beamformer(){ - +Beamformer::~Beamformer() { + } -MicConfig Beamformer::getMicConfig() const{ +MicConfig Beamformer::getMicConfig() const { return micConfig; } -void Beamformer::setMicConfig(MicConfig micConfig_){ - micConfig=micConfig_; +void Beamformer::setMicConfig(MicConfig micConfig_) { + micConfig = micConfig_; } -void Beamformer::initAlg(){ - +void Beamformer::initAlg() { + /** Determine configuration parameters */ - switch (micConfig){ + switch (micConfig) { case LMA_1ESTICK: numMic = 16; break; @@ -114,146 +112,147 @@ void Beamformer::initAlg(){ } /** Distance between microphones in eSticks*/ const float micDist = 0.03; - alg = std::make_unique(micDist,numMic,sampleRate,soundspeed); - + alg = std::make_unique(micDist, numMic, sampleRate, soundspeed); + firLen = alg->getFirLen(); - + /** Create shared FFT object */ - fft = std::make_shared(ceil(log2(firLen+maximumExpectedSamplesPerBlock-1))); - + fft = std::make_shared(ceil(log2(firLen + maximumExpectedSamplesPerBlock - 1))); + /** Allocate FIR filters */ - for (auto &f : firIR){ - f = AudioBuffer(numMic,firLen); + for (auto &f : firIR) { + f = AudioBuffer(numMic, firLen); f.clear(); } - for (auto &f : firFFT){ - f = FIR::AudioBufferFFT(numMic,fft); + for (auto &f : firFFT) { + f = AudioBufferFFT(numMic, fft); f.clear(); } /** Allocate input buffers */ - inputBuffer = FIR::AudioBufferFFT(numMic, fft); - + inputBuffer = AudioBufferFFT(numMic, fft); + /** Allocate convolution buffer */ - convolutionBuffer = FIR::AudioBufferFFT(1, fft); - + convolutionBuffer = AudioBufferFFT(1, fft); + /** Allocate beam output buffer */ - beamBuffer.setSize(numBeams, convolutionBuffer.getNumSamples()/2); + beamBuffer.setSize(numBeams, convolutionBuffer.getNumSamples() / 2); beamBuffer.clear(); - + /** Allocate DOA input buffer */ doaInputBuffer.setSize(numMic, maximumExpectedSamplesPerBlock); doaInputBuffer.clear(); - + /** Set DOA input Filter */ doaBPFilters.clear(); doaBPFilters.resize(numMic); - IIRCoefficients doaIIRCoeff = IIRCoefficients::makeBandPass(sampleRate,doaBPfreq, doaBPQ); - for (auto &f : doaBPFilters){ + IIRCoefficients doaIIRCoeff = IIRCoefficients::makeBandPass(sampleRate, doaBPfreq, doaBPQ); + for (auto &f : doaBPFilters) { f.setCoefficients(doaIIRCoeff); } - + /** Prepare and start DOA thread */ - doaThread = std::make_unique(*this,numDoas,sampleRate,numMic,firLen,fft); + doaThread = std::make_unique(*this, numDoas, sampleRate, numMic, firLen, fft); doaThread->startTimerHz(doaUpdateFrequency); } -void Beamformer::prepareToPlay(double sampleRate_, int maximumExpectedSamplesPerBlock_){ - +void Beamformer::prepareToPlay(double sampleRate_, int maximumExpectedSamplesPerBlock_) { + sampleRate = sampleRate_; maximumExpectedSamplesPerBlock = maximumExpectedSamplesPerBlock_; - + /** Alpha for FIR update */ - alpha = 1-exp(-(maximumExpectedSamplesPerBlock/sampleRate)/firUpdateTimeConst); - + alpha = 1 - exp(-(maximumExpectedSamplesPerBlock / sampleRate) / firUpdateTimeConst); + initAlg(); } -void Beamformer::setBeamParameters(int beamIdx, const BeamParameters& beamParams){ - alg->getFir(firIR[beamIdx], beamParams,alpha); +void Beamformer::setBeamParameters(int beamIdx, const BeamParameters &beamParams) { + alg->getFir(firIR[beamIdx], beamParams, alpha); firFFT[beamIdx].setTimeSeries(firIR[beamIdx]); firFFT[beamIdx].prepareForConvolution(); } -void Beamformer::processBlock(const AudioBuffer &inBuffer){ - +void Beamformer::processBlock(const AudioBuffer &inBuffer) { + { GenericScopedLock lock(doaInputBufferLock); - for (auto chIdx = 0;chIdx &fir,const BeamParameters& params,float alpha) const{ +void Beamformer::getFir(AudioBuffer &fir, const BeamParameters ¶ms, float alpha) const { alg->getFir(fir, params, alpha); } -void Beamformer::getDoaInputBuffer(FIR::AudioBufferFFT& dst) const{ +void Beamformer::getDoaInputBuffer(AudioBufferFFT &dst) const { GenericScopedLock lock(doaInputBufferLock); dst.setTimeSeries(doaInputBuffer); dst.prepareForConvolution(); } -void Beamformer::getBeams(AudioBuffer& outBuffer){ +void Beamformer::getBeams(AudioBuffer &outBuffer) { jassert(outBuffer.getNumChannels() == numBeams); auto numSplsOut = outBuffer.getNumSamples(); auto numSplsShift = beamBuffer.getNumSamples() - numSplsOut; - AudioBuffer tmp(1,numSplsShift); - for (auto beamIdx = 0; beamIdx < numBeams; beamIdx++){ + AudioBuffer tmp(1, numSplsShift); + for (auto beamIdx = 0; beamIdx < numBeams; beamIdx++) { /** Copy beamBuffer to outBuffer */ outBuffer.copyFrom(beamIdx, 0, beamBuffer, beamIdx, 0, numSplsOut); /** Shift beamBuffer */ - FloatVectorOperations::copy(beamBuffer.getWritePointer(beamIdx), beamBuffer.getReadPointer(beamIdx)+numSplsOut, numSplsShift); - beamBuffer.clear(beamIdx, numSplsShift, beamBuffer.getNumSamples()-numSplsShift); + FloatVectorOperations::copy(beamBuffer.getWritePointer(beamIdx), + beamBuffer.getReadPointer(beamIdx) + numSplsOut, numSplsShift); + beamBuffer.clear(beamIdx, numSplsShift, beamBuffer.getNumSamples() - numSplsShift); } } -void Beamformer::setDoaEnergy(const std::vector& energy){ +void Beamformer::setDoaEnergy(const std::vector &energy) { GenericScopedLock lock(doaLock); doaLevels = energy; } -void Beamformer::getDoaEnergy(std::vector& outDoaLevels) const{ +void Beamformer::getDoaEnergy(std::vector &outDoaLevels) const { GenericScopedLock lock(doaLock); outDoaLevels = doaLevels; } -void Beamformer::releaseResources(){ - +void Beamformer::releaseResources() { + /** Release FIR filters */ - for (auto &f : firIR){ + for (auto &f : firIR) { f.setSize(0, 0); } - for (auto &f : firFFT){ + for (auto &f : firFFT) { f.setSize(0, 0); } /** Release input buffer */ inputBuffer.setSize(0, 0); - + /** Release convolution buffer */ convolutionBuffer.setSize(0, 0); - + /** Release beam buffer */ beamBuffer.setSize(0, 0); - + /** Stop DOA thread */ doaThread.reset(); - + } diff --git a/Source/Beamformer.h b/Source/Beamformer.h index 71359fa..ba784e0 100644 --- a/Source/Beamformer.h +++ b/Source/Beamformer.h @@ -1,108 +1,94 @@ /* - ============================================================================== + Beamforming processing class - Beamformer.h - Created: 14 Apr 2020 9:12:12am - Author: Luca Bondi - - ============================================================================== - */ + Authors: + Luca Bondi (luca.bondi@polimi.it) +*/ #pragma once #include "../JuceLibraryCode/JuceHeader.h" -#include "BeamformingAlgorithm.h" -#include "FIR.h" -#include "DAS.h" - -/** Available configurations */ -typedef enum { - LMA_1ESTICK, - LMA_2ESTICK, - LMA_3ESTICK, - LMA_4ESTICK, -} MicConfig; - -const StringArray micConfigLabels({ - "Single", - "Hor 2", - "Hor 3", - "Hor 4", -}); +#include "ebeamerDefs.h" +#include "AudioBufferFFT.h" +#include "BeamformingAlgorithms.h" + + // ============================================================================== -/** Pre-declare class */ class Beamformer; -/** Separate thread used to compute DOA */ -class BeamformerDoa: public Timer{ +/** Class that computes periodically the Direction of Arrival of sound + */ +class BeamformerDoa : public Timer { public: - BeamformerDoa(Beamformer& b, + BeamformerDoa(Beamformer &b, int numDoas_, float sampleRate_, int numActiveInputChannels, int firLen, std::shared_ptr fft_); + ~BeamformerDoa(); - + void timerCallback() override; private: - + /** Reference to the Beamformer */ - Beamformer& beamformer; - + Beamformer &beamformer; + /** Number of directions of arrival */ int numDoas; - + /** Sampling frequency [Hz] */ float sampleRate; - + /** FFT */ std::shared_ptr fft; - + /** Inputs' buffer */ - FIR::AudioBufferFFT inputBuffer; - + AudioBufferFFT inputBuffer; + /** Convolution buffer */ - FIR::AudioBufferFFT convolutionBuffer; - + AudioBufferFFT convolutionBuffer; + /** FIR filters for DOA estimation */ - std::vector doaFirFFT; - + std::vector doaFirFFT; + /** DOA beam */ AudioBuffer doaBeam; - + /** DOA levels [dB] */ std::vector doaLevels; - + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BeamformerDoa); - + }; // ============================================================================== -class Beamformer{ - +class Beamformer { + public: - - + + /** Initialize the Beamformer with a set of static parameters. @param numBeams: number of beams the beamformer has to compute @param numDoas: number of directions of arrival to compute the energy */ Beamformer(int numBeams, int numDoas); + /** Destructor. */ ~Beamformer(); - + /** Set microphone configuration */ void setMicConfig(MicConfig micConfig_); - + /** Get microphone configuration */ MicConfig getMicConfig() const; - + /** Set the parameters before execution. To be called inside AudioProcessor::prepareToPlay. @@ -110,122 +96,122 @@ class Beamformer{ on sample rate, buffer size and channel configurations. */ void prepareToPlay(double sampleRate_, int maximumExpectedSamplesPerBlock_); - + /** Process a new block of samples. To be called inside AudioProcessor::processBlock. */ - void processBlock(const AudioBuffer& inBuffer); - + void processBlock(const AudioBuffer &inBuffer); + /** Copy the current beams outputs to the provided output buffer To be called inside AudioProcessor::processBlock, after Beamformer::processBlock */ - void getBeams(AudioBuffer& outBuffer); - + void getBeams(AudioBuffer &outBuffer); + /** Set the parameters for a specific beam */ - void setBeamParameters(int beamIdx, const BeamParameters& beamParams); - + void setBeamParameters(int beamIdx, const BeamParameters &beamParams); + /** Get FIR in time domain for a given direction of arrival @param fir: an AudioBuffer object with numChannels >= number of microphones and numSamples >= firLen @param params: beam parameters @param alpha: exponential interpolation coefficient. 1 means complete override (instant update), 0 means no override (complete preservation) */ - void getFir(AudioBuffer&fir,const BeamParameters& params,float alpha=1) const; - + void getFir(AudioBuffer &fir, const BeamParameters ¶ms, float alpha = 1) const; + /** Copy the estimated energy contribution from the directions of arrival */ - void getDoaEnergy(std::vector& energy) const; - + void getDoaEnergy(std::vector &energy) const; + /** Set the estimated energy contribution from the directions of arrival */ - void setDoaEnergy(const std::vector& energy); - + void setDoaEnergy(const std::vector &energy); + /** Get last doa filtered input buffer */ - void getDoaInputBuffer(FIR::AudioBufferFFT& dst) const; - + void getDoaInputBuffer(AudioBufferFFT &dst) const; + /** Release not needed resources. To be called inside AudioProcessor::releaseResources. */ void releaseResources(); - + private: - + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Beamformer); - + /** Sound speed [m/s] */ const float soundspeed = 343; - + /** Sample rate [Hz] */ float sampleRate = 48000; - + /** Maximum buffer size [samples] */ int maximumExpectedSamplesPerBlock = 64; - + /** Number of microphones */ int numMic = 16; - + /** Number of beams */ int numBeams; - + /** Number of directions of arrival */ int numDoas; - + /** Beamforming algorithm */ std::unique_ptr alg; /** FIR filters length. Diepends on the algorithm */ int firLen; - + /** Shared FFT pointer */ std::shared_ptr fft; - + /** FIR filters for each beam */ std::vector> firIR; - std::vector firFFT; - + std::vector firFFT; + /** Inputs' buffer */ - FIR::AudioBufferFFT inputBuffer; - + AudioBufferFFT inputBuffer; + /** Convolution buffer */ - FIR::AudioBufferFFT convolutionBuffer; - + AudioBufferFFT convolutionBuffer; + /** Beams' outputs buffer */ AudioBuffer beamBuffer; - + /** FIR coefficients update time constant [s] */ const float firUpdateTimeConst = 0.2; /** FIR coefficients update alpha */ float alpha = 1; - + /** Microphones configuration */ MicConfig micConfig = LMA_1ESTICK; - + /** Initialize the beamforming algorithm */ void initAlg(); - + /** DOA thread */ std::unique_ptr doaThread; - + /**DOA update requency [Hz] */ const float doaUpdateFrequency = 10; - + /** DOA levels [dB] */ std::vector doaLevels; - + /** DOA Band pass Filters */ std::vector doaBPFilters; const float doaBPfreq = 2000; const float doaBPQ = 1; - + /** inputBuffer lock */ SpinLock doaInputBufferLock; - + /** Input buffer with DOA-filtered input signal */ AudioBuffer doaInputBuffer; - + /** DOA Lock */ SpinLock doaLock; - - + + }; diff --git a/Source/BeamformingAlgorithm.h b/Source/BeamformingAlgorithm.h deleted file mode 100644 index af891f1..0000000 --- a/Source/BeamformingAlgorithm.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - ============================================================================== - - BeamformingAlgorithm.h - Created: 15 Apr 2020 5:48:24pm - Author: Luca Bondi - - ============================================================================== -*/ - -#pragma once - -/** Beam parameters data structure for a linear 1D array */ -typedef struct{ - /** DIrection of arrival of the beam. - Range: -1 (source closer to first microphone) to +1 (source closer to last microphone) - */ - float doa; - /** Width of the beam. - Range: 0 (the most focused) to 1 (the least focused) - */ - float width; -} BeamParameters; - - - -/** Virtual class extended by all beamforming algorithms */ -class BeamformingAlgorithm{ - -public: - - virtual ~BeamformingAlgorithm(){}; - - /** Get the minimum FIR length for the given configuration [samples] */ - virtual int getFirLen() const = 0; - - /** Get FIR in time domain for a given direction of arrival - - @param fir: an AudioBuffer object with numChannels >= number of microphones and numSamples >= firLen - @param params: beam parameters - @param alpha: exponential interpolation coefficient. 1 means complete override (instant update), 0 means no override (complete preservation) - */ - virtual void getFir(AudioBuffer&fir,const BeamParameters& params,float alpha=1) const = 0; - -}; diff --git a/Source/BeamformingAlgorithms.cpp b/Source/BeamformingAlgorithms.cpp new file mode 100644 index 0000000..03789cd --- /dev/null +++ b/Source/BeamformingAlgorithms.cpp @@ -0,0 +1,73 @@ +/* + Beamforming algorithms + + Authors: + Luca Bondi (luca.bondi@polimi.it) +*/ + +#include "BeamformingAlgorithms.h" + +namespace DAS { + + FarfieldLMA::FarfieldLMA(float micDist_, int numMic_, float fs_, float soundspeed_) { + + micDist = micDist_; + numMic = numMic_; + fs = fs_; + soundspeed = soundspeed_; + + commonDelay = 64; + firLen = ceil(numMic * micDist / soundspeed * fs) + 2 * commonDelay; + + fft = std::make_unique(ceil(log2(firLen))); + + win.resize(fft->getSize()); + designTukeyWindow(win, fft->getSize(), commonDelay / 2); + + freqAxes = Vec::LinSpaced(fft->getSize(), 0, fs * (fft->getSize() - 1) / fft->getSize()); + + } + + int FarfieldLMA::getFirLen() const { + return firLen; + } + + void FarfieldLMA::getFir(AudioBuffer &fir, const BeamParameters ¶ms, float alpha) const { + + /** Angle in radians (0 front, pi/2 source closer to last channel, -pi/2 source closer to first channel */ + const float angleRad = (params.doa + 1) * pi / 2; + /** Delay between adjacent microphones [s] */ + const float delta = -cos(angleRad) * micDist / soundspeed; + /** Compute delays for each microphone [s] */ + Vec micDelays = delta * Vec::LinSpaced(numMic, 0, numMic - 1); + /** Compensate for minimum delay and apply common delay */ + micDelays.array() += -micDelays.minCoeff() + commonDelay / fs; + /** Compute the fractional delays in frequency domain */ + CpxMtx irFFT = (-j2pi * freqAxes * micDelays.transpose()).array().exp(); + + + /** Compute how many microphones are muted at each end */ + const int inactiveMicAtBorder = roundToInt((numMic / 2 - 1) * params.width); + /** Generate the mask of active microphones */ + Vec micGains = Vec::Ones(numMic); + micGains.head(inactiveMicAtBorder).array() = 0; + micGains.tail(inactiveMicAtBorder).array() = 0; + /** Normalize the power */ + micGains.array() *= referencePower / micGains.sum(); + + /** Apply the gain */ + irFFT = irFFT.cwiseProduct(micGains.transpose().replicate(freqAxes.size(), 1)); + + /** Convert from requency to time domain and add to destination*/ + for (auto micIdx = 0; micIdx < jmin(numMic, fir.getNumChannels()); micIdx++) { + freqToTime(fir, micIdx, irFFT.col(micIdx), fft.get(), win, alpha); + } + /** Clear the remaining FIR, if any */ + for (auto micIdx = jmin(numMic, fir.getNumChannels()); micIdx < fir.getNumChannels(); micIdx++) { + fir.clear(micIdx, 0, fir.getNumSamples()); + } + + } + + +} diff --git a/Source/BeamformingAlgorithms.h b/Source/BeamformingAlgorithms.h new file mode 100644 index 0000000..e272ba3 --- /dev/null +++ b/Source/BeamformingAlgorithms.h @@ -0,0 +1,112 @@ +/* + Beamforming algorithms + + Authors: + Luca Bondi (luca.bondi@polimi.it) +*/ + +#pragma once + +#include "../JuceLibraryCode/JuceHeader.h" +#include "SignalProcessing.h" +#include "BeamformingAlgorithms.h" + +/** Beam parameters data structure for a linear 1D array */ +typedef struct { + /** DIrection of arrival of the beam. + Range: -1 (source closer to first microphone) to +1 (source closer to last microphone) + */ + float doa; + /** Width of the beam. + Range: 0 (the most focused) to 1 (the least focused) + */ + float width; +} BeamParameters; + + +/** Virtual class extended by all beamforming algorithms */ +class BeamformingAlgorithm { + +public: + + virtual ~BeamformingAlgorithm() {}; + + /** Get the minimum FIR length for the given configuration [samples] */ + virtual int getFirLen() const = 0; + + /** Get FIR in time domain for a given direction of arrival + + @param fir: an AudioBuffer object with numChannels >= number of microphones and numSamples >= firLen + @param params: beam parameters + @param alpha: exponential interpolation coefficient. 1 means complete override (instant update), 0 means no override (complete preservation) + */ + virtual void getFir(AudioBuffer &fir, const BeamParameters ¶ms, float alpha = 1) const = 0; + +}; + + +namespace DAS { + +/** Farfield Linear Microphone Array Delay and Sum beamformer + + This class is used do setup a LMA and compute the FIR impulse response + */ + class FarfieldLMA : public BeamformingAlgorithm { + + public: + + /** initialize the LMA + + @param micDist: microphones distance [m] + @param numMic: number of microphone capsules + @param fs: sampling frequency [Hz] + @param soundspeed: sampling frequency [m/s] + */ + FarfieldLMA(float micDist, int numMic, float fs, float soundspeed); + + /** Get the minimum FIR length for the given configuration [samples] */ + int getFirLen() const override; + + /** Get FIR in time domain for a given direction of arrival + + @param fir: an AudioBuffer object with numChannels >= number of microphones and numSamples >= firLen + @param params: beam parameters + @param alpha: exponential interpolation coefficient. 1 means complete override (instant update), 0 means no override (complete preservation) + */ + void getFir(AudioBuffer &fir, const BeamParameters ¶ms, float alpha = 1) const override; + + private: + + /** Distance between microphones [m] */ + float micDist; + + /** Number of microphones */ + int numMic; + + /** Sampling frequency [Hz] */ + float fs; + + /** Soundspeed [m/s] */ + float soundspeed; + + /** Common delay applied to all the filters to make filters causal [samples] */ + int commonDelay; + + /** Length of FIR filters [samples] */ + int firLen; + + /** FFT object */ + std::unique_ptr fft; + + /** Window applied to the FIR filters in time domain */ + Vec win; + + /** Frequencies axes */ + Vec freqAxes; + + /** Reference power for normalization */ + const float referencePower = 3; + + }; + +} diff --git a/Source/CpuLoadComp.cpp b/Source/CpuLoadComp.cpp index a20a67c..f31ec28 100644 --- a/Source/CpuLoadComp.cpp +++ b/Source/CpuLoadComp.cpp @@ -1,22 +1,18 @@ /* - ============================================================================== - - CpuLoadComp.cpp - Created: 18 Apr 2020 4:23:20pm - Author: Luca Bondi - - ============================================================================== + CPU load component + + Authors: + Luca Bondi (luca.bondi@polimi.it) */ #include #include "CpuLoadComp.h" //============================================================================== -CpuLoadComp::CpuLoadComp(const EbeamerAudioProcessor &p):processor(p) -{ +CpuLoadComp::CpuLoadComp() { text.setFont(textHeight); - text.setText(String(int(processor.getAverageLoad()*100))+"%"); + text.setText("0 %"); text.setReadOnly(true); label.setFont(textHeight); label.setText("CPU load", NotificationType::dontSendNotification); @@ -25,21 +21,24 @@ CpuLoadComp::CpuLoadComp(const EbeamerAudioProcessor &p):processor(p) addAndMakeVisible(text); } -CpuLoadComp::~CpuLoadComp() -{ +CpuLoadComp::~CpuLoadComp() { +} + +void CpuLoadComp::paint(Graphics &g) { } -void CpuLoadComp::paint (Graphics& g) -{ +void CpuLoadComp::setSource(Callback *cb) { + callback = cb; } -void CpuLoadComp::resized() -{ +void CpuLoadComp::resized() { auto area = getLocalBounds(); area.removeFromLeft(labelWidth); text.setBounds(area); } -void CpuLoadComp::timerCallback(){ - text.setText(String(int(processor.getAverageLoad()*100))+"%"); +void CpuLoadComp::timerCallback() { + if (callback == nullptr) + return; + text.setText(String(int(callback->getCpuLoad() * 100)) + "%"); } diff --git a/Source/CpuLoadComp.h b/Source/CpuLoadComp.h index 152037a..7933cea 100644 --- a/Source/CpuLoadComp.h +++ b/Source/CpuLoadComp.h @@ -1,46 +1,52 @@ /* - ============================================================================== - - CpuLoadComp.h - Created: 18 Apr 2020 4:23:20pm - Author: Luca Bondi - - ============================================================================== + CPU load component + + Authors: + Luca Bondi (luca.bondi@polimi.it) */ #pragma once #include -#include "PluginProcessor.h" //============================================================================== -/* -*/ -class CpuLoadComp : public Component, - public Timer -{ + +class CpuLoadComp : public Component, + public Timer { public: - CpuLoadComp(const EbeamerAudioProcessor &p); + CpuLoadComp(); + ~CpuLoadComp(); - void paint (Graphics&) override; + class Callback { + public: + virtual ~Callback() = default; + + virtual float getCpuLoad() const = 0; + }; + + void setSource(Callback *cb); + + void paint(Graphics &) override; + void resized() override; private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CpuLoadComp) - + /** Load indicator text */ TextEditor text; /** Label for load indicator text */ Label label; + /** Timer callback */ void timerCallback() override; - - /** Processor instance */ - const EbeamerAudioProcessor &processor; - + + /** Callback instance */ + Callback *callback = nullptr; + // Constants const float textHeight = 10; const float labelWidth = 50; - + }; diff --git a/Source/DAS.cpp b/Source/DAS.cpp deleted file mode 100644 index 989c134..0000000 --- a/Source/DAS.cpp +++ /dev/null @@ -1,78 +0,0 @@ -/* - ============================================================================== - - DAS.cpp - Created: 15 Apr 2020 9:21:10am - Author: Luca Bondi - - ============================================================================== -*/ - -#include "DAS.h" - -namespace DAS{ - - - -FarfieldLMA::FarfieldLMA(float micDist_,int numMic_,float fs_,float soundspeed_){ - - micDist = micDist_; - numMic = numMic_; - fs = fs_; - soundspeed = soundspeed_; - - commonDelay = 64; - firLen = ceil(numMic*micDist/soundspeed*fs) + 2*commonDelay; - - fft = std::make_unique(ceil(log2(firLen))); - - win.resize(fft->getSize()); - designTukeyWindow(win, fft->getSize(), commonDelay/2); - - freqAxes = Vec::LinSpaced(fft->getSize(),0,fs*(fft->getSize()-1)/fft->getSize()); - -} - -int FarfieldLMA::getFirLen() const{ - return firLen; -} - -void FarfieldLMA::getFir(AudioBuffer&fir,const BeamParameters& params,float alpha) const{ - - /** Angle in radians (0 front, pi/2 source closer to last channel, -pi/2 source closer to first channel */ - const float angleRad = (params.doa+1)*pi/2; - /** Delay between adjacent microphones [s] */ - const float delta = -cos(angleRad)*micDist/soundspeed; - /** Compute delays for each microphone [s] */ - Vec micDelays = delta*Vec::LinSpaced(numMic, 0, numMic-1); - /** Compensate for minimum delay and apply common delay */ - micDelays.array() += - micDelays.minCoeff() + commonDelay/fs; - /** Compute the fractional delays in frequency domain */ - CpxMtx irFFT = (-j2pi*freqAxes*micDelays.transpose()).array().exp(); - - - /** Compute how many microphones are muted at each end */ - const int inactiveMicAtBorder = roundToInt((numMic/2-1)*params.width); - /** Generate the mask of active microphones */ - Vec micGains = Vec::Ones(numMic); - micGains.head(inactiveMicAtBorder).array() = 0; - micGains.tail(inactiveMicAtBorder).array() = 0; - /** Normalize the power */ - micGains.array() *= referencePower/micGains.sum(); - - /** Apply the gain */ - irFFT = irFFT.cwiseProduct(micGains.transpose().replicate(freqAxes.size(), 1)); - - /** Convert from requency to time domain and add to destination*/ - for (auto micIdx=0;micIdx= number of microphones and numSamples >= firLen - @param params: beam parameters - @param alpha: exponential interpolation coefficient. 1 means complete override (instant update), 0 means no override (complete preservation) - */ - void getFir(AudioBuffer&fir,const BeamParameters& params,float alpha=1) const override; - -private: - - /** Distance between microphones [m] */ - float micDist; - - /** Number of microphones */ - int numMic; - - /** Sampling frequency [Hz] */ - float fs; - - /** Soundspeed [m/s] */ - float soundspeed; - - /** Common delay applied to all the filters to make filters causal [samples] */ - int commonDelay; - - /** Length of FIR filters [samples] */ - int firLen; - - /** FFT object */ - std::unique_ptr fft; - - /** Window applied to the FIR filters in time domain */ - Vec win; - - /** Frequencies axes */ - Vec freqAxes; - - /** Reference power for normalization */ - const float referencePower = 3; - -}; - -} diff --git a/Source/FIR.h b/Source/FIR.h deleted file mode 100644 index c9162b1..0000000 --- a/Source/FIR.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - ============================================================================== - - vFIR.h - Created: 27 Apr 2019 9:45:28am - Author: Luca Bondi - - ============================================================================== -*/ - -#pragma once - -#include "../JuceLibraryCode/JuceHeader.h" - -namespace FIR{ - class AudioBufferFFT: public AudioBuffer{ - - private: - AudioBuffer convBuffer; - std::shared_ptr fft; - bool readyForConvolution = false; - - static void prepareForConvolution (float *samples, int fftSize) noexcept; - static void convolutionProcessingAndAccumulate (const float *input, const float *impulse, float *output, int fftSize); - static void updateSymmetricFrequencyDomainData (float* samples, int fftSize) noexcept; - - void updateSymmetricFrequency(); - - public: - AudioBufferFFT(){}; - AudioBufferFFT(int numChannels,std::shared_ptr&); - AudioBufferFFT(const AudioBuffer&,std::shared_ptr&); - - void reset(); - void setTimeSeries(const AudioBuffer&); - void getTimeSeries(AudioBuffer&); - void getTimeSeries(int sourceCh, AudioBuffer& dest,int destCh); - void addTimeSeries(AudioBuffer&); - void addTimeSeries(int sourceCh, AudioBuffer& dest,int destCh); - - void convolve(int outputChannel, const AudioBufferFFT& in_, int inChannel, AudioBufferFFT& filter_, int filterChannel ); - - void prepareForConvolution(); - bool isReadyForConvolution() const {return readyForConvolution;}; - - }; -} - diff --git a/Source/MeterComp.cpp b/Source/MeterComp.cpp new file mode 100644 index 0000000..c1dd410 --- /dev/null +++ b/Source/MeterComp.cpp @@ -0,0 +1,193 @@ +/* + Meter components + + Authors: + Luca Bondi (luca.bondi@polimi.it) +*/ + +#include "MeterComp.h" + + +void RoundLed::paint(Graphics &g) { + + Rectangle area = getLocalBounds().toFloat(); + auto side = area.getHeight() > area.getWidth() ? area.getWidth() : area.getHeight(); + auto ctr = area.getCentre(); + area = Rectangle(side, side); + area.setCentre(ctr); + + g.setColour(colour); + g.fillEllipse(area); +} + +void RoundLed::resized() { + +} + + +void MultiChannelLedBar::makeLayout() { + removeAllChildren(); + leds.clear(); + if (callback == nullptr) { + return; + } + callback->getMeterValues(values, meterId); + auto num = values.size(); + for (auto ledIdx = 0; ledIdx < num; ++ledIdx) { + leds.push_back(std::make_unique()); + leds[ledIdx]->colour = Colours::grey; + addAndMakeVisible(leds[ledIdx].get()); + } + resized(); +} + +void MultiChannelLedBar::paint(Graphics &) { + +} + +void MultiChannelLedBar::resized() { + + if (callback == nullptr) { + return; + } + callback->getMeterValues(values, meterId); + auto num = values.size(); + Rectangle area = getLocalBounds(); + + int step = isHorizontal ? floor(area.getWidth() / num) : floor(area.getHeight() / num); + int otherDim = isHorizontal ? area.getHeight() : area.getWidth(); + otherDim = jmin(otherDim, step - 1); + + const auto areaCtr = area.getCentre(); + + // Re-center the area + if (isHorizontal) { + area.setWidth((int) (step * num)); + area.setHeight(otherDim); + } else { + area.setHeight((int) (step * num)); + area.setWidth(otherDim); + } + area.setCentre(areaCtr); + + for (auto ledIdx = 0; ledIdx < num; ++ledIdx) { + Rectangle ledArea = isHorizontal ? area.removeFromLeft(step) : area.removeFromTop(step); + leds[ledIdx]->setBounds(ledArea); + } + +} + +void MultiChannelLedBar::timerCallback() { + + if (callback == nullptr) + return; + + callback->getMeterValues(values, meterId); + + if (values.size() != leds.size()) { + makeLayout(); + } + + for (auto ledIdx = 0; ledIdx < leds.size(); ++ledIdx) { + auto value = values.at(ledIdx); + Colour col; + if (value > Decibels::decibelsToGain(RED_LT)) { + col = Colours::red; + } else if (value > Decibels::decibelsToGain(YELLOW_LT)) { + col = Colours::yellow; + } else if (value > Decibels::decibelsToGain(GREEN_LT)) { + col = Colours::lightgreen; + } else { + col = Colours::grey; + } + leds[ledIdx]->colour = col; + } + + repaint(); +} + +void MultiChannelLedBar::setCallback(MeterDecay::Callback *cb, int metId) { + callback = cb; + meterId = metId; + callback->getMeterValues(values, meterId); + if (values.size() > 0) { + makeLayout(); + } +} + + +SingleChannelLedBar::SingleChannelLedBar(size_t numLeds, bool isHorizontal) { + jassert(numLeds > 4); + + this->isHorizontal = isHorizontal; + + const float ledStep = 3; //dB + + leds.clear(); + th.clear(); + for (auto ledIdx = 0; ledIdx < numLeds; ++ledIdx) { + leds.push_back(std::make_unique()); + + auto ledThDb = ledIdx == (numLeds - 1) ? RED_LT : -((numLeds - 1 - ledIdx) * ledStep); + th.push_back(ledThDb); + leds[ledIdx]->colour = thToColour(ledThDb, false); + + addAndMakeVisible(leds[ledIdx].get()); + } +} + +void SingleChannelLedBar::setCallback(MeterDecay::Callback *pr, int metId, int ch) { + provider = pr; + meterId = metId; + channel = ch; +} + +void SingleChannelLedBar::paint(Graphics &) { + +} + +void SingleChannelLedBar::resized() { + + Rectangle area = getLocalBounds().toFloat(); + auto num = leds.size(); + float step = isHorizontal ? area.getWidth() / num : area.getHeight() / num; + for (auto ledIdx = 0; ledIdx < num; ++ledIdx) { + Rectangle ledArea = isHorizontal ? area.removeFromLeft(step) : area.removeFromBottom(step); + leds[ledIdx]->setBounds(ledArea.toNearestInt()); + } + +} + +void SingleChannelLedBar::timerCallback() { + if (provider == nullptr) + return; + + auto valueDb = Decibels::gainToDecibels(provider->getMeterValue(meterId, channel)); + for (auto ledIdx = 0; ledIdx < leds.size(); ++ledIdx) + leds[ledIdx]->colour = thToColour(th[ledIdx], valueDb > th[ledIdx]); + + repaint(); +} + +Colour SingleChannelLedBar::thToColour(float th, bool active) { + if (th >= RED_LT) { + if (active) + return Colours::red; + else + return Colours::darkred; + } else if (th >= YELLOW_LT) { + if (active) + return Colours::yellow; + else + return Colours::darkgoldenrod; + } else { + if (active) + return Colours::lightgreen; + else + return Colours::darkgreen; + } +} + +MuteButton::MuteButton() { + setClickingTogglesState(true); +} diff --git a/Source/MeterComp.h b/Source/MeterComp.h new file mode 100644 index 0000000..577884e --- /dev/null +++ b/Source/MeterComp.h @@ -0,0 +1,104 @@ +/* + Meter components + + Authors: + Luca Bondi (luca.bondi@polimi.it) +*/ + +#pragma once + + +#define RED_LT -0.5f //dB +#define YELLOW_LT -6.0f //dB +#define GREEN_LT -20.0f //dB + +#include "../JuceLibraryCode/JuceHeader.h" +#include "MidiComp.h" +#include "MeterDecay.h" + +//============================================================================== + +class RoundLed : public Component { +public: + + RoundLed() {}; + + Colour colour; + + void paint(Graphics &) override; + + void resized() override; + +private: + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RoundLed) +}; + + +//============================================================================== +class MultiChannelLedBar : public Component, public Timer { +public: + + MultiChannelLedBar() {}; + + ~MultiChannelLedBar() {}; + + void paint(Graphics &) override; + + void resized() override; + + void setCallback(MeterDecay::Callback *cb, int metId); + + void setHorizontal() { isHorizontal = true; }; + + void setVertical() { isHorizontal = false; }; + + +private: + + MeterDecay::Callback *callback = nullptr; + int meterId; + + bool isHorizontal = true; + + std::vector> leds; + std::vector values; + + void timerCallback() override; + + void makeLayout(); + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MultiChannelLedBar) +}; + +//============================================================================== +class SingleChannelLedBar : public Component, public Timer { +public: + + SingleChannelLedBar(size_t numLeds = 7, bool isHorizontal = false); + + ~SingleChannelLedBar() {}; + + void setCallback(MeterDecay::Callback *cb, int meterId, int channel); + + void paint(Graphics &) override; + + void resized() override; + + static Colour thToColour(float th, bool active); + +private: + + MeterDecay::Callback *provider = nullptr; + int meterId; + int channel; + + bool isHorizontal; + + std::vector th; + std::vector> leds; + + void timerCallback() override; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SingleChannelLedBar) +}; diff --git a/Source/MeterDecay.cpp b/Source/MeterDecay.cpp new file mode 100644 index 0000000..70d3a9a --- /dev/null +++ b/Source/MeterDecay.cpp @@ -0,0 +1,70 @@ +/* + Meter Decay + A signal meter with decay + + Authors: + Luca Bondi (luca.bondi@polimi.it) +*/ + +#include "MeterDecay.h" + + +MeterDecay::MeterDecay(float fs, float duration, float blockSize, int numChannels) { + + int numBlocks = (int) ceil(duration * fs / blockSize); + + minMaxCircularBuffer.resize(numChannels); + idxs.resize(numChannels); + for (auto channelIdx = 0; channelIdx < numChannels; ++channelIdx) { + idxs[channelIdx] = 0; + minMaxCircularBuffer[channelIdx].resize(numBlocks); + } + +} + +void MeterDecay::push(const AudioBuffer &signal) { + GenericScopedLock l(minMaxCircularBufferLock); + + for (auto channelIdx = 0; channelIdx < signal.getNumChannels(); ++channelIdx) { + Range minMax = FloatVectorOperations::findMinAndMax(signal.getReadPointer(channelIdx), + signal.getNumSamples()); + float maxAbs = jmax(abs(minMax.getStart()), abs(minMax.getEnd())); + minMaxCircularBuffer[channelIdx][idxs[channelIdx]++] = maxAbs; + if (idxs[channelIdx] >= minMaxCircularBuffer[channelIdx].size()) { + idxs[channelIdx] = 0; + } + } +} + +void MeterDecay::get(std::vector &values) const { + GenericScopedLock l(minMaxCircularBufferLock); + values.resize(minMaxCircularBuffer.size()); + for (auto channelIdx = 0; channelIdx < minMaxCircularBuffer.size(); ++channelIdx) { + float maxVal = 0; + for (auto idx = 0; idx < minMaxCircularBuffer[channelIdx].size(); ++idx) { + maxVal = jmax(maxVal, minMaxCircularBuffer[channelIdx][idx]); + } + values[channelIdx] = maxVal; + } +} + +float MeterDecay::get(int ch) const { + GenericScopedLock l(minMaxCircularBufferLock); + float maxVal = 0; + for (auto idx = 0; idx < minMaxCircularBuffer[ch].size(); ++idx) { + maxVal = jmax(maxVal, minMaxCircularBuffer[ch][idx]); + } + return maxVal; +} + +float panToLinearGain(float gain, bool isLeftChannel) { + const float db_at0 = -4.5; //How many dB at each channel when pan is centered (0) + jassert(gain >= -1); + jassert(gain <= 1); + float alpha = std::pow(10., (db_at0 / 20.)); + if (isLeftChannel) { + gain = -gain; + } + float y = (0.5 - alpha) * std::pow(gain, 2.) + 0.5 * gain + alpha; + return y; +} diff --git a/Source/MeterDecay.h b/Source/MeterDecay.h new file mode 100644 index 0000000..1b888aa --- /dev/null +++ b/Source/MeterDecay.h @@ -0,0 +1,45 @@ +/* + Meter Decay + A signal meter with decay + + Authors: + Luca Bondi (luca.bondi@polimi.it) +*/ + +#pragma once + +#include "../JuceLibraryCode/JuceHeader.h" + + +class MeterDecay { + +public: + + MeterDecay(float fs, float duration, float blockSize, int numChannels); + + void push(const AudioBuffer &signal); + + void get(std::vector &meter) const; + + float get(int ch) const; + + class Callback { + public: + virtual ~Callback() = default; + + virtual float getMeterValue(int meterId, int channel) const = 0; + + virtual void getMeterValues(std::vector &values, int meterId) const = 0; + }; + +private: + + std::vector idxs; + std::vector> minMaxCircularBuffer; + + SpinLock minMaxCircularBufferLock; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MeterDecay) +}; + +float panToLinearGain(float gain, bool isLeftChannel); diff --git a/Source/MidiCC.h b/Source/MidiCC.h new file mode 100644 index 0000000..783ae1c --- /dev/null +++ b/Source/MidiCC.h @@ -0,0 +1,44 @@ +/* + Midi CC definitions and classes + + Authors: + Luca Bondi (luca.bondi@polimi.it) +*/ + +#pragma once + +class MidiCC { +public: + + class Callback { + public: + + virtual ~Callback() = default; + + /** Start learning the specified parameter */ + virtual void startCCLearning(const String &p) = 0; + + /** Stop learning the previous parameter */ + virtual void stopCCLearning() = 0; + + /** Get parameter being learned */ + virtual String getCCLearning() const = 0; + + /** Get a read-only reference to the parameters to CC mapping */ + virtual const std::map &getParamToCCMapping() = 0; + + /** Remove mapping between MidiCC and parameter */ + virtual void removeCCParamMapping(const String ¶m) = 0; + + }; + + bool operator<(const MidiCC &rhs) const { + if (channel == rhs.channel) { + return number < rhs.number; + } + return channel < rhs.channel; + }; + + int channel; + int number; +}; diff --git a/Source/MidiComp.cpp b/Source/MidiComp.cpp index bc173d3..f075645 100644 --- a/Source/MidiComp.cpp +++ b/Source/MidiComp.cpp @@ -1,11 +1,8 @@ /* - ============================================================================== - - SliderCC.cpp - Created: 22 Apr 2020 12:32:05pm - Author: Luca Bondi - - ============================================================================== + Base classes for GUI components used with MIDI CC + + Authors: + Luca Bondi (luca.bondi@polimi.it) */ #include @@ -13,67 +10,65 @@ //============================================================================== -MidiCCPopup::MidiCCPopup(Component& owner_):owner(owner_){ - +MidiCCPopup::MidiCCPopup(Component &owner_) : owner(owner_) { + } -MidiCCPopup::~MidiCCPopup(){ - +MidiCCPopup::~MidiCCPopup() { + } -void MidiCCPopup::setProcessorParamName(EbeamerAudioProcessor* proc, const String & param){ - processor = proc; +void MidiCCPopup::setCallback(MidiCC::Callback *cb, const String ¶m) { + callback = cb; paramName = param; } -bool MidiCCPopup::isLearning() const{ - return paramName == processor->getCCLearning(); +bool MidiCCPopup::isLearning() const { + return paramName == callback->getCCLearning(); } -void MidiCCPopup::showPopupMenu() -{ +void MidiCCPopup::showPopupMenu() { PopupMenu m; - m.setLookAndFeel (&owner.getLookAndFeel()); - if (isLearning()){ - m.addItem(1,"Stop learning CC",true,false); - }else{ - m.addItem(1,"Learn CC",true,false); + m.setLookAndFeel(&owner.getLookAndFeel()); + if (isLearning()) { + m.addItem(1, "Stop learning CC", true, false); + } else { + m.addItem(1, "Learn CC", true, false); } - - auto mapping = processor->getParamToCCMapping(); - if (mapping.count(paramName) > 0){ - m.addItem(2,"Forget CC ",true,false); - m.addItem(3,"Chan: " + String(mapping[paramName].channel) + " - CC: " + String(mapping[paramName].number),false,false); + + auto mapping = callback->getParamToCCMapping(); + if (mapping.count(paramName) > 0) { + m.addItem(2, "Forget CC ", true, false); + m.addItem(3, "Chan: " + String(mapping[paramName].channel) + " - CC: " + String(mapping[paramName].number), + false, false); } - if (popupArea.getX() == 0){ + if (popupArea.getX() == 0) { popupArea = PopupMenu::Options().getTargetScreenArea(); } - m.showAt(popupArea,0,0,0,0,ModalCallbackFunction::create (sliderMenuCallback, this)); - + m.showAt(popupArea, 0, 0, 0, 0, ModalCallbackFunction::create(sliderMenuCallback, this)); + } -void MidiCCPopup::sliderMenuCallback (int result, MidiCCPopup* popup) -{ - if (popup != nullptr) - { - switch (result) - { +void MidiCCPopup::sliderMenuCallback(int result, MidiCCPopup *popup) { + if (popup != nullptr) { + switch (result) { case 1: - if (!popup->isLearning()){ - popup->processor->removeCCParamMapping(popup->paramName); - popup->processor->startCCLearning(popup->paramName); + if (!popup->isLearning()) { + popup->callback->removeCCParamMapping(popup->paramName); + popup->callback->startCCLearning(popup->paramName); popup->showPopupMenu(); - }else{ - popup->processor->stopCCLearning(); - popup->popupArea.setBounds(0,0,0,0); + } else { + popup->callback->stopCCLearning(); + popup->popupArea.setBounds(0, 0, 0, 0); } break; case 2: - popup->processor->removeCCParamMapping(popup->paramName); - popup->popupArea.setBounds(0,0,0,0); + popup->callback->removeCCParamMapping(popup->paramName); + popup->popupArea.setBounds(0, 0, 0, 0); + break; + default: break; - default: break; } } } diff --git a/Source/MidiComp.h b/Source/MidiComp.h index 7cd1564..f45f3f3 100644 --- a/Source/MidiComp.h +++ b/Source/MidiComp.h @@ -1,83 +1,180 @@ /* - ============================================================================== + Base classes for GUI components used with MIDI CC - SliderCC.h - Created: 22 Apr 2020 12:32:05pm - Author: Luca Bondi - - ============================================================================== - */ + Authors: + Luca Bondi (luca.bondi@polimi.it) +*/ #pragma once +#define DB_MINUS_INF -100.0 #include -#include "PluginProcessor.h" +#include "MidiCC.h" //============================================================================== -class MidiCCPopup -{ +class MidiCCPopup { public: - MidiCCPopup(Component& owner); + MidiCCPopup(Component &owner); + ~MidiCCPopup(); - - void setProcessorParamName(EbeamerAudioProcessor* proc, const String & param); - + + void setCallback(MidiCC::Callback *cb, const String ¶m); + void showPopupMenu(); - + private: - EbeamerAudioProcessor* processor = nullptr; + MidiCC::Callback *callback = nullptr; String paramName = ""; Rectangle popupArea; - Component& owner; - static void sliderMenuCallback (int result, MidiCCPopup* popup); - + Component &owner; + + static void sliderMenuCallback(int result, MidiCCPopup *popup); + bool isLearning() const; }; //============================================================================== -class SliderCC : public Slider, public MidiCCPopup{ +class SliderCC : public Slider, public MidiCCPopup { public: - SliderCC():MidiCCPopup(dynamic_cast(*this)){}; + SliderCC() : MidiCCPopup(dynamic_cast(*this)) {}; + ~SliderCC() override {}; - - void mouseDown (const MouseEvent& e) override{ - if (e.mods.isPopupMenu()){ + + void mouseDown(const MouseEvent &e) override { + if (e.mods.isPopupMenu()) { MidiCCPopup::showPopupMenu(); - }else{ + } else { Slider::mouseDown(e); } } - + }; //============================================================================== -class TextButtonCC : public TextButton, public MidiCCPopup{ +class TextButtonCC : public TextButton, public MidiCCPopup { public: - TextButtonCC():MidiCCPopup(dynamic_cast(*this)){}; - ~TextButtonCC() override {}; - - void mouseDown (const MouseEvent& e) override{ - if (e.mods.isPopupMenu()){ + TextButtonCC() : MidiCCPopup(dynamic_cast(*this)) {}; + + ~TextButtonCC() override {}; + + void mouseDown(const MouseEvent &e) override { + if (e.mods.isPopupMenu()) { MidiCCPopup::showPopupMenu(); - }else{ + } else { TextButton::mouseDown(e); } } - + }; //============================================================================== -class ToggleButtonCC : public ToggleButton, public MidiCCPopup{ +class ToggleButtonCC : public ToggleButton, public MidiCCPopup { public: - ToggleButtonCC():MidiCCPopup(dynamic_cast(*this)){}; + ToggleButtonCC() : MidiCCPopup(dynamic_cast(*this)) {}; + ~ToggleButtonCC() override {}; - - void mouseDown (const MouseEvent& e) override{ - if (e.mods.isPopupMenu()){ + + void mouseDown(const MouseEvent &e) override { + if (e.mods.isPopupMenu()) { MidiCCPopup::showPopupMenu(); - }else{ + } else { ToggleButton::mouseDown(e); } } - + +}; + +//============================================================================== +class DecibelSlider : public SliderCC { +public: + + DecibelSlider() {}; + + double getValueFromText(const String &text) override { + + auto decibelText = text.upToFirstOccurrenceOf("dB", false, false).trim(); + + return decibelText.equalsIgnoreCase("-INF") ? DB_MINUS_INF + : decibelText.getDoubleValue(); // [2] + } + + String getTextFromValue(double value) override { + return Decibels::toString(value, 1, DB_MINUS_INF); + } + +private: + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DecibelSlider) +}; + +//============================================================================== + +class FrequencySlider : public SliderCC { +public: + + FrequencySlider() {}; + + double getValueFromText(const String &text) override { + + auto hzText = text.upToFirstOccurrenceOf("Hz", false, false).trim(); + auto hzVal = roundToInt(hzText.getDoubleValue()); + return hzVal; + } + + String getTextFromValue(double value) override { + std::ostringstream valueString; + valueString << roundToInt(value) << " Hz"; + return valueString.str(); + } + +private: + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FrequencySlider) +}; + +//============================================================================== + +class PanSlider : public SliderCC { +public: + + PanSlider() {}; + + double getValueFromText(const String &text) override { + + if (text.startsWithIgnoreCase("C") || text.startsWithIgnoreCase("0")) { + return 0; + } + + float sign = 1; + if (text.endsWithIgnoreCase("L")) { + sign = -1; + } + + String valueText = text.upToFirstOccurrenceOf("%", false, false).trim(); + double val = valueText.getDoubleValue() / 100.; + return sign * val; + } + + String getTextFromValue(double value) override { + jassert(value >= -1 && value <= 1); + std::ostringstream valueString; + if (value == 0) { + valueString << "C"; + } else if (value < 0) { + valueString << roundToInt(abs(value) * 100) << " % L"; + } else { + valueString << roundToInt((value) * 100) << " % R"; + } + + return valueString.str(); + } + +private: + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PanSlider) +}; + +//============================================================================== +class MuteButton : public TextButtonCC { +public: + + MuteButton(); + }; diff --git a/Source/PluginEditor.cpp b/Source/PluginEditor.cpp index 1a10c80..1e3c1e2 100644 --- a/Source/PluginEditor.cpp +++ b/Source/PluginEditor.cpp @@ -1,48 +1,55 @@ +/* + eBeamer Plugin Processor GUI + + Authors: + Luca Bondi (luca.bondi@polimi.it) +*/ + #include "PluginProcessor.h" #include "PluginEditor.h" -JucebeamAudioProcessorEditor::JucebeamAudioProcessorEditor (EbeamerAudioProcessor& p,AudioProcessorValueTreeState& v) -: AudioProcessorEditor (&p), processor (p), valueTreeState(v), scene(p), cpuLoad(p) -{ +JucebeamAudioProcessorEditor::JucebeamAudioProcessorEditor(EbeamerAudioProcessor &p, AudioProcessorValueTreeState &v) + : AudioProcessorEditor(&p), processor(p), valueTreeState(v) { //============================================================================== - setSize (GUI_WIDTH, GUI_HEIGHT); + setSize(GUI_WIDTH, GUI_HEIGHT); //============================================================================== + scene.setCallback(&p); scene.setBeamColors(beamColours); - addAndMakeVisible (scene); + addAndMakeVisible(scene); //============================================================================== steerLabel.setText("STEER", NotificationType::dontSendNotification); steerLabel.setJustificationType(Justification::centred); addAndMakeVisible(steerLabel); - + steeringBeam1Label.setText("1", NotificationType::dontSendNotification); steeringBeam1Label.setJustificationType(Justification::left); steeringBeam1Label.attachToComponent(&steeringBeam1Slider, true); addAndMakeVisible(steeringBeam1Label); - + steeringBeam2Label.setText("2", NotificationType::dontSendNotification); steeringBeam2Label.setJustificationType(Justification::left); steeringBeam2Label.attachToComponent(&steeringBeam2Slider, true); addAndMakeVisible(steeringBeam2Label); - + steeringBeam1Slider.setSliderStyle(Slider::LinearHorizontal); - steeringBeam1Slider.setTextBoxStyle(Slider::TextBoxRight,false,LABEL_WIDTH,LABEL_HEIGHT); + steeringBeam1Slider.setTextBoxStyle(Slider::TextBoxRight, false, LABEL_WIDTH, LABEL_HEIGHT); steeringBeam1Slider.setColour(Slider::thumbColourId, beamColours[0]); steeringBeam1Slider.setPopupMenuEnabled(true); - steeringBeam1Slider.setProcessorParamName(&processor, "steerBeam1"); + steeringBeam1Slider.setCallback(&processor, "steerBeam1"); addAndMakeVisible(steeringBeam1Slider); steeringBeam2Slider.setSliderStyle(Slider::LinearHorizontal); - steeringBeam2Slider.setTextBoxStyle(Slider::TextBoxRight,false,LABEL_WIDTH,LABEL_HEIGHT); + steeringBeam2Slider.setTextBoxStyle(Slider::TextBoxRight, false, LABEL_WIDTH, LABEL_HEIGHT); steeringBeam2Slider.setColour(Slider::thumbColourId, beamColours[1]); steeringBeam2Slider.setPopupMenuEnabled(true); - steeringBeam2Slider.setProcessorParamName(&processor, "steerBeam2"); + steeringBeam2Slider.setCallback(&processor, "steerBeam2"); addAndMakeVisible(steeringBeam2Slider); - - steeringBeam1SliderAttachment.reset(new SliderAttachment (valueTreeState, "steerBeam1", steeringBeam1Slider)); - steeringBeam2SliderAttachment.reset(new SliderAttachment (valueTreeState, "steerBeam2", steeringBeam2Slider)); + + steeringBeam1SliderAttachment.reset(new SliderAttachment(valueTreeState, "steerBeam1", steeringBeam1Slider)); + steeringBeam2SliderAttachment.reset(new SliderAttachment(valueTreeState, "steerBeam2", steeringBeam2Slider)); //============================================================================== widthLabel.setText("WIDTH", NotificationType::dontSendNotification); @@ -50,21 +57,21 @@ JucebeamAudioProcessorEditor::JucebeamAudioProcessorEditor (EbeamerAudioProcesso addAndMakeVisible(widthLabel); widthBeam1Knob.setSliderStyle(Slider::RotaryHorizontalVerticalDrag); - widthBeam1Knob.setTextBoxStyle(Slider::TextBoxRight,false,LABEL_WIDTH,LABEL_HEIGHT); + widthBeam1Knob.setTextBoxStyle(Slider::TextBoxRight, false, LABEL_WIDTH, LABEL_HEIGHT); widthBeam1Knob.setColour(Slider::thumbColourId, beamColours[0]); widthBeam1Knob.setPopupMenuEnabled(true); - widthBeam1Knob.setProcessorParamName(&processor, "widthBeam1"); + widthBeam1Knob.setCallback(&processor, "widthBeam1"); addAndMakeVisible(widthBeam1Knob); widthBeam2Knob.setSliderStyle(Slider::RotaryHorizontalVerticalDrag); - widthBeam2Knob.setTextBoxStyle(Slider::TextBoxLeft,false,LABEL_WIDTH,LABEL_HEIGHT); + widthBeam2Knob.setTextBoxStyle(Slider::TextBoxLeft, false, LABEL_WIDTH, LABEL_HEIGHT); widthBeam2Knob.setColour(Slider::thumbColourId, beamColours[1]); widthBeam2Knob.setPopupMenuEnabled(true); - widthBeam2Knob.setProcessorParamName(&processor, "widthBeam2"); + widthBeam2Knob.setCallback(&processor, "widthBeam2"); addAndMakeVisible(widthBeam2Knob); - - widthBeam1KnobAttachment.reset(new SliderAttachment (valueTreeState, "widthBeam1", widthBeam1Knob)); - widthBeam2KnobAttachment.reset(new SliderAttachment (valueTreeState, "widthBeam2", widthBeam2Knob)); + + widthBeam1KnobAttachment.reset(new SliderAttachment(valueTreeState, "widthBeam1", widthBeam1Knob)); + widthBeam2KnobAttachment.reset(new SliderAttachment(valueTreeState, "widthBeam2", widthBeam2Knob)); //============================================================================== panLabel.setText("PAN", NotificationType::dontSendNotification); @@ -72,21 +79,21 @@ JucebeamAudioProcessorEditor::JucebeamAudioProcessorEditor (EbeamerAudioProcesso addAndMakeVisible(panLabel); panBeam1Knob.setSliderStyle(Slider::RotaryHorizontalVerticalDrag); - panBeam1Knob.setTextBoxStyle(Slider::TextBoxRight,false,LABEL_WIDTH,LABEL_HEIGHT); + panBeam1Knob.setTextBoxStyle(Slider::TextBoxRight, false, LABEL_WIDTH, LABEL_HEIGHT); panBeam1Knob.setColour(Slider::thumbColourId, beamColours[0]); panBeam1Knob.setPopupMenuEnabled(true); - panBeam1Knob.setProcessorParamName(&processor, "panBeam1"); + panBeam1Knob.setCallback(&processor, "panBeam1"); addAndMakeVisible(panBeam1Knob); panBeam2Knob.setSliderStyle(Slider::RotaryHorizontalVerticalDrag); - panBeam2Knob.setTextBoxStyle(Slider::TextBoxLeft,false,LABEL_WIDTH,LABEL_HEIGHT); + panBeam2Knob.setTextBoxStyle(Slider::TextBoxLeft, false, LABEL_WIDTH, LABEL_HEIGHT); panBeam2Knob.setColour(Slider::thumbColourId, beamColours[1]); panBeam2Knob.setPopupMenuEnabled(true); - panBeam2Knob.setProcessorParamName(&processor, "panBeam2"); + panBeam2Knob.setCallback(&processor, "panBeam2"); addAndMakeVisible(panBeam2Knob); - - panBeam1KnobAttachment.reset(new SliderAttachment (valueTreeState, "panBeam1", panBeam1Knob)); - panBeam2KnobAttachment.reset(new SliderAttachment (valueTreeState, "panBeam2", panBeam2Knob)); + + panBeam1KnobAttachment.reset(new SliderAttachment(valueTreeState, "panBeam1", panBeam1Knob)); + panBeam2KnobAttachment.reset(new SliderAttachment(valueTreeState, "panBeam2", panBeam2Knob)); //============================================================================== @@ -95,48 +102,48 @@ JucebeamAudioProcessorEditor::JucebeamAudioProcessorEditor (EbeamerAudioProcesso addAndMakeVisible(levelLabel); levelBeam1Knob.setSliderStyle(Slider::RotaryHorizontalVerticalDrag); - levelBeam1Knob.setTextBoxStyle(Slider::TextBoxRight,false,LABEL_WIDTH,LABEL_HEIGHT); + levelBeam1Knob.setTextBoxStyle(Slider::TextBoxRight, false, LABEL_WIDTH, LABEL_HEIGHT); levelBeam1Knob.setColour(Slider::thumbColourId, beamColours[0]); levelBeam1Knob.setPopupMenuEnabled(true); - levelBeam1Knob.setProcessorParamName(&processor, "levelBeam1"); + levelBeam1Knob.setCallback(&processor, "levelBeam1"); addAndMakeVisible(levelBeam1Knob); levelBeam2Knob.setSliderStyle(Slider::RotaryHorizontalVerticalDrag); - levelBeam2Knob.setTextBoxStyle(Slider::TextBoxLeft,false,LABEL_WIDTH,LABEL_HEIGHT); + levelBeam2Knob.setTextBoxStyle(Slider::TextBoxLeft, false, LABEL_WIDTH, LABEL_HEIGHT); levelBeam2Knob.setColour(Slider::thumbColourId, beamColours[1]); levelBeam2Knob.setPopupMenuEnabled(true); - levelBeam2Knob.setProcessorParamName(&processor, "levelBeam2"); + levelBeam2Knob.setCallback(&processor, "levelBeam2"); addAndMakeVisible(levelBeam2Knob); - levelBeam1KnobAttachment.reset(new SliderAttachment (valueTreeState, "levelBeam1", levelBeam1Knob)); - levelBeam2KnobAttachment.reset(new SliderAttachment (valueTreeState, "levelBeam2", levelBeam2Knob)); + levelBeam1KnobAttachment.reset(new SliderAttachment(valueTreeState, "levelBeam1", levelBeam1Knob)); + levelBeam2KnobAttachment.reset(new SliderAttachment(valueTreeState, "levelBeam2", levelBeam2Knob)); //============================================================================== - + muteLabel.setText("MUTE", NotificationType::dontSendNotification); muteLabel.setJustificationType(Justification::centred); addAndMakeVisible(muteLabel); muteBeam1Button.setButtonText("1"); - muteBeam1Button.setProcessorParamName(&processor, "muteBeam1"); + muteBeam1Button.setCallback(&processor, "muteBeam1"); addAndMakeVisible(muteBeam1Button); muteBeam2Button.setButtonText("2"); - muteBeam2Button.setProcessorParamName(&processor, "muteBeam2"); + muteBeam2Button.setCallback(&processor, "muteBeam2"); addAndMakeVisible(muteBeam2Button); - - beam1MuteButtonAttachment.reset(new ButtonAttachment (valueTreeState, "muteBeam1", muteBeam1Button)); - beam2MuteButtonAttachment.reset(new ButtonAttachment (valueTreeState, "muteBeam2", muteBeam2Button)); - - getLookAndFeel().setColour (MuteButton::buttonOnColourId, Colours::darkred); + + beam1MuteButtonAttachment.reset(new ButtonAttachment(valueTreeState, "muteBeam1", muteBeam1Button)); + beam2MuteButtonAttachment.reset(new ButtonAttachment(valueTreeState, "muteBeam2", muteBeam2Button)); + + getLookAndFeel().setColour(MuteButton::buttonOnColourId, Colours::darkred); //============================================================================== - - beam1Meter.setSource(p.beamMeters,0,p.beamMetersLock); + + beam1Meter.setCallback(&processor, 1, 0); beam1Meter.startTimerHz(BEAM_METER_UPDATE_FREQ); addAndMakeVisible(beam1Meter); - - beam2Meter.setSource(p.beamMeters,1,p.beamMetersLock); + + beam2Meter.setCallback(&processor, 1, 1); beam2Meter.startTimerHz(BEAM_METER_UPDATE_FREQ); addAndMakeVisible(beam2Meter); @@ -145,50 +152,51 @@ JucebeamAudioProcessorEditor::JucebeamAudioProcessorEditor (EbeamerAudioProcesso hpfLabel.setText("HPF", NotificationType::dontSendNotification); hpfLabel.setJustificationType(Justification::left); hpfLabel.attachToComponent(&hpfSlider, true); - + hpfSlider.setSliderStyle(Slider::LinearHorizontal); - hpfSlider.setTextBoxStyle(Slider::TextBoxRight,false,LABEL_WIDTH,LABEL_HEIGHT); + hpfSlider.setTextBoxStyle(Slider::TextBoxRight, false, LABEL_WIDTH, LABEL_HEIGHT); hpfSlider.setPopupMenuEnabled(true); - hpfSlider.setProcessorParamName(&processor, "hpf"); + hpfSlider.setCallback(&processor, "hpf"); addAndMakeVisible(hpfSlider); - - hpfSliderAttachment.reset(new SliderAttachment (valueTreeState, "hpf", hpfSlider)); + + hpfSliderAttachment.reset(new SliderAttachment(valueTreeState, "hpf", hpfSlider)); //============================================================================== - - inputMeter.setSource(processor.inputMeters, processor.inputMetersLock); + + inputMeter.setCallback(&processor, 0); inputMeter.startTimerHz(INPUT_METER_UPDATE_FREQ); addAndMakeVisible(inputMeter); - + //============================================================================== - + gainLabel.setText("GAIN", NotificationType::dontSendNotification); gainLabel.setJustificationType(Justification::left); gainLabel.attachToComponent(&gainSlider, true); - + gainSlider.setSliderStyle(Slider::LinearHorizontal); - gainSlider.setTextBoxStyle(Slider::TextBoxRight,false,LABEL_WIDTH,LABEL_HEIGHT); + gainSlider.setTextBoxStyle(Slider::TextBoxRight, false, LABEL_WIDTH, LABEL_HEIGHT); gainSlider.setPopupMenuEnabled(true); - gainSlider.setProcessorParamName(&processor, "gainMic"); + gainSlider.setCallback(&processor, "gainMic"); addAndMakeVisible(gainSlider); - - gainSliderAttachment.reset(new SliderAttachment (valueTreeState, "gainMic", gainSlider)); - + + gainSliderAttachment.reset(new SliderAttachment(valueTreeState, "gainMic", gainSlider)); + //===================================================== // Add CPU Load and start its timer + cpuLoad.setSource(&processor); cpuLoad.startTimerHz(CPULOAD_UPDATE_FREQ); addAndMakeVisible(cpuLoad); - + //===================================================== // Add front facing toggle frontToggleLabel.setText("FLIP", NotificationType::dontSendNotification); frontToggleLabel.setFont(10); frontToggleLabel.attachToComponent(&frontToggle, true); - frontToggle.setProcessorParamName(&processor, "frontFacing"); + frontToggle.setCallback(&processor, "frontFacing"); addAndMakeVisible(frontToggle); - - frontToggleAttachment.reset(new ButtonAttachment (valueTreeState, "frontFacing", frontToggle)); - + + frontToggleAttachment.reset(new ButtonAttachment(valueTreeState, "frontFacing", frontToggle)); + //===================================================== // Configuration selection combo configComboLabel.setText("CONFIG", NotificationType::dontSendNotification); @@ -196,34 +204,32 @@ JucebeamAudioProcessorEditor::JucebeamAudioProcessorEditor (EbeamerAudioProcesso configComboLabel.attachToComponent(&configCombo, true); configCombo.addItemList(micConfigLabels, 10); addAndMakeVisible(configCombo); - - configComboLabelAttachment.reset(new ComboBoxAttachment (valueTreeState, "config", configCombo)); - + + configComboLabelAttachment.reset(new ComboBoxAttachment(valueTreeState, "config", configCombo)); + } -JucebeamAudioProcessorEditor::~JucebeamAudioProcessorEditor() -{ +JucebeamAudioProcessorEditor::~JucebeamAudioProcessorEditor() { } //============================================================================== -void JucebeamAudioProcessorEditor::paint (Graphics& g) -{ +void JucebeamAudioProcessorEditor::paint(Graphics &g) { // (Our component is opaque, so we must completely fill the background with a solid colour) - g.fillAll (getLookAndFeel().findColour (ResizableWindow::backgroundColourId)); + g.fillAll(getLookAndFeel().findColour(ResizableWindow::backgroundColourId)); + + g.setColour(Colours::white); + g.setFont(15.0f); - g.setColour (Colours::white); - g.setFont (15.0f); - const auto versionArea = getBounds().removeFromBottom(10); g.setColour(Colours::lightgrey); g.setFont(12); - g.drawText("ISPL and Eventide - eBeamer v" + String(JucePlugin_VersionString),versionArea,Justification::centredBottom,false); + g.drawText("ISPL and Eventide - eBeamer v" + String(JucePlugin_VersionString), versionArea, + Justification::centredBottom, false); } -void JucebeamAudioProcessorEditor::resized() -{ +void JucebeamAudioProcessorEditor::resized() { auto area = getLocalBounds(); area.removeFromLeft(LEFT_RIGHT_MARGIN); @@ -232,48 +238,48 @@ void JucebeamAudioProcessorEditor::resized() area.removeFromBottom(TOP_BOTTOM_MARGIN); auto sceneArea = area.removeFromTop(SCENE_HEIGHT); - sceneArea.removeFromRight((area.getWidth()-SCENE_WIDTH)/2); - sceneArea.removeFromLeft((area.getWidth()-SCENE_WIDTH)/2); + sceneArea.removeFromRight((area.getWidth() - SCENE_WIDTH) / 2); + sceneArea.removeFromLeft((area.getWidth() - SCENE_WIDTH) / 2); scene.setBounds(sceneArea); area.removeFromTop(STEER_SLIDER_TOP_MARGIN); - steeringBeam1Slider.setBounds( area.removeFromTop(STEER_SLIDER_HEIGHT).withTrimmedLeft(LABEL_BEAM_WIDTH)); - steeringBeam2Slider.setBounds( area.removeFromTop(STEER_SLIDER_HEIGHT).withTrimmedLeft(LABEL_BEAM_WIDTH)); + steeringBeam1Slider.setBounds(area.removeFromTop(STEER_SLIDER_HEIGHT).withTrimmedLeft(LABEL_BEAM_WIDTH)); + steeringBeam2Slider.setBounds(area.removeFromTop(STEER_SLIDER_HEIGHT).withTrimmedLeft(LABEL_BEAM_WIDTH)); steerLabel.setBounds(area.removeFromTop(LABEL_HEIGHT)); area.removeFromLeft(KNOBS_LEFT_RIGHT_MARGIN); area.removeFromRight(KNOBS_LEFT_RIGHT_MARGIN); - auto knobsArea = area.removeFromTop(KNOB_HEIGHT+KNOB_TOP_MARGIN); + auto knobsArea = area.removeFromTop(KNOB_HEIGHT + KNOB_TOP_MARGIN); knobsArea.removeFromTop(KNOB_TOP_MARGIN); widthBeam1Knob.setBounds(knobsArea.removeFromLeft(KNOB_WIDTH)); widthBeam2Knob.setBounds(knobsArea.removeFromRight(KNOB_WIDTH)); widthLabel.setBounds(knobsArea); - knobsArea = area.removeFromTop(KNOB_HEIGHT+KNOB_TOP_MARGIN); + knobsArea = area.removeFromTop(KNOB_HEIGHT + KNOB_TOP_MARGIN); knobsArea.removeFromTop(KNOB_TOP_MARGIN); panBeam1Knob.setBounds(knobsArea.removeFromLeft(KNOB_WIDTH)); panBeam2Knob.setBounds(knobsArea.removeFromRight(KNOB_WIDTH)); panLabel.setBounds(knobsArea); - knobsArea = area.removeFromTop(KNOB_HEIGHT+KNOB_TOP_MARGIN); + knobsArea = area.removeFromTop(KNOB_HEIGHT + KNOB_TOP_MARGIN); knobsArea.removeFromTop(KNOB_TOP_MARGIN); levelBeam1Knob.setBounds(knobsArea.removeFromLeft(KNOB_WIDTH)); levelBeam2Knob.setBounds(knobsArea.removeFromRight(KNOB_WIDTH)); - auto meterArea = knobsArea.removeFromLeft(BEAM_LED_WIDTH+BEAM_LEFT_RIGHT_MARGIN); + auto meterArea = knobsArea.removeFromLeft(BEAM_LED_WIDTH + BEAM_LEFT_RIGHT_MARGIN); meterArea.removeFromTop(BEAM_TOP_BOTTOM_MARGIN); meterArea.removeFromBottom(BEAM_TOP_BOTTOM_MARGIN); meterArea.removeFromLeft(BEAM_LEFT_RIGHT_MARGIN); beam1Meter.setBounds(meterArea.removeFromLeft(BEAM_LED_WIDTH)); - meterArea = knobsArea.removeFromRight(BEAM_LED_WIDTH+BEAM_LEFT_RIGHT_MARGIN); + meterArea = knobsArea.removeFromRight(BEAM_LED_WIDTH + BEAM_LEFT_RIGHT_MARGIN); meterArea.removeFromTop(BEAM_TOP_BOTTOM_MARGIN); meterArea.removeFromBottom(BEAM_TOP_BOTTOM_MARGIN); meterArea.removeFromRight(BEAM_LEFT_RIGHT_MARGIN); beam2Meter.setBounds(meterArea.removeFromRight(BEAM_LED_WIDTH)); levelLabel.setBounds(knobsArea); - auto mutesArea = area.removeFromTop(MUTE_HEIGHT+MUTE_TOP_MARGIN); + auto mutesArea = area.removeFromTop(MUTE_HEIGHT + MUTE_TOP_MARGIN); mutesArea.removeFromTop(MUTE_TOP_MARGIN); mutesArea.removeFromLeft(MUTE_LEFT_RIGHT_MARGIN); mutesArea.removeFromRight(MUTE_LEFT_RIGHT_MARGIN); @@ -283,25 +289,25 @@ void JucebeamAudioProcessorEditor::resized() area.removeFromTop(INPUT_SECTION_TOP_MARGIN); hpfSlider.setBounds(area.removeFromTop(INPUT_HPF_SLIDER_HEIGHT).withTrimmedLeft(INPUT_HPF_LABEL_WIDTH)); - + auto inputLedArea = area.removeFromTop(INPUT_LED_HEIGHT); inputLedArea.removeFromLeft(INPUT_LEFT_RIGHT_MARGIN); inputLedArea.removeFromRight(INPUT_LEFT_RIGHT_MARGIN); inputMeter.setBounds(inputLedArea); - + gainSlider.setBounds(area.removeFromTop(INPUT_GAIN_SLIDER_HEIGHT).withTrimmedLeft(INPUT_GAIN_LABEL_WIDTH)); - + //=============================================================== /** Prepare area for the performance monitor */ auto performanceMonitorArea = area.removeFromTop(PREFORMANCE_MONITOR_HEIGHT); - + /** Set area for CPU Load */ cpuLoad.setBounds(performanceMonitorArea.removeFromLeft(CPULOAD_WIDTH)); /** Set area for front toggle */ performanceMonitorArea.removeFromLeft(FRONT_TOGGLE_LABEL_WIDTH); frontToggle.setBounds(performanceMonitorArea.removeFromLeft(FRONT_TOGGLE_WIDTH)); - + /** Set area for config combo */ performanceMonitorArea.removeFromLeft(CONFIG_COMBO_LABEL_WIDTH); configCombo.setBounds(performanceMonitorArea.removeFromLeft(CONFIG_COMBO_WIDTH)); diff --git a/Source/PluginEditor.h b/Source/PluginEditor.h index 986fc3b..795f2e0 100644 --- a/Source/PluginEditor.h +++ b/Source/PluginEditor.h @@ -1,81 +1,45 @@ +/* + eBeamer Plugin Processor GUI + + Authors: + Luca Bondi (luca.bondi@polimi.it) +*/ + #pragma once #include "../JuceLibraryCode/JuceHeader.h" -#include "SceneComponent.h" -#include "AudioComponents.h" +#include "ebeamerDefs.h" +#include "SceneComp.h" +#include "MeterComp.h" #include "CpuLoadComp.h" #include "MidiComp.h" -#define GUI_WIDTH 540 - -#define LABEL_BEAM_WIDTH 25 -#define STEER_SLIDER_HEIGHT 40 -#define STEER_SLIDER_TOP_MARGIN 10 -#define KNOB_WIDTH 150 -#define KNOB_HEIGHT 80 -#define KNOB_TOP_MARGIN 8 -#define MUTE_HEIGHT 40 -#define MUTE_WIDTH 40 -#define MUTE_LEFT_RIGHT_MARGIN 20 -#define MUTE_TOP_MARGIN 8 -#define LABEL_WIDTH 70 -#define LABEL_HEIGHT 20 -#define LEFT_RIGHT_MARGIN 20 -#define TOP_BOTTOM_MARGIN 20 -#define KNOBS_LEFT_RIGHT_MARGIN 20 -#define BEAM_LED_WIDTH 5 -#define BEAM_TOP_BOTTOM_MARGIN 10 -#define BEAM_LEFT_RIGHT_MARGIN 10 - -#define INPUT_SECTION_TOP_MARGIN 20 -#define INPUT_HPF_SLIDER_HEIGHT 40 -#define INPUT_HPF_LABEL_WIDTH 50 -#define INPUT_LEFT_RIGHT_MARGIN 50 -#define INPUT_LED_HEIGHT 5 -#define INPUT_GAIN_SLIDER_HEIGHT 40 -#define INPUT_GAIN_LABEL_WIDTH 50 - -#define PREFORMANCE_MONITOR_HEIGHT 20 -#define CPULOAD_WIDTH 80 -#define CPULOAD_UPDATE_FREQ 10 //Hz - -#define FRONT_TOGGLE_LABEL_WIDTH 40 -#define FRONT_TOGGLE_WIDTH 25 - -#define CONFIG_COMBO_LABEL_WIDTH 65 -#define CONFIG_COMBO_WIDTH 80 - -#define INPUT_METER_UPDATE_FREQ 10 //Hz -#define BEAM_METER_UPDATE_FREQ 10 //Hz -#define ENERGY_UPDATE_FREQ 10 //Hz - - //============================================================================== -class JucebeamAudioProcessorEditor : public AudioProcessorEditor -{ +class JucebeamAudioProcessorEditor : public AudioProcessorEditor { public: - + typedef AudioProcessorValueTreeState::SliderAttachment SliderAttachment; typedef AudioProcessorValueTreeState::ButtonAttachment ButtonAttachment; typedef AudioProcessorValueTreeState::ComboBoxAttachment ComboBoxAttachment; - JucebeamAudioProcessorEditor (EbeamerAudioProcessor&,AudioProcessorValueTreeState& v); + JucebeamAudioProcessorEditor(EbeamerAudioProcessor &, AudioProcessorValueTreeState &v); + ~JucebeamAudioProcessorEditor(); - //============================================================================== - void paint (Graphics&) override; + void paint(Graphics &) override; + void resized() override; private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JucebeamAudioProcessorEditor); - - EbeamerAudioProcessor& processor; - AudioProcessorValueTreeState& valueTreeState; + + EbeamerAudioProcessor &processor; + AudioProcessorValueTreeState &valueTreeState; //============================================================================== - SceneComponent scene; - + SceneComp scene; + //============================================================================== Label steerLabel; Label steeringBeam1Label, steeringBeam2Label; @@ -86,7 +50,7 @@ class JucebeamAudioProcessorEditor : public AudioProcessorEditor Label widthLabel; SliderCC widthBeam1Knob, widthBeam2Knob; std::unique_ptr widthBeam1KnobAttachment, widthBeam2KnobAttachment; - + //============================================================================== Label panLabel; PanSlider panBeam1Knob, panBeam2Knob; @@ -101,39 +65,39 @@ class JucebeamAudioProcessorEditor : public AudioProcessorEditor Label muteLabel; MuteButton muteBeam1Button, muteBeam2Button; std::unique_ptr beam1MuteButtonAttachment, beam2MuteButtonAttachment; - + //============================================================================== MultiChannelLedBar inputMeter; SingleChannelLedBar beam1Meter, beam2Meter; - + //============================================================================== Label hpfLabel; FrequencySlider hpfSlider; std::unique_ptr hpfSliderAttachment; - + //============================================================================== Label gainLabel; DecibelSlider gainSlider; std::unique_ptr gainSliderAttachment; - + //============================================================================== /** CPU load component */ CpuLoadComp cpuLoad; - + //============================================================================== /** Swap side toggle component */ Label frontToggleLabel; ToggleButtonCC frontToggle; std::unique_ptr frontToggleAttachment; - + //============================================================================== /** Configuration selection combo */ Label configComboLabel; ComboBox configCombo; std::unique_ptr configComboLabelAttachment; - + //============================================================================== - const std::vector beamColours = {Colours::orangered,Colours::royalblue}; + const std::vector beamColours = {Colours::orangered, Colours::royalblue}; }; diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index 6d0154c..bb5dc3e 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -1,135 +1,137 @@ +/* + eBeamer Plugin Processor + + Authors: + Luca Bondi (luca.bondi@polimi.it) +*/ + #include "PluginProcessor.h" #include "PluginEditor.h" -/* Allocate static members to use them in the constructor */ -const int EbeamerAudioProcessor::numBeams; -const int EbeamerAudioProcessor::numDoas; - //============================================================================== // Helper functions AudioProcessorValueTreeState::ParameterLayout initializeParameters() { - + std::vector> params; - + // Values in dB params.push_back(std::make_unique("config", //tag "Configuration", //name micConfigLabels, //choices 0 //default - )); - + )); + params.push_back(std::make_unique("frontFacing", //tag "Front facing", //name false //default - )); - + )); + params.push_back(std::make_unique("gainMic", //tag "Mic gain", //name 0.0f, //min 40.0f, //max 20.0f //default - )); - + )); + // Values in Hz params.push_back(std::make_unique("hpf", //tag "HPF", 20.0f, //min 500.0f, //max 250.0f //default - )); - + )); + { - for (auto beamIdx = 0; beamIdx < EbeamerAudioProcessor::numBeams; ++beamIdx){ - auto defaultDirection = beamIdx == 0 ? -0.5 : 0.5; - params.push_back(std::make_unique("steerBeam"+String(beamIdx+1), //tag - "Steering beam"+String(beamIdx+1), //name + for (auto beamIdx = 0; beamIdx < NUM_BEAMS; ++beamIdx) { + auto defaultDirection = beamIdx == 0 ? -0.5 : 0.5; + params.push_back(std::make_unique("steerBeam" + String(beamIdx + 1), //tag + "Steering beam" + String(beamIdx + 1), //name -1.0f, //min 1.0f, //max defaultDirection //default - )); - params.push_back(std::make_unique("widthBeam"+String(beamIdx+1), //tag - "Width beam"+String(beamIdx+1), //name + )); + params.push_back(std::make_unique("widthBeam" + String(beamIdx + 1), //tag + "Width beam" + String(beamIdx + 1), //name 0.0f, //min 1.0f,//max 0.3f//default - )); - auto defaultPan = beamIdx == 0 ? -0.5 : 0.5; - params.push_back(std::make_unique("panBeam"+String(beamIdx+1), //tag - "Pan beam"+String(beamIdx+1), //name + )); + auto defaultPan = beamIdx == 0 ? -0.5 : 0.5; + params.push_back(std::make_unique("panBeam" + String(beamIdx + 1), //tag + "Pan beam" + String(beamIdx + 1), //name -1.0f, //min 1.0f, //max defaultPan //default - )); - params.push_back(std::make_unique("levelBeam"+String(beamIdx+1), //tag - "Level beam"+String(beamIdx+1), //name + )); + params.push_back(std::make_unique("levelBeam" + String(beamIdx + 1), //tag + "Level beam" + String(beamIdx + 1), //name -10.0f, //min 10.0f, //max 0.0f //default - )); - - params.push_back(std::make_unique("muteBeam"+String(beamIdx+1), //tag - "Mute beam"+String(beamIdx+1), //name + )); + + params.push_back(std::make_unique("muteBeam" + String(beamIdx + 1), //tag + "Mute beam" + String(beamIdx + 1), //name false //default - )); - + )); + } } - - return { params.begin(), params.end() }; + + return {params.begin(), params.end()}; } //============================================================================== EbeamerAudioProcessor::EbeamerAudioProcessor() -: AudioProcessor (BusesProperties() //The default bus layout accommodates for 4 buses of 16 channels each. - .withInput ("eStick#1", AudioChannelSet::ambisonic(3), true) - .withInput ("eStick#2", AudioChannelSet::ambisonic(3), true) - .withInput ("eStick#3", AudioChannelSet::ambisonic(3), true) - .withInput ("eStick#4", AudioChannelSet::ambisonic(3), true) - .withOutput ("Output", AudioChannelSet::stereo(), true) - ),parameters(*this,nullptr,Identifier("eBeamerParams"),initializeParameters()) -{ - + : AudioProcessor(BusesProperties() //The default bus layout accommodates for 4 buses of 16 channels each. + .withInput("eStick#1", AudioChannelSet::ambisonic(3), true) + .withInput("eStick#2", AudioChannelSet::ambisonic(3), true) + .withInput("eStick#3", AudioChannelSet::ambisonic(3), true) + .withInput("eStick#4", AudioChannelSet::ambisonic(3), true) + .withOutput("Output", AudioChannelSet::stereo(), true) +), parameters(*this, nullptr, Identifier("eBeamerParams"), initializeParameters()) { + /** Get parameters pointers */ configParam = parameters.getRawParameterValue("config"); parameters.addParameterListener("config", this); frontFacingParam = parameters.getRawParameterValue("frontFacing"); hpfFreqParam = parameters.getRawParameterValue("hpf"); micGainParam = parameters.getRawParameterValue("gainMic"); - - for (auto beamIdx=0;beamIdx(numBeams,numDoas); - beamformer->setMicConfig(static_cast((int)*configParam)); + beamformer = std::make_unique(NUM_BEAMS, NUM_DOAS); + beamformer->setMicConfig(static_cast((int) *configParam)); } //============================================================================== -bool EbeamerAudioProcessor::isBusesLayoutSupported (const BusesLayout& layouts) const{ +bool EbeamerAudioProcessor::isBusesLayoutSupported(const BusesLayout &layouts) const { // This plug-in supports up to 4 eSticks, for a total amount of 64 channels in input. // VST3 allows for a maximum of 25 channels per bus. // To make things simpler in terms of patching, each input bus counts for at most 16 channels. // This configuration allows REAPER to be configured with a 64 channels track. - for (auto bus : layouts.inputBuses){ - if ( bus.size() > 16 ){ + for (auto bus : layouts.inputBuses) { + if (bus.size() > 16) { return false; } } - for (auto bus : layouts.outputBuses){ - if ( bus.size() > 16 ){ + for (auto bus : layouts.outputBuses) { + if (bus.size() > 16) { // We have to allow the output bus to grow to the size of the input bus for compatibility with REAPER return false; } } - if ((layouts.getMainInputChannels() < 2) or (layouts.getMainOutputChannels() < 2)){ + if ((layouts.getMainInputChannels() < 2) or (layouts.getMainOutputChannels() < 2)) { // In any case don't allow less than 2 input and 2 output channels return false; } @@ -137,77 +139,74 @@ bool EbeamerAudioProcessor::isBusesLayoutSupported (const BusesLayout& layouts) } //============================================================================== -void EbeamerAudioProcessor::prepareToPlay (double sampleRate_, int maximumExpectedSamplesPerBlock_) -{ - +void EbeamerAudioProcessor::prepareToPlay(double sampleRate_, int maximumExpectedSamplesPerBlock_) { + GenericScopedLock lock(processingLock); - + sampleRate = sampleRate_; maximumExpectedSamplesPerBlock = maximumExpectedSamplesPerBlock_; - + /** Number of active input channels */ numActiveInputChannels = getTotalNumInputChannels(); - + /** Number of active output channels */ - numActiveOutputChannels = jmin(numBeams,getTotalNumOutputChannels()); - + numActiveOutputChannels = jmin(NUM_BEAMS, getTotalNumOutputChannels()); + /** Initialize the input gain */ micGain.reset(); - micGain.prepare({sampleRate, static_cast(maximumExpectedSamplesPerBlock),numActiveInputChannels}); + micGain.prepare({sampleRate, static_cast(maximumExpectedSamplesPerBlock), numActiveInputChannels}); micGain.setGainDecibels(*micGainParam); micGain.setRampDurationSeconds(gainTimeConst); - + /** Initialize the High Pass Filters */ iirHPFfilters.clear(); iirHPFfilters.resize(numActiveInputChannels); prevHpfFreq = 0; - + /** Initialize the beamformer */ beamformer->prepareToPlay(sampleRate, maximumExpectedSamplesPerBlock); - + /** Initialize beams' buffer */ - beamBuffer.setSize(numBeams, maximumExpectedSamplesPerBlock); - + beamBuffer.setSize(NUM_BEAMS, maximumExpectedSamplesPerBlock); + /** Initialize beam level gains */ - for (auto beamIdx = 0; beamIdx < numBeams; ++beamIdx){ + for (auto beamIdx = 0; beamIdx < NUM_BEAMS; ++beamIdx) { beamGain[beamIdx].reset(); - beamGain[beamIdx].prepare({sampleRate, static_cast(maximumExpectedSamplesPerBlock),1}); + beamGain[beamIdx].prepare({sampleRate, static_cast(maximumExpectedSamplesPerBlock), 1}); beamGain[beamIdx].setGainDecibels(*levelBeamParam[beamIdx]); beamGain[beamIdx].setRampDurationSeconds(gainTimeConst); } - + /** initialize meters */ - inputMeterDecay = std::make_unique(sampleRate,metersDecay,maximumExpectedSamplesPerBlock,numActiveInputChannels); - inputMeters.resize(numActiveInputChannels); - beamMeterDecay = std::make_unique(sampleRate,metersDecay,maximumExpectedSamplesPerBlock,numBeams); - beamMeters.resize(numBeams); - + inputMeterDecay = std::make_unique(sampleRate, metersDecay, maximumExpectedSamplesPerBlock, + numActiveInputChannels); + beamMeterDecay = std::make_unique(sampleRate, metersDecay, maximumExpectedSamplesPerBlock, NUM_BEAMS); + resourcesAllocated = true; - + /** Time constants */ - loadAlpha = 1-exp(-(maximumExpectedSamplesPerBlock/sampleRate)/loadTimeConst); - + loadAlpha = 1 - exp(-(maximumExpectedSamplesPerBlock / sampleRate) / loadTimeConst); + } -void EbeamerAudioProcessor::releaseResources() -{ - +void EbeamerAudioProcessor::releaseResources() { + GenericScopedLock lock(processingLock); - + resourcesAllocated = false; - + /** Clear beam buffer */ - beamBuffer.setSize(numBeams, 0); - + beamBuffer.setSize(NUM_BEAMS, 0); + /** Clear the HPF */ iirHPFfilters.clear(); - + /** Clear the Beamformer */ beamformer->releaseResources(); } -bool EbeamerAudioProcessor::insertCCParamMapping(const MidiCC& cc, const String& param){ - if (paramToCcMap.count(param) > 0 || ccToParamMap.count(cc) > 0){ +bool EbeamerAudioProcessor::insertCCParamMapping(const MidiCC &cc, const String ¶m) { + if (paramToCcMap.count(param) > 0 || ccToParamMap.count(cc) > 0) { return false; } ccToParamMap[cc] = param; @@ -215,235 +214,248 @@ bool EbeamerAudioProcessor::insertCCParamMapping(const MidiCC& cc, const String& return true; } -void EbeamerAudioProcessor::removeCCParamMapping(const String& param){ - if (paramToCcMap.count(param) > 0){ +void EbeamerAudioProcessor::removeCCParamMapping(const String ¶m) { + if (paramToCcMap.count(param) > 0) { auto cc = paramToCcMap[param]; paramToCcMap.erase(param); ccToParamMap.erase(cc); } } -void EbeamerAudioProcessor::processCC(const MidiCC& cc, int value){ - +void EbeamerAudioProcessor::processCC(const MidiCC &cc, int value) { + const String paramTag = ccToParamMap[cc]; Value val = parameters.getParameterAsValue(paramTag); auto range = parameters.getParameterRange(paramTag); const bool isButton = range.interval == 1 && range.start == 0 && range.end == 1; - if (isButton){ - if (value==127){ - val.setValue(!((bool)val.getValue())); + if (isButton) { + if (value == 127) { + val.setValue(!((bool) val.getValue())); } - }else{ - val.setValue(range.convertFrom0to1(value/127.)); + } else { + val.setValue(range.convertFrom0to1(value / 127.)); } - + } -void EbeamerAudioProcessor::startCCLearning(const String& p){ +void EbeamerAudioProcessor::startCCLearning(const String &p) { paramCCToLearn = p; } -void EbeamerAudioProcessor::stopCCLearning(){ + +void EbeamerAudioProcessor::stopCCLearning() { paramCCToLearn = ""; } -String EbeamerAudioProcessor::getCCLearning() const{ +String EbeamerAudioProcessor::getCCLearning() const { return paramCCToLearn; } -const std::map& EbeamerAudioProcessor::getParamToCCMapping(){ +const std::map &EbeamerAudioProcessor::getParamToCCMapping() { return paramToCcMap; } -void EbeamerAudioProcessor::processMidi(MidiBuffer& midiMessages){ - +void EbeamerAudioProcessor::processMidi(MidiBuffer &midiMessages) { + // Loop over CC messages - MidiBuffer::Iterator midiIter (midiMessages); + MidiBuffer::Iterator midiIter(midiMessages); MidiMessage midiMess; int samplePosition; - while (midiIter.getNextEvent(midiMess, samplePosition)){ - if (midiMess.isController()){ - - MidiCC cc = {midiMess.getChannel(),midiMess.getControllerNumber()}; - if (ccToParamMap.count(cc) > 0){ + while (midiIter.getNextEvent(midiMess, samplePosition)) { + if (midiMess.isController()) { + + MidiCC cc = {midiMess.getChannel(), midiMess.getControllerNumber()}; + if (ccToParamMap.count(cc) > 0) { /** Process the CC message if mapped */ - processCC(cc,midiMess.getControllerValue()); - }else if (paramCCToLearn.length() > 0){ + processCC(cc, midiMess.getControllerValue()); + } else if (paramCCToLearn.length() > 0) { /** Remove then add the CC parameter */ removeCCParamMapping(paramCCToLearn); insertCCParamMapping(cc, paramCCToLearn); } } } - + /** Clear all messages */ midiMessages.clear(); - + } -void EbeamerAudioProcessor::processBlock (AudioBuffer& buffer, MidiBuffer& midiMessages) -{ - +void EbeamerAudioProcessor::processBlock(AudioBuffer &buffer, MidiBuffer &midiMessages) { + const auto startTick = Time::getHighResolutionTicks(); - + GenericScopedLock lock(processingLock); - + processMidi(midiMessages); - + /** If resources are not allocated this is an out-of-order request */ - if (!resourcesAllocated){ + if (!resourcesAllocated) { jassertfalse; return; } - + ScopedNoDenormals noDenormals; - + /**Apply input gain directly on input buffer */ micGain.setGainDecibels(*micGainParam); { auto block = juce::dsp::AudioBlock(buffer).getSubsetChannelBlock(0, numActiveInputChannels); - auto context = juce::dsp::ProcessContextReplacing (block); + auto context = juce::dsp::ProcessContextReplacing(block); micGain.process(context); } - + // Mic meter inputMeterDecay->push(buffer); - { - GenericScopedLock lock(inputMetersLock); - inputMeters = inputMeterDecay->get(); - } - + /** Renew IIR coefficient if cut frequency changed */ - if(prevHpfFreq != (bool)*hpfFreqParam){ + if (prevHpfFreq != (bool) *hpfFreqParam) { iirCoeffHPF = IIRCoefficients::makeHighPass(sampleRate, *hpfFreqParam); prevHpfFreq = *hpfFreqParam; - for (auto& iirHPFfilter : iirHPFfilters){ + for (auto &iirHPFfilter : iirHPFfilters) { iirHPFfilter.setCoefficients(iirCoeffHPF); } } - + /**Apply HPF directly on input buffer */ - for (auto inChannel = 0; inChannel < numActiveInputChannels; ++inChannel){ + for (auto inChannel = 0; inChannel < numActiveInputChannels; ++inChannel) { iirHPFfilters[inChannel].processSamples(buffer.getWritePointer(inChannel), buffer.getNumSamples()); } - + /** Set beams parameters */ - for (auto beamIdx = 0;beamIdx< numBeams; beamIdx++){ + for (auto beamIdx = 0; beamIdx < NUM_BEAMS; beamIdx++) { float beamDoa = *steeringBeamParam[beamIdx]; beamDoa = *frontFacingParam ? -beamDoa : beamDoa; - BeamParameters beamParams = {beamDoa,*widthBeamParam[beamIdx]}; + BeamParameters beamParams = {beamDoa, *widthBeamParam[beamIdx]}; beamformer->setBeamParameters(beamIdx, beamParams); } - + /** Call the beamformer */ beamformer->processBlock(buffer); - + /** Retrieve beamformer outputs */ beamformer->getBeams(beamBuffer); - + /** Apply beams mute and volume */ - for (auto beamIdx = 0; beamIdx < numBeams; ++beamIdx){ - if ((bool)*muteBeamParam[beamIdx] == false){ + for (auto beamIdx = 0; beamIdx < NUM_BEAMS; ++beamIdx) { + if ((bool) *muteBeamParam[beamIdx] == false) { beamGain[beamIdx].setGainDecibels(*levelBeamParam[beamIdx]); - }else{ + } else { beamGain[beamIdx].setGainLinear(0); } - auto block = dsp::AudioBlock(beamBuffer).getSubsetChannelBlock(beamIdx, 1).getSubBlock(0, buffer.getNumSamples()); - auto contextToUse = dsp::ProcessContextReplacing (block); + auto block = dsp::AudioBlock(beamBuffer).getSubsetChannelBlock(beamIdx, 1).getSubBlock(0, + buffer.getNumSamples()); + auto contextToUse = dsp::ProcessContextReplacing(block); beamGain[beamIdx].process(contextToUse); } - + /** Measure beam output volume */ beamMeterDecay->push(beamBuffer); - { - GenericScopedLock lock(beamMetersLock); - beamMeters = beamMeterDecay->get(); - } - + /** Clear buffer */ buffer.clear(); - + /** Sum beams in output channels */ - for (int outChannel = 0; outChannel < numActiveOutputChannels; ++outChannel){ + for (int outChannel = 0; outChannel < numActiveOutputChannels; ++outChannel) { /** Sum the contributes from each beam */ - for (int beamIdx = 0; beamIdx < numBeams; ++beamIdx){ - auto channelBeamGain = panToLinearGain((float)*panBeamParam[beamIdx],outChannel==0); + for (int beamIdx = 0; beamIdx < NUM_BEAMS; ++beamIdx) { + auto channelBeamGain = panToLinearGain((float) *panBeamParam[beamIdx], outChannel == 0); buffer.addFrom(outChannel, 0, beamBuffer, beamIdx, 0, buffer.getNumSamples(), channelBeamGain); } } - + /** Update load */ { const float elapsedTime = Time::highResolutionTicksToSeconds(Time::getHighResolutionTicks() - startTick); - const float curLoad = elapsedTime / (maximumExpectedSamplesPerBlock/sampleRate); + const float curLoad = elapsedTime / (maximumExpectedSamplesPerBlock / sampleRate); GenericScopedLock lock(loadLock); - load = (load*(1-loadAlpha))+(curLoad*loadAlpha); + load = (load * (1 - loadAlpha)) + (curLoad * loadAlpha); } - + } -const AudioProcessorValueTreeState& EbeamerAudioProcessor::getParams() const{ - return parameters; +//============================================================================== +// Meters +void EbeamerAudioProcessor::getMeterValues(std::vector &meter, int meterId) const { + switch (meterId) { + case 0: + inputMeterDecay->get(meter); + break; + case 1: + beamMeterDecay->get(meter); + break; + } } +float EbeamerAudioProcessor::getMeterValue(int meterId, int channel) const { + switch (meterId) { + case 0: + return inputMeterDecay->get(channel); + case 1: + return beamMeterDecay->get(channel); + default: + return 0; + } +} +//============================================================================== -void EbeamerAudioProcessor::parameterChanged (const String ¶meterID, float newValue){ - if (parameterID == "config"){ - setMicConfig(static_cast((int)(newValue))); +void EbeamerAudioProcessor::parameterChanged(const String ¶meterID, float newValue) { + if (parameterID == "config") { + setMicConfig(static_cast((int) (newValue))); } } //============================================================================== -void EbeamerAudioProcessor::setMicConfig(const MicConfig& mc){ +void EbeamerAudioProcessor::setMicConfig(const MicConfig &mc) { beamformer->setMicConfig(mc); prepareToPlay(sampleRate, maximumExpectedSamplesPerBlock); } //============================================================================== -float EbeamerAudioProcessor::getAverageLoad() const{ +float EbeamerAudioProcessor::getCpuLoad() const { GenericScopedLock lock(loadLock); return load; } //============================================================================== -void EbeamerAudioProcessor::getStateInformation (MemoryBlock& destData){ +void EbeamerAudioProcessor::getStateInformation(MemoryBlock &destData) { /** Root XML */ - std::unique_ptr xml(new XmlElement ("eBeamerRoot")); - + std::unique_ptr xml(new XmlElement("eBeamerRoot")); + /** Parameters state */ auto state = parameters.copyState(); - XmlElement* xmlParams = new XmlElement(*state.createXml()); + XmlElement *xmlParams = new XmlElement(*state.createXml()); xml->addChildElement(xmlParams); - + /** Save Midi CC - Params Maping */ auto xmlMidi = xml->createNewChildElement("eBeamerMidiMap"); - for (auto m : paramToCcMap){ + for (auto m : paramToCcMap) { auto el = xmlMidi->createNewChildElement(m.first); el->setAttribute("channel", m.second.channel); el->setAttribute("number", m.second.number); } - copyXmlToBinary (*xml, destData); + copyXmlToBinary(*xml, destData); } -void EbeamerAudioProcessor::setStateInformation (const void* data, int sizeInBytes){ - - std::unique_ptr xmlState (getXmlFromBinary (data, sizeInBytes)); - - if (xmlState.get() != nullptr){ - if (xmlState->hasTagName("eBeamerRoot")){ - forEachXmlChildElement (*xmlState, rootElement){ - if (rootElement->hasTagName (parameters.state.getType())){ +void EbeamerAudioProcessor::setStateInformation(const void *data, int sizeInBytes) { + + std::unique_ptr xmlState(getXmlFromBinary(data, sizeInBytes)); + + if (xmlState.get() != nullptr) { + if (xmlState->hasTagName("eBeamerRoot")) { + forEachXmlChildElement (*xmlState, rootElement) { + if (rootElement->hasTagName(parameters.state.getType())) { /** Parameters state */ - parameters.replaceState (ValueTree::fromXml (*rootElement)); - }else if(rootElement->hasTagName ("eBeamerMidiMap")){ + parameters.replaceState(ValueTree::fromXml(*rootElement)); + } else if (rootElement->hasTagName("eBeamerMidiMap")) { /** Load Midi CC - Params Maping */ ccToParamMap.clear(); paramToCcMap.clear(); stopCCLearning(); - forEachXmlChildElement (*rootElement, e){ + forEachXmlChildElement (*rootElement, e) { String tag = e->getTagName(); int channel = e->getIntAttribute("channel"); int number = e->getIntAttribute("number"); - insertCCParamMapping({channel,number}, tag); + insertCCParamMapping({channel, number}, tag); } } } @@ -452,36 +464,37 @@ void EbeamerAudioProcessor::setStateInformation (const void* data, int sizeInByt } //============================================================================== -// Meters -float EbeamerAudioProcessor::getBeamMeter(int channel){ - GenericScopedLock lock(beamMetersLock); - return beamMeters[channel]; +const std::atomic *EbeamerAudioProcessor::getFrontFacingParam() const { + return parameters.getRawParameterValue("frontFacing"); } -std::vector EbeamerAudioProcessor::getInputMeters(){ - GenericScopedLock lock(inputMetersLock); - return inputMeters; +const std::atomic *EbeamerAudioProcessor::getBeamMute(int idx) const { + return parameters.getRawParameterValue("muteBeam" + String(idx + 1)); } -//============================================================================== -const std::unique_ptr& EbeamerAudioProcessor::getBeamformer() const{ - return beamformer; +const std::atomic *EbeamerAudioProcessor::getBeamWidth(int idx) const { + return parameters.getRawParameterValue("widthBeam" + String(idx + 1)); +} + +const std::atomic *EbeamerAudioProcessor::getBeamSteer(int idx) const { + return parameters.getRawParameterValue("steerBeam" + String(idx + 1)); +} + +void EbeamerAudioProcessor::getDoaEnergy(std::vector &energy) const { + beamformer->getDoaEnergy(energy); } //============================================================================== // Unchanged JUCE default functions -EbeamerAudioProcessor::~EbeamerAudioProcessor() -{ +EbeamerAudioProcessor::~EbeamerAudioProcessor() { } -const String EbeamerAudioProcessor::getName() const -{ +const String EbeamerAudioProcessor::getName() const { return JucePlugin_Name; } -bool EbeamerAudioProcessor::acceptsMidi() const -{ +bool EbeamerAudioProcessor::acceptsMidi() const { #if JucePlugin_WantsMidiInput return true; #else @@ -489,8 +502,7 @@ bool EbeamerAudioProcessor::acceptsMidi() const #endif } -bool EbeamerAudioProcessor::producesMidi() const -{ +bool EbeamerAudioProcessor::producesMidi() const { #if JucePlugin_ProducesMidiOutput return true; #else @@ -498,8 +510,7 @@ bool EbeamerAudioProcessor::producesMidi() const #endif } -bool EbeamerAudioProcessor::isMidiEffect() const -{ +bool EbeamerAudioProcessor::isMidiEffect() const { #if JucePlugin_IsMidiEffect return true; #else @@ -507,48 +518,40 @@ bool EbeamerAudioProcessor::isMidiEffect() const #endif } -double EbeamerAudioProcessor::getTailLengthSeconds() const -{ +double EbeamerAudioProcessor::getTailLengthSeconds() const { return 0.0; } -int EbeamerAudioProcessor::getNumPrograms() -{ +int EbeamerAudioProcessor::getNumPrograms() { return 1; // NB: some hosts don't cope very well if you tell them there are 0 programs, // so this should be at least 1, even if you're not really implementing programs. } -int EbeamerAudioProcessor::getCurrentProgram() -{ +int EbeamerAudioProcessor::getCurrentProgram() { return 0; } -void EbeamerAudioProcessor::setCurrentProgram (int index) -{ +void EbeamerAudioProcessor::setCurrentProgram(int index) { } -const String EbeamerAudioProcessor::getProgramName (int index) -{ +const String EbeamerAudioProcessor::getProgramName(int index) { return {}; } -void EbeamerAudioProcessor::changeProgramName (int index, const String& newName) -{ +void EbeamerAudioProcessor::changeProgramName(int index, const String &newName) { } //============================================================================== // This creates new instances of the plugin.. -AudioProcessor* JUCE_CALLTYPE createPluginFilter() -{ +AudioProcessor *JUCE_CALLTYPE createPluginFilter() { return new EbeamerAudioProcessor(); } //============================================================================== -AudioProcessorEditor* EbeamerAudioProcessor::createEditor() -{ - return new JucebeamAudioProcessorEditor (*this,parameters); +AudioProcessorEditor *EbeamerAudioProcessor::createEditor() { + return new JucebeamAudioProcessorEditor(*this, parameters); } -bool EbeamerAudioProcessor::hasEditor() const -{ + +bool EbeamerAudioProcessor::hasEditor() const { return true; } diff --git a/Source/PluginProcessor.h b/Source/PluginProcessor.h index f288f12..fd2a69c 100644 --- a/Source/PluginProcessor.h +++ b/Source/PluginProcessor.h @@ -1,119 +1,130 @@ +/* + eBeamer Plugin Processor + + Authors: + Luca Bondi (luca.bondi@polimi.it) +*/ + #pragma once #include "../JuceLibraryCode/JuceHeader.h" -#include "AudioParts.h" +#include "MidiCC.h" +#include "MeterDecay.h" #include "Beamformer.h" +#include "CpuLoadComp.h" +#include "SceneComp.h" //============================================================================== -/** Midi CC type */ -typedef struct{ - int channel; - int number; -} MidiCC; - -struct MidiCcCompare{ - bool operator() (const MidiCC& lhs, const MidiCC& rhs) const{ - if (lhs.channel == rhs.channel){ - return lhs.number < rhs.number; - } - return lhs.channel < rhs.channel; - } -}; - -//============================================================================== - - -class EbeamerAudioProcessor : public AudioProcessor, - public AudioProcessorValueTreeState::Listener -{ +class EbeamerAudioProcessor : + public AudioProcessor, + public AudioProcessorValueTreeState::Listener, + public MeterDecay::Callback, + public CpuLoadComp::Callback, + public SceneComp::Callback, + public MidiCC::Callback { public: + //============================================================================== - /** Number of beams */ - static const int numBeams = 2; - - /** Number of directions of arrival computed and displayed */ - static const int numDoas = 25; - - //============================================================================== + // JUCE plugin methods + EbeamerAudioProcessor(); + ~EbeamerAudioProcessor(); - + const String getName() const override; + bool acceptsMidi() const override; + bool producesMidi() const override; + bool isMidiEffect() const override; + double getTailLengthSeconds() const override; - - bool isBusesLayoutSupported (const BusesLayout& layouts) const override; - void prepareToPlay (double sampleRate, int maximumExpectedSamplesPerBlock) override; - void processBlock (AudioBuffer&, MidiBuffer&) override; + + bool isBusesLayoutSupported(const BusesLayout &layouts) const override; + + void prepareToPlay(double sampleRate, int maximumExpectedSamplesPerBlock) override; + + void processBlock(AudioBuffer &, MidiBuffer &) override; + void releaseResources() override; int getNumPrograms() override; + int getCurrentProgram() override; - void setCurrentProgram (int index) override; - const String getProgramName (int index) override; - void changeProgramName (int index, const String& newName) override; - //============================================================================== - AudioProcessorEditor* createEditor() override; + void setCurrentProgram(int index) override; + + const String getProgramName(int index) override; + + void changeProgramName(int index, const String &newName) override; + + AudioProcessorEditor *createEditor() override; + bool hasEditor() const override; + void getStateInformation(MemoryBlock &destData) override; + + void setStateInformation(const void *data, int sizeInBytes) override; + //============================================================================== - void getStateInformation (MemoryBlock& destData) override; - void setStateInformation (const void* data, int sizeInBytes) override; - - //============================================================================== - // Meters - float getBeamMeter(int channel); - std::vector getInputMeters(); - std::vector inputMeters; - std::vector beamMeters; - SpinLock inputMetersLock; - SpinLock beamMetersLock; - - //============================================================================== - // Beamformer - const std::unique_ptr& getBeamformer() const; - - //============================================================================== - /** Averagel load */ - float getAverageLoad() const; - + // MeterDecay Callback + void getMeterValues(std::vector &meter, int meterId) const override; + + float getMeterValue(int meterId, int channel) const override; + //============================================================================== - const AudioProcessorValueTreeState& getParams() const; - + // CpuLoadComp Callback + float getCpuLoad() const override; + //============================================================================== + // MidiCC Callback /** Start learning the specified parameter */ - void startCCLearning(const String& p); + void startCCLearning(const String &p) override; + /** Stop learning the previous parameter */ - void stopCCLearning(); + void stopCCLearning() override; + /** Get parameter being learned */ - String getCCLearning() const; + String getCCLearning() const override; + /** Get a read-only reference to the parameters to CC mapping */ - const std::map& getParamToCCMapping(); + const std::map &getParamToCCMapping() override; + /** Remove mapping between MidiCC and parameter */ - void removeCCParamMapping(const String& param); + void removeCCParamMapping(const String ¶m) override; + + //============================================================================== + //SceneComponent Callback + const std::atomic *getFrontFacingParam() const override; + + const std::atomic *getBeamMute(int idx) const override; + + const std::atomic *getBeamWidth(int idx) const override; + + const std::atomic *getBeamSteer(int idx) const override; + + void getDoaEnergy(std::vector &energy) const override; private: //============================================================================== JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (EbeamerAudioProcessor) - + //============================================================================== /** Number of active input channels */ juce::uint32 numActiveInputChannels = 0; /** Number of active output channels */ juce::uint32 numActiveOutputChannels = 0; - + //============================================================================== /** Time Constant for input gain variations */ const float gainTimeConst = 0.1; /** Input gain, common to all microphones */ dsp::Gain micGain; /** Beam gain for each beam */ - dsp::Gain beamGain[numBeams]; - + dsp::Gain beamGain[NUM_BEAMS]; + //============================================================================== /** Previous HPF cut frequency */ float prevHpfFreq = 0; @@ -121,7 +132,7 @@ class EbeamerAudioProcessor : public AudioProcessor, IIRCoefficients iirCoeffHPF; /** IIR HPF */ std::vector iirHPFfilters; - + //============================================================================== /** The active beamformer */ std::unique_ptr beamformer; @@ -130,36 +141,36 @@ class EbeamerAudioProcessor : public AudioProcessor, // Meters std::unique_ptr inputMeterDecay; std::unique_ptr beamMeterDecay; - + /** Decay of meters [s] */ const float metersDecay = 0.2; - + //============================================================================== // Beams buffers AudioBuffer beamBuffer; - + //============================================================================== /** Lock to prevent releaseResources being called when processBlock is running. AudioPluginHost does it. */ SpinLock processingLock; - + /** Resources for runtime are allocated. This flag is used to compensate for out-of-order calls to prepareToPlay, processBlock and releaseResources */ bool resourcesAllocated = false; - + /** Sample rate [Hz] */ float sampleRate = 48000; - + /** Maximum number of samples per block */ int maximumExpectedSamplesPerBlock = 4096; - + //============================================================================== /** Set a new microphone configuration */ - void setMicConfig(const MicConfig& mc); - + void setMicConfig(const MicConfig &mc); + //============================================================================== - + /** Measured average load */ float load = 0; /** Load time constant [s] */ @@ -168,45 +179,45 @@ class EbeamerAudioProcessor : public AudioProcessor, float loadAlpha = 1; /** Load lock */ SpinLock loadLock; - + //============================================================================== - + /** Processor parameters tree */ AudioProcessorValueTreeState parameters; - + //============================================================================== // VST parameters - std::atomic* steeringBeamParam[numBeams]; - std::atomic* widthBeamParam[numBeams]; - std::atomic* panBeamParam[numBeams]; - std::atomic* levelBeamParam[numBeams]; - std::atomic* muteBeamParam[numBeams]; - std::atomic* micGainParam; - std::atomic* hpfFreqParam; - std::atomic* frontFacingParam; - std::atomic* configParam; - - void parameterChanged (const String ¶meterID, float newValue) override; - + std::atomic *steeringBeamParam[NUM_BEAMS]; + std::atomic *widthBeamParam[NUM_BEAMS]; + std::atomic *panBeamParam[NUM_BEAMS]; + std::atomic *levelBeamParam[NUM_BEAMS]; + std::atomic *muteBeamParam[NUM_BEAMS]; + std::atomic *micGainParam; + std::atomic *hpfFreqParam; + std::atomic *frontFacingParam; + std::atomic *configParam; + + void parameterChanged(const String ¶meterID, float newValue) override; + //============================================================================== // MIDI management - - std::map ccToParamMap; - std::map paramToCcMap; - + + std::map ccToParamMap; + std::map paramToCcMap; + /** Process all the received MIDI messages */ - void processMidi(MidiBuffer& midiMessages); - + void processMidi(MidiBuffer &midiMessages); + /** Process a MIDI CC message and update parameter as needed */ - void processCC(const MidiCC& cc, int value); - + void processCC(const MidiCC &cc, int value); + /** Insert mapping between MidiCC and parameter @return: true if insertion successful, false if either cc or param already mapped */ - bool insertCCParamMapping(const MidiCC& cc, const String& param); - + bool insertCCParamMapping(const MidiCC &cc, const String ¶m); + /** Parameter whose CC is being learned */ String paramCCToLearn = ""; - + }; diff --git a/Source/SceneComp.cpp b/Source/SceneComp.cpp new file mode 100644 index 0000000..ce6a699 --- /dev/null +++ b/Source/SceneComp.cpp @@ -0,0 +1,250 @@ +/* + Acoustic scene components + + Authors: + Matteo Scerbo (matteo.scerbo@mail.polimi.it) + Luca Bondi (luca.bondi@polimi.it) +*/ + +#include "SceneComp.h" + +TileComp::TileComp() { + frontFacingTransf = AffineTransform::rotation(MathConstants::pi, SCENE_WIDTH / 2, SCENE_HEIGHT / 2); +} + +void TileComp::paint(Graphics &g) { + Path path; + + path.startNewSubPath(corners[0][0]); + path.lineTo(corners[1][0]); + path.lineTo(corners[1][1]); + path.lineTo(corners[0][1]); + path.closeSubPath(); + + if ((frontFacing != nullptr) && (bool) *frontFacing) { + //TODO: Move this to ComputeVertices in Grid Component + path.applyTransform(frontFacingTransf); + } + + g.setColour(tileColour); + g.fillPath(path); + + g.setColour(Colours::black); + PathStrokeType strokeType(0.5f); + g.strokePath(path, strokeType); +} + +void TileComp::setFrontFacingParam(const std::atomic *p) { + frontFacing = p; +} + +void TileComp::setColour(const Colour &col) { + tileColour = col; +} + +void TileComp::setCorners(const juce::Point &p1, + const juce::Point &p2, + const juce::Point &p3, + const juce::Point &p4) { + corners[0][0] = p1; + corners[1][0] = p2; + corners[0][1] = p3; + corners[1][1] = p4; +} +//============================================================================== +//============================================================================== + +GridComp::GridComp() { + for (int i = 0; i < TILE_ROW_COUNT; i++) + for (int j = 0; j < NUM_DOAS; j++) + addAndMakeVisible(tiles[i][j]); + + energy.resize(NUM_DOAS); + energyPreGain.resize(NUM_DOAS, -30); + + + // Compute led tresholds + const float ledStep = 3; //dB + + th.clear(); + for (auto ledIdx = TILE_ROW_COUNT - 1; ledIdx >= 0; --ledIdx) { + auto ledThDb = ledIdx == (TILE_ROW_COUNT - 1) ? RED_LT : -((TILE_ROW_COUNT - 1 - ledIdx) * ledStep); + th.push_back(ledThDb); + } + + startTimerHz(gridUpdateFrequency); + +} + +void GridComp::setCallback(const Callback *c) { + callback = c; +} + +void GridComp::setParams(const std::atomic *frontFacing) { + for (int i = 0; i < TILE_ROW_COUNT; i++) { + for (int j = 0; j < NUM_DOAS; j++) { + tiles[i][j].setFrontFacingParam(frontFacing); + } + } +} + +void GridComp::resized() { + computeVertices(); + + for (int i = 0; i < TILE_ROW_COUNT; i++) { + for (int j = 0; j < NUM_DOAS; j++) { + + tiles[i][j].setCorners(vertices[i][j], vertices[i + 1][j], vertices[i][j + 1], vertices[i + 1][j + 1]); + + tiles[i][j].setBounds(getLocalBounds()); + + if (i < TILE_ROW_COUNT / 4) + tiles[i][j].setColour(Colours::red.darker(0.9)); + + if (TILE_ROW_COUNT / 4 <= i && i < TILE_ROW_COUNT / 2) + tiles[i][j].setColour(Colours::yellow.darker(0.9)); + + if (i >= TILE_ROW_COUNT / 2) + tiles[i][j].setColour(Colours::green.darker(0.9)); + + } + } +} + +void GridComp::timerCallback() { + + std::vector newEnergy(NUM_DOAS); + callback->getDoaEnergy(newEnergy); + + for (auto dirIdx = 0; dirIdx < energyPreGain.size(); ++dirIdx) { + energyPreGain[dirIdx] = ((1 - inertia) * (newEnergy[dirIdx])) + (inertia * energyPreGain[dirIdx]); + } + + // Very basic automatic gain + auto rangeEnergy = FloatVectorOperations::findMinAndMax(energyPreGain.data(), (int) energyPreGain.size()); + auto maxLevel = rangeEnergy.getEnd() + gain; + + if (maxLevel > 0) { + gain = jmax(gain - maxLevel - 3, minGain); + } else if (maxLevel < -18) { + gain = jmin(gain - maxLevel, maxGain); + } else if (maxLevel > -3) { + gain = jmax(gain - 0.5f, minGain); + } else if (maxLevel < -9) { + gain = jmin(gain + 0.5f, maxGain); + } + + for (auto dirIdx = 0; dirIdx < energyPreGain.size(); ++dirIdx) { + energy[dirIdx] = energyPreGain[dirIdx] + gain; + } + + for (int j = 0; j < NUM_DOAS; j++) { + for (int i = 0; i < TILE_ROW_COUNT; i++) { + tiles[i][j].setColour(SingleChannelLedBar::thToColour(th[i], energy[j] > th[i])); + } + } + + repaint(); + +} + +void GridComp::computeVertices() { + const float w = SCENE_WIDTH; + const float h = SCENE_HEIGHT; + + float angle_diff = MathConstants::pi / NUM_DOAS; + + for (int i = 0; i <= TILE_ROW_COUNT; i++) { + + const float radius = h - h * (exp((float) i / TILE_ROW_COUNT) - 1) / (exp(1) - 1); + + for (int j = 0; j <= NUM_DOAS; j++) { + const float angle = j * angle_diff; + + vertices[i][j].setX(w / 2 - radius * cos(angle)); + vertices[i][j].setY(h - radius * sin(angle)); + + } + } +} + +//============================================================================== +//============================================================================== + +void BeamComp::setParams(const std::atomic *frontFacing, + const std::atomic *mute, + const std::atomic *width, + const std::atomic *steer) { + muteParam = mute; + widthParam = width; + steerParam = steer; + frontFacingParam = frontFacing; +} + +void BeamComp::paint(Graphics &g) { + + const float width = (0.1 + 2.9 * (*widthParam)) * SCENE_WIDTH / 10; + const float position = *steerParam; + + Path path; + path.startNewSubPath(0, 0); + path.cubicTo(width, -SCENE_WIDTH / 3, width, -SCENE_WIDTH / 2, 0, -SCENE_WIDTH / 2); + path.cubicTo(-width, -SCENE_WIDTH / 2, -width, -SCENE_WIDTH / 3, 0, 0); + path.closeSubPath(); + + path.applyTransform(AffineTransform::rotation((MathConstants::pi / 2) * position)); + path.applyTransform(AffineTransform::translation(SCENE_WIDTH / 2, SCENE_WIDTH / 2)); + + if ((bool) *frontFacingParam) { + path.applyTransform(AffineTransform::verticalFlip(SCENE_HEIGHT)); + } + + if (~(bool) *muteParam) { + g.setColour(baseColour.brighter()); + g.setOpacity(0.4); + g.fillPath(path); + } + + g.setColour(baseColour); + g.setOpacity(0.8); + PathStrokeType strokeType(2); + g.strokePath(path, strokeType); +} + +//============================================================================== +//============================================================================== + +SceneComp::SceneComp() { + addAndMakeVisible(grid); + for (int i = 0; i < NUM_BEAMS; i++) + addAndMakeVisible(beams[i]); +} + +void SceneComp::setCallback(const Callback *c) { + grid.setCallback(c); + grid.setParams(c->getFrontFacingParam()); + + for (auto idx = 0; idx < NUM_BEAMS; idx++) { + beams[idx].setParams(c->getFrontFacingParam(), c->getBeamMute(idx), c->getBeamWidth(idx), c->getBeamSteer(idx)); + } +} + +void SceneComp::paint(Graphics &g) { + g.fillAll(getLookAndFeel().findColour(ResizableWindow::backgroundColourId)); +} + +void SceneComp::resized() { + grid.setBounds(getLocalBounds()); + for (int i = 0; i < NUM_BEAMS; i++) + beams[i].setBounds(getLocalBounds()); +} + +void SceneComp::setBeamColors(const std::vector &colours) { + jassert(colours.size() == NUM_BEAMS); + for (auto beamIdx = 0; beamIdx < NUM_BEAMS; ++beamIdx) { + beams[beamIdx].setBaseColor(colours[beamIdx]); + } +} + +//============================================================================== +//============================================================================== diff --git a/Source/SceneComp.h b/Source/SceneComp.h new file mode 100644 index 0000000..1eb4695 --- /dev/null +++ b/Source/SceneComp.h @@ -0,0 +1,161 @@ +/* + Acoustic scene components + + Authors: + Matteo Scerbo (matteo.scerbo@mail.polimi.it) + Luca Bondi (luca.bondi@polimi.it) +*/ + +#pragma once + +#include "../JuceLibraryCode/JuceHeader.h" +#include "ebeamerDefs.h" +#include "MeterComp.h" +#include "Beamformer.h" + +class TileComp : public Component { +public: + + TileComp(); + + ~TileComp() {}; + + void paint(Graphics &) override; + + void resized() override {}; + + void setFrontFacingParam(const std::atomic *p); + + void setColour(const Colour &col); + + void setCorners(const juce::Point &, + const juce::Point &, + const juce::Point &, + const juce::Point &); + +private: + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TileComp) + + juce::Point corners[2][2]; + Colour tileColour; + + const std::atomic *frontFacing = nullptr; + AffineTransform frontFacingTransf; +}; + +//============================================================================== + +class GridComp : public Component, public Timer { +public: + GridComp(); + + ~GridComp() {}; + + void resized() override; + + class Callback { + public: + virtual ~Callback() = default; + + virtual void getDoaEnergy(std::vector &energy) const = 0; + }; + + void setCallback(const Callback *p); + + void setParams(const std::atomic *frontFacing); + +private: + + TileComp tiles[TILE_ROW_COUNT][NUM_DOAS]; + juce::Point vertices[TILE_ROW_COUNT + 1][NUM_DOAS + 1]; + + const Callback *callback = nullptr; + + std::vector th; + + std::vector energy, energyPreGain; + float inertia = 0.85; + float gain = 0; + const float maxGain = 60, minGain = -20; + + const float gridUpdateFrequency = 10; + + void computeVertices(); + + void timerCallback() override; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GridComp) +}; + +//============================================================================== + +class BeamComp : public Component { +public: + BeamComp() {}; + + ~BeamComp() {}; + + void paint(Graphics &) override; + + void resized() override {}; + + void setParams(const std::atomic *frontFacing, + const std::atomic *mute, + const std::atomic *width, + const std::atomic *steer); + + //TODO: Use LookAndFeel + void setBaseColor(Colour colour) { baseColour = colour; } + +private: + + const std::atomic *frontFacingParam = nullptr; + const std::atomic *muteParam = nullptr; + const std::atomic *widthParam = nullptr; + const std::atomic *steerParam = nullptr; + + + Colour baseColour = Colours::lightblue; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BeamComp) +}; + +//============================================================================== + +class SceneComp : public Component { +public: + SceneComp(); + + ~SceneComp() {}; + + class Callback : public GridComp::Callback { + public: + virtual ~Callback() = default; + + virtual const std::atomic *getFrontFacingParam() const = 0; + + virtual const std::atomic *getBeamMute(int idx) const = 0; + + virtual const std::atomic *getBeamWidth(int idx) const = 0; + + virtual const std::atomic *getBeamSteer(int idx) const = 0; + }; + + void setCallback(const Callback *c); + + void paint(Graphics &) override; + + void resized() override; + + //TODO: Use LookAndFeel + void setBeamColors(const std::vector &colours); + + +private: + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SceneComp) + + BeamComp beams[NUM_BEAMS]; + GridComp grid; +}; diff --git a/Source/SceneComponent.cpp b/Source/SceneComponent.cpp deleted file mode 100644 index 71682db..0000000 --- a/Source/SceneComponent.cpp +++ /dev/null @@ -1,286 +0,0 @@ -/* - ============================================================================== - - SceneComponent.cpp - Created: 15 Mar 2019 5:39:56pm - Author: Matteo Scerbo (matteo.scerbo@mail.polimi.it) - Author: Luca Bondi (luca.bondi@polimi.it) - - ============================================================================== -*/ - -#include "SceneComponent.h" - -//============================================================================== -//============================================================================== - -TileComponent::TileComponent() -{ - frontFacingTransf = AffineTransform::rotation(MathConstants::pi,SCENE_WIDTH/2,SCENE_HEIGHT/2); -} - -TileComponent::~TileComponent() -{ -} -void TileComponent::paint(Graphics& g) -{ - Path path; - - path.startNewSubPath(corners[0][0]); - path.lineTo(corners[1][0]); - path.lineTo(corners[1][1]); - path.lineTo(corners[0][1]); - path.closeSubPath(); - - if ((bool)*frontFacing){ - path.applyTransform(frontFacingTransf); - } - - g.setColour(tileColour); - g.fillPath(path); - - g.setColour(Colours::black); - PathStrokeType strokeType(0.5f); - g.strokePath(path, strokeType); -} - -void TileComponent::resized() -{ -} - -void TileComponent::setProcessor(const EbeamerAudioProcessor * p){ - processor = p; - frontFacing = processor->getParams().getRawParameterValue("frontFacing"); -} -//============================================================================== -//============================================================================== - -GridComponent::GridComponent(){ - for(int i = 0; i < TILE_ROW_COUNT; i++) - for(int j = 0; j < EbeamerAudioProcessor::numDoas; j++) - addAndMakeVisible (tiles[i][j]); - - energy.resize(EbeamerAudioProcessor::numDoas); - energyPreGain.resize(EbeamerAudioProcessor::numDoas,-30); - - - // Compute led tresholds - const float ledStep = 3; //dB - - th.clear(); - for (auto ledIdx = TILE_ROW_COUNT - 1; ledIdx >= 0; --ledIdx){ - auto ledThDb = ledIdx == (TILE_ROW_COUNT-1) ? RED_LT : -((TILE_ROW_COUNT - 1 - ledIdx) *ledStep); - th.push_back(ledThDb); - } - - startTimerHz(gridUpdateFrequency); - -} - -GridComponent::~GridComponent() -{ -} - -void GridComponent::setProcessor(const EbeamerAudioProcessor * p){ - processor = p; - - for(int i = 0; i < TILE_ROW_COUNT; i++){ - for(int j = 0; j < EbeamerAudioProcessor::numDoas; j++){ - tiles[i][j].setProcessor(processor); - } - } -} - -//============================================================================== - -void GridComponent::resized() -{ - computeVertices(); - - for(int i = 0; i < TILE_ROW_COUNT; i++){ - for(int j = 0; j < EbeamerAudioProcessor::numDoas; j++){ - - tiles[i][j].corners[0][0] = vertices[i ][j ]; - tiles[i][j].corners[1][0] = vertices[i+1][j ]; - tiles[i][j].corners[0][1] = vertices[i ][j+1]; - tiles[i][j].corners[1][1] = vertices[i+1][j+1]; - - tiles[i][j].setBounds(getLocalBounds()); - - if(i < TILE_ROW_COUNT/4) - tiles[i][j].tileColour = Colours::red.darker(0.9); - - if(TILE_ROW_COUNT/4 <= i && i < TILE_ROW_COUNT/2) - tiles[i][j].tileColour = Colours::yellow.darker(0.9); - - if(i >= TILE_ROW_COUNT/2) - tiles[i][j].tileColour = Colours::green.darker(0.9); - - } - } -} - -void GridComponent::timerCallback(){ - - std::vector newEnergy(EbeamerAudioProcessor::numDoas); - processor->getBeamformer()->getDoaEnergy(newEnergy); - - for (auto dirIdx = 0; dirIdx < energyPreGain.size(); ++dirIdx){ - energyPreGain[dirIdx] = ((1-inertia) * (newEnergy[dirIdx])) + (inertia * energyPreGain[dirIdx]); - } - - // Very basic automatic gain - auto rangeEnergy = FloatVectorOperations::findMinAndMax(energyPreGain.data(), (int)energyPreGain.size()); - auto minLevel = rangeEnergy.getStart() + gain; - auto maxLevel = rangeEnergy.getEnd() + gain; - - if (maxLevel > 0){ - gain = jmax(gain-maxLevel-3,minGain); - }else if (maxLevel < -18){ - gain = jmin(gain-maxLevel,maxGain); - }else if (maxLevel > -3){ - gain = jmax(gain-0.5f,minGain); - }else if (maxLevel < -9){ - gain = jmin(gain+0.5f,maxGain); - } - - for (auto dirIdx = 0; dirIdx < energyPreGain.size(); ++dirIdx){ - energy[dirIdx] = energyPreGain[dirIdx] + gain; - } - - for(int j = 0; j < EbeamerAudioProcessor::numDoas; j++){ - for(int i = 0; i < TILE_ROW_COUNT; i++){ - tiles[i][j].tileColour = SingleChannelLedBar::thToColour(th[i],energy[j] > th[i]); - } - } - - repaint(); - -} - -void GridComponent::computeVertices() -{ - const float w = SCENE_WIDTH; - const float h = SCENE_HEIGHT; - - float angle_diff = MathConstants::pi / EbeamerAudioProcessor::numDoas; - // float radius_diff = h / TILE_ROW_COUNT; - - for(int i = 0; i <= TILE_ROW_COUNT; i++){ - // Linear - // float radius = h - i * radius_diff; - - // Square - // float radius = h - h * pow( (float)i / TILE_ROW_COUNT, 2 ); - - // Inverse square - // float radius = h * sqrt( (float)(TILE_ROW_COUNT - i) / TILE_ROW_COUNT ); - - // Exponential - const float radius = h - h * ( exp( (float)i / TILE_ROW_COUNT ) - 1 ) / ( exp( 1 ) - 1 ); - - for(int j = 0; j <= EbeamerAudioProcessor::numDoas; j++){ - const float angle = j*angle_diff; - - vertices[i][j].setX( w/2 - radius*cos(angle)); - vertices[i][j].setY( h - radius*sin(angle)); - - } - } -} - -//============================================================================== -//============================================================================== - -BeamComponent::BeamComponent(){ -} - -BeamComponent::~BeamComponent(){ -} - -void BeamComponent::setProcessor(const EbeamerAudioProcessor * p, int beamId_){ - beamId=beamId_; - processor = p; - muteParam = processor->getParams().getRawParameterValue(String("muteBeam") + String(beamId)); - widthParam = processor->getParams().getRawParameterValue(String("widthBeam") + String(beamId)); - steerParam = processor->getParams().getRawParameterValue(String("steerBeam") + String(beamId)); - frontFacingParam = processor->getParams().getRawParameterValue("frontFacing"); -} - -void BeamComponent::paint(Graphics& g){ - - const float width = (0.1 + 2.9*(*widthParam)) * SCENE_WIDTH/10; - const float position = *steerParam; - - Path path; - path.startNewSubPath(0, 0); - path.cubicTo( width, -SCENE_WIDTH/3, width, -SCENE_WIDTH/2, 0, -SCENE_WIDTH/2); - path.cubicTo(-width, -SCENE_WIDTH/2, -width, -SCENE_WIDTH/3, 0, 0); - path.closeSubPath(); - - path.applyTransform(AffineTransform::rotation( (MathConstants::pi/2) * position)); - path.applyTransform(AffineTransform::translation(SCENE_WIDTH/2, SCENE_WIDTH/2)); - - if ((bool)*frontFacingParam){ - path.applyTransform(AffineTransform::verticalFlip(SCENE_HEIGHT)); - } - - if (~(bool)*muteParam){ - g.setColour(baseColour.brighter()); - g.setOpacity(0.4); - g.fillPath(path); - } - - g.setColour (baseColour); - g.setOpacity(0.8); - PathStrokeType strokeType(2); - g.strokePath(path, strokeType); -} - -void BeamComponent::resized(){ -} - -//============================================================================== -//============================================================================== - -SceneComponent::SceneComponent(const EbeamerAudioProcessor& p){ - - beams[0].setProcessor(&p,1); - beams[1].setProcessor(&p,2); - - grid.setProcessor(&p); - - addAndMakeVisible (grid); - for(int i = 0; i < EbeamerAudioProcessor::numBeams; i++) - addAndMakeVisible (beams[i]); - -} - -SceneComponent::~SceneComponent() -{ -} - -//============================================================================== - -void SceneComponent::paint(Graphics& g) -{ - g.fillAll(getLookAndFeel().findColour (ResizableWindow::backgroundColourId)); -} - -void SceneComponent::resized() -{ - grid.setBounds(getLocalBounds()); - for(int i = 0; i < EbeamerAudioProcessor::numBeams; i++) - beams[i].setBounds(getLocalBounds()); -} - -void SceneComponent::setBeamColors(const std::vector &colours){ - jassert(colours.size() == EbeamerAudioProcessor::numBeams); - for (auto beamIdx = 0;beamIdx < EbeamerAudioProcessor::numBeams;++beamIdx) - { - beams[beamIdx].setBaseColor(colours[beamIdx]); - } -} - -//============================================================================== -//============================================================================== diff --git a/Source/SceneComponent.h b/Source/SceneComponent.h deleted file mode 100644 index ed292a8..0000000 --- a/Source/SceneComponent.h +++ /dev/null @@ -1,138 +0,0 @@ -/* - ============================================================================== - - SceneComponent.h - Created: 15 Mar 2019 5:39:56pm - Author: Matteo Scerbo (matteo.scerbo@mail.polimi.it) - Author: Luca Bondi (luca.bondi@polimi.it) - - ============================================================================== -*/ - -#pragma once - -#define PERSPECTIVE_RATIO 5 -#define TILE_ROW_COUNT 7 - -#define SCENE_WIDTH 460 - -#define GUI_HEIGHT 830 -#define SCENE_HEIGHT 230 - - -#include "../JuceLibraryCode/JuceHeader.h" -#include "PluginProcessor.h" -#include "AudioComponents.h" -#include "Beamformer.h" - - -//============================================================================== - -class TileComponent : public Component -{ -public: - - juce::Point corners[2][2]; - Colour tileColour; - - TileComponent(); - ~TileComponent(); - - void paint(Graphics&) override; - void resized() override; - void setProcessor(const EbeamerAudioProcessor * p); - -private: - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TileComponent) - - const EbeamerAudioProcessor * processor; - std::atomic* frontFacing; - AffineTransform frontFacingTransf; -}; - -//============================================================================== - -class GridComponent : public Component, public Timer -{ -public: - GridComponent(); - ~GridComponent(); - - void resized() override; - - void setProcessor(const EbeamerAudioProcessor * p); - -private: - - TileComponent tiles[TILE_ROW_COUNT][EbeamerAudioProcessor::numDoas]; - juce::Point vertices[TILE_ROW_COUNT+1][EbeamerAudioProcessor::numDoas+1]; - - const EbeamerAudioProcessor * processor; - - std::vector th; - - std::vector energy, energyPreGain; - float inertia = 0.85; - float gain = 0; - const float maxGain = 60, minGain = -20; - - const float gridUpdateFrequency = 10; - - void computeVertices(); - void timerCallback() override; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GridComponent) -}; - -//============================================================================== - -class BeamComponent : public Component -{ -public: - BeamComponent(); - ~BeamComponent(); - - void paint(Graphics&) override; - void resized() override; - - void setProcessor(const EbeamerAudioProcessor * p, int beamId_); - - void setBaseColor(Colour colour){baseColour = colour;} - -private: - - int beamId; - - std::atomic* frontFacingParam; - std::atomic* muteParam; - std::atomic* widthParam; - std::atomic* steerParam; - - const EbeamerAudioProcessor * processor; - - Colour baseColour = Colours::lightblue; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BeamComponent) -}; - -//============================================================================== - -class SceneComponent : public Component -{ -public: - SceneComponent(const EbeamerAudioProcessor& p); - ~SceneComponent(); - - void setBeamColors(const std::vector &colours); - - void paint(Graphics&) override; - void resized() override; - - GridComponent grid; - BeamComponent beams[EbeamerAudioProcessor::numBeams]; - -private: - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SceneComponent) -}; diff --git a/Source/SigProc.cpp b/Source/SignalProcessing.cpp similarity index 53% rename from Source/SigProc.cpp rename to Source/SignalProcessing.cpp index 3e45229..394c339 100644 --- a/Source/SigProc.cpp +++ b/Source/SignalProcessing.cpp @@ -1,25 +1,22 @@ /* - ============================================================================== - - Linalg.cpp - Created: 15 Apr 2020 10:13:19am - Author: Luca Bondi - - ============================================================================== + Signal processing utilities + + Authors: + Luca Bondi (luca.bondi@polimi.it) */ -#include "SigProc.h" +#include "SignalProcessing.h" -void designTukeyWindow(Vec &win, size_t winLen, size_t rampLen){ +void designTukeyWindow(Vec &win, size_t winLen, size_t rampLen) { jassert(winLen <= win.size() && winLen >= 3); - jassert(rampLen >=0 && rampLen <= winLen/2 ); + jassert(rampLen >= 0 && rampLen <= winLen / 2); Vec winTmp = Vec::Ones(winLen, 1); if (rampLen > 0) { winTmp.head(rampLen) = - ((Vec::LinSpaced(rampLen, -pi, 0)).array().cos() + 1.0f) * 0.5f; + ((Vec::LinSpaced(rampLen, -pi, 0)).array().cos() + 1.0f) * 0.5f; winTmp.tail(rampLen) = winTmp.head(rampLen).colwise().reverse(); } /** Center the window */ @@ -29,11 +26,13 @@ void designTukeyWindow(Vec &win, size_t winLen, size_t rampLen){ } -void freqToTime(AudioBuffer& time, const int timeCh, const CpxVec& freq, const juce::dsp::FFT* fft, const Vec& window, float alpha){ - - alpha = jlimit(0.f,1.f,alpha); - - AudioBuffer tmp(1,fft->getSize()*2); +void +freqToTime(AudioBuffer &time, const int timeCh, const CpxVec &freq, const juce::dsp::FFT *fft, const Vec &window, + float alpha) { + + alpha = jlimit(0.f, 1.f, alpha); + + AudioBuffer tmp(1, fft->getSize() * 2); tmp.clear(); FloatVectorOperations::copy(tmp.getWritePointer(0), @@ -41,18 +40,19 @@ void freqToTime(AudioBuffer& time, const int timeCh, const CpxVec& freq, (fft->getSize() / 2 + 1) * 2); fft->performRealOnlyInverseTransform(tmp.getWritePointer(0)); - if (window.size()){ + if (window.size()) { /** Apply windowing to IR */ FloatVectorOperations::multiply(tmp.getWritePointer(0), window.data(), fft->getSize()); } - if (alpha < 1){ + if (alpha < 1) { /** Exp smoothing */ - FloatVectorOperations::multiply(time.getWritePointer(timeCh),1.f-alpha,time.getNumSamples()); - FloatVectorOperations::addWithMultiply(time.getWritePointer(timeCh), tmp.getReadPointer(0), alpha, time.getNumSamples()); - }else{ - FloatVectorOperations::copy(time.getWritePointer(timeCh),tmp.getReadPointer(0),time.getNumSamples()); + FloatVectorOperations::multiply(time.getWritePointer(timeCh), 1.f - alpha, time.getNumSamples()); + FloatVectorOperations::addWithMultiply(time.getWritePointer(timeCh), tmp.getReadPointer(0), alpha, + time.getNumSamples()); + } else { + FloatVectorOperations::copy(time.getWritePointer(timeCh), tmp.getReadPointer(0), time.getNumSamples()); } } diff --git a/Source/SigProc.h b/Source/SignalProcessing.h similarity index 72% rename from Source/SigProc.h rename to Source/SignalProcessing.h index 0958110..85d38ea 100644 --- a/Source/SigProc.h +++ b/Source/SignalProcessing.h @@ -1,11 +1,8 @@ /* - ============================================================================== - - Linalg.h - Created: 15 Apr 2020 10:13:19am - Author: Luca Bondi - - ============================================================================== + Signal processing utilities + + Authors: + Luca Bondi (luca.bondi@polimi.it) */ #pragma once @@ -39,4 +36,5 @@ void designTukeyWindow(Vec &win, size_t winLen, size_t rampLen); @param alpha: exponential interpolation coefficient. 1 means complete override (instant update), 0 means no override (complete preservation) */ -void freqToTime(AudioBuffer& time, const int timeCh, const CpxVec& freq, const juce::dsp::FFT* fft, const Vec& window = Vec(), float alpha = 1); +void freqToTime(AudioBuffer &time, const int timeCh, const CpxVec &freq, const juce::dsp::FFT *fft, + const Vec &window = Vec(), float alpha = 1); diff --git a/Source/ebeamerDefs.h b/Source/ebeamerDefs.h new file mode 100644 index 0000000..db892e6 --- /dev/null +++ b/Source/ebeamerDefs.h @@ -0,0 +1,77 @@ +/* + Project-wise types and definitions + + Authors: + Luca Bondi (luca.bondi@polimi.it) +*/ + +#pragma once + +#define NUM_BEAMS 2 +#define NUM_DOAS 25 + +#define GUI_WIDTH 540 +#define GUI_HEIGHT 830 + +#define SCENE_WIDTH 460 +#define SCENE_HEIGHT 230 + +#define TILE_ROW_COUNT 7 + +#define LABEL_BEAM_WIDTH 25 +#define STEER_SLIDER_HEIGHT 40 +#define STEER_SLIDER_TOP_MARGIN 10 +#define KNOB_WIDTH 150 +#define KNOB_HEIGHT 80 +#define KNOB_TOP_MARGIN 8 +#define MUTE_HEIGHT 40 +#define MUTE_WIDTH 40 +#define MUTE_LEFT_RIGHT_MARGIN 20 +#define MUTE_TOP_MARGIN 8 +#define LABEL_WIDTH 70 +#define LABEL_HEIGHT 20 +#define LEFT_RIGHT_MARGIN 20 +#define TOP_BOTTOM_MARGIN 20 +#define KNOBS_LEFT_RIGHT_MARGIN 20 +#define BEAM_LED_WIDTH 5 +#define BEAM_TOP_BOTTOM_MARGIN 10 +#define BEAM_LEFT_RIGHT_MARGIN 10 + +#define INPUT_SECTION_TOP_MARGIN 20 +#define INPUT_HPF_SLIDER_HEIGHT 40 +#define INPUT_HPF_LABEL_WIDTH 50 +#define INPUT_LEFT_RIGHT_MARGIN 50 +#define INPUT_LED_HEIGHT 5 +#define INPUT_GAIN_SLIDER_HEIGHT 40 +#define INPUT_GAIN_LABEL_WIDTH 50 + +#define PREFORMANCE_MONITOR_HEIGHT 20 +#define CPULOAD_WIDTH 80 +#define CPULOAD_UPDATE_FREQ 10 //Hz + +#define FRONT_TOGGLE_LABEL_WIDTH 40 +#define FRONT_TOGGLE_WIDTH 25 + +#define CONFIG_COMBO_LABEL_WIDTH 65 +#define CONFIG_COMBO_WIDTH 80 + +#define INPUT_METER_UPDATE_FREQ 10 //Hz +#define BEAM_METER_UPDATE_FREQ 10 //Hz +#define ENERGY_UPDATE_FREQ 10 //Hz + + +/** Available eSticks configurations type */ +typedef enum { + LMA_1ESTICK, + LMA_2ESTICK, + LMA_3ESTICK, + LMA_4ESTICK, +} MicConfig; + +/** Available eSticks configurations labels */ +const StringArray micConfigLabels({ + "Single", + "Hor 2", + "Hor 3", + "Hor 4", + }); diff --git a/eBeamer.jucer b/eBeamer.jucer index 3b0d949..5ed0a12 100644 --- a/eBeamer.jucer +++ b/eBeamer.jucer @@ -520,36 +520,36 @@ - - - - - - - - - - - - - - - + + + + + + - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 4015778ed22b9b0ecc109b584927566d615c8ec4 Mon Sep 17 00:00:00 2001 From: Luca Bondi Date: Sat, 2 May 2020 16:41:03 +0200 Subject: [PATCH 2/4] Bugfix in right click on buttons fix #44 --- Source/MeterComp.cpp | 4 ---- Source/MidiComp.cpp | 9 ++------- Source/MidiComp.h | 32 ++++++++++++++++++++++++-------- 3 files changed, 26 insertions(+), 19 deletions(-) diff --git a/Source/MeterComp.cpp b/Source/MeterComp.cpp index c1dd410..25b6656 100644 --- a/Source/MeterComp.cpp +++ b/Source/MeterComp.cpp @@ -187,7 +187,3 @@ Colour SingleChannelLedBar::thToColour(float th, bool active) { return Colours::darkgreen; } } - -MuteButton::MuteButton() { - setClickingTogglesState(true); -} diff --git a/Source/MidiComp.cpp b/Source/MidiComp.cpp index f075645..f2fdeb6 100644 --- a/Source/MidiComp.cpp +++ b/Source/MidiComp.cpp @@ -9,13 +9,8 @@ #include "MidiComp.h" //============================================================================== - -MidiCCPopup::MidiCCPopup(Component &owner_) : owner(owner_) { - -} - -MidiCCPopup::~MidiCCPopup() { - +MidiCCPopup::MidiCCPopup(Component &o):owner(o){ + } void MidiCCPopup::setCallback(MidiCC::Callback *cb, const String ¶m) { diff --git a/Source/MidiComp.h b/Source/MidiComp.h index f45f3f3..32f364d 100644 --- a/Source/MidiComp.h +++ b/Source/MidiComp.h @@ -16,7 +16,7 @@ class MidiCCPopup { public: MidiCCPopup(Component &owner); - ~MidiCCPopup(); + ~MidiCCPopup(){}; void setCallback(MidiCC::Callback *cb, const String ¶m); @@ -58,12 +58,29 @@ class TextButtonCC : public TextButton, public MidiCCPopup { ~TextButtonCC() override {}; void mouseDown(const MouseEvent &e) override { + if (e.mods.isPopupMenu()) { MidiCCPopup::showPopupMenu(); } else { TextButton::mouseDown(e); } } + + void mouseUp(const MouseEvent &e) override { + if (!e.mods.isPopupMenu()){ + TextButton::mouseUp(e); + } + } + +}; + +//============================================================================== +class MuteButton : public TextButtonCC { +public: + + MuteButton(){ + setClickingTogglesState(true); + }; }; @@ -81,7 +98,12 @@ class ToggleButtonCC : public ToggleButton, public MidiCCPopup { ToggleButton::mouseDown(e); } } - + + void mouseUp(const MouseEvent &e) override { + if (!e.mods.isPopupMenu()){ + ToggleButton::mouseUp(e); + } + } }; //============================================================================== @@ -171,10 +193,4 @@ class PanSlider : public SliderCC { JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PanSlider) }; -//============================================================================== -class MuteButton : public TextButtonCC { -public: - MuteButton(); - -}; From f79bd026134f2600a12434ef59990bd0a98f8089 Mon Sep 17 00:00:00 2001 From: Luca Bondi Date: Sat, 2 May 2020 18:13:12 +0200 Subject: [PATCH 3/4] VS2019 compatibility --- .gitignore | 2 + ASIO/asio.h | 1070 ---------------------------------- ASIO/asiodrvr.h | 76 --- ASIO/asiosys.h | 82 --- ASIO/combase.h | 284 --------- ASIO/iasiodrv.h | 37 -- ASIO/wxdebug.h | 326 ----------- JuceLibraryCode/AppConfig.h | 6 +- JuceLibraryCode/JuceHeader.h | 4 +- Source/PluginProcessor.cpp | 2 +- eBeamer.jucer | 25 +- 11 files changed, 31 insertions(+), 1883 deletions(-) delete mode 100755 ASIO/asio.h delete mode 100755 ASIO/asiodrvr.h delete mode 100755 ASIO/asiosys.h delete mode 100755 ASIO/combase.h delete mode 100755 ASIO/iasiodrv.h delete mode 100755 ASIO/wxdebug.h diff --git a/.gitignore b/.gitignore index 53bc3b3..c670fbb 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ Builds/ *.filtergraph + +ASIO/ diff --git a/ASIO/asio.h b/ASIO/asio.h deleted file mode 100755 index c74199f..0000000 --- a/ASIO/asio.h +++ /dev/null @@ -1,1070 +0,0 @@ -//--------------------------------------------------------------------------------------------------- -//--------------------------------------------------------------------------------------------------- - -/* - Steinberg Audio Stream I/O API - (c) 1997 - 2013, Steinberg Media Technologies GmbH - - ASIO Interface Specification v 2.3 - - 2005 - Added support for DSD sample data (in cooperation with Sony) - 2012 - Added support for drop out detection - - - - basic concept is an i/o synchronous double-buffer scheme: - - on bufferSwitch(index == 0), host will read/write: - - after ASIOStart(), the - read first input buffer A (index 0) - | will be invalid (empty) - * ------------------------ - |------------------------|-----------------------| - | | | - | Input Buffer A (0) | Input Buffer B (1) | - | | | - |------------------------|-----------------------| - | | | - | Output Buffer A (0) | Output Buffer B (1) | - | | | - |------------------------|-----------------------| - * ------------------------- - | before calling ASIOStart(), - write host will have filled output - buffer B (index 1) already - - *please* take special care of proper statement of input - and output latencies (see ASIOGetLatencies()), these - control sequencer sync accuracy - -*/ - -//--------------------------------------------------------------------------------------------------- -//--------------------------------------------------------------------------------------------------- - -/* - -prototypes summary: - -ASIOError ASIOInit(ASIODriverInfo *info); -ASIOError ASIOExit(void); -ASIOError ASIOStart(void); -ASIOError ASIOStop(void); -ASIOError ASIOGetChannels(long *numInputChannels, long *numOutputChannels); -ASIOError ASIOGetLatencies(long *inputLatency, long *outputLatency); -ASIOError ASIOGetBufferSize(long *minSize, long *maxSize, long *preferredSize, long *granularity); -ASIOError ASIOCanSampleRate(ASIOSampleRate sampleRate); -ASIOError ASIOGetSampleRate(ASIOSampleRate *currentRate); -ASIOError ASIOSetSampleRate(ASIOSampleRate sampleRate); -ASIOError ASIOGetClockSources(ASIOClockSource *clocks, long *numSources); -ASIOError ASIOSetClockSource(long reference); -ASIOError ASIOGetSamplePosition (ASIOSamples *sPos, ASIOTimeStamp *tStamp); -ASIOError ASIOGetChannelInfo(ASIOChannelInfo *info); -ASIOError ASIOCreateBuffers(ASIOBufferInfo *bufferInfos, long numChannels, - long bufferSize, ASIOCallbacks *callbacks); -ASIOError ASIODisposeBuffers(void); -ASIOError ASIOControlPanel(void); -void *ASIOFuture(long selector, void *params); -ASIOError ASIOOutputReady(void); - -*/ - -//--------------------------------------------------------------------------------------------------- -//--------------------------------------------------------------------------------------------------- - -#ifndef __ASIO_H -#define __ASIO_H - -// force 4 byte alignment -#if defined(_MSC_VER) && !defined(__MWERKS__) -#pragma pack(push,4) -#elif PRAGMA_ALIGN_SUPPORTED -#pragma options align = native -#endif - -//- - - - - - - - - - - - - - - - - - - - - - - - - -// Type definitions -//- - - - - - - - - - - - - - - - - - - - - - - - - - -// number of samples data type is 64 bit integer -#if NATIVE_INT64 - typedef long long int ASIOSamples; -#else - typedef struct ASIOSamples { - unsigned long hi; - unsigned long lo; - } ASIOSamples; -#endif - -// Timestamp data type is 64 bit integer, -// Time format is Nanoseconds. -#if NATIVE_INT64 - typedef long long int ASIOTimeStamp ; -#else - typedef struct ASIOTimeStamp { - unsigned long hi; - unsigned long lo; - } ASIOTimeStamp; -#endif - -// Samplerates are expressed in IEEE 754 64 bit double float, -// native format as host computer -#if IEEE754_64FLOAT - typedef double ASIOSampleRate; -#else - typedef struct ASIOSampleRate { - char ieee[8]; - } ASIOSampleRate; -#endif - -// Boolean values are expressed as long -typedef long ASIOBool; -enum { - ASIOFalse = 0, - ASIOTrue = 1 -}; - -// Sample Types are expressed as long -typedef long ASIOSampleType; -enum { - ASIOSTInt16MSB = 0, - ASIOSTInt24MSB = 1, // used for 20 bits as well - ASIOSTInt32MSB = 2, - ASIOSTFloat32MSB = 3, // IEEE 754 32 bit float - ASIOSTFloat64MSB = 4, // IEEE 754 64 bit double float - - // these are used for 32 bit data buffer, with different alignment of the data inside - // 32 bit PCI bus systems can be more easily used with these - ASIOSTInt32MSB16 = 8, // 32 bit data with 16 bit alignment - ASIOSTInt32MSB18 = 9, // 32 bit data with 18 bit alignment - ASIOSTInt32MSB20 = 10, // 32 bit data with 20 bit alignment - ASIOSTInt32MSB24 = 11, // 32 bit data with 24 bit alignment - - ASIOSTInt16LSB = 16, - ASIOSTInt24LSB = 17, // used for 20 bits as well - ASIOSTInt32LSB = 18, - ASIOSTFloat32LSB = 19, // IEEE 754 32 bit float, as found on Intel x86 architecture - ASIOSTFloat64LSB = 20, // IEEE 754 64 bit double float, as found on Intel x86 architecture - - // these are used for 32 bit data buffer, with different alignment of the data inside - // 32 bit PCI bus systems can more easily used with these - ASIOSTInt32LSB16 = 24, // 32 bit data with 18 bit alignment - ASIOSTInt32LSB18 = 25, // 32 bit data with 18 bit alignment - ASIOSTInt32LSB20 = 26, // 32 bit data with 20 bit alignment - ASIOSTInt32LSB24 = 27, // 32 bit data with 24 bit alignment - - // ASIO DSD format. - ASIOSTDSDInt8LSB1 = 32, // DSD 1 bit data, 8 samples per byte. First sample in Least significant bit. - ASIOSTDSDInt8MSB1 = 33, // DSD 1 bit data, 8 samples per byte. First sample in Most significant bit. - ASIOSTDSDInt8NER8 = 40, // DSD 8 bit data, 1 sample per byte. No Endianness required. - - ASIOSTLastEntry -}; - -/*----------------------------------------------------------------------------- -// DSD operation and buffer layout -// Definition by Steinberg/Sony Oxford. -// -// We have tried to treat DSD as PCM and so keep a consistant structure across -// the ASIO interface. -// -// DSD's sample rate is normally referenced as a multiple of 44.1Khz, so -// the standard sample rate is refered to as 64Fs (or 2.8224Mhz). We looked -// at making a special case for DSD and adding a field to the ASIOFuture that -// would allow the user to select the Over Sampleing Rate (OSR) as a seperate -// entity but decided in the end just to treat it as a simple value of -// 2.8224Mhz and use the standard interface to set it. -// -// The second problem was the "word" size, in PCM the word size is always a -// greater than or equal to 8 bits (a byte). This makes life easy as we can -// then pack the samples into the "natural" size for the machine. -// In DSD the "word" size is 1 bit. This is not a major problem and can easily -// be dealt with if we ensure that we always deal with a multiple of 8 samples. -// -// DSD brings with it another twist to the Endianness religion. How are the -// samples packed into the byte. It would be nice to just say the most significant -// bit is always the first sample, however there would then be a performance hit -// on little endian machines. Looking at how some of the processing goes... -// Little endian machines like the first sample to be in the Least Significant Bit, -// this is because when you write it to memory the data is in the correct format -// to be shifted in and out of the words. -// Big endian machine prefer the first sample to be in the Most Significant Bit, -// again for the same reasion. -// -// And just when things were looking really muddy there is a proposed extension to -// DSD that uses 8 bit word sizes. It does not care what endianness you use. -// -// Switching the driver between DSD and PCM mode -// ASIOFuture allows for extending the ASIO API quite transparently. -// See kAsioSetIoFormat, kAsioGetIoFormat, kAsioCanDoIoFormat -// -//-----------------------------------------------------------------------------*/ - - -//- - - - - - - - - - - - - - - - - - - - - - - - - -// Error codes -//- - - - - - - - - - - - - - - - - - - - - - - - - - -typedef long ASIOError; -enum { - ASE_OK = 0, // This value will be returned whenever the call succeeded - ASE_SUCCESS = 0x3f4847a0, // unique success return value for ASIOFuture calls - ASE_NotPresent = -1000, // hardware input or output is not present or available - ASE_HWMalfunction, // hardware is malfunctioning (can be returned by any ASIO function) - ASE_InvalidParameter, // input parameter invalid - ASE_InvalidMode, // hardware is in a bad mode or used in a bad mode - ASE_SPNotAdvancing, // hardware is not running when sample position is inquired - ASE_NoClock, // sample clock or rate cannot be determined or is not present - ASE_NoMemory // not enough memory for completing the request -}; - -//--------------------------------------------------------------------------------------------------- -//--------------------------------------------------------------------------------------------------- - -//- - - - - - - - - - - - - - - - - - - - - - - - - -// Time Info support -//- - - - - - - - - - - - - - - - - - - - - - - - - - -typedef struct ASIOTimeCode -{ - double speed; // speed relation (fraction of nominal speed) - // optional; set to 0. or 1. if not supported - ASIOSamples timeCodeSamples; // time in samples - unsigned long flags; // some information flags (see below) - char future[64]; -} ASIOTimeCode; - -typedef enum ASIOTimeCodeFlags -{ - kTcValid = 1, - kTcRunning = 1 << 1, - kTcReverse = 1 << 2, - kTcOnspeed = 1 << 3, - kTcStill = 1 << 4, - - kTcSpeedValid = 1 << 8 -} ASIOTimeCodeFlags; - -typedef struct AsioTimeInfo -{ - double speed; // absolute speed (1. = nominal) - ASIOTimeStamp systemTime; // system time related to samplePosition, in nanoseconds - // on mac, must be derived from Microseconds() (not UpTime()!) - // on windows, must be derived from timeGetTime() - ASIOSamples samplePosition; - ASIOSampleRate sampleRate; // current rate - unsigned long flags; // (see below) - char reserved[12]; -} AsioTimeInfo; - -typedef enum AsioTimeInfoFlags -{ - kSystemTimeValid = 1, // must always be valid - kSamplePositionValid = 1 << 1, // must always be valid - kSampleRateValid = 1 << 2, - kSpeedValid = 1 << 3, - - kSampleRateChanged = 1 << 4, - kClockSourceChanged = 1 << 5 -} AsioTimeInfoFlags; - -typedef struct ASIOTime // both input/output -{ - long reserved[4]; // must be 0 - struct AsioTimeInfo timeInfo; // required - struct ASIOTimeCode timeCode; // optional, evaluated if (timeCode.flags & kTcValid) -} ASIOTime; - -/* - -using time info: -it is recommended to use the new method with time info even if the asio -device does not support timecode; continuous calls to ASIOGetSamplePosition -and ASIOGetSampleRate are avoided, and there is a more defined relationship -between callback time and the time info. - -see the example below. -to initiate time info mode, after you have received the callbacks pointer in -ASIOCreateBuffers, you will call the asioMessage callback with kAsioSupportsTimeInfo -as the argument. if this returns 1, host has accepted time info mode. -now host expects the new callback bufferSwitchTimeInfo to be used instead -of the old bufferSwitch method. the ASIOTime structure is assumed to be valid -and accessible until the callback returns. - -using time code: -if the device supports reading time code, it will call host's asioMessage callback -with kAsioSupportsTimeCode as the selector. it may then fill the according -fields and set the kTcValid flag. -host will call the future method with the kAsioEnableTimeCodeRead selector when -it wants to enable or disable tc reading by the device. you should also support -the kAsioCanTimeInfo and kAsioCanTimeCode selectors in ASIOFuture (see example). - -note: -the AsioTimeInfo/ASIOTimeCode pair is supposed to work in both directions. -as a matter of convention, the relationship between the sample -position counter and the time code at buffer switch time is -(ignoring offset between tc and sample pos when tc is running): - -on input: sample 0 -> input buffer sample 0 -> time code 0 -on output: sample 0 -> output buffer sample 0 -> time code 0 - -this means that for 'real' calculations, one has to take into account -the according latencies. - -example: - -ASIOTime asioTime; - -in createBuffers() -{ - memset(&asioTime, 0, sizeof(ASIOTime)); - AsioTimeInfo* ti = &asioTime.timeInfo; - ti->sampleRate = theSampleRate; - ASIOTimeCode* tc = &asioTime.timeCode; - tc->speed = 1.; - timeInfoMode = false; - canTimeCode = false; - if(callbacks->asioMessage(kAsioSupportsTimeInfo, 0, 0, 0) == 1) - { - timeInfoMode = true; -#if kCanTimeCode - if(callbacks->asioMessage(kAsioSupportsTimeCode, 0, 0, 0) == 1) - canTimeCode = true; -#endif - } -} - -void switchBuffers(long doubleBufferIndex, bool processNow) -{ - if(timeInfoMode) - { - AsioTimeInfo* ti = &asioTime.timeInfo; - ti->flags = kSystemTimeValid | kSamplePositionValid | kSampleRateValid; - ti->systemTime = theNanoSeconds; - ti->samplePosition = theSamplePosition; - if(ti->sampleRate != theSampleRate) - ti->flags |= kSampleRateChanged; - ti->sampleRate = theSampleRate; - -#if kCanTimeCode - if(canTimeCode && timeCodeEnabled) - { - ASIOTimeCode* tc = &asioTime.timeCode; - tc->timeCodeSamples = tcSamples; // tc in samples - tc->flags = kTcValid | kTcRunning | kTcOnspeed; // if so... - } - ASIOTime* bb = callbacks->bufferSwitchTimeInfo(&asioTime, doubleBufferIndex, processNow ? ASIOTrue : ASIOFalse); -#else - callbacks->bufferSwitchTimeInfo(&asioTime, doubleBufferIndex, processNow ? ASIOTrue : ASIOFalse); -#endif - } - else - callbacks->bufferSwitch(doubleBufferIndex, ASIOFalse); -} - -ASIOError ASIOFuture(long selector, void *params) -{ - switch(selector) - { - case kAsioEnableTimeCodeRead: - timeCodeEnabled = true; - return ASE_SUCCESS; - case kAsioDisableTimeCodeRead: - timeCodeEnabled = false; - return ASE_SUCCESS; - case kAsioCanTimeInfo: - return ASE_SUCCESS; - #if kCanTimeCode - case kAsioCanTimeCode: - return ASE_SUCCESS; - #endif - } - return ASE_NotPresent; -}; - -*/ - -//- - - - - - - - - - - - - - - - - - - - - - - - - -// application's audio stream handler callbacks -//- - - - - - - - - - - - - - - - - - - - - - - - - - -typedef struct ASIOCallbacks -{ - void (*bufferSwitch) (long doubleBufferIndex, ASIOBool directProcess); - // bufferSwitch indicates that both input and output are to be processed. - // the current buffer half index (0 for A, 1 for B) determines - // - the output buffer that the host should start to fill. the other buffer - // will be passed to output hardware regardless of whether it got filled - // in time or not. - // - the input buffer that is now filled with incoming data. Note that - // because of the synchronicity of i/o, the input always has at - // least one buffer latency in relation to the output. - // directProcess suggests to the host whether it should immedeately - // start processing (directProcess == ASIOTrue), or whether its process - // should be deferred because the call comes from a very low level - // (for instance, a high level priority interrupt), and direct processing - // would cause timing instabilities for the rest of the system. If in doubt, - // directProcess should be set to ASIOFalse. - // Note: bufferSwitch may be called at interrupt time for highest efficiency. - - void (*sampleRateDidChange) (ASIOSampleRate sRate); - // gets called when the AudioStreamIO detects a sample rate change - // If sample rate is unknown, 0 is passed (for instance, clock loss - // when externally synchronized). - - long (*asioMessage) (long selector, long value, void* message, double* opt); - // generic callback for various purposes, see selectors below. - // note this is only present if the asio version is 2 or higher - - ASIOTime* (*bufferSwitchTimeInfo) (ASIOTime* params, long doubleBufferIndex, ASIOBool directProcess); - // new callback with time info. makes ASIOGetSamplePosition() and various - // calls to ASIOGetSampleRate obsolete, - // and allows for timecode sync etc. to be preferred; will be used if - // the driver calls asioMessage with selector kAsioSupportsTimeInfo. -} ASIOCallbacks; - -// asioMessage selectors -enum -{ - kAsioSelectorSupported = 1, // selector in , returns 1L if supported, - // 0 otherwise - kAsioEngineVersion, // returns engine (host) asio implementation version, - // 2 or higher - kAsioResetRequest, // request driver reset. if accepted, this - // will close the driver (ASIO_Exit() ) and - // re-open it again (ASIO_Init() etc). some - // drivers need to reconfigure for instance - // when the sample rate changes, or some basic - // changes have been made in ASIO_ControlPanel(). - // returns 1L; note the request is merely passed - // to the application, there is no way to determine - // if it gets accepted at this time (but it usually - // will be). - kAsioBufferSizeChange, // not yet supported, will currently always return 0L. - // for now, use kAsioResetRequest instead. - // once implemented, the new buffer size is expected - // in , and on success returns 1L - kAsioResyncRequest, // the driver went out of sync, such that - // the timestamp is no longer valid. this - // is a request to re-start the engine and - // slave devices (sequencer). returns 1 for ok, - // 0 if not supported. - kAsioLatenciesChanged, // the drivers latencies have changed. The engine - // will refetch the latencies. - kAsioSupportsTimeInfo, // if host returns true here, it will expect the - // callback bufferSwitchTimeInfo to be called instead - // of bufferSwitch - kAsioSupportsTimeCode, // - kAsioMMCCommand, // unused - value: number of commands, message points to mmc commands - kAsioSupportsInputMonitor, // kAsioSupportsXXX return 1 if host supports this - kAsioSupportsInputGain, // unused and undefined - kAsioSupportsInputMeter, // unused and undefined - kAsioSupportsOutputGain, // unused and undefined - kAsioSupportsOutputMeter, // unused and undefined - kAsioOverload, // driver detected an overload - - kAsioNumMessageSelectors -}; - -//--------------------------------------------------------------------------------------------------- -//--------------------------------------------------------------------------------------------------- - -//- - - - - - - - - - - - - - - - - - - - - - - - - -// (De-)Construction -//- - - - - - - - - - - - - - - - - - - - - - - - - - -typedef struct ASIODriverInfo -{ - long asioVersion; // currently, 2 - long driverVersion; // driver specific - char name[32]; - char errorMessage[124]; - void *sysRef; // on input: system reference - // (Windows: application main window handle, Mac & SGI: 0) -} ASIODriverInfo; - -ASIOError ASIOInit(ASIODriverInfo *info); -/* Purpose: - Initialize the AudioStreamIO. - Parameter: - info: pointer to an ASIODriver structure: - - asioVersion: - - on input, the host version. *** Note *** this is 0 for earlier asio - implementations, and the asioMessage callback is implemeted - only if asioVersion is 2 or greater. sorry but due to a design fault - the driver doesn't have access to the host version in ASIOInit :-( - added selector for host (engine) version in the asioMessage callback - so we're ok from now on. - - on return, asio implementation version. - older versions are 1 - if you support this version (namely, ASIO_outputReady() ) - this should be 2 or higher. also see the note in - ASIO_getTimeStamp() ! - - version: on return, the driver version (format is driver specific) - - name: on return, a null-terminated string containing the driver's name - - error message: on return, should contain a user message describing - the type of error that occured during ASIOInit(), if any. - - sysRef: platform specific - Returns: - If neither input nor output is present ASE_NotPresent - will be returned. - ASE_NoMemory, ASE_HWMalfunction are other possible error conditions -*/ - -ASIOError ASIOExit(void); -/* Purpose: - Terminates the AudioStreamIO. - Parameter: - None. - Returns: - If neither input nor output is present ASE_NotPresent - will be returned. - Notes: this implies ASIOStop() and ASIODisposeBuffers(), - meaning that no host callbacks must be accessed after ASIOExit(). -*/ - -//- - - - - - - - - - - - - - - - - - - - - - - - - -// Start/Stop -//- - - - - - - - - - - - - - - - - - - - - - - - - - -ASIOError ASIOStart(void); -/* Purpose: - Start input and output processing synchronously. - This will - - reset the sample counter to zero - - start the hardware (both input and output) - The first call to the hosts' bufferSwitch(index == 0) then tells - the host to read from input buffer A (index 0), and start - processing to output buffer A while output buffer B (which - has been filled by the host prior to calling ASIOStart()) - is possibly sounding (see also ASIOGetLatencies()) - Parameter: - None. - Returns: - If neither input nor output is present, ASE_NotPresent - will be returned. - If the hardware fails to start, ASE_HWMalfunction will be returned. - Notes: - There is no restriction on the time that ASIOStart() takes - to perform (that is, it is not considered a realtime trigger). -*/ - -ASIOError ASIOStop(void); -/* Purpose: - Stops input and output processing altogether. - Parameter: - None. - Returns: - If neither input nor output is present ASE_NotPresent - will be returned. - Notes: - On return from ASIOStop(), the driver must in no - case call the hosts' bufferSwitch() routine. -*/ - -//- - - - - - - - - - - - - - - - - - - - - - - - - -// Inquiry methods and sample rate -//- - - - - - - - - - - - - - - - - - - - - - - - - - -ASIOError ASIOGetChannels(long *numInputChannels, long *numOutputChannels); -/* Purpose: - Returns number of individual input/output channels. - Parameter: - numInputChannels will hold the number of available input channels - numOutputChannels will hold the number of available output channels - Returns: - If no input/output is present ASE_NotPresent will be returned. - If only inputs, or only outputs are available, the according - other parameter will be zero, and ASE_OK is returned. -*/ - -ASIOError ASIOGetLatencies(long *inputLatency, long *outputLatency); -/* Purpose: - Returns the input and output latencies. This includes - device specific delays, like FIFOs etc. - Parameter: - inputLatency will hold the 'age' of the first sample frame - in the input buffer when the hosts reads it in bufferSwitch() - (this is theoretical, meaning it does not include the overhead - and delay between the actual physical switch, and the time - when bufferSitch() enters). - This will usually be the size of one block in sample frames, plus - device specific latencies. - - outputLatency will specify the time between the buffer switch, - and the time when the next play buffer will start to sound. - The next play buffer is defined as the one the host starts - processing after (or at) bufferSwitch(), indicated by the - index parameter (0 for buffer A, 1 for buffer B). - It will usually be either one block, if the host writes directly - to a dma buffer, or two or more blocks if the buffer is 'latched' by - the driver. As an example, on ASIOStart(), the host will have filled - the play buffer at index 1 already; when it gets the callback (with - the parameter index == 0), this tells it to read from the input - buffer 0, and start to fill the play buffer 0 (assuming that now - play buffer 1 is already sounding). In this case, the output - latency is one block. If the driver decides to copy buffer 1 - at that time, and pass it to the hardware at the next slot (which - is most commonly done, but should be avoided), the output latency - becomes two blocks instead, resulting in a total i/o latency of at least - 3 blocks. As memory access is the main bottleneck in native dsp processing, - and to acheive less latency, it is highly recommended to try to avoid - copying (this is also why the driver is the owner of the buffers). To - summarize, the minimum i/o latency can be acheived if the input buffer - is processed by the host into the output buffer which will physically - start to sound on the next time slice. Also note that the host expects - the bufferSwitch() callback to be accessed for each time slice in order - to retain sync, possibly recursively; if it fails to process a block in - time, it will suspend its operation for some time in order to recover. - Returns: - If no input/output is present ASE_NotPresent will be returned. -*/ - -ASIOError ASIOGetBufferSize(long *minSize, long *maxSize, long *preferredSize, long *granularity); -/* Purpose: - Returns min, max, and preferred buffer sizes for input/output - Parameter: - minSize will hold the minimum buffer size - maxSize will hold the maxium possible buffer size - preferredSize will hold the preferred buffer size (a size which - best fits performance and hardware requirements) - granularity will hold the granularity at which buffer sizes - may differ. Usually, the buffer size will be a power of 2; - in this case, granularity will hold -1 on return, signalling - possible buffer sizes starting from minSize, increased in - powers of 2 up to maxSize. - Returns: - If no input/output is present ASE_NotPresent will be returned. - Notes: - When minimum and maximum buffer size are equal, - the preferred buffer size has to be the same value as well; granularity - should be 0 in this case. -*/ - -ASIOError ASIOCanSampleRate(ASIOSampleRate sampleRate); -/* Purpose: - Inquires the hardware for the available sample rates. - Parameter: - sampleRate is the rate in question. - Returns: - If the inquired sample rate is not supported, ASE_NoClock will be returned. - If no input/output is present ASE_NotPresent will be returned. -*/ -ASIOError ASIOGetSampleRate(ASIOSampleRate *currentRate); -/* Purpose: - Get the current sample Rate. - Parameter: - currentRate will hold the current sample rate on return. - Returns: - If sample rate is unknown, sampleRate will be 0 and ASE_NoClock will be returned. - If no input/output is present ASE_NotPresent will be returned. - Notes: -*/ - -ASIOError ASIOSetSampleRate(ASIOSampleRate sampleRate); -/* Purpose: - Set the hardware to the requested sample Rate. If sampleRate == 0, - enable external sync. - Parameter: - sampleRate: on input, the requested rate - Returns: - If sampleRate is unknown ASE_NoClock will be returned. - If the current clock is external, and sampleRate is != 0, - ASE_InvalidMode will be returned - If no input/output is present ASE_NotPresent will be returned. - Notes: -*/ - -typedef struct ASIOClockSource -{ - long index; // as used for ASIOSetClockSource() - long associatedChannel; // for instance, S/PDIF or AES/EBU - long associatedGroup; // see channel groups (ASIOGetChannelInfo()) - ASIOBool isCurrentSource; // ASIOTrue if this is the current clock source - char name[32]; // for user selection -} ASIOClockSource; - -ASIOError ASIOGetClockSources(ASIOClockSource *clocks, long *numSources); -/* Purpose: - Get the available external audio clock sources - Parameter: - clocks points to an array of ASIOClockSource structures: - - index: this is used to identify the clock source - when ASIOSetClockSource() is accessed, should be - an index counting from zero - - associatedInputChannel: the first channel of an associated - input group, if any. - - associatedGroup: the group index of that channel. - groups of channels are defined to seperate for - instance analog, S/PDIF, AES/EBU, ADAT connectors etc, - when present simultaniously. Note that associated channel - is enumerated according to numInputs/numOutputs, means it - is independant from a group (see also ASIOGetChannelInfo()) - inputs are associated to a clock if the physical connection - transfers both data and clock (like S/PDIF, AES/EBU, or - ADAT inputs). if there is no input channel associated with - the clock source (like Word Clock, or internal oscillator), both - associatedChannel and associatedGroup should be set to -1. - - isCurrentSource: on exit, ASIOTrue if this is the current clock - source, ASIOFalse else - - name: a null-terminated string for user selection of the available sources. - numSources: - on input: the number of allocated array members - on output: the number of available clock sources, at least - 1 (internal clock generator). - Returns: - If no input/output is present ASE_NotPresent will be returned. - Notes: -*/ - -ASIOError ASIOSetClockSource(long index); -/* Purpose: - Set the audio clock source - Parameter: - index as obtained from an inquiry to ASIOGetClockSources() - Returns: - If no input/output is present ASE_NotPresent will be returned. - If the clock can not be selected because an input channel which - carries the current clock source is active, ASE_InvalidMode - *may* be returned (this depends on the properties of the driver - and/or hardware). - Notes: - Should *not* return ASE_NoClock if there is no clock signal present - at the selected source; this will be inquired via ASIOGetSampleRate(). - It should call the host callback procedure sampleRateHasChanged(), - if the switch causes a sample rate change, or if no external clock - is present at the selected source. -*/ - -ASIOError ASIOGetSamplePosition (ASIOSamples *sPos, ASIOTimeStamp *tStamp); -/* Purpose: - Inquires the sample position/time stamp pair. - Parameter: - sPos will hold the sample position on return. The sample - position is reset to zero when ASIOStart() gets called. - tStamp will hold the system time when the sample position - was latched. - Returns: - If no input/output is present, ASE_NotPresent will be returned. - If there is no clock, ASE_SPNotAdvancing will be returned. - Notes: - - in order to be able to synchronise properly, - the sample position / time stamp pair must refer to the current block, - that is, the engine will call ASIOGetSamplePosition() in its bufferSwitch() - callback and expect the time for the current block. thus, when requested - in the very first bufferSwitch after ASIO_Start(), the sample position - should be zero, and the time stamp should refer to the very time where - the stream was started. it also means that the sample position must be - block aligned. the driver must ensure proper interpolation if the system - time can not be determined for the block position. the driver is responsible - for precise time stamps as it usually has most direct access to lower - level resources. proper behaviour of ASIO_GetSamplePosition() and ASIO_GetLatencies() - are essential for precise media synchronization! -*/ - -typedef struct ASIOChannelInfo -{ - long channel; // on input, channel index - ASIOBool isInput; // on input - ASIOBool isActive; // on exit - long channelGroup; // dto - ASIOSampleType type; // dto - char name[32]; // dto -} ASIOChannelInfo; - -ASIOError ASIOGetChannelInfo(ASIOChannelInfo *info); -/* Purpose: - retreive information about the nature of a channel - Parameter: - info: pointer to a ASIOChannelInfo structure with - - channel: on input, the channel index of the channel in question. - - isInput: on input, ASIOTrue if info for an input channel is - requested, else output - - channelGroup: on return, the channel group that the channel - belongs to. For drivers which support different types of - channels, like analog, S/PDIF, AES/EBU, ADAT etc interfaces, - there should be a reasonable grouping of these types. Groups - are always independant form a channel index, that is, a channel - index always counts from 0 to numInputs/numOutputs regardless - of the group it may belong to. - There will always be at least one group (group 0). Please - also note that by default, the host may decide to activate - channels 0 and 1; thus, these should belong to the most - useful type (analog i/o, if present). - - type: on return, contains the sample type of the channel - - isActive: on return, ASIOTrue if channel is active as it was - installed by ASIOCreateBuffers(), ASIOFalse else - - name: describing the type of channel in question. Used to allow - for user selection, and enabling of specific channels. examples: - "Analog In", "SPDIF Out" etc - Returns: - If no input/output is present ASE_NotPresent will be returned. - Notes: - If possible, the string should be organised such that the first - characters are most significantly describing the nature of the - port, to allow for identification even if the view showing the - port name is too small to display more than 8 characters, for - instance. -*/ - -//- - - - - - - - - - - - - - - - - - - - - - - - - -// Buffer preparation -//- - - - - - - - - - - - - - - - - - - - - - - - - - -typedef struct ASIOBufferInfo -{ - ASIOBool isInput; // on input: ASIOTrue: input, else output - long channelNum; // on input: channel index - void *buffers[2]; // on output: double buffer addresses -} ASIOBufferInfo; - -ASIOError ASIOCreateBuffers(ASIOBufferInfo *bufferInfos, long numChannels, - long bufferSize, ASIOCallbacks *callbacks); - -/* Purpose: - Allocates input/output buffers for all input and output channels to be activated. - Parameter: - bufferInfos is a pointer to an array of ASIOBufferInfo structures: - - isInput: on input, ASIOTrue if the buffer is to be allocated - for an input, output buffer else - - channelNum: on input, the index of the channel in question - (counting from 0) - - buffers: on exit, 2 pointers to the halves of the channels' double-buffer. - the size of the buffer(s) of course depend on both the ASIOSampleType - as obtained from ASIOGetChannelInfo(), and bufferSize - numChannels is the sum of all input and output channels to be created; - thus bufferInfos is a pointer to an array of numChannels ASIOBufferInfo - structures. - bufferSize selects one of the possible buffer sizes as obtained from - ASIOGetBufferSizes(). - callbacks is a pointer to an ASIOCallbacks structure. - Returns: - If not enough memory is available ASE_NoMemory will be returned. - If no input/output is present ASE_NotPresent will be returned. - If bufferSize is not supported, or one or more of the bufferInfos elements - contain invalid settings, ASE_InvalidMode will be returned. - Notes: - If individual channel selection is not possible but requested, - the driver has to handle this. namely, bufferSwitch() will only - have filled buffers of enabled outputs. If possible, processing - and buss activities overhead should be avoided for channels which - were not enabled here. -*/ - -ASIOError ASIODisposeBuffers(void); -/* Purpose: - Releases all buffers for the device. - Parameter: - None. - Returns: - If no buffer were ever prepared, ASE_InvalidMode will be returned. - If no input/output is present ASE_NotPresent will be returned. - Notes: - This implies ASIOStop(). -*/ - -ASIOError ASIOControlPanel(void); -/* Purpose: - request the driver to start a control panel component - for device specific user settings. This will not be - accessed on some platforms (where the component is accessed - instead). - Parameter: - None. - Returns: - If no panel is available ASE_NotPresent will be returned. - Actually, the return code is ignored. - Notes: - if the user applied settings which require a re-configuration - of parts or all of the enigine and/or driver (such as a change of - the block size), the asioMessage callback can be used (see - ASIO_Callbacks). -*/ - -ASIOError ASIOFuture(long selector, void *params); -/* Purpose: - various - Parameter: - selector: operation Code as to be defined. zero is reserved for - testing purposes. - params: depends on the selector; usually pointer to a structure - for passing and retreiving any type and amount of parameters. - Returns: - the return value is also selector dependant. if the selector - is unknown, ASE_InvalidParameter should be returned to prevent - further calls with this selector. on success, ASE_SUCCESS - must be returned (note: ASE_OK is *not* sufficient!) - Notes: - see selectors defined below. -*/ - -enum -{ - kAsioEnableTimeCodeRead = 1, // no arguments - kAsioDisableTimeCodeRead, // no arguments - kAsioSetInputMonitor, // ASIOInputMonitor* in params - kAsioTransport, // ASIOTransportParameters* in params - kAsioSetInputGain, // ASIOChannelControls* in params, apply gain - kAsioGetInputMeter, // ASIOChannelControls* in params, fill meter - kAsioSetOutputGain, // ASIOChannelControls* in params, apply gain - kAsioGetOutputMeter, // ASIOChannelControls* in params, fill meter - kAsioCanInputMonitor, // no arguments for kAsioCanXXX selectors - kAsioCanTimeInfo, - kAsioCanTimeCode, - kAsioCanTransport, - kAsioCanInputGain, - kAsioCanInputMeter, - kAsioCanOutputGain, - kAsioCanOutputMeter, - kAsioOptionalOne, - - // DSD support - // The following extensions are required to allow switching - // and control of the DSD subsystem. - kAsioSetIoFormat = 0x23111961, /* ASIOIoFormat * in params. */ - kAsioGetIoFormat = 0x23111983, /* ASIOIoFormat * in params. */ - kAsioCanDoIoFormat = 0x23112004, /* ASIOIoFormat * in params. */ - - // Extension for drop out detection - kAsioCanReportOverload = 0x24042012, /* return ASE_SUCCESS if driver can detect and report overloads */ - - kAsioGetInternalBufferSamples = 0x25042012 /* ASIOInternalBufferInfo * in params. Deliver size of driver internal buffering, return ASE_SUCCESS if supported */ -}; - -typedef struct ASIOInputMonitor -{ - long input; // this input was set to monitor (or off), -1: all - long output; // suggested output for monitoring the input (if so) - long gain; // suggested gain, ranging 0 - 0x7fffffffL (-inf to +12 dB) - ASIOBool state; // ASIOTrue => on, ASIOFalse => off - long pan; // suggested pan, 0 => all left, 0x7fffffff => right -} ASIOInputMonitor; - -typedef struct ASIOChannelControls -{ - long channel; // on input, channel index - ASIOBool isInput; // on input - long gain; // on input, ranges 0 thru 0x7fffffff - long meter; // on return, ranges 0 thru 0x7fffffff - char future[32]; -} ASIOChannelControls; - -typedef struct ASIOTransportParameters -{ - long command; // see enum below - ASIOSamples samplePosition; - long track; - long trackSwitches[16]; // 512 tracks on/off - char future[64]; -} ASIOTransportParameters; - -enum -{ - kTransStart = 1, - kTransStop, - kTransLocate, // to samplePosition - kTransPunchIn, - kTransPunchOut, - kTransArmOn, // track - kTransArmOff, // track - kTransMonitorOn, // track - kTransMonitorOff, // track - kTransArm, // trackSwitches - kTransMonitor // trackSwitches -}; - -/* -// DSD support -// Some notes on how to use ASIOIoFormatType. -// -// The caller will fill the format with the request types. -// If the board can do the request then it will leave the -// values unchanged. If the board does not support the -// request then it will change that entry to Invalid (-1) -// -// So to request DSD then -// -// ASIOIoFormat NeedThis={kASIODSDFormat}; -// -// if(ASE_SUCCESS != ASIOFuture(kAsioSetIoFormat,&NeedThis) ){ -// // If the board did not accept one of the parameters then the -// // whole call will fail and the failing parameter will -// // have had its value changes to -1. -// } -// -// Note: Switching between the formats need to be done before the "prepared" -// state (see ASIO 2 documentation) is entered. -*/ -typedef long int ASIOIoFormatType; -enum ASIOIoFormatType_e -{ - kASIOFormatInvalid = -1, - kASIOPCMFormat = 0, - kASIODSDFormat = 1, -}; - -typedef struct ASIOIoFormat_s -{ - ASIOIoFormatType FormatType; - char future[512-sizeof(ASIOIoFormatType)]; -} ASIOIoFormat; - -// Extension for drop detection -// Note: Refers to buffering that goes beyond the double buffer e.g. used by USB driver designs -typedef struct ASIOInternalBufferInfo -{ - long inputSamples; // size of driver's internal input buffering which is included in getLatencies - long outputSamples; // size of driver's internal output buffering which is included in getLatencies -} ASIOInternalBufferInfo; - - -ASIOError ASIOOutputReady(void); -/* Purpose: - this tells the driver that the host has completed processing - the output buffers. if the data format required by the hardware - differs from the supported asio formats, but the hardware - buffers are DMA buffers, the driver will have to convert - the audio stream data; as the bufferSwitch callback is - usually issued at dma block switch time, the driver will - have to convert the *previous* host buffer, which increases - the output latency by one block. - when the host finds out that ASIOOutputReady() returns - true, it will issue this call whenever it completed - output processing. then the driver can convert the - host data directly to the dma buffer to be played next, - reducing output latency by one block. - another way to look at it is, that the buffer switch is called - in order to pass the *input* stream to the host, so that it can - process the input into the output, and the output stream is passed - to the driver when the host has completed its process. - Parameter: - None - Returns: - only if the above mentioned scenario is given, and a reduction - of output latency can be acheived by this mechanism, should - ASE_OK be returned. otherwise (and usually), ASE_NotPresent - should be returned in order to prevent further calls to this - function. note that the host may want to determine if it is - to use this when the system is not yet fully initialized, so - ASE_OK should always be returned if the mechanism makes sense. - Notes: - please remeber to adjust ASIOGetLatencies() according to - whether ASIOOutputReady() was ever called or not, if your - driver supports this scenario. - also note that the engine may fail to call ASIO_OutputReady() - in time in overload cases. as already mentioned, bufferSwitch - should be called for every block regardless of whether a block - could be processed in time. -*/ - -// restore old alignment -#if defined(_MSC_VER) && !defined(__MWERKS__) -#pragma pack(pop) -#elif PRAGMA_ALIGN_SUPPORTED -#pragma options align = reset -#endif - -#endif - diff --git a/ASIO/asiodrvr.h b/ASIO/asiodrvr.h deleted file mode 100755 index 663f75a..0000000 --- a/ASIO/asiodrvr.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - Steinberg Audio Stream I/O API - (c) 1996, Steinberg Soft- und Hardware GmbH - charlie (May 1996) - - asiodrvr.h - c++ superclass to implement asio functionality. from this, - you can derive whatever required -*/ - -#ifndef _asiodrvr_ -#define _asiodrvr_ - -// cpu and os system we are running on -#include "asiosys.h" -// basic "C" interface -#include "asio.h" - -class AsioDriver; -extern AsioDriver *getDriver(); // for generic constructor - -#if WINDOWS -#include -#include "combase.h" -#include "iasiodrv.h" -class AsioDriver : public IASIO ,public CUnknown -{ -public: - AsioDriver(LPUNKNOWN pUnk, HRESULT *phr); - - DECLARE_IUNKNOWN - // Factory method - static CUnknown *CreateInstance(LPUNKNOWN pUnk, HRESULT *phr); - // IUnknown - virtual HRESULT STDMETHODCALLTYPE NonDelegatingQueryInterface(REFIID riid,void **ppvObject); - -#else - -class AsioDriver -{ -public: - AsioDriver(); -#endif - virtual ~AsioDriver(); - - virtual ASIOBool init(void* sysRef); - virtual void getDriverName(char *name); // max 32 bytes incl. terminating zero - virtual long getDriverVersion(); - virtual void getErrorMessage(char *string); // max 124 bytes incl. - - virtual ASIOError start(); - virtual ASIOError stop(); - - virtual ASIOError getChannels(long *numInputChannels, long *numOutputChannels); - virtual ASIOError getLatencies(long *inputLatency, long *outputLatency); - virtual ASIOError getBufferSize(long *minSize, long *maxSize, - long *preferredSize, long *granularity); - - virtual ASIOError canSampleRate(ASIOSampleRate sampleRate); - virtual ASIOError getSampleRate(ASIOSampleRate *sampleRate); - virtual ASIOError setSampleRate(ASIOSampleRate sampleRate); - virtual ASIOError getClockSources(ASIOClockSource *clocks, long *numSources); - virtual ASIOError setClockSource(long reference); - - virtual ASIOError getSamplePosition(ASIOSamples *sPos, ASIOTimeStamp *tStamp); - virtual ASIOError getChannelInfo(ASIOChannelInfo *info); - - virtual ASIOError createBuffers(ASIOBufferInfo *bufferInfos, long numChannels, - long bufferSize, ASIOCallbacks *callbacks); - virtual ASIOError disposeBuffers(); - - virtual ASIOError controlPanel(); - virtual ASIOError future(long selector, void *opt); - virtual ASIOError outputReady(); -}; -#endif diff --git a/ASIO/asiosys.h b/ASIO/asiosys.h deleted file mode 100755 index 003cf1a..0000000 --- a/ASIO/asiosys.h +++ /dev/null @@ -1,82 +0,0 @@ -#ifndef __asiosys__ - #define __asiosys__ - - #if defined(_WIN32) || defined(_WIN64) - #undef MAC - #define PPC 0 - #define WINDOWS 1 - #define SGI 0 - #define SUN 0 - #define LINUX 0 - #define BEOS 0 - - #define NATIVE_INT64 0 - #define IEEE754_64FLOAT 1 - - #elif BEOS - #define MAC 0 - #define PPC 0 - #define WINDOWS 0 - #define PC 0 - #define SGI 0 - #define SUN 0 - #define LINUX 0 - - #define NATIVE_INT64 0 - #define IEEE754_64FLOAT 1 - - #ifndef DEBUG - #define DEBUG 0 - #if DEBUG - void DEBUGGERMESSAGE(char *string); - #else - #define DEBUGGERMESSAGE(a) - #endif - #endif - - #elif SGI - #define MAC 0 - #define PPC 0 - #define WINDOWS 0 - #define PC 0 - #define SUN 0 - #define LINUX 0 - #define BEOS 0 - - #define NATIVE_INT64 0 - #define IEEE754_64FLOAT 1 - - #ifndef DEBUG - #define DEBUG 0 - #if DEBUG - void DEBUGGERMESSAGE(char *string); - #else - #define DEBUGGERMESSAGE(a) - #endif - #endif - - #else // MAC - - #define MAC 1 - #define PPC 1 - #define WINDOWS 0 - #define PC 0 - #define SGI 0 - #define SUN 0 - #define LINUX 0 - #define BEOS 0 - - #define NATIVE_INT64 0 - #define IEEE754_64FLOAT 1 - - #ifndef DEBUG - #define DEBUG 0 - #if DEBUG - void DEBUGGERMESSAGE(char *string); - #else - #define DEBUGGERMESSAGE(a) - #endif - #endif - #endif - -#endif diff --git a/ASIO/combase.h b/ASIO/combase.h deleted file mode 100755 index fc51c94..0000000 --- a/ASIO/combase.h +++ /dev/null @@ -1,284 +0,0 @@ -//==========================================================================; -// -// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY -// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR -// PURPOSE. -// -// Copyright (c) 1992 - 1996 Microsoft Corporation. All Rights Reserved. -// -//--------------------------------------------------------------------------; - -// Base class hierachy for creating COM objects, December 1994 - -/* - -a. Derive your COM object from CUnknown - -b. Make a static CreateInstance function that takes an LPUNKNOWN, an HRESULT * - and a TCHAR *. The LPUNKNOWN defines the object to delegate IUnknown calls - to. The HRESULT * allows error codes to be passed around constructors and - the TCHAR * is a descriptive name that can be printed on the debugger. - - It is important that constructors only change the HRESULT * if they have - to set an ERROR code, if it was successful then leave it alone or you may - overwrite an error code from an object previously created. - - When you call a constructor the descriptive name should be in static store - as we do not copy the string. To stop large amounts of memory being used - in retail builds by all these static strings use the NAME macro, - - CMyFilter = new CImplFilter(NAME("My filter"),pUnknown,phr); - if (FAILED(hr)) { - return hr; - } - - In retail builds NAME(_x_) compiles to NULL, the base CBaseObject class - knows not to do anything with objects that don't have a name. - -c. Have a constructor for your object that passes the LPUNKNOWN, HRESULT * and - TCHAR * to the CUnknown constructor. You can set the HRESULT if you have an - error, or just simply pass it through to the constructor. - - The object creation will fail in the class factory if the HRESULT indicates - an error (ie FAILED(HRESULT) == TRUE) - -d. Create a FactoryTemplate with your object's class id and CreateInstance - function. - -Then (for each interface) either - -Multiple inheritance - -1. Also derive it from ISomeInterface -2. Include DECLARE_IUNKNOWN in your class definition to declare - implementations of QueryInterface, AddRef and Release that - call the outer unknown -3. Override NonDelegatingQueryInterface to expose ISomeInterface by - code something like - - if (riid == IID_ISomeInterface) { - return GetInterface((ISomeInterface *) this, ppv); - } else { - return CUnknown::NonDelegatingQueryInterface(riid, ppv); - } - -4. Declare and implement the member functions of ISomeInterface. - -or: Nested interfaces - -1. Declare a class derived from CUnknown -2. Include DECLARE_IUNKNOWN in your class definition -3. Override NonDelegatingQueryInterface to expose ISomeInterface by - code something like - - if (riid == IID_ISomeInterface) { - return GetInterface((ISomeInterface *) this, ppv); - } else { - return CUnknown::NonDelegatingQueryInterface(riid, ppv); - } - -4. Implement the member functions of ISomeInterface. Use GetOwner() to - access the COM object class. - -And in your COM object class: - -5. Make the nested class a friend of the COM object class, and declare - an instance of the nested class as a member of the COM object class. - - NOTE that because you must always pass the outer unknown and an hResult - to the CUnknown constructor you cannot use a default constructor, in - other words you will have to make the member variable a pointer to the - class and make a NEW call in your constructor to actually create it. - -6. override the NonDelegatingQueryInterface with code like this: - - if (riid == IID_ISomeInterface) { - return m_pImplFilter-> - NonDelegatingQueryInterface(IID_ISomeInterface, ppv); - } else { - return CUnknown::NonDelegatingQueryInterface(riid, ppv); - } - -You can have mixed classes which support some interfaces via multiple -inheritance and some via nested classes - -*/ - -#ifndef __COMBASE__ -#define __COMBASE__ - -/* The DLLENTRY module initialises the module handle on loading */ - -extern HINSTANCE g_hInst; - -/* On DLL load remember which platform we are running on */ - -extern DWORD g_amPlatform; -extern OSVERSIONINFO g_osInfo; // Filled in by GetVersionEx - -/* Version of IUnknown that is renamed to allow a class to support both - non delegating and delegating IUnknowns in the same COM object */ - -DECLARE_INTERFACE(INonDelegatingUnknown) -{ - STDMETHOD(NonDelegatingQueryInterface) (THIS_ REFIID, LPVOID *) PURE; - STDMETHOD_(ULONG, NonDelegatingAddRef)(THIS) PURE; - STDMETHOD_(ULONG, NonDelegatingRelease)(THIS) PURE; -}; - -typedef INonDelegatingUnknown *PNDUNKNOWN; - - -/* This is the base object class that supports active object counting. As - part of the debug facilities we trace every time a C++ object is created - or destroyed. The name of the object has to be passed up through the class - derivation list during construction as you cannot call virtual functions - in the constructor. The downside of all this is that every single object - constructor has to take an object name parameter that describes it */ - -class CBaseObject -{ - -private: - - // Disable the copy constructor and assignment by default so you will get - // compiler errors instead of unexpected behaviour if you pass objects - // by value or assign objects. - CBaseObject(const CBaseObject& objectSrc); // no implementation - void operator=(const CBaseObject& objectSrc); // no implementation - -private: - static LONG m_cObjects; /* Total number of objects active */ - -protected: -#ifdef DEBUG - DWORD m_dwCookie; /* Cookie identifying this object */ -#endif - - -public: - - /* These increment and decrement the number of active objects */ - - CBaseObject(TCHAR *pName); - ~CBaseObject(); - - /* Call this to find if there are any CUnknown derived objects active */ - - static LONG ObjectsActive() { - return m_cObjects; - }; -}; - - -/* An object that supports one or more COM interfaces will be based on - this class. It supports counting of total objects for DLLCanUnloadNow - support, and an implementation of the core non delegating IUnknown */ - -class CUnknown : public INonDelegatingUnknown, - public CBaseObject -{ - -private: - const LPUNKNOWN m_pUnknown; /* Owner of this object */ - -protected: /* So we can override NonDelegatingRelease() */ - volatile LONG m_cRef; /* Number of reference counts */ - -public: - - CUnknown(TCHAR *pName, LPUNKNOWN pUnk, HRESULT *phr); - virtual ~CUnknown() {}; - - /* Return the owner of this object */ - - LPUNKNOWN GetOwner() const { - return m_pUnknown; - }; - - /* Called from the class factory to create a new instance, it is - pure virtual so it must be overriden in your derived class */ - - /* static CUnknown *CreateInstance(LPUNKNOWN, HRESULT *) */ - - /* Non delegating unknown implementation */ - - STDMETHODIMP NonDelegatingQueryInterface(REFIID, void **); - STDMETHODIMP_(ULONG) NonDelegatingAddRef(); - STDMETHODIMP_(ULONG) NonDelegatingRelease(); - - /* Return an interface pointer to a requesting client - performing a thread safe AddRef as necessary */ - - HRESULT GetInterface(LPUNKNOWN pUnk, void **ppv); - - -}; - -#if WINVER < 0x0501 - -/* The standard InterlockedXXX functions won't take volatiles */ -static inline LONG InterlockedIncrement( volatile LONG * plong ) -{ return InterlockedIncrement( const_cast( plong ) ); } - -static inline LONG InterlockedDecrement( volatile LONG * plong ) -{ return InterlockedDecrement( const_cast( plong ) ); } - -static inline LONG InterlockedExchange( volatile LONG * plong, LONG new_value ) -{ return InterlockedExchange( const_cast( plong ), new_value ); } - -#endif - -/* A function that can create a new COM object */ - -typedef CUnknown *(*LPFNNewCOMObject)(LPUNKNOWN pUnkOuter, HRESULT *phr); - -/* A function (can be NULL) which is called from the DLL entrypoint - routine for each factory template: - - bLoading - TRUE on DLL load, FALSE on DLL unload - rclsid - the m_ClsID of the entry -*/ -typedef void (*LPFNInitRoutine)(BOOL bLoading, const CLSID *rclsid); - -/* Create one of these per object class in an array so that - the default class factory code can create new instances */ - -class CFactoryTemplate { - -public: - - const WCHAR *m_Name; - const CLSID *m_ClsID; - LPFNNewCOMObject m_lpfnNew; - LPFNInitRoutine m_lpfnInit; - - BOOL IsClassID(REFCLSID rclsid) const { - return (IsEqualCLSID(*m_ClsID,rclsid)); - }; - - CUnknown *CreateInstance(LPUNKNOWN pUnk, HRESULT *phr) const { - return m_lpfnNew(pUnk, phr); - }; -}; - - -/* You must override the (pure virtual) NonDelegatingQueryInterface to return - interface pointers (using GetInterface) to the interfaces your derived - class supports (the default implementation only supports IUnknown) */ - -#define DECLARE_IUNKNOWN \ - STDMETHODIMP QueryInterface(REFIID riid, void **ppv) { \ - return GetOwner()->QueryInterface(riid,ppv); \ - }; \ - STDMETHODIMP_(ULONG) AddRef() { \ - return GetOwner()->AddRef(); \ - }; \ - STDMETHODIMP_(ULONG) Release() { \ - return GetOwner()->Release(); \ - }; - -#endif /* __COMBASE__ */ - - diff --git a/ASIO/iasiodrv.h b/ASIO/iasiodrv.h deleted file mode 100755 index 64d2dbb..0000000 --- a/ASIO/iasiodrv.h +++ /dev/null @@ -1,37 +0,0 @@ -#include "asiosys.h" -#include "asio.h" - -/* Forward Declarations */ - -#ifndef __ASIODRIVER_FWD_DEFINED__ -#define __ASIODRIVER_FWD_DEFINED__ -typedef interface IASIO IASIO; -#endif /* __ASIODRIVER_FWD_DEFINED__ */ - -interface IASIO : public IUnknown -{ - - virtual ASIOBool init(void *sysHandle) = 0; - virtual void getDriverName(char *name) = 0; - virtual long getDriverVersion() = 0; - virtual void getErrorMessage(char *string) = 0; - virtual ASIOError start() = 0; - virtual ASIOError stop() = 0; - virtual ASIOError getChannels(long *numInputChannels, long *numOutputChannels) = 0; - virtual ASIOError getLatencies(long *inputLatency, long *outputLatency) = 0; - virtual ASIOError getBufferSize(long *minSize, long *maxSize, - long *preferredSize, long *granularity) = 0; - virtual ASIOError canSampleRate(ASIOSampleRate sampleRate) = 0; - virtual ASIOError getSampleRate(ASIOSampleRate *sampleRate) = 0; - virtual ASIOError setSampleRate(ASIOSampleRate sampleRate) = 0; - virtual ASIOError getClockSources(ASIOClockSource *clocks, long *numSources) = 0; - virtual ASIOError setClockSource(long reference) = 0; - virtual ASIOError getSamplePosition(ASIOSamples *sPos, ASIOTimeStamp *tStamp) = 0; - virtual ASIOError getChannelInfo(ASIOChannelInfo *info) = 0; - virtual ASIOError createBuffers(ASIOBufferInfo *bufferInfos, long numChannels, - long bufferSize, ASIOCallbacks *callbacks) = 0; - virtual ASIOError disposeBuffers() = 0; - virtual ASIOError controlPanel() = 0; - virtual ASIOError future(long selector,void *opt) = 0; - virtual ASIOError outputReady() = 0; -}; diff --git a/ASIO/wxdebug.h b/ASIO/wxdebug.h deleted file mode 100755 index 3d8585c..0000000 --- a/ASIO/wxdebug.h +++ /dev/null @@ -1,326 +0,0 @@ -//==========================================================================; -// -// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY -// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR -// PURPOSE. -// -// Copyright (c) 1992 - 1996 Microsoft Corporation. All Rights Reserved. -// -//--------------------------------------------------------------------------; - -// Debugging facilities, January 1995 - -#ifndef __WXDEBUG__ -#define __WXDEBUG__ - -// Avoid conflict with MFC -#undef ASSERT - -// This library provides fairly straight forward debugging functionality, this -// is split into two main sections. The first is assertion handling, there are -// three types of assertions provided here. The most commonly used one is the -// ASSERT(condition) macro which will pop up a message box including the file -// and line number if the condition evaluates to FALSE. Then there is the -// EXECUTE_ASSERT macro which is the same as ASSERT except the condition will -// still be executed in NON debug builds. The final type of assertion is the -// KASSERT macro which is more suitable for pure (perhaps kernel) filters as -// the condition is printed onto the debugger rather than in a message box. -// -// The other part of the debug module facilties is general purpose logging. -// This is accessed by calling DbgLog(). The function takes a type and level -// field which define the type of informational string you are presenting and -// it's relative importance. The type field can be a combination (one or more) -// of LOG_TIMING, LOG_TRACE, LOG_MEMORY, LOG_LOCKING and LOG_ERROR. The level -// is a DWORD value where zero defines highest important. Use of zero as the -// debug logging level is to be encouraged ONLY for major errors or events as -// they will ALWAYS be displayed on the debugger. Other debug output has it's -// level matched against the current debug output level stored in the registry -// for this module and if less than the current setting it will be displayed. -// -// Each module or executable has it's own debug output level for each of the -// five types. These are read in when the DbgInitialise function is called -// for DLLs linking to STRMBASE.LIB this is done automatically when the DLL -// is loaded, executables must call it explicitely with the module instance -// handle given to them through the WINMAIN entry point. An executable must -// also call DbgTerminate when they have finished to clean up the resources -// the debug library uses, once again this is done automatically for DLLs - -// These are the five different categories of logging information - -enum { LOG_TIMING = 0x01, // Timing and performance measurements - LOG_TRACE = 0x02, // General step point call tracing - LOG_MEMORY = 0x04, // Memory and object allocation/destruction - LOG_LOCKING = 0x08, // Locking/unlocking of critical sections - LOG_ERROR = 0x10 }; // Debug error notification - -enum { CDISP_HEX = 0x01, - CDISP_DEC = 0x02}; - -// For each object created derived from CBaseObject (in debug builds) we -// create a descriptor that holds it's name (statically allocated memory) -// and a cookie we assign it. We keep a list of all the active objects -// we have registered so that we can dump a list of remaining objects - -typedef struct tag_ObjectDesc { - TCHAR *m_pName; - DWORD m_dwCookie; - tag_ObjectDesc *m_pNext; -} ObjectDesc; - -#define DLLIMPORT __declspec(dllimport) -#define DLLEXPORT __declspec(dllexport) - -#ifdef DEBUG - - #define NAME(x) TEXT(x) - - // These are used internally by the debug library (PRIVATE) - - void DbgInitKeyLevels(HKEY hKey); - void DbgInitGlobalSettings(); - void DbgInitModuleSettings(); - void DbgInitModuleName(); - DWORD DbgRegisterObjectCreation(TCHAR *pObjectName); - BOOL DbgRegisterObjectDestruction(DWORD dwCookie); - - // These are the PUBLIC entry points - - BOOL DbgCheckModuleLevel(DWORD Type,DWORD Level); - void DbgSetModuleLevel(DWORD Type,DWORD Level); - - // Initialise the library with the module handle - - void DbgInitialise(HINSTANCE hInst); - void DbgTerminate(); - - void DbgDumpObjectRegister(); - - // Display error and logging to the user - - void DbgAssert(const TCHAR *pCondition,const TCHAR *pFileName,INT iLine); - void DbgBreakPoint(const TCHAR *pCondition,const TCHAR *pFileName,INT iLine); - void DbgKernelAssert(const TCHAR *pCondition,const TCHAR *pFileName,INT iLine); - void DbgLogInfo(DWORD Type,DWORD Level,const TCHAR *pFormat,...); - void DbgOutString(LPCTSTR psz); - - // Debug infinite wait stuff - DWORD DbgWaitForSingleObject(HANDLE h); - DWORD DbgWaitForMultipleObjects(DWORD nCount, - CONST HANDLE *lpHandles, - BOOL bWaitAll); - void DbgSetWaitTimeout(DWORD dwTimeout); - -#ifdef __strmif_h__ - void DisplayType(LPSTR label, const AM_MEDIA_TYPE *pmtIn); -#endif - - #define KASSERT(_x_) if (!(_x_)) \ - DbgKernelAssert(TEXT(#_x_),TEXT(__FILE__),__LINE__) - - // Break on the debugger without putting up a message box - // message goes to debugger instead - - #define KDbgBreak(_x_) \ - DbgKernelAssert(TEXT(#_x_),TEXT(__FILE__),__LINE__) - - #define ASSERT(_x_) if (!(_x_)) \ - DbgAssert(TEXT(#_x_),TEXT(__FILE__),__LINE__) - - // Put up a message box informing the user of a halt - // condition in the program - - #define DbgBreak(_x_) \ - DbgBreakPoint(TEXT(#_x_),TEXT(__FILE__),__LINE__) - - #define EXECUTE_ASSERT(_x_) ASSERT(_x_) - #define DbgLog(_x_) DbgLogInfo _x_ - - // MFC style trace macros - - #define NOTE(_x_) DbgLog((LOG_TRACE,5,TEXT(_x_))); - #define NOTE1(_x_,a) DbgLog((LOG_TRACE,5,TEXT(_x_),a)); - #define NOTE2(_x_,a,b) DbgLog((LOG_TRACE,5,TEXT(_x_),a,b)); - #define NOTE3(_x_,a,b,c) DbgLog((LOG_TRACE,5,TEXT(_x_),a,b,c)); - #define NOTE4(_x_,a,b,c,d) DbgLog((LOG_TRACE,5,TEXT(_x_),a,b,c,d)); - #define NOTE5(_x_,a,b,c,d,e) DbgLog((LOG_TRACE,5,TEXT(_x_),a,b,c,d,e)); - -#else - - // Retail builds make public debug functions inert - WARNING the source - // files do not define or build any of the entry points in debug builds - // (public entry points compile to nothing) so if you go trying to call - // any of the private entry points in your source they won't compile - - #define NAME(_x_) NULL - - #define DbgInitialise(hInst) - #define DbgTerminate() - #define DbgLog(_x_) - #define DbgOutString(psz) - - #define DbgRegisterObjectCreation(pObjectName) - #define DbgRegisterObjectDestruction(dwCookie) - #define DbgDumpObjectRegister() - - #define DbgCheckModuleLevel(Type,Level) - #define DbgSetModuleLevel(Type,Level) - - #define DbgWaitForSingleObject(h) WaitForSingleObject(h, INFINITE) - #define DbgWaitForMultipleObjects(nCount, lpHandles, bWaitAll) \ - WaitForMultipleObjects(nCount, lpHandles, bWaitAll, INFINITE) - #define DbgSetWaitTimeout(dwTimeout) - - #define KDbgBreak(_x_) - #define DbgBreak(_x_) - - #define KASSERT(_x_) - #define ASSERT(_x_) - #define EXECUTE_ASSERT(_x_) _x_ - - // MFC style trace macros - - #define NOTE(_x_) - #define NOTE1(_x_,a) - #define NOTE2(_x_,a,b) - #define NOTE3(_x_,a,b,c) - #define NOTE4(_x_,a,b,c,d) - #define NOTE5(_x_,a,b,c,d,e) - - #define DisplayType(label, pmtIn) - -#endif - - -// Checks a pointer which should be non NULL - can be used as follows. - -#define CheckPointer(p,ret) {if((p)==NULL) return (ret);} - -// HRESULT Foo(VOID *pBar) -// { -// CheckPointer(pBar,E_INVALIDARG) -// } -// -// Or if the function returns a boolean -// -// BOOL Foo(VOID *pBar) -// { -// CheckPointer(pBar,FALSE) -// } - -// These validate pointers when symbol VFWROBUST is defined -// This will normally be defined in debug not retail builds - -#ifdef DEBUG - #define VFWROBUST -#endif - -#ifdef VFWROBUST - - #define ValidateReadPtr(p,cb) \ - {if(IsBadReadPtr((PVOID)p,cb) == TRUE) \ - DbgBreak("Invalid read pointer");} - - #define ValidateWritePtr(p,cb) \ - {if(IsBadWritePtr((PVOID)p,cb) == TRUE) \ - DbgBreak("Invalid write pointer");} - - #define ValidateReadWritePtr(p,cb) \ - {ValidateReadPtr(p,cb) ValidateWritePtr(p,cb)} - - #define ValidateStringPtr(p) \ - {if(IsBadStringPtr((LPCTSTR)p,INFINITE) == TRUE) \ - DbgBreak("Invalid string pointer");} - - #define ValidateStringPtrA(p) \ - {if(IsBadStringPtrA((LPCSTR)p,INFINITE) == TRUE) \ - DbgBreak("Invalid ANSII string pointer");} - - #define ValidateStringPtrW(p) \ - {if(IsBadStringPtrW((LPCWSTR)p,INFINITE) == TRUE) \ - DbgBreak("Invalid UNICODE string pointer");} - -#else - #define ValidateReadPtr(p,cb) - #define ValidateWritePtr(p,cb) - #define ValidateReadWritePtr(p,cb) - #define ValidateStringPtr(p) - #define ValidateStringPtrA(p) - #define ValidateStringPtrW(p) -#endif - - -#ifdef _OBJBASE_H_ - - // Outputting GUID names. If you want to include the name - // associated with a GUID (eg CLSID_...) then - // - // GuidNames[yourGUID] - // - // Returns the name defined in uuids.h as a string - - typedef struct { - TCHAR *szName; - GUID guid; - } GUID_STRING_ENTRY; - - class CGuidNameList { - public: - TCHAR *operator [] (const GUID& guid); - }; - - extern CGuidNameList GuidNames; - -#endif - - -// REMIND macro - generates warning as reminder to complete coding -// (eg) usage: -// -// #pragma message (REMIND("Add automation support")) - - -#define QUOTE(x) #x -#define QQUOTE(y) QUOTE(y) -#define REMIND(str) __FILE__ "(" QQUOTE(__LINE__) ") : " str - - -// Hack to display objects in a useful format -// -// eg If you want to display a LONGLONG ll in a debug string do (eg) -// -// DbgLog((LOG_TRACE, n, TEXT("Value is %s"), (LPCTSTR)CDisp(ll, CDISP_HEX))); - - -class CDispBasic -{ -public: - CDispBasic() { m_pString = m_String; }; - ~CDispBasic(); -protected: - PTCHAR m_pString; // normally points to m_String... unless too much data - TCHAR m_String[50]; -}; -class CDisp : public CDispBasic -{ -public: - CDisp(LONGLONG ll, int Format = CDISP_HEX); // Display a LONGLONG in CDISP_HEX or CDISP_DEC form - CDisp(REFCLSID clsid); // Display a GUID - CDisp(double d); // Display a floating point number -#ifdef __strmif_h__ -#ifdef __STREAMS__ - CDisp(CRefTime t); // Display a Reference Time -#endif - CDisp(IPin *pPin); // Display a pin as {filter clsid}(pin name) -#endif // __strmif_h__ - ~CDisp(); - - // Implement cast to (LPCTSTR) as parameter to logger - operator LPCTSTR() - { - return (LPCTSTR)m_pString; - }; -}; - -#endif // __WXDEBUG__ - diff --git a/JuceLibraryCode/AppConfig.h b/JuceLibraryCode/AppConfig.h index b075c4f..c729d3d 100644 --- a/JuceLibraryCode/AppConfig.h +++ b/JuceLibraryCode/AppConfig.h @@ -390,13 +390,13 @@ #define JucePlugin_EditorRequiresKeyboardFocus 0 #endif #ifndef JucePlugin_Version - #define JucePlugin_Version 1.0.0 + #define JucePlugin_Version 1.0.1 #endif #ifndef JucePlugin_VersionCode - #define JucePlugin_VersionCode 0x10000 + #define JucePlugin_VersionCode 0x10001 #endif #ifndef JucePlugin_VersionString - #define JucePlugin_VersionString "1.0.0" + #define JucePlugin_VersionString "1.0.1" #endif #ifndef JucePlugin_VSTUniqueID #define JucePlugin_VSTUniqueID JucePlugin_PluginCode diff --git a/JuceLibraryCode/JuceHeader.h b/JuceLibraryCode/JuceHeader.h index 3abe38d..ab8e813 100644 --- a/JuceLibraryCode/JuceHeader.h +++ b/JuceLibraryCode/JuceHeader.h @@ -50,7 +50,7 @@ namespace ProjectInfo { const char* const projectName = "eBeamer"; const char* const companyName = "Polimi ISPL - Eventide"; - const char* const versionString = "1.0.0"; - const int versionNumber = 0x10000; + const char* const versionString = "1.0.1"; + const int versionNumber = 0x10001; } #endif diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index bb5dc3e..ee4d11d 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -131,7 +131,7 @@ bool EbeamerAudioProcessor::isBusesLayoutSupported(const BusesLayout &layouts) c return false; } } - if ((layouts.getMainInputChannels() < 2) or (layouts.getMainOutputChannels() < 2)) { + if ((layouts.getMainInputChannels() < 2) || (layouts.getMainOutputChannels() < 2)) { // In any case don't allow less than 2 input and 2 output channels return false; } diff --git a/eBeamer.jucer b/eBeamer.jucer index 5ed0a12..e55302f 100644 --- a/eBeamer.jucer +++ b/eBeamer.jucer @@ -5,10 +5,10 @@ pluginVST3Category="Spatial" pluginAAXCategory="512" pluginRTASCategory="512" pluginVSTCategory="kPlugCategSpacializer" pluginAUMainType="'aufx'" pluginName="eBeamer" pluginDesc="eStick beam controller" pluginManufacturer="ISPL Polimi" - pluginManufacturerCode="Ispl" pluginCode="ebea" version="1.0.0" + pluginManufacturerCode="Ispl" pluginCode="ebea" version="1.0.1" bundleIdentifier="it.polimi.deib.ispl.ebeamer" companyWebsite="http://ispl.deib.polimi.it/" aaxIdentifier="it.polimi.deib.ispl.ebeamer" pluginAUExportPrefix="ebeamerAU" - pluginCharacteristicsValue="pluginWantsMidiIn"> + pluginCharacteristicsValue="pluginWantsMidiIn" headerPath="..\..\ASIO\common"> @@ -600,6 +600,27 @@ + + + + + + + + + + + + + + + + + + + + + From c1f8432008e24a7db16d332bead0a17b61a3f39b Mon Sep 17 00:00:00 2001 From: Luca Bondi Date: Sat, 2 May 2020 18:26:38 +0200 Subject: [PATCH 4/4] Import fix for XCode --- Source/CpuLoadComp.h | 2 +- Source/MidiComp.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/CpuLoadComp.h b/Source/CpuLoadComp.h index 7933cea..d0302e8 100644 --- a/Source/CpuLoadComp.h +++ b/Source/CpuLoadComp.h @@ -7,7 +7,7 @@ #pragma once -#include +#include "../JuceLibraryCode/JuceHeader.h" //============================================================================== diff --git a/Source/MidiComp.h b/Source/MidiComp.h index 32f364d..d3bc4a8 100644 --- a/Source/MidiComp.h +++ b/Source/MidiComp.h @@ -8,7 +8,7 @@ #pragma once #define DB_MINUS_INF -100.0 -#include +#include "../JuceLibraryCode/JuceHeader.h" #include "MidiCC.h" //==============================================================================