kopia lustrzana https://github.com/jameshball/osci-render
Refactor BufferConsumer to use a double buffer that results in significantly less audio loss
rodzic
8296014272
commit
6054d81541
|
@ -754,7 +754,6 @@ void OscirenderAudioProcessor::processBlock(juce::AudioBuffer<float>& buffer, ju
|
|||
juce::SpinLock::ScopedLockType scope(consumerLock);
|
||||
for (auto consumer : consumers) {
|
||||
consumer->write(OsciPoint(x, y, 1));
|
||||
consumer->notifyIfFull();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "../PluginProcessor.h"
|
||||
|
||||
PitchDetector::PitchDetector(OscirenderAudioProcessor& audioProcessor) : juce::Thread("PitchDetector"), audioProcessor(audioProcessor) {
|
||||
consumer = audioProcessor.consumerRegister(fftSize);
|
||||
startThread();
|
||||
}
|
||||
|
||||
|
@ -15,15 +16,11 @@ PitchDetector::~PitchDetector() {
|
|||
|
||||
void PitchDetector::run() {
|
||||
while (!threadShouldExit()) {
|
||||
{
|
||||
juce::CriticalSection::ScopedLockType scope(consumerLock);
|
||||
consumer = audioProcessor.consumerRegister(buffer);
|
||||
}
|
||||
audioProcessor.consumerRead(consumer);
|
||||
|
||||
// buffer is for 2 channels, so we need to only use one
|
||||
for (int i = 0; i < fftSize; i++) {
|
||||
fftData[i] = buffer[i].x;
|
||||
fftData[i] = consumer->getFullBuffer()[i].x;
|
||||
}
|
||||
|
||||
forwardFFT.performFrequencyOnlyForwardTransform(fftData.data());
|
||||
|
|
|
@ -22,7 +22,6 @@ private:
|
|||
|
||||
juce::CriticalSection consumerLock;
|
||||
std::shared_ptr<BufferConsumer> consumer;
|
||||
std::vector<OsciPoint> buffer = std::vector<OsciPoint>(fftSize);
|
||||
juce::dsp::FFT forwardFFT{fftOrder};
|
||||
std::array<float, fftSize * 2> fftData;
|
||||
OscirenderAudioProcessor& audioProcessor;
|
||||
|
|
|
@ -153,14 +153,10 @@ void VisualiserComponent::run() {
|
|||
resetBuffer();
|
||||
}
|
||||
|
||||
{
|
||||
juce::CriticalSection::ScopedLockType scope(consumerLock);
|
||||
consumer = consumerManager.consumerRegister(tempBuffer);
|
||||
}
|
||||
consumerManager.consumerRead(consumer);
|
||||
|
||||
// TODO: Find a way to immediately call consumerRegister after consumerRead so that no audio is missed
|
||||
setBuffer(tempBuffer);
|
||||
setBuffer(consumer->getFullBuffer());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -247,7 +243,12 @@ void VisualiserComponent::paintXY(juce::Graphics& g, juce::Rectangle<float> area
|
|||
|
||||
void VisualiserComponent::resetBuffer() {
|
||||
sampleRate = (int) sampleRateManager.getSampleRate();
|
||||
tempBuffer = std::vector<OsciPoint>(sampleRate * BUFFER_LENGTH_SECS);
|
||||
|
||||
{
|
||||
juce::CriticalSection::ScopedLockType scope(consumerLock);
|
||||
consumerManager.consumerStop(consumer);
|
||||
consumer = consumerManager.consumerRegister(sampleRate * BUFFER_LENGTH_SECS);
|
||||
}
|
||||
}
|
||||
|
||||
void VisualiserComponent::toggleRecording() {
|
||||
|
|
|
@ -80,7 +80,6 @@ private:
|
|||
SvgButton popOutButton{ "popOut", BinaryData::open_in_new_svg, juce::Colours::white, juce::Colours::white };
|
||||
SvgButton settingsButton{ "settings", BinaryData::cog_svg, juce::Colours::white, juce::Colours::white };
|
||||
|
||||
std::vector<OsciPoint> tempBuffer;
|
||||
int precision = 4;
|
||||
|
||||
juce::CriticalSection consumerLock;
|
||||
|
|
|
@ -15,6 +15,8 @@ VisualiserOpenGLComponent::~VisualiserOpenGLComponent() {
|
|||
void VisualiserOpenGLComponent::newOpenGLContextCreated() {
|
||||
using namespace juce::gl;
|
||||
|
||||
juce::CriticalSection::ScopedLockType lock(samplesLock);
|
||||
|
||||
juce::OpenGLHelpers::clear(juce::Colours::black);
|
||||
glColorMask(true, true, true, true);
|
||||
|
||||
|
@ -304,8 +306,7 @@ void VisualiserOpenGLComponent::openGLContextClosing() {
|
|||
}
|
||||
|
||||
void VisualiserOpenGLComponent::updateBuffer(std::vector<OsciPoint>& buffer) {
|
||||
// TODO: Figure out whether locking on samplesLock is required
|
||||
//juce::CriticalSection::ScopedLockType lock(samplesLock);
|
||||
juce::CriticalSection::ScopedLockType lock(samplesLock);
|
||||
|
||||
if (xSamples.size() != buffer.size()) {
|
||||
needsReattach = true;
|
||||
|
@ -323,6 +324,8 @@ void VisualiserOpenGLComponent::updateBuffer(std::vector<OsciPoint>& buffer) {
|
|||
}
|
||||
|
||||
void VisualiserOpenGLComponent::handleAsyncUpdate() {
|
||||
juce::CriticalSection::ScopedLockType lock(samplesLock);
|
||||
|
||||
int newResampledSize = xSamples.size() * RESAMPLE_RATIO;
|
||||
|
||||
smoothedXSamples.resize(newResampledSize);
|
||||
|
@ -350,7 +353,7 @@ void VisualiserOpenGLComponent::handleAsyncUpdate() {
|
|||
|
||||
void VisualiserOpenGLComponent::renderOpenGL() {
|
||||
if (openGLContext.isActive()) {
|
||||
//juce::CriticalSection::ScopedLockType lock(samplesLock);
|
||||
juce::CriticalSection::ScopedLockType lock(samplesLock);
|
||||
|
||||
if (graticuleEnabled != settings.getGraticuleEnabled() || smudgesEnabled != settings.getSmudgesEnabled()) {
|
||||
graticuleEnabled = settings.getGraticuleEnabled();
|
||||
|
@ -457,13 +460,11 @@ Texture VisualiserOpenGLComponent::makeTexture(int width, int height) {
|
|||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_FLOAT, nullptr);
|
||||
|
||||
// Set texture filtering and wrapping
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0); // Unbind
|
||||
|
||||
return { textureID, width, height };
|
||||
|
@ -477,7 +478,6 @@ void VisualiserOpenGLComponent::drawLineTexture(const std::vector<float>& xPoint
|
|||
fade();
|
||||
drawLine(xPoints, yPoints, zPoints);
|
||||
glBindTexture(GL_TEXTURE_2D, targetTexture.value().id);
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
}
|
||||
|
||||
void VisualiserOpenGLComponent::saveTextureToFile(GLuint textureID, int width, int height, const juce::File& file) {
|
||||
|
@ -582,7 +582,6 @@ void VisualiserOpenGLComponent::drawTexture(std::optional<Texture> texture0, std
|
|||
|
||||
if (targetTexture.has_value()) {
|
||||
glBindTexture(GL_TEXTURE_2D, targetTexture.value().id);
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -782,7 +781,6 @@ Texture VisualiserOpenGLComponent::createScreenTexture() {
|
|||
glLineWidth(1.0f);
|
||||
glDrawArrays(GL_LINES, 0, data.size());
|
||||
glBindTexture(GL_TEXTURE_2D, targetTexture.value().id);
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
}
|
||||
|
||||
|
|
|
@ -102,16 +102,10 @@ void VolumeComponent::run() {
|
|||
resetBuffer();
|
||||
}
|
||||
|
||||
if (buffer.size() == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
{
|
||||
juce::CriticalSection::ScopedLockType lock(consumerLock);
|
||||
consumer = audioProcessor.consumerRegister(buffer);
|
||||
}
|
||||
audioProcessor.consumerRead(consumer);
|
||||
|
||||
auto buffer = consumer->getFullBuffer();
|
||||
|
||||
float leftVolume = 0;
|
||||
float rightVolume = 0;
|
||||
|
||||
|
@ -149,5 +143,10 @@ void VolumeComponent::resized() {
|
|||
|
||||
void VolumeComponent::resetBuffer() {
|
||||
sampleRate = (int) audioProcessor.currentSampleRate;
|
||||
buffer = std::vector<OsciPoint>(BUFFER_DURATION_SECS * sampleRate);
|
||||
|
||||
{
|
||||
juce::CriticalSection::ScopedLockType scope(consumerLock);
|
||||
audioProcessor.consumerStop(consumer);
|
||||
consumer = audioProcessor.consumerRegister(BUFFER_DURATION_SECS * sampleRate);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,7 +76,6 @@ private:
|
|||
const double BUFFER_DURATION_SECS = 0.02;
|
||||
|
||||
int sampleRate = DEFAULT_SAMPLE_RATE;
|
||||
std::vector<OsciPoint> buffer = std::vector<OsciPoint>(BUFFER_DURATION_SECS * DEFAULT_SAMPLE_RATE);
|
||||
|
||||
std::atomic<float> leftVolume = 0;
|
||||
std::atomic<float> rightVolume = 0;
|
||||
|
|
|
@ -42,7 +42,10 @@ public:
|
|||
|
||||
class BufferConsumer {
|
||||
public:
|
||||
BufferConsumer(std::vector<OsciPoint>& buffer) : buffer(buffer) {}
|
||||
BufferConsumer(std::size_t size) {
|
||||
buffer1.resize(size);
|
||||
buffer2.resize(size);
|
||||
}
|
||||
|
||||
~BufferConsumer() {}
|
||||
|
||||
|
@ -50,12 +53,6 @@ public:
|
|||
sema.acquire();
|
||||
}
|
||||
|
||||
void notifyIfFull() {
|
||||
if (offset >= buffer.size()) {
|
||||
sema.release();
|
||||
}
|
||||
}
|
||||
|
||||
// to be used when the audio thread is being destroyed to
|
||||
// make sure that everything waiting on it stops waiting.
|
||||
void forceNotify() {
|
||||
|
@ -63,13 +60,24 @@ public:
|
|||
}
|
||||
|
||||
void write(OsciPoint point) {
|
||||
if (offset < buffer.size()) {
|
||||
buffer[offset++] = point;
|
||||
if (offset >= buffer->size()) {
|
||||
buffer = buffer == &buffer1 ? &buffer2 : &buffer1;
|
||||
offset = 0;
|
||||
sema.release();
|
||||
}
|
||||
|
||||
(*buffer)[offset++] = point;
|
||||
}
|
||||
|
||||
// whatever buffer is not currently being written to
|
||||
std::vector<OsciPoint>& getFullBuffer() {
|
||||
return buffer == &buffer1 ? buffer2 : buffer1;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<OsciPoint>& buffer;
|
||||
std::vector<OsciPoint> buffer1;
|
||||
std::vector<OsciPoint> buffer2;
|
||||
std::vector<OsciPoint>* buffer = &buffer1;
|
||||
Semaphore sema{0};
|
||||
int offset = 0;
|
||||
};
|
||||
|
|
|
@ -10,8 +10,8 @@ public:
|
|||
ConsumerManager() {}
|
||||
~ConsumerManager() {}
|
||||
|
||||
std::shared_ptr<BufferConsumer> consumerRegister(std::vector<OsciPoint>& buffer) {
|
||||
std::shared_ptr<BufferConsumer> consumer = std::make_shared<BufferConsumer>(buffer);
|
||||
std::shared_ptr<BufferConsumer> consumerRegister(std::size_t size) {
|
||||
std::shared_ptr<BufferConsumer> consumer = std::make_shared<BufferConsumer>(size);
|
||||
juce::SpinLock::ScopedLockType scope(consumerLock);
|
||||
consumers.push_back(consumer);
|
||||
|
||||
|
@ -20,8 +20,6 @@ public:
|
|||
|
||||
void consumerRead(std::shared_ptr<BufferConsumer> consumer) {
|
||||
consumer->waitUntilFull();
|
||||
juce::SpinLock::ScopedLockType scope(consumerLock);
|
||||
consumers.erase(std::remove(consumers.begin(), consumers.end(), consumer), consumers.end());
|
||||
}
|
||||
|
||||
void consumerStop(std::shared_ptr<BufferConsumer> consumer) {
|
||||
|
|
Ładowanie…
Reference in New Issue