kopia lustrzana https://github.com/jameshball/osci-render
234 wiersze
7.6 KiB
C++
234 wiersze
7.6 KiB
C++
![]() |
#include "EffectPluginProcessor.h"
|
||
|
#include "EffectPluginEditor.h"
|
||
|
|
||
|
//==============================================================================
|
||
|
EffectAudioProcessor::EffectAudioProcessor()
|
||
|
#ifndef JucePlugin_PreferredChannelConfigurations
|
||
|
: AudioProcessor(
|
||
|
BusesProperties()
|
||
|
.withInput("Input", juce::AudioChannelSet::stereo(), true)
|
||
|
.withOutput("Output", juce::AudioChannelSet::stereo(), true)
|
||
|
)
|
||
|
#endif
|
||
|
{
|
||
|
effects.push_back(bitCrush);
|
||
|
|
||
|
for (auto effect : visualiserParameters.effects) {
|
||
|
effects.push_back(effect);
|
||
|
}
|
||
|
|
||
|
for (auto effect : effects) {
|
||
|
for (auto effectParameter : effect->parameters) {
|
||
|
auto parameters = effectParameter->getParameters();
|
||
|
for (auto parameter : parameters) {
|
||
|
addParameter(parameter);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
titleShapes = titleParser.draw();
|
||
|
titleShapesLength = osci::Shape::totalLength(titleShapes);
|
||
|
}
|
||
|
|
||
|
const juce::String EffectAudioProcessor::getName() const {
|
||
|
return JucePlugin_Name;
|
||
|
}
|
||
|
|
||
|
bool EffectAudioProcessor::acceptsMidi() const {
|
||
|
#if JucePlugin_WantsMidiInput
|
||
|
return true;
|
||
|
#else
|
||
|
return false;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
bool EffectAudioProcessor::producesMidi() const {
|
||
|
#if JucePlugin_ProducesMidiOutput
|
||
|
return true;
|
||
|
#else
|
||
|
return false;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
bool EffectAudioProcessor::isMidiEffect() const {
|
||
|
#if JucePlugin_IsMidiEffect
|
||
|
return true;
|
||
|
#else
|
||
|
return false;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
double EffectAudioProcessor::getTailLengthSeconds() const {
|
||
|
return 0.0;
|
||
|
}
|
||
|
|
||
|
int EffectAudioProcessor::getNumPrograms() {
|
||
|
return 1; // NB: some hosts don't cope very well if you tell them there are 0 programs,
|
||
|
// so this should be at least 1, even if you're not really implementing programs.
|
||
|
}
|
||
|
|
||
|
int EffectAudioProcessor::getCurrentProgram() {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void EffectAudioProcessor::setCurrentProgram(int index) {
|
||
|
}
|
||
|
|
||
|
const juce::String EffectAudioProcessor::getProgramName(int index) {
|
||
|
return {};
|
||
|
}
|
||
|
|
||
|
void EffectAudioProcessor::changeProgramName(int index, const juce::String& newName) {}
|
||
|
|
||
|
void EffectAudioProcessor::prepareToPlay(double sampleRate, int samplesPerBlock) {
|
||
|
for (auto& effect : effects) {
|
||
|
effect->updateSampleRate(sampleRate);
|
||
|
}
|
||
|
|
||
|
currentSampleRate = sampleRate;
|
||
|
|
||
|
threadManager.prepare(sampleRate, samplesPerBlock);
|
||
|
}
|
||
|
|
||
|
void EffectAudioProcessor::processBlock(juce::AudioBuffer<float>& buffer, juce::MidiBuffer& midiMessages) {
|
||
|
juce::ScopedNoDenormals noDenormals;
|
||
|
|
||
|
auto input = getBusBuffer(buffer, true, 0);
|
||
|
auto output = getBusBuffer(buffer, false, 0);
|
||
|
float EPSILON = 0.0001f;
|
||
|
|
||
|
midiMessages.clear();
|
||
|
|
||
|
auto inputArray = input.getArrayOfWritePointers();
|
||
|
auto outputArray = output.getArrayOfWritePointers();
|
||
|
|
||
|
for (int sample = 0; sample < input.getNumSamples(); ++sample) {
|
||
|
float x = input.getNumChannels() > 0 ? inputArray[0][sample] : 0.0f;
|
||
|
float y = input.getNumChannels() > 1 ? inputArray[1][sample] : 0.0f;
|
||
|
|
||
|
osci::Point point = { x, y, 1.0f };
|
||
|
|
||
|
for (auto& effect : effects) {
|
||
|
point = effect->apply(sample, point);
|
||
|
}
|
||
|
|
||
|
osci::Point audioGainPoint = autoGain->apply(sample, point);
|
||
|
double limit = 0.95;
|
||
|
audioGainPoint.scale(limit, limit, 1.0); // scale the point to fit the screen
|
||
|
audioGainPoint.x = juce::jlimit(-limit, limit, audioGainPoint.x);
|
||
|
audioGainPoint.y = juce::jlimit(-limit, limit, audioGainPoint.y);
|
||
|
|
||
|
threadManager.write(audioGainPoint, "VisualiserRendererMain");
|
||
|
|
||
|
if (output.getNumChannels() > 0) {
|
||
|
outputArray[0][sample] = point.x;
|
||
|
}
|
||
|
if (output.getNumChannels() > 1) {
|
||
|
outputArray[1][sample] = point.y;
|
||
|
}
|
||
|
|
||
|
// handle the title drawing
|
||
|
|
||
|
osci::Point titlePoint = { 0.0, 0.0, 1.0f };
|
||
|
if (currentTitleShape < titleShapes.size()) {
|
||
|
auto& shape = titleShapes[currentTitleShape];
|
||
|
double length = shape->length();
|
||
|
double drawingProgress = length == 0.0 ? 1 : titleShapeDrawn / length;
|
||
|
titlePoint = shape->nextVector(drawingProgress);
|
||
|
titlePoint.z = 1.0f;
|
||
|
|
||
|
// apply bit crush without animating values, as this has already been done
|
||
|
titlePoint = bitCrush->apply(sample, titlePoint, 0.0, false);
|
||
|
}
|
||
|
|
||
|
threadManager.write(titlePoint, "VisualiserRendererTitle");
|
||
|
|
||
|
incrementTitleShapeDrawing();
|
||
|
|
||
|
if (titleFrameDrawn >= titleShapesLength) {
|
||
|
double currentShapeLength = 0;
|
||
|
if (currentTitleShape < titleShapes.size()) {
|
||
|
currentShapeLength = titleShapes[currentTitleShape]->len;
|
||
|
}
|
||
|
titleFrameDrawn -= titleShapesLength;
|
||
|
currentTitleShape = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void EffectAudioProcessor::incrementTitleShapeDrawing() {
|
||
|
if (titleShapes.size() <= 0) return;
|
||
|
double length = currentTitleShape < titleShapes.size() ? titleShapes[currentTitleShape]->len : 0.0;
|
||
|
double FREQUENCY = 60.0;
|
||
|
double lengthIncrement = titleShapesLength / (currentSampleRate / FREQUENCY);
|
||
|
titleFrameDrawn += lengthIncrement;
|
||
|
titleShapeDrawn += lengthIncrement;
|
||
|
|
||
|
// 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 (titleShapeDrawn > length) {
|
||
|
titleShapeDrawn -= length;
|
||
|
currentTitleShape++;
|
||
|
if (currentTitleShape >= titleShapes.size()) {
|
||
|
currentTitleShape = 0;
|
||
|
}
|
||
|
length = titleShapes[currentTitleShape]->len;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void EffectAudioProcessor::getStateInformation(juce::MemoryBlock& destData) {
|
||
|
std::unique_ptr<juce::XmlElement> xml = std::make_unique<juce::XmlElement>("project");
|
||
|
xml->setAttribute("version", ProjectInfo::versionString);
|
||
|
|
||
|
auto effectsXml = xml->createNewChildElement("effects");
|
||
|
for (auto effect : effects) {
|
||
|
effect->save(effectsXml->createNewChildElement("effect"));
|
||
|
}
|
||
|
|
||
|
copyXmlToBinary(*xml, destData);
|
||
|
}
|
||
|
|
||
|
void EffectAudioProcessor::setStateInformation(const void* data, int sizeInBytes) {
|
||
|
std::unique_ptr<juce::XmlElement> xml;
|
||
|
|
||
|
const uint32_t magicXmlNumber = 0x21324356;
|
||
|
if (sizeInBytes > 8 && juce::ByteOrder::littleEndianInt(data) == magicXmlNumber) {
|
||
|
// this is a binary xml format
|
||
|
xml = getXmlFromBinary(data, sizeInBytes);
|
||
|
} else {
|
||
|
// this is a text xml format
|
||
|
xml = juce::XmlDocument::parse(juce::String((const char*)data, sizeInBytes));
|
||
|
} if (xml.get() != nullptr && xml->hasTagName("project")) {
|
||
|
auto effectsXml = xml->getChildByName("effects");
|
||
|
if (effectsXml != nullptr) {
|
||
|
for (auto effectXml : effectsXml->getChildIterator()) {
|
||
|
if (effectXml->getStringAttribute("id") == bitCrush->getId()) {
|
||
|
bitCrush->load(effectXml);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void EffectAudioProcessor::releaseResources() {
|
||
|
// When playback stops, you can use this as an opportunity to free up any
|
||
|
// spare memory, etc.
|
||
|
}
|
||
|
|
||
|
bool EffectAudioProcessor::isBusesLayoutSupported(const BusesLayout& layouts) const {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool EffectAudioProcessor::hasEditor() const {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
juce::AudioProcessorEditor* EffectAudioProcessor::createEditor() {
|
||
|
return new EffectPluginEditor(*this);
|
||
|
}
|
||
|
|
||
|
juce::AudioProcessor* JUCE_CALLTYPE createPluginFilter() {
|
||
|
return new EffectAudioProcessor();
|
||
|
}
|