kopia lustrzana https://github.com/jameshball/osci-render
Remove old buffer producer and consumer and replace with the way legacy osci-render does it
rodzic
adf5624e4e
commit
259dadbf3b
|
@ -284,4 +284,4 @@ double OscirenderAudioProcessor::valueFromLegacy(double value, const juce::Strin
|
|||
return std::pow(12000.0, value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -540,7 +540,14 @@ void OscirenderAudioProcessor::processBlock(juce::AudioBuffer<float>& buffer, ju
|
|||
channelData[0][sample] = x;
|
||||
}
|
||||
|
||||
audioProducer.write(x, y);
|
||||
{
|
||||
juce::SpinLock::ScopedLockType scope(consumerLock);
|
||||
for (auto consumer : consumers) {
|
||||
consumer->write(x);
|
||||
consumer->write(y);
|
||||
consumer->notifyIfFull();
|
||||
}
|
||||
}
|
||||
|
||||
actualTraceMax = juce::jmax(actualTraceMin + MIN_TRACE, juce::jmin(traceMaxValue, 1.0));
|
||||
actualTraceMin = juce::jmax(MIN_TRACE, juce::jmin(traceMinValue, actualTraceMax - MIN_TRACE));
|
||||
|
@ -721,6 +728,21 @@ void OscirenderAudioProcessor::setStateInformation(const void* data, int sizeInB
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void OscirenderAudioProcessor::read(std::vector<float>& buffer) {
|
||||
std::shared_ptr<BufferConsumer> consumer = std::make_shared<BufferConsumer>(buffer);
|
||||
{
|
||||
juce::SpinLock::ScopedLockType scope(consumerLock);
|
||||
consumers.push_back(consumer);
|
||||
}
|
||||
consumer->waitUntilFull();
|
||||
{
|
||||
juce::SpinLock::ScopedLockType scope(consumerLock);
|
||||
consumers.erase(std::remove(consumers.begin(), consumers.end(), consumer), consumers.end());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
// This creates new instances of the plugin..
|
||||
juce::AudioProcessor* JUCE_CALLTYPE createPluginFilter()
|
||||
|
|
|
@ -13,9 +13,9 @@
|
|||
#include "parser/FileParser.h"
|
||||
#include "parser/FrameProducer.h"
|
||||
#include "parser/FrameConsumer.h"
|
||||
#include "concurrency/BufferConsumer.h"
|
||||
#include "audio/Effect.h"
|
||||
#include <numbers>
|
||||
#include "concurrency/BufferProducer.h"
|
||||
#include "audio/AudioWebSocketServer.h"
|
||||
#include "audio/DelayEffect.h"
|
||||
#include "audio/PitchDetector.h"
|
||||
|
@ -60,6 +60,7 @@ public:
|
|||
void changeProgramName(int index, const juce::String& newName) override;
|
||||
void getStateInformation(juce::MemoryBlock& destData) override;
|
||||
void setStateInformation(const void* data, int sizeInBytes) override;
|
||||
void read(std::vector<float>& buffer);
|
||||
|
||||
int VERSION_HINT = 1;
|
||||
|
||||
|
@ -173,9 +174,7 @@ public:
|
|||
|
||||
FrameProducer producer = FrameProducer(*this, std::make_shared<FileParser>());
|
||||
|
||||
BufferProducer audioProducer;
|
||||
|
||||
PitchDetector pitchDetector{audioProducer};
|
||||
PitchDetector pitchDetector{*this};
|
||||
std::shared_ptr<WobbleEffect> wobbleEffect = std::make_shared<WobbleEffect>(pitchDetector);
|
||||
|
||||
// shouldn't be accessed by audio thread, but needs to persist when GUI is closed
|
||||
|
@ -244,8 +243,11 @@ private:
|
|||
double actualTraceMin = traceMinValue;
|
||||
bool traceMaxEnabled = false;
|
||||
bool traceMinEnabled = false;
|
||||
|
||||
juce::SpinLock consumerLock;
|
||||
std::vector<std::shared_ptr<BufferConsumer>> consumers;
|
||||
|
||||
AudioWebSocketServer softwareOscilloscopeServer{audioProducer};
|
||||
AudioWebSocketServer softwareOscilloscopeServer{*this};
|
||||
|
||||
void updateFrame();
|
||||
void updateLengthIncrement();
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "AudioWebSocketServer.h"
|
||||
#include "../PluginProcessor.h"
|
||||
|
||||
AudioWebSocketServer::AudioWebSocketServer(BufferProducer& producer) : juce::Thread("AudioWebSocketServer"), producer(producer) {
|
||||
AudioWebSocketServer::AudioWebSocketServer(OscirenderAudioProcessor& audioProcessor) : juce::Thread("AudioWebSocketServer"), audioProcessor(audioProcessor) {
|
||||
server.setOnClientMessageCallback([](std::shared_ptr<ix::ConnectionState> connectionState, ix::WebSocket & webSocket, const ix::WebSocketMessagePtr & msg) {
|
||||
// The ConnectionState object contains information about the connection,
|
||||
// at this point only the client ip address and the port.
|
||||
|
@ -40,18 +41,15 @@ AudioWebSocketServer::AudioWebSocketServer(BufferProducer& producer) : juce::Thr
|
|||
AudioWebSocketServer::~AudioWebSocketServer() {
|
||||
server.stop();
|
||||
ix::uninitNetSystem();
|
||||
producer.unregisterConsumer(consumer);
|
||||
stopThread(1000);
|
||||
}
|
||||
|
||||
void AudioWebSocketServer::run() {
|
||||
producer.registerConsumer(consumer);
|
||||
|
||||
while (!threadShouldExit()) {
|
||||
auto floatBuffer = consumer->startProcessing();
|
||||
audioProcessor.read(floatBuffer);
|
||||
|
||||
for (int i = 0; i < floatBuffer->size(); i++) {
|
||||
short sample = floatBuffer->at(i) * 32767;
|
||||
for (int i = 0; i < floatBuffer.size(); i++) {
|
||||
short sample = floatBuffer[i] * 32767;
|
||||
char b0 = sample & 0xff;
|
||||
char b1 = (sample >> 8) & 0xff;
|
||||
buffer[2 * i] = b0;
|
||||
|
@ -62,8 +60,6 @@ void AudioWebSocketServer::run() {
|
|||
ix::IXWebSocketSendData data{buffer, sizeof(buffer)};
|
||||
client->sendBinary(data);
|
||||
}
|
||||
|
||||
consumer->finishedProcessing();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
#pragma once
|
||||
#include <JuceHeader.h>
|
||||
#include "../ixwebsocket/IXWebSocketServer.h"
|
||||
#include "../concurrency/BufferProducer.h"
|
||||
|
||||
class OscirenderAudioProcessor;
|
||||
class AudioWebSocketServer : juce::Thread {
|
||||
public:
|
||||
AudioWebSocketServer(BufferProducer& producer);
|
||||
AudioWebSocketServer(OscirenderAudioProcessor& audioProcessor);
|
||||
~AudioWebSocketServer();
|
||||
|
||||
void run() override;
|
||||
private:
|
||||
ix::WebSocketServer server{ 42988 };
|
||||
|
||||
BufferProducer& producer;
|
||||
std::shared_ptr<BufferConsumer> consumer = std::make_shared<BufferConsumer>(4096);
|
||||
OscirenderAudioProcessor& audioProcessor;
|
||||
std::vector<float> floatBuffer = std::vector<float>(2 * 4096);
|
||||
char buffer[4096 * 2 * 2];
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,24 +1,21 @@
|
|||
#include "PitchDetector.h"
|
||||
#include "PitchDetector.h"
|
||||
#include "../PluginProcessor.h"
|
||||
|
||||
PitchDetector::PitchDetector(BufferProducer& producer) : juce::Thread("PitchDetector"), producer(producer) {
|
||||
PitchDetector::PitchDetector(OscirenderAudioProcessor& audioProcessor) : juce::Thread("PitchDetector"), audioProcessor(audioProcessor) {
|
||||
startThread();
|
||||
}
|
||||
|
||||
PitchDetector::~PitchDetector() {
|
||||
producer.unregisterConsumer(consumer);
|
||||
stopThread(1000);
|
||||
}
|
||||
|
||||
void PitchDetector::run() {
|
||||
producer.registerConsumer(consumer);
|
||||
|
||||
while (!threadShouldExit()) {
|
||||
auto buffer = consumer->startProcessing();
|
||||
audioProcessor.read(buffer);
|
||||
|
||||
// buffer is for 2 channels, so we need to only use one
|
||||
for (int i = 0; i < fftSize; i++) {
|
||||
fftData[i] = buffer->at(2 * i);
|
||||
fftData[i] = buffer[2 * i];
|
||||
}
|
||||
|
||||
forwardFFT.performFrequencyOnlyForwardTransform(fftData.data());
|
||||
|
@ -37,8 +34,6 @@ void PitchDetector::run() {
|
|||
}
|
||||
|
||||
frequency = frequencyFromIndex(maxIndex);
|
||||
|
||||
consumer->finishedProcessing();
|
||||
triggerAsyncUpdate();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
#pragma once
|
||||
#include <JuceHeader.h>
|
||||
#include "../concurrency/BufferConsumer.h"
|
||||
#include "../concurrency/BufferProducer.h"
|
||||
|
||||
|
||||
class OscirenderAudioProcessor;
|
||||
class PitchDetector : public juce::Thread, public juce::AsyncUpdater {
|
||||
public:
|
||||
PitchDetector(BufferProducer& producer);
|
||||
PitchDetector(OscirenderAudioProcessor& audioProcessor);
|
||||
~PitchDetector();
|
||||
|
||||
void run() override;
|
||||
|
@ -21,10 +19,10 @@ private:
|
|||
static constexpr int fftOrder = 15;
|
||||
static constexpr int fftSize = 1 << fftOrder;
|
||||
|
||||
std::shared_ptr<BufferConsumer> consumer = std::make_shared<BufferConsumer>(fftSize);
|
||||
std::vector<float> buffer = std::vector<float>(2 * fftSize);
|
||||
juce::dsp::FFT forwardFFT{fftOrder};
|
||||
std::array<float, fftSize * 2> fftData;
|
||||
BufferProducer& producer;
|
||||
OscirenderAudioProcessor& audioProcessor;
|
||||
std::vector<std::function<void(float)>> callbacks;
|
||||
juce::SpinLock lock;
|
||||
float sampleRate = 192000.0f;
|
||||
|
@ -32,4 +30,4 @@ private:
|
|||
float frequencyFromIndex(int index);
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PitchDetector)
|
||||
};
|
||||
};
|
||||
|
|
|
@ -7,7 +7,6 @@ VisualiserComponent::VisualiserComponent(int numChannels, OscirenderAudioProcess
|
|||
}
|
||||
|
||||
VisualiserComponent::~VisualiserComponent() {
|
||||
audioProcessor.audioProducer.unregisterConsumer(consumer);
|
||||
stopThread(1000);
|
||||
}
|
||||
|
||||
|
@ -44,12 +43,9 @@ void VisualiserComponent::timerCallback() {
|
|||
}
|
||||
|
||||
void VisualiserComponent::run() {
|
||||
audioProcessor.audioProducer.registerConsumer(consumer);
|
||||
|
||||
while (!threadShouldExit()) {
|
||||
auto buffer = consumer->startProcessing();
|
||||
setBuffer(*buffer);
|
||||
consumer->finishedProcessing();
|
||||
audioProcessor.read(tempBuffer);
|
||||
setBuffer(tempBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,8 +23,8 @@ private:
|
|||
int numChannels = 2;
|
||||
juce::Colour backgroundColour, waveformColour;
|
||||
OscirenderAudioProcessor& audioProcessor;
|
||||
std::shared_ptr<BufferConsumer> consumer = std::make_shared<BufferConsumer>(4096);
|
||||
std::vector<float> tempBuffer = std::vector<float>(2 * 4096);
|
||||
int precision = 4;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(VisualiserComponent)
|
||||
};
|
||||
};
|
||||
|
|
|
@ -47,7 +47,6 @@ VolumeComponent::VolumeComponent(OscirenderAudioProcessor& p) : audioProcessor(p
|
|||
}
|
||||
|
||||
VolumeComponent::~VolumeComponent() {
|
||||
audioProcessor.audioProducer.unregisterConsumer(consumer);
|
||||
stopThread(1000);
|
||||
}
|
||||
|
||||
|
@ -92,29 +91,25 @@ void VolumeComponent::timerCallback() {
|
|||
}
|
||||
|
||||
void VolumeComponent::run() {
|
||||
audioProcessor.audioProducer.registerConsumer(consumer);
|
||||
|
||||
while (!threadShouldExit()) {
|
||||
auto buffer = consumer->startProcessing();
|
||||
audioProcessor.read(buffer);
|
||||
|
||||
float leftVolume = 0;
|
||||
float rightVolume = 0;
|
||||
|
||||
for (int i = 0; i < buffer->size(); i += 2) {
|
||||
leftVolume += buffer->at(i) * buffer->at(i);
|
||||
rightVolume += buffer->at(i + 1) * buffer->at(i + 1);
|
||||
for (int i = 0; i < buffer.size(); i += 2) {
|
||||
leftVolume += buffer[i] * buffer[i];
|
||||
rightVolume += buffer[i + 1] * buffer[i + 1];
|
||||
}
|
||||
// RMS
|
||||
leftVolume = std::sqrt(leftVolume / (buffer->size() / 2));
|
||||
rightVolume = std::sqrt(rightVolume / (buffer->size() / 2));
|
||||
leftVolume = std::sqrt(leftVolume / (buffer.size() / 2));
|
||||
rightVolume = std::sqrt(rightVolume / (buffer.size() / 2));
|
||||
|
||||
this->leftVolume = leftVolume;
|
||||
this->rightVolume = rightVolume;
|
||||
|
||||
avgLeftVolume = (avgLeftVolume * 0.95) + (leftVolume * 0.05);
|
||||
avgRightVolume = (avgRightVolume * 0.95) + (rightVolume * 0.05);
|
||||
|
||||
consumer->finishedProcessing();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <JuceHeader.h>
|
||||
#include "../concurrency/BufferConsumer.h"
|
||||
#include "../PluginProcessor.h"
|
||||
#include "../LookAndFeel.h"
|
||||
|
||||
|
@ -71,7 +70,7 @@ public:
|
|||
|
||||
private:
|
||||
OscirenderAudioProcessor& audioProcessor;
|
||||
std::shared_ptr<BufferConsumer> consumer = std::make_shared<BufferConsumer>(1 << 11);
|
||||
std::vector<float> buffer = std::vector<float>(2 * 1 << 11);
|
||||
|
||||
std::atomic<float> leftVolume = 0;
|
||||
std::atomic<float> rightVolume = 0;
|
||||
|
@ -87,4 +86,4 @@ private:
|
|||
std::unique_ptr<juce::Drawable> thresholdIcon;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(VolumeComponent)
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,123 +1,68 @@
|
|||
#pragma once
|
||||
|
||||
#include <JuceHeader.h>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
// This is a helper class for the producer and consumer threads.
|
||||
//
|
||||
// ORDER OF OPERATIONS:
|
||||
// 1. Consumer is created.
|
||||
// 2. The thread that owns the consumers calls registerConsumer() on the producer, which acquires the lock on the first buffer to be written to by calling getBuffer().
|
||||
// LOOP:
|
||||
// 3. The consumer calls startProcessing() to signal that they want to start processing the current buffer.
|
||||
// 4. The producer calls finishedWriting() to signal that they have finished writing to the current buffer, which gives the lock to the consumer.
|
||||
// 5. The consumer calls finishedProcessing() to signal that they have finished processing the current buffer.
|
||||
// 6. The producer calls getBuffer() to acquire the lock on the next buffer to be written to.
|
||||
// GOTO LOOP
|
||||
// 7. The thread that owns the consumer calls unregisterConsumer() on the producer at some point during the loop, which releases the lock on the current buffer.
|
||||
//
|
||||
class BufferConsumer {
|
||||
// FROM https://gist.github.com/Kuxe/6bdd5b748b5f11b303a5cccbb8c8a731
|
||||
/** General semaphore with N permissions **/
|
||||
class Semaphore {
|
||||
const size_t num_permissions;
|
||||
size_t avail;
|
||||
std::mutex m;
|
||||
std::condition_variable cv;
|
||||
public:
|
||||
BufferConsumer(int bufferSize) {
|
||||
firstBuffer->resize(2 * bufferSize, 0.0);
|
||||
secondBuffer->resize(2 * bufferSize, 0.0);
|
||||
/** Default constructor. Default semaphore is a binary semaphore **/
|
||||
explicit Semaphore(const size_t& num_permissions = 1) : num_permissions(num_permissions), avail(num_permissions) { }
|
||||
|
||||
/** Copy constructor. Does not copy state of mutex or condition variable,
|
||||
only the number of permissions and number of available permissions **/
|
||||
Semaphore(const Semaphore& s) : num_permissions(s.num_permissions), avail(s.avail) { }
|
||||
|
||||
void acquire() {
|
||||
std::unique_lock<std::mutex> lk(m);
|
||||
cv.wait(lk, [this] { return avail > 0; });
|
||||
avail--;
|
||||
lk.unlock();
|
||||
}
|
||||
|
||||
~BufferConsumer() {}
|
||||
|
||||
// Returns the buffer that is ready to be written to.
|
||||
// This is only called by the producer thread.
|
||||
// force forces the lock to be acquired.
|
||||
// Returns nullptr if the lock can't be acquired when force is false.
|
||||
// It is only called when the global producer lock is held.
|
||||
std::shared_ptr<std::vector<float>> getBuffer(bool force) {
|
||||
auto buffer = firstBufferWriting ? firstBuffer : secondBuffer;
|
||||
if (lockHeldForWriting) {
|
||||
return buffer;
|
||||
}
|
||||
auto bufferLock = firstBufferWriting ? firstBufferLock : secondBufferLock;
|
||||
|
||||
if (force) {
|
||||
bufferLock->enter();
|
||||
lockHeldForWriting = true;
|
||||
return buffer;
|
||||
} else if (bufferLock->tryEnter()) {
|
||||
lockHeldForWriting = true;
|
||||
return buffer;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
void release() {
|
||||
m.lock();
|
||||
avail++;
|
||||
m.unlock();
|
||||
cv.notify_one();
|
||||
}
|
||||
|
||||
// This is only called by the producer thread. It is only called when the global
|
||||
// producer lock is held.
|
||||
void finishedWriting() {
|
||||
auto bufferLock = firstBufferWriting ? firstBufferLock : secondBufferLock;
|
||||
lockHeldForWriting = false;
|
||||
firstBufferWriting = !firstBufferWriting;
|
||||
// Try locking before we unlock the current buffer so that
|
||||
// the consumer doesn't start processing before we
|
||||
// unlock the buffer. Ignore if we can't get the lock
|
||||
// because the consumer is still processing.
|
||||
getBuffer(false);
|
||||
bufferLock->exit();
|
||||
size_t available() const {
|
||||
return avail;
|
||||
}
|
||||
|
||||
void releaseLock() {
|
||||
if (lockHeldForWriting) {
|
||||
auto bufferLock = firstBufferWriting ? firstBufferLock : secondBufferLock;
|
||||
bufferLock->exit();
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the buffer that has been written to fully and is ready to be processed.
|
||||
// This will lock the buffer so that the producer can't write to it while we're processing.
|
||||
std::shared_ptr<std::vector<float>> startProcessing() {
|
||||
auto buffer = firstBufferProcessing ? firstBuffer : secondBuffer;
|
||||
auto bufferLock = firstBufferProcessing ? firstBufferLock : secondBufferLock;
|
||||
|
||||
bufferLock->enter();
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// This should be called after processing has finished.
|
||||
// It releases the lock on the buffer so that the producer can write to it again.
|
||||
void finishedProcessing() {
|
||||
auto bufferLock = firstBufferProcessing ? firstBufferLock : secondBufferLock;
|
||||
firstBufferProcessing = !firstBufferProcessing;
|
||||
bufferLock->exit();
|
||||
}
|
||||
|
||||
std::shared_ptr<std::vector<float>> firstBuffer = std::make_shared<std::vector<float>>();
|
||||
std::shared_ptr<std::vector<float>> secondBuffer = std::make_shared<std::vector<float>>();
|
||||
std::shared_ptr<juce::CriticalSection> firstBufferLock = std::make_shared<juce::CriticalSection>();
|
||||
std::shared_ptr<juce::CriticalSection> secondBufferLock = std::make_shared<juce::CriticalSection>();
|
||||
private:
|
||||
// Indirectly used by the producer to signal whether it holds the lock on the buffer.
|
||||
// This is accurate if the global producer lock is held as the buffer lock is acquired
|
||||
// and this is set to true before the global producer lock is released.
|
||||
bool lockHeldForWriting = false;
|
||||
bool firstBufferWriting = true;
|
||||
bool firstBufferProcessing = true;
|
||||
};
|
||||
|
||||
class DummyConsumer : public juce::Thread {
|
||||
|
||||
class BufferConsumer {
|
||||
public:
|
||||
DummyConsumer(std::shared_ptr<BufferConsumer> consumer) : juce::Thread("DummyConsumer"), consumer(consumer) {}
|
||||
~DummyConsumer() {}
|
||||
BufferConsumer(std::vector<float>& buffer) : buffer(buffer) {}
|
||||
|
||||
void run() override {
|
||||
while (!threadShouldExit()) {
|
||||
auto buffer = consumer->startProcessing();
|
||||
~BufferConsumer() {}
|
||||
|
||||
void waitUntilFull() {
|
||||
sema.acquire();
|
||||
}
|
||||
|
||||
float total = 0.0;
|
||||
for (int i = 0; i < buffer->size(); i++) {
|
||||
total += (*buffer)[i];
|
||||
}
|
||||
DBG(total);
|
||||
|
||||
consumer->finishedProcessing();
|
||||
void notifyIfFull() {
|
||||
if (offset >= buffer.size()) {
|
||||
sema.release();
|
||||
}
|
||||
}
|
||||
|
||||
void write(double d) {
|
||||
if (offset < buffer.size()) {
|
||||
buffer[offset++] = d;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<BufferConsumer> consumer;
|
||||
};
|
||||
std::vector<float>& buffer;
|
||||
Semaphore sema{0};
|
||||
int offset = 0;
|
||||
};
|
||||
|
|
|
@ -1,63 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <JuceHeader.h>
|
||||
#include "BufferConsumer.h"
|
||||
|
||||
class BufferProducer {
|
||||
public:
|
||||
BufferProducer() {}
|
||||
~BufferProducer() {}
|
||||
|
||||
// This should add the buffers and locks to the vectors
|
||||
// and then lock the first buffer lock so it can start
|
||||
// being written to.
|
||||
// This is only called by the thread that owns the consumer thread.
|
||||
void registerConsumer(std::shared_ptr<BufferConsumer> consumer) {
|
||||
juce::CriticalSection::ScopedLockType l(lock);
|
||||
consumers.push_back(consumer);
|
||||
bufferPositions.push_back(0);
|
||||
consumer->getBuffer(true);
|
||||
}
|
||||
|
||||
// This is only called by the thread that owns the consumer thread.
|
||||
// This can't happen at the same time as write() it locks the producer lock.
|
||||
void unregisterConsumer(std::shared_ptr<BufferConsumer> consumer) {
|
||||
juce::CriticalSection::ScopedLockType l(lock);
|
||||
for (int i = 0; i < consumers.size(); i++) {
|
||||
if (consumers[i] == consumer) {
|
||||
consumer->releaseLock();
|
||||
consumers.erase(consumers.begin() + i);
|
||||
bufferPositions.erase(bufferPositions.begin() + i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Writes a sample to the current buffer for all consumers.
|
||||
void write(float left, float right) {
|
||||
juce::CriticalSection::ScopedLockType l(lock);
|
||||
for (int i = 0; i < consumers.size(); i++) {
|
||||
std::shared_ptr<std::vector<float>> buffer = consumers[i]->getBuffer(false);
|
||||
if (buffer == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
(*buffer)[bufferPositions[i]] = left;
|
||||
(*buffer)[bufferPositions[i] + 1] = right;
|
||||
bufferPositions[i] += 2;
|
||||
|
||||
// If we've reached the end of the buffer, switch
|
||||
// to the other buffer and unlock it. This signals
|
||||
// to the consumer that it can start processing!
|
||||
if (bufferPositions[i] >= buffer->size()) {
|
||||
bufferPositions[i] = 0;
|
||||
consumers[i]->finishedWriting();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
juce::CriticalSection lock;
|
||||
std::vector<std::shared_ptr<BufferConsumer>> consumers;
|
||||
std::vector<int> bufferPositions;
|
||||
};
|
|
@ -133,8 +133,6 @@
|
|||
<GROUP id="{9F5970A9-8094-E7F3-7AC1-812AE5589B9F}" name="concurrency">
|
||||
<FILE id="WQ2W15" name="BufferConsumer.h" compile="0" resource="0"
|
||||
file="Source/concurrency/BufferConsumer.h"/>
|
||||
<FILE id="yWTiQQ" name="BufferProducer.h" compile="0" resource="0"
|
||||
file="Source/concurrency/BufferProducer.h"/>
|
||||
</GROUP>
|
||||
<FILE id="I44EdJ" name="EffectsComponent.cpp" compile="1" resource="0"
|
||||
file="Source/EffectsComponent.cpp"/>
|
||||
|
|
Ładowanie…
Reference in New Issue