Get basic MIDI input working

pull/170/head
James Ball 2023-08-28 22:06:21 +01:00
rodzic 5be6ec80e9
commit 13a6a6e2e3
12 zmienionych plików z 325 dodań i 215 usunięć

Wyświetl plik

@ -32,8 +32,6 @@ OscirenderAudioProcessor::OscirenderAudioProcessor()
)
#endif
{
producer.startThread();
// locking isn't necessary here because we are in the constructor
toggleableEffects.push_back(std::make_shared<Effect>(
@ -137,6 +135,10 @@ OscirenderAudioProcessor::OscirenderAudioProcessor()
for (auto parameter : booleanParameters) {
addParameter(parameter);
}
for (int i = 0; i < 4; i++) {
synth.addVoice(new ShapeVoice(*this));
}
}
OscirenderAudioProcessor::~OscirenderAudioProcessor() {}
@ -194,6 +196,7 @@ void OscirenderAudioProcessor::changeProgramName(int index, const juce::String&
void OscirenderAudioProcessor::prepareToPlay(double sampleRate, int samplesPerBlock) {
currentSampleRate = sampleRate;
pitchDetector.setSampleRate(sampleRate);
synth.setCurrentPlaybackSampleRate(sampleRate);
}
void OscirenderAudioProcessor::releaseResources() {
@ -304,7 +307,8 @@ void OscirenderAudioProcessor::updateFileBlock(int index, std::shared_ptr<juce::
void OscirenderAudioProcessor::addFile(juce::File file) {
fileBlocks.push_back(std::make_shared<juce::MemoryBlock>());
fileNames.push_back(file.getFileName());
parsers.push_back(std::make_unique<FileParser>());
parsers.push_back(std::make_shared<FileParser>());
sounds.push_back(new ShapeSound(parsers.back()));
file.createInputStream()->readIntoMemoryBlock(*fileBlocks.back());
openFile(fileBlocks.size() - 1);
@ -314,7 +318,8 @@ void OscirenderAudioProcessor::addFile(juce::File file) {
void OscirenderAudioProcessor::addFile(juce::String fileName, const char* data, const int size) {
fileBlocks.push_back(std::make_shared<juce::MemoryBlock>());
fileNames.push_back(fileName);
parsers.push_back(std::make_unique<FileParser>());
parsers.push_back(std::make_shared<FileParser>());
sounds.push_back(new ShapeSound(parsers.back()));
fileBlocks.back()->append(data, size);
openFile(fileBlocks.size() - 1);
@ -324,7 +329,8 @@ void OscirenderAudioProcessor::addFile(juce::String fileName, const char* data,
void OscirenderAudioProcessor::addFile(juce::String fileName, std::shared_ptr<juce::MemoryBlock> data) {
fileBlocks.push_back(data);
fileNames.push_back(fileName);
parsers.push_back(std::make_unique<FileParser>());
parsers.push_back(std::make_shared<FileParser>());
sounds.push_back(new ShapeSound(parsers.back()));
openFile(fileBlocks.size() - 1);
}
@ -337,6 +343,7 @@ void OscirenderAudioProcessor::removeFile(int index) {
fileBlocks.erase(fileBlocks.begin() + index);
fileNames.erase(fileNames.begin() + index);
parsers.erase(parsers.begin() + index);
sounds.erase(sounds.begin() + index);
auto newFileIndex = index;
if (newFileIndex >= fileBlocks.size()) {
newFileIndex = fileBlocks.size() - 1;
@ -363,17 +370,18 @@ void OscirenderAudioProcessor::openFile(int index) {
// used ONLY for changing the current file to an EXISTING file.
// much faster than openFile(int index) because it doesn't reparse any files.
// parsersLock AND effectsLock must be locked before calling this function
// TODO: This should change whatever the ShapeSound is to the new index
void OscirenderAudioProcessor::changeCurrentFile(int index) {
synth.clearSounds();
if (index == -1) {
currentFile = -1;
producer.setSource(std::make_shared<FileParser>(), -1);
}
if (index < 0 || index >= fileBlocks.size()) {
return;
}
producer.setSource(parsers[index], index);
synth.addSound(sounds[index]);
currentFile = index;
invalidateFrameBuffer = true;
updateLuaValues();
updateObjValues();
}
@ -402,113 +410,19 @@ std::shared_ptr<juce::MemoryBlock> OscirenderAudioProcessor::getFileBlock(int in
return fileBlocks[index];
}
void OscirenderAudioProcessor::addFrame(std::vector<std::unique_ptr<Shape>> frame, int fileIndex) {
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));
}
frameBufferIndices[scope.startIndex1] = fileIndex;
}
if (scope.blockSize2 > 0) {
frameBuffer[scope.startIndex2].clear();
for (auto& shape : frame) {
frameBuffer[scope.startIndex2].push_back(std::move(shape));
}
frameBufferIndices[scope.startIndex2] = fileIndex;
}
}
void OscirenderAudioProcessor::updateFrame() {
currentShape = 0;
shapeDrawn = 0.0;
frameDrawn = 0.0;
if (frameFifo.getNumReady() > 0) {
{
const auto scope = frameFifo.read(1);
if (scope.blockSize1 > 0) {
frame.swap(frameBuffer[scope.startIndex1]);
currentBufferIndex = frameBufferIndices[scope.startIndex1];
} else if (scope.blockSize2 > 0) {
frame.swap(frameBuffer[scope.startIndex2]);
currentBufferIndex = frameBufferIndices[scope.startIndex2];
}
frameLength = Shape::totalLength(frame);
}
}
}
void OscirenderAudioProcessor::updateLengthIncrement() {
double traceMaxValue = traceMaxEnabled ? actualTraceMax : 1.0;
double traceMinValue = traceMinEnabled ? actualTraceMin : 0.0;
double proportionalLength = (traceMaxValue - traceMinValue) * frameLength;
lengthIncrement = juce::jmax(proportionalLength / (currentSampleRate / frequency), MIN_LENGTH_INCREMENT);
}
void OscirenderAudioProcessor::processBlock(juce::AudioBuffer<float>& buffer, juce::MidiBuffer& midiMessages)
{
void OscirenderAudioProcessor::processBlock(juce::AudioBuffer<float>& buffer, juce::MidiBuffer& midiMessages) {
juce::ScopedNoDenormals noDenormals;
auto totalNumInputChannels = getTotalNumInputChannels();
auto totalNumOutputChannels = getTotalNumOutputChannels();
// In case we have more outputs than inputs, this code clears any output
// channels that didn't contain input data, (because these aren't
// guaranteed to be empty - they may contain garbage).
// This is here to avoid people getting screaming feedback
// when they first compile a plugin, but obviously you don't need to keep
// this code if your algorithm always overwrites all the output channels.
for (auto i = totalNumInputChannels; i < totalNumOutputChannels; ++i)
buffer.clear (i, 0, buffer.getNumSamples());
buffer.clear();
synth.renderNextBlock(buffer, midiMessages, 0, buffer.getNumSamples());
midiMessages.clear();
auto* channelData = buffer.getArrayOfWritePointers();
auto numSamples = buffer.getNumSamples();
if (invalidateFrameBuffer) {
frameFifo.reset();
// keeps getting the next frame until the frame comes from the file that we want to render.
// this MIGHT be hacky and cause issues later down the line, but for now it works as a
// solution to get instant changing of current file when pressing j and k.
while (currentBufferIndex != currentFile) {
updateFrame();
}
invalidateFrameBuffer = false;
}
for (auto sample = 0; sample < numSamples; ++sample) {
updateLengthIncrement();
traceMaxEnabled = false;
traceMinEnabled = false;
Vector2 channels;
double x = 0.0;
double y = 0.0;
std::shared_ptr<FileParser> sampleParser;
{
juce::SpinLock::ScopedLockType lock(parsersLock);
if (currentFile >= 0 && parsers[currentFile]->isSample()) {
sampleParser = parsers[currentFile];
}
}
bool renderingSample = sampleParser != nullptr;
if (renderingSample) {
channels = sampleParser->nextSample();
} else if (currentShape < frame.size()) {
auto& shape = frame[currentShape];
double length = shape->length();
double drawingProgress = length == 0.0 ? 1 : shapeDrawn / length;
channels = shape->nextVector(drawingProgress);
}
for (auto sample = 0; sample < buffer.getNumSamples(); ++sample) {
Vector2 channels = {buffer.getSample(0, sample), buffer.getSample(1, sample)};
{
juce::SpinLock::ScopedLockType lock1(parsersLock);
@ -523,8 +437,8 @@ void OscirenderAudioProcessor::processBlock(juce::AudioBuffer<float>& buffer, ju
}
}
x = channels.x;
y = channels.y;
double x = channels.x;
double y = channels.y;
x *= volume;
y *= volume;
@ -541,54 +455,6 @@ void OscirenderAudioProcessor::processBlock(juce::AudioBuffer<float>& buffer, ju
}
audioProducer.write(x, y);
actualTraceMax = juce::jmax(actualTraceMin + MIN_TRACE, juce::jmin(traceMaxValue, 1.0));
actualTraceMin = juce::jmax(MIN_TRACE, juce::jmin(traceMinValue, actualTraceMax - MIN_TRACE));
if (!renderingSample) {
incrementShapeDrawing();
}
double drawnFrameLength = traceMaxEnabled ? actualTraceMax * frameLength : frameLength;
if (!renderingSample && frameDrawn >= drawnFrameLength) {
updateFrame();
// TODO: updateFrame already iterates over all the shapes,
// so we can improve performance by calculating frameDrawn
// and shapeDrawn directly. frameDrawn is simply actualTraceMin * frameLength
// but shapeDrawn is the amount of the current shape that has been drawn so
// we need to iterate over all the shapes to calculate it.
if (traceMinEnabled) {
while (frameDrawn < actualTraceMin * frameLength) {
incrementShapeDrawing();
}
}
}
}
}
// TODO this is the slowest part of the program - any way to improve this would help!
void OscirenderAudioProcessor::incrementShapeDrawing() {
double length = currentShape < frame.size() ? frame[currentShape]->len : 0.0;
// hard cap on how many times it can be over the length to
// prevent audio stuttering
auto increment = juce::jmin(lengthIncrement, 20 * length);
frameDrawn += increment;
shapeDrawn += increment;
// Need to skip all shapes that the lengthIncrement draws over.
// This is especially an issue when there are lots of small lines being
// drawn.
while (shapeDrawn > length) {
shapeDrawn -= length;
currentShape++;
if (currentShape >= frame.size()) {
currentShape = 0;
break;
}
// POTENTIAL TODO: Think of a way to make this more efficient when iterating
// this loop many times
length = frame[currentShape]->len;
}
}

Wyświetl plik

@ -10,10 +10,9 @@
#include <JuceHeader.h>
#include "shape/Shape.h"
#include "parser/FileParser.h"
#include "parser/FrameProducer.h"
#include "parser/FrameConsumer.h"
#include "audio/Effect.h"
#include "audio/ShapeSound.h"
#include "audio/ShapeVoice.h"
#include <numbers>
#include "concurrency/BufferProducer.h"
#include "audio/AudioWebSocketServer.h"
@ -29,7 +28,6 @@ class OscirenderAudioProcessor : public juce::AudioProcessor
#if JucePlugin_Enable_ARA
, public juce::AudioProcessorARAExtension
#endif
, public FrameConsumer
{
public:
OscirenderAudioProcessor();
@ -158,19 +156,29 @@ public:
}, new EffectParameter("Rotate Speed", "objRotateSpeed", 0.0, -1.0, 1.0)
);
std::shared_ptr<Effect> traceMax = std::make_shared<Effect>(
[this](int index, Vector2 input, const std::vector<double>& values, double sampleRate) {
return input;
}, new EffectParameter("Trace max", "traceMax", 1.0, 0.0, 1.0)
);
std::shared_ptr<Effect> traceMin = std::make_shared<Effect>(
[this](int index, Vector2 input, const std::vector<double>& values, double sampleRate) {
return input;
}, new EffectParameter("Trace min", "traceMin", 0.0, 0.0, 1.0)
);
std::shared_ptr<DelayEffect> delayEffect = std::make_shared<DelayEffect>();
std::shared_ptr<PerspectiveEffect> perspectiveEffect = std::make_shared<PerspectiveEffect>();
juce::SpinLock parsersLock;
std::vector<std::shared_ptr<FileParser>> parsers;
std::vector<ShapeSound::Ptr> sounds;
std::vector<std::shared_ptr<juce::MemoryBlock>> fileBlocks;
std::vector<juce::String> fileNames;
std::atomic<int> currentFile = -1;
juce::ChangeBroadcaster broadcaster;
FrameProducer producer = FrameProducer(*this, std::make_shared<FileParser>());
BufferProducer audioProducer;
PitchDetector pitchDetector{audioProducer};
@ -184,7 +192,6 @@ public:
juce::Font font = juce::Font(juce::Font::getDefaultSansSerifFontName(), 1.0f, juce::Font::plain);
void addLuaSlider();
void addFrame(std::vector<std::unique_ptr<Shape>> frame, int fileIndex) override;
void updateEffectPrecedence();
void updateFileBlock(int index, std::shared_ptr<juce::MemoryBlock> block);
void addFile(juce::File file);
@ -204,50 +211,14 @@ private:
std::atomic<double> volume = 1.0;
std::atomic<double> threshold = 1.0;
juce::AbstractFifo frameFifo{ 10 };
std::vector<std::unique_ptr<Shape>> frameBuffer[10];
int frameBufferIndices[10];
int currentShape = 0;
std::vector<std::unique_ptr<Shape>> frame;
int currentBufferIndex = -1;
double frameLength;
double shapeDrawn = 0.0;
double frameDrawn = 0.0;
double lengthIncrement = 0.0;
bool invalidateFrameBuffer = false;
std::vector<BooleanParameter*> booleanParameters;
std::vector<std::shared_ptr<Effect>> allEffects;
std::vector<std::shared_ptr<Effect>> permanentEffects;
std::shared_ptr<Effect> traceMax = std::make_shared<Effect>(
[this](int index, Vector2 input, const std::vector<double>& values, double sampleRate) {
traceMaxValue = values[0];
traceMaxEnabled = true;
return input;
}, new EffectParameter("Trace max", "traceMax", 1.0, 0.0, 1.0)
);
std::shared_ptr<Effect> traceMin = std::make_shared<Effect>(
[this](int index, Vector2 input, const std::vector<double>& values, double sampleRate) {
traceMinValue = values[0];
traceMinEnabled = true;
return input;
}, new EffectParameter("Trace min", "traceMin", 0.0, 0.0, 1.0)
);
const double MIN_TRACE = 0.005;
double traceMaxValue = traceMax->getValue();
double traceMinValue = traceMin->getValue();
double actualTraceMax = traceMaxValue;
double actualTraceMin = traceMinValue;
bool traceMaxEnabled = false;
bool traceMinEnabled = false;
juce::Synthesiser synth;
AudioWebSocketServer softwareOscilloscopeServer{audioProducer};
void updateFrame();
void updateLengthIncrement();
void incrementShapeDrawing();
void updateLuaValues();
void updateObjValues();
std::shared_ptr<Effect> getEffect(juce::String id);

Wyświetl plik

@ -91,6 +91,16 @@ double Effect::getValue() {
return getValue(0);
}
// Not thread safe! Should only be called from the audio thread
double Effect::getActualValue(int index) {
return actualValues[index];
}
// Not thread safe! Should only be called from the audio thread
double Effect::getActualValue() {
return actualValues[0];
}
void Effect::setValue(int index, double value) {
parameters[index]->setUnnormalisedValueNotifyingHost(value);
}

Wyświetl plik

@ -18,6 +18,8 @@ public:
void apply();
double getValue(int index);
double getValue();
double getActualValue(int index);
double getActualValue();
void setValue(int index, double value);
void setValue(double value);
int getPrecedence();

Wyświetl plik

@ -0,0 +1,54 @@
#include "ShapeSound.h"
ShapeSound::ShapeSound(std::shared_ptr<FileParser> parser) : parser(parser) {
if (parser->isSample()) {
producer = std::make_unique<FrameProducer>(*this, std::make_shared<FileParser>());
} else {
producer = std::make_unique<FrameProducer>(*this, parser);
}
producer->startThread();
}
bool ShapeSound::appliesToNote(int note) {
return true;
}
bool ShapeSound::appliesToChannel(int channel) {
return true;
}
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));
}
}
}
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);
}
}
return frameLength;
}

