plugin/ac: use double buffer
Double buffer instead of circular buffer
This commit is contained in:
parent
9cd838abc3
commit
dc42e4f38e
4 changed files with 37 additions and 73 deletions
|
|
@ -2,12 +2,10 @@
|
||||||
#include "service.hpp"
|
#include "service.hpp"
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QVector>
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <pipewire/pipewire.h>
|
#include <pipewire/pipewire.h>
|
||||||
#include <qmutex.h>
|
|
||||||
#include <spa/param/audio/format-utils.h>
|
#include <spa/param/audio/format-utils.h>
|
||||||
#include <spa/param/latency-utils.h>
|
#include <spa/param/latency-utils.h>
|
||||||
#include <stop_token>
|
#include <stop_token>
|
||||||
|
|
@ -157,13 +155,14 @@ unsigned int PipeWireWorker::nextPowerOf2(unsigned int n) {
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioCollector::AudioCollector(uint32_t sampleRate, uint32_t chunkSize, uint32_t bufferSize, QObject* parent)
|
AudioCollector::AudioCollector(uint32_t sampleRate, uint32_t chunkSize, QObject* parent)
|
||||||
: Service(parent)
|
: Service(parent)
|
||||||
, m_buffer(bufferSize, 0.0f)
|
, m_buffer1(chunkSize)
|
||||||
, m_bufferIndex(bufferSize)
|
, m_buffer2(chunkSize)
|
||||||
|
, m_readBuffer(&m_buffer1)
|
||||||
|
, m_writeBuffer(&m_buffer2)
|
||||||
, m_sampleRate(sampleRate)
|
, m_sampleRate(sampleRate)
|
||||||
, m_chunkSize(chunkSize)
|
, m_chunkSize(chunkSize) {}
|
||||||
, m_bufferSize(bufferSize) {}
|
|
||||||
|
|
||||||
AudioCollector::~AudioCollector() {
|
AudioCollector::~AudioCollector() {
|
||||||
stop();
|
stop();
|
||||||
|
|
@ -185,72 +184,49 @@ uint32_t AudioCollector::chunkSize() const {
|
||||||
return m_chunkSize;
|
return m_chunkSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t AudioCollector::bufferSize() const {
|
|
||||||
return m_bufferSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AudioCollector::clearBuffer() {
|
void AudioCollector::clearBuffer() {
|
||||||
std::lock_guard<std::mutex> lock(m_bufferMutex);
|
auto* writeBuffer = m_writeBuffer.load(std::memory_order_relaxed);
|
||||||
std::fill(m_buffer.begin(), m_buffer.end(), 0.0f);
|
std::fill(writeBuffer->begin(), writeBuffer->end(), 0.0f);
|
||||||
m_bufferIndex = m_bufferSize;
|
|
||||||
|
auto* oldRead = m_readBuffer.exchange(writeBuffer, std::memory_order_acq_rel);
|
||||||
|
m_writeBuffer.store(oldRead, std::memory_order_release);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioCollector::loadChunk(const int16_t* samples, uint32_t count) {
|
void AudioCollector::loadChunk(const int16_t* samples, uint32_t count) {
|
||||||
std::lock_guard<std::mutex> lock(m_bufferMutex);
|
if (count > m_chunkSize) {
|
||||||
|
count = m_chunkSize;
|
||||||
while (count > 0) {
|
|
||||||
const auto spaceToEnd = m_bufferSize - m_bufferIndex;
|
|
||||||
const auto toCopy = (count < spaceToEnd) ? count : spaceToEnd;
|
|
||||||
|
|
||||||
std::transform(samples, samples + toCopy, m_buffer.begin() + m_bufferIndex, [](int16_t sample) {
|
|
||||||
return sample / 32768.0f;
|
|
||||||
});
|
|
||||||
|
|
||||||
m_bufferIndex = (m_bufferIndex + toCopy) % m_bufferSize;
|
|
||||||
samples += toCopy;
|
|
||||||
count -= toCopy;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto* writeBuffer = m_writeBuffer.load(std::memory_order_relaxed);
|
||||||
|
std::transform(samples, samples + count, writeBuffer->begin(), [](int16_t sample) {
|
||||||
|
return sample / 32768.0f;
|
||||||
|
});
|
||||||
|
|
||||||
|
auto* oldRead = m_readBuffer.exchange(writeBuffer, std::memory_order_acq_rel);
|
||||||
|
m_writeBuffer.store(oldRead, std::memory_order_release);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t AudioCollector::readChunk(float* out, uint32_t count) {
|
uint32_t AudioCollector::readChunk(float* out, uint32_t count) {
|
||||||
std::lock_guard<std::mutex> lock(m_bufferMutex);
|
if (count == 0 || count > m_chunkSize) {
|
||||||
|
count = m_chunkSize;
|
||||||
if (count == 0 || count > m_bufferSize) {
|
|
||||||
count = m_bufferSize;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto start = (m_bufferIndex + m_bufferSize - count) % m_bufferSize;
|
auto* readBuffer = m_readBuffer.load(std::memory_order_acquire);
|
||||||
const auto firstChunk = std::min(count, m_bufferSize - start);
|
std::memcpy(out, readBuffer->data(), count * sizeof(float));
|
||||||
|
|
||||||
std::copy(m_buffer.begin() + start, m_buffer.begin() + start + firstChunk, out);
|
|
||||||
|
|
||||||
if (firstChunk < count) {
|
|
||||||
std::copy(m_buffer.begin(), m_buffer.begin() + (count - firstChunk), out + firstChunk);
|
|
||||||
}
|
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t AudioCollector::readChunk(double* out, uint32_t count) {
|
uint32_t AudioCollector::readChunk(double* out, uint32_t count) {
|
||||||
std::lock_guard<std::mutex> lock(m_bufferMutex);
|
if (count == 0 || count > m_chunkSize) {
|
||||||
|
count = m_chunkSize;
|
||||||
if (count == 0 || count > m_bufferSize) {
|
|
||||||
count = m_bufferSize;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto start = (m_bufferIndex + m_bufferSize - count) % m_bufferSize;
|
auto* readBuffer = m_readBuffer.load(std::memory_order_acquire);
|
||||||
const auto firstChunk = std::min(count, m_bufferSize - start);
|
std::transform(readBuffer->begin(), readBuffer->begin() + count, out, [](float sample) {
|
||||||
|
|
||||||
std::transform(m_buffer.begin() + start, m_buffer.begin() + start + firstChunk, out, [](float sample) {
|
|
||||||
return static_cast<double>(sample);
|
return static_cast<double>(sample);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (firstChunk < count) {
|
|
||||||
std::transform(m_buffer.begin(), m_buffer.begin() + (count - firstChunk), out + firstChunk, [](float sample) {
|
|
||||||
return static_cast<double>(sample);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include "service.hpp"
|
#include "service.hpp"
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <atomic>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <pipewire/pipewire.h>
|
#include <pipewire/pipewire.h>
|
||||||
|
|
@ -43,15 +44,13 @@ class AudioCollector : public Service {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit AudioCollector(
|
explicit AudioCollector(uint32_t sampleRate = 44100, uint32_t chunkSize = 512, QObject* parent = nullptr);
|
||||||
uint32_t sampleRate = 44100, uint32_t chunkSize = 512, uint32_t bufferSize = 512, QObject* parent = nullptr);
|
|
||||||
~AudioCollector();
|
~AudioCollector();
|
||||||
|
|
||||||
static AudioCollector* instance();
|
static AudioCollector* instance();
|
||||||
|
|
||||||
[[nodiscard]] uint32_t sampleRate() const;
|
[[nodiscard]] uint32_t sampleRate() const;
|
||||||
[[nodiscard]] uint32_t chunkSize() const;
|
[[nodiscard]] uint32_t chunkSize() const;
|
||||||
[[nodiscard]] uint32_t bufferSize() const;
|
|
||||||
|
|
||||||
void clearBuffer();
|
void clearBuffer();
|
||||||
void loadChunk(const int16_t* samples, uint32_t count);
|
void loadChunk(const int16_t* samples, uint32_t count);
|
||||||
|
|
@ -63,13 +62,14 @@ private:
|
||||||
inline static std::mutex s_mutex;
|
inline static std::mutex s_mutex;
|
||||||
|
|
||||||
std::jthread m_thread;
|
std::jthread m_thread;
|
||||||
std::vector<float> m_buffer;
|
std::vector<float> m_buffer1;
|
||||||
uint32_t m_bufferIndex;
|
std::vector<float> m_buffer2;
|
||||||
std::mutex m_bufferMutex;
|
std::atomic<std::vector<float>*> m_readBuffer;
|
||||||
|
std::atomic<std::vector<float>*> m_writeBuffer;
|
||||||
|
uint32_t m_sampleCount;
|
||||||
|
|
||||||
const uint32_t m_sampleRate;
|
const uint32_t m_sampleRate;
|
||||||
const uint32_t m_chunkSize;
|
const uint32_t m_chunkSize;
|
||||||
const uint32_t m_bufferSize;
|
|
||||||
|
|
||||||
void start() override;
|
void start() override;
|
||||||
void stop() override;
|
void stop() override;
|
||||||
|
|
|
||||||
|
|
@ -2,22 +2,16 @@
|
||||||
|
|
||||||
#include "audiocollector.hpp"
|
#include "audiocollector.hpp"
|
||||||
#include "service.hpp"
|
#include "service.hpp"
|
||||||
#include <QAudioSource>
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QIODevice>
|
|
||||||
#include <QMediaDevices>
|
|
||||||
#include <QMutexLocker>
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
#include <QVector>
|
|
||||||
|
|
||||||
namespace caelestia {
|
namespace caelestia {
|
||||||
|
|
||||||
AudioProcessor::AudioProcessor(QObject* parent)
|
AudioProcessor::AudioProcessor(QObject* parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
, m_sampleRate(AudioCollector::instance()->sampleRate())
|
, m_sampleRate(AudioCollector::instance()->sampleRate())
|
||||||
, m_chunkSize(AudioCollector::instance()->chunkSize())
|
, m_chunkSize(AudioCollector::instance()->chunkSize()) {}
|
||||||
, m_bufferSize(AudioCollector::instance()->bufferSize()) {}
|
|
||||||
|
|
||||||
AudioProcessor::~AudioProcessor() {
|
AudioProcessor::~AudioProcessor() {
|
||||||
stop();
|
stop();
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,9 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "service.hpp"
|
#include "service.hpp"
|
||||||
#include <QAudioSource>
|
|
||||||
#include <QIODevice>
|
|
||||||
#include <QMutex>
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QQueue>
|
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QVector>
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <qqmlintegration.h>
|
#include <qqmlintegration.h>
|
||||||
|
|
||||||
|
|
@ -26,7 +21,6 @@ public:
|
||||||
protected:
|
protected:
|
||||||
uint32_t m_sampleRate;
|
uint32_t m_sampleRate;
|
||||||
uint32_t m_chunkSize;
|
uint32_t m_chunkSize;
|
||||||
uint32_t m_bufferSize;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QTimer* m_timer;
|
QTimer* m_timer;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue