diff --git a/CHANGELOG b/CHANGELOG index 0f0bd54..f441393 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -4,6 +4,7 @@ === 1.0.27 === * Implemented TruePeak meter. +* Implemented Panorama meter (Panometer). * Implemented ScaledMeterGraph with subsampling option. * Several fixes and improvements in RawRingBuffer. diff --git a/include/lsp-plug.in/dsp-units/meters/Panometer.h b/include/lsp-plug.in/dsp-units/meters/Panometer.h new file mode 100644 index 0000000..715a824 --- /dev/null +++ b/include/lsp-plug.in/dsp-units/meters/Panometer.h @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2024 Linux Studio Plugins Project + * (C) 2024 Vladimir Sadovnikov + * + * This file is part of lsp-dsp-units + * Created on: 12 нояб. 2024 г. + * + * lsp-dsp-units is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * lsp-dsp-units is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with lsp-dsp-units. If not, see . + */ + +#ifndef LSP_PLUG_IN_DSP_UNITS_METERS_PANOMETER_H_ +#define LSP_PLUG_IN_DSP_UNITS_METERS_PANOMETER_H_ + +#include + +#include +#include +#include +#include + +namespace lsp +{ + namespace dspu + { + /** + * Pan law + */ + enum pan_law_t + { + /** + * Linear pan law between left and right channels + */ + PAN_LAW_LINEAR, + + /** + * Equal power pan law between left and right channels + */ + PAN_LAW_EQUAL_POWER + }; + + /** + * Corellometer. Computes normalized correlation between two signals. + */ + class LSP_DSP_UNITS_PUBLIC Panometer + { + private: + float *vInA; // History of input buffer 1 + float *vInB; // History of input buffer 2 + pan_law_t enPanLaw; // Pan law + float fValueA; // Current mean square value for input 1 + float fValueB; // Current mean square value for input 2 + float fNorm; // Norming factor + float fDefault; // Default value + uint32_t nCapacity; // The overall capacity + uint32_t nHead; // Write position of the buffer + uint32_t nMaxPeriod; // Maximum measurement period + uint32_t nPeriod; // Measurement period + uint32_t nWindow; // Number of samples processed before reset + + uint8_t *pData; // Pointer to the allocated data + + public: + Panometer(); + Panometer(const Panometer &) = delete; + Panometer(Panometer &&) = delete; + ~Panometer(); + + Panometer & operator = (const Panometer &) = delete; + Panometer && operator = (Panometer &&) = delete; + + /** + * Construct object + */ + void construct(); + + /** + * Destroy object + */ + void destroy(); + + /** + * Initialize object + * @param max_chunk_size the maximum period size in samples + * @return + */ + status_t init(size_t max_period); + + public: + /** + * Set pan law + * @param law pan law + */ + void set_pan_law(pan_law_t law); + + /** + * Get pan law + * @return pan law + */ + inline pan_law_t pan_law() const { return enPanLaw; } + + /** + * Set default value for panorama if it is not possible to compute it + * @param dfl default value for panorama + */ + void set_default_pan(float dfl); + + /** + * Get default value for panorama if it is not possible to compute it + * @return default value for panorama + */ + inline float default_pan() const { return fDefault; } + + /** + * Set the correlation computation period + * @param period correlation computation period + */ + void set_period(size_t period); + + /** + * Get the correlation computation period + * @return correlation computation period + */ + inline size_t period() const { return nPeriod; } + + /** + * Clear internal state + */ + void clear(); + + /** + * Process the input data and compute the correlation function + * @param dst destination buffer to store the correlation function + * @param a pointer to the first buffer + * @param b pointer to the second buffer + * @param count number of samples to process + */ + void process(float *dst, const float *a, const float *b, size_t count); + + /** + * Dump the state + * @param dumper dumper + */ + void dump(IStateDumper *v) const; + }; + + } /* namespace dspu */ +} /* namespace lsp */ + + + +#endif /* LSP_PLUG_IN_DSP_UNITS_METERS_PANOMETER_H_ */ diff --git a/src/main/meters/Panometer.cpp b/src/main/meters/Panometer.cpp new file mode 100644 index 0000000..572e0ee --- /dev/null +++ b/src/main/meters/Panometer.cpp @@ -0,0 +1,238 @@ +/* + * Copyright (C) 2024 Linux Studio Plugins Project + * (C) 2024 Vladimir Sadovnikov + * + * This file is part of lsp-dsp-units + * Created on: 12 нояб. 2024 г. + * + * lsp-dsp-units is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * lsp-dsp-units is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with lsp-dsp-units. If not, see . + */ + +#include +#include +#include + +namespace lsp +{ + namespace dspu + { + constexpr size_t BUFFER_SIZE = 0x400; + + Panometer::Panometer() + { + construct(); + } + + Panometer::~Panometer() + { + destroy(); + } + + void Panometer::construct() + { + vInA = NULL; + vInB = NULL; + enPanLaw = PAN_LAW_EQUAL_POWER; + fValueA = 0.0f; + fValueB = 0.0f; + fNorm = 0.0f; + fDefault = 0.5f; + nCapacity = 0; + nHead = 0; + nMaxPeriod = 0; + nPeriod = 0; + nWindow = 0; + + pData = NULL; + } + + void Panometer::destroy() + { + if (pData != NULL) + { + free_aligned(pData); + vInA = NULL; + vInB = NULL; + pData = NULL; + } + } + + status_t Panometer::init(size_t max_period) + { + destroy(); + + // Allocate data + const size_t capacity = align_size(max_period + BUFFER_SIZE, DEFAULT_ALIGN); + const size_t szof_buf = capacity * sizeof(float); + const size_t to_alloc = 2 * szof_buf; + uint8_t *data = NULL; + + uint8_t *ptr = alloc_aligned(data, to_alloc); + if (ptr == NULL) + return STATUS_NO_MEM; + + // Commit state + vInA = advance_ptr_bytes(ptr, szof_buf); + vInB = advance_ptr_bytes(ptr, szof_buf); + nCapacity = capacity; + nHead = 0; + nMaxPeriod = max_period; + nPeriod = 0; + + pData = data; + + // Cleanup buffers (both vInA and vInB -> nCapacity * 2) + dsp::fill_zero(vInA, nCapacity * 2); + + return STATUS_OK; + } + + void Panometer::set_pan_law(pan_law_t law) + { + enPanLaw = law; + } + + void Panometer::set_default_pan(float dfl) + { + fDefault = dfl; + } + + void Panometer::set_period(size_t period) + { + period = lsp_min(period, nMaxPeriod); + if (period == nPeriod) + return; + + nPeriod = period; + nWindow = period; + fValueA = 0.0f; + fValueB = 0.0f; + fNorm = (period > 0) ? 1.0f / period : 1.0f; + } + + void Panometer::clear() + { + dsp::fill_zero(vInA, nCapacity); + dsp::fill_zero(vInB, nCapacity); + + nHead = nPeriod; + } + + void Panometer::process(float *dst, const float *a, const float *b, size_t count) + { + for (size_t offset=0; offset= nPeriod) + { + fValueA = 0.0f; + fValueB = 0.0f; + + if (nHead < tail) + { + fValueA = dsp::h_sum(&vInA[tail], nCapacity - tail); + fValueB = dsp::h_sum(&vInB[tail], nCapacity - tail); + fValueA += dsp::h_sum(&vInA[0], nHead); + fValueB += dsp::h_sum(&vInB[0], nHead); + } + else + { + fValueA = dsp::h_sum(&vInA[tail], nPeriod); + fValueB = dsp::h_sum(&vInB[tail], nPeriod); + } + nWindow = 0; + } + + const size_t can_do = nPeriod - nWindow; + size_t to_do = lsp_min( + count - offset, // Number of samples left in input buffers + nCapacity - nMaxPeriod, // Number of free samples in the ring buffer + nCapacity - nHead, // The number of samples before head goes out of ring buffer + nCapacity - tail); // The number of samples before tail goes out of ring buffer + to_do = lsp_min(to_do, can_do); + + // Fill buffers with data + float *ah = &vInA[nHead]; + float *bh = &vInB[nHead]; + float *at = &vInA[tail]; + float *bt = &vInB[tail]; + + dsp::sqr2(ah, &a[offset], to_do); + dsp::sqr2(bh, &b[offset], to_do); + + // Estimate the actual pan value + float va = fValueA; + float vb = fValueB; + if (enPanLaw == PAN_LAW_LINEAR) + { + for (size_t i=0; i 1e-5f) ? sr / den : fDefault; + } + } + else + { + for (size_t i=0; i 1e-10f) ? sr / den : fDefault; + } + } + fValueA = va; + fValueB = vb; + + // Update pointers and counters + nHead = (nHead + to_do) % nCapacity; + nWindow += to_do; + offset += to_do; + dst += to_do; + } + } + + void Panometer::dump(IStateDumper *v) const + { + v->write("vInA", vInA); + v->write("vInB", vInB); + v->write("enPanLaw", enPanLaw); + v->write("fValueA", fValueA); + v->write("fValueB", fValueB); + v->write("fNorm", fNorm); + v->write("fDefault", fDefault); + v->write("nCapacity", nCapacity); + v->write("nHead", nHead); + v->write("nMaxPeriod", nMaxPeriod); + v->write("nPeriod", nPeriod); + v->write("nWindow", nWindow); + + v->write("pData", pData); + } + + } /* namespace dspu */ +} /* namespace lsp */ + +