Wyświetl plik

@ -0,0 +1,26 @@
#pragma once
#include <JuceHeader.h>
#include "../parser/FileParser.h"
#include "../parser/FrameProducer.h"
#include "../parser/FrameConsumer.h"
class ShapeSound : public juce::SynthesiserSound, public FrameConsumer {
public:
ShapeSound(std::shared_ptr<FileParser> parser);
bool appliesToNote(int note) override;
bool appliesToChannel(int channel) override;
void addFrame(std::vector<std::unique_ptr<Shape>>& frame) override;
double updateFrame(std::vector<std::unique_ptr<Shape>>& frame);
std::shared_ptr<FileParser> parser;
using Ptr = juce::ReferenceCountedObjectPtr<ShapeSound>;
private:
juce::AbstractFifo frameFifo{ 10 };
std::vector<std::unique_ptr<Shape>> frameBuffer[10];
std::unique_ptr<FrameProducer> producer;
double frameLength = 0.0;
};

Wyświetl plik

@ -0,0 +1,150 @@
#include "ShapeVoice.h"
#include "../PluginProcessor.h"
ShapeVoice::ShapeVoice(OscirenderAudioProcessor& p) : audioProcessor(p) {
actualTraceMin = audioProcessor.traceMin->getValue();
actualTraceMax = audioProcessor.traceMax->getValue();
}
bool ShapeVoice::canPlaySound(juce::SynthesiserSound* sound) {
return dynamic_cast<ShapeSound*> (sound) != nullptr;
}
void ShapeVoice::startNote(int midiNoteNumber, float velocity, juce::SynthesiserSound* sound, int currentPitchWheelPosition) {
auto* shapeSound = dynamic_cast<ShapeSound*>(sound);
if (shapeSound != nullptr) {
this->sound = shapeSound;
while (frame.empty()) {
frameLength = shapeSound->updateFrame(frame);
}
tailOff = 0.0;
frequency = juce::MidiMessage::getMidiNoteInHertz(midiNoteNumber);
}
}
// TODO this is the slowest part of the program - any way to improve this would help!
void ShapeVoice::incrementShapeDrawing() {
double length = currentShape < frame.size() ? frame[currentShape]->len : 0.0;
// hard cap on how many times it can be over the length to
// prevent audio stuttering
auto increment = juce::jmin(lengthIncrement, 20 * length);
frameDrawn += increment;
shapeDrawn += increment;
// Need to skip all shapes that the lengthIncrement draws over.
// This is especially an issue when there are lots of small lines being
// drawn.
while (shapeDrawn > length) {
shapeDrawn -= length;
currentShape++;
if (currentShape >= frame.size()) {
currentShape = 0;
break;
}
// POTENTIAL TODO: Think of a way to make this more efficient when iterating
// this loop many times
length = frame[currentShape]->len;
}
}
void ShapeVoice::renderNextBlock(juce::AudioSampleBuffer& outputBuffer, int startSample, int numSamples) {
juce::ScopedNoDenormals noDenormals;
int numChannels = outputBuffer.getNumChannels();
for (auto sample = startSample; sample < startSample + numSamples; ++sample) {
bool traceMinEnabled = audioProcessor.traceMin->enabled->getBoolValue();
bool traceMaxEnabled = audioProcessor.traceMax->enabled->getBoolValue();
// update length increment
double traceMax = traceMaxEnabled ? actualTraceMax : 1.0;
double traceMin = traceMinEnabled ? actualTraceMin : 0.0;
double proportionalLength = (traceMax - traceMin) * frameLength;
// double frequency = audioProcessor.frequencyEffect->getActualValue();
lengthIncrement = juce::jmax(proportionalLength / (audioProcessor.currentSampleRate / frequency), MIN_LENGTH_INCREMENT);
Vector2 channels;
double x = 0.0;
double y = 0.0;
bool renderingSample = true;
if (sound != nullptr) {
renderingSample = sound->parser->isSample();
if (renderingSample) {
channels = sound->parser->nextSample();
} else if (currentShape < frame.size()) {
auto& shape = frame[currentShape];
double length = shape->length();
double drawingProgress = length == 0.0 ? 1 : shapeDrawn / length;
channels = shape->nextVector(drawingProgress);
}
}
x = channels.x;
y = channels.y;
if (tailOff > 0.0) {
tailOff *= 0.99999;
if (tailOff < 0.005) {
clearCurrentNote();
sound = nullptr;
break;
}
}
double gain = tailOff == 0.0 ? 1.0 : tailOff;
if (numChannels >= 2) {
outputBuffer.addSample(0, sample, x * gain);
outputBuffer.addSample(1, sample, y * gain);
} else if (numChannels == 1) {
outputBuffer.addSample(0, sample, x * gain);
}
double traceMinValue = audioProcessor.traceMin->getActualValue();
double traceMaxValue = audioProcessor.traceMax->getActualValue();
actualTraceMax = juce::jmax(actualTraceMin + MIN_TRACE, juce::jmin(traceMaxValue, 1.0));
actualTraceMin = juce::jmax(MIN_TRACE, juce::jmin(traceMinValue, actualTraceMax - MIN_TRACE));
if (!renderingSample) {
incrementShapeDrawing();
}
double drawnFrameLength = traceMaxEnabled ? actualTraceMax * frameLength : frameLength;
if (!renderingSample && frameDrawn >= drawnFrameLength) {
if (sound != nullptr) {
frameLength = sound->updateFrame(frame);
}
// TODO: updateFrame already iterates over all the shapes,
// so we can improve performance by calculating frameDrawn
// and shapeDrawn directly. frameDrawn is simply actualTraceMin * frameLength
// but shapeDrawn is the amount of the current shape that has been drawn so
// we need to iterate over all the shapes to calculate it.
if (traceMinEnabled) {
while (frameDrawn < actualTraceMin * frameLength) {
incrementShapeDrawing();
}
}
}
}
}
void ShapeVoice::stopNote(float velocity, bool allowTailOff) {
if (allowTailOff) {
if (tailOff == 0.0) {
tailOff = 1.0;
}
} else {
clearCurrentNote();
sound = nullptr;
}
}
void ShapeVoice::pitchWheelMoved(int newPitchWheelValue) {}
void ShapeVoice::controllerMoved(int controllerNumber, int newControllerValue) {}

