Skip to content

Commit

Permalink
Merge pull request #55 from polimi-ispl/develop
Browse files Browse the repository at this point in the history
Common libraries with Ebeam OSC controller
  • Loading branch information
luca-bondi authored May 2, 2021
2 parents 9fbe68d + 7d37678 commit e520a85
Show file tree
Hide file tree
Showing 31 changed files with 502 additions and 2,399 deletions.
3 changes: 3 additions & 0 deletions JuceLibraryCode/AppConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@
#define JUCE_PROJUCER_VERSION 0x60008

//==============================================================================
#define JUCE_MODULE_AVAILABLE_ebeamer_common 1
#define JUCE_MODULE_AVAILABLE_ebeamer_gui 1
#define JUCE_MODULE_AVAILABLE_ebeamer_osc 1
#define JUCE_MODULE_AVAILABLE_juce_audio_basics 1
#define JUCE_MODULE_AVAILABLE_juce_audio_devices 1
#define JUCE_MODULE_AVAILABLE_juce_audio_formats 1
Expand Down
7 changes: 5 additions & 2 deletions JuceLibraryCode/JuceHeader.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@

#include "AppConfig.h"

#include <ebeamer_common/ebeamer_common.h>
#include <ebeamer_gui/ebeamer_gui.h>
#include <ebeamer_osc/ebeamer_osc.h>
#include <juce_audio_basics/juce_audio_basics.h>
#include <juce_audio_devices/juce_audio_devices.h>
#include <juce_audio_formats/juce_audio_formats.h>
Expand Down Expand Up @@ -51,7 +54,7 @@ namespace ProjectInfo
{
const char* const projectName = "Ebeamer";
const char* const companyName = "ISPL and Eventide";
const char* const versionString = "2.0.0";
const int versionNumber = 0x20000;
const char* const versionString = "2.1.0";
const int versionNumber = 0x20100;
}
#endif
6 changes: 3 additions & 3 deletions JuceLibraryCode/JucePluginDefines.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,13 @@
#define JucePlugin_EditorRequiresKeyboardFocus 0
#endif
#ifndef JucePlugin_Version
#define JucePlugin_Version 2.0.0
#define JucePlugin_Version 2.1.0
#endif
#ifndef JucePlugin_VersionCode
#define JucePlugin_VersionCode 0x20000
#define JucePlugin_VersionCode 0x20100
#endif
#ifndef JucePlugin_VersionString
#define JucePlugin_VersionString "2.0.0"
#define JucePlugin_VersionString "2.1.0"
#endif
#ifndef JucePlugin_VSTUniqueID
#define JucePlugin_VSTUniqueID JucePlugin_PluginCode
Expand Down
9 changes: 9 additions & 0 deletions JuceLibraryCode/include_ebeamer_common.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/*
IMPORTANT! This file is auto-generated each time you save your
project - if you alter its contents, your changes may be overwritten!
*/

#include "AppConfig.h"
#include <ebeamer_common/ebeamer_common.cpp>
9 changes: 9 additions & 0 deletions JuceLibraryCode/include_ebeamer_gui.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/*
IMPORTANT! This file is auto-generated each time you save your
project - if you alter its contents, your changes may be overwritten!
*/

#include "AppConfig.h"
#include <ebeamer_gui/ebeamer_gui.cpp>
9 changes: 9 additions & 0 deletions JuceLibraryCode/include_ebeamer_osc.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/*
IMPORTANT! This file is auto-generated each time you save your
project - if you alter its contents, your changes may be overwritten!
*/

