osci-render/Source/SosciPluginProcessor.cpp

194 wiersze
7.2 KiB
C++

#include "SosciPluginProcessor.h"
#include "SosciPluginEditor.h"
#include "audio/EffectParameter.h"
SosciAudioProcessor::SosciAudioProcessor() {
// demo audio file on standalone only
if (juce::JUCEApplicationBase::isStandaloneApp()) {
std::unique_ptr<juce::InputStream> stream = std::make_unique<juce::MemoryInputStream>(BinaryData::sosci_flac, BinaryData::sosci_flacSize, false);
loadAudioFile(std::move(stream));
}
addAllParameters();
}
SosciAudioProcessor::~SosciAudioProcessor() {}
void SosciAudioProcessor::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();
juce::SpinLock::ScopedLockType lock2(wavParserLock);
bool readingFromWav = wavParser.isInitialised();
for (int sample = 0; sample < input.getNumSamples(); ++sample) {
OsciPoint point;
if (readingFromWav) {
point = wavParser.getSample();
} else {
float x = input.getNumChannels() > 0 ? inputArray[0][sample] : 0.0f;
float y = input.getNumChannels() > 1 ? inputArray[1][sample] : 0.0f;
float brightness = 1.0f;
if (input.getNumChannels() > 2 && !forceDisableBrightnessInput) {
float brightnessChannel = inputArray[2][sample];
// Only enable brightness if we actually receive a signal on the brightness channel
if (!brightnessEnabled && brightnessChannel > EPSILON) {
brightnessEnabled = true;
}
if (brightnessEnabled) {
brightness = brightnessChannel;
}
}
point = { x, y, brightness };
}
// no negative brightness
point.z = juce::jlimit(0.0, 1.0, point.z);
for (auto& effect : permanentEffects) {
point = effect->apply(sample, point);
}
// this is the point that the visualiser will draw
threadManager.write(point, "VisualiserComponent");
point.scale(volume, volume, 1.0);
// clip
point.x = juce::jmax(-threshold, juce::jmin(threshold.load(), point.x));
point.y = juce::jmax(-threshold, juce::jmin(threshold.load(), point.y));
// this is the point that the volume component will draw (i.e. post scale/clipping)
threadManager.write(point, "VolumeComponent");
if (output.getNumChannels() > 0) {
outputArray[0][sample] = point.x;
}
if (output.getNumChannels() > 1) {
outputArray[1][sample] = point.y;
}
if (output.getNumChannels() > 2) {
outputArray[2][sample] = point.z;
}
}
}
void SosciAudioProcessor::getStateInformation(juce::MemoryBlock& destData) {
// we need to stop recording the visualiser when saving the state, otherwise
// there are issues. This is the only place we can do this because there is
// no callback when closing the standalone app except for this.
if (haltRecording != nullptr && juce::JUCEApplicationBase::isStandaloneApp()) {
haltRecording();
}
juce::SpinLock::ScopedLockType lock2(effectsLock);
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"));
}
auto booleanParametersXml = xml->createNewChildElement("booleanParameters");
for (auto parameter : booleanParameters) {
auto parameterXml = booleanParametersXml->createNewChildElement("parameter");
parameter->save(parameterXml);
}
auto floatParametersXml = xml->createNewChildElement("floatParameters");
for (auto parameter : floatParameters) {
auto parameterXml = floatParametersXml->createNewChildElement("parameter");
parameter->save(parameterXml);
}
auto intParametersXml = xml->createNewChildElement("intParameters");
for (auto parameter : intParameters) {
auto parameterXml = intParametersXml->createNewChildElement("parameter");
parameter->save(parameterXml);
}
recordingParameters.save(xml.get());
copyXmlToBinary(*xml, destData);
}
void SosciAudioProcessor::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")) {
juce::SpinLock::ScopedLockType lock2(effectsLock);
auto effectsXml = xml->getChildByName("effects");
if (effectsXml != nullptr) {
for (auto effectXml : effectsXml->getChildIterator()) {
auto effect = getEffect(effectXml->getStringAttribute("id"));
if (effect != nullptr) {
effect->load(effectXml);
}
}
}
auto booleanParametersXml = xml->getChildByName("booleanParameters");
if (booleanParametersXml != nullptr) {
for (auto parameterXml : booleanParametersXml->getChildIterator()) {
auto parameter = getBooleanParameter(parameterXml->getStringAttribute("id"));
if (parameter != nullptr) {
parameter->load(parameterXml);
}
}
}
auto floatParametersXml = xml->getChildByName("floatParameters");
if (floatParametersXml != nullptr) {
for (auto parameterXml : floatParametersXml->getChildIterator()) {
auto parameter = getFloatParameter(parameterXml->getStringAttribute("id"));
if (parameter != nullptr) {
parameter->load(parameterXml);
}
}
}
auto intParametersXml = xml->getChildByName("intParameters");
if (intParametersXml != nullptr) {
for (auto parameterXml : intParametersXml->getChildIterator()) {
auto parameter = getIntParameter(parameterXml->getStringAttribute("id"));
if (parameter != nullptr) {
parameter->load(parameterXml);
}
}
}
recordingParameters.load(xml.get());
}
}
juce::AudioProcessorEditor* SosciAudioProcessor::createEditor() {
auto editor = new SosciPluginEditor(*this);
return editor;
}
juce::AudioProcessor* JUCE_CALLTYPE createPluginFilter() {
return new SosciAudioProcessor();
}