Wyświetl plik

@ -0,0 +1,37 @@
#pragma once
#include <JuceHeader.h>
#include "ShapeSound.h"
class OscirenderAudioProcessor;
class ShapeVoice : public juce::SynthesiserVoice {
public:
ShapeVoice(OscirenderAudioProcessor& p);
bool canPlaySound(juce::SynthesiserSound* sound) override;
void startNote(int midiNoteNumber, float velocity, juce::SynthesiserSound* sound, int currentPitchWheelPosition) override;
void renderNextBlock(juce::AudioSampleBuffer& outputBuffer, int startSample, int numSamples) override;
void stopNote(float velocity, bool allowTailOff) override;
void pitchWheelMoved(int newPitchWheelValue) override;
void controllerMoved(int controllerNumber, int newControllerValue) override;
void incrementShapeDrawing();
private:
const double MIN_TRACE = 0.005;
const double MIN_LENGTH_INCREMENT = 0.000001;
OscirenderAudioProcessor& audioProcessor;
std::vector<std::unique_ptr<Shape>> frame;
ShapeSound* sound = nullptr;
double actualTraceMin;
double actualTraceMax;
double frameLength = 0.0;
int currentShape = 0;
double shapeDrawn = 0.0;
double frameDrawn = 0.0;
double lengthIncrement = 0.0;
double tailOff = 0.0;
double frequency = 1.0;
};