#include "AppConfig.h"
#include <ebeamer_osc/ebeamer_osc.cpp>
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ A JUCE-based VST3 plug-in and standalone application to support the [Estick](htt
- If you're developing on Windows, download and extract the [ASIO ASK](https://www.steinberg.net/en/company/developers.html) in the *ASIO/* folder.
- Don't have the Esticks yet? Start developing with the [Estick simulator](https://github.com/luca-bondi/estick-simulator).

## Dependencies
- [Ebeamer JUCE modules](https://github.com/luca-bondi/ebeamer-modules)

## Contributing
- Any contribution to the project is highly appreciated! Get in touch to know more.

Expand Down
38 changes: 36 additions & 2 deletions Source/AudioBufferFFT.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -145,15 +145,49 @@ void AudioBufferFFT::prepareForConvolution() {
}
}

void AudioBufferFFT::convolve(int outputChannel, const AudioBufferFFT &in_, int inChannel, AudioBufferFFT &filter_,
void AudioBufferFFT::convolveAndAdd(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;
}

void AudioBufferFFT::convolve(int outputChannel, const AudioBufferFFT &in_, int inChannel, AudioBufferFFT &filter_,
int filterChannel) {

clear(outputChannel, 0, getNumSamples());
convolveAndAdd(outputChannel, in_, inChannel, filter_, filterChannel);
}

AudioBufferFFT& AudioBufferFFT::operator= (const AudioBufferFFT& other){

if (this != &other)
{
setSize (other.getNumChannels(), other.getNumSamples(), false, false, false);

if (other.hasBeenCleared())
{
clear();
}
else
{

for (int i = 0; i < getNumChannels(); ++i)
FloatVectorOperations::copy (getWritePointer(i), other.getReadPointer(i), getNumSamples());
}

readyForConvolution = other.readyForConvolution;
fft = other.fft;
convBuffer = other.convBuffer;

}

return *this;

}

16 changes: 9 additions & 7 deletions Source/AudioBufferFFT.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,24 +32,26 @@ class AudioBufferFFT : public AudioBuffer<float> {

void
convolve(int outputChannel, const AudioBufferFFT &in_, int inChannel, AudioBufferFFT &filter_, int filterChannel);


void
convolveAndAdd(int outputChannel, const AudioBufferFFT &in_, int inChannel, AudioBufferFFT &filter_, int filterChannel);

void prepareForConvolution();

void updateSymmetricFrequency();

bool isReadyForConvolution() const { return readyForConvolution; };

AudioBufferFFT& operator= (const AudioBufferFFT& other);

private:
AudioBuffer<float> convBuffer;
std::shared_ptr<dsp::FFT> fft;
bool readyForConvolution = false;

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;

};

109 changes: 70 additions & 39 deletions Source/Beamformer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,43 @@

#include "Beamformer.h"

#define NUM_DOAX 25
#define NUM_DOAY 9

BeamformerDoa::BeamformerDoa(Beamformer &b,
int numDoaHor_,
int numDoaVer_,
float sampleRate_,
int numActiveInputChannels,
int firLen,
float expectedRate,
std::shared_ptr<dsp::FFT> fft_) : Thread("DOA"), beamformer(b) {

numDoaHor = numDoaHor_;
numDoaVer = numDoaVer_;
fft = fft_;
sampleRate = sampleRate_;
doaUpdateFrequency = expectedRate;

/** Initialize levels and FIR */
doaLevels.resize(numDoaVer,numDoaHor);
doaLevels.setConstant(-100);
newDoaLevels.resize(numDoaVer,numDoaHor);
newDoaLevels.setConstant(-100);
doaFirFFT.resize(numDoaHor*numDoaVer);

/** Time constants */
alpha = 1 - exp(-(1/doaUpdateFrequency) / timeConst);

/** Allocate inputBuffer */
inputBuffer = AudioBufferFFT(numActiveInputChannels, fft);

/** Allocate convolution buffer */
convolutionBuffer = AudioBufferFFT(1, fft);

/** Allocate DOA beam */
doaBeam.setSize(1, fft->getSize());
/* Determine frequency bins for energy average */
lowFreqIdx = lowFreq/sampleRate*fft->getSize();
numFreqBins = highFreq/sampleRate*fft->getSize() - lowFreqIdx;

/** Compute FIR for DOA estimation */
AudioBuffer<float> tmpFir(numActiveInputChannels, firLen);
Expand All @@ -56,6 +67,12 @@ void BeamformerDoa::run() {

while (!threadShouldExit()){

/* Wait for previous doa to be consumed before computing a new one */
while (!threadShouldExit() && beamformer.isDoaOutputBufferNew())
sleep(5);
if (threadShouldExit())
return;

const auto startTick = Time::getHighResolutionTicks();

beamformer.getDoaInputBuffer(inputBuffer);
Expand All @@ -64,27 +81,35 @@ void BeamformerDoa::run() {
for (auto vDirIdx = 0; vDirIdx < numDoaVer; vDirIdx++) {
for (auto hDirIdx = 0; hDirIdx < numDoaHor; hDirIdx++) {
auto dirIdx = vDirIdx * numDoaHor + hDirIdx;
doaBeam.clear();

convolutionBuffer.clear();
for (auto inCh = 0; inCh < inputBuffer.getNumChannels(); inCh++) {
/** Convolve inputs and DOA FIR */
convolutionBuffer.convolve(0, inputBuffer, inCh, doaFirFFT[dirIdx], inCh);
convolutionBuffer.addToTimeSeries(0, doaBeam, 0);
/** Convolve inputs and DOA FIR and sum*/
convolutionBuffer.convolveAndAdd(0, inputBuffer, inCh, doaFirFFT[dirIdx], inCh);
}

const Range<float> minMax = FloatVectorOperations::findMinAndMax(doaBeam.getReadPointer(0),
doaBeam.getNumSamples());
const float dirEnergy = jmax(abs(minMax.getStart()), abs(minMax.getEnd()));
/** Back to regular FFT data */
convolutionBuffer.updateSymmetricFrequency();

std::complex<float>* cplxData = (std::complex<float>*)convolutionBuffer.getReadPointer(0);
Eigen::Map<CplxVec> doaBeamMap(cplxData,convolutionBuffer.getNumSamples()/2);

const float dirEnergy = doaBeamMap.segment(lowFreqIdx,numFreqBins).array().abs().sum()/float(numFreqBins);
const float dirEnergyDb = Decibels::gainToDecibels(dirEnergy);
doaLevels(vDirIdx,hDirIdx) = dirEnergyDb;
newDoaLevels(vDirIdx,hDirIdx) = dirEnergyDb;
}
}
doaLevels = (doaLevels * (1 - alpha)) + (newDoaLevels * alpha);
beamformer.setDoaEnergy(doaLevels);

const auto endTick = Time::getHighResolutionTicks();
const float elapsedTime = Time::highResolutionTicksToSeconds(endTick-startTick);
const float expectedPeriod = 1.f/ENERGY_UPDATE_FREQ;
if (elapsedTime < expectedPeriod){
Time::waitForMillisecondCounter(Time::getMillisecondCounter() + (expectedPeriod-elapsedTime));
const float expectedPeriod = 1.f/doaUpdateFrequency;
const float sleepTime = expectedPeriod-elapsedTime;
if (sleepTime > 0){
sleep(expectedPeriod-elapsedTime);
}else{
//TODO: can't keep up, reduce complexity
}

}
Expand All @@ -95,7 +120,7 @@ BeamformerDoa::~BeamformerDoa(){
}

// ==============================================================================
Beamformer::Beamformer(int numBeams_, MicConfig mic, double sampleRate_, int maximumExpectedSamplesPerBlock_) {
Beamformer::Beamformer(int numBeams_, MicConfig mic, double sampleRate_, int maximumExpectedSamplesPerBlock_,float doaRefreshRate) {

numBeams = numBeams_;
numDoaVer = isLinearArray(mic) ? 1 : NUM_DOAY;
Expand Down Expand Up @@ -177,22 +202,12 @@ Beamformer::Beamformer(int numBeams_, MicConfig mic, double sampleRate_, int max
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) {
f.setCoefficients(doaIIRCoeff);
}
doaInputBuffer = AudioBufferFFT(numMic, fft);
doaInputBuffer.prepareForConvolution();

/** Prepare and start DOA thread */
doaThread = std::make_unique<BeamformerDoa>(*this, numDoaHor, numDoaVer, sampleRate, numMic, firLen, fft);
#ifndef HEADLESS
doaThread = std::make_unique<BeamformerDoa>(*this, numDoaHor, numDoaVer, sampleRate, numMic, firLen, doaRefreshRate, fft);
doaThread->startThread();
#endif

}

Expand All @@ -215,18 +230,16 @@ void Beamformer::setBeamParameters(int beamIdx, const BeamParameters &beamParams

void Beamformer::processBlock(const AudioBuffer<float> &inBuffer) {

{
GenericScopedLock<SpinLock> lock(doaInputBufferLock);
for (auto chIdx = 0; chIdx < jmin(numMic, inBuffer.getNumChannels()); chIdx++) {
doaInputBuffer.copyFrom(chIdx, 0, inBuffer, chIdx, 0, inBuffer.getNumSamples());
doaBPFilters[chIdx].processSamples(doaInputBuffer.getWritePointer(chIdx), inBuffer.getNumSamples());
}
}

/** Compute inputs FFT */
inputBuffer.setTimeSeries(inBuffer);
inputBuffer.prepareForConvolution();

if (!doaInputBufferNew){
GenericScopedLock<SpinLock> lock(doaInputBufferLock);
doaInputBuffer = inputBuffer;
doaInputBufferNew = true;
}

for (auto beamIdx = 0; beamIdx < numBeams; beamIdx++) {
for (auto inCh = 0; inCh < inputBuffer.getNumChannels(); inCh++) {
/** Convolve inputs and FIR */
Expand All @@ -242,10 +255,10 @@ void Beamformer::getFir(AudioBuffer<float> &fir, const BeamParameters &params, f
alg->getFir(fir, params, alpha);
}

void Beamformer::getDoaInputBuffer(AudioBufferFFT &dst) const {
void Beamformer::getDoaInputBuffer(AudioBufferFFT &dst) {
GenericScopedLock<SpinLock> lock(doaInputBufferLock);
dst.setTimeSeries(doaInputBuffer);
dst.prepareForConvolution();
dst = doaInputBuffer;
doaInputBufferNew = false;
}

void Beamformer::getBeams(AudioBuffer<float> &outBuffer) {
Expand All @@ -265,9 +278,27 @@ void Beamformer::getBeams(AudioBuffer<float> &outBuffer) {
void Beamformer::setDoaEnergy(const Mtx &energy) {
GenericScopedLock<SpinLock> lock(doaLock);
doaLevels = energy;
doaOutputBufferNew = true;
}

void Beamformer::getDoaEnergy(Mtx &outDoaLevels) const {
void Beamformer::getDoaEnergy(Mtx &outDoaLevels) {
GenericScopedLock<SpinLock> lock(doaLock);
outDoaLevels = doaLevels;
doaOutputBufferNew = false;
}

MemoryBlock Beamformer::getDoaEnergy(){
GenericScopedLock<SpinLock> lock(doaLock);

MemoryBlock mb(doaLevels.size()*sizeof(float)+2);
mb[0] = (uint8)doaLevels.rows();
mb[1] = (uint8)doaLevels.cols();
mb.copyFrom(doaLevels.data(), 2, doaLevels.size()*sizeof(float));

doaOutputBufferNew = false;
return mb;
}

bool Beamformer::isDoaOutputBufferNew() const{
return doaOutputBufferNew;
}
Loading

0 comments on commit e520a85

Please sign in to comment.