Use a blocking queue instead of lock free to massively reduce CPU usage

pull/170/head
James Ball 2023-09-09 15:32:03 +01:00
rodzic 32b2d2d35d
commit a6d25a122c
6 zmienionych plików z 103 dodań i 37 usunięć

Wyświetl plik

@ -444,8 +444,11 @@ void OscirenderAudioProcessor::processBlock(juce::AudioBuffer<float>& buffer, ju
}
prevMidiEnabled = usingMidi;
const double EPSILON = 0.00001;
{
if (volume > EPSILON) {
juce::SpinLock::ScopedLockType lock1(parsersLock);
juce::SpinLock::ScopedLockType lock2(effectsLock);
synth.renderNextBlock(buffer, midiMessages, 0, buffer.getNumSamples());
@ -460,9 +463,11 @@ void OscirenderAudioProcessor::processBlock(juce::AudioBuffer<float>& buffer, ju
{
juce::SpinLock::ScopedLockType lock1(parsersLock);
juce::SpinLock::ScopedLockType lock2(effectsLock);
for (auto& effect : toggleableEffects) {
if (effect->enabled->getValue()) {
channels = effect->apply(sample, channels);
if (volume > EPSILON) {
for (auto& effect : toggleableEffects) {
if (effect->enabled->getValue()) {
channels = effect->apply(sample, channels);
}
}
}
for (auto& effect : permanentEffects) {

Wyświetl plik

@ -9,6 +9,11 @@ ShapeSound::ShapeSound(std::shared_ptr<FileParser> parser) : parser(parser) {
producer->startThread();
}
ShapeSound::~ShapeSound() {
frames.kill();
producer->stopThread(1000);
}
bool ShapeSound::appliesToNote(int note) {
return true;
}
@ -18,36 +23,12 @@ bool ShapeSound::appliesToChannel(int channel) {
}
void ShapeSound::addFrame(std::vector<std::unique_ptr<Shape>>& frame) {
const auto scope = frameFifo.write(1);
if (scope.blockSize1 > 0) {
frameBuffer[scope.startIndex1].clear();
for (auto& shape : frame) {
frameBuffer[scope.startIndex1].push_back(std::move(shape));
}
}
if (scope.blockSize2 > 0) {
frameBuffer[scope.startIndex2].clear();
for (auto& shape : frame) {
frameBuffer[scope.startIndex2].push_back(std::move(shape));
}
}
frames.push(std::move(frame));
}
double ShapeSound::updateFrame(std::vector<std::unique_ptr<Shape>>& frame) {
if (frameFifo.getNumReady() > 0) {
{
const auto scope = frameFifo.read(1);
if (scope.blockSize1 > 0) {
frame.swap(frameBuffer[scope.startIndex1]);
} else if (scope.blockSize2 > 0) {
frame.swap(frameBuffer[scope.startIndex2]);
}
frameLength = Shape::totalLength(frame);
}
if (frames.try_pop(frame)) {
frameLength = Shape::totalLength(frame);
}
return frameLength;

Wyświetl plik

@ -3,10 +3,12 @@
#include "../parser/FileParser.h"
#include "../parser/FrameProducer.h"
#include "../parser/FrameConsumer.h"
#include "../concurrency/BlockingQueue.h"
class ShapeSound : public juce::SynthesiserSound, public FrameConsumer {
public:
ShapeSound(std::shared_ptr<FileParser> parser);
~ShapeSound() override;
bool appliesToNote(int note) override;
bool appliesToChannel(int channel) override;
@ -19,8 +21,7 @@ public:
private:
juce::AbstractFifo frameFifo{ 10 };
std::vector<std::unique_ptr<Shape>> frameBuffer[10];
BlockingQueue frames{10};
std::unique_ptr<FrameProducer> producer;
double frameLength = 0.0;
};

Wyświetl plik

@ -0,0 +1,81 @@
#pragma once
#include <mutex>
#include <condition_variable>
#include <queue>
typedef std::vector<std::unique_ptr<Shape>> Frame;
class BlockingQueue {
std::vector<Frame> content;
std::atomic<int> size = 0;
int head = 0;
std::atomic<bool> killed = false;
std::mutex mutex;
std::condition_variable not_empty;
std::condition_variable not_full;
BlockingQueue(const BlockingQueue &) = delete;
BlockingQueue(BlockingQueue &&) = delete;
BlockingQueue &operator = (const BlockingQueue &) = delete;
BlockingQueue &operator = (BlockingQueue &&) = delete;
public:
BlockingQueue(size_t capacity) {
content = std::vector<Frame>(capacity);
}
void kill() {
killed = true;
not_empty.notify_all();
not_full.notify_all();
}
void push(Frame &&item) {
{
std::unique_lock<std::mutex> lk(mutex);
not_full.wait(lk, [this]() { return size < content.size() || killed; });
content[head] = std::move(item);
size++;
}
not_empty.notify_one();
}
bool try_push(Frame &&item) {
{
std::unique_lock<std::mutex> lk(mutex);
if (size == content.size()) {
return false;
}
content[head] = std::move(item);
size++;
}
not_empty.notify_one();
return true;
}
void pop(Frame &item) {
{
std::unique_lock<std::mutex> lk(mutex);
not_empty.wait(lk, [this]() { return size > 0 || killed; });
content[head].swap(item);
head = (head + 1) % content.size();
size--;
}
not_full.notify_one();
}
bool try_pop(Frame &item) {
{
std::unique_lock<std::mutex> lk(mutex);
if (size == 0) {
return false;
}
content[head].swap(item);
head = (head + 1) % content.size();
size--;
}
not_full.notify_one();
return true;
}
};

Wyświetl plik

@ -2,10 +2,7 @@
FrameProducer::FrameProducer(FrameConsumer& fc, std::shared_ptr<FileParser> fs) : frameConsumer(fc), frameSource(fs), juce::Thread("producer", 0) {}
FrameProducer::~FrameProducer() {
frameSource->disable();
stopThread(-1);
}
FrameProducer::~FrameProducer() {}
void FrameProducer::run() {
while (!threadShouldExit()) {

Wyświetl plik

@ -135,6 +135,7 @@
file="Source/components/VolumeComponent.h"/>
</GROUP>
<GROUP id="{9F5970A9-8094-E7F3-7AC1-812AE5589B9F}" name="concurrency">
<FILE id="F5kUMH" name="BlockingQueue.h" compile="0" resource="0" file="Source/concurrency/BlockingQueue.h"/>
<FILE id="WQ2W15" name="BufferConsumer.h" compile="0" resource="0"
file="Source/concurrency/BufferConsumer.h"/>
</GROUP>