Wyświetl plik

@ -6,5 +6,5 @@
class FrameConsumer {
public:
virtual void addFrame(std::vector<std::unique_ptr<Shape>> frame, int fileIndex) = 0;
virtual void addFrame(std::vector<std::unique_ptr<Shape>>& frame) = 0;
};

Wyświetl plik

@ -9,15 +9,7 @@ FrameProducer::~FrameProducer() {
void FrameProducer::run() {
while (!threadShouldExit()) {
// this lock is needed so that frameSource isn't deleted whilst nextFrame() is being called
juce::SpinLock::ScopedLockType scope(lock);
frameConsumer.addFrame(frameSource->nextFrame(), sourceFileIndex);
auto frame = frameSource->nextFrame();
frameConsumer.addFrame(frame);
}
}
void FrameProducer::setSource(std::shared_ptr<FileParser> source, int fileIndex) {
juce::SpinLock::ScopedLockType scope(lock);
frameSource->disable();
frameSource = source;
sourceFileIndex = fileIndex;
}

Wyświetl plik

@ -10,10 +10,8 @@ public:
~FrameProducer() override;
void run() override;
void setSource(std::shared_ptr<FileParser>, int fileIndex);
private:
juce::SpinLock lock;
FrameConsumer& frameConsumer;
std::shared_ptr<FileParser> frameSource;
int sourceFileIndex = -1;
};

Wyświetl plik

@ -66,6 +66,10 @@
<FILE id="PbbNqz" name="RotateEffect.cpp" compile="1" resource="0"
file="Source/audio/RotateEffect.cpp"/>
<FILE id="tUwNZV" name="RotateEffect.h" compile="0" resource="0" file="Source/audio/RotateEffect.h"/>
<FILE id="dBaZAV" name="ShapeSound.cpp" compile="1" resource="0" file="Source/audio/ShapeSound.cpp"/>
<FILE id="VKBirB" name="ShapeSound.h" compile="0" resource="0" file="Source/audio/ShapeSound.h"/>
<FILE id="UcPZ09" name="ShapeVoice.cpp" compile="1" resource="0" file="Source/audio/ShapeVoice.cpp"/>
<FILE id="WId4vx" name="ShapeVoice.h" compile="0" resource="0" file="Source/audio/ShapeVoice.h"/>
<FILE id="iUEfwT" name="SmoothEffect.cpp" compile="1" resource="0"
file="Source/audio/SmoothEffect.cpp"/>
<FILE id="Vwjht7" name="SmoothEffect.h" compile="0" resource="0" file="Source/audio/SmoothEffect.h